summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/ValidationKit
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit')
-rw-r--r--src/VBox/ValidationKit/.scm-settings79
-rw-r--r--src/VBox/ValidationKit/Config.kmk282
-rw-r--r--src/VBox/ValidationKit/Makefile.kmk400
-rw-r--r--src/VBox/ValidationKit/ValidationKitCodingGuidelines.cpp87
-rw-r--r--src/VBox/ValidationKit/analysis/Makefile.kmk45
-rw-r--r--src/VBox/ValidationKit/analysis/__init__.py40
-rwxr-xr-xsrc/VBox/ValidationKit/analysis/analyze.py447
-rwxr-xr-xsrc/VBox/ValidationKit/analysis/reader.py762
-rwxr-xr-xsrc/VBox/ValidationKit/analysis/reporting.py746
-rw-r--r--src/VBox/ValidationKit/bootsectors/Config.kmk951
-rw-r--r--src/VBox/ValidationKit/bootsectors/Makefile.kmk472
-rw-r--r--src/VBox/ValidationKit/bootsectors/VBoxBs2Linker.cpp229
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector-empty.asm70
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector-pae.asm175
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector-shutdown.asm90
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-api.mac162
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-boot-registers-1.asm86
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-end.mac52
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac2313
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-init-traps.mac1619
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-macros-1.mac60
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac2655
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-2.mac224
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-routines.mac246
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec-template.mac115
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec.mac209
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1-template.mac385
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1.asm386
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-ac-loop.asm125
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1-template.mac84
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1.asm129
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-db-loop.asm161
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1-template.mac352
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1.asm285
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1-template.mac567
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1.asm120
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac1061
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1.asm164
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1-template.mac1973
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1.asm138
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2-template.mac511
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2.asm129
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-first.mac84
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-structures.mac101
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-template-footer.mac116
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-template-header.mac803
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-test1-template.mac836
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-test1.asm216
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-triple-fault-1.asm390
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-64-1.asm121
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-big-template.asm91
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-kernel.asm527
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-apic-1-32.c32119
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-apic-1.c108
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-32.c3266
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-asm.asm283
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c321890
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.c1548
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.mac1879
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-x0.c6400
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2.c127
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-asm.asm49
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.c68
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.mac124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1.c321736
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-asm.asm44
-rwxr-xr-xsrc/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py665
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-template.c6152
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.c69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.h817
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-asm.asm72
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.c3196
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.mac845
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2.c130
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-asm.asm48
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-template.mac2963
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3.c3212210
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1-asm.asm242
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1.c64166
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-asm.asm69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-template.mac167
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-x0.c1086
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1.c76
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-asm.asm172
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.c409
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.mac418
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-fpustate-1.c94
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-locking-1.c246
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-memalloc-1.c64282
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-timers-1-x0.c102
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-timers-1.c75
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-timing-1-32.c32363
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-timing-1-exe.c126
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-timing-1.c63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/Makefile.kmk760
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp362
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3ObjConverter.cpp5530
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/asmdefs-first.mac62
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm594
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-CreateHybridFarRet.asm63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-SwitchFromV86To16BitAndCallC.asm109
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-Trap16Generic.asm720
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Data.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Generic.asm401
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c32-Trap32Generic.asm546
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c64-Trap64Generic.asm337
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Disable.asm115
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Enable.asm122
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm88
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-CpuDetectData.c54
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxAlloc.c54
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxCopy.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxFree.c56
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetAbridgedFtw.c71
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFcw.c59
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFsw.c59
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMm.c60
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsr.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsrMask.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetSize.c69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetXmm.c64
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetYmm.c70
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxInit.c84
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestore.asm155
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestoreEx.asm136
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSave.asm166
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSaveEx.asm136
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetAbridgedFtw.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFcw.c60
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFsw.c60
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMm.c66
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsr.c56
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsrMask.c56
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetXmm.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetYmm.c79
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetCpuVendor.c63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeName.c72
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeNameShortLower.c72
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdRead.asm75
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWait.asm64
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWrite.asm82
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAlloc.c111
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAllocZ.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemChr.asm88
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCmp.asm99
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCpy.c85
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemFree.c73
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemGuardedTestPage.c109
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemMove.c88
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPCpy.c58
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPrintInfo.c95
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemSet.asm102
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemZero.asm103
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingAlias.c193
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingData.c59
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForLM.c115
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPAE.c102
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPP.c163
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingMapRamAbove4GForLM.c120
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingProtect.c392
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingQueryAddressInfo.c159
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingSetupCanonicalTraps.c123
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Panic.asm48
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PerCpuData.c74
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicMaskAll.c51
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicSetup.c90
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicUpdateMask.c55
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PitIrqHandler.c76
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintChr.asm115
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStr.c44
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStrN.asm204
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintU32.asm93
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintX32.asm97
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Printf.c95
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertToRingX.c182
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertV86ToRm.c55
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxGetRspSsAsCurPtr.c71
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxPrint.c77
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxRestore.asm608
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSave.asm271
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveEx.asm460
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveForMode.c65
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGpr.c64
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromCurPtr.c60
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromFlat.c75
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromCurPtr.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromFlat.c75
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromLnkPtr.c87
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr0.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr2.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr3.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr4.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr0.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr1.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr2.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr3.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr6.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr7.asm89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDrX.asm135
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetLdtr.asm80
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetTr.asm80
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetXcr0.asm77
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr0.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr2.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr3.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr4.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr0.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr1.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr2.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr3.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr6.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr7.asm96
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDrX.asm142
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetLdtr.asm91
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetTr.asm108
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetXcr0.asm104
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32.c47
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32NoClobber.asm114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToProtFar16.asm128
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToRealMode.asm163
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToProtFar16.asm142
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToRealMode.asm104
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToCurPtr.c51
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToFlat.c49
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToFlat.asm100
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToRealMode.asm157
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar32ToFlat32.c55
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtModeCodeToRealMode.asm122
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeCodeToProtMode.asm94
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToFlat.asm101
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToProtFar16.asm151
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitCode.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitData.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup32BitCode.c62
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate64.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Shutdown.asm63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAlloc.c64
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAllocEx.c111
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabFree.c69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabInit.c72
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAdd.c53
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAlloc.c67
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAllocEx.c68
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListFree.c64
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListInit.c50
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrCpy.c51
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrFormatV.c788
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrLen.c47
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrNLen.c47
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrPrintf.c105
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm78
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm126
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm109
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16Bit.asm130
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16BitV86.asm133
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo32Bit.asm162
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo64Bit.asm120
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing0.asm73
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing1.asm73
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing2.asm73
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing3.asm73
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRingX.asm103
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Syscall.asm94
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckExtCtx.c287
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckRegCtxEx.c107
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestData.c135
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestDoModesByOneHlp.asm253
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestFailed.c151
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestHostPrintf.c114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestInit.c77
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestIsVmmDevTestingPresent.asm78
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestNow.asm113
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestPrintf.c146
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU32.asm94
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU8.asm86
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithStr.asm90
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithU32.asm88
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSkipped.c100
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSub.c105
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubDone.c54
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubErrorCount.c54
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestTerm.c117
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestValue.c86
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16Init.c129
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16SetGate.c62
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32Init.c101
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32SetGate.c62
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64Init.c109
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64SetGate.c65
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapDefaultHandler.c331
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapHandlersData.asm52
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapPrintFrame.c89
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapReInit.c65
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86Init.c122
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86SetGate.c51
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetDpl.c57
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandler.c57
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandlerEx.c71
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmp.asm143
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestore.c56
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreInRm.c133
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c66
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithRm.c59
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapUnsetJmp.c58
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt32Div.c50
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt64Div.c50
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullGdtr.asm195
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullIdtr.asm195
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-hexdigits.c42
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-common.h214
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x0.c1639
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x1.c1639
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c1639
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c3239
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c6439
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-memory.h109
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-paging.h69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic-data.c52
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic.h70
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pit.c170
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-test.h179
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-common.mac281
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-dosexe.asm43
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-lm64.asm91
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pe32.asm69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pp32.asm69
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-pe16.asm105
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-rm.asm59
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-BiosInt15hE820.asm245
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm347
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-EnteredMode.asm279
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-Name.asm44
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-NameShortLower.asm46
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForLM64.asm141
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE16.asm55
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE32.asm127
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP16.asm55
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP32.asm142
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchTo32BitAndCallC.asm164
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM16.asm136
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM32.asm203
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM64.asm114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16.asm243
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_32.asm114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_V86.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32.asm202
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32_16.asm132
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAEV86.asm118
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16.asm198
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_32.asm114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_V86.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32.asm179
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32_16.asm132
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPEV86.asm118
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16.asm258
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_32.asm114
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_V86.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32.asm209
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32_16.asm132
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPPV86.asm118
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToRM.asm411
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.c380
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.h99
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMax.c380
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMaxStub.asm63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOne.c424
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOneStub.asm63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesHlp.asm1139
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesStub.asm63
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapInit.c61
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm174
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSystemCallHandler.asm891
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitAll.c100
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitGdt.c71
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitMemory.c317
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-shutdown.c87
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-system-data.asm1056
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I4D.asm80
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DQ.asm121
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DR.asm121
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8RS.asm78
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U4D.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DQ.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DR.asm124
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8LS.asm80
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8RS.asm82
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8D.asm81
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8RS.asm70
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8D.asm81
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8LS.asm72
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8M.asm93
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8RS.asm70
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3cpudt.c71
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-autostubs.kmk194
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-docs.c170
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-define.h256
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-undef.h256
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code.h52
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-data.h295
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.h93
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.mac142
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.h532
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.mac532
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.h4534
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.mac1767
-rw-r--r--src/VBox/ValidationKit/bootsectors/todo.txt10
-rw-r--r--src/VBox/ValidationKit/common/Makefile.kmk77
-rwxr-xr-xsrc/VBox/ValidationKit/common/__init__.py47
-rw-r--r--src/VBox/ValidationKit/common/constants/Makefile.kup0
-rwxr-xr-xsrc/VBox/ValidationKit/common/constants/__init__.py47
-rw-r--r--src/VBox/ValidationKit/common/constants/result.py51
-rw-r--r--src/VBox/ValidationKit/common/constants/rtexitcode.py61
-rw-r--r--src/VBox/ValidationKit/common/constants/tbreq.py131
-rw-r--r--src/VBox/ValidationKit/common/constants/tbresp.py93
-rw-r--r--src/VBox/ValidationKit/common/constants/valueunit.py168
-rwxr-xr-xsrc/VBox/ValidationKit/common/netutils.py146
-rw-r--r--src/VBox/ValidationKit/common/pathutils.py123
-rwxr-xr-xsrc/VBox/ValidationKit/common/utils.py2571
-rwxr-xr-xsrc/VBox/ValidationKit/common/webutils.py223
-rw-r--r--src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html1354
-rw-r--r--src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt1061
-rw-r--r--src/VBox/ValidationKit/docs/Makefile.kmk69
-rw-r--r--src/VBox/ValidationKit/docs/TestBoxImaging.html758
-rw-r--r--src/VBox/ValidationKit/docs/TestBoxImaging.txt368
-rw-r--r--src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.html601
-rw-r--r--src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.txt207
-rw-r--r--src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html467
-rw-r--r--src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt113
-rw-r--r--src/VBox/ValidationKit/docs/WindbgPython.txt10
-rwxr-xr-xsrc/VBox/ValidationKit/docs/testbox-maintenance.sh409
-rwxr-xr-xsrc/VBox/ValidationKit/docs/testbox-pxe-conf.sh162
-rw-r--r--src/VBox/ValidationKit/docs/valkit.txt1
-rw-r--r--src/VBox/ValidationKit/jshintrc.js40
-rw-r--r--src/VBox/ValidationKit/readme.txt3
-rw-r--r--src/VBox/ValidationKit/snippets/alloc-1.c110
-rw-r--r--src/VBox/ValidationKit/snippets/time-1.c123
-rw-r--r--src/VBox/ValidationKit/testboxscript/Makefile.kmk97
-rw-r--r--src/VBox/ValidationKit/testboxscript/TestBoxHelper.cpp780
-rw-r--r--src/VBox/ValidationKit/testboxscript/darwin/setup-routines.sh190
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/linux/setup-routines.sh172
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/linux/testboxscript-service.sh519
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/setup.sh714
-rw-r--r--src/VBox/ValidationKit/testboxscript/solaris/setup-routines.sh360
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxcommand.py362
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxcommons.py146
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxconnection.py312
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxscript.py137
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxscript_real.py1073
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxtasks.py944
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/testboxupgrade.py339
-rw-r--r--src/VBox/ValidationKit/testboxscript/win/autoexec-testbox.cmd72
-rwxr-xr-xsrc/VBox/ValidationKit/testboxscript/win/fix_stale_refs.py160
-rw-r--r--src/VBox/ValidationKit/testboxscript/win/readme.txt157
-rw-r--r--src/VBox/ValidationKit/testdriver/Makefile.kmk48
-rw-r--r--src/VBox/ValidationKit/testdriver/__init__.py41
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/base.py1860
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/btresolver.py626
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/reporter.py1984
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/testfileset.py690
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/tst-txsclient.py315
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/txsclient.py2376
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vbox.py4581
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxcon.py94
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxinstaller.py1251
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxtestfileset.py149
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxtestvms.py2105
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/vboxwrappers.py3666
-rw-r--r--src/VBox/ValidationKit/testdriver/win-vbox-net-drvstore-cleanup.ps171
-rw-r--r--src/VBox/ValidationKit/testdriver/win-vbox-net-uninstall.ps1253
-rwxr-xr-xsrc/VBox/ValidationKit/testdriver/winbase.py336
-rw-r--r--src/VBox/ValidationKit/testmanager/Makefile.kmk54
-rw-r--r--src/VBox/ValidationKit/testmanager/__init__.py40
-rw-r--r--src/VBox/ValidationKit/testmanager/apache-template-2.2.conf87
-rw-r--r--src/VBox/ValidationKit/testmanager/apache-template-2.4.conf81
-rw-r--r--src/VBox/ValidationKit/testmanager/batch/Makefile.kmk46
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/add_build.py137
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/check_for_deleted_builds.py133
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/close_orphaned_testsets.py105
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/del_build.py95
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/filearchiver.py282
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/quota.py319
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/regen_sched_queues.py132
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/vcs_import.py205
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py1832
-rw-r--r--src/VBox/ValidationKit/testmanager/cgi/Makefile.kmk46
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/admin.py77
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/debuginfo.py71
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/index.py78
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/logout.py79
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/logout2.py81
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/rest.py81
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/status.py519
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/cgi/testboxdisp.py75
-rw-r--r--src/VBox/ValidationKit/testmanager/config.py261
-rw-r--r--src/VBox/ValidationKit/testmanager/core/Makefile.kmk46
-rw-r--r--src/VBox/ValidationKit/testmanager/core/__init__.py40
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/base.py1514
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/build.py891
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/buildblacklist.py324
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/buildsource.py524
-rw-r--r--src/VBox/ValidationKit/testmanager/core/coreconsts.py100
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/db.py745
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/dbobjcache.py200
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/failurecategory.py392
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/failurereason.py580
-rw-r--r--src/VBox/ValidationKit/testmanager/core/globalresource.pgsql118
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/globalresource.py328
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/report.py1307
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/restdispatcher.py455
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/schedgroup.py1352
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/schedqueue.py153
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/schedulerbase.py1570
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/schedulerbeci.py128
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/systemchangelog.py202
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/systemlog.py186
-rw-r--r--src/VBox/ValidationKit/testmanager/core/testbox.pgsql635
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testbox.py1286
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testboxcontroller.py954
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testboxstatus.py317
-rw-r--r--src/VBox/ValidationKit/testmanager/core/testcase.pgsql275
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testcase.py1467
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testcaseargs.py416
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testgroup.py771
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testresultfailures.py529
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testresults.py2926
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/testset.py869
-rw-r--r--src/VBox/ValidationKit/testmanager/core/useraccount.pgsql178
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/useraccount.py302
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/vcsbugreference.py251
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/vcsrevisions.py254
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/webservergluebase.py717
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/core/webservergluecgi.py100
-rw-r--r--src/VBox/ValidationKit/testmanager/db/Makefile.kmk98
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase.dmd8
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/DataTypes.xml15
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/47E390DE-0671-C4B1-8428-0F45CBEE18F8.xml37
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1.xml37
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/subviews/E9476B45-3C62-EE27-4705-6F1EFAD11B74.xml21
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultRDBMSSites.xml12
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultdomains.xml13
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dl_settings.xml288
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dr_custom_scripts.xml360
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/Logical.xml7
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/16464F5A-64BE-D2ED-91E0-BCBD0AA34680.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/1BEAB532-23CA-8628-0C97-7CAD39119A4E.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/24150FB1-B00F-4F69-6F77-49ECB58F0F66.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/28DD93CF-D058-7343-CD47-E9B435E1AC16.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/2F6ACC6D-3D17-537D-8ADF-F8424395B345.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4579B792-2F35-D72A-1A3B-C7E53C41A766.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4D937E7C-3A28-E52D-89C0-EC8804C62367.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/504221DA-1B57-4EAD-39DB-40FD553E9FA2.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/6A886CEE-579B-48FF-63F6-0FB03393FBF6.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/7AE36CC1-A030-63E5-6EF3-72FCD04815EE.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90367AFB-BA2D-A918-46B9-1E5DE53ACC48.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90F477EE-35D6-21A7-B693-E5724FB07476.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/9F78B73C-056D-DDEF-8C50-A9DA76B9E724.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A352A20F-310D-E285-FBC9-90DD0DA7BB9B.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A6A5F317-479C-A0DD-CAAE-9DCB56B29D40.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B36A186B-CDB3-7851-8C38-12EA8D50EAEB.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B82DAF9A-6F99-5CF6-4D99-A391BAD66192.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C332E3D7-638B-6CA8-24BF-383CA8659A3A.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C79482B8-771B-FAD8-0337-163E3A45003A.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/D09E0DE5-99D6-2991-032A-A8A124F6ACBA.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DCC79294-5434-1DED-298C-6473DEE59FBA.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DE366053-6F7A-7F42-ABA3-00E583098C37.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/E93BBF08-067B-A665-39F3-CF488A6547B2.xml52
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/876CB767-80BA-6C8E-AACA-F1CCC95C445E.xml16
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/D487AFDC-4027-F824-EA29-5C6D0ABB9E1E.xml16
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/01537211-CCFB-0A1E-B43B-E8C641B69471.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/02096BBB-0795-1759-1E26-2877BE36BB59.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/0CCF1DE3-7916-9054-BEA6-C601FF564DB2.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/10867E70-94CE-FDAF-6B6E-2742D3A49E57.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/11710A55-6423-1904-841A-C7D2AB8CEEBF.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/1C189437-742B-B999-C955-7754C8ADB089.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/34733942-1305-4CA1-47EB-ACE724B04E69.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3563C940-E524-7F96-7AE0-DAC3C1C17AFC.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3B7C8913-EB6A-47B1-27D0-E2C85EE9048B.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/518CE489-97B4-C05C-07A2-E3DBF14EE267.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/68A0C3E1-0FA1-8414-A361-33B08A8EDB39.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7497D76B-781B-3BDD-D797-FFBDB974F772.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/89A83E25-364B-6B73-0613-FEAD875EF9FB.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/A182A65A-47AE-5D00-9A30-BC20AB050BF2.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B346381F-48FE-E495-01A7-E22EC26AEE8A.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B3596116-540F-6397-ECE4-58A386644E15.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/BAD8EC05-6F14-4E38-366C-B4B660C6F38A.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/CCD38E11-8557-EB34-2651-07EB29E83FA6.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E2A47942-ED55-E81D-4C71-9A134C49C147.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E4FE88E9-EE21-B43B-B0FE-A153E38246F9.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E62AE7DF-49EE-9280-B328-A867CBD273AE.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E74406B5-20F1-4323-DC99-6E45982CB606.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EC4EB506-3DBE-7F36-6451-F31920EDAB52.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880.xml17
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/016BA1CF-6EA4-9CA4-CDF7-3AAA507EF6EF.xml40
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/32D718B4-250F-95DC-37F0-C0A817F69020.xml70
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/571DBBAF-CDDA-1C46-4220-D1319C0EEC00.xml24
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/65FA5BA0-CC9C-C108-BB1B-AC9E13F5BC83.xml127
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/AFCEF013-4CF2-4A5A-79A3-31521C1CA20A.xml306
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/F936BE6D-7A74-1B57-7564-41C1E13B973B.xml33
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap.xml3
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap_RMB082B14A-BEA8-D8A7-D661-197F34766ED3.xml3
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rdbms/TestManagerDatabase_RDBMSSites.xml2
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3.xml8
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3/subviews/6CEC5843-B4DD-D9B0-54D4-2845569D5E9F.xml13
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/types.xml933
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseComments.pgsql1193
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseDefaultUserAccounts.pgsql43
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks.pgsql90
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks2.pgsql77
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql1950
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseMap.pngbin0 -> 44750 bytes
-rw-r--r--src/VBox/ValidationKit/testmanager/db/TestManagerVBoxPilot-1.pgsql101
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/db/gen-sql-comments.py236
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/db/partial-db-dump.py392
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r01-builds-1.pgsql91
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r02-testboxes-1.pgsql194
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r03-teststatus-1.pgsql48
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r04-teststatus-2.pgsql46
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r05-teststatus-3.pgsql54
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r06-buildsources-1.pgsql46
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r07-testresults-1.pgsql47
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r08-testresultvalues-1.pgsql47
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r09-testsets-1.pgsql48
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r10-testresultvalues-2.pgsql111
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r11-testsets-2.pgsql214
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r12-testresultvalues-3-testsets-3.pgsql58
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r13-buildcategories-1-vcsrevisions-1.pgsql134
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r14-testboxes-2.pgsql201
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r15-index-sorting.pgsql108
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r16-testcaseargs-1-testresultfailures-1.pgsql122
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r17-testresultvalues-4.pgsql48
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r18-testresultfiles-1-testresultmsgs-1.pgsql171
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql356
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r20-testcases-1-testgroups-1-schedgroups-1.pgsql67
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r21-testsets-4.pgsql290
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r22-testboxes-3-teststatus-4-testboxinschedgroups-1.pgsql181
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r23-users-2.pgsql60
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r24-vcsbugreferences-1.pgsql59
-rw-r--r--src/VBox/ValidationKit/testmanager/db/tmdb-r25-vcsrevisions-2.pgsql45
-rw-r--r--src/VBox/ValidationKit/testmanager/debug/Makefile.kmk46
-rw-r--r--src/VBox/ValidationKit/testmanager/debug/__init__.py40
-rw-r--r--src/VBox/ValidationKit/testmanager/debug/add_testbox.pgsql76
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/debug/cgiprofiling.py83
-rw-r--r--src/VBox/ValidationKit/testmanager/debug/functions.pgsql82
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/Makefile.kup0
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/css/common.css1183
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/css/details.css216
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/css/graphwiz.css237
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css132
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox.svg806
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox_64px.pngbin0 -> 7884 bytes
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/images/tmfavicon.icobin0 -> 3262 bytes
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup0
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/js/common.js1926
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js126
-rw-r--r--src/VBox/ValidationKit/testmanager/htdocs/js/vcsrevisions.js237
-rw-r--r--src/VBox/ValidationKit/testmanager/misc/Makefile.kmk46
-rw-r--r--src/VBox/ValidationKit/testmanager/misc/htpasswd-logout1
-rw-r--r--src/VBox/ValidationKit/testmanager/misc/htpasswd-sample2
-rw-r--r--src/VBox/ValidationKit/testmanager/readme.txt125
-rw-r--r--src/VBox/ValidationKit/testmanager/selftest/st1-load.pgsql164
-rw-r--r--src/VBox/ValidationKit/testmanager/selftest/st1-unload.pgsql87
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/Makefile.kmk47
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/__init__.py40
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/template-details.html45
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/template-graphwiz.html46
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/template-tooltip.html20
-rw-r--r--src/VBox/ValidationKit/testmanager/webui/template.html65
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadmin.py1270
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminbuild.py154
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminbuildblacklist.py164
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminbuildcategory.py122
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminbuildsource.py160
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminfailurecategory.py155
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminfailurereason.py175
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminglobalrsrc.py130
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminschedgroup.py205
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminschedqueue.py79
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminsystemchangelog.py447
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminsystemdbdump.py72
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminsystemlog.py85
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py490
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadmintestcase.py258
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadmintestgroup.py197
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuiadminuseraccount.py110
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuibase.py1245
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuicontentbase.py1290
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuigraphwiz.py660
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpform.py1111
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpgraph.py128
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpgraphbase.py131
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpgraphgooglechart.py376
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpgraphmatplotlib.py341
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuihlpgraphsimple.py163
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuilogviewer.py251
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuimain.py1344
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuireport.py817
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuitestresult.py965
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuitestresultfailure.py110
-rwxr-xr-xsrc/VBox/ValidationKit/testmanager/webui/wuivcshistory.py101
-rw-r--r--src/VBox/ValidationKit/tests/Makefile.kmk60
-rw-r--r--src/VBox/ValidationKit/tests/__init__.py40
-rw-r--r--src/VBox/ValidationKit/tests/additions/Makefile.kmk54
-rwxr-xr-xsrc/VBox/ValidationKit/tests/additions/tdAddBasic1.py649
-rwxr-xr-xsrc/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py5503
-rwxr-xr-xsrc/VBox/ValidationKit/tests/additions/tdAddSharedFolders1.py361
-rw-r--r--src/VBox/ValidationKit/tests/api/Makefile.kmk72
-rw-r--r--src/VBox/ValidationKit/tests/api/__init__.py39
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdApi1.py99
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ovabin0 -> 9216 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ovabin0 -> 77312 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ovabin0 -> 77312 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ovabin0 -> 9728 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ovabin0 -> 9728 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ovabin0 -> 116224 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ovabin0 -> 116224 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem74
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ovabin0 -> 112640 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ovabin0 -> 112640 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ovabin0 -> 218624 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ovabin0 -> 218624 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem134
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ovabin0 -> 78336 bytes
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdAppliance1.py217
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdCloneMedium1.py243
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py203
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdMoveMedium1.py217
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdMoveVm1.py768
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdPython1.py220
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdTreeDepth1.py249
-rw-r--r--src/VBox/ValidationKit/tests/audio/Makefile.kmk50
-rwxr-xr-xsrc/VBox/ValidationKit/tests/audio/tdAudioTest.py823
-rwxr-xr-xsrc/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py240
-rw-r--r--src/VBox/ValidationKit/tests/autostart/Makefile.kmk51
-rwxr-xr-xsrc/VBox/ValidationKit/tests/autostart/tdAutostart1.py1443
-rw-r--r--src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk52
-rwxr-xr-xsrc/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py122
-rwxr-xr-xsrc/VBox/ValidationKit/tests/benchmarks/tdBenchmark2.py195
-rw-r--r--src/VBox/ValidationKit/tests/cpu/Makefile.kmk51
-rwxr-xr-xsrc/VBox/ValidationKit/tests/cpu/tdCpuPae1.py264
-rw-r--r--src/VBox/ValidationKit/tests/installation/Makefile.kmk52
-rwxr-xr-xsrc/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py258
-rwxr-xr-xsrc/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py409
-rwxr-xr-xsrc/VBox/ValidationKit/tests/installation/tdGuestOsUnattendedInst1.py769
-rw-r--r--src/VBox/ValidationKit/tests/network/Makefile.kmk51
-rwxr-xr-xsrc/VBox/ValidationKit/tests/network/tdNetBenchmark1.py633
-rw-r--r--src/VBox/ValidationKit/tests/selftests/Makefile.kmk54
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest1.py66
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest2.py131
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest3.py125
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest4.py125
-rw-r--r--src/VBox/ValidationKit/tests/serial/Makefile.kmk52
-rwxr-xr-xsrc/VBox/ValidationKit/tests/serial/loopback.py255
-rwxr-xr-xsrc/VBox/ValidationKit/tests/serial/tdSerial1.py345
-rwxr-xr-xsrc/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py367
-rw-r--r--src/VBox/ValidationKit/tests/smoketests/Makefile.kmk52
-rwxr-xr-xsrc/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py124
-rwxr-xr-xsrc/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py173
-rw-r--r--src/VBox/ValidationKit/tests/storage/Makefile.kmk56
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/remoteexecutor.py314
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/storagecfg.py681
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py1469
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageRawDrive1.py1692
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py414
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageStress1.py513
-rw-r--r--src/VBox/ValidationKit/tests/teleportation/Makefile.kmk51
-rwxr-xr-xsrc/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py963
-rw-r--r--src/VBox/ValidationKit/tests/unittests/Makefile.kmk51
-rwxr-xr-xsrc/VBox/ValidationKit/tests/unittests/tdUnitTest1.py1282
-rw-r--r--src/VBox/ValidationKit/tests/usb/Makefile.kmk53
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/tdUsb1.py590
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/tst-utsgadget.py154
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/usbgadget.py1478
-rw-r--r--src/VBox/ValidationKit/utils/Makefile.kmk78
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/Makefile.kmk87
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/TestExecService.cpp4037
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceInternal.h232
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceSerial.cpp417
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceTcp.cpp842
-rwxr-xr-xsrc/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-nat.sh174
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.desktop6
-rwxr-xr-xsrc/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.sh208
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.service18
-rwxr-xr-xsrc/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.sh173
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs-sol10.xml84
-rwxr-xr-xsrc/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.sh64
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.xml84
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-readme.txt145
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-runvm-readme.txt54
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.cmd41
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.reg13
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.cmd40
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.reg13
-rw-r--r--src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.xml75
-rw-r--r--src/VBox/ValidationKit/utils/audio/Makefile.kmk220
-rw-r--r--src/VBox/ValidationKit/utils/audio/ntPlayToneWaveX.cpp226
-rw-r--r--src/VBox/ValidationKit/utils/audio/readme.txt2
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkat.cpp1649
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkatCmdGeneric.cpp1169
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp481
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkatCommon.cpp1760
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp1621
-rw-r--r--src/VBox/ValidationKit/utils/audio/vkatInternal.h547
-rw-r--r--src/VBox/ValidationKit/utils/clipboard/ClipUtil.cpp1793
-rw-r--r--src/VBox/ValidationKit/utils/clipboard/Makefile.kmk53
-rw-r--r--src/VBox/ValidationKit/utils/cpu/Makefile.kmk84
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-app.cpp1376
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-appA.asm319
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-core.cpp2368
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp297
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet.h1092
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cidet.mac75
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp223
-rw-r--r--src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp205
-rw-r--r--src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm160
-rw-r--r--src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp272
-rw-r--r--src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm162
-rw-r--r--src/VBox/ValidationKit/utils/cpu/rdtsc.cpp294
-rw-r--r--src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm162
-rw-r--r--src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp130
-rw-r--r--src/VBox/ValidationKit/utils/dos/DosSleep.c67
-rw-r--r--src/VBox/ValidationKit/utils/dos/DosVmOff.asm79
-rw-r--r--src/VBox/ValidationKit/utils/dos/WinExit.asm95
-rw-r--r--src/VBox/ValidationKit/utils/fs/FsPerf.cpp6834
-rw-r--r--src/VBox/ValidationKit/utils/fs/Makefile.kmk49
-rw-r--r--src/VBox/ValidationKit/utils/misc/Makefile.kmk84
-rw-r--r--src/VBox/ValidationKit/utils/misc/loadgenerator.cpp366
-rw-r--r--src/VBox/ValidationKit/utils/misc/loadgeneratorR0.cpp101
-rw-r--r--src/VBox/ValidationKit/utils/misc/vts_rm.cpp54
-rw-r--r--src/VBox/ValidationKit/utils/misc/vts_tar.cpp54
-rw-r--r--src/VBox/ValidationKit/utils/network/Makefile.kmk49
-rw-r--r--src/VBox/ValidationKit/utils/network/NetPerf.cpp2008
-rw-r--r--src/VBox/ValidationKit/utils/nt/Makefile.kmk63
-rw-r--r--src/VBox/ValidationKit/utils/nt/ntFlushVirtualMemory.cpp498
-rw-r--r--src/VBox/ValidationKit/utils/nt/ntsetfreq.cpp161
-rw-r--r--src/VBox/ValidationKit/utils/nt/nttimesources.cpp246
-rw-r--r--src/VBox/ValidationKit/utils/serial/Makefile.kmk49
-rw-r--r--src/VBox/ValidationKit/utils/serial/SerialTest.cpp1115
-rw-r--r--src/VBox/ValidationKit/utils/storage/IoPerf.cpp1405
-rw-r--r--src/VBox/ValidationKit/utils/storage/Makefile.kmk49
-rw-r--r--src/VBox/ValidationKit/utils/usb/Makefile.kmk74
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTest.cpp667
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestService.cpp1658
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.cpp211
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.h546
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetCfg.cpp462
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetClassTest.cpp470
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHost.cpp179
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostInternal.h132
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostUsbIp.cpp263
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetInternal.h118
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceInternal.h226
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform-linux.cpp448
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform.h105
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.cpp120
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.h373
-rw-r--r--src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp512
-rw-r--r--src/VBox/ValidationKit/vms/t-dos20.txt27
-rw-r--r--src/VBox/ValidationKit/vms/t-dos401-emm386-win30me.txt9
-rw-r--r--src/VBox/ValidationKit/vms/t-dos401-win30me.txt88
-rw-r--r--src/VBox/ValidationKit/vms/t-dos50-emm386-win31.txt9
-rw-r--r--src/VBox/ValidationKit/vms/t-dos50-win31.txt78
-rw-r--r--src/VBox/ValidationKit/vms/t-dos622-emm386.txt8
-rw-r--r--src/VBox/ValidationKit/vms/t-dos622.txt50
-rw-r--r--src/VBox/ValidationKit/vms/t-dos71.txt34
-rw-r--r--src/VBox/ValidationKit/vms/t-nsthwvirt-ubuntu-64.txt26
-rw-r--r--src/VBox/ValidationKit/vms/t-nt310.txt27
-rw-r--r--src/VBox/ValidationKit/vms/t-nt350.txt23
-rw-r--r--src/VBox/ValidationKit/vms/t-nt4sp1.txt21
-rw-r--r--src/VBox/ValidationKit/vms/t-sol11u1.txt16
-rw-r--r--src/VBox/ValidationKit/vms/t-ubuntu-15_10-64-efi.txt12
-rw-r--r--src/VBox/ValidationKit/vms/t-ubuntu-20_04-64.txt36
-rw-r--r--src/VBox/ValidationKit/vms/t-win7-32-1.txt8
-rw-r--r--src/VBox/ValidationKit/vms/t-win7-32.txt14
-rw-r--r--src/VBox/ValidationKit/vms/t-xppro.txt44
882 files changed, 287079 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/.scm-settings b/src/VBox/ValidationKit/.scm-settings
new file mode 100644
index 00000000..d512ff9b
--- /dev/null
+++ b/src/VBox/ValidationKit/.scm-settings
@@ -0,0 +1,79 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the Validation Kit.
+#
+
+#
+# Copyright (C) 2017-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+# The validation kit is dual licensed.
+--license-ose-dual
+
+*.c16: --treat-as .c
+*.c32: --treat-as .c
+*.c64: --treat-as .c
+
+*.css: --treat-as .h --no-fix-header-guards
+*.js: --treat-as .h --no-fix-header-guards
+
+/testdriver/*.ps1: --treat-as Makefile
+/testmanager/apache-template-*.conf: --treat-as Makefile
+
+# Skip stuff without licenses and such.
+--filter-out-files *.txt
+--filter-out-files *.html
+--filter-out-files *.svg
+--filter-out-files /testmanager/misc/htpasswd-logout
+--filter-out-files /testmanager/misc/htpasswd-sample
+
+# Skip the XML for database diagrams.
+--filter-out-files /testmanager/db/TestManagerDatabase/*.xml
+--filter-out-files /testmanager/db/TestManagerDatabase.dmd
+
+# Skip ova test data
+--filter-out-files *.ova
+--filter-out-files *.pem
+
+# Skip some plain config files
+--filter-out-files /utils/TestExecServ/linux/vboxtxs.service
+--filter-out-files /utils/TestExecServ/win/*.reg
+--filter-out-files /utils/usb/linux/usbtest.service
+
+# misc
+/bootsectors/bs3kit/bs3kit-mangling-code.h: --no-fix-header-guards
+/bootsectors/bs3kit/bs3kit-mangling-code-define.h: --no-fix-header-guards
+/bootsectors/bs3kit/bs3kit-mangling-code-undef.h: --no-fix-header-guards
+/bootsectors/bs3kit/bs3kit-template-header.h: --no-fix-header-guards
+/bootsectors/bs3kit/bs3kit-template-footer.h: --no-fix-header-guards
+/bootsectors/bs3kit/bs3-cmn-instantiate-common.h: --no-fix-header-guards
+/bootsectors/bs3kit/*.h: --guard-relative-to-dir bootsectors/bs3kit/ --guard-prefix BS3KIT_INCLUDED_
+
diff --git a/src/VBox/ValidationKit/Config.kmk b/src/VBox/ValidationKit/Config.kmk
new file mode 100644
index 00000000..fe239f4d
--- /dev/null
+++ b/src/VBox/ValidationKit/Config.kmk
@@ -0,0 +1,282 @@
+# $Id: Config.kmk $
+## @file
+# kBuild Configuration file for the VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VALIDATIONKIT_CONFIG_KMK_INCLUDED = 1
+
+# Include the top-level configure file.
+ifndef VBOX_ROOT_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/Config.kmk
+endif
+
+#
+# Globals
+#
+VBOX_PATH_VALIDATIONKIT_SRC := $(PATH_ROOT)/src/VBox/ValidationKit
+
+
+#
+# Base template for Validation Kit R3 programs that drops the -static flag since we only want to use the
+# static version of our own libraries and not the system libs.
+#
+TEMPLATE_VBoxValidationKitR3Base = VBox Validation Kit ring-3 program base, both guest and host.
+TEMPLATE_VBoxValidationKitR3Base_EXTENDS = VBoxR3Static
+ifeq ($(KBUILD_TARGET),win)
+ TEMPLATE_VBoxValidationKitR3Base_LDFLAGS = $(filter-out -IntegrityCheck, $(TEMPLATE_VBoxR3Static_LDFLAGS))
+else ifn1of ($(KBUILD_TARGET), darwin solaris win)
+ TEMPLATE_VBoxValidationKitR3Base_CFLAGS = $(filter-out -static, $(TEMPLATE_VBoxR3Static_CFLAGS))
+ TEMPLATE_VBoxValidationKitR3Base_CXXFLAGS = $(filter-out -static, $(TEMPLATE_VBoxR3Static_CXXFLAGS))
+ TEMPLATE_VBoxValidationKitR3Base_OBJCFLAGS = $(filter-out -static, $(TEMPLATE_VBoxR3Static_OBJCFLAGS))
+ TEMPLATE_VBoxValidationKitR3Base_LDFLAGS = $(filter-out -static, $(TEMPLATE_VBoxR3Static_LDFLAGS))
+endif
+TEMPLATE_VBoxValidationKitR3Base_DEFS = $(filter-out VBOX_WITH_DTRACE,$(TEMPLATE_VBoxR3Static_DEFS))
+TEMPLATE_VBoxValidationKitR3Base_LIBS = $(TEMPLATE_VBoxR3Static_LIBS)
+if1of ($(KBUILD_TARGET), linux)
+ if $(VBOX_GCC_VERSION_CXX) < 40800
+ TEMPLATE_VBoxValidationKitR3Base_LIBS += supc++
+ TEMPLATE_VBoxValidationKitR3Base_LDTOOL = $(subst GXX,GCC,$(TEMPLATE_VBoxR3Static_TOOL))
+ endif
+endif
+TEMPLATE_VBoxValidationKitR3Base_LDFLAGS.darwin = $(TEMPLATE_VBoxR3Static_LDFLAGS.darwin) -framework IOKit
+
+
+#
+# Template for building ring-3 programs for the Validation Kit.
+# These programs can run on any host or guest.
+#
+TEMPLATE_VBoxValidationKitR3 = VBox Validation Kit ring-3 program, both guest and host.
+TEMPLATE_VBoxValidationKitR3_EXTENDS = VBoxValidationKitR3Base
+TEMPLATE_VBoxValidationKitR3_EXTENDS_BY = appending
+TEMPLATE_VBoxValidationKitR3_INST = $(INST_VALIDATIONKIT)$(KBUILD_TARGET)/$(KBUILD_TARGET_ARCH)/
+TEMPLATE_VBoxValidationKitR3_SDKS.win = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxNtDll
+TEMPLATE_VBoxValidationKitR3_DEFS = IN_RT_R3
+TEMPLATE_VBoxValidationKitR3_LIBS.darwin = iconv
+TEMPLATE_VBoxValidationKitR3_LIBS.freebsd = iconv
+TEMPLATE_VBoxValidationKitR3_LIBS = \
+ $(PATH_STAGE_LIB)/RuntimeR3$(VBOX_SUFF_LIB)
+ifeq ($(KBUILD_TARGET),solaris)
+ TEMPLATE_VBoxValidationKitR3_LIBS += \
+ kstat \
+ nsl \
+ contract
+ if1of ($(KBUILD_TARGET_ARCH), amd64 x86)
+ TEMPLATE_VBoxValidationKitR3_LIBS += \
+ smbios
+ endif
+endif
+ifneq ($(KBUILD_TARGET),win)
+ TEMPLATE_VBoxValidationKitR3_LIBS += \
+ $(SDK_VBoxZlib_LIBS)
+endif
+
+# Make VCC100 output work on NT3.x, NT4, W2K, XP and W2K3.
+ifndef VBOX_WITH_NOCRT_STATIC
+ TEMPLATE_VBoxValidationKitR3_LIBS.win.x86 = \
+ $(PATH_TOOL_$(TEMPLATE_VBoxValidationKitR3_TOOL.win.x86)_LIB)/oldnames.lib \
+ $(PATH_TOOL_$(TEMPLATE_VBoxValidationKitR3_TOOL.win.x86)_LIB)/libcmt$(VBOX_VCC_CRT_TYPE).lib \
+ $(PATH_TOOL_$(TEMPLATE_VBoxValidationKitR3_TOOL.win.x86)_LIB)/libcpmt$(VBOX_VCC_CRT_TYPE).lib \
+ $(PATH_STAGE_LIB)/RuntimeR3VccTricks$(VBOX_SUFF_LIB)
+ TEMPLATE_VBoxValidationKitR3_LDFLAGS.win.x86 = \
+ -Include:_vcc100_shell32_fakes_cpp \
+ -Include:_vcc100_shell32_fakes_asm \
+ -Section:.bss,RW!K
+endif
+TEMPLATE_VBoxValidationKitR3_LDFLAGS.win.x86 += -NoOptIData
+TEMPLATE_VBoxValidationKitR3_POST_CMDS.win.x86 = $(if $(eq $(tool_do),LINK_LIBRARY),,$(VBOX_PE_SET_VERSION) $(out)$$(NLTAB))$(TEMPLATE_VBoxValidationKitR3Base_POST_CMDS.win.x86)$$(NLTAB)
+TEMPLATE_VBoxValidationKitR3_POST_CMDS.win.amd64 = $(if $(eq $(tool_do),LINK_LIBRARY),,$(VBOX_PE_SET_VERSION) $(out)$$(NLTAB))$(TEMPLATE_VBoxValidationKitR3Base_POST_CMDS.win.amd64)$$(NLTAB)
+TEMPLATE_VBoxValidationKitR3_LNK_DEPS.win.x86 = $(if $(eq $(tool_do),LINK_LIBRARY),,$(VBOX_PE_SET_VERSION)) $(TEMPLATE_VBoxValidationKitR3Base_LNK_DEPS.win.x86)
+TEMPLATE_VBoxValidationKitR3_LNK_DEPS.win.amd64 = $(if $(eq $(tool_do),LINK_LIBRARY),,$(VBOX_PE_SET_VERSION)) $(TEMPLATE_VBoxValidationKitR3Base_LNK_DEPS.win.amd64)
+
+#TODO: TEMPLATE_VBoxValidationKitR3_EXTENDS = VBoxGuestR3Exe
+
+TEMPLATE_VBoxValidationKitR3_USES.win += vboximportchecker
+TEMPLATE_VBoxValidationKitR3_VBOX_IMPORT_CHECKER.win.x86 = nt31
+TEMPLATE_VBoxValidationKitR3_VBOX_IMPORT_CHECKER.win.amd64 = xp64
+
+
+#
+# Template for ring-3 testcases to be included on the Validation Kit .ISO.
+#
+# Needed for running the ring-3 testcases on older guests (like NT4 / XP).
+# Those testcases then run as part of the Validation Kit and are included on the Validation Kit .ISO.
+# See @bugref:10195.
+#
+TEMPLATE_VBoxValidationKitR3TstExe = VBox Ring 3 Testcase Exe for Validation Kit .ISO
+TEMPLATE_VBoxValidationKitR3TstExe_EXTENDS = VBoxValidationKitR3
+TEMPLATE_VBoxValidationKitR3TstExe_INST = $(INST_VALIDATIONKIT)/testcase/$(KBUILD_TARGET)/$(KBUILD_TARGET_ARCH)/testcase/
+
+
+#
+# Template for building ring-3 programs for the Validation Kit.
+# When these programs run on the host they may take advantage of the
+# support driver if installed.
+#
+TEMPLATE_VBoxValidationKitR3SupDrv = VBox Validation Kit ring-3 program, mainly host.
+TEMPLATE_VBoxValidationKitR3SupDrv_EXTENDS = VBoxValidationKitR3
+TEMPLATE_VBoxValidationKitR3SupDrv_EXTENDS_BY = appending
+TEMPLATE_VBoxValidationKitR3SupDrv_DEFS = IN_SUP_R3
+TEMPLATE_VBoxValidationKitR3SupDrv_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR3Static$(VBOX_SUFF_LIB) \
+ $(PATH_STAGE_LIB)/RuntimeR3$(VBOX_SUFF_LIB)
+ifndef VBOX_WITH_NOCRT_STATIC
+ TEMPLATE_VBoxValidationKitR3SupDrv_LDFLAGS.win.x86 = \
+ -Include:_vcc100_ntdll_fakes_cpp \
+ -Include:_vcc100_ntdll_fakes_asm
+endif
+
+#
+# Template for building agnostic ring-0 host modules for the Validation Kit.
+#
+TEMPLATE_VBoxValidationKitR0 = VBox Validation Kit agnostic ring-0 host module.
+TEMPLATE_VBoxValidationKitR0_EXTENDS = VBoxR0
+TEMPLATE_VBoxValidationKitR0_EXTENDS_BY = appending
+TEMPLATE_VBoxValidationKitR0_INST = $(INST_VALIDATIONKIT)$(KBUILD_TARGET)/$(KBUILD_TARGET_ARCH)/
+TEMPLATE_VBoxValidationKitR0_DEFS = IN_RT_R0
+TEMPLATE_VBoxValidationKitR0_LIBS = \
+ $(PATH_STAGE_LIB)/RuntimeR0$(VBOX_SUFF_LIB) \
+ $(VBOX_LIB_SUPR0)
+
+#
+# List of python sources that should be linted and unittested.
+#
+VBOX_VALIDATIONKIT_PYTHON_SOURCES :=
+VBOX_VALIDATIONKIT_PYLINT_TARGETS :=
+VBOX_VALIDATIONKIT_PYUNITTEST_EXCLUDE :=
+
+ifdef VBOX_WITH_PYLINT
+ TESTING +=
+endif
+
+#
+# Process python sources.
+#
+if1of ($(KBUILD_TARGET), win os2)
+ VBOX_PYTHONPATH_VALIDATIONKIT = $(PYTHONPATH);$(VBOX_PATH_VALIDATIONKIT_SRC);$(VBOX_PATH_VALIDATIONKIT_SRC)/testboxscript;$(VBOX_PATH_VALIDATIONKIT_SRC)/testmanager;$(VBOX_PATH_VALIDATIONKIT_SRC)/tests/additions;$(VBOX_PATH_VALIDATIONKIT_SRC)/../VMM/VMMAll
+else
+ VBOX_PYTHONPATH_VALIDATIONKIT = $(PYTHONPATH):$(VBOX_PATH_VALIDATIONKIT_SRC):$(VBOX_PATH_VALIDATIONKIT_SRC)/testboxscript:$(VBOX_PATH_VALIDATIONKIT_SRC)/testmanager:$(VBOX_PATH_VALIDATIONKIT_SRC)/tests/additions:$(VBOX_PATH_VALIDATIONKIT_SRC)/../VMM/VMMAll
+endif
+BLDDIRS += $(PATH_TARGET)/pylint $(PATH_TARGET)/pyunittest
+
+define def_vbox_validationkit_py_check
+ $(eval name:=$(basename $(notdir $(py))))
+
+ pylint: $(name)-py-phony.o
+ $(name).o: $(name)-py-phony.o
+ $(PATH_TARGET)/pylint/$(name).o $(name)-py-phony.o:: $(py) | $(PATH_TARGET)/pylint/
+ ifdef VBOX_WITH_PYLINT
+ $(QUIET2)$(call MSG_L1,Subjecting $(py) to pylint...)
+ $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_VALIDATIONKIT)" -- \
+ $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_VALIDATIONKIT_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
+ endif
+ $(QUIET)$(APPEND) -t "$(PATH_TARGET)/pylint/$(name).o"
+
+ ifn1of ($(py), $(VBOX_VALIDATIONKIT_PYUNITTEST_EXCLUDE))
+ pyunittest: $(name)-pyunittest.o
+ $(PATH_TARGET)/pyunittest/$(name).o $(name)-pyunittest.o:: $(py) | $(PATH_TARGET)/pyunittest/
+ $(QUIET2)$(call MSG_L1,Unittesting Python source $(py)...)
+ $(QUIET)$(REDIRECT) -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_VALIDATIONKIT)" -C $(dir $(py)) \
+ -- $(VBOX_UNITTEST_PYTHON) -m unittest -v $(notdir $(basename $(py)))
+ $(QUIET)$(APPEND) -t "$(PATH_TARGET)/pyunittest/$(name).o"
+ VBOX_VALIDATIONKIT_PYUNITTEST_TARGETS += $(PATH_TARGET)/pyunittest/$(name).o
+
+ TESTING += $(name)-pyunittest.o
+ endif
+ TESTING += $(name)-py-phony.o
+ VBOX_VALIDATIONKIT_PYLINT_TARGETS += $(PATH_TARGET)/pylint/$(name).o
+endef # def_vbox_validationkit_py_check
+
+
+define def_vbox_validationkit_process_python_sources
+ if $(words $(_SUB_MAKEFILE_STACK)) <= 0 || "$1" == "FORCE"
+ $(foreach py, $(VBOX_VALIDATIONKIT_PYTHON_SOURCES), $(eval $(def_vbox_validationkit_py_check)))
+ endif
+endef
+
+
+
+#
+# http://www.jshint.com
+#
+VBOX_JSHINT ?= jshint
+VBOX_JSHINT_FLAGS := --config $(VBOX_PATH_VALIDATIONKIT_SRC)/jshintrc.js --verbose
+ifndef VBOX_WITH_JSHINT
+ VBOX_WITH_JSHINT := $(which $(VBOX_JSHINT))
+endif
+
+#
+# List of javascript sources that should be checked and linted.
+#
+VBOX_VALIDATIONKIT_JS_SOURCES :=
+
+define def_vbox_validationkit_js_check
+ $(eval name:=$(basename $(notdir $(js))))
+ $(name).o $(name).obj: # $(PATH_SUB_CURRENT)/$(js)
+ -$(REDIRECT) -E LC_ALL=C -C $(dir $(js)) -- $$(VBOX_JSHINT) ./$(notdir $(js)) $$(VBOX_JSHINT_FLAGS)
+ jslint: $(name).o
+endef
+
+ifdef VBOX_WITH_JSHINT
+ define def_vbox_validationkit_process_js_sources
+ if $(words $(_SUB_MAKEFILE_STACK)) <= 0
+ $(foreach js, $(VBOX_VALIDATIONKIT_JS_SOURCES), $(eval $(def_vbox_validationkit_js_check)))
+ endif
+ endef
+endif
+
+
+#
+# List of IPRT testcases that will be included in the ValKit.
+#
+ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+ VALKIT_UNITTESTS_WHITELIST_IPRT := \
+ tstFile \
+ tstFileLock \
+ tstRTPathQueryInfo \
+ tstRTPipe \
+ tstRTProcCreateEx \
+ tstRTProcCreatePrf \
+ tstRTProcQueryUsername \
+ tstThread-1 \
+ tstUtf8
+
+ VALKIT_UNITTESTS_WHITELIST_IPRT.linux += \
+ tstRTProcWait \
+ tstRTProcIsRunningByName
+
+ VALKIT_UNITTESTS_WHITELIST_IPRT.win += \
+ tstRTProcWait
+
+endif # VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+
diff --git a/src/VBox/ValidationKit/Makefile.kmk b/src/VBox/ValidationKit/Makefile.kmk
new file mode 100644
index 00000000..136a1987
--- /dev/null
+++ b/src/VBox/ValidationKit/Makefile.kmk
@@ -0,0 +1,400 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Make sure our Config.kmk is included.
+ifndef VBOX_VALIDATIONKIT_CONFIG_KMK_INCLUDED
+ include $(PATH_SUB_CURRENT)/Config.kmk
+endif
+
+# Collect install targets
+VBOX_VALIDATIONKIT_INSTALLS := $(INSTALLS)
+
+#
+# Include sub-makefiles.
+#
+include $(PATH_SUB_CURRENT)/tests/Makefile.kmk
+ifneq ($(KBUILD_HOST),os2) # needs yasm
+ if1of ($(KBUILD_TARGET_ARCH), amd64 x86) # needs yasm
+ include $(PATH_SUB_CURRENT)/bootsectors/Makefile.kmk
+ endif
+endif
+include $(PATH_SUB_CURRENT)/utils/Makefile.kmk
+include $(PATH_SUB_CURRENT)/common/Makefile.kmk
+include $(PATH_SUB_CURRENT)/testboxscript/Makefile.kmk
+include $(PATH_SUB_CURRENT)/testdriver/Makefile.kmk
+include $(PATH_SUB_CURRENT)/testmanager/Makefile.kmk
+
+#
+# Globals.
+#
+
+# The current target is enabled by default.
+VBOX_WITH_VALIDATIONKIT_PACKING.$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH) = 1
+ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+ VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING.$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH) = 1
+endif
+
+#
+# Install the test driver framework.
+#
+INSTALLS += ValidationKit-testdriver
+ValidationKit-testdriver_TEMPLATE = VBoxValidationKitR3
+ValidationKit-testdriver_INST = $(INST_VALIDATIONKIT)testdriver/
+ValidationKit-testdriver_MODE = a+r,u+w
+ValidationKit-testdriver_SOURCES := \
+ $(PATH_SUB_CURRENT)/testdriver/__init__.py \
+ $(PATH_SUB_CURRENT)/testdriver/base.py \
+ $(PATH_SUB_CURRENT)/testdriver/btresolver.py \
+ $(PATH_SUB_CURRENT)/testdriver/reporter.py \
+ $(PATH_SUB_CURRENT)/testdriver/testfileset.py \
+ $(PATH_SUB_CURRENT)/testdriver/tst-txsclient.py \
+ $(PATH_SUB_CURRENT)/testdriver/txsclient.py \
+ $(PATH_SUB_CURRENT)/testdriver/vbox.py \
+ $(PATH_SUB_CURRENT)/testdriver/vboxcon.py \
+ $(PATH_SUB_CURRENT)/testdriver/vboxtestfileset.py \
+ $(PATH_SUB_CURRENT)/testdriver/vboxtestvms.py \
+ $(PATH_SUB_CURRENT)/testdriver/vboxwrappers.py \
+ $(PATH_SUB_CURRENT)/testdriver/winbase.py \
+ $(PATH_SUB_CURRENT)/testdriver/win-vbox-net-uninstall.ps1
+ValidationKit-testdriver_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/testdriver/vboxinstaller.py
+$(PATH_SUB_CURRENT)/testdriver/txsclient.py_VBOX_PYCHECKER_FLAGS = --no-reimport
+
+
+#
+# Packing target.
+#
+ifndef VBOX_WITHOUT_VALIDATIONKIT_ZIP
+
+ VBOX_VALIDATIONKIT_INSTALLS := $(filter-out $(VBOX_VALIDATIONKIT_INSTALLS), $(INSTALLS))
+
+ #
+ # VBoxValidationKit.zip.
+ # TODO: Don't pack the python stuff multiple times? Lazy works for now.
+ #
+ PACKING += $(PATH_OUT)/VBoxValidationKit.zip
+ $(PATH_OUT)/VBoxValidationKit.zip: \
+ $(VBOX_PATH_VALIDATIONKIT)/VBoxValidationKit.iso \
+ $(VBOX_PATH_VALIDATIONKIT)/ \
+ $$(foreach inst, $$(VBOX_VALIDATIONKIT_INSTALLS), \
+ $$(filter $(PATH_STAGE)/$(INST_VALIDATIONKIT)%, $$($$(inst)_2_STAGE_TARGETS)))
+ $(call MSG_L1,Packing the Test Suite $@)
+ $(QUIET)$(RM) -f $@
+ # Note: Exclude packing the testcase directory into .zip, as that directory only needs to be
+ # included in VBoxValidationKit.iso.
+ $(foreach os, darwin freebsd linux os2 solaris win,$(foreach arch, x86 amd64 \
+ ,$(if-expr defined(VBOX_WITH_VALIDATIONKIT_PACKING.$(os).$(arch))\
+ ,$(NLTAB)$(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE)/$(os).$(arch)/$(KBUILD_TYPE)/ \
+ -- $(VBOX_ZIP) -r9 $@ $(INST_VALIDATIONKIT) -x '$(INST_VALIDATIONKIT)testcase/*' -x '*.pyc',)))
+
+ #
+ # VBoxTestBoxScript.zip - For setting up the testboxes.
+ #
+ PACKING += $(PATH_OUT)/VBoxTestBoxScript.zip
+ $(PATH_OUT)/VBoxTestBoxScript.zip: \
+ $$(testboxscript_2_STAGE_TARGETS) \
+ $$(testboxscript-common_2_STAGE_TARGETS) \
+ $$(TestBoxHelper_2_STAGE_TARGETS)
+ $(call MSG_L1,Packing the TestBox Script files to $@)
+ $(QUIET)$(RM) -f $@
+ $(foreach os, darwin freebsd linux os2 solaris win,$(foreach arch, x86 amd64 \
+ ,$(if-expr defined(VBOX_WITH_VALIDATIONKIT_PACKING.$(os).$(arch))\
+ ,$(NLTAB)$(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE)/$(os).$(arch)/$(KBUILD_TYPE)/ \
+ -- $(VBOX_ZIP) -r9X $@ $(INST_TESTBOXSCRIPT) -x '*.pyc',)))
+
+endif # !VBOX_WITHOUT_VALIDATIONKIT_ZIP
+
+
+#
+# Automatically lint python code and python unit tests during build.
+#
+if defined(VBOX_WITH_PYLINT) && !defined(VBOX_WITHOUT_AUTO_PYLINT)
+ OTHERS += $(PATH_TARGET)/pylint.run
+ OTHER_CLEAN += $(PATH_TARGET)/pylint.run
+ $(PATH_TARGET)/pylint.run: $$(filter-out %/testboxscript.o, $$(VBOX_VALIDATIONKIT_PYLINT_TARGETS))
+ $(QUIET)$(APPEND) -t "$@"
+endif
+if defined(VBOX_WITH_PYLINT) && !defined(VBOX_WITHOUT_AUTO_PYUNITTEST) # Tied to pylint for hysterical raisins.
+ OTHERS += $(PATH_TARGET)/pyunittest.run
+ OTHER_CLEAN += $(PATH_TARGET)/pyunittest.run
+ $(PATH_TARGET)/pyunittest.run: $$(VBOX_VALIDATIONKIT_PYUNITTEST_TARGETS)
+ $(QUIET)$(APPEND) -t "$@"
+endif
+
+$(evalcall def_vbox_validationkit_process_python_sources,FORCE)
+$(evalcall def_vbox_validationkit_process_js_sources,FORCE)
+include $(FILE_KBUILD_SUB_FOOTER)
+
+
+VBOX_VALIDATIONKIT_ISO_RSP = $(PATH_TARGET)/VBoxValidationKitISO.rsp
+OTHERS_CLEAN += $(VBOX_VALIDATIONKIT_ISO_RSP)
+
+#
+# Construct the file spec for creating the Validation Kit guest iso.
+#
+VBOX_VALIDATIONKIT_FILESPEC = \
+ valkit.txt=$(VBOX_PATH_VALIDATIONKIT_SRC)/docs/valkit.txt \
+ $(VBOX_PATH_VALIDATIONKIT)/vboxtxs-readme.txt
+ifneq ($(KBUILD_HOST),os2)
+ if1of ($(KBUILD_TARGET_ARCH), amd64 x86)
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT)/bootsectors/bootsector-pae.img \
+ $(VBOX_PATH_VALIDATIONKIT)/bootsectors/bootsector-shutdown.img
+ endif
+endif
+
+VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS =
+
+# Generate VBOX_PATH_VALIDATIONKIT.os.arch variables.
+$(foreach os, darwin freebsd linux os2 solaris win,$(foreach arch, x86 amd64, \
+ $(eval VBOX_PATH_VALIDATIONKIT.$(os).$(arch) = $(PATH_OUT_BASE)/$(os).$(arch)/$(KBUILD_TYPE)/$(INST_VALIDATIONKIT)$(os)/$(arch)) \
+ $(eval VBOX_PATH_VALIDATIONKIT_UNITTESTS.$(os).$(arch) = $(abspath $(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/../../testcase/$(os)/$(arch)/testcase)) \
+))
+
+# Common files first.
+define def_vbox_validationkit_common_files
+ ifdef VBOX_WITH_VALIDATIONKIT_PACKING.$(os).$(arch)
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/exceptionsR3$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/cpu-alloc-all-mem$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/cpu-numa$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/FsPerf$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/IoPerf$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/NetPerf$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/LoadGenerator$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/SerialTest$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/TestExecService$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/vts_rm$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/vts_shutdown$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/vts_tar$(TMP_SUFF_EXE) \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/xmmsaving$(TMP_SUFF_EXE)
+ if1of ($(arch), amd64) ## HACK ALERT! This mirrors VBOX_WITH_R0_MODULES logic in /Config.kmk.
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/loadgeneratorR0.r0
+ endif
+ ifn1of ($(os), os2) ## not compiling bootsectors, no yasm. could fix this better.
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/bs3-timing-1$(TMP_SUFF_EXE)
+ endif
+ ifn1of ($(os), os2 freebsd netbsd openbsd) ## must match utils/audio/Makefile.kmk
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $$(VBOX_PATH_VALIDATIONKIT.$(os).$(arch))/vkat$(TMP_SUFF_EXE)
+ endif
+ endif
+endef
+
+TMP_SUFF_EXE = .exe
+$(foreach os, os2 win,$(foreach arch, x86 amd64, \
+ $(eval $(def_vbox_validationkit_common_files)) \
+))
+TMP_SUFF_EXE =
+$(foreach os, darwin freebsd linux solaris,$(foreach arch, x86 amd64, \
+ $(eval $(def_vbox_validationkit_common_files)) \
+))
+
+# OS specific files - Linux
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.linux.x86
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT.linux.x86)/UsbTest
+endif
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.linux.amd64
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT.linux.amd64)/UsbTest
+endif
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.linux.x86
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.x86)/../vboxtxs)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.x86)/../vboxtxs-nat)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.x86)/../vboxtxs.service)
+
+else ifdef VBOX_WITH_VALIDATIONKIT_PACKING.linux.amd64
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.amd64)/../vboxtxs)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.amd64)/../vboxtxs-nat)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.linux.amd64)/../vboxtxs.service)
+endif
+
+# OS specific files - OS/2
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.os2.x86
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc06.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc061.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc062.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc063.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc064.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc065.dll \
+ $(VBOX_PATH_VALIDATIONKIT.os2.x86)/libc066.dll
+endif
+
+# OS specific files - Solaris
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.solaris.x86
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.x86)/../vboxtxs.sh)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.x86)/../vboxtxs.xml)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.x86)/../vboxtxs-sol10.xml)
+else ifdef VBOX_WITH_VALIDATIONKIT_PACKING.solaris.amd64
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.amd64)/../vboxtxs.sh)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.amd64)/../vboxtxs.xml)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.solaris.amd64)/../vboxtxs-sol10.xml)
+endif
+
+# OS specific files - Windows
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.win.x86
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT.win.x86)/ntSetFreq.exe \
+ $(VBOX_PATH_VALIDATIONKIT.win.x86)/ntTimeSources.exe
+ # Disabled for now; does not work without WinMM.dll export verification files.
+ #ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
+ # VBOX_VALIDATIONKIT_FILESPEC += \
+ # $(VBOX_PATH_VALIDATIONKIT.win.x86)/ntPlayToneWaveX.exe
+ #endif
+endif
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.win.amd64
+ VBOX_VALIDATIONKIT_FILESPEC += \
+ $(VBOX_PATH_VALIDATIONKIT.win.amd64)/ntSetFreq.exe \
+ $(VBOX_PATH_VALIDATIONKIT.win.amd64)/ntTimeSources.exe
+ # Disabled for now; does not work without WinMM.dll export verification files.
+ #ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
+ # VBOX_VALIDATIONKIT_FILESPEC += \
+ # $(VBOX_PATH_VALIDATIONKIT.win.amd64)/ntPlayToneWaveX.exe
+ #endif
+endif
+ifdef VBOX_WITH_VALIDATIONKIT_PACKING.win.x86
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.x86)/../vboxtxs.cmd)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.x86)/../vboxtxs.reg)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.x86)/../vboxtxs-nat.cmd)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.x86)/../vboxtxs-nat.reg)
+else ifdef VBOX_WITH_VALIDATIONKIT_PACKING.win.amd64
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.amd64)/../vboxtxs.cmd)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.amd64)/../vboxtxs.reg)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.amd64)/../vboxtxs-nat.cmd)
+ VBOX_VALIDATIONKIT_FILESPEC += $(abspath $(VBOX_PATH_VALIDATIONKIT.win.amd64)/../vboxtxs-nat.reg)
+endif
+
+#
+# If enabled, this includes specially built unit tests (statically linked, for guests) located
+# in the $(VBOX_PATH_VALIDATIONKIT)/../../testcase/ directory.
+#
+# This is useful if we want to run those on platforms where we don't have / support
+# any host support anymore (like Windows XP, for instance).
+#
+# Note that executing the tests require an additional component (tdUnitTest test driver)
+# which runs as part of the Validation Kit.
+#
+# See @bugref{10195}
+#
+ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+ define def_vbox_validationkit_unittests
+ # IPRT unit tests.
+ VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS += \
+ $(foreach whitelisted, $(VALKIT_UNITTESTS_WHITELIST_IPRT) $(VALKIT_UNITTESTS_WHITELIST_IPRT.$(os)), \
+ $$(wildcard $(VBOX_PATH_VALIDATIONKIT_UNITTESTS.$(os).$(arch))/$(whitelisted)$(TMP_SUFF_EXE)))
+ # Unit tests which utilize parts of the Guest Additions.
+ VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS += \
+ $(foreach whitelisted, $(VALKIT_UNITTESTS_WHITELIST_GUEST_ADDITIONS) $(VALKIT_UNITTESTS_WHITELIST_GUEST_ADDITIONS.$(os)), \
+ $$(wildcard $(VBOX_PATH_VALIDATIONKIT_UNITTESTS.$(os).$(arch))/$(whitelisted)$(TMP_SUFF_EXE)))
+ endef
+
+ TMP_SUFF_EXE = .exe
+ $(foreach os, win,$(foreach arch, x86 amd64, \
+ $(eval $(def_vbox_validationkit_unittests)) \
+ ))
+ TMP_SUFF_EXE =
+ $(foreach os, darwin freebsd linux solaris,$(foreach arch, x86 amd64, \
+ $(eval $(def_vbox_validationkit_unittests)) \
+ ))
+
+endif # VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+
+#
+# Build the Validation Kit guest ISO response file for RTIsoMaker.
+#
+# We need this response file because passing all those arguments to RTIsoMaker
+# will blow up the maximum command line length on some OSes.
+#
+$(VBOX_VALIDATIONKIT_ISO_RSP): | $$(dir $$@)
+ $(call MSG_L1,Creating Validation Kit guest ISO response file $@ )
+ kmk_builtin_append -nt "$@" \
+ '--iso-level=3' \
+ '--rock-ridge' \
+ '--joliet' \
+ '--rational-attribs' \
+ '--random-order-verification=2048' \
+ $(foreach spec,$(VBOX_VALIDATIONKIT_FILESPEC) \
+ ,$(if $(findstring =,$(spec)), $(spec), /$(lastword $(subst /$(INST_VALIDATIONKIT), ,$(spec))=$(spec))) ) \
+ $(foreach spec,$(VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS) \
+ ,$(if $(findstring =,$(spec)), $(spec), /$(lastword $(subst /$(INST_VALIDATIONKIT), ,$(spec))=$(spec))) ) \
+ $(foreach spec,$(filter-out %.txt %.dll %.xml %.reg %.img, $(VBOX_VALIDATIONKIT_FILESPEC)) \
+ ,--chmod=a+x:/$(lastword $(if $(findstring =,$(spec)), \
+ $(subst =, $(SP), $(spec)), \
+ $(subst /$(INST_VALIDATIONKIT), ,$(spec))))) \
+ $(foreach spec,$(filter-out %.txt %.dll %.xml %.reg %.img, $(VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS)) \
+ ,--chmod=a+x:/$(lastword $(if $(findstring =,$(spec)), \
+ $(subst =, $(SP), $(spec)), \
+ $(subst $(INST_VALIDATIONKIT), ,$(spec))))) \
+ '--volume-id="VBOXVALK_$(VBOX_SVN_REV)_$(VBOX_VERSION_STRING_RAW)"' \
+ '--name-setup=joliet' \
+ '--volume-id="VBoxValK $(VBOX_SVN_REV)"'
+
+#
+# Build the Validation Kit guest ISO.
+#
+$(VBOX_PATH_VALIDATIONKIT)/VBoxValidationKit.iso: \
+ $(filter-out %=deleteme=, $(subst =,=deleteme= , $(VBOX_VALIDATIONKIT_FILESPEC))) \
+ $(VBOX_SVN_REV_KMK) \
+ $(VBOX_PATH_VALIDATIONKIT_SRC)/Makefile.kmk \
+ $(VBOX_VALIDATIONKIT_ISO_RSP) \
+ | $(if-expr defined(VBOX_USE_RTISOMAKER),$(VBOX_RTISOMAKER),)
+ $(call MSG_TOOL,RTIsoMaker,,$@)
+ $(QUIET)$(MKDIR) -p $(@D)
+ifneq ($(KBUILD_TARGET),os2)
+ $(QUIET)$(ECHO) VBOX_VALIDATIONKIT_FILESPEC $(VBOX_VALIDATIONKIT_FILESPEC)
+endif
+ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+ $(foreach os, darwin linux solaris win,$(foreach arch, x86 amd64, \
+ $(call MSG_L1, VBOX_PATH_VALIDATIONKIT_UNITTESTS.$(os).$(arch): $(VBOX_PATH_VALIDATIONKIT_UNITTESTS.$(os).$(arch))) \
+ ))
+ $(call MSG_L1, VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS $(VBOX_VALIDATIONKIT_FILESPEC_UNITTESTS))
+endif
+ $(VBOX_RTISOMAKER) \
+ @$(VBOX_VALIDATIONKIT_ISO_RSP) \
+ --output $@
+
+
+
+# Alias for creating the iso.
+.PHONY: validationkit-iso
+validationkit-iso: $(VBOX_PATH_VALIDATIONKIT)/VBoxValidationKit.iso
diff --git a/src/VBox/ValidationKit/ValidationKitCodingGuidelines.cpp b/src/VBox/ValidationKit/ValidationKitCodingGuidelines.cpp
new file mode 100644
index 00000000..c2f3cb57
--- /dev/null
+++ b/src/VBox/ValidationKit/ValidationKitCodingGuidelines.cpp
@@ -0,0 +1,87 @@
+/* $Id: ValidationKitCodingGuidelines.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - Coding Guidelines.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/** @page pg_validationkit_guideline Validation Kit Coding Guidelines
+ *
+ * The guidelines extends the VBox coding guidelines (@ref pg_vbox_guideline)
+ * and currently only defines python prefixes and linting.
+ *
+ *
+ * @section sec_validationkit_guideline_python Python
+ *
+ * Python is a typeless language so using prefixes to indicate the intended
+ * type of a variable or attribute can be very helpful.
+ *
+ * Type prefixes:
+ * - 'b' for byte (octect).
+ * - 'ch' for a single character.
+ * - 'f' for boolean and flags.
+ * - 'fn' for function or method references.
+ * - 'fp' for floating point values.
+ * - 'i' for integers.
+ * - 'l' for long integers.
+ * - 'o' for objects, structures and anything with attributes that doesn't
+ * match any of the other type prefixes.
+ * - 'r' for a range or xrange.
+ * - 's' for a string (can be unicode).
+ * - 'su' for a unicode string when the distinction is important.
+ *
+ * Collection qualifiers:
+ * - 'a' for a list or an array.
+ * - 'd' for a dictionary.
+ * - 'h' for a set (hashed).
+ * - 't' for a tuple.
+ *
+ * Other qualifiers:
+ * - 'c' for a count. Implies integer or long integer type. Higest
+ * priority.
+ * - 'sec' for a second value. Implies long integer type.
+ * - 'ms' for a millisecond value. Implies long integer type.
+ * - 'us' for a microsecond value. Implies long integer type.
+ * - 'ns' for a nanosecond value. Implies long integer type.
+ *
+ * The 'ms', 'us', 'ns' and 'se' qualifiers can be capitalized when prefixed by
+ * 'c', e.g. cMsElapsed. While this technically means they are no longer a
+ * prefix, it's easier to read and everyone understands what it means.
+ *
+ * The type collection qualifiers comes first, then the other qualifiers and
+ * finally the type qualifier.
+ *
+ * Python statements are terminated by semicolons (';') as a convention.
+ *
+ */
+
diff --git a/src/VBox/ValidationKit/analysis/Makefile.kmk b/src/VBox/ValidationKit/analysis/Makefile.kmk
new file mode 100644
index 00000000..901250a7
--- /dev/null
+++ b/src/VBox/ValidationKit/analysis/Makefile.kmk
@@ -0,0 +1,45 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Python Test Driver.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/analysis/__init__.py b/src/VBox/ValidationKit/analysis/__init__.py
new file mode 100644
index 00000000..3f6a51e3
--- /dev/null
+++ b/src/VBox/ValidationKit/analysis/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Test analysis package
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/analysis/analyze.py b/src/VBox/ValidationKit/analysis/analyze.py
new file mode 100755
index 00000000..ec2b3cc1
--- /dev/null
+++ b/src/VBox/ValidationKit/analysis/analyze.py
@@ -0,0 +1,447 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: analyze.py $
+
+"""
+Analyzer CLI.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import re;
+import os;
+import textwrap;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from analysis import reader
+from analysis import reporting
+
+
+def usage():
+ """
+ Display usage.
+ """
+ # Set up the output wrapper.
+ try: cCols = os.get_terminal_size()[0] # since 3.3
+ except: cCols = 79;
+ oWrapper = textwrap.TextWrapper(width = cCols);
+
+ # Do the outputting.
+ print('Tool for comparing test results.');
+ print('');
+ oWrapper.subsequent_indent = ' ' * (len('usage: ') + 4);
+ print(oWrapper.fill('usage: analyze.py [options] [collection-1] -- [collection-2] [-- [collection3] [..]])'))
+ oWrapper.subsequent_indent = '';
+ print('');
+ print(oWrapper.fill('This tool compares two or more result collections, using one as a baseline (first by default) '
+ 'and showing how the results in others differs from it.'));
+ print('');
+ print(oWrapper.fill('The results (XML file) from one or more test runs makes up a collection. A collection can be '
+ 'named using the --name <name> option, or will get a sequential name automatically. The baseline '
+ 'collection will have "(baseline)" appended to its name.'));
+ print('');
+ print(oWrapper.fill('A test run produces one XML file, either via the testdriver/reporter.py machinery or via the IPRT '
+ 'test.cpp code. In the latter case it can be enabled and controlled via IPRT_TEST_FILE. A collection '
+ 'consists of one or more of test runs (i.e. XML result files). These are combined (aka distilled) '
+ 'into a single set of results before comparing them with the others. The --best and --avg options '
+ 'controls how this combining is done. The need for this is mainly to try counteract some of the '
+ 'instability typically found in the restuls. Just because one test run produces a better result '
+ 'after a change does not necessarily mean this will always be the case and that the change was to '
+ 'the better, it might just have been regular fluctuations in the test results.'));
+
+ oWrapper.initial_indent = ' ';
+ oWrapper.subsequent_indent = ' ';
+ print('');
+ print('Options governing combining (distillation):');
+ print(' --avg, --average');
+ print(oWrapper.fill('Picks the best result by calculating the average values across all the runs.'));
+ print('');
+ print(' --best');
+ print(oWrapper.fill('Picks the best result from all the runs. For values, this means making guessing what result is '
+ 'better based on the unit. This may not always lead to the right choices.'));
+ print(oWrapper.initial_indent + 'Default: --best');
+
+ print('');
+ print('Options relating to collections:');
+ print(' --name <name>');
+ print(oWrapper.fill('Sets the name of the current collection. By default a collection gets a sequential number.'));
+ print('');
+ print(' --baseline <num>');
+ print(oWrapper.fill('Sets collection given by <num> (0-based) as the baseline collection.'));
+ print(oWrapper.initial_indent + 'Default: --baseline 0')
+
+ print('');
+ print('Filtering options:');
+ print(' --filter-test <substring>');
+ print(oWrapper.fill('Exclude tests not containing any of the substrings given via the --filter-test option. The '
+ 'matching is done with full test name, i.e. all parent names are prepended with ", " as separator '
+ '(for example "tstIOInstr, CPUID EAX=1").'));
+ print('');
+ print(' --filter-test-out <substring>');
+ print(oWrapper.fill('Exclude tests containing the given substring. As with --filter-test, the matching is done against '
+ 'the full test name.'));
+ print('');
+ print(' --filter-value <substring>');
+ print(oWrapper.fill('Exclude values not containing any of the substrings given via the --filter-value option. The '
+ 'matching is done against the value name prefixed by the full test name and ": " '
+ '(for example "tstIOInstr, CPUID EAX=1: real mode, CPUID").'));
+ print('');
+ print(' --filter-value-out <substring>');
+ print(oWrapper.fill('Exclude value containing the given substring. As with --filter-value, the matching is done against '
+ 'the value name prefixed by the full test name.'));
+
+ print('');
+ print(' --regex-test <expr>');
+ print(oWrapper.fill('Same as --filter-test except the substring matching is done via a regular expression.'));
+ print('');
+ print(' --regex-test-out <expr>');
+ print(oWrapper.fill('Same as --filter-test-out except the substring matching is done via a regular expression.'));
+ print('');
+ print(' --regex-value <expr>');
+ print(oWrapper.fill('Same as --filter-value except the substring matching is done via a regular expression.'));
+ print('');
+ print(' --regex-value-out <expr>');
+ print(oWrapper.fill('Same as --filter-value-out except the substring matching is done via a regular expression.'));
+ print('');
+ print(' --filter-out-empty-leaf-tests');
+ print(oWrapper.fill('Removes any leaf tests that are without any values or sub-tests. This is useful when '
+ 'only considering values, especially when doing additional value filtering.'));
+
+ print('');
+ print('Analysis options:');
+ print(' --pct-same-value <float>');
+ print(oWrapper.fill('The threshold at which the percent difference between two values are considered the same '
+ 'during analysis.'));
+ print(oWrapper.initial_indent + 'Default: --pct-same-value 0.10');
+
+ print('');
+ print('Output options:');
+ print(' --brief, --verbose');
+ print(oWrapper.fill('Whether to omit (--brief) the value for non-baseline runs and just get along with the difference.'));
+ print(oWrapper.initial_indent + 'Default: --brief');
+ print('');
+ print(' --pct <num>, --pct-precision <num>');
+ print(oWrapper.fill('Specifies the number of decimal place to use when formatting the difference as percent.'));
+ print(oWrapper.initial_indent + 'Default: --pct 2');
+ return 1;
+
+
+class ResultCollection(object):
+ """
+ One or more test runs that should be merged before comparison.
+ """
+
+ def __init__(self, sName):
+ self.sName = sName;
+ self.aoTestTrees = [] # type: [Test]
+ self.asTestFiles = [] # type: [str] - runs parallel to aoTestTrees
+ self.oDistilled = None # type: Test
+
+ def append(self, sFilename):
+ """
+ Loads sFilename and appends the result.
+ Returns True on success, False on failure.
+ """
+ oTestTree = reader.parseTestResult(sFilename);
+ if oTestTree:
+ self.aoTestTrees.append(oTestTree);
+ self.asTestFiles.append(sFilename);
+ return True;
+ return False;
+
+ def isEmpty(self):
+ """ Checks if the result is empty. """
+ return len(self.aoTestTrees) == 0;
+
+ def filterTests(self, asFilters):
+ """
+ Keeps all the tests in the test trees sub-string matching asFilters (str or re).
+ """
+ for oTestTree in self.aoTestTrees:
+ oTestTree.filterTests(asFilters);
+ return self;
+
+ def filterOutTests(self, asFilters):
+ """
+ Removes all the tests in the test trees sub-string matching asFilters (str or re).
+ """
+ for oTestTree in self.aoTestTrees:
+ oTestTree.filterOutTests(asFilters);
+ return self;
+
+ def filterValues(self, asFilters):
+ """
+ Keeps all the tests in the test trees sub-string matching asFilters (str or re).
+ """
+ for oTestTree in self.aoTestTrees:
+ oTestTree.filterValues(asFilters);
+ return self;
+
+ def filterOutValues(self, asFilters):
+ """
+ Removes all the tests in the test trees sub-string matching asFilters (str or re).
+ """
+ for oTestTree in self.aoTestTrees:
+ oTestTree.filterOutValues(asFilters);
+ return self;
+
+ def filterOutEmptyLeafTests(self):
+ """
+ Removes all the tests in the test trees that have neither child tests nor values.
+ """
+ for oTestTree in self.aoTestTrees:
+ oTestTree.filterOutEmptyLeafTests();
+ return self;
+
+ def distill(self, sMethod, fDropLoners = False):
+ """
+ Distills the set of test results into a single one by the given method.
+
+ Valid sMethod values:
+ - 'best': Pick the best result for each test and value among all the test runs.
+ - 'avg': Calculate the average value among all the test runs.
+
+ When fDropLoners is True, tests and values that only appear in a single test run
+ will be discarded. When False (the default), the lone result will be used.
+ """
+ assert sMethod in ['best', 'avg'];
+ assert not self.oDistilled;
+
+ # If empty, nothing to do.
+ if self.isEmpty():
+ return None;
+
+ # If there is only a single tree, make a deep copy of it.
+ if len(self.aoTestTrees) == 1:
+ oDistilled = self.aoTestTrees[0].clone();
+ else:
+
+ # Since we don't know if the test runs are all from the same test, we create
+ # dummy root tests for each run and use these are the start for the distillation.
+ aoDummyInputTests = [];
+ for oRun in self.aoTestTrees:
+ oDummy = reader.Test();
+ oDummy.aoChildren = [oRun,];
+ aoDummyInputTests.append(oDummy);
+
+ # Similarly, we end up with a "dummy" root test for the result.
+ oDistilled = reader.Test();
+ oDistilled.distill(aoDummyInputTests, sMethod, fDropLoners);
+
+ # We can drop this if there is only a single child, i.e. if all runs are for
+ # the same test.
+ if len(oDistilled.aoChildren) == 1:
+ oDistilled = oDistilled.aoChildren[0];
+
+ self.oDistilled = oDistilled;
+ return oDistilled;
+
+
+
+# matchWithValue hacks.
+g_asOptions = [];
+g_iOptInd = 1;
+g_sOptArg = '';
+
+def matchWithValue(sOption):
+ """ Matches an option with a value, placing the value in g_sOptArg if it matches. """
+ global g_asOptions, g_iOptInd, g_sOptArg;
+ sArg = g_asOptions[g_iOptInd];
+ if sArg.startswith(sOption):
+ if len(sArg) == len(sOption):
+ if g_iOptInd + 1 < len(g_asOptions):
+ g_iOptInd += 1;
+ g_sOptArg = g_asOptions[g_iOptInd];
+ return True;
+
+ print('syntax error: Option %s takes a value!' % (sOption,));
+ raise Exception('syntax error: Option %s takes a value!' % (sOption,));
+
+ if sArg[len(sOption)] in ('=', ':'):
+ g_sOptArg = sArg[len(sOption) + 1:];
+ return True;
+ return False;
+
+
+def main(asArgs):
+ """ C style main(). """
+ #
+ # Parse arguments
+ #
+ oCurCollection = ResultCollection('#0');
+ aoCollections = [ oCurCollection, ];
+ iBaseline = 0;
+ sDistillationMethod = 'best';
+ fBrief = True;
+ cPctPrecision = 2;
+ rdPctSameValue = 0.1;
+ asTestFilters = [];
+ asTestOutFilters = [];
+ asValueFilters = [];
+ asValueOutFilters = [];
+ fFilterOutEmptyLeafTest = True;
+
+ global g_asOptions, g_iOptInd, g_sOptArg;
+ g_asOptions = asArgs;
+ g_iOptInd = 1;
+ while g_iOptInd < len(asArgs):
+ sArg = asArgs[g_iOptInd];
+ g_sOptArg = '';
+ #print("dbg: g_iOptInd=%s '%s'" % (g_iOptInd, sArg,));
+
+ if sArg.startswith('--help'):
+ return usage();
+
+ if matchWithValue('--filter-test'):
+ asTestFilters.append(g_sOptArg);
+ elif matchWithValue('--filter-test-out'):
+ asTestOutFilters.append(g_sOptArg);
+ elif matchWithValue('--filter-value'):
+ asValueFilters.append(g_sOptArg);
+ elif matchWithValue('--filter-value-out'):
+ asValueOutFilters.append(g_sOptArg);
+
+ elif matchWithValue('--regex-test'):
+ asTestFilters.append(re.compile(g_sOptArg));
+ elif matchWithValue('--regex-test-out'):
+ asTestOutFilters.append(re.compile(g_sOptArg));
+ elif matchWithValue('--regex-value'):
+ asValueFilters.append(re.compile(g_sOptArg));
+ elif matchWithValue('--regex-value-out'):
+ asValueOutFilters.append(re.compile(g_sOptArg));
+
+ elif sArg == '--filter-out-empty-leaf-tests':
+ fFilterOutEmptyLeafTest = True;
+ elif sArg == '--no-filter-out-empty-leaf-tests':
+ fFilterOutEmptyLeafTest = False;
+
+ elif sArg == '--best':
+ sDistillationMethod = 'best';
+ elif sArg in ('--avg', '--average'):
+ sDistillationMethod = 'avg';
+
+ elif sArg == '--brief':
+ fBrief = True;
+ elif sArg == '--verbose':
+ fBrief = False;
+
+ elif matchWithValue('--pct') or matchWithValue('--pct-precision'):
+ cPctPrecision = int(g_sOptArg);
+ elif matchWithValue('--base') or matchWithValue('--baseline'):
+ iBaseline = int(g_sOptArg);
+
+ elif matchWithValue('--pct-same-value'):
+ rdPctSameValue = float(g_sOptArg);
+
+ # '--' starts a new collection. If current one is empty, drop it.
+ elif sArg == '--':
+ print("dbg: new collection");
+ #if oCurCollection.isEmpty():
+ # del aoCollections[-1];
+ oCurCollection = ResultCollection("#%s" % (len(aoCollections),));
+ aoCollections.append(oCurCollection);
+
+ # Name the current result collection.
+ elif matchWithValue('--name'):
+ oCurCollection.sName = g_sOptArg;
+
+ # Read in a file and add it to the current data set.
+ else:
+ if not oCurCollection.append(sArg):
+ return 1;
+ g_iOptInd += 1;
+
+ #
+ # Post argument parsing processing.
+ #
+
+ # Drop the last collection if empty.
+ if oCurCollection.isEmpty():
+ del aoCollections[-1];
+ if not aoCollections:
+ print("error: No input files given!");
+ return 1;
+
+ # Check the baseline value and mark the column as such.
+ if iBaseline < 0 or iBaseline > len(aoCollections):
+ print("error: specified baseline is out of range: %s, valid range 0 <= baseline < %s"
+ % (iBaseline, len(aoCollections),));
+ return 1;
+ aoCollections[iBaseline].sName += ' (baseline)';
+
+ #
+ # Apply filtering before distilling each collection into a single result tree.
+ #
+ if asTestFilters:
+ for oCollection in aoCollections:
+ oCollection.filterTests(asTestFilters);
+ if asTestOutFilters:
+ for oCollection in aoCollections:
+ oCollection.filterOutTests(asTestOutFilters);
+
+ if asValueFilters:
+ for oCollection in aoCollections:
+ oCollection.filterValues(asValueFilters);
+ if asValueOutFilters:
+ for oCollection in aoCollections:
+ oCollection.filterOutValues(asValueOutFilters);
+
+ if fFilterOutEmptyLeafTest:
+ for oCollection in aoCollections:
+ oCollection.filterOutEmptyLeafTests();
+
+ # Distillation.
+ for oCollection in aoCollections:
+ oCollection.distill(sDistillationMethod);
+
+ #
+ # Produce the report.
+ #
+ oTable = reporting.RunTable(iBaseline, fBrief, cPctPrecision, rdPctSameValue);
+ oTable.populateFromRuns([oCollection.oDistilled for oCollection in aoCollections],
+ [oCollection.sName for oCollection in aoCollections]);
+ print('\n'.join(oTable.formatAsText()));
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/analysis/reader.py b/src/VBox/ValidationKit/analysis/reader.py
new file mode 100755
index 00000000..9e98e270
--- /dev/null
+++ b/src/VBox/ValidationKit/analysis/reader.py
@@ -0,0 +1,762 @@
+# -*- coding: utf-8 -*-
+# $Id: reader.py $
+
+"""
+XML reader module.
+
+This produces a test result tree that can be processed and passed to
+reporting.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+__all__ = [ 'parseTestResult', ]
+
+# Standard python imports.
+import datetime;
+import os;
+import re;
+import sys;
+import traceback;
+
+# Only the main script needs to modify the path.
+try: __file__;
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+sys.path.append(g_ksValidationKitDir);
+
+# ValidationKit imports.
+from common import utils;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+# pylint: disable=missing-docstring
+
+
+class Value(object):
+ """
+ Represents a value. Usually this is benchmark result or parameter.
+ """
+
+ kdBestByUnit = {
+ "%": +1, # Difficult to say what's best really.
+ "bytes": +1, # Difficult to say what's best really.
+ "bytes/s": +2,
+ "KB": +1,
+ "KB/s": +2,
+ "MB": +1,
+ "MB/s": +2,
+ "packets": +2,
+ "packets/s": +2,
+ "frames": +2,
+ "frames/s": +2,
+ "occurrences": +1, # Difficult to say what's best really.
+ "occurrences/s": +2,
+ "roundtrips": +2,
+ "calls": +1, # Difficult to say what's best really.
+ "calls/s": +2,
+ "s": -2,
+ "ms": -2,
+ "ns": -2,
+ "ns/call": -2,
+ "ns/frame": -2,
+ "ns/occurrence": -2,
+ "ns/packet": -2,
+ "ns/roundtrip": -2,
+ "ins": +2,
+ "ins/sec": +2,
+ "": +1, # Difficult to say what's best really.
+ "pp1k": -2,
+ "pp10k": -2,
+ "ppm": -2,
+ "ppb": -2,
+ "ticks": -1, # Difficult to say what's best really.
+ "ticks/call": -2,
+ "ticks/occ": -2,
+ "pages": +1, # Difficult to say what's best really.
+ "pages/s": +2,
+ "ticks/page": -2,
+ "ns/page": -2,
+ "ps": -1, # Difficult to say what's best really.
+ "ps/call": -2,
+ "ps/frame": -2,
+ "ps/occurrence": -2,
+ "ps/packet": -2,
+ "ps/roundtrip": -2,
+ "ps/page": -2,
+ };
+
+ def __init__(self, oTest, sName = None, sUnit = None, sTimestamp = None, lValue = None):
+ self.oTest = oTest;
+ self.sName = sName;
+ self.sUnit = sUnit;
+ self.sTimestamp = sTimestamp;
+ self.lValue = self.valueToInteger(lValue);
+ assert self.lValue is None or isinstance(self.lValue, (int, long)), "lValue=%s %s" % (self.lValue, type(self.lValue),);
+
+ def clone(self, oParentTest):
+ """
+ Clones the value.
+ """
+ return Value(oParentTest, self.sName, self.sUnit, self.sTimestamp, self.lValue);
+
+ def matchFilters(self, sPrefix, aoFilters):
+ """
+ Checks for any substring match between aoFilters (str or re.Pattern)
+ and the value name prefixed by sPrefix.
+
+ Returns True if any of the filters matches.
+ Returns False if none of the filters matches.
+ """
+ sFullName = sPrefix + self.sName;
+ for oFilter in aoFilters:
+ if oFilter.search(sFullName) is not None if isinstance(oFilter, re.Pattern) else sFullName.find(oFilter) >= 0:
+ return True;
+ return False;
+
+ def canDoBetterCompare(self):
+ """
+ Checks whether we can do a confident better-than comparsion of the value.
+ """
+ return self.sUnit is not None and self.kdBestByUnit[self.sUnit] not in (-1, 0, 1);
+
+ def getBetterRelation(self):
+ """
+ Returns +2 if larger values are definintely better.
+ Returns +1 if larger values are likely to be better.
+ Returns 0 if we have no clue.
+ Returns -1 if smaller values are likey to better.
+ Returns -2 if smaller values are definitely better.
+ """
+ if self.sUnit is None:
+ return 0;
+ return self.kdBestByUnit[self.sUnit];
+
+ @staticmethod
+ def valueToInteger(sValue):
+ """
+ Returns integer (long) represention of lValue.
+ Returns None if it cannot be converted to integer.
+
+ Raises an exception if sValue isn't an integer.
+ """
+ if sValue is None or isinstance(sValue, (int, long)):
+ return sValue;
+ sValue = sValue.strip();
+ if not sValue:
+ return None;
+ return long(sValue);
+
+ # Manipluation
+
+ def distill(self, aoValues, sMethod):
+ """
+ Distills the value of the object from values from multiple test runs.
+ """
+ if not aoValues:
+ return self;
+
+ # Everything except the value comes from the first run.
+ self.sName = aoValues[0].sName;
+ self.sTimestamp = aoValues[0].sTimestamp;
+ self.sUnit = aoValues[0].sUnit;
+
+ # Find the value to use according to sMethod.
+ if len(aoValues) == 1:
+ self.lValue = aoValues[0].lValue;
+ else:
+ alValuesXcptInvalid = [oValue.lValue for oValue in aoValues if oValue.lValue is not None];
+ if not alValuesXcptInvalid:
+ # No integer result, so just pick the first value whatever it is.
+ self.lValue = aoValues[0].lValue;
+
+ elif sMethod == 'best':
+ # Pick the best result out of the whole bunch.
+ if self.kdBestByUnit[self.sUnit] >= 0:
+ self.lValue = max(alValuesXcptInvalid);
+ else:
+ self.lValue = min(alValuesXcptInvalid);
+
+ elif sMethod == 'avg':
+ # Calculate the average.
+ self.lValue = (sum(alValuesXcptInvalid) + len(alValuesXcptInvalid) // 2) // len(alValuesXcptInvalid);
+
+ else:
+ assert False;
+ self.lValue = aoValues[0].lValue;
+
+ return self;
+
+
+ # debug
+
+ def printValue(self, cIndent):
+ print('%sValue: name=%s timestamp=%s unit=%s value=%s'
+ % (''.ljust(cIndent*2), self.sName, self.sTimestamp, self.sUnit, self.lValue));
+
+
+class Test(object):
+ """
+ Nested test result.
+ """
+ def __init__(self, oParent = None, hsAttrs = None):
+ self.aoChildren = [] # type: list(Test)
+ self.aoValues = [];
+ self.oParent = oParent;
+ self.sName = hsAttrs['name'] if hsAttrs else None;
+ self.sStartTS = hsAttrs['timestamp'] if hsAttrs else None;
+ self.sEndTS = None;
+ self.sStatus = None;
+ self.cErrors = -1;
+
+ def clone(self, oParent = None):
+ """
+ Returns a deep copy.
+ """
+ oClone = Test(oParent, {'name': self.sName, 'timestamp': self.sStartTS});
+
+ for oChild in self.aoChildren:
+ oClone.aoChildren.append(oChild.clone(oClone));
+
+ for oValue in self.aoValues:
+ oClone.aoValues.append(oValue.clone(oClone));
+
+ oClone.sEndTS = self.sEndTS;
+ oClone.sStatus = self.sStatus;
+ oClone.cErrors = self.cErrors;
+ return oClone;
+
+ # parsing
+
+ def addChild(self, oChild):
+ self.aoChildren.append(oChild);
+ return oChild;
+
+ def addValue(self, oValue):
+ self.aoValues.append(oValue);
+ return oValue;
+
+ def __markCompleted(self, sTimestamp):
+ """ Sets sEndTS if not already done. """
+ if not self.sEndTS:
+ self.sEndTS = sTimestamp;
+
+ def markPassed(self, sTimestamp):
+ self.__markCompleted(sTimestamp);
+ self.sStatus = 'passed';
+ self.cErrors = 0;
+
+ def markSkipped(self, sTimestamp):
+ self.__markCompleted(sTimestamp);
+ self.sStatus = 'skipped';
+ self.cErrors = 0;
+
+ def markFailed(self, sTimestamp, cErrors):
+ self.__markCompleted(sTimestamp);
+ self.sStatus = 'failed';
+ self.cErrors = cErrors;
+
+ def markEnd(self, sTimestamp, cErrors):
+ self.__markCompleted(sTimestamp);
+ if self.sStatus is None:
+ self.sStatus = 'failed' if cErrors != 0 else 'end';
+ self.cErrors = 0;
+
+ def mergeInIncludedTest(self, oTest):
+ """ oTest will be robbed. """
+ if oTest is not None:
+ for oChild in oTest.aoChildren:
+ oChild.oParent = self;
+ self.aoChildren.append(oChild);
+ for oValue in oTest.aoValues:
+ oValue.oTest = self;
+ self.aoValues.append(oValue);
+ oTest.aoChildren = [];
+ oTest.aoValues = [];
+
+ # debug
+
+ def printTree(self, iLevel = 0):
+ print('%sTest: name=%s start=%s end=%s' % (''.ljust(iLevel*2), self.sName, self.sStartTS, self.sEndTS));
+ for oChild in self.aoChildren:
+ oChild.printTree(iLevel + 1);
+ for oValue in self.aoValues:
+ oValue.printValue(iLevel + 1);
+
+ # getters / queries
+
+ def getFullNameWorker(self, cSkipUpper):
+ if self.oParent is None:
+ return (self.sName, 0);
+ sName, iLevel = self.oParent.getFullNameWorker(cSkipUpper);
+ if iLevel < cSkipUpper:
+ sName = self.sName;
+ else:
+ sName += ', ' + self.sName;
+ return (sName, iLevel + 1);
+
+ def getFullName(self, cSkipUpper = 2):
+ return self.getFullNameWorker(cSkipUpper)[0];
+
+ def matchFilters(self, aoFilters):
+ """
+ Checks for any substring match between aoFilters (str or re.Pattern)
+ and the full test name.
+
+ Returns True if any of the filters matches.
+ Returns False if none of the filters matches.
+ """
+ sFullName = self.getFullName();
+ for oFilter in aoFilters:
+ if oFilter.search(sFullName) is not None if isinstance(oFilter, re.Pattern) else sFullName.find(oFilter) >= 0:
+ return True;
+ return False;
+
+ # manipulation
+
+ def filterTestsWorker(self, asFilters, fReturnOnMatch):
+ # depth first
+ i = 0;
+ while i < len(self.aoChildren):
+ if self.aoChildren[i].filterTestsWorker(asFilters, fReturnOnMatch):
+ i += 1;
+ else:
+ self.aoChildren[i].oParent = None;
+ del self.aoChildren[i];
+
+ # If we have children, they must've matched up.
+ if self.aoChildren:
+ return True;
+ if self.matchFilters(asFilters):
+ return fReturnOnMatch;
+ return not fReturnOnMatch;
+
+ def filterTests(self, asFilters):
+ """ Keep tests matching asFilters. """
+ if asFilters:
+ self.filterTestsWorker(asFilters, True);
+ return self;
+
+ def filterOutTests(self, asFilters):
+ """ Removes tests matching asFilters. """
+ if asFilters:
+ self.filterTestsWorker(asFilters, False);
+ return self;
+
+ def filterValuesWorker(self, asFilters, fKeepWhen):
+ # Process children recursively.
+ for oChild in self.aoChildren:
+ oChild.filterValuesWorker(asFilters, fKeepWhen);
+
+ # Filter our values.
+ iValue = len(self.aoValues);
+ if iValue > 0:
+ sFullname = self.getFullName() + ': ';
+ while iValue > 0:
+ iValue -= 1;
+ if self.aoValues[iValue].matchFilters(sFullname, asFilters) != fKeepWhen:
+ del self.aoValues[iValue];
+ return None;
+
+ def filterValues(self, asFilters):
+ """ Keep values matching asFilters. """
+ if asFilters:
+ self.filterValuesWorker(asFilters, True);
+ return self;
+
+ def filterOutValues(self, asFilters):
+ """ Removes values matching asFilters. """
+ if asFilters:
+ self.filterValuesWorker(asFilters, False);
+ return self;
+
+ def filterOutEmptyLeafTests(self):
+ """
+ Removes any child tests that has neither values nor sub-tests.
+ Returns True if leaf, False if not.
+ """
+ iChild = len(self.aoChildren);
+ while iChild > 0:
+ iChild -= 1;
+ if self.aoChildren[iChild].filterOutEmptyLeafTests():
+ del self.aoChildren[iChild];
+ return not self.aoChildren and not self.aoValues;
+
+ @staticmethod
+ def calcDurationStatic(sStartTS, sEndTS):
+ """
+ Returns None the start timestamp is absent or invalid.
+ Returns datetime.timedelta otherwise.
+ """
+ if not sStartTS:
+ return None;
+ try:
+ oStart = utils.parseIsoTimestamp(sStartTS);
+ except:
+ return None;
+
+ if not sEndTS:
+ return datetime.timedelta.max;
+ try:
+ oEnd = utils.parseIsoTimestamp(sEndTS);
+ except:
+ return datetime.timedelta.max;
+
+ return oEnd - oStart;
+
+ def calcDuration(self):
+ """
+ Returns the duration as a datetime.timedelta object or None if not available.
+ """
+ return self.calcDurationStatic(self.sStartTS, self.sEndTS);
+
+ def calcDurationAsMicroseconds(self):
+ """
+ Returns the duration as microseconds or None if not available.
+ """
+ oDuration = self.calcDuration();
+ if not oDuration:
+ return None;
+ return (oDuration.days * 86400 + oDuration.seconds) * 1000000 + oDuration.microseconds;
+
+ @staticmethod
+ def distillTimes(aoTestRuns, sMethod, sStatus):
+ """
+ Destills the error counts of the tests.
+ Returns a (sStartTS, sEndTS) pair.
+ """
+
+ #
+ # Start by assembling two list of start and end times for all runs that have a start timestamp.
+ # Then sort out the special cases where no run has a start timestamp and only a single one has.
+ #
+ asStartTS = [oRun.sStartTS for oRun in aoTestRuns if oRun.sStartTS];
+ if not asStartTS:
+ return (None, None);
+ asEndTS = [oRun.sEndTS for oRun in aoTestRuns if oRun.sStartTS]; # parallel to asStartTS, so we don't check sEndTS.
+ if len(asStartTS) == 1:
+ return (asStartTS[0], asEndTS[0]);
+
+ #
+ # Calculate durations for all runs.
+ #
+ if sMethod == 'best':
+ aoDurations = [Test.calcDurationStatic(oRun.sStartTS, oRun.sEndTS) for oRun in aoTestRuns if oRun.sStatus == sStatus];
+ if not aoDurations or aoDurations.count(None) == len(aoDurations):
+ aoDurations = [Test.calcDurationStatic(oRun.sStartTS, oRun.sEndTS) for oRun in aoTestRuns];
+ if aoDurations.count(None) == len(aoDurations):
+ return (asStartTS[0], None);
+ oDuration = min([oDuration for oDuration in aoDurations if oDuration is not None]);
+
+ elif sMethod == 'avg':
+ print("dbg: 0: sStatus=%s []=%s"
+ % (sStatus, [(Test.calcDurationStatic(oRun.sStartTS, oRun.sEndTS),oRun.sStatus) for oRun in aoTestRuns],));
+ aoDurations = [Test.calcDurationStatic(oRun.sStartTS, oRun.sEndTS) for oRun in aoTestRuns if oRun.sStatus == sStatus];
+ print("dbg: 1: aoDurations=%s" % (aoDurations,))
+ aoDurations = [oDuration for oDuration in aoDurations if oDuration];
+ print("dbg: 2: aoDurations=%s" % (aoDurations,))
+ if not aoDurations:
+ return (asStartTS[0], None);
+ aoDurations = [oDuration for oDuration in aoDurations if oDuration < datetime.timedelta.max];
+ print("dbg: 3: aoDurations=%s" % (aoDurations,))
+ if not aoDurations:
+ return (asStartTS[0], None);
+ # sum doesn't work on timedelta, so do it manually.
+ oDuration = aoDurations[0];
+ for i in range(1, len(aoDurations)):
+ oDuration += aoDurations[i];
+ print("dbg: 5: oDuration=%s" % (aoDurations,))
+ oDuration = oDuration / len(aoDurations);
+ print("dbg: 6: oDuration=%s" % (aoDurations,))
+
+ else:
+ assert False;
+ return (asStartTS[0], asEndTS[0]);
+
+ # Check unfinished test.
+ if oDuration >= datetime.timedelta.max:
+ return (asStartTS[0], None);
+
+ # Calculate and format the end timestamp string.
+ oStartTS = utils.parseIsoTimestamp(asStartTS[0]);
+ oEndTS = oStartTS + oDuration;
+ return (asStartTS[0], utils.formatIsoTimestamp(oEndTS));
+
+ @staticmethod
+ def distillStatus(aoTestRuns, sMethod):
+ """
+ Destills the status of the tests.
+ Returns the status.
+ """
+ asStatuses = [oRun.sStatus for oRun in aoTestRuns];
+
+ if sMethod == 'best':
+ for sStatus in ('passed', 'failed', 'skipped'):
+ if sStatus in asStatuses:
+ return sStatus;
+ return asStatuses[0];
+
+ if sMethod == 'avg':
+ cPassed = asStatuses.count('passed');
+ cFailed = asStatuses.count('failed');
+ cSkipped = asStatuses.count('skipped');
+ cEnd = asStatuses.count('end');
+ cNone = asStatuses.count(None);
+ if cPassed >= cFailed and cPassed >= cSkipped and cPassed >= cNone and cPassed >= cEnd:
+ return 'passed';
+ if cFailed >= cPassed and cFailed >= cSkipped and cFailed >= cNone and cFailed >= cEnd:
+ return 'failed';
+ if cSkipped >= cPassed and cSkipped >= cFailed and cSkipped >= cNone and cSkipped >= cEnd:
+ return 'skipped';
+ if cEnd >= cPassed and cEnd >= cFailed and cEnd >= cNone and cEnd >= cSkipped:
+ return 'end';
+ return None;
+
+ assert False;
+ return asStatuses[0];
+
+ @staticmethod
+ def distillErrors(aoTestRuns, sMethod):
+ """
+ Destills the error counts of the tests.
+ Returns the status.
+ """
+ acErrorsXcptNeg = [oRun.cErrors for oRun in aoTestRuns if oRun.cErrors >= 0];
+
+ if sMethod == 'best':
+ if acErrorsXcptNeg:
+ return min(acErrorsXcptNeg);
+ elif sMethod == 'avg':
+ if acErrorsXcptNeg:
+ return sum(acErrorsXcptNeg) // len(acErrorsXcptNeg);
+ else:
+ assert False;
+ return -1;
+
+ def distill(self, aoTestRuns, sMethod, fDropLoners):
+ """
+ Distills the test runs into this test.
+ """
+ #
+ # Recurse first (before we create too much state in the stack
+ # frame) and do child tests.
+ #
+ # We copy the child lists of each test run so we can remove tests we've
+ # processed from each run and thus make sure we include tests in
+ #
+ #
+ aaoChildren = [list(oRun.aoChildren) for oRun in aoTestRuns];
+
+ # Process the tests for each run.
+ for i, _ in enumerate(aaoChildren):
+ # Process all tests for the current run.
+ while len(aaoChildren[i]) > 0:
+ oFirst = aaoChildren[i].pop(0);
+
+ # Build a list of sub-test runs by searching remaining runs by test name.
+ aoSameSubTests = [oFirst,];
+ for j in range(i + 1, len(aaoChildren)):
+ aoThis = aaoChildren[j];
+ for iThis, oThis in enumerate(aoThis):
+ if oThis.sName == oFirst.sName:
+ del aoThis[iThis];
+ aoSameSubTests.append(oThis);
+ break;
+
+ # Apply fDropLoners.
+ if not fDropLoners or len(aoSameSubTests) > 1 or len(aaoChildren) == 1:
+ # Create an empty test and call distill on it with the subtest array, unless
+ # of course that the array only has one member and we can simply clone it.
+ if len(aoSameSubTests) == 1:
+ self.addChild(oFirst.clone(self));
+ else:
+ oSubTest = Test(self);
+ oSubTest.sName = oFirst.sName;
+ oSubTest.distill(aoSameSubTests, sMethod, fDropLoners);
+ self.addChild(oSubTest);
+ del aaoChildren;
+
+ #
+ # Do values. Similar approch as for the sub-tests.
+ #
+ aaoValues = [list(oRun.aoValues) for oRun in aoTestRuns];
+
+ # Process the values for each run.
+ for i,_ in enumerate(aaoValues):
+ # Process all values for the current run.
+ while len(aaoValues[i]) > 0:
+ oFirst = aaoValues[i].pop(0);
+
+ # Build a list of values runs by searching remaining runs by value name and unit.
+ aoSameValues = [oFirst,];
+ for j in range(i + 1, len(aaoValues)):
+ aoThis = aaoValues[j];
+ for iThis, oThis in enumerate(aoThis):
+ if oThis.sName == oFirst.sName and oThis.sUnit == oFirst.sUnit:
+ del aoThis[iThis];
+ aoSameValues.append(oThis);
+ break;
+
+ # Apply fDropLoners.
+ if not fDropLoners or len(aoSameValues) > 1 or len(aaoValues) == 1:
+ # Create an empty test and call distill on it with the subtest array, unless
+ # of course that the array only has one member and we can simply clone it.
+ if len(aoSameValues) == 1:
+ self.aoValues.append(oFirst.clone(self));
+ else:
+ oValue = Value(self);
+ oValue.distill(aoSameValues, sMethod);
+ self.aoValues.append(oValue);
+ del aaoValues;
+
+ #
+ # Distill test properties.
+ #
+ self.sStatus = self.distillStatus(aoTestRuns, sMethod);
+ self.cErrors = self.distillErrors(aoTestRuns, sMethod);
+ (self.sStartTS, self.sEndTS) = self.distillTimes(aoTestRuns, sMethod, self.sStatus);
+ print("dbg: %s: sStartTS=%s, sEndTS=%s" % (self.sName, self.sStartTS, self.sEndTS));
+
+ return self;
+
+
+class XmlLogReader(object):
+ """
+ XML log reader class.
+ """
+
+ def __init__(self, sXmlFile):
+ self.sXmlFile = sXmlFile;
+ self.oRoot = Test(None, {'name': 'root', 'timestamp': ''});
+ self.oTest = self.oRoot;
+ self.iLevel = 0;
+ self.oValue = None;
+
+ def parse(self):
+ try:
+ oFile = open(self.sXmlFile, 'rb'); # pylint: disable=consider-using-with
+ except:
+ traceback.print_exc();
+ return False;
+
+ from xml.parsers.expat import ParserCreate
+ oParser = ParserCreate();
+ oParser.StartElementHandler = self.handleElementStart;
+ oParser.CharacterDataHandler = self.handleElementData;
+ oParser.EndElementHandler = self.handleElementEnd;
+ try:
+ oParser.ParseFile(oFile);
+ except:
+ traceback.print_exc();
+ oFile.close();
+ return False;
+ oFile.close();
+ return True;
+
+ def handleElementStart(self, sName, hsAttrs):
+ #print('%s%s: %s' % (''.ljust(self.iLevel * 2), sName, str(hsAttrs)));
+ if sName in ('Test', 'SubTest',):
+ self.iLevel += 1;
+ self.oTest = self.oTest.addChild(Test(self.oTest, hsAttrs));
+ elif sName == 'Value':
+ self.oValue = self.oTest.addValue(Value(self.oTest, hsAttrs.get('name'), hsAttrs.get('unit'),
+ hsAttrs.get('timestamp'), hsAttrs.get('value')));
+ elif sName == 'End':
+ self.oTest.markEnd(hsAttrs.get('timestamp'), int(hsAttrs.get('errors', '0')));
+ elif sName == 'Passed':
+ self.oTest.markPassed(hsAttrs.get('timestamp'));
+ elif sName == 'Skipped':
+ self.oTest.markSkipped(hsAttrs.get('timestamp'));
+ elif sName == 'Failed':
+ self.oTest.markFailed(hsAttrs.get('timestamp'), int(hsAttrs['errors']));
+ elif sName == 'Include':
+ self.handleInclude(hsAttrs);
+ else:
+ print('Unknown element "%s"' % (sName,));
+
+ def handleElementData(self, sData):
+ if self.oValue is not None:
+ self.oValue.addData(sData);
+ elif sData.strip() != '':
+ print('Unexpected data "%s"' % (sData,));
+ return True;
+
+ def handleElementEnd(self, sName):
+ if sName in ('Test', 'Subtest',):
+ self.iLevel -= 1;
+ self.oTest = self.oTest.oParent;
+ elif sName == 'Value':
+ self.oValue = None;
+ return True;
+
+ def handleInclude(self, hsAttrs):
+ # relative or absolute path.
+ sXmlFile = hsAttrs['filename'];
+ if not os.path.isabs(sXmlFile):
+ sXmlFile = os.path.join(os.path.dirname(self.sXmlFile), sXmlFile);
+
+ # Try parse it.
+ oSub = parseTestResult(sXmlFile);
+ if oSub is None:
+ print('error: failed to parse include "%s"' % (sXmlFile,));
+ else:
+ # Skip the root and the next level before merging it the subtest and
+ # values in to the current test. The reason for this is that the
+ # include is the output of some sub-program we've run and we don't need
+ # the extra test level it automatically adds.
+ #
+ # More benchmark heuristics: Walk down until we find more than one
+ # test or values.
+ oSub2 = oSub;
+ while len(oSub2.aoChildren) == 1 and not oSub2.aoValues:
+ oSub2 = oSub2.aoChildren[0];
+ if not oSub2.aoValues:
+ oSub2 = oSub;
+ self.oTest.mergeInIncludedTest(oSub2);
+ return True;
+
+def parseTestResult(sXmlFile):
+ """
+ Parses the test results in the XML.
+ Returns result tree.
+ Returns None on failure.
+ """
+ oXlr = XmlLogReader(sXmlFile);
+ if oXlr.parse():
+ if len(oXlr.oRoot.aoChildren) == 1 and not oXlr.oRoot.aoValues:
+ return oXlr.oRoot.aoChildren[0];
+ return oXlr.oRoot;
+ return None;
+
diff --git a/src/VBox/ValidationKit/analysis/reporting.py b/src/VBox/ValidationKit/analysis/reporting.py
new file mode 100755
index 00000000..ba8db6a0
--- /dev/null
+++ b/src/VBox/ValidationKit/analysis/reporting.py
@@ -0,0 +1,746 @@
+# -*- coding: utf-8 -*-
+# $Id: reporting.py $
+
+"""
+Test Result Report Writer.
+
+This takes a processed test result tree and creates a HTML, re-structured text,
+or normal text report from it.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__;
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+sys.path.append(g_ksValidationKitDir);
+
+# ValidationKit imports.
+from common import utils;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+##################################################################################################################################
+# Run Table #
+##################################################################################################################################
+
+def alignTextLeft(sText, cchWidth):
+ """ Left aligns text and pads it to cchWidth characters length. """
+ return sText + ' ' * (cchWidth - min(len(sText), cchWidth));
+
+
+def alignTextRight(sText, cchWidth):
+ """ Right aligns text and pads it to cchWidth characters length. """
+ return ' ' * (cchWidth - min(len(sText), cchWidth)) + sText;
+
+
+def alignTextCenter(sText, cchWidth):
+ """ Pads the text equally on both sides to cchWidth characters length. """
+ return alignTextLeft(' ' * ((cchWidth - min(len(sText), cchWidth)) // 2) + sText, cchWidth);
+
+
+g_kiAlignLeft = -1;
+g_kiAlignRight = 1;
+g_kiAlignCenter = 0;
+def alignText(sText, cchWidth, iAlignType):
+ """
+ General alignment method.
+
+ Negative iAlignType for left aligning, zero for entered, and positive for
+ right aligning the text.
+ """
+ if iAlignType < 0:
+ return alignTextLeft(sText, cchWidth);
+ if iAlignType > 0:
+ return alignTextRight(sText, cchWidth);
+ return alignTextCenter(sText, cchWidth);
+
+
+class TextColumnWidth(object):
+ """
+ Tracking the width of a column, dealing with sub-columns and such.
+ """
+
+ def __init__(self):
+ self.cch = 0;
+ self.dacchSub = {};
+
+ def update(self, oWidth, cchSubColSpacing = 1):
+ """
+ Updates the column width tracking with oWidth, which is either
+ an int or an array of ints (sub columns).
+ """
+ if isinstance(oWidth, int):
+ self.cch = max(self.cch, oWidth);
+ else:
+ cSubCols = len(oWidth);
+ if cSubCols not in self.dacchSub:
+ self.dacchSub[cSubCols] = list(oWidth);
+ self.cch = max(self.cch, sum(oWidth) + cchSubColSpacing * (cSubCols - 1));
+ else:
+ acchSubCols = self.dacchSub[cSubCols];
+ for iSub in range(cSubCols):
+ acchSubCols[iSub] = max(acchSubCols[iSub], oWidth[iSub]);
+ self.cch = max(self.cch, sum(acchSubCols) + cchSubColSpacing * (cSubCols - 1));
+
+ def finalize(self):
+ """ Finalizes sub-column sizes. """
+ ## @todo maybe do something here, maybe not...
+ return self;
+
+ def hasSubColumns(self):
+ """ Checks if there are sub-columns for this column. """
+ return not self.dacchSub;
+
+class TextWidths(object):
+ """
+ Tracks the column widths for text rending of the table.
+ """
+ def __init__(self, cchSubColSpacing = 1, ):
+ self.cchName = 1;
+ self.aoColumns = [] # type: TextColumnWidth
+ self.cchSubColSpacing = cchSubColSpacing;
+ self.fFinalized = False;
+
+ def update(self, aoWidths):
+ """ Updates the tracker with the returns of calcColumnWidthsForText. """
+ if not aoWidths[0]:
+ self.cchName = max(self.cchName, aoWidths[1]);
+
+ for iCol, oWidth in enumerate(aoWidths[2]):
+ if iCol >= len(self.aoColumns):
+ self.aoColumns.append(TextColumnWidth());
+ self.aoColumns[iCol].update(oWidth, self.cchSubColSpacing);
+
+ return self;
+
+ def finalize(self):
+ """ Finalizes sub-column sizes. """
+ for oColumnWidth in self.aoColumns:
+ oColumnWidth.finalize();
+ self.fFinalized = True;
+ return self;
+
+ def getColumnWidth(self, iColumn, cSubs = None, iSub = None):
+ """ Returns the width of the specified column. """
+ if not self.fFinalized:
+ return 0;
+ assert iColumn < len(self.aoColumns), "iColumn=%s vs %s" % (iColumn, len(self.aoColumns),);
+ oColumn = self.aoColumns[iColumn];
+ if cSubs is not None:
+ assert iSub < cSubs;
+ if cSubs != 1:
+ assert cSubs in oColumn.dacchSub, \
+ "iColumn=%s cSubs=%s iSub=%s; dacchSub=%s" % (iColumn, cSubs, iSub, oColumn.dacchSub);
+ return oColumn.dacchSub[cSubs][iSub];
+ return oColumn.cch;
+
+
+class TextElement(object):
+ """
+ A text element (cell/sub-cell in a table).
+ """
+
+ def __init__(self, sText = '', iAlign = g_kiAlignRight): # type: (str, int) -> None
+ self.sText = sText;
+ self.iAlign = iAlign;
+
+ def asText(self, cchWidth): # type: (int) -> str
+ """ Pads the text to width of cchWidth characters. """
+ return alignText(self.sText, cchWidth, self.iAlign);
+
+
+class RunRow(object):
+ """
+ Run table row.
+ """
+
+ def __init__(self, iLevel, sName, iRun = 0): # type: (int, str, int) -> None
+ self.iLevel = iLevel;
+ self.sName = sName;
+ self.iFirstRun = iRun;
+
+ # Fields used while formatting (set during construction or calcColumnWidthsForText/Html).
+ self.cColumns = 0; ##< Number of columns.
+ self.fSkip = False ##< Whether or not to skip this row in the output.
+
+ # Format as Text:
+
+ def formatNameAsText(self, cchWidth): # (int) -> TextElement
+ """ Format the row as text. """
+ _ = cchWidth;
+ return TextElement(' ' * (self.iLevel * 2) + self.sName, g_kiAlignLeft);
+
+ def getColumnCountAsText(self, oTable):
+ """
+ Called by calcColumnWidthsForText for getting an up-to-date self.cColumns value.
+ Override this to update cColumns after construction.
+ """
+ _ = oTable;
+ return self.cColumns;
+
+ def formatColumnAsText(self, iColumn, oTable): # type: (int, RunTable) -> [TextElement]
+ """ Returns an array of TextElements for the given column in this row. """
+ _ = iColumn; _ = oTable;
+ return [ TextElement(),];
+
+ def calcColumnWidthsForText(self, oTable): # type: (RunTable) -> (bool, int, [])
+ """
+ Calculates the column widths for text rendering.
+
+ Returns a tuple consisting of the fSkip, the formatted name width, and an
+ array of column widths. The entries in the latter are either integer
+ widths or arrays of subcolumn integer widths.
+ """
+ aoRetCols = [];
+ cColumns = self.getColumnCountAsText(oTable);
+ for iColumn in range(cColumns):
+ aoSubColumns = self.formatColumnAsText(iColumn, oTable);
+ if len(aoSubColumns) == 1:
+ aoRetCols.append(len(aoSubColumns[0].sText));
+ else:
+ aoRetCols.append([len(oSubColumn.sText) for oSubColumn in aoSubColumns]);
+ return (False, len(self.formatNameAsText(0).sText), aoRetCols);
+
+ def renderAsText(self, oWidths, oTable): # type: (TextWidths, RunTable) -> str
+ """
+ Renders the row as text.
+
+ Returns string.
+ """
+ sRow = self.formatNameAsText(oWidths.cchName).asText(oWidths.cchName);
+ sRow = sRow + ' ' * (oWidths.cchName - min(len(sRow), oWidths.cchName)) + ' : ';
+
+ for iColumn in range(self.cColumns):
+ aoSubCols = self.formatColumnAsText(iColumn, oTable);
+ sCell = '';
+ for iSub, oText in enumerate(aoSubCols):
+ cchWidth = oWidths.getColumnWidth(iColumn, len(aoSubCols), iSub);
+ if iSub > 0:
+ sCell += ' ' * oWidths.cchSubColSpacing;
+ sCell += oText.asText(cchWidth);
+ cchWidth = oWidths.getColumnWidth(iColumn);
+ sRow += (' | ' if iColumn > 0 else '') + ' ' * (cchWidth - min(cchWidth, len(sCell))) + sCell;
+
+ return sRow;
+
+ @staticmethod
+ def formatDiffAsText(lNumber, lBaseline):
+ """ Formats the difference between lNumber and lBaseline as text. """
+ if lNumber is not None:
+ if lBaseline is not None:
+ if lNumber < lBaseline:
+ return '-' + utils.formatNumber(lBaseline - lNumber); ## @todo formatter is busted for negative nums.
+ if lNumber > lBaseline:
+ return '+' + utils.formatNumber(lNumber - lBaseline);
+ return '0';
+ return '';
+
+ @staticmethod
+ def formatPctAsText(chSign, rdPct, cPctPrecision):
+ """ Formats percentage value as text. """
+ if rdPct >= 100:
+ return '%s%s%%' % (chSign, utils.formatNumber(int(rdPct + 0.5)),);
+ if round(rdPct, cPctPrecision) != 0:
+ return '%s%.*f%%' % (chSign, cPctPrecision, rdPct,); # %.*f rounds.
+ return '~' + chSign + '0.' + '0' * cPctPrecision + '%';
+
+ @staticmethod
+ def formatDiffInPctAsText(lNumber, lBaseline, cPctPrecision):
+ """ Formats the difference between lNumber and lBaseline in precent as text. """
+ if lNumber is not None:
+ if lBaseline is not None:
+ ## @todo implement cPctPrecision
+ if lNumber == lBaseline:
+ return '0.' + '0'*cPctPrecision + '%';
+
+ lDiff = lNumber - lBaseline;
+ chSign = '+';
+ if lDiff < 0:
+ lDiff = -lDiff;
+ chSign = '-';
+ return RunRow.formatPctAsText(chSign, lDiff / float(lBaseline) * 100, cPctPrecision);
+ return '';
+
+
+class RunHeaderRow(RunRow):
+ """
+ Run table header row.
+ """
+ def __init__(self, sName, asColumns): # type: (str, [str]) -> None
+ RunRow.__init__(self, 0, sName);
+ self.asColumns = asColumns
+ self.cColumns = len(asColumns);
+
+ def formatColumnAsText(self, iColumn, oTable): # type: (int, RunTable) -> [TextElement]
+ return [TextElement(self.asColumns[iColumn], g_kiAlignCenter),];
+
+
+class RunFooterRow(RunHeaderRow):
+ """
+ Run table footer row.
+ """
+ def __init__(self, sName, asColumns):
+ RunHeaderRow.__init__(self, sName, asColumns);
+
+
+class RunSeparatorRow(RunRow):
+ """
+ Base class for separator rows.
+ """
+ def __init__(self):
+ RunRow.__init__(self, 0, '');
+
+ def calcTableWidthAsText(self, oWidths):
+ """ Returns the table width for when rendered as text. """
+ cchWidth = oWidths.cchName;
+ for oCol in oWidths.aoColumns:
+ cchWidth += 3 + oCol.cch;
+ return cchWidth;
+
+
+class RunHeaderSeparatorRow(RunSeparatorRow):
+ """
+ Run table header separator row.
+ """
+ def __init__(self):
+ RunSeparatorRow.__init__(self);
+
+ def renderAsText(self, oWidths, oTable):
+ _ = oTable;
+ return '=' * self.calcTableWidthAsText(oWidths);
+
+
+class RunFooterSeparatorRow(RunHeaderSeparatorRow):
+ """
+ Run table footer separator row.
+ """
+ def __init__(self):
+ RunHeaderSeparatorRow.__init__(self);
+
+
+class RunTestRow(RunRow):
+ """
+ Run table test row.
+ """
+
+ def __init__(self, iLevel, oTest, iRun, aoTests = None): # type: (int, reader.Test, int, [reader.Test]) -> None
+ RunRow.__init__(self, iLevel, oTest.sName, iRun);
+ assert oTest;
+ self.oTest = oTest;
+ if aoTests is None:
+ aoTests = [None for i in range(iRun)];
+ aoTests.append(oTest);
+ else:
+ aoTests= list(aoTests);
+ self.aoTests = aoTests
+
+ def isSameTest(self, oTest):
+ """ Checks if oTest belongs to this row or not. """
+ return oTest.sName == self.oTest.sName;
+
+ def getBaseTest(self, oTable):
+ """ Returns the baseline test. """
+ oBaseTest = self.aoTests[oTable.iBaseline];
+ if not oBaseTest:
+ oBaseTest = self.aoTests[self.iFirstRun];
+ return oBaseTest;
+
+
+class RunTestStartRow(RunTestRow):
+ """
+ Run table start of test row.
+ """
+
+ def __init__(self, iLevel, oTest, iRun): # type: (int, reader.Test, int) -> None
+ RunTestRow.__init__(self, iLevel, oTest, iRun);
+
+ def renderAsText(self, oWidths, oTable):
+ _ = oTable;
+ sRet = self.formatNameAsText(oWidths.cchName).asText(oWidths.cchName);
+ sRet += ' : ';
+ sRet += ' | '.join(['-' * oCol.cch for oCol in oWidths.aoColumns]);
+ return sRet;
+
+
+class RunTestEndRow(RunTestRow):
+ """
+ Run table end of test row.
+ """
+
+ def __init__(self, oStartRow): # type: (RunTestStartRow) -> None
+ RunTestRow.__init__(self, oStartRow.iLevel, oStartRow.oTest, oStartRow.iFirstRun, oStartRow.aoTests);
+ self.oStartRow = oStartRow # type: RunTestStartRow
+
+ def getColumnCountAsText(self, oTable):
+ self.cColumns = len(self.aoTests);
+ return self.cColumns;
+
+ def formatColumnAsText(self, iColumn, oTable):
+ oTest = self.aoTests[iColumn];
+ if oTest and oTest.sStatus:
+ if oTest.cErrors > 0:
+ return [ TextElement(oTest.sStatus, g_kiAlignCenter),
+ TextElement(utils.formatNumber(oTest.cErrors) + 'errors') ];
+ return [ TextElement(oTest.sStatus, g_kiAlignCenter) ];
+ return [ TextElement(), ];
+
+
+class RunTestEndRow2(RunTestRow):
+ """
+ Run table 2nd end of test row, this shows the times.
+ """
+
+ def __init__(self, oStartRow): # type: (RunTestStartRow) -> None
+ RunTestRow.__init__(self, oStartRow.iLevel, oStartRow.oTest, oStartRow.iFirstRun, oStartRow.aoTests);
+ self.oStartRow = oStartRow # type: RunTestStartRow
+
+ def formatNameAsText(self, cchWidth):
+ _ = cchWidth;
+ return TextElement('runtime', g_kiAlignRight);
+
+ def getColumnCountAsText(self, oTable):
+ self.cColumns = len(self.aoTests);
+ return self.cColumns;
+
+ def formatColumnAsText(self, iColumn, oTable):
+ oTest = self.aoTests[iColumn];
+ if oTest:
+ cUsElapsed = oTest.calcDurationAsMicroseconds();
+ if cUsElapsed:
+ oBaseTest = self.getBaseTest(oTable);
+ if oTest is oBaseTest:
+ return [ TextElement(utils.formatNumber(cUsElapsed)), TextElement('us', g_kiAlignLeft), ];
+ cUsElapsedBase = oBaseTest.calcDurationAsMicroseconds();
+ aoRet = [
+ TextElement(utils.formatNumber(cUsElapsed)),
+ TextElement(self.formatDiffAsText(cUsElapsed, cUsElapsedBase)),
+ TextElement(self.formatDiffInPctAsText(cUsElapsed, cUsElapsedBase, oTable.cPctPrecision)),
+ ];
+ return aoRet[1:] if oTable.fBrief else aoRet;
+ return [ TextElement(), ];
+
+
+class RunTestValueAnalysisRow(RunTestRow):
+ """
+ Run table row with value analysis for a test, see if we have an improvement or not.
+ """
+ def __init__(self, oStartRow): # type: (RunTestStartRow) -> None
+ RunTestRow.__init__(self, oStartRow.iLevel, oStartRow.oTest, oStartRow.iFirstRun, oStartRow.aoTests);
+ self.oStartRow = oStartRow # type: RunTestStartRow
+ self.cColumns = len(self.aoTests);
+
+ def formatNameAsText(self, cchWidth):
+ _ = cchWidth;
+ return TextElement('value analysis', g_kiAlignRight);
+
+ def formatColumnAsText(self, iColumn, oTable):
+ oBaseline = self.getBaseTest(oTable);
+ oTest = self.aoTests[iColumn];
+ if not oTest or oTest is oBaseline:
+ return [TextElement(),];
+
+ #
+ # This is a bit ugly, but it means we don't have to re-merge the values.
+ #
+ cTotal = 0;
+ cBetter = 0;
+ cWorse = 0;
+ cSame = 0;
+ cUncertain = 0;
+ rdPctTotal = 0.0;
+
+ iRow = oTable.aoRows.index(self.oStartRow); # ugly
+ while iRow < len(oTable.aoRows):
+ oRow = oTable.aoRows[iRow];
+ if oRow is self:
+ break;
+ if isinstance(oRow, RunValueRow):
+ oValue = oRow.aoValues[iColumn];
+ oBaseValue = oRow.getBaseValue(oTable);
+ if oValue is not None and oValue is not oBaseValue:
+ iBetter = oValue.getBetterRelation();
+ if iBetter != 0:
+ lDiff = oValue.lValue - oBaseValue.lValue;
+ rdPct = abs(lDiff / float(oBaseValue.lValue) * 100);
+ if rdPct < oTable.rdPctSameValue:
+ cSame += 1;
+ else:
+ if lDiff > 0 if iBetter > 0 else lDiff < 0:
+ cBetter += 1;
+ rdPctTotal += rdPct;
+ else:
+ cWorse += 1;
+ rdPctTotal += -rdPct;
+ cUncertain += 1 if iBetter in (1, -1) else 0;
+ cTotal += 1;
+ iRow += 1;
+
+ #
+ # Format the result.
+ #
+ aoRet = [];
+ if not oTable.fBrief:
+ sText = u' \u2193%u' % (cWorse,);
+ sText = u' \u2248%u' % (cSame,) + alignTextRight(sText, 4);
+ sText = u'\u2191%u' % (cBetter,) + alignTextRight(sText, 8);
+ aoRet = [TextElement(sText),];
+
+ if cSame >= cWorse and cSame >= cBetter:
+ sVerdict = 'same';
+ elif cWorse >= cSame and cWorse >= cBetter:
+ sVerdict = 'worse';
+ else:
+ sVerdict = 'better';
+ if cUncertain > 0:
+ sVerdict = 'probably ' + sVerdict;
+ aoRet.append(TextElement(sVerdict));
+
+ rdPctAvg = abs(rdPctTotal / cTotal); # Yes, average of the percentages!
+ aoRet.append(TextElement(self.formatPctAsText('+' if rdPctTotal >= 0 else '-', rdPctAvg, oTable.cPctPrecision)));
+
+ return aoRet;
+
+
+class RunValueRow(RunRow):
+ """
+ Run table value row.
+ """
+
+ def __init__(self, iLevel, oValue, iRun): # type: (int, reader.Value, int) -> None
+ RunRow.__init__(self, iLevel, oValue.sName, iRun);
+ self.oValue = oValue;
+ self.aoValues = [None for i in range(iRun)];
+ self.aoValues.append(oValue);
+
+ def isSameValue(self, oValue):
+ """ Checks if oValue belongs to this row or not. """
+ return oValue.sName == self.oValue.sName and oValue.sUnit == self.oValue.sUnit;
+
+ # Formatting as Text.
+
+ @staticmethod
+ def formatOneValueAsText(oValue): # type: (reader.Value) -> str
+ """ Formats a value. """
+ if not oValue:
+ return "N/A";
+ return utils.formatNumber(oValue.lValue);
+
+ def getBaseValue(self, oTable):
+ """ Returns the base value instance. """
+ oBaseValue = self.aoValues[oTable.iBaseline];
+ if not oBaseValue:
+ oBaseValue = self.aoValues[self.iFirstRun];
+ return oBaseValue;
+
+ def getColumnCountAsText(self, oTable):
+ self.cColumns = len(self.aoValues);
+ return self.cColumns;
+
+ def formatColumnAsText(self, iColumn, oTable):
+ oValue = self.aoValues[iColumn];
+ oBaseValue = self.getBaseValue(oTable);
+ if oValue is oBaseValue:
+ return [ TextElement(self.formatOneValueAsText(oValue)),
+ TextElement(oValue.sUnit, g_kiAlignLeft), ];
+ aoRet = [
+ TextElement(self.formatOneValueAsText(oValue)),
+ TextElement(self.formatDiffAsText(oValue.lValue if oValue else None, oBaseValue.lValue)),
+ TextElement(self.formatDiffInPctAsText(oValue.lValue if oValue else None, oBaseValue.lValue, oTable.cPctPrecision))
+ ];
+ return aoRet[1:] if oTable.fBrief else aoRet;
+
+
+class RunTable(object):
+ """
+ Result table.
+
+ This contains one or more test runs as columns.
+ """
+
+ def __init__(self, iBaseline = 0, fBrief = True, cPctPrecision = 2, rdPctSameValue = 0.10): # (int, bool, int, float) -> None
+ self.asColumns = [] # type: [str] ##< Column names.
+ self.aoRows = [] # type: [RunRow] ##< The table rows.
+ self.iBaseline = iBaseline # type: int ##< Which column is the baseline when diffing things.
+ self.fBrief = fBrief # type: bool ##< Whether to exclude the numerical values of non-baseline runs.
+ self.cPctPrecision = cPctPrecision # type: int ##< Number of decimal points in diff percentage value.
+ self.rdPctSameValue = rdPctSameValue # type: float ##< The percent value at which a value difference is considered
+ ## to be the same during value analysis.
+ def __populateFromValues(self, aaoValueRuns, iLevel): # type: ([reader.Value]) -> None
+ """
+ Internal worker for __populateFromRuns()
+
+ This will modify the sub-lists inside aaoValueRuns, returning with them all empty.
+
+ Returns True if an value analysis row should be added, False if not.
+ """
+ # Same as for __populateFromRuns, only no recursion.
+ fAnalysisRow = False;
+ for iValueRun, aoValuesForRun in enumerate(aaoValueRuns):
+ while aoValuesForRun:
+ oRow = RunValueRow(iLevel, aoValuesForRun.pop(0), iValueRun);
+ self.aoRows.append(oRow);
+
+ # Pop matching values from the other runs of this test.
+ for iOtherRun in range(iValueRun + 1, len(aaoValueRuns)):
+ aoValuesForOtherRun = aaoValueRuns[iOtherRun];
+ for iValueToPop, oOtherValue in enumerate(aoValuesForOtherRun):
+ if oRow.isSameValue(oOtherValue):
+ oRow.aoValues.append(aoValuesForOtherRun.pop(iValueToPop));
+ break;
+ if len(oRow.aoValues) <= iOtherRun:
+ oRow.aoValues.append(None);
+
+ fAnalysisRow = fAnalysisRow or oRow.oValue.canDoBetterCompare();
+ return fAnalysisRow;
+
+ def __populateFromRuns(self, aaoTestRuns, iLevel): # type: ([reader.Test]) -> None
+ """
+ Internal worker for populateFromRuns()
+
+ This will modify the sub-lists inside aaoTestRuns, returning with them all empty.
+ """
+
+ #
+ # Currently doing depth first, so values are always at the end.
+ # Nominally, we should inject values according to the timestamp.
+ # However, that's too much work right now and can be done later if needed.
+ #
+ for iRun, aoTestForRun in enumerate(aaoTestRuns):
+ while aoTestForRun:
+ # Pop the next test and create a start-test row for it.
+ oStartRow = RunTestStartRow(iLevel, aoTestForRun.pop(0), iRun);
+ self.aoRows.append(oStartRow);
+
+ # Pop matching tests from the other runs.
+ for iOtherRun in range(iRun + 1, len(aaoTestRuns)):
+ aoOtherTestRun = aaoTestRuns[iOtherRun];
+ for iTestToPop, oOtherTest in enumerate(aoOtherTestRun):
+ if oStartRow.isSameTest(oOtherTest):
+ oStartRow.aoTests.append(aoOtherTestRun.pop(iTestToPop));
+ break;
+ if len(oStartRow.aoTests) <= iOtherRun:
+ oStartRow.aoTests.append(None);
+
+ # Now recursively do the subtests for it and then do the values.
+ self.__populateFromRuns( [list(oTest.aoChildren) if oTest else list() for oTest in oStartRow.aoTests], iLevel+1);
+ fValueAnalysisRow = self.__populateFromValues([list(oTest.aoValues)
+ if oTest else list() for oTest in oStartRow.aoTests], iLevel+1);
+
+ # Add the end-test row for it.
+ self.aoRows.append(RunTestEndRow(oStartRow));
+ self.aoRows.append(RunTestEndRow2(oStartRow));
+ if fValueAnalysisRow:
+ self.aoRows.append(RunTestValueAnalysisRow(oStartRow));
+
+ return self;
+
+ def populateFromRuns(self, aoTestRuns, asRunNames = None): # type: ([reader.Test], [str]) -> RunTable
+ """
+ Populates the table from the series of runs.
+
+ The aoTestRuns and asRunNames run in parallel. If the latter isn't
+ given, the names will just be ordinals starting with #0 for the
+ first column.
+
+ Returns self.
+ """
+ #
+ # Deal with the column names first.
+ #
+ if asRunNames:
+ self.asColumns = list(asRunNames);
+ else:
+ self.asColumns = [];
+ iCol = len(self.asColumns);
+ while iCol < len(aoTestRuns):
+ self.asColumns.append('#%u%s' % (iCol, ' (baseline)' if iCol == self.iBaseline else '',));
+
+ self.aoRows = [
+ RunHeaderSeparatorRow(),
+ RunHeaderRow('Test / Value', self.asColumns),
+ RunHeaderSeparatorRow(),
+ ];
+
+ #
+ # Now flatten the test trees into a table.
+ #
+ self.__populateFromRuns([[oTestRun,] for oTestRun in aoTestRuns], 0);
+
+ #
+ # Add a footer if there are a lot of rows.
+ #
+ if len(self.aoRows) - 2 > 40:
+ self.aoRows.extend([RunFooterSeparatorRow(), RunFooterRow('', self.asColumns),]);
+
+ return self;
+
+ #
+ # Text formatting.
+ #
+
+ def formatAsText(self):
+ """
+ Formats the table as text.
+
+ Returns a string array of the output lines.
+ """
+
+ #
+ # Pass 1: Calculate column widths.
+ #
+ oWidths = TextWidths(1);
+ for oRow in self.aoRows:
+ oWidths.update(oRow.calcColumnWidthsForText(self));
+ oWidths.finalize();
+
+ #
+ # Pass 2: Generate the output strings.
+ #
+ asRet = [];
+ for oRow in self.aoRows:
+ if not oRow.fSkip:
+ asRet.append(oRow.renderAsText(oWidths, self));
+
+ return asRet;
+
diff --git a/src/VBox/ValidationKit/bootsectors/Config.kmk b/src/VBox/ValidationKit/bootsectors/Config.kmk
new file mode 100644
index 00000000..64a416e2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/Config.kmk
@@ -0,0 +1,951 @@
+# $Id: Config.kmk $
+## @file
+# kBuild Configuration file for VirtualBox Boot Sector Kit 3.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_BOOTSECTORS_CONFIG_KMK_INCLUDED = 1
+
+# Include the parent configure file.
+ifndef VBOX_VALIDATIONKIT_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/src/VBox/ValidationKit/Config.kmk
+endif
+
+# Add our 32-bit and 64-bit C properties.
+KBUILD_COMPILE_CATEGTORIES += C32 C64
+PROPS_TOOLS += C32TOOL C64TOOL
+PROPS_SINGLE += C32TOOL C64TOOL C32OBJSUFF C64OBJSUFF
+PROPS_ACCUMULATE_R += C32FLAGS C64FLAGS C32DEFS C64DEFS
+PROPS_ACCUMULATE_L += C32INCS C64INCS
+
+if 0 # Adding as few as possible new properties.
+ KBUILD_COMPILE_CATEGTORIES += C16
+ PROPS_TOOLS += C16TOOL
+ PROPS_SINGLE += C16TOOL C16OBJSUFF
+ PROPS_ACCUMULATE_R += C16FLAGS C16DEFS
+ PROPS_ACCUMULATE_L += C16INCS
+endif
+
+# Add noarch to the architectures list (will be there by default in a new kBuild).
+KBUILD_ARCHES += noarch
+
+
+# The bootsector directory.
+VBOX_PATH_BOOTSECTORS_SRC = $(VBOX_PATH_VALIDATIONKIT_SRC)/bootsectors
+
+# The bs3kit source directory.
+VBOX_PATH_BS3KIT_SRC = $(VBOX_PATH_BOOTSECTORS_SRC)/bs3kit
+
+
+# The 16-bit code & data segment classes.
+if 1
+ BS3KIT_CLASS_CODE16 = CODE
+ BS3KIT_SEGNM_DATA16 =
+ BS3KIT_CLASS_DATA16 = DATA
+ BS3KIT_GRPNM_DATA16 = DGROUP
+ BS3KIT_CLASS_BSS16 = BSS
+else
+ BS3KIT_CLASS_CODE16 = BS3CLASS16CODE
+ BS3KIT_SEGNM_DATA16 = BS3DATA16
+ BS3KIT_CLASS_DATA16 = FAR_DATA
+ BS3KIT_GRPNM_DATA16 = BS3DATA16_GROUP
+ BS3KIT_CLASS_BSS16 = ???
+endif
+
+
+##
+# Macro for generating near-call aliases for one 16-bit C function.
+# @param 1 The target name.
+# @param 2 The common function.
+BS3KIT_FN_GEN_CMN_NEARSTUB = $(evalcall2 def_Bs3KitGenNearStubSource,$1,_$2_c16,_$2_f16)
+
+##
+# Macro for generating near-call aliases for one 16-bit C mode function.
+# @param 1 The target name.
+# @param 2 The mode function.
+BS3KIT_FN_GEN_MODE_NEARSTUB = $(foreach suff, \
+ _rm \
+ _pe16 \
+ _pe16_v86 \
+ _pe32_16 \
+ _pev86 \
+ _pp16 \
+ _pp16_v86 \
+ _pp32_16 \
+ _ppv86 \
+ _pae16 \
+ _pae16_v86 \
+ _pae32_16 \
+ _paev86 \
+ _lm16 \
+ ,$(evalcall2 def_Bs3KitGenNearStubSource,$1,_$2$(suff),_$2$(suff)_far))
+
+# @param 1 The target name.
+# @param 2 The near function name.
+# @param 3 The far function name.
+define def_Bs3KitGenNearStubSource
+ $1_SOURCES += $$($1_0_OUTDIR)/stub$2.asm
+ $1_CLEAN += $$($1_0_OUTDIR)/stub$2.asm
+ $$$$($1_0_OUTDIR)/stub$2.asm: $$(VBOX_PATH_BOOTSECTORS_SRC)/Config.kmk | $$$$(dir $$$$@)
+ $(QUIET)$(APPEND) -tn $$@ \
+ '%include "bs3kit.mac"' \
+ 'BS3_BEGIN_TEXT16' \
+ ' extern $3' \
+ 'BS3_BEGIN_TEXT16_NEARSTUBS' \
+ 'BS3_GLOBAL_NAME_EX $2, function, 6' \
+ ' pop ax' \
+ ' push cs' \
+ ' push ax' \
+ ' jmp $3 wrt CGROUP16'
+endef
+
+
+##
+# Macro for generating far-call aliases for zero or more 16-bit C or assembly functions.
+# @param 1 The target name.
+# @param 2 The common function.
+# @param 3 The parameter size in bytes.
+BS3KIT_FN_GEN_CMN_FARSTUB = $(evalcall2 def_Bs3KitGenFarStubSource,$1,$2,_f16,_c16,$3)
+
+##
+# Macro for generating far-call aliases for zero or more 16-bit C mode functions.
+# @param 1 The target name.
+# @param 2 The mode function.
+# @param 3 The parameter size in bytes.
+BS3KIT_FN_GEN_MODE_FARSTUB = $(foreach suff, \
+ _rm \
+ _pe16 \
+ _pe16_v86 \
+ _pe32_16 \
+ _pev86 \
+ _pp16 \
+ _pp16_v86 \
+ _pp32_16 \
+ _ppv86 \
+ _pae16 \
+ _pae16_v86 \
+ _pae32_16 \
+ _paev86 \
+ _lm16 \
+ ,$(evalcall2 def_Bs3KitGenFarStubSource,$1,$2,$(suff)_far,$(suff),$3))
+
+# @param 1 The target name.
+# @param 2 The function name.
+# @param 3 The far function suffix.
+# @param 4 The near function suffix.
+# @param 5 The parameter size in bytes.
+define def_Bs3KitGenFarStubSource
+ $1_SOURCES += $$($1_0_OUTDIR)/stub_$2$3.asm
+ $1_CLEAN += $$($1_0_OUTDIR)/stub_$2$3.asm
+ $$$$($1_0_OUTDIR)/stub_$2$3.asm: $$(VBOX_PATH_BOOTSECTORS_SRC)/Config.kmk | $$$$(dir $$$$@)
+ $(QUIET)$(APPEND) -tn $$@ \
+ '%include "bs3kit.mac"' \
+ 'BS3_BEGIN_TEXT16' \
+ ' extern _$2$4' \
+ 'BS3_BEGIN_TEXT16_FARSTUBS' \
+ 'BS3_PROC_BEGIN _$2$3' \
+ ' CPU 8086' \
+ ' inc bp' \
+ ' push bp' \
+ ' mov bp, sp' \
+ '%assign offParam $5' \
+ '%rep $5 / 2' \
+ ' push word [bp + 2 + 4 + offParam - 2]' \
+ '%assign offParam offParam - 2' \
+ '%endrep' \
+ ' call _$2$4' \
+ ' add sp, $5' \
+ ' pop bp' \
+ ' dec bp' \
+ ' retf' \
+ 'BS3_PROC_END _$2$3' \
+ ''
+endef
+
+
+#
+# Tools Tools Tools
+# Tools Tools Tools
+# Tools Tools Tools
+#
+
+if defined(VBOX_USE_KSUBMIT) && "$(KBUILD_HOST)" == "win"
+ VBOX_BS3KIT_KSUBMIT_OBJ_CONV := kmk_builtin_kSubmit --
+else
+ VBOX_BS3KIT_KSUBMIT_OBJ_CONV :=
+endif
+
+# Dummy CP "linker" tool.
+TOOL_VBoxBsCpLd = Dummy copy linker.
+TOOL_VBoxBsCpLd_LINK_MISCBIN_OUTPUT =
+TOOL_VBoxBsCpLd_LINK_MISCBIN_DEPEND =
+TOOL_VBoxBsCpLd_LINK_MISCBIN_DEPORD =
+define TOOL_VBoxBsCpLd_LINK_MISCBIN_CMDS
+ $(CP) -- $(objs) $(othersrc) "$(out)"
+endef
+
+# Dummy exit 1 "linker" tool.
+TOOL_VBoxBsUnusedLd = Dummy unused linker.
+TOOL_VBoxBsUnusedLd_LINK_MISCBIN_OUTPUT =
+TOOL_VBoxBsUnusedLd_LINK_MISCBIN_DEPEND =
+TOOL_VBoxBsUnusedLd_LINK_MISCBIN_DEPORD =
+define TOOL_VBoxBsUnusedLd_LINK_MISCBIN_CMDS
+ echo "cannot use this template for linking"
+ exit 1
+endef
+
+# NASM tool with dependency workarounds (change dir to force consistent results; add -MP).
+# Requires http://permalink.gmane.org/gmane.comp.lang.nasm.devel/3704 to work.
+include $(KBUILD_PATH)/tools/NASM.kmk
+TOOL_VBoxNasm = Our version of the NASM tool
+ifndef TOOL_VBoxNasm_PATH
+ TOOL_VBoxNasm_PATH := $(firstword $(rsort $(wildcard $(KBUILD_DEVTOOLS_HST)/nasm/v*.*)))
+ if "$(TOOL_VBoxNasm_PATH)" == "" && "$(KBUILD_DEVTOOLS_HST_ALT)" != ""
+ TOOL_VBoxNasm_PATH := $(firstword $(rsort $(wildcard $(KBUILD_DEVTOOLS_HST_ALT)/nasm/v*.*)))
+ endif
+endif
+ifneq ($(TOOL_VBoxNasm_PATH),)
+ TOOL_VBoxNasm_AS ?= $(TOOL_VBoxNasm_PATH)/nasm$(HOSTSUFF_EXE)
+else
+ TOOL_VBoxNasm_AS ?= nasm$(HOSTSUFF_EXE)
+endif
+TOOL_VBoxNasm_ASFLAGS ?= $(TOOL_NASM_ASFLAGS)
+TOOL_VBoxNasm_COMPILE_AS_OUTPUT = $(outbase).lst
+TOOL_VBoxNasm_COMPILE_AS_OUTPUT_MAYBE = $(obj).original
+TOOL_VBoxNasm_COMPILE_AS_DEPEND = $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_VBoxNasm_COMPILE_AS_DEPORD =
+define TOOL_VBoxNasm_COMPILE_AS_CMDS
+ ifdef TOOL_VBoxNasm_USE_KSUBMIT
+ $(QUIET)kmk_builtin_kSubmit -C $(PATH_OUT_BASE) -- $(TOOL_VBoxNasm_AS)\
+ $(flags) $(addsuffix /,$(addprefix -i, $(incs))) $(addprefix -D, $(defs))\
+ -l $(outbase).lst\
+ -o $(obj)\
+ -MD "$(dep)" -MP\
+ $(abspath $(source))
+ else
+ $(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE) -- $(TOOL_VBoxNasm_AS)\
+ $(flags) $(addsuffix /,$(addprefix -i, $(incs))) $(addprefix -D, $(defs))\
+ -l $(outbase).lst\
+ -o $(obj)\
+ -MD "$(dep)" -MP\
+ $(abspath $(source))
+ endif
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+#
+# ELF 64-bit compiler tool with object conversion.
+#
+# Mac needs cross compiler: sudo port install x86_64-elf-gcc
+#
+TOOL_Bs3Gcc64Elf64 := AMD64/ELF64 gcc/g++ (cross) compiler.
+ifeq ($(KBUILD_HOST),darwin)
+ TOOL_Bs3Gcc64Elf64_CC ?= x86_64-elf-gcc$(HOSTSUFF_EXE) -m64
+ TOOL_Bs3Gcc64Elf64_CXX ?= x86_64-elf-g++$(HOSTSUFF_EXE) -m64
+else
+ TOOL_Bs3Gcc64Elf64_CC ?= gcc$(HOSTSUFF_EXE) -m64
+ TOOL_Bs3Gcc64Elf64_CXX ?= g++$(HOSTSUFF_EXE) -m64
+endif
+ifdef SLKRUNS
+ TOOL_Bs3Gcc64Elf64_CC += -fmessage-length=0
+ TOOL_Bs3Gcc64Elf64_CXX += -fmessage-length=0
+endif
+TOOL_Bs3Gcc64Elf64_COBJSUFF = .o64
+TOOL_Bs3Gcc64Elf64_CFLAGS = -fno-pie -x c $(VBOX_GCC_Wa_cma_nocompress_debug_sections) -ffreestanding
+TOOL_Bs3Gcc64Elf64_CFLAGS.debug = -g
+TOOL_Bs3Gcc64Elf64_CFLAGS.profile = -O2 #-g -pg
+TOOL_Bs3Gcc64Elf64_CFLAGS.release = -O2
+TOOL_Bs3Gcc64Elf64_CINCS =
+TOOL_Bs3Gcc64Elf64_CDEFS =
+TOOL_Bs3Gcc64Elf64_COMPILE_C_DEPEND = $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Gcc64Elf64_COMPILE_C_DEPORD =
+TOOL_Bs3Gcc64Elf64_COMPILE_C_OUTPUT =
+TOOL_Bs3Gcc64Elf64_COMPILE_C_OUTPUT_MAYBE = $(obj).original
+define TOOL_Bs3Gcc64Elf64_COMPILE_C_CMDS
+ $(QUIET)$(TOOL_Bs3Gcc64Elf64_CC) -c\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Wp,-MD,$(dep) -Wp,-MT,$(obj) -Wp,-MP\
+ -o $(obj)\
+ $(abspath $(source))
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+ $(QUIET)$(APPEND) -n "$(dep)" "" "$(source):" ""
+endef
+
+TOOL_Bs3Gcc64Elf64_C64OBJSUFF = $(TOOL_Bs3Gcc64Elf64_COBJSUFF)
+TOOL_Bs3Gcc64Elf64_C64FLAGS = $(TOOL_Bs3Gcc64Elf64_CFLAGS)
+TOOL_Bs3Gcc64Elf64_C64FLAGS.debug = $(TOOL_Bs3Gcc64Elf64_CFLAGS.debug)
+TOOL_Bs3Gcc64Elf64_C64FLAGS.profile = $(TOOL_Bs3Gcc64Elf64_CFLAGS.profile)
+TOOL_Bs3Gcc64Elf64_C64FLAGS.release = $(TOOL_Bs3Gcc64Elf64_CFLAGS.release)
+TOOL_Bs3Gcc64Elf64_C64INCS = $(TOOL_Bs3Gcc64Elf64_CINCS)
+TOOL_Bs3Gcc64Elf64_C64DEFS = $(TOOL_Bs3Gcc64Elf64_CDEFS)
+TOOL_Bs3Gcc64Elf64_COMPILE_C64_DEPEND = $(TOOL_Bs3Gcc64Elf64_COMPILE_C_DEPEND)
+TOOL_Bs3Gcc64Elf64_COMPILE_C64_DEPORD = $(TOOL_Bs3Gcc64Elf64_COMPILE_C_DEPORD)
+TOOL_Bs3Gcc64Elf64_COMPILE_C64_OUTPUT = $(TOOL_Bs3Gcc64Elf64_COMPILE_C_OUTPUT)
+TOOL_Bs3Gcc64Elf64_COMPILE_C64_OUTPUT_MAYBE = $(TOOL_Bs3Gcc64Elf64_COMPILE_C_OUTPUT_MAYBE)
+define TOOL_Bs3Gcc64Elf64_COMPILE_C64_CMDS
+$(TOOL_Bs3Gcc64Elf64_COMPILE_C_CMDS)
+endef
+
+TOOL_Bs3Gcc64Elf64_CXXOBJSUFF ?= .o
+TOOL_Bs3Gcc64Elf64_CXXFLAGS ?= -fno-pie $(VBOX_GCC_Wa_cma_nocompress_debug_sections) -ffreestanding
+TOOL_Bs3Gcc64Elf64_CXXFLAGS.debug ?= -g0 # no debug info, thank you
+TOOL_Bs3Gcc64Elf64_CXXFLAGS.profile ?= -O2 #-g -pg
+TOOL_Bs3Gcc64Elf64_CXXFLAGS.release ?= -O2
+TOOL_Bs3Gcc64Elf64_CXXINCS ?=
+TOOL_Bs3Gcc64Elf64_CXXDEFS ?=
+TOOL_Bs3Gcc64Elf64_COMPILE_CXX_DEPEND = $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Gcc64Elf64_COMPILE_CXX_DEPORD =
+TOOL_Bs3Gcc64Elf64_COMPILE_CXX_OUTPUT =
+TOOL_Bs3Gcc64Elf64_COMPILE_CXX_OUTPUT_MAYBE = $(obj).original
+define TOOL_Bs3Gcc64Elf64_COMPILE_CXX_CMDS
+ $(QUIET)$(TOOL_Bs3Gcc64Elf64_CXX) -c\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Wp,-MD,$(dep) -Wp,-MT,$(obj) -Wp,-MP\
+ -o $(obj)\
+ $(abspath $(source))
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+ $(QUIET)$(APPEND) -n "$(dep)" "" "$(source):" ""
+endef
+
+#
+# Visual C++ tool variant that runs the object converter afterwards.
+#
+TOOL_Bs3Vcc64 := Visual C++ 64-bit
+TOOL_Bs3Vcc64_CC = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_CC)
+TOOL_Bs3Vcc64_CXX = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_CXX)
+TOOL_Bs3Vcc64_COBJSUFF = .o64
+TOOL_Bs3Vcc64_CFLAGS = $(filter-out -TC -Zi,$(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_CFLAGS)) -TC -Z7
+TOOL_Bs3Vcc64_CFLAGS.debug =
+TOOL_Bs3Vcc64_CFLAGS.dbgopt = -O1
+TOOL_Bs3Vcc64_CFLAGS.profile = -O1
+TOOL_Bs3Vcc64_CFLAGS.release = -O1
+TOOL_Bs3Vcc64_CINCS = $(PATH_TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_INC)
+TOOL_Bs3Vcc64_CDEFS =
+TOOL_Bs3Vcc64_COMPILE_C_DEPEND = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_C_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Vcc64_COMPILE_C_DEPORD = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_C_DEPORD)
+TOOL_Bs3Vcc64_COMPILE_C_OUTPUT = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_C_OUTPUT)
+TOOL_Bs3Vcc64_COMPILE_C_OUTPUT_MAYBE = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_C_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Vcc64_COMPILE_C_CMDS
+$(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_C_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+TOOL_Bs3Vcc64_C64OBJSUFF = $(TOOL_Bs3Vcc64_COBJSUFF)
+TOOL_Bs3Vcc64_C64FLAGS = $(TOOL_Bs3Vcc64_CFLAGS)
+TOOL_Bs3Vcc64_C64FLAGS.debug = $(TOOL_Bs3Vcc64_CFLAGS.debug)
+TOOL_Bs3Vcc64_C64FLAGS.dbgopt = $(TOOL_Bs3Vcc64_CFLAGS.dbgopt)
+TOOL_Bs3Vcc64_C64FLAGS.profile = $(TOOL_Bs3Vcc64_CFLAGS.profile)
+TOOL_Bs3Vcc64_C64FLAGS.release = $(TOOL_Bs3Vcc64_CFLAGS.release)
+TOOL_Bs3Vcc64_C64INCS = $(TOOL_Bs3Vcc64_CINCS)
+TOOL_Bs3Vcc64_C64DEFS = $(TOOL_Bs3Vcc64_CDEFS)
+TOOL_Bs3Vcc64_COMPILE_C64_DEPEND = $(TOOL_Bs3Vcc64_COMPILE_C_DEPEND)
+TOOL_Bs3Vcc64_COMPILE_C64_DEPORD = $(TOOL_Bs3Vcc64_COMPILE_C_DEPORD)
+TOOL_Bs3Vcc64_COMPILE_C64_OUTPUT = $(TOOL_Bs3Vcc64_COMPILE_C_OUTPUT)
+TOOL_Bs3Vcc64_COMPILE_C64_OUTPUT_MAYBE = $(TOOL_Bs3Vcc64_COMPILE_C_OUTPUT_MAYBE)
+define TOOL_Bs3Vcc64_COMPILE_C64_CMDS
+$(TOOL_Bs3Vcc64_COMPILE_C_CMDS)
+endef
+
+TOOL_Bs3Vcc64_CXXOBJSUFF = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_CXXOBJSUFF)
+TOOL_Bs3Vcc64_CXXFLAGS = $(filter-out -Zi,$(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_CXXFLAGS)) -TP -Z7
+TOOL_Bs3Vcc64_CXXFLAGS.debug =
+TOOL_Bs3Vcc64_CXXFLAGS.dbgopt = -O1
+TOOL_Bs3Vcc64_CXXFLAGS.profile = -O1
+TOOL_Bs3Vcc64_CXXFLAGS.release = -O1
+TOOL_Bs3Vcc64_CXXINCS = $(PATH_TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_INC)
+TOOL_Bs3Vcc64_CXXDEFS =
+TOOL_Bs3Vcc64_COMPILE_CXX_DEPEND = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_CXX_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Vcc64_COMPILE_CXX_DEPORD = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_CXX_DEPORD)
+TOOL_Bs3Vcc64_COMPILE_CXX_OUTPUT = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_CXX_OUTPUT)
+TOOL_Bs3Vcc64_COMPILE_CXX_OUTPUT_MAYBE = $(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_CXX_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Vcc64_COMPILE_CXX_CMDS
+$(TOOL_$(VBOX_VCC_TOOL_STEM)AMD64_COMPILE_CXX_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+#
+# 32-bit OpenWatcom C/C++ tool variant that runs the object converter afterwards
+# to rename intrinsic functions so they don't clash with the 16-bit compiler.
+#
+TOOL_Bs3Ow32 := OpenWatcom C/C++ 32-bit with object convertsion
+TOOL_Bs3Ow32_CC = $(TOOL_OPENWATCOM_CC)
+TOOL_Bs3Ow32_CXX = $(TOOL_OPENWATCOM_CXX)
+TOOL_Bs3Ow32_COBJSUFF = .o32
+TOOL_Bs3Ow32_CFLAGS = $(TOOL_OPENWATCOM_CFLAGS)
+# -adfs \ - This is too complicated and it doesn't support stubbing files (svn rename fun.h pain.h). Use kDepObj instead.
+# -ad=$(call TOOL_OPENWATCOM_FIX_SLASHES,$(dep)) \
+# -adt=$(call TOOL_OPENWATCOM_FIX_SLASHES,$(obj)) \
+# -add=$(call TOOL_OPENWATCOM_FIX_SLASHES,$(abspath $(source))) \
+# -adhp=$(call TOOL_OPENWATCOM_FIX_SLASHES,$(dir $(abspath $(source))))
+TOOL_Bs3Ow32_CFLAGS.debug = $(TOOL_OPENWATCOM_CFLAGS.debug)
+TOOL_Bs3Ow32_CFLAGS.dbgopt = $(TOOL_OPENWATCOM_CFLAGS.dbgopt)
+TOOL_Bs3Ow32_CFLAGS.profile = $(TOOL_OPENWATCOM_CFLAGS.profile)
+TOOL_Bs3Ow32_CFLAGS.release = $(TOOL_OPENWATCOM_CFLAGS.release)
+TOOL_Bs3Ow32_CINCS = $(TOOL_OPENWATCOM_CINCS)
+TOOL_Bs3Ow32_CDEFS =
+TOOL_Bs3Ow32_COMPILE_C_DEPEND = $(TOOL_OPENWATCOM_COMPILE_C_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Ow32_COMPILE_C_DEPORD = $(TOOL_OPENWATCOM_COMPILE_C_DEPORD)
+TOOL_Bs3Ow32_COMPILE_C_OUTPUT = $(TOOL_OPENWATCOM_COMPILE_C_OUTPUT)
+TOOL_Bs3Ow32_COMPILE_C_OUTPUT_MAYBE = $(TOOL_OPENWATCOM_COMPILE_C_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Ow32_COMPILE_C_CMDS
+$(TOOL_OPENWATCOM_COMPILE_C_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+TOOL_Bs3Ow32_C32OBJSUFF = $(TOOL_Bs3Ow32_COBJSUFF)
+TOOL_Bs3Ow32_C32FLAGS = $(TOOL_Bs3Ow32_CFLAGS)
+TOOL_Bs3Ow32_C32FLAGS.debug = $(TOOL_Bs3Ow32_CFLAGS.debug)
+TOOL_Bs3Ow32_C32FLAGS.dbgopt = $(TOOL_Bs3Ow32_CFLAGS.dbgopt)
+TOOL_Bs3Ow32_C32FLAGS.profile = $(TOOL_Bs3Ow32_CFLAGS.profile)
+TOOL_Bs3Ow32_C32FLAGS.release = $(TOOL_Bs3Ow32_CFLAGS.release)
+TOOL_Bs3Ow32_C32INCS = $(TOOL_Bs3Ow32_CINCS)
+TOOL_Bs3Ow32_C32DEFS =
+TOOL_Bs3Ow32_COMPILE_C32_DEPEND = $(TOOL_Bs3Ow32_COMPILE_C_DEPEND)
+TOOL_Bs3Ow32_COMPILE_C32_DEPORD = $(TOOL_Bs3Ow32_COMPILE_C_DEPORD)
+TOOL_Bs3Ow32_COMPILE_C32_OUTPUT = $(TOOL_Bs3Ow32_COMPILE_C_OUTPUT)
+TOOL_Bs3Ow32_COMPILE_C32_OUTPUT_MAYBE = $(TOOL_Bs3Ow32_COMPILE_C_OUTPUT_MAYBE)
+define TOOL_Bs3Ow32_COMPILE_C32_CMDS
+$(TOOL_Bs3Ow32_COMPILE_C_CMDS)
+endef
+
+TOOL_Bs3Ow32_CXXOBJSUFF = $(TOOL_OPENWATCOM_CXXOBJSUFF)
+TOOL_Bs3Ow32_CXXFLAGS = $(TOOL_OPENWATCOM_CXXFLAGS) -ad=$(call TOOL_OPENWATCOM_FIX_SLASHES,$(dep)) -adfs
+TOOL_Bs3Ow32_CXXFLAGS.debug = $(TOOL_OPENWATCOM_CXXFLAGS.debug)
+TOOL_Bs3Ow32_CXXFLAGS.dbgopt = $(TOOL_OPENWATCOM_CXXFLAGS.dbgopt)
+TOOL_Bs3Ow32_CXXFLAGS.profile = $(TOOL_OPENWATCOM_CXXFLAGS.profile)
+TOOL_Bs3Ow32_CXXFLAGS.release = $(TOOL_OPENWATCOM_CXXFLAGS.release)
+TOOL_Bs3Ow32_CXXINCS = $(TOOL_OPENWATCOM_CXXINCS)
+TOOL_Bs3Ow32_CXXDEFS =
+TOOL_Bs3Ow32_COMPILE_CXX_DEPEND = $(TOOL_OPENWATCOM_COMPILE_CXX_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Ow32_COMPILE_CXX_DEPORD = $(TOOL_OPENWATCOM_COMPILE_CXX_DEPORD)
+TOOL_Bs3Ow32_COMPILE_CXX_OUTPUT = $(TOOL_OPENWATCOM_COMPILE_CXX_OUTPUT)
+TOOL_Bs3Ow32_COMPILE_CXX_OUTPUT_MAYBE = $(TOOL_OPENWATCOM_COMPILE_CXX_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Ow32_COMPILE_CXX_CMDS
+$(TOOL_OPENWATCOM_COMPILE_CXX_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+
+#
+# 16-bit OpenWatcom C/C++ tool variant that runs the object converter afterwards
+# to rename intrinsic functions so they don't clash with the 16-bit compiler.
+#
+TOOL_Bs3Ow16 := OpenWatcom C/C++ 16-bit with object convertsion
+TOOL_Bs3Ow16_CC = $(TOOL_OPENWATCOM-16_CC)
+TOOL_Bs3Ow16_CXX = $(TOOL_OPENWATCOM-16_CXX)
+TOOL_Bs3Ow16_COBJSUFF = .o16
+TOOL_Bs3Ow16_CFLAGS = $(TOOL_OPENWATCOM-16_CFLAGS)
+TOOL_Bs3Ow16_CFLAGS.debug = $(TOOL_OPENWATCOM-16_CFLAGS.debug)
+TOOL_Bs3Ow16_CFLAGS.dbgopt = $(TOOL_OPENWATCOM-16_CFLAGS.dbgopt)
+TOOL_Bs3Ow16_CFLAGS.profile = $(TOOL_OPENWATCOM-16_CFLAGS.profile)
+TOOL_Bs3Ow16_CFLAGS.release = $(TOOL_OPENWATCOM-16_CFLAGS.release)
+TOOL_Bs3Ow16_CINCS = $(TOOL_OPENWATCOM-16_CINCS)
+TOOL_Bs3Ow16_CDEFS =
+TOOL_Bs3Ow16_COMPILE_C_DEPEND = $(TOOL_OPENWATCOM-16_COMPILE_C_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Ow16_COMPILE_C_DEPORD = $(TOOL_OPENWATCOM-16_COMPILE_C_DEPORD)
+TOOL_Bs3Ow16_COMPILE_C_OUTPUT = $(TOOL_OPENWATCOM-16_COMPILE_C_OUTPUT)
+TOOL_Bs3Ow16_COMPILE_C_OUTPUT_MAYBE = $(TOOL_OPENWATCOM-16_COMPILE_C_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Ow16_COMPILE_C_CMDS
+$(TOOL_OPENWATCOM-16_COMPILE_C_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+TOOL_Bs3Ow16_C16OBJSUFF = $(TOOL_Bs3Ow16_C16OBJSUFF)
+TOOL_Bs3Ow16_C16FLAGS = $(TOOL_Bs3Ow16_C16FLAGS)
+TOOL_Bs3Ow16_C16FLAGS.debug = $(TOOL_Bs3Ow16_C16FLAGS.debug)
+TOOL_Bs3Ow16_C16FLAGS.dbgopt = $(TOOL_Bs3Ow16_C16FLAGS.dbgopt)
+TOOL_Bs3Ow16_C16FLAGS.profile = $(TOOL_Bs3Ow16_C16FLAGS.profile)
+TOOL_Bs3Ow16_C16FLAGS.release = $(TOOL_Bs3Ow16_C16FLAGS.release)
+TOOL_Bs3Ow16_C16INCS = $(TOOL_Bs3Ow16_C16INCS)
+TOOL_Bs3Ow16_C16DEFS = $(TOOL_Bs3Ow16_C16DEFS)
+TOOL_Bs3Ow16_COMPILE_C16_DEPEND = $(TOOL_Bs3Ow16_COMPILE_C16_DEPEND)
+TOOL_Bs3Ow16_COMPILE_C16_DEPORD = $(TOOL_Bs3Ow16_COMPILE_C16_DEPORD)
+TOOL_Bs3Ow16_COMPILE_C16_OUTPUT = $(TOOL_Bs3Ow16_COMPILE_C16_OUTPUT)
+TOOL_Bs3Ow16_COMPILE_C16_OUTPUT_MAYBE = $(TOOL_Bs3Ow16_COMPILE_C16_OUTPUT_MAYBE)
+define TOOL_Bs3Ow16_COMPILE_C16_CMDS
+$(TOOL_Bs3Ow16_COMPILE_C_CMDS)
+endef
+
+TOOL_Bs3Ow16_CXXOBJSUFF = $(TOOL_OPENWATCOM-16_CXXOBJSUFF)
+TOOL_Bs3Ow16_CXXFLAGS = $(TOOL_OPENWATCOM-16_CXXFLAGS)
+TOOL_Bs3Ow16_CXXFLAGS.debug = $(TOOL_OPENWATCOM-16_CXXFLAGS.debug)
+TOOL_Bs3Ow16_CXXFLAGS.dbgopt = $(TOOL_OPENWATCOM-16_CXXFLAGS.dbgopt)
+TOOL_Bs3Ow16_CXXFLAGS.profile = $(TOOL_OPENWATCOM-16_CXXFLAGS.profile)
+TOOL_Bs3Ow16_CXXFLAGS.release = $(TOOL_OPENWATCOM-16_CXXFLAGS.release)
+TOOL_Bs3Ow16_CXXINCS = $(TOOL_OPENWATCOM-16_CXXINCS)
+TOOL_Bs3Ow16_CXXDEFS =
+TOOL_Bs3Ow16_COMPILE_CXX_DEPEND = $(TOOL_OPENWATCOM-16_COMPILE_CXX_DEPEND) $(VBoxBs3ObjConverter_1_TARGET)
+TOOL_Bs3Ow16_COMPILE_CXX_DEPORD = $(TOOL_OPENWATCOM-16_COMPILE_CXX_DEPORD)
+TOOL_Bs3Ow16_COMPILE_CXX_OUTPUT = $(TOOL_OPENWATCOM-16_COMPILE_CXX_OUTPUT)
+TOOL_Bs3Ow16_COMPILE_CXX_OUTPUT_MAYBE = $(TOOL_OPENWATCOM-16_COMPILE_CXX_OUTPUT_MAYBE) $(obj).original
+define TOOL_Bs3Ow16_COMPILE_CXX_CMDS
+$(TOOL_OPENWATCOM-16_COMPILE_CXX_CMDS)
+ $(QUIET)$(VBOX_BS3KIT_KSUBMIT_OBJ_CONV) $(VBoxBs3ObjConverter_1_TARGET) "$(obj)"
+endef
+
+# Debug info format depends on what we use for 64-bit.
+if 1 #1of ($(KBUILD_HOST), win) - wlink dwarf .sym files are useless for binary blobs
+ BS3_OW_DBG_OPT = -hc -d1+
+ #BS3_OW_DBG_OPT = -hd -d1+
+ BS3_OW_DBG_LDOPT = codeview
+else
+ BS3_OW_DBG_OPT = -hd -d1+
+ BS3_OW_DBG_LDOPT = dwarf
+endif
+
+#
+# Source handlers for .c16, .c32 and .c64
+#
+define VBoxBs3KitImgSrcHandler_16bit_c
+ local type := C
+ $(kb-src-one 2)
+endef
+
+C32TOOL = Bs3Ow32
+define VBoxBs3KitImgSrcHandler_32bit_c
+ local type := C32
+ $(kb-src-one 2)
+endef
+
+define VBoxBs3KitImgSrcHandler_64bit_c
+ local type := C64
+ $(kb-src-one 2)
+endef
+
+
+#
+# BS3Kit template for assembly and 16-bit code.
+#
+# Note! Using -d1 as -d1+ and -d2 causes suboptimal code to be generated (strlen
+# reloading string pointer argument all the time).
+# Update! -d1+ is required for line number information in code living in include
+# files and any DWARF stuff at all. So, we'll ignore poor code quality.
+# Note! Optimization options should come after debug stuff as -d2 for instance
+# disables all optimziations.
+# Note! We use BS3CLASS16CODE because of wdis code detection heuristics requires the class
+# of a code segment to be exactly 'CODE', or ending with 'CODE' or 'TEXT' (more
+# recent wdis have a -c=<clsnm> option, but not the one we currently use ).
+#
+#
+# Compiler options explained:
+# -nt=xxxx Sets the text segment name.
+# -nc=xxxx Sets the text segment class name.
+# -nd=xxxx Sets the data segment name.
+# -ecc Sets the default calling convension to __cdecl
+# Update: We don't use this in 16-bit code as it causes unfavorable reloading of DS before calling
+# inlined functions (e.g. iprt/asm.h). Instead we use -ecw and __cdecl where needed.
+# Update: With -zdp the DS reloading is gone. Code is slightly larger, but seems to cure stability
+# issues in bs3CpuBasic2_RaiseXcpt1 (workers ending up with default calling convention).
+# -ecw Sets the default calling convension to __watcall ()
+# -q Quiet, no logos or stuff.
+# -0 Use 8086 instruction set (16-bit only).
+# -3 Use 386 instruction set (16-bit only).
+# -e<num> Stop after <num> errors.
+# -wx Maxium warning level.
+# -zl Don't emit default library information.
+# -zdp DS pegged to BS3DATA16_GROUP/DGROUP.
+# -zu Assume SS != DS.
+# -mc Compact memory model, far data, small code.
+# -ml Large memory model, far data, far code.
+# -mf Flat memory model (32-bit).
+# -d+ Enabled better /dVAR=XXX parsing, using space as delimiter instead of alpha-numerical/whatever.
+# -d1 Debug info: Globals and line numbers.
+# -s No stack overflow checks.
+# -oa Relaxed aliasing constraints.
+# -ob Branch prediction.
+# -of Generate stack frames when needed.
+# -oi Inline instrinsics functions.
+# -ol Loop optimizations.
+# -oh Expensive optimizations. (saves a byte or two)
+# -or Reorder for best pipeline.
+# -os Favor size over speed.
+#
+TEMPLATE_VBoxBS3KitImg = Template for building BS3Kit test images.
+TEMPLATE_VBoxBS3KitImg_BLD_TRG = os-agnostic
+TEMPLATE_VBoxBS3KitImg_BLD_TRG_ARCH = noarch
+TEMPLATE_VBoxBS3KitImg_INST = $(INST_VALIDATIONKIT)bootsectors/
+TEMPLATE_VBoxBS3KitImg_BINSUFF = .img
+TEMPLATE_VBoxBS3KitImg_MODE = 0644
+TEMPLATE_VBoxBS3KitImg_SRC_HANDLERS = \
+ .c16:VBoxBs3KitImgSrcHandler_16bit_c \
+ .c32:VBoxBs3KitImgSrcHandler_32bit_c \
+ .c64:VBoxBs3KitImgSrcHandler_64bit_c
+TEMPLATE_VBoxBS3KitImg_ASOBJSUFF = .o16
+TEMPLATE_VBoxBS3KitImg_ASTOOL = VBoxNasm
+TEMPLATE_VBoxBS3KitImg_ASFLAGS = -f obj -g $(BS3KIT_NASM_allow_64_bit) -w+orphan-labels
+TEMPLATE_VBoxBS3KitImg_ASDEFS = ASM_FORMAT_OMF RT_NOINC_SEGMENTS __NASM__ ARCH_BITS=16 RT_ARCH_X86 ASM_MODEL_FAR_CODE \
+ BS3CLASS16CODE=$(BS3KIT_CLASS_CODE16) BS3KIT_CLASS_DATA16=$(BS3KIT_CLASS_DATA16) \
+ BS3KIT_GRPNM_DATA16=$(BS3KIT_GRPNM_DATA16) BS3KIT_CLASS_BSS16=$(BS3KIT_CLASS_BSS16)
+TEMPLATE_VBoxBS3KitImg_DEFS = IN_BS3KIT
+TEMPLATE_VBoxBS3KitImg_DEFS.debug = BS3_STRICT
+
+TEMPLATE_VBoxBS3KitImg_ARTOOL = OPENWATCOM-16
+
+TEMPLATE_VBoxBS3KitImg_CTOOL = Bs3Ow16
+TEMPLATE_VBoxBS3KitImg_CXXTOOL = Bs3Ow16
+TEMPLATE_VBoxBS3KitImg_CFLAGS = $(if $(BS3KIT_SEGNM_DATA16),-nd=$(BS3KIT_SEGNM_DATA16),) \
+ -nt=BS3TEXT16 -nc=$(BS3KIT_CLASS_CODE16) -ecc -q -0 -e125 -wx -zl -zdp -zu -ml $(BS3_OW_DBG_OPT) -s -oa -ob -of -oi -ol -or -os -oh -d+
+TEMPLATE_VBoxBS3KitImg_CXXFLAGS = $(if $(BS3KIT_SEGNM_DATA16),-nd=$(BS3KIT_SEGNM_DATA16),) \
+ -nt=BS3TEXT16 -nc=$(BS3KIT_CLASS_CODE16) -ecc -q -0 -e125 -wx -zl -zdp -zu -ml $(BS3_OW_DBG_OPT) -s -oa -ob -of -oi -ol -or -os -oh -d+
+TEMPLATE_VBoxBS3KitImg_CDEFS = ARCH_BITS=16 RT_ARCH_X86
+
+TEMPLATE_VBoxBS3KitImg_TOOL = $(NO_SUCH_VARIABLE)
+TEMPLATE_VBoxBS3KitImg_C16TOOL = $(TEMPLATE_VBoxBS3KitImg_CTOOL)
+TEMPLATE_VBoxBS3KitImg_C16FLAGS = $(TEMPLATE_VBoxBS3KitImg_CFLAGS)
+TEMPLATE_VBoxBS3KitImg_C16DEFS = $(TEMPLATE_VBoxBS3KitImg_CDEFS)
+TEMPLATE_VBoxBS3KitImg_C32TOOL := Bs3Ow32
+TEMPLATE_VBoxBS3KitImg_C32FLAGS = $(TEMPLATE_VBoxBS3KitImg32_CFLAGS)
+TEMPLATE_VBoxBS3KitImg_C32DEFS = ARCH_BITS=32 RT_ARCH_X86
+TEMPLATE_VBoxBS3KitImg_C64TOOL = $(TEMPLATE_VBoxBS3KitImg64_CTOOL)
+TEMPLATE_VBoxBS3KitImg_C64FLAGS = $(TEMPLATE_VBoxBS3KitImg64_CFLAGS)
+TEMPLATE_VBoxBS3KitImg_C64DEFS = ARCH_BITS=64 RT_ARCH_AMD64
+
+TEMPLATE_VBoxBS3KitImg_INCS = $(VBOX_PATH_BS3KIT_SRC) .
+TEMPLATE_VBoxBS3KitImg_LDTOOL = OPENWATCOM-WL
+
+# linker options:
+# system dos: Link a 16-bit DOS binary.
+# output raw ...: Produce a raw DOS binary for loading at flat address 10000h.
+# The following is for ordering segments.
+# option start=_start: The start symbol in bs3-first-xxx.asm.
+# debug codeview/dwarf all: Full debug information either in codeview or dwarf.
+# option symfile: Produce a separate symbol file with the debug info.
+# option map: Produce a map file.
+# option farcalls: Change intrasegment far calls into 'push cs; seg ds; call symbol' where possible.
+# option statics: ?
+# option verbose: Verbose map file?
+# option disable 1014: Disable warning about 'stack segment not found'.
+# option disable 1080: Disable warning about '%1 is a 32-bit object file'.
+#
+# Note! We're pushing DATA16 to 0x20000 because it's impossible to force wlink
+# to give us a real-mode + GDT compatible alignment (0ffffff80h), i.e.
+# real-mode address on the form 0fff8:0000.
+TEMPLATE_VBoxBS3KitImg_LDFLAGS = system dos \
+ debug $(BS3_OW_DBG_LDOPT) all \
+ option quiet, map, statics, verbose, symfile, start=_start, farcalls \
+ disable 1014, 1080 \
+ \
+ output raw offset=0x10000 \
+ order \
+ clname BS3FLAT segaddr=0x0000 \
+ segment BS3FLAT segaddr=0x0000 \
+ clname $(BS3KIT_CLASS_CODE16) segaddr=0x1000 \
+ segment BS3TEXT16 \
+ segment BS3TEXT16_NEARSTUBS \
+ segment BS3TEXT16_FARSTUBS \
+ segment BS3TEXT16_END \
+ clname BS3SYSTEM16 segaddr=0x2000 \
+ segment BS3SYSTEM16 \
+ $(if-expr "$(BS3KIT_SEGNM_DATA16)" == "", \
+ clname DATA \
+ segment BS3DATA16 segaddr=0x2900 \
+ segment BS3DATA16_DATA \
+ segment DATA \
+ segment _DATA \
+ segment BS3DATA16CONST \
+ segment CONST \
+ segment BS3DATA16CONST2 \
+ segment CONST2 \
+ segment STRINGS \
+ segment BS3DATA16_END \
+ clname BSS \
+ segment BSS \
+ segment _BSS \
+ segment BS3DATA16_END \
+ clname FAR_DATA \
+ segment FAR_DATA \
+ , \
+ clname FAR_DATA \
+ segment BS3DATA16 segaddr=0x2900 \
+ segment FAR_DATA \
+ segment BS3DATA16CONST \
+ segment BS3DATA16CONST2 \
+ segment BS3DATA16_DATA \
+ segment BS3DATA16_END \
+ ) \
+ segment BS3DATA32 \
+ segment BS3DATA32CONST \
+ segment BS3DATA32CONST2 \
+ segment BS3DATA32_DATA \
+ segment BS3DATA32_BSS \
+ segment BS3DATA32_END \
+ \
+ segment BS3DATA64 \
+ segment BS3DATA64CONST \
+ segment BS3DATA64_BSS \
+ segment BS3DATA64_END \
+ clname BS3CLASS16RMCODE \
+ segment BS3RMCODE16_START \
+ segment BS3RMCODE16 \
+ segment BS3RMCODE16_END \
+ clname BS3CLASS16X0CODE \
+ segment BS3X0CODE16_START \
+ segment BS3X0CODE16 \
+ segment BS3X0CODE16_END \
+ clname BS3CLASS16X1CODE \
+ segment BS3X1CODE16_START \
+ segment BS3X1CODE16 \
+ segment BS3X1CODE16_END \
+ clname BS3CLASS32CODE \
+ segment BS3TEXT32_START \
+ segment BS3TEXT32 \
+ segment BS3TEXT32_END \
+ clname BS3CLASSSEPARATE32AND64BITCODE \
+ segment BS3SEPARATE32AND64BITCODE \
+ segment BS3SEPARATE32AND64BITCODE_END \
+ clname BS3CLASS64CODE \
+ segment BS3TEXT64_START \
+ segment BS3TEXT64 \
+ segment BS3TEXT64_END
+
+TEMPLATE_VBoxBS3KitImg_LNK_DEPS = \
+ $(bs3-bootsector_1_TARGET) \
+ $(VBoxBs3Linker_1_TARGET)
+TEMPLATE_VBoxBS3KitImg_POST_CMDS = $(if $(eq $(tool_do),LINK_LIBRARY) \
+ ,,$(QUIET)$(MV_EXT) -f -- "$(out)" "$(out).tmp" \
+ $$(NLTAB)$(QUIET)$(VBoxBs3Linker_1_TARGET) -o $(out) $(bs3-bootsector_1_TARGET) $(out).tmp \
+ $$(NLTAB)$(QUIET)$(RM_EXT) -f -- "$(out).tmp") \
+ $(eval .PRECIOUS: $(outbase).map) # ugly hack!
+
+
+TEMPLATE_VBoxBS3KitImg_LIBS = \
+ $(PATH_OBJ)/bs3kit-common-16/bs3kit-common-16.lib \
+ $(PATH_OBJ)/bs3kit-common-32/bs3kit-common-32.lib \
+ $(PATH_OBJ)/bs3kit-common-64/bs3kit-common-64.lib \
+ \
+ $(PATH_OBJ)/bs3kit-rm/bs3kit-rm.lib \
+ $(PATH_OBJ)/bs3kit-pe16/bs3kit-pe16.lib \
+ $(PATH_OBJ)/bs3kit-pe16_32/bs3kit-pe16_32.lib \
+ $(PATH_OBJ)/bs3kit-pe16_v86/bs3kit-pe16_v86.lib \
+ $(PATH_OBJ)/bs3kit-pe32/bs3kit-pe32.lib \
+ $(PATH_OBJ)/bs3kit-pe32_16/bs3kit-pe32_16.lib \
+ $(PATH_OBJ)/bs3kit-pev86/bs3kit-pev86.lib \
+ $(PATH_OBJ)/bs3kit-pp16/bs3kit-pp16.lib \
+ $(PATH_OBJ)/bs3kit-pp16_32/bs3kit-pp16_32.lib \
+ $(PATH_OBJ)/bs3kit-pp16_v86/bs3kit-pp16_v86.lib \
+ $(PATH_OBJ)/bs3kit-pp32/bs3kit-pp32.lib \
+ $(PATH_OBJ)/bs3kit-pp32_16/bs3kit-pp32_16.lib \
+ $(PATH_OBJ)/bs3kit-ppv86/bs3kit-ppv86.lib \
+ $(PATH_OBJ)/bs3kit-pae16/bs3kit-pae16.lib \
+ $(PATH_OBJ)/bs3kit-pae16_32/bs3kit-pae16_32.lib \
+ $(PATH_OBJ)/bs3kit-pae16_v86/bs3kit-pae16_v86.lib \
+ $(PATH_OBJ)/bs3kit-pae32/bs3kit-pae32.lib \
+ $(PATH_OBJ)/bs3kit-pae32_16/bs3kit-pae32_16.lib \
+ $(PATH_OBJ)/bs3kit-paev86/bs3kit-paev86.lib \
+ $(PATH_OBJ)/bs3kit-lm16/bs3kit-lm16.lib \
+ $(PATH_OBJ)/bs3kit-lm32/bs3kit-lm32.lib \
+ $(PATH_OBJ)/bs3kit-lm64/bs3kit-lm64.lib
+
+# BS3Kit template for 32-bit code.
+TEMPLATE_VBoxBS3KitImg32 = Template for building BS3Kit test images.
+TEMPLATE_VBoxBS3KitImg32_BLD_TRG = os-agnostic
+TEMPLATE_VBoxBS3KitImg32_BLD_TRG_ARCH = x86
+TEMPLATE_VBoxBS3KitImg32_INSTTYPE = none
+TEMPLATE_VBoxBS3KitImg32_ASTOOL = VBoxNasm
+TEMPLATE_VBoxBS3KitImg32_ASOBJSUFF = .o32
+TEMPLATE_VBoxBS3KitImg32_ASFLAGS = -f obj -g $(BS3KIT_NASM_allow_64_bit) -w+orphan-labels
+TEMPLATE_VBoxBS3KitImg32_ASDEFS = ASM_FORMAT_OMF RT_NOINC_SEGMENTS __NASM__ \
+ BS3CLASS16CODE=$(BS3KIT_CLASS_CODE16) BS3KIT_CLASS_DATA16=$(BS3KIT_CLASS_DATA16) \
+ BS3KIT_GRPNM_DATA16=$(BS3KIT_GRPNM_DATA16) BS3KIT_CLASS_BSS16=$(BS3KIT_CLASS_BSS16)
+TEMPLATE_VBoxBS3KitImg32_DEFS = ARCH_BITS=32 IN_BS3KIT
+TEMPLATE_VBoxBS3KitImg32_DEFS.debug = BS3_STRICT
+TEMPLATE_VBoxBS3KitImg32_ARTOOL = OPENWATCOM
+TEMPLATE_VBoxBS3KitImg32_CTOOL = Bs3Ow32
+TEMPLATE_VBoxBS3KitImg32_CXXTOOL = Bs3Ow32
+TEMPLATE_VBoxBS3KitImg32_CFLAGS = \
+ -nt=BS3TEXT32 -nd=BS3DATA32 -nc=BS3CLASS32CODE -ecc -q -e125 -wx -zl -mf $(BS3_OW_DBG_OPT) -s -oa -ob -of -oi -ol -or -os -d+
+TEMPLATE_VBoxBS3KitImg32_CXXFLAGS = \
+ -nt=BS3TEXT32 -nd=BS3DATA32 -nc=BS3CLASS32CODE -ecc -q -e125 -wx -zl -mf $(BS3_OW_DBG_OPT) -s -oa -ob -of -oi -ol -or -os -d+
+TEMPLATE_VBoxBS3KitImg32_INCS = $(VBOX_PATH_BS3KIT_SRC) .
+TEMPLATE_VBoxBS3KitImg32_LDTOOL = VBoxBsUnusedLd
+
+# BS3Kit template for 64-bit code.
+TEMPLATE_VBoxBS3KitImg64 = Template for building BS3Kit test images.
+TEMPLATE_VBoxBS3KitImg64_BLD_TRG = os-agnostic
+TEMPLATE_VBoxBS3KitImg64_BLD_TRG_ARCH = amd64
+TEMPLATE_VBoxBS3KitImg64_INSTTYPE = none
+TEMPLATE_VBoxBS3KitImg64_ASTOOL = VBoxNasm
+TEMPLATE_VBoxBS3KitImg64_ASOBJSUFF = .o64
+TEMPLATE_VBoxBS3KitImg64_ASFLAGS = -f obj -g $(BS3KIT_NASM_allow_64_bit) -w+orphan-labels
+TEMPLATE_VBoxBS3KitImg64_ASDEFS = ASM_FORMAT_OMF ASM_CALL64_MSC RT_NOINC_SEGMENTS __NASM__ \
+ BS3CLASS16CODE=$(BS3KIT_CLASS_CODE16) BS3KIT_CLASS_DATA16=$(BS3KIT_CLASS_DATA16) \
+ BS3KIT_GRPNM_DATA16=$(BS3KIT_GRPNM_DATA16) BS3KIT_CLASS_BSS16=$(BS3KIT_CLASS_BSS16)
+TEMPLATE_VBoxBS3KitImg64_DEFS = IN_BS3KIT ARCH_BITS=64
+TEMPLATE_VBoxBS3KitImg64_DEFS.debug = BS3_STRICT
+TEMPLATE_VBoxBS3KitImg64_ARTOOL = OPENWATCOM
+TEMPLATE_VBoxBS3KitImg64_INCS = $(VBOX_PATH_BS3KIT_SRC) .
+if1of ($(KBUILD_HOST), win)
+ ifndef TOOL_VCC100AMD64 # For win.x86 builds.
+ include $(KBUILD_PATH)/tools/$(VBOX_VCC_TOOL_STEM)AMD64.kmk
+ endif
+ TEMPLATE_VBoxBS3KitImg64_CTOOL := Bs3Vcc64
+ TEMPLATE_VBoxBS3KitImg64_CXXTOOL := Bs3Vcc64
+ TEMPLATE_VBoxBS3KitImg64_CFLAGS = -Z7 -O1 -Oi -GF -GS- -Gy- -Gs65536
+ TEMPLATE_VBoxBS3KitImg64_CXXFLAGS = -Z7 -O1 -Oi -GF -GS- -Gy- -Gs65536
+else
+ TEMPLATE_VBoxBS3KitImg64_CTOOL := Bs3Gcc64Elf64
+ TEMPLATE_VBoxBS3KitImg64_CXXTOOL := Bs3Gcc64Elf64
+ # Note! -mx32 would be exactly what we needed here, however it causes internal compiler errors with 4.8.4 on gentoo.
+ TEMPLATE_VBoxBS3KitImg64_CFLAGS = -m64 -maccumulate-outgoing-args -g -Os -fno-omit-frame-pointer $(VBOX_GCC_fno-stack-protector) $(VBOX_GCC_WARN_PEDANTIC_C) \
+ -msoft-float -fno-exceptions -mno-sse -mno-mmx -mno-sse2 -mno-3dnow $(VBOX_GCC_fno-stack-protector)
+ TEMPLATE_VBoxBS3KitImg64_CXXFLAGS = -m64 -maccumulate-outgoing-args -g -Os -fno-omit-frame-pointer $(VBOX_GCC_fno-stack-protector) $(VBOX_GCC_WARN_PEDANTIC_CXX) \
+ -msoft-float -fno-exceptions -mno-sse -mno-mmx -mno-sse2 -mno-3dnow $(VBOX_GCC_fno-stack-protector)
+endif
+TEMPLATE_VBoxBS3KitImg64_LDTOOL = VBoxBsUnusedLd
+
+# BS3Kit template for the bootsector.
+TEMPLATE_VBoxBS3KitBS = Template for building BS3Kit test images.
+TEMPLATE_VBoxBS3KitBS_BLD_TRG = os-agnostic
+TEMPLATE_VBoxBS3KitBS_BLD_TRG_ARCH = x86
+TEMPLATE_VBoxBS3KitBS_INST = $(INST_VALIDATIONKIT)bootsectors/
+TEMPLATE_VBoxBS3KitBS_INSTTYPE = none
+TEMPLATE_VBoxBS3KitBS_BINSUFF = .img
+TEMPLATE_VBoxBS3KitBS_MODE = 0644
+TEMPLATE_VBoxBS3KitBS_ASTOOL = YASM
+TEMPLATE_VBoxBS3KitBS_ASFLAGS = -f bin --mapfile
+TEMPLATE_VBoxBS3KitBS_ASDEFS = ASM_FORMAT_BIN RT_NOINC_SEGMENTS ARCH_BITS=16 __YASM__ \
+ BS3CLASS16CODE=$(BS3KIT_CLASS_CODE16) BS3KIT_CLASS_DATA16=$(BS3KIT_CLASS_DATA16) \
+ BS3KIT_GRPNM_DATA16=$(BS3KIT_GRPNM_DATA16) BS3KIT_CLASS_BSS16=$(BS3KIT_CLASS_BSS16)
+TEMPLATE_VBoxBS3KitBS_INCS = $(VBOX_PATH_BS3KIT_SRC) .
+TEMPLATE_VBoxBS3KitBS_LDTOOL = VBoxBsCpLd
+
+
+
+#
+# Extends VBoxBS3KitImg
+# User must starts SOURCES with: $(VBOX_PATH_BS3KIT_SRC)/bs3-first-dosexe.asm
+## disable 1014, 1080, 1150
+#
+TEMPLATE_VBoxBS3KitUtil = Utility using bs3kit code.
+TEMPLATE_VBoxBS3KitUtil_EXTENDS = VBoxBS3KitImg
+TEMPLATE_VBoxBS3KitUtil_BINSUFF = .exe
+TEMPLATE_VBoxBS3KitUtil_DEFS = $(TEMPLATE_VBoxBS3KitImg_DEFS) BS3_IS_DOS_EXE
+TEMPLATE_VBoxBS3KitUtil_CFLAGS = $(filter-out -zl,$(TEMPLATE_VBoxBS3KitImg_CFLAGS))
+TEMPLATE_VBoxBS3KitUtil_CXXFLAGS = $(filter-out -zl,$(TEMPLATE_VBoxBS3KitImg_CXXFLAGS))
+TEMPLATE_VBoxBS3KitUtil_LDFLAGS = system dos \
+ debug $(BS3_OW_DBG_LDOPT) all \
+ option quiet, map, statics, verbose, symfile \
+ disable 1080 \
+ order \
+ clname $(BS3KIT_CLASS_CODE16) \
+ segment BEGTEXT \
+ segment BS3TEXT16 \
+ segment _TEXT \
+ segment BS3TEXT16_NEARSTUBS \
+ segment BS3TEXT16_FARSTUBS \
+ segment BS3TEXT16_END \
+ clname BS3SYSTEM16 \
+ segment BS3SYSTEM16 \
+ \
+ clname BEGDATA \
+ segment _NULL \
+ segment _AFTERNULL \
+ clname DATA \
+ $(if-expr "$(BS3KIT_SEGNM_DATA16)" == "", \
+ segment BS3DATA16 \
+ segment BS3DATA16CONST \
+ segment CONST \
+ segment BS3DATA16CONST2 \
+ segment CONST2 \
+ , \
+ segment CONST \
+ segment CONST2 \
+ ) \
+ segment _DATA \
+ segment XIB \
+ segment XI \
+ segment XIE \
+ segment YIB \
+ segment YI \
+ segment YIE \
+ segment STRINGS \
+ $(if-expr "$(BS3KIT_SEGNM_DATA16)" == "", \
+ segment BS3DATA16_DATA \
+ ,) \
+ segment DATA \
+ clname BSS \
+ segment _BSS \
+ segment BSS \
+ segment BS3DATA16_END \
+ clname STACK \
+ segment STACK \
+ \
+ clname FAR_DATA \
+ $(if-expr "$(BS3KIT_SEGNM_DATA16)" != "", \
+ segment BS3DATA16 \
+ segment BS3DATA16_DATA \
+ segment BS3DATA16CONST \
+ segment BS3DATA16CONST2 \
+ segment FAR_DATA \
+ segment BS3DATA16_END \
+ , \
+ segment FAR_DATA \
+ ) \
+ segment BS3DATA32 \
+ segment BS3DATA32CONST \
+ segment BS3DATA32CONST2 \
+ segment BS3DATA32_DATA \
+ segment BS3DATA32_BSS \
+ segment BS3DATA32_END \
+ \
+ segment BS3DATA64 \
+ segment BS3DATA64CONST \
+ segment BS3DATA64_BSS \
+ segment BS3DATA64_END \
+ clname BS3CLASS16RMCODE \
+ segment BS3RMCODE16_START \
+ segment BS3RMCODE16 \
+ segment BS3RMCODE16_END \
+ clname BS3CLASS16X0CODE \
+ segment BS3X0CODE16_START \
+ segment BS3X0CODE16 \
+ segment BS3X0CODE16_END \
+ clname BS3CLASS16X1CODE \
+ segment BS3X1CODE16_START \
+ segment BS3X1CODE16 \
+ segment BS3X1CODE16_END \
+ clname BS3CLASS32CODE \
+ segment BS3TEXT32 \
+ segment BS3TEXT32_END \
+ clname BS3CLASSSEPARATE32AND64BITCODE \
+ segment BS3SEPARATE32AND64BITCODE \
+ segment BS3SEPARATE32AND64BITCODE_END \
+ clname BS3CLASS64CODE \
+ segment BS3TEXT64 \
+ segment BS3TEXT64_END
+# clname BS3FLAT segaddr=0x0000 \
+# segment BS3FLAT segaddr=0x0000
+
+TEMPLATE_VBoxBS3KitUtil_LNK_DEPS = $(NO_SUCH_VARIABLE)
+TEMPLATE_VBoxBS3KitUtil_POST_CMDS = $(NO_SUCH_VARIABLE)
+
diff --git a/src/VBox/ValidationKit/bootsectors/Makefile.kmk b/src/VBox/ValidationKit/bootsectors/Makefile.kmk
new file mode 100644
index 00000000..930362f6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/Makefile.kmk
@@ -0,0 +1,472 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Bootsector Tests for Test Drivers or standalone testing.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Make sure our Config.kmk gets included when kmk is running from a parent directory.
+#
+ifndef VBOX_BOOTSECTORS_CONFIG_KMK_INCLUDED
+ include $(PATH_SUB_CURRENT)/Config.kmk
+endif
+
+
+#
+# Include sub-makefile.
+#
+# The VBOX_WITH_BS3KIT feature requires NASM 2.12 and either MSVC or gcc
+# with ms_abi function attribute (gcc v4.4+, MSVC default).
+# Some 32-bit gcc compilers come without 64-bit support (e.g. EL5).
+#
+if defined(VBOX_WITH_OPEN_WATCOM)
+ if1of ($(KBUILD_TARGET), win)
+ VBOX_WITH_BS3KIT = 1
+ else if $(VBOX_GCC_VERSION_CC) >= 40400 # ms_abi was added in 4.4
+ if1of ($(KBUILD_TARGET), linux)
+ ifneq ($(VBOX_GCC_m64),)
+ VBOX_WITH_BS3KIT = 1
+ endif
+ endif
+ endif
+ ifdef VBOX_WITH_BS3KIT
+ include $(PATH_SUB_CURRENT)/bs3kit/Makefile.kmk
+ endif
+endif
+
+
+#
+# Boot Sector "Linker" tool.
+#
+TOOL_VBoxBootSectorLd = Joins one or more BS2 object files into a floppy img.
+TOOL_VBoxBootSectorLd_LINK_MISCBIN_OUTPUT =
+TOOL_VBoxBootSectorLd_LINK_MISCBIN_DEPEND =
+TOOL_VBoxBootSectorLd_LINK_MISCBIN_DEPORD = $(VBoxBs2Linker_1_TARGET)
+define TOOL_VBoxBootSectorLd_LINK_MISCBIN_CMDS
+ $(VBoxBs2Linker_1_TARGET) -o $(out) $(objs) $(othersrc)
+endef
+
+BLDPROGS += VBoxBs2Linker
+VBoxBs2Linker_TEMPLATE = VBoxBldProg
+VBoxBs2Linker_SOURCES = VBoxBs2Linker.cpp
+
+
+#
+# Makes a boot sector test image.
+#
+TEMPLATE_VBoxBsTestImg = kBuild tool config for building boot sector stuff.
+TEMPLATE_VBoxBsTestImg_INST = $(INST_VALIDATIONKIT)bootsectors/
+TEMPLATE_VBoxBsTestImg_BINSUFF = .img
+TEMPLATE_VBoxBsTestImg_MODE = 0644
+TEMPLATE_VBoxBsTestImg_ASTOOL = YASM
+TEMPLATE_VBoxBsTestImg_ASFLAGS = -f bin -P $(VBOX_PATH_BOOTSECTORS_SRC)/bootsector2-first.mac $(VBOX_YASM_Wno-segreg-in-64bit) --mapfile
+TEMPLATE_VBoxBsTestImg_ASDEFS = ASM_FORMAT_BIN
+TEMPLATE_VBoxBsTestImg_INCS = \
+ . \
+ ../../VMM/testcase/Instructions
+TEMPLATE_VBoxBsTestImg_LDTOOL = VBoxBootSectorLd
+
+
+#
+# The boot sector tests.
+#
+MISCBINS += bootsector-shutdown
+bootsector-shutdown_TEMPLATE = VBoxBsTestImg
+bootsector-shutdown_SOURCES = bootsector-shutdown.asm
+
+MISCBINS += bootsector-pae
+bootsector-pae_TEMPLATE = VBoxBsTestImg
+bootsector-pae_SOURCES = bootsector-pae.asm
+
+MISCBINS += bootsector-empty
+bootsector-empty_TEMPLATE = VBoxBsTestImg
+bootsector-empty_SOURCES = bootsector-empty.asm
+
+MISCBINS += bootsector2-test1
+bootsector2-test1_TEMPLATE = VBoxBsTestImg
+bootsector2-test1_SOURCES = bootsector2-test1.asm
+
+MISCBINS += bootsector2-cpu-hidden-regs-1
+bootsector2-cpu-hidden-regs-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-hidden-regs-1_SOURCES = bootsector2-cpu-hidden-regs-1.asm
+
+MISCBINS += bootsector2-cpu-instr-1
+bootsector2-cpu-instr-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-instr-1_SOURCES = bootsector2-cpu-instr-1.asm
+
+MISCBINS += bootsector2-cpu-pf-1
+bootsector2-cpu-pf-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-pf-1_SOURCES = bootsector2-cpu-pf-1.asm
+
+MISCBINS += bootsector2-cpu-xcpt-1
+bootsector2-cpu-xcpt-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-xcpt-1_SOURCES = bootsector2-cpu-xcpt-1.asm
+
+MISCBINS += bootsector2-cpu-xcpt-2
+bootsector2-cpu-xcpt-2_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-xcpt-2_SOURCES = bootsector2-cpu-xcpt-2.asm
+
+MISCBINS += bootsector2-cpu-a20-1
+bootsector2-cpu-a20-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-a20-1_SOURCES = bootsector2-cpu-a20-1.asm
+
+MISCBINS += bootsector2-cpu-basic-1
+bootsector2-cpu-basic-1_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-basic-1_SOURCES = bootsector2-cpu-basic-1.asm
+
+MISCBINS += bootsector2-cpu-ac-loop
+bootsector2-cpu-ac-loop_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-ac-loop_SOURCES = bootsector2-cpu-ac-loop.asm
+
+MISCBINS += bootsector2-cpu-db-loop
+bootsector2-cpu-db-loop_TEMPLATE = VBoxBsTestImg
+bootsector2-cpu-db-loop_SOURCES = bootsector2-cpu-db-loop.asm
+
+MISCBINS += bootsector2-boot-registers-1
+bootsector2-boot-registers-1_TEMPLATE = VBoxBsTestImg
+bootsector2-boot-registers-1_SOURCES = bootsector2-boot-registers-1.asm
+
+MISCBINS += bootsector2-triple-fault-1
+bootsector2-triple-fault-1_TEMPLATE = VBoxBsTestImg
+bootsector2-triple-fault-1_SOURCES = bootsector2-triple-fault-1.asm
+
+
+ifeq ($(USERNAME),birdxx)
+ if1of ($(KBUILD_HOST).$(KBUILD_HOST_ARCH), win.amd64)
+ #
+ # Generated instruction tests (work in progress).
+ #
+
+ VBOX_PATH_VBINSTST = $(PATH_ROOT)/src/VBox/VMM/testcase/Instructions
+ VBOX_VBINSTST_GEN = $(VBOX_PATH_VBINSTST)/InstructionTestGen.py
+ VBOX_BOOTSECTOR2_VBINSTST_AMD64_GEN = $(VBOX_BLD_PYTHON) $(VBOX_VBINSTST_GEN) \
+ --split 3 --target bs2-r0-64 --output-base $(bootsectors_0_OUTDIR)/VBInsTst-64 --test-size tiny
+ VBOX_BOOTSECTOR2_VBINSTST_AMD64_FILES = $(shell $(VBOX_BOOTSECTOR2_VBINSTST_AMD64_GEN) --makefile-mode)
+
+ #$$(bootsectors_0_OUTDIR)/VBInsTst.ts + $$(VBOX_BOOTSECTOR2_VBINSTST_AMD64_FILES): $(VBOX_VBINSTST_GEN) | $$(dir $$@)
+ # $(VBOX_BOOTSECTOR2_VBINSTST_AMD64_GEN)
+ # $(APPEND) -t $@
+ #
+ #bootsectors_SOURCES += $(bootsectors_0_OUTDIR)/bootsector2-vbinstst-1.img
+ #bootsectors_CLEAN += $(VBOX_BOOTSECTOR2_VBINSTST_AMD64_FILES)
+ #
+ #$$(bootsectors_0_OUTDIR)/bootsector2-vbinstst-1.img: \
+ # $(PATH_SUB_CURRENT)/bootsector2-vbinstst-64-1.asm \
+ # $$(bootsectors_0_OUTDIR)/VBInsTst-64.asm
+ # $(TOOL_$(VBOX_ASTOOL)_AS) -f bin -D ASM_FORMAT_BIN -I $(dir $@) -I $(PATH_ROOT)/include -I $(VBOX_PATH_VBINSTST) -o $@ -L nasm -l $@.lst $<
+
+ MISCBINS += bootsector2-vbinstst-kernel
+ bootsector2-vbinstst-kernel_TEMPLATE = VBoxBsTestImg
+ bootsector2-vbinstst-kernel_SOURCES = \
+ bootsector2-vbinstst-kernel.asm
+
+
+ MISCBINS += bootsector2-vbinstst-64-1
+ bootsector2-vbinstst-64-1_TEMPLATE = VBoxBsTestImg
+ bootsector2-vbinstst-64-1_DEFS = \
+ BS2_BIG_IMAGE_LM64 \
+ BS2_BIG_IMAGE_GEN_SOURCE_FILE=bs2-vbinstst-64-1.asm \
+ BS2_BIG_IMAGE_GEN_TEST_NAME=\"bs2-vbinstst-64-1\"
+ bootsector2-vbinstst-64-1_INCS = $(bootsector2-vbinstst-64-1_0_OUTDIR)/
+ bootsector2-vbinstst-64-1_SOURCES = \
+ bootsector2-vbinstst-kernel.asm \
+ bootsector2-vbinstst-big-template.asm
+ bootsector2-vbinstst-64-1_INTERMEDIATES = \
+ $(bootsector2-vbinstst-64-1_0_OUTDIR)/bs2-vbinstst-64-1.asm
+ bootsector2-vbinstst-64-1_CLEAN = \
+ $(bootsector2-vbinstst-64-1_0_OUTDIR)/bs2-vbinstst-64-1.asm
+
+ $$(bootsector2-vbinstst-64-1_0_OUTDIR)/bs2-vbinstst-64-1.asm: $(VBOX_VBINSTST_GEN) | $$(dir $$@)
+ $(REDIRECT) -0 /dev/null -- $(VBOX_BLD_PYTHON) $(VBOX_VBINSTST_GEN) --target bs2-r0-64-big --output-base $(basename $@) --test-size medium
+
+ MISCBINS += bootsector2-vbinstst-32-1
+ bootsector2-vbinstst-32-1_TEMPLATE = VBoxBsTestImg
+ bootsector2-vbinstst-32-1_DEFS = \
+ BS2_BIG_IMAGE_PP32 \
+ BS2_BIG_IMAGE_GEN_SOURCE_FILE=bs2-vbinstst-32-1.asm \
+ BS2_BIG_IMAGE_GEN_TEST_NAME=\"bs2-vbinstst-32-1\"
+ bootsector2-vbinstst-32-1_INCS = $(bootsector2-vbinstst-32-1_0_OUTDIR)/
+ bootsector2-vbinstst-32-1_SOURCES = \
+ bootsector2-vbinstst-kernel.asm \
+ bootsector2-vbinstst-big-template.asm
+ bootsector2-vbinstst-32-1_INTERMEDIATES = \
+ $(bootsector2-vbinstst-32-1_0_OUTDIR)/bs2-vbinstst-32-1.asm
+ bootsector2-vbinstst-32-1_CLEAN = \
+ $(bootsector2-vbinstst-32-1_0_OUTDIR)/bs2-vbinstst-32-1.asm
+
+ $$(bootsector2-vbinstst-32-1_0_OUTDIR)/bs2-vbinstst-32-1.asm: $(VBOX_VBINSTST_GEN) | $$(dir $$@)
+ $(REDIRECT) -0 /dev/null -- $(VBOX_BLD_PYTHON) $(VBOX_VBINSTST_GEN) --target bs2-r0-32-big --output-base $(basename $@) --test-size medium
+
+ endif
+endif # bird-only
+
+
+ifdef VBOX_WITH_BS3KIT
+ #
+ # Bs3kit
+ #
+
+ #
+ # APIC
+ #
+ MISCBINS += bs3-apic-1
+ bs3-apic-1_TEMPLATE = VBoxBS3KitImg
+ bs3-apic-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-apic-1.c \
+ bs3-apic-1-32.c32
+
+
+ # CPU basics #2 (first being bootsector2-cpu-basic-1).
+ MISCBINS += bs3-cpu-basic-2
+ bs3-cpu-basic-2_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-basic-2_INCS = .
+ bs3-cpu-basic-2_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-cpu-basic-2-template.c
+ bs3-cpu-basic-2_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-cpu-basic-2-template.c
+ bs3-cpu-basic-2_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-cpu-basic-2.c \
+ bs3-cpu-basic-2-x0.c \
+ bs3-cpu-basic-2-32.c32 \
+ bs3-cpu-basic-2-pf.c32 \
+ bs3-cpu-basic-2-asm.asm \
+ bs3kit/bs3-cmn-instantiate-x0.c16 \
+ bs3kit/bs3-cmn-instantiate.c32 \
+ bs3kit/bs3-cmn-instantiate.c64
+ bs3-cpu-basic-2-template.o:: \
+ $$(bs3-cpu-basic-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate-x0.o16 \
+ $$(bs3-cpu-basic-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o32 \
+ $$(bs3-cpu-basic-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o64 \
+ $$(bs3-cpu-basic-2_0_OUTDIR)/bs3-cpu-basic-2-asm.o16
+
+ #
+ # CPU weird stuff #1.
+ #
+ MISCBINS += bs3-cpu-weird-1
+ bs3-cpu-weird-1_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-weird-1_INCS = .
+ bs3-cpu-weird-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-cpu-weird-1.c \
+ bs3-cpu-weird-1-x0.c \
+ bs3-cpu-weird-1-asm.asm
+
+ #
+ # 64-bit CPU state #1.
+ #
+ MISCBINS += bs3-cpu-state64-1
+ bs3-cpu-state64-1_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-state64-1_INCS = .
+ bs3-cpu-state64-1_SOURCES = \
+ bs3kit/bs3-first-init-all-lm64.asm \
+ bs3-cpu-state64-1.c64 \
+ bs3-cpu-state64-1-asm.asm
+
+ #
+ # FPU state corruption checker.
+ #
+ MISCBINS += bs3-fpustate-1
+ bs3-fpustate-1_TEMPLATE = VBoxBS3KitImg
+ bs3-fpustate-1_INCS = .
+ bs3-fpustate-1_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-fpustate-1-template.c
+ bs3-fpustate-1_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-fpustate-1-template.c
+ bs3-fpustate-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-fpustate-1.c \
+ bs3kit/bs3-cmn-instantiate.c16 \
+ bs3kit/bs3-cmn-instantiate.c32 \
+ bs3kit/bs3-cmn-instantiate.c64 \
+ bs3-fpustate-1-asm.asm
+ bs3-fpustate-1-template.o:: \
+ $$(bs3-fpustate-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o16 \
+ $$(bs3-fpustate-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o32 \
+ $$(bs3-fpustate-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o64 \
+ $$(bs3-fpustate-1_0_OUTDIR)/bs3-fpustate-1-asm.o16
+
+ #
+ # CPU instruction decoding experiments.
+ #
+ MISCBINS += bs3-cpu-decoding-1
+ bs3-cpu-decoding-1_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-decoding-1_INCS = .
+ bs3-cpu-decoding-1_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-cpu-decoding-1-template.c
+ bs3-cpu-decoding-1_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-cpu-decoding-1-template.c
+ bs3-cpu-decoding-1_SOURCES = \
+ bs3kit/bs3-first-init-all-pp32.asm \
+ bs3-cpu-decoding-1.c32 \
+ bs3-cpu-decoding-1-asm.asm
+ # bs3kit/bs3-cmn-instantiate.c16 \
+ # bs3kit/bs3-cmn-instantiate.c32 \
+ # bs3kit/bs3-cmn-instantiate.c64
+ bs3-cpu-decoding-1-template.o:: \
+ $$(bs3-cpu-decoding-1_0_OUTDIR)/bs3-cpu-decoding-1-asm.o16
+ # $$(bs3-cpu-decoding-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o16 \
+ # $$(bs3-cpu-decoding-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o32 \
+ # $$(bs3-cpu-decoding-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o64 \
+
+
+ #
+ # CPU instructions #2 (first being bootsector2-cpu-instr-1).
+ #
+ MISCBINS += bs3-cpu-instr-2
+ bs3-cpu-instr-2_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-instr-2_INCS = .
+ bs3-cpu-instr-2_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-cpu-instr-2-template.c
+ bs3-cpu-instr-2_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-cpu-instr-2-template.c
+ bs3-cpu-instr-2_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-cpu-instr-2.c \
+ bs3-cpu-instr-2-asm.asm \
+ bs3kit/bs3-cmn-instantiate-x0.c16 \
+ bs3kit/bs3-cmn-instantiate.c32 \
+ bs3kit/bs3-cmn-instantiate.c64
+ bs3-cpu-instr-2-template.o:: \
+ $$(bs3-cpu-instr-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate-x0.o16 \
+ $$(bs3-cpu-instr-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o32 \
+ $$(bs3-cpu-instr-2_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o64 \
+ $$(bs3-cpu-instr-2_0_OUTDIR)/bs3-cpu-instr-2-asm.o16
+
+ #
+ # CPU instructions #3 - SSE, ++.
+ #
+ MISCBINS += bs3-cpu-instr-3
+ bs3-cpu-instr-3_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-instr-3_INCS = .
+ bs3-cpu-instr-3_SOURCES = \
+ bs3kit/bs3-first-init-all-pe32.asm \
+ bs3-cpu-instr-3.c32 \
+ bs3-cpu-instr-3-asm.asm
+ bs3-cpu-instr-3-template.o:: \
+ $$(bs3-cpu-instr-3_0_OUTDIR)/bs3-cpu-instr-3-asm.o16
+
+ #
+ # CPU generated instruction tests #1
+ #
+ MISCBINS += bs3-cpu-generated-1
+ bs3-cpu-generated-1_TEMPLATE = VBoxBS3KitImg
+ bs3-cpu-generated-1_INCS = .
+ bs3-cpu-generated-1_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-cpu-generated-1-template.c
+ bs3-cpu-generated-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-cpu-generated-1.c \
+ bs3-cpu-generated-1-asm.asm \
+ bs3kit/bs3-cmn-instantiate-x0.c16 \
+ bs3kit/bs3-cmn-instantiate.c32 \
+ bs3kit/bs3-cmn-instantiate.c64 \
+ $(bs3-cpu-generated-1_0_OUTDIR)/bs3-cpu-generated-1-data.c16
+ bs3-cpu-generated-1_CLEAN = $(bs3-cpu-generated-1_0_OUTDIR)/bs3-cpu-generated-1-data.c16
+
+ bs3-cpu-generated-1-template.o:: \
+ $$(bs3-cpu-generated-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate-x0.o16 \
+ $$(bs3-cpu-generated-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o32 \
+ $$(bs3-cpu-generated-1_0_OUTDIR)/bs3kit/bs3-cmn-instantiate.o64 \
+ $$(bs3-cpu-generated-1_0_OUTDIR)/bs3-cpu-generated-1-asm.o16
+
+ $$(bs3-cpu-generated-1_0_OUTDIR)/bs3-cpu-generated-1-data.c16: \
+ $(PATH_SUB_CURRENT)/bs3-cpu-generated-1-data.py \
+ $(PATH_SUB_CURRENT)/../../VMM/VMMAll/IEMAllInstructionsPython.py \
+ $(PATH_SUB_CURRENT)/../../VMM/VMMAll/IEMAllInstructions*.cpp.h \
+ | $$(dir $$@)
+ $(REDIRECT) -0 /dev/null -- $(VBOX_BLD_PYTHON) $< $@
+
+ #
+ # Memory allocation.
+ #
+ MISCBINS += bs3-memalloc-1
+ bs3-memalloc-1_TEMPLATE = VBoxBS3KitImg
+ bs3-memalloc-1_INCS = .
+ bs3-memalloc-1_SOURCES = \
+ bs3kit/bs3-first-init-all-lm64.asm \
+ bs3-memalloc-1.c64
+
+
+ #
+ # Timer Interrupts
+ #
+ MISCBINS += bs3-timers-1
+ bs3-timers-1_TEMPLATE = VBoxBS3KitImg
+ bs3-timers-1_INCS = .
+ bs3-timers-1_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-timers-1-template.c
+ bs3-timers-1_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-timers-1-template.c
+ bs3-timers-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-timers-1.c \
+ bs3-timers-1-x0.c
+
+ #
+ # Timing
+ #
+ MISCBINS += bs3-timing-1
+ bs3-timing-1_TEMPLATE = VBoxBS3KitImg
+ bs3-timing-1_INCS = .
+ bs3-timing-1_DEFS = BS3_CMN_INSTANTIATE_FILE1=bs3-timing-1-template.c
+ bs3-timing-1_DEFS += BS3_MODE_INSTANTIATE_FILE1=bs3-timing-1-template.c
+ bs3-timing-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-timing-1.c \
+ bs3-timing-1-32.c32
+
+ #
+ # Lock contention and interruption.
+ #
+ MISCBINS += bs3-locking-1
+ bs3-locking-1_TEMPLATE = VBoxBS3KitImg
+ bs3-locking-1_INCS = .
+ bs3-locking-1_SOURCES = \
+ bs3kit/bs3-first-rm.asm \
+ bs3-locking-1.c
+
+endif # VBOX_WITH_BS3KIT
+
+
+#
+# Executable version of the bs3-timing-1 bootsector.
+#
+PROGRAMS += bs3-timing-1-exe
+bs3-timing-1-exe_TEMPLATE = VBoxValidationKitR3
+bs3-timing-1-exe_NAME = bs3-timing-1
+bs3-timing-1-exe_SOURCES = bs3-timing-1-exe.c
+
+
+#
+# pylint
+#
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+$(evalcall def_vbox_validationkit_process_python_sources)
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/bootsectors/VBoxBs2Linker.cpp b/src/VBox/ValidationKit/bootsectors/VBoxBs2Linker.cpp
new file mode 100644
index 00000000..cabc174f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/VBoxBs2Linker.cpp
@@ -0,0 +1,229 @@
+/* $Id: VBoxBs2Linker.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - Boot Sector 2 "linker".
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <iprt/types.h>
+
+
+int main(int argc, char **argv)
+{
+ const char *pszOutput = NULL;
+ const char **papszInputs = (const char **)calloc(argc, sizeof(const char *));
+ unsigned cInputs = 0;
+
+ /*
+ * Scan the arguments.
+ */
+ for (int i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ const char *pszOpt = &argv[i][1];
+ if (*pszOpt == '-')
+ {
+ /* Convert long options to short ones. */
+ pszOpt--;
+ if (!strcmp(pszOpt, "--output"))
+ pszOpt = "o";
+ else if (!strcmp(pszOpt, "--version"))
+ pszOpt = "V";
+ else if (!strcmp(pszOpt, "--help"))
+ pszOpt = "h";
+ else
+ {
+ fprintf(stderr, "syntax errro: Unknown options '%s'\n", pszOpt);
+ free(papszInputs);
+ return 2;
+ }
+ }
+
+ /* Process the list of short options. */
+ while (*pszOpt)
+ {
+ switch (*pszOpt++)
+ {
+ case 'o':
+ {
+ const char *pszValue = pszOpt;
+ pszOpt = strchr(pszOpt, '\0');
+ if (*pszValue == '=')
+ pszValue++;
+ else if (!*pszValue)
+ {
+ if (i + 1 >= argc)
+ {
+ fprintf(stderr, "syntax error: The --output option expects a filename.\n");
+ free(papszInputs);
+ return 12;
+ }
+ pszValue = argv[++i];
+ }
+ if (pszOutput)
+ {
+ fprintf(stderr, "Only one output file is allowed. You've specified '%s' and '%s'\n",
+ pszOutput, pszValue);
+ free(papszInputs);
+ return 2;
+ }
+ pszOutput = pszValue;
+ pszOpt = "";
+ break;
+ }
+
+ case 'V':
+ printf("%s\n", "$Revision: 155244 $");
+ free(papszInputs);
+ return 0;
+
+ case '?':
+ case 'h':
+ printf("usage: %s [options] -o <output> <input1> [input2 ... [inputN]]\n", argv[0]);
+ free(papszInputs);
+ return 0;
+ }
+ }
+ }
+ else
+ papszInputs[cInputs++] = argv[i];
+ }
+
+ if (!pszOutput)
+ {
+ fprintf(stderr, "syntax error: No output file was specified (-o or --output).\n");
+ free(papszInputs);
+ return 2;
+ }
+ if (cInputs == 0)
+ {
+ fprintf(stderr, "syntax error: No input files was specified.\n");
+ free(papszInputs);
+ return 2;
+ }
+
+
+ /*
+ * Do the job.
+ */
+ /* Open the output file. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ FILE *pOutput = fopen(pszOutput, "wb");
+#else
+ FILE *pOutput = fopen(pszOutput, "w");
+#endif
+ if (!pOutput)
+ {
+ fprintf(stderr, "error: Failed to open output file '%s' for writing\n", pszOutput);
+ free(papszInputs);
+ return 1;
+ }
+
+ /* Copy the input files to the output file, with sector padding applied. */
+ int rcExit = 0;
+ size_t off = 0;
+ for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
+ {
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ FILE *pInput = fopen(papszInputs[i], "rb");
+#else
+ FILE *pInput = fopen(papszInputs[i], "r");
+#endif
+ if (pInput)
+ {
+ for (;;)
+ {
+ /* Read a block from the input file. */
+ uint8_t abBuf[4096];
+ size_t cbRead = fread(abBuf, sizeof(uint8_t), 4096, pInput);
+ if (!cbRead || ferror(pInput))
+ break;
+
+ /* Padd the end of the file if necessary. */
+ if (cbRead != 4096 && !feof(pInput))
+ {
+ fprintf(stderr, "error: fread returned %u bytes, but we're not at the end of the file yet...\n",
+ (unsigned)cbRead);
+ rcExit = 1;
+ break;
+ }
+ if ((cbRead & 0x1ff) != 0)
+ {
+ memset(&abBuf[cbRead], 0, 4096 - cbRead);
+ cbRead = (cbRead + 0x1ff) & ~0x1ffU;
+ }
+
+ /* Write the block to the output file. */
+ if (fwrite(abBuf, sizeof(uint8_t), cbRead, pOutput) == cbRead)
+ off += cbRead;
+ else
+ {
+ fprintf(stderr, "error: fwrite failed\n");
+ rcExit = 1;
+ break;
+ }
+ }
+
+ if (ferror(pInput))
+ {
+ fprintf(stderr, "error: Error reading '%s'.\n", papszInputs[i]);
+ rcExit = 1;
+ }
+ fclose(pInput);
+ }
+ else
+ {
+ fprintf(stderr, "error: Failed to open '%s' for reading.\n", papszInputs[i]);
+ rcExit = 1;
+ }
+ }
+
+ /* Finally, close the output file (can fail because of buffered data). */
+ if (fclose(stderr) != 0)
+ {
+ fprintf(stderr, "error: Error closing '%s'.\n", pszOutput);
+ rcExit = 1;
+ }
+
+ fclose(pOutput);
+ free(papszInputs);
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector-empty.asm b/src/VBox/ValidationKit/bootsectors/bootsector-empty.asm
new file mode 100644
index 00000000..b1aec9fe
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector-empty.asm
@@ -0,0 +1,70 @@
+; $Id: bootsector-empty.asm $
+;; @file
+; Empty bootsector can be used as example
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;; The boot sector load address.
+%define BS_ADDR 0x7c00
+%define PDP_ADDR 0x9000
+%define PD_ADDR 0xa000
+
+
+BITS 16
+start:
+ ; Start with a jump just to follow the convention.
+ jmp short the_code
+ nop
+times 3ah db 0
+
+the_code:
+ ; put the code here
+
+
+
+hlt_again:
+ hlt
+ cli
+ jmp hlt_again
+
+ ;
+ ; The GDT.
+ ;
+padding:
+times 510 - (padding - start) db 0
+ db 055h, 0aah
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector-pae.asm b/src/VBox/ValidationKit/bootsectors/bootsector-pae.asm
new file mode 100644
index 00000000..9c47c157
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector-pae.asm
@@ -0,0 +1,175 @@
+; $Id: bootsector-pae.asm $
+;; @file
+; Bootsector that switches the CPU info PAE mode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/bios.mac"
+
+
+;; The boot sector load address.
+%define BS_ADDR 0x7c00
+%define PDP_ADDR 0x9000
+%define PD_ADDR 0xa000
+
+
+BITS 16
+start:
+ ; Start with a jump just to follow the convention.
+ jmp short the_code
+ nop
+times 3ah db 0
+
+the_code:
+ cli
+ xor edx, edx
+ mov ds, dx ; Use 0 based addresses
+
+ ;
+ ; Create a paging hierarchy
+ ;
+ mov cx, 4
+ xor esi, esi ; physical address
+ mov ebx, PDP_ADDR
+ mov edi, PD_ADDR
+pdptr_loop:
+ ; The page directory pointer entry.
+ mov dword [ebx], edi
+ or word [bx], X86_PDPE_P
+ mov dword [ebx + 4], edx
+
+ ; The page directory.
+pd_loop:
+ mov dword [edi], esi
+ or word [di], X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_PS
+ mov dword [edi + 4], 0
+ add esi, 0x00200000 ; 2MB
+ add di, 8
+ test di, 0fffh
+ jnz pd_loop
+
+ add bx, 8
+ loop pdptr_loop
+
+ ;
+ ; Switch to protected mode.
+ ;
+ lgdt [(gdtr - start) + BS_ADDR]
+ lidt [(idtr_null - start) + BS_ADDR]
+
+ mov eax, PDP_ADDR
+ mov cr3, eax
+
+ mov eax, cr4
+ or eax, X86_CR4_PAE | X86_CR4_PSE
+ mov cr4, eax
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp far 0x0008:((code32_start - start) + BS_ADDR) ; 8=32-bit CS
+
+BITS 32
+code32_start:
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ax, 0x18
+ mov es, ax
+ mov esp, 0x80000
+
+ ; eye catchers
+ mov eax, 0xCafeBabe
+ mov ebx, eax
+ mov ecx, eax
+ mov edx, eax
+ mov edi, eax
+ mov esi, eax
+ mov ebp, eax
+
+ ;
+ ; Boch shutdown request.
+ ;
+ mov bl, 64
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT
+retry:
+ mov ecx, 8
+ mov esi, (szShutdown - start) + BS_ADDR
+ rep outsb
+ xchg dx, ax ; alternate between the new (VBox) and old (Bochs) ports.
+ dec bl
+ jnz retry
+ ; Shutdown failed!
+hlt_again:
+ hlt
+ cli
+ jmp hlt_again
+
+ ;
+ ; The GDT.
+ ;
+align 8, db 0
+gdt:
+ dw 0, 0, 0, 0 ; null selector
+ dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x08)
+ dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x10)
+ dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat stack segment (0x18)
+
+gdtr:
+ dw 8*4-1 ; limit 15:00
+ dw (gdt - start) + BS_ADDR ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+
+idtr_null:
+ dw 0 ; limit 15:00
+ dw (gdt - start) + BS_ADDR ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+
+szShutdown:
+ db 'Shutdown', 0
+
+ ;
+ ; Padd the remainder of the sector with zeros and
+ ; end it with the dos signature.
+ ;
+padding:
+times 510 - (padding - start) db 0
+ db 055h, 0aah
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector-shutdown.asm b/src/VBox/ValidationKit/bootsectors/bootsector-shutdown.asm
new file mode 100644
index 00000000..93ab5ff4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector-shutdown.asm
@@ -0,0 +1,90 @@
+; $Id: bootsector-shutdown.asm $
+;; @file
+; Bootsector for grub chainloading that shutdowns the VM.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "VBox/bios.mac"
+
+
+BITS 16
+start:
+ ; Start with a jump just to follow the convention.
+ jmp short the_code
+ nop
+times 3ah db 0
+
+the_code:
+ cli
+
+ ;
+ ; VBox/Bochs shutdown request - write "Shutdown" byte by byte to shutdown port.
+ ;
+ mov cx, 64
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov bx, VBOX_BIOS_OLD_SHUTDOWN_PORT
+retry:
+ mov al, 'S'
+ out dx, al
+ mov al, 'h'
+ out dx, al
+ mov al, 'u'
+ out dx, al
+ mov al, 't'
+ out dx, al
+ mov al, 'd'
+ out dx, al
+ mov al, 'o'
+ out dx, al
+ mov al, 'w'
+ out dx, al
+ mov al, 'n'
+ out dx, al
+ xchg dx, bx ; alternate between the new (VBox) and old (Bochs) ports.
+ loop retry
+
+ ;
+ ; Shutdown failed!
+ ;
+
+ ;; @todo print some message before halting.
+ hlt
+
+ ;
+ ; Padd the remainder of the sector with zeros and
+ ; end it with the dos signature.
+ ;
+padding:
+times 510 - (padding - start) db 0
+ db 055h, 0aah
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-api.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-api.mac
new file mode 100644
index 00000000..d7444391
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-api.mac
@@ -0,0 +1,162 @@
+; $Id: bootsector2-api.mac $
+;; @file
+; Bootsector2 API definition for use by split images (kernel < 1MB < image).
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef ___bootsector2_api_mac
+%define ___bootsector2_api_mac
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "bootsector2-structures.mac"
+
+;;
+; The load address for big images.
+%define BS2_BIG_LOAD_ADDR 0x120000
+
+
+;;
+; API Template for lazy birds.
+;
+%macro BS2_API_TEMPLATE 0,
+
+ BS2_API_TEMPLATE_ACTION(Bs2DisableA20)
+ BS2_API_TEMPLATE_ACTION(Bs2DisableA20ViaKbd)
+ BS2_API_TEMPLATE_ACTION(Bs2DisableA20ViaPortA)
+ BS2_API_TEMPLATE_ACTION(Bs2DisableNX)
+ BS2_API_TEMPLATE_ACTION(Bs2EnableA20)
+ BS2_API_TEMPLATE_ACTION(Bs2EnableA20ViaKbd)
+ BS2_API_TEMPLATE_ACTION(Bs2EnableA20ViaPortA)
+ BS2_API_TEMPLATE_ACTION(Bs2EnableNX)
+ BS2_API_TEMPLATE_ACTION(Bs2IsNXSupported)
+ BS2_API_TEMPLATE_ACTION(Bs2KbdRead)
+ BS2_API_TEMPLATE_ACTION(Bs2KbdWait)
+ BS2_API_TEMPLATE_ACTION(Bs2KbdWrite)
+ BS2_API_TEMPLATE_ACTION(Bs2PanicIfVMMDevTestingIsMissing)
+ BS2_API_TEMPLATE_ACTION(Bs2SetupNX)
+ BS2_API_TEMPLATE_ACTION(CalcBenchmarkIterations)
+ BS2_API_TEMPLATE_ACTION(CalcTestPerSecond)
+ BS2_API_TEMPLATE_ACTION(GetElapsedNanoTS)
+ BS2_API_TEMPLATE_ACTION(GetNanoTS)
+ BS2_API_TEMPLATE_ACTION(PrintChr)
+ BS2_API_TEMPLATE_ACTION(PrintF)
+ BS2_API_TEMPLATE_ACTION(PrintStr)
+ BS2_API_TEMPLATE_ACTION(PrintStrColonSpaces)
+ BS2_API_TEMPLATE_ACTION(PrintStrSpacesColonSpace)
+ BS2_API_TEMPLATE_ACTION(PrintU32)
+ BS2_API_TEMPLATE_ACTION(ReportResult)
+ BS2_API_TEMPLATE_ACTION(Shutdown)
+ BS2_API_TEMPLATE_ACTION(StrFormatF)
+ BS2_API_TEMPLATE_ACTION(StrFormatV)
+ BS2_API_TEMPLATE_ACTION(StrLen)
+ BS2_API_TEMPLATE_ACTION(TestCheckTrap)
+ BS2_API_TEMPLATE_ACTION(TestDumpCurrentRegisters)
+ BS2_API_TEMPLATE_ACTION(TestDumpRegisters)
+ BS2_API_TEMPLATE_ACTION(TestFailed)
+ BS2_API_TEMPLATE_ACTION(TestFailedF)
+ BS2_API_TEMPLATE_ACTION(TestInit)
+ BS2_API_TEMPLATE_ACTION(TestRestoreRegisters)
+ BS2_API_TEMPLATE_ACTION(TestSaveRegisters)
+ BS2_API_TEMPLATE_ACTION(testSendStrCmd)
+ BS2_API_TEMPLATE_ACTION(TestSkipped)
+ BS2_API_TEMPLATE_ACTION(TestSub)
+ BS2_API_TEMPLATE_ACTION(testSubCleanup)
+ BS2_API_TEMPLATE_ACTION(TestSubDone)
+ BS2_API_TEMPLATE_ACTION(TestSubErrorCount)
+ BS2_API_TEMPLATE_ACTION(TestTerm)
+ BS2_API_TEMPLATE_ACTION(TestValueReg)
+ BS2_API_TEMPLATE_ACTION(TestValueU32)
+ BS2_API_TEMPLATE_ACTION(TestInstallTrapRecs)
+
+%endmacro
+
+
+;;
+; This defines the API pointers.
+;
+ABSOLUTE 0x500
+;; Start the structure with a magic number.
+NAME(g_u32Bs2ApiMagic): resd 1
+;; And a version number.
+NAME(g_u32Bs2ApiVersion): resd 1
+
+; The real mode and v8086 mode entry points (far pointers).
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) NAME(g_pfn %+ a_Name %+ _r86): resd 1
+BS2_API_TEMPLATE
+
+; The 16-bit protected mode entry points (far pointers).
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) NAME(g_pfn %+ a_Name %+ _p16): resd 1
+BS2_API_TEMPLATE
+
+; The 32-bit protected mode entry points (flat pointers).
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) NAME(g_pfn %+ a_Name %+ _p32): resd 1
+BS2_API_TEMPLATE
+
+; The 64-bit protected (long) mode entry points.
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) NAME(g_pfn %+ a_Name %+ _p64): resq 1
+BS2_API_TEMPLATE
+
+;; End the structure with a magic so it's integrity can be verified.
+NAME(g_u32Bs2ApiEndMagic) resd 1
+
+;;
+; The current API magic value (Douglas Carl Engelbart).
+%define BS2_API_MAGIC 0x19250130
+
+;;
+; The current API version.
+%define BS2_API_VERSION 0x00010000
+
+BEGINCODE
+
+
+;; @name Service trap vector interface.
+; @{
+%define BS2_SYSCALL_TO_RING0 0
+%define BS2_SYSCALL_TO_RING1 1
+%define BS2_SYSCALL_TO_RING2 2
+%define BS2_SYSCALL_TO_RING3 3
+
+;; The service vector.
+%define BS2_TRAP_SYSCALL 20h
+;; @}
+
+%endif
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-boot-registers-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-boot-registers-1.asm
new file mode 100644
index 00000000..e58d25a7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-boot-registers-1.asm
@@ -0,0 +1,86 @@
+; $Id: bootsector2-boot-registers-1.asm $
+;; @file
+; Bootsector that prints the register status at boot.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;
+; Initialize in real mode, preserving the registers.
+;
+%define BS2_INIT_RM
+%define BS2_INIT_SAVE_REGS
+%include "bootsector2-common-init-code.mac"
+
+main:
+ mov ax, BS2_REG_SAVE_ADDR
+ call NAME(TestDumpRegisters_r86)
+
+ xor ax, ax
+ mov al, [es:di]
+ push ax
+ mov al, [es:di + 1]
+ push ax
+ mov al, [es:di + 2]
+ push ax
+ mov al, [es:di + 3]
+ push ax
+ push ds
+ push .s_szPnpFmt1
+ call NAME(PrintF_r86)
+ pop ax
+ push .s_szPnpFmt2
+ call NAME(PrintF_r86)
+ pop ax
+ push .s_szPnpFmt3
+ call NAME(PrintF_r86)
+
+ call NAME(Bs2Panic)
+
+.s_szPnpFmt1:
+ db 'es:di -> %RX8 %RX8 %RX8 %RX8 ',0
+.s_szPnpFmt2:
+ db '%c%c%c%c', 0
+.s_szPnpFmt3:
+ db 13, 10, 0
+
+;
+; Pad the image so it loads cleanly.
+;
+BS2_PAD_IMAGE
+the_end:
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-end.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-end.mac
new file mode 100644
index 00000000..01629499
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-end.mac
@@ -0,0 +1,52 @@
+; $Id: bootsector2-common-end.mac $
+;; @file
+; Boot sector 2 - End of code.
+;
+; @note Don't forget to cinldue bootsector2-common-traprec-end.mac!
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Terminate the trap records if opened.
+;
+%ifdef BS2_WITH_TRAPRECS
+ BS2_TRAP_RECS_END
+%endif
+
+;
+; Pad the image so it loads cleanly.
+;
+BEGINEND
+the_end:
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac
new file mode 100644
index 00000000..a95217d5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac
@@ -0,0 +1,2313 @@
+; $Id: bootsector2-common-init-code.mac $
+;; @file
+; Common bootsector code init.
+;
+; In addition to initialize the stack at %7bf0 it loads the first 512KB of the
+; floppy image at %7c00. The control is handed over with interrupts disabled
+; to a 'main' function defined by the includer.
+;
+; The following defines controls the mode we call main in:
+; - BS2_INIT_RM (default)
+; - BS2_INIT_PE32
+; - BS2_INIT_PP32
+; - BS2_INIT_PAE32
+; - BS2_INIT_LM64
+;
+; The following defines controls code inclusion:
+; - BS2_INC_RM
+; - BS2_INC_PE
+; - BS2_INC_PE16
+; - BS2_INC_PE32
+; - BS2_INC_PEV86
+; - BS2_INC_PP
+; - BS2_INC_PP16
+; - BS2_INC_PP32
+; - BS2_INC_PPV86
+; - BS2_INC_PAE
+; - BS2_INC_PAE16
+; - BS2_INC_PAE32
+; - BS2_INC_PAEV86
+; - BS2_INC_LM
+; - BS2_INC_LM16
+; - BS2_INC_LM32
+; - BS2_INC_LM64
+; - BS2_INC_CMN_R86
+; - BS2_INC_CMN_V86
+; - BS2_INC_CMN_P16
+; - BS2_INC_CMN_P32
+; - BS2_INC_CMN_P64
+; - BS2_WITH_TRAPS
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+; map files should include symbols and everything.
+[map all]
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "bootsector2-structures.mac"
+%include "bootsector2-common-macros-1.mac"
+%include "VBox/bios.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;; @name Static Memory Allocation
+; @{
+;; The boot sector load address.
+%define BS2_ADDR 07c00h
+;; The stack is located before the code (and will overflow into the interrupt
+; table and other essential system data).
+%define STACK_ADDR (BS2_ADDR - 256)
+
+%ifdef BS2_WITH_TRAPS
+;; The address of the ring-0 stack in bs2Tss32BitDf.
+%define BS2_DF_R0_STACK_ADDR 06800h
+;; The address of the ring-0 stack in TSSxx.
+%define BS2_R0_STACK_ADDR 06000h
+;; The address of the ring-1 stack in TSSxx.
+%define BS2_R1_STACK_ADDR 05000h
+;; The address of the ring-2 stack in TSSxx.
+%define BS2_R2_STACK_ADDR 04800h
+%endif ; BS2_WITH_TRAPS
+
+;;
+; Where we save the boot registers during init.
+%define BS2_REG_SAVE_ADDR 06000h
+
+;; The start of the memory area used for paging, stacks and so forth.
+%define BS2_PXX_BASE 080000h
+
+;; The page map level 4 address (all entries point to BS2_LM_PDP_ADDR).
+%define BS2_LM_PML4_ADDR 080000h
+;; The long mode page directory pointer table address.
+%define BS2_LM_PDP_ADDR 081000h
+;; The PAE page directory pointer table address.
+%define BS2_PAE_PDP_ADDR 082000h
+;; The address of the 4 PAE page directories. Also used by long mode.
+%define BS2_PAE_PD_ADDR 083000h
+;; The address of the 32-bit page directory.
+%define BS2_32B_PD_ADDR 087000h
+;; User page table #0.
+%define BS2_USER_PX_0_ADDR 088000h
+;; User page table #1.
+%define BS2_USER_PX_1_ADDR 089000h
+;; User page table #2.
+%define BS2_USER_PX_2_ADDR 08a000h
+;; User page table #3.
+%define BS2_USER_PX_3_ADDR 08b000h
+;; User page table #4.
+%define BS2_USER_PX_4_ADDR 08c000h
+;; User page table #5.
+%define BS2_USER_PX_5_ADDR 08d000h
+;; User page table #6.
+%define BS2_USER_PX_6_ADDR 08e000h
+;; User page table #7.
+%define BS2_USER_PX_7_ADDR 08f000h
+;; The selector to use when accessing the PDP and PD from real mode.
+%define BS2_PXX_SEL 08000h
+;; Converts a BS2_P*_ADDR into a BS2_PXX_SEL selector offset.
+%define BS2_PXX_OFF(Addr) ( (Addr) - (BS2_PXX_SEL * 16) )
+
+;; The base address in the default address spaces of the range where we are
+; free to muck about as much as we like. (This is a virtual address.)
+%define BS2_MUCK_ABOUT_BASE 000400000h
+
+; We have some free space here 090000h...09a000h (stacks moved)
+
+;; The address of the LDT.
+%define BS2_LDT_BASE 09b000h
+;; The size of the LDT in bytes.
+%define BS2_LDT_SIZE 001fffh
+
+;; The start of the memory area used for paging, stacks and so forth.
+%define BS2_PXX_LAST 09ffffh
+;; @}
+
+
+;;
+; @name Group of 32-bit, 16-bit and 64-bit selectors for one ring.
+; @{
+%define BS2_SEL_GRP_CS32 00h
+%define BS2_SEL_GRP_DS32 08h
+%define BS2_SEL_GRP_SS32 10h
+%define BS2_SEL_GRP_CS16 18h
+%define BS2_SEL_GRP_DS16 20h
+%define BS2_SEL_GRP_SS16 28h
+%define BS2_SEL_GRP_CS64 30h
+%define BS2_SEL_GRP_DS64 38h
+%define BS2_SEL_GRP_SS64 38h
+%define BS2_SEL_GRP_SIZE 40h
+;; @}
+
+
+;; Move to program.
+%ifndef BS2_WITHOUT_RAW_MODE
+ %define BS2_WITH_RAW_MODE
+%endif
+
+; Implicit code inclusion based on the init mode.
+%ifdef BS2_INIT_PE32
+ %define BS2_INC_PE32
+%elifdef BS2_INIT_PP32
+ %define BS2_INC_PP32
+%elifdef BS2_INIT_PAE32
+ %define BS2_INC_PAE32
+%elifdef BS2_INIT_LM64
+ %define BS2_INC_LM64
+%else
+ %define BS2_INIT_RM ; the default
+ %define BS2_INC_RM
+%endif
+
+; Aliases.
+%ifdef BS2_INC_PE
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PEV86
+%endif
+%ifdef BS2_INC_PP
+ %define BS2_INC_PP16
+ %define BS2_INC_PP32
+ %define BS2_INC_PPV86
+%endif
+%ifdef BS2_INC_PAE
+ %define BS2_INC_PAE16
+ %define BS2_INC_PAE32
+ %define BS2_INC_PAEV86
+%endif
+%ifdef BS2_INC_LM
+ %define BS2_INC_LM16
+ %define BS2_INC_LM32
+ %define BS2_INC_LM64
+%endif
+
+; Common code.
+%ifdef BS2_INC_RM
+ %define BS2_INC_CMN_R86
+%endif
+%ifdef BS2_INC_PE16
+ %define BS2_INC_CMN_P16
+ %define BS2_INC_CMN_PE
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PE32
+ %define BS2_INC_CMN_P32
+ %define BS2_INC_CMN_PE
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PEV86
+ %define BS2_INC_CMN_R86
+ %define BS2_INC_CMN_V86
+ %define BS2_INC_CMN_PE
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PP16
+ %define BS2_INC_CMN_P16
+ %define BS2_INC_CMN_PP
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PP32
+ %define BS2_INC_CMN_P32
+ %define BS2_INC_CMN_PP
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PPV86
+ %define BS2_INC_CMN_R86
+ %define BS2_INC_CMN_V86
+ %define BS2_INC_CMN_PP
+ %define BS2_INC_CMN_PM
+%endif
+%ifdef BS2_INC_PAE16
+ %define BS2_INC_CMN_P16
+ %define BS2_INC_CMN_PAE
+ %define BS2_INC_CMN_PM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+%ifdef BS2_INC_PAE32
+ %define BS2_INC_CMN_P32
+ %define BS2_INC_CMN_PAE
+ %define BS2_INC_CMN_PM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+%ifdef BS2_INC_PAEV86
+ %define BS2_INC_CMN_R86
+ %define BS2_INC_CMN_V86
+ %define BS2_INC_CMN_PAE
+ %define BS2_INC_CMN_PM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+%ifdef BS2_INC_LM16
+ %define BS2_INC_CMN_P16
+ %define BS2_INC_CMN_LM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+%ifdef BS2_INC_LM32
+ %define BS2_INC_CMN_P32
+ %define BS2_INC_CMN_LM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+%ifdef BS2_INC_LM64
+ %define BS2_INC_CMN_LM64
+ %define BS2_INC_CMN_LM
+ %define BS2_INC_CMN_PAE_LM
+%endif
+
+
+;
+; Misc defines.
+;
+;; The offset of the TSS32.CR3 field.
+%define BS2_TSS32_CR3_OFF 01ch
+
+
+;*******************************************************************************
+;* Structures and Typedefs *
+;*******************************************************************************
+
+
+;
+; Start with a jump just to follow the convention.
+; Also declare all segments/sections to establish them and their order.
+;
+ ORG BS2_ADDR
+
+section .text valign=16 align=16 progbits
+section .data vfollows=.text follows=.text valign=16 align=16 progbits
+section .texthigh vfollows=.data follows=.data valign=16 align=16 progbits
+section .traprecs vfollows=.texthigh follows=.texthigh valign=8 align=8 progbits
+section .end vfollows=.traprecs follows=.traprecs valign=512 align=512 progbits
+
+%define BEGINCODELOW section .text ;;< For 16-bit code.
+%define BEGINCODEHIGH section .texthigh ;;< For 32-bit and 64-bit code.
+%define BEGINEND section .end ;;< For aligning image to 512 bytes.
+
+BEGINCODELOW
+BITS 16
+start:
+ jmp short bs2InitCode
+ nop
+ nop ; alignment
+
+;
+; Abuse the bios parameter block area for data storage.
+;
+gdtr:
+ dw bs2GdtEnd - bs2Gdt - 1 ; limit 15:00
+ dw bs2Gdt ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+
+idtr_null:
+ dw 0 ; limit 15:00
+ dw bs2Gdt ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+
+%ifdef BS2_WITH_TRAPS
+ %ifdef BS2_INC_CMN_PM
+idtr_32bit:
+ dw bs2Idt32bitEnd - bs2Idt32bit -1 ; limit 15:00
+ dw bs2Idt32bit ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+ %endif
+
+ %ifdef BS2_INC_CMN_LM
+idtr_64bit:
+ dw bs2Idt64bitEnd - bs2Idt64bit -1 ; limit 15:00
+ dw bs2Idt64bit ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+ %endif
+
+%elifdef BS2_WITH_RAW_MODE
+idtr_dummy_32bit:
+ dw bs2DummyIdt32bitEnd - bs2DummyIdt32bit -1 ; limit 15:00
+ dw bs2DummyIdt32bit ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+%endif
+
+idtr_real_mode:
+ dw 01ffh ; limit 15:00
+ dw 0 ; base 15:00
+ db 0 ; base 23:16
+ db 0 ; unused
+
+g_achHex:
+ db '0123456789abcdef', 0
+
+g_bBootDrv:
+ db 80h ; Not in the official BPB location, but whatever.
+g_fCpuIntel:
+ db 0
+g_fCpuAmd:
+ db 0
+
+bs2BpbPadding:
+ times 3dh - (bs2BpbPadding - start) db 0
+
+
+;
+; Where to real init code starts.
+;
+bs2InitCode:
+ cli
+
+%ifdef BS2_INIT_SAVE_REGS
+ ; save the registers if we've been asked to do so.
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rax], eax
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rsp], esp
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rbp], ebp
+ mov ax, ss
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.ss], ax
+ mov ax, ds
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.ds], ax
+ mov ax, es
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.es], ax
+ mov ax, fs
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.fs], ax
+ mov ax, gs
+%endif
+
+ ; set up the segment reisters and stack.
+ xor eax, eax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ mov esp, STACK_ADDR
+ mov [esp], eax ; clear the first 16 bytes
+ mov [esp + 04h], eax
+ mov [esp + 08h], eax ; fake rbp.
+ mov [esp + 0ch], eax ; fake ebp and bp
+ mov ebp, esp
+
+%ifdef BS2_INIT_SAVE_REGS
+ ; Save more registers now that ds is known and the stack is usable.
+ pushfd
+ pop eax
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rflags], eax
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rbx], ebx
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rcx], ecx
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rdx], edx
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rsi], esi
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.rdi], edi
+%endif
+
+ ; Make sure caching is enabled and alignment is off.
+ mov eax, cr0
+%ifdef BS2_INIT_SAVE_REGS
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.cr0], eax
+%endif
+ and eax, ~(X86_CR0_NW | X86_CR0_CD | X86_CR0_AM)
+ mov cr0, eax
+
+ ; Load all the code.
+ call bs2InitLoadImage
+ mov [g_bBootDrv], dl
+
+ ; Initialize the data structures for the included modes requiring this.
+%ifdef BS2_INC_CMN_PP
+ call bs2InitPagedProtMode
+%endif
+%ifdef BS2_INC_CMN_PAE
+ call bs2InitPaeProtMode
+%endif
+%ifdef BS2_INC_CMN_LM
+ call bs2InitLongMode
+%endif
+
+ ; Entered the desired mode.
+%ifdef BS2_INIT_PE32
+ call Bs2EnterMode_rm_pe32
+BITS 32
+%endif
+%ifdef BS2_INIT_PP32
+ call Bs2EnterMode_rm_pp32
+BITS 32
+%endif
+%ifdef BS2_INIT_PAE32
+ call Bs2EnterMode_rm_pae32
+BITS 32
+%endif
+%ifdef BS2_INIT_LM64
+ call Bs2EnterMode_rm_lm64
+BITS 64
+%endif
+%ifdef BS2_INIT_RM
+ call SetCpuModeGlobals_rm
+%endif
+
+%ifdef BS2_WITH_RAW_MODE
+ ;
+ ; Mask interrupts and then set IF.
+ ;
+ mov al, 0ffh
+ out 021h, al
+ out 0a1h, al
+ sti
+%endif
+
+ jmp bs2DoneInit
+
+
+;;
+; Loads the image off the floppy.
+;
+; This uses the the_end label to figure out the length. For this to work
+; cleanly the label must be aligned on a sector boundrary. Use BS2_PAD_IMAGE
+; to make sure this is the case.
+;
+; Clobbers everything except ebp and esp. Panics on failure.
+;
+; @param dl The boot drive number (from BIOS).
+; @uses ax, cx, bx, esi, di
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2InitLoadImage
+ push bp
+ mov bp, sp
+ push es
+%define bSavedDiskNo byte [bp - 04h]
+ push dx
+%define bMaxSector byte [bp - 06h]
+ push 0
+%define bMaxHead byte [bp - 08h]
+ push 0
+%define bMaxCylinder byte [bp - 0ah]
+ push 0
+
+ ;
+ ; Try figure the geometry.
+ ;
+ mov ah, 08h
+ int 13h
+ jc .failure
+ mov bMaxSector, cl
+ mov bMaxHead, dh
+ mov bMaxCylinder, ch
+ mov dl, bSavedDiskNo
+
+ ;
+ ; Reload all the sectors one at a time (avoids problems).
+ ;
+ lea esi, [dword the_end]
+ sub esi, start
+ shr esi, 9 ; si = number of sectors to load.
+ mov di, BS2_ADDR / 16 ; The current load segment.
+ mov cx, 0001h ; ch/cylinder=0 (0-based); cl/sector=1 (1-based)
+ xor dh, dh ; dh/head=0
+.the_load_loop:
+ xor bx, bx
+ mov es, di ; es:bx -> buffer
+ mov ax, 0201h ; al=1 sector; ah=read function
+ int 13h
+ jc .failure
+
+ ; advance to the next sector/head/cylinder.
+ inc cl
+ cmp cl, bMaxSector
+ jbe .adv_addr
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead
+ jbe .adv_addr
+
+ mov dh, 0
+ inc ch
+
+.adv_addr:
+ add di, 512 / 16
+ dec si
+ jnz .the_load_loop
+
+ add sp, 3*2
+ pop dx
+ pop es
+ leave
+ ret
+
+ ;
+ ; Something went wrong, display a message.
+ ;
+.failure:
+ pusha
+
+ ; print message
+ mov si, .s_szErrMsg
+ mov ah, 0eh
+ xor bx, bx
+.failure_next_char:
+ lodsb
+ int 10h
+ cmp si, .s_szErrMsgEnd
+ jb .failure_next_char
+
+ ; format the error number.
+ movzx bx, byte [bp - 2 - 1] ; read the ah of the pusha frame
+ shr bl, 4
+ mov al, [bx + g_achHex]
+ int 10h
+
+ movzx bx, byte [bp - 2 - 1] ; read the ah of the pusha frame
+ and bl, 0fh
+ mov al, [bx + g_achHex]
+ int 10h
+
+ ; panic
+ popa
+ call Bs2Panic
+.s_szErrMsg:
+ db 13, 10, 'read error: '
+.s_szErrMsgEnd:
+ENDPROC bs2InitLoadImage
+
+;; Pads the image so bs2InitLoadImage can load it without trouble.
+%macro BS2_PAD_IMAGE 0
+bs2PadImageLabel:
+; times ( (512*18*2) - ( (bs2PadImageLabel - start) % (512*18*2) ) ) db 0 ; track aligned size.
+ times ( 512 - ( (bs2PadImageLabel - start) % 512 ) ) db 0 ; sector aligned size.
+; times ( 10000h - BS2_ADDR - (bs2PadImageLabel - start) ) db 0 ; full segment 0 size.
+%endmacro
+
+
+;;
+; Shutdown routine that will work in real and protected mode, providing
+; that SS is valid that we can load it into DS.
+;
+; Does not return.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2Shutdown
+ cli
+ mov bl, 64
+ mov ax, ss
+ mov ds, ax
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT
+.retry:
+ mov ecx, 8
+ mov esi, .s_szShutdown
+ rep outsb
+ xchg dx, ax ; alternate between the new (VBox) and old (Bochs) ports.
+ dec bl
+ jnz .retry
+ ; Shutdown failed!
+ jmp Bs2Panic
+.s_szShutdown:
+ db 'Shutdown', 0
+ENDPROC Bs2Shutdown
+
+
+;;
+; Panic routine for real mode.
+;
+; Does not return.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2Panic
+ cli
+.hlt_again:
+ hlt
+ jmp .hlt_again
+ENDPROC Bs2Panic
+
+
+;
+; Padd the remainder of the sector with zeros and
+; end it with the dos signature.
+;
+bs2Padding:
+ times 510 - (bs2Padding - start) db 0
+ db 055h, 0aah
+
+
+;
+; The GDT (X86DESCGENERIC).
+;
+align 8, db 0
+bs2Gdt:
+ dw 00000h, 00000h, 00000h, 00000h ; null selector
+%define BS2_SEL_R0_BASE 08h
+%define BS2_SEL_CS32 08h
+ dw 0ffffh, 00000h, 09b00h, 000cfh ; 32-bit flat code segment.
+%define BS2_SEL_DS32 10h
+ dw 0ffffh, 00000h, 09300h, 000cfh ; 32-bit flat data segment.
+%define BS2_SEL_SS32 18h
+ dw 0ffffh, 00000h, 09300h, 000cfh ; 32-bit flat stack segment.
+%define BS2_SEL_CS16 20h
+ dw 0ffffh, 00000h, 09b00h, 00000h ; 16-bit code segment with base 0.
+%define BS2_SEL_DS16 28h
+ dw 0ffffh, 00000h, 09300h, 00000h ; 16-bit data segment with base 0.
+%define BS2_SEL_SS16 30h
+ dw 0ffffh, 00000h, 09300h, 00000h ; 16-bit stack segment with base 0.
+%define BS2_SEL_CS64 38h
+ dw 0ffffh, 00000h, 09a00h, 000afh ; 64-bit code segment.
+%define BS2_SEL_DS64 40h
+%define BS2_SEL_SS64 40h
+ dw 0ffffh, 00000h, 09300h, 000afh ; 64-bit stack and data segment.
+ dw 00000h, 00000h, 00000h, 00000h ; Unused
+%define BS2_SEL_MMIO16 50h
+%define BS2_SEL_MMIO16_BASE 00df000h
+ dw 0ffffh, 0f000h, 0930dh, 00000h ; 16-bit VMMDev MMIO segment with base 0df000h.
+ dw 00000h, 00000h, 00000h, 00000h ; Unused
+%define BS2_SEL_LDT 60h ; LDT usage requires manual LLDT and setting up.
+ dw BS2_LDT_SIZE, BS2_LDT_BASE & 0xffff, 08200h | ((BS2_LDT_BASE >> 16) & 0xff), 00000h
+ dw 00000h, 00000h, 00000h, 00000h ; zero for 64-bit mode.
+%define BS2_SEL_CS16_EO 70h
+ dw 0fffeh, 00000h, 09800h, 00000h ; 16-bit code segment with base 0, not accessed, execute only, short limit.
+ dw 00000h, 00000h, 00000h, 00000h ; unused.
+%ifdef BS2_WITH_TRAPS
+ %ifdef BS2_INC_CMN_PM
+ %define BS2_SEL_TSS32 80h
+ dw (bs2Tss32BitEnd - bs2Tss32Bit) - 1 ; 32-bit TSS.
+ dw bs2Tss32Bit
+ db 0
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+ %define BS2_SEL_TSS32_DF 88h
+ dw (bs2Tss32BitDfEnd - bs2Tss32BitDf) - 1; 32-bit TSS, double fault.
+ dw bs2Tss32BitDf
+ db 0
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+ %else
+ dw 00000h, 00000h, 00000h, 00000h
+ dw 00000h, 00000h, 00000h, 00000h
+ %endif
+ %ifdef BS2_INC_CMN_LM
+ %define BS2_SEL_TSS64 90h
+ dw (bs2Tss64BitEnd - bs2Tss64Bit) - 1 ; 32-bit TSS.
+ dw bs2Tss64Bit
+ db 0
+ db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80
+ dw 0
+ dw 00000h, 00000h, 00000h, 00000h ; 2nd half of the 64-bit selector (not necessary).
+ %else
+ dw 00000h, 00000h, 00000h, 00000h
+ dw 00000h, 00000h, 00000h, 00000h
+ %endif
+%endif
+ ; Ring-1 selectors.
+%define BS2_SEL_R1_BASE 0a0h
+%define BS2_SEL_R1_CS32 0a0h
+ dw 0ffffh, 00000h, 0bb00h, 000cfh ; Ring-1 32-bit flat code segment.
+%define BS2_SEL_R1_DS32 0a8h
+ dw 0ffffh, 00000h, 0b300h, 000cfh ; Ring-1 32-bit flat data segment.
+%define BS2_SEL_R1_SS32 0b0h
+ dw 0ffffh, 00000h, 0b300h, 000cfh ; Ring-1 32-bit flat stack segment.
+%define BS2_SEL_R1_CS16 0b8h
+ dw 0ffffh, 00000h, 0bb00h, 00000h ; Ring-1 16-bit code segment with base 0.
+%define BS2_SEL_R1_DS16 0c0h
+ dw 0ffffh, 00000h, 0b300h, 00000h ; Ring-1 16-bit data segment with base 0.
+%define BS2_SEL_R1_SS16 0c8h
+ dw 0ffffh, 00000h, 0b300h, 00000h ; Ring-1 16-bit stack segment with base 0.
+%define BS2_SEL_R1_CS64 0d0h
+ dw 0ffffh, 00000h, 0ba00h, 000afh ; Ring-1 64-bit code segment.
+%define BS2_SEL_R1_DS64 0d8h
+%define BS2_SEL_R1_SS64 0d8h
+ dw 0ffffh, 00000h, 0b300h, 000afh ; Ring-1 64-bit stack and data segment.
+
+ ; Ring-2 selectors.
+%define BS2_SEL_R2_BASE 0e0h
+%define BS2_SEL_R2_CS32 0e0h
+ dw 0ffffh, 00000h, 0db00h, 000cfh ; Ring-2 32-bit flat code segment.
+%define BS2_SEL_R2_DS32 0e8h
+ dw 0ffffh, 00000h, 0d300h, 000cfh ; Ring-2 32-bit flat data segment.
+%define BS2_SEL_R2_SS32 0f0h
+ dw 0ffffh, 00000h, 0d300h, 000cfh ; Ring-2 32-bit flat stack segment.
+%define BS2_SEL_R2_CS16 0f8h
+ dw 0ffffh, 00000h, 0db00h, 00000h ; Ring-2 16-bit code segment with base 0.
+%define BS2_SEL_R2_DS16 0f0h
+ dw 0ffffh, 00000h, 0d300h, 00000h ; Ring-2 16-bit data segment with base 0.
+%define BS2_SEL_R2_SS16 108h
+ dw 0ffffh, 00000h, 0d300h, 00000h ; Ring-2 16-bit stack segment with base 0.
+%define BS2_SEL_R2_CS64 110h
+ dw 0ffffh, 00000h, 0da00h, 000afh ; Ring-2 64-bit code segment.
+%define BS2_SEL_R2_DS64 118h
+%define BS2_SEL_R2_SS64 118h
+ dw 0ffffh, 00000h, 0d300h, 000afh ; Ring-2 64-bit stack and data segment.
+
+ ; Ring-3 selectors.
+%define BS2_SEL_R3_BASE 120h
+%define BS2_SEL_R3_CS32 120h
+ dw 0ffffh, 00000h, 0fb00h, 000cfh ; Ring-3 32-bit flat code segment.
+%define BS2_SEL_R3_DS32 128h
+ dw 0ffffh, 00000h, 0f300h, 000cfh ; Ring-3 32-bit flat data segment.
+%define BS2_SEL_R3_SS32 130h
+ dw 0ffffh, 00000h, 0f300h, 000cfh ; Ring-3 32-bit flat stack segment.
+%define BS2_SEL_R3_CS16 138h
+ dw 0ffffh, 00000h, 0fb00h, 00000h ; Ring-3 16-bit code segment with base 0.
+%define BS2_SEL_R3_DS16 140h
+ dw 0ffffh, 00000h, 0f300h, 00000h ; Ring-3 16-bit data segment with base 0.
+%define BS2_SEL_R3_SS16 148h
+ dw 0ffffh, 00000h, 0f300h, 00000h ; Ring-3 16-bit stack segment with base 0.
+%define BS2_SEL_R3_CS64 150h
+ dw 0ffffh, 00000h, 0fa00h, 000afh ; Ring-1 64-bit code segment.
+%define BS2_SEL_R3_DS64 158h
+%define BS2_SEL_R3_SS64 158h
+ dw 0ffffh, 00000h, 0f300h, 000afh ; Ring-1 64-bit stack and data segment.
+
+ ; Here follows a bunch of spare GDT entries for (ab)use in testing.
+%define BS2_SEL_SPARE0 160h
+bs2GdtSpare0:
+ dq 0
+%define BS2_SEL_SPARE1 (BS2_SEL_SPARE0 + 08h)
+bs2GdtSpare1:
+ dq 0
+%define BS2_SEL_SPARE2 (BS2_SEL_SPARE0 + 10h)
+bs2GdtSpare2:
+ dq 0
+%define BS2_SEL_SPARE3 (BS2_SEL_SPARE0 + 18h)
+bs2GdtSpare3:
+ dq 0
+bs2GdtEnd:
+
+
+%ifndef BS2_WITH_TRAPS
+ %ifdef BS2_WITH_RAW_MODE
+;
+; Dummy 32-bit IDT for making CSAM happy.
+;
+align 16, db 0
+bs2DummyIdt32bit:
+ dw 0, 0, 0, 0
+ dw 0, 0, 0, 0
+ dw 0, 0, 0, 0
+ dw 0, 0, 0, 0
+bs2DummyIdt32bitEnd
+ %endif
+%endif
+
+
+;
+; Mode initialization routines.
+;
+
+%ifdef BS2_INC_CMN_PP
+;;
+; Initializes the paged protected mode structures during init.
+;
+; @uses ebx, esi
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2InitPagedProtMode
+ push ds
+
+ ;
+ ; Create a paging hierarchy
+ ;
+ mov bx, BS2_PXX_SEL
+ mov ds, bx
+ mov ebx, BS2_PXX_OFF(BS2_32B_PD_ADDR)
+ xor esi, esi ; physical address
+
+ ; The page directory.
+.pd_loop:
+ mov dword [bx], esi
+ or word [bx], X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_PS | X86_PDE4M_US
+ add esi, _4M
+ add bx, 4
+ test bx, 0fffh
+ jnz .pd_loop
+
+%ifdef BS2_WITH_RAW_MODE
+ ;
+ ; Make sure there is some free space for the hypervisor near the top
+ ; of the address space (last 4MB is mapped).
+ ;
+ and byte [bx - 08h], 0feh
+ and byte [bx - 0ch], 0feh
+ and byte [bx - 10h], 0feh
+ and byte [bx - 14h], 0feh
+%endif
+
+ pop ds
+ ret
+ENDPROC bs2InitPagedProtMode
+%endif ; BS2_INC_CMN_PP
+
+
+%ifdef BS2_INC_CMN_PAE_LM
+;;
+; Initializes the PAE page directories.
+;
+; Assumes ds is set to BS2_PXX_SEL already and that edx is zero.
+;
+; @uses ebx, esi
+; @internal
+;
+BEGINPROC bs2InitPaePageDirs
+ mov esi, X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_PS | X86_PDE4M_US
+ mov ebx, BS2_PXX_OFF(BS2_PAE_PD_ADDR)
+.pd_loop:
+ mov [bx], esi
+ mov [bx + 4], edx
+ add esi, _2M
+ add bx, 8
+ test bx, 0fffh
+ jnz .pd_loop
+ cmp bx, BS2_PXX_OFF(BS2_PAE_PD_ADDR + 4*_4K)
+ jne .pd_loop
+
+%ifdef BS2_WITH_RAW_MODE
+ ;
+ ; Make sure there is some free space for the hypervisor near the top
+ ; of the address space (last 4MB is mapped).
+ ;
+ and byte [bx - 10h], 0feh
+ and byte [bx - 18h], 0feh
+ and byte [bx - 20h], 0feh
+ and byte [bx - 28h], 0feh
+%endif
+
+ ret
+ENDPROC bs2InitPaePageDirs
+%endif ; BS2_INC_CMN_PAE_LM
+
+
+%ifdef BS2_INC_CMN_PAE
+;;
+; Initializes the PAE protected mode structures during init.
+;
+; @uses edx, ebx, esi.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2InitPaeProtMode
+ push ds
+
+ mov dx, BS2_PXX_SEL
+ mov ds, dx
+ xor edx, edx
+
+ ;
+ ; Join paths with long mode.
+ ;
+ call bs2InitPaePageDirs
+
+ ;
+ ; Create the page directory pointer table.
+ ;
+ mov ebx, BS2_PXX_OFF(BS2_PAE_PDP_ADDR)
+ mov dword [bx], (BS2_PAE_PD_ADDR ) | X86_PDPE_P
+ mov dword [bx + 04h], edx
+ mov dword [bx + 08h], (BS2_PAE_PD_ADDR + 1000h) | X86_PDPE_P
+ mov dword [bx + 0ch], edx
+ mov dword [bx + 10h], (BS2_PAE_PD_ADDR + 2000h) | X86_PDPE_P
+ mov dword [bx + 14h], edx
+ mov dword [bx + 18h], (BS2_PAE_PD_ADDR + 3000h) | X86_PDPE_P
+ mov dword [bx + 1ch], edx
+
+ pop ds
+ ret
+ENDPROC bs2InitPaeProtMode
+%endif ; BS2_INC_CMN_PAE
+
+
+%ifdef BS2_INC_CMN_LM
+;;
+; Initializes the long mode structures during init.
+;
+; @uses edx, ebx, esi
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2InitLongMode
+ push ds
+
+ mov dx, BS2_PXX_SEL
+ mov ds, dx
+ xor edx, edx
+
+ ;
+ ; Join paths with the PAE code.
+ ;
+ call bs2InitPaePageDirs
+
+ ;
+ ; Create the long mode page directory pointer table.
+ ;
+ mov ebx, BS2_PXX_OFF(BS2_LM_PDP_ADDR)
+.pdptr_loop:
+ mov dword [bx], (BS2_PAE_PD_ADDR ) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US
+ mov dword [bx + 04h], edx
+ mov dword [bx + 08h], (BS2_PAE_PD_ADDR + 1000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US
+ mov dword [bx + 0ch], edx
+ mov dword [bx + 10h], (BS2_PAE_PD_ADDR + 2000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US
+ mov dword [bx + 14h], edx
+ mov dword [bx + 18h], (BS2_PAE_PD_ADDR + 3000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US
+ mov dword [bx + 1ch], edx
+ add bx, 20h
+ test bx, 0fffh
+ jnz .pdptr_loop
+
+ ;
+ ; Set up the page map level 4 table, all entries mapping the same PDPTR.
+ ;
+ mov ebx, BS2_PXX_OFF(BS2_LM_PML4_ADDR)
+.pml4_loop:
+ mov dword [bx], BS2_LM_PDP_ADDR | X86_PML4E_P | X86_PML4E_RW | X86_PML4E_US
+ mov dword [bx + 4], edx
+ add bx, 8
+ test bx, 0fffh
+ jnz .pml4_loop
+
+ pop ds
+ ret
+ENDPROC bs2InitLongMode
+%endif ; BS2_INC_CMN_LM
+
+
+
+;
+; Routines for entering the different modes.
+;
+
+%ifdef BS2_INC_RM
+;;
+; Dummy.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_rm
+ ret
+ENDPROC Bs2EnterMode_rm_rm
+%endif ; BS2_INC_RM
+
+
+%ifdef BS2_INC_PE16
+;;
+; Enters unpaged protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; ebp and esp converted to 32/16-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pe16
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push word SetCpuModeGlobals_pe16
+%endif
+
+ ;
+ ; Switch to protected mode.
+ ;
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, cr0
+ or eax, X86_CR0_PE
+ and eax, 0ffffffffh - X86_CR0_PG
+ mov cr0, eax
+ jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16
+ENDPROC Bs2EnterMode_rm_pe16
+%endif ; BS2_INC_PE16
+
+
+%ifdef BS2_INC_PE32
+;;
+; Enters unpaged protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; ebp and esp converted to 32-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pe32
+ push word 0
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push dword SetCpuModeGlobals_pe32
+%endif
+
+ ; Do the mode switch.
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, cr0
+ or eax, X86_CR0_PE
+ and eax, 0ffffffffh - X86_CR0_PG
+ mov cr0, eax
+ jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32
+ENDPROC Bs2EnterMode_rm_pe32
+%endif ; BS2_INC_PE32
+
+
+;; @todo BS2_INC_PEV86
+
+
+%ifdef BS2_INC_PP16
+;;
+; Enters paged protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; ebp and esp converted to 16/32-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pp16
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push word SetCpuModeGlobals_pp16
+%endif
+
+ ; Do the mode switch.
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, BS2_32B_PD_ADDR
+ mov cr3, eax
+%ifdef BS2_WITH_TRAPS
+ mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax
+%endif
+
+ mov eax, cr4
+ or eax, X86_CR4_PSE
+ and eax, ~X86_CR4_PAE
+ mov cr4, eax
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP
+ mov cr0, eax
+ jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16
+ENDPROC Bs2EnterMode_rm_pp16
+%endif ; BS2_INC_PP16
+
+
+%ifdef BS2_INC_PP32
+;;
+; Enters paged protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; ebp and esp converted to 32-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pp32
+ push word 0
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push dword SetCpuModeGlobals_pp32
+%endif
+
+ ; Do the mode switch.
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, BS2_32B_PD_ADDR
+ mov cr3, eax
+%ifdef BS2_WITH_TRAPS
+ mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax
+%endif
+
+ mov eax, cr4
+ or eax, X86_CR4_PSE
+ and eax, ~X86_CR4_PAE
+ mov cr4, eax
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP
+ mov cr0, eax
+ jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32
+ENDPROC Bs2EnterMode_rm_pp32
+%endif ; BS2_INC_PP16
+
+
+;; @todo BS2_INC_PPV86
+
+
+%ifdef BS2_INC_PAE16
+;;
+; Enters PAE protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; ebp and esp converted to 16/32-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pae16
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push word SetCpuModeGlobals_pae16
+%endif
+
+ ; Do the mode switch.
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, BS2_PAE_PDP_ADDR
+ mov cr3, eax
+%ifdef BS2_WITH_TRAPS
+ mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax
+%endif
+
+ mov eax, cr4
+ or eax, X86_CR4_PAE | X86_CR4_PSE
+ mov cr4, eax
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP
+ mov cr0, eax
+ jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16
+ENDPROC Bs2EnterMode_rm_pae16
+%endif ; BS2_INC_PAE16
+
+
+%ifdef BS2_INC_PAE32
+;;
+; Enters PAE protected mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; ebp and esp converted to 32-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_pae32
+ push word 0
+ push eax
+ pushfd
+ cli
+%ifndef BS2_NOINC_COMMON
+ push dword SetCpuModeGlobals_pae32
+%endif
+
+ ; Do the mode switch.
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_32bit]
+%elifdef BS2_WITH_RAW_MODE
+ lidt [idtr_dummy_32bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, BS2_PAE_PDP_ADDR
+ mov cr3, eax
+%ifdef BS2_WITH_TRAPS
+ mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax
+%endif
+
+ mov eax, cr4
+ or eax, X86_CR4_PAE | X86_CR4_PSE
+ mov cr4, eax
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP
+ mov cr0, eax
+ jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32
+ENDPROC Bs2EnterMode_rm_pae32
+%endif ; BS2_INC_PAE32
+
+
+;; @todo BS2_INC_PAEV86
+
+
+%ifdef BS2_INC_LM16
+;;
+; Enters long mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; rbp and rsp converted to 16/32/64-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_lm16
+ call Bs2EnterMode_rm_lm64
+BITS 64
+ call Bs2Thunk_lm64_lm16
+BITS 16
+ ret
+ENDPROC Bs2EnterMode_rm_lm16
+%endif ; BS2_INC_LM16
+
+
+%ifdef BS2_INC_LM32
+;;
+; Enters long mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; rbp and rsp converted to 16/32/64-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_lm32
+ ; Change the return address into a 32-bit one.
+ push word 0 ; Reserved 2 extra bytes for 32-bit return address.
+ push eax ; Save eax.
+ movzx eax, word [esp + 6h] ; Read narrow return address.
+ mov [esp + 4h], eax ; Store wide return address.
+ pop eax ; Restore eax.
+
+ ; Do the mode switch and thunking.
+ call Bs2EnterMode_rm_lm64
+BITS 64
+ call Bs2Thunk_lm64_lm32
+BITS 32
+ ret
+ENDPROC Bs2EnterMode_rm_lm32
+%endif ; BS2_INC_LM32
+
+
+%ifdef BS2_INC_LM64
+;;
+; Enters long mode from real mode (cs = 0).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors.
+; rbp and rsp converted to 64-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2EnterMode_rm_lm64
+ push word 0 ; reserve bytes for 64-bit return address.
+ push dword 0
+ push dword 0 ; rax
+ push eax
+ push dword 0 ; rcx
+ push ecx
+ push dword 0 ; rdx
+ push edx
+ push dword 0 ; rflags
+ pushfd
+ cli
+
+ ; Fix the return address.
+ mov ax, [esp + 20h + 6h]
+ mov word [esp + 20h + 6h], 0
+ mov [esp + 20h], ax
+
+ ;
+ ; Switch to long mode.
+ ;
+ xor ax, ax
+ mov ds, ax
+ lgdt [gdtr]
+%ifdef BS2_WITH_TRAPS
+ lidt [idtr_64bit]
+%else
+ lidt [idtr_null]
+%endif
+
+ mov eax, BS2_LM_PML4_ADDR
+ mov cr3, eax
+
+ mov eax, cr4
+ or eax, X86_CR4_PAE | X86_CR4_PSE
+ mov cr4, eax
+
+ mov ecx, MSR_K6_EFER
+ rdmsr
+ or eax, MSR_K6_EFER_LME
+ wrmsr
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP
+ mov cr0, eax
+ jmp far BS2_SEL_CS64:.code64_start
+
+BITS 64
+.code64_start:
+ mov eax, BS2_SEL_DS64
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ax, BS2_SEL_SS64
+ mov ss, ax
+%ifdef BS2_WITH_TRAPS
+ btr dword [bs2Gdt + BS2_SEL_TSS64 + 32/8], 1+8 ; busy -> avail
+ %ifndef BS2_WITH_MANUAL_LTR
+ mov ax, BS2_SEL_TSS64
+ ltr ax
+ %endif
+%endif
+
+ and ebp, 0ffffh
+ and esp, 0ffffh
+ and edi, 0ffffffffh
+ and esi, 0ffffffffh
+ and ebx, 0ffffffffh
+
+%ifndef BS2_NOINC_COMMON
+ call SetCpuModeGlobals_lm64
+%endif
+
+ popf
+ pop rdx
+ pop rcx
+ pop rax
+ ret
+ENDPROC Bs2EnterMode_rm_lm64
+%endif ; BS2_INC_PAE32
+
+
+
+%ifdef BS2_INC_CMN_P16
+;;
+; Code shared by the three 16-bit protected mode switchers.
+;
+; @internal
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2ProtModeCode16Start_p16
+ ; Initialize the registers.
+ mov ax, BS2_SEL_DS16
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ax, BS2_SEL_SS16
+ mov ss, ax
+ and esp, 0ffffh
+ and ebp, 0ffffh
+%ifdef BS2_WITH_TRAPS
+ btr word [bs2Gdt + BS2_SEL_TSS32 + 32/8], 1+8 ; busy -> avail
+ btr word [bs2Gdt + BS2_SEL_TSS32_DF + 32/8], 1+8 ; busy -> avail
+ %ifndef BS2_WITH_MANUAL_LTR
+ mov ax, BS2_SEL_TSS32
+ ltr ax
+ %endif
+%endif
+
+%ifndef BS2_NOINC_COMMON
+ ; Set up mode specific global variables.
+ pop ax
+ call ax
+%endif
+
+ popfd
+ pop eax
+ ret
+ENDPROC bs2ProtModeCode16Start_p16
+%endif ; BS2_INC_CMN_P16
+
+
+%ifdef BS2_INC_CMN_P32
+;;
+; Code shared by the three 32-bit protected mode switchers.
+;
+; @internal
+;
+BEGINCODELOW
+BITS 32
+BEGINPROC bs2ProtModeCode32Start_p32
+ ; Initialize the registers.
+ mov ax, BS2_SEL_DS32
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ax, BS2_SEL_SS32
+ mov ss, ax
+ and esp, 0ffffh
+ and ebp, 0ffffh
+%ifdef BS2_WITH_TRAPS
+ btr dword [bs2Gdt + BS2_SEL_TSS32 + 32/8], 1+8 ; busy -> avail
+ btr dword [bs2Gdt + BS2_SEL_TSS32_DF + 32/8], 1+8 ; busy -> avail
+ %ifndef BS2_WITH_MANUAL_LTR
+ mov ax, BS2_SEL_TSS32
+ ltr ax
+ %endif
+%endif
+
+%ifndef BS2_NOINC_COMMON
+ ; Set up mode specific global variables.
+ pop eax
+ call eax
+%endif
+
+ ; Make the return address 32-bit and then return.
+ movzx eax, word [esp + 0ah]
+ mov [esp + 8h], eax
+ popfd
+ pop eax
+ ret
+ENDPROC bs2ProtModeCode32Start_p32
+%endif ; BS2_INC_CMN_P32
+
+
+
+;
+; Routines for exitting the different modes.
+;
+
+
+%ifdef BS2_INC_RM
+;;
+; Dummy.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_rm
+ ret
+ENDPROC Bs2ExitMode_rm
+%endif ; BS2_INC_RM
+
+
+%ifdef BS2_INC_PE16
+;;
+; See bs2ExitMode_p16.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_pe16
+ push bs2ExitModeCleanupNop_rm
+ jmp bs2ExitMode_p16
+ENDPROC Bs2ExitMode_pe16
+%endif ; BS2_INC_PE16
+
+
+%ifdef BS2_INC_PE32
+;;
+; See bs2ExitMode_p32.
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2ExitMode_pe32
+ push bs2ExitModeCleanupNop_rm
+ jmp bs2ExitMode_p32
+ENDPROC Bs2ExitMode_pe32
+%endif ; BS2_INC_PE32
+
+
+%ifdef BS2_INC_PEV86
+;;
+; See Bs2ExitMode_v86.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_pev86
+ push bs2ExitModeCleanupNop_rm
+ jmp Bs2ExitMode_pv86
+ENDPROC Bs2ExitMode_pev86
+%endif ; BS2_INC_PEV86
+
+
+%ifdef BS2_INC_PP16
+;;
+; See bs2ExitMode_p16.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_pp16
+ push bs2ExitModeCleanupNop_rm
+ jmp bs2ExitMode_p16
+ENDPROC Bs2ExitMode_pp16
+%endif ; BS2_INC_PP16
+
+
+%ifdef BS2_INC_PP32
+;;
+; See bs2ExitMode_p32.
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2ExitMode_pp32
+ push bs2ExitModeCleanupNop_rm
+ jmp bs2ExitMode_p32
+ENDPROC Bs2ExitMode_pp32
+%endif ; BS2_INC_PP32
+
+
+%ifdef BS2_INC_PPV86
+;;
+; See Bs2ExitMode_v86.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_ppv86
+ push bs2ExitModeCleanupNop_rm
+ jmp Bs2ExitMode_pv86
+ENDPROC Bs2ExitMode_ppv86
+%endif ; BS2_INC_PPV86
+
+
+
+%ifdef BS2_INC_PAE16
+;;
+; See bs2ExitMode_p16.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_pae16
+ push bs2ExitModeCleanupPae_rm
+ jmp bs2ExitMode_p16
+ENDPROC Bs2ExitMode_pae16
+%endif ; BS2_INC_pae16
+
+
+%ifdef BS2_INC_PAE32
+;;
+; See bs2ExitMode_p32.
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2ExitMode_pae32
+ push bs2ExitModeCleanupPae_rm
+ jmp bs2ExitMode_p32
+ENDPROC Bs2ExitMode_pae32
+%endif ; BS2_INC_PAE32
+
+
+%ifdef BS2_INC_PAEV86
+;;
+; See Bs2ExitMode_v86.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_paev86
+ push bs2ExitModeCleanupPae_rm
+ jmp Bs2ExitMode_pv86
+ENDPROC Bs2ExitMode_paev86
+%endif ; BS2_INC_PAEV86
+
+
+%ifdef BS2_INC_LM16
+;;
+; See bs2ExitMode_p16.
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2ExitMode_lm16
+ push bs2ExitModeCleanupLm_rm
+ jmp bs2ExitMode_p16
+ENDPROC Bs2ExitMode_lm16
+%endif ; BS2_INC_lm16
+
+
+%ifdef BS2_INC_LM32
+;;
+; See bs2ExitMode_p32.
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2ExitMode_lm32
+ push bs2ExitModeCleanupLm_rm
+ jmp bs2ExitMode_p32
+ENDPROC Bs2ExitMode_lm32
+%endif ; BS2_INC_LM32
+
+
+%ifdef BS2_INC_LM64
+;;
+; See Bs2ExitMode_v86.
+BEGINCODELOW
+BITS 64
+BEGINPROC Bs2ExitMode_lm64
+ call Bs2Thunk_lm64_lm16
+BITS 16
+ pop dword [esp]
+ pop word [esp]
+ push bs2ExitModeCleanupLm_rm
+ jmp bs2ExitMode_p16
+ENDPROC Bs2ExitMode_lm64
+%endif ; BS2_INC_LM64
+
+
+; Isn't there a better way to do this in yasm?
+%ifdef BS2_INC_CMN_P16
+ %define BS2_INC_BS2EXITMODE_P16
+%elifdef BS2_INC_CMN_LM
+ %define BS2_INC_BS2EXITMODE_P16
+%elifdef BS2_INC_CMN_P32
+ %define BS2_INC_BS2EXITMODE_P16
+%endif
+
+%ifdef BS2_INC_BS2EXITMODE_P16
+;;
+; Exit 16-bit protected or long mode (cs = CS16, ss = SS16).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with the 0 selector.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2ExitMode_p16
+ push eax
+ pushfd
+ cli
+
+ ; Make sure we've got the right SS and CS values (paranoia).
+ mov ax, BS2_SEL_SS16
+ mov ss, ax
+ jmp far BS2_SEL_CS16:.code16_start
+
+.code16_start:
+ ; Turn off paging and protected mode.
+ mov eax, cr0
+ and eax, 0ffffffffh - X86_CR0_PE - X86_CR0_PG
+ mov cr0, eax
+ jmp far 0000:.real_mode_start
+
+.real_mode_start:
+ ; Load the correct real mode segment registers.
+ xor ax, ax
+ mov ss, ax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Load the real mode idtr.
+ lidt [idtr_real_mode]
+
+ ; Cleanup.
+ mov ax, [esp + 8h]
+ call ax
+
+%ifndef BS2_NOINC_COMMON
+ ; Set globals.
+ call SetCpuModeGlobals_rm
+%endif
+
+ popfd
+ pop eax
+ add sp, 2h ; cleanup routine address
+ ret
+ENDPROC bs2ExitMode_p16
+%endif ; BS2_INC_CMN_P16
+
+
+%ifdef BS2_INC_CMN_P32
+;;
+; Exit 32-bit protected or long mode (cs = CS32, ss = SS32).
+;
+; The return address as well as the stack registers must be somewhere within
+; the first 64KB of the address space.
+;
+; @returns cs,ds,ss,es,gs,fs loaded with the 0 selector.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 32
+BEGINPROC bs2ExitMode_p32
+ push eax
+ mov eax, [esp + 8h] ; return address
+ mov [esp + 0ah], ax
+ mov eax, [esp + 4h] ; cleanup routine address
+ mov [esp + 08h], ax
+ pop eax
+ add esp, 4h
+
+ call Bs2Thunk_p32_p16
+BITS 16
+ jmp bs2ExitMode_p16
+ENDPROC bs2ExitMode_p32
+%endif ; BS2_INC_CMN_P32
+
+
+;;
+; Dummy cleanup routine.
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2ExitModeCleanupNop_rm
+ ret
+ENDPROC bs2ExitModeCleanupNop_rm
+
+
+%ifdef BS2_INC_CMN_PAE
+;;
+; Cleans up after leaving PAE mode.
+; @uses nothing
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2ExitModeCleanupPae_rm
+ push eax
+ mov eax, cr4
+ and eax, ~X86_CR4_PAE
+ mov cr4, eax
+ pop eax
+ ret
+ENDPROC bs2ExitModeCleanupPae_rm
+%endif
+
+
+%ifdef BS2_INC_CMN_LM
+;;
+; Cleans up after leaving long mode.
+; @uses nothing
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2ExitModeCleanupLm_rm
+ push eax
+ push edx
+ push ecx
+
+ mov ecx, MSR_K6_EFER
+ rdmsr
+ and eax, ~MSR_K6_EFER_LME
+ wrmsr
+
+ pop ecx
+ pop edx
+ pop eax
+ ret
+ENDPROC bs2ExitModeCleanupLm_rm
+%endif
+
+
+
+;
+; Thunking routines for switching between 16/32/64-bit code.
+;
+
+
+%ifdef BS2_INC_CMN_PM
+;;
+; Switches from 32-bit to 16-bit mode ({eip,esp,ebp} < 64KB).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 32
+BEGINPROC Bs2Thunk_p32_p16
+ push eax
+ pushf
+ cli
+ mov ax, BS2_SEL_SS16
+ jmp far BS2_SEL_CS16:.code16_start
+BITS 16
+.code16_start:
+ mov ss, ax
+ mov ax, BS2_SEL_DS16
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Fix the return address and then return.
+ mov ax, [esp + 08h]
+ mov [esp + 0ah], ax
+ popfd
+ pop eax
+ add sp, 2h
+ ret
+ENDPROC Bs2Thunk_p32_p16
+ %define Bs2Thunk_p32_pe16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM
+ %define Bs2Thunk_p32_pp16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM
+ %define Bs2Thunk_p32_pae16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_CMN_PM
+
+
+%ifdef BS2_INC_CMN_PM
+;;
+; Switches from 16-bit to 32-bit mode (cs = CS16, ds = DS16).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; ebp and esp converted to 32bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2Thunk_p16_p32
+ push word 0
+ push eax
+ pushfd
+ cli
+ jmp far BS2_SEL_CS32:.code32_start
+BITS 32
+.code32_start:
+ mov eax, BS2_SEL_SS32
+ mov ss, ax
+ mov ax, BS2_SEL_DS32
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ and ebp, 0ffffh
+ and esp, 0ffffh
+
+ ; Fix the return address and then return.
+ movzx eax, word [esp + 0ah]
+ mov [esp + 08h], eax
+ popfd
+ pop eax
+ ret
+ENDPROC Bs2Thunk_p16_p32
+ %define Bs2Thunk_p16_pe32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM
+ %define Bs2Thunk_p16_pp32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM
+ %define Bs2Thunk_p16_pae32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_CMN_PM
+
+
+%ifdef BS2_INC_CMN_LM
+;;
+; Switches from 64-bit to 16-bit mode ({rip,rsp,rbp} < 64KB).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 64
+BEGINPROC Bs2Thunk_lm64_lm16
+ push rax
+ pushf
+ cli
+ mov ax, BS2_SEL_SS16
+ push BS2_SEL_CS16
+ push .code16_start
+ retf
+BITS 16
+.code16_start:
+ mov ss, ax
+ mov ax, BS2_SEL_DS16
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Fix the return address and then return.
+ mov ax, [esp + 10h]
+ mov [esp + 16h], ax
+ popfd
+ add sp, 4h
+ pop eax
+ add sp, 4h + 6h
+ ret
+ENDPROC Bs2Thunk_lm64_lm16
+ %define Bs2Thunk_p64_lm16 Bs2Thunk_lm64_lm16 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_CMN_LM
+
+
+%ifdef BS2_INC_LM16
+;;
+; Switches from 16-bit to 64-bit mode (cs = CS16, ss = SS16).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors.
+; rbp and rsp converted to 16/32/64-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2Thunk_lm16_lm64
+ push word 0
+ push dword 0
+ push dword 0
+ push eax
+ push dword 0
+ pushfd
+ cli
+ mov eax, BS2_SEL_SS64
+ jmp far BS2_SEL_CS64:.code64_start
+BITS 64
+.code64_start:
+ mov ss, ax
+ mov ax, BS2_SEL_DS64
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ and ebp, 0ffffh
+ and esp, 0ffffh
+ and edi, 0ffffffffh
+ and esi, 0ffffffffh
+ and ebx, 0ffffffffh
+
+ ; Fix the return address and then return.
+ movzx rax, word [rsp + 16h]
+ mov [rsp + 10h], rax
+ popf
+ pop rax
+ ret
+ENDPROC Bs2Thunk_lm16_lm64
+ %define Bs2Thunk_p16_lm64 Bs2Thunk_lm16_lm64 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_LM16
+
+
+%ifdef BS2_INC_LM32
+;;
+; Switches from 64-bit to 32-bit mode ({rip,rsp,rbp} < 4GB).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODEHIGH
+BITS 64
+BEGINPROC Bs2Thunk_lm64_lm32
+ push rax
+ pushf
+ cli
+ mov ax, BS2_SEL_SS32
+ push BS2_SEL_CS32
+ push .code32_start
+ retf
+BITS 32
+.code32_start:
+ mov ss, ax
+ mov ax, BS2_SEL_DS32
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Fix the return address and then return.
+ mov eax, [esp + 10h]
+ mov [esp + 14h], eax
+ popfd
+ mov eax, [esp + 4h]
+ lea esp, [esp + 10h]
+ ret
+ENDPROC Bs2Thunk_lm64_lm32
+ %define Bs2Thunk_p64_lm32 Bs2Thunk_lm64_lm32 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_LM32
+
+
+%ifdef BS2_INC_LM32
+;;
+; Switches from 32-bit to 64-bit mode (cs = CS32, ss = SS32).
+;
+; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors.
+; rbp and rsp converted to 32/64-bit.
+; All other registers are preserved.
+; @uses nothing
+;
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2Thunk_lm32_lm64
+ push dword 0
+ push dword 0
+ push eax
+ push dword 0
+ pushfd
+ cli
+ mov eax, BS2_SEL_SS64
+ jmp far BS2_SEL_CS64:.code64_start
+BITS 64
+.code64_start:
+ mov ss, ax
+ mov ax, BS2_SEL_DS64
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ and ebp, 0ffffffffh
+ and esp, 0ffffffffh
+ and edi, 0ffffffffh
+ and esi, 0ffffffffh
+ and ebx, 0ffffffffh
+
+ ; Fix the return address and then return.
+ mov eax, [rsp + 14h]
+ mov [rsp + 10h], rax
+ popf
+ pop rax
+ ret
+ENDPROC Bs2Thunk_lm32_lm64
+ %define Bs2Thunk_p32_lm64 Bs2Thunk_lm32_lm64 ; Alternative name for TMPL_NM
+%endif ; BS2_INC_LM32
+
+
+
+
+;
+; Routines for checking if mode is supported or not.
+;
+; @returns al=1 & ZF=0 if supported, al=0 & ZF=1 if not.
+; @uses nothing.
+;
+
+; These are easy.
+BEGINCODELOW
+BITS 16
+BEGINPROC bs2IsModeSupportedYes_rm
+GLOBALNAME Bs2IsModeSupported_rm_rm
+GLOBALNAME Bs2IsModeSupported_rm_pe16
+GLOBALNAME Bs2IsModeSupported_rm_pe32
+GLOBALNAME Bs2IsModeSupported_rm_pev86
+GLOBALNAME Bs2IsModeSupported_rm_pp16
+GLOBALNAME Bs2IsModeSupported_rm_pp32
+GLOBALNAME Bs2IsModeSupported_rm_ppv86
+ mov al, 1
+ test al, al
+ ret
+ENDPROC bs2IsModeSupportedYes_rm
+
+
+%ifdef BS2_INC_CMN_PAE
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2IsPaeSupported_16
+GLOBALNAME Bs2IsModeSupported_rm_pae16
+GLOBALNAME Bs2IsModeSupported_rm_pae32
+GLOBALNAME Bs2IsModeSupported_rm_paev86
+ push eax
+ push ebx
+ push ecx
+ push edx
+
+ mov eax, 0
+ cpuid
+ cmp eax, 1
+ jb .no
+ cmp eax, 1000h
+ ja .no
+
+ mov eax, 1
+ cpuid
+ test edx, X86_CPUID_FEATURE_EDX_PAE
+
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+
+ mov al, 0
+ jz .no
+ mov al, 1
+.no:
+ ret
+ENDPROC Bs2IsPaeSupported_16
+%endif ; BS2_INC_CMN_PAE
+
+
+%ifdef BS2_INC_CMN_LM
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2IsLongModeSupported_16
+GLOBALNAME Bs2IsModeSupported_rm_lm16
+GLOBALNAME Bs2IsModeSupported_rm_lm32
+GLOBALNAME Bs2IsModeSupported_rm_lm64
+ push eax
+ push ebx
+ push ecx
+ push edx
+
+ mov eax, 080000000h
+ cpuid
+ cmp eax, 080000001h
+ jb .no
+ cmp eax, 080001000h
+ ja .no
+
+ mov eax, 080000001h
+ cpuid
+ test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE
+
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+
+ mov al, 0
+ jz .no
+ mov al, 1
+.no:
+ ret
+ENDPROC Bs2IsLongModeSupported_16
+%endif ; BS2_INC_CMN_LM
+
+
+;
+; Include addition init/base code.
+;
+%ifdef BS2_WITH_TRAPS
+ %include "bootsector2-common-init-traps.mac"
+%endif
+
+
+;
+; Include common code.
+;
+%ifndef BS2_NOINC_COMMON
+ %include "bootsector2-common-routines.mac"
+%endif
+
+;
+; Include trap records if requested.
+;
+%ifdef BS2_WITH_TRAPRECS
+ %include "bootsector2-common-traprec.mac"
+%endif
+
+;
+; Map stuff for the initial environment.
+;
+%ifdef BS2_INIT_RM
+ %define TMPL_RM
+%endif
+%ifdef BS2_INIT_PE32
+ %define TMPL_PE32
+%endif
+%ifdef BS2_INIT_PP32
+ %define TMPL_PP32
+%endif
+%ifdef BS2_INIT_PAE32
+ %define TMPL_PAE32
+%endif
+%ifdef BS2_INIT_LM64
+ %define TMPL_LM64
+%endif
+%include "bootsector2-template-header.mac"
+
+
+;
+; Where we jump after initialization.
+;
+TMPL_BEGINCODE
+BITS TMPL_BITS
+bs2DoneInit:
+%ifdef BS2_INIT_SAVE_REGS
+ mov eax, cr2
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.cr2], eax
+ mov eax, cr3
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.cr3], eax
+ mov eax, cr4
+ mov [BS2_REG_SAVE_ADDR + BS2REGS.cr4], eax
+ mov byte [BS2_REG_SAVE_ADDR + BS2REGS.cBits], 16
+ xor eax, eax
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.cs], ax
+ mov ax, start
+ mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rip], eax
+%endif
+
+%ifdef BS2_WITH_TRAPRECS
+ ;
+ ; Install the trap records.
+ ;
+ BS2_TRAP_RECS_INSTALL
+%endif
+
+ ;
+ ; Detect the CPU.
+ ;
+ xor eax, eax
+ mov [g_fCpuIntel], al
+ mov [g_fCpuAmd], al
+ cpuid
+ cmp ecx, 0x444d4163
+ jne .not_amd
+ cmp edx, 0x69746e65
+ jne .not_amd
+ cmp ebx, 0x68747541
+ jne .not_amd
+ mov byte [g_fCpuAmd], 1
+ jmp .not_intel
+
+.not_amd:
+ cmp ecx, 0x6c65746e
+ jne .not_intel
+ cmp edx, 0x49656e69
+ jne .not_intel
+ cmp ebx, 0x756e6547
+ jne .not_intel
+ mov byte [g_fCpuIntel], 1
+.not_intel:
+
+ ;
+ ; Call the user 'main' procedure (shouldn't return).
+ ;
+ call main
+.panic_again
+ call Bs2Panic
+ jmp .panic_again
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-traps.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-traps.mac
new file mode 100644
index 00000000..d9069b84
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-traps.mac
@@ -0,0 +1,1619 @@
+; $Id: bootsector2-common-init-traps.mac $
+;; @file
+; Common bootsector code init, traps.
+;
+; This is included from bootsector2-common-init-code.mac and was split out of
+; that file to keep the size manageable.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef BS2_WITH_TRAPS
+ %error "huh? BS2_WITH_TRAPS is not defined!"
+%endif
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "bootsector2-structures.mac"
+%include "bootsector2-api.mac"
+
+
+;*******************************************************************************
+;* Global Variables *
+;*******************************************************************************
+BEGINCODELOW
+ALIGNDATA(8)
+;; Where to resume execution after a trap (if g_fTrapPrepared is set).
+; @internal
+g_TrapResumeRIP:
+ dq 0
+;; Set if we've prepared for a trap.
+; @internal
+g_fTrapPrepared:
+ db 0
+;; Benchmark indicator.
+; This is set to the expected trap number when we're benchmarking and 0ffh when
+; we aren't benchmarking.
+; @internal
+g_u8TrapBenchmarkNo:
+ db 0ffh
+ db 0 ; alignment padding.
+;; The last trap number.
+GLOBALNAME g_u8LastTrapNo
+ db 0
+;; The number of traps since last call to Bs2TrapReset.
+GLOBALNAME g_u32cTraps
+ dd 0
+;; The last trap error code.
+GLOBALNAME g_u64LastTrapErr
+ dq 0
+;; The register frame of the last trap (BS2REGS).
+GLOBALNAME g_LastTrapRegs
+ times (BS2REGS_size) db 0
+
+;; The RFLAGS/EFLAGS inside last invoked trap handler.
+GLOBALNAME g_u64LastTrapHandlerRFlags
+ dq 0
+;; The CS inside last invoked trap handler.
+GLOBALNAME g_u16LastTrapHandlerCS
+ dw 0
+;; The SS inside last invoked trap handler.
+GLOBALNAME g_u16LastTrapHandlerSS
+ dw 0
+ dw 0,0 ; alignment
+;; The RSP inside the last invoked trap handler, i.e. when bs2Trap_XX_32bit is
+; entered, so including fake error code and vector number.
+GLOBALNAME g_u64LastTrapHandlerRSP
+ dq 0
+
+;;
+; Pointer to an array of BS2TRAPREC1 records.
+GLOBALNAME g_paTrapRecs
+ dq 0
+;; Number of entries in the array g_paTrapRecs points to.
+GLOBALNAME g_cTrapRecs
+ dd 0
+;; The index of the last BS2TRAPREC1 we hit.
+GLOBALNAME g_iTrapRecLast
+ dd 0
+;; The base address the BS2TRAPREC.offWhere values are relative to.
+GLOBALNAME g_pTrapRecBase
+ dq 0
+
+
+;;
+; Reset all the trap globals to default.
+;
+; This undos the effect of any previous Bs2TrapPrepare call.
+;
+; @uses nothing.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2TrapReset_rm16
+ push xBP
+ mov xBP, xSP
+ push ds
+ push word 0
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov dword [g_TrapResumeRIP], 0
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], 0ffh
+ mov byte [g_fTrapPrepared], 0
+
+ pop ds
+ leave
+ ret
+ENDPROC Bs2TrapReset_rm16
+
+
+;;
+; Reset all the trap globals to default.
+;
+; This undos the effect of any previous Bs2TrapPrepare call.
+;
+; @uses nothing.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2TrapReset_p16
+ push ds
+ push BS2_SEL_DS16
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov dword [g_TrapResumeRIP], 0
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], 0ffh
+ mov byte [g_fTrapPrepared], 0
+
+ pop ds
+ ret
+ENDPROC Bs2TrapReset_p16
+
+
+
+;;
+; Reset all the trap globals to default.
+;
+; This undos the effect of any previous Bs2TrapPrepare call.
+;
+; @uses nothing.
+;
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2TrapReset_p32
+ push ds
+ push BS2_SEL_DS32
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov dword [g_TrapResumeRIP], 0
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], 0ffh
+ mov byte [g_fTrapPrepared], 0
+
+ pop ds
+ ret
+ENDPROC Bs2TrapReset_p32
+
+
+;;
+; Reset all the trap globals to default.
+;
+; This undos the effect of any previous Bs2TrapPrepare call.
+;
+; @uses nothing.
+;
+BEGINCODEHIGH
+BITS 64
+BEGINPROC Bs2TrapReset_p64
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov qword [g_u64LastTrapErr], 0
+ mov qword [g_TrapResumeRIP], 0
+ mov byte [g_u8TrapBenchmarkNo], 0ffh
+ mov byte [g_fTrapPrepared], 0
+ ret
+ENDPROC Bs2TrapReset_p64
+
+
+
+;;
+; Prepare for a test that will trap.
+;
+; @param xAX Where to resume after the trap.
+; @param dl Set to 0ffh for tests and the expected trap number when
+; preparing a benchmark.
+; @uses nothing.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2TrapPrepare_rm16
+ push xBP
+ mov xBP, xSP
+ push ds
+ push word 0
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov word [g_TrapResumeRIP], ax
+ mov word [g_TrapResumeRIP + 2], 0
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], dl
+ mov byte [g_fTrapPrepared], 1
+
+ pop ds
+ leave
+ ret
+ENDPROC Bs2TrapPrepare_rm16
+
+
+;;
+; Prepare for a test that will trap.
+;
+; @param ax Where to resume after the trap.
+; @param dl Set to 0ffh for tests and the expected trap number when
+; preparing a benchmark.
+; @uses nothing.
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC Bs2TrapPrepare_p16
+ push ds
+ push BS2_SEL_DS16
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov word [g_TrapResumeRIP], ax
+ mov word [g_TrapResumeRIP + 2], 0
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], dl
+ mov byte [g_fTrapPrepared], 1
+
+ pop ds
+ ret
+ENDPROC Bs2TrapPrepare_p16
+
+
+;;
+; Prepare for a test that will trap.
+;
+; @param eax Where to resume after the trap.
+; @param dl Set to 0ffh for tests and the expected trap number when
+; preparing a benchmark.
+; @uses nothing.
+;
+BEGINCODEHIGH
+BITS 32
+BEGINPROC Bs2TrapPrepare_p32
+ push ds
+ push BS2_SEL_DS32
+ pop ds
+
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov dword [g_u64LastTrapErr], 0
+ mov dword [g_u64LastTrapErr + 4], 0
+ mov dword [g_TrapResumeRIP], eax
+ mov dword [g_TrapResumeRIP + 4], 0
+ mov byte [g_u8TrapBenchmarkNo], dl
+ mov byte [g_fTrapPrepared], 1
+
+ pop ds
+ ret
+ENDPROC Bs2TrapPrepare_p32
+
+
+;;
+; Prepare for a test that will trap.
+;
+; @param rax Where to resume after the trap.
+; @param dl Set to 0ffh for tests and the expected trap number when
+; preparing a benchmark.
+; @uses nothing.
+;
+BEGINCODEHIGH
+BITS 64
+BEGINPROC Bs2TrapPrepare_p64
+ mov dword [g_u32cTraps], 0
+ mov byte [g_u8LastTrapNo], 0ffh
+ mov qword [g_u64LastTrapErr], 0
+ mov qword [g_TrapResumeRIP], rax
+ mov byte [g_u8TrapBenchmarkNo], dl
+ mov byte [g_fTrapPrepared], 1
+ ret
+ENDPROC Bs2TrapPrepare_p64
+
+
+BEGINCODELOW ; The TSSes, IDTs and handlers must be 16-bit addressable.
+
+%ifdef BS2_INC_CMN_PM
+;
+; 32-bit TSS (X86TSS32).
+;
+ALIGNDATA(16)
+bs2Tss32Bit:
+ dw 07fffh ; selPrev - Back link to previous task. (static)
+ dw 0h ; padding1;
+ dd BS2_R0_STACK_ADDR ; esp0 - Ring-0 stack pointer. (static)
+ dw BS2_SEL_SS32 ; ss0
+ dw 0 ; padding
+ dd BS2_R1_STACK_ADDR ; esp1 - Ring-1 stack pointer. (static)
+ dw 0 ; ss1
+ dw 0 ; padding
+ dd BS2_R2_STACK_ADDR ; esp2 - Ring-1 stack pointer. (static)
+ dw 0 ; ss2
+ dw 0 ; padding
+ dd 0ffffffffh ; cr3 - Page directory for the task. (static)
+ dd 0 ; eip - EIP before task switch.
+ dd 0 ; eflags - EFLAGS before task switch.
+ dd 0 ; eax - EAX before task switch.
+ dd 0 ; ecx - ECX before task switch.
+ dd 0 ; edx - EDX before task switch.
+ dd 0 ; ebx - EBX before task switch.
+ dd 0 ; esp - ESP before task switch.
+ dd 0 ; ebp - EBP before task switch.
+ dd 0 ; esi - ESI before task switch.
+ dd 0 ; edi - EDI before task switch.
+ dw 0, 0 ; es,pad - ES before task switch.
+ dw 0, 0 ; cs,pad - CS before task switch.
+ dw 0, 0 ; ss,pad - SS before task switch.
+ dw 0, 0 ; ds,pad - DS before task switch.
+ dw 0, 0 ; fs,pad - FS before task switch.
+ dw 0, 0 ; gs,pad - GS before task switch.
+ dw 0, 0 ; ldt,pad - LDTR before task switch.
+ dw 0 ; fDebugTrap - Debug trap flag.
+ dw 7fffh ; offIoBitmap - Offset relative to the TSS of the
+ ; start of the I/O Bitmap and the end of the
+ ; interrupt redirection bitmap.
+ ; IntRedirBitmap - 32 bytes for the virtual interrupt redirection bitmap. (VME)
+bs2Tss32BitEnd:
+times (68h - (bs2Tss32BitEnd - bs2Tss32Bit)) db 0
+times ((bs2Tss32BitEnd - bs2Tss32Bit) - 68h) db 0
+
+
+;
+; 32-bit TSS for #DF (X86TSS32).
+;
+ALIGNDATA(16)
+bs2Tss32BitDf:
+ dw 07fffh ; selPrev - Back link to previous task. (static)
+ dw 0h ; padding1;
+ dd BS2_DF_R0_STACK_ADDR ; esp0 - Ring-0 stack pointer. (static)
+ dw BS2_SEL_SS32 ; ss0
+ dw 0 ; padding
+ dd 0 ; esp1 - Ring-1 stack pointer. (static)
+ dw 0 ; ss1
+ dw 0 ; padding
+ dd 0 ; esp2 - Ring-1 stack pointer. (static)
+ dw 0 ; ss2
+ dw 0 ; padding
+ dd 0ffffffffh ; cr3 - Page directory for the task. (static)
+ dd bs2Trap_08h_32bit ; eip - EIP before task switch. */
+ dd 0 ; eflags - EFLAGS before task switch. */
+ dd 0 ; eax - EAX before task switch. */
+ dd 0 ; ecx - ECX before task switch. */
+ dd 0 ; edx - EDX before task switch. */
+ dd 0 ; ebx - EBX before task switch. */
+ dd BS2_DF_R0_STACK_ADDR ; esp - ESP before task switch. */
+ dd 0 ; ebp - EBP before task switch. */
+ dd 0 ; esi - ESI before task switch. */
+ dd 0 ; edi - EDI before task switch. */
+ dw BS2_SEL_DS32, 0 ; es,pad - ES before task switch. */
+ dw BS2_SEL_CS32, 0 ; cs,pad - CS before task switch. */
+ dw BS2_SEL_SS32, 0 ; ss,pad - SS before task switch. */
+ dw BS2_SEL_DS32, 0 ; ds,pad - DS before task switch. */
+ dw BS2_SEL_DS32, 0 ; fs,pad - FS before task switch. */
+ dw BS2_SEL_DS32, 0 ; gs,pad - GS before task switch. */
+ dw 0, 0 ; ldt,pad- LDTR before task switch. */
+ dw 0 ; fDebugTrap - Debug trap flag.
+ dw 7fffh ; offIoBitmap - Offset relative to the TSS of the
+ ; start of the I/O Bitmap and the end of the
+ ; interrupt redirection bitmap.
+ ; IntRedirBitmap - 32 bytes for the virtual interrupt redirection bitmap. (VME)
+bs2Tss32BitDfEnd:
+times (68h - (bs2Tss32BitDfEnd - bs2Tss32BitDf)) db 0
+times ((bs2Tss32BitDfEnd - bs2Tss32BitDf) - 68h) db 0
+
+
+;
+; 32-bit IDT (X86DESCGATE).
+;
+ALIGNDATA(16)
+bs2Idt32bit:
+ dw bs2Trap_00h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_01h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_02h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+bs2Idt32bit_BP:
+ dw bs2Trap_03h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_04h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_05h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_06h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_07h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw 0, BS2_SEL_TSS32_DF, 08500h, 00000h ; p=1 dpl=0 type=taskgate
+ dw bs2Trap_09h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0ah_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0bh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0ch_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0dh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0eh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_0fh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_10h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_11h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_12h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_13h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_14h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_15h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_16h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_17h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_18h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_19h_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1ah_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1bh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1ch_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1dh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1eh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2Trap_1fh_32bit, BS2_SEL_CS32, 08e00h, 00000h ; p=1 dpl=0 type=int32gate
+ dw bs2TrapService32bit,BS2_SEL_CS32,0ee00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=3 type=int32gate
+%define BS2_TRAP_SERVICE_NO 30h
+;; @todo
+bs2Idt32bitEnd
+
+;
+; 32-bit trap handlers.
+;
+BITS 32
+%macro bs2Trap_XX_32bit_macro 1
+bs2Trap_ %+ %1 %+ _32bit:
+ push %1
+ jmp bs2Trap_XX_32bit
+%endmacro
+%macro bs2Trap_XX_32bit_macro_no_err 1
+bs2Trap_ %+ %1 %+ _32bit:
+ push 0
+ push %1
+ jmp bs2Trap_XX_32bit
+%endmacro
+ bs2Trap_XX_32bit_macro_no_err 00h
+ bs2Trap_XX_32bit_macro_no_err 01h
+ bs2Trap_XX_32bit_macro_no_err 02h
+ bs2Trap_XX_32bit_macro_no_err 03h
+ bs2Trap_XX_32bit_macro_no_err 04h
+ bs2Trap_XX_32bit_macro_no_err 05h
+ bs2Trap_XX_32bit_macro_no_err 06h
+ bs2Trap_XX_32bit_macro_no_err 07h
+ bs2Trap_XX_32bit_macro 08h
+ bs2Trap_XX_32bit_macro_no_err 09h
+ bs2Trap_XX_32bit_macro 0ah
+ bs2Trap_XX_32bit_macro 0bh
+ bs2Trap_XX_32bit_macro 0ch
+ bs2Trap_XX_32bit_macro 0dh
+ bs2Trap_XX_32bit_macro 0eh
+ bs2Trap_XX_32bit_macro_no_err 0fh
+ bs2Trap_XX_32bit_macro_no_err 10h
+ bs2Trap_XX_32bit_macro 11h
+ bs2Trap_XX_32bit_macro_no_err 12h
+ bs2Trap_XX_32bit_macro_no_err 13h
+ bs2Trap_XX_32bit_macro_no_err 14h
+ bs2Trap_XX_32bit_macro_no_err 15h
+ bs2Trap_XX_32bit_macro_no_err 16h
+ bs2Trap_XX_32bit_macro_no_err 17h
+ bs2Trap_XX_32bit_macro_no_err 18h
+ bs2Trap_XX_32bit_macro_no_err 19h
+ bs2Trap_XX_32bit_macro_no_err 1ah
+ bs2Trap_XX_32bit_macro_no_err 1bh
+ bs2Trap_XX_32bit_macro_no_err 1ch
+ bs2Trap_XX_32bit_macro_no_err 1dh
+ bs2Trap_XX_32bit_macro_no_err 1eh
+ bs2Trap_XX_32bit_macro_no_err 1fh
+
+;;
+; Common 32-bit trap handler.
+;
+; return GS ebp + 2ch - v86
+; return FS ebp + 28h - v86
+; return DS ebp + 24h - v86
+; return ES ebp + 20h - v86
+; return SS ebp + 1ch - higher privilege
+; return ESP ebp + 18h - higher privilege
+; return EFLAGS ebp + 14h
+; return CS ebp + 10h
+; return EIP ebp + 0ch
+; error code ebp + 08h
+; vector # ebp + 04h
+BITS 32
+BEGINCODEHIGH
+BEGINPROC bs2Trap_XX_32bit
+ push ebp ; ebp + 00h
+ mov ebp, esp
+ pushfd ; ebp - 04h
+ push eax ; ebp - 08h
+ push ebx ; ebp - 0ch
+ push ecx ; ebp - 10h
+ push ds ; ebp - 14h
+
+ mov eax, ss ; load flat DS. Using SS here because of conforming IDTE.CS tests.
+ mov ds, ax
+
+ pushfd ; Clear the AC flag.
+ and dword [esp], ~X86_EFL_AC
+ popfd
+
+ ;
+ ; Benchmark mode? Then resume the action right away!
+ ;
+ mov eax, [ebp + 04h]
+ cmp [g_u8TrapBenchmarkNo], al
+ jne .test_mode
+ cmp byte [g_fTrapPrepared], 0
+ je .test_mode
+ mov eax, [g_TrapResumeRIP]
+ mov [ebp + 0ch], eax
+
+ pop ds
+ pop ecx
+ pop ebx
+ ;pop eax
+ ;popfd
+ leave
+ add esp, 08h ; Skip the vector # and error code.
+ xor eax, eax
+ iret
+
+
+ ;
+ ; Update the globals.
+ ;
+.test_mode:
+ xor ecx, ecx ; zero register
+ inc dword [g_u32cTraps]
+ mov eax, [ebp + 04h]
+ mov [g_u8LastTrapNo], al
+ mov eax, [ebp + 08h]
+ mov [g_u64LastTrapErr], eax
+ mov [g_u64LastTrapErr + 4], ecx
+ mov eax, [ebp - 04h]
+ mov [g_u64LastTrapHandlerRFlags], eax
+ mov dword [g_u64LastTrapHandlerRFlags + 4], ecx
+ mov ax, cs
+ mov [g_u16LastTrapHandlerCS], ax
+ mov ax, ss
+ mov [g_u16LastTrapHandlerSS], ax
+ lea eax, [ebp + 4]
+ mov [g_u64LastTrapHandlerRSP], eax
+ mov [g_u64LastTrapHandlerRSP + 4], ecx
+
+ ;
+ ; Save the registers.
+ ;
+ lea ebx, [g_LastTrapRegs]
+ mov eax, [ebp - 08h]
+ mov [ebx + BS2REGS.rax], eax
+ mov [ebx + BS2REGS.rax + 4], ecx
+ mov eax, [ebp - 0ch]
+ mov [ebx + BS2REGS.rbx], eax
+ mov [ebx + BS2REGS.rbx + 4], ecx
+ mov eax, [ebp - 10h]
+ mov [ebx + BS2REGS.rcx], eax
+ mov [ebx + BS2REGS.rcx + 4], ecx
+ mov [ebx + BS2REGS.rdx], edx
+ mov [ebx + BS2REGS.rdx + 4], ecx
+ mov [ebx + BS2REGS.rdi], edi
+ mov [ebx + BS2REGS.rdi + 4], ecx
+ mov [ebx + BS2REGS.rsi], esi
+ mov [ebx + BS2REGS.rsi + 4], ecx
+ mov eax, [ebp]
+ mov [ebx + BS2REGS.rbp], eax
+ mov [ebx + BS2REGS.rbp + 4], ecx
+ mov eax, [ebp + 0ch]
+ mov [ebx + BS2REGS.rip], eax
+ mov [ebx + BS2REGS.rip + 4], ecx
+ mov [ebx + BS2REGS.r8], ecx
+ mov [ebx + BS2REGS.r8 + 4], ecx
+ mov [ebx + BS2REGS.r9], ecx
+ mov [ebx + BS2REGS.r9 + 4], ecx
+ mov [ebx + BS2REGS.r10], ecx
+ mov [ebx + BS2REGS.r10 + 4], ecx
+ mov [ebx + BS2REGS.r11], ecx
+ mov [ebx + BS2REGS.r11 + 4], ecx
+ mov [ebx + BS2REGS.r12], ecx
+ mov [ebx + BS2REGS.r12 + 4], ecx
+ mov [ebx + BS2REGS.r13], ecx
+ mov [ebx + BS2REGS.r13 + 4], ecx
+ mov [ebx + BS2REGS.r14], ecx
+ mov [ebx + BS2REGS.r14 + 4], ecx
+ mov [ebx + BS2REGS.r15], ecx
+ mov [ebx + BS2REGS.r15 + 4], ecx
+ mov eax, [ebp + 14h]
+ mov [ebx + BS2REGS.rflags], eax
+ mov [ebx + BS2REGS.rflags+4],ecx
+ mov eax, [ebp + 10h]
+ mov [ebx + BS2REGS.cs], ax
+ mov [ebx + BS2REGS.cBits], byte 32
+
+ ; Part of the stack varies depending on the trap context.
+ test dword [ebx + BS2REGS.rflags], X86_EFL_VM
+ jnz .v86
+ test ax, 7h
+ jz .ring0
+
+.ring0:
+ lea eax, [ebp + 18h]
+ mov [ebx + BS2REGS.rsp], eax
+ mov [ebx + BS2REGS.rsp + 4], ecx
+ mov [ebx + BS2REGS.ss], ss
+ mov eax, [ebp - 14h]
+ mov [ebx + BS2REGS.ds], ax
+ mov [ebx + BS2REGS.es], es
+ mov [ebx + BS2REGS.fs], fs
+ mov [ebx + BS2REGS.gs], gs
+ jmp .do_crX
+
+.higher_privilege:
+ mov eax, [ebp + 18h]
+ mov [ebx + BS2REGS.rsp], eax
+ mov [ebx + BS2REGS.rsp + 4], ecx
+ mov eax, [ebp + 20h]
+ mov [ebx + BS2REGS.ss], ax
+ mov eax, [ebp - 14h]
+ mov [ebx + BS2REGS.ds], ax
+ mov [ebx + BS2REGS.es], es
+ mov [ebx + BS2REGS.fs], fs
+ mov [ebx + BS2REGS.gs], gs
+ jmp .do_crX
+
+.v86:
+ mov eax, [ebp + 18h]
+ mov [ebx + BS2REGS.rsp], eax
+ mov [ebx + BS2REGS.rsp + 4], ecx
+ mov eax, [ebp + 1ch]
+ mov [ebx + BS2REGS.ss], ax
+ mov eax, [ebp + 24h]
+ mov [ebx + BS2REGS.ds], ax
+ mov eax, [ebp + 20h]
+ mov [ebx + BS2REGS.es], ax
+ mov eax, [ebp + 28h]
+ mov [ebx + BS2REGS.fs], ax
+ mov eax, [ebp + 2ch]
+ mov [ebx + BS2REGS.gs], ax
+ ;jmp .do_crX
+
+.do_crX:
+ ; The CRx registers are only accessible from ring-0 (CS=conforming, CPL < 0)
+ test byte [ebx + BS2REGS.ss], 3
+ jnz .skip_crX
+ mov eax, cr0
+ mov [ebx + BS2REGS.cr0], eax
+ mov [ebx + BS2REGS.cr0 + 4], ecx
+ mov eax, cr2
+ mov [ebx + BS2REGS.cr2], eax
+ mov [ebx + BS2REGS.cr2 + 4], ecx
+ mov eax, cr3
+ mov [ebx + BS2REGS.cr3], eax
+ mov [ebx + BS2REGS.cr3 + 4], ecx
+ mov eax, cr4
+ mov [ebx + BS2REGS.cr4], eax
+ mov [ebx + BS2REGS.cr4 + 4], ecx
+ mov [ebx + BS2REGS.cr8], ecx
+ mov [ebx + BS2REGS.cr8 + 4], ecx
+.skip_crX:
+
+ ;
+ ; Advance to a prepared resume position or panic.
+ ;
+ cmp byte [g_fTrapPrepared], 0
+ je .no_resume_pos
+ mov byte [g_fTrapPrepared], 0
+ mov eax, [g_TrapResumeRIP]
+ mov [ebp + 0ch], eax
+
+.resume:
+%ifdef BS2_WITH_XCPT_DB_CLEARING_TF
+ cmp byte [ebp + 04h], X86_XCPT_DB ; make sure we won't trap again due to a TF.
+ jne .resume_no_clear_trap_flags
+ and word [ebp + 14h], ~X86_EFL_TF
+.resume_no_clear_trap_flags:
+%endif
+ pop ds
+ pop ecx
+ pop ebx
+ pop eax
+ ;popfd
+ leave
+ add esp, 8h
+ iret
+
+
+.no_resume_pos:
+ ;
+ ; Look for a trap record.
+ ;
+ mov ecx, [g_cTrapRecs] ; the number of records.
+ test ecx, ecx
+ jz .panic
+ mov eax, [g_LastTrapRegs + BS2REGS.rip]
+ sub eax, [g_pTrapRecBase] ; the offWhere we're looking for.
+ jb .panic
+
+ ; Look starting at the previous record first.
+ mov ebx, [g_iTrapRecLast]
+ sub ecx, ebx
+ jbe .traprec_loop2 ; g_iTrapRecLast is out of range.
+ shl ebx, BS2TRAPREC_SIZE_SHIFT
+ add ebx, [g_paTrapRecs] ; ebx points to the record we hit last time.
+.traprec_loop1_next:
+ cmp [ebx + BS2TRAPREC.offWhere], eax
+ je .traprec_found
+ add ebx, BS2TRAPREC_size
+ dec ecx
+ jnz .traprec_loop1_next
+
+ ; Start searching from the start, stopping at the previous record.
+.traprec_loop2:
+ mov ecx, [g_iTrapRecLast]
+ or ecx, ecx
+ jz .panic ; not found.
+ mov ebx, [g_paTrapRecs]
+.traprec_loop2_next:
+ cmp [ebx + BS2TRAPREC.offWhere], eax
+ je .traprec_found
+ add ebx, BS2TRAPREC_size
+ dec ecx
+ jnz .traprec_loop2_next
+ jmp .panic ; not found
+
+.traprec_found:
+ ; Remember the hit for the next trap.
+ mov eax, ebx
+ sub eax, [g_paTrapRecs]
+ shr eax, BS2TRAPREC_SIZE_SHIFT
+ mov [g_iTrapRecLast], eax
+
+ ;
+ ; Fail the test if we got the wrong trap or error code.
+ ;
+ mov al, [g_u8LastTrapNo]
+ cmp al, [ebx + BS2TRAPREC.u8TrapNo]
+ je .traprec_ok_trap
+ push eax
+ movzx eax, byte [ebx + BS2TRAPREC.u8TrapNo]
+ push eax
+ push .s_szWrongTrap
+ call NAME(TestFailedF_p32)
+ add esp, 12
+
+.traprec_ok_trap:
+ mov ax, [g_u64LastTrapErr]
+ cmp ax, [ebx + BS2TRAPREC.u16ErrCd]
+ je .traprec_ok_err_cd
+ push eax
+ movzx eax, word [ebx + BS2TRAPREC.u16ErrCd]
+ push eax
+ push .s_szWrongErrCd
+ call NAME(TestFailedF_p32)
+ add esp, 12
+
+.traprec_ok_err_cd:
+ ;
+ ; Advance the EIP and resume execution.
+ ;
+ movzx eax, byte [ebx + BS2TRAPREC.offResumeAddend]
+ add eax, [g_LastTrapRegs + BS2REGS.rip]
+ mov [ebp + 0ch], eax
+ jmp .resume
+
+
+ ;
+ ; Write panic message and then halt.
+ ;
+.panic:
+ push dword [g_LastTrapRegs + BS2REGS.rflags]
+ push dword [g_LastTrapRegs + BS2REGS.ss]
+ push dword [g_LastTrapRegs + BS2REGS.gs]
+ push dword [g_LastTrapRegs + BS2REGS.fs]
+ push dword [g_LastTrapRegs + BS2REGS.es]
+ push dword [g_LastTrapRegs + BS2REGS.ds]
+ push dword [g_LastTrapRegs + BS2REGS.cs]
+ ; line break
+ push dword [g_LastTrapRegs + BS2REGS.cr4]
+ push dword [g_LastTrapRegs + BS2REGS.cr3]
+ push dword [g_LastTrapRegs + BS2REGS.cr0]
+ push dword [g_LastTrapRegs + BS2REGS.rbp]
+ push dword [g_LastTrapRegs + BS2REGS.rsp]
+ push dword [g_LastTrapRegs + BS2REGS.rip]
+ ; line break
+ push dword [g_LastTrapRegs + BS2REGS.rdi]
+ push dword [g_LastTrapRegs + BS2REGS.rsi]
+ push dword [g_LastTrapRegs + BS2REGS.rdx]
+ push dword [g_LastTrapRegs + BS2REGS.rcx]
+ push dword [g_LastTrapRegs + BS2REGS.rbx]
+ push dword [g_LastTrapRegs + BS2REGS.rax]
+ ; line break
+ mov eax, [ebp + 08h]
+ push eax
+ mov eax, cr2
+ push eax
+ mov eax, [ebp + 0ch]
+ push eax
+ movzx eax, word [ebp + 10h]
+ push eax
+ movzx eax, byte [ebp + 04h]
+ push eax
+ push .s_szPanicMsg
+ call NAME(TestFailedF_p32)
+
+ call Bs2Panic
+ jmp .panic ; paranoia
+
+.s_szPanicMsg:
+ db 'trap #%RX8 at %RX16:%RX32 cr2=%RX32 err=%RX32', 13, 10
+ db 'eax=%RX32 ebx=%RX32 ecx=%RX32 edx=%RX32 esi=%RX32 edi=%RX32', 13, 10
+ db 'eip=%RX32 esp=%RX32 ebp=%RX32 cr0=%RX32 cr3=%RX32 cr4=%RX32', 13, 10
+ db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16 eflags=%RX32', 13, 10
+ db 0
+.s_szWrongTrap:
+ db 'Expected trap %RX8 got %RX8', 13, 10, 0
+.s_szWrongErrCd:
+ db 'Expected errcd %RX16 got %RX16', 13, 10, 0
+ENDPROC bs2Trap_XX_32bit
+
+;;
+; Service IRQ handler, 32-bit version.
+;
+; Takes requests in eax and later maybe parameters in other registers.
+;
+; return GS ebp + 24h - v86
+; return FS ebp + 20h - v86
+; return DS ebp + 1ch - v86
+; return ES ebp + 18h - v86
+; return SS ebp + 14h - higher privilege
+; return ESP ebp + 10h - higher privilege
+; return EFLAGS ebp + 0ch
+; return CS ebp + 08h
+; return EIP ebp + 04h
+BEGINCODELOW
+BEGINPROC bs2TrapService32bit
+ jmp .highsegment
+BEGINCODEHIGH
+.highsegment:
+ push ebp ; ebp
+ mov ebp, esp
+ push eax ; ebp - 04h
+ push edx ; ebp - 08h
+ push ecx ; ebp - 0ch
+ push ebx ; ebp - 10h
+ push ds ; ebp - 14h
+
+ mov dx, ss
+ mov ds, dx
+
+ ;
+ ; Classify the caller context in cl.
+ ;; @todo What if CS on the stack is conforming?
+ ;
+%define BS2_TRP_SRV_CALLER_SAME_RING 0
+%define BS2_TRP_SRV_CALLER_OTHER_RING 1
+%define BS2_TRP_SRV_CALLER_VM 2
+ test dword [ebp + 0ch], X86_EFL_VM
+ jnz .vm_ctx
+
+ mov cx, ss
+ mov ch, [ebp + 08h] ; cs
+ and cx, 00303h
+ cmp ch, cl
+ jz .same_ctx
+ mov cl, BS2_TRP_SRV_CALLER_OTHER_RING
+ jmp .done_ctx
+.vm_ctx:
+ mov cl, BS2_TRP_SRV_CALLER_VM
+ jmp .done_ctx
+.same_ctx:
+ mov cl, BS2_TRP_SRV_CALLER_SAME_RING
+.done_ctx:
+
+ ;
+ ; Switch (eax).
+ ;
+ cmp eax, BS2_SYSCALL_TO_RING3
+ jbe .to_ringX
+
+ ; Unknown request.
+.failure:
+ mov eax, -1
+.return: ; careful with ebp here!
+ pop ds
+ pop ebx
+ pop ecx
+ pop edx
+ ;pop eax
+ leave
+ iretd
+
+ ;
+ ; Switching to the ring specified by eax.
+ ; Annoying that ss:esp isn't always restored.
+ ;
+.to_ringX:
+ cmp cl, BS2_TRP_SRV_CALLER_VM
+ je .failure
+ sub al, BS2_SYSCALL_TO_RING0
+
+ ; Fake missing stack registers if necessary.
+ cmp cl, BS2_TRP_SRV_CALLER_SAME_RING
+ jnz .have_stack_regs
+
+ sub esp, 8h
+ sub ebp, 8h
+ xor ebx, ebx
+.move_more:
+ mov edx, [esp + 8 + ebx]
+ mov [esp + ebx], edx
+ add ebx, 4
+ cmp ebx, 9*4
+ jb .move_more
+
+ mov dx, ss
+ mov [ebp + 14h], edx
+ lea edx, [ebp + 18h]
+ mov [ebp + 10h], edx
+
+.have_stack_regs:
+ ; Translate the selector registers
+ mov dx, [ebp - 14h]
+ call bs2SRegToRing
+ mov [ebp - 14h], dx
+
+ mov dx, es
+ call bs2SRegToRing
+ mov es, dx
+
+ mov dx, fs
+ call bs2SRegToRing
+ mov fs, dx
+
+ mov dx, gs
+ call bs2SRegToRing
+ mov gs, dx
+
+ mov dx, [ebp + 08h] ; cs
+ call bs2SRegToRing
+ mov [ebp + 08h], dx
+
+ mov dx, [ebp + 14h] ; ss
+ call bs2SRegToRing
+ mov [ebp + 14h], dx
+
+ or dword [ebp + 0ch], X86_EFL_IOPL ; set IOPL=3
+
+ ; If the desired target is ring-0 we cannot use iret.
+ cmp al, 0
+ je .iret_to_ring_with_stack
+
+.done_success:
+ xor eax, eax
+ jmp .return
+
+.iret_to_ring_with_stack:
+ ;
+ ; Move the iret-to-same-ring to the desired return position. By also
+ ; moving the saved ebp we make the leave instruction do stack
+ ; adjusting/switching for us.
+ ;
+ cli ; paranoia, it's disable already.
+ mov eax, [ebp + 10h]
+ lea edx, [ebp + 18h]
+ cmp eax, edx
+ lea ecx, [ebp + 08h] ; same stack, just shifted 8 bytes
+ je .move_iret_and_ebp
+ mov ecx, [ebp + 10h] ; different stack.
+ sub ecx, 10h
+.move_iret_and_ebp:
+ mov edx, [ebp + 0ch]
+ mov eax, [ebp + 08h]
+ mov [ecx + 0ch], edx
+ mov [ecx + 08h], eax
+ mov edx, [ebp + 04h]
+ mov eax, [ebp + 00h]
+ mov [ecx + 04h], edx
+ mov [ecx + 00h], eax
+ mov ebp, ecx
+ xor eax, eax
+ jmp .return
+
+ENDPROC bs2TrapService32bit
+
+%endif ; BS2_INC_CMN_PM
+
+
+%ifdef BS2_INC_CMN_LM
+;
+; 64-bit TSS (X86TSS64).
+;
+BEGINCODELOW
+ALIGNDATA(16)
+bs2Tss64Bit:
+ dd 0 ; 00h - u32Reserved - Reserved.
+ dq BS2_R0_STACK_ADDR ; 04h - rsp0 - Ring-0 stack pointer. (static)
+ dq BS2_R1_STACK_ADDR ; 1ch - rsp1 - Ring-1 stack pointer. (static)
+ dq BS2_R2_STACK_ADDR ; 14h - rsp2 - Ring-2 stack pointer. (static)
+ dq 0 ; 2ch - reserved
+ dq BS2_DF_R0_STACK_ADDR ; 24h - ist1;
+ dq BS2_R0_STACK_ADDR ; 3ch - ist2;
+ dq BS2_R0_STACK_ADDR ; 34h - ist3;
+ dq BS2_R0_STACK_ADDR ; 4ch - ist4;
+ dq BS2_R0_STACK_ADDR ; 44h - ist5;
+ dq BS2_R0_STACK_ADDR ; 5ch - ist6;
+ dq BS2_R0_STACK_ADDR ; 54h - ist7;
+ dw 0,0,0,0,0 ; 6ch - reserved
+ dw 0 ; 76h - offIoBitmap - Offset relative to the TSS of the
+ ; 00h - start of the I/O Bitmap and the end of the
+ ; 00h - interrupt redirection bitmap.
+bs2Tss64BitEnd:
+times (68h - (bs2Tss64BitEnd - bs2Tss64Bit)) db 0
+times ((bs2Tss64BitEnd - bs2Tss64Bit) - 68h) db 0
+
+;
+; 64-bit IDT (X86DESC64GATE).
+;
+BEGINCODELOW
+ALIGNDATA(16)
+bs2Idt64bit:
+ dw bs2Trap_00h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_01h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_02h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+bs2Idt64bit_BP:
+ dw bs2Trap_03h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_04h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_05h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_06h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_07h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_08h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_09h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0ah_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0bh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0ch_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0dh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0eh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_0fh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_10h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_11h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_12h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_13h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_14h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_15h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_16h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_17h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_18h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_19h_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1ah_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1bh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1ch_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1dh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1eh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2Trap_1fh_64bit, BS2_SEL_CS64, 08e00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=0 type=int64gate
+ dw bs2TrapService64bit,BS2_SEL_CS64,0ee00h, 00000h, 0, 0, 0, 0 ; p=1 dpl=3 type=int64gate
+bs2Idt64bitEnd
+
+
+;
+; 64-bit trap handlers.
+;
+BITS 64
+%macro bs2Trap_XX_64bit_macro 1
+BEGINCODELOW
+bs2Trap_ %+ %1 %+ _64bit:
+ push %1
+ jmp bs2Trap_XX_64bit
+%endmacro
+%macro bs2Trap_XX_64bit_macro_no_err 1
+bs2Trap_ %+ %1 %+ _64bit:
+ push 0
+ push %1
+ jmp bs2Trap_XX_64bit
+%endmacro
+ bs2Trap_XX_64bit_macro_no_err 00h
+ bs2Trap_XX_64bit_macro_no_err 01h
+ bs2Trap_XX_64bit_macro_no_err 02h
+ bs2Trap_XX_64bit_macro_no_err 03h
+ bs2Trap_XX_64bit_macro_no_err 04h
+ bs2Trap_XX_64bit_macro_no_err 05h
+ bs2Trap_XX_64bit_macro_no_err 06h
+ bs2Trap_XX_64bit_macro_no_err 07h
+ bs2Trap_XX_64bit_macro 08h
+ bs2Trap_XX_64bit_macro_no_err 09h
+ bs2Trap_XX_64bit_macro 0ah
+ bs2Trap_XX_64bit_macro 0bh
+ bs2Trap_XX_64bit_macro 0ch
+ bs2Trap_XX_64bit_macro 0dh
+ bs2Trap_XX_64bit_macro 0eh
+ bs2Trap_XX_64bit_macro_no_err 0fh
+ bs2Trap_XX_64bit_macro_no_err 10h
+ bs2Trap_XX_64bit_macro 11h
+ bs2Trap_XX_64bit_macro_no_err 12h
+ bs2Trap_XX_64bit_macro_no_err 13h
+ bs2Trap_XX_64bit_macro_no_err 14h
+ bs2Trap_XX_64bit_macro_no_err 15h
+ bs2Trap_XX_64bit_macro_no_err 16h
+ bs2Trap_XX_64bit_macro_no_err 17h
+ bs2Trap_XX_64bit_macro_no_err 18h
+ bs2Trap_XX_64bit_macro_no_err 19h
+ bs2Trap_XX_64bit_macro_no_err 1ah
+ bs2Trap_XX_64bit_macro_no_err 1bh
+ bs2Trap_XX_64bit_macro_no_err 1ch
+ bs2Trap_XX_64bit_macro_no_err 1dh
+ bs2Trap_XX_64bit_macro_no_err 1eh
+ bs2Trap_XX_64bit_macro_no_err 1fh
+
+;;
+; Common 64-bit trap handler.
+;
+; return SS rbp + 38h
+; return RSP rbp + 30h
+; return RFLAGS rbp + 28h
+; return CS rbp + 20h
+; return RIP rbp + 18h
+; error code rbp + 10h
+; vector # rbp + 08h
+BEGINCODEHIGH
+BEGINPROC bs2Trap_XX_64bit
+ push rbp ; rbp + 00h
+ mov rbp, rsp
+ pushfq ; rbp - 08h
+ push rax ; rbp - 10h
+ push rbx ; rbp - 18h
+
+ ;
+ ; Benchmark mode? Then resume the action right away!
+ ;
+ mov rax, [rbp + 08h]
+ cmp [g_u8TrapBenchmarkNo], al
+ jne .test_mode
+ cmp byte [g_fTrapPrepared], 0
+ je .test_mode
+ mov rax, [g_TrapResumeRIP]
+ mov [rbp + 18h], rax
+
+ pop rbx
+ ;pop rax
+ ;popfq
+ leave
+ add rsp, 10h ; Skip the vector # and error code.
+ xor rax, rax
+ iretq
+
+ ;
+ ; Save the trap information
+ ;
+.test_mode:
+ inc dword [g_u32cTraps]
+ mov rax, [rbp + 08h]
+ mov [g_u8LastTrapNo], al
+ mov rax, [rbp + 10h]
+ mov [g_u64LastTrapErr], rax
+ mov rax, [rbp - 08h]
+ mov [g_u64LastTrapHandlerRFlags], rax
+ mov ax, cs
+ mov [g_u16LastTrapHandlerCS], ax
+ mov ax, ss
+ mov [g_u16LastTrapHandlerSS], ax
+ lea rax, [rbp + 8]
+ mov [g_u64LastTrapHandlerRSP], rax
+
+ ; Save the registers.
+ lea rbx, [g_LastTrapRegs]
+ mov rax, [rbp - 10h]
+ mov [rbx + BS2REGS.rax], rax
+ mov rax, [rbp - 18h]
+ mov [rbx + BS2REGS.rbx], rax
+ mov [rbx + BS2REGS.rcx], rcx
+ mov [rbx + BS2REGS.rdx], rdx
+ mov [rbx + BS2REGS.rdi], rdi
+ mov [rbx + BS2REGS.rsi], rsi
+ mov rax, [rbp]
+ mov [rbx + BS2REGS.rbp], rax
+ mov rax, [rbp + 30h]
+ mov [rbx + BS2REGS.rsp], rax
+ mov rax, [rbp + 18h]
+ mov [rbx + BS2REGS.rip], rax
+ mov [rbx + BS2REGS.r8], r8
+ mov [rbx + BS2REGS.r9], r9
+ mov [rbx + BS2REGS.r10], r10
+ mov [rbx + BS2REGS.r11], r11
+ mov [rbx + BS2REGS.r12], r12
+ mov [rbx + BS2REGS.r13], r13
+ mov [rbx + BS2REGS.r14], r14
+ mov [rbx + BS2REGS.r15], r15
+ mov rax, [rbp + 28h]
+ mov [rbx + BS2REGS.rflags], rax
+ mov rax, [rbp + 20h]
+ mov [rbx + BS2REGS.cs], ax
+ mov [rbx + BS2REGS.ds], ds
+ mov [rbx + BS2REGS.es], es
+ mov [rbx + BS2REGS.fs], fs
+ mov [rbx + BS2REGS.gs], gs
+ mov rax, [rbp + 38h]
+ mov [rbx + BS2REGS.ss], ax
+ mov [rbx + BS2REGS.cBits], byte 64
+
+ ; The CRx registers are only accessible from ring-0 (CS=conforming, CPL < 0)
+ test byte [rbx + BS2REGS.ss], 3
+ jnz .skip_crX
+ mov rax, cr0
+ mov [rbx + BS2REGS.cr0], rax
+ mov rax, cr2
+ mov [rbx + BS2REGS.cr2], rax
+ mov rax, cr3
+ mov [rbx + BS2REGS.cr3], rax
+ mov rax, cr4
+ mov [rbx + BS2REGS.cr4], rax
+ mov rax, cr8
+ mov [rbx + BS2REGS.cr8], rax
+.skip_crX:
+
+ ;
+ ; Advance to a prepared resume position or panic.
+ ;
+ cmp byte [g_fTrapPrepared], 0
+ je .no_resume_pos
+ mov byte [g_fTrapPrepared], 0
+ mov rax, [g_TrapResumeRIP]
+ mov [rbp + 18h], rax
+ jmp .resume
+
+.resume:
+%ifdef BS2_WITH_XCPT_DB_CLEARING_TF
+ cmp byte [rbp + 08h], X86_XCPT_DB ; make sure we won't trap again due to a TF.
+ jne .resume_no_clear_trap_flags
+ and word [rbp + 28h], ~X86_EFL_TF
+.resume_no_clear_trap_flags:
+%endif
+ pop rbx
+ pop rax
+ ;popfq
+ leave
+ add rsp, 10h
+ iretq
+
+
+.no_resume_pos:
+ ;
+ ; Look for a trap record.
+ ;
+ push rcx
+
+ mov ecx, [g_cTrapRecs] ; the number of records.
+ test ecx, ecx
+ jz .panic
+ mov rax, [g_LastTrapRegs + BS2REGS.rip]
+ sub rax, [g_pTrapRecBase] ; the offWhere we're looking for.
+ jb .panic
+ mov rbx, _4G
+ cmp rax, rbx
+ jae .panic ; out of range.
+
+ ; Look starting at the previous record first.
+ mov ebx, [g_iTrapRecLast]
+ sub ecx, ebx
+ jbe .traprec_loop2 ; g_iTrapRecLast is out of range.
+ shl rbx, BS2TRAPREC_SIZE_SHIFT
+ add rbx, [g_paTrapRecs] ; ebx points to the record we hit last time.
+.traprec_loop1_next:
+ cmp [rbx + BS2TRAPREC.offWhere], eax
+ je .traprec_found
+ add rbx, BS2TRAPREC_size
+ dec ecx
+ jnz .traprec_loop1_next
+
+ ; Start searching from the start, stopping at the previous record.
+.traprec_loop2:
+ mov ecx, [g_iTrapRecLast]
+ or ecx, ecx
+ jz .panic ; not found.
+ mov rbx, [g_paTrapRecs]
+.traprec_loop2_next:
+ cmp [rbx + BS2TRAPREC.offWhere], eax
+ je .traprec_found
+ add rbx, BS2TRAPREC_size
+ dec ecx
+ jnz .traprec_loop2_next
+ jmp .panic ; not found
+
+.traprec_found:
+ ; Remember the hit for the next trap.
+ mov rax, rbx
+ sub rax, [g_paTrapRecs]
+ shr rax, BS2TRAPREC_SIZE_SHIFT
+ mov [g_iTrapRecLast], eax
+
+ ;
+ ; Fail the test if we got the wrong trap or error code.
+ ;
+ mov al, [g_u8LastTrapNo wrt rip]
+ cmp al, [rbx + BS2TRAPREC.u8TrapNo]
+ je .traprec_ok_trap
+ push rax
+ movzx rax, byte [rbx + BS2TRAPREC.u8TrapNo]
+ push rax
+ push .s_szWrongTrap
+ call NAME(TestFailedF_p64)
+ add rsp, 24
+
+.traprec_ok_trap:
+ mov ax, [g_u64LastTrapErr wrt rip]
+ cmp ax, [rbx + BS2TRAPREC.u16ErrCd]
+ je .traprec_ok_err_cd
+ push rax
+ movzx rax, word [rbx + BS2TRAPREC.u16ErrCd]
+ push rax
+ push .s_szWrongErrCd
+ call NAME(TestFailedF_p64)
+ add rsp, 24
+
+.traprec_ok_err_cd:
+ ;
+ ; Advance the EIP and resume execution.
+ ;
+ movzx rax, byte [rbx + BS2TRAPREC.offResumeAddend]
+ add rax, [g_LastTrapRegs + BS2REGS.rip]
+ mov [rbp + 18h], rax
+
+ pop rcx
+ jmp .resume
+
+
+ ;
+ ; Format a panic message and halt.
+ ;
+.panic:
+ lea rbx, [g_LastTrapRegs]
+ ; line break
+ movzx eax, word [rbx + BS2REGS.ss]
+ push rax
+ movzx eax, word [rbx + BS2REGS.gs]
+ push rax
+ movzx eax, word [rbx + BS2REGS.fs]
+ push rax
+ movzx eax, word [rbx + BS2REGS.es]
+ push rax
+ movzx eax, word [rbx + BS2REGS.ds]
+ push rax
+ movzx eax, word [rbx + BS2REGS.cs]
+ push rax
+ ; line break
+ push qword [rbx + BS2REGS.rbp]
+ push qword [rbx + BS2REGS.rsp]
+ push qword [rbx + BS2REGS.rip]
+ ; line break
+ push qword [rbx + BS2REGS.rflags]
+ push qword [rbx + BS2REGS.r15]
+ push qword [rbx + BS2REGS.r14]
+ ; line break
+ push qword [rbx + BS2REGS.r13]
+ push qword [rbx + BS2REGS.r12]
+ push qword [rbx + BS2REGS.r11]
+ ; line break
+ push qword [rbx + BS2REGS.r10]
+ push qword [rbx + BS2REGS.r9]
+ push qword [rbx + BS2REGS.r8]
+ ; line break
+ push qword [rbx + BS2REGS.rdi]
+ push qword [rbx + BS2REGS.rsi]
+ push qword [rbx + BS2REGS.rdx]
+ ; line break
+ push qword [rbx + BS2REGS.rcx]
+ push qword [rbx + BS2REGS.rbx]
+ push qword [rbx + BS2REGS.rax]
+ ; line break
+ mov eax, [rbx + BS2REGS.cr8]
+ push rax
+ mov eax, [rbx + BS2REGS.cr4]
+ push rax
+ mov eax, [rbx + BS2REGS.cr3]
+ push rax
+ mov eax, [rbx + BS2REGS.cr0]
+ push rax
+ ; line break
+ push qword [rbp + 10h]
+ push qword [rbx + BS2REGS.cr2]
+ push qword [rbx + BS2REGS.rip]
+ movzx eax, word [rbp + BS2REGS.ss]
+ push rax
+ movzx eax, byte [rbp + 08h]
+ push rax
+ push .s_szPanicMsg
+ call NAME(TestFailedF_p64)
+
+ call Bs2Panic
+ jmp .panic ; paranoia
+
+.s_szPanicMsg:
+ db 'trap #%RX8 at %RX16:%RX64 cr2=%RX64 err=%RX64', 13, 10
+ db 'cr0=%RX64 cr3=%RX64 cr4=%RX64 cr8=%RX16', 13, 10
+ db 'rax=%RX64 rbx=%RX64 rcx=%RX64', 13, 10
+ db 'rdx=%RX64 rsi=%RX64 rdi=%RX64', 13, 10
+ db 'r8 =%RX64 r9 =%RX64 r10=%RX64', 13, 10
+ db 'r11=%RX64 r12=%RX64 r13=%RX64', 13, 10
+ db 'r14=%RX64 r15=%RX64 rfl=%RX64', 13, 10
+ db 'rip=%RX64 rsp=%RX64 rbp=%RX64 ', 13, 10
+ db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16', 13, 10
+ db 0
+.s_szWrongTrap:
+ db 'Expected trap %RX8 got %RX8', 13, 10, 0
+.s_szWrongErrCd:
+ db 'Expected errcd %RX16 got %RX16', 13, 10, 0
+ENDPROC bs2Trap_XX_64bit
+
+
+;;
+; Service IRQ handler.
+;
+; Takes requests in eax and later maybe parameters in other registers.
+;
+; return SS rbp + 28h
+; return RSP rbp + 20h
+; return RFLAGS rbp + 18h
+; return CS rbp + 10h
+; return RIP rbp + 08h
+BEGINCODELOW
+BEGINPROC bs2TrapService64bit
+ jmp .highsegment
+BEGINCODEHIGH
+.highsegment:
+ push rbp
+ mov rbp, rsp
+ push rax
+ push rdx
+ push rcx
+
+
+ ;
+ ; Switch (eax).
+ ;
+ cmp eax, BS2_SYSCALL_TO_RING3
+ jbe .to_ringX
+
+ ; Unknown request.
+ mov rax, -1
+.return:
+ pop rcx
+ pop rdx
+ ;pop rax
+ leave
+ iretq
+
+ ;
+ ; Switching to the ring specified by eax.
+ ;
+.to_ringX:
+ sub eax, BS2_SYSCALL_TO_RING0 ; al = new ring number.
+
+ mov dx, ds
+ call bs2SRegToRing
+ mov ds, dx
+
+ mov dx, es
+ call bs2SRegToRing
+ mov es, dx
+
+ mov dx, fs
+ call bs2SRegToRing
+ mov fs, dx
+
+ mov dx, gs
+ call bs2SRegToRing
+ mov gs, dx
+
+ mov dx, [rbp + 10h] ; cs
+ call bs2SRegToRing
+ mov [rbp + 10h], dx
+
+ mov dx, [rbp + 28h] ; ss
+ call bs2SRegToRing
+ mov [rbp + 28h], dx
+
+ or dword [ebp + 18h], X86_EFL_IOPL ; set IOPL=3
+
+ jmp .done_success
+
+.done_success:
+ xor eax, eax
+ jmp .return
+
+ENDPROC bs2TrapService64bit
+
+%endif ; BS2_INC_CMN_LM
+
+
+;;
+; Converts a segment value (dx) to the ring specified by al.
+;
+; If the selector isn't a known CS, DS or SS selector it will be set to null.
+;
+; @returns dx
+; @param al The desired ring.
+; @param dx The segment to convert.
+;
+; @remarks WARNING! This has to work exactly the same both in 32-bit and 64-bit mode.
+;
+BEGINCODEHIGH
+BITS 32
+BEGINPROC bs2SRegToRing
+ ;
+ ; Classify the incoming selector.
+ ;
+ cmp dx, BS2_SEL_R0_BASE
+ jb .null
+ cmp dx, BS2_SEL_R0_BASE + BS2_SEL_GRP_SIZE
+ jb .ring0
+
+ cmp dx, BS2_SEL_R1_BASE
+ jb .miss
+ cmp dx, BS2_SEL_R1_BASE + BS2_SEL_GRP_SIZE
+ jb .ring1
+
+ cmp dx, BS2_SEL_R2_BASE
+ jb .miss
+ cmp dx, BS2_SEL_R2_BASE + BS2_SEL_GRP_SIZE
+ jb .ring2
+
+ cmp dx, BS2_SEL_R3_BASE
+ jb .miss
+ cmp dx, BS2_SEL_R3_BASE + BS2_SEL_GRP_SIZE
+ jb .ring3
+ jmp .miss
+
+ ;
+ ; Convert the incoming selector to ring-0 and then from ring-0 to the
+ ; desired one.
+ ;
+.ring0:
+ cmp al, 0
+ je .done
+
+ add dx, BS2_SEL_R1_BASE - BS2_SEL_R0_BASE
+ cmp al, 1
+ je .done
+
+ add dx, BS2_SEL_R2_BASE - BS2_SEL_R1_BASE
+ cmp al, 2
+ je .done
+
+ add dx, BS2_SEL_R3_BASE - BS2_SEL_R2_BASE
+ cmp al, 3
+ je .done
+.panic:
+ hlt
+ jmp .panic
+
+.ring1:
+ sub dx, BS2_SEL_R1_BASE - BS2_SEL_R0_BASE
+ jmp .ring0
+.ring2:
+ sub dx, BS2_SEL_R2_BASE - BS2_SEL_R0_BASE
+ jmp .ring0
+.ring3:
+ sub dx, BS2_SEL_R3_BASE - BS2_SEL_R0_BASE
+ jmp .ring0
+
+.done:
+ and dl, ~3h
+ or dl, al ; set the RPL
+ ret
+
+.miss:
+.null:
+ xor dx, dx
+ ret
+ENDPROC bs2SRegToRing
+
+BEGINCODELOW
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-macros-1.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-macros-1.mac
new file mode 100644
index 00000000..4596393a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-macros-1.mac
@@ -0,0 +1,60 @@
+; $Id: bootsector2-common-macros-1.mac $
+;; @file
+; Common bootsector macros.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;;
+; Asserts a test.
+;
+; @param %1 First cmp operand.
+; @param %2 First cmp operand.
+; @param %3 Which kind of conditional jump to make
+; @param %4 The message to print (format string, no arguments please).
+;
+%macro TEST_ASSERT_SIMPLE 4
+ cmp %1, %2
+ %3 %%.ok
+ push dword __LINE__
+ %ifdef TMPL_16BIT
+ push ds
+ %endif
+ push %%.s_szMsg
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB*2
+ jmp %%.ok
+%%.s_szMsg: db %4, " (0x%RX32)", 0
+%%.ok:
+%endmacro
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac
new file mode 100644
index 00000000..bc8802c4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac
@@ -0,0 +1,2655 @@
+; $Id: bootsector2-common-routines-template-1.mac $
+;; @file
+; bootsector2 common routines - template containing code common to related modes.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bootsector2-template-header.mac"
+%include "VBox/bios.mac"
+
+ALIGNCODE(32)
+GLOBALNAME TMPL_NM_CMN(g_szMode)
+ db TMPL_MODE_STR, 0
+
+
+;;
+; Shutdown routine.
+;
+; Does not return.
+;
+; @uses N/A
+;
+BEGINPROC TMPL_NM_CMN(Shutdown)
+%ifdef TMPL_16BIT
+ jmp Bs2Shutdown
+%else
+ cli
+ mov bl, 64
+ mov ax, ss
+ mov ds, ax
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT
+.retry:
+ mov ecx, 8
+ mov esi, .s_szShutdown
+ rep outsb
+ xchg dx, ax ; alternate between the new (VBox) and old (Bochs) ports.
+ dec bl
+ jnz .retry
+ ; Shutdown failed!
+ jmp Bs2Panic
+.s_szShutdown:
+ db 'Shutdown', 0
+%endif
+ENDPROC TMPL_NM_CMN(Shutdown)
+
+
+;;
+; Prints a 32-bit unsigned integer on the screen.
+;
+; @param eax The value to print.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(PrintU32)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ push sBX
+%ifdef TMPL_16BIT
+ push ds
+
+ mov cx, ss
+ mov ds, cx
+%endif
+
+ ; Allocate a stack buffer and terminate it. ds:bx points ot the end.
+ sub xSP, 30h
+ mov xBX, xSP
+ add xBX, 2fh
+ mov byte [xBX], 0
+
+ mov ecx, 10 ; what to divide by
+.next:
+ xor edx, edx
+ div ecx ; edx:eax / ecx -> eax and rest in edx.
+ add dl, '0'
+ dec xBX
+ mov [xBX], dl
+ cmp eax, 0
+ jnz .next
+
+ ; Print the string.
+ mov xAX, xBX
+ call TMPL_NM_CMN(PrintStr)
+
+ add xSP, 30h
+%ifdef TMPL_16BIT
+ pop ds
+%endif
+ pop sBX
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(PrintU32)
+
+
+;;
+; Equivalent to RTPrintf, but a max output length of 1KB.
+;
+; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the
+; caller does the cleanup (cdecl sans volatile regs).
+;
+; @param fpszFormat The format string (far pointer on 16-bit
+; systems). See StrFormatV for format details.
+; @param ... Zero or more format string arguments.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(PrintF)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+ push xCX
+ push xBX
+%ifdef TMPL_16BIT
+ push ds
+ push es
+ push fs
+%endif
+ sub xSP, 0400h ; string buffer (1024 bytes)
+
+ ;
+ ; Format the failure string and call TestFailed.
+ ;
+%ifdef TMPL_16BIT
+ mov ax, ss ; buffer address.
+ mov ds, ax
+ mov ax, sp
+ mov xDX, 0400h ; buffer size.
+ les cx, [bp + 4] ; format string
+ mov bx, ss ; argument list
+ mov fs, bx
+ mov bx, bp
+ add bx, 8
+%else
+ mov xAX, xSP ; buffer address
+ mov xDX, 0400h ; buffer size
+ mov xCX, [xBP + xCB * 2] ; format string
+ lea xBX, [xBP + xCB * 3] ; argument list.
+%endif
+ call TMPL_NM_CMN(StrFormatV)
+ call TMPL_NM_CMN(PrintStr)
+
+ add xSP, 0400h
+%ifdef TMPL_16BIT
+ pop fs
+ pop es
+ pop ds
+%endif
+ pop xBX
+ pop xCX
+ pop xDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(PrintF)
+
+
+;;
+; Print a string followed by a semicolon and at least one space.
+;
+; @param ds:ax The string to print.
+; @param dx The desired minimum length of the output. That is
+; string + colon + spaces.
+; @uses nothing.
+;
+BEGINPROC TMPL_NM_CMN(PrintStrColonSpaces)
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xCX
+
+ call TMPL_NM_CMN(PrintStr)
+ call TMPL_NM_CMN(StrLen)
+ mov cx, ax
+ mov al, ':'
+ call TMPL_NM_CMN(PrintChr)
+ inc cx
+ mov al, ' '
+.next_space:
+ call TMPL_NM_CMN(PrintChr)
+ inc cx
+ cmp cx, dx
+ jb .next_space
+
+ pop xCX
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(PrintStrColonSpaces)
+
+
+;;
+; Print a string followed by a 0+ spaces, a semicolon and a space.
+;
+; @param ds:ax The string to print.
+; @param dx The desired minimum length of the output. That is
+; string + spaces + colon + space.
+; @uses nothing.
+;
+BEGINPROC TMPL_NM_CMN(PrintStrSpacesColonSpace)
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xCX
+
+ call TMPL_NM_CMN(PrintStr)
+ call TMPL_NM_CMN(StrLen)
+ mov cx, ax
+ inc cx
+ mov al, ' '
+.next_space:
+ inc cx
+ cmp cx, dx
+ jae .done_spaces
+ call TMPL_NM_CMN(PrintChr)
+ jmp .next_space
+.done_spaces:
+ mov al, ':'
+ call TMPL_NM_CMN(PrintChr)
+ mov al, ' '
+ call TMPL_NM_CMN(PrintChr)
+
+ pop xCX
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(PrintStrSpacesColonSpace)
+
+
+;;
+; Store the current nanosecond timestamp in [ax] (qword).
+;
+; @param ds:ax Where to store the 64-bit timestamp.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(GetNanoTS)
+ push sAX
+ push sCX
+ push xDX
+%ifdef TMPL_16BIT
+ movzx ecx, ax
+%else
+ mov xCX, xAX
+%endif
+
+ mov dx, VMMDEV_TESTING_IOPORT_TS_LOW
+ in eax, dx
+ mov [sCX], eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_TS_HIGH
+ in eax, dx
+ mov [sCX + 4], eax
+
+ pop xDX
+ pop sCX
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(GetNanoTS)
+
+
+
+;;
+; Calculates the time elapsed since [ax] (qword), storing it at [ax] (qword).
+;
+; @param ds:ax Where to get the start timestamp (input) and where
+; to store the time elapsed (output). qword
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(GetElapsedNanoTS)
+ push sAX
+ push sCX
+ push xDX
+%ifdef TMPL_16BIT
+ movzx ecx, ax
+%else
+ mov xCX, xAX
+%endif
+
+ mov dx, VMMDEV_TESTING_IOPORT_TS_LOW
+ in eax, dx
+ sub eax, [sCX]
+ mov [sCX], eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_TS_HIGH
+ in eax, dx
+ sbb eax, [sCX + 4]
+ mov [sCX + 4], eax
+
+ pop xDX
+ pop sCX
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(GetElapsedNanoTS)
+
+
+;;
+; Sends a command to VMMDev followed by a single string.
+;
+; If the VMMDev is not present or is not being used, this function will
+; do nothing.
+;
+; @param eax The command.
+; @param ds:dx The string (zero terminated).
+; @uses nothing
+; @internal
+;
+BEGINPROC TMPL_NM_CMN(testSendStrCmd)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xDX
+
+ cmp byte [g_fbBs2VMMDevTesting], 0
+ je .no_vmmdev
+
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+ out dx, eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ pop xBX
+ push xBX
+ dec xBX
+.next_char:
+ inc xBX
+ mov al, [xBX]
+ out dx, al
+ test al, al
+ jnz .next_char
+
+.no_vmmdev:
+ pop xDX
+ pop xBX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(testSendStrCmd)
+
+
+;;
+; Equivalent to RTTestCreate + RTTestBanner
+;
+; @param DS16:xAX Pointer to a zero terminated string naming the
+; test. Must be a global constant.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestInit)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+
+ ; Initialize the globals.
+ mov [g_npszBs2Test], xAX
+ xor eax, eax
+ mov [g_uscBs2TestErrors], ax
+ mov [g_npszBs2SubTest], eax
+ mov [g_uscBs2SubTestAtErrors], ax
+ mov byte [g_fbBs2SubTestReported], 1
+ mov [g_uscBs2SubTests], ax
+ mov [g_uscBs2SubTestsFailed], ax
+
+ ; Print the name. RTTestBanner
+ mov xAX, [g_npszBs2Test]
+ call TMPL_NM_CMN(PrintStr)
+ mov xAX, .s_szTesting
+ call TMPL_NM_CMN(PrintStr)
+
+ ; Report it to the VMMDev.
+ mov eax, VMMDEV_TESTING_CMD_INIT
+ mov xDX, [g_npszBs2Test]
+ call TMPL_NM_CMN(testSendStrCmd)
+
+ pop xDX
+ pop sAX
+ leave
+ ret
+.s_szTesting:
+ db ': TESTING...', 13, 10, 0
+ENDPROC TMPL_NM_CMN(TestInit)
+
+
+;;
+; rtTestSubTestReport
+; @uses nothing
+; @internal
+BEGINPROC TMPL_NM_CMN(testSubTestReport)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push xDX
+
+ ; Check if there is anything to do.
+ cmp byte [g_fbBs2SubTestReported], 0
+ jne .done
+ xor xAX, xAX ; load the sub test name pointer for later
+ mov xAX, [g_npszBs2SubTest]
+ test xAX, xAX
+ jz .done
+
+ ; Start the printing.
+ mov dx, 48
+ call TMPL_NM_CMN(PrintStrSpacesColonSpace)
+
+ mov byte [g_fbBs2SubTestReported], 1
+ mov cx, [g_uscBs2TestErrors]
+ sub cx, [g_uscBs2SubTestAtErrors]
+ and ecx, 0ffffh
+ jnz .failed
+
+ ; passed
+ mov xAX, .s_szPassed
+ call TMPL_NM_CMN(PrintStr)
+ jmp .vmmdev
+
+ ; failed
+.failed:
+ mov xAX, .s_szFailure
+ call TMPL_NM_CMN(PrintStr)
+ mov eax, ecx
+ call TMPL_NM_CMN(PrintU32)
+ mov xAX, .s_szFailureEnd
+ call TMPL_NM_CMN(PrintStr)
+
+ ; report to VMMDev
+.vmmdev:
+ cmp byte [g_fbBs2VMMDevTesting], 0
+ je .no_vmmdev
+
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+ mov eax, VMMDEV_TESTING_CMD_SUB_DONE
+ out dx, eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ mov eax, ecx
+ out dx, eax
+
+.no_vmmdev:
+.done:
+ pop xDX
+ pop sCX
+ pop sAX
+ leave
+ ret
+.s_szPassed:
+ db 'PASSED', 13, 10, 0
+.s_szFailure:
+ db 'FAILED (', 0
+.s_szFailureEnd:
+ db ' errors)', 13, 10, 0
+ENDPROC TMPL_NM_CMN(testSubTestReport)
+
+
+;;
+; rtTestSubCleanup
+; @uses nothing
+; @internal
+BEGINPROC TMPL_NM_CMN(testSubCleanup)
+ push xBP
+ mov xBP, xSP
+
+ cmp dword [g_npszBs2SubTest], 0
+ je .cleaned_up
+
+ call TMPL_NM_CMN(testSubTestReport)
+ mov dword [g_npszBs2SubTest], 0
+ mov byte [g_fbBs2SubTestReported], 0
+
+.cleaned_up:
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(testSubCleanup)
+
+
+;;
+; Equivalent to RTTestSub.
+;
+; @param ds:xAX Pointer to a zero terminated string naming the sub test.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestSub)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+
+ ; Complete and cleanup any current sub test.
+ call TMPL_NM_CMN(testSubCleanup)
+
+ ; Start a new sub test.
+ inc word [g_uscBs2SubTests]
+ mov dx, [g_uscBs2TestErrors]
+ mov [g_uscBs2SubTestAtErrors], dx
+ mov [g_npszBs2SubTest], xAX
+ mov byte [g_fbBs2SubTestReported], 0
+
+ ; Report it to the VMMDev.
+ mov xDX, xAX
+ mov eax, VMMDEV_TESTING_CMD_SUB_NEW
+ call TMPL_NM_CMN(testSendStrCmd)
+
+ pop xDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestSub)
+
+
+;;
+; Calculates the error count for the current sub test.
+;
+; @returns ax Error count for the current sub test.
+; @uses ax
+;
+BEGINPROC TMPL_NM_CMN(TestSubErrorCount)
+ pushf
+
+ mov ax, [g_uscBs2TestErrors]
+ sub ax, [g_uscBs2SubTestAtErrors]
+
+ popf
+ ret
+ENDPROC TMPL_NM_CMN(TestSubErrorCount)
+
+
+
+;;
+; Equivalent to RTTestValue.
+;
+; @param ds:ax The value name.
+; @param edx The 32-bit value to report.
+; @param cl The unit (VMMDEV_TESTING_UNIT_XXX).
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestValueU32)
+ push xBP
+ mov xBP, xSP
+ push sDX
+ push sCX
+ push sAX
+ push sSI
+ pushf
+ cld
+
+ mov xSI, xAX ; xSI = name
+
+ ; Print it.
+ mov dx, 48
+ call TMPL_NM_CMN(PrintStrSpacesColonSpace)
+ mov eax, [xBP - sCB]
+ call TMPL_NM_CMN(PrintU32)
+ mov al, ' '
+ call TMPL_NM_CMN(PrintChr)
+ movzx sAX, cl ; ASSUMES correct input.
+ mov edx, eax ; edx = unit
+ shl xAX, 4 ; * 16
+ add xAX, g_aszBs2TestUnitNames
+ call TMPL_NM_CMN(PrintStr)
+ mov al, 13
+ call TMPL_NM_CMN(PrintChr)
+ mov al, 10
+ call TMPL_NM_CMN(PrintChr)
+
+ ; Report it to the host.
+ cmp byte [g_fbBs2VMMDevTesting], 0
+ je .no_vmmdev
+ mov ecx, edx ; ecx = unit
+
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+ mov eax, VMMDEV_TESTING_CMD_VALUE
+ out dx, eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ mov eax, [xBP - sCB]
+ out dx, eax ; value - low dword
+ xor eax, eax
+ out dx, eax ; value - high dword
+ mov eax, ecx
+ out dx, eax ; unit
+.next_char:
+ lodsb
+ out dx, al
+ test al, al
+ jnz .next_char
+
+.no_vmmdev:
+ popf
+ pop sSI
+ pop sAX
+ pop sCX
+ pop sDX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestValueU32)
+
+
+;;
+; RTTestValue + DBGFR3RegNmQueryU64.
+;
+; @param ds:ax The value name and register name separated by a colon.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestValueReg)
+ push xBP
+ mov xBP, xSP
+ push sDX
+ push sAX
+ push sSI
+ pushf
+ cld
+
+ mov xSI, xAX ; xSI = name
+
+ ; Report it to the host.
+ cmp byte [g_fbBs2VMMDevTesting], 0
+ je .no_vmmdev
+
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+ mov eax, VMMDEV_TESTING_CMD_VALUE_REG
+ out dx, eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+.next_char:
+ lodsb
+ out dx, al
+ test al, al
+ jnz .next_char
+
+.no_vmmdev:
+ popf
+ pop sSI
+ pop sAX
+ pop sDX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestValueReg)
+
+
+;;
+; Equivalent to RTTestFailed("%s", ds:xAX).
+;
+; @param ds:xAX Failure explanation.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestFailed)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+
+ ; Increment the error count.
+ inc word [g_uscBs2TestErrors]
+
+ ; Print failure message.
+ call TMPL_NM_CMN(PrintStr)
+
+ ; Report it to the VMMDev.
+ mov xDX, xAX
+ mov eax, VMMDEV_TESTING_CMD_FAILED
+ call TMPL_NM_CMN(testSendStrCmd)
+
+ pop xDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestFailed)
+
+
+;;
+; Equivalent to RTTestFailed.
+;
+; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the
+; caller does the cleanup (cdecl sans volatile regs).
+;
+; @param fpszFormat The format string (far pointer on 16-bit
+; systems). See StrFormatV for format details.
+; @param ... Zero or more format string arguments.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestFailedF)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+ push xCX
+ push xBX
+%ifdef TMPL_16BIT
+ push ds
+ push es
+ push fs
+%endif
+ sub xSP, 0400h ; string buffer (1024 bytes)
+
+ ;
+ ; Format the failure string and call TestFailed.
+ ;
+%ifdef TMPL_16BIT
+ mov ax, ss ; buffer address.
+ mov ds, ax
+ mov ax, sp
+ mov xDX, 0400h ; buffer size.
+ les cx, [bp + 4] ; format string
+ mov bx, ss ; argument list
+ mov fs, bx
+ mov bx, bp
+ add bx, 8
+%else
+ mov xAX, xSP ; buffer address
+ mov xDX, 0400h ; buffer size
+ mov xCX, [xBP + xCB * 2] ; format string
+ lea xBX, [xBP + xCB * 3] ; argument list.
+%endif
+ call TMPL_NM_CMN(StrFormatV)
+ call TMPL_NM_CMN(TestFailed)
+
+ add xSP, 0400h
+%ifdef TMPL_16BIT
+ pop fs
+ pop es
+ pop ds
+%endif
+ pop xBX
+ pop xCX
+ pop xDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestFailedF)
+
+
+;;
+; Equivalent to RTTestSkipped("%s", ds:xAX).
+;
+; @param ds:xAX Explanation.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestSkipped)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xDX
+
+ ; Print reason.
+ call TMPL_NM_CMN(PrintStr)
+
+ ; Report it to the VMMDev.
+ mov xDX, xAX
+ mov eax, VMMDEV_TESTING_CMD_SKIPPED
+ call TMPL_NM_CMN(testSendStrCmd)
+
+ pop xDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestSkipped)
+
+
+
+;;
+; Equivalent to RTTestSubDone.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(TestSubDone)
+ jmp TMPL_NM_CMN(testSubCleanup)
+ENDPROC TMPL_NM_CMN(TestSubDone)
+
+
+;;
+; Equivalent to RTTestSummaryAndDestroy, does not return.
+;
+BEGINPROC TMPL_NM_CMN(TestTerm)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push xDX
+
+ ; Complete and cleanup any current sub test.
+ call TMPL_NM_CMN(testSubCleanup)
+
+ ; Print test summary.
+ mov xAX, [g_npszBs2Test]
+ call TMPL_NM_CMN(PrintStr)
+
+ mov cx, [g_uscBs2TestErrors]
+ and ecx, 0ffffh
+ jnz .failure
+
+ ; success
+ mov xAX, .s_szSuccess
+ call TMPL_NM_CMN(PrintStr)
+ jmp .vmmdev
+
+ ; failure
+.failure:
+ mov xAX, .s_szFailure
+ call TMPL_NM_CMN(PrintStr)
+ mov eax, ecx
+ call TMPL_NM_CMN(PrintU32)
+ mov xAX, .s_szFailureEnd
+ call TMPL_NM_CMN(PrintStr)
+
+ ; report to VMMDev
+.vmmdev:
+ cmp byte [g_fbBs2VMMDevTesting], 0
+ je .no_vmmdev
+
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+ mov eax, VMMDEV_TESTING_CMD_TERM
+ out dx, eax
+
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ mov eax, ecx
+ out dx, eax
+.no_vmmdev:
+
+ ; Shut down the VM by default.
+ call TMPL_NM_CMN(Shutdown)
+
+ pop xDX
+ pop sCX
+ pop sAX
+ leave
+ ret
+.s_szSuccess:
+ db ': SUCCESS', 13, 10, 0
+.s_szFailure:
+ db ': FAILURE - ', 0
+.s_szFailureEnd:
+ db ' errors', 13, 10, 0
+ENDPROC TMPL_NM_CMN(TestTerm)
+
+
+
+
+;;
+; Report a result (elapsed time).
+;
+; @param ds:ax Pointer to the elapsed time.
+; @param edx The number of tests executed.
+; @param ds:cx The test description.
+;
+; @users nothing
+;
+BEGINPROC TMPL_NM_CMN(ReportResult)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push xCX
+
+%if 0
+ push sDX
+ push xCX
+ push sDX
+ mov edx, [sAX]
+ push sDX
+ mov edx, [sAX + 4]
+ push sDX
+ push .szDbg
+ call TMPL_NM_CMN(PrintF)
+ add xSP, 4 * sCB + xCB
+ pop sDX
+ jmp .end_debug
+.szDbg:
+ db 'ReportResult(%RX32.%RX32, %RX32, %s)', 13, 10, 0
+.end_debug:
+%endif
+
+ call TMPL_NM_CMN(CalcTestPerSecond)
+ mov edx, eax
+ mov xAX, xCX
+ mov cl, VMMDEV_TESTING_UNIT_INSTRS_PER_SEC
+ call TMPL_NM_CMN(TestValueU32)
+
+ pop xCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(ReportResult)
+
+
+%ifdef BS2_WITH_TRAPS
+;;
+; Checks a trap, complains if not the expected one.
+;
+; @param al The expected trap number.
+; @param sDX The expected trap error code.
+; @param sCX The expected fault eip.
+; @param sBX The expected fault address.
+; @returns al=1 and ZF=0 on success.
+; @returns al=0 and ZF=1 on failure
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(TestCheckTrap)
+ push xBP
+ mov xBP, xSP
+%define a_u8ExpectedTrapNo byte [xBP - xCB]
+ push xAX
+%define a_uExpectedErr sPRE [xBP - xCB - sCB*1]
+ push sDX
+%define a_uExpectedFaultPC sPRE [xBP - xCB - sCB*2]
+ push sCX
+%define a_uExpectedFaultAddr sPRE [xBP - xCB - sCB*3]
+ push sBX
+
+ ; Any traps at all?
+ cmp dword [g_u32cTraps], 0
+ jne .trapped
+ mov xAX, .s_szNoTrap
+ jmp .failed
+.trapped:
+
+ ; Exactly one trap.
+ cmp dword [g_u32cTraps], 1
+ je .one_trap
+ mov xAX, .s_szToManyTraps
+ jmp .failed
+.one_trap:
+
+ ; The right trap.
+ cmp byte [g_u8LastTrapNo], al
+ je .right_trap_no
+ mov xAX, .s_szWrongTrapNo
+ jmp .failed
+.right_trap_no:
+
+ ; The right error code.
+ cmp [g_u64LastTrapErr], sDX
+%ifndef TMPL_64BIT
+ jne .bad_err_cd
+ cmp dword [g_u64LastTrapErr + 4], 0
+%endif
+ je .right_err_cd
+.bad_err_cd:
+ mov xAX, .s_szWrongErrCd
+ jmp .failed
+.right_err_cd:
+
+ ; The fault PC.
+ cmp [g_LastTrapRegs + BS2REGS.rip], sCX
+%ifndef TMPL_64BIT
+ jne .bad_pc
+ cmp dword [g_LastTrapRegs + BS2REGS.rip + 4], 0
+%endif
+ je .right_pc
+.bad_pc:
+ mov xAX, .s_szWrongPc
+ jmp .failed
+.right_pc:
+
+
+ ; The fault address (PF only).
+ cmp al, 0eh
+ jne .right_fault_address
+ cmp [g_LastTrapRegs + BS2REGS.cr2], sBX
+%ifndef TMPL_64BIT
+ jne .bad_fault_address
+ cmp dword [g_LastTrapRegs + BS2REGS.cr2 + 4], 0
+%endif
+ je .right_fault_address
+.bad_fault_address:
+ mov xAX, .s_szWrongFaultAddress
+ jmp .failed
+.right_fault_address:
+
+ pop sBX
+ pop sCX
+ pop sDX
+ pop xAX
+ leave
+ mov al, 1
+ test al, al
+ ret
+
+ ;
+ ; Reportfailure
+ ;
+.failed:
+ mov xDX, xSP ; save xSP - lazy bird.
+ cmp a_u8ExpectedTrapNo, 0eh
+ jne .not_pf2
+%ifndef TMPL_64BIT
+ push dword 0
+%endif
+ push a_uExpectedFaultAddr
+.not_pf1:
+%ifndef TMPL_64BIT
+ push dword 0
+%endif
+ push a_uExpectedErr
+%ifndef TMPL_64BIT
+ push dword 0
+%endif
+ push a_uExpectedFaultPC
+ movzx xBX, a_u8ExpectedTrapNo
+ push xBX
+ ; line break
+ cmp a_u8ExpectedTrapNo, 0eh
+ jne .not_pf2
+%ifndef TMPL_64BIT
+ push dword [g_LastTrapRegs + BS2REGS.cr2 + 4]
+%endif
+ push sPRE [g_LastTrapRegs + BS2REGS.cr2]
+.not_pf2:
+%ifndef TMPL_64BIT
+ push dword [g_u64LastTrapErr + 4]
+%endif
+ push sPRE [g_u64LastTrapErr]
+%ifndef TMPL_64BIT
+ push dword [g_LastTrapRegs + BS2REGS.rip + 4]
+%endif
+ push sPRE [g_LastTrapRegs + BS2REGS.rip]
+ movzx xBX, byte [g_u8LastTrapNo]
+ push xBX
+ push xAX ; msg
+ mov xAX, .s_szFailureMsg
+ cmp a_u8ExpectedTrapNo, 0eh
+ jne .not_pf3
+ mov xAX, .s_szFailurePfMsg
+.not_pf3:
+ push xAX ; format string
+ call TMPL_NM_CMN(TestFailedF)
+ mov xSP, xDX ; clean up call frame
+
+.done:
+ pop sBX
+ pop sCX
+ pop sDX
+ pop xAX
+ leave
+ xor al, al
+ ret
+
+.s_szFailureMsg:
+ db '%s', 13, 10
+ db ' got trap %RX8 pc=%RX64 err=%RX64', 13, 10
+ db ' expected %RX8 pc=%RX64 err=%RX64', 13, 10, 0
+.s_szFailurePfMsg:
+ db '%s', 13, 10
+ db ' got trap %RX8 pc=%RX64 err=%RX64 cr2=%RX64', 13, 10,
+ db ' expected %RX8 pc=%RX64 err=%RX64 cr2=%RX64', 13, 10, 0
+.s_szNoTrap:
+ db 'no traps', 0
+.s_szToManyTraps:
+ db 'too many traps', 0
+.s_szWrongTrapNo:
+ db 'wrong trap number', 0
+.s_szWrongErrCd:
+ db 'wrong error code', 0
+.s_szWrongPc:
+ db 'wrong xIP', 0
+.s_szWrongFaultAddress:
+ db 'wrong fault address', 0
+%undef a_u8ExpectedTrapNo
+%undef a_u32ExpectedErr
+%undef a_u32ExpectedFaultPC
+%undef a_u32ExpectedFaultAddr
+ENDPROC TMPL_NM_CMN(TestCheckTrap)
+%endif ; BS2_WITH_TRAPS
+
+
+%ifdef BS2_WITH_TRAPS
+;;
+; Installs the active list of trap records (BS2TRAPREC).
+;
+; @param sAX Flat address of the trap records (BS2TRAPREC).
+; Assumes that DS is FLAT.
+; @param edx The number of trap records.
+; @param sCX Flat image base address, i.e. what BS2TRAPREC.offWhere
+; is relative to.
+; @returns al=1 and ZF=0 on success.
+; @returns al=0 and ZF=1 on failure
+;
+; @uses sAX (return value)
+;
+BEGINPROC TMPL_NM_CMN(TestInstallTrapRecs)
+ push xBP
+ mov xBP, xSP
+ push sDX
+ push sBX
+ push sCX
+ push sDI
+ push sSI
+
+ ; Make sure the record array is within limits.
+ cmp edx, _4M
+ jae .nok
+
+ ; Scan the trap records.
+ mov sDI, sAX
+ mov esi, edx
+ or esi, esi
+ jnz .ok
+.next:
+ cmp dword [sDI + BS2TRAPREC.offWhere], _2G
+ jae .nok
+
+ cmp dword [sDI + BS2TRAPREC.offResumeAddend], 0
+ je .nok
+ cmp dword [sDI + BS2TRAPREC.offResumeAddend], 0xff
+ je .nok
+
+ cmp dword [sDI + BS2TRAPREC.u8TrapNo], X86_XCPT_LAST
+ ja .nok
+
+ ; next.
+ add sDI, BS2TRAPREC_size
+ dec esi
+ jnz .next
+
+ ; Set the global variables.
+.ok:
+ xor esi, esi
+ mov [g_paTrapRecs + 4], esi
+ mov [g_paTrapRecs], sAX
+ mov [g_cTrapRecs], edx
+ mov [g_iTrapRecLast], esi
+ mov [g_pTrapRecBase + 4], esi
+ mov [g_pTrapRecBase], sCX
+ mov eax, 1
+ or eax, eax
+
+.done:
+ pop sSI
+ pop sDI
+ pop sBX
+ pop sCX
+ pop sDX
+ leave
+ ret
+
+.nok:
+ xor eax, eax
+ jmp .done
+ENDPROC TMPL_NM_CMN(TestInstallTrapRecs)
+%endif ; BS2_WITH_TRAPS
+
+
+;;
+; Calculate the number of tests executed per second.
+;
+; @param ds:ax Pointer to the elapsed time.
+; @param edx The number of tests executed.
+; @returns The tests per second in eax.
+;
+; @uses eax (return value)
+;
+BEGINPROC TMPL_NM_CMN(CalcTestPerSecond)
+ push xBP
+ mov xBP, xSP
+ push sDX
+ push sCX
+%ifdef TMPL_16BIT
+ movzx eax, ax
+%endif
+
+ ; Calc NS per test.
+ mov ecx, edx
+ cmp ecx, 0
+ jz .div_zero
+ movzx eax, ax
+ mov edx, [sAX + 4]
+ cmp edx, ecx
+ jae .div_overflow
+ mov eax, [sAX]
+ shld edx, eax, 10
+ shl eax,10
+ div ecx ; eax = NS per test
+
+ ; Calc tests per second.
+ mov ecx, eax
+ cmp ecx, 0
+ jz .div_zero
+ mov edx, 0xee
+ mov eax, 0x6b280000 ; 1024G
+ div ecx ; eax = tests per second
+
+.done:
+ pop sCX
+ pop sDX
+ leave
+ ret
+
+.div_zero:
+ mov eax, 0
+ jmp .done
+
+.div_overflow:
+ mov eax, 4242424242
+ jmp .done
+ENDPROC TMPL_NM_CMN(CalcTestPerSecond)
+
+
+;;
+; Calculate the number of iterations for a benchmark based on the time a short
+; calibration run too.
+;
+; @param ds:xAX Pointer to the elapsed time.
+; @param edx The number of tests iterations executed.
+; @param ecx The desired test length, in seconds.
+; @returns The tests iterations in eax.
+;
+; @uses eax (return value)
+;
+BEGINPROC TMPL_NM_CMN(CalcBenchmarkIterations)
+ push xBP
+ mov xBP, xSP
+ push sDX
+ push sCX
+%ifdef TMPL_16BIT
+ movzx eax, ax
+%endif
+
+ ; Calc NS per test.
+ mov ecx, edx
+ cmp ecx, 0
+ jz .div_zero
+ movzx eax, ax
+ mov edx, [sAX + 4]
+ cmp edx, ecx
+ jae .div_overflow
+ mov eax, [sAX]
+ div ecx ; eax = NS per test
+
+ ; Calc tests per second.
+ mov ecx, eax
+ cmp ecx, 0
+ jz .div_zero
+ xor edx, edx
+ mov eax, 1000000000 ; 1G
+ div ecx ; eax = tests per second
+
+ ; Multiply this up to the desired number of seconds.
+ mov edx, [xBP - xCB*2]
+ mul edx
+ test edx, edx
+ jnz .mult_32bit_overflow
+ cmp eax, _64K
+ jle .too_small
+
+.done:
+ pop sCX
+ pop sDX
+ leave
+ ret
+
+.too_small:
+ mov eax, _64K
+ jmp .done
+
+.mult_32bit_overflow:
+ mov eax, 0ffff0000h
+ jmp .done
+
+.div_zero:
+ mov eax, [xBP - xCB]
+ shl eax, 8
+ jmp .fudge
+
+.div_overflow:
+ mov eax, [xBP - xCB]
+ shr eax, 4
+.fudge:
+ add eax, 10
+ jmp .done
+ENDPROC TMPL_NM_CMN(CalcBenchmarkIterations)
+
+
+;;
+; Prints a string on the screen.
+;
+; @param ds:ax The string to find the length of.
+; @returns The string length in ax.
+;
+; @uses ax (return value)
+;
+BEGINPROC TMPL_NM_CMN(StrLen)
+ push xBP
+ mov xBP, xSP
+ push xBX
+
+ mov xBX, xAX
+ dec xBX
+.next:
+ inc xBX
+ cmp byte [xBX], byte 0
+ jnz .next
+
+ xchg xAX, xBX
+ sub xAX, xBX
+
+ pop xBX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(StrLen)
+
+
+
+;;
+; Simple string format, taking an argument list.
+;
+; Implemented:
+; - %RX8
+; - %RX16
+; - %RX32
+; - %RX64
+; - %s
+;
+; Planned:
+; - %RU8
+; - %RU16
+; - %RU32
+; - %RU64
+; - %RI8
+; - %RI16
+; - %RI32
+; - %RI64
+;
+; @param ds:xAX The buffer address.
+; @param xDX The buffer size.
+; @param es:xCX The format string.
+; @param fs:xBX The argument list.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(StrFormatV)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ push sBX
+ push sDI
+ push sSI
+ pushf
+ cld
+%ifdef TMPL_16BIT
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov si, ds
+ mov di, es
+ mov ds, di
+ mov es, si
+ mov di, ax ; es:di -> output buffer.
+ mov si, cx ; ds:si -> format string.
+%define a_pArgs [fs:bx]
+%define a_pu32ArgsHighDW dword [fs:bx + 4]
+%else
+ mov xDI, xAX ; (es:)xDI -> output buffer.
+ mov xSI, xCX ; (ds:)xSI -> format string.
+%define a_pArgs [xBX]
+%define a_pu32ArgsHighDW dword [xBX + 4]
+%endif
+ xchg xCX, xDX ; xCX=buffer size.
+
+ ;
+ ; Make sure we've got space for a terminator char in the output buffer.
+ ;
+ test xCX, xCX
+ jz .return
+ dec xCX
+ jz .done
+
+ ;
+ ; In this loop we're free to use sDX and (with some caution) sAX.
+ ;
+.format_loop:
+ lodsb
+ test al, al
+ jz .done
+ cmp al, '%'
+ je .switch
+
+ ; Emit the character in al if there is room for it.
+.emit_al:
+ test xCX, xCX
+ jz .done
+ stosb
+ dec xCX
+ jmp .format_loop
+
+ ; Try recognize the format specifier.
+.switch:
+ lodsb
+ cmp al, 's'
+ je .switch_case_string
+ cmp al, 'c'
+ je .switch_case_char
+ cmp al, 'R'
+ jne .switch_default
+ lodsb
+ cmp al, 'X'
+ jne .switch_case_number
+ cmp al, 'U'
+ jne .switch_case_number
+ cmp al, 'I'
+ jne .switch_case_number
+
+.switch_default:
+ test al, al
+ jz .done
+ mov al, '!'
+ jmp .emit_al
+
+ ; parse the number part.
+.switch_case_number:
+ mov ah, al ; ah = {X,U,I}
+ lodsb
+ cmp al, '8'
+ je .switch_case_number_8bit
+ cmp al, '1'
+ je .switch_case_number_16bit
+ cmp al, '3'
+ je .switch_case_number_32bit
+ cmp al, '6'
+ je .switch_case_number_64bit
+ jmp .switch_default
+
+
+ ;
+ ; Common code for 8-bit, 16-bit and 32-bit.
+ ;
+ ; The first part load the value into edx, ah={X,U,I},
+ ; al=(max-hex, max-unsigned-dec).
+ ;
+.switch_case_number_8bit:
+ mov al, (2 << 4) | 2
+ movzx edx, byte a_pArgs
+ add xBX, xCB
+ cmp ah, 'I'
+ jne .switch_case_number_common_32bit_hex_or_unsigned
+ movsx edx, dl
+ jmp .switch_case_number_common_32bit_signed
+
+.switch_case_number_16bit:
+ lodsb
+ cmp al, '6'
+ jne .switch_default
+ mov al, (4 << 4) | 5
+ movzx edx, word a_pArgs
+ add xBX, xCB
+ cmp ah, 'I'
+ jne .switch_case_number_common_32bit_hex_or_unsigned
+ movsx edx, dx
+ jmp .switch_case_number_common_32bit_signed
+
+.switch_case_number_32bit:
+ lodsb
+ cmp al, '2'
+ jne .switch_default
+ mov al, (8 << 4) | 10
+ mov edx, dword a_pArgs
+ add xBX, sCB
+ cmp ah, 'I'
+ je .switch_case_number_common_32bit_signed
+
+.switch_case_number_common_32bit_hex_or_unsigned:
+ cmp ah, 'X'
+ jne .switch_case_number_common_32bit_unsigned
+ shr al, 4
+ and xAX, 0fh
+ cmp xCX, xAX
+ jb .switch_case_number_bad_buf
+ call .format_32bit_hex_subroutine
+ jmp .format_loop
+
+.switch_case_number_common_32bit_unsigned:
+ and xAX, 0fh
+ cmp xCX, xAX
+ jb .switch_case_number_bad_buf
+ call .format_32bit_dec_subroutine
+ jmp .format_loop
+
+.switch_case_number_common_32bit_signed:
+ cmp edx, 0
+ jge .switch_case_number_common_32bit_unsigned
+ and xAX, 0fh
+ inc xAX ; sign char
+ cmp xCX, xAX
+ jb .switch_case_number_bad_buf
+ ; Emit the minus sign, invert the value and join the unsigned formatting.
+ push xAX
+ mov al, '-'
+ stosb
+ dec xCX
+ pop xAX
+ neg edx
+ call .format_32bit_dec_subroutine
+ jmp .format_loop
+
+
+ ;
+ ; 64-bit is special, to simplify we treat all formats as hex...
+ ;
+.switch_case_number_64bit:
+ lodsb
+ cmp al, '4'
+ jne .switch_default
+ cmp ah, 'X'
+ je .switch_case_number_64bit_hex
+ cmp ah, 'I'
+ je .switch_case_number_64bit_signed
+ jmp .switch_case_number_64bit_unsigned
+
+.switch_case_number_64bit_hex:
+ mov eax, dword a_pArgs
+ mov edx, a_pu32ArgsHighDW
+ add xBX, 8
+ cmp xCX, 8+1+8
+ jb .switch_case_number_bad_buf
+ ; Simple, format it as two 32-bit hex values.
+ push sAX
+ mov eax, 8
+ call .format_32bit_hex_subroutine
+ mov al, 27h ; '\'' - how do we escape this with yasm?
+ stosb
+ dec xCX
+ pop sDX
+ mov eax, 8
+ call .format_32bit_hex_subroutine
+ jmp .format_loop
+
+.switch_case_number_64bit_unsigned:
+ cmp xCX, 19
+ jb .switch_case_number_bad_buf
+.switch_case_number_64bit_unsigned_format_it:
+ ;; @todo implement me
+ jmp .switch_case_number_64bit_hex
+
+.switch_case_number_64bit_signed:
+ mov eax, dword a_pArgs
+ mov edx, a_pu32ArgsHighDW
+ add xBX, 8
+ cmp xCX, 20
+ jb .switch_case_number_bad_buf
+ test edx, 080000000h
+ jz .switch_case_number_64bit_unsigned_format_it
+ ; Emit the minus sign, invert the value and join the unsigned formatting.
+ push xAX
+ mov al, '-'
+ stosb
+ dec xCX
+ pop xAX
+ neg eax
+ neg edx
+ jmp .switch_case_number_64bit_unsigned_format_it
+
+
+ ; The remaining buffer is too small to hold the number.
+.switch_case_number_bad_buf:
+ mov al, '^'
+ jmp .emit_al
+
+
+ ;
+ ; Emit a string.
+ ;
+.switch_case_string:
+%ifdef TMPL_16BIT
+ lgs dx, a_pArgs
+ add xBX, 4
+%else
+ mov xDX, a_pArgs
+ add xBX, xCB
+%endif
+ test xCX, xCX
+.switch_case_string_loop:
+ jz .done
+%ifdef TMPL_16BIT
+ mov al, [gs:edx]
+%else
+ mov al, [xDX]
+%endif
+ test al, al
+ jz .format_loop
+ inc xDX
+ stosb
+ dec xCX
+ jmp .switch_case_string_loop
+
+ ;
+ ; Emit a char.
+ ;
+.switch_case_char:
+ mov al, byte a_pArgs
+ add xBX, xCB
+ jmp .emit_al
+
+
+ ;
+ ; Done, just emit the terminator char.
+ ;
+.done:
+ xor al, al
+ stosb
+
+.return:
+%ifdef TMPL_16BIT
+ pop gs
+ pop fs
+ pop es
+ pop ds
+%endif
+ popf
+ pop sSI
+ pop sDI
+ pop sBX
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+%undef a_pArgs
+%undef a_pu32ArgsHighDW
+
+;;
+; Internal subroutine for formatting a hex number into the buffer.
+; @param al The precision (2, 4, 8).
+; @param edx The value.
+;
+; @uses ecx, edi
+;
+.format_32bit_hex_subroutine:
+ push xAX
+ push sDX
+ push xBX
+
+ ; Rotate edx into position.
+ mov ebx, 8
+ sub bl, al
+ shl bl, 2
+ xchg cl, bl
+ rol edx, cl
+ xchg bl, cl
+
+ mov bl, al ; Width counter
+.format_32bit_hex_subroutine_next:
+ rol edx, 4
+ mov eax, edx
+ and eax, 0fh
+ add sAX, g_achHex
+ mov al, [cs:sAX]
+ stosb
+ dec xCX
+ dec bl
+ jnz .format_32bit_hex_subroutine_next
+
+ pop xBX
+ pop sDX
+ pop xAX
+ ret
+
+
+;;
+; Internal subroutine for formatting a hex number into the buffer.
+; @param al The max precision (2, 5, 10).
+; @param edx The value.
+; @param xCX Counter register to decrement as characters are emited.
+; @param es:xDI Where to write the output, xDI is updated.
+;
+; @uses xCX, xDI
+;
+.format_32bit_dec_subroutine:
+%if 0 ;; @todo implement this
+ sub xSP, 20h
+ ; Format in reverse order into a stack buffer.
+
+ ; Append the stack buffer to the string, reversing it in the process.
+
+ add xSP, 20h
+%else
+ call .format_32bit_hex_subroutine
+%endif
+ ret
+
+ENDPROC TMPL_NM_CMN(StrFormatV)
+
+
+;;
+; Very limited RTStrPrintf version.
+;
+; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the
+; caller does the cleanup (cdecl sans volatile regs).
+;
+; @param fpszBuf The output buffer.
+; @param cbBuf The output buffer size (natural size).
+; @param fpszFormat The format string (far pointer in 16-bit).
+; See StrFormatV for format.
+; @param ... Zero or more format string arguments.
+; @uses nothing
+;
+BEGINPROC TMPL_NM_CMN(StrFormatF)
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xDX
+ push xCX
+ push xBX
+%ifdef TMPL_16BIT
+ push ds
+ push es
+ push fs
+
+ lds xAX, [bp + 04h]
+ mov dx, [bp + 08h]
+ les xCX, [bp + 0ah]
+ mov bx, ss
+ mov fs, bx
+ mov bx, bp
+ add bx, 0eh
+%else
+ mov xAX, [xBP + xCB * 2]
+ mov xDX, [xBP + xCB * 3]
+ mov xCX, [xBP + xCB * 4]
+ lea xBX, [xBP + xCB * 5]
+%endif
+ call TMPL_NM_CMN(StrFormatV)
+
+%ifdef TMPL_16BIT
+ pop fs
+ pop es
+ pop ds
+%endif
+ pop xBX
+ pop xCX
+ pop xDX
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(StrFormatF)
+
+
+;;
+; Dumps the CPU registers.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(TestDumpCurrentRegisters)
+%ifndef TMPL_64BIT
+ push xBP
+ mov xBP, xSP
+ push eax
+ pushfd
+
+ push dword [xBP - sCB - 4] ; eflags
+ mov eax, cr2
+ push eax
+ xor eax, eax
+ mov eax, gs
+ push eax
+ mov eax, fs
+ push eax
+ mov eax, es
+ push eax
+ mov eax, ds
+ push eax
+ mov eax, cs
+ push eax
+
+ mov eax, cr4
+ push eax
+ mov eax, cr3
+ push eax
+ mov eax, cr0
+ push eax
+ mov eax, ebp ; return EBP
+ mov xAX, [xBP]
+ push eax
+
+ mov eax, ebp ; return ESP
+ add xAX, xCB
+ push eax
+
+ mov xAX, [xBP + xCB] ; return EIP
+ %ifdef TMPL_16BIT
+ movzx eax, ax
+ %endif
+ push eax
+
+ push edi
+ push esi
+ push edx
+ push ecx
+ push ebx
+ push dword [xBP - sCB] ; eax
+
+ %ifdef TMPL_16BIT
+ push cs
+ %endif
+ push .s_szRegFmt
+ call TMPL_NM_CMN(PrintF)
+
+ popfd
+ pop eax
+ leave
+ ret
+
+.s_szRegFmt:
+ db 'eax=%RX32 ebx=%RX32 ecx=%RX32 edx=%RX32 esi=%RX32 edi=%RX32', 13, 10
+ db 'eip=%RX32 esp=%RX32 ebp=%RX32 cr0=%RX32 cr3=%RX32 cr4=%RX32', 13, 10
+ db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16 cr2=%RX32 eflags=%RX32', 13, 10, 0
+
+%else ; 64-bit
+ push .s_szRegFmt
+ call TMPL_NM_CMN(PrintF)
+ ret
+
+.s_szRegFmt:
+ db 'TestDumpCurrentRegisters not implemented', 13, 10, 0
+
+%endif ; 64-bit
+ENDPROC TMPL_NM_CMN(TestDumpCurrentRegisters)
+
+
+
+;;
+; Dumps the CPU registers.
+;
+; @param ds:xAX Pointer to the register frame to dump.
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(TestDumpRegisters)
+ push xBP
+ mov xBP, xSP
+ pushf
+ push sDX
+ push sBX
+ mov xBX, xAX
+
+ cmp byte [xBX + BS2REGS.cBits], 64
+ je .dump_64bit_regs
+
+ push -1 ; sanity
+ mov edx, [xBX + BS2REGS.rflags]
+ push sDX
+ mov edx, [xBX + BS2REGS.cr2]
+ push sDX
+ xor edx, edx
+ mov dx, [xBX + BS2REGS.ss]
+ push xDX
+ mov dx, [xBX + BS2REGS.gs]
+ push xDX
+ mov dx, [xBX + BS2REGS.fs]
+ push xDX
+ mov dx, [xBX + BS2REGS.es]
+ push xDX
+ mov dx, [xBX + BS2REGS.ds]
+ push xDX
+ mov dx, [xBX + BS2REGS.cs]
+ push xDX
+
+ mov edx, [xBX + BS2REGS.cr4]
+ push sDX
+ mov edx, [xBX + BS2REGS.cr3]
+ push sDX
+ mov edx, [xBX + BS2REGS.cr0]
+ push sDX
+ mov edx, [xBX + BS2REGS.rbp]
+ push sDX
+ mov edx, [xBX + BS2REGS.rsp]
+ push sDX
+ mov edx, [xBX + BS2REGS.rip]
+ push sDX
+
+ mov edx, [xBX + BS2REGS.rdi]
+ push sDX
+ mov edx, [xBX + BS2REGS.rsi]
+ push sDX
+ mov edx, [xBX + BS2REGS.rdx]
+ push sDX
+ mov edx, [xBX + BS2REGS.rcx]
+ push sDX
+ mov edx, [xBX + BS2REGS.rbx]
+ push sDX
+ mov edx, [xBX + BS2REGS.rax]
+ push sDX
+
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .s_szReg32Fmt
+ call TMPL_NM_CMN(PrintF)
+ jmp .return
+
+.dump_64bit_regs:
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .s_szReg64Fmt
+ call TMPL_NM_CMN(PrintF)
+
+.return:
+ lea xSP, [xBP - sCB*2 - xCB]
+ pop sBX
+ pop sDX
+ popf
+ leave
+ ret
+
+.s_szReg32Fmt:
+ db 'eax=%RX32 ebx=%RX32 ecx=%RX32 edx=%RX32 esi=%RX32 edi=%RX32', 13, 10
+ db 'eip=%RX32 esp=%RX32 ebp=%RX32 cr0=%RX32 cr3=%RX32 cr4=%RX32', 13, 10
+ db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16 cr2=%RX32 eflags=%RX32', 13, 10
+ db 0
+
+.s_szReg64Fmt:
+ db 'TestDumpCurrentRegisters not implemented', 13, 10, 0
+ENDPROC TMPL_NM_CMN(TestDumpRegisters)
+
+
+;;
+; Saves the CPU registers.
+;
+; @param ds:xAX Pointer to the register frame to dump.
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(TestSaveRegisters)
+ push xBP
+ mov xBP, xSP
+%ifdef TMPL_16BIT
+ pushfd
+%else
+ pushf ; - 1*sCB
+%endif
+ push sBX ; - 2*sCB
+ push sAX ; - 3*sCB
+ push sDX ; - 4*sCB
+
+ xor edx, edx ; zero register.
+ mov xBX, xAX ; xBX for addressing, xAX for scratch.
+
+%ifdef TMPL_64BIT
+ mov rax, [xSP + sCB*1]
+ mov [xBX + BS2REGS.rax], rax
+ mov rax, [xSP + sCB*2]
+ mov [xBX + BS2REGS.rbx], rax
+ mov [xBX + BS2REGS.rcx], rcx
+ mov rax, [xSP]
+ mov [xBX + BS2REGS.rdx], rax
+ mov [xBX + BS2REGS.rdi], rdi
+ mov [xBX + BS2REGS.rsi], rsi
+ mov rax, [xBP]
+ mov [xBX + BS2REGS.rbp], rax
+ lea rax, [xBP + 16]
+ mov [xBX + BS2REGS.rsp], rax
+ mov rax, [xBP + 8]
+ mov [xBX + BS2REGS.rip], rax
+ mov [xBX + BS2REGS.r8], r8
+ mov [xBX + BS2REGS.r9], r9
+ mov [xBX + BS2REGS.r10], r10
+ mov [xBX + BS2REGS.r11], r11
+ mov [xBX + BS2REGS.r12], r12
+ mov [xBX + BS2REGS.r13], r13
+ mov [xBX + BS2REGS.r14], r14
+ mov [xBX + BS2REGS.r15], r15
+ mov rax, [xBP - sCB]
+ mov [xBX + BS2REGS.rflags], rax
+ mov ax, cs
+ mov [xBX + BS2REGS.cs], ax
+ mov ax, ds
+ mov [xBX + BS2REGS.ds], ax
+ mov ax, es
+ mov [xBX + BS2REGS.es], ax
+ mov ax, fs
+ mov [xBX + BS2REGS.fs], ax
+ mov ax, gs
+ mov [xBX + BS2REGS.gs], ax
+ mov ax, ss
+ mov [xBX + BS2REGS.ss], ax
+ mov byte [xBX + BS2REGS.cBits], 64
+ mov [xBX + BS2REGS.pad ], dl
+ mov [xBX + BS2REGS.pad + 1], dx
+ mov rax, cr0
+ mov [xBX + BS2REGS.cr0], rax
+ mov rax, cr2
+ mov [xBX + BS2REGS.cr2], rax
+ mov rax, cr3
+ mov [xBX + BS2REGS.cr3], rax
+ mov rax, cr4
+ mov [xBX + BS2REGS.cr4], rax
+ mov rax, cr8
+ mov [xBX + BS2REGS.cr8], rax
+%else ; !TMPL_64
+ mov eax, [xBP - sCB*3]
+ mov dword [xBX + BS2REGS.rax], eax
+ mov dword [xBX + BS2REGS.rax + 4], edx
+ mov eax, [xBP - sCB*2]
+ mov dword [xBX + BS2REGS.rbx], eax
+ mov dword [xBX + BS2REGS.rbx + 4], edx
+ mov dword [xBX + BS2REGS.rcx], ecx
+ mov dword [xBX + BS2REGS.rcx + 4], edx
+ mov eax, [xBP - sCB*4]
+ mov dword [xBX + BS2REGS.rdx], eax
+ mov dword [xBX + BS2REGS.rdx + 4], edx
+ mov dword [xBX + BS2REGS.rdi], edi
+ mov dword [xBX + BS2REGS.rdi + 4], edx
+ mov dword [xBX + BS2REGS.rsi], esi
+ mov dword [xBX + BS2REGS.rsi + 4], edx
+ mov eax, ebp
+ mov ax, [xBP]
+ mov dword [xBX + BS2REGS.rbp], eax
+ mov dword [xBX + BS2REGS.rbp + 4], edx
+ %ifdef TMPL_16BIT
+ mov eax, esp
+ mov ax, bp
+ sub ax, 4
+ %else
+ lea eax, [ebp + 8]
+ %endif
+ mov dword [xBX + BS2REGS.rsp], eax
+ mov dword [xBX + BS2REGS.rsp + 4], edx
+ %ifdef TMPL_16BIT
+ movzx eax, word [xBP + 2]
+ %else
+ mov eax, [xBP + 4]
+ %endif
+ mov dword [xBX + BS2REGS.rip], eax
+ mov dword [xBX + BS2REGS.rip + 4], edx
+ mov dword [xBX + BS2REGS.r8 ], edx
+ mov dword [xBX + BS2REGS.r8 + 4], edx
+ mov dword [xBX + BS2REGS.r9 ], edx
+ mov dword [xBX + BS2REGS.r9 + 4], edx
+ mov dword [xBX + BS2REGS.r10 ], edx
+ mov dword [xBX + BS2REGS.r10 + 4], edx
+ mov dword [xBX + BS2REGS.r11 ], edx
+ mov dword [xBX + BS2REGS.r11 + 4], edx
+ mov dword [xBX + BS2REGS.r12 ], edx
+ mov dword [xBX + BS2REGS.r12 + 4], edx
+ mov dword [xBX + BS2REGS.r13 ], edx
+ mov dword [xBX + BS2REGS.r13 + 4], edx
+ mov dword [xBX + BS2REGS.r14 ], edx
+ mov dword [xBX + BS2REGS.r14 + 4], edx
+ mov dword [xBX + BS2REGS.r15 ], edx
+ mov dword [xBX + BS2REGS.r15 + 4], edx
+ mov eax, [xBP - sCB]
+ mov dword [xBX + BS2REGS.rflags], eax
+ mov dword [xBX + BS2REGS.rflags + 4], edx
+ mov ax, cs
+ mov [xBX + BS2REGS.cs], ax
+ mov ax, ds
+ mov [xBX + BS2REGS.ds], ax
+ mov ax, es
+ mov [xBX + BS2REGS.es], ax
+ mov ax, fs
+ mov [xBX + BS2REGS.fs], ax
+ mov ax, gs
+ mov [xBX + BS2REGS.gs], ax
+ mov ax, ss
+ mov [xBX + BS2REGS.ss], ax
+ %ifdef TMPL_16BIT
+ mov byte [xBX + BS2REGS.cBits], 16
+ %else
+ mov byte [xBX + BS2REGS.cBits], 32
+ %endif
+ mov [xBX + BS2REGS.pad ], dl
+ mov [xBX + BS2REGS.pad + 1], dx
+ mov eax, cr0
+ mov dword [xBX + BS2REGS.cr0], eax
+ mov dword [xBX + BS2REGS.cr0 + 4], edx
+ mov eax, cr2
+ mov dword [xBX + BS2REGS.cr2], eax
+ mov dword [xBX + BS2REGS.cr2 + 4], edx
+ mov eax, cr3
+ mov dword [xBX + BS2REGS.cr3], eax
+ mov dword [xBX + BS2REGS.cr3 + 4], edx
+ mov eax, cr4
+ mov dword [xBX + BS2REGS.cr4], eax
+ mov dword [xBX + BS2REGS.cr4 + 4], edx
+ mov dword [xBX + BS2REGS.cr8], edx
+ mov dword [xBX + BS2REGS.cr8 + 4], edx
+%endif ; !TMPL_64
+
+.return:
+ pop sDX
+ pop sAX
+ pop sBX
+%ifdef TMPL_16BIT
+ popfd
+%else
+ popf
+%endif
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestSaveRegisters)
+
+
+;;
+; Restores the CPU registers, except for rsp, rip, cs, ss and ds.
+;
+; @param ds:xAX Pointer to the register frame to dump.
+; @uses All, but RSP, RIP, CS, SS and DS.
+;
+BEGINPROC TMPL_NM_CMN(TestRestoreRegisters)
+ push xBP
+ mov xBP, xSP
+%ifdef TMPL_16BIT
+ pushfd
+%else
+ pushf ; - 1*sCB
+%endif
+ push sBX ; - 2*sCB
+ push sAX ; - 3*sCB
+
+ mov xBX, xAX ; xBX for addressing, xAX for scratch.
+
+ mov sAX, [xBX + BS2REGS.rax]
+ mov [xBP - 3*sCB], sAX
+ mov sAX, [xBX + BS2REGS.rbx]
+ mov [xBP - 2*sCB], sAX
+ mov sCX, [xBX + BS2REGS.rcx]
+ mov sDX, [xBX + BS2REGS.rdx]
+ mov sDI, [xBX + BS2REGS.rdi]
+ mov sSI, [xBX + BS2REGS.rsi]
+ ; skip rsp, rbp or rip.
+%ifdef TMPL_64BIT
+ mov r8, [xBX + BS2REGS.r8]
+ mov r9, [xBX + BS2REGS.r9]
+ mov r10, [xBX + BS2REGS.r10]
+ mov r11, [xBX + BS2REGS.r11]
+ mov r12, [xBX + BS2REGS.r12]
+ mov r13, [xBX + BS2REGS.r13]
+ mov r14, [xBX + BS2REGS.r14]
+ mov r15, [xBX + BS2REGS.r15]
+%endif
+ mov sAX, [xBX + BS2REGS.rflags]
+ mov [xBP - sCB], sAX
+ ; skip cs & ds.
+ mov ax, [xBX + BS2REGS.es]
+ mov es, ax
+ mov ax, [xBX + BS2REGS.fs]
+ mov fs, ax
+ mov ax, [xBX + BS2REGS.gs]
+ mov gs, ax
+ ; skip ss
+ mov sAX, [xBX + BS2REGS.cr0]
+ mov cr0, sAX
+ mov sAX, [xBX + BS2REGS.cr2]
+ mov cr2, sAX
+ mov sAX, [xBX + BS2REGS.cr3]
+ mov cr3, sAX
+ mov sAX, [xBX + BS2REGS.cr4]
+ mov cr4, sAX
+
+.return:
+ pop sAX
+ pop sBX
+%ifdef TMPL_16BIT
+ popfd
+%else
+ popf
+%endif
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(TestRestoreRegisters)
+
+
+;;
+; Enables the A20 gate.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2EnableA20)
+ call TMPL_NM_CMN(Bs2EnableA20ViaPortA)
+; call TMPL_NM_CMN(Bs2EnableA20ViaKbd)
+ ret
+ENDPROC TMPL_NM_CMN(Bs2EnableA20)
+
+
+;;
+; Disables the A20 gate.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2DisableA20)
+ ; Must call both because they may be ORed together on real HW.
+ call TMPL_NM_CMN(Bs2DisableA20ViaKbd)
+ call TMPL_NM_CMN(Bs2DisableA20ViaPortA)
+ ret
+ENDPROC TMPL_NM_CMN(Bs2DisableA20)
+
+
+;;
+; Waits for the keyboard controller to become ready.
+;
+; @uses Nothing
+;
+BEGINPROC TMPL_NM_CMN(Bs2KbdWait)
+ push xAX
+
+.check_status:
+ in al, 64h
+ test al, 1 ; KBD_STAT_OBF
+ jnz .read_data_and_status
+ test al, 2 ; KBD_STAT_IBF
+ jnz .check_status
+
+ pop xAX
+ ret
+
+.read_data_and_status:
+ in al, 60h
+ jmp .check_status
+ENDPROC TMPL_NM_CMN(Bs2KbdWait)
+
+
+;;
+; Sends a read command to the keyboard controller and gets the result.
+;
+; The caller is responsible for making sure the keyboard controller is ready
+; for a command (call Bs2KbdWait if unsure).
+;
+; @param al The read command.
+; @returns The value read is returned.
+; @uses al (obviously)
+;
+BEGINPROC TMPL_NM_CMN(Bs2KbdRead)
+ out 64h, al ; Write the command.
+
+.check_status:
+ in al, 64h
+ test al, 1 ; KBD_STAT_OBF
+ jz .check_status
+
+ in al, 60h ; Read the data.
+ ret
+ENDPROC TMPL_NM_CMN(Bs2KbdRead)
+
+
+;;
+; Sends a write command to the keyboard controller and then sends the data.
+;
+; The caller is responsible for making sure the keyboard controller is ready
+; for a command (call Bs2KbdWait if unsure).
+;
+; @param al The write command.
+; @param ah The data to write.
+; @uses Nothing.
+;
+; @todo Return status?
+;
+BEGINPROC TMPL_NM_CMN(Bs2KbdWrite)
+ out 64h, al ; Write the command.
+ call TMPL_NM_CMN(Bs2KbdWait)
+
+ xchg al, ah
+ out 60h, al ; Write the data.
+ call TMPL_NM_CMN(Bs2KbdWait)
+ xchg al, ah
+
+ ret
+ENDPROC TMPL_NM_CMN(Bs2KbdWrite)
+
+
+;;
+; Enables the A20 gate via the keyboard controller.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2EnableA20ViaKbd)
+ push xAX
+ pushf
+ cli
+
+ call TMPL_NM_CMN(Bs2KbdWait)
+ mov al, 0d0h ; KBD_CCMD_READ_OUTPORT
+ call TMPL_NM_CMN(Bs2KbdRead)
+
+ mov ah, 002h
+ or ah, al
+ mov al, 0d1h ; KBD_CCMD_WRITE_OUTPORT
+ call TMPL_NM_CMN(Bs2KbdWrite)
+
+ mov al, 0ffh ; KBD_CMD_RESET
+ out 64h, al
+ call TMPL_NM_CMN(Bs2KbdWait)
+
+ popf
+ pop xAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2EnableA20ViaKbd)
+
+
+;;
+; Disables the A20 gate via the keyboard controller.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2DisableA20ViaKbd)
+ push xAX
+ pushf
+ cli
+
+ call TMPL_NM_CMN(Bs2KbdWait)
+ mov al, 0d0h ; KBD_CCMD_READ_OUTPORT
+ call TMPL_NM_CMN(Bs2KbdRead)
+
+ mov ah, 0fdh ; ~2
+ and ah, al
+ mov al, 0d1h ; KBD_CCMD_WRITE_OUTPORT
+ call TMPL_NM_CMN(Bs2KbdWrite)
+
+ mov al, 0ffh ; KBD_CMD_RESET
+ out 64h, al
+ call TMPL_NM_CMN(Bs2KbdWait)
+
+ popf
+ pop xAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2DisableA20ViaKbd)
+
+
+;;
+; Enables the A20 gate via control port A (PS/2 style).
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2EnableA20ViaPortA)
+ push xAX
+
+ ; Use Control port A, assuming a PS/2 style system.
+ in al, 092h
+ test al, 02h
+ jnz .done ; avoid trouble writing back the same value.
+ or al, 2 ; enable the A20 gate.
+ out 092h, al
+
+.done:
+ pop xAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2EnableA20ViaPortA)
+
+
+;;
+; Disables the A20 gate via control port A (PS/2 style).
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2DisableA20ViaPortA)
+ push xAX
+
+ ; Use Control port A, assuming a PS/2 style system.
+ in al, 092h
+ test al, 02h
+ jz .done ; avoid trouble writing back the same value.
+ and al, 0fdh ; disable the A20 gate.
+ out 092h, al
+
+.done:
+ pop xAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2DisableA20ViaPortA)
+
+
+;;
+; Checks if the no-execution bit is supported.
+;
+; @returns AL=1 and ZF=0 if supported.
+; @returns AL=0 and ZF=1 if not.
+; @uses al
+;
+BEGINPROC TMPL_NM_CMN(Bs2IsNXSupported)
+ push xBP
+ mov xBP, xSP
+ push sBX
+ push sDX
+ push sCX
+ push sAX
+
+ mov eax, 080000000h
+ cpuid
+ cmp eax, 080000001h
+ jb .not_supported
+ cmp eax, 080001000h
+ jae .not_supported
+
+ mov eax, 080000001h
+ cpuid
+ test edx, X86_CPUID_EXT_FEATURE_EDX_NX
+ jz .not_supported
+
+ ; supported
+ pop sAX
+ mov al, 1
+
+.return:
+ pop sCX
+ pop sDX
+ pop sBX
+ leave
+ ret
+.not_supported:
+ pop sAX
+ xor al, al
+ jmp .return
+ENDPROC TMPL_NM_CMN(Bs2IsNXSupported)
+
+
+;;
+; Sets EFER.NXE=al if NXE is supported.
+;
+; @param al 0 if NXE should be disabled, non-zero if it should
+; be enabled.
+; @uses nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2SetupNX)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+
+ call TMPL_NM_CMN(Bs2IsNXSupported)
+ jz .done
+
+ mov ecx, MSR_K6_EFER
+ rdmsr
+ test byte [xBP - sCB], 0ffh
+ jz .disable_it
+ or eax, MSR_K6_EFER_NXE
+ jmp .set_it
+.disable_it:
+ and eax, ~MSR_K6_EFER_NXE
+.set_it:
+ wrmsr
+
+.done:
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(Bs2SetupNX)
+
+
+;;
+; Disables NX if supported.
+;
+; @uses nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2DisableNX)
+ push xBP
+ mov xBP, xSP
+ push xAX
+
+ xor al, al
+ call TMPL_NM_CMN(Bs2SetupNX)
+
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(Bs2DisableNX)
+
+
+;;
+; Enables NX if supported.
+;
+; @uses nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2EnableNX)
+ push xBP
+ mov xBP, xSP
+ push xAX
+
+ mov al, 1
+ call TMPL_NM_CMN(Bs2SetupNX)
+
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM_CMN(Bs2EnableNX)
+
+
+;;
+; Panics if the testing feature of the VMMDev is missing.
+;
+; A message will be printed.
+;
+; @uses Nothing.
+;
+BEGINPROC TMPL_NM_CMN(Bs2PanicIfVMMDevTestingIsMissing)
+ push xDX
+ push sAX
+
+ xor eax, eax
+ mov dx, VMMDEV_TESTING_IOPORT_NOP
+ in eax, dx
+ cmp eax, VMMDEV_TESTING_NOP_RET
+ je .ok
+
+ mov xAX, .s_szMissingVMMDevTesting
+ call TMPL_NM_CMN(PrintStr)
+ call Bs2Panic
+
+.ok:
+ pop sAX
+ pop xDX
+ ret
+
+.s_szMissingVMMDevTesting:
+ db 'fatal error: The testing feature of the VMMDevVMMDev is not present!', 13, 10, 0
+ENDPROC TMPL_NM_CMN(Bs2PanicIfVMMDevTestingIsMissing)
+
+
+
+%ifdef BS2_WITH_TRAPS
+
+;;
+; Switches to ring-0 from whatever the current mode is.
+;
+; @uses cs, ss, ds, es, fs, gs
+;
+BEGINPROC TMPL_NM_CMN(Bs2ToRing0)
+ push sAX
+ mov sAX, BS2_SYSCALL_TO_RING0
+ int BS2_TRAP_SYSCALL
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2ToRing0)
+
+;;
+; Switches to ring-1 from whatever the current mode is.
+;
+; @uses cs, ss, ds, es, fs, gs
+;
+BEGINPROC TMPL_NM_CMN(Bs2ToRing1)
+ push sAX
+ mov sAX, BS2_SYSCALL_TO_RING1
+ int BS2_TRAP_SYSCALL
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2ToRing1)
+
+;;
+; Switches to ring-0 from whatever the current mode is.
+;
+; @uses cs, ss, ds, es, fs, gs
+;
+BEGINPROC TMPL_NM_CMN(Bs2ToRing2)
+ push sAX
+ mov sAX, BS2_SYSCALL_TO_RING2
+ int BS2_TRAP_SYSCALL
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2ToRing2)
+
+;;
+; Switches to ring-3 from whatever the current mode is.
+;
+; @uses cs, ss, ds, es, fs, gs
+;
+BEGINPROC TMPL_NM_CMN(Bs2ToRing3)
+ push sAX
+ mov sAX, BS2_SYSCALL_TO_RING3
+ int BS2_TRAP_SYSCALL
+ pop sAX
+ ret
+ENDPROC TMPL_NM_CMN(Bs2ToRing3)
+
+;;
+; Switches the given ring from whatever the current mode is.
+;
+; @param AL The desired ring.
+; @uses cs, ss, ds, es, fs, gs
+;
+BEGINPROC TMPL_NM_CMN(Bs2ToRingN)
+ pushf
+ cmp al, 3
+ je .ring3
+ cmp al, 2
+ je .ring2
+ cmp al, 1
+ je .ring1
+.ring0:
+ call TMPL_NM_CMN(Bs2ToRing0)
+.done:
+ popf
+ ret
+
+.ring1:
+ call TMPL_NM_CMN(Bs2ToRing1)
+ jmp .done
+.ring2:
+ call TMPL_NM_CMN(Bs2ToRing2)
+ jmp .done
+.ring3:
+ call TMPL_NM_CMN(Bs2ToRing3)
+ jmp .done
+ENDPROC TMPL_NM_CMN(Bs2ToRingN)
+
+%endif ; BS2_WITH_TRAPS
+
+
+
+;
+; Wrapper for dynamically calling the right specific method.
+; This avoid putting large portions of the code in the 2nd template.
+;
+
+TMPL_NM_CMN(g_pfnPrintStrInternal): TMPL_PTR_DEF 0
+TMPL_NM_CMN(g_pfnPrintChrInternal): TMPL_PTR_DEF 0
+
+BEGINPROC TMPL_NM_CMN(PrintStr)
+ jmp [TMPL_NM_CMN(g_pfnPrintStrInternal)]
+ENDPROC TMPL_NM_CMN(PrintStr)
+
+BEGINPROC TMPL_NM_CMN(PrintChr)
+ jmp [TMPL_NM_CMN(g_pfnPrintChrInternal)]
+ENDPROC TMPL_NM_CMN(PrintChr)
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-2.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-2.mac
new file mode 100644
index 00000000..89432af8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-2.mac
@@ -0,0 +1,224 @@
+; $Id: bootsector2-common-routines-template-2.mac $
+;; @file
+; bootsector2 common routines - template containing code specific to each mode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bootsector2-template-header.mac"
+ALIGNCODE(32)
+GLOBALNAME TMPL_NM(g_szMode)
+ db TMPL_MODE_STR, 0
+
+
+;;
+; Prints a string on the screen.
+;
+; @param ds:ax The string to print (null terminated).
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(PrintStr)
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xBX
+ push xSI
+
+ mov xSI, xAX
+.next:
+ lodsb
+ test al, al
+ jz .done
+%ifdef TMPL_HAVE_BIOS
+ mov bx, 0ff00h
+ mov ah, 0eh
+ int 10h
+%else
+ call TMPL_NM(PrintChr)
+%endif
+ jmp .next
+
+.done:
+ pop xSI
+ pop xBX
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM(PrintStr)
+
+
+;;
+; Prints a string on the screen.
+;
+; @param al The character to print.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BEGINPROC TMPL_NM(PrintChr)
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xBX
+
+%ifndef TMPL_HAVE_BIOS
+ %ifdef BS2_WITH_TRAPS
+ mov bx, cs
+ and xBX, 0x3
+ push xBX
+ jz .ring_ok
+ call TMPL_NM_CMN(Bs2ToRing0)
+.ring_ok:
+ %endif
+
+ mov bl, al
+ call TMPL_NM(LeaveCpuMode)
+ mov al, bl
+BITS 16
+%endif
+
+ mov bx, 0ff00h
+ mov ah, 0eh
+ int 10h
+
+%ifndef TMPL_HAVE_BIOS
+ call TMPL_NM(EnterCpuMode)
+BITS TMPL_BITS
+ %ifdef BS2_WITH_TRAPS
+ pop xAX
+ test al, al
+ jz .ring_restored
+ call TMPL_NM_CMN(Bs2ToRingN)
+.ring_restored:
+ %endif
+%endif
+
+ pop xBX
+ pop xAX
+ leave
+ ret
+ENDPROC TMPL_NM(PrintChr)
+TMPL_BEGINCODE
+
+
+%ifndef TMPL_HAVE_BIOS
+
+;;
+; Leaves the current CPU mode and returns to real mode.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(LeaveCpuMode)
+ jmp TMPL_NM(Bs2ExitMode)
+ENDPROC TMPL_NM(LeaveCpuMode)
+
+
+;;
+; Undo what LeaveCpuMode did.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(EnterCpuMode)
+ jmp TMPL_NM(Bs2EnterMode_rm)
+ENDPROC TMPL_NM(EnterCpuMode)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+%endif ; TMPL_HAVE_BIOS
+
+
+;;
+; Sets the global variable for the current CPU mode.
+;
+; @uses nothing.
+;
+BEGINPROC TMPL_NM(SetCpuModeGlobals)
+%ifdef TMPL_CMN_PE
+ %ifdef BS2_INC_PE16
+ mov word [g_pfnPrintStrInternal_p16], PrintStr_pe16
+ mov word [g_pfnPrintChrInternal_p16], PrintChr_pe16
+ %endif
+ %ifdef BS2_INC_PE32
+ mov dword [g_pfnPrintStrInternal_p32], PrintStr_pe32
+ mov dword [g_pfnPrintChrInternal_p32], PrintChr_pe32
+ %endif
+
+%elifdef TMPL_CMN_PP
+ %ifdef BS2_INC_PP16
+ mov word [g_pfnPrintStrInternal_p16], PrintStr_pp16
+ mov word [g_pfnPrintChrInternal_p16], PrintChr_pp16
+ %endif
+ %ifdef BS2_INC_PP32
+ mov dword [g_pfnPrintStrInternal_p32], PrintStr_pp32
+ mov dword [g_pfnPrintChrInternal_p32], PrintChr_pp32
+ %endif
+
+%elifdef TMPL_CMN_PAE
+ %ifdef BS2_INC_PAE16
+ mov word [g_pfnPrintStrInternal_p16], PrintStr_pae16
+ mov word [g_pfnPrintChrInternal_p16], PrintChr_pae16
+ %endif
+ %ifdef BS2_INC_PAE32
+ mov dword [g_pfnPrintStrInternal_p32], PrintStr_pae32
+ mov dword [g_pfnPrintChrInternal_p32], PrintChr_pae32
+ %endif
+
+%elifdef TMPL_CMN_LM
+ %ifdef BS2_INC_LM16
+ mov word [g_pfnPrintStrInternal_p16], PrintStr_lm16
+ mov word [g_pfnPrintChrInternal_p16], PrintChr_lm16
+ %endif
+ %ifdef BS2_INC_LM32
+ mov dword [g_pfnPrintStrInternal_p32], PrintStr_lm32
+ mov dword [g_pfnPrintChrInternal_p32], PrintChr_lm32
+ %endif
+ %ifdef BS2_INC_LM64
+ mov dword [g_pfnPrintStrInternal_p64], PrintStr_lm64
+ mov dword [g_pfnPrintChrInternal_p64], PrintChr_lm64
+ %endif
+
+%elifdef TMPL_16BIT
+ mov word [TMPL_NM_CMN(g_pfnPrintStrInternal)], TMPL_NM(PrintStr)
+ mov word [TMPL_NM_CMN(g_pfnPrintChrInternal)], TMPL_NM(PrintChr)
+%else
+ %error "missing case"
+%endif
+ ret
+ENDPROC TMPL_NM(SetCpuModeGlobals)
+
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines.mac
new file mode 100644
index 00000000..95221d0a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines.mac
@@ -0,0 +1,246 @@
+; $Id: bootsector2-common-routines.mac $
+;; @file
+; Common bootsector routines.
+;
+; This is just a bit file with common code that can be included at the end of
+; a bootsector2-xxx.asm file. Conventions (used elsewhere as well):
+; - _rm - real-mode function suffix.
+; - _r86 - common real and virtual 8086 mode suffix.
+; - _p16 - common 16-bit protected mode suffix.
+; - _p32 - common 32-bit protected mode suffix.
+; - _p64 - common 64-bit long mode suffix.
+; - _pe16 - 16-bit unpaged protected mode suffix.
+; - _pe32 - 32-bit unpaged protected mode suffix.
+; - _pev86 - v8086 unpaged protected mode suffix.
+; - _pp16 - 16-bit paged protected mode suffix.
+; - _pp32 - 32-bit paged protected mode suffix.
+; - _ppv86 - v8086 paged protected mode suffix.
+; - _pae16 - 16-bit pae protected mode suffix.
+; - _pae32 - 32-bit pae protected mode suffix.
+; - _paev86- v8086 pae protected mode suffix.
+; - _lm16 - 16-bit long mode suffix.
+; - _lm32 - 32-bit long mode suffix.
+; - _lm64 - 64-bit long mode suffix.
+;
+; The routines uses a custom register based calling convention for all cpu
+; modes so that the users can template multi mode code. To make life easy for
+; the programmer all registers are preserved with the exception of rflags and
+; any return registers that may be used. Routines that does not return
+; anything will only clobber eflags.
+;
+; The parameter register allocation order:
+; rax, rdx, rcx, rbx, rsi, rdi(, r8, r9, r10, r11)
+;
+; When pointers are passed by 16-bit code, segments registers are allocated in
+; the following order:
+; ds, es, fs, gs.
+;
+; The return register allocations are:
+; - edx:eax for 64-bit values in 16 and 32-bit mode,
+; - eax for 32-bit,
+; - ax for 16-bit,
+; - al for 8-bit.
+;
+; Routines may use other calling convensions will be named differently.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Structures and Typedefs *
+;*******************************************************************************
+
+
+;*******************************************************************************
+;* Global Variables *
+;*******************************************************************************
+BEGINCODELOW
+ALIGNDATA(32)
+;; Indicates whether the VMMDev is operational.
+GLOBALNAME g_fbBs2VMMDevTesting
+ db 1
+ db 0 ; padding
+
+;; The test name (DS16:xxx).
+g_npszBs2Test:
+ dd 0
+;; The number of tests that have failed.
+g_uscBs2TestErrors:
+ dw 0
+;; The subtest name (DS16:xxx).
+g_npszBs2SubTest
+ dd 0
+;; The start error count of the current subtest.
+g_uscBs2SubTestAtErrors:
+ dw 0
+;; Whether we've reported the sub-test result or not.
+g_fbBs2SubTestReported:
+ db 0
+ db 0 ; padding
+;; The number of sub tests.
+g_uscBs2SubTests:
+ dw 0
+;; The number of sub tests that failed.
+g_uscBs2SubTestsFailed:
+ dw 0
+
+
+;; VMMDEV_TESTING_UNIT_XXX -> string
+g_aszBs2TestUnitNames:
+ db 'i','n','v', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db '%', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'b','y','t','e','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'b','y','t','e','s','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'K','B', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'K','B','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'M','B', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'M','B','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','a','c','k','e','t','s', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','a','c','k','e','t','s','/','s', 0, 0, 0, 0, 0, 0, 0
+ db 'f','r','a','m','e','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'f','r','a','m','e','s','/', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'o','c','c', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'o','c','c','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'r','n','d','t','r','p', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'c','a','l','l','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'c','a','l','l','s','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 's', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'm','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'n','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'n','s','/','c','a','l','l', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'n','s','/','f','r','a','m','e', 0, 0, 0, 0, 0, 0, 0, 0
+ db 'n','s','/','o','c','c', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'n','s','/','p','a','c','k','e','t', 0, 0, 0, 0, 0, 0, 0
+ db 'n','s','/','r','n','d','t','r','p', 0, 0, 0, 0, 0, 0, 0
+ db 'i','n','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'i','n','s','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; none
+ db 'p','p','1','k', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','p','1','0','k', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','p','m', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','p','b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 't','i','c','k','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 't','i','c','k','s','/','c','a','l','l', 0, 0, 0, 0, 0, 0
+ db 't','i','c','k','s','/','o','c','c', 0, 0, 0, 0, 0, 0, 0
+ db 'p','a','g','e','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','a','g','e','s','/','s', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 't','i','c','k','s','/','p','a','g','e', 0, 0, 0, 0, 0, 0
+ db 'n','s','/','p','a','g','e', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','s', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','c','a','l','l', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','f','r','a','m','e', 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','o','c','c', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','p','a','c','k','e','t', 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','r','n','d','t','r','p', 0, 0, 0, 0, 0, 0, 0
+ db 'p','s','/','p','a','g','e', 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
+
+
+;
+; Instantiate the common template code.
+;
+%ifdef BS2_INC_CMN_R86
+ %define TMPL_RM
+ %include "bootsector2-common-routines-template-1.mac"
+%endif
+%ifdef BS2_INC_CMN_P16
+ %define TMPL_PE16
+ %include "bootsector2-common-routines-template-1.mac"
+%endif
+%ifdef BS2_INC_CMN_P32
+ %define TMPL_PE32
+ %include "bootsector2-common-routines-template-1.mac"
+%endif
+%ifdef BS2_INC_LM64
+ %define TMPL_LM64
+ %include "bootsector2-common-routines-template-1.mac"
+%endif
+
+;
+; Instantiate the mode specific code.
+;
+%ifdef BS2_INC_RM
+ %define TMPL_RM
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PE16
+ %define TMPL_PE16
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PE32
+ %define TMPL_PE32
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PEV86
+ %define TMPL_PEV86
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PP16
+ %define TMPL_PP16
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PP32
+ %define TMPL_PP32
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PPV86
+ %define TMPL_PPV86
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PAE16
+ %define TMPL_PAE16
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PAE32
+ %define TMPL_PAE32
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_PAEV86
+ %define TMPL_PAEV86
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_LM16
+ %define TMPL_LM16
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_LM32
+ %define TMPL_LM32
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+%ifdef BS2_INC_LM64
+ %define TMPL_LM64
+ %include "bootsector2-common-routines-template-2.mac"
+%endif
+
+BEGINCODELOW
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec-template.mac
new file mode 100644
index 00000000..2c16f780
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec-template.mac
@@ -0,0 +1,115 @@
+; $Id: bootsector2-common-traprec-template.mac $
+;; @file
+; Boot sector 2 - Trap Records, Code Template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bootsector2-template-header.mac"
+
+
+;;
+; Internal worker for reporting a missing trap
+;
+; The callee cleans up the arguments on the stack.
+;
+; @param [xBP + xCB*2] bExpected Expected exception number.
+; @param [xBP + xCB*2+1] szExpected The name of the exception (2 bytes + terminator).
+; @uses None
+;
+BEGINPROC TMPL_NM_CMN(TestFailedMissingTrapInternal)
+ push xBP
+ mov xBP, xSP
+ pushf
+ push sAX
+
+ movzx eax, byte [xBP + xCB*2]
+ push xAX
+ lea sAX, [sBP + xCB*2+1]
+%ifdef TMPL_16BIT
+ push ss
+%endif
+ push xAX
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .szFmt
+ call TMPL_NM_CMN(TestFailedF)
+%ifdef TMPL_16BIT
+ add xSP, xCB*5
+%else
+ add xSP, xCB*3
+%endif
+
+ pop sAX
+ popf
+ leave
+ ret sCB
+.szFmt: db 'Missing trap #%s (%RX8)', 13, 10, 0
+ENDPROC TMPL_NM_CMN(TestFailedMissingTrapInternal)
+
+%ifndef TestFailedMissingTrapTemplate_defined
+ ;;
+ ; Internal template.
+ %macro TestFailedMissingTrapTemplate 4
+ BEGINPROC TMPL_NM_CMN(TestFailedMissingTrap_%1)
+ push dword RT_MAKE_U32_FROM_U8(%1, %2, %3, %4)
+ call TMPL_NM_CMN(TestFailedMissingTrapInternal)
+ ret
+ ENDPROC TMPL_NM_CMN(TestFailedMissingTrap_%1)
+ %endmacro
+ %define TestFailedMissingTrapTemplate_defined
+%endif
+
+TestFailedMissingTrapTemplate X86_XCPT_DE, 'D', 'E', 0
+TestFailedMissingTrapTemplate X86_XCPT_DB, 'D', 'B', 0
+TestFailedMissingTrapTemplate X86_XCPT_NMI, 'N', 'M', 0
+TestFailedMissingTrapTemplate X86_XCPT_BP, 'B', 'P', 0
+TestFailedMissingTrapTemplate X86_XCPT_OF, 'O', 'F', 0
+TestFailedMissingTrapTemplate X86_XCPT_BR, 'B', 'R', 0
+TestFailedMissingTrapTemplate X86_XCPT_UD, 'U', 'D', 0
+TestFailedMissingTrapTemplate X86_XCPT_NM, 'N', 'M', 0
+;TestFailedMissingTrapTemplate X86_XCPT_DF, 'D', 'F', 0
+;TestFailedMissingTrapTemplate X86_XCPT_CO_SEG_OVERRUN, 'C', 'O', 0
+TestFailedMissingTrapTemplate X86_XCPT_TS, 'T', 'S', 0
+TestFailedMissingTrapTemplate X86_XCPT_NP, 'N', 'P', 0
+TestFailedMissingTrapTemplate X86_XCPT_SS, 'S', 'S', 0
+TestFailedMissingTrapTemplate X86_XCPT_GP, 'G', 'P', 0
+TestFailedMissingTrapTemplate X86_XCPT_PF, 'P', 'F', 0
+TestFailedMissingTrapTemplate X86_XCPT_MF, 'M', 'F', 0
+TestFailedMissingTrapTemplate X86_XCPT_AC, 'A', 'C', 0
+;TestFailedMissingTrapTemplate X86_XCPT_MC, 'M', 'C', 0
+TestFailedMissingTrapTemplate X86_XCPT_XF, 'X', 'F', 0
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec.mac
new file mode 100644
index 00000000..0b927dc5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-traprec.mac
@@ -0,0 +1,209 @@
+; $Id: bootsector2-common-traprec.mac $
+;; @file
+; Boot sector 2 - Trap Records.
+;
+; @note Don't forget to cinldue bootsector2-common-traprec-end.mac!
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef ___bootsector2_common_traprec_mac___
+%define ___bootsector2_common_traprec_mac___
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/x86.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;;
+; The base address for the records (only important for 64-bit code
+; loaded above 4GB).
+; We use 0 by default so we don't create too complex expressions for YASM.
+%ifndef BS2_TRAP_RECS_BASE
+ %define BS2_TRAP_RECS_BASE 0
+%endif
+
+;;
+; Macro to emit an trapping instruction.
+;
+; @param 1 The trap number (X86_XCPT_XXX).
+; @param 2 The error code, 0 if none.
+; @param 3+ The instruction.
+;
+; @sa BS2_TRAP_BRANCH_INSTR
+;
+%macro BS2_TRAP_INSTR 3+,
+ [section .traprecs]
+ istruc BS2TRAPREC
+ at BS2TRAPREC.offWhere, dd (%%trapinstr - BS2_TRAP_RECS_BASE)
+ at BS2TRAPREC.offResumeAddend, db (%%resume - %%trapinstr)
+ at BS2TRAPREC.u8TrapNo, db %1
+ at BS2TRAPREC.u16ErrCd, dw %2
+ iend
+ __SECT__
+ %if %1 != X86_XCPT_BP
+ %%trapinstr:
+ %3
+ %else
+ %3
+ %%trapinstr:
+ %endif
+ call TMPL_NM_CMN(TestFailedMissingTrap_ %+ %1)
+ %%resume:
+%endmacro
+
+;;
+; Macro to emit an trapping instruction.
+;
+; @param 1 The trap number (X86_XCPT_XXX).
+; @param 2 The error code, 0 if none.
+; @param 3 The name of the branch label.
+; @param 4+ The instruction.
+;
+%macro BS2_TRAP_BRANCH_INSTR 4+,
+ [section .traprecs]
+ istruc BS2TRAPREC
+ at BS2TRAPREC.offWhere, dd (%%trapinstr - BS2_TRAP_RECS_BASE)
+ at BS2TRAPREC.offResumeAddend, db (%%resume - %%trapinstr)
+ at BS2TRAPREC.u8TrapNo, db %1
+ at BS2TRAPREC.u16ErrCd, dw %2
+ iend
+ __SECT__
+ %%trapinstr:
+ %4
+ %3:
+ call TMPL_NM_CMN(TestFailedMissingTrap_ %+ %1)
+ %%resume:
+%endmacro
+
+;;
+; Sets up the trap records section.
+; @internal
+%macro BS2_TRAP_RECS_BEGIN 0,
+ [section .traprecs] ; Declared in bootsector2-common-init-code.mac
+ dq 0ffffffffeeeeeeeeh
+g_aTrapRecs:
+ __SECT__
+%endmacro
+
+;;
+; Terminates the trap records section.
+; @internal
+%macro BS2_TRAP_RECS_END 0,
+ [section .traprecs]
+g_aTrapRecsEnd:
+ dq 0ddddddddcccccccch
+ __SECT__
+%endmacro
+
+
+;;
+; Macro for installing the trap records.
+;
+; This must be invoked prior to the traps.
+;
+; @uses Stack
+;
+%macro BS2_TRAP_RECS_INSTALL 0,
+ push sAX
+ push sDX
+ push sCX
+
+ mov sAX, NAME(g_aTrapRecs)
+ mov edx, NAME(g_aTrapRecsEnd) - NAME(g_aTrapRecs)
+ shr edx, BS2TRAPREC_SIZE_SHIFT
+ mov sCX, BS2_TRAP_RECS_BASE
+ call TMPL_NM_CMN(TestInstallTrapRecs)
+
+ pop sAX
+ pop sDX
+ pop sCX
+%endmacro
+
+
+;;
+; Macro for uninstalling the trap records.
+;
+; @uses Stack
+;
+%macro BS2_TRAP_RECS_UNINSTALL 0,
+ push sAX
+ push sDX
+ push sCX
+
+ xor sAX, sAX
+ xor edx, edx
+ xor sCX, sCX
+ call TMPL_NM_CMN(TestInstallTrapRecs)
+
+ pop sAX
+ pop sDX
+ pop sCX
+%endmacro
+
+
+;
+; Setup the trap record segment.
+;
+BS2_TRAP_RECS_BEGIN
+BEGINCODELOW
+
+
+;
+; Instantiate code templates.
+;
+%ifdef BS2_INC_CMN_R86
+ %define TMPL_RM
+ %include "bootsector2-common-traprec-template.mac"
+%endif
+%ifdef BS2_INC_CMN_P16
+ %define TMPL_PE16
+ %include "bootsector2-common-traprec-template.mac"
+%endif
+%ifdef BS2_INC_CMN_P32
+ %define TMPL_PE32
+ %include "bootsector2-common-traprec-template.mac"
+%endif
+%ifdef BS2_INC_LM64
+ %define TMPL_LM64
+ %include "bootsector2-common-traprec-template.mac"
+%endif
+
+BEGINCODELOW
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1-template.mac
new file mode 100644
index 00000000..2b67f908
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1-template.mac
@@ -0,0 +1,385 @@
+; $Id: bootsector2-cpu-a20-1-template.mac $
+;; @file
+; bootsector2 A20 - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;;
+; Inner loop dealing with one 64KB segment.
+;
+BEGINPROC TMPL_NM(TestA20_EnabledInner)
+.inner_loop:
+ mov ah, [es:xDI]
+ mov al, [ds:xSI]
+ cmp al, ah
+ jne .inner_next
+
+ not al
+ mov [es:xDI], al
+ cmp al, [ds:xSI]
+ mov [es:xDI], ah
+ je .failed
+
+.inner_next:
+ inc xDI
+ inc xSI
+ dec ecx
+ jnz .inner_loop
+
+ clc
+ ret
+
+.failed:
+ push sBX
+ push sAX
+ push sSI
+ push sDI
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .s_szModifiyError
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB * 5
+ stc
+ ret
+.s_szModifiyError:
+ db TMPL_MODE_STR, '- Same memory; EDI=%RX32 ESI=%RX32 EAX=%RX32 EBX=%RX32', 13, 10, 0
+ENDPROC TMPL_NM(TestA20_EnabledInner)
+
+
+;;
+; Inner loop dealing with one 64KB segment.
+;
+BEGINPROC TMPL_NM(TestA20_DisabledInner)
+.inner_loop:
+ mov ah, [es:xDI]
+ mov al, [ds:xSI]
+ cmp al, ah
+ jne .failed1
+
+ not al
+ mov [es:xDI], al
+ cmp al, [ds:xSI]
+ mov [es:xDI], ah
+ jne .failed2
+
+ inc xDI
+ inc xSI
+ dec ecx
+ jnz .inner_loop
+
+ clc
+ ret
+
+.failed1:
+ push sCX
+ push sBX
+ push sAX
+ push sSI
+ push sDI
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .s_szNotEqual
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB * 6
+ stc
+ ret
+.s_szNotEqual:
+ db TMPL_MODE_STR, ' - Not equal; EDI=%RX32 ESI=%RX32 EAX=%RX32 EBX=%RX32 ECX=%RX32', 13, 10, 0
+
+.failed2:
+ push sCX
+ push sBX
+ push sAX
+ push sSI
+ push sDI
+%ifdef TMPL_16BIT
+ push cs
+%endif
+ push .s_szModifiyError
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB * 6
+ stc
+ ret
+
+.s_szModifiyError:
+ db TMPL_MODE_STR, ' - Modify error; EDI=%RX32 ESI=%RX32 EAX=%RX32 EBX=%RX32 ECX=%RX32', 13, 10, 0
+ENDPROC TMPL_NM(TestA20_DisabledInner)
+
+
+;;
+; Scans memory calling sDX for each segment.
+;
+BEGINPROC TMPL_NM(TestA20_ScanMemory)
+ push sAX
+ push sBX
+ push sCX
+ push sDX
+ push sSI
+ push sDI
+ pushf
+ cli
+%ifdef TMPL_16BIT
+ push es
+ push ds
+%endif
+
+ ;
+ ; The outer loop walks a segment (64 KB) at a time.
+ ;
+ mov ebx, _1M
+.outer_loop:
+
+ ; Set up the registers.
+%ifdef TMPL_CMN_R86
+ mov ax, 0ffffh
+ mov edi, 00010h
+ mov es, ax
+ mov ax, 00000h
+ mov esi, 00000h
+ mov ds, ax
+ mov ecx, 01000h ; at 101000 there was a VMMDevTesting MMIO page.
+%elifdef TMPL_16BIT
+ ;; @todo need a selector we can modify.
+ jmp .done
+
+%else
+ mov edi, ebx
+ mov esi, ebx
+ and esi, ~_1M
+ mov ecx, _64K
+%endif
+%ifndef TMPL_CMN_R86
+ ; Should we skip this segment or only check parts of it?
+ cmp esi, edi ; affected by A20?
+ je .outer_next
+
+%if BS2_PXX_LAST != 09ffffh
+ %error BS2_PXX_LAST does not have the expected value.
+%endif
+ cmp ebx, BS2_PXX_BASE + _1M ; don't mess with page tables, stacks, MMIO or ROMs.
+ jb .not_low_rom_mmio_region
+ cmp ebx, _1M + _1M
+ jb .outer_next
+.not_low_rom_mmio_region:
+
+ cmp ebx, BS2_ADDR + _1M
+ ja .not_bs2
+ mov ecx, BS2_ADDR ; don't overwrite our own code.
+.not_bs2:
+ cmp ebx, _1M
+ jne .not_VMMDevTestingMMIO
+ mov ecx, 1000h ; don't bother with the MMIO
+.not_VMMDevTestingMMIO:
+%endif ; TMPL_CMN_R86
+
+ ; Invoke the callback.
+ call xDX
+ jc .failure
+
+%ifndef TMPL_CMN_R86
+.outer_next:
+ add ebx, _64K
+ cmp ebx, 32*_1M
+ jbe .outer_loop
+%endif
+
+.done:
+%ifdef TMPL_16BIT
+ pop ds
+ pop es
+%endif
+ popf
+ pop sDI
+ pop sSI
+ pop sDX
+ pop sCX
+ pop sBX
+ pop sAX
+ ret
+
+.failure:
+%if 1
+ cmp ebx, _1M
+ je .contine_at_next_MB
+ cmp ebx, _2M
+ je .contine_at_next_MB
+ cmp ebx, _1M + _2M
+ je .contine_at_next_MB
+ cmp ebx, _4M
+ je .contine_at_next_MB
+%endif
+ jmp .done
+
+.contine_at_next_MB:
+ add ebx, _1M
+ jmp .outer_loop
+ENDPROC TMPL_NM(TestA20_ScanMemory)
+
+
+BEGINPROC TMPL_NM(TestA20_Enabled)
+ push sDX
+ mov xDX, TMPL_NM(TestA20_EnabledInner)
+ call TMPL_NM(TestA20_ScanMemory)
+ pop sDX
+ ret
+ENDPROC TMPL_NM(TestA20_Enabled)
+
+
+;;
+; Checks that the first 64KB at 1MB wraps is the same physical memory as at
+; address 0.
+;
+BEGINPROC TMPL_NM(TestA20_Disabled)
+ push sDX
+ mov xDX, TMPL_NM(TestA20_DisabledInner)
+ call TMPL_NM(TestA20_ScanMemory)
+ pop sDX
+ ret
+ENDPROC TMPL_NM(TestA20_Disabled)
+
+
+BEGINPROC TMPL_NM(TestA20_FlushAll)
+ push sAX
+ wbinvd
+ mov sAX, cr3
+ mov cr3, sAX
+ wbinvd
+ pop sAX
+ ret
+ENDPROC TMPL_NM(TestA20_FlushAll)
+
+
+
+;;
+; Do the A20 tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(TestA20_rm)
+ push eax
+
+ mov ax, .s_szTestName
+ call TestSub_r86
+
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .skip_not_supported
+
+ ;
+ ; Do tests with A20 enabled.
+ ;
+ call Bs2EnableA20_r86
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Enabled)
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call TestSubErrorCount_r86
+ cmp ax, 0
+ jne .done
+
+ ;
+ ; Do tests with A20 disabled.
+ ;
+ call Bs2DisableA20_r86
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Disabled)
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call TestSubErrorCount_r86
+ cmp ax, 0
+ jne .done
+
+%ifndef TMPL_CMN_V86
+ ;
+ ; Change A20 state without leaving and entering the CPU mode.
+ ;
+ call Bs2EnableA20_r86
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ call TMPL_NM(TestA20_Enabled)
+
+ call TMPL_NM_CMN(Bs2DisableA20)
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Disabled)
+
+ call TMPL_NM_CMN(Bs2EnableA20)
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Enabled)
+
+ call TMPL_NM_CMN(Bs2DisableA20)
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Disabled)
+
+ call TMPL_NM_CMN(Bs2EnableA20)
+ call TMPL_NM(TestA20_FlushAll)
+ call TMPL_NM(TestA20_Enabled)
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+%endif ; !TMPL_CMN_V86
+
+.done:
+ call Bs2DisableA20_r86
+.done1:
+ call TestSubDone_r86
+
+ pop eax
+ ret
+
+.skip_not_supported:
+ mov eax, .s_szSkipNotSupported
+ call TestSkipped_r86
+ jmp .done1
+
+.s_szTestName:
+ db TMPL_MODE_STR, 0
+.s_szSkipNotSupported:
+ db TMPL_MODE_STR, ' is not supported by the CPU', 10, 13, 0
+ENDPROC TMPL_NM(TestA20_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1.asm
new file mode 100644
index 00000000..2b1aef60
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-a20-1.asm
@@ -0,0 +1,386 @@
+; $Id: bootsector2-cpu-a20-1.asm $
+;; @file
+; Bootsector that checks the A20 emulation.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;
+; Include and execute the init code.
+;
+%define BS2_WITH_TRAPS
+%define BS2_INIT_RM
+%define BS2_INC_PE16
+%define BS2_INC_PE32
+%define BS2_INC_PP32
+%define BS2_INC_PAE32
+%define BS2_INC_LM64
+%include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+
+ ;
+ ; The actual tests.
+ ;
+ call TestA20_1 ; must come first
+ call TestA20_rm_rm
+ ;call TestA20_rm_pe16
+ call TestA20_rm_pe32
+ call TestA20_rm_pp32
+ ;call TestA20_rm_pp16
+ call TestA20_rm_pae32
+ call TestA20_rm_lm64
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ call Bs2Panic
+
+.s_szTstName:
+ db 'tstA20-1', 0
+.s_szInitialA20Status:
+ db 'Initial A20 state', 0
+ENDPROC main
+
+
+;
+; Do some initial tests.
+;
+BEGINPROC TestA20_1
+ push eax
+ push edx
+ push ecx
+ push ebx
+ push edi
+
+ ;
+ ; Check that the A20 gate is disabled when we come from the BIOS.
+ ;
+ mov ax, .s_szInitialA20Status
+ call TestSub_r86
+
+ call IsA20GateEnabled_rm
+ mov di, ax ; save A20 state in AX for bios test.
+ cmp al, 0
+ je .initial_state_done
+ mov ax, .s_szBadInitialA20Status
+ call TestFailed_r86
+ jmp .initial_state_done
+.s_szInitialA20Status:
+ db 'Initial A20 state', 0
+.s_szBadInitialA20Status:
+ db 'Initial A20 state is enabled, expected disabled', 10, 13, 0
+.initial_state_done:
+ call TestSubDone_r86
+
+ ;
+ ; Disable it via the BIOS interface and check.
+ ;
+ mov ax, .s_szBios
+ call TestSub_r86
+
+ ; query support
+ mov ax, 2403h
+ int 15h
+ jnc .bios_2403_ok
+ movzx edx, ax
+ mov ax, .s_szBios2403Error
+ mov cl, VMMDEV_TESTING_UNIT_NONE
+ call TestValueU32_r86
+ jmp .bios_2403_done
+.bios_2403_ok:
+ movzx edx, al
+ mov ax, .s_szBios2403Mask
+ mov cl, VMMDEV_TESTING_UNIT_NONE
+ call TestValueU32_r86
+.bios_2403_done:
+
+ ; Check what the bios thinks the state is.
+ call BiosIsA20GateEnabled_rm
+ cmp ax, di
+ je .bios_2402_done
+ push di
+ push ax
+ push word ds
+ push word .s_szBios2402Error
+ call TestFailedF_r86
+ add sp, 8
+.bios_2402_done:
+
+ ; Loop to make sure we get all transitions and ends up with A20 disabled.
+ mov cx, 10h
+.bios_loop:
+ ; enable it
+ mov ax, 2401h
+ push cx ; paranoia that seems necessary for at least one AMI bios.
+ int 15h
+ pop cx
+ jnc .bios_continue1
+ mov ax, .s_szBiosFailed2401
+ jmp .bios_failed
+.bios_continue1:
+
+ call IsA20GateEnabled_rm
+ cmp al, 1
+ je .bios_continue2
+ mov ax, .s_szBiosEnableFailed
+ jmp .bios_failed
+.bios_continue2:
+
+ ; disable
+ mov ax, 2400h
+ push cx ; paranoia that seems necessary for at least one AMI bios.
+ int 15h
+ pop cx
+ jnc .bios_continue3
+ mov ax, .s_szBiosFailed2400
+ jmp .bios_failed
+.bios_continue3:
+ call IsA20GateEnabled_rm
+ cmp al, 0
+ je .bios_continue4
+ mov ax, .s_szBiosDisableFailed
+ jmp .bios_failed
+.bios_continue4:
+
+ loop .bios_loop
+ jmp .bios_done
+.s_szBios:
+ db 'INT 15h AH=24 A20 Gate interface', 0
+.s_szBios2403Mask:
+ db 'AX=2403 return (AL)', 0
+.s_szBios2403Error:
+ db 'AX=2403 error (AX)', 10, 13, 0
+.s_szBios2402Error:
+ db '2402h -> AX=%RX16 expected %RX16', 10, 13, 0
+.s_szBiosFailed2400:
+ db '2400h interface failed', 10, 13, 0
+.s_szBiosFailed2401:
+ db '2401h interface failed', 10, 13, 0
+.s_szBiosDisableFailed:
+ db 'BIOS failed to disable A20 (or bad CPU)', 10, 13, 0
+.s_szBiosEnableFailed:
+ db 'BIOS failed to enable A20', 10, 13, 0
+.bios_failed:
+ call TestFailed_r86
+.bios_done:
+ call TestSubDone_r86
+ call Bs2DisableA20ViaPortA_r86
+ call Bs2DisableA20ViaKbd_r86
+
+ ;
+ ; Test the fast A20 gate interface.
+ ;
+ mov ax, .s_szFastA20
+ call TestSub_r86
+
+ mov cx, 10h
+.fast_loop:
+ call Bs2EnableA20ViaPortA_r86
+ call IsA20GateEnabled_rm
+ cmp al, 1
+ mov ax, .s_szFastEnableFailed
+ jne .fast_failed
+
+ call Bs2DisableA20ViaPortA_r86
+ call IsA20GateEnabled_rm
+ cmp al, 0
+ mov ax, .s_szFastDisableFailed
+ jne .fast_failed
+ loop .fast_loop
+
+ jmp .fast_done
+.s_szFastA20:
+ db 'Fast A20 Gate Interface', 0
+.s_szFastDisableFailed:
+ db 'Fast A20 gate disabling failed', 10, 13, 0
+.s_szFastEnableFailed:
+ db 'Fast A20 gate enabling failed', 10, 13, 0
+.fast_failed:
+ call TestFailed_r86
+.fast_done:
+ call TestSubDone_r86
+ call Bs2DisableA20ViaPortA_r86
+ call Bs2DisableA20ViaKbd_r86
+
+ ;
+ ; Test the keyboard interface.
+ ;
+ mov ax, .s_szKeyboardA20
+ call TestSub_r86
+
+ mov cx, 10h
+.kbd_loop:
+ call Bs2EnableA20ViaKbd_r86
+ call IsA20GateEnabled_rm
+ cmp al, 1
+ mov ax, .s_szKbdEnableFailed
+ jne .kbd_failed
+
+ call Bs2DisableA20ViaKbd_r86
+ call IsA20GateEnabled_rm
+ cmp al, 0
+ mov ax, .s_szKbdDisableFailed
+ jne .kbd_failed
+ loop .kbd_loop
+
+ jmp .kbd_done
+.s_szKeyboardA20:
+ db 'Keyboard A20 Gate Interface', 0
+.s_szKbdDisableFailed:
+ db 'Disabling the A20 gate via the keyboard controller failed', 10, 13, 0
+.s_szKbdEnableFailed:
+ db 'Enabling the A20 gate via the keyboard controller failed', 10, 13, 0
+.kbd_failed:
+ call TestFailed_r86
+.kbd_done:
+ call TestSubDone_r86
+ call Bs2DisableA20ViaPortA_r86
+ call Bs2DisableA20ViaKbd_r86
+
+ pop edi
+ pop ebx
+ pop ecx
+ pop edx
+ pop eax
+ ret
+ENDPROC TestA20_1
+
+
+;;
+; Checks if the A20 gate is enabled.
+;
+; This is do by temporarily changing a word at address 0000000h and see if this
+; is reflected at address 0100000h (1 MB). The word written is
+; ~*(word *)0x100000h to make sure it won't accidentally match.
+;
+; @returns ax 1 if enabled, 0 if disabled.
+;
+BEGINPROC IsA20GateEnabled_rm
+ push ds
+ push es
+ push dx
+ pushf
+ cli
+
+.once_again:
+ xor ax, ax
+ mov ds, ax
+ dec ax
+ mov es, ax
+
+ mov ax, [es:0010h] ; 0ffff:0010 => 0100000h (1 MB)
+ mov dx, [ds:0000h] ; 00000:0000 => 0000000h - save it
+ not ax
+ mov [ds:0000h], ax ; 0000000h - write ~[0100000h]
+ cmp [es:0010h], ax ; 0100000h - same as 0000000h if A20 is disabled.
+ mov [ds:0000h], dx ; 0000000h - restore original value
+ setne al
+ movzx ax, al
+
+ popf
+ pop dx
+ pop es
+ pop ds
+ ret
+ENDPROC IsA20GateEnabled_rm
+
+;;
+; Checks if the BIOS thinks the A20 gate is enabled.
+;
+; @returns ax 1 if enabled, 0 if disabled.
+;
+BEGINPROC BiosIsA20GateEnabled_rm
+ push ecx
+ push eax
+
+ mov ax, 2402h
+ int 15h
+ jnc .ok
+ mov al, 080h
+.ok:
+ mov cx, ax
+ pop eax
+ mov ax, cx
+ pop ecx
+ ret
+ENDPROC BiosIsA20GateEnabled_rm
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_RM
+%include "bootsector2-cpu-a20-1-template.mac"
+;%define TMPL_CMN_V86
+;%include "bootsector2-cpu-a20-1-template.mac"
+%define TMPL_PE16
+%include "bootsector2-cpu-a20-1-template.mac"
+%define TMPL_PE32
+%include "bootsector2-cpu-a20-1-template.mac"
+;%define TMPL_PP16
+;%include "bootsector2-cpu-a20-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-a20-1-template.mac"
+;%define TMPL_PAE16
+;%include "bootsector2-cpu-a20-1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-a20-1-template.mac"
+;%define TMPL_LM16
+;%include "bootsector2-cpu-a20-1-template.mac"
+;%define TMPL_LM32
+;%include "bootsector2-cpu-a20-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-a20-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-ac-loop.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-ac-loop.asm
new file mode 100644
index 00000000..6e5c5cb7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-ac-loop.asm
@@ -0,0 +1,125 @@
+; $Id: bootsector2-cpu-ac-loop.asm $
+;; @file
+; Bootsector test for debug exceptions.
+;
+; Recommended (but not necessary):
+; VBoxManage setextradata bs-cpu-xcpt-2 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_PE32
+ %define BS2_WITH_TRAPS
+ %define BS2_WITH_TRAPRECS
+ %define BS2_INC_PE32
+ %define BS2_INC_RM ; for SetCpuModeGlobals_rm
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The main() function.
+;
+BEGINPROC main
+ BITS 32
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_p32
+ call Bs2EnableA20_p32
+ cli ; raw-mode hack
+
+ ;
+ ; Execute the tests
+ ;
+ sub esp, 20h
+
+ ; Get the address of the #AC IDT entry.
+ sidt [esp]
+ mov eax, [esp + 2]
+ add eax, 8 * X86_XCPT_AC
+
+ ; Make it execute in ring-3.
+ mov word [eax + 2], BS2_SEL_R3_CS32 ; u16Sel
+ or byte [eax + 5], 3 << 5 ; u2Dpl = 3
+
+ ; Enable AC.
+ mov eax, cr0
+ or eax, X86_CR0_AM
+ mov cr0, eax
+
+ ; Switch to ring-3
+ call Bs2ToRing3_p32
+
+ ; Enable AC.
+ pushfd
+ or dword [esp], X86_EFL_AC
+ popfd
+
+ ;; Test it. - won't work as the handle touches CR2, which traps in ring-3.
+ ;BS2_TRAP_INSTR X86_XCPT_AC, 0, mov dword [esp + 3], 0
+
+ ; Misalign the stack and use it.
+ or esp, 3
+ push esp ; this will loop forever on real intel hardware.
+ and esp, ~3h
+
+ add esp, 20h
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_p32
+ ret
+
+.s_szTstName:
+ db 'tstCpuAcLoop', 0
+ENDPROC main
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1-template.mac
new file mode 100644
index 00000000..1b637236
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1-template.mac
@@ -0,0 +1,84 @@
+; $Id: bootsector2-cpu-basic-1-template.mac $
+;; @file
+; bootsector2 basic #1 - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(TestBasic1_rm)
+ push eax
+
+ mov ax, .s_szTestName
+ call TestSub_r86
+
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .skip_not_supported
+
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ ; Later, currently just getting thru the mode switch is good enough.
+ nop
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done1:
+ call TestSubDone_r86
+
+ pop eax
+ ret
+
+.skip_not_supported:
+ mov eax, .s_szSkipNotSupported
+ call TestSkipped_r86
+ jmp .done1
+
+.s_szTestName:
+ db TMPL_MODE_STR, 0
+.s_szSkipNotSupported:
+ db TMPL_MODE_STR, ' is not supported by the CPU', 10, 13, 0
+ENDPROC TMPL_NM(TestBasic1_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1.asm
new file mode 100644
index 00000000..4f4bc7e3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-basic-1.asm
@@ -0,0 +1,129 @@
+; $Id: bootsector2-cpu-basic-1.asm $
+;; @file
+; Bootsector that checks the basic CPU operation.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;
+; Include and execute the init code.
+;
+%define BS2_WITH_TRAPS
+%define BS2_INIT_RM
+%define BS2_INC_PE16
+%define BS2_INC_PE32
+%define BS2_INC_PP16
+%define BS2_INC_PP32
+%define BS2_INC_PAE32
+%define BS2_INC_PAE16
+%define BS2_INC_LM16
+%define BS2_INC_LM32
+%define BS2_INC_LM64
+%include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+
+ ;
+ ; The actual tests.
+ ;
+ call TestBasic1_rm_rm
+ call TestBasic1_rm_pe16
+ call TestBasic1_rm_pe32
+ call TestBasic1_rm_pp32
+ call TestBasic1_rm_pp16
+ call TestBasic1_rm_pae16
+ call TestBasic1_rm_pae32
+ call TestBasic1_rm_lm64
+ call TestBasic1_rm_lm32
+ call TestBasic1_rm_lm16
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ call Bs2Panic
+
+.s_szTstName:
+ db 'tstBasic1-1', 0
+.s_szInitialBasic1Status:
+ db 'Initial Basic1 state', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_RM
+%include "bootsector2-cpu-basic-1-template.mac"
+;%define TMPL_CMN_V86
+;%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PE16
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PE32
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PP16
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PAE16
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_LM16
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_LM32
+%include "bootsector2-cpu-basic-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-basic-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-db-loop.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-db-loop.asm
new file mode 100644
index 00000000..351975ff
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-db-loop.asm
@@ -0,0 +1,161 @@
+; $Id: bootsector2-cpu-db-loop.asm $
+;; @file
+; Bootsector test for debug exception loop.
+;
+; Recommended (but not necessary):
+; VBoxManage setextradata bs-cpu-db-loop VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_PE32
+ %define BS2_WITH_TRAPS
+ %define BS2_WITH_XCPT_DB_CLEARING_TF
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_RM ; for SetCpuModeGlobals_rm
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The main() function.
+;
+BEGINPROC main
+ BITS 32
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_p32
+ call Bs2EnableA20_p32
+ cli ; raw-mode hack
+ sub esp, 20h
+
+ call Bs2Thunk_p32_p16
+ BITS 16
+
+ ;
+ ; We require a stack that can wrap around here. The default stack
+ ; doesn't allow us to do this, so we'll configure a custom one
+ ; where the page tables usually are.
+ ;
+ mov eax, [bs2Gdt + BS2_SEL_SS16]
+ mov ebx, [bs2Gdt + BS2_SEL_SS16 + 4]
+
+ and eax, 0xffff
+ or eax, (BS2_PXX_BASE & 0xffff) << 16
+ and ebx, 0x00ffff00
+ or ebx, BS2_PXX_BASE & 0xff000000
+ or ebx, (BS2_PXX_BASE & 0x00ff0000) >> 16
+ mov [bs2GdtSpare0], eax
+ mov [bs2GdtSpare0 + 4], ebx
+
+
+ ;
+ ; Switch the stack.
+ ;
+ mov ax, ss
+ mov es, ax ; saved ss
+ mov edi, esp ; saved esp
+
+ mov ax, BS2_SEL_SPARE0
+ mov ss, ax
+ mov esp, 0xfff0
+
+
+ ;
+ ; Arm the breakpoint.
+ ;
+ and dword [esp + 2], 0
+ sidt [esp]
+ mov eax, [esp + 2]
+ add eax, 8
+ mov dr0, eax
+ mov eax, X86_DR7_RA1_MASK | X86_DR7_GE \
+ | X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_RW) | X86_DR7_LEN(0, X86_DR7_LEN_DWORD)
+ mov dr7, eax
+
+ ;
+ ; Trigger a single step exception.
+ ;
+ pushf
+ or word [xSP], X86_EFL_TF
+ popf
+ xchg eax, ebx
+ xchg edx, ecx ; should get a #DB here.
+ xchg eax, ebx
+ xchg edx, ecx
+
+ ;
+ ; If we get thus far, we've failed.
+ ;
+ mov ax, es ; restore ss
+ mov ss, ax
+ mov esp, edi ; restore esp
+
+ call Bs2Thunk_p16_p32
+ BITS 32
+
+ mov eax, .s_szFailed
+ call TestFailed_p32
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_p32
+ add esp, 20h
+ ret
+
+.s_szTstName:
+ db 'tstCpuDbLoop', 0
+.s_szFailed:
+ db 'no #DB loop detected',0
+ENDPROC main
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1-template.mac
new file mode 100644
index 00000000..c4872c7d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1-template.mac
@@ -0,0 +1,352 @@
+; $Id: bootsector2-cpu-hidden-regs-1-template.mac $
+;; @file
+; bootsector2 hidden CPU registers - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+;;
+; Helper for reporting several register values at in a sequence.
+;
+BEGINPROC TMPL_NM(TestValueRegSZZ)
+ push sAX
+ push sBX
+
+ mov xBX, xAX
+.next:
+ mov xAX, xBX
+ call TMPL_NM_CMN(TestValueReg)
+.inner_next:
+ inc xBX
+ cmp byte [xBX], 0
+ jne .inner_next
+
+ inc xBX
+ cmp byte [xBX], 0
+ je .done
+ jmp .next
+
+.done
+ pop sBX
+ pop sAX
+ ret
+ENDPROC TMPL_NM(TestValueRegSZZ)
+
+;;
+; Tests various LDTR values
+;
+BEGINPROC TMPL_NM(doLdtrTests)
+ push sAX
+
+ ; The inital LDT.
+ mov sAX, .szLdtrInitial
+ call TMPL_NM(TestValueRegSZZ)
+
+ ; Load our LDT
+ mov eax, BS2_SEL_LDT
+ lldt ax
+ mov sAX, .szLdtrValid
+ call TMPL_NM(TestValueRegSZZ)
+
+ ; NULL LDTR.
+ xor eax, eax
+ lldt ax
+ mov sAX, .szLdtr0
+ call TMPL_NM(TestValueRegSZZ)
+
+ ; NULL(1) LDTR.
+ mov eax, 1
+ lldt ax
+ mov sAX, .szLdtr1
+ call TMPL_NM(TestValueRegSZZ)
+
+ ; NULL(2) LDTR.
+ mov eax, 2
+ lldt ax
+ mov sAX, .szLdtr2
+ call TMPL_NM(TestValueRegSZZ)
+
+ ; NULL(3) LDTR.
+ mov eax, 3
+ lldt ax
+ mov sAX, .szLdtr3
+ call TMPL_NM(TestValueRegSZZ)
+
+.done
+ pop sAX
+ ret
+
+.szLdtrInitial:
+ db 'LDTR(Initial) sel:ldtr', 0
+ db 'LDTR(Initial) base:ldtr_base', 0
+ db 'LDTR(Initial) limit:ldtr_lim', 0
+ db 'LDTR(Initial) attr:ldtr_attr', 0
+ db 0
+.szLdtrValid:
+ db 'LDTR(Valid) sel:ldtr', 0
+ db 'LDTR(Valid) base:ldtr_base', 0
+ db 'LDTR(Valid) limit:ldtr_lim', 0
+ db 'LDTR(Valid) attr:ldtr_attr', 0
+ db 0
+.szLdtr0:
+ db 'LDTR(0) sel:ldtr', 0
+ db 'LDTR(0) base:ldtr_base', 0
+ db 'LDTR(0) limit:ldtr_lim', 0
+ db 'LDTR(0) attr:ldtr_attr', 0
+ db 0
+.szLdtr1:
+ db 'LDTR(1) sel:ldtr', 0
+ db 'LDTR(1) base:ldtr_base', 0
+ db 'LDTR(1) limit:ldtr_lim', 0
+ db 'LDTR(1) attr:ldtr_attr', 0
+ db 0
+.szLdtr2:
+ db 'LDTR(2) sel:ldtr', 0
+ db 'LDTR(2) base:ldtr_base', 0
+ db 'LDTR(2) limit:ldtr_lim', 0
+ db 'LDTR(2) attr:ldtr_attr', 0
+ db 0
+.szLdtr3:
+ db 'LDTR(3) sel:ldtr', 0
+ db 'LDTR(3) base:ldtr_base', 0
+ db 'LDTR(3) limit:ldtr_lim', 0
+ db 'LDTR(3) attr:ldtr_attr', 0
+ db 0
+ENDPROC TMPL_NM(doLdtrTests)
+
+
+;;
+; Tests various LDTR values
+;
+BEGINPROC TMPL_NM(doTrTests)
+ push sAX
+
+ ; Initial TR values.
+ mov sAX, .szTrInitial
+ call TMPL_NM(TestValueRegSZZ)
+ jmp .next1
+.szTrInitial:
+ db 'TR(Initial) sel:tr', 0
+ db 'TR(Initial) base:tr_base', 0
+ db 'TR(Initial) limit:tr_lim', 0
+ db 'TR(Initial) attr:tr_attr', 0
+ db 0
+.next1:
+
+ ; Our TR.
+%ifdef TMPL_CMN_LM
+ mov ax, BS2_SEL_TSS64
+ ltr ax
+ mov sAX, .szTrTss64
+ call TMPL_NM(TestValueRegSZZ)
+ jmp .next2
+.szTrTss64:
+ db 'TR(64) sel:tr', 0
+ db 'TR(64) base:tr_base', 0
+ db 'TR(64) limit:tr_lim', 0
+ db 'TR(64) attr:tr_attr', 0
+ db 0
+
+%elifdef TMPL_PP32
+ mov ax, BS2_SEL_TSS32
+ ltr ax
+ mov sAX, .szTrTss32
+ call TMPL_NM(TestValueRegSZZ)
+ jmp .next2
+.szTrTss32:
+ db 'TR(32) sel:tr', 0
+ db 'TR(32) base:tr_base', 0
+ db 'TR(32) limit:tr_lim', 0
+ db 'TR(32) attr:tr_attr', 0
+ db 0
+;%elifdef TMPL_PP16
+; mov ax, BS2_SEL_TSS16
+; mov sAX, .szTrTss16
+; call TMPL_NM(TestValueRegSZZ)
+%endif
+.next2:
+
+ ; Note! Loading 0 into TR is not possible, unlike with LDTR.
+
+.done
+ pop sAX
+ ret
+ENDPROC TMPL_NM(doTrTests)
+
+
+;;
+; Test loading of NULL selectors.
+;
+BEGINPROC TMPL_NM(doNullSelTests)
+ push sAX
+ push xBX
+ push gs
+
+ mov ax, ss
+ mov gs, ax
+ mov sAX, .szGsSs
+ call TMPL_NM(TestValueRegSZZ)
+
+ xor eax, eax
+ mov gs, ax
+ mov sAX, .szGs0
+ call TMPL_NM(TestValueRegSZZ)
+
+ mov ax, 3
+ mov gs, ax
+ mov sAX, .szGs3
+ call TMPL_NM(TestValueRegSZZ)
+
+%ifdef TMPL_64BIT
+ pushf
+ cli
+ mov bx, ss
+ mov ax, 0
+ mov ss, ax
+ mov sAX, .szSs0
+ call TMPL_NM(TestValueRegSZZ)
+ mov ss, bx
+ popf
+
+ call TMPL_NM_CMN(Bs2ToRing2)
+ mov bx, ss
+ mov ax, 2
+ mov ss, ax
+ mov sAX, .szSs1Ring2
+ call TMPL_NM(TestValueRegSZZ)
+ mov ss, bx
+
+ test byte [g_fCpuAmd], 1
+ jz .not_amd
+ mov ax, 3
+ mov ss, ax
+ mov sAX, .szSs3Ring2
+ call TMPL_NM(TestValueRegSZZ)
+
+.not_amd:
+ call TMPL_NM_CMN(Bs2ToRing0)
+
+%endif
+
+ pop gs
+ pop xBX
+ pop sAX
+ ret
+
+.szGsSs:
+ db 'GS(ss) sel:gs', 0
+ db 'GS(ss) base:gs_base', 0
+ db 'GS(ss) limit:gs_lim', 0
+ db 'GS(ss) attr:gs_attr', 0
+ db 0
+.szGs0:
+ db 'GS(0) sel:gs', 0
+ db 'GS(0) base:gs_base', 0
+ db 'GS(0) limit:gs_lim', 0
+ db 'GS(0) attr:gs_attr', 0
+ db 0
+.szGs3:
+ db 'GS(3) sel:gs', 0
+ db 'GS(3) base:gs_base', 0
+ db 'GS(3) limit:gs_lim', 0
+ db 'GS(3) attr:gs_attr', 0
+ db 0
+%ifdef TMPL_64BIT
+.szSs0:
+ db 'SS(0) sel:ss', 0
+ db 'SS(0) base:ss_base', 0
+ db 'SS(0) limit:ss_lim', 0
+ db 'SS(0) attr:ss_attr', 0
+ db 0
+.szSs1Ring2
+ db 'ring-2 SS(2) sel:ss', 0
+ db 'ring-2 SS(2) base:ss_base', 0
+ db 'ring-2 SS(2) limit:ss_lim', 0
+ db 'ring-2 SS(2) attr:ss_attr', 0
+ db 0
+.szSs3Ring2
+ db 'ring-2 SS(3) sel:ss', 0
+ db 'ring-2 SS(3) base:ss_base', 0
+ db 'ring-2 SS(3) limit:ss_lim', 0
+ db 'ring-2 SS(3) attr:ss_attr', 0
+ db 0
+%endif
+ENDPROC TMPL_NM(doNullSelTests)
+
+
+BEGINPROC TMPL_NM(doTestsWorker)
+ push sAX
+
+ mov xAX, .s_szSubTest
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(doLdtrTests)
+ call TMPL_NM(doTrTests)
+ call TMPL_NM(doNullSelTests)
+
+.done
+ pop sAX
+ ret
+
+.s_szSubTest:
+ db TMPL_MODE_STR, 0
+ENDPROC TMPL_NM(doTestsWorker)
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(doTests_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ call TMPL_NM(doTestsWorker)
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+ENDPROC TMPL_NM(doTests_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1.asm
new file mode 100644
index 00000000..a6ecc634
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-hidden-regs-1.asm
@@ -0,0 +1,285 @@
+; $Id: bootsector2-cpu-hidden-regs-1.asm $
+;; @file
+; Bootsector that shows/tests the content of hidden CPU registers.
+;
+; Requires VMMDevTesting. Enable it via VBoxManage:
+; VBoxManage setextradata bs-cpu-hidden-regs-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP32
+ %define BS2_INC_LM64
+ %define BS2_WITH_TRAPS
+ %define BS2_WITH_MANUAL_LTR
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+ call Bs2PanicIfVMMDevTestingIsMissing_r86
+
+ call reportPostBiosValues
+ call rmTests
+ call doTests_rm_pe32
+ call doTests_rm_pp32
+ call doTests_rm_lm64
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ call Bs2Panic
+
+.s_szTstName:
+ db 'tstCpuHidRegs', 0
+ENDPROC main
+
+
+;
+; Reports the values of interesting hidden registers as we start the test, i.e.
+; right after the BIOS completed.
+;
+BEGINPROC reportPostBiosValues
+ push ax
+ push bx
+ mov ax, .s_szTstInitial
+ call TestSub_r86
+
+ mov ax, .s_szzStart
+ call TestValueRegSZZ_rm
+
+.done
+ pop bx
+ pop ax
+ ret
+
+.s_szzStart:
+ db 'BIOS - ldtr:ldtr', 0;
+ db 'BIOS - ldtr_base:ldtr_base', 0;
+ db 'BIOS - ldtr_limit:ldtr_lim', 0;
+ db 'BIOS - ldtr_attr:ldtr_attr', 0;
+ db 'BIOS - tr:tr', 0;
+ db 'BIOS - tr_base:tr_base', 0;
+ db 'BIOS - tr_limit:tr_lim', 0;
+ db 'BIOS - tr_attr:tr_attr', 0;
+ db 'BIOS - cs:cs', 0;
+ db 'BIOS - cs_base:cs_base', 0;
+ db 'BIOS - cs_limit:cs_lim', 0;
+ db 'BIOS - cs_attr:cs_attr', 0;
+ db 'BIOS - ss:ss', 0;
+ db 'BIOS - ss_base:ss_base', 0;
+ db 'BIOS - ss_limit:ss_lim', 0;
+ db 'BIOS - ss_attr:ss_attr', 0;
+ db 'BIOS - ds:ds', 0;
+ db 'BIOS - ds_base:ds_base', 0;
+ db 'BIOS - ds_limit:ds_lim', 0;
+ db 'BIOS - ds_attr:ds_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szTstInitial:
+ db 'Post BIOS Values', 0
+ENDPROC reportPostBiosValues
+
+
+;
+; Reports the values of interesting hidden registers as we start the test, i.e.
+; right after the BIOS completed.
+;
+BEGINPROC rmTests
+ push eax
+ push ebx
+ pushfd
+ cli
+
+ mov ax, .s_szTstRM
+ call TestSub_r86
+
+ ; Check if CS changes when leaving protected mode.
+ mov ax, .s_szzRMPre
+ call TestValueRegSZZ_rm
+ mov byte [cs:.s_dwDummy], 1
+ call Bs2EnterMode_rm_pe32
+BITS 32
+ mov eax, .s_szzProt32
+ call TestValueRegSZZ_pe32
+ ; mov word [cs:.s_dwDummy], 2 - this shall GP(CS).
+ call Bs2ExitMode_pe32
+BITS 16
+ mov ax, .s_szzRMPost
+ call TestValueRegSZZ_rm
+ mov dword [cs:.s_dwDummy], 3
+
+ ;
+ ; What happens if we make CS32 execute-only and return to real-mode.
+ ;
+ mov byte [cs:.s_dwDummy], 1
+ call Bs2EnterMode_rm_pe16
+ jmp BS2_SEL_CS16_EO:.loaded_cs16_eo
+.loaded_cs16_eo:
+ mov eax, .s_szzProtEO
+ call TestValueRegSZZ_pe16
+ ; mov ax, word [cs:.s_dwDummy] - this shall GP(CS).
+ ; mov word [cs:.s_dwDummy], 2 - this shall GP(CS).
+
+ ; Leave real-mode ourselves.
+ mov eax, cr0
+ and eax, ~X86_CR0_PE
+ mov cr0, eax
+
+ ; All but cs gets reloaded.
+ xor ax, ax
+ mov ss, ax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; Display CS and do a test.
+ mov ax, .s_szzRMEO
+ call TestValueRegSZZ_rm
+
+ mov ax, [cs:.s_dwDummy] ; works on intel
+ mov dword [cs:.s_dwDummy], 3 ; ditto
+
+ jmp far 0000:.load_rm_cs
+.load_rm_cs:
+ ; Display CS to check that it remained unchanged.
+ mov ax, .s_szzRMEO2
+ call TestValueRegSZZ_rm
+
+ ; Cleanup everything properly.
+ call Bs2EnterMode_rm_pe32
+BITS 32
+ call Bs2ExitMode_pe32
+BITS 16
+
+ popfd
+ pop ebx
+ pop eax
+ ret
+
+.s_dwDummy:
+ dd 0
+.s_szzRMPre:
+ db 'RM Pre - cs:cs', 0;
+ db 'RM Pre - cs_base:cs_base', 0;
+ db 'RM Pre - cs_limit:cs_lim', 0;
+ db 'RM Pre - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szzProt32:
+ db 'Prot32 - cs:cs', 0;
+ db 'Prot32 - cs_base:cs_base', 0;
+ db 'Prot32 - cs_limit:cs_lim', 0;
+ db 'Prot32 - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szzRMPost:
+ db 'RM Post - cs:cs', 0;
+ db 'RM Post - cs_base:cs_base', 0;
+ db 'RM Post - cs_limit:cs_lim', 0;
+ db 'RM Post - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szzProtEO:
+ db 'Prot 16 EO,L-1,NA - cs:cs', 0;
+ db 'Prot 16 EO,L-1,NA - cs_base:cs_base', 0;
+ db 'Prot 16 EO,L-1,NA - cs_limit:cs_lim', 0;
+ db 'Prot 16 EO,L-1,NA - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szzRMEO:
+ db 'RM Post EO,L-1,NA - cs:cs', 0;
+ db 'RM Post EO,L-1,NA - cs_base:cs_base', 0;
+ db 'RM Post EO,L-1,NA - cs_limit:cs_lim', 0;
+ db 'RM Post EO,L-1,NA - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szzRMEO2:
+ db 'RM CS(0) EO,L-1 - cs:cs', 0;
+ db 'RM CS(0) EO,L-1 - cs_base:cs_base', 0;
+ db 'RM CS(0) EO,L-1 - cs_limit:cs_lim', 0;
+ db 'RM CS(0) EO,L-1 - cs_attr:cs_attr', 0;
+ db 0,0,0,0 ; terminator
+.s_szTstRM:
+ db 'Real Mode Test', 0
+ENDPROC rmTests
+
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_RM
+%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_CMN_V86
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+%define TMPL_PE16
+%include "bootsector2-cpu-hidden-regs-1-template.mac"
+%define TMPL_PE32
+%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_PP16
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_PAE16
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_PAE32
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_LM16
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+;%define TMPL_LM32
+;%include "bootsector2-cpu-hidden-regs-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-hidden-regs-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1-template.mac
new file mode 100644
index 00000000..228651be
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1-template.mac
@@ -0,0 +1,567 @@
+; $Id: bootsector2-cpu-instr-1-template.mac $
+;; @file
+; Bootsector test for misc instruction - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+
+;;
+; Memory fence instructions (SSE2).
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestMemFences)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ sub xSP, 80h ; iret stack frame space.
+ mov xSI, xSP ; Save the stack register.
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+ ;
+ ; SSE2 supported?
+ ;
+ mov eax, 1
+ xor ecx, ecx
+ cpuid
+ test edx, X86_CPUID_FEATURE_EDX_SSE2
+ jz .skip
+
+ ;
+ ; Check that the standard instruction encodings work.
+ ;
+ mov xBX, [xSP + 10h]
+ mov [xSP], xAX
+ mfence
+ mov [xSP], xCX
+ mov xBX, [xSP + 08h]
+ sfence
+ mov [xSP], xDX
+ mov xBX, [xSP]
+ lfence
+ mov bx, [xSP + 04h]
+
+
+ ;
+ ; The instruction encodings in the intel manual may open the RM as well
+ ; as prefixes open to interpretation. AMD sets RM=0 in their docs.
+ ;
+ ; lfence = 0f,ea,e8
+ ; mfence = 0f,ea,f0
+ ; sfence = 0f,ea,f8
+ ; (RM is the lower 3 bits of the last byte.)
+
+%assign MY_RM 0xe8
+%rep 18h
+ db 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_CS, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_DS, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_ES, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_FS, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_GS, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_SS, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_SIZE_ADDR, 0fh, 0aeh, MY_RM
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_SIZE_OP, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_LOCK, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_REPNZ, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_REPZ, 0fh, 0aeh, MY_RM ; (used in group)
+%ifdef TMPL_64BIT
+ %assign MY_REX 0x40
+ %rep 10h
+ ; Rex prefixes doesn't change anything.
+ db MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_CS, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_DS, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_ES, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_FS, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_GS, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_SS, MY_REX, 0fh, 0aeh, MY_RM
+ db X86_OP_PRF_SIZE_ADDR, MY_REX, 0fh, 0aeh, MY_RM
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_SIZE_OP, MY_REX, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_LOCK, MY_REX, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_REPNZ, MY_REX, 0fh, 0aeh, MY_RM ; (used in group)
+ BS2_TRAP_INSTR X86_XCPT_UD, 0, db X86_OP_PRF_REPZ, MY_REX, 0fh, 0aeh, MY_RM ; (used in group)
+ %assign MY_REX (MY_REX + 1)
+ %endrep
+%endif
+ %assign MY_RM (MY_RM + 1)
+%endrep
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+.done:
+ mov xSP, xSI
+ add xSP, 80h
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.skip:
+ mov xAX, .s_szSse2Missing
+ call TMPL_NM_CMN(TestSubDone)
+ jmp .done
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', mfence et al.', 0
+.s_szSse2Missing:
+ db 'SSE2 is missing', 0
+ENDPROC TMPL_NM(TestMemFences)
+
+
+;;
+; Floating-point to integer conversion (SSE/SSE2).
+; Neither Intel nor AMD explicitly document what happens for the 32-bit forms
+; of CVTxx2SI in 64-bit mode with regard to the high dword of a 64-bit
+; destination register.
+;
+; @uses XMM0, and BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestCvtSize)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ sub xSP, 80h ; iret stack frame space.
+ mov xSI, xSP ; Save the stack register.
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+ ;
+ ; SSE2 supported?
+ ;
+ mov eax, 1
+ xor ecx, ecx
+ cpuid
+ test edx, X86_CPUID_FEATURE_EDX_SSE2
+ jz .skip
+
+%ifdef TMPL_64BIT
+
+ ;
+ ; Have to enable OSFXSR for SSE instructions to work.
+ ;
+ mov rcx,cr4
+ mov rsi,rcx
+ or rcx,200h
+ mov cr4,rcx
+
+ ;
+ ; Load 32-bit float -2.75 into XMM0
+ ;
+ mov eax, 0C0300000h
+ movd xmm0, eax
+ mov rbx, -1 ; make sure high dword is not zero
+ cvtss2si ebx, xmm0 ; result is -3
+ mov eax, -3 ; high dword of rax zeroed
+ TEST_ASSERT_SIMPLE rbx, rax, jz, "CVTSS2SI EBX"
+
+ mov eax, 0C0300000h
+ movd xmm0, eax
+ mov rbx, -1
+ cvttss2si ebx, xmm0 ; result is -2
+ mov eax, -2
+ TEST_ASSERT_SIMPLE rbx, rax, jz, "CVTTSS2SI EBX"
+
+ ;
+ ; Load 64-bit double -2.75 into XMM0
+ ;
+ mov rax, 0C006000000000000h
+ movd xmm0, rax
+ mov rbx, -1
+ cvtsd2si ebx, xmm0
+ mov eax, -3
+ TEST_ASSERT_SIMPLE rbx, rax, jz, "CVTSD2SI EBX"
+
+ mov rax, 0C006000000000000h
+ mov rbx, -1
+ movd xmm0, rax
+ cvttsd2si ebx, xmm0
+ mov eax,-2
+ TEST_ASSERT_SIMPLE rbx, rax, jz, "CVTTSD2SI EBX"
+
+ ;
+ ; Restore prior CR4 value
+ ;
+ mov cr4,rsi
+%endif
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+.done:
+ mov xSP, xSI
+ add xSP, 80h
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.skip:
+ mov xAX, .s_szSse2Missing
+ call TMPL_NM_CMN(TestSubDone)
+ jmp .done
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', cvtss2si et al.', 0
+.s_szSse2Missing:
+ db 'SSE2 is missing', 0
+ENDPROC TMPL_NM(TestCvtSize)
+
+
+;;
+; Test what CMPXCHG with 32-bit operand size does to 64-bit registers,
+; as this is not particularly well documented by either Intel or AMD.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestCmpxchg32)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ sub xSP, 80h ; iret stack frame space.
+ mov xSI, xSP ; Save the stack register.
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+%ifdef TMPL_64BIT
+
+ ;
+ ; CMPXCHG reg, reg - values not equal, eax written
+ ;
+ mov rax, -1 ; Load registers with 64-bit values
+ mov rbx, -2
+ mov rcx, -3
+ cmpxchg ebx, ecx ; Not equal, writes ebx to eax
+ mov edx, -2 ; Clears high dword
+ TEST_ASSERT_SIMPLE rax, rdx, jz, "CMPXCHG reg, unequal, rax set"
+ mov rdx, -2 ; All ones still in high dword
+ TEST_ASSERT_SIMPLE rbx, rdx, jz, "CMPXCHG reg, unequal, rbx not set"
+
+ ;
+ ; CMPXCHG reg, reg - values equal, first operand written
+ ;
+ mov rax, -4 ; Load registers with 64-bit values
+ mov rbx, -4
+ mov rcx, -5
+ cmpxchg ebx, ecx ; Equal, writes ecx to ebx
+ mov edx, -5 ; Clears high dword
+ TEST_ASSERT_SIMPLE rbx, rdx, jz, "CMPXCHG reg, equal, rbx set"
+ mov rdx, -4 ; All ones still in high dword
+ TEST_ASSERT_SIMPLE rax, rdx, jz, "CMPXCHG reg, equal, rax not set"
+
+ ;
+ ; CMPXCHG mem, reg - values not equal, eax written
+ ;
+ mov rax, -1 ; Load registers with 64-bit values
+ mov rbx, -2
+ push rbx
+ mov rcx, -3
+ cmpxchg [rsp], ecx ; Not equal, writes eax
+ mov edx, -2 ; Clears high dword
+ TEST_ASSERT_SIMPLE rax, rdx, jz, "CMPXCHG mem, unequal, rax set"
+ pop rbx
+
+ ;
+ ; CMPXCHG mem, reg - values equal, first operand written
+ ;
+ mov rax, -4 ; Load registers with 64-bit values
+ mov rbx, -4
+ push rbx
+ mov rcx, -5
+ cmpxchg [rsp], ecx ; Equal, writes ecx to memory
+ mov rdx, -4 ; All ones in high dword
+ TEST_ASSERT_SIMPLE rax, rdx, jz, "CMPXCHG mem, equal, rax not set"
+ pop rbx
+
+ ;
+ ; CMPXCHG8B mem, reg - values equal, memory written
+ ; compares edx:eax with mem64
+ ;
+ mov rdx, -1 ; Load registers with 64-bit values
+ mov rax, -4
+ mov rcx, -1
+ mov rbx, -5
+ mov rsi, -4
+ push rsi
+ cmpxchg8b [rsp] ; Equal, writes ecx:ebx to memory
+ mov rsi, -4 ; All ones in high dword
+ TEST_ASSERT_SIMPLE rax, rsi, jz, "CMPXCHG8B mem, equal, rax not set"
+ mov rsi, -1 ; All ones in high dword
+ TEST_ASSERT_SIMPLE rdx, rsi, jz, "CMPXCHG8B mem, equal, rdx not set"
+ pop rsi
+
+ ;
+ ; CMPXCHG8B mem, reg - values unequal, edx:eax written
+ ; compares edx:eax with mem64
+ ;
+ mov rdx, -1 ; Load registers with 64-bit values
+ mov rax, -2
+ mov rcx, -1
+ mov rbx, -4
+ mov rsi, -3
+ push rsi
+ cmpxchg8b [rsp] ; Not equal, writes memory to edx:eax
+ mov esi, -3 ; Clears high dword
+ TEST_ASSERT_SIMPLE rax, rsi, jz, "CMPXCHG8B mem, unequal, rax set"
+ mov esi, -1 ; Clears high dword
+ TEST_ASSERT_SIMPLE rdx, rsi, jz, "CMPXCHG8B mem, unequal, rdx set"
+ pop rsi
+
+%endif
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+.done:
+ mov xSP, xSI
+ add xSP, 80h
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', 32-bit CMPXCHG in 64-bit mode', 0
+ENDPROC TMPL_NM(TestCmpxchg32)
+
+
+;;
+; Proving intel manual wrong about using REX.X for BSWAP R8-R15 on 64-bit.
+; Checking the 'undefined' 16-bit bswap behavior.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestBSwap)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ sub xSP, 80h ; iret stack frame space.
+ mov xSI, xSP ; Save the stack register.
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+ ;
+ ; Assert sanity.
+ ;
+ mov eax, 11223344h
+ bswap eax
+ TEST_ASSERT_SIMPLE eax, 44332211h, jz, "32-bit BSWAP EAX"
+
+ ;
+ ; Buggy manual (325383-041US, December 2011).
+ ;
+%ifdef TMPL_64BIT
+ push r8
+
+ mov r8d, 55667788h
+ mov eax, 55667788h
+ db X86_OP_REX_X
+ bswap eax ; does it access r8 or eax?
+ TEST_ASSERT_SIMPLE eax, 88776655h, jz, "REX.X BSWAP EAX - Wrong EAX."
+ TEST_ASSERT_SIMPLE r8, 55667788h, jz, "REX.X BSWAP EAX - Wrong R8."
+
+ mov r8d, 55667788h
+ mov eax, 55667788h
+ db X86_OP_REX_R
+ bswap eax ; does it access r8 or eax?
+ TEST_ASSERT_SIMPLE eax, 88776655h, jz, "REX.R BSWAP EAX - Wrong EAX."
+ TEST_ASSERT_SIMPLE r8, 55667788h, jz, "REX.R BSWAP EAX - Wrong R8."
+
+ mov r8d, 55667788h
+ mov eax, 55667788h
+ db X86_OP_REX_B
+ bswap eax ; does it access r8 or eax?
+ TEST_ASSERT_SIMPLE rax, 55667788h, jz, "REX.B BSWAP R8D - Wrong RAX."
+ TEST_ASSERT_SIMPLE r8d, 88776655h, jz, "REX.B BSWAP R8D - Wrong R8D."
+
+ pop r8
+%endif
+
+ ;
+ ; 'Undefined' 16-bit behavior.
+ ;
+ ; Zeroing of the lower 16-bits has been observed on:
+ ; - Intel(R) Core(TM) i7-3960X CPU @ 3.30GHz
+ ;
+%ifndef TestBSwap16_defined
+ %define TestBSwap16_defined
+ %macro TestBSwap16 3,
+ mov %3, %2 ; save the primary register.
+ %ifdef TMPL_64BIT
+ mov %2, 0ffffffff98765432h ; Set the upper bit as well.
+ %else
+ mov %2, 98765432h
+ %endif
+ %ifndef TMPL_16BIT
+ db X86_OP_PRF_SIZE_OP
+ %endif
+ bswap %1
+ xchg %2, %3 ; Restore and save the result (xSP).
+ TEST_ASSERT_SIMPLE %3, 98760000h, jz, "Unexpected 16-bit BSWAP error."
+ %endmacro
+%endif
+
+ TestBSwap16 eax, sAX, sSI
+ TestBSwap16 ebx, sBX, sSI
+ TestBSwap16 ecx, sCX, sSI
+ TestBSwap16 edx, sDX, sSI
+ TestBSwap16 esp, sSP, sSI
+ TestBSwap16 ebp, sBP, sSI
+ TestBSwap16 edi, sDI, sSI
+ TestBSwap16 esi, sSI, sDI
+%ifdef TMPL_64BIT
+ TestBSwap16 r8d, r8, rax
+ TestBSwap16 r9d, r9, rax
+ TestBSwap16 r10d, r10, rax
+ TestBSwap16 r11d, r11, rax
+ TestBSwap16 r12d, r12, rax
+ TestBSwap16 r13d, r13, rax
+ TestBSwap16 r14d, r14, rax
+ TestBSwap16 r15d, r15, rax
+%endif
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+.done:
+ mov xSP, xSI
+ add xSP, 80h
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', bswap', 0
+ENDPROC TMPL_NM(TestBSwap)
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoTestsForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Test exception handler basics using INT3 and #BP.
+ ;
+
+ call TMPL_NM(TestMemFences)
+ call TMPL_NM(TestBSwap)
+%ifdef TMPL_64BIT
+ ; Specifically tests 64-bit behavior.
+ call TMPL_NM(TestCvtSize)
+ call TMPL_NM(TestCmpxchg32)
+%endif
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call Bs2DisableNX_r86
+
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoTestsForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1.asm
new file mode 100644
index 00000000..ae59ea11
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-instr-1.asm
@@ -0,0 +1,120 @@
+; $Id: bootsector2-cpu-instr-1.asm $
+;; @file
+; Bootsector test for misc instructions.
+;
+; Recommended (but not necessary):
+; VBoxManage setextradata bs-cpu-instr-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+; Include and execute the init code.
+%define BS2_INIT_RM
+%define BS2_WITH_TRAPS
+%define BS2_INC_RM
+%define BS2_INC_PE32
+%define BS2_INC_PP32
+%define BS2_INC_PAE32
+%define BS2_INC_LM32
+%define BS2_INC_LM64
+%define BS2_WITH_TRAPRECS
+%include "bootsector2-common-init-code.mac"
+
+
+;
+; The main() function.
+;
+BEGINPROC main
+ BITS 16
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+
+
+ ;
+ ; Execute the tests
+ ;
+%if 1
+ call NAME(DoTestsForMode_rm_pe32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pp32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pae32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_lm64)
+%endif
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ ret
+
+.s_szTstName:
+ db 'tstCpuInstr1', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_PE32
+%include "bootsector2-cpu-instr-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-instr-1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-instr-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-instr-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac
new file mode 100644
index 00000000..86c7ce4c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac
@@ -0,0 +1,1061 @@
+; $Id: bootsector2-cpu-pf-1-template.mac $
+;; @file
+; Bootsector test for various types of #PFs - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%undef BIG_PAGE_SIZE
+%undef PXE_SIZE
+%ifdef TMPL_CMN_PP
+ %define BIG_PAGE_SIZE _4M
+ %define PXE_SIZE 4
+%else
+ %define BIG_PAGE_SIZE _2M
+ %define PXE_SIZE 8
+%endif
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoTestsForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ mov ax, [bp - 2]
+ test al, al
+ jz .nx_disabled
+ call Bs2IsNXSupported_r86
+ jz .done
+ call Bs2EnableNX_r86
+.nx_disabled:
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Do the tests.
+ ;
+ call TMPL_NM(TestNotPresent)
+ ;; @todo call TMPL_NM(TestReadOnly)
+ ;; @todo call TMPL_NM(TestSupervisor)
+ ;; @todo call TMPL_NM(TestReservedBits)
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call Bs2DisableNX_r86
+
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoTestsForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoBenchmarksForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Do the tests.
+ ;
+ call TMPL_NM(BenchmarkNotPresent)
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoBenchmarksForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Does the page-not-present tests.
+;
+; @param al Set if NXE=1, clear if NXE=0.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestNotPresent)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ ;
+ ; Setup sCX for all the following tests.
+ ;
+ xor sCX, sCX
+ test al, al
+ jz .no_nxe
+ mov sCX, X86_TRAP_PF_ID
+.no_nxe:
+
+ ;
+ ; First test, big page not present.
+ ;
+ mov xAX, .s_szBigPageNotPresent
+ test sCX, sCX
+ jz .test1_nxe
+ mov xAX, .s_szBigPageNotPresentNX
+.test1_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+.test1_cleanup:
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ;
+ ; The second test, normal page not present.
+ ;
+ mov xAX, .s_szPageNotPresent
+ test sCX, sCX
+ jz .test2_nxe
+ mov xAX, .s_szPageNotPresentNX
+.test2_nxe:
+ call TMPL_NM_CMN(TestSub)
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstPutPageTableAt)
+
+ ; Make the first and last page not-present.
+ and byte [BS2_USER_PX_0_ADDR], ~X86_PTE_P
+ and byte [BS2_USER_PX_0_ADDR + 01000h - PXE_SIZE], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Do the tests.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test2_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test2_cleanup
+
+.test2_cleanup:
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstRestoreBigPageAt)
+
+
+%if PXE_SIZE == 8 ; PAE or LM
+ ;
+ ; The third test, mark a page directory pointer entry not present.
+ ;
+ mov xAX, .s_szPdpeNotPresent
+ test sCX, sCX
+ jz .test3_nxe
+ mov xAX, .s_szPdpeNotPresentNX
+.test3_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ call TMPL_NM(TestGetPdpeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+ mov sAX, TST_SCRATCH_PDPT_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+ mov sAX, TST_SCRATCH_PDPT_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+.test3_cleanup:
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ call TMPL_NM(TestGetPdpeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+%endif ; PAE || LM
+
+
+%ifdef TMPL_LM64
+ ;
+ ; The fourth test, mark a page map level 4 entry not present.
+ ;
+ mov xAX, .s_szPml4eNotPresent
+ test sCX, sCX
+ jz .test4_nxe
+ mov xAX, .s_szPml4eNotPresentNX
+.test4_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PML4_BASE
+ call TMPL_NM(TestGetPml4eAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PML4_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+ mov sAX, TST_SCRATCH_PML4_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+ mov sAX, TST_SCRATCH_PML4_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+.test4_cleanup:
+ mov sAX, TST_SCRATCH_PML4_BASE
+ call TMPL_NM(TestGetPml4eAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+%endif
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szBigPageNotPresent:
+ db TMPL_MODE_STR, ', !NX, big page NP', 0
+.s_szBigPageNotPresentNX:
+ db TMPL_MODE_STR, ', NX, big page NP', 0
+.s_szPageNotPresent:
+ db TMPL_MODE_STR, ', !NX, page NP', 0
+.s_szPageNotPresentNX:
+ db TMPL_MODE_STR, ', NX, page NP', 0
+%if PXE_SIZE == 8 ; PAE or LM
+.s_szPdpeNotPresent:
+ db TMPL_MODE_STR, ', !NX, PDPE NP', 0
+.s_szPdpeNotPresentNX:
+ db TMPL_MODE_STR, ', NX, PDPE NP', 0
+%endif
+%ifdef TMPL_LM64
+.s_szPml4eNotPresent:
+ db TMPL_MODE_STR, ', !NX, PML4E NP', 0
+.s_szPml4eNotPresentNX:
+ db TMPL_MODE_STR, ', NX, PML4E NP', 0
+%endif
+ENDPROC TMPL_NM(TestNotPresent)
+
+
+
+;;
+; Does the page-not-present benchmark.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkNotPresent)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push sCX
+ push sDX
+ push xDI
+ push xSI
+ sub xSP, 20h
+
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ ;
+ ; The First benchmark: Big page not present.
+ ;
+
+ ; Mark the big test page not present.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Benchmark.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov xDX, .s_szBigPageNotPresent
+ mov xCX, .s_szBigPageNotPresentFailed
+ call TMPL_NM(TstBenchmark32BitReads)
+
+ ; Cleanup.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ;
+ ; The second benchmark: Normal page not present.
+ ;
+
+ ; Replace the big page with a page table and make the first and last
+ ; pages not-present.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstPutPageTableAt)
+
+ and byte [BS2_USER_PX_0_ADDR], ~X86_PTE_P
+ and byte [BS2_USER_PX_0_ADDR + 01000h - PXE_SIZE], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Benchmark.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov xDX, .s_szPageNotPresent
+ mov xCX, .s_szPageNotPresentFailed
+ call TMPL_NM(TstBenchmark32BitReads)
+
+ ; Cleanup
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstRestoreBigPageAt)
+
+
+ ;
+ ; Done.
+ ;
+ add xSP, 20h
+ pop xSI
+ pop xDI
+ pop sDX
+ pop sCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szBigPageNotPresent:
+ db TMPL_MODE_STR, ', read NP big page', 0
+.s_szBigPageNotPresentFailed:
+ db TMPL_MODE_STR, ', reading NP big page failed', 13, 10, 0
+.s_szPageNotPresent:
+ db TMPL_MODE_STR, ', read NP page', 0
+.s_szPageNotPresentFailed:
+ db TMPL_MODE_STR, ', reading NP page failed', 13, 10, 0
+ENDPROC TMPL_NM(BenchmarkNotPresent)
+
+
+;;
+; Benchmark 32-bit reads at a give location.
+;
+; Will report the result under the name given via xDX. Will report any test
+; failure giving the string pointed to by xCX as explanation.
+;
+; @param sAX The location to do the reads.
+; @param xDX The test value name.
+; @param xCX The failure string
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstBenchmark32BitReads)
+ push xBP
+ mov xBP, xSP
+%define a_pu32Test [xBP - sCB]
+ push sAX
+%define a_pszValue [xBP - sCB*2]
+ push sDX
+%define a_pszFailure [xBP - sCB*3]
+ push sCX
+ push sSI
+ push sDI
+%define u64NanoTS [xBP - sCB*5 - 8h]
+ sub xSP, 8h
+
+ ;
+ ; Calibrate the test so it doesn't take forever.
+ ;
+ mov xAX, .calibrate_resume
+ mov dl, 0eh
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ mov ecx, TST_CALIBRATE_LOOP_COUNT
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetNanoTS)
+
+.calibrate_loop:
+ mov sAX, a_pu32Test
+ mov esi, [sAX]
+.calibrate_resume:
+ test sAX, sAX
+ jnz .failure
+ dec ecx
+ jnz .calibrate_loop
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+ call TMPL_NM_CMN(Bs2TrapReset)
+
+ ; Figure out how many iterations is required for the full benchmark.
+ mov ecx, TST_BENCHMARK_PERIOD_IN_SECS
+ mov edx, TST_CALIBRATE_LOOP_COUNT
+ mov xAX, xSP
+ call TMPL_NM_CMN(CalcBenchmarkIterations)
+ mov ecx, eax ; iteration count.
+
+ ;
+ ; Do the full benchmark run.
+ ;
+ mov xAX, .bench_resume
+ mov dl, 0eh
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ mov edx, ecx ; save test count for ReportResult.
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetNanoTS)
+.bench_loop:
+ mov xAX, a_pu32Test
+ mov esi, [eax]
+.bench_resume:
+ test eax, eax
+ jnz .failure
+ dec ecx
+ jnz .bench_loop
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+ call TMPL_NM_CMN(Bs2TrapReset)
+
+ mov xCX, a_pszValue
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(ReportResult)
+
+.return:
+ pop sDI
+ pop sSI
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+
+.failure:
+ call TMPL_NM_CMN(Bs2TrapReset)
+ mov xAX, a_pszFailure
+ call TMPL_NM_CMN(TestFailed)
+ jmp .return
+
+%undef a_pszFailure
+%undef a_pu32Test
+%undef a_pszValue
+%undef a_pszFailure
+%undef u64NanoTS
+ENDPROC TMPL_NM(TstBenchmark32BitReads)
+
+
+;;
+; Fills the test area with return instructions.
+;
+; @uses nothing.
+;
+BEGINPROC TMPL_NM(TestFillTestAreaWithRet)
+ push xBP
+ mov xBP, xSP
+ push xDI
+ push xCX
+ push xAX
+
+ mov xDI, TST_SCRATCH_PD_BASE
+ mov xCX, (_4M + _4M) / 4
+ mov eax, 0c3c3c3c3h
+ rep stosd
+
+ mov xDI, TST_SCRATCH_PDPT_BASE
+ mov xCX, (_4M + _4M) / 4
+ mov eax, 0c3c3c3c3h
+ rep stosd
+
+%ifdef TMPL_LM64
+ mov xDI, TST_SCRATCH_PML4_BASE
+ mov xCX, (_4M + _4M) / 8
+ mov rax, 0c3c3c3c3c3c3c3c3h
+ rep stosq
+%endif
+
+ pop xAX
+ pop xCX
+ pop xDI
+ leave
+ ret
+ENDPROC TMPL_NM(TestFillTestAreaWithRet)
+
+
+;;
+; Gets the page directory address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns ds:xAX The page directory address.
+; @param sAX The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPdeAddr)
+ push xBP
+ mov xBP, xSP
+ push sBX
+ push sCX
+
+%ifdef TMPL_CMN_PP
+ ; PDPE
+ shr sAX, X86_PD_SHIFT
+ and sAX, X86_PD_MASK
+ shl sAX, 2
+ mov sBX, cr3
+ and sBX, X86_CR3_PAGE_MASK
+ add sAX, sBX
+
+%else
+ %ifdef TMPL_CMN_LM
+ ; PML4E
+ mov sCX, sAX
+ shr sCX, X86_PML4_SHIFT
+ and sCX, X86_PML4_MASK
+ shl sCX, 3
+ mov sBX, cr3
+ and sBX, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add sBX, sCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+ %else
+ mov sBX, cr3
+ and sBX, X86_CR3_PAE_PAGE_MASK
+ %endif
+
+ ; PDPE
+ mov sCX, sAX
+ shr sCX, X86_PDPT_SHIFT
+ %ifdef TMPL_CMN_LM
+ and sCX, X86_PDPT_MASK_AMD64
+ %else
+ and sCX, X86_PDPT_MASK_PAE
+ %endif
+ shl sCX, 3
+ add sBX, xCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+
+ ; PDE
+ shr sAX, X86_PD_PAE_SHIFT
+ and sAX, X86_PD_PAE_MASK
+ shl sAX, 3
+ add sAX, sBX
+%endif
+
+ pop sCX
+ pop sBX
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPdeAddr)
+
+
+%if PXE_SIZE == 8 ; PAE or LM
+;;
+; Gets the page directory pointer entry for an address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns ds:xAX The pointer to the PDPE.
+; @param sAX The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPdpeAddr)
+ push xBP
+ mov xBP, xSP
+ push sBX
+ push sCX
+
+%ifdef TMPL_CMN_PP
+ %error "misconfig"
+%endif
+
+%ifdef TMPL_CMN_LM
+ ; PML4E
+ mov sCX, sAX
+ shr sCX, X86_PML4_SHIFT
+ and sCX, X86_PML4_MASK
+ shl sCX, 3
+ mov sBX, cr3
+ and sBX, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add sBX, sCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+%else
+ mov sBX, cr3
+ and sBX, X86_CR3_PAE_PAGE_MASK
+%endif
+
+ ; PDPE
+ shr sAX, X86_PDPT_SHIFT
+%ifdef TMPL_CMN_LM
+ and sAX, X86_PDPT_MASK_AMD64
+%else
+ and sAX, X86_PDPT_MASK_PAE
+%endif
+ shl sAX, 3
+ add sAX, sBX
+
+ pop sCX
+ pop sBX
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPdpeAddr)
+%endif ; PAE or LM
+
+
+%ifdef TMPL_CMN_LM
+;;
+; Gets the page map level 4 entry for an address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns rax The pointer to the PML4E.
+; @param rax The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPml4eAddr)
+ push xBP
+ mov xBP, xSP
+ push rbx
+
+ ; PML4E
+ shr rax, X86_PML4_SHIFT
+ and rax, X86_PML4_MASK
+ shl rax, 3
+ mov rbx, cr3
+ and rbx, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add rax, rbx
+
+ pop rbx
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPml4eAddr)
+%endif ; TMPL_CMN_LM
+
+
+;;
+; Initialize page table #0 and hooks it up at the specified address.
+;
+; The page table will have identity mapped pages. The TLBs are flushed
+; wholesale. The caller will have to reconstruct the PDE when finished.
+;
+; @param sAX The virtual address (big page -> page table).
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstPutPageTableAt)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDI
+ push sSI
+
+ ; initialize a page table.
+ mov sDI, BS2_USER_PX_0_ADDR
+ mov sSI, sAX
+.init_loop:
+%if PXE_SIZE == 8
+ mov [sDI + 4], dword 0
+ mov [sDI], sSI
+%else
+ mov [sDI], esi
+%endif
+ or byte [sDI], X86_PTE_P | X86_PTE_RW
+ add sSI, _4K
+ add sDI, PXE_SIZE
+ test sDI, 0fffh
+ jnz .init_loop
+
+ ; hook it up instead of the big page.
+ and sAX, ~(BIG_PAGE_SIZE - 1)
+ mov sDI, sAX
+ call TMPL_NM(TestGetPdeAddr)
+ mov dword [sAX], BS2_USER_PX_0_ADDR | X86_PDE_P | X86_PDE_RW | X86_PDE_RW
+%if PXE_SIZE == 8
+ mov dword [sAX + 4], 0
+%endif
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Make sure it works.
+ mov eax, 0c3c3c3c3h
+ mov ecx, BIG_PAGE_SIZE / 4
+ rep stosd
+
+ pop sSI
+ pop sDI
+ pop sCX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM(TstPutPageTableAt)
+
+
+;;
+; Restores the big page for a virtual address, undoing harm done by a
+; previous TstPutPageTableAt call.
+;
+; @param sAX The virtual address to restore to a big page.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstRestoreBigPageAt)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDI
+
+ ; Set it up, inheriting bits from the previous PDE.
+ and sAX, ~(BIG_PAGE_SIZE - 1)
+ mov sDI, sAX ; save it for later.
+ call TMPL_NM(TestGetPdeAddr)
+ mov sCX, [sAX - PXE_SIZE]
+ and sCX, X86_PDE4M_US | X86_PDE4M_RW | X86_PDE4M_G | X86_PDE4M_PAT | X86_PDE4M_AVL | X86_PDE4M_PCD | X86_PDE4M_PWT
+ or sCX, X86_PDE4M_P | X86_PDE4M_PS
+ or sCX, sDI
+%if PXE_SIZE == 8
+ mov dword [sAX + 4], 0
+ mov [sAX], sCX
+%else
+ mov [sAX], ecx
+%endif
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Make sure it works.
+ mov eax, 0c3c3c3c3h
+ mov ecx, BIG_PAGE_SIZE / 4
+ rep stosd
+
+ pop sDI
+ pop sCX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM(TstRestoreBigPageAt)
+
+
+
+;;
+; Hammers a page.
+;
+; Accesses a page in a few different ways, expecting all of the accesses to
+; cause some kind of page fault. The caller just makes sure the page causes
+; a fault and points us to it.
+;
+; @returns al=1, ZF=0 on success.
+; @returns al=0, ZF=1 on failure.
+; @param sAX The page.
+; @param sDX The base error code to expect.
+; @param xCX X86_TRAP_PF_ID if NXE, otherwise 0.
+; @uses al
+;
+BEGINPROC TMPL_NM(TestHammerPage)
+ push xBP
+ mov xBP, xSP
+ push sBX
+%define a_uErrorExec sPRE [xBP - sCB*2]
+ push sCX
+%define a_uErrorFixed sPRE [xBP - sCB*3]
+ push sDX
+ push sDI
+ push sSI
+%define a_pPage sPRE [xBP - sCB*6]
+ push sAX
+
+ ;
+ ; First reads of different sizes.
+ ;
+ mov sDI, a_pPage
+.read_byte_loop:
+ mov dl, 0ffh
+ mov xAX, .read_byte_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_byte:
+ mov cl, byte [sDI]
+.read_byte_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_byte ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_byte_loop
+
+ mov sDI, a_pPage
+.read_word_loop:
+ mov dl, 0ffh
+ mov xAX, .read_word_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_word:
+ mov cx, word [sDI]
+.read_word_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_word ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_word_loop
+
+ mov sDI, a_pPage
+.read_dword_loop:
+ mov dl, 0ffh
+ mov xAX, .read_dword_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_dword:
+ mov ecx, dword [sDI]
+.read_dword_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_dword ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_dword_loop
+
+ ;
+ ; Then writes of different sizes.
+ ;
+ mov sDI, a_pPage
+.write_byte_loop:
+ mov dl, 0ffh
+ mov xAX, .write_byte_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_byte:
+ mov byte [sDI], 0c3h ; (ret instruction)
+.write_byte_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_byte ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_byte_loop
+
+ mov sDI, a_pPage
+.write_word_loop:
+ mov dl, 0ffh
+ mov xAX, .write_word_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_word:
+ mov word [sDI], 0c3c3h ; (2 ret instructions)
+.write_word_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_word ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_word_loop
+
+ mov sDI, a_pPage
+.write_dword_loop:
+ mov dl, 0ffh
+ mov xAX, .write_dword_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_dword:
+ mov dword [sDI], 0c3c3c3c3h ; (4 ret instructions)
+.write_dword_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_dword ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_dword_loop
+
+ ;
+ ; Execute access.
+ ;
+ mov sDI, a_pPage
+ mov xSI, xSP
+.call_loop:
+ mov dl, 0ffh
+ mov xAX, .call_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ call sDI
+.call_resume:
+ mov xSP, xSI ; restore xSP since the call will change it before #PF'ing.
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, a_uErrorExec
+ mov sCX, sDI ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .call_loop
+
+
+ mov sDI, a_pPage
+ mov xSI, xSP
+.jmp_loop:
+ mov dl, 0ffh
+ mov xAX, .jmp_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ push .jmp_resume ; push a return address in case of failure.
+ jmp sDI
+.jmp_resume:
+ mov xSP, xSI ; restore xSP in case the jmp didn't trap.
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, a_uErrorExec
+ mov sCX, sDI ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .jmp_loop
+
+ ; successfull return.
+ pop sAX
+ xor al, al
+ inc al
+.return:
+ pop sSI
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ leave
+ ret
+
+.failed:
+ pop sAX
+ xor al, al
+ jmp .return
+%undef a_uErrorFixed
+%undef a_uErrorExec
+%undef a_pPage
+ENDPROC TMPL_NM(TestHammerPage)
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1.asm
new file mode 100644
index 00000000..0347c027
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1.asm
@@ -0,0 +1,164 @@
+; $Id: bootsector2-cpu-pf-1.asm $
+;; @file
+; Bootsector test for various types of #PFs.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;; Base address at which we can start testing page tables and page directories.
+%define TST_SCRATCH_PD_BASE BS2_MUCK_ABOUT_BASE
+;; Base address at which we can start testing the page pointer table.
+%define TST_SCRATCH_PDPT_BASE (1 << X86_PDPT_SHIFT)
+;; Base address at which we can start testing the page map level 4.
+%define TST_SCRATCH_PML4_BASE ((1 << X86_PML4_SHIFT) + TST_SCRATCH_PD_BASE)
+
+;; The number of loops done during calibration.
+%define TST_CALIBRATE_LOOP_COUNT 10000
+;; The desired benchmark period (seconds).
+%define TST_BENCHMARK_PERIOD_IN_SECS 2
+
+
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_WITH_TRAPS
+ %define BS2_INC_RM
+; %define BS2_INC_PP16
+ %define BS2_INC_PP32
+; %define BS2_INC_PAE16
+ %define BS2_INC_PAE32
+; %define BS2_INC_LM16
+; %define BS2_INC_LM32
+ %define BS2_INC_LM64
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+
+ ;
+ ; Execute the tests
+ ;
+%if 1
+ %if 1
+ xor eax, eax ; NXE=0 (N/A)
+ call NAME(DoTestsForMode_rm_pp32)
+ %endif
+ %if 1
+ xor eax, eax ; NXE=0
+ call NAME(DoTestsForMode_rm_pae32)
+ mov eax, 1 ; NXE=1
+ call NAME(DoTestsForMode_rm_pae32)
+ %endif
+ %if 1
+ xor eax, eax ; NXE=0
+ call NAME(DoTestsForMode_rm_lm64)
+ mov eax, 1 ; NXE=1
+ call NAME(DoTestsForMode_rm_lm64)
+ %endif
+%endif
+
+ ;
+ ; Execute benchmarks.
+ ;
+%if 1
+ mov ax, .s_szTstBenchmark
+ call NAME(TestSub_r86)
+ call NAME(DoBenchmarksForMode_rm_pp32)
+ call NAME(DoBenchmarksForMode_rm_pae32)
+ call NAME(DoBenchmarksForMode_rm_lm64)
+ call NAME(TestSubDone_r86)
+%endif
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ ret
+
+.s_szTstBenchmark:
+ db 'Benchmark', 0
+.s_szTstName:
+ db 'tstIOIntr', 0
+.s_szTstX:
+ db 'X', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+;%define TMPL_PP16
+;%include "bootsector2-cpu-pf-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-pf-1-template.mac"
+;%define TMPL_PAE16
+;%include "bootsector2-cpu-pf-1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-pf-1-template.mac"
+;%define TMPL_LM16
+;%include "bootsector2-cpu-pf-1-template.mac"
+;%define TMPL_LM32
+;%include "bootsector2-cpu-pf-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-pf-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1-template.mac
new file mode 100644
index 00000000..56374932
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1-template.mac
@@ -0,0 +1,1973 @@
+; $Id: bootsector2-cpu-xcpt-1-template.mac $
+;; @file
+; Bootsector test for basic exceptions - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;;
+; Some 32/64 macros.
+;
+%if TMPL_BITS == 32
+ %define bs2Idt_BP bs2Idt32bit_BP
+ %define MY_R0_CS BS2_SEL_CS32
+ %define MY_R1_CS BS2_SEL_R1_CS32
+ %define MY_R2_CS BS2_SEL_R2_CS32
+ %define MY_R3_CS BS2_SEL_R3_CS32
+
+ %define MY_R0_DS BS2_SEL_DS32
+ %define MY_R1_DS BS2_SEL_R1_DS32
+ %define MY_R2_DS BS2_SEL_R2_DS32
+ %define MY_R3_DS BS2_SEL_R3_DS32
+
+ %define MY_R0_SS BS2_SEL_SS32
+ %define MY_R1_SS BS2_SEL_R1_SS32
+ %define MY_R2_SS BS2_SEL_R2_SS32
+ %define MY_R3_SS BS2_SEL_R3_SS32
+
+%else
+ %define bs2Idt_BP bs2Idt64bit_BP
+ %define MY_R0_CS BS2_SEL_CS64
+ %define MY_R1_CS BS2_SEL_R1_CS64
+ %define MY_R2_CS BS2_SEL_R2_CS64
+ %define MY_R3_CS BS2_SEL_R3_CS64
+
+ %define MY_R0_DS BS2_SEL_DS64
+ %define MY_R1_DS BS2_SEL_R1_DS64
+ %define MY_R2_DS BS2_SEL_R2_DS64
+ %define MY_R3_DS BS2_SEL_R3_DS64
+
+ %define MY_R0_SS BS2_SEL_SS64
+ %define MY_R1_SS BS2_SEL_R1_SS64
+ %define MY_R2_SS BS2_SEL_R2_SS64
+ %define MY_R3_SS BS2_SEL_R3_SS64
+%endif
+
+%ifdef TMPL_64BIT
+ %assign MY_IS_64BIT 1
+%else
+ %assign MY_IS_64BIT 0
+%endif
+
+
+;*******************************************************************************
+;* Global Variables *
+;*******************************************************************************
+%ifndef CPU_XCPT_1_GLOBALS
+ %define CPU_XCPT_1_GLOBALS
+ g_szWrongIfStateFmt:
+ db 'Wrong IF state (0x%RX32) on line 0x%RX32', 0
+ g_szWrongHandlerCsFmt:
+ db 'Wrong handler CS=%RX16, expected %RX16 (line 0x%RX32)', 0
+ g_szWrongCurCsFmt:
+ db 'Wrong CS=%RX16, expected %RX16 (line 0x%RX32)', 0
+ g_szWrongCurSRegFmt_fs:
+ db 'Wrong FS=%RX16, expected %RX16 (line 0x%RX32)', 0
+ g_szWrongCurSRegFmt_ss:
+ db 'Wrong SS=%RX16, expected %RX16 (line 0x%RX32)', 0
+
+
+;;
+; Asserts a test.
+;
+; @param %1 First cmp operand.
+; @param %2 First cmp operand.
+; @param %3 Which kind of conditional jump to make
+; @param %4 The message to print (format string, no arguments please).
+;
+%macro ASSERT_SIMPLE 4
+ cmp %1, %2
+ %3 %%.ok
+ push dword __LINE__
+ %ifdef TMPL_16BIT
+ push ds
+ %endif
+ push %%.s_szMsg
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB*2
+ jmp %%.ok
+%%.s_szMsg: db %4, " (0x%RX32)", 0
+%%.ok:
+%endmacro
+
+
+ ;;
+ ; Asserts that the IF flag is set or clear when the trap handler was called.
+ ;
+ ; @param 1 jnz or jz.
+ ;
+ ; @uses rax, flags, and stack.
+ ;
+ %macro ASSERT_TRAP_EFLAGS_IF 1
+ test word [g_u64LastTrapHandlerRFlags xWrtRIP], X86_EFL_IF
+ %1 %%.ok
+ %ifdef TMPL_LM64
+ push __LINE__
+ push qword [g_u64LastTrapHandlerRFlags xWrtRIP]
+ lea rax, [g_szWrongIfStateFmt wrt RIP]
+ push rax
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 24
+ %elifdef TMPL_16
+ push dword __LINE__
+ push dword [g_u64LastTrapHandlerRFlags]
+ push cs
+ push g_szWrongIfStateFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 12
+ %else
+ push __LINE__
+ push dword [g_u64LastTrapHandlerRFlags]
+ push g_szWrongIfStateFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 12
+ %endif
+ %%.ok:
+ %endmacro
+
+
+ ;;
+ ; Asserts that a certain CS value when the trap handler was called.
+ ;
+ ; @param 1 The CS value.
+ ;
+ ; @uses rax, flags, and stack.
+ ;
+ %macro ASSERT_TRAP_CS_VALUE 1
+ cmp word [g_u16LastTrapHandlerCS xWrtRIP], (%1)
+ je %%.ok
+ %ifdef TMPL_LM64
+ push __LINE__
+ push (%1)
+ movzx eax, word [g_u16LastTrapHandlerCS xWrtRIP]
+ push rax
+ lea rax, [g_szWrongHandlerCsFmt wrt RIP]
+ push rax
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 32
+ %elifdef TMPL_16
+ push dword __LINE__
+ push word (%1)
+ push word [g_u16LastTrapHandlerCS]
+ push cs
+ push g_szWrongHandlerCsFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 12
+ %else
+ push __LINE__
+ push (%1)
+ movzx eax, word [g_u16LastTrapHandlerCS]
+ push eax
+ push g_szWrongHandlerCsFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 16
+ %endif
+ %%.ok:
+ %endmacro
+
+ ;;
+ ; Asserts that a certain CS value right now, CS being loaded in BX.
+ ;
+ ; @param bx The CS value.
+ ; @param 1 The expected CS value.
+ ;
+ ; @uses rax, flags, and stack.
+ ;
+ %macro ASSERT_CUR_CS_VALUE_IN_BX 1
+ cmp bx, (%1)
+ je %%.ok
+ %ifdef TMPL_LM64
+ push __LINE__
+ push (%1)
+ push rbx
+ lea rax, [g_szWrongCurCsFmt wrt RIP]
+ push rax
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 32
+ %elifdef TMPL_16
+ push dword __LINE__
+ push word (%1)
+ push bx
+ push g_szWrongCurCsFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 12
+ %else
+ push __LINE__
+ push (%1)
+ push ebx
+ push g_szWrongCurCsFmt
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 16
+ %endif
+ %%.ok:
+ %endmacro
+
+ ;;
+ ; Asserts that the given segment register has a certain value right now.
+ ;
+ ; @param 1 The segment register
+ ; @param 2 The value.
+ ;
+ ; @uses rax, flags, and stack.
+ ;
+ %macro ASSERT_CUR_SREG_VALUE 2
+ mov ax, %1
+ cmp ax, (%2)
+ je %%.ok
+ %ifdef TMPL_LM64
+ push __LINE__
+ push (%2)
+ push rax
+ lea rax, [g_szWrongCurSRegFmt_ %+ %1 wrt RIP]
+ push rax
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 32
+ %elifdef TMPL_16
+ push dword __LINE__
+ push word (%2)
+ push ax
+ push g_szWrongCurSRegFmt_ %+ %1
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 12
+ %else
+ push __LINE__
+ push (%2)
+ push eax
+ push g_szWrongCurSRegFmt_ %+ %1
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, 16
+ %endif
+ %%.ok:
+ %endmacro
+
+
+%endif
+
+
+;;
+; Checks different gate types.
+;
+BEGINPROC TMPL_NM(TestGateType)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Check that int3 works and save the IDTE before making changes.
+ ;
+ ; We'll be changing X86DESCGATE.u4Type, which starts at bit 0x28 (that
+ ; is byte 5) and is 4-bit wide, and X86DESCGATE.u1DescType, which is
+ ; at bit 2c.
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check that int3 works before we start messing around...
+
+%ifdef TMPL_LM64
+ push qword [bs2Idt_BP xWrtRIP]
+ push qword [bs2Idt_BP + 8 xWrtRIP]
+%else
+ push dword [bs2Idt_BP xWrtRIP]
+ push dword [bs2Idt_BP + 4 xWrtRIP]
+%endif
+ mov xDI, xSP ; for catching stack errors
+
+ ;
+ ; Check all kinds of none system selectors first (they should all GP(3+IDT))
+ ;
+%assign u4Type 0
+%rep 16
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], RT_BIT(4) | u4Type
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+ %assign u4Type (u4Type + 1)
+%endrep
+
+ ;
+ ; Illegal system types.
+ ;
+%ifdef TMPL_LM64
+ %assign u4Type 0
+ %rep 14
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], u4Type
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+ %assign u4Type (u4Type + 1)
+ %endrep
+%else
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_286_TSS_AVAIL
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_LDT
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_286_TSS_BUSY
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_286_CALL_GATE
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED2
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TSS_AVAIL
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED3
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TSS_BUSY
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED4
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_CALL_GATE
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+%endif
+
+ ;
+ ; Legal types.
+ ;
+ pushf
+ sti ; make sure interrupts are enabled.
+
+%ifdef TMPL_LM64
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], AMD64_SEL_TYPE_SYS_INT_GATE
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_EFLAGS_IF jz
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], AMD64_SEL_TYPE_SYS_TRAP_GATE
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_EFLAGS_IF jnz
+%else
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_INT_GATE
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_EFLAGS_IF jz
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TRAP_GATE
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_EFLAGS_IF jnz
+
+ ;; @todo X86_SEL_TYPE_SYS_TASK_GATE, X86_SEL_TYPE_SYS_286_INT_GATE, X86_SEL_TYPE_SYS_286_TRAP_GATE, X86_SEL_TYPE_SYS_386_CALL_GATE
+%endif
+
+ popf
+
+ ;
+ ; Check that a not-present gate GPs. The not-present bit is 0x2f.
+ ;
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+%ifdef TMPL_LM64
+ or byte [bs2Idt_BP + 5 xWrtRIP], AMD64_SEL_TYPE_SYS_INT_GATE
+%else
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TRAP_GATE
+%endif
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 07fh
+ BS2_TRAP_INSTR X86_XCPT_NP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; Restore the descriptor and make sure it works.
+ ;
+ ASSERT_SIMPLE xDI, xSP, je, "Someone busted xSP during this test."
+%ifdef TMPL_LM64
+ pop qword [bs2Idt_BP + 8 xWrtRIP]
+ pop qword [bs2Idt_BP xWrtRIP]
+%else
+ pop dword [bs2Idt_BP + 4 xWrtRIP]
+ pop dword [bs2Idt_BP xWrtRIP]
+%endif
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', IDTE type checks', 0
+ENDPROC TMPL_NM(TestGateType)
+
+
+;;
+; Checks different code selector types.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestCodeSelector)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Modify the first extra selector to be various kinds of invalid code
+ ; selectors.
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check that int3 works before we start messing around...
+
+%ifdef TMPL_LM64
+ push qword [bs2Idt_BP xWrtRIP]
+ push qword [bs2Idt_BP + 8 xWrtRIP]
+%else
+ push dword [bs2Idt_BP xWrtRIP]
+ push dword [bs2Idt_BP + 4 xWrtRIP]
+%endif
+
+ mov ecx, [bs2Gdt + MY_R0_CS xWrtRIP]
+ mov [bs2GdtSpare0 xWrtRIP], ecx
+ mov ecx, [bs2Gdt + MY_R0_CS + 4 xWrtRIP]
+ mov [bs2GdtSpare0 + 4 xWrtRIP], ecx ; GdtSpare0 is a copy of the CS descriptor now.
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], BS2_SEL_SPARE0
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check again to make sure the CS copy is fine.
+
+
+ ; Data selector (u4Type starts at bit 0x28, that is byte 5) .
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RO
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RO_ACC
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RW
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RW_ACC
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RO_DOWN
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RO_DOWN_ACC
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RW_DOWN
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RW_DOWN_ACC
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ ; Executable selector types (works fine).
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_EO
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_EO_ACC
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_EO_CONF
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_EO_CONF_ACC
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_CONF
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_CONF_ACC
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Test with the code selector set to NULL.
+ ;
+ mov word [bs2Idt_BP + 2 xWrtRIP], 0
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], 1
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], 2
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], 3
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], BS2_SEL_SPARE0 ; restore our CS
+
+ ;
+ ; Test with the code selector marked as not present but otherwise valid.
+ ;
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 07fh
+ BS2_TRAP_INSTR X86_XCPT_NP, BS2_SEL_SPARE0, int3
+
+ ;
+ ; Invalid CS selector and not present, we should get a GP.
+ ; Intel states that the present bit is checked after the type.
+ ;
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 070h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RW_DOWN_ACC
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+%ifdef TMPL_LM64
+ ; Long mode variations on invalid (L and D bits) pitted against NP.
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 070h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC
+ and byte [bs2GdtSpare0 + 6 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6)) ; (0x35=u1Long, 0x36=u1DefBig) = (0, 0)
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ or byte [bs2GdtSpare0 + 6 xWrtRIP], RT_BIT(6) ; (0x35=u1Long, 0x36=u1DefBig) = (0, 1)
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ or byte [bs2GdtSpare0 + 6 xWrtRIP], RT_BIT(5) ; (0x35=u1Long, 0x36=u1DefBig) = (1, 1)
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 6 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2GdtSpare0 + 6 xWrtRIP], RT_BIT(5) ; restored
+%endif
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 070h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC | 080h ; restore CS to present & valid.
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; make sure this is so.
+
+ ;
+ ; Check the CS DPL vs IDTE DPL.
+ ; X86DESCGENERIC.u2Dpl is at bit 0x2d (i.e. in byte 5).
+ ;
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], 0 ; CS.DPL == 0 == CPL
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], 1 << 5 ; CS.DPL == 1 < CPL
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], 2 << 5 ; CS.DPL == 2 < CPL
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], 3 << 5 ; CS.DPL == 3 < CPL
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ ; Restore.
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 010h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC | 080h ; restore CS to present, valid and DPL=0
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; make sure it's restored.
+
+ ;
+ ; Is RPL is ignored? Yes, it is.
+ ;
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL ; RPL = 0
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0
+
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ or byte [bs2Idt_BP + 2 xWrtRIP], 1 ; RPL = 1
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0
+
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ or byte [bs2Idt_BP + 2 xWrtRIP], 2 ; RPL = 2
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0
+
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ or byte [bs2Idt_BP + 2 xWrtRIP], 3 ; RPL = 3
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0
+
+ ;
+ ; Conforming CS.
+ ;
+ or byte [bs2Idt_BP + 5 xWrtRIP], (3 << 5) ; IDTE.DPL = 3
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 090h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_CONF_ACC ; CS.DPL=0, code, read, conforming
+
+ call TMPL_NM_CMN(Bs2ToRing1)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 1
+
+ call TMPL_NM_CMN(Bs2ToRing2)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 2
+
+ call TMPL_NM_CMN(Bs2ToRing3)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 3
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 0
+
+ ; RPL is ignored. Only CPL matters.
+ or byte [bs2Idt_BP + 2 xWrtRIP], (3 << 5) ; IDTE.CS.RPL=3
+ call TMPL_NM_CMN(Bs2ToRing2)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 2
+
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ or byte [bs2Idt_BP + 2 xWrtRIP], (1 << 5) ; IDTE.CS.RPL=1
+ call TMPL_NM_CMN(Bs2ToRing2)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 2
+
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ or byte [bs2Idt_BP + 2 xWrtRIP], (2 << 5) ; IDTE.CS.RPL=2
+ call TMPL_NM_CMN(Bs2ToRing2)
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_TRAP_CS_VALUE BS2_SEL_SPARE0 | 2
+
+ ; Change the CS.DPL to 1 and try it from ring-0.
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 09fh
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], (1 << 5) ; CS.DPL=1
+ BS2_TRAP_INSTR X86_XCPT_GP, BS2_SEL_SPARE0, int3
+
+ ; Restore.
+ and word [bs2Idt_BP + 2 xWrtRIP], X86_SEL_MASK_OFF_RPL
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0x9f ; IDTE.DPL=0
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 010h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_ER_ACC | 080h ; restore CS to present, valid and DPL=0
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; make sure it's restored.
+
+ ;
+ ; Limit / canonical checks.
+ ;
+ ; Messing with X86DESCGENERIC.u16LimitLow which is at bit 0,
+ ; X86DESCGENERIC.u4LimitHigh which is at bit 0x30, and
+ ; X86DESCGENERIC.u1Granularity which is at bit 0x37.
+ ;
+ mov word [bs2GdtSpare0 xWrtRIP], 0010h
+ and byte [bs2GdtSpare0 + 6 xWrtRIP], 070h ; setting limit to 0x10, ASSUMES IDTE.off > 0x10
+%ifdef TMPL_LM64
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+%else
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+%endif
+
+%ifdef TMPL_LM64
+ or dword [bs2Idt_BP + 8 xWrtRIP], 0x007f7f33
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, int3
+%endif
+
+ ; Who takes precedence? CS NP or the above GP? NP does.
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 07fh
+ BS2_TRAP_INSTR X86_XCPT_NP, BS2_SEL_SPARE0, int3
+
+
+%ifdef TMPL_LM64
+ ; Who takes precedence? IDTE NP or the not canoncial GP? NP does.
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], 80h
+ and byte [bs2Idt_BP + 5 xWrtRIP], 07fh
+ BS2_TRAP_INSTR X86_XCPT_NP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+%endif
+
+ ;
+ ; Restore the descriptor and make sure it works.
+ ;
+%ifdef TMPL_LM64
+ pop qword [bs2Idt_BP + 8 xWrtRIP]
+ pop qword [bs2Idt_BP xWrtRIP]
+%else
+ pop dword [bs2Idt_BP + 4 xWrtRIP]
+ pop dword [bs2Idt_BP xWrtRIP]
+%endif
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', IDTE CS checks', 0
+ENDPROC TMPL_NM(TestCodeSelector)
+
+
+;;
+; Checks that the IDTE type is checked before the CS type.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestCheckOrderCsTypeVsIdteType)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Check the int3 and save its IDTE.
+ ;
+ ; We'll be changing X86DESCGATE.u4Type, which starts at bit 0x28 (that
+ ; is byte 5) and is 4-bit wide, and X86DESCGATE.u1DescType, which is
+ ; at bit 2c.
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check that int3 works before we start messing around...
+
+%ifdef TMPL_LM64
+ push qword [bs2Idt_BP xWrtRIP]
+ push qword [bs2Idt_BP + 8 xWrtRIP]
+%else
+ push dword [bs2Idt_BP xWrtRIP]
+ push dword [bs2Idt_BP + 4 xWrtRIP]
+%endif
+
+ ;
+ ; Make a copy of our CS descriptor into spare one and make INT3 use it.
+ ;
+ mov ecx, [bs2Gdt + MY_R0_CS xWrtRIP]
+ mov [bs2GdtSpare0 xWrtRIP], ecx
+ mov ecx, [bs2Gdt + MY_R0_CS + 4 xWrtRIP]
+ mov [bs2GdtSpare0 + 4 xWrtRIP], ecx ; GdtSpare0 is a copy of the CS descriptor now.
+
+ mov word [bs2Idt_BP + 2 xWrtRIP], BS2_SEL_SPARE0
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check again to make sure the CS copy is fine.
+
+ ;
+ ; Make both the IDTE type and CS invalid, we should end up with a IDT GP not the CS one.
+ ; CS = data selector and IDTE invalid 0 type.
+ ;
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 0f0h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_RO
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0e0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; Make the IDTE not-present but otherwise fine, keeping CS invalid.
+ ;
+ and byte [bs2Idt_BP + 5 xWrtRIP], 070h
+%ifdef TMPL_LM64
+ or byte [bs2Idt_BP + 5 xWrtRIP], AMD64_SEL_TYPE_SYS_INT_GATE
+%else
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TRAP_GATE
+%endif
+ BS2_TRAP_INSTR X86_XCPT_NP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; Make the CS not present as well.
+ ;
+ and byte [bs2GdtSpare0 + 5 xWrtRIP], 070h
+ or byte [bs2GdtSpare0 + 5 xWrtRIP], X86_SEL_TYPE_EO
+ BS2_TRAP_INSTR X86_XCPT_NP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; CS not present, IDTE invalid but present.
+ ;
+ and byte [bs2Idt_BP + 5 xWrtRIP], 0f0h
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_UNDEFINED | 0x80
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; CS NULL, IDTE invalid but present.
+ ;
+ mov word [bs2Idt_BP + 2 xWrtRIP], 0
+ BS2_TRAP_INSTR X86_XCPT_GP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; CS NULL, IDTE valid but not present.
+ ;
+ and byte [bs2Idt_BP + 5 xWrtRIP], 070h
+%ifdef TMPL_LM64
+ or byte [bs2Idt_BP + 5 xWrtRIP], AMD64_SEL_TYPE_SYS_INT_GATE
+%else
+ or byte [bs2Idt_BP + 5 xWrtRIP], X86_SEL_TYPE_SYS_386_TRAP_GATE
+%endif
+ BS2_TRAP_INSTR X86_XCPT_NP, (3 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT, int3
+
+ ;
+ ; Restore the descriptor and make sure it works.
+ ;
+%ifdef TMPL_LM64
+ pop qword [bs2Idt_BP + 8 xWrtRIP]
+ pop qword [bs2Idt_BP xWrtRIP]
+%else
+ pop dword [bs2Idt_BP + 4 xWrtRIP]
+ pop dword [bs2Idt_BP xWrtRIP]
+%endif
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', IDTE.type before CS.type', 0
+ENDPROC TMPL_NM(TestCheckOrderCsTypeVsIdteType)
+
+
+;;
+; Checks stack switching behavior.
+;
+; @uses none
+;
+BEGINPROC TMPL_NM(TestStack)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ pushf
+ cli
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Check the int3, save its IDTE, then make it ring-3 accessible.
+ ;
+ ; X86DESCGENERIC.u2Dpl is at bit 0x2d (i.e. in byte 5).
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check that int3 works before we start messing around...
+
+%ifdef TMPL_LM64
+ push qword [bs2Idt_BP xWrtRIP]
+ push qword [bs2Idt_BP + 8 xWrtRIP]
+%else
+ push dword [bs2Idt_BP xWrtRIP]
+ push dword [bs2Idt_BP + 4 xWrtRIP]
+%endif
+
+ and byte [bs2Idt_BP + 5 xWrtRIP], ~(RT_BIT(5) | RT_BIT(6))
+ or byte [bs2Idt_BP + 5 xWrtRIP], 3 << 5 ; DPL == 3
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+
+ ;
+ ; In ring-0 no stack switching is performed.
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov xBX, [g_u64LastTrapHandlerRSP]
+%ifdef TMPL_64BIT
+ mov rax, rsp
+ and rax, ~15
+ sub rax, 7*8
+%else
+ lea eax, [esp - 5*4]
+%endif
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong xSP value for ring-0 -> ring-0 int3."
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ax, ss
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-0 -> ring-0 int3."
+
+ ;
+ ; Switch to ring-1 and watch stack switching take place.
+ ;
+ call TMPL_NM_CMN(Bs2ToRing1)
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov xBX, [g_u64LastTrapHandlerRSP]
+ mov sAX, BS2_R0_STACK_ADDR
+%ifdef TMPL_64BIT
+ and rax, ~15
+ sub rax, 7*8
+%else
+ sub eax, 7*4
+%endif
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong xSP value for ring-1 -> ring-0 int3."
+ mov bx, [g_u16LastTrapHandlerSS]
+%ifdef TMPL_64BIT
+ mov ax, 0
+%else
+ mov ax, MY_R0_SS
+%endif
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-1 -> ring-0 int3."
+
+ call TMPL_NM_CMN(Bs2ToRing0)
+
+ ;
+ ; Missaligned stack, ring-0 -> ring-0.
+ ;
+ mov xDI, xSP ; save the stack pointer.
+%rep 15
+ sub xSP, 1h
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov xBX, [g_u64LastTrapHandlerRSP]
+%ifdef TMPL_64BIT
+ mov rax, rsp
+ and rax, ~15
+ sub rax, 7*8
+%else
+ lea eax, [esp - 5*4]
+%endif
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong xSP value for ring-0 -> ring-0 int3, w/ unaligned stack."
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ax, ss
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-0 -> ring-0 int3, w/ unaligned stack."
+
+%endrep
+ mov xSP, xDI ; restore the stack pointer.
+
+ ;
+ ; Missaligned stack, ring-1 -> ring-0.
+ ;
+ call TMPL_NM_CMN(Bs2ToRing1)
+
+ mov sSI, BS2_R0_STACK_ADDR - 16
+%rep 16
+ add sSI, 1
+%ifdef TMPL_64BIT
+ mov [bs2Tss64Bit + 4], sSI
+%else
+ mov [bs2Tss32Bit + 4], sSI
+%endif
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov xBX, [g_u64LastTrapHandlerRSP]
+ mov sAX, sSI
+%ifdef TMPL_64BIT
+ and rax, ~15
+ sub rax, 7*8
+%else
+ sub eax, 7*4
+%endif
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong xSP value for ring-1 -> ring-0 int3, w/ unaligned ring-0 stack."
+ mov bx, [g_u16LastTrapHandlerSS]
+%ifdef TMPL_64BIT
+ mov ax, 0
+%else
+ mov ax, MY_R0_SS
+%endif
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong SS value for ring-1 -> ring-0 int3, w/ unaligned ring-0 stack."
+
+%endrep
+ call TMPL_NM_CMN(Bs2ToRing0)
+
+
+%ifdef TMPL_64BIT
+ ;
+ ; Stack table (AMD64 only), ring-0 -> ring-0.
+ ;
+ and byte [bs2Idt_BP + 4], ~7
+ or byte [bs2Idt_BP + 4], 3 ; IDTE.IST=3
+
+ mov rdi, [bs2Tss64Bit + X86TSS64.ist3]
+ mov rsi, BS2_R0_STACK_ADDR - 128
+ %rep 16
+ sub rsi, 1h
+ mov [bs2Tss64Bit + X86TSS64.ist3], rsi
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov rbx, [g_u64LastTrapHandlerRSP]
+ mov rax, rsi
+ and rax, ~15
+ sub rax, 7*8
+ ASSERT_SIMPLE rax, rbx, je, "Wrong xSP value for ring-0 -> ring-0 int3, w/ unaligned IST."
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ax, ss
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-0 -> ring-0 int3, w/ unaligned IST."
+
+ %endrep
+
+ ; Continue in ring-1,2,3.
+ %assign uCurRing 1
+ %rep 3
+ call TMPL_NM_CMN(Bs2ToRing %+ uCurRing)
+ %rep 16
+ sub rsi, 1h
+ mov [bs2Tss64Bit + X86TSS64.ist3], rsi
+
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov rbx, [g_u64LastTrapHandlerRSP]
+ mov rax, rsi
+ and rax, ~15
+ sub rax, 7*8
+ ASSERT_SIMPLE rax, rbx, je, "Wrong xSP value for ring-X -> ring-0 int3, w/ unaligned IST."
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ax, 0
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-X -> ring-0 int3, w/ unaligned IST."
+ %endrep
+ call TMPL_NM_CMN(Bs2ToRing0)
+ %assign uCurRing (uCurRing + 1)
+ %endrep
+
+ mov [bs2Tss64Bit + X86TSS64.ist3], rdi ; restore original value
+ and byte [bs2Idt_BP + 4], ~7 ; IDTE.IST=0
+
+
+ ;
+ ; Check SS handling when interrupting 32-bit code with a 64-bit handler.
+ ;
+ call Bs2Thunk_lm64_lm32
+ BITS 32
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ax, ss
+ call Bs2Thunk_lm32_lm64
+ BITS 64
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-0-32 -> ring-0-64 int3, w/ 32-bit stack."
+
+ call Bs2Thunk_lm64_lm32
+ BITS 32
+ mov cx, ss
+ mov ax, BS2_SEL_SS16
+ mov ss, ax
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+ mov bx, [g_u16LastTrapHandlerSS]
+ mov ss, cx
+ call Bs2Thunk_lm32_lm64
+ BITS 64
+ ASSERT_SIMPLE ax, bx, je, "Wrong SS value for ring-0-32 -> ring-0-64 int3, w/ 16-bit stack."
+
+%endif ; TMPL_64BIT
+
+
+ ;
+ ; Restore the descriptor and make sure it works.
+ ;
+%ifdef TMPL_LM64
+ pop qword [bs2Idt_BP + 8 xWrtRIP]
+ pop qword [bs2Idt_BP xWrtRIP]
+%else
+ pop dword [bs2Idt_BP + 4 xWrtRIP]
+ pop dword [bs2Idt_BP xWrtRIP]
+%endif
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ popf
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', Stack switching', 0
+ENDPROC TMPL_NM(TestStack)
+
+
+
+;;
+; Loads MY_R0_CS into CS.
+;
+; @uses stack, cs, flags
+;
+BEGINPROC TMPL_NM(TestLoadMyCS)
+ push 0
+ push xAX
+
+ ; Make it a far return with MY_R0_CS + CPL.
+ mov xAX, [xSP + xCB*2]
+ mov [xSP + xCB*1], xAX
+ mov xAX, ss
+%ifdef TMPL_64BIT
+ sub xAX, BS2_SEL_GRP_SS64 - BS2_SEL_GRP_CS64
+%elifdef TMPL_32BIT
+ sub xAX, BS2_SEL_GRP_SS32 - BS2_SEL_GRP_CS32
+%elifdef TMPL_16BIT
+ sub xAX, BS2_SEL_GRP_SS16 - BS2_SEL_GRP_CS16
+%else
+ TMPL_xxBIT is not defined
+%endif
+ mov [xSP + xCB*2], xAX
+
+ pop xAX
+ retf
+ENDPROC TMPL_NM(TestLoadMyCS)
+
+
+;;
+; Checks our understanding of how conforming segments are handled.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestConforming)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+ ;
+ ; Check the int3.
+ ;
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3 ; check that int3 works before we start messing around...
+
+ mov xDI, xSP ; save the stack pointer.
+ sub xSP, 20h
+
+ ;
+ ; In this test we will do various experiments with code using a
+ ; conforming CS. The main purpose is to check that CS.RPL is always the
+ ; same as CPL, despite earlier beliefs to the contrary. Because if it
+ ; is different, iret cannot dermine the CPL to return to among other
+ ; interesting problems.
+ ;
+ mov ecx, [bs2Gdt + MY_R0_CS xWrtRIP]
+ mov [bs2GdtSpare0 xWrtRIP], ecx
+ mov ecx, [bs2Gdt + MY_R0_CS + 4 xWrtRIP]
+ mov [bs2GdtSpare0 + 4 xWrtRIP], ecx ; GdtSpare0 is a copy of the CS descriptor now.
+ and byte [bs2GdtSpare0 + 5], 0x90 ; DPL = 0
+ or byte [bs2GdtSpare0 + 5], X86_SEL_TYPE_ER_CONF_ACC
+
+%assign uCurRing 0
+%rep 4
+ ; Far jumps.
+ %assign uSpecifiedRpl 0
+ %rep 4
+ call TMPL_NM_CMN(Bs2ToRing %+ uCurRing)
+ lea xAX, [.far_jmp_target_ %+ uSpecifiedRpl %+ uCurRing]
+ %ifdef TMPL_64BIT ; AMD doesn't have an jmp far m16:m64 instruction, it ignores REX.W apparently. Intel does though.
+ ; Tested on: Bulldozer
+ mov dword [xSP + 4], BS2_SEL_SPARE0 | uSpecifiedRpl
+ mov [xSP], eax
+ jmp far dword [xSP]
+ %else
+ mov dword [xSP + xCB], BS2_SEL_SPARE0 | uSpecifiedRpl
+ mov [xSP], xAX
+ jmp far xPRE [xSP]
+ %endif
+.far_jmp_target_ %+ uSpecifiedRpl %+ uCurRing:
+ mov bx, cs
+ call TMPL_NM(TestLoadMyCS)
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_CUR_CS_VALUE_IN_BX BS2_SEL_SPARE0 | uCurRing
+ %assign uSpecifiedRpl uSpecifiedRpl + 1
+ %endrep
+
+ ; Far calls.
+ %assign uSpecifiedRpl 0
+ %rep 4
+ call TMPL_NM_CMN(Bs2ToRing %+ uCurRing)
+ mov xSI, xSP
+ lea xAX, [.far_call_target_ %+ uSpecifiedRpl %+ uCurRing]
+ %ifdef TMPL_64BIT ; AMD doesn't have an call far m16:m64 instruction, it ignores REX.W apparently. Intel does though.
+ ; Tested on: Bulldozer
+ mov dword [xSP + 4], BS2_SEL_SPARE0 | uSpecifiedRpl
+ mov [xSP], eax
+ call far dword [xSP]
+ %else
+ mov dword [xSP + xCB], BS2_SEL_SPARE0 | uSpecifiedRpl
+ mov [xSP], xAX
+ call far xPRE [xSP]
+ %endif
+.far_call_target_ %+ uSpecifiedRpl %+ uCurRing:
+ mov bx, cs
+ %ifdef TMPL_64BIT
+ add xSP, 4 * 2
+ %else
+ add xSP, xCB * 2
+ %endif
+ call TMPL_NM(TestLoadMyCS)
+ call TMPL_NM_CMN(Bs2ToRing0)
+ ASSERT_CUR_CS_VALUE_IN_BX BS2_SEL_SPARE0 | uCurRing
+ %assign uSpecifiedRpl uSpecifiedRpl + 1
+ %endrep
+
+ %assign uCurRing uCurRing + 1
+%endrep
+
+ ;
+ ; While at it, lets check something about RPL and non-conforming
+ ; segments. The check when loading is supposed to be RPL >= DPL,
+ ; except for when loading SS, where RPL = DPL = CPL.
+ ;
+
+ ; ring-0
+ mov dx, MY_R0_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R0_DS | 0
+ mov dx, MY_R0_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+
+ ; ring-0 - Lower DPL isn't an issue, only RPL vs DPL.
+ mov dx, MY_R1_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R1_DS | 0
+ mov dx, MY_R1_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R1_DS | 1
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+
+ mov dx, MY_R2_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 0
+ mov dx, MY_R2_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 2
+ mov dx, MY_R2_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+
+ mov dx, MY_R3_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 0
+ mov dx, MY_R3_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 1
+ mov dx, MY_R3_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 2
+ mov dx, MY_R3_DS | 3
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 3
+
+ ; ring-0 - What works above doesn't work with ss.
+ mov dx, MY_R1_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov ss, dx
+ mov dx, MY_R1_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov ss, dx
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov ss, dx
+ mov dx, MY_R2_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov ss, dx
+ mov dx, MY_R3_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+
+
+ ; ring-1
+ call TMPL_NM_CMN(Bs2ToRing1)
+
+ mov dx, MY_R1_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R1_DS | 0
+ mov dx, MY_R1_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R1_DS | 1
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+ mov dx, MY_R1_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+
+ mov dx, MY_R0_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+
+ ; ring-1 - Lower DPL isn't an issue, only RPL vs DPL.
+ mov dx, MY_R2_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 0
+ mov dx, MY_R2_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 1
+ mov dx, MY_R2_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 2
+ mov dx, MY_R2_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+
+ mov dx, MY_R3_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 0
+ mov dx, MY_R3_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 1
+ mov dx, MY_R3_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 2
+ mov dx, MY_R3_DS | 3
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 3
+
+ ; ring-1 - What works above doesn't work with ss.
+ mov dx, MY_R1_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov ss, dx
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov ss, dx
+ mov dx, MY_R2_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov ss, dx
+ mov dx, MY_R3_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+
+
+ ; ring-2
+ call TMPL_NM_CMN(Bs2ToRing2)
+
+ mov dx, MY_R2_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 0
+ mov dx, MY_R2_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 1
+ mov dx, MY_R2_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R2_DS | 2
+ mov dx, MY_R2_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+
+ mov dx, MY_R0_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R1_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+
+ ; ring-2 - Lower DPL isn't an issue, only RPL vs DPL.
+ mov dx, MY_R3_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 0
+ mov dx, MY_R3_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 1
+ mov dx, MY_R3_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 2
+ mov dx, MY_R3_DS | 3
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 3
+
+ ; ring-2 - What works above doesn't work with ss.
+ mov dx, MY_R2_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov ss, dx
+ mov dx, MY_R2_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov ss, dx
+ mov dx, MY_R3_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+
+
+ ; ring-3
+ call TMPL_NM_CMN(Bs2ToRing3)
+
+ mov dx, MY_R3_DS | 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 0
+ mov dx, MY_R3_DS | 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 1
+ mov dx, MY_R3_DS | 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 2
+ mov dx, MY_R3_DS | 3
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, MY_R3_DS | 3
+
+ mov dx, MY_R0_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+ mov dx, MY_R0_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R0_DS, mov fs, dx
+
+ mov dx, MY_R1_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+ mov dx, MY_R1_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R1_DS, mov fs, dx
+
+ mov dx, MY_R2_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+ mov dx, MY_R2_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+ mov dx, MY_R2_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+ mov dx, MY_R2_DS | 3
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R2_DS, mov fs, dx
+
+ ; ring-0 - What works above doesn't work with ss.
+ mov dx, MY_R3_DS | 0
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 1
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+ mov dx, MY_R3_DS | 2
+ BS2_TRAP_INSTR X86_XCPT_GP, MY_R3_DS, mov ss, dx
+
+ call TMPL_NM_CMN(Bs2ToRing0)
+
+
+ ;
+ ; One more odd thing, NULL selectors and RPL.
+ ;
+ pushf
+ cli
+
+%assign uCurRing 0
+%rep 4
+ ; Null sectors.
+ call TMPL_NM_CMN(Bs2ToRing %+ uCurRing)
+ mov si, ss
+
+ mov dx, 0
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, 0
+ %if MY_IS_64BIT == 0 || uCurRing != 0
+ %ifdef TMPL_64BIT ; AMD is doing something inconsistent.
+ %if uCurRing != 3
+ test byte [g_fCpuAmd], 1
+ jz .null_0_not_amd_ %+ uCurRing
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 0
+ jmp .null_0_next_ %+ uCurRing
+.null_0_not_amd_ %+ uCurRing:
+ %endif
+ %endif
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, mov ss, dx
+.null_0_next_ %+ uCurRing:
+ %else
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 0
+ %endif
+ mov ss, si
+
+ mov dx, 1
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, 1
+ %if MY_IS_64BIT == 0 || uCurRing != 1
+ %ifdef TMPL_64BIT ; AMD is doing something inconsistent.
+ %if uCurRing != 3
+ test byte [g_fCpuAmd], 1
+ jz .null_1_not_amd_ %+ uCurRing
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 1
+ jmp .null_1_next_ %+ uCurRing
+.null_1_not_amd_ %+ uCurRing:
+ %endif
+ %endif
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, mov ss, dx
+.null_1_next_ %+ uCurRing:
+ %else
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 1
+ %endif
+ mov ss, si
+
+ mov dx, 2
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, 2
+ %if MY_IS_64BIT == 0 || uCurRing != 2
+ %ifdef TMPL_64BIT ; AMD is doing something inconsistent.
+ %if uCurRing != 3
+ test byte [g_fCpuAmd], 1
+ jz .null_2_not_amd_ %+ uCurRing
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 2
+ jmp .null_2_next_ %+ uCurRing
+.null_2_not_amd_ %+ uCurRing:
+ %endif
+ %endif
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, mov ss, dx
+.null_2_next_ %+ uCurRing:
+ %else
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 2
+ %endif
+ mov ss, si
+
+ mov dx, 3
+ mov fs, dx
+ ASSERT_CUR_SREG_VALUE fs, 3
+ %ifdef TMPL_64BIT ; AMD is doing something inconsistent.
+ %if uCurRing != 3
+ test byte [g_fCpuAmd], 1
+ jz .null_3_not_amd_ %+ uCurRing
+ mov ss, dx
+ ASSERT_CUR_SREG_VALUE ss, 3
+ jmp .null_3_next_ %+ uCurRing
+.null_3_not_amd_ %+ uCurRing:
+ %endif
+ %endif
+ BS2_TRAP_INSTR X86_XCPT_GP, 0, mov ss, dx
+.null_3_next_ %+ uCurRing:
+ mov ss, si
+
+ %assign uCurRing uCurRing + 1
+%endrep
+ call TMPL_NM_CMN(Bs2ToRing0)
+
+ ; Restore the selectors.
+ mov dx, MY_R0_DS
+ mov ds, dx
+ mov es, dx
+ mov fs, dx
+ mov gs, dx
+ popf
+
+
+ ;
+ ; Restore the descriptor and make sure it works.
+ ;
+ mov xSP, xDI ; restore the stack pointer.
+ BS2_TRAP_INSTR X86_XCPT_BP, 0, int3
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', Conforming CS, ++', 0
+ENDPROC TMPL_NM(TestConforming)
+
+
+
+;;
+; Returning from interrupt/trap/whatever handlers.
+;
+; @uses No registers, but BS2_SEL_SPARE0 is trashed.
+;
+BEGINPROC TMPL_NM(TestReturn)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+ sub xSP, 80h ; iret stack frame space.
+ mov xSI, xSP ; Save the stack register.
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+%ifdef TMPL_64BIT
+ pushfq
+ pop rdi ; rdi contains good flags register value.
+
+ ;
+ ; 64-bit mode: IRETQ unconditional pop of SS:RSP.
+ ;
+ mov qword [rsp + 20h], MY_R0_SS
+ mov [rsp + 18h], rsp
+ mov [rsp + 10h], rdi
+ mov qword [rsp + 08h], MY_R0_CS
+ lea rax, [.resume1 wrt rip]
+ mov [rsp + 00h], rax
+ iretq
+
+.resume1:
+ pushfq
+ pop rbx
+ ASSERT_SIMPLE rsp, rsi, je, "Wrong RSP after IRETQ."
+ mov rsp, rsi
+ ASSERT_SIMPLE rbx, rdi, je, "Wrong flags after IRETQ."
+ mov ax, ss
+ ASSERT_SIMPLE ax, MY_R0_SS, je, "Wrong SS after IRETQ."
+ mov ax, cs
+ ASSERT_SIMPLE ax, MY_R0_CS, je, "Wrong CS after IRETQ."
+
+ ; 64-bit mode: The NT flag causes #GP(0)
+ mov qword [rsp + 20h], MY_R0_SS
+ lea rax, [rsp - 100h]
+ mov [rsp + 18h], rax
+ mov [rsp + 10h], rdi
+ mov qword [rsp + 08h], MY_R0_CS
+ lea rax, [.resume2 wrt rip]
+ mov [rsp + 00h], rax
+ push rdi
+ or dword [rsp], X86_EFL_NT
+ popfq
+ BS2_TRAP_BRANCH_INSTR X86_XCPT_GP, 0, .resume2, iretq
+ pushfq
+ pop rbx
+ push rdi
+ popfq
+ ASSERT_SIMPLE rsp, rsi, je, "Wrong RSP after IRETQ."
+ mov rsp, rsi
+ mov rax, rdi
+ or rax, X86_EFL_NT
+ ASSERT_SIMPLE rbx, rax, je, "Wrong flags after IRETQ GP(0)-NT."
+ mov ax, ss
+ ASSERT_SIMPLE ax, MY_R0_SS, je, "Wrong SS after IRETQ."
+ mov ax, cs
+ ASSERT_SIMPLE ax, MY_R0_CS, je, "Wrong CS after IRETQ."
+
+ ; 64-bit mode: The VM flag is disregarded.
+ mov qword [rsp + 20h], MY_R0_SS
+ lea rax, [rsp - 88h]
+ mov [rsp + 18h], rax
+ mov [rsp + 10h], rdi
+ or dword [rsp + 10h], X86_EFL_VM
+ mov qword [rsp + 08h], MY_R0_CS
+ lea rax, [.resume3 wrt rip]
+ mov [rsp + 00h], rax
+ iretq
+.resume3:
+ pushfq
+ pop rbx
+ add rsp, 88h
+ ASSERT_SIMPLE rsp, rsi, je, "Wrong RSP after IRETQ."
+ mov rsp, rsi
+ mov rax, rdi
+ ASSERT_SIMPLE rbx, rax, je, "Wrong flags after IRETQ GP(0)-NT."
+ mov ax, ss
+ ASSERT_SIMPLE ax, MY_R0_SS, je, "Wrong SS after IRETQ."
+ mov ax, cs
+ ASSERT_SIMPLE ax, MY_R0_CS, je, "Wrong CS after IRETQ."
+
+ ;
+ ; 64-bit mode: IRETD unconditionally pops SS:ESP as well.
+ ;
+ mov dword [rsp + 10h], MY_R0_SS
+ lea eax, [esp - 18h]
+ mov [rsp + 0ch], eax
+ mov [rsp + 08h], edi
+ mov dword [rsp + 04h], MY_R0_CS
+ lea eax, [.resume20 wrt rip]
+ mov [rsp + 00h], eax
+ iretd
+.resume20:
+ pushfq
+ pop rbx
+ add rsp, 18h
+ ASSERT_SIMPLE rsp, rsi, je, "Wrong RSP after IRETD."
+ mov rsp, rsi
+ ASSERT_SIMPLE rbx, rdi, je, "Wrong flags after IRETD."
+ mov ax, ss
+ ASSERT_SIMPLE ax, MY_R0_SS, je, "Wrong SS after IRETD."
+ mov ax, cs
+ ASSERT_SIMPLE ax, MY_R0_CS, je, "Wrong CS after IRETD."
+
+ ;
+ ; 64-bit mode: IRET unconditionally pops SS:SP as well.
+ ;
+ mov word [rsp + 08h], MY_R0_SS
+ lea eax, [esp - 1ah]
+ mov [rsp + 06h], ax
+ mov [rsp + 04h], di
+ mov word [rsp + 02h], MY_R0_CS
+ mov word [rsp + 00h], .resume30
+ o16 iret
+BEGINCODELOW
+.resume30:
+ jmp .high1
+BEGINCODEHIGH
+.high1:
+ pushfq
+ pop rbx
+ add rsp, 1ah
+ ASSERT_SIMPLE rsp, rsi, je, "Wrong RSP after IRET."
+ mov rsp, rsi
+ ASSERT_SIMPLE rbx, rdi, je, "Wrong flags after IRET."
+ mov ax, ss
+ ASSERT_SIMPLE ax, MY_R0_SS, je, "Wrong SS after IRET."
+ mov ax, cs
+ ASSERT_SIMPLE ax, MY_R0_CS, je, "Wrong CS after IRET."
+
+
+%elifdef TMPL_32BIT
+ ; later...
+%endif
+
+ ;
+ ; Returning to 16-bit code, what happens to upper ESP bits?
+ ;
+ cli
+ mov xBX, xSP ; save the current stack address
+
+ mov sAX, BS2_SEL_R3_SS16 | 3
+ push sAX ; Return SS
+ movzx edi, bx
+ or edi, 0xdead0000
+ push sDI ; Return sSP
+%ifdef TMPL_64BIT
+ pushfq
+%else
+ pushfd
+%endif
+ mov sAX, BS2_SEL_R3_CS16 | 3
+ push sAX ; Return CS
+ lea sAX, [.resume100 xWrtRIP]
+ push sAX ; Return sIP
+%ifdef TMPL_64BIT
+ iretq
+%else
+ iretd
+%endif
+
+BEGINCODELOW
+BITS 16
+.resume100:
+ xchg ebx, esp
+ call Bs2ToRing0_p16
+ call TMPL_NM(Bs2Thunk_p16)
+BITS TMPL_BITS
+ jmp .high100
+BEGINCODEHIGH
+.high100:
+ and edi, 0ffffh
+ ASSERT_SIMPLE ebx, edi, je, "IRET to 16-bit didn't restore ESP as expected [#1]."
+
+%ifndef TMPL_16BIT
+ ;
+ ; Take two on 16-bit return, does the high word of ESP leak?
+ ;
+ cli
+ mov sBX, sSP ; save the current stack address
+ mov xSP, BS2_MUCK_ABOUT_BASE + 1000h
+
+ mov sAX, BS2_SEL_R3_SS16 | 3
+ push sAX ; Return SS
+ mov sDI, sBX
+ push sDI ; Return sSP
+ %ifdef TMPL_64BIT
+ pushfq
+ %else
+ pushfd
+ %endif
+ mov sAX, BS2_SEL_R3_CS16 | 3
+ push sAX ; Return CS
+ lea sAX, [.resume101 xWrtRIP]
+ push sAX ; Return sIP
+ %ifdef TMPL_64BIT
+ iretq
+ %else
+ iretd
+ %endif
+
+BEGINCODELOW
+BITS 16
+.resume101:
+ xchg ebx, esp
+ call Bs2ToRing0_p16
+ call TMPL_NM(Bs2Thunk_p16)
+BITS TMPL_BITS
+ jmp .high101
+BEGINCODEHIGH
+.high101:
+ or edi, (BS2_MUCK_ABOUT_BASE + 1000h) & 0ffff0000h
+ ASSERT_SIMPLE ebx, edi, je, "IRET to 16-bit didn't restore ESP as expected [#2]."
+%endif ; Not 16-bit.
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ mov xSP, xSI
+ add xSP, 80h
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', IRET', 0
+ENDPROC TMPL_NM(TestReturn)
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoTestsForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Test exception handler basics using INT3 and #BP.
+ ;
+
+ call TMPL_NM(TestGateType)
+ call TMPL_NM(TestCodeSelector)
+ call TMPL_NM(TestCheckOrderCsTypeVsIdteType)
+ call TMPL_NM(TestStack)
+ call TMPL_NM(TestConforming)
+ call TMPL_NM(TestReturn)
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call Bs2DisableNX_r86
+
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoTestsForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1.asm
new file mode 100644
index 00000000..f16566b5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-1.asm
@@ -0,0 +1,138 @@
+; $Id: bootsector2-cpu-xcpt-1.asm $
+;; @file
+; Bootsector test for basic exception stuff.
+;
+; Recommended (but not necessary):
+; VBoxManage setextradata bs-cpu-xcpt-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;; Base address at which we can start testing page tables and page directories.
+%define TST_SCRATCH_PD_BASE BS2_MUCK_ABOUT_BASE
+;; Base address at which we can start testing the page pointer table.
+%define TST_SCRATCH_PDPT_BASE (1 << X86_PDPT_SHIFT)
+;; Base address at which we can start testing the page map level 4.
+%define TST_SCRATCH_PML4_BASE ((1 << X86_PML4_SHIFT) + TST_SCRATCH_PD_BASE)
+
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_WITH_TRAPS
+ %define BS2_INC_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP16
+ %define BS2_INC_PP32
+ %define BS2_INC_PAE16
+ %define BS2_INC_PAE32
+ %define BS2_INC_LM16
+ %define BS2_INC_LM32
+ %define BS2_INC_LM64
+ %define BS2_WITH_TRAPRECS
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The main() function.
+;
+BEGINPROC main
+ BITS 16
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+
+
+ ;
+ ; Execute the tests
+ ;
+%if 1
+ call NAME(DoTestsForMode_rm_pe32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pp32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pae32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_lm64)
+%endif
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ ret
+
+.s_szTstName:
+ db 'tstCpuXcpt1', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_PE32
+%include "bootsector2-cpu-xcpt-1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-xcpt-1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-xcpt-1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-xcpt-1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2-template.mac
new file mode 100644
index 00000000..720ec9a1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2-template.mac
@@ -0,0 +1,511 @@
+; $Id: bootsector2-cpu-xcpt-2-template.mac $
+;; @file
+; Bootsector test for debug exceptions - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+;;
+; Some 32/64 macros.
+;
+%if TMPL_BITS == 32
+ %define bs2Idt_BP bs2Idt32bit_BP
+ %define MY_R0_CS BS2_SEL_CS32
+ %define MY_R1_CS BS2_SEL_R1_CS32
+ %define MY_R2_CS BS2_SEL_R2_CS32
+ %define MY_R3_CS BS2_SEL_R3_CS32
+
+ %define MY_R0_DS BS2_SEL_DS32
+ %define MY_R1_DS BS2_SEL_R1_DS32
+ %define MY_R2_DS BS2_SEL_R2_DS32
+ %define MY_R3_DS BS2_SEL_R3_DS32
+
+ %define MY_R0_SS BS2_SEL_SS32
+ %define MY_R1_SS BS2_SEL_R1_SS32
+ %define MY_R2_SS BS2_SEL_R2_SS32
+ %define MY_R3_SS BS2_SEL_R3_SS32
+
+%else
+ %define bs2Idt_BP bs2Idt64bit_BP
+ %define MY_R0_CS BS2_SEL_CS64
+ %define MY_R1_CS BS2_SEL_R1_CS64
+ %define MY_R2_CS BS2_SEL_R2_CS64
+ %define MY_R3_CS BS2_SEL_R3_CS64
+
+ %define MY_R0_DS BS2_SEL_DS64
+ %define MY_R1_DS BS2_SEL_R1_DS64
+ %define MY_R2_DS BS2_SEL_R2_DS64
+ %define MY_R3_DS BS2_SEL_R3_DS64
+
+ %define MY_R0_SS BS2_SEL_SS64
+ %define MY_R1_SS BS2_SEL_R1_SS64
+ %define MY_R2_SS BS2_SEL_R2_SS64
+ %define MY_R3_SS BS2_SEL_R3_SS64
+%endif
+
+%ifdef TMPL_64BIT
+ %assign MY_IS_64BIT 1
+%else
+ %assign MY_IS_64BIT 0
+%endif
+
+;; Uncomment this to do lots more iterations (takes time!).
+%define QUICK_TEST
+
+
+;*******************************************************************************
+;* Global Variables *
+;*******************************************************************************
+%ifndef CPU_XCPT_1_GLOBALS
+ %define CPU_XCPT_1_GLOBALS
+
+;;
+; Asserts a test.
+;
+; @param %1 First cmp operand.
+; @param %2 First cmp operand.
+; @param %3 Which kind of conditional jump to make
+; @param %4 The message to print (format string, no arguments please).
+;
+ %macro ASSERT_SIMPLE 4
+ cmp %1, %2
+ %3 %%.ok
+ cli ; raw-mode hack
+ push dword __LINE__
+ %ifdef TMPL_16BIT
+ push ds
+ %endif
+ push %%.s_szMsg
+ call TMPL_NM_CMN(TestFailedF)
+ add xSP, sCB*2
+ ;hlt
+ sti
+ jmp %%.ok
+ %%.s_szMsg: db %4, " (0x%RX32)", 0
+ %%.ok:
+ %endmacro
+
+%endif
+
+
+;;
+; Disable the breakpoints as well as check RA1 bits.
+;
+; @changes DRx
+;
+BEGINPROC TMPL_NM(DisableBps)
+ push sAX
+ push sBX
+ sPUSHF
+
+ xor eax, eax
+ mov dr7, sAX
+ mov dr6, sAX
+ mov dr0, sAX
+ mov dr1, sAX
+ mov dr2, sAX
+ mov dr3, sAX
+
+ mov sAX, dr6
+ mov ebx, X86_DR6_RA1_MASK
+ ASSERT_SIMPLE sAX, xBX, je, "Wrong DR6 value (RA1)."
+ mov sAX, dr7
+ mov ebx, X86_DR7_RA1_MASK
+ ASSERT_SIMPLE sAX, sBX, je, "Wrong DR7 value (RA1)."
+
+ sPOPF
+ pop sBX
+ pop sAX
+ ret
+ENDPROC TMPL_NM(DisableBps)
+
+
+;;
+; Checks different gate types.
+;
+BEGINPROC TMPL_NM(TestStepping)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Step one instruction a lot of times to catch DR6 mismanagement.
+ ;
+%ifdef QUICK_TEST
+ mov ecx, 0x1000
+%else
+ mov ecx, 0x80000
+%endif
+.the_1st_loop:
+
+ mov eax, X86_DR6_INIT_VAL
+ mov dr6, sAX
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+ sPUSHF
+ or word [xSP], X86_EFL_TF
+ sPOPF
+ xchg ebx, eax
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0xaabbccdd, je, "xchg wasn't executed (eax)."
+ ASSERT_SIMPLE ebx, 0x12345678, je, "xchg wasn't executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_BS), je, "Wrong DR6 value."
+
+ dec ecx
+ jnz .the_1st_loop
+
+ ;
+ ; Check that certain bits in DR6 is preserved and others not.
+ ;
+%ifdef QUICK_TEST
+ mov ecx, 0x200
+%else
+ mov ecx, 0x20000
+%endif
+.the_2nd_loop:
+ mov eax, X86_DR6_INIT_VAL | X86_DR6_B0 | X86_DR6_B1 | X86_DR6_B2 | X86_DR6_B3 | X86_DR6_BT | X86_DR6_BD
+ mov dr6, sAX
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+ sPUSHF
+ or word [xSP], X86_EFL_TF
+ sPOPF
+ xchg ebx, eax
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0xaabbccdd, je, "xchg wasn't executed (eax)."
+ ASSERT_SIMPLE ebx, 0x12345678, je, "xchg wasn't executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_BS | X86_DR6_INIT_VAL | X86_DR6_BT | X86_DR6_BD), je, "Wrong DR6 value."
+
+ dec ecx
+ jnz .the_2nd_loop
+
+ ;
+ ; Done.
+ ;
+ cli ; raw-mode hack
+ call TMPL_NM_CMN(TestSubDone)
+ sti
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', EFLAGS.TF stepping', 0
+ENDPROC TMPL_NM(TestGateType)
+
+
+;;
+; Check execution breakpoint.
+;
+BEGINPROC TMPL_NM(TestBpExec)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Arm all 4 breakpoints and check DR6 management.
+ ;
+%ifdef QUICK_TEST
+ mov ecx, 0x1000
+%else
+ mov ecx, 0x80000
+%endif
+ lea sAX, [.bp_dr0 xWrtRIP]
+ mov dr0, sAX
+ lea sAX, [.bp_dr1 xWrtRIP]
+ mov dr1, sAX
+ lea sAX, [.bp_dr2 xWrtRIP]
+ mov dr2, sAX
+ lea sAX, [.bp_dr3 xWrtRIP]
+ mov dr3, sAX
+ mov eax, X86_DR7_RA1_MASK | X86_DR7_G0 | X86_DR7_G1 | X86_DR7_G2 | X86_DR7_G3 | X86_DR7_GE
+ mov dr7, sAX
+
+.the_loop:
+ mov eax, X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BT | X86_DR6_BD
+ mov dr6, sAX
+
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+.bp_dr0:
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, xchg ebx, eax
+ ASSERT_SIMPLE eax, 0x12345678, je, "xchg was executed (eax)."
+ ASSERT_SIMPLE ebx, 0xaabbccdd, je, "xchg was executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BT | X86_DR6_BD | X86_DR6_B0), je, "Wrong DR6 value (dr0)."
+
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+.bp_dr1:
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, xchg ebx, eax
+ ASSERT_SIMPLE eax, 0x12345678, je, "xchg was executed (eax)."
+ ASSERT_SIMPLE ebx, 0xaabbccdd, je, "xchg was executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BT | X86_DR6_BD | X86_DR6_B1), je, "Wrong DR6 value (dr1)."
+
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+.bp_dr2:
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, xchg ebx, eax
+ ASSERT_SIMPLE eax, 0x12345678, je, "xchg was executed (eax)."
+ ASSERT_SIMPLE ebx, 0xaabbccdd, je, "xchg was executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BT | X86_DR6_BD | X86_DR6_B2), je, "Wrong DR6 value (dr2)."
+
+ mov eax, 0x12345678
+ mov ebx, 0xaabbccdd
+.bp_dr3:
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, xchg ebx, eax
+ ASSERT_SIMPLE eax, 0x12345678, je, "xchg was executed (eax)."
+ ASSERT_SIMPLE ebx, 0xaabbccdd, je, "xchg was executed (ebx)."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BT | X86_DR6_BD | X86_DR6_B3), je, "Wrong DR6 value (dr3)."
+
+ dec ecx
+ jnz .the_loop
+
+ ;
+ ; Touch the code, making sure the BPs don't trigger on data access.
+ ;
+ mov al, [.bp_dr0 xWrtRIP]
+ mov [.bp_dr0 xWrtRIP], al
+ mov al, [.bp_dr1 xWrtRIP]
+ mov [.bp_dr1 xWrtRIP], al
+ mov al, [.bp_dr2 xWrtRIP]
+ mov [.bp_dr2 xWrtRIP], al
+ mov al, [.bp_dr3 xWrtRIP]
+ mov [.bp_dr3 xWrtRIP], al
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM(DisableBps)
+ cli ; raw-mode hack
+ call TMPL_NM_CMN(TestSubDone)
+ sti
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', Exec BP', 0
+ENDPROC TMPL_NM(TestBpExec)
+
+
+;;
+; Check I/O breakpoints.
+;
+BEGINPROC TMPL_NM(TestBpIo)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ mov xAX, .s_szSubTestName
+ call TMPL_NM_CMN(TestSub)
+
+
+ ;
+ ; Arm all 4 breakpoints and check range handling and such.
+ ;
+ mov sAX, cr4
+ or sAX, X86_CR4_DE
+ mov cr4, sAX
+
+%ifdef QUICK_TEST
+ mov ecx, 1000
+%else
+ mov ecx, 4096
+%endif
+ mov sAX, 84h
+ mov dr0, sAX
+ mov sAX, 85h
+ mov dr1, sAX
+ mov sAX, 86h
+ mov dr2, sAX
+ mov sAX, 8ch
+ mov dr3, sAX
+ mov eax, X86_DR7_RA1_MASK | X86_DR7_LE | X86_DR7_GE \
+ | X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_IO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE) \
+ | X86_DR7_L1 | X86_DR7_G1 | X86_DR7_RW(1, X86_DR7_RW_IO) | X86_DR7_LEN(1, X86_DR7_LEN_WORD) \
+ | X86_DR7_L2 | X86_DR7_G2 | X86_DR7_RW(2, X86_DR7_RW_IO) | X86_DR7_LEN(2, X86_DR7_LEN_DWORD) \
+ | X86_DR7_L3 | X86_DR7_G3 | X86_DR7_RW(3, X86_DR7_RW_IO) | X86_DR7_LEN(3, X86_DR7_LEN_DWORD)
+ mov dr7, sAX
+
+.the_loop:
+ mov eax, X86_DR6_INIT_VAL
+ mov dr6, sAX
+
+ mov eax, 0x12345678
+ in eax, 84h
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0x12345678, jne, "in was not executed."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_B0), je, "Wrong DR6 value (dr0)."
+
+ mov ebx, 0x12345678
+ in eax, 85h
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0x12345678, jne, "in was not executed."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_B1), je, "Wrong DR6 value (dr1)."
+
+ mov eax, 0x12345678
+ in eax, 86h
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0x12345678, jne, "in was not executed."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_B2), je, "Wrong DR6 value (dr2)."
+
+ mov eax, 0x12345678
+ in eax, 8ch
+ BS2_TRAP_INSTR X86_XCPT_DB, 0, nop
+ ASSERT_SIMPLE eax, 0x12345678, jne, "in was not executed."
+ mov sAX, dr6
+ ASSERT_SIMPLE eax, (X86_DR6_INIT_VAL | X86_DR6_B3), je, "Wrong DR6 value (dr3)."
+
+ dec ecx
+ jnz .the_loop
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM(DisableBps)
+ cli ; raw-mode hack
+ call TMPL_NM_CMN(TestSubDone)
+ sti
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szSubTestName:
+ db TMPL_MODE_STR, ', I/O BP', 0
+ENDPROC TMPL_NM(TestBpIo)
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoTestsForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ pushf
+ sti ; raw-mode hacks
+
+ ;
+ ; Do the testing.
+ ;
+
+ call TMPL_NM(TestStepping)
+ call TMPL_NM(TestBpExec)
+ call TMPL_NM(TestBpIo)
+
+ ;
+ ; Back to real mode.
+ ;
+ popf
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call Bs2DisableNX_r86
+
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoTestsForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2.asm
new file mode 100644
index 00000000..8943575e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-xcpt-2.asm
@@ -0,0 +1,129 @@
+; $Id: bootsector2-cpu-xcpt-2.asm $
+;; @file
+; Bootsector test for debug exceptions.
+;
+; Recommended (but not necessary):
+; VBoxManage setextradata bs-cpu-xcpt-2 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_WITH_TRAPS
+ %define BS2_INC_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP16
+ %define BS2_INC_PP32
+ %define BS2_INC_PAE16
+ %define BS2_INC_PAE32
+ %define BS2_INC_LM16
+ %define BS2_INC_LM32
+ %define BS2_INC_LM64
+ %define BS2_WITH_TRAPRECS
+ %define BS2_WITH_XCPT_DB_CLEARING_TF
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The main() function.
+;
+BEGINPROC main
+ BITS 16
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+ cli ; raw-mode hack
+
+
+ ;
+ ; Execute the tests
+ ;
+%if 1
+ call NAME(DoTestsForMode_rm_pe32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pp32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_pae32)
+%endif
+%if 1
+ call NAME(DoTestsForMode_rm_lm64)
+%endif
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ ret
+
+.s_szTstName:
+ db 'tstCpuXcpt2', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_PE32
+%include "bootsector2-cpu-xcpt-2-template.mac"
+%define TMPL_PP32
+%include "bootsector2-cpu-xcpt-2-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-cpu-xcpt-2-template.mac"
+%define TMPL_LM64
+%include "bootsector2-cpu-xcpt-2-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-first.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-first.mac
new file mode 100644
index 00000000..46ebf6d9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-first.mac
@@ -0,0 +1,84 @@
+; $Id: bootsector2-first.mac $
+;; @file
+; bootsector2 first include file - works around YASM / kBuild issues.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef ___bootsector2_first_mac
+%define ___bootsector2_first_mac
+
+;
+; Undefine thing that shouldn't be defined if we're targeting the
+; binary format directly. These macros comes from DEFS in Config.kmk.
+;
+%ifdef ASM_FORMAT_BIN
+ %undef RT_ARCH_AMD64
+ %undef RT_ARCH_X86
+
+ %undef RT_OS_DARWIN
+ %undef RT_OS_FREEBSD
+ %undef RT_OS_HAIKU
+ %undef RT_OS_LINUX
+ %undef RT_OS_NETBSD
+ %undef RT_OS_OPENBSD
+ %undef RT_OS_OS2
+ %undef RT_OS_WINDOWS
+
+ %undef __AMD64__
+ %undef __x86_64__
+ %undef __i386__
+ %undef __I386__
+ %undef __x86__
+ %undef __X86__
+
+ %undef __WIN__
+ %undef __WIN32__
+ %undef __WIN64__
+%endif
+
+
+;
+; Include standard includes.
+;
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+
+;
+; Open the code segment.
+;
+BEGINCODE
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-structures.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-structures.mac
new file mode 100644
index 00000000..cd92de13
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-structures.mac
@@ -0,0 +1,101 @@
+; $Id: bootsector2-structures.mac $
+;; @file
+; Common structures.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef ___bootsector2_structures_mac___
+%define ___bootsector2_structures_mac___
+
+
+;;
+; Registers. Used by traps and such.
+;
+struc BS2REGS
+ .rax resq 1
+ .rbx resq 1
+ .rcx resq 1
+ .rdx resq 1
+ .rdi resq 1
+ .rsi resq 1
+ .rbp resq 1
+ .rsp resq 1
+ .rip resq 1
+ .r8 resq 1
+ .r9 resq 1
+ .r10 resq 1
+ .r11 resq 1
+ .r12 resq 1
+ .r13 resq 1
+ .r14 resq 1
+ .r15 resq 1
+ .rflags resq 1
+ .cs resw 1
+ .ds resw 1
+ .es resw 1
+ .fs resw 1
+ .gs resw 1
+ .ss resw 1
+ .cBits resb 1
+ .pad resb 3
+ .cr0 resq 1
+ .cr2 resq 1
+ .cr3 resq 1
+ .cr4 resq 1
+ .cr8 resq 1
+ ;; @todo Add floating point registers when they are active.
+endstruc
+
+
+
+;;
+; Trap record.
+;
+struc BS2TRAPREC
+ ;; The trap location relative to the base address given at
+ ; registration time.
+ .offWhere resd 1
+ ;; What to add to .offWhere to calculate the resume address.
+ .offResumeAddend resb 1
+ ;; The trap number.
+ .u8TrapNo resb 1
+ ;; The error code if the trap takes one.
+ .u16ErrCd resw 1
+endstruc
+
+;; The size shift.
+%define BS2TRAPREC_SIZE_SHIFT 3
+
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-template-footer.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-template-footer.mac
new file mode 100644
index 00000000..3692a67a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-template-footer.mac
@@ -0,0 +1,116 @@
+; $Id: bootsector2-template-footer.mac $
+;; @file
+; bootsector2 footer for multi-mode code templates.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Undefine macros defined by the header.
+;
+; Note! The following is useful for verifying that all macros are included here:
+;
+; for i in `grep "%define" bootsector2-template-header.mac \
+; | sed -e 's/^ *%define *//' -e 's/^\([^() ]*\).*$/\1/' \
+; | sort -u`
+; do
+; if ! grep -wF "%undef $i" bootsector2-template-footer.mac; then
+; echo $i
+; fi
+; done
+;
+%undef TMPL_RM
+%undef TMPL_PE16
+%undef TMPL_PE32
+%undef TMPL_PEV86
+%undef TMPL_PP16
+%undef TMPL_PP32
+%undef TMPL_PPV86
+%undef TMPL_PAE16
+%undef TMPL_PAE32
+%undef TMPL_PAEV86
+%undef TMPL_LM16
+%undef TMPL_LM32
+%undef TMPL_LM64
+
+%undef TMPL_CMN_PE
+%undef TMPL_CMN_PP
+%undef TMPL_CMN_PAE
+%undef TMPL_CMN_LM
+%undef TMPL_CMN_V86
+
+%undef TMPL_CMN_P16
+%undef TMPL_CMN_P32
+%undef TMPL_CMN_P64
+%undef TMPL_CMN_R16
+%undef TMPL_CMN_R86
+
+%undef TMPL_NM
+%undef TMPL_NM_CMN
+%undef TMPL_MODE
+%undef TMPL_MODE_STR
+%undef TMPL_16BIT
+%undef TMPL_32BIT
+%undef TMPL_64BIT
+%undef TMPL_BITS
+%undef TMPL_PTR_DEF
+%undef TMPL_HAVE_BIOS
+%undef TMPL_BEGINCODE
+
+%undef xCB
+%undef xDEF
+%undef xRES
+%undef xPRE
+%undef xSP
+%undef xBP
+%undef xAX
+%undef xBX
+%undef xCX
+%undef xDX
+%undef xDI
+%undef xSI
+%undef xWrtRIP
+
+%undef sCB
+%undef sDEF
+%undef sRES
+%undef sPRE
+%undef sSP
+%undef sBP
+%undef sAX
+%undef sBX
+%undef sCX
+%undef sDX
+%undef sDI
+%undef sSI
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-template-header.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-template-header.mac
new file mode 100644
index 00000000..fe5eabb6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-template-header.mac
@@ -0,0 +1,803 @@
+; $Id: bootsector2-template-header.mac $
+;; @file
+; bootsector2 header for multi-mode code templates.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;
+; Check and expand the mode defines.
+; One of the following must be defined:
+; - TMPL_RM - real mode.
+; - TMPL_PE16 - 16-bit protected mode, unpaged.
+; - TMPL_PE32 - 32-bit protected mode, unpaged.
+; - TMPL_PEV86 - virtual 8086 mode under protected mode, unpaged.
+; - TMPL_PP16 - 16-bit protected mode, paged.
+; - TMPL_PP32 - 32-bit protected mode, paged.
+; - TMPL_PPV86 - virtual 8086 mode under protected mode, paged.
+; - TMPL_PAE16 - 16-bit protected mode with PAE (paged).
+; - TMPL_PAE32 - 16-bit protected mode with PAE (paged).
+; - TMPL_PAEV86- virtual 8086 mode under protected mode with PAE (paged).
+; - TMPL_LM16 - 16-bit long mode (paged).
+; - TMPL_LM32 - 32-bit long mode (paged).
+; - TMPL_LM64 - 64-bit long mode (paged).
+;
+; Derived indicators:
+; - TMPL_CMN_PE = TMPL_PE16 | TMPL_PE32 | TMPL_PEV86
+; - TMPL_CMN_PP = TMPL_PP16 | TMPL_PP32 | TMPL_PPV86
+; - TMPL_CMN_PAE = TMPL_PAE16 | TMPL_PAE32 | TMPL_PAEV86
+; - TMPL_CMN_LM = TMPL_LM16 | TMPL_LM32 | TMPL_LM64
+; - TMPL_CMN_V86 = TMPL_PEV86 | TMPL_PPV86 | TMPL_PAEV86
+; - TMPL_CMN_R86 = TMPL_CMN_V86 | TMPL_RM
+;
+%ifdef TMPL_RM
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_RM' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_RM' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_RM' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_RM' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_RM' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_RM' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_RM' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_RM' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_RM' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_RM' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_RM' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_RM' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _rm
+ %define TMPL_NM_CMN(Name) Name %+ _r86
+ %define TMPL_MODE_STR 'real mode'
+ %define TMPL_HAVE_BIOS
+ %define TMPL_CMN_R86
+%endif
+
+%ifdef TMPL_PE16
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PE16' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PE16' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_RM' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PE16' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PE16' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PE16' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PE16' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PE16' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PE32' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PE16' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PE16' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PE16' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PE
+ %define TMPL_CMN_P16
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _pe16
+ %define TMPL_NM_CMN(Name) Name %+ _p16
+ %define TMPL_MODE_STR '16-bit unpaged protected mode'
+%endif
+
+%ifdef TMPL_PE32
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PE32' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PE32' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PE32' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PE32' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PE32' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PE32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PE32' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PE32' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAE86
+ %error "Both 'TMPL_PE32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PE32' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PE32' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PE32' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PE
+ %define TMPL_CMN_P32
+ %define TMPL_32BIT
+ %define TMPL_BITS 32
+ %define TMPL_PTR_DEF dd
+ %define TMPL_NM(Name) Name %+ _pe32
+ %define TMPL_NM_CMN(Name) Name %+ _p32
+ %define TMPL_MODE_STR '32-bit unpaged protected mode'
+%endif
+
+%ifdef TMPL_PEV86
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PEV86' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PEV86' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PEV86' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PEV86' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PEV86' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PEV86' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PEV86' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PEV86' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAE86
+ %error "Both 'TMPL_PEV86' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PEV86' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PEV86' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PEV86' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PE
+ %define TMPL_CMN_V86
+ %define TMPL_CMN_R86
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _pev86
+ %define TMPL_NM_CMN(Name) Name %+ _r86
+ %define TMPL_MODE_STR 'v8086 unpaged protected mode'
+%endif
+
+%ifdef TMPL_PP16
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PP16' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PP16' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PP16' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PP16' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PP16' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PP32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PP16' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PP16' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PP16' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PP16' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PP16' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PP16' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PP
+ %define TMPL_CMN_P16
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _pp16
+ %define TMPL_NM_CMN(Name) Name %+ _p16
+ %define TMPL_MODE_STR '16-bit paged protected mode'
+%endif
+
+%ifdef TMPL_PP32
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PP32' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PP32' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PP32' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PP32' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PP32' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PP32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PP32' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PP32' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PP32' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PP32' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PP32' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PP32' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PP
+ %define TMPL_CMN_P32
+ %define TMPL_32BIT
+ %define TMPL_BITS 32
+ %define TMPL_PTR_DEF dd
+ %define TMPL_NM(Name) Name %+ _pp32
+ %define TMPL_NM_CMN(Name) Name %+ _p32
+ %define TMPL_MODE_STR '32-bit paged protected mode'
+%endif
+
+%ifdef TMPL_PPV86
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PPV86' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PPV86' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PPV86' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PPV86' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PPV86' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PPV86' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PPV86' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PPV86' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PPV86' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PPV86' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PPV86' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PPV86' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PP
+ %define TMPL_CMN_V86
+ %define TMPL_CMN_R86
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _ppv86
+ %define TMPL_NM_CMN(Name) Name %+ _r86
+ %define TMPL_MODE_STR 'v8086 paged protected mode'
+%endif
+
+%ifdef TMPL_PAE16
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PAE16' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PAE16' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PAE16' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PAE16' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PAE16' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PAE16' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PAE16' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_PAE16' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PAE16' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PAE16' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PAE16' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PAE16' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PAE
+ %define TMPL_16BIT
+ %define TMPL_CMN_P16
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _pae16
+ %define TMPL_NM_CMN(Name) Name %+ _p16
+ %define TMPL_MODE_STR '16-bit pae protected mode'
+%endif
+
+%ifdef TMPL_PAE32
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PAE32' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PAE32' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PAE32' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PAE32' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PAE32' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PAE32' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PAE32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PAE32' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PAE32' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PAE32' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PAE32' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PAE32' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PAE
+ %define TMPL_CMN_P32
+ %define TMPL_32BIT
+ %define TMPL_BITS 32
+ %define TMPL_PTR_DEF dd
+ %define TMPL_NM(Name) Name %+ _pae32
+ %define TMPL_NM_CMN(Name) Name %+ _p32
+ %define TMPL_MODE_STR '32-bit pae protected mode'
+%endif
+
+%ifdef TMPL_PAEV86
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_PAEV86' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_PAEV86' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_PAEV86' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_PAEV86' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_PAEV86' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_PAE
+ %define TMPL_CMN_V86
+ %define TMPL_CMN_R86
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _paev86
+ %define TMPL_NM_CMN(Name) Name %+ _r86
+ %define TMPL_MODE_STR 'v8086 pae protected mode'
+%endif
+
+%ifdef TMPL_LM16
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_LM16' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_LM16' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_LM16' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_LM16' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_LM16' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_LM16' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_LM16' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_LM16' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_LM16' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_LM16' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_LM16' and 'TMPL_LM32' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_LM16' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_LM
+ %define TMPL_CMN_P16
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_NM(Name) Name %+ _lm16
+ %define TMPL_NM_CMN(Name) Name %+ _p16
+ %define TMPL_MODE_STR '16-bit long mode'
+%endif
+
+%ifdef TMPL_LM32
+ %ifdef TMPL_RM
+ %error "Both 'TMPL_LM32' and 'TMPL_RM' are defined."
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_LM32' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_LM32' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_LM32' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_LM32' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_LM32' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_LM32' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_LM32' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_LM32' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_LM32' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_LM32' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM64
+ %error "Both 'TMPL_LM32' and 'TMPL_LM64' are defined."
+ %endif
+ %define TMPL_CMN_LM
+ %define TMPL_CMN_P32
+ %define TMPL_32BIT
+ %define TMPL_BITS 32
+ %define TMPL_PTR_DEF dd
+ %define TMPL_NM(Name) Name %+ _lm32
+ %define TMPL_NM_CMN(Name) Name %+ _p32
+ %define TMPL_MODE_STR '32-bit long mode'
+%endif
+
+%ifdef TMPL_LM64
+ %ifdef TMPL_RM
+ %error ""Both 'TMPL_LM64' and 'TMPL_RM' are defined.""
+ %endif
+ %ifdef TMPL_PE16
+ %error "Both 'TMPL_LM64' and 'TMPL_PE16' are defined."
+ %endif
+ %ifdef TMPL_PE32
+ %error "Both 'TMPL_LM64' and 'TMPL_PE32' are defined."
+ %endif
+ %ifdef TMPL_PEV86
+ %error "Both 'TMPL_LM64' and 'TMPL_PEV86' are defined."
+ %endif
+ %ifdef TMPL_PP16
+ %error "Both 'TMPL_LM64' and 'TMPL_PP16' are defined."
+ %endif
+ %ifdef TMPL_PP32
+ %error "Both 'TMPL_LM64' and 'TMPL_PP32' are defined."
+ %endif
+ %ifdef TMPL_PPV86
+ %error "Both 'TMPL_LM64' and 'TMPL_PPV86' are defined."
+ %endif
+ %ifdef TMPL_PAE16
+ %error "Both 'TMPL_LM64' and 'TMPL_PAE16' are defined."
+ %endif
+ %ifdef TMPL_PAE32
+ %error "Both 'TMPL_LM64' and 'TMPL_PAE32' are defined."
+ %endif
+ %ifdef TMPL_PAEV86
+ %error "Both 'TMPL_LM64' and 'TMPL_PAEV86' are defined."
+ %endif
+ %ifdef TMPL_LM16
+ %error "Both 'TMPL_LM64' and 'TMPL_LM16' are defined."
+ %endif
+ %ifdef TMPL_LM32
+ %error "Both 'TMPL_LM64' and 'TMPL_LM32' are defined."
+ %endif
+ %define TMPL_CMN_LM
+ %define TMPL_CMN_P64
+ %define TMPL_64BIT
+ %define TMPL_BITS 64
+ %define TMPL_PTR_DEF dq
+ %define TMPL_NM(Name) Name %+ _lm64
+ %define TMPL_NM_CMN(Name) Name %+ _p64
+ %define TMPL_MODE_STR '64-bit long mode'
+%endif
+
+%ifndef TMPL_MODE_STR
+ %error "internal error"
+%endif
+
+
+;
+; Register aliases.
+;
+%ifdef TMPL_64BIT
+ %define xCB 8
+ %define xDEF dq
+ %define xRES resq
+ %define xPRE qword
+ %define xSP rsp
+ %define xBP rbp
+ %define xAX rax
+ %define xBX rbx
+ %define xCX rcx
+ %define xDX rdx
+ %define xDI rdi
+ %define xSI rsi
+ %define xWrtRIP wrt rip
+ %define xPUSHF pushq
+ %define xPOPF popfq
+%else
+ %ifdef TMPL_32BIT
+ %define xCB 4
+ %define xDEF dd
+ %define xRES resd
+ %define xPRE dword
+ %define xSP esp
+ %define xBP ebp
+ %define xAX eax
+ %define xBX ebx
+ %define xCX ecx
+ %define xDX edx
+ %define xDI edi
+ %define xSI esi
+ %define xWrtRIP
+ %define xPUSHF pushfd
+ %define xPOPF popfd
+ %else
+ %ifndef TMPL_16BIT
+ %error "TMPL_XXBIT is not defined."
+ %endif
+ %define xCB 2
+ %define xDEF dw
+ %define xRES resw
+ %define xPRE word
+ %define xSP sp
+ %define xBP bp
+ %define xAX ax
+ %define xBX bx
+ %define xCX cx
+ %define xDX dx
+ %define xDI di
+ %define xSI si
+ %define xWrtRIP
+ %define xPUSHF pushf
+ %define xPOPF popf
+ %endif
+%endif
+
+;
+; Register names corresponding to the max size for pop/push <reg>.
+;
+; 16-bit can push both 32-bit and 16-bit registers. This 's' prefixed variant
+; is used when 16-bit should use the 32-bit register.
+;
+%ifdef TMPL_64BIT
+ %define sCB 8
+ %define sDEF dq
+ %define sRES resq
+ %define sPRE qword
+ %define sSP rsp
+ %define sBP rbp
+ %define sAX rax
+ %define sBX rbx
+ %define sCX rcx
+ %define sDX rdx
+ %define sDI rdi
+ %define sSI rsi
+ %define sPUSHF pushfq
+ %define sPOPF popfq
+%else
+ %define sCB 4
+ %define sDEF dd
+ %define sRES resd
+ %define sPRE dword
+ %define sSP esp
+ %define sBP ebp
+ %define sAX eax
+ %define sBX ebx
+ %define sCX ecx
+ %define sDX edx
+ %define sDI edi
+ %define sSI esi
+ %define sPUSHF pushfd
+ %define sPOPF popfd
+%endif
+
+;
+; Default code segment.
+;
+%ifdef TMPL_64BIT
+ %define TMPL_BEGINCODE BEGINCODEHIGH
+%elifdef TMPL_32BIT
+ %define TMPL_BEGINCODE BEGINCODEHIGH
+%elifdef TMPL_16BIT
+ %define TMPL_BEGINCODE BEGINCODELOW
+%else
+ %error "Missing TMPL_xxBIT!"
+%endif
+TMPL_BEGINCODE
+
+;
+; Change the bitness.
+;
+%ifdef TMPL_64BIT
+BITS 64
+%else
+ %ifdef TMPL_32BIT
+BITS 32
+ %else
+BITS 16
+ %endif
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-test1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-test1-template.mac
new file mode 100644
index 00000000..6a5472c7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-test1-template.mac
@@ -0,0 +1,836 @@
+; $Id: bootsector2-test1-template.mac $
+;; @file
+; bootsector2 test1 - multi mode template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bootsector2-template-header.mac"
+
+;;
+; Run the CPUID benchmark for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkFlushCmd_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sBX
+ push sCX
+ push sDX
+ push sDI
+ sub sSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test.
+ mov edi, TEST_INSTRUCTION_COUNT_IO / 4
+%define MSR_IA32_FLUSH_CMD 0x10b
+%define MSR_IA32_FLUSH_CMD_F_L1D RT_BIT_32(0)
+ mov ecx, MSR_IA32_FLUSH_CMD
+ mov eax, MSR_IA32_FLUSH_CMD_F_L1D
+ xor edx, edx
+.again:
+ wrmsr
+ wrmsr
+ wrmsr
+ wrmsr
+ dec edi
+ jnz .again
+
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, TEST_INSTRUCTION_COUNT_IO
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add sSP, 20h
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ pop sAX
+ leave
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+
+.s_szTestName:
+ db TMPL_MODE_STR, ', FLUSH_CMD', 0
+ENDPROC TMPL_NM(BenchmarkFlushCmd_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Run the CPUID benchmark for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkCpuId_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sBX
+ push sCX
+ push sDX
+ push sDI
+ sub sSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test.
+ mov edi, TEST_INSTRUCTION_COUNT_CPUID / 4
+.again:
+ mov eax, 1
+ cpuid
+ mov eax, 1
+ cpuid
+ mov eax, 1
+ cpuid
+ mov eax, 1
+ cpuid
+ dec edi
+ jnz .again
+
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, TEST_INSTRUCTION_COUNT_CPUID
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add sSP, 20h
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ pop sAX
+ leave
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+
+.s_szTestName:
+ db TMPL_MODE_STR, ', CPUID', 0
+ENDPROC TMPL_NM(BenchmarkCpuId_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Run the RDTSC benchmark for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkRdTsc_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sBX
+ push sCX
+ push sDX
+ push sDI
+ sub sSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test.
+ mov edi, TEST_INSTRUCTION_COUNT_RDTSC / 4
+.again:
+ rdtsc
+ rdtsc
+ rdtsc
+ rdtsc
+ dec edi
+ jnz .again
+
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, TEST_INSTRUCTION_COUNT_RDTSC
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add sSP, 20h
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ pop sAX
+ leave
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+
+.s_szTestName:
+ db TMPL_MODE_STR, ', RDTSC', 0
+ENDPROC TMPL_NM(BenchmarkRdTsc_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Run the Read CR4 benchmark for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkRdCr4_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sBX
+ push sCX
+ push sDX
+ push sDI
+ sub sSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test.
+ mov edi, TEST_INSTRUCTION_COUNT_READCR4 / 4
+.again:
+ mov sAX, cr4
+ mov sAX, cr4
+ mov sAX, cr4
+ mov sAX, cr4
+ dec edi
+ jnz .again
+
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, TEST_INSTRUCTION_COUNT_READCR4
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add sSP, 20h
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ pop sAX
+ leave
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+
+.s_szTestName:
+ db TMPL_MODE_STR, ', Read CR4', 0
+ENDPROC TMPL_NM(BenchmarkRdCr4_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Prologue for the I/O port tests.
+%ifndef HaveIoPortPrologue
+%define HaveIoPortPrologue
+%macro IoPortPrologue 2
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ sub xSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test.
+ mov dx, %2
+ mov ecx, (%1) / 5
+%endmacro
+%endif
+
+
+;;
+; Epilogue for the I/O port tests.
+%ifndef HaveIoPortEpilogue
+%define HaveIoPortEpilogue
+%macro IoPortEpilogue 1
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, (%1)
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add xSP, 20h
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+%endmacro
+%endif
+
+
+;;
+; Benchmarks: IN eax, NOP
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop32In)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit IN', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop32In)
+
+
+;;
+; Benchmarks: OUT NOP, eax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop32Out)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit OUT', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop32Out)
+
+
+;;
+; Benchmarks: IN ax, NOP
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop16In)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ in ax, dx
+ in ax, dx
+ in ax, dx
+ in ax, dx
+ in ax, dx
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 16-bit IN', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop16In)
+
+
+;;
+; Benchmarks: OUT NOP, ax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop16Out)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ out dx, ax
+ out dx, ax
+ out dx, ax
+ out dx, ax
+ out dx, ax
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 16-bit OUT', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop16Out)
+
+
+;;
+; Benchmarks: IN al, NOP
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop8In)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ in al, dx
+ in al, dx
+ in al, dx
+ in al, dx
+ in al, dx
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 8-bit IN', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop8In)
+
+
+;;
+; Benchmarks: OUT NOP, al
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortNop8Out)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP
+.again:
+ out dx, al
+ out dx, al
+ out dx, al
+ out dx, al
+ out dx, al
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 8-bit OUT', 0
+ENDPROC TMPL_NM(BenchmarkIoPortNop8Out)
+
+
+;;
+; Benchmarks: IN eax, NOP_R3
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortRing3Nop32In)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP_R3
+.again:
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ in eax, dx
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit IN-to-ring-3', 0
+ENDPROC TMPL_NM(BenchmarkIoPortRing3Nop32In)
+
+
+;;
+; Benchmarks: OUT NOP_R3, eax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkIoPortRing3Nop32Out)
+ IoPortPrologue TEST_INSTRUCTION_COUNT_IO, VMMDEV_TESTING_IOPORT_NOP_R3
+.again:
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ out dx, eax
+ dec ecx
+ jnz .again
+ IoPortEpilogue TEST_INSTRUCTION_COUNT_IO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit OUT-to-ring-3', 0
+ENDPROC TMPL_NM(BenchmarkIoPortRing3Nop32Out)
+
+
+%undef IoPortPrologue
+%undef IoPortEpilogue
+
+
+;;
+; Run the I/O benchmarks for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkIoPortNop_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ call TMPL_NM(BenchmarkIoPortNop32In)
+ call TMPL_NM(BenchmarkIoPortNop32Out)
+%ifndef QUICK_TEST
+ call TMPL_NM(BenchmarkIoPortNop16In)
+ call TMPL_NM(BenchmarkIoPortNop16Out)
+ call TMPL_NM(BenchmarkIoPortNop8In)
+ call TMPL_NM(BenchmarkIoPortNop8Out)
+%endif
+ call TMPL_NM(BenchmarkIoPortRing3Nop32In)
+ call TMPL_NM(BenchmarkIoPortRing3Nop32Out)
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+ENDPROC TMPL_NM(BenchmarkIoPortNop_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+
+
+;;
+; Prologue for the MMIO tests.
+%ifndef HaveMmioPrologue
+%define HaveMmioPrologue
+%macro MmioPrologue 2
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ push sBX
+ sub xSP, 20h
+
+ ; Get the current time.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetNanoTS)
+
+ ; Do the test - X million 32-bit IN instructions.
+%ifdef TMPL_16BIT
+ mov dx, ds ; save ds
+ %ifdef TMPL_RM
+ mov bx, VMMDEV_TESTING_MMIO_RM_SEL
+ mov ds, bx
+ mov ebx, VMMDEV_TESTING_MMIO_RM_OFF(%2)
+ %else
+ mov bx, BS2_SEL_MMIO16
+ mov ds, bx
+ mov ebx, %2 - BS2_SEL_MMIO16_BASE
+ %endif
+%else
+ mov xBX, %2
+%endif
+ mov ecx, (%1) / 5
+%endmacro
+%endif
+
+;;
+; Epilogue for the MMIO tests.
+%ifndef HaveMmioEpilogue
+%define HaveMmioEpilogue
+%macro MmioEpilogue 1
+%ifdef TMPL_16BIT
+ mov ds, dx ; restore ds
+%endif
+
+ ; Calc the elapsed time and report the result.
+ mov xAX, xSP
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+
+ mov xCX, .s_szTestName
+ mov edx, (%1)
+ mov xAX, xSP
+ call TMPL_NM_CMN(ReportResult)
+
+ add xSP, 20h
+ pop sBX
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+%endmacro
+%endif
+
+
+;;
+; Benchmarks: MOV eax, [NOP]
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop32Read)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit read', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop32Read)
+
+
+;;
+; Benchmarks: MOV [NOP], eax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop32Write)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit write', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop32Write)
+
+
+;;
+; Benchmarks: MOV ax, [NOP]
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop16Read)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov ax, [xBX]
+ mov ax, [xBX]
+ mov ax, [xBX]
+ mov ax, [xBX]
+ mov ax, [xBX]
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 16-bit read', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop16Read)
+
+
+;;
+; Benchmarks: MOV [NOP], ax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop16Write)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov [xBX], ax
+ mov [xBX], ax
+ mov [xBX], ax
+ mov [xBX], ax
+ mov [xBX], ax
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 16-bit write', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop16Write)
+
+
+;;
+; Benchmarks: MOV al, [NOP]
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop8Read)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov al, [xBX]
+ mov al, [xBX]
+ mov al, [xBX]
+ mov al, [xBX]
+ mov al, [xBX]
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 8-bit read', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop8Read)
+
+
+;;
+; Benchmarks: MOV [NOP], al
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioNop8Write)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP
+.again:
+ mov [xBX], al
+ mov [xBX], al
+ mov [xBX], al
+ mov [xBX], al
+ mov [xBX], al
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 8-bit write', 0
+ENDPROC TMPL_NM(BenchmarkMmioNop8Write)
+
+
+;;
+; Benchmarks: MOV eax, [NOP_R3]
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioRing3Nop32Read)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP_R3
+.again:
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ mov eax, [sBX]
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit read-to-ring-3', 0
+ENDPROC TMPL_NM(BenchmarkMmioRing3Nop32Read)
+
+
+;;
+; Benchmarks: MOV [NOP_R3], eax
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkMmioRing3Nop32Write)
+ MmioPrologue TEST_INSTRUCTION_COUNT_MMIO, VMMDEV_TESTING_MMIO_NOP_R3
+.again:
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ mov [sBX], eax
+ dec ecx
+ jnz .again
+ MmioEpilogue TEST_INSTRUCTION_COUNT_MMIO
+.s_szTestName:
+ db TMPL_MODE_STR, ', 32-bit write-to-ring-3', 0
+ENDPROC TMPL_NM(BenchmarkMmioRing3Nop32Write)
+
+
+%undef MmioPrologue
+%undef MmioEpilogue
+
+
+;;
+; Do the MMIO tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(BenchmarkMmioNop_rm)
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ call TMPL_NM(BenchmarkMmioNop32Read)
+ call TMPL_NM(BenchmarkMmioNop32Write)
+%ifndef QUICK_TEST
+ call TMPL_NM(BenchmarkMmioNop16Read)
+ call TMPL_NM(BenchmarkMmioNop16Write)
+ call TMPL_NM(BenchmarkMmioNop8Read)
+ call TMPL_NM(BenchmarkMmioNop8Write)
+%endif
+ call TMPL_NM(BenchmarkMmioRing3Nop32Read)
+ call TMPL_NM(BenchmarkMmioRing3Nop32Write)
+
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ ret
+ENDPROC TMPL_NM(BenchmarkMmioNop_rm)
+
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+%include "bootsector2-template-footer.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-test1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-test1.asm
new file mode 100644
index 00000000..ad363b6d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-test1.asm
@@ -0,0 +1,216 @@
+; $Id: bootsector2-test1.asm $
+;; @file
+; Bootsector that benchmarks I/O and MMIO roundtrip time.
+; VBoxManage setextradata bs-test1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+; VBoxManage setextradata bs-test1 VBoxInternal/Devices/VMMDev/0/Config/TestingMMIO 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;; The number of instructions to test.
+%define TEST_INSTRUCTION_COUNT_IO 2000000
+
+;; The number of CPUID instructions to test.
+%define TEST_INSTRUCTION_COUNT_CPUID 8000000
+
+;; The number of RDTSC instructions to test.
+%define TEST_INSTRUCTION_COUNT_RDTSC 4000000
+
+;; The number of RDTSC instructions to test.
+%define TEST_INSTRUCTION_COUNT_READCR4 1000000
+
+;; The number of instructions to test.
+%define TEST_INSTRUCTION_COUNT_MMIO 750000
+
+;; Define this to drop unnecessary test variations.
+%define QUICK_TEST
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP32
+ %define BS2_INC_PAE32
+ %define BS2_INC_LM64
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+ call Bs2PanicIfVMMDevTestingIsMissing_r86
+
+%if 0
+ ;
+ ; IA32_FLUSH_CMD.
+ ;
+ mov ax, .s_szTstFlushCmd
+ call TestSub_r86
+ call BenchmarkFlushCmd_rm_pp32
+ call BenchmarkFlushCmd_rm_pae32
+ call BenchmarkFlushCmd_rm_lm64
+ call BenchmarkFlushCmd_rm_pe16
+ call BenchmarkFlushCmd_rm_pe32
+ call BenchmarkFlushCmd_rm_rm
+%endif
+
+ ;
+ ; CPUID.
+ ;
+ mov ax, .s_szTstCpuId
+ call TestSub_r86
+ call BenchmarkCpuId_rm_pp32
+ call BenchmarkCpuId_rm_pae32
+ call BenchmarkCpuId_rm_lm64
+ call BenchmarkCpuId_rm_pe16
+ call BenchmarkCpuId_rm_pe32
+ call BenchmarkCpuId_rm_rm
+
+ ;
+ ; RDTSC.
+ ;
+ mov ax, .s_szTstRdTsc
+ call TestSub_r86
+ call BenchmarkRdTsc_rm_pp32
+ call BenchmarkRdTsc_rm_pae32
+ call BenchmarkRdTsc_rm_lm64
+ call BenchmarkRdTsc_rm_pe16
+ call BenchmarkRdTsc_rm_pe32
+ call BenchmarkRdTsc_rm_rm
+
+ ;
+ ; Read CR4
+ ;
+ mov ax, .s_szTstRdCr4
+ call TestSub_r86
+ call BenchmarkRdCr4_rm_pp32
+ call BenchmarkRdCr4_rm_pae32
+ call BenchmarkRdCr4_rm_lm64
+ call BenchmarkRdCr4_rm_pe16
+ call BenchmarkRdCr4_rm_pe32
+ call BenchmarkRdCr4_rm_rm
+
+ ;
+ ; I/O port access.
+ ;
+ mov ax, .s_szTstNopIoPort
+ call TestSub_r86
+ call BenchmarkIoPortNop_rm_rm
+ call BenchmarkIoPortNop_rm_pe16
+ call BenchmarkIoPortNop_rm_pe32
+ call BenchmarkIoPortNop_rm_pp32
+ call BenchmarkIoPortNop_rm_pae32
+ call BenchmarkIoPortNop_rm_lm64
+
+ ;
+ ; MMIO access.
+ ;
+ mov ax, .s_szTstNopMmio
+ call TestSub_r86
+ call BenchmarkMmioNop_rm_pp32
+ call BenchmarkMmioNop_rm_pae32
+ call BenchmarkMmioNop_rm_lm64
+ call BenchmarkMmioNop_rm_pe16
+ call BenchmarkMmioNop_rm_pe32
+ call BenchmarkMmioNop_rm_rm
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ call Bs2Panic
+
+.s_szTstName:
+ db 'tstIOIntr', 0
+.s_szTstCpuId:
+ db 'CPUID EAX=1', 0
+.s_szTstFlushCmd:
+ db 'IA32_FLUSH_CMD', 0
+.s_szTstRdTsc:
+ db 'RDTSC', 0
+.s_szTstRdCr4:
+ db 'Read CR4', 0
+.s_szTstNopIoPort:
+ db 'NOP I/O Port Access', 0
+.s_szTstNopMmio:
+ db 'NOP MMIO Access', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_RM
+%include "bootsector2-test1-template.mac"
+;%define TMPL_CMN_V86
+;%include "bootsector2-test1-template.mac"
+%define TMPL_PE16
+%include "bootsector2-test1-template.mac"
+%define TMPL_PE32
+%include "bootsector2-test1-template.mac"
+;%define TMPL_PP16
+;%include "bootsector2-test1-template.mac"
+%define TMPL_PP32
+%include "bootsector2-test1-template.mac"
+;%define TMPL_PAE16
+;%include "bootsector2-test1-template.mac"
+%define TMPL_PAE32
+%include "bootsector2-test1-template.mac"
+;%define TMPL_LM16
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_LM32
+;%include "bootsector2-test1-template.mac"
+%define TMPL_LM64
+%include "bootsector2-test1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-triple-fault-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-triple-fault-1.asm
new file mode 100644
index 00000000..12729d91
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-triple-fault-1.asm
@@ -0,0 +1,390 @@
+; $Id: bootsector2-triple-fault-1.asm $
+;; @file
+; Bootsector for testing triple faults.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;; The number of instructions to test.
+%define TEST_INSTRUCTION_COUNT_IO 2000000
+
+;; The number of instructions to test.
+%define TEST_INSTRUCTION_COUNT_MMIO 750000
+
+;; Define this to drop unnecessary test variations.
+%define QUICK_TEST
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP32
+ %define BS2_INC_PAE32
+ %define BS2_INC_LM64
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The test driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+
+ ;
+ ; Did we get here from a reboot triggered below?
+ ;
+ push ds
+ mov ax, 2000h ; 128 KB is enough for the test program
+.boot_check_loop:
+ mov ds, ax
+ cmp dword [0], 064726962h
+ jne .boot_check_next
+ cmp dword [4], 062697264h
+ je .warm_reset_broken
+
+.boot_check_next:
+ mov dword [0], 064726962h
+ mov dword [4], 062697264h
+ add ax, 1000h
+ cmp ax, 8000h
+ jbe .boot_check_loop
+ pop ds
+ jmp .fine
+
+.warm_reset_broken:
+ pop ds
+ mov ax, .s_szWarmResetBroken
+ call NAME(TestFailed_r86)
+ jmp .done
+.s_szWarmResetBroken:
+ db 'Warm reset vector support is broken', 0dh, 0ah, 0
+.fine:
+
+ ;
+ ; Test that the warm reset interface works.
+ ;
+ mov ax, .s_szPrecondTest5
+ call NAME(TestSub_r86)
+ mov al, 05h
+ call NAME(SetWarmResetJmp)
+ cmp ax, 0
+ jne .precond_test_A
+ call NAME(TestReboot_r86)
+
+.precond_test_A:
+ mov ax, .s_szPrecondTestA
+ call NAME(TestSub_r86)
+ mov al, 0Ah
+ call NAME(SetWarmResetJmp)
+ cmp ax, 0
+ jne .precond_test_A_passed
+ call NAME(TestReboot_r86)
+.precond_test_A_passed:
+ call NAME(TestSubDone_r86)
+
+ ;
+ ; The real tests.
+ ;
+
+
+ ;
+ ; We're done.
+ ;
+.done:
+ call NAME(TestTerm_r86)
+ call Bs2Panic
+
+.s_szTstName:
+ db 'tstTriple', 0
+.s_szPrecondTest5:
+ db 'Shutdown Action 5', 0
+.s_szPrecondTestA:
+ db 'Shutdown Action A', 0
+ENDPROC main
+
+
+
+;;
+; Sets up the warm reset vector.
+;
+; @param ax Where to resume exeuction.
+; @param dl Shutdown action command to use, 5h or Fh.
+;
+; @uses nothing
+;
+BEGINPROC SetUpWarmReset
+ push bp
+ mov bp, sp
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push edi
+ push esi
+ push es
+
+ ;
+ ; Set up the warm reboot vector.
+ ;
+ mov bx, 40h
+ mov es, bx
+
+ mov ecx, [es:67h] ; debug
+ mov word [es:67h], ax
+ mov bx, cs
+ mov word [es:67h+2], bx
+
+ mov bx, [es:72h] ; debug
+ mov word [es:72h], 1234h ; warm reboot
+
+ wbinvd
+
+ mov al, 0fh
+ out 70h, al ; set register index
+ in al, 71h
+ mov ah, al ; debug
+ mov al, dl ; shutdown action command
+ out 71h, al ; set cmos[f] = a - invoke testResume as early as possible.
+ in al, 71h ; debug / paranoia
+ movzx si, al
+
+ ; Debug print.
+%if 1
+ mov di, sp ; save sp (lazy bird)
+ in al, 64h
+ push ax ; kbd status
+ push si ; cmos[f] after
+ mov al, ah ; cmos[f] before
+ push ax
+ push word [0472h] ; 40:72 word after
+ push bx ; 40:72 word before
+ push word [0467h] ; 40:67 far addr after
+ push word [0469h]
+ push cx ; 40:67 far addr before
+ shr ecx, 16
+ push dx
+ push ds
+ push .s_szDebugFmt
+ call NAME(PrintF_r86)
+ mov sp, di ; restore sp.
+;.forever:
+; cli
+; hlt
+; jmp .forever
+%endif
+
+ pop es
+ pop esi
+ pop edi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ leave
+ ret
+
+.s_szDebugFmt:
+ db 'dbg: 40:67=%RX16:%RX16 (%RX16:%RX16) 40:72=%RX16 (%RX16) cmos[f]=%RX8 (%RX8) kbdsts=%RX8', 0dh, 0ah, 0
+ENDPROC SetUpWarmReset
+
+
+;;
+; Sets up the warm reset vector.
+;
+; @returns ax = 0 on setup call, ax = 1 on resume return.
+; @param al Shutdown action command to use, 5h or Fh.
+; @uses ax
+;
+BEGINPROC SetWarmResetJmp
+ push bp
+ mov bp, sp
+ push dx
+
+ mov dl, al
+ mov ax, .resume
+ call NAME(SetUpWarmReset)
+
+%ifdef DEBUG
+ push cs
+ push .s_szDbg1
+ call NAME(PrintF_r86)
+ add sp, 4
+%endif
+
+ mov ax, .s_ResumeRegs
+ call NAME(TestSaveRegisters_r86)
+
+%ifdef DEBUG
+ push cs
+ push .s_szDbg2
+ call NAME(PrintF_r86)
+ add sp, 4
+%endif
+
+ mov dx, [bp - 2]
+ mov [.s_ResumeRegs + BS2REGS.rdx], dx
+ mov ax, bp
+ add ax, 4
+ mov [.s_ResumeRegs + BS2REGS.rsp], ax
+ mov ax, [bp]
+ mov [.s_ResumeRegs + BS2REGS.rbp], ax
+ mov ax, [bp + 2]
+ mov [.s_ResumeRegs + BS2REGS.rip], ax
+ mov word [.s_ResumeRegs + BS2REGS.rax], 1
+
+%ifdef DEBUG
+ push cs
+ push .s_szDbg3
+ call NAME(PrintF_r86)
+ add sp, 4
+%endif
+
+ xor ax, ax
+.done:
+ pop dx
+ leave
+ ret
+
+.resume:
+ cli
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov ax, [.s_ResumeRegs + BS2REGS.ss]
+ mov ss, ax
+ mov esp, [.s_ResumeRegs + BS2REGS.rsp]
+ mov ebp, [.s_ResumeRegs + BS2REGS.rbp]
+
+%ifdef DEBUG
+ push ds
+ push .s_szDbg4
+ call NAME(PrintF_r86)
+ add sp, 4
+%endif
+
+ mov ax, .s_ResumeRegs
+ call NAME(TestRestoreRegisters_r86)
+ mov ax, [.s_ResumeRegs + BS2REGS.rip]
+ push ax
+ mov ax, 1
+ ret
+ ;jmp word [.s_ResumeRegs + BS2REGS.rip]
+
+.s_ResumeRegs:
+ times (BS2REGS_size) db 0
+%ifdef DEBUG
+.s_szDbg1:
+ db 'dbg 1', 0dh, 0ah, 0
+.s_szDbg2:
+ db 'dbg 2', 0dh, 0ah, 0
+.s_szDbg3:
+ db 'dbg 3', 0dh, 0ah, 0
+.s_szDbg4:
+ db 'dbg 4', 0dh, 0ah, 0
+%endif
+ENDPROC SetWarmResetJmp
+
+
+;;
+; Reboot the machine. Will not return.
+;
+BEGINPROC TestReboot_r86
+%ifdef DEBUG
+ ; Debug
+ push ds
+ push .s_szDbg
+ call NAME(PrintF_r86)
+%endif
+ ; Via port A
+ in al, 92h
+ and al, ~1
+ out 92h, al
+ or al, 1
+ out 92h, al
+ in al, 92h
+.forever:
+ cli
+ hlt
+ jmp .forever
+%ifdef DEBUG
+.s_szDbg:
+ db 'Rebooting...', 0dh, 0ah, 0
+%endif
+ENDPROC TestReboot_r86
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+;%define TMPL_RM
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_CMN_V86
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PE16
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PE32
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PP16
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PP32
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PAE16
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_PAE32
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_LM16
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_LM32
+;%include "bootsector2-test1-template.mac"
+;%define TMPL_LM64
+;%include "bootsector2-test1-template.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-64-1.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-64-1.asm
new file mode 100644
index 00000000..92f5c27a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-64-1.asm
@@ -0,0 +1,121 @@
+; $Id: bootsector2-vbinstst-64-1.asm $
+;; @file
+; Bootsector tests instructions in 64-bit mode.
+; VBoxManage setextradata bs-vbinstst-64-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_INC_LM64
+ %define BS2_WITH_TRAPS
+ %include "bootsector2-common-init-code.mac"
+
+
+;
+; The benchmark driver
+;
+BEGINPROC main
+ ;
+ ; Test prologue.
+ ;
+ cli
+ mov ax, .s_szTstName
+ call TestInit_r86
+ call Bs2EnableA20_r86
+ call Bs2PanicIfVMMDevTestingIsMissing_r86
+ lea eax, [dword the_end]
+ cmp eax, dword 80000h
+ jae .size_nok
+
+
+ ;
+ ; Do the testing.
+ ;
+ call Bs2IsModeSupported_rm_lm64
+ jz .done
+ call Bs2EnterMode_rm_lm64
+BITS 64
+
+ call TestInstrMain_lm64
+
+ call TestSubDone_p64
+ call Bs2ExitMode_lm64
+BITS 16
+.done:
+
+ ;
+ ; We're done.
+ ;
+ call TestTerm_r86
+ call Bs2Panic
+ jmp .done
+
+.size_nok:
+ push eax
+ push word ds
+ push .s_szSizeNok
+ call TestFailedF_r86
+ jmp .done
+
+.s_szSizeNok:
+ db 'Test is too big (%RX32)', 0
+.s_szTstName:
+ db 'VBInsTst-64-1', 0
+ENDPROC main
+
+
+;
+; Instantiate the template code.
+;
+%include "bootsector2-template-footer.mac" ; reset the initial environemnt.
+
+%define TMPL_LM64
+%include "bootsector2-template-header.mac"
+BITS 64
+%include "VBInsTst-64.asm"
+%include "bootsector2-template-footer.mac"
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-big-template.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-big-template.asm
new file mode 100644
index 00000000..06252d62
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-big-template.asm
@@ -0,0 +1,91 @@
+; $Id: bootsector2-vbinstst-big-template.asm $
+;; @file
+; Boot Sector 2 with big instruction test image template. For use with
+; bootsector2-vbinstst-kernel.asm. Requires:
+; VBoxManage setextradata bs-vbinstst-64-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Set up the assembler environment.
+;
+%include "bootsector2-first.mac"
+%include "bootsector2-api.mac"
+
+%include "bootsector2-template-footer.mac"
+%ifdef BS2_BIG_IMAGE_LM64
+ %define TMPL_LM64
+ %include "bootsector2-template-header.mac"
+ BITS 64
+
+%elifdef BS2_BIG_IMAGE_PAE32
+ %define TMPL_PAE32
+ %include "bootsector2-template-header.mac"
+ BITS 32
+
+%elifdef BS2_BIG_IMAGE_PP32
+ %define TMPL_PP32
+ %include "bootsector2-template-header.mac"
+ BITS 32
+
+%else
+ %error Do not know which mode to run in.
+ mov bad,instr
+%endif
+
+
+ ORG BS2_BIG_LOAD_ADDR
+
+;
+; The entry point is the first byte in the image.
+;
+bs2_big_image_start:
+entrypoint:
+ mov xAX, .s_szTestName
+ call [TMPL_NM_CMN(g_pfnTestInit) xWrtRIP]
+ call TMPL_NM(TestInstrMain)
+ call [TMPL_NM_CMN(g_pfnTestTerm) xWrtRIP]
+.hltloop:
+ hlt
+ jmp .hltloop
+
+.s_szTestName:
+ db BS2_BIG_IMAGE_GEN_TEST_NAME, 0
+
+
+;
+; Instantiate the template code.
+;
+%include "BS2_BIG_IMAGE_GEN_SOURCE_FILE"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-kernel.asm b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-kernel.asm
new file mode 100644
index 00000000..b1a53f7f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-vbinstst-kernel.asm
@@ -0,0 +1,527 @@
+; $Id: bootsector2-vbinstst-kernel.asm $
+;; @file
+; bootsector #2 kernel for big instruction testcases.
+; VBoxManage setextradata bs-vbinstst-64-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; This is always the first include file.
+;
+%include "bootsector2-first.mac"
+
+;
+; Include and execute the init code.
+;
+ %define BS2_INIT_RM
+ %define BS2_WITH_TRAPS
+ %define BS2_WITHOUT_RAW_MODE ; causes troubles with PIC/floppy.
+
+ %define BS2_INC_RM
+ %define BS2_INC_PE16
+ %define BS2_INC_PE32
+ %define BS2_INC_PP16
+ %define BS2_INC_PP32
+ %define BS2_INC_PAE16
+ %define BS2_INC_PAE32
+ %define BS2_INC_LM16
+ %define BS2_INC_LM32
+ %define BS2_INC_LM64
+ %include "bootsector2-common-init-code.mac"
+ %include "bootsector2-api.mac"
+ %include "iprt/formats/pe.mac"
+ %include "iprt/formats/mz.mac"
+
+
+BEGINCODE
+BEGINPROC main
+;
+; Set up the runtime environment.
+;
+ call Bs2EnableA20_r86
+
+ ; 16-bit real mode.
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _r86)], dword NAME(a_Name %+ _r86)
+ BS2_API_TEMPLATE
+
+ ; 16-bit protected mode.
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov word [NAME(g_pfn %+ a_Name %+ _p16)], word NAME(a_Name %+ _p16)
+ BS2_API_TEMPLATE
+ mov eax, BS2_SEL_CS16
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov [NAME(g_pfn %+ a_Name %+ _p16) + 2], ax
+ BS2_API_TEMPLATE
+
+ ; 32-bit
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p32)], dword NAME(a_Name %+ _p32)
+ BS2_API_TEMPLATE
+
+ ; 64-bit
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p64)], dword NAME(a_Name %+ _p64)
+ BS2_API_TEMPLATE
+ xor eax, eax
+%undef BS2_API_TEMPLATE_ACTION
+%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p64) + 4], eax
+ BS2_API_TEMPLATE
+
+ ; The magic markers and version number.
+ mov dword [g_u32Bs2ApiMagic], BS2_API_MAGIC
+ mov dword [g_u32Bs2ApiEndMagic], BS2_API_MAGIC
+ mov dword [g_u32Bs2ApiVersion], BS2_API_VERSION
+
+;
+; Load the extended image into high memory.
+;
+ mov dl, [g_bBootDrv]
+ call NAME(bs2LoadBigImage)
+
+;
+; Hand control over to the extended image.
+;
+%ifdef BS2_BIG_IMAGE_LM64
+ call Bs2EnterMode_rm_lm64
+BITS 64
+ mov eax, BS2_BIG_LOAD_ADDR
+ call rax
+ call Bs2ExitMode_lm64
+BITS 16
+
+%elifdef BS2_BIG_IMAGE_PP32
+ call Bs2EnterMode_rm_pp32
+BITS 32
+ mov eax, BS2_BIG_LOAD_ADDR
+ call eax
+ call Bs2ExitMode_pp32
+BITS 16
+
+%elifdef BS2_BIG_IMAGE_PAE32
+ call Bs2EnterMode_rm_pae32
+BITS 32
+ mov eax, BS2_BIG_LOAD_ADDR
+ call eax
+ call Bs2ExitMode_pae32
+BITS 16
+
+%else
+ ;
+ ; Probe the image, looking for an executable format we can deal with.
+ ; Not doing a lot of checking here, but who cares right now...
+ ;
+ call Bs2EnterMode_rm_pp32
+BITS 32
+ mov eax, BS2_BIG_LOAD_ADDR
+ cmp word [eax], IMAGE_DOS_SIGNATURE
+ jne .not_dos
+ add eax, [eax + IMAGE_DOS_HEADER.e_lfanew]
+.not_dos:
+ cmp dword [eax], IMAGE_NT_SIGNATURE
+ je .is_pe
+ mov eax, BS2_BIG_LOAD_ADDR
+ jmp .start_32
+
+.is_pe:
+ lea edx, [eax + IMAGE_NT_HEADERS32.FileHeader]
+ cmp word [edx + IMAGE_FILE_HEADER.Machine], IMAGE_FILE_MACHINE_I386
+ je .is_pe32
+ cmp word [edx + IMAGE_FILE_HEADER.Machine], IMAGE_FILE_MACHINE_AMD64
+ je .is_pe64
+ jmp .panic_32
+
+.is_pe32:
+ add edx, IMAGE_FILE_HEADER_size
+ mov eax, [edx + IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint]
+ add eax, BS2_BIG_LOAD_ADDR
+ jmp .start_32
+
+.is_pe64:
+ add edx, IMAGE_FILE_HEADER_size
+ mov eax, [edx + IMAGE_OPTIONAL_HEADER64.AddressOfEntryPoint]
+ add eax, BS2_BIG_LOAD_ADDR
+ jmp .start_64
+
+ ; Start executing at eax in 32-bit mode (current).
+.start_32:
+ call eax
+.panic_32:
+ call Bs2ExitMode_pp32
+BITS 16
+ jmp .panic
+
+ ; Start executing at eax in 64-bit mode.
+BITS 32
+.start_64:
+ call Bs2ExitMode_pp32
+BITS 16
+ call Bs2EnterMode_rm_lm64
+BITS 64
+ mov eax, eax
+ call rax
+ call Bs2ExitMode_lm64
+BITS 16
+ jmp .panic
+
+.panic:
+%endif
+ call Bs2Panic
+ENDPROC main
+
+
+
+
+;;
+; Loads the big image off the floppy.
+;
+; This uses the the_end label to figure out the starting offset.
+; The length is assumed to be the whole floppy.
+;
+; Clobbers nothing, except for 68KB of memory beyond the_end.
+;
+; @param dl The boot drive number (from BIOS).
+;
+BITS 16
+BEGINPROC bs2LoadBigImage
+ push ebp
+ movzx ebp, sp
+
+%define bSavedDiskNo byte [bp - 02h]
+ push dx
+%define bMaxSector byte [bp - 04h]
+ push 0
+%define bMaxHead byte [bp - 06h]
+ push 0
+%define bMaxCylinder byte [bp - 08h]
+ push 0
+%define pbHighDst dword [bp - 0ch]
+ push dword BS2_BIG_LOAD_ADDR
+%define SegTemp word [bp - 0eh]
+ push 0
+%define fStatus byte [bp - 10h]
+ push 0
+
+ push es
+ push ds
+ push eax
+ push edx
+ push ecx
+ push ebx
+ push edi
+ push esi
+ push ebp
+
+ ; Display message.
+ push cs
+ push .s_szLoadingBigImage
+ call PrintF_r86
+ add sp, 4
+
+
+ ;
+ ; Try figure the geometry. This defines how much we'll read.
+ ;
+ mov ah, 08h
+ xor di, di ; (es:di = 0000:0000 works around some buggy bioses, says wikipedia.)
+ mov es, di
+ int 13h
+ jc .param_error
+ mov bMaxSector, cl ; Do the cl[7:6]+ch stuff so we can address 255 sectors on the fake 63MB floppy.
+ mov bMaxHead, dh
+ mov bMaxCylinder, ch ; See above.
+ mov dl, bSavedDiskNo
+%if 0
+ movzx ax, bMaxCylinder
+ push ax
+ movzx cx, bMaxHead
+ push cx
+ movzx ax, bMaxSector
+ push ax
+ push ds
+ push .s_szDbgParam
+ call PrintF_r86
+ jmp .dprintf_param_done
+.s_szDbgParam:
+ db 13, 10, 'Floppy params max: sectors=%RX16 heads=%RX16 cylinders=%RX16', 13, 10, 0
+.dprintf_param_done:
+%endif
+
+ ;
+ ; Skip the kernel image (this could be done more efficiently, but this
+ ; also does the trick).
+ ;
+ lea eax, [dword the_end]
+ sub eax, start
+ shr eax, 9 ; sectors to skip
+ mov cx, 0001h ; sector (1-based), cylinder (0-based).
+ xor dh, dh ; head (0-based).
+.skip_one_more:
+ inc cl
+ cmp cl, bMaxSector
+ jbe .decrement_sector_count
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead ; ASSUMES bMaxHead < 255.
+ jbe .decrement_sector_count
+
+ mov dh, 0
+ inc ch
+
+.decrement_sector_count:
+ dec ax
+ jnz .skip_one_more
+
+
+
+ ;
+ ; Load loop. We load and copy 64 KB at the time into the high location.
+ ; Fixed registers (above): dl=drive, cl[7:6]:ch=cylinder, dh=head, cl[5:0]=sector.
+ ;
+ lea eax, [dword the_end + 0ffffh]
+ and eax, 0ffff0000h
+ shr eax, 4
+ mov SegTemp, ax ; the 64KB segment we use for temporary storage.
+
+.the_load_loop:
+ mov al, '.'
+ call PrintChr_r86
+
+ ; Fill the segment with int3s (in case we don't read a full 64KB).
+ mov eax, 0cccccccch
+ mov di, SegTemp
+ mov es, di
+ xor edi, edi
+ push ecx
+ cld
+ mov cx, 4000h
+ rep stosd
+ pop ecx
+
+ ;
+ ; Load a bunch of sectors into the temp segment.
+ ;
+ xor ebx, ebx
+.the_sector_load_loop:
+ ; Figure how many sectors we can read without switching track or side.
+ movzx ax, bMaxSector
+ sub al, cl
+ inc al ; al = sectors left to read in the current track on the current side.
+ mov di, bx
+ shr di, 9 ; bx/512 = current sector offset.
+ neg di
+ add di, 10000h / 512 ; di = sectors left to read in the 64KB buffer.
+ cmp ax, di ; ax = min(ax, di)
+ jbe .use_ax_sector_count1
+ mov ax, di
+.use_ax_sector_count1:
+ cmp ax, 64 ; ax = min(ax,64) - Our BIOS limitation is 72, play safe.
+ jbe .use_ax_sector_count2
+ mov ax, 64
+.use_ax_sector_count2:
+ mov di, ax ; save the number of sectors we read
+
+ ; Do the reading.
+%if 0
+ push bx
+ push ax
+ push dx
+ push cx
+ push cs
+ push .s_szDbgRead
+ call PrintF_r86
+ jmp .after_read_dprintf
+.s_szDbgRead: db 'Reading CX=%RX16 DX=%RX16 AX=%RX16 BX=%RX16', 13, 10, 0
+.after_read_dprintf:
+%endif
+ push bx
+ mov ah, 02h ; ah=read function
+ int 13h
+ pop bx
+ jc .read_error
+
+ ; advance to the next sector/head/cylinder and address (lazy impl).
+.advance_another_sector:
+ cmp cl, bMaxSector
+ je .next_head
+ inc cl
+ jmp .adv_addr
+
+.next_head:
+ mov cl, 1
+ cmp dh, bMaxHead
+ je .next_cylinder
+ inc dh
+ jmp .adv_addr
+
+.next_cylinder:
+ mov dh, 0
+ cmp ch, bMaxCylinder ; No the cl[7:6]+ch stuff so we can address 255 sectors on the fake 63MB floppy.
+ jb .update_ch
+ mov fStatus, 1
+ jmp .move_block
+.update_ch:
+ inc ch
+
+.adv_addr:
+ add bx, 512
+ dec di
+ jnz .advance_another_sector
+
+ test bx, bx
+ jnz .the_sector_load_loop
+
+.move_block:
+ ;
+ ; Copy the memory into high mem.
+ ;
+%if 0
+ mov edi, pbHighDst
+ push edi
+ push cs
+ push .s_szDbgMove
+ call PrintF_r86
+ jmp .after_move_dprintf
+.s_szDbgMove: db 'Moving memory to EDI=%RX32', 13, 10, 0
+.after_move_dprintf:
+%endif
+
+ push ecx
+ push edx
+ push ds
+ push es
+ call Bs2EnterMode_rm_pp32
+BITS 32
+ ; Copy
+ mov edi, pbHighDst
+ movzx esi, SegTemp
+ shl esi, 4
+ mov ecx, 10000h / 4
+ cld
+ rep movsd
+
+ ; Verify
+ mov edi, pbHighDst
+ movzx esi, SegTemp
+ shl esi, 4
+ mov ecx, 10000h / 4
+ cld
+ repe cmpsd
+ je .mem_verified_ok
+ mov fStatus, 2
+
+.mem_verified_ok:
+ mov pbHighDst, edi
+
+ call Bs2ExitMode_pp32
+BITS 16
+ pop es
+ pop ds
+ pop edx
+ pop ecx
+
+ ; Continue reading and copying?
+ cmp fStatus, 0
+ je .the_load_loop
+
+ ; Do we quit the loop on a failure?
+ cmp fStatus, 2
+ je .verify_failed_msg
+
+ ;
+ ; Done, so end the current message line.
+ ;
+ mov al, 13
+ call PrintChr_r86
+ mov al, 10
+ call PrintChr_r86
+
+
+ pop esi
+ pop edi
+ pop ebx
+ pop ecx
+ pop edx
+ pop eax
+ pop ds
+ pop es
+ mov sp, bp
+ pop ebp
+ ret
+
+
+ ;
+ ; Something went wrong, display a message.
+ ;
+.verify_failed_msg:
+ mov edi, pbHighDst
+ push edi
+ push cs
+ push .s_szVerifyFailed
+ jmp .print_message_and_panic
+
+.param_error:
+ push ax
+ push cs
+ push .s_szParamError
+ jmp .print_message_and_panic
+
+.read_error:
+ push ax
+ push cs
+ push .s_szReadError
+ jmp .print_message_and_panic
+
+.print_message_and_panic:
+ call PrintF_r86
+ call Bs2Panic
+ jmp .print_message_and_panic
+
+.s_szReadError:
+ db 13, 10, 'Error reading: %RX8', 13, 10, 0
+.s_szParamError:
+ db 13, 10, 'Error getting params: %RX8', 13, 10, 0
+.s_szVerifyFailed:
+ db 13, 10, 'Failed to move block high... (%RX32) Got enough memory configured?', 13, 10, 0
+.s_szLoadingBigImage:
+ db 'Loading 2nd image.', 0
+ENDPROC bs2LoadBigImage
+
+
+;
+; End sections and image.
+;
+%include "bootsector2-common-end.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-apic-1-32.c32 b/src/VBox/ValidationKit/bootsectors/bs3-apic-1-32.c32
new file mode 100644
index 00000000..76b5b020
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-apic-1-32.c32
@@ -0,0 +1,119 @@
+/* $Id: bs3-apic-1-32.c32 $ */
+/** @file
+ * BS3Kit - bs3-apic-1, 32-bit C code.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/x86.h>
+#include <VBox/apic.h>
+
+
+static void printBitmap(const char BS3_FAR *pszName, uint32_t BS3_FAR volatile *pau32Bitmap)
+{
+ unsigned off;
+ Bs3TestPrintf("%s:", pszName);
+ for (off = 0; off < 0x80; off += 0x10)
+ {
+ uint32_t uVal = pau32Bitmap[off / sizeof(uint32_t)];
+ Bs3TestPrintf(off != 0 ? "'%08x" : "%08x", uVal);
+ }
+ Bs3TestPrintf("\n");
+}
+
+
+BS3_DECL(void) ProtModeApicTests(void)
+{
+ uint64_t uApicBase2, uApicBase;
+
+ Bs3TestSub("protected mode");
+ uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
+
+ /* Disable the APIC (according to wiki.osdev.org/APIC, disabling the
+ APIC could require a CPU reset to re-enable it, but it works for us): */
+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase & ~(uint64_t)MSR_IA32_APICBASE_EN);
+ uApicBase2 = ASMRdMsr(MSR_IA32_APICBASE);
+ if (uApicBase2 == (uApicBase & ~(uint64_t)MSR_IA32_APICBASE_EN))
+ Bs3TestPrintf("Disabling worked.\n");
+ else
+ Bs3TestFailedF("Disabling the APIC did not work (%#RX64)", uApicBase2);
+
+ /* Enabling the APIC: */
+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase | MSR_IA32_APICBASE_EN);
+ uApicBase2 = ASMRdMsr(MSR_IA32_APICBASE);
+ if (uApicBase2 == (uApicBase | MSR_IA32_APICBASE_EN))
+ {
+ uint8_t BS3_FAR volatile * const pabApic = (uint8_t BS3_FAR volatile *)((uintptr_t)uApicBase & X86_PAGE_4K_BASE_MASK);
+ uint32_t BS3_FAR volatile * const pau32Apic = (uint32_t BS3_FAR volatile *)pabApic;
+ uint32_t i, uVal, uVal2;
+ Bs3TestPrintf("Enabling worked.\n");
+
+ /*
+ * Do some register reads and such.
+ */
+ uVal = pau32Apic[XAPIC_OFF_VERSION / sizeof(uint32_t)];
+ Bs3TestPrintf("APIC version: %#x\n", uVal);
+ if ( APIC_REG_VERSION_GET_VER(uVal) != XAPIC_HARDWARE_VERSION_P4
+ && APIC_REG_VERSION_GET_VER(uVal) != XAPIC_HARDWARE_VERSION_P6)
+ Bs3TestFailedF("Unexpected APIC version: %#x (%#x)", APIC_REG_VERSION_GET_VER(uVal), uVal);
+
+ Bs3TestPrintf("APIC ID: %#x\n", pau32Apic[XAPIC_OFF_ID / sizeof(uint32_t)]);
+
+ Bs3TestPrintf("TPR: %#x\n", pau32Apic[XAPIC_OFF_TPR / sizeof(uint32_t)]);
+ for (i = 0; i < 5; i++)
+ {
+ Bs3TestPrintf("TPR write test iteration #%u\n", i + 1);
+ uVal = 256;
+ while (uVal-- > 0)
+ {
+ pau32Apic[XAPIC_OFF_TPR / sizeof(uint32_t)] = uVal;
+ uVal2 = pau32Apic[XAPIC_OFF_TPR / sizeof(uint32_t)];
+ if (uVal2 != uVal)
+ Bs3TestFailedF("Setting TPR to %#x failed, read back %#x", uVal, uVal2);
+ }
+ }
+ Bs3TestPrintf("APR: %#x\n", pau32Apic[XAPIC_OFF_APR / sizeof(uint32_t)]);
+ Bs3TestPrintf("PPR: %#x\n", pau32Apic[XAPIC_OFF_PPR / sizeof(uint32_t)]);
+ printBitmap("ISR", &pau32Apic[XAPIC_OFF_ISR0 / sizeof(uint32_t)]);
+ printBitmap("TMR", &pau32Apic[XAPIC_OFF_TMR0 / sizeof(uint32_t)]);
+ printBitmap("IRR", &pau32Apic[XAPIC_OFF_IRR0 / sizeof(uint32_t)]);
+ }
+ else
+ Bs3TestFailedF("Enabling the APIC did not work (%#RX64)", uApicBase2);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-apic-1.c b/src/VBox/ValidationKit/bootsectors/bs3-apic-1.c
new file mode 100644
index 00000000..4b079776
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-apic-1.c
@@ -0,0 +1,108 @@
+/* $Id: bs3-apic-1.c $ */
+/** @file
+ * BS3Kit - bs3-apic-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3_DECL_CALLBACK(void) ProtModeApicTests(void);
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-apic-1");
+ Bs3TestPrintf("g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestSub("real-mode");
+
+ /*
+ * Check that there is an APIC
+ */
+ if (!(g_uBs3CpuDetected & BS3CPU_F_CPUID))
+ Bs3TestFailed("CPUID not supported");
+ else if (!(ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_MSR))
+ Bs3TestFailed("No APIC: RDMSR/WRMSR not supported!");
+ else if (!(ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_APIC))
+ Bs3TestFailed("No APIC: CPUID(1) does not have EDX_APIC set!\n");
+ else
+ {
+ uint64_t uApicBase2;
+ uint64_t uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
+ Bs3TestPrintf("MSR_IA32_APICBASE=%#RX64 %s, %s cpu%s\n",
+ uApicBase,
+ uApicBase & MSR_IA32_APICBASE_EN ? "enabled" : "disabled",
+ uApicBase & MSR_IA32_APICBASE_BSP ? "bootstrap" : "slave",
+ uApicBase & MSR_IA32_APICBASE_EXTD ? ", x2apic" : "",
+ (uApicBase & X86_PAGE_4K_BASE_MASK) == MSR_IA32_APICBASE_ADDR ? ", !non-default address!" : "");
+
+ /* Disable the APIC (according to wiki.osdev.org/APIC, disabling the
+ APIC could require a CPU reset to re-enable it, but it works for us): */
+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase & ~(uint64_t)MSR_IA32_APICBASE_EN);
+ uApicBase2 = ASMRdMsr(MSR_IA32_APICBASE);
+ if (uApicBase2 == (uApicBase & ~(uint64_t)MSR_IA32_APICBASE_EN))
+ Bs3TestPrintf("Disabling worked.\n");
+ else
+ Bs3TestFailedF("Disabling the APIC did not work (%#RX64)", uApicBase2);
+
+ /* Enabling the APIC: */
+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase | MSR_IA32_APICBASE_EN);
+ uApicBase2 = ASMRdMsr(MSR_IA32_APICBASE);
+ if (uApicBase2 == (uApicBase | MSR_IA32_APICBASE_EN))
+ {
+ Bs3TestPrintf("Enabling worked.\n");
+
+ /*
+ * Do the rest of the testing in protected mode since we cannot
+ * (easily) access the APIC address from real mode.
+ */
+ Bs3SwitchTo32BitAndCallC_rm(ProtModeApicTests, 0);
+ }
+ else
+ Bs3TestFailedF("Enabling the APIC did not work (%#RX64)", uApicBase2);
+ }
+
+ Bs3TestTerm();
+ Bs3Shutdown();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-32.c32 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-32.c32
new file mode 100644
index 00000000..8525d27f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-32.c32
@@ -0,0 +1,66 @@
+/* $Id: bs3-cpu-basic-2-32.c32 $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, 32-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+FNBS3TESTDOMODE bs3CpuBasic2_RaiseXcpt0e_c32;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEBYONEENTRY g_aModeByOne32Tests[] =
+{
+ { "#PF", bs3CpuBasic2_RaiseXcpt0e_c32, BS3TESTMODEBYONEENTRY_F_ONLY_PAGING },
+};
+
+
+BS3_DECL(void) bs3CpuBasic2_Do32BitTests_pe32(void)
+{
+ Bs3TestPrintf("bs3CpuBasic2_Do32BitTests=%#x\n", g_uBs3CpuDetected);
+
+ Bs3TestDoModesByOne_pe32(g_aModeByOne32Tests, RT_ELEMENTS(g_aModeByOne32Tests), 0);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-asm.asm
new file mode 100644
index 00000000..ac7c250e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-asm.asm
@@ -0,0 +1,283 @@
+; $Id: bs3-cpu-basic-2-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-basic-2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+BS3_GLOBAL_DATA g_bs3CpuBasic2_ud2_FlatAddr, 4
+ dd _bs3CpuBasic2_ud2 wrt FLAT
+
+
+
+;
+; CPU mode agnostic test code snippets.
+;
+BS3_BEGIN_TEXT16
+
+BS3_PROC_BEGIN _bs3CpuBasic2_ud2
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_ud2
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_salc_ud2
+ salc ; #UD in 64-bit mode
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_salc_ud2
+
+BS3_PROC_BEGIN _bs3CpuBasic2_swapgs
+.again:
+ db 00fh, 001h, 0f8h ; swapgs - #UD when not in 64-bit mode.
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_swapgs
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_Int80
+ int 80h
+.again: ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_Int80
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_Int81
+ int 81h
+.again: ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_Int81
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_Int82
+ int 82h
+.again: ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_Int82
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_Int83
+ int 83h
+.again: ud2
+ jmp .again
+BS3_PROC_END _bs3CpuBasic2_Int83
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_iret
+ iret
+BS3_PROC_END _bs3CpuBasic2_iret
+AssertCompile(_bs3CpuBasic2_iret_EndProc - _bs3CpuBasic2_iret == 1)
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_iret_opsize
+ iretd
+BS3_PROC_END _bs3CpuBasic2_iret_opsize
+AssertCompile(_bs3CpuBasic2_iret_opsize_EndProc - _bs3CpuBasic2_iret_opsize == 2)
+
+
+BS3_PROC_BEGIN _bs3CpuBasic2_iret_rexw
+ BS3_SET_BITS 64
+ iretq
+ BS3_SET_BITS 16
+BS3_PROC_END _bs3CpuBasic2_iret_rexw
+AssertCompile(_bs3CpuBasic2_iret_rexw_EndProc - _bs3CpuBasic2_iret_rexw == 2)
+
+
+;
+; CPU mode agnostic test code snippets.
+;
+BS3_BEGIN_TEXT32
+
+;;
+; @param [xBP + xCB*2] puDst
+; @param [xBP + xCB*3] uNewValue
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_Store_mov, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ mov xCX, [xBP + xCB*2]
+ mov xAX, [xBP + xCB*3]
+ mov [xCX], xAX
+ leave
+ ret
+BS3_PROC_END_CMN bs3CpuBasic2_Store_mov
+
+;;
+; @param [xBP + xCB*2] puDst
+; @param [xBP + xCB*3] uNewValue
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_Store_xchg, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ mov xCX, [xBP + xCB*2]
+ mov xAX, [xBP + xCB*3]
+ xchg [xCX], xAX
+ leave
+ ret
+BS3_PROC_END_CMN bs3CpuBasic2_Store_xchg
+
+;;
+; @param [xBP + xCB*2] puDst
+; @param [xBP + xCB*3] uNewValue
+; @param [xBP + xCB*4] uOldValue
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_Store_cmpxchg, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ mov xCX, [xBP + xCB*2]
+ mov xDX, [xBP + xCB*3]
+ mov xAX, [xBP + xCB*4]
+.again:
+ cmpxchg [xCX], xDX
+ jnz .again
+ leave
+ ret
+BS3_PROC_END_CMN bs3CpuBasic2_Store_cmpxchg
+
+
+;
+; Jump code segment 64KB.
+;
+; There is no ORG directive in OMF mode of course. :-(
+;
+section BS3JMPTEXT16 align=16 CLASS=BS3CLASS16JMPCODE PRIVATE USE16
+ GROUP BS3GROUPJMPTEXT16 BS3JMPTEXT16
+ BS3_SET_BITS 16
+
+; 0000: Start with two int3 filler instructions.
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmptext16_start), function, 2
+ int3
+ int3
+
+; 0002: This is the target for forward wrap around jumps, should they succeed.
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_target_wrap_forward), function, 2
+ ud2
+ align 8, int3
+
+; 0008
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jb_wrap_backward__ud2), function, 2
+ db 0ebh, -012h ; jmp (0x0008 + 2 - 0x12 = 0xFFFFFFF8 (-8))
+ int3
+
+; 000b
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jb_opsize_wrap_backward__ud2), function, 3
+ db 066h, 0ebh, -016h ; jmp (0x000b + 3 - 0x16 = 0xFFFFFFF8 (-8))
+ int3
+
+ align 0x80, int3
+; 0080
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jv16_wrap_backward__ud2), function, 3
+ db 0e9h ; jmp (0x0080 + 3 - 0x8b = 0xFFFFFFF8 (-8))
+ dw -08bh
+ int3
+
+; 0084
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jv16_opsize_wrap_backward__ud2), function, 6
+ db 066h, 0e9h ; jmp (0x0084 + 6 - 0x92 = 0xFFFFFFF8 (-8))
+ dd -092h
+ int3
+
+; 008b
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_call_jv16_wrap_backward__ud2), function, 3
+ db 0e8h ; call (0x008b + 3 - 0x96)
+ dw -096h
+ int3
+
+; 008f
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_call_jv16_opsize_wrap_backward__ud2), function, 6
+ db 066h, 0e8h ; call (0x008f + 6 - 0x9d = 0xFFFFFFF8 (-8))
+ dd -09dh
+ int3
+
+
+ align 0x100, int3 ; Note! Doesn't work correctly for higher values.
+ times (0xff6b - 0x100) int3
+
+; ff6b
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_call_jv16_wrap_forward__ud2), function, 4
+ db 0e8h ; call (0xff6b+3 + 0x94 = 0x10002 (65538))
+ dw 094h
+ int3
+
+; ff6f
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_call_jv16_opsize_wrap_forward__ud2), function, 7
+ db 066h, 0e8h ; o32 call (0xff6f+6 + 0x8d = 0x10002 (65538))
+ dd 08dh
+ int3
+
+; ff76
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jv16_wrap_forward__ud2), function, 5
+ db 0e9h ; jmp (0xff76+4 + 0x88 = 0x10002 (65538))
+ dw 089h
+ int3
+
+; ff7a
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jv16_opsize_wrap_forward__ud2), function, 7
+ db 066h, 0e9h ; o32 jmp (0xff7a+6 + 0x82 = 0x10002 (65538))
+ dd 082h
+ int3
+
+; ff81
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jb_wrap_forward__ud2), function, 2
+ db 0ebh, 07fh ; jmp (0xff81+2 + 0x7f = 0x10002 (65538))
+ int3
+
+; ff84
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_jb_opsize_wrap_forward__ud2), function, 3
+ db 066h, 0ebh, 07bh ; o32 jmp (0xff84+3 + 0x7b = 0x10002 (65538))
+; ff87
+
+ times (0xfff8 - 0xff87) int3
+
+; fff8: This is the target for backward wrap around jumps, should they succeed.
+BS3_GLOBAL_NAME_EX NAME(bs3CpuBasic2_jmp_target_wrap_backward), function, 2
+ ud2
+ times 6 int3
+; End of segment.
+
+BS3_BEGIN_TEXT16
+
+;
+; Instantiate code templates.
+;
+BS3_INSTANTIATE_COMMON_TEMPLATE "bs3-cpu-basic-2-template.mac"
+BS3_INSTANTIATE_TEMPLATE_WITH_WEIRD_ONES "bs3-cpu-basic-2-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32
new file mode 100644
index 00000000..1323f092
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32
@@ -0,0 +1,1890 @@
+/* $Id: bs3-cpu-basic-2-pf.c32 $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, 32-bit C code for testing \#PF.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define CHECK_MEMBER(a_pszMode, a_szName, a_szFmt, a_Actual, a_Expected) \
+ do { \
+ if ((a_Actual) == (a_Expected)) { /* likely */ } \
+ else Bs3TestFailedF("%u - %s: " a_szName "=" a_szFmt " expected " a_szFmt, \
+ g_usBs3TestStep, (a_pszMode), (a_Actual), (a_Expected)); \
+ } while (0)
+
+#define BS3CPUBASIC2PF_HALT(pThis) \
+ do { \
+ Bs3TestPrintf("Halting: pteworker=%s store=%s accessor=%s\n", \
+ pThis->pszPteWorker, pThis->pszStore, pThis->pszAccessor); \
+ ASMHalt(); \
+ } while (0)
+
+
+/** @def BS3CPUBASIC2PF_FASTER
+ * This is useful for IEM execution. */
+#define BS3CPUBASIC2PF_FASTER
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef void BS3_CALL FNBS3CPUBASIC2PFSNIPPET(void);
+
+typedef struct FNBS3CPUBASIC2PFTSTCODE
+{
+ FNBS3CPUBASIC2PFSNIPPET *pfn;
+ uint8_t offUd2;
+
+} FNBS3CPUBASIC2PFTSTCODE;
+typedef FNBS3CPUBASIC2PFTSTCODE const *PCFNBS3CPUBASIC2PFTSTCODE;
+
+typedef struct BS3CPUBASIC2PFTTSTCMNMODE
+{
+ uint8_t bMode;
+ FNBS3CPUBASIC2PFTSTCODE MovLoad;
+ FNBS3CPUBASIC2PFTSTCODE MovStore;
+ FNBS3CPUBASIC2PFTSTCODE Xchg;
+ FNBS3CPUBASIC2PFTSTCODE CmpXchg;
+ FNBS3CPUBASIC2PFTSTCODE DivMem;
+} BS3CPUBASIC2PFTTSTCMNMODE;
+typedef BS3CPUBASIC2PFTTSTCMNMODE const *PCBS3CPUBASIC2PFTTSTCMNMODE;
+
+
+typedef struct BS3CPUBASIC2PFSTATE
+{
+ /** The mode we're currently testing. */
+ uint8_t bMode;
+ /** The size of a natural access. */
+ uint8_t cbAccess;
+ /** The common mode functions. */
+ PCBS3CPUBASIC2PFTTSTCMNMODE pCmnMode;
+ /** Address of the test area (alias). */
+ union
+ {
+ uint64_t u;
+ uint32_t u32;
+ uint16_t u16;
+ } uTestAddr;
+ /** Pointer to the orignal test area mapping. */
+ uint8_t *pbOrgTest;
+ /** The size of the test area (at least two pages). */
+ uint32_t cbTest;
+ /** cbTest expressed as a page count. */
+ uint16_t cTestPages;
+ /** The number of PTEs in the first PTE, i.e. what we can
+ * safely access via PgInfo.u.Pae.pPte/PgInfo.u.Legacy.pPte. */
+ uint16_t cTest1stPtes;
+ /** The number of PDEs for cTestPages. */
+ uint16_t cTestPdes;
+ /** 16-bit data selector for uTestAddr.u32. */
+ uint16_t uSel16TestData;
+ /** 16-bit code selector for uTestAddr.u32. */
+ uint16_t uSel16TestCode;
+ /** The size of the PDE backup. */
+ uint16_t cbPdeBackup;
+ /** The size of the PTE backup. */
+ uint16_t cbPteBackup;
+ /** Test paging information for uTestAddr.u. */
+ BS3PAGINGINFO4ADDR PgInfo;
+
+ /** Set if we can use the INVLPG instruction. */
+ bool fUseInvlPg;
+ /** Physical addressing width. */
+ uint8_t cBitsPhysWidth;
+
+ /** Reflects CR0.WP. */
+ bool fWp;
+ /** Reflects EFER.NXE & CR4.PAE. */
+ bool fNxe;
+
+ const char *pszAccessor;
+ const char *pszPteWorker;
+ const char *pszStore;
+
+ /** Trap context frame. */
+ BS3TRAPFRAME TrapCtx;
+ /** Expected result context. */
+ BS3REGCTX ExpectCtx;
+
+ /** The PML4E backup. */
+ uint64_t u64Pml4eBackup;
+ /** The PDPTE backup. */
+ uint64_t u64PdpteBackup;
+ /** The PDE backup. */
+ uint64_t au64PdeBackup[16];
+ /** The PTE backup. */
+ union
+ {
+ uint32_t Legacy[X86_PG_ENTRIES];
+ uint64_t Pae[X86_PG_PAE_ENTRIES];
+ } PteBackup;
+
+} BS3CPUBASIC2PFSTATE;
+/** Pointer to state for the \#PF test. */
+typedef BS3CPUBASIC2PFSTATE *PBS3CPUBASIC2PFSTATE;
+
+
+/**
+ * Paging modification worker.
+ */
+typedef struct BS3CPUBASIC2PFMODPT
+{
+ const char *pszName;
+ uint32_t fPresent : 1;
+ uint32_t fUser : 1;
+ uint32_t fWriteable : 1;
+ uint32_t fNoExecute : 1;
+ uint32_t fReserved : 1;
+ uint32_t uModifyArg : 24;
+ void (*pfnModify)(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, struct BS3CPUBASIC2PFMODPT const *pEntry,
+ uint32_t fClearMask, uint32_t fSetMask);
+ bool (*pfnApplicable)(PBS3CPUBASIC2PFSTATE pThis, struct BS3CPUBASIC2PFMODPT const *pEntry);
+} BS3CPUBASIC2PFMODPT;
+typedef BS3CPUBASIC2PFMODPT const *PCBS3CPUBASIC2PFMODPT;
+
+/** Page level protection. Alternative is page directory or higher level. */
+#define BS3CB2PFACC_F_PAGE_LEVEL RT_BIT(0)
+/** Directly access the boobytrapped page, no edging on or off it. */
+#define BS3CB2PFACC_F_DIRECT RT_BIT(1)
+
+/**
+ * Memory accessor.
+ */
+typedef struct BS3CPUBASIC2PFACCESSOR
+{
+ /** Accessor name. */
+ const char *pszName;
+ /** The accessor. */
+ void (*pfnAccessor)(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd);
+ /** The X86_TRAP_PF_XXX access flags this access sets. */
+ uint32_t fAccess;
+ /** The exception when things are fine. */
+ uint8_t bOkayXcpt;
+} BS3CPUBASIC2PFACCESSOR;
+typedef const BS3CPUBASIC2PFACCESSOR *PCBS3CPUBASIC2PFACCESSOR;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+FNBS3TESTDOMODE bs3CpuBasic2_RaiseXcpt0e_c32;
+
+/* bs3-cpu-basic-2-asm.asm: */
+void BS3_CALL bs3CpuBasic2_Store_mov_c32(void *pvDst, uint32_t uValue, uint32_t uOld);
+void BS3_CALL bs3CpuBasic2_Store_xchg_c32(void *pvDst, uint32_t uValue, uint32_t uOld);
+void BS3_CALL bs3CpuBasic2_Store_cmpxchg_c32(void *pvDst, uint32_t uValue, uint32_t uOld);
+
+
+/* bs3-cpu-basic-2-template.mac: */
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c16;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c16;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c16;
+
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c32;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c32;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c32;
+
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c64;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c64;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64;
+FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c64;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Page table access functions. */
+static const struct
+{
+ const char *pszName;
+ void (BS3_CALL *pfnStore)(void *pvDst, uint32_t uValue, uint32_t uOld);
+} g_aStoreMethods[] =
+{
+ { "mov", bs3CpuBasic2_Store_mov_c32 },
+ { "xchg", bs3CpuBasic2_Store_xchg_c32 },
+ { "cmpxchg", bs3CpuBasic2_Store_cmpxchg_c32 },
+};
+
+
+static const BS3CPUBASIC2PFTTSTCMNMODE g_aCmnModes[] =
+{
+ {
+ BS3_MODE_CODE_16,
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c16, 2 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c16, 2 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16, 2 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16, 3 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c16, 2 },
+ },
+ {
+ BS3_MODE_CODE_32,
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c32, 2 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c32, 2 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32, 2 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32, 3 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c32, 2 },
+ },
+ {
+ BS3_MODE_CODE_64,
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c64, 2 + 1 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c64, 2 + 1 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64, 2 + 1 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64, 3 + 1 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c64, 2 + 1 },
+ },
+ {
+ BS3_MODE_CODE_V86,
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c16, 2 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c16, 2 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16, 2 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16, 3 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c16, 2 },
+ },
+};
+
+
+/**
+ * Compares a CPU trap.
+ */
+static void bs3CpuBasic2Pf_CompareCtx(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pExpectCtx, int cbPcAdjust,
+ uint8_t bXcpt, unsigned uErrCd)
+{
+ const char *pszHint = "xxxx";
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint32_t fExtraEfl;
+
+ CHECK_MEMBER(pszHint, "bXcpt", "%#04x", pThis->TrapCtx.bXcpt, bXcpt);
+ CHECK_MEMBER(pszHint, "uErrCd", "%#06RX16", (uint16_t)pThis->TrapCtx.uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */
+
+ fExtraEfl = X86_EFL_RF;
+ if (BS3_MODE_IS_16BIT_SYS(g_bBs3CurrentMode))
+ fExtraEfl = 0;
+ else
+ fExtraEfl = X86_EFL_RF;
+ Bs3TestCheckRegCtxEx(&pThis->TrapCtx.Ctx, pExpectCtx, cbPcAdjust, 0 /*cbSpAdjust*/, fExtraEfl, pszHint, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(&pThis->TrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd);
+ BS3CPUBASIC2PF_HALT(pThis);
+#endif
+ }
+}
+
+
+/**
+ * Compares a CPU trap.
+ */
+static void bs3CpuBasic2Pf_CompareSimpleCtx(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pStartCtx, int offAddPC,
+ uint8_t bXcpt, unsigned uErrCd, uint64_t uCr2)
+{
+ const char *pszHint = "xxxx";
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint64_t const uSavedCr2 = pStartCtx->cr2.u;
+ uint32_t fExtraEfl;
+
+ CHECK_MEMBER(pszHint, "bXcpt", "%#04x", pThis->TrapCtx.bXcpt, bXcpt);
+ CHECK_MEMBER(pszHint, "uErrCd", "%#06RX16", (uint16_t)pThis->TrapCtx.uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */
+
+ fExtraEfl = X86_EFL_RF;
+ if (BS3_MODE_IS_16BIT_SYS(g_bBs3CurrentMode))
+ fExtraEfl = 0;
+ else
+ fExtraEfl = X86_EFL_RF;
+ pStartCtx->cr2.u = uCr2;
+ Bs3TestCheckRegCtxEx(&pThis->TrapCtx.Ctx, pStartCtx, offAddPC, 0 /*cbSpAdjust*/, fExtraEfl, pszHint, g_usBs3TestStep);
+ pStartCtx->cr2.u = uSavedCr2;
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(&pThis->TrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd);
+ BS3CPUBASIC2PF_HALT(pThis);
+#endif
+ }
+}
+
+
+/**
+ * Checks the trap context for a simple \#PF trap.
+ */
+static void bs3CpuBasic2Pf_CompareSimplePf(PBS3CPUBASIC2PFSTATE pThis, PCBS3REGCTX pStartCtx, int offAddPC,
+ unsigned uErrCd, uint64_t uCr2)
+{
+ bs3CpuBasic2Pf_CompareSimpleCtx(pThis, (PBS3REGCTX)pStartCtx, offAddPC, X86_XCPT_PF, uErrCd, uCr2);
+}
+
+/**
+ * Checks the trap context for a simple \#UD trap.
+ */
+static void bs3CpuBasic2Pf_CompareSimpleUd(PBS3CPUBASIC2PFSTATE pThis, PCBS3REGCTX pStartCtx, int offAddPC)
+{
+ bs3CpuBasic2Pf_CompareSimpleCtx(pThis, (PBS3REGCTX)pStartCtx, offAddPC, X86_XCPT_UD, 0, pStartCtx->cr2.u);
+}
+
+
+/**
+ * Restores all the paging entries from backup and flushes everything.
+ */
+static void bs3CpuBasic2Pf_FlushAll(void)
+{
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ {
+ uint32_t uCr4 = ASMGetCR4();
+ if (uCr4 & (X86_CR4_PGE | X86_CR4_PCIDE))
+ {
+ ASMSetCR4(uCr4 & ~(X86_CR4_PGE | X86_CR4_PCIDE));
+ ASMSetCR4(uCr4);
+ return;
+ }
+ }
+
+ ASMReloadCR3();
+}
+
+
+/**
+ * Restores all the paging entries from backup and flushes everything.
+ *
+ * @param pThis Test state data.
+ */
+static void bs3CpuBasic2Pf_RestoreFromBackups(PBS3CPUBASIC2PFSTATE pThis)
+{
+ Bs3MemCpy(pThis->PgInfo.u.Legacy.pPte, &pThis->PteBackup, pThis->cbPteBackup);
+ Bs3MemCpy(pThis->PgInfo.u.Legacy.pPde, pThis->au64PdeBackup, pThis->cbPdeBackup);
+ if (pThis->PgInfo.cEntries > 2)
+ pThis->PgInfo.u.Pae.pPdpe->u = pThis->u64PdpteBackup;
+ if (pThis->PgInfo.cEntries > 3)
+ pThis->PgInfo.u.Pae.pPml4e->u = pThis->u64Pml4eBackup;
+ bs3CpuBasic2Pf_FlushAll();
+}
+
+
+/** @name BS3CPUBASIC2PFACCESSOR::pfnAccessor Implementations
+ * @{ */
+
+static void bs3CpuBasic2Pf_DoExec(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ uint8_t *pbOrgTest = pThis->pbOrgTest;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE + 1 : X86_PAGE_SIZE + 2;
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? offEnd - 1 : X86_PAGE_SIZE - 5;
+
+ for (; off < offEnd; off++)
+ {
+ /* Emit a little bit of code (using the original allocation mapping) and point pCtx to it. */
+ pbOrgTest[off + 0] = X86_OP_PRF_SIZE_ADDR;
+ pbOrgTest[off + 1] = X86_OP_PRF_SIZE_OP;
+ pbOrgTest[off + 2] = 0x90; /* NOP */
+ pbOrgTest[off + 3] = 0x0f; /* UD2 */
+ pbOrgTest[off + 4] = 0x0b;
+ pbOrgTest[off + 5] = 0xeb; /* JMP $-4 */
+ pbOrgTest[off + 6] = 0xfc;
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ default:
+ pCtx->rip.u = pThis->uTestAddr.u + off;
+ break;
+ case BS3_MODE_CODE_16:
+ Bs3SelSetup16BitCode(&Bs3GdteSpare01, pThis->uTestAddr.u32, pCtx->bCpl);
+ pCtx->rip.u = off;
+ pCtx->cs = BS3_SEL_SPARE_01 | pCtx->bCpl;
+ break;
+ case BS3_MODE_CODE_V86:
+ /** @todo fix me. */
+ return;
+ }
+ //Bs3TestPrintf("cs:rip=%04x:%010RX64 iRing=%d\n", pCtx->cs, pCtx->rip.u, pCtx->bCpl);
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off < X86_PAGE_SIZE - 4))
+ bs3CpuBasic2Pf_CompareSimpleUd(pThis, pCtx, 3);
+ else if (!(fFlags & BS3CB2PFACC_F_PAGE_LEVEL) || off >= X86_PAGE_SIZE)
+ bs3CpuBasic2Pf_CompareSimplePf(pThis, pCtx, 0, uPfErrCd, pThis->uTestAddr.u + off);
+ else
+ bs3CpuBasic2Pf_CompareSimplePf(pThis, pCtx,
+ off + 3 == X86_PAGE_SIZE || off + 4 == X86_PAGE_SIZE
+ ? RT_MIN(X86_PAGE_SIZE, off + 3) - off : 0,
+ uPfErrCd, pThis->uTestAddr.u + RT_MIN(X86_PAGE_SIZE, off + 4));
+ }
+}
+
+
+static void bs3CpuBasic2Pf_SetCsEip(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, PCFNBS3CPUBASIC2PFTSTCODE pCode)
+{
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ default:
+ pCtx->rip.u = (uintptr_t)pCode->pfn;
+ break;
+
+ case BS3_MODE_CODE_16:
+ {
+ uint32_t uFar16 = Bs3SelFlatCodeToProtFar16((uintptr_t)pCode->pfn);
+ pCtx->rip.u = (uint16_t)uFar16;
+ pCtx->cs = (uint16_t)(uFar16 >> 16) | pCtx->bCpl;
+ pCtx->cs += (uint16_t)pCtx->bCpl << BS3_SEL_RING_SHIFT;
+ break;
+ }
+
+ case BS3_MODE_CODE_V86:
+ {
+ uint32_t uFar16 = Bs3SelFlatCodeToRealMode((uintptr_t)pCode->pfn);
+ pCtx->rip.u = (uint16_t)uFar16;
+ pCtx->cs = (uint16_t)(uFar16 >> 16);
+ break;
+ }
+ }
+}
+
+
+/**
+ * Test a simple load instruction around the edges of page two.
+ *
+ * @param pThis The test stat data.
+ * @param pCtx The test context.
+ * @param fFlags BS3CB2PFACC_F_XXX.
+ * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise
+ * X86_XCPT_UD.
+ * @param uPfErrCd The error code for \#PFs.
+ */
+static void bs3CpuBasic2Pf_DoMovLoad(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ static uint64_t const s_uValue = UINT64_C(0x7c4d0114428d);
+ uint64_t uExpectRax;
+ unsigned i;
+
+ /*
+ * Adjust the incoming context and calculate our expections.
+ */
+ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->MovLoad);
+ Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx));
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16:
+ case BS3_MODE_CODE_V86:
+ uExpectRax = (uint16_t)s_uValue | (pCtx->rax.u & UINT64_C(0xffffffffffff0000));
+ break;
+ case BS3_MODE_CODE_32:
+ uExpectRax = (uint32_t)s_uValue | (pCtx->rax.u & UINT64_C(0xffffffff00000000));
+ break;
+ case BS3_MODE_CODE_64:
+ uExpectRax = s_uValue;
+ break;
+ }
+ if (uExpectRax == pCtx->rax.u)
+ pCtx->rax.u = ~pCtx->rax.u;
+
+ /*
+ * Make two approaches to the test page (the 2nd one):
+ * - i=0: Start on the 1st page and edge into the 2nd.
+ * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7);
+
+ for (; off < offEnd; off++)
+ {
+ *(uint64_t *)&pThis->pbOrgTest[off] = s_uValue;
+ if (BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off;
+ else
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off;
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2)
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) )
+ {
+ pThis->ExpectCtx.rax.u = uExpectRax;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovLoad.offUd2, X86_XCPT_UD, 0 /*uErrCd*/);
+ pThis->ExpectCtx.rax = pCtx->rax;
+ }
+ else
+ {
+ if (off < X86_PAGE_SIZE)
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE;
+ else
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd);
+ pThis->ExpectCtx.cr2 = pCtx->cr2;
+ }
+ }
+
+ if (fFlags & BS3CB2PFACC_F_DIRECT)
+ break;
+ }
+}
+
+
+/**
+ * Test a simple store instruction around the edges of page two.
+ *
+ * @param pThis The test stat data.
+ * @param pCtx The test context.
+ * @param fFlags BS3CB2PFACC_F_XXX.
+ * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise
+ * X86_XCPT_UD.
+ * @param uPfErrCd The error code for \#PFs.
+ */
+static void bs3CpuBasic2Pf_DoMovStore(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags,
+ uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ static uint64_t const s_uValue = UINT64_C(0x3af45ead86a34a26);
+ static uint64_t const s_uValueFlipped = UINT64_C(0xc50ba152795cb5d9);
+ uint64_t const uRaxSaved = pCtx->rax.u;
+ uint64_t uExpectStored;
+ unsigned i;
+
+ /*
+ * Adjust the incoming context and calculate our expections.
+ */
+ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->MovStore);
+ if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64)
+ pCtx->rax.u = (uint32_t)s_uValue; /* leave the upper part zero */
+ else
+ pCtx->rax.u = s_uValue;
+
+ Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx));
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16:
+ case BS3_MODE_CODE_V86:
+ uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000));
+ break;
+ case BS3_MODE_CODE_32:
+ uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000));
+ break;
+ case BS3_MODE_CODE_64:
+ uExpectStored = s_uValue;
+ break;
+ }
+
+ /*
+ * Make two approaches to the test page (the 2nd one):
+ * - i=0: Start on the 1st page and edge into the 2nd.
+ * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7);
+ for (; off < offEnd; off++)
+ {
+ *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped;
+ if (BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off;
+ else
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off;
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2)
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) )
+ {
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovStore.offUd2, X86_XCPT_UD, 0 /*uErrCd*/);
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored)
+ Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored);
+ }
+ else
+ {
+ if (off < X86_PAGE_SIZE)
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE;
+ else
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd);
+ pThis->ExpectCtx.cr2 = pCtx->cr2;
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped)
+ Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped);
+
+ }
+ }
+
+ if (fFlags & BS3CB2PFACC_F_DIRECT)
+ break;
+ }
+
+ pCtx->rax.u = uRaxSaved;
+}
+
+
+/**
+ * Test a xchg instruction around the edges of page two.
+ *
+ * @param pThis The test stat data.
+ * @param pCtx The test context.
+ * @param fFlags BS3CB2PFACC_F_XXX.
+ * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise
+ * X86_XCPT_UD.
+ * @param uPfErrCd The error code for \#PFs.
+ */
+static void bs3CpuBasic2Pf_DoXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ static uint64_t const s_uValue = UINT64_C(0xea58699648e2f32c);
+ static uint64_t const s_uValueFlipped = UINT64_C(0x15a79669b71d0cd3);
+ uint64_t const uRaxSaved = pCtx->rax.u;
+ uint64_t uRaxIn;
+ uint64_t uExpectedRax;
+ uint64_t uExpectStored;
+ unsigned i;
+
+ /*
+ * Adjust the incoming context and calculate our expections.
+ */
+ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->Xchg);
+ if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64)
+ uRaxIn = (uint32_t)s_uValue; /* leave the upper part zero */
+ else
+ uRaxIn = s_uValue;
+
+ Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx));
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16:
+ case BS3_MODE_CODE_V86:
+ uExpectedRax = (uint16_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffffffff0000));
+ uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000));
+ break;
+ case BS3_MODE_CODE_32:
+ uExpectedRax = (uint32_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffff00000000));
+ uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000));
+ break;
+ case BS3_MODE_CODE_64:
+ uExpectedRax = s_uValueFlipped;
+ uExpectStored = s_uValue;
+ break;
+ }
+
+ /*
+ * Make two approaches to the test page (the 2nd one):
+ * - i=0: Start on the 1st page and edge into the 2nd.
+ * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7);
+ for (; off < offEnd; off++)
+ {
+ *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped;
+ pCtx->rax.u = uRaxIn;
+ if (BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off;
+ else
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off;
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2)
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) )
+ {
+ pThis->ExpectCtx.rax.u = uExpectedRax;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->Xchg.offUd2, X86_XCPT_UD, 0 /*uErrCd*/);
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored)
+ Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored);
+ }
+ else
+ {
+ pThis->ExpectCtx.rax.u = uRaxIn;
+ if (off < X86_PAGE_SIZE)
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE;
+ else
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd);
+ pThis->ExpectCtx.cr2 = pCtx->cr2;
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped)
+ Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped);
+ }
+ }
+
+ if (fFlags & BS3CB2PFACC_F_DIRECT)
+ break;
+ }
+
+ pCtx->rax.u = uRaxSaved;
+}
+
+
+/**
+ * Test a cmpxchg instruction around the edges of page two.
+ *
+ * @param pThis The test stat data.
+ * @param pCtx The test context.
+ * @param fFlags BS3CB2PFACC_F_XXX.
+ * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise
+ * X86_XCPT_UD.
+ * @param uPfErrCd The error code for \#PFs.
+ * @param fMissmatch Whether to fail and not store (@c true), or succeed
+ * and do the store.
+ */
+static void bs3CpuBasic2Pf_DoCmpXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags,
+ uint8_t bXcpt, uint8_t uPfErrCd, bool fMissmatch)
+{
+ static uint64_t const s_uValue = UINT64_C(0xea58699648e2f32c);
+ static uint64_t const s_uValueFlipped = UINT64_C(0x15a79669b71d0cd3);
+ static uint64_t const s_uValueOther = UINT64_C(0x2171239bcb044c81);
+ uint64_t const uRaxSaved = pCtx->rax.u;
+ uint64_t const uRcxSaved = pCtx->rcx.u;
+ uint64_t uRaxIn;
+ uint64_t uExpectedRax;
+ uint32_t uExpectedFlags;
+ uint64_t uExpectStored;
+ unsigned i;
+
+ /*
+ * Adjust the incoming context and calculate our expections.
+ * Hint: CMPXCHG [xBX],xCX ; xAX compare and update implicit, ZF set to !fMissmatch.
+ */
+ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->CmpXchg);
+ if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64)
+ {
+ uRaxIn = (uint32_t)(fMissmatch ? s_uValueOther : s_uValueFlipped); /* leave the upper part zero */
+ pCtx->rcx.u = (uint32_t)s_uValue; /* ditto */
+ }
+ else
+ {
+ uRaxIn = fMissmatch ? s_uValueOther : s_uValueFlipped;
+ pCtx->rcx.u = s_uValue;
+ }
+ if (fMissmatch)
+ pCtx->rflags.u32 |= X86_EFL_ZF;
+ else
+ pCtx->rflags.u32 &= ~X86_EFL_ZF;
+
+ Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx));
+ uExpectedFlags = pCtx->rflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_SF | X86_EFL_OF | X86_EFL_ZF);
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16:
+ case BS3_MODE_CODE_V86:
+ uExpectedRax = (uint16_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffffffff0000));
+ uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000));
+ uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF;
+ break;
+ case BS3_MODE_CODE_32:
+ uExpectedRax = (uint32_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffff00000000));
+ uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000));
+ uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF;
+ break;
+ case BS3_MODE_CODE_64:
+ uExpectedRax = s_uValueFlipped;
+ uExpectStored = s_uValue;
+ uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF;
+ break;
+ }
+ if (fMissmatch)
+ uExpectStored = s_uValueFlipped;
+
+ /*
+ * Make two approaches to the test page (the 2nd one):
+ * - i=0: Start on the 1st page and edge into the 2nd.
+ * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7);
+ for (; off < offEnd; off++)
+ {
+ *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped;
+ pCtx->rax.u = uRaxIn;
+ if (BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off;
+ else
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off;
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2)
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) )
+ {
+ pThis->ExpectCtx.rax.u = uExpectedRax;
+ pThis->ExpectCtx.rflags.u32 = uExpectedFlags;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->CmpXchg.offUd2, X86_XCPT_UD, 0 /*uErrCd*/);
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored)
+ Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored);
+ }
+ else
+ {
+ pThis->ExpectCtx.rax.u = uRaxIn;
+ pThis->ExpectCtx.rflags = pCtx->rflags;
+ if (off < X86_PAGE_SIZE)
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE;
+ else
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd);
+ pThis->ExpectCtx.cr2 = pCtx->cr2;
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped)
+ Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped);
+ }
+ }
+
+ if (fFlags & BS3CB2PFACC_F_DIRECT)
+ break;
+ }
+
+ pCtx->rax.u = uRaxSaved;
+ pCtx->rcx.u = uRcxSaved;
+}
+
+
+static void bs3CpuBasic2Pf_DoCmpXchgMiss(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags,
+ uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ bs3CpuBasic2Pf_DoCmpXchg(pThis, pCtx, fFlags, bXcpt, uPfErrCd, true /*fMissmatch*/ );
+}
+
+
+static void bs3CpuBasic2Pf_DoCmpXchgMatch(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags,
+ uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ bs3CpuBasic2Pf_DoCmpXchg(pThis, pCtx, fFlags, bXcpt, uPfErrCd , false /*fMissmatch*/ );
+}
+
+
+/**
+ * @interface_method_impl{BS3CPUBASIC2PFACCESSOR,pfnAccessor,
+ * DIV [MEM=0] for checking the accessed bit}
+ */
+static void bs3CpuBasic2Pf_DoDivByZero(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags,
+ uint8_t bXcpt, uint8_t uPfErrCd)
+{
+ static uint64_t const s_uFiller = UINT64_C(0x9856703711f4069e);
+ uint64_t uZeroAndFill;
+ unsigned i;
+
+ /*
+ * Adjust the incoming context and calculate our expections.
+ */
+ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->DivMem);
+
+ Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx));
+ switch (pThis->bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16:
+ case BS3_MODE_CODE_V86:
+ uZeroAndFill = s_uFiller & UINT64_C(0xffffffffffff0000);
+ break;
+ case BS3_MODE_CODE_32:
+ uZeroAndFill = s_uFiller & UINT64_C(0xffffffff00000000);
+ break;
+ case BS3_MODE_CODE_64:
+ uZeroAndFill = 0;
+ break;
+ }
+
+ /*
+ * Make two approaches to the test page (the 2nd one):
+ * - i=0: Start on the 1st page and edge into the 2nd.
+ * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess;
+ unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7);
+ for (; off < offEnd; off++)
+ {
+ *(uint64_t *)&pThis->pbOrgTest[off] = uZeroAndFill;
+ if (BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off;
+ else
+ pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off;
+
+ Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx);
+ //if (pThis->bMode == BS3_MODE_PP16_32) Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd);
+
+ if ( bXcpt != X86_XCPT_PF
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2)
+ || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) )
+ {
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, X86_XCPT_DE, 0 /*uErrCd*/);
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != uZeroAndFill)
+ Bs3TestFailedF("%u - %s: Modified source op: %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uZeroAndFill);
+ }
+ else
+ {
+ if (off < X86_PAGE_SIZE)
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE;
+ else
+ pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off;
+ bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd);
+ pThis->ExpectCtx.cr2 = pCtx->cr2;
+ if (*(uint64_t *)&pThis->pbOrgTest[off] != uZeroAndFill)
+ Bs3TestFailedF("%u - %s: Modified source op: %#RX64, expected %#RX64",
+ g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uZeroAndFill);
+ }
+ }
+
+ if (fFlags & BS3CB2PFACC_F_DIRECT)
+ break;
+ }
+}
+
+
+static BS3CPUBASIC2PFACCESSOR const g_aAccessors[] =
+{
+ { "DoExec", bs3CpuBasic2Pf_DoExec, X86_TRAP_PF_ID, X86_XCPT_UD },
+ { "DoMovLoad", bs3CpuBasic2Pf_DoMovLoad, 0, X86_XCPT_UD },
+ { "DoMovStore", bs3CpuBasic2Pf_DoMovStore, X86_TRAP_PF_RW, X86_XCPT_UD },
+ { "DoXchg", bs3CpuBasic2Pf_DoXchg, X86_TRAP_PF_RW, X86_XCPT_UD },
+ { "DoCmpXchgMiss", bs3CpuBasic2Pf_DoCmpXchgMiss, X86_TRAP_PF_RW, X86_XCPT_UD },
+ { "DoCmpXhcgMatch", bs3CpuBasic2Pf_DoCmpXchgMatch, X86_TRAP_PF_RW, X86_XCPT_UD },
+ { "DoDivByZero", bs3CpuBasic2Pf_DoDivByZero, 0, X86_XCPT_DE },
+};
+
+/** @} */
+
+
+/** @name BS3CPUBASIC2PFMODPT::pfnModify implementations.
+ * @{ */
+
+
+static void bs3CpuBasic2Pf_ClearMask(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry,
+ uint32_t fClearMask, uint32_t fSetMask)
+{
+ if (pThis->PgInfo.cbEntry == 4)
+ {
+ uint32_t const uOrg = pThis->PteBackup.Legacy[1];
+ uint32_t uNew = ((uOrg & ~fClearMask) | fSetMask) & ~(uint32_t)pEntry->uModifyArg;
+ uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u;
+ g_aStoreMethods[iStore].pfnStore(pThis->PgInfo.u.Legacy.pPte + 1, uNew, uOld);
+ }
+ else
+ {
+ uint64_t const uOrg = pThis->PteBackup.Pae[1];
+ uint64_t uNew = ((uOrg & ~(uint64_t)fClearMask) | fSetMask) & ~(uint64_t)pEntry->uModifyArg;
+ uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u;
+
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld);
+ if ((uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32))
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1],
+ (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32));
+ }
+}
+
+static void bs3CpuBasic2Pf_SetBit(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry,
+ uint32_t fClearMask, uint32_t fSetMask)
+{
+ if (pThis->PgInfo.cbEntry == 4)
+ {
+ uint32_t const uOrg = pThis->PteBackup.Legacy[1];
+ uint32_t uNew = (uOrg & ~fClearMask) | fSetMask | RT_BIT_32(pEntry->uModifyArg);
+ uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u;
+ g_aStoreMethods[iStore].pfnStore(pThis->PgInfo.u.Legacy.pPte + 1, uNew, uOld);
+ }
+ else
+ {
+ uint64_t const uOrg = pThis->PteBackup.Pae[1];
+ uint64_t uNew = ((uOrg & ~(uint64_t)fClearMask) | fSetMask) | RT_BIT_64(pEntry->uModifyArg);
+ uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u;
+
+ if (pEntry->uModifyArg < 32 || (uint32_t)uNew != (uint32_t)uOld)
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld);
+ if (pEntry->uModifyArg >= 32 || (uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32))
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1],
+ (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32));
+ }
+}
+
+static void bs3CpuBasic2Pf_NoChange(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry,
+ uint32_t fClearMask, uint32_t fSetMask)
+{
+ if (pThis->PgInfo.cbEntry == 4)
+ {
+ uint32_t const uOrg = pThis->PteBackup.Legacy[1];
+ uint32_t uNew = (uOrg & ~fClearMask) | fSetMask;
+ uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u;
+ if (uNew != uOld)
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Legacy.pPte[1], uNew, uOld);
+ }
+ else
+ {
+ uint64_t const uOrg = pThis->PteBackup.Pae[1];
+ uint64_t uNew = (uOrg & ~(uint64_t)fClearMask) | fSetMask;
+ uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u;
+ if (uNew != uOld)
+ {
+ if ((uint32_t)uNew != (uint32_t)uOld)
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld);
+ if ((uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32))
+ g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1],
+ (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32));
+ }
+ }
+}
+
+/** @} */
+
+
+/** @name BS3CPUBASIC2PFMODPT::pfnApplicable implementations.
+ * @{ */
+
+static bool bs3CpuBasic2Pf_IsPteBitReserved(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry)
+{
+ if (pThis->PgInfo.cbEntry == 8)
+ {
+ /* Bits 52..63 or 62 (NXE=1). */
+ if (pThis->PgInfo.cEntries == 3)
+ {
+ if ((uint32_t)(pEntry->uModifyArg - 52U) < (uint32_t)(12 - pThis->fNxe))
+ return true;
+ }
+ else if (pEntry->uModifyArg == 63 && !pThis->fNxe)
+ return true;
+
+ /* Reserved physical address bits. */
+ if (pEntry->uModifyArg < 52)
+ {
+ if ((uint32_t)pEntry->uModifyArg >= (uint32_t)pThis->cBitsPhysWidth)
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool bs3CpuBasic2Pf_IsPteBitSoftwareUsable(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry)
+{
+ if (pThis->PgInfo.cbEntry == 8)
+ {
+ if (pThis->PgInfo.cEntries != 3)
+ {
+ if ((uint32_t)(pEntry->uModifyArg - 52U) < (uint32_t)11)
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static bool bs3CpuBasic2Pf_IsNxe(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry)
+{
+ return pThis->fNxe && pThis->PgInfo.cbEntry == 8;
+}
+
+/** @} */
+
+
+static const BS3CPUBASIC2PFMODPT g_aPteWorkers[] =
+{
+/* { pszName, P U W NX RSV ModiyfArg pfnModify, pfnApplicable }, */
+ { "org", 1, 1, 1, 0, 0, 0, bs3CpuBasic2Pf_NoChange, NULL },
+ { "!US", 1, 0, 1, 0, 0, X86_PTE_US, bs3CpuBasic2Pf_ClearMask, NULL },
+ { "!RW", 1, 1, 0, 0, 0, X86_PTE_RW, bs3CpuBasic2Pf_ClearMask, NULL },
+ { "!RW+!US", 1, 0, 0, 0, 0, X86_PTE_RW | X86_PTE_US, bs3CpuBasic2Pf_ClearMask, NULL },
+ { "!P", 0, 0, 0, 0, 0, X86_PTE_P, bs3CpuBasic2Pf_ClearMask, NULL },
+ { "NX", 1, 1, 1, 1, 0, 63, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsNxe },
+ { "RSVPH[32]", 0, 0, 0, 0, 1, 32, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[33]", 0, 0, 0, 0, 1, 33, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[34]", 0, 0, 0, 0, 1, 34, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[35]", 0, 0, 0, 0, 1, 35, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[36]", 0, 0, 0, 0, 1, 36, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[37]", 0, 0, 0, 0, 1, 37, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[38]", 0, 0, 0, 0, 1, 38, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[39]", 0, 0, 0, 0, 1, 39, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[40]", 0, 0, 0, 0, 1, 40, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[41]", 0, 0, 0, 0, 1, 41, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[42]", 0, 0, 0, 0, 1, 42, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[43]", 0, 0, 0, 0, 1, 43, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[44]", 0, 0, 0, 0, 1, 44, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[45]", 0, 0, 0, 0, 1, 45, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[46]", 0, 0, 0, 0, 1, 46, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[47]", 0, 0, 0, 0, 1, 47, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[48]", 0, 0, 0, 0, 1, 48, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[49]", 0, 0, 0, 0, 1, 49, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[50]", 0, 0, 0, 0, 1, 50, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSVPH[51]", 0, 0, 0, 0, 1, 51, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[52]", 0, 0, 0, 0, 1, 52, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[53]", 0, 0, 0, 0, 1, 53, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[54]", 0, 0, 0, 0, 1, 54, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[55]", 0, 0, 0, 0, 1, 55, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[56]", 0, 0, 0, 0, 1, 56, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[57]", 0, 0, 0, 0, 1, 57, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[58]", 0, 0, 0, 0, 1, 58, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[59]", 0, 0, 0, 0, 1, 59, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[60]", 0, 0, 0, 0, 1, 60, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[61]", 0, 0, 0, 0, 1, 61, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[62]", 0, 0, 0, 0, 1, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[62]", 0, 0, 0, 0, 1, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "RSV[63]", 0, 0, 0, 0, 1, 63, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved },
+ { "!RSV[52]", 1, 1, 1, 0, 0, 52, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[53]", 1, 1, 1, 0, 0, 53, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[54]", 1, 1, 1, 0, 0, 54, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[55]", 1, 1, 1, 0, 0, 55, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[56]", 1, 1, 1, 0, 0, 56, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[57]", 1, 1, 1, 0, 0, 57, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[58]", 1, 1, 1, 0, 0, 58, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[59]", 1, 1, 1, 0, 0, 59, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[60]", 1, 1, 1, 0, 0, 60, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[61]", 1, 1, 1, 0, 0, 61, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+ { "!RSV[62]", 1, 1, 1, 0, 0, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable },
+
+};
+
+
+/**
+ * Worker for bs3CpuBasic2_RaiseXcpt0e_c32 that does the actual testing.
+ *
+ * Caller does all the cleaning up.
+ *
+ * @returns Error count.
+ * @param pThis Test state data.
+ * @param fNxe Whether NX is enabled.
+ */
+static uint8_t bs3CpuBasic2_RaiseXcpt0eWorker(PBS3CPUBASIC2PFSTATE register pThis, bool const fWp, bool const fNxe)
+{
+ unsigned iLevel;
+ unsigned iRing;
+ unsigned iStore;
+ unsigned iAccessor;
+ unsigned iOuter;
+ unsigned cPml4Tests;
+ unsigned cPdPtrTests;
+ uint32_t const fPfIdMask = fNxe ? UINT32_MAX : ~X86_TRAP_PF_ID;
+ BS3REGCTX aCtxts[4];
+
+ pThis->fWp = fWp;
+ pThis->fNxe = fNxe;
+
+ /** @todo figure out V8086 testing. */
+ if ((pThis->bMode & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86)
+ return BS3TESTDOMODE_SKIPPED;
+
+
+ /* paranoia: Touch the various big stack structures to ensure the compiler has allocated stack for them. */
+ for (iRing = 0; iRing < RT_ELEMENTS(aCtxts); iRing++)
+ Bs3MemZero(&aCtxts[iRing], sizeof(aCtxts[iRing]));
+
+ /*
+ * Set up a few contexts for testing this stuff.
+ */
+ Bs3RegCtxSaveEx(&aCtxts[0], pThis->bMode, 2048);
+ for (iRing = 1; iRing < 4; iRing++)
+ {
+ aCtxts[iRing] = aCtxts[0];
+ Bs3RegCtxConvertToRingX(&aCtxts[iRing], iRing);
+ }
+
+ if (!BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ {
+ for (iRing = 0; iRing < 4; iRing++)
+ aCtxts[iRing].rbx.u = pThis->uTestAddr.u;
+ }
+ else
+ {
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ aCtxts[iRing].ds = pThis->uSel16TestData;
+ aCtxts[iRing].rbx.u = 0;
+ }
+ }
+
+ /*
+ * Check basic operation:
+ */
+ for (iRing = 0; iRing < 4; iRing++)
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ g_aAccessors[iAccessor].pfnAccessor(pThis, &aCtxts[iRing], BS3CB2PFACC_F_PAGE_LEVEL, X86_XCPT_UD, UINT8_MAX);
+
+ /*
+ * Some PTE checks. We only mess with the 2nd page.
+ */
+ for (iOuter = 0; iOuter < 2; iOuter++)
+ {
+ uint32_t const fAccessor = (iOuter == 0 ? BS3CB2PFACC_F_DIRECT : 0) | BS3CB2PFACC_F_PAGE_LEVEL;
+ unsigned iPteWrk;
+
+ bs3CpuBasic2Pf_FlushAll();
+ for (iPteWrk = 0; iPteWrk < RT_ELEMENTS(g_aPteWorkers); iPteWrk++)
+ {
+ BS3CPUBASIC2PFMODPT EffWrk;
+ const BS3CPUBASIC2PFMODPT *pPteWrk = &g_aPteWorkers[iPteWrk];
+ if (pPteWrk->pfnApplicable && !pPteWrk->pfnApplicable(pThis, pPteWrk))
+ continue;
+
+ pThis->pszPteWorker = pPteWrk->pszName;
+
+ EffWrk = *pPteWrk;
+
+#if 1
+ /*
+ * Do the modification once, then test all different accesses
+ * without flushing the TLB or anything in-between.
+ */
+ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++)
+ {
+ pThis->pszStore = g_aStoreMethods[iStore].pszName;
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0);
+
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ PBS3REGCTX const pCtx = &aCtxts[iRing];
+ if ( EffWrk.fReserved
+ || !EffWrk.fPresent
+ || (!EffWrk.fUser && iRing == 3))
+ {
+ uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD
+ : EffWrk.fPresent ? X86_TRAP_PF_P : 0)
+ | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ }
+ }
+ else
+ {
+ uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+ if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID)
+ && EffWrk.fNoExecute)
+ || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ && !EffWrk.fWriteable
+ && (fWp || iRing == 3)) )
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ else
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ }
+ }
+ }
+
+ /* Reset the paging + full flush. */
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+ }
+#endif
+
+#define CHECK_AD_BITS(a_fExpectedAD) \
+ do { \
+ uint32_t fActualAD = ( pThis->PgInfo.cbEntry == 8 \
+ ? pThis->PgInfo.u.Pae.pPte[1].au32[0] : pThis->PgInfo.u.Legacy.pPte[1].au32[0]) \
+ & (X86_PTE_A | X86_PTE_D); \
+ if (fActualAD != (a_fExpectedAD)) \
+ { \
+ Bs3TestFailedF("%u - %s/%u: unexpected A/D bits: %#x, expected %#x\n", \
+ g_usBs3TestStep, "xxxx", __LINE__, fActualAD, a_fExpectedAD); \
+ BS3CPUBASIC2PF_HALT(pThis); \
+ } \
+ } while (0)
+
+ /*
+ * Again, but redoing everything for each accessor.
+ */
+ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++)
+ {
+ pThis->pszStore = g_aStoreMethods[iStore].pszName;
+
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ PBS3REGCTX const pCtx = &aCtxts[iRing];
+
+ if ( EffWrk.fReserved
+ || !EffWrk.fPresent
+ || (!EffWrk.fUser && iRing == 3))
+ {
+ uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD
+ : EffWrk.fPresent ? X86_TRAP_PF_P : 0)
+ | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+ }
+ }
+ else
+ {
+ uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+ if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID)
+ && EffWrk.fNoExecute)
+ || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ && !EffWrk.fWriteable
+ && (fWp || iRing == 3)) )
+ {
+ uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(0);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_D);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+ }
+ else
+ {
+ uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ ? X86_PTE_A | X86_PTE_D : X86_PTE_A;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_D);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_A);
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Again, but using invalidate page.
+ */
+ if (pThis->fUseInvlPg)
+ {
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+
+ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++)
+ {
+ pThis->pszStore = g_aStoreMethods[iStore].pszName;
+
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ PBS3REGCTX const pCtx = &aCtxts[iRing];
+
+ if ( EffWrk.fReserved
+ || !EffWrk.fPresent
+ || (!EffWrk.fUser && iRing == 3))
+ {
+ uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD
+ : EffWrk.fPresent ? X86_TRAP_PF_P : 0)
+ | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+ }
+ }
+ else
+ {
+ uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+ if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID)
+ && EffWrk.fNoExecute)
+ || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ && !EffWrk.fWriteable
+ && (fWp || iRing == 3)) )
+ {
+ uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(0);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A);
+ }
+ else
+ {
+ uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ ? X86_PTE_A | X86_PTE_D : X86_PTE_A;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_A);
+ }
+ }
+ }
+ }
+ }
+
+ bs3CpuBasic2Pf_RestoreFromBackups(pThis);
+ }
+ }
+ }
+
+
+ /*
+ * Do all 4 paging levels. We start out with full access to the page and
+ * restrict it in various ways.
+ *
+ * (On the final level we only mess with the 2nd page for now.)
+ */
+ cPdPtrTests = 1;
+ cPml4Tests = 1;
+ if (pThis->uTestAddr.u >= UINT64_C(0x8000000000))
+ {
+ cPml4Tests = 2;
+ cPdPtrTests = 2;
+ }
+ else if (pThis->PgInfo.cEntries == 3)
+ cPdPtrTests = 2;
+
+#if 0
+ /* Loop 1: Accessor flags. */
+ for (iOuter = 0; iOuter < 2; iOuter++)
+ {
+ uint32_t const fAccessor = (iOuter == 0 ? BS3CB2PFACC_F_DIRECT : 0) | BS3CB2PFACC_F_PAGE_LEVEL;
+
+ /* Loop 2: Paging store method. */
+ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++)
+ {
+ unsigned iPml4Test;
+ int8_t cReserved = 0;
+ int8_t cNotPresent = 0;
+ int8_t cNotWrite = 0;
+ int8_t cNotUser = 0;
+ int8_t cExecute = 0;
+
+ /* Loop 3: Page map level 4 */
+ for (iPml4Test = 0; iPml4Test < cPml4Tests; iPml4Test++)
+ {
+ unsigned iPdPtrTest;
+
+ /* Loop 4: Page directory pointer table. */
+ for (iPdPtrTest = 0; iPdPtrTest < cPdPtrTests; iPdPtrTest++)
+ {
+ unsigned iPdTest;
+
+ /* Loop 5: Page directory. */
+ for (iPdTest = 0; iPdTest < 2; iPdTest++)
+ {
+ unsigned iPtTest;
+
+ /* Loop 6: Page table. */
+ for (iPtTest = 0; iPtTest < 2; iPtTest++)
+ {
+ /* Loop 7: Accessor ring. */
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ PBS3REGCTX const pCtx = &aCtxts[iRing];
+
+ if ( EffWrk.fReserved
+ || !EffWrk.fPresent
+ || (!EffWrk.fUser && iRing == 3))
+ {
+ uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD
+ : EffWrk.fPresent ? X86_TRAP_PF_P : 0)
+ | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF,
+ fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask));
+ CHECK_AD_BITS(0);
+ }
+ }
+ else
+ {
+ uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0);
+ for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++)
+ {
+ pThis->pszAccessor = g_aAccessors[iAccessor].pszName;
+ if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID)
+ && EffWrk.fNoExecute)
+ || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ && !EffWrk.fWriteable
+ && (fWp || iRing == 3)) )
+ {
+ uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(0);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd);
+ CHECK_AD_BITS(X86_PTE_A);
+ }
+ else
+ {
+ uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW)
+ ? X86_PTE_A | X86_PTE_D : X86_PTE_A;
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(X86_PTE_A | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_D);
+
+ pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A);
+ ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE);
+ g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX);
+ CHECK_AD_BITS(fExpectedAD | X86_PTE_A);
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+ }
+#endif
+
+ /*
+ * Check reserved bits on each paging level.
+ */
+
+ /* Loop 1: Accessor flags (only direct for now). */
+ for (iOuter = 0; iOuter < 1; iOuter++)
+ {
+ uint32_t const fAccessor = BS3CB2PFACC_F_DIRECT;
+
+ /* Loop 2: Paging store method. */
+ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++)
+ {
+ /* Loop 3: Accessor ring. */
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ /* Loop 4: Which level we mess up. */
+ for (iLevel = 0; iLevel < pThis->PgInfo.cEntries; iLevel++)
+ {
+#if 0
+ const BS3CPUBASIC2PFMODPT *pPteWrk = &g_aPteWorkers[iPteWrk];
+ if (pThis->PgInfo.)
+ {
+ }
+#endif
+
+
+ }
+ }
+ }
+ }
+
+
+
+ return 0;
+}
+
+
+BS3_DECL_CALLBACK(uint8_t) bs3CpuBasic2_RaiseXcpt0e_c32(uint8_t bMode)
+{
+ void *pvTestUnaligned;
+ uint32_t cbTestUnaligned = _8M;
+ uint8_t bRet = 1;
+ int rc;
+ BS3CPUBASIC2PFSTATE State;
+
+ /*
+ * Initalize the state data.
+ */
+ Bs3MemZero(&State, sizeof(State));
+ State.bMode = bMode;
+ switch (bMode & BS3_MODE_CODE_MASK)
+ {
+ case BS3_MODE_CODE_16: State.cbAccess = sizeof(uint16_t); break;
+ case BS3_MODE_CODE_V86: State.cbAccess = sizeof(uint16_t); break;
+ case BS3_MODE_CODE_32: State.cbAccess = sizeof(uint32_t); break;
+ case BS3_MODE_CODE_64: State.cbAccess = sizeof(uint64_t); break;
+ }
+ State.pCmnMode = &g_aCmnModes[0];
+ while (State.pCmnMode->bMode != (bMode & BS3_MODE_CODE_MASK))
+ State.pCmnMode++;
+ State.fUseInvlPg = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486;
+
+ /* Figure physical addressing width. */
+ State.cBitsPhysWidth = 32;
+ if ( (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ && (ASMCpuId_EDX(1) & (X86_CPUID_FEATURE_EDX_PSE36 | X86_CPUID_FEATURE_EDX_PAE)) )
+ State.cBitsPhysWidth = 36;
+
+ if ( (g_uBs3CpuDetected & BS3CPU_F_CPUID_EXT_LEAVES)
+ && ASMCpuId_EAX(0x80000000) >= 0x80000008)
+ {
+ uint8_t cBits = (uint8_t)ASMCpuId_EAX(0x80000008);
+ if (cBits >= 32 && cBits <= 52)
+ State.cBitsPhysWidth = cBits;
+ else
+ Bs3TestPrintf("CPUID 0x80000008: Physical bitcount out of range: %u\n", cBits);
+ }
+ //Bs3TestPrintf("Physical bitcount: %u\n", State.cBitsPhysWidth);
+
+ /*
+ * Allocate a some memory we can play around with, then carve a size aligned
+ * chunk out of it so we might be able to maybe play with 2/4MB pages too.
+ */
+ cbTestUnaligned = _8M * 2;
+ while ((pvTestUnaligned = Bs3MemAlloc(BS3MEMKIND_FLAT32, cbTestUnaligned)) == NULL)
+ {
+ cbTestUnaligned >>= 1;
+ if (cbTestUnaligned <= _16K)
+ {
+ Bs3TestFailed("Failed to allocate memory to play around with\n");
+ return 1;
+ }
+ }
+
+ /* align. */
+ if ((uintptr_t)pvTestUnaligned & (cbTestUnaligned - 1))
+ {
+ State.cbTest = cbTestUnaligned >> 1;
+ State.pbOrgTest = (uint8_t *)(((uintptr_t)pvTestUnaligned + State.cbTest - 1) & ~(State.cbTest - 1));
+ }
+ else
+ {
+ State.pbOrgTest = pvTestUnaligned;
+ State.cbTest = cbTestUnaligned;
+ }
+ State.cTestPages = State.cbTest >> X86_PAGE_SHIFT;
+
+ /*
+ * Alias this memory far away from where our code and data lives.
+ */
+ if (bMode & BS3_MODE_CODE_64)
+ State.uTestAddr.u = UINT64_C(0x0000648680000000);
+ else
+ State.uTestAddr.u = UINT32_C(0x80000000);
+ rc = Bs3PagingAlias(State.uTestAddr.u, (uintptr_t)State.pbOrgTest, State.cbTest, X86_PTE_P | X86_PTE_RW | X86_PTE_US);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Bs3PagingQueryAddressInfo(State.uTestAddr.u, &State.PgInfo);
+ if (RT_SUCCESS(rc))
+ {
+//if (bMode & BS3_MODE_CODE_64) ASMHalt();
+ /* Set values that derives from the test memory size and paging info. */
+ if (State.PgInfo.cEntries == 2)
+ {
+ State.cTestPdes = (State.cTestPages + X86_PG_ENTRIES - 1) / X86_PG_ENTRIES;
+ State.cTest1stPtes = RT_MIN(State.cTestPages, X86_PG_ENTRIES);
+ State.cbPdeBackup = State.cTestPdes * (X86_PAGE_SIZE / X86_PG_ENTRIES);
+ State.cbPteBackup = State.cTest1stPtes * (X86_PAGE_SIZE / X86_PG_ENTRIES);
+ }
+ else
+ {
+ State.cTestPdes = (State.cTestPages + X86_PG_PAE_ENTRIES - 1) / X86_PG_PAE_ENTRIES;
+ State.cTest1stPtes = RT_MIN(State.cTestPages, X86_PG_PAE_ENTRIES);
+ State.cbPdeBackup = State.cTestPdes * (X86_PAGE_SIZE / X86_PG_PAE_ENTRIES);
+ State.cbPteBackup = State.cTest1stPtes * (X86_PAGE_SIZE / X86_PG_PAE_ENTRIES);
+ }
+#ifdef BS3CPUBASIC2PF_FASTER
+ State.cbPteBackup = State.PgInfo.cbEntry * 4;
+#endif
+ if (State.cTestPdes <= RT_ELEMENTS(State.au64PdeBackup))
+ {
+ uint32_t cr0 = ASMGetCR0();
+
+ /* Back up the structures. */
+ Bs3MemCpy(&State.PteBackup, State.PgInfo.u.Legacy.pPte, State.cbPteBackup);
+ Bs3MemCpy(State.au64PdeBackup, State.PgInfo.u.Legacy.pPde, State.cbPdeBackup);
+ if (State.PgInfo.cEntries > 2)
+ State.u64PdpteBackup = State.PgInfo.u.Pae.pPdpe->u;
+ if (State.PgInfo.cEntries > 3)
+ State.u64Pml4eBackup = State.PgInfo.u.Pae.pPml4e->u;
+
+ /*
+ * Setup a 16-bit selector for accessing the alias.
+ */
+ Bs3SelSetup16BitData(&Bs3GdteSpare00, State.uTestAddr.u32);
+ State.uSel16TestData = BS3_SEL_SPARE_00 | 3;
+
+ /*
+ * Do the testing.
+ */
+ ASMSetCR0(ASMGetCR0() & ~X86_CR0_WP);
+ bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, false /*fWp*/, false /*fNxe*/);
+ if (bRet == 0 && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ {
+ ASMSetCR0(ASMGetCR0() | X86_CR0_WP);
+ bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, true /*fWp*/, false /*fNxe*/);
+ }
+
+ /* Do again with NX enabled. */
+ if (bRet == 0 && (g_uBs3CpuDetected & BS3CPU_F_NX))
+ {
+ ASMWrMsr(MSR_K6_EFER, ASMRdMsr(MSR_K6_EFER) | MSR_K6_EFER_NXE);
+ ASMSetCR0(ASMGetCR0() & ~X86_CR0_WP);
+ bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, false /*fWp*/, State.PgInfo.cbEntry == 8 /*fNxe*/);
+ ASMSetCR0(ASMGetCR0() | X86_CR0_WP);
+ bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, true /*fWp*/, State.PgInfo.cbEntry == 8 /*fNxe*/);
+ ASMWrMsr(MSR_K6_EFER, ASMRdMsr(MSR_K6_EFER) & ~MSR_K6_EFER_NXE);
+ }
+ bs3CpuBasic2Pf_RestoreFromBackups(&State);
+ ASMSetCR0((ASMGetCR0() & ~X86_CR0_WP) | (cr0 & X86_CR0_WP));
+ }
+ else
+ Bs3TestFailedF("cTestPdes=%u!\n", State.cTestPdes);
+ }
+ else
+ Bs3TestFailedF("Bs3PagingQueryAddressInfo failed: %d\n", rc);
+ Bs3PagingUnalias(State.uTestAddr.u, State.cbTest);
+ }
+ else
+ Bs3TestFailedF("Bs3PagingAlias failed! rc=%d\n", rc);
+ Bs3MemFree(pvTestUnaligned, cbTestUnaligned);
+ return bRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.c
new file mode 100644
index 00000000..1c2a8b5f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.c
@@ -0,0 +1,1548 @@
+/* $Id: bs3-cpu-basic-2-template.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#undef CHECK_MEMBER
+#define CHECK_MEMBER(a_szName, a_szFmt, a_Actual, a_Expected) \
+ do \
+ { \
+ if ((a_Actual) == (a_Expected)) { /* likely */ } \
+ else bs3CpuBasic2_FailedF(a_szName "=" a_szFmt " expected " a_szFmt, (a_Actual), (a_Expected)); \
+ } while (0)
+
+
+#ifdef BS3_INSTANTIATING_MODE
+# undef MyBs3Idt
+# undef MY_SYS_SEL_R0_CS
+# undef MY_SYS_SEL_R0_CS_CNF
+# undef MY_SYS_SEL_R0_DS
+# undef MY_SYS_SEL_R0_SS
+# if BS3_MODE_IS_16BIT_SYS(TMPL_MODE)
+# define MyBs3Idt Bs3Idt16
+# define MY_SYS_SEL_R0_CS BS3_SEL_R0_CS16
+# define MY_SYS_SEL_R0_CS_CNF BS3_SEL_R0_CS16_CNF
+# define MY_SYS_SEL_R0_DS BS3_SEL_R0_DS16
+# define MY_SYS_SEL_R0_SS BS3_SEL_R0_SS16
+# elif BS3_MODE_IS_32BIT_SYS(TMPL_MODE)
+# define MyBs3Idt Bs3Idt32
+# define MY_SYS_SEL_R0_CS BS3_SEL_R0_CS32
+# define MY_SYS_SEL_R0_CS_CNF BS3_SEL_R0_CS32_CNF
+# define MY_SYS_SEL_R0_DS BS3_SEL_R0_DS32
+# define MY_SYS_SEL_R0_SS BS3_SEL_R0_SS32
+# elif BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+# define MyBs3Idt Bs3Idt64
+# define MY_SYS_SEL_R0_CS BS3_SEL_R0_CS64
+# define MY_SYS_SEL_R0_CS_CNF BS3_SEL_R0_CS64_CNF
+# define MY_SYS_SEL_R0_DS BS3_SEL_R0_DS64
+# define MY_SYS_SEL_R0_SS BS3_SEL_R0_DS64
+# else
+# error "TMPL_MODE"
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+typedef struct BS3CB2INVLDESCTYPE
+{
+ uint8_t u4Type;
+ uint8_t u1DescType;
+} BS3CB2INVLDESCTYPE;
+#endif
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+extern FNBS3FAR bs3CpuBasic2_Int80;
+extern FNBS3FAR bs3CpuBasic2_Int81;
+extern FNBS3FAR bs3CpuBasic2_Int82;
+extern FNBS3FAR bs3CpuBasic2_Int83;
+extern FNBS3FAR bs3CpuBasic2_ud2;
+# define g_bs3CpuBasic2_ud2_FlatAddr BS3_DATA_NM(g_bs3CpuBasic2_ud2_FlatAddr)
+extern uint32_t g_bs3CpuBasic2_ud2_FlatAddr;
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+# define g_pszTestMode BS3_CMN_NM(g_pszTestMode)
+static const char BS3_FAR *g_pszTestMode = (const char *)1;
+# define g_bTestMode BS3_CMN_NM(g_bTestMode)
+static uint8_t g_bTestMode = 1;
+# define g_f16BitSys BS3_CMN_NM(g_f16BitSys)
+static bool g_f16BitSys = 1;
+
+
+/** Table containing invalid CS selector types. */
+static const BS3CB2INVLDESCTYPE g_aInvalidCsTypes[] =
+{
+ { X86_SEL_TYPE_RO, 1 },
+ { X86_SEL_TYPE_RO_ACC, 1 },
+ { X86_SEL_TYPE_RW, 1 },
+ { X86_SEL_TYPE_RW_ACC, 1 },
+ { X86_SEL_TYPE_RO_DOWN, 1 },
+ { X86_SEL_TYPE_RO_DOWN_ACC, 1 },
+ { X86_SEL_TYPE_RW_DOWN, 1 },
+ { X86_SEL_TYPE_RW_DOWN_ACC, 1 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 3, 0 },
+ { 4, 0 },
+ { 5, 0 },
+ { 6, 0 },
+ { 7, 0 },
+ { 8, 0 },
+ { 9, 0 },
+ { 10, 0 },
+ { 11, 0 },
+ { 12, 0 },
+ { 13, 0 },
+ { 14, 0 },
+ { 15, 0 },
+};
+
+/** Table containing invalid SS selector types. */
+static const BS3CB2INVLDESCTYPE g_aInvalidSsTypes[] =
+{
+ { X86_SEL_TYPE_EO, 1 },
+ { X86_SEL_TYPE_EO_ACC, 1 },
+ { X86_SEL_TYPE_ER, 1 },
+ { X86_SEL_TYPE_ER_ACC, 1 },
+ { X86_SEL_TYPE_EO_CONF, 1 },
+ { X86_SEL_TYPE_EO_CONF_ACC, 1 },
+ { X86_SEL_TYPE_ER_CONF, 1 },
+ { X86_SEL_TYPE_ER_CONF_ACC, 1 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 3, 0 },
+ { 4, 0 },
+ { 5, 0 },
+ { 6, 0 },
+ { 7, 0 },
+ { 8, 0 },
+ { 9, 0 },
+ { 10, 0 },
+ { 11, 0 },
+ { 12, 0 },
+ { 13, 0 },
+ { 14, 0 },
+ { 15, 0 },
+};
+
+#endif /* BS3_INSTANTIATING_CMN - global */
+
+#ifdef BS3_INSTANTIATING_CMN
+
+/**
+ * Wrapper around Bs3TestFailedF that prefixes the error with g_usBs3TestStep
+ * and g_pszTestMode.
+ */
+# define bs3CpuBasic2_FailedF BS3_CMN_NM(bs3CpuBasic2_FailedF)
+BS3_DECL_NEAR(void) bs3CpuBasic2_FailedF(const char *pszFormat, ...)
+{
+ va_list va;
+
+ char szTmp[168];
+ va_start(va, pszFormat);
+ Bs3StrPrintfV(szTmp, sizeof(szTmp), pszFormat, va);
+ va_end(va);
+
+ Bs3TestFailedF("%u - %s: %s", g_usBs3TestStep, g_pszTestMode, szTmp);
+}
+
+
+/**
+ * Compares trap stuff.
+ */
+# define bs3CpuBasic2_CompareIntCtx1 BS3_CMN_NM(bs3CpuBasic2_CompareIntCtx1)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareIntCtx1(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint8_t bXcpt)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, 0);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, 2 /*int xx*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting in CompareTrapCtx1: bXcpt=%#x\n", bXcpt);
+ ASMHalt();
+#endif
+ }
+}
+
+
+/**
+ * Compares trap stuff.
+ */
+# define bs3CpuBasic2_CompareTrapCtx2 BS3_CMN_NM(bs3CpuBasic2_CompareTrapCtx2)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareTrapCtx2(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t cbIpAdjust,
+ uint8_t bXcpt, uint16_t uHandlerCs)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, 0);
+ CHECK_MEMBER("uHandlerCs", "%#06x", pTrapCtx->uHandlerCs, uHandlerCs);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, cbIpAdjust, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting in CompareTrapCtx2: bXcpt=%#x\n", bXcpt);
+ ASMHalt();
+#endif
+ }
+}
+
+/**
+ * Compares a CPU trap.
+ */
+# define bs3CpuBasic2_CompareCpuTrapCtx BS3_CMN_NM(bs3CpuBasic2_CompareCpuTrapCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareCpuTrapCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd,
+ uint8_t bXcpt, bool f486ResumeFlagHint)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint32_t fExtraEfl;
+
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX16", (uint16_t)pTrapCtx->uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */
+
+ fExtraEfl = X86_EFL_RF;
+ if ( g_f16BitSys
+ || ( !f486ResumeFlagHint
+ && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) <= BS3CPU_80486 ) )
+ fExtraEfl = 0;
+ else
+ fExtraEfl = X86_EFL_RF;
+#if 0 /** @todo Running on an AMD Phenom II X6 1100T under AMD-V I'm not getting good X86_EFL_RF results. Enable this to get on with other work. */
+ fExtraEfl = pTrapCtx->Ctx.rflags.u32 & X86_EFL_RF;
+#endif
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, 0 /*cbIpAdjust*/, 0 /*cbSpAdjust*/, fExtraEfl, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd);
+ ASMHalt();
+#endif
+ }
+}
+
+
+/**
+ * Compares \#GP trap.
+ */
+# define bs3CpuBasic2_CompareGpCtx BS3_CMN_NM(bs3CpuBasic2_CompareGpCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareGpCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_GP, true /*f486ResumeFlagHint*/);
+}
+
+/**
+ * Compares \#NP trap.
+ */
+# define bs3CpuBasic2_CompareNpCtx BS3_CMN_NM(bs3CpuBasic2_CompareNpCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareNpCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_NP, true /*f486ResumeFlagHint*/);
+}
+
+/**
+ * Compares \#SS trap.
+ */
+# define bs3CpuBasic2_CompareSsCtx BS3_CMN_NM(bs3CpuBasic2_CompareSsCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareSsCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd, bool f486ResumeFlagHint)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_SS, f486ResumeFlagHint);
+}
+
+/**
+ * Compares \#TS trap.
+ */
+# define bs3CpuBasic2_CompareTsCtx BS3_CMN_NM(bs3CpuBasic2_CompareTsCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareTsCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_TS, false /*f486ResumeFlagHint*/);
+}
+
+/**
+ * Compares \#PF trap.
+ */
+# define bs3CpuBasic2_ComparePfCtx BS3_CMN_NM(bs3CpuBasic2_ComparePfCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_ComparePfCtx(PCBS3TRAPFRAME pTrapCtx, PBS3REGCTX pStartCtx, uint16_t uErrCd, uint64_t uCr2Expected)
+{
+ uint64_t const uCr2Saved = pStartCtx->cr2.u;
+ pStartCtx->cr2.u = uCr2Expected;
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_PF, true /*f486ResumeFlagHint*/);
+ pStartCtx->cr2.u = uCr2Saved;
+}
+
+/**
+ * Compares \#UD trap.
+ */
+# define bs3CpuBasic2_CompareUdCtx BS3_CMN_NM(bs3CpuBasic2_CompareUdCtx)
+BS3_DECL_NEAR(void) bs3CpuBasic2_CompareUdCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, 0 /*no error code*/, X86_XCPT_UD, true /*f486ResumeFlagHint*/);
+}
+
+
+# define bs3CpuBasic2_RaiseXcpt1Common BS3_CMN_NM(bs3CpuBasic2_RaiseXcpt1Common)
+BS3_DECL_NEAR(void) bs3CpuBasic2_RaiseXcpt1Common(uint16_t const uSysR0Cs, uint16_t const uSysR0CsConf, uint16_t const uSysR0Ss,
+ PX86DESC const paIdt, unsigned const cIdteShift)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx80;
+ BS3REGCTX Ctx81;
+ BS3REGCTX Ctx82;
+ BS3REGCTX Ctx83;
+ BS3REGCTX CtxTmp;
+ BS3REGCTX CtxTmp2;
+ PBS3REGCTX apCtx8x[4];
+ unsigned iCtx;
+ unsigned iRing;
+ unsigned iDpl;
+ unsigned iRpl;
+ unsigned i, j, k;
+ uint32_t uExpected;
+ bool const f486Plus = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486;
+# if TMPL_BITS == 16
+ bool const f386Plus = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386;
+ bool const f286 = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) == BS3CPU_80286;
+# else
+ bool const f286 = false;
+ bool const f386Plus = true;
+ int rc;
+ uint8_t *pbIdtCopyAlloc;
+ PX86DESC pIdtCopy;
+ const unsigned cbIdte = 1 << (3 + cIdteShift);
+ RTCCUINTXREG uCr0Saved = ASMGetCR0();
+ RTGDTR GdtrSaved;
+# endif
+ RTIDTR IdtrSaved;
+ RTIDTR Idtr;
+
+ ASMGetIDTR(&IdtrSaved);
+# if TMPL_BITS != 16
+ ASMGetGDTR(&GdtrSaved);
+# endif
+
+ /* make sure they're allocated */
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&Ctx80, sizeof(Ctx80));
+ Bs3MemZero(&Ctx81, sizeof(Ctx81));
+ Bs3MemZero(&Ctx82, sizeof(Ctx82));
+ Bs3MemZero(&Ctx83, sizeof(Ctx83));
+ Bs3MemZero(&CtxTmp, sizeof(CtxTmp));
+ Bs3MemZero(&CtxTmp2, sizeof(CtxTmp2));
+
+ /* Context array. */
+ apCtx8x[0] = &Ctx80;
+ apCtx8x[1] = &Ctx81;
+ apCtx8x[2] = &Ctx82;
+ apCtx8x[3] = &Ctx83;
+
+# if TMPL_BITS != 16
+ /* Allocate memory for playing around with the IDT. */
+ pbIdtCopyAlloc = NULL;
+ if (BS3_MODE_IS_PAGED(g_bTestMode))
+ pbIdtCopyAlloc = Bs3MemAlloc(BS3MEMKIND_FLAT32, 12*_1K);
+# endif
+
+ /*
+ * IDT entry 80 thru 83 are assigned DPLs according to the number.
+ * (We'll be useing more, but this'll do for now.)
+ */
+ paIdt[0x80 << cIdteShift].Gate.u2Dpl = 0;
+ paIdt[0x81 << cIdteShift].Gate.u2Dpl = 1;
+ paIdt[0x82 << cIdteShift].Gate.u2Dpl = 2;
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 3;
+
+ Bs3RegCtxSave(&Ctx80);
+ Ctx80.rsp.u -= 0x300;
+ Ctx80.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int80);
+# if TMPL_BITS == 16
+ Ctx80.cs = BS3_MODE_IS_RM_OR_V86(g_bTestMode) ? BS3_SEL_TEXT16 : BS3_SEL_R0_CS16;
+# elif TMPL_BITS == 32
+ g_uBs3TrapEipHint = Ctx80.rip.u32;
+# endif
+ Bs3MemCpy(&Ctx81, &Ctx80, sizeof(Ctx80));
+ Ctx81.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int81);
+ Bs3MemCpy(&Ctx82, &Ctx80, sizeof(Ctx80));
+ Ctx82.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int82);
+ Bs3MemCpy(&Ctx83, &Ctx80, sizeof(Ctx80));
+ Ctx83.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int83);
+
+ /*
+ * Check that all the above gates work from ring-0.
+ */
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ g_usBs3TestStep = iCtx;
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = apCtx8x[iCtx]->rip.u32;
+# endif
+ Bs3TrapSetJmpAndRestore(apCtx8x[iCtx], &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, apCtx8x[iCtx], 0x80+iCtx /*bXcpt*/);
+ }
+
+ /*
+ * Check that the gate DPL checks works.
+ */
+ g_usBs3TestStep = 100;
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ g_usBs3TestStep++;
+ }
+ }
+
+ /*
+ * Modify the gate CS value and run the handler at a different CPL.
+ * Throw RPL variations into the mix (completely ignored) together
+ * with gate presence.
+ * 1. CPL <= GATE.DPL
+ * 2. GATE.P
+ * 3. GATE.CS.DPL <= CPL (non-conforming segments)
+ */
+ g_usBs3TestStep = 1000;
+ for (i = 0; i <= 3; i++)
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = apCtx8x[iCtx]->rip.u32;
+# endif
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ for (j = 0; j <= 3; j++)
+ {
+ uint16_t const uCs = (uSysR0Cs | j) + (i << BS3_SEL_RING_SHIFT);
+ for (k = 0; k < 2; k++)
+ {
+ g_usBs3TestStep++;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = k;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (k == 0)
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (i > iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ else
+ {
+ uint16_t uExpectedCs = uCs & X86_SEL_MASK_OFF_RPL;
+ if (i <= iCtx && i <= iRing)
+ uExpectedCs |= i;
+ bs3CpuBasic2_CompareTrapCtx2(&TrapCtx, &CtxTmp, 2 /*int 8xh*/, 0x80 + iCtx /*bXcpt*/, uExpectedCs);
+ }
+ }
+ }
+
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 1600);
+
+ /*
+ * Various CS and SS related faults
+ *
+ * We temporarily reconfigure gate 80 and 83 with new CS selectors, the
+ * latter have a CS.DPL of 2 for testing ring transisions and SS loading
+ * without making it impossible to handle faults.
+ */
+ g_usBs3TestStep = 1600;
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ Bs3GdteTestPage00.Gen.u1Present = 0;
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_00;
+
+ /* CS.PRESENT = 0 */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("selector was accessed");
+ g_usBs3TestStep++;
+
+ /* Check that GATE.DPL is checked before CS.PRESENT. */
+ for (iRing = 1; iRing < 4; iRing++)
+ {
+ Bs3MemCpy(&CtxTmp, &Ctx80, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, (0x80 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("selector was accessed");
+ g_usBs3TestStep++;
+ }
+
+ /* CS.DPL mismatch takes precedence over CS.PRESENT = 0. */
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("CS selector was accessed");
+ g_usBs3TestStep++;
+ for (iDpl = 1; iDpl < 4; iDpl++)
+ {
+ Bs3GdteTestPage00.Gen.u2Dpl = iDpl;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("CS selector was accessed");
+ g_usBs3TestStep++;
+ }
+
+ /* 1608: Check all the invalid CS selector types alone. */
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ for (i = 0; i < RT_ELEMENTS(g_aInvalidCsTypes); i++)
+ {
+ Bs3GdteTestPage00.Gen.u4Type = g_aInvalidCsTypes[i].u4Type;
+ Bs3GdteTestPage00.Gen.u1DescType = g_aInvalidCsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type != g_aInvalidCsTypes[i].u4Type)
+ bs3CpuBasic2_FailedF("Invalid CS type %#x/%u -> %#x/%u\n",
+ g_aInvalidCsTypes[i].u4Type, g_aInvalidCsTypes[i].u1DescType,
+ Bs3GdteTestPage00.Gen.u4Type, Bs3GdteTestPage00.Gen.u1DescType);
+ g_usBs3TestStep++;
+
+ /* Incorrect CS.TYPE takes precedence over CS.PRESENT = 0. */
+ Bs3GdteTestPage00.Gen.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ Bs3GdteTestPage00.Gen.u1Present = 1;
+ g_usBs3TestStep++;
+ }
+
+ /* Fix CS again. */
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+
+ /* 1632: Test SS. */
+ if (!BS3_MODE_IS_64BIT_SYS(g_bTestMode))
+ {
+ uint16_t BS3_FAR *puTssSs2 = BS3_MODE_IS_16BIT_SYS(g_bTestMode) ? &Bs3Tss16.ss2 : &Bs3Tss32.ss2;
+ uint16_t const uSavedSs2 = *puTssSs2;
+ X86DESC const SavedGate83 = paIdt[0x83 << cIdteShift];
+
+ /* Make the handler execute in ring-2. */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage02.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_02 | 2;
+
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3); /* yeah, from 3 so SS:xSP is reloaded. */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ if (!(Bs3GdteTestPage02.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("CS selector was not access");
+ g_usBs3TestStep++;
+
+ /* Create a SS.DPL=2 stack segment and check that SS2.RPL matters and
+ that we get #SS if the selector isn't present. */
+ i = 0; /* used for cycling thru invalid CS types */
+ for (k = 0; k < 10; k++)
+ {
+ /* k=0: present,
+ k=1: not-present,
+ k=2: present but very low limit,
+ k=3: not-present, low limit.
+ k=4: present, read-only.
+ k=5: not-present, read-only.
+ k=6: present, code-selector.
+ k=7: not-present, code-selector.
+ k=8: present, read-write / no access + system (=LDT).
+ k=9: not-present, read-write / no access + system (=LDT).
+ */
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u1Present = !(k & 1);
+ if (k >= 8)
+ {
+ Bs3GdteTestPage03.Gen.u1DescType = 0; /* system */
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RW; /* = LDT */
+ }
+ else if (k >= 6)
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_ER;
+ else if (k >= 4)
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RO;
+ else if (k >= 2)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = 0x400;
+ Bs3GdteTestPage03.Gen.u4LimitHigh = 0;
+ Bs3GdteTestPage03.Gen.u1Granularity = 0;
+ }
+
+ for (iDpl = 0; iDpl < 4; iDpl++)
+ {
+ Bs3GdteTestPage03.Gen.u2Dpl = iDpl;
+
+ for (iRpl = 0; iRpl < 4; iRpl++)
+ {
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | iRpl;
+ //Bs3TestPrintf("k=%u iDpl=%u iRpl=%u step=%u\n", k, iDpl, iRpl, g_usBs3TestStep);
+ Bs3GdteTestPage02.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iRpl != 2 || iRpl != iDpl || k >= 4)
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ else if (k != 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03,
+ k == 2 /*f486ResumeFlagHint*/);
+ else
+ {
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ if (TrapCtx.uHandlerSs != (BS3_SEL_TEST_PAGE_03 | 2))
+ bs3CpuBasic2_FailedF("uHandlerSs=%#x expected %#x\n", TrapCtx.uHandlerSs, BS3_SEL_TEST_PAGE_03 | 2);
+ }
+ if (!(Bs3GdteTestPage02.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("CS selector was not access");
+ if ( TrapCtx.bXcpt == 0x83
+ || (TrapCtx.bXcpt == X86_XCPT_SS && k == 2) )
+ {
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("SS selector was not accessed");
+ }
+ else if (Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("SS selector was accessed");
+ g_usBs3TestStep++;
+
+ /* +1: Modify the gate DPL to check that this is checked before SS.DPL and SS.PRESENT. */
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 2;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, (0x83 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 3;
+ g_usBs3TestStep++;
+
+ /* +2: Check the CS.DPL check is done before the SS ones. Restoring the
+ ring-0 INT 83 context triggers the CS.DPL < CPL check. */
+ Bs3TrapSetJmpAndRestore(&Ctx83, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx83, BS3_SEL_TEST_PAGE_02);
+ g_usBs3TestStep++;
+
+ /* +3: Now mark the CS selector not present and check that that also triggers before SS stuff. */
+ Bs3GdteTestPage02.Gen.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_02);
+ Bs3GdteTestPage02.Gen.u1Present = 1;
+ g_usBs3TestStep++;
+
+ /* +4: Make the CS selector some invalid type and check it triggers before SS stuff. */
+ Bs3GdteTestPage02.Gen.u4Type = g_aInvalidCsTypes[i].u4Type;
+ Bs3GdteTestPage02.Gen.u1DescType = g_aInvalidCsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_02);
+ Bs3GdteTestPage02.Gen.u4Type = X86_SEL_TYPE_ER_ACC;
+ Bs3GdteTestPage02.Gen.u1DescType = 1;
+ g_usBs3TestStep++;
+
+ /* +5: Now, make the CS selector limit too small and that it triggers after SS trouble.
+ The 286 had a simpler approach to these GP(0). */
+ Bs3GdteTestPage02.Gen.u16LimitLow = 0;
+ Bs3GdteTestPage02.Gen.u4LimitHigh = 0;
+ Bs3GdteTestPage02.Gen.u1Granularity = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (f286)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/);
+ else if (iRpl != 2 || iRpl != iDpl || k >= 4)
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ else if (k != 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, k == 2 /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/);
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ g_usBs3TestStep++;
+ }
+ }
+ }
+
+ /* Check all the invalid SS selector types alone. */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | 2;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ g_usBs3TestStep++;
+ for (i = 0; i < RT_ELEMENTS(g_aInvalidSsTypes); i++)
+ {
+ Bs3GdteTestPage03.Gen.u4Type = g_aInvalidSsTypes[i].u4Type;
+ Bs3GdteTestPage03.Gen.u1DescType = g_aInvalidSsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ if (Bs3GdteTestPage03.Gen.u4Type != g_aInvalidSsTypes[i].u4Type)
+ bs3CpuBasic2_FailedF("Invalid SS type %#x/%u -> %#x/%u\n",
+ g_aInvalidSsTypes[i].u4Type, g_aInvalidSsTypes[i].u1DescType,
+ Bs3GdteTestPage03.Gen.u4Type, Bs3GdteTestPage03.Gen.u1DescType);
+ g_usBs3TestStep++;
+ }
+
+ /*
+ * Continue the SS experiments with a expand down segment. We'll use
+ * the same setup as we already have with gate 83h being DPL and
+ * having CS.DPL=2.
+ *
+ * Expand down segments are weird. The valid area is practically speaking
+ * reversed. So, a 16-bit segment with a limit of 0x6000 will have valid
+ * addresses from 0xffff thru 0x6001.
+ *
+ * So, with expand down segments we can more easily cut partially into the
+ * pushing of the iret frame and trigger more interesting behavior than
+ * with regular "expand up" segments where the whole pushing area is either
+ * all fine or not not fine.
+ */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u2Dpl = 2;
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RW_DOWN;
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | 2;
+
+ /* First test, limit = max --> no bytes accessible --> #GP */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, true /*f486ResumeFlagHint*/);
+
+ /* Second test, limit = 0 --> all by zero byte accessible --> works */
+ Bs3GdteTestPage03.Gen.u16LimitLow = 0;
+ Bs3GdteTestPage03.Gen.u4LimitHigh = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+
+ /* Modify the gate handler to be a dummy that immediately does UD2
+ and triggers #UD, then advance the limit down till we get the #UD. */
+ Bs3GdteTestPage03.Gen.u1Granularity = 0;
+
+ Bs3MemCpy(&CtxTmp2, &CtxTmp, sizeof(CtxTmp2)); /* #UD result context */
+ if (g_f16BitSys)
+ {
+ CtxTmp2.rip.u = g_bs3CpuBasic2_ud2_FlatAddr - BS3_ADDR_BS3TEXT16;
+ Bs3Trap16SetGate(0x83, X86_SEL_TYPE_SYS_286_INT_GATE, 3, BS3_SEL_TEST_PAGE_02, CtxTmp2.rip.u16, 0 /*cParams*/);
+ CtxTmp2.rsp.u = Bs3Tss16.sp2 - 2*5;
+ }
+ else
+ {
+ CtxTmp2.rip.u = g_bs3CpuBasic2_ud2_FlatAddr;
+ Bs3Trap32SetGate(0x83, X86_SEL_TYPE_SYS_386_INT_GATE, 3, BS3_SEL_TEST_PAGE_02, CtxTmp2.rip.u32, 0 /*cParams*/);
+ CtxTmp2.rsp.u = Bs3Tss32.esp2 - 4*5;
+ }
+ CtxTmp2.bMode = g_bTestMode; /* g_bBs3CurrentMode not changed by the UD2 handler. */
+ CtxTmp2.cs = BS3_SEL_TEST_PAGE_02 | 2;
+ CtxTmp2.ss = BS3_SEL_TEST_PAGE_03 | 2;
+ CtxTmp2.bCpl = 2;
+
+ /* test run. */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+
+ /* Real run. */
+ i = (g_f16BitSys ? 2 : 4) * 6 + 1;
+ while (i-- > 0)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = CtxTmp2.rsp.u16 + i - 1;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (i > 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, true /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+ }
+
+ /* Do a run where we do the same-ring kind of access. */
+ Bs3RegCtxConvertToRingX(&CtxTmp, 2);
+ if (g_f16BitSys)
+ {
+ CtxTmp2.rsp.u32 = CtxTmp.rsp.u32 - 2*3;
+ i = 2*3 - 1;
+ }
+ else
+ {
+ CtxTmp2.rsp.u32 = CtxTmp.rsp.u32 - 4*3;
+ i = 4*3 - 1;
+ }
+ CtxTmp.ss = BS3_SEL_TEST_PAGE_03 | 2;
+ CtxTmp2.ds = CtxTmp.ds;
+ CtxTmp2.es = CtxTmp.es;
+ CtxTmp2.fs = CtxTmp.fs;
+ CtxTmp2.gs = CtxTmp.gs;
+ while (i-- > 0)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = CtxTmp2.rsp.u16 + i - 1;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (i > 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, 0 /*BS3_SEL_TEST_PAGE_03*/, true /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+ }
+
+ *puTssSs2 = uSavedSs2;
+ paIdt[0x83 << cIdteShift] = SavedGate83;
+ }
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ BS3_ASSERT(g_usBs3TestStep < 3000);
+
+ /*
+ * Modify the gate CS value with a conforming segment.
+ */
+ g_usBs3TestStep = 3000;
+ for (i = 0; i <= 3; i++) /* cs.dpl */
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+
+ for (j = 0; j <= 3; j++) /* rpl */
+ {
+ uint16_t const uCs = (uSysR0CsConf | j) + (i << BS3_SEL_RING_SHIFT);
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ //Bs3TestPrintf("%u/%u/%u/%u: cs=%04x hcs=%04x xcpt=%02x\n", i, iRing, iCtx, j, uCs, TrapCtx.uHandlerCs, TrapCtx.bXcpt);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ g_usBs3TestStep++;
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (i > iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ }
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 3500);
+
+ /*
+ * The gates must be 64-bit in long mode.
+ */
+ if (cIdteShift != 0)
+ {
+ g_usBs3TestStep = 3500;
+ for (i = 0; i <= 3; i++)
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ for (j = 0; j < 2; j++)
+ {
+ static const uint16_t s_auCSes[2] = { BS3_SEL_R0_CS16, BS3_SEL_R0_CS32 };
+ uint16_t uCs = (s_auCSes[j] | i) + (i << BS3_SEL_RING_SHIFT);
+ g_usBs3TestStep++;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ }
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 4000);
+ }
+
+ /*
+ * IDT limit check. The 286 does not access X86DESCGATE::u16OffsetHigh.
+ */
+ g_usBs3TestStep = 5000;
+ i = (0x80 << (cIdteShift + 3)) - 1;
+ j = (0x82 << (cIdteShift + 3)) - (!f286 ? 1 : 3);
+ k = (0x83 << (cIdteShift + 3)) - 1;
+ for (; i <= k; i++, g_usBs3TestStep++)
+ {
+ Idtr = IdtrSaved;
+ Idtr.cbIdt = i;
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (i < j)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx81, (0x81 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ }
+ ASMSetIDTR(&IdtrSaved);
+ BS3_ASSERT(g_usBs3TestStep < 5100);
+
+# if TMPL_BITS != 16 /* Only do the paging related stuff in 32-bit and 64-bit modes. */
+
+ /*
+ * IDT page not present. Placing the IDT copy such that 0x80 is on the
+ * first page and 0x81 is on the second page. We need proceed to move
+ * it down byte by byte to check that any inaccessible byte means #PF.
+ *
+ * Note! We must reload the alternative IDTR for each run as any kind of
+ * printing to the string (like error reporting) will cause a switch
+ * to real mode and back, reloading the default IDTR.
+ */
+ g_usBs3TestStep = 5200;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ uint32_t const uCr2Expected = Bs3SelPtrToFlat(pbIdtCopyAlloc) + _4K;
+ for (j = 0; j < cbIdte; j++)
+ {
+ pIdtCopy = (PX86DESC)&pbIdtCopyAlloc[_4K - cbIdte * 0x81 - j];
+ Bs3MemCpy(pIdtCopy, paIdt, cbIdte * 256);
+
+ Idtr.cbIdt = IdtrSaved.cbIdt;
+ Idtr.pIdt = Bs3SelPtrToFlat(pIdtCopy);
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ rc = Bs3PagingProtect(uCr2Expected, _4K, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, 0 /*uErrCd*/, uCr2Expected);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, X86_TRAP_PF_RW /*uErrCd*/, uCr2Expected + 4 - RT_MIN(j, 4));
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(uCr2Expected, _4K, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+
+ /* Check if that the entry type is checked after the whole IDTE has been cleared for #PF. */
+ pIdtCopy[0x80 << cIdteShift].Gate.u4Type = 0;
+ rc = Bs3PagingProtect(uCr2Expected, _4K, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, 0 /*uErrCd*/, uCr2Expected);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, X86_TRAP_PF_RW /*uErrCd*/, uCr2Expected + 4 - RT_MIN(j, 4));
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(uCr2Expected, _4K, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+ }
+ }
+ else
+ Bs3TestPrintf("Bs3PagingProtectPtr: %d\n", i);
+
+ ASMSetIDTR(&IdtrSaved);
+ }
+ }
+
+ /*
+ * The read/write and user/supervisor bits the IDT PTEs are irrelevant.
+ */
+ g_usBs3TestStep = 5300;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ Bs3MemCpy(pbIdtCopyAlloc, paIdt, cbIdte * 256);
+ Idtr.cbIdt = IdtrSaved.cbIdt;
+ Idtr.pIdt = Bs3SelPtrToFlat(pbIdtCopyAlloc);
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ rc = Bs3PagingProtect(Idtr.pIdt, _4K, 0 /*fSet*/, X86_PTE_RW | X86_PTE_US /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(Idtr.pIdt, _4K, X86_PTE_RW | X86_PTE_US /*fSet*/, 0 /*fClear*/);
+ }
+ ASMSetIDTR(&IdtrSaved);
+ }
+
+ /*
+ * Check that CS.u1Accessed is set to 1. Use the test page selector #0 and #3 together
+ * with interrupt gates 80h and 83h, respectively.
+ */
+/** @todo Throw in SS.u1Accessed too. */
+ g_usBs3TestStep = 5400;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_00;
+
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Cs + (3 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_03; /* rpl is ignored, so leave it as zero. */
+
+ /* Check that the CS.A bit is being set on a general basis and that
+ the special CS values work with out generic handler code. */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ if (TrapCtx.uHandlerCs != (BS3_SEL_TEST_PAGE_03 | 3))
+ bs3CpuBasic2_FailedF("uHandlerCs=%#x, expected %#x", TrapCtx.uHandlerCs, (BS3_SEL_TEST_PAGE_03 | 3));
+ g_usBs3TestStep++;
+
+ /*
+ * Now check that setting CS.u1Access to 1 does __NOT__ trigger a page
+ * fault due to the RW bit being zero.
+ * (We check both with with and without the WP bit if 80486.)
+ */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ ASMSetCR0(uCr0Saved | X86_CR0_WP);
+
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ rc = Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, 0 /*fSet*/, X86_PTE_RW /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ /* ring-0 handler */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ /* ring-3 handler */
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ /* clear WP and repeat the above. */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ ASMSetCR0(uCr0Saved & ~X86_CR0_WP);
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* (No need to RW the page - ring-0, WP=0.) */
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* (No need to RW the page - ring-0, WP=0.) */
+
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!n", Bs3GdteTestPage03.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, X86_PTE_RW /*fSet*/, 0 /*fClear*/);
+ }
+
+ ASMSetCR0(uCr0Saved);
+
+ /*
+ * While we're here, check that if the CS GDT entry is a non-present
+ * page we do get a #PF with the rigth error code and CR2.
+ */
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* Just for fun, really a pointless gesture. */
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ rc = Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx80, 0 /*uErrCd*/, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx80, X86_TRAP_PF_RW, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00 + 4);
+ g_usBs3TestStep++;
+
+ /* Do it from ring-3 to check ErrCd, which doesn't set X86_TRAP_PF_US it turns out. */
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_03);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &CtxTmp, X86_TRAP_PF_RW, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_03 + 4);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("u4Type=%#x, accessed! #1", Bs3GdteTestPage00.Gen.u4Type);
+ if (Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("u4Type=%#x, accessed! #2", Bs3GdteTestPage03.Gen.u4Type);
+ }
+
+ /* restore */
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = uSysR0Cs;// + (3 << BS3_SEL_RING_SHIFT) + 3;
+ }
+
+# endif /* 32 || 64*/
+
+ /*
+ * Check broad EFLAGS effects.
+ */
+ g_usBs3TestStep = 5600;
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ /* all set */
+ CtxTmp.rflags.u32 &= X86_EFL_VM | X86_EFL_1;
+ CtxTmp.rflags.u32 |= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF /* | X86_EFL_TF */ /*| X86_EFL_IF*/
+ | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL /* | X86_EFL_NT*/;
+ if (f486Plus)
+ CtxTmp.rflags.u32 |= X86_EFL_AC;
+ if (f486Plus && !g_f16BitSys)
+ CtxTmp.rflags.u32 |= X86_EFL_RF;
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ CtxTmp.rflags.u32 |= X86_EFL_VIF | X86_EFL_VIP;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ CtxTmp.rflags.u32 &= ~X86_EFL_RF;
+
+ if (iCtx >= iRing)
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ uExpected = CtxTmp.rflags.u32
+ & ( X86_EFL_1 | X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_DF
+ | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT | X86_EFL_VM | X86_EFL_AC | X86_EFL_VIF | X86_EFL_VIP
+ | X86_EFL_ID /*| X86_EFL_TF*/ /*| X86_EFL_IF*/ /*| X86_EFL_RF*/ );
+ if (TrapCtx.fHandlerRfl != uExpected)
+ bs3CpuBasic2_FailedF("unexpected handler rflags value: %RX64 expected %RX32; CtxTmp.rflags=%RX64 Ctx.rflags=%RX64\n",
+ TrapCtx.fHandlerRfl, uExpected, CtxTmp.rflags.u, TrapCtx.Ctx.rflags.u);
+ g_usBs3TestStep++;
+
+ /* all cleared */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80286)
+ CtxTmp.rflags.u32 = apCtx8x[iCtx]->rflags.u32 & (X86_EFL_RA1_MASK | UINT16_C(0xf000));
+ else
+ CtxTmp.rflags.u32 = apCtx8x[iCtx]->rflags.u32 & (X86_EFL_VM | X86_EFL_RA1_MASK);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iCtx >= iRing)
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ uExpected = CtxTmp.rflags.u32;
+ if (TrapCtx.fHandlerRfl != uExpected)
+ bs3CpuBasic2_FailedF("unexpected handler rflags value: %RX64 expected %RX32; CtxTmp.rflags=%RX64 Ctx.rflags=%RX64\n",
+ TrapCtx.fHandlerRfl, uExpected, CtxTmp.rflags.u, TrapCtx.Ctx.rflags.u);
+ g_usBs3TestStep++;
+ }
+ }
+
+/** @todo CS.LIMIT / canonical(CS) */
+
+
+ /*
+ * Check invalid gate types.
+ */
+ g_usBs3TestStep = 32000;
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ static const uint16_t s_auCSes[] = { BS3_SEL_R0_CS16, BS3_SEL_R0_CS32, BS3_SEL_R0_CS64,
+ BS3_SEL_TSS16, BS3_SEL_TSS32, BS3_SEL_TSS64, 0, BS3_SEL_SPARE_1f };
+ static uint16_t const s_auInvlTypes64[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+ static uint16_t const s_auInvlTypes32[] = { 0, 1, 2, 3, 8, 9, 10, 11, 13,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ /*286:*/ 12, 14, 15 };
+ uint16_t const * const pauInvTypes = cIdteShift != 0 ? s_auInvlTypes64 : s_auInvlTypes32;
+ uint16_t const cInvTypes = cIdteShift != 0 ? RT_ELEMENTS(s_auInvlTypes64)
+ : f386Plus ? RT_ELEMENTS(s_auInvlTypes32) - 3 : RT_ELEMENTS(s_auInvlTypes32);
+
+
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ unsigned iType;
+
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+ for (iType = 0; iType < cInvTypes; iType++)
+ {
+ uint8_t const bSavedType = paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1DescType = pauInvTypes[iType] >> 4;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type = pauInvTypes[iType] & 0xf;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < RT_ELEMENTS(s_auCSes); j++)
+ {
+ uint16_t uCs = (unsigned)(s_auCSes[j] - BS3_SEL_R0_FIRST) < (unsigned)(4 << BS3_SEL_RING_SHIFT)
+ ? (s_auCSes[j] | i) + (i << BS3_SEL_RING_SHIFT)
+ : s_auCSes[j] | i;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x type=%#x\n", g_usBs3TestStep, iCtx, iRing, i, uCs, pauInvTypes[iType]);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ g_usBs3TestStep++;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+
+ /* Mark it not-present to check that invalid type takes precedence. */
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ g_usBs3TestStep++;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type = bSavedType;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1DescType = 0;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 62000U && g_usBs3TestStep > 32000U);
+
+
+ /** @todo
+ * - Run \#PF and \#GP (and others?) at CPLs other than zero.
+ * - Quickly generate all faults.
+ * - All the peculiarities v8086.
+ */
+
+# if TMPL_BITS != 16
+ Bs3MemFree(pbIdtCopyAlloc, 12*_1K);
+# endif
+}
+
+# if ARCH_BITS != 64
+
+/**
+ * Worker for bs3CpuBasic2_TssGateEsp that tests the INT 80 from outer rings.
+ */
+# define bs3CpuBasic2_TssGateEsp_AltStackOuterRing BS3_CMN_NM(bs3CpuBasic2_TssGateEsp_AltStackOuterRing)
+BS3_DECL_NEAR(void) bs3CpuBasic2_TssGateEsp_AltStackOuterRing(PCBS3REGCTX pCtx, uint8_t bRing, uint8_t *pbAltStack,
+ size_t cbAltStack, bool f16BitStack, bool f16BitTss,
+ bool f16BitHandler, unsigned uLine)
+{
+ uint8_t const cbIretFrame = f16BitHandler ? 5*2 : 5*4;
+ BS3REGCTX Ctx2;
+ BS3TRAPFRAME TrapCtx;
+ uint8_t *pbTmp;
+ g_usBs3TestStep = uLine;
+
+ Bs3MemCpy(&Ctx2, pCtx, sizeof(Ctx2));
+ Bs3RegCtxConvertToRingX(&Ctx2, bRing);
+
+ if (pbAltStack)
+ {
+ Ctx2.rsp.u = Bs3SelPtrToFlat(pbAltStack + 0x1980);
+ Bs3MemZero(pbAltStack, cbAltStack);
+ }
+
+ Bs3TrapSetJmpAndRestore(&Ctx2, &TrapCtx);
+
+ if (!f16BitStack && f16BitTss)
+ Ctx2.rsp.u &= UINT16_MAX;
+
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx2, 0x80 /*bXcpt*/);
+ CHECK_MEMBER("bCpl", "%u", TrapCtx.Ctx.bCpl, bRing);
+ CHECK_MEMBER("cbIretFrame", "%#x", TrapCtx.cbIretFrame, cbIretFrame);
+
+ if (pbAltStack)
+ {
+ uint64_t uExpectedRsp = (f16BitTss ? Bs3Tss16.sp0 : Bs3Tss32.esp0) - cbIretFrame;
+ if (f16BitStack)
+ {
+ uExpectedRsp &= UINT16_MAX;
+ uExpectedRsp |= Ctx2.rsp.u & ~(uint64_t)UINT16_MAX;
+ }
+ if ( TrapCtx.uHandlerRsp != uExpectedRsp
+ || TrapCtx.uHandlerSs != (f16BitTss ? Bs3Tss16.ss0 : Bs3Tss32.ss0))
+ bs3CpuBasic2_FailedF("handler SS:ESP=%04x:%08RX64, expected %04x:%08RX16",
+ TrapCtx.uHandlerSs, TrapCtx.uHandlerRsp, Bs3Tss16.ss0, uExpectedRsp);
+
+ pbTmp = (uint8_t *)ASMMemFirstNonZero(pbAltStack, cbAltStack);
+ if ((f16BitStack || TrapCtx.uHandlerRsp <= UINT16_MAX) && pbTmp != NULL)
+ bs3CpuBasic2_FailedF("someone touched the alt stack (%p) with SS:ESP=%04x:%#RX32: %p=%02x",
+ pbAltStack, Ctx2.ss, Ctx2.rsp.u32, pbTmp, *pbTmp);
+ else if (!f16BitStack && TrapCtx.uHandlerRsp > UINT16_MAX && pbTmp == NULL)
+ bs3CpuBasic2_FailedF("the alt stack (%p) was not used SS:ESP=%04x:%#RX32\n", pbAltStack, Ctx2.ss, Ctx2.rsp.u32);
+ }
+}
+
+# define bs3CpuBasic2_TssGateEspCommon BS3_CMN_NM(bs3CpuBasic2_TssGateEspCommon)
+BS3_DECL_NEAR(void) bs3CpuBasic2_TssGateEspCommon(bool const g_f16BitSys, PX86DESC const paIdt, unsigned const cIdteShift)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX Ctx2;
+# if TMPL_BITS == 16
+ uint8_t *pbTmp;
+# endif
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&Ctx2, sizeof(Ctx2));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ Bs3RegCtxSave(&Ctx);
+ Ctx.rsp.u -= 0x80;
+
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, bs3CpuBasic2_Int80);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+# endif
+
+ /*
+ * We'll be using IDT entry 80 and 81 here. The first one will be
+ * accessible from all DPLs, the latter not. So, start with setting
+ * the DPLs.
+ */
+ paIdt[0x80 << cIdteShift].Gate.u2Dpl = 3;
+ paIdt[0x81 << cIdteShift].Gate.u2Dpl = 0;
+
+ /*
+ * Check that the basic stuff works first.
+ */
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ g_usBs3TestStep = __LINE__;
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx, 0x80 /*bXcpt*/);
+
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 1, NULL, 0, g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 2, NULL, 0, g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 3, NULL, 0, g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+
+ /*
+ * Check that the upper part of ESP is preserved when doing .
+ */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ {
+ size_t const cbAltStack = _8K;
+ uint8_t *pbAltStack = Bs3MemAllocZ(BS3MEMKIND_TILED, cbAltStack);
+ if (pbAltStack)
+ {
+ /* same ring */
+ g_usBs3TestStep = __LINE__;
+ Bs3MemCpy(&Ctx2, &Ctx, sizeof(Ctx2));
+ Ctx2.rsp.u = Bs3SelPtrToFlat(pbAltStack + 0x1980);
+ if (Bs3TrapSetJmp(&TrapCtx))
+ Bs3RegCtxRestore(&Ctx2, 0); /* (does not return) */
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx2, 0x80 /*bXcpt*/);
+# if TMPL_BITS == 16
+ if ((pbTmp = (uint8_t *)ASMMemFirstNonZero(pbAltStack, cbAltStack)) != NULL)
+ bs3CpuBasic2_FailedF("someone touched the alt stack (%p) with SS:ESP=%04x:%#RX32: %p=%02x\n",
+ pbAltStack, Ctx2.ss, Ctx2.rsp.u32, pbTmp, *pbTmp);
+# else
+ if (ASMMemIsZero(pbAltStack, cbAltStack))
+ bs3CpuBasic2_FailedF("alt stack wasn't used despite SS:ESP=%04x:%#RX32\n", Ctx2.ss, Ctx2.rsp.u32);
+# endif
+
+ /* Different rings (load SS0:SP0 from TSS). */
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 1, pbAltStack, cbAltStack,
+ g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 2, pbAltStack, cbAltStack,
+ g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 3, pbAltStack, cbAltStack,
+ g_f16BitSys, g_f16BitSys, g_f16BitSys, __LINE__);
+
+ /* Different rings but switch the SS bitness in the TSS. */
+ if (g_f16BitSys)
+ {
+ Bs3Tss16.ss0 = BS3_SEL_R0_SS32;
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 1, pbAltStack, cbAltStack,
+ false, g_f16BitSys, g_f16BitSys, __LINE__);
+ Bs3Tss16.ss0 = BS3_SEL_R0_SS16;
+ }
+ else
+ {
+ Bs3Tss32.ss0 = BS3_SEL_R0_SS16;
+ bs3CpuBasic2_TssGateEsp_AltStackOuterRing(&Ctx, 1, pbAltStack, cbAltStack,
+ true, g_f16BitSys, g_f16BitSys, __LINE__);
+ Bs3Tss32.ss0 = BS3_SEL_R0_SS32;
+ }
+
+ Bs3MemFree(pbAltStack, cbAltStack);
+ }
+ else
+ Bs3TestPrintf("%s: Skipping ESP check, alloc failed\n", g_pszTestMode);
+ }
+ else
+ Bs3TestPrintf("%s: Skipping ESP check, CPU too old\n", g_pszTestMode);
+}
+
+# endif /* ARCH_BITS != 64 */
+#endif /* BS3_INSTANTIATING_CMN */
+
+
+/*
+ * Mode specific code.
+ * Mode specific code.
+ * Mode specific code.
+ */
+#ifdef BS3_INSTANTIATING_MODE
+
+BS3_DECL_FAR(uint8_t) TMPL_NM(bs3CpuBasic2_TssGateEsp)(uint8_t bMode)
+{
+ uint8_t bRet = 0;
+
+ g_pszTestMode = TMPL_NM(g_szBs3ModeName);
+ g_bTestMode = bMode;
+ g_f16BitSys = BS3_MODE_IS_16BIT_SYS(TMPL_MODE);
+
+# if TMPL_MODE == BS3_MODE_PE16 \
+ || TMPL_MODE == BS3_MODE_PE16_32 \
+ || TMPL_MODE == BS3_MODE_PP16 \
+ || TMPL_MODE == BS3_MODE_PP16_32 \
+ || TMPL_MODE == BS3_MODE_PAE16 \
+ || TMPL_MODE == BS3_MODE_PAE16_32 \
+ || TMPL_MODE == BS3_MODE_PE32
+ bs3CpuBasic2_TssGateEspCommon(BS3_MODE_IS_16BIT_SYS(TMPL_MODE),
+ (PX86DESC)MyBs3Idt,
+ BS3_MODE_IS_64BIT_SYS(TMPL_MODE) ? 1 : 0);
+# else
+ bRet = BS3TESTDOMODE_SKIPPED;
+# endif
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapInit();
+ return bRet;
+}
+
+
+BS3_DECL_FAR(uint8_t) TMPL_NM(bs3CpuBasic2_RaiseXcpt1)(uint8_t bMode)
+{
+ g_pszTestMode = TMPL_NM(g_szBs3ModeName);
+ g_bTestMode = bMode;
+ g_f16BitSys = BS3_MODE_IS_16BIT_SYS(TMPL_MODE);
+
+# if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+
+ /*
+ * Pass to common worker which is only compiled once per mode.
+ */
+ bs3CpuBasic2_RaiseXcpt1Common(MY_SYS_SEL_R0_CS,
+ MY_SYS_SEL_R0_CS_CNF,
+ MY_SYS_SEL_R0_SS,
+ (PX86DESC)MyBs3Idt,
+ BS3_MODE_IS_64BIT_SYS(TMPL_MODE) ? 1 : 0);
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapInit();
+ return 0;
+# elif TMPL_MODE == BS3_MODE_RM
+
+ /*
+ * Check
+ */
+ /** @todo check */
+ return BS3TESTDOMODE_SKIPPED;
+
+# else
+ return BS3TESTDOMODE_SKIPPED;
+# endif
+}
+
+#endif /* BS3_INSTANTIATING_MODE */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.mac
new file mode 100644
index 00000000..a07c37b4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-template.mac
@@ -0,0 +1,1879 @@
+; $Id: bs3-cpu-basic-2-template.mac $
+;; @file
+; BS3Kit - bs3-cpu-basic-2 assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+%ifnmacro BS3_CPUBAS2_UD_OFF
+%macro BS3_CPUBAS2_UD_OFF 1
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(%1) %+ _offUD, , 1
+ db BS3_CMN_NM(%1).again - BS3_CMN_NM(%1)
+%endmacro
+%endif
+
+%undef BS3_CPUBAS2_REF_LABEL_VIA_CS
+%if TMPL_BITS == 16
+ %define BS3_CPUBAS2_REF_LABEL_VIA_CS(a_Label) cs:a_Label
+%elif TMPL_BITS == 32
+ %define BS3_CPUBAS2_REF_LABEL_VIA_CS(a_Label) cs:a_Label wrt FLAT
+%elif TMPL_BITS == 64
+ %define BS3_CPUBAS2_REF_LABEL_VIA_CS(a_Label) a_Label wrt FLAT
+%else
+ %error TMPL_BITS
+%endif
+
+;;
+; Macro for generating far jmp instruction w/o nasm adding REX.W prefixes.
+;
+; @param 1 The label of the memory pointer.
+; @param 2 Prefix: 0: none, 1: 066h, 2: REX.W, 3: 066h REX.W
+%ifnmacro BS3_CPUBAS2_JMP_FAR_MEM_LABEL
+%macro BS3_CPUBAS2_JMP_FAR_MEM_LABEL 2
+ %if (%2) == 1 || (%2) == 3
+ db 066h ; o16/o32
+ %endif
+ %if TMPL_BITS != 64
+ jmp far [BS3_CPUBAS2_REF_LABEL_VIA_CS(%1)]
+ %elif TMPL_BITS == 64
+ ; 48FF2C25[040C0000] <3> jmp far [BS3_CPUBAS2_REF_LABLE_VIA_CS(.fpfn)]
+ %if (%2) == 2 || (%2) == 3
+ db 048h ; REX.W
+ %endif
+ db 0ffh, 02ch, 025h
+ dd %1 wrt FLAT
+ %else
+ %error TMPL_BITS
+ %endif
+%endmacro
+%endif
+
+;;
+; Macro for generating far call instruction w/o nasm adding REX.W prefixes.
+;
+; @param 1 The label of the memory pointer.
+; @param 2 Prefix: 0: none, 1: 066h, 2: REX.W, 3: 066h REX.W
+%ifnmacro BS3_CPUBAS2_CALL_FAR_MEM_LABEL
+%macro BS3_CPUBAS2_CALL_FAR_MEM_LABEL 2
+ %if (%2) == 1 || (%2) == 3
+ db 066h ; o16/o32
+ %endif
+ %if TMPL_BITS != 64
+ call far [BS3_CPUBAS2_REF_LABEL_VIA_CS(%1)]
+ %elif TMPL_BITS == 64
+ %if (%2) == 2 || (%2) == 3
+ db 048h ; REX.W
+ %endif
+ db 0ffh, 01ch, 025h ; call far [mem]
+ dd %1 wrt FLAT
+ %else
+ %error TMPL_BITS
+ %endif
+%endmacro
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+
+;
+; Test code snippets containing code which differs between 16-bit, 32-bit
+; and 64-bit CPUs modes.
+;
+%ifdef BS3_INSTANTIATING_CMN
+
+;
+; SIDT
+;
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_bx_ud2, BS3_PBC_NEAR
+ sidt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_bx_ud2) == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_opsize_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_opsize_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ sidt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_opsize_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_opsize_bx_ud2
+
+ %if TMPL_BITS == 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_rexw_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_rexw_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_REX_W
+ sidt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_rexw_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_rexw_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_opsize_rexw_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_opsize_rexw_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ db X86_OP_REX_W
+ sidt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_opsize_rexw_bx_ud2) == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_opsize_rexw_bx_ud2
+ %endif
+
+ %if TMPL_BITS != 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_ss_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_ss_bx_ud2, BS3_PBC_NEAR
+ sidt [ss:xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_ss_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_ss_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sidt_opsize_ss_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sidt_opsize_ss_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ sidt [ss:xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sidt_opsize_ss_bx_ud2) == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_sidt_opsize_ss_bx_ud2
+ %endif
+
+
+;
+; SGDT
+;
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_bx_ud2, BS3_PBC_NEAR
+ sgdt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_bx_ud2) == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_opsize_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_opsize_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ sgdt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_opsize_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_opsize_bx_ud2
+
+ %if TMPL_BITS == 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_rexw_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_rexw_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_REX_W
+ sgdt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_rexw_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_rexw_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ db X86_OP_REX_W
+ sgdt [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2) == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2
+ %endif
+
+ %if TMPL_BITS != 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_ss_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_ss_bx_ud2, BS3_PBC_NEAR
+ sgdt [ss:xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_ss_bx_ud2) == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_ss_bx_ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_sgdt_opsize_ss_bx_ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_sgdt_opsize_ss_bx_ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ sgdt [ss:xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_sgdt_opsize_ss_bx_ud2) == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_sgdt_opsize_ss_bx_ud2
+ %endif
+
+
+;
+; LIDT
+;
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ lidt [xBX]
+ sidt [BS3_NOT_64BIT(es:) xDI]
+ lidt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2) == BS3_IF_64BIT_OTHERWISE(9,11))
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ lidt [xBX]
+ sidt [BS3_NOT_64BIT(es:) xDI]
+ lidt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2) == BS3_IF_64BIT_OTHERWISE(10,12))
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2
+
+%if TMPL_BITS == 16
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ lidt [xBX]
+ jmp dword BS3_SEL_R0_CS32:.in_32bit wrt FLAT
+ BS3_SET_BITS 32
+.in_32bit:
+ sidt [es:edi]
+ lidt [es:esi]
+ jmp dword BS3_SEL_R0_CS16:.again wrt CGROUP16
+ BS3_SET_BITS 16
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2) == 27)
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2
+%endif
+
+ %if TMPL_BITS == 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_REX_W
+ lidt [xBX]
+ sidt [xDI]
+ lidt [xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2) == 10)
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ db X86_OP_REX_W
+ lidt [xBX]
+ sidt [xDI]
+ lidt [xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2) == 11)
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2
+ %endif
+
+ %if TMPL_BITS != 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ lidt [ss:xBX]
+ sidt [BS3_NOT_64BIT(es:) xDI]
+ lidt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2) == 12)
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ lidt [ss:xBX]
+ sidt [BS3_NOT_64BIT(es:) xDI]
+ lidt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2) == 13)
+BS3_PROC_END_CMN bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2
+ %endif
+
+
+;
+; LGDT
+;
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ lgdt [xBX]
+ sgdt [BS3_NOT_64BIT(es:) xDI]
+ lgdt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2) == BS3_IF_64BIT_OTHERWISE(9,11))
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ lgdt [xBX]
+ sgdt [BS3_NOT_64BIT(es:) xDI]
+ lgdt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2) == BS3_IF_64BIT_OTHERWISE(10,12))
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2
+
+ %if TMPL_BITS == 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_REX_W
+ lgdt [xBX]
+ sgdt [xDI]
+ lgdt [xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2) == 10)
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ db X86_OP_REX_W
+ lgdt [xBX]
+ sgdt [xDI]
+ lgdt [xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2) == 11)
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2
+ %endif
+
+ %if TMPL_BITS != 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ lgdt [ss:xBX]
+ sgdt [BS3_NOT_64BIT(es:) xDI]
+ lgdt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2) == 12)
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2, BS3_PBC_NEAR
+ db X86_OP_PRF_SIZE_OP
+ lgdt [ss:xBX]
+ sgdt [BS3_NOT_64BIT(es:) xDI]
+ lgdt [BS3_NOT_64BIT(es:) xSI]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_CMN_NM(bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2) == 13)
+BS3_PROC_END_CMN bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2
+ %endif ; TMPL_BITS != 64
+
+;
+; #PF & #AC
+;
+
+; For testing read access.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_mov_ax_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_mov_ax_ds_bx__ud2, BS3_PBC_NEAR
+ mov xAX, [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2 + (TMPL_BITS == 64))
+BS3_PROC_END_CMN bs3CpuBasic2_mov_ax_ds_bx__ud2
+
+
+; For testing write access.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_mov_ds_bx_ax__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_mov_ds_bx_ax__ud2, BS3_PBC_NEAR
+ mov [xBX], xAX
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2 + (TMPL_BITS == 64))
+BS3_PROC_END_CMN bs3CpuBasic2_mov_ds_bx_ax__ud2
+
+
+; For testing read+write access.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_xchg_ds_bx_ax__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_xchg_ds_bx_ax__ud2, BS3_PBC_NEAR
+ xchg [xBX], xAX
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2 + (TMPL_BITS == 64))
+BS3_PROC_END_CMN bs3CpuBasic2_xchg_ds_bx_ax__ud2
+
+
+; Another read+write access test.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2, BS3_PBC_NEAR
+ cmpxchg [xBX], xCX
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 3 + (TMPL_BITS == 64))
+BS3_PROC_END_CMN bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2
+
+
+; For testing read access from an aborted instruction: DIV by zero
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_div_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_div_ds_bx__ud2, BS3_PBC_NEAR
+ div xPRE [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2 + (TMPL_BITS == 64))
+BS3_PROC_END_CMN bs3CpuBasic2_div_ds_bx__ud2
+
+; For testing FLD m80 alignment (#AC).
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_fninit_fld_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_fninit_fld_ds_bx__ud2, BS3_PBC_NEAR
+ fninit ; make sure to not trigger a stack overflow.
+.actual_test_instruction:
+ fld tword [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.actual_test_instruction - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_fninit_fld_ds_bx__ud2
+
+; For testing FBLD m80 alignment (#AC).
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_fninit_fbld_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_fninit_fbld_ds_bx__ud2, BS3_PBC_NEAR
+ fninit ; make sure to not trigger a stack overflow.
+.actual_test_instruction:
+ fbld tword [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.actual_test_instruction - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_fninit_fbld_ds_bx__ud2
+
+; For testing FST m80 alignment (#AC).
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2, BS3_PBC_NEAR
+ fninit ; make sure to not trigger a stack overflow.
+ fldz ; make sure we've got something to store
+.actual_test_instruction:
+ fstp tword [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.actual_test_instruction - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2
+
+; For testing FXSAVE alignment (#AC/#GP).
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_fxsave_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_fxsave_ds_bx__ud2, BS3_PBC_NEAR
+ fxsave [xBX]
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_fxsave_ds_bx__ud2
+
+
+; Two memory operands: push [mem]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_push_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_push_ds_bx__ud2, BS3_PBC_NEAR
+ push xPRE [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_push_ds_bx__ud2
+
+; Two memory operands: pop [mem]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_push_ax__pop_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_push_ax__pop_ds_bx__ud2, BS3_PBC_NEAR
+ push xAX
+ pop xPRE [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_push_ax__pop_ds_bx__ud2
+
+; Two memory operands: call [mem]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ds_bx__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ds_bx__ud2, BS3_PBC_NEAR
+ call xPRE [xBX]
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_call_ds_bx__ud2
+
+; For testing #GP vs #PF write
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_insb__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_insb__ud2, BS3_PBC_NEAR
+ insb
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 1)
+BS3_PROC_END_CMN bs3CpuBasic2_insb__ud2
+
+
+;*********************************************************************************************************************************
+;* Non-far JMP & CALL Tests (simple ones). *
+;*********************************************************************************************************************************
+
+; jmp rel8 (forwards)
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jb__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jb__ud2, BS3_PBC_NEAR
+ jmp short .again
+.post_jmp:
+ times 7 int3
+.again: ud2
+ int3
+ jmp .again
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jb__ud2
+
+
+; jmp rel8 (backwards)
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jb_back__ud2),.again), function, 2
+ ud2
+ times 7 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jb_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jb_back__ud2, BS3_PBC_NEAR
+ jmp short .again
+.post_jmp:
+ int3
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 2)
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jb_back__ud2
+
+
+; jmp rel16 (forwards)
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jv__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jv__ud2, BS3_PBC_NEAR
+ jmp near .again
+.post_jmp:
+ times 9 int3
+.again: ud2
+ int3
+ jmp .again
+ %if TMPL_BITS == 16
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 3)
+ %else
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 5)
+ %endif
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jv__ud2
+
+
+; jmp rel16 (backwards)
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jv_back__ud2),.again), function, 2
+ ud2
+ times 6 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jv_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jv_back__ud2, BS3_PBC_NEAR
+ jmp near .again
+.post_jmp:
+ int3
+ %if TMPL_BITS == 16
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 3)
+ %else
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 5)
+ %endif
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jv_back__ud2
+
+
+; jmp [indirect]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_mem__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_mem__ud2, BS3_PBC_NEAR
+%if TMPL_BITS == 16
+ jmp [word cs:.npAgain]
+%elif TMPL_BITS == 32
+ jmp [dword cs:.npAgain]
+%else
+ jmp [.npAgain]
+%endif
+.post_jmp:
+ times 9 int3
+.npAgain:
+ %if TMPL_BITS == 16
+ dw BS3_TEXT16_WRT(.again)
+ %else
+ dd .again wrt FLAT
+ %if TMPL_BITS == 64
+ dd 0
+ %endif
+ %endif
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_mem__ud2
+
+; jmp [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_xAX__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_xAX__ud2, BS3_PBC_NEAR
+ jmp xAX
+.post_jmp:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_xAX__ud2
+
+; jmp [xDI]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_xDI__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_xDI__ud2, BS3_PBC_NEAR
+ jmp xDI
+.post_jmp:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_xDI__ud2
+
+ %if TMPL_BITS == 64
+; jmp [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_r9__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_r9__ud2, BS3_PBC_NEAR
+ jmp r9
+.post_jmp:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_r9__ud2
+ %endif
+
+
+; call rel16/32 (forwards)
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_jv__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_jv__ud2, BS3_PBC_NEAR
+ call near .again
+.post_call:
+ times 9 int3
+.again: ud2
+ int3
+ jmp .again
+ %if TMPL_BITS == 16
+AssertCompile(.post_call - BS3_LAST_LABEL == 3)
+ %else
+AssertCompile(.post_call - BS3_LAST_LABEL == 5)
+ %endif
+BS3_PROC_END_CMN bs3CpuBasic2_call_jv__ud2
+
+; call rel16/32 (backwards)
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_call_jv_back__ud2),.again), function, 2
+ ud2
+ times 6 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_jv_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_jv_back__ud2, BS3_PBC_NEAR
+ call near .again
+.post_call:
+ int3
+ %if TMPL_BITS == 16
+AssertCompile(.post_call - BS3_LAST_LABEL == 3)
+ %else
+AssertCompile(.post_call - BS3_LAST_LABEL == 5)
+ %endif
+BS3_PROC_END_CMN bs3CpuBasic2_call_jv_back__ud2
+
+; call [indirect]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_mem__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_mem__ud2, BS3_PBC_NEAR
+%if TMPL_BITS == 16
+ call [word cs:.npAgain]
+%elif TMPL_BITS == 32
+ call [dword cs:.npAgain]
+%else
+ call [.npAgain]
+%endif
+.post_call:
+ times 9 int3
+.npAgain:
+ %if TMPL_BITS == 16
+ dw BS3_TEXT16_WRT(.again)
+ %else
+ dd .again wrt FLAT
+ %if TMPL_BITS == 64
+ dd 0
+ %endif
+ %endif
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_mem__ud2
+
+; call [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_xAX__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_xAX__ud2, BS3_PBC_NEAR
+ call xAX
+.post_call:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_xAX__ud2
+
+; call [xDI]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_xDI__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_xDI__ud2, BS3_PBC_NEAR
+ call xDI
+.post_call:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_xDI__ud2
+
+ %if TMPL_BITS == 64
+; call [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_r9__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_r9__ud2, BS3_PBC_NEAR
+ call r9
+.post_call:
+ times 17 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_r9__ud2
+ %endif
+
+
+;
+; When applying opsize, we need to put this in the 16-bit text segment to
+; better control where we end up in 32-bit and 64-bit mode.
+;
+; Try keep the code out of the IVT and BIOS data area. We ASSUME that the
+; BS3TEXT16 segment portion in this object file will be at the start of the
+; image, so we won't waste much space padding up to offset 0x600.
+;
+BS3_BEGIN_TEXT16 TMPL_BITS
+%if TMPL_BITS == 32
+ %assign here ($ - $$)
+ %if here < 0x600
+ times (0x600 - here) int3
+ %endif
+%endif
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_jmp_opsize_begin), , 1
+
+
+; jmp rel8 (forwards) with opsize override.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jb_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jb_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ jmp short .again
+.post_jmp:
+ times 8 int3
+.again: ud2
+ int3
+ jmp .again
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jb_opsize__ud2
+
+
+; jmp rel8 (backwards) with opsize override.
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jb_opsize_back__ud2),.again), function, 2
+ ud2
+ times 19 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jb_opsize_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jb_opsize_back__ud2, BS3_PBC_NEAR
+ db 66h
+ jmp short .again
+.post_jmp:
+ int3
+AssertCompile(.post_jmp - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jb_opsize_back__ud2
+
+
+; jmp rel16 (forwards) with opsize override.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jv_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jv_opsize__ud2, BS3_PBC_NEAR
+ db 66h, 0e9h ; o32 jmp near .again
+ %if TMPL_BITS != 32
+ dd 11
+ %else
+ dw 11
+ %endif
+.post_jmp:
+ times 11 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jv_opsize__ud2
+
+
+; jmp rel16 (backwards) with opsize override.
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jv_opsize_back__ud2),.again), function, 2
+ ud2
+ times 19 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_jv_opsize_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_jv_opsize_back__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS != 32
+ db 66h, 0e9h ; o32 jmp near .again
+ dd RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jv_opsize_back__ud2),.again) - .post_jmp
+ %else
+ db 66h, 0e9h ; o16 jmp near .again
+ dw RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_jmp_jv_opsize_back__ud2),.again) - .post_jmp
+ %endif
+.post_jmp:
+ int3
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_jv_opsize_back__ud2
+
+
+; jmp [indirect]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_mem_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_mem_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ %if TMPL_BITS == 16
+ jmp [word cs:.npAgain]
+ %elif TMPL_BITS == 32
+ jmp [dword cs:.npAgain wrt FLAT]
+ %else
+ jmp [.npAgain wrt FLAT]
+ %endif
+.post_jmp:
+ times 9 int3
+.npAgain:
+ %if TMPL_BITS == 16
+ dw BS3_TEXT16_WRT(.again)
+ dw 0
+ %else
+ dw .again wrt CGROUP16
+ dw 0faceh, 0f00dh, 07777h ; non-canonical address
+ %endif
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_mem_opsize__ud2
+
+ %if TMPL_BITS == 64
+; jmp [indirect] - 64-bit intel version
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_mem_opsize__ud2__intel
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_mem_opsize__ud2__intel, BS3_PBC_NEAR
+ db 66h
+ jmp [.npAgain wrt FLAT]
+.post_jmp:
+ times 8 int3
+.npAgain:
+ dd .again wrt FLAT
+ dd 0
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_mem_opsize__ud2__intel
+ %endif
+
+; jmp [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmp_ind_xAX_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmp_ind_xAX_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ jmp xAX
+.post_jmp:
+ times 8 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmp_ind_xAX_opsize__ud2
+
+
+; call rel16/32 (forwards) with opsize override.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_jv_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_jv_opsize__ud2, BS3_PBC_NEAR
+ db 66h, 0e8h ; o32 jmp near .again
+ %if TMPL_BITS != 32
+ dd 12
+ %else
+ dw 12
+ %endif
+.post_call:
+ times 12 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_jv_opsize__ud2
+
+
+; call rel16/32 (backwards) with opsize override.
+BS3_GLOBAL_NAME_EX RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_call_jv_opsize_back__ud2),.again), function, 2
+ ud2
+ times 19 int3
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_jv_opsize_back__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_jv_opsize_back__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS != 32
+ db 66h, 0e8h ; o32 call near .again
+ dd RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_call_jv_opsize_back__ud2),.again) - .post_call
+ %else
+ db 66h, 0e8h ; o16 call near .again
+ dw RT_CONCAT(BS3_CMN_NM(bs3CpuBasic2_call_jv_opsize_back__ud2),.again) - .post_call
+ %endif
+.post_call:
+ int3
+BS3_PROC_END_CMN bs3CpuBasic2_call_jv_opsize_back__ud2
+
+; call [indirect]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_mem_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_mem_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ %if TMPL_BITS == 16
+ call [word cs:.npAgain]
+ %elif TMPL_BITS == 32
+ call [dword cs:.npAgain wrt FLAT]
+ %else
+ call [.npAgain wrt FLAT]
+ %endif
+.post_call:
+ times 9 int3
+.npAgain:
+ %if TMPL_BITS == 16
+ dw BS3_TEXT16_WRT(.again)
+ dw 0
+ %else
+ dw .again wrt CGROUP16
+ dw 0faceh, 0f00dh, 07777h ; non-canonical address
+ %endif
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_mem_opsize__ud2
+
+ %if TMPL_BITS == 64
+; call [indirect] - 64-bit intel version
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_mem_opsize__ud2__intel
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_mem_opsize__ud2__intel, BS3_PBC_NEAR
+ db 66h
+ call [.npAgain wrt FLAT]
+.post_call:
+ times 8 int3
+.npAgain:
+ dd .again wrt FLAT
+ dd 0
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_mem_opsize__ud2__intel
+ %endif
+
+; call [xAX]
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_call_ind_xAX_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_call_ind_xAX_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ call xAX
+.post_call:
+ times 8 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_call_ind_xAX_opsize__ud2
+
+
+;
+; Mark the end of the opsize jmp section.
+;
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_jmp_opsize_end), , 1
+ int3
+TMPL_BEGIN_TEXT
+
+
+;*********************************************************************************************************************************
+;* FAR JMP ABS *
+;*********************************************************************************************************************************
+
+;
+; Mark the start of the opsize far jmp/call section.
+;
+; Here we also need to keep the 16-bit code out of the IVT and BIOS data area,
+; not just the 32-bit and 64-bit code like for the above opsize cases.
+;
+BS3_BEGIN_TEXT16 TMPL_BITS
+ %assign here $-$$
+%if here < 0x600
+ times (0x600 - here) int3
+%endif
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_far_jmp_call_opsize_begin), , 1
+ int3
+TMPL_BEGIN_TEXT
+
+ %if TMPL_BITS == 16
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_rm__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_rm__ud2, BS3_PBC_NEAR
+ db 0eah
+ dw .again wrt CGROUP16
+ dw BS3_SEL_TEXT16
+.post_jmp:
+ times 2 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_rm__ud2
+ %endif
+
+ %if TMPL_BITS != 64
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_same_r0__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_same_r0__ud2, BS3_PBC_NEAR
+ db 0eah
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %endif
+.post_jmp:
+ times 7 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_same_r0__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_same_r1__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_same_r1__ud2, BS3_PBC_NEAR
+ db 0eah ; inter privilege jmp -> #GP(dst-cs)
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R1_CS16 | 1
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R1_CS32 | 1
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_same_r1__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_same_r2__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_same_r2__ud2, BS3_PBC_NEAR
+ db 0eah ; inter privilege jmp -> #GP(dst-cs)
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R2_CS16 | 2
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS32 | 2
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_same_r2__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_same_r3__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_same_r3__ud2, BS3_PBC_NEAR
+ db 0eah ; inter privilege jmp -> #GP(dst-cs)
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R3_CS16 | 3
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R3_CS32 | 3
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_same_r3__ud2
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2, BS3_PBC_NEAR
+ db 066h, 0eah
+ %if TMPL_BITS == 32
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %endif
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2
+TMPL_BEGIN_TEXT
+
+; Do a jmp to BS3_SEL_R0_CS64. Except for when we're in long mode, this will
+; result in a 16-bit CS with zero base and 4G limit.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS == 16
+ db 066h
+ %endif
+ db 0eah
+ dd .jmp_target wrt FLAT
+ dw BS3_SEL_R0_CS64
+ times 8 int3
+.jmp_target:
+ salc ; #UD in 64-bit mode
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+; Variation of the previous with a CS16 copy that has the L bit set, emulating
+; pre-AMD64 software using the L bit for other stuff. (Don't run in long mode
+; w/o copying the 3 bytes to the 0xxxxh memory range.)
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS != 16
+ db 066h
+ %endif
+ db 0eah
+ dw .jmp_target wrt CGROUP16
+ dw BS3_SEL_SPARE_00 ; ASSUMES this is set up as CGROUP16 but with L=1.
+ times 3 int3
+.jmp_target:
+ salc ; #UD in 64-bit mode
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2
+TMPL_BEGIN_TEXT
+
+ %endif ; TMPL_BITS != 64
+
+
+
+;*********************************************************************************************************************************
+;* FAR CALL ABS *
+;*********************************************************************************************************************************
+
+ %if TMPL_BITS == 16
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_rm__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_rm__ud2, BS3_PBC_NEAR
+ db 09ah
+ dw .again wrt CGROUP16
+ dw BS3_SEL_TEXT16
+.post_call:
+ times 2 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_rm__ud2
+ %endif
+
+ %if TMPL_BITS != 64
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_same_r0__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_same_r0__ud2, BS3_PBC_NEAR
+ db 09ah
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %endif
+.post_call:
+ times 7 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_same_r0__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_same_r1__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_same_r1__ud2, BS3_PBC_NEAR
+ db 09ah
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R1_CS16 | 1
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R1_CS32 | 1
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_same_r1__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_same_r2__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_same_r2__ud2, BS3_PBC_NEAR
+ db 09ah
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R2_CS16 | 2
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS32 | 2
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_same_r2__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_same_r3__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_same_r3__ud2, BS3_PBC_NEAR
+ db 09ah
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R3_CS16 | 3
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R3_CS32 | 3
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_same_r3__ud2
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2, BS3_PBC_NEAR
+ db 066h, 09ah
+ %if TMPL_BITS == 32
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %endif
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2
+TMPL_BEGIN_TEXT
+
+; Do a call to BS3_SEL_R0_CS64. Except for when we're in long mode, this will
+; result in a 16-bit CS with zero base and 4G limit.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_r0_cs64__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_r0_cs64__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS == 16
+ db 066h
+ %endif
+ db 09ah
+ dd .call_target wrt FLAT
+ dw BS3_SEL_R0_CS64
+ times 8 int3
+.call_target:
+ salc ; #UD in 64-bit mode
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_r0_cs64__ud2
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+; Variation of the previous with a CS16 copy that has the L bit set, emulating
+; pre-AMD64 software using the L bit for other stuff. (Don't run in long mode
+; w/o copying the 3 bytes to the 0xxxxh memory range.)
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_ptr_r0_cs16l__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_ptr_r0_cs16l__ud2, BS3_PBC_NEAR
+ %if TMPL_BITS != 16
+ db 066h
+ %endif
+ db 09ah
+ dw .call_target wrt CGROUP16
+ dw BS3_SEL_SPARE_00 ; ASSUMES this is set up as CGROUP16 but with L=1.
+ times 3 int3
+.call_target:
+ salc ; #UD in 64-bit mode
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_ptr_r0_cs16l__ud2
+TMPL_BEGIN_TEXT
+
+ %endif ; TMPL_BITS != 64
+
+
+;*********************************************************************************************************************************
+;* INDIRECT FAR JMP *
+;*********************************************************************************************************************************
+
+ %if TMPL_BITS == 16
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_rm__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_rm__ud2, BS3_PBC_NEAR
+ jmp far [BS3_CPUBAS2_REF_LABEL_VIA_CS(.fpfn)]
+ int3
+.fpfn:
+ dw .again wrt CGROUP16
+ dw BS3_SEL_TEXT16
+.post_jmp:
+ times 2 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_rm__ud2
+ %endif
+
+;;
+; Since AMD and Intel treat REX.W differently, we need two versions of the
+; test functions here and use a macro to accomplish that.
+;
+; @param 1 Symbol suffix
+; @param 2 0 for AMD, 1 for Intel.
+;
+%ifnmacro jmpf_macro
+%macro jmpf_macro 2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_same_r0__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_same_r0__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R0_CS64
+ %endif
+.post_jmp:
+ times 7 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_same_r0__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_same_r1__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_same_r1__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R1_CS16 | 1
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R1_CS32 | 1
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R1_CS64 | 1
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_same_r1__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_same_r2__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_same_r2__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, 0
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R2_CS16 | 2
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS32 | 2
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS64 | 2
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_same_r2__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_same_r3__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_same_r3__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R3_CS16 | 3
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R3_CS32 | 3
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R3_CS64 | 3
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_same_r3__ud2 %+ %1
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_r0_cs16__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_r0_cs16__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, (TMPL_BITS != 16) ; TMPL_BITS != 16 ? 1 : 0
+.fpfn:
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_r0_cs16__ud2 %+ %1
+TMPL_BEGIN_TEXT
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_r0_cs32__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_r0_cs32__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, (TMPL_BITS == 16) ; TMPL_BITS == 16 ? 1 : 0
+.fpfn:
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_r0_cs32__ud2 %+ %1
+
+; Do a jmp to BS3_SEL_R0_CS64. Except for when we're in long mode, this will
+; result in a 16-bit CS with zero base and 4G limit.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_r0_cs64__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_r0_cs64__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, (2 - (TMPL_BITS == 16)) ; TMPL_BITS == 16 ? 1 : 2
+.fpfn:
+ dd .jmp_target wrt FLAT
+ %if TMPL_BITS == 64 && %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R0_CS64
+ times 8 int3
+.jmp_target:
+ %if TMPL_BITS != 64
+ salc ; #UD in 64-bit mode
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_r0_cs64__ud2 %+ %1
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+; Variation of the previous with a CS16 copy that has the L bit set, emulating
+; pre-AMD64 software using the L bit for other stuff. (Don't run _c16/32 in
+; long mode w/o copying the 3 bytes to the 0xxxxh memory range.)
+; The _c64 version will test that the base is ignored.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_JMP_FAR_MEM_LABEL .fpfn, (TMPL_BITS == 32) ; TMPL_BITS == 32 ? 1 : 0
+.fpfn:
+ %if TMPL_BITS != 64
+ dw .jmp_target wrt CGROUP16
+ %else
+ dd .jmp_target wrt FLAT
+ %endif
+ dw BS3_SEL_SPARE_00 ; ASSUMES this is set up as CGROUP16 but with L=1.
+ times 3 int3
+.jmp_target:
+ %if TMPL_BITS != 64
+ salc ; #UD in 64-bit mode
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2 %+ %1
+TMPL_BEGIN_TEXT
+
+%endmacro
+%endif
+
+; Instantiate the above code
+jmpf_macro , 0
+ %if TMPL_BITS == 64
+jmpf_macro _intel, 1
+ %endif
+
+
+;*********************************************************************************************************************************
+;* INDIRECT FAR CALL *
+;*********************************************************************************************************************************
+
+ %if TMPL_BITS == 16
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_rm__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_rm__ud2, BS3_PBC_NEAR
+ call far [BS3_CPUBAS2_REF_LABEL_VIA_CS(.fpfn)]
+ int3
+.fpfn:
+ dw .again wrt CGROUP16
+ dw BS3_SEL_TEXT16
+.post_jmp:
+ times 2 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_rm__ud2
+ %endif
+
+
+;;
+; Since AMD and Intel treat REX.W differently, we need two versions of the
+; test functions here and use a macro to accomplish that.
+;
+; @param 1 Symbol suffix
+; @param 2 0 for AMD, 1 for Intel.
+;
+%ifnmacro callf_macro
+%macro callf_macro 2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_same_r0__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_same_r0__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R0_CS64
+ %endif
+.post_call:
+ times 7 int3
+.again: ud2
+ int3
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_same_r0__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_same_r1__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_same_r1__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R1_CS16 | 1
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R1_CS32 | 1
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R1_CS64 | 1
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_same_r1__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_same_r2__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_same_r2__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, 0
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R2_CS16 | 2
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS32 | 2
+ %else
+ dd .again wrt FLAT
+ dw BS3_SEL_R2_CS64 | 2
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_same_r2__ud2 %+ %1
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_same_r3__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_same_r3__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, 2
+.fpfn:
+ %if TMPL_BITS == 16
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R3_CS16 | 3
+ %elif TMPL_BITS == 32
+ dd .again wrt FLAT
+ dw BS3_SEL_R3_CS32 | 3
+ %else
+ dd .again wrt FLAT
+ %if %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R3_CS64 | 3
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_same_r3__ud2 %+ %1
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_r0_cs16__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_r0_cs16__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, (TMPL_BITS != 16) ; (TMPL_BITS == 16 ? 0 : 1)
+.fpfn:
+ dw .again wrt CGROUP16
+ dw BS3_SEL_R0_CS16
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_r0_cs16__ud2 %+ %1
+TMPL_BEGIN_TEXT
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_r0_cs32__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_r0_cs32__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, (TMPL_BITS == 16) ; (TMPL_BITS == 16 ? 1 : 0)
+.fpfn:
+ dd .again wrt FLAT
+ dw BS3_SEL_R0_CS32
+ times 4 int3
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_r0_cs32__ud2 %+ %1
+
+; Do a call to BS3_SEL_R0_CS64. Except for when we're in long mode, this will
+; result in a 16-bit CS with zero base and 4G limit.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_r0_cs64__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_r0_cs64__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, (2 - (TMPL_BITS == 16)) ; (TMPL_BITS == 16 ? 1 : 2)
+.fpfn:
+ dd .call_target wrt FLAT
+ %if TMPL_BITS == 64 && %2 != 0
+ dd 0fffff000h
+ %endif
+ dw BS3_SEL_R0_CS64
+ times 8 int3
+.call_target:
+ %if TMPL_BITS != 64
+ salc ; #UD in 64-bit mode
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_r0_cs64__ud2 %+ %1
+
+BS3_BEGIN_TEXT16 TMPL_BITS
+; Variation of the previous with a CS16 copy that has the L bit set, emulating
+; pre-AMD64 software using the L bit for other stuff. (Don't run _c16/32 in
+; long mode w/o copying the 3 bytes to the 0xxxxh memory range.)
+; The _c64 version will test that the base is ignored.
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_callf_mem_r0_cs16l__ud2 %+ %1
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_callf_mem_r0_cs16l__ud2 %+ %1, BS3_PBC_NEAR
+ BS3_CPUBAS2_CALL_FAR_MEM_LABEL .fpfn, (TMPL_BITS == 32) ; (TMPL_BITS == 32 ? 1 : 0)
+.fpfn:
+ %if TMPL_BITS != 64
+ dw .call_target wrt CGROUP16
+ %else
+ dd .call_target wrt FLAT
+ %endif
+ dw BS3_SEL_SPARE_00 ; ASSUMES this is set up as CGROUP16 but with L=1.
+ times 3 int3
+.call_target:
+ %if TMPL_BITS != 64
+ salc ; #UD in 64-bit mode
+ %endif
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_callf_mem_r0_cs16l__ud2 %+ %1
+TMPL_BEGIN_TEXT
+
+%endmacro ; callf_macro
+%endif
+
+; Instantiate the above code
+callf_macro , 0
+ %if TMPL_BITS == 64
+callf_macro _intel, 1
+ %endif
+
+;
+; Mark the end of the opsize far jmp/call section.
+;
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_far_jmp_call_opsize_end), , 1
+ int3
+TMPL_BEGIN_TEXT
+
+
+;*********************************************************************************************************************************
+;* Near RET *
+;*********************************************************************************************************************************
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn__ud2, BS3_PBC_NEAR
+ ret
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_retn__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i24__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i24__ud2, BS3_PBC_NEAR
+ ret 24
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i24__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i0__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i0__ud2, BS3_PBC_NEAR
+ ret 0
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i0__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i760__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i760__ud2, BS3_PBC_NEAR
+ ret 760
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 3)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i760__ud2
+
+ %if TMPL_BITS == 64
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_rexw__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_rexw__ud2, BS3_PBC_NEAR
+ db 048h ; REX.W
+ ret
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_retn_rexw__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i24_rexw__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i24_rexw__ud2, BS3_PBC_NEAR
+ db 048h ; REX.W
+ ret 24
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i24_rexw__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_opsize_rexw__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_opsize_rexw__ud2, BS3_PBC_NEAR
+ db 66h, 048h
+ ret
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_retn_opsize_rexw__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i24_opsize_rexw__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i24_opsize_rexw__ud2, BS3_PBC_NEAR
+ db 66h, 048h
+ ret 24
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i24_opsize_rexw__ud2
+
+ %endif
+
+; Mark the start of opsize tests as we end up below 64K in 32-bit and 64-bit when used.
+BS3_BEGIN_TEXT16 TMPL_BITS
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_retn_opsize_begin), , 1
+ int3
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ ret
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_retn_opsize__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i24_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i24_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ ret 24
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i24_opsize__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i0_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i0_opsize__ud2, BS3_PBC_NEAR
+ db 66h
+ ret 0
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i0_opsize__ud2
+
+ %if TMPL_BITS == 64
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_rexw_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_rexw_opsize__ud2, BS3_PBC_NEAR
+ db 048h, 66h
+ ret
+.again: ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuBasic2_retn_rexw_opsize__ud2
+
+BS3_CPUBAS2_UD_OFF bs3CpuBasic2_retn_i24_rexw_opsize__ud2
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retn_i24_rexw_opsize__ud2, BS3_PBC_NEAR
+ db 048h, 66h
+ ret 24
+.again: ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuBasic2_retn_i24_rexw_opsize__ud2
+ %endif
+
+; End of opsize tests.
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuBasic2_retn_opsize_end), , 1
+ int3
+TMPL_BEGIN_TEXT
+
+
+;*********************************************************************************************************************************
+;* FAR RET *
+;*********************************************************************************************************************************
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf, BS3_PBC_NEAR
+ db 0cbh ; retf
+BS3_PROC_END_CMN bs3CpuBasic2_retf
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_opsize, BS3_PBC_NEAR
+ db 066h, 0cbh ; o32/o16 retf
+BS3_PROC_END_CMN bs3CpuBasic2_retf_opsize
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i32, BS3_PBC_NEAR
+ db 0cah, 20h, 0 ; retf 32
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i32
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i32_opsize, BS3_PBC_NEAR
+ db 066h, 0cah, 20h, 0 ; o32/o16 retf 32
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i32_opsize
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i888, BS3_PBC_NEAR
+ db 0cah, 78h, 03h ; retf 888 (0x378)
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i888
+
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_rexw, BS3_PBC_NEAR
+ db 048h, 0cbh ; o64 retf
+BS3_PROC_END_CMN bs3CpuBasic2_retf_rexw
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_opsize_rexw, BS3_PBC_NEAR
+ db 066h, 048h, 0cbh ; o16 o64 retf
+BS3_PROC_END_CMN bs3CpuBasic2_retf_opsize_rexw
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_rexw_opsize, BS3_PBC_NEAR
+ db 048h, 066h, 0cbh ; o64 o16 retf
+BS3_PROC_END_CMN bs3CpuBasic2_retf_rexw_opsize
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i24_rexw, BS3_PBC_NEAR
+ db 048h, 0cah, 24, 0 ; o64 retf 24
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i24_rexw
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i24_opsize_rexw, BS3_PBC_NEAR
+ db 066h, 048h, 0cah, 24, 0 ; o16 o64 retf 24
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i24_opsize_rexw
+
+BS3_PROC_BEGIN_CMN bs3CpuBasic2_retf_i24_rexw_opsize, BS3_PBC_NEAR
+ db 048h, 066h, 0cah, 24, 0 ; o64 o16 retf 24
+BS3_PROC_END_CMN bs3CpuBasic2_retf_i24_rexw_opsize
+ %endif
+
+
+%endif ; BS3_INSTANTIATING_CMN
+
+%include "bs3kit-template-footer.mac" ; reset environment
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-x0.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-x0.c
new file mode 100644
index 00000000..cbdb68a8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-x0.c
@@ -0,0 +1,6400 @@
+/* $Id: bs3-cpu-basic-2-x0.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, C test driver code (16-bit).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define BS3_USE_X0_TEXT_SEG
+#include <bs3kit.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#undef CHECK_MEMBER
+#define CHECK_MEMBER(a_szName, a_szFmt, a_Actual, a_Expected) \
+ do \
+ { \
+ if ((a_Actual) == (a_Expected)) { /* likely */ } \
+ else bs3CpuBasic2_FailedF(a_szName "=" a_szFmt " expected " a_szFmt, (a_Actual), (a_Expected)); \
+ } while (0)
+
+
+/** Indicating that we've got operand size prefix and that it matters. */
+#define BS3CB2SIDTSGDT_F_OPSIZE UINT8_C(0x01)
+/** Worker requires 386 or later. */
+#define BS3CB2SIDTSGDT_F_386PLUS UINT8_C(0x02)
+
+
+/** @name MYOP_XXX - Values for FNBS3CPUBASIC2ACTSTCODE::fOp.
+ *
+ * These are flags, though we've precombined a few shortening things down.
+ *
+ * @{ */
+#define MYOP_LD 0x1 /**< The instruction loads. */
+#define MYOP_ST 0x2 /**< The instruction stores */
+#define MYOP_EFL 0x4 /**< The instruction modifies EFLAGS. */
+#define MYOP_AC_GP 0x8 /**< The instruction may cause either \#AC or \#GP (FXSAVE). */
+
+#define MYOP_LD_ST 0x3 /**< Convenience: The instruction both loads and stores. */
+#define MYOP_LD_DIV 0x5 /**< Convenience: DIV instruction - loading and modifying flags. */
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Near void pointer. */
+typedef void BS3_NEAR *NPVOID;
+
+typedef struct BS3CB2INVLDESCTYPE
+{
+ uint8_t u4Type;
+ uint8_t u1DescType;
+} BS3CB2INVLDESCTYPE;
+
+typedef struct BS3CB2SIDTSGDT
+{
+ const char *pszDesc;
+ FPFNBS3FAR fpfnWorker;
+ uint8_t cbInstr;
+ bool fSs;
+ uint8_t bMode;
+ uint8_t fFlags;
+} BS3CB2SIDTSGDT;
+
+
+typedef void BS3_CALL FNBS3CPUBASIC2ACSNIPPET(void);
+
+typedef struct FNBS3CPUBASIC2ACTSTCODE
+{
+ FNBS3CPUBASIC2ACSNIPPET BS3_FAR *pfn;
+ uint8_t fOp;
+ uint16_t cbMem;
+ uint8_t cbAlign;
+ uint8_t offFaultInstr; /**< For skipping fninit with the fld test. */
+} FNBS3CPUBASIC2ACTSTCODE;
+typedef FNBS3CPUBASIC2ACTSTCODE const *PCFNBS3CPUBASIC2ACTSTCODE;
+
+typedef struct BS3CPUBASIC2ACTTSTCMNMODE
+{
+ uint8_t bMode;
+ uint16_t cEntries;
+ PCFNBS3CPUBASIC2ACTSTCODE paEntries;
+} BS3CPUBASIC2PFTTSTCMNMODE;
+typedef BS3CPUBASIC2PFTTSTCMNMODE const *PCBS3CPUBASIC2PFTTSTCMNMODE;
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern FNBS3FAR bs3CpuBasic2_Int80;
+extern FNBS3FAR bs3CpuBasic2_Int81;
+extern FNBS3FAR bs3CpuBasic2_Int82;
+extern FNBS3FAR bs3CpuBasic2_Int83;
+
+extern FNBS3FAR bs3CpuBasic2_ud2;
+#define g_bs3CpuBasic2_ud2_FlatAddr BS3_DATA_NM(g_bs3CpuBasic2_ud2_FlatAddr)
+extern uint32_t g_bs3CpuBasic2_ud2_FlatAddr;
+
+extern FNBS3FAR bs3CpuBasic2_salc_ud2;
+extern FNBS3FAR bs3CpuBasic2_swapgs;
+
+extern FNBS3FAR bs3CpuBasic2_iret;
+extern FNBS3FAR bs3CpuBasic2_iret_opsize;
+extern FNBS3FAR bs3CpuBasic2_iret_rexw;
+
+extern FNBS3FAR bs3CpuBasic2_sidt_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sidt_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sidt_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sidt_ss_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sidt_ss_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sidt_rexw_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_ss_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_ss_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sidt_opsize_rexw_bx_ud2_c64;
+
+extern FNBS3FAR bs3CpuBasic2_sgdt_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sgdt_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sgdt_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sgdt_ss_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sgdt_ss_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sgdt_rexw_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_bx_ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_ss_bx_ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_ss_bx_ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2_c64;
+
+extern FNBS3FAR bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2_c64;
+
+extern FNBS3FAR bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c64;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c16;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c32;
+extern FNBS3FAR bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2_c64;
+
+
+/* bs3-cpu-basic-2-template.mac: */
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fld_ds_bx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c16;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fxsave_ds_bx__ud2_c16;
+
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fld_ds_bx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c32;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fxsave_ds_bx__ud2_c32;
+
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fld_ds_bx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c64;
+FNBS3CPUBASIC2ACSNIPPET bs3CpuBasic2_fxsave_ds_bx__ud2_c64;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const char BS3_FAR *g_pszTestMode = (const char *)1;
+static uint8_t g_bTestMode = 1;
+static bool g_f16BitSys = 1;
+
+
+/** SIDT test workers. */
+static BS3CB2SIDTSGDT const g_aSidtWorkers[] =
+{
+ { "sidt [bx]", bs3CpuBasic2_sidt_bx_ud2_c16, 3, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "sidt [ss:bx]", bs3CpuBasic2_sidt_ss_bx_ud2_c16, 4, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "o32 sidt [bx]", bs3CpuBasic2_sidt_opsize_bx_ud2_c16, 4, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_386PLUS },
+ { "o32 sidt [ss:bx]", bs3CpuBasic2_sidt_opsize_ss_bx_ud2_c16, 5, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_386PLUS },
+ { "sidt [ebx]", bs3CpuBasic2_sidt_bx_ud2_c32, 3, false, BS3_MODE_CODE_32, 0 },
+ { "sidt [ss:ebx]", bs3CpuBasic2_sidt_ss_bx_ud2_c32, 4, true, BS3_MODE_CODE_32, 0 },
+ { "o16 sidt [ebx]", bs3CpuBasic2_sidt_opsize_bx_ud2_c32, 4, false, BS3_MODE_CODE_32, 0 },
+ { "o16 sidt [ss:ebx]", bs3CpuBasic2_sidt_opsize_ss_bx_ud2_c32, 5, true, BS3_MODE_CODE_32, 0 },
+ { "sidt [rbx]", bs3CpuBasic2_sidt_bx_ud2_c64, 3, false, BS3_MODE_CODE_64, 0 },
+ { "o64 sidt [rbx]", bs3CpuBasic2_sidt_rexw_bx_ud2_c64, 4, false, BS3_MODE_CODE_64, 0 },
+ { "o32 sidt [rbx]", bs3CpuBasic2_sidt_opsize_bx_ud2_c64, 4, false, BS3_MODE_CODE_64, 0 },
+ { "o32 o64 sidt [rbx]", bs3CpuBasic2_sidt_opsize_rexw_bx_ud2_c64, 5, false, BS3_MODE_CODE_64, 0 },
+};
+
+/** SGDT test workers. */
+static BS3CB2SIDTSGDT const g_aSgdtWorkers[] =
+{
+ { "sgdt [bx]", bs3CpuBasic2_sgdt_bx_ud2_c16, 3, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "sgdt [ss:bx]", bs3CpuBasic2_sgdt_ss_bx_ud2_c16, 4, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "o32 sgdt [bx]", bs3CpuBasic2_sgdt_opsize_bx_ud2_c16, 4, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_386PLUS },
+ { "o32 sgdt [ss:bx]", bs3CpuBasic2_sgdt_opsize_ss_bx_ud2_c16, 5, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_386PLUS },
+ { "sgdt [ebx]", bs3CpuBasic2_sgdt_bx_ud2_c32, 3, false, BS3_MODE_CODE_32, 0 },
+ { "sgdt [ss:ebx]", bs3CpuBasic2_sgdt_ss_bx_ud2_c32, 4, true, BS3_MODE_CODE_32, 0 },
+ { "o16 sgdt [ebx]", bs3CpuBasic2_sgdt_opsize_bx_ud2_c32, 4, false, BS3_MODE_CODE_32, 0 },
+ { "o16 sgdt [ss:ebx]", bs3CpuBasic2_sgdt_opsize_ss_bx_ud2_c32, 5, true, BS3_MODE_CODE_32, 0 },
+ { "sgdt [rbx]", bs3CpuBasic2_sgdt_bx_ud2_c64, 3, false, BS3_MODE_CODE_64, 0 },
+ { "o64 sgdt [rbx]", bs3CpuBasic2_sgdt_rexw_bx_ud2_c64, 4, false, BS3_MODE_CODE_64, 0 },
+ { "o32 sgdt [rbx]", bs3CpuBasic2_sgdt_opsize_bx_ud2_c64, 4, false, BS3_MODE_CODE_64, 0 },
+ { "o32 o64 sgdt [rbx]", bs3CpuBasic2_sgdt_opsize_rexw_bx_ud2_c64, 5, false, BS3_MODE_CODE_64, 0 },
+};
+
+/** LIDT test workers. */
+static BS3CB2SIDTSGDT const g_aLidtWorkers[] =
+{
+ { "lidt [bx]", bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c16, 11, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "lidt [ss:bx]", bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2_c16, 12, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "o32 lidt [bx]", bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c16, 12, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_OPSIZE | BS3CB2SIDTSGDT_F_386PLUS },
+ { "o32 lidt [bx]; sidt32", bs3CpuBasic2_lidt_opsize_bx__sidt32_es_di__lidt_es_si__ud2_c16, 27, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_OPSIZE | BS3CB2SIDTSGDT_F_386PLUS },
+ { "o32 lidt [ss:bx]", bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2_c16, 13, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_OPSIZE | BS3CB2SIDTSGDT_F_386PLUS },
+ { "lidt [ebx]", bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c32, 11, false, BS3_MODE_CODE_32, 0 },
+ { "lidt [ss:ebx]", bs3CpuBasic2_lidt_ss_bx__sidt_es_di__lidt_es_si__ud2_c32, 12, true, BS3_MODE_CODE_32, 0 },
+ { "o16 lidt [ebx]", bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c32, 12, false, BS3_MODE_CODE_32, BS3CB2SIDTSGDT_F_OPSIZE },
+ { "o16 lidt [ss:ebx]", bs3CpuBasic2_lidt_opsize_ss_bx__sidt_es_di__lidt_es_si__ud2_c32, 13, true, BS3_MODE_CODE_32, BS3CB2SIDTSGDT_F_OPSIZE },
+ { "lidt [rbx]", bs3CpuBasic2_lidt_bx__sidt_es_di__lidt_es_si__ud2_c64, 9, false, BS3_MODE_CODE_64, 0 },
+ { "o64 lidt [rbx]", bs3CpuBasic2_lidt_rexw_bx__sidt_es_di__lidt_es_si__ud2_c64, 10, false, BS3_MODE_CODE_64, 0 },
+ { "o32 lidt [rbx]", bs3CpuBasic2_lidt_opsize_bx__sidt_es_di__lidt_es_si__ud2_c64, 10, false, BS3_MODE_CODE_64, 0 },
+ { "o32 o64 lidt [rbx]", bs3CpuBasic2_lidt_opsize_rexw_bx__sidt_es_di__lidt_es_si__ud2_c64, 11, false, BS3_MODE_CODE_64, 0 },
+};
+
+/** LGDT test workers. */
+static BS3CB2SIDTSGDT const g_aLgdtWorkers[] =
+{
+ { "lgdt [bx]", bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c16, 11, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "lgdt [ss:bx]", bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c16, 12, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, 0 },
+ { "o32 lgdt [bx]", bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c16, 12, false, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_OPSIZE | BS3CB2SIDTSGDT_F_386PLUS },
+ { "o32 lgdt [ss:bx]", bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c16, 13, true, BS3_MODE_CODE_16 | BS3_MODE_CODE_V86, BS3CB2SIDTSGDT_F_OPSIZE | BS3CB2SIDTSGDT_F_386PLUS },
+ { "lgdt [ebx]", bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c32, 11, false, BS3_MODE_CODE_32, 0 },
+ { "lgdt [ss:ebx]", bs3CpuBasic2_lgdt_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c32, 12, true, BS3_MODE_CODE_32, 0 },
+ { "o16 lgdt [ebx]", bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c32, 12, false, BS3_MODE_CODE_32, BS3CB2SIDTSGDT_F_OPSIZE },
+ { "o16 lgdt [ss:ebx]", bs3CpuBasic2_lgdt_opsize_ss_bx__sgdt_es_di__lgdt_es_si__ud2_c32, 13, true, BS3_MODE_CODE_32, BS3CB2SIDTSGDT_F_OPSIZE },
+ { "lgdt [rbx]", bs3CpuBasic2_lgdt_bx__sgdt_es_di__lgdt_es_si__ud2_c64, 9, false, BS3_MODE_CODE_64, 0 },
+ { "o64 lgdt [rbx]", bs3CpuBasic2_lgdt_rexw_bx__sgdt_es_di__lgdt_es_si__ud2_c64, 10, false, BS3_MODE_CODE_64, 0 },
+ { "o32 lgdt [rbx]", bs3CpuBasic2_lgdt_opsize_bx__sgdt_es_di__lgdt_es_si__ud2_c64, 10, false, BS3_MODE_CODE_64, 0 },
+ { "o32 o64 lgdt [rbx]", bs3CpuBasic2_lgdt_opsize_rexw_bx__sgdt_es_di__lgdt_es_si__ud2_c64, 11, false, BS3_MODE_CODE_64, 0 },
+};
+
+
+
+#if 0
+/** Table containing invalid CS selector types. */
+static const BS3CB2INVLDESCTYPE g_aInvalidCsTypes[] =
+{
+ { X86_SEL_TYPE_RO, 1 },
+ { X86_SEL_TYPE_RO_ACC, 1 },
+ { X86_SEL_TYPE_RW, 1 },
+ { X86_SEL_TYPE_RW_ACC, 1 },
+ { X86_SEL_TYPE_RO_DOWN, 1 },
+ { X86_SEL_TYPE_RO_DOWN_ACC, 1 },
+ { X86_SEL_TYPE_RW_DOWN, 1 },
+ { X86_SEL_TYPE_RW_DOWN_ACC, 1 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 3, 0 },
+ { 4, 0 },
+ { 5, 0 },
+ { 6, 0 },
+ { 7, 0 },
+ { 8, 0 },
+ { 9, 0 },
+ { 10, 0 },
+ { 11, 0 },
+ { 12, 0 },
+ { 13, 0 },
+ { 14, 0 },
+ { 15, 0 },
+};
+
+/** Table containing invalid SS selector types. */
+static const BS3CB2INVLDESCTYPE g_aInvalidSsTypes[] =
+{
+ { X86_SEL_TYPE_EO, 1 },
+ { X86_SEL_TYPE_EO_ACC, 1 },
+ { X86_SEL_TYPE_ER, 1 },
+ { X86_SEL_TYPE_ER_ACC, 1 },
+ { X86_SEL_TYPE_EO_CONF, 1 },
+ { X86_SEL_TYPE_EO_CONF_ACC, 1 },
+ { X86_SEL_TYPE_ER_CONF, 1 },
+ { X86_SEL_TYPE_ER_CONF_ACC, 1 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 3, 0 },
+ { 4, 0 },
+ { 5, 0 },
+ { 6, 0 },
+ { 7, 0 },
+ { 8, 0 },
+ { 9, 0 },
+ { 10, 0 },
+ { 11, 0 },
+ { 12, 0 },
+ { 13, 0 },
+ { 14, 0 },
+ { 15, 0 },
+};
+#endif
+
+
+static const FNBS3CPUBASIC2ACTSTCODE g_aCmn16[] =
+{
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c16, MYOP_LD, 2, 2 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c16, MYOP_ST, 2, 2 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16, MYOP_LD_ST, 2, 2 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16, MYOP_LD_ST | MYOP_EFL, 2, 2 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c16, MYOP_LD_DIV, 2, 2 },
+ { bs3CpuBasic2_fninit_fld_ds_bx__ud2_c16, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c16, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c16, MYOP_ST, 10, 8, 4 /*fninit+fldz*/ },
+ { bs3CpuBasic2_fxsave_ds_bx__ud2_c16, MYOP_ST | MYOP_AC_GP, 512, 16 },
+};
+
+static const FNBS3CPUBASIC2ACTSTCODE g_aCmn32[] =
+{
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c32, MYOP_LD, 4, 4 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c32, MYOP_ST, 4, 4 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32, MYOP_LD_ST, 4, 4 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32, MYOP_LD_ST | MYOP_EFL, 4, 4 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c32, MYOP_LD_DIV, 4, 4 },
+ { bs3CpuBasic2_fninit_fld_ds_bx__ud2_c32, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c32, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c32, MYOP_ST, 10, 8, 4 /*fninit+fldz*/ },
+ { bs3CpuBasic2_fxsave_ds_bx__ud2_c32, MYOP_ST | MYOP_AC_GP, 512, 16 },
+};
+
+static const FNBS3CPUBASIC2ACTSTCODE g_aCmn64[] =
+{
+ { bs3CpuBasic2_mov_ax_ds_bx__ud2_c64, MYOP_LD, 8, 8 },
+ { bs3CpuBasic2_mov_ds_bx_ax__ud2_c64, MYOP_ST, 8, 8 },
+ { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64, MYOP_LD_ST, 8, 8 },
+ { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64, MYOP_LD_ST | MYOP_EFL, 8, 8 },
+ { bs3CpuBasic2_div_ds_bx__ud2_c64, MYOP_LD_DIV, 8, 8 },
+ { bs3CpuBasic2_fninit_fld_ds_bx__ud2_c64, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fbld_ds_bx__ud2_c64, MYOP_LD, 10, 8, 2 /*fninit*/ },
+ { bs3CpuBasic2_fninit_fldz_fstp_ds_bx__ud2_c64, MYOP_ST, 10, 8, 4 /*fninit+fldz*/ },
+ { bs3CpuBasic2_fxsave_ds_bx__ud2_c64, MYOP_ST | MYOP_AC_GP, 512, 16 },
+};
+
+static const BS3CPUBASIC2PFTTSTCMNMODE g_aCmnModes[] =
+{
+ { BS3_MODE_CODE_16, RT_ELEMENTS(g_aCmn16), g_aCmn16 },
+ { BS3_MODE_CODE_V86, RT_ELEMENTS(g_aCmn16), g_aCmn16 },
+ { BS3_MODE_CODE_32, RT_ELEMENTS(g_aCmn32), g_aCmn32 },
+ { BS3_MODE_CODE_64, RT_ELEMENTS(g_aCmn64), g_aCmn64 },
+};
+
+
+/**
+ * Sets globals according to the mode.
+ *
+ * @param bTestMode The test mode.
+ */
+static void bs3CpuBasic2_SetGlobals(uint8_t bTestMode)
+{
+ g_bTestMode = bTestMode;
+ g_pszTestMode = Bs3GetModeName(bTestMode);
+ g_f16BitSys = BS3_MODE_IS_16BIT_SYS(bTestMode);
+ g_usBs3TestStep = 0;
+}
+
+
+uint32_t ASMGetESP(void);
+#pragma aux ASMGetESP = \
+ ".386" \
+ "mov ax, sp" \
+ "mov edx, esp" \
+ "shr edx, 16" \
+ value [ax dx] \
+ modify exact [ax dx];
+
+
+/**
+ * Wrapper around Bs3TestFailedF that prefixes the error with g_usBs3TestStep
+ * and g_pszTestMode.
+ */
+static void bs3CpuBasic2_FailedF(const char *pszFormat, ...)
+{
+ va_list va;
+
+ char szTmp[168];
+ va_start(va, pszFormat);
+ Bs3StrPrintfV(szTmp, sizeof(szTmp), pszFormat, va);
+ va_end(va);
+
+ Bs3TestFailedF("%u - %s: %s", g_usBs3TestStep, g_pszTestMode, szTmp);
+}
+
+
+#if 0
+/**
+ * Compares trap stuff.
+ */
+static void bs3CpuBasic2_CompareIntCtx1(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint8_t bXcpt)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, 0);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, 2 /*int xx*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting in CompareTrapCtx1: bXcpt=%#x\n", bXcpt);
+ ASMHalt();
+#endif
+ }
+}
+#endif
+
+
+#if 0
+/**
+ * Compares trap stuff.
+ */
+static void bs3CpuBasic2_CompareTrapCtx2(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t cbIpAdjust,
+ uint8_t bXcpt, uint16_t uHandlerCs)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, 0);
+ CHECK_MEMBER("uHandlerCs", "%#06x", pTrapCtx->uHandlerCs, uHandlerCs);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, cbIpAdjust, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting in CompareTrapCtx2: bXcpt=%#x\n", bXcpt);
+ ASMHalt();
+#endif
+ }
+}
+#endif
+
+/**
+ * Compares a CPU trap.
+ */
+static void bs3CpuBasic2_CompareCpuTrapCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd,
+ uint8_t bXcpt, bool f486ResumeFlagHint, uint8_t cbIpAdjust)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint32_t fExtraEfl;
+
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX16", (uint16_t)pTrapCtx->uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */
+
+ if ( g_f16BitSys
+ || bXcpt == X86_XCPT_DB /* hack (10980xe)... */
+ || ( !f486ResumeFlagHint
+ && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) <= BS3CPU_80486 ) )
+ fExtraEfl = 0;
+ else
+ fExtraEfl = X86_EFL_RF;
+#if 0 /** @todo Running on an AMD Phenom II X6 1100T under AMD-V I'm not getting good X86_EFL_RF results. Enable this to get on with other work. */
+ fExtraEfl = pTrapCtx->Ctx.rflags.u32 & X86_EFL_RF;
+#endif
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, cbIpAdjust, 0 /*cbSpAdjust*/, fExtraEfl, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+#if 1
+ Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+ Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd);
+ ASMHalt();
+#endif
+ }
+}
+
+
+/**
+ * Compares \#GP trap.
+ */
+static void bs3CpuBasic2_CompareGpCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_GP, true /*f486ResumeFlagHint*/, 0 /*cbIpAdjust*/);
+}
+
+#if 0
+/**
+ * Compares \#NP trap.
+ */
+static void bs3CpuBasic2_CompareNpCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_NP, true /*f486ResumeFlagHint*/, 0 /*cbIpAdjust*/);
+}
+#endif
+
+/**
+ * Compares \#SS trap.
+ */
+static void bs3CpuBasic2_CompareSsCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd, bool f486ResumeFlagHint)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_SS, f486ResumeFlagHint, 0 /*cbIpAdjust*/);
+}
+
+#if 0
+/**
+ * Compares \#TS trap.
+ */
+static void bs3CpuBasic2_CompareTsCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint16_t uErrCd)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_TS, false /*f486ResumeFlagHint*/, 0 /*cbIpAdjust*/);
+}
+#endif
+
+/**
+ * Compares \#PF trap.
+ */
+static void bs3CpuBasic2_ComparePfCtx(PCBS3TRAPFRAME pTrapCtx, PBS3REGCTX pStartCtx, uint16_t uErrCd,
+ uint64_t uCr2Expected, uint8_t cbIpAdjust)
+{
+ uint64_t const uCr2Saved = pStartCtx->cr2.u;
+ pStartCtx->cr2.u = uCr2Expected;
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, uErrCd, X86_XCPT_PF, true /*f486ResumeFlagHint*/, cbIpAdjust);
+ pStartCtx->cr2.u = uCr2Saved;
+}
+
+/**
+ * Compares \#UD trap.
+ */
+static void bs3CpuBasic2_CompareUdCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, 0 /*no error code*/, X86_XCPT_UD,
+ true /*f486ResumeFlagHint*/, 0 /*cbIpAdjust*/);
+}
+
+/**
+ * Compares \#AC trap.
+ */
+static void bs3CpuBasic2_CompareAcCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint8_t cbIpAdjust)
+{
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, 0 /*always zero*/, X86_XCPT_AC, true /*f486ResumeFlagHint*/, cbIpAdjust);
+}
+
+/**
+ * Compares \#DB trap.
+ */
+static void bs3CpuBasic2_CompareDbCtx(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint32_t fDr6Expect)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint32_t const fDr6 = Bs3RegGetDr6();
+ fDr6Expect |= X86_DR6_RA1_MASK;
+ CHECK_MEMBER("dr6", "%#08RX32", fDr6, fDr6Expect);
+
+ bs3CpuBasic2_CompareCpuTrapCtx(pTrapCtx, pStartCtx, 0 /*always zero*/, X86_XCPT_DB, false /*f486ResumeFlagHint?*/, 0 /*cbIpAdjust*/);
+
+ if (Bs3TestSubErrorCount() > cErrorsBefore)
+ {
+#if 0
+ Bs3TestPrintf("Halting\n");
+ ASMHalt();
+#endif
+ }
+}
+
+
+/**
+ * Checks that DR6 has the initial value, i.e. is unchanged when other exception
+ * was raised before a \#DB could occur.
+ */
+static void bs3CpuBasic2_CheckDr6InitVal(void)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint32_t const fDr6 = Bs3RegGetDr6();
+ uint32_t const fDr6Expect = X86_DR6_INIT_VAL;
+ CHECK_MEMBER("dr6", "%#08RX32", fDr6, fDr6Expect);
+ if (Bs3TestSubErrorCount() > cErrorsBefore)
+ {
+ Bs3TestPrintf("Halting\n");
+ ASMHalt();
+ }
+}
+
+#if 0 /* convert me */
+static void bs3CpuBasic2_RaiseXcpt1Common(uint16_t const uSysR0Cs, uint16_t const uSysR0CsConf, uint16_t const uSysR0Ss,
+ PX86DESC const paIdt, unsigned const cIdteShift)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx80;
+ BS3REGCTX Ctx81;
+ BS3REGCTX Ctx82;
+ BS3REGCTX Ctx83;
+ BS3REGCTX CtxTmp;
+ BS3REGCTX CtxTmp2;
+ PBS3REGCTX apCtx8x[4];
+ unsigned iCtx;
+ unsigned iRing;
+ unsigned iDpl;
+ unsigned iRpl;
+ unsigned i, j, k;
+ uint32_t uExpected;
+ bool const f486Plus = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486;
+# if TMPL_BITS == 16
+ bool const f386Plus = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386;
+ bool const f286 = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) == BS3CPU_80286;
+# else
+ bool const f286 = false;
+ bool const f386Plus = true;
+ int rc;
+ uint8_t *pbIdtCopyAlloc;
+ PX86DESC pIdtCopy;
+ const unsigned cbIdte = 1 << (3 + cIdteShift);
+ RTCCUINTXREG uCr0Saved = ASMGetCR0();
+ RTGDTR GdtrSaved;
+# endif
+ RTIDTR IdtrSaved;
+ RTIDTR Idtr;
+
+ ASMGetIDTR(&IdtrSaved);
+# if TMPL_BITS != 16
+ ASMGetGDTR(&GdtrSaved);
+# endif
+
+ /* make sure they're allocated */
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&Ctx80, sizeof(Ctx80));
+ Bs3MemZero(&Ctx81, sizeof(Ctx81));
+ Bs3MemZero(&Ctx82, sizeof(Ctx82));
+ Bs3MemZero(&Ctx83, sizeof(Ctx83));
+ Bs3MemZero(&CtxTmp, sizeof(CtxTmp));
+ Bs3MemZero(&CtxTmp2, sizeof(CtxTmp2));
+
+ /* Context array. */
+ apCtx8x[0] = &Ctx80;
+ apCtx8x[1] = &Ctx81;
+ apCtx8x[2] = &Ctx82;
+ apCtx8x[3] = &Ctx83;
+
+# if TMPL_BITS != 16
+ /* Allocate memory for playing around with the IDT. */
+ pbIdtCopyAlloc = NULL;
+ if (BS3_MODE_IS_PAGED(g_bTestMode))
+ pbIdtCopyAlloc = Bs3MemAlloc(BS3MEMKIND_FLAT32, 12*_1K);
+# endif
+
+ /*
+ * IDT entry 80 thru 83 are assigned DPLs according to the number.
+ * (We'll be useing more, but this'll do for now.)
+ */
+ paIdt[0x80 << cIdteShift].Gate.u2Dpl = 0;
+ paIdt[0x81 << cIdteShift].Gate.u2Dpl = 1;
+ paIdt[0x82 << cIdteShift].Gate.u2Dpl = 2;
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 3;
+
+ Bs3RegCtxSave(&Ctx80);
+ Ctx80.rsp.u -= 0x300;
+ Ctx80.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int80);
+# if TMPL_BITS == 16
+ Ctx80.cs = BS3_MODE_IS_RM_OR_V86(g_bTestMode) ? BS3_SEL_TEXT16 : BS3_SEL_R0_CS16;
+# elif TMPL_BITS == 32
+ g_uBs3TrapEipHint = Ctx80.rip.u32;
+# endif
+ Bs3MemCpy(&Ctx81, &Ctx80, sizeof(Ctx80));
+ Ctx81.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int81);
+ Bs3MemCpy(&Ctx82, &Ctx80, sizeof(Ctx80));
+ Ctx82.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int82);
+ Bs3MemCpy(&Ctx83, &Ctx80, sizeof(Ctx80));
+ Ctx83.rip.u = (uintptr_t)BS3_FP_OFF(&bs3CpuBasic2_Int83);
+
+ /*
+ * Check that all the above gates work from ring-0.
+ */
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ g_usBs3TestStep = iCtx;
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = apCtx8x[iCtx]->rip.u32;
+# endif
+ Bs3TrapSetJmpAndRestore(apCtx8x[iCtx], &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, apCtx8x[iCtx], 0x80+iCtx /*bXcpt*/);
+ }
+
+ /*
+ * Check that the gate DPL checks works.
+ */
+ g_usBs3TestStep = 100;
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ g_usBs3TestStep++;
+ }
+ }
+
+ /*
+ * Modify the gate CS value and run the handler at a different CPL.
+ * Throw RPL variations into the mix (completely ignored) together
+ * with gate presence.
+ * 1. CPL <= GATE.DPL
+ * 2. GATE.P
+ * 3. GATE.CS.DPL <= CPL (non-conforming segments)
+ */
+ g_usBs3TestStep = 1000;
+ for (i = 0; i <= 3; i++)
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = apCtx8x[iCtx]->rip.u32;
+# endif
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ for (j = 0; j <= 3; j++)
+ {
+ uint16_t const uCs = (uSysR0Cs | j) + (i << BS3_SEL_RING_SHIFT);
+ for (k = 0; k < 2; k++)
+ {
+ g_usBs3TestStep++;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = k;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (k == 0)
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (i > iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ else
+ {
+ uint16_t uExpectedCs = uCs & X86_SEL_MASK_OFF_RPL;
+ if (i <= iCtx && i <= iRing)
+ uExpectedCs |= i;
+ bs3CpuBasic2_CompareTrapCtx2(&TrapCtx, &CtxTmp, 2 /*int 8xh*/, 0x80 + iCtx /*bXcpt*/, uExpectedCs);
+ }
+ }
+ }
+
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 1600);
+
+ /*
+ * Various CS and SS related faults
+ *
+ * We temporarily reconfigure gate 80 and 83 with new CS selectors, the
+ * latter have a CS.DPL of 2 for testing ring transisions and SS loading
+ * without making it impossible to handle faults.
+ */
+ g_usBs3TestStep = 1600;
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ Bs3GdteTestPage00.Gen.u1Present = 0;
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_00;
+
+ /* CS.PRESENT = 0 */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("selector was accessed");
+ g_usBs3TestStep++;
+
+ /* Check that GATE.DPL is checked before CS.PRESENT. */
+ for (iRing = 1; iRing < 4; iRing++)
+ {
+ Bs3MemCpy(&CtxTmp, &Ctx80, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, (0x80 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("selector was accessed");
+ g_usBs3TestStep++;
+ }
+
+ /* CS.DPL mismatch takes precedence over CS.PRESENT = 0. */
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("CS selector was accessed");
+ g_usBs3TestStep++;
+ for (iDpl = 1; iDpl < 4; iDpl++)
+ {
+ Bs3GdteTestPage00.Gen.u2Dpl = iDpl;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("CS selector was accessed");
+ g_usBs3TestStep++;
+ }
+
+ /* 1608: Check all the invalid CS selector types alone. */
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ for (i = 0; i < RT_ELEMENTS(g_aInvalidCsTypes); i++)
+ {
+ Bs3GdteTestPage00.Gen.u4Type = g_aInvalidCsTypes[i].u4Type;
+ Bs3GdteTestPage00.Gen.u1DescType = g_aInvalidCsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ if (Bs3GdteTestPage00.Gen.u4Type != g_aInvalidCsTypes[i].u4Type)
+ bs3CpuBasic2_FailedF("Invalid CS type %#x/%u -> %#x/%u\n",
+ g_aInvalidCsTypes[i].u4Type, g_aInvalidCsTypes[i].u1DescType,
+ Bs3GdteTestPage00.Gen.u4Type, Bs3GdteTestPage00.Gen.u1DescType);
+ g_usBs3TestStep++;
+
+ /* Incorrect CS.TYPE takes precedence over CS.PRESENT = 0. */
+ Bs3GdteTestPage00.Gen.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx80, BS3_SEL_TEST_PAGE_00);
+ Bs3GdteTestPage00.Gen.u1Present = 1;
+ g_usBs3TestStep++;
+ }
+
+ /* Fix CS again. */
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+
+ /* 1632: Test SS. */
+ if (!BS3_MODE_IS_64BIT_SYS(g_bTestMode))
+ {
+ uint16_t BS3_FAR *puTssSs2 = BS3_MODE_IS_16BIT_SYS(g_bTestMode) ? &Bs3Tss16.ss2 : &Bs3Tss32.ss2;
+ uint16_t const uSavedSs2 = *puTssSs2;
+ X86DESC const SavedGate83 = paIdt[0x83 << cIdteShift];
+
+ /* Make the handler execute in ring-2. */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage02.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_02 | 2;
+
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3); /* yeah, from 3 so SS:xSP is reloaded. */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ if (!(Bs3GdteTestPage02.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("CS selector was not access");
+ g_usBs3TestStep++;
+
+ /* Create a SS.DPL=2 stack segment and check that SS2.RPL matters and
+ that we get #SS if the selector isn't present. */
+ i = 0; /* used for cycling thru invalid CS types */
+ for (k = 0; k < 10; k++)
+ {
+ /* k=0: present,
+ k=1: not-present,
+ k=2: present but very low limit,
+ k=3: not-present, low limit.
+ k=4: present, read-only.
+ k=5: not-present, read-only.
+ k=6: present, code-selector.
+ k=7: not-present, code-selector.
+ k=8: present, read-write / no access + system (=LDT).
+ k=9: not-present, read-write / no access + system (=LDT).
+ */
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u1Present = !(k & 1);
+ if (k >= 8)
+ {
+ Bs3GdteTestPage03.Gen.u1DescType = 0; /* system */
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RW; /* = LDT */
+ }
+ else if (k >= 6)
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_ER;
+ else if (k >= 4)
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RO;
+ else if (k >= 2)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = 0x400;
+ Bs3GdteTestPage03.Gen.u4LimitHigh = 0;
+ Bs3GdteTestPage03.Gen.u1Granularity = 0;
+ }
+
+ for (iDpl = 0; iDpl < 4; iDpl++)
+ {
+ Bs3GdteTestPage03.Gen.u2Dpl = iDpl;
+
+ for (iRpl = 0; iRpl < 4; iRpl++)
+ {
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | iRpl;
+ //Bs3TestPrintf("k=%u iDpl=%u iRpl=%u step=%u\n", k, iDpl, iRpl, g_usBs3TestStep);
+ Bs3GdteTestPage02.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iRpl != 2 || iRpl != iDpl || k >= 4)
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ else if (k != 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03,
+ k == 2 /*f486ResumeFlagHint*/);
+ else
+ {
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ if (TrapCtx.uHandlerSs != (BS3_SEL_TEST_PAGE_03 | 2))
+ bs3CpuBasic2_FailedF("uHandlerSs=%#x expected %#x\n", TrapCtx.uHandlerSs, BS3_SEL_TEST_PAGE_03 | 2);
+ }
+ if (!(Bs3GdteTestPage02.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("CS selector was not access");
+ if ( TrapCtx.bXcpt == 0x83
+ || (TrapCtx.bXcpt == X86_XCPT_SS && k == 2) )
+ {
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("SS selector was not accessed");
+ }
+ else if (Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("SS selector was accessed");
+ g_usBs3TestStep++;
+
+ /* +1: Modify the gate DPL to check that this is checked before SS.DPL and SS.PRESENT. */
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 2;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, (0x83 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ paIdt[0x83 << cIdteShift].Gate.u2Dpl = 3;
+ g_usBs3TestStep++;
+
+ /* +2: Check the CS.DPL check is done before the SS ones. Restoring the
+ ring-0 INT 83 context triggers the CS.DPL < CPL check. */
+ Bs3TrapSetJmpAndRestore(&Ctx83, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx83, BS3_SEL_TEST_PAGE_02);
+ g_usBs3TestStep++;
+
+ /* +3: Now mark the CS selector not present and check that that also triggers before SS stuff. */
+ Bs3GdteTestPage02.Gen.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareNpCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_02);
+ Bs3GdteTestPage02.Gen.u1Present = 1;
+ g_usBs3TestStep++;
+
+ /* +4: Make the CS selector some invalid type and check it triggers before SS stuff. */
+ Bs3GdteTestPage02.Gen.u4Type = g_aInvalidCsTypes[i].u4Type;
+ Bs3GdteTestPage02.Gen.u1DescType = g_aInvalidCsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_02);
+ Bs3GdteTestPage02.Gen.u4Type = X86_SEL_TYPE_ER_ACC;
+ Bs3GdteTestPage02.Gen.u1DescType = 1;
+ g_usBs3TestStep++;
+
+ /* +5: Now, make the CS selector limit too small and that it triggers after SS trouble.
+ The 286 had a simpler approach to these GP(0). */
+ Bs3GdteTestPage02.Gen.u16LimitLow = 0;
+ Bs3GdteTestPage02.Gen.u4LimitHigh = 0;
+ Bs3GdteTestPage02.Gen.u1Granularity = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (f286)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/);
+ else if (iRpl != 2 || iRpl != iDpl || k >= 4)
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ else if (k != 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, k == 2 /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/);
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ g_usBs3TestStep++;
+ }
+ }
+ }
+
+ /* Check all the invalid SS selector types alone. */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | 2;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+ g_usBs3TestStep++;
+ for (i = 0; i < RT_ELEMENTS(g_aInvalidSsTypes); i++)
+ {
+ Bs3GdteTestPage03.Gen.u4Type = g_aInvalidSsTypes[i].u4Type;
+ Bs3GdteTestPage03.Gen.u1DescType = g_aInvalidSsTypes[i].u1DescType;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareTsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03);
+ if (Bs3GdteTestPage03.Gen.u4Type != g_aInvalidSsTypes[i].u4Type)
+ bs3CpuBasic2_FailedF("Invalid SS type %#x/%u -> %#x/%u\n",
+ g_aInvalidSsTypes[i].u4Type, g_aInvalidSsTypes[i].u1DescType,
+ Bs3GdteTestPage03.Gen.u4Type, Bs3GdteTestPage03.Gen.u1DescType);
+ g_usBs3TestStep++;
+ }
+
+ /*
+ * Continue the SS experiments with a expand down segment. We'll use
+ * the same setup as we already have with gate 83h being DPL and
+ * having CS.DPL=2.
+ *
+ * Expand down segments are weird. The valid area is practically speaking
+ * reversed. So, a 16-bit segment with a limit of 0x6000 will have valid
+ * addresses from 0xffff thru 0x6001.
+ *
+ * So, with expand down segments we can more easily cut partially into the
+ * pushing of the iret frame and trigger more interesting behavior than
+ * with regular "expand up" segments where the whole pushing area is either
+ * all fine or not not fine.
+ */
+ Bs3GdteTestPage02 = Bs3Gdt[(uSysR0Cs + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Ss + (2 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u2Dpl = 2;
+ Bs3GdteTestPage03.Gen.u4Type = X86_SEL_TYPE_RW_DOWN;
+ *puTssSs2 = BS3_SEL_TEST_PAGE_03 | 2;
+
+ /* First test, limit = max --> no bytes accessible --> #GP */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, true /*f486ResumeFlagHint*/);
+
+ /* Second test, limit = 0 --> all by zero byte accessible --> works */
+ Bs3GdteTestPage03.Gen.u16LimitLow = 0;
+ Bs3GdteTestPage03.Gen.u4LimitHigh = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83);
+
+ /* Modify the gate handler to be a dummy that immediately does UD2
+ and triggers #UD, then advance the limit down till we get the #UD. */
+ Bs3GdteTestPage03.Gen.u1Granularity = 0;
+
+ Bs3MemCpy(&CtxTmp2, &CtxTmp, sizeof(CtxTmp2)); /* #UD result context */
+ if (g_f16BitSys)
+ {
+ CtxTmp2.rip.u = g_bs3CpuBasic2_ud2_FlatAddr - BS3_ADDR_BS3TEXT16;
+ Bs3Trap16SetGate(0x83, X86_SEL_TYPE_SYS_286_INT_GATE, 3, BS3_SEL_TEST_PAGE_02, CtxTmp2.rip.u16, 0 /*cParams*/);
+ CtxTmp2.rsp.u = Bs3Tss16.sp2 - 2*5;
+ }
+ else
+ {
+ CtxTmp2.rip.u = g_bs3CpuBasic2_ud2_FlatAddr;
+ Bs3Trap32SetGate(0x83, X86_SEL_TYPE_SYS_386_INT_GATE, 3, BS3_SEL_TEST_PAGE_02, CtxTmp2.rip.u32, 0 /*cParams*/);
+ CtxTmp2.rsp.u = Bs3Tss32.esp2 - 4*5;
+ }
+ CtxTmp2.bMode = g_bTestMode; /* g_bBs3CurrentMode not changed by the UD2 handler. */
+ CtxTmp2.cs = BS3_SEL_TEST_PAGE_02 | 2;
+ CtxTmp2.ss = BS3_SEL_TEST_PAGE_03 | 2;
+ CtxTmp2.bCpl = 2;
+
+ /* test run. */
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+
+ /* Real run. */
+ i = (g_f16BitSys ? 2 : 4) * 6 + 1;
+ while (i-- > 0)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = CtxTmp2.rsp.u16 + i - 1;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (i > 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, BS3_SEL_TEST_PAGE_03, true /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+ }
+
+ /* Do a run where we do the same-ring kind of access. */
+ Bs3RegCtxConvertToRingX(&CtxTmp, 2);
+ if (g_f16BitSys)
+ {
+ CtxTmp2.rsp.u32 = CtxTmp.rsp.u32 - 2*3;
+ i = 2*3 - 1;
+ }
+ else
+ {
+ CtxTmp2.rsp.u32 = CtxTmp.rsp.u32 - 4*3;
+ i = 4*3 - 1;
+ }
+ CtxTmp.ss = BS3_SEL_TEST_PAGE_03 | 2;
+ CtxTmp2.ds = CtxTmp.ds;
+ CtxTmp2.es = CtxTmp.es;
+ CtxTmp2.fs = CtxTmp.fs;
+ CtxTmp2.gs = CtxTmp.gs;
+ while (i-- > 0)
+ {
+ Bs3GdteTestPage03.Gen.u16LimitLow = CtxTmp2.rsp.u16 + i - 1;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (i > 0)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &CtxTmp, 0 /*BS3_SEL_TEST_PAGE_03*/, true /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxTmp2);
+ g_usBs3TestStep++;
+ }
+
+ *puTssSs2 = uSavedSs2;
+ paIdt[0x83 << cIdteShift] = SavedGate83;
+ }
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ BS3_ASSERT(g_usBs3TestStep < 3000);
+
+ /*
+ * Modify the gate CS value with a conforming segment.
+ */
+ g_usBs3TestStep = 3000;
+ for (i = 0; i <= 3; i++) /* cs.dpl */
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+
+ for (j = 0; j <= 3; j++) /* rpl */
+ {
+ uint16_t const uCs = (uSysR0CsConf | j) + (i << BS3_SEL_RING_SHIFT);
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ //Bs3TestPrintf("%u/%u/%u/%u: cs=%04x hcs=%04x xcpt=%02x\n", i, iRing, iCtx, j, uCs, TrapCtx.uHandlerCs, TrapCtx.bXcpt);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ g_usBs3TestStep++;
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else if (i > iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ }
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 3500);
+
+ /*
+ * The gates must be 64-bit in long mode.
+ */
+ if (cIdteShift != 0)
+ {
+ g_usBs3TestStep = 3500;
+ for (i = 0; i <= 3; i++)
+ {
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ for (j = 0; j < 2; j++)
+ {
+ static const uint16_t s_auCSes[2] = { BS3_SEL_R0_CS16, BS3_SEL_R0_CS32 };
+ uint16_t uCs = (s_auCSes[j] | i) + (i << BS3_SEL_RING_SHIFT);
+ g_usBs3TestStep++;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x\n", g_usBs3TestStep, iCtx, iRing, i, uCs);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ /*Bs3TrapPrintFrame(&TrapCtx);*/
+ if (iCtx < iRing)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, uCs & X86_SEL_MASK_OFF_RPL);
+ }
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 4000);
+ }
+
+ /*
+ * IDT limit check. The 286 does not access X86DESCGATE::u16OffsetHigh.
+ */
+ g_usBs3TestStep = 5000;
+ i = (0x80 << (cIdteShift + 3)) - 1;
+ j = (0x82 << (cIdteShift + 3)) - (!f286 ? 1 : 3);
+ k = (0x83 << (cIdteShift + 3)) - 1;
+ for (; i <= k; i++, g_usBs3TestStep++)
+ {
+ Idtr = IdtrSaved;
+ Idtr.cbIdt = i;
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (i < j)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx81, (0x81 << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ else
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ }
+ ASMSetIDTR(&IdtrSaved);
+ BS3_ASSERT(g_usBs3TestStep < 5100);
+
+# if TMPL_BITS != 16 /* Only do the paging related stuff in 32-bit and 64-bit modes. */
+
+ /*
+ * IDT page not present. Placing the IDT copy such that 0x80 is on the
+ * first page and 0x81 is on the second page. We need proceed to move
+ * it down byte by byte to check that any inaccessible byte means #PF.
+ *
+ * Note! We must reload the alternative IDTR for each run as any kind of
+ * printing to the string (like error reporting) will cause a switch
+ * to real mode and back, reloading the default IDTR.
+ */
+ g_usBs3TestStep = 5200;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ uint32_t const uCr2Expected = Bs3SelPtrToFlat(pbIdtCopyAlloc) + _4K;
+ for (j = 0; j < cbIdte; j++)
+ {
+ pIdtCopy = (PX86DESC)&pbIdtCopyAlloc[_4K - cbIdte * 0x81 - j];
+ Bs3MemCpy(pIdtCopy, paIdt, cbIdte * 256);
+
+ Idtr.cbIdt = IdtrSaved.cbIdt;
+ Idtr.pIdt = Bs3SelPtrToFlat(pIdtCopy);
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ rc = Bs3PagingProtect(uCr2Expected, _4K, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, 0 /*uErrCd*/, uCr2Expected);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, X86_TRAP_PF_RW /*uErrCd*/, uCr2Expected + 4 - RT_MIN(j, 4));
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(uCr2Expected, _4K, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+
+ /* Check if that the entry type is checked after the whole IDTE has been cleared for #PF. */
+ pIdtCopy[0x80 << cIdteShift].Gate.u4Type = 0;
+ rc = Bs3PagingProtect(uCr2Expected, _4K, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, 0 /*uErrCd*/, uCr2Expected);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx81, X86_TRAP_PF_RW /*uErrCd*/, uCr2Expected + 4 - RT_MIN(j, 4));
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(uCr2Expected, _4K, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+ }
+ }
+ else
+ Bs3TestPrintf("Bs3PagingProtectPtr: %d\n", i);
+
+ ASMSetIDTR(&IdtrSaved);
+ }
+ }
+
+ /*
+ * The read/write and user/supervisor bits the IDT PTEs are irrelevant.
+ */
+ g_usBs3TestStep = 5300;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ Bs3MemCpy(pbIdtCopyAlloc, paIdt, cbIdte * 256);
+ Idtr.cbIdt = IdtrSaved.cbIdt;
+ Idtr.pIdt = Bs3SelPtrToFlat(pbIdtCopyAlloc);
+
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ rc = Bs3PagingProtect(Idtr.pIdt, _4K, 0 /*fSet*/, X86_PTE_RW | X86_PTE_US /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ ASMSetIDTR(&Idtr);
+ Bs3TrapSetJmpAndRestore(&Ctx81, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx81, 0x81 /*bXcpt*/);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(Idtr.pIdt, _4K, X86_PTE_RW | X86_PTE_US /*fSet*/, 0 /*fClear*/);
+ }
+ ASMSetIDTR(&IdtrSaved);
+ }
+
+ /*
+ * Check that CS.u1Accessed is set to 1. Use the test page selector #0 and #3 together
+ * with interrupt gates 80h and 83h, respectively.
+ */
+/** @todo Throw in SS.u1Accessed too. */
+ g_usBs3TestStep = 5400;
+ if (BS3_MODE_IS_PAGED(g_bTestMode) && pbIdtCopyAlloc)
+ {
+ Bs3GdteTestPage00 = Bs3Gdt[uSysR0Cs >> X86_SEL_SHIFT];
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_00;
+
+ Bs3GdteTestPage03 = Bs3Gdt[(uSysR0Cs + (3 << BS3_SEL_RING_SHIFT)) >> X86_SEL_SHIFT];
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = BS3_SEL_TEST_PAGE_03; /* rpl is ignored, so leave it as zero. */
+
+ /* Check that the CS.A bit is being set on a general basis and that
+ the special CS values work with out generic handler code. */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ if (TrapCtx.uHandlerCs != (BS3_SEL_TEST_PAGE_03 | 3))
+ bs3CpuBasic2_FailedF("uHandlerCs=%#x, expected %#x", TrapCtx.uHandlerCs, (BS3_SEL_TEST_PAGE_03 | 3));
+ g_usBs3TestStep++;
+
+ /*
+ * Now check that setting CS.u1Access to 1 does __NOT__ trigger a page
+ * fault due to the RW bit being zero.
+ * (We check both with with and without the WP bit if 80486.)
+ */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ ASMSetCR0(uCr0Saved | X86_CR0_WP);
+
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ rc = Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, 0 /*fSet*/, X86_PTE_RW /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ /* ring-0 handler */
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ /* ring-3 handler */
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ /* clear WP and repeat the above. */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ ASMSetCR0(uCr0Saved & ~X86_CR0_WP);
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* (No need to RW the page - ring-0, WP=0.) */
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* (No need to RW the page - ring-0, WP=0.) */
+
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &Ctx80, 0x80 /*bXcpt*/);
+ if (!(Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!", Bs3GdteTestPage00.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x83 /*bXcpt*/);
+ if (!(Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+ bs3CpuBasic2_FailedF("u4Type=%#x, not accessed!n", Bs3GdteTestPage03.Gen.u4Type);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, X86_PTE_RW /*fSet*/, 0 /*fClear*/);
+ }
+
+ ASMSetCR0(uCr0Saved);
+
+ /*
+ * While we're here, check that if the CS GDT entry is a non-present
+ * page we do get a #PF with the rigth error code and CR2.
+ */
+ Bs3GdteTestPage00.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED; /* Just for fun, really a pointless gesture. */
+ Bs3GdteTestPage03.Gen.u4Type &= ~X86_SEL_TYPE_ACCESSED;
+ rc = Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, 0 /*fSet*/, X86_PTE_P /*fClear*/);
+ if (RT_SUCCESS(rc))
+ {
+ Bs3TrapSetJmpAndRestore(&Ctx80, &TrapCtx);
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx80, 0 /*uErrCd*/, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx80, X86_TRAP_PF_RW, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00 + 4);
+ g_usBs3TestStep++;
+
+ /* Do it from ring-3 to check ErrCd, which doesn't set X86_TRAP_PF_US it turns out. */
+ Bs3MemCpy(&CtxTmp, &Ctx83, sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, 3);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+
+ if (f486Plus)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &CtxTmp, 0 /*uErrCd*/, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_03);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &CtxTmp, X86_TRAP_PF_RW, GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_03 + 4);
+ g_usBs3TestStep++;
+
+ Bs3PagingProtect(GdtrSaved.pGdt + BS3_SEL_TEST_PAGE_00, 8, X86_PTE_P /*fSet*/, 0 /*fClear*/);
+ if (Bs3GdteTestPage00.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("u4Type=%#x, accessed! #1", Bs3GdteTestPage00.Gen.u4Type);
+ if (Bs3GdteTestPage03.Gen.u4Type & X86_SEL_TYPE_ACCESSED)
+ bs3CpuBasic2_FailedF("u4Type=%#x, accessed! #2", Bs3GdteTestPage03.Gen.u4Type);
+ }
+
+ /* restore */
+ paIdt[0x80 << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[0x83 << cIdteShift].Gate.u16Sel = uSysR0Cs;// + (3 << BS3_SEL_RING_SHIFT) + 3;
+ }
+
+# endif /* 32 || 64*/
+
+ /*
+ * Check broad EFLAGS effects.
+ */
+ g_usBs3TestStep = 5600;
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ for (iRing = 0; iRing < 4; iRing++)
+ {
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+
+ /* all set */
+ CtxTmp.rflags.u32 &= X86_EFL_VM | X86_EFL_1;
+ CtxTmp.rflags.u32 |= X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF /* | X86_EFL_TF */ /*| X86_EFL_IF*/
+ | X86_EFL_DF | X86_EFL_OF | X86_EFL_IOPL /* | X86_EFL_NT*/;
+ if (f486Plus)
+ CtxTmp.rflags.u32 |= X86_EFL_AC;
+ if (f486Plus && !g_f16BitSys)
+ CtxTmp.rflags.u32 |= X86_EFL_RF;
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ CtxTmp.rflags.u32 |= X86_EFL_VIF | X86_EFL_VIP;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ CtxTmp.rflags.u32 &= ~X86_EFL_RF;
+
+ if (iCtx >= iRing)
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ uExpected = CtxTmp.rflags.u32
+ & ( X86_EFL_1 | X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_DF
+ | X86_EFL_OF | X86_EFL_IOPL | X86_EFL_NT | X86_EFL_VM | X86_EFL_AC | X86_EFL_VIF | X86_EFL_VIP
+ | X86_EFL_ID /*| X86_EFL_TF*/ /*| X86_EFL_IF*/ /*| X86_EFL_RF*/ );
+ if (TrapCtx.fHandlerRfl != uExpected)
+ bs3CpuBasic2_FailedF("unexpected handler rflags value: %RX64 expected %RX32; CtxTmp.rflags=%RX64 Ctx.rflags=%RX64\n",
+ TrapCtx.fHandlerRfl, uExpected, CtxTmp.rflags.u, TrapCtx.Ctx.rflags.u);
+ g_usBs3TestStep++;
+
+ /* all cleared */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80286)
+ CtxTmp.rflags.u32 = apCtx8x[iCtx]->rflags.u32 & (X86_EFL_RA1_MASK | UINT16_C(0xf000));
+ else
+ CtxTmp.rflags.u32 = apCtx8x[iCtx]->rflags.u32 & (X86_EFL_VM | X86_EFL_RA1_MASK);
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ if (iCtx >= iRing)
+ bs3CpuBasic2_CompareIntCtx1(&TrapCtx, &CtxTmp, 0x80 + iCtx /*bXcpt*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ uExpected = CtxTmp.rflags.u32;
+ if (TrapCtx.fHandlerRfl != uExpected)
+ bs3CpuBasic2_FailedF("unexpected handler rflags value: %RX64 expected %RX32; CtxTmp.rflags=%RX64 Ctx.rflags=%RX64\n",
+ TrapCtx.fHandlerRfl, uExpected, CtxTmp.rflags.u, TrapCtx.Ctx.rflags.u);
+ g_usBs3TestStep++;
+ }
+ }
+
+/** @todo CS.LIMIT / canonical(CS) */
+
+
+ /*
+ * Check invalid gate types.
+ */
+ g_usBs3TestStep = 32000;
+ for (iRing = 0; iRing <= 3; iRing++)
+ {
+ static const uint16_t s_auCSes[] = { BS3_SEL_R0_CS16, BS3_SEL_R0_CS32, BS3_SEL_R0_CS64,
+ BS3_SEL_TSS16, BS3_SEL_TSS32, BS3_SEL_TSS64, 0, BS3_SEL_SPARE_1f };
+ static uint16_t const s_auInvlTypes64[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+ static uint16_t const s_auInvlTypes32[] = { 0, 1, 2, 3, 8, 9, 10, 11, 13,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ /*286:*/ 12, 14, 15 };
+ uint16_t const * const pauInvTypes = cIdteShift != 0 ? s_auInvlTypes64 : s_auInvlTypes32;
+ uint16_t const cInvTypes = cIdteShift != 0 ? RT_ELEMENTS(s_auInvlTypes64)
+ : f386Plus ? RT_ELEMENTS(s_auInvlTypes32) - 3 : RT_ELEMENTS(s_auInvlTypes32);
+
+
+ for (iCtx = 0; iCtx < RT_ELEMENTS(apCtx8x); iCtx++)
+ {
+ unsigned iType;
+
+ Bs3MemCpy(&CtxTmp, apCtx8x[iCtx], sizeof(CtxTmp));
+ Bs3RegCtxConvertToRingX(&CtxTmp, iRing);
+# if TMPL_BITS == 32
+ g_uBs3TrapEipHint = CtxTmp.rip.u32;
+# endif
+ for (iType = 0; iType < cInvTypes; iType++)
+ {
+ uint8_t const bSavedType = paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1DescType = pauInvTypes[iType] >> 4;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type = pauInvTypes[iType] & 0xf;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < RT_ELEMENTS(s_auCSes); j++)
+ {
+ uint16_t uCs = (unsigned)(s_auCSes[j] - BS3_SEL_R0_FIRST) < (unsigned)(4 << BS3_SEL_RING_SHIFT)
+ ? (s_auCSes[j] | i) + (i << BS3_SEL_RING_SHIFT)
+ : s_auCSes[j] | i;
+ /*Bs3TestPrintf("g_usBs3TestStep=%u iCtx=%u iRing=%u i=%u uCs=%04x type=%#x\n", g_usBs3TestStep, iCtx, iRing, i, uCs, pauInvTypes[iType]);*/
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uCs;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ g_usBs3TestStep++;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+
+ /* Mark it not-present to check that invalid type takes precedence. */
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 0;
+ Bs3TrapSetJmpAndRestore(&CtxTmp, &TrapCtx);
+ g_usBs3TestStep++;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxTmp, ((0x80 + iCtx) << X86_TRAP_ERR_SEL_SHIFT) | X86_TRAP_ERR_IDT);
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u16Sel = uSysR0Cs;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u4Type = bSavedType;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1DescType = 0;
+ paIdt[(0x80 + iCtx) << cIdteShift].Gate.u1Present = 1;
+ }
+ }
+ }
+ BS3_ASSERT(g_usBs3TestStep < 62000U && g_usBs3TestStep > 32000U);
+
+
+ /** @todo
+ * - Run \#PF and \#GP (and others?) at CPLs other than zero.
+ * - Quickly generate all faults.
+ * - All the peculiarities v8086.
+ */
+
+# if TMPL_BITS != 16
+ Bs3MemFree(pbIdtCopyAlloc, 12*_1K);
+# endif
+}
+#endif /* convert me */
+
+
+static void bs3CpuBasic2_RaiseXcpt11Worker(uint8_t bMode, uint8_t *pbBuf, unsigned cbCacheLine, bool fAm, bool fPf,
+ RTCCUINTXREG uFlatBufPtr, BS3CPUBASIC2PFTTSTCMNMODE const BS3_FAR *pCmn)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxUdExpected;
+ uint8_t const cRings = bMode == BS3_MODE_RM ? 1 : 4;
+ uint8_t iRing;
+ uint16_t iTest;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxUdExpected, sizeof(CtxUdExpected));
+
+ /*
+ * Test all relevant rings.
+ *
+ * The memory operand is ds:xBX, so point it to pbBuf.
+ * The test snippets mostly use xAX as operand, with the div
+ * one also using xDX, so make sure they make some sense.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ Ctx.cr0.u32 &= ~(X86_CR0_MP | X86_CR0_EM | X86_CR0_TS); /* so fninit + fld works */
+
+ for (iRing = BS3_MODE_IS_V86(bMode) ? 3 : 0; iRing < cRings; iRing++)
+ {
+ uint32_t uEbx;
+ uint8_t fAc;
+
+ if (!BS3_MODE_IS_RM_OR_V86(bMode))
+ Bs3RegCtxConvertToRingX(&Ctx, iRing);
+
+ if (!fPf || BS3_MODE_IS_32BIT_CODE(bMode) || BS3_MODE_IS_64BIT_CODE(bMode))
+ Bs3RegCtxSetGrpDsFromCurPtr(&Ctx, &Ctx.rbx, pbBuf);
+ else
+ {
+ /* Bs3RegCtxSetGrpDsFromCurPtr barfs when trying to output a sel:off address for the aliased buffer. */
+ Ctx.ds = BS3_FP_SEG(pbBuf);
+ Ctx.rbx.u32 = BS3_FP_OFF(pbBuf);
+ }
+ uEbx = Ctx.rbx.u32;
+
+ Ctx.rax.u = (bMode & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_64
+ ? UINT64_C(0x80868028680386fe) : UINT32_C(0x65020686);
+ Ctx.rdx.u = UINT32_C(0x00100100); /* careful with range due to div */
+
+ Bs3MemCpy(&CtxUdExpected, &Ctx, sizeof(Ctx));
+
+ /*
+ * AC flag loop.
+ */
+ for (fAc = 0; fAc < 2; fAc++)
+ {
+ if (fAc)
+ Ctx.rflags.u32 |= X86_EFL_AC;
+ else
+ Ctx.rflags.u32 &= ~X86_EFL_AC;
+
+ /*
+ * Loop over the test snippets.
+ */
+ for (iTest = 0; iTest < pCmn->cEntries; iTest++)
+ {
+ uint8_t const fOp = pCmn->paEntries[iTest].fOp;
+ uint16_t const cbMem = pCmn->paEntries[iTest].cbMem;
+ uint8_t const cbAlign = pCmn->paEntries[iTest].cbAlign;
+ uint16_t const cbMax = cbCacheLine + cbMem;
+ uint16_t offMem;
+ uint8_t BS3_FAR *poffUd = (uint8_t BS3_FAR *)Bs3SelLnkPtrToCurPtr(pCmn->paEntries[iTest].pfn);
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, pCmn->paEntries[iTest].pfn);
+ CtxUdExpected.rip = Ctx.rip;
+ CtxUdExpected.rip.u = Ctx.rip.u + poffUd[-1];
+ CtxUdExpected.cs = Ctx.cs;
+ CtxUdExpected.rflags = Ctx.rflags;
+ if (bMode == BS3_MODE_RM)
+ CtxUdExpected.rflags.u32 &= ~X86_EFL_AC; /** @todo investigate. automatically cleared, or is it just our code? Observed with bs3-cpu-instr-3 too (10980xe), seems to be the CPU doing it. */
+ CtxUdExpected.rdx = Ctx.rdx;
+ CtxUdExpected.rax = Ctx.rax;
+ if (fOp & MYOP_LD)
+ {
+ switch (cbMem)
+ {
+ case 2:
+ CtxUdExpected.rax.u16 = 0x0101;
+ break;
+ case 4:
+ CtxUdExpected.rax.u32 = UINT32_C(0x01010101);
+ break;
+ case 8:
+ CtxUdExpected.rax.u64 = UINT64_C(0x0101010101010101);
+ break;
+ }
+ }
+
+ /*
+ * Buffer misalignment loop.
+ * Note! We must make sure to cross a cache line here to make sure
+ * to cover the split-lock scenario. (The buffer is cache
+ * line aligned.)
+ */
+ for (offMem = 0; offMem < cbMax; offMem++)
+ {
+ bool const fMisaligned = (offMem & (cbAlign - 1)) != 0;
+ unsigned offBuf = cbMax + cbMem * 2;
+ while (offBuf-- > 0)
+ pbBuf[offBuf] = 1; /* byte-by-byte to make sure it doesn't trigger AC. */
+
+ CtxUdExpected.rbx.u32 = Ctx.rbx.u32 = uEbx + offMem; /* ASSUMES memory in first 4GB. */
+ if (BS3_MODE_IS_16BIT_SYS(bMode))
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+
+ //Bs3TestPrintf("iRing=%d iTest=%d cs:rip=%04RX16:%08RX32 ds:rbx=%04RX16:%08RX32 ss:esp=%04RX16:%08RX32 bXcpt=%#x errcd=%#x fAm=%d fAc=%d ESP=%#RX32\n",
+ // iRing, iTest, Ctx.cs, Ctx.rip.u32, Ctx.ds, Ctx.rbx.u32, Ctx.ss, Ctx.rsp.u32, TrapCtx.bXcpt, (unsigned)TrapCtx.uErrCd, fAm, fAc, ASMGetESP());
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+
+ if ( (pCmn->paEntries[iTest].fOp & MYOP_AC_GP)
+ && fMisaligned
+ && (!fAm || iRing != 3 || !fAc || (offMem & 3 /* 10980XE */) == 0) )
+ {
+ if (fAc && bMode == BS3_MODE_RM)
+ TrapCtx.Ctx.rflags.u32 |= X86_EFL_AC;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ }
+ else if (fPf && iRing == 3 && (!fAm || !fAc || !fMisaligned)) /* #AC beats #PF */
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx,
+ X86_TRAP_PF_P | X86_TRAP_PF_US
+ | (pCmn->paEntries[iTest].fOp & MYOP_ST ? X86_TRAP_PF_RW : 0),
+ uFlatBufPtr + offMem + (cbMem > 64 ? cbMem - 1 /*FXSAVE*/ : 0),
+ pCmn->paEntries[iTest].offFaultInstr);
+ else if (!fAm || iRing != 3 || !fAc || !fMisaligned)
+ {
+ if (fOp & MYOP_EFL)
+ {
+ CtxUdExpected.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ CtxUdExpected.rflags.u16 |= TrapCtx.Ctx.rflags.u16 & X86_EFL_STATUS_BITS;
+ }
+ if (fOp == MYOP_LD_DIV)
+ {
+ CtxUdExpected.rax = TrapCtx.Ctx.rax;
+ CtxUdExpected.rdx = TrapCtx.Ctx.rdx;
+ }
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ }
+ else
+ bs3CpuBasic2_CompareAcCtx(&TrapCtx, &Ctx, pCmn->paEntries[iTest].offFaultInstr);
+
+ g_usBs3TestStep++;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Entrypoint for \#AC tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ *
+ * @note When testing v8086 code, we'll be running in v8086 mode. So, careful
+ * with control registers and such.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_RaiseXcpt11)(uint8_t bMode)
+{
+ unsigned cbCacheLine = 128; /** @todo detect */
+ uint8_t BS3_FAR *pbBufAlloc;
+ uint8_t BS3_FAR *pbBuf;
+ unsigned idxCmnModes;
+ uint32_t fCr0;
+
+ /*
+ * Skip if 386 or older.
+ */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80486)
+ {
+ Bs3TestSkipped("#AC test requires 486 or later");
+ return BS3TESTDOMODE_SKIPPED;
+ }
+
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /* Get us a 64-byte aligned buffer. */
+ pbBufAlloc = pbBuf = Bs3MemAllocZ(BS3_MODE_IS_RM_OR_V86(bMode) ? BS3MEMKIND_REAL : BS3MEMKIND_TILED, X86_PAGE_SIZE * 2);
+ if (!pbBufAlloc)
+ return Bs3TestFailed("Failed to allocate 2 pages of real-mode memory");
+ if (BS3_FP_OFF(pbBuf) & (X86_PAGE_SIZE - 1))
+ pbBuf = &pbBufAlloc[X86_PAGE_SIZE - (BS3_FP_OFF(pbBuf) & X86_PAGE_OFFSET_MASK)];
+ BS3_ASSERT(pbBuf - pbBufAlloc <= X86_PAGE_SIZE);
+ //Bs3TestPrintf("pbBuf=%p\n", pbBuf);
+
+ /* Find the g_aCmnModes entry. */
+ idxCmnModes = 0;
+ while (g_aCmnModes[idxCmnModes].bMode != (bMode & BS3_MODE_CODE_MASK))
+ idxCmnModes++;
+ //Bs3TestPrintf("idxCmnModes=%d bMode=%#x\n", idxCmnModes, bMode);
+
+ /* First round is w/o alignment checks enabled. */
+ //Bs3TestPrintf("round 1\n");
+ fCr0 = Bs3RegGetCr0();
+ BS3_ASSERT(!(fCr0 & X86_CR0_AM));
+ Bs3RegSetCr0(fCr0 & ~X86_CR0_AM);
+#if 1
+ bs3CpuBasic2_RaiseXcpt11Worker(bMode, pbBuf, cbCacheLine, false /*fAm*/, false /*fPf*/, 0, &g_aCmnModes[idxCmnModes]);
+#endif
+
+ /* The second round is with aligment checks enabled. */
+#if 1
+ //Bs3TestPrintf("round 2\n");
+ Bs3RegSetCr0(Bs3RegGetCr0() | X86_CR0_AM);
+ bs3CpuBasic2_RaiseXcpt11Worker(bMode, pbBuf, cbCacheLine, true /*fAm*/, false /*fPf*/, 0, &g_aCmnModes[idxCmnModes]);
+#endif
+
+#if 1
+ /* The third and fourth round access the buffer via a page alias that's not
+ accessible from ring-3. The third round has ACs disabled and the fourth
+ has them enabled. */
+ if (BS3_MODE_IS_PAGED(bMode) && !BS3_MODE_IS_V86(bMode))
+ {
+ /* Alias the buffer as system memory so ring-3 access with AC+AM will cause #PF: */
+ /** @todo the aliasing is not necessary any more... */
+ int rc;
+ RTCCUINTXREG uFlatBufPtr = Bs3SelPtrToFlat(pbBuf);
+ uint64_t const uAliasPgPtr = bMode & BS3_MODE_CODE_64 ? UINT64_C(0x0000648680000000) : UINT32_C(0x80000000);
+ rc = Bs3PagingAlias(uAliasPgPtr, uFlatBufPtr & ~(uint64_t)X86_PAGE_OFFSET_MASK, X86_PAGE_SIZE * 2,
+ X86_PTE_P | X86_PTE_RW);
+ if (RT_SUCCESS(rc))
+ {
+ /* We 'misalign' the segment base here to make sure it's the final
+ address that gets alignment checked and not just the operand value. */
+ RTCCUINTXREG uAliasBufPtr = (RTCCUINTXREG)uAliasPgPtr + (uFlatBufPtr & X86_PAGE_OFFSET_MASK);
+ uint8_t BS3_FAR *pbBufAlias = BS3_FP_MAKE(BS3_SEL_SPARE_00 | 3, (uFlatBufPtr & X86_PAGE_OFFSET_MASK) + 1);
+ Bs3SelSetup16BitData(&Bs3GdteSpare00, uAliasPgPtr - 1);
+
+ //Bs3TestPrintf("round 3 pbBufAlias=%p\n", pbBufAlias);
+ Bs3RegSetCr0(Bs3RegGetCr0() & ~X86_CR0_AM);
+ bs3CpuBasic2_RaiseXcpt11Worker(bMode, pbBufAlias, cbCacheLine, false /*fAm*/,
+ true /*fPf*/, uAliasBufPtr, &g_aCmnModes[idxCmnModes]);
+
+ //Bs3TestPrintf("round 4\n");
+ Bs3RegSetCr0(Bs3RegGetCr0() | X86_CR0_AM);
+ bs3CpuBasic2_RaiseXcpt11Worker(bMode, pbBufAlias, cbCacheLine, true /*fAm*/,
+ true /*fPf*/, uAliasBufPtr, &g_aCmnModes[idxCmnModes]);
+
+ Bs3PagingUnalias(uAliasPgPtr, X86_PAGE_SIZE * 2);
+ }
+ else
+ Bs3TestFailedF("Bs3PagingAlias failed with %Rrc", rc);
+ }
+#endif
+
+ Bs3MemFree(pbBufAlloc, X86_PAGE_SIZE * 2);
+ Bs3RegSetCr0(fCr0);
+ return 0;
+}
+
+
+/**
+ * Executes one round of SIDT and SGDT tests using one assembly worker.
+ *
+ * This is written with driving everything from the 16-bit or 32-bit worker in
+ * mind, i.e. not assuming the test bitcount is the same as the current.
+ */
+static void bs3CpuBasic2_sidt_sgdt_One(BS3CB2SIDTSGDT const BS3_FAR *pWorker, uint8_t bTestMode, uint8_t bRing,
+ uint8_t const *pbExpected)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxUdExpected;
+ BS3REGCTX TmpCtx;
+ uint8_t const cbBuf = 8*2; /* test buffer area */
+ uint8_t abBuf[8*2 + 8 + 8]; /* test buffer w/ misalignment test space and some extra guard. */
+ uint8_t BS3_FAR *pbBuf = abBuf;
+ uint8_t const cbIdtr = BS3_MODE_IS_64BIT_CODE(bTestMode) ? 2+8 : 2+4;
+ bool const f286 = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) == BS3CPU_80286;
+ uint8_t bFiller;
+ int off;
+ int off2;
+ unsigned cb;
+ uint8_t BS3_FAR *pbTest;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxUdExpected, sizeof(CtxUdExpected));
+ Bs3MemZero(&TmpCtx, sizeof(TmpCtx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&abBuf, sizeof(abBuf));
+
+ /* Create a context, give this routine some more stack space, point the context
+ at our SIDT [xBX] + UD2 combo, and point DS:xBX at abBuf. */
+ Bs3RegCtxSaveEx(&Ctx, bTestMode, 256 /*cbExtraStack*/);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBuf);
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, pWorker->fpfnWorker);
+ if (BS3_MODE_IS_16BIT_SYS(bTestMode))
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+ if (!BS3_MODE_IS_RM_OR_V86(bTestMode))
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+
+ /* For successful SIDT attempts, we'll stop at the UD2. */
+ Bs3MemCpy(&CtxUdExpected, &Ctx, sizeof(Ctx));
+ CtxUdExpected.rip.u += pWorker->cbInstr;
+
+ /*
+ * Check that it works at all and that only bytes we expect gets written to.
+ */
+ /* First with zero buffer. */
+ Bs3MemZero(abBuf, sizeof(abBuf));
+ if (!ASMMemIsAllU8(abBuf, sizeof(abBuf), 0))
+ Bs3TestFailedF("ASMMemIsAllU8 or Bs3MemZero is busted: abBuf=%.*Rhxs\n", sizeof(abBuf), pbBuf);
+ if (!ASMMemIsZero(abBuf, sizeof(abBuf)))
+ Bs3TestFailedF("ASMMemIsZero or Bs3MemZero is busted: abBuf=%.*Rhxs\n", sizeof(abBuf), pbBuf);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (f286 && abBuf[cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#1): %#x\n", abBuf[cbIdtr - 1]);
+ if (!ASMMemIsZero(&abBuf[cbIdtr], cbBuf - cbIdtr))
+ Bs3TestFailedF("Unexpected buffer bytes set (#1): cbIdtr=%u abBuf=%.*Rhxs\n", cbIdtr, cbBuf, pbBuf);
+ if (Bs3MemCmp(abBuf, pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (%s,#1): expected %.*Rhxs, got %.*Rhxs\n", pWorker->pszDesc, cbIdtr, pbExpected, cbIdtr, abBuf);
+ g_usBs3TestStep++;
+
+ /* Again with a buffer filled with a byte not occuring in the previous result. */
+ bFiller = 0x55;
+ while (Bs3MemChr(abBuf, bFiller, cbBuf) != NULL)
+ bFiller++;
+ Bs3MemSet(abBuf, bFiller, sizeof(abBuf));
+ if (!ASMMemIsAllU8(abBuf, sizeof(abBuf), bFiller))
+ Bs3TestFailedF("ASMMemIsAllU8 or Bs3MemSet is busted: bFiller=%#x abBuf=%.*Rhxs\n", bFiller, sizeof(abBuf), pbBuf);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (f286 && abBuf[cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#2): %#x\n", abBuf[cbIdtr - 1]);
+ if (!ASMMemIsAllU8(&abBuf[cbIdtr], cbBuf - cbIdtr, bFiller))
+ Bs3TestFailedF("Unexpected buffer bytes set (#2): cbIdtr=%u bFiller=%#x abBuf=%.*Rhxs\n", cbIdtr, bFiller, cbBuf, pbBuf);
+ if (Bs3MemChr(abBuf, bFiller, cbIdtr) != NULL)
+ Bs3TestFailedF("Not all bytes touched: cbIdtr=%u bFiller=%#x abBuf=%.*Rhxs\n", cbIdtr, bFiller, cbBuf, pbBuf);
+ if (Bs3MemCmp(abBuf, pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (%s,#2): expected %.*Rhxs, got %.*Rhxs\n", pWorker->pszDesc, cbIdtr, pbExpected, cbIdtr, abBuf);
+ g_usBs3TestStep++;
+
+ /*
+ * Slide the buffer along 8 bytes to cover misalignment.
+ */
+ for (off = 0; off < 8; off++)
+ {
+ pbBuf = &abBuf[off];
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &abBuf[off]);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+
+ /* First with zero buffer. */
+ Bs3MemZero(abBuf, sizeof(abBuf));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (off > 0 && !ASMMemIsZero(abBuf, off))
+ Bs3TestFailedF("Unexpected buffer bytes set before (#3): cbIdtr=%u off=%u abBuf=%.*Rhxs\n",
+ cbIdtr, off, off + cbBuf, abBuf);
+ if (!ASMMemIsZero(&abBuf[off + cbIdtr], sizeof(abBuf) - cbIdtr - off))
+ Bs3TestFailedF("Unexpected buffer bytes set after (#3): cbIdtr=%u off=%u abBuf=%.*Rhxs\n",
+ cbIdtr, off, off + cbBuf, abBuf);
+ if (f286 && abBuf[off + cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#3): %#x\n", abBuf[off + cbIdtr - 1]);
+ if (Bs3MemCmp(&abBuf[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#3): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &abBuf[off]);
+ g_usBs3TestStep++;
+
+ /* Again with a buffer filled with a byte not occuring in the previous result. */
+ Bs3MemSet(abBuf, bFiller, sizeof(abBuf));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (off > 0 && !ASMMemIsAllU8(abBuf, off, bFiller))
+ Bs3TestFailedF("Unexpected buffer bytes set before (#4): cbIdtr=%u off=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, bFiller, off + cbBuf, abBuf);
+ if (!ASMMemIsAllU8(&abBuf[off + cbIdtr], sizeof(abBuf) - cbIdtr - off, bFiller))
+ Bs3TestFailedF("Unexpected buffer bytes set after (#4): cbIdtr=%u off=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, bFiller, off + cbBuf, abBuf);
+ if (Bs3MemChr(&abBuf[off], bFiller, cbIdtr) != NULL)
+ Bs3TestFailedF("Not all bytes touched (#4): cbIdtr=%u off=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, bFiller, off + cbBuf, abBuf);
+ if (f286 && abBuf[off + cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#4): %#x\n", abBuf[off + cbIdtr - 1]);
+ if (Bs3MemCmp(&abBuf[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#4): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &abBuf[off]);
+ g_usBs3TestStep++;
+ }
+ pbBuf = abBuf;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBuf);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+
+ /*
+ * Play with the selector limit if the target mode supports limit checking
+ * We use BS3_SEL_TEST_PAGE_00 for this
+ */
+ if ( !BS3_MODE_IS_RM_OR_V86(bTestMode)
+ && !BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ uint16_t cbLimit;
+ uint32_t uFlatBuf = Bs3SelPtrToFlat(abBuf);
+ Bs3GdteTestPage00 = Bs3Gdte_DATA16;
+ Bs3GdteTestPage00.Gen.u2Dpl = bRing;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatBuf;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatBuf >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatBuf >> 24);
+
+ if (pWorker->fSs)
+ CtxUdExpected.ss = Ctx.ss = BS3_SEL_TEST_PAGE_00 | bRing;
+ else
+ CtxUdExpected.ds = Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+
+ /* Expand up (normal). */
+ for (off = 0; off < 8; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = 0; cbLimit < cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(abBuf, bFiller, sizeof(abBuf));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off + cbIdtr <= cbLimit + 1)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemChr(&abBuf[off], bFiller, cbIdtr) != NULL)
+ Bs3TestFailedF("Not all bytes touched (#5): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ if (Bs3MemCmp(&abBuf[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#5): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &abBuf[off]);
+ if (f286 && abBuf[off + cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#5): %#x\n", abBuf[off + cbIdtr - 1]);
+ }
+ else
+ {
+ if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if (off + 2 <= cbLimit + 1)
+ {
+ if (Bs3MemChr(&abBuf[off], bFiller, 2) != NULL)
+ Bs3TestFailedF("Limit bytes not touched (#6): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ if (Bs3MemCmp(&abBuf[off], pbExpected, 2) != 0)
+ Bs3TestFailedF("Mismatch (#6): expected %.2Rhxs, got %.2Rhxs\n", pbExpected, &abBuf[off]);
+ if (!ASMMemIsAllU8(&abBuf[off + 2], cbIdtr - 2, bFiller))
+ Bs3TestFailedF("Base bytes touched on #GP (#6): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ }
+ else if (!ASMMemIsAllU8(abBuf, sizeof(abBuf), bFiller))
+ Bs3TestFailedF("Bytes touched on #GP: cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ }
+
+ if (off > 0 && !ASMMemIsAllU8(abBuf, off, bFiller))
+ Bs3TestFailedF("Leading bytes touched (#7): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ if (!ASMMemIsAllU8(&abBuf[off + cbIdtr], sizeof(abBuf) - off - cbIdtr, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#7): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+
+ g_usBs3TestStep++;
+ }
+ }
+
+ /* Expand down (weird). Inverted valid area compared to expand up,
+ so a limit of zero give us a valid range for 0001..0ffffh (instead of
+ a segment with one valid byte at 0000h). Whereas a limit of 0fffeh
+ means one valid byte at 0ffffh, and a limit of 0ffffh means none
+ (because in a normal expand up the 0ffffh means all 64KB are
+ accessible). */
+ Bs3GdteTestPage00.Gen.u4Type = X86_SEL_TYPE_RW_DOWN_ACC;
+ for (off = 0; off < 8; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = 0; cbLimit < cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(abBuf, bFiller, sizeof(abBuf));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+
+ if (off > cbLimit)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemChr(&abBuf[off], bFiller, cbIdtr) != NULL)
+ Bs3TestFailedF("Not all bytes touched (#8): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ if (Bs3MemCmp(&abBuf[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#8): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &abBuf[off]);
+ if (f286 && abBuf[off + cbIdtr - 1] != 0xff)
+ Bs3TestFailedF("286: Top base byte isn't 0xff (#8): %#x\n", abBuf[off + cbIdtr - 1]);
+ }
+ else
+ {
+ if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if (!ASMMemIsAllU8(abBuf, sizeof(abBuf), bFiller))
+ Bs3TestFailedF("Bytes touched on #GP: cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ }
+
+ if (off > 0 && !ASMMemIsAllU8(abBuf, off, bFiller))
+ Bs3TestFailedF("Leading bytes touched (#9): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+ if (!ASMMemIsAllU8(&abBuf[off + cbIdtr], sizeof(abBuf) - off - cbIdtr, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#9): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x abBuf=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, off + cbBuf, abBuf);
+
+ g_usBs3TestStep++;
+ }
+ }
+
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBuf);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+ CtxUdExpected.ss = Ctx.ss;
+ CtxUdExpected.ds = Ctx.ds;
+ }
+
+ /*
+ * Play with the paging.
+ */
+ if ( BS3_MODE_IS_PAGED(bTestMode)
+ && (!pWorker->fSs || bRing == 3) /* SS.DPL == CPL, we'll get some tiled ring-3 selector here. */
+ && (pbTest = (uint8_t BS3_FAR *)Bs3MemGuardedTestPageAlloc(BS3MEMKIND_TILED)) != NULL)
+ {
+ RTCCUINTXREG uFlatTest = Bs3SelPtrToFlat(pbTest);
+
+ /*
+ * Slide the buffer towards the trailing guard page. We'll observe the
+ * first word being written entirely separately from the 2nd dword/qword.
+ */
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE - cbIdtr * 2], bFiller, cbIdtr * 2);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &pbTest[off]);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off + cbIdtr <= X86_PAGE_SIZE)
+ {
+ CtxUdExpected.rbx = Ctx.rbx;
+ CtxUdExpected.ss = Ctx.ss;
+ CtxUdExpected.ds = Ctx.ds;
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#9): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ }
+ else
+ {
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, X86_TRAP_PF_RW | (Ctx.bCpl == 3 ? X86_TRAP_PF_US : 0),
+ uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ if ( off <= X86_PAGE_SIZE - 2
+ && Bs3MemCmp(&pbTest[off], pbExpected, 2) != 0)
+ Bs3TestFailedF("Mismatch (#10): Expected limit %.2Rhxs, got %.2Rhxs; off=%#x\n",
+ pbExpected, &pbTest[off], off);
+ if ( off < X86_PAGE_SIZE - 2
+ && !ASMMemIsAllU8(&pbTest[off + 2], X86_PAGE_SIZE - off - 2, bFiller))
+ Bs3TestFailedF("Wrote partial base on #PF (#10): bFiller=%#x, got %.*Rhxs; off=%#x\n",
+ bFiller, X86_PAGE_SIZE - off - 2, &pbTest[off + 2], off);
+ if (off == X86_PAGE_SIZE - 1 && pbTest[off] != bFiller)
+ Bs3TestFailedF("Wrote partial limit on #PF (#10): Expected %02x, got %02x\n", bFiller, pbTest[off]);
+ }
+ g_usBs3TestStep++;
+ }
+
+ /*
+ * Now, do it the other way around. It should look normal now since writing
+ * the limit will #PF first and nothing should be written.
+ */
+ for (off = cbIdtr + 4; off >= -cbIdtr - 4; off--)
+ {
+ Bs3MemSet(pbTest, bFiller, 48);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &pbTest[off]);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off >= 0)
+ {
+ CtxUdExpected.rbx = Ctx.rbx;
+ CtxUdExpected.ss = Ctx.ss;
+ CtxUdExpected.ds = Ctx.ds;
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#11): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ }
+ else
+ {
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, X86_TRAP_PF_RW | (Ctx.bCpl == 3 ? X86_TRAP_PF_US : 0),
+ uFlatTest + off, 0 /*cbIpAdjust*/);
+ if ( -off < cbIdtr
+ && !ASMMemIsAllU8(pbTest, cbIdtr + off, bFiller))
+ Bs3TestFailedF("Wrote partial content on #PF (#12): bFiller=%#x, found %.*Rhxs; off=%d\n",
+ bFiller, cbIdtr + off, pbTest, off);
+ }
+ if (!ASMMemIsAllU8(&pbTest[RT_MAX(cbIdtr + off, 0)], 16, bFiller))
+ Bs3TestFailedF("Wrote beyond expected area (#13): bFiller=%#x, found %.16Rhxs; off=%d\n",
+ bFiller, &pbTest[RT_MAX(cbIdtr + off, 0)], off);
+ g_usBs3TestStep++;
+ }
+
+ /*
+ * Combine paging and segment limit and check ordering.
+ * This is kind of interesting here since it the instruction seems to
+ * be doing two separate writes.
+ */
+ if ( !BS3_MODE_IS_RM_OR_V86(bTestMode)
+ && !BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ uint16_t cbLimit;
+
+ Bs3GdteTestPage00 = Bs3Gdte_DATA16;
+ Bs3GdteTestPage00.Gen.u2Dpl = bRing;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatTest;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatTest >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatTest >> 24);
+
+ if (pWorker->fSs)
+ CtxUdExpected.ss = Ctx.ss = BS3_SEL_TEST_PAGE_00 | bRing;
+ else
+ CtxUdExpected.ds = Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+
+ /* Expand up (normal), approaching tail guard page. */
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = X86_PAGE_SIZE - cbIdtr*2; cbLimit < X86_PAGE_SIZE + cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE - cbIdtr * 2], bFiller, cbIdtr * 2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off + cbIdtr <= cbLimit + 1)
+ {
+ /* No #GP, but maybe #PF. */
+ if (off + cbIdtr <= X86_PAGE_SIZE)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#14): expected %.*Rhxs, got %.*Rhxs\n",
+ cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ }
+ else
+ {
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, X86_TRAP_PF_RW | (Ctx.bCpl == 3 ? X86_TRAP_PF_US : 0),
+ uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ if ( off <= X86_PAGE_SIZE - 2
+ && Bs3MemCmp(&pbTest[off], pbExpected, 2) != 0)
+ Bs3TestFailedF("Mismatch (#15): Expected limit %.2Rhxs, got %.2Rhxs; off=%#x\n",
+ pbExpected, &pbTest[off], off);
+ cb = X86_PAGE_SIZE - off - 2;
+ if ( off < X86_PAGE_SIZE - 2
+ && !ASMMemIsAllU8(&pbTest[off + 2], cb, bFiller))
+ Bs3TestFailedF("Wrote partial base on #PF (#15): bFiller=%#x, got %.*Rhxs; off=%#x\n",
+ bFiller, cb, &pbTest[off + 2], off);
+ if (off == X86_PAGE_SIZE - 1 && pbTest[off] != bFiller)
+ Bs3TestFailedF("Wrote partial limit on #PF (#15): Expected %02x, got %02x\n", bFiller, pbTest[off]);
+ }
+ }
+ else if (off + 2 <= cbLimit + 1)
+ {
+ /* [ig]tr.limit writing does not cause #GP, but may cause #PG, if not writing the base causes #GP. */
+ if (off <= X86_PAGE_SIZE - 2)
+ {
+ if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, 2) != 0)
+ Bs3TestFailedF("Mismatch (#16): Expected limit %.2Rhxs, got %.2Rhxs; off=%#x\n",
+ pbExpected, &pbTest[off], off);
+ cb = X86_PAGE_SIZE - off - 2;
+ if ( off < X86_PAGE_SIZE - 2
+ && !ASMMemIsAllU8(&pbTest[off + 2], cb, bFiller))
+ Bs3TestFailedF("Wrote partial base with limit (#16): bFiller=%#x, got %.*Rhxs; off=%#x\n",
+ bFiller, cb, &pbTest[off + 2], off);
+ }
+ else
+ {
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, X86_TRAP_PF_RW | (Ctx.bCpl == 3 ? X86_TRAP_PF_US : 0),
+ uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ if ( off < X86_PAGE_SIZE
+ && !ASMMemIsAllU8(&pbTest[off], X86_PAGE_SIZE - off, bFiller))
+ Bs3TestFailedF("Mismatch (#16): Partial limit write on #PF: bFiller=%#x, got %.*Rhxs\n",
+ bFiller, X86_PAGE_SIZE - off, &pbTest[off]);
+ }
+ }
+ else
+ {
+ /* #GP/#SS on limit. */
+ if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if ( off < X86_PAGE_SIZE
+ && !ASMMemIsAllU8(&pbTest[off], X86_PAGE_SIZE - off, bFiller))
+ Bs3TestFailedF("Mismatch (#17): Partial write on #GP: bFiller=%#x, got %.*Rhxs\n",
+ bFiller, X86_PAGE_SIZE - off, &pbTest[off]);
+ }
+
+ cb = RT_MIN(cbIdtr * 2, off - (X86_PAGE_SIZE - cbIdtr*2));
+ if (!ASMMemIsAllU8(&pbTest[X86_PAGE_SIZE - cbIdtr * 2], cb, bFiller))
+ Bs3TestFailedF("Leading bytes touched (#18): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x pbTest=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, cb, pbTest[X86_PAGE_SIZE - cbIdtr * 2]);
+
+ g_usBs3TestStep++;
+
+ /* Set DS to 0 and check that we get #GP(0). */
+ if (!pWorker->fSs)
+ {
+ Ctx.ds = 0;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+ g_usBs3TestStep++;
+ }
+ }
+ }
+
+ /* Expand down. */
+ pbTest -= X86_PAGE_SIZE; /* Note! we're backing up a page to simplify things */
+ uFlatTest -= X86_PAGE_SIZE;
+
+ Bs3GdteTestPage00.Gen.u4Type = X86_SEL_TYPE_RW_DOWN_ACC;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatTest;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatTest >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatTest >> 24);
+
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = X86_PAGE_SIZE - cbIdtr*2; cbLimit < X86_PAGE_SIZE + cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE], bFiller, cbIdtr * 2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (cbLimit < off && off >= X86_PAGE_SIZE)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#19): expected %.*Rhxs, got %.*Rhxs\n",
+ cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ cb = X86_PAGE_SIZE + cbIdtr*2 - off;
+ if (!ASMMemIsAllU8(&pbTest[off + cbIdtr], cb, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#20): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x pbTest=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, cb, pbTest[off + cbIdtr]);
+ }
+ else
+ {
+ if (cbLimit < off && off < X86_PAGE_SIZE)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, X86_TRAP_PF_RW | (Ctx.bCpl == 3 ? X86_TRAP_PF_US : 0),
+ uFlatTest + off, 0 /*cbIpAdjust*/);
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ cb = cbIdtr*2;
+ if (!ASMMemIsAllU8(&pbTest[X86_PAGE_SIZE], cb, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#20): cbIdtr=%u off=%u cbLimit=%u bFiller=%#x pbTest=%.*Rhxs\n",
+ cbIdtr, off, cbLimit, bFiller, cb, pbTest[X86_PAGE_SIZE]);
+ }
+ g_usBs3TestStep++;
+ }
+ }
+
+ pbTest += X86_PAGE_SIZE;
+ uFlatTest += X86_PAGE_SIZE;
+ }
+
+ Bs3MemGuardedTestPageFree(pbTest);
+ }
+
+ /*
+ * Check non-canonical 64-bit space.
+ */
+ if ( BS3_MODE_IS_64BIT_CODE(bTestMode)
+ && (pbTest = (uint8_t BS3_FAR *)Bs3PagingSetupCanonicalTraps()) != NULL)
+ {
+ /* Make our references relative to the gap. */
+ pbTest += g_cbBs3PagingOneCanonicalTrap;
+
+ /* Hit it from below. */
+ for (off = -cbIdtr - 8; off < cbIdtr + 8; off++)
+ {
+ Ctx.rbx.u = CtxUdExpected.rbx.u = UINT64_C(0x0000800000000000) + off;
+ Bs3MemSet(&pbTest[-64], bFiller, 64*2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off + cbIdtr <= 0)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#21): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ }
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if (off <= -2 && Bs3MemCmp(&pbTest[off], pbExpected, 2) != 0)
+ Bs3TestFailedF("Mismatch (#21): expected limit %.2Rhxs, got %.2Rhxs\n", pbExpected, &pbTest[off]);
+ off2 = off <= -2 ? 2 : 0;
+ cb = cbIdtr - off2;
+ if (!ASMMemIsAllU8(&pbTest[off + off2], cb, bFiller))
+ Bs3TestFailedF("Mismatch (#21): touched base %.*Rhxs, got %.*Rhxs\n",
+ cb, &pbExpected[off], cb, &pbTest[off + off2]);
+ }
+ if (!ASMMemIsAllU8(&pbTest[off - 16], 16, bFiller))
+ Bs3TestFailedF("Leading bytes touched (#21): bFiller=%#x, got %.16Rhxs\n", bFiller, &pbTest[off]);
+ if (!ASMMemIsAllU8(&pbTest[off + cbIdtr], 16, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#21): bFiller=%#x, got %.16Rhxs\n", bFiller, &pbTest[off + cbIdtr]);
+ }
+
+ /* Hit it from above. */
+ for (off = -cbIdtr - 8; off < cbIdtr + 8; off++)
+ {
+ Ctx.rbx.u = CtxUdExpected.rbx.u = UINT64_C(0xffff800000000000) + off;
+ Bs3MemSet(&pbTest[-64], bFiller, 64*2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off >= 0)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(&pbTest[off], pbExpected, cbIdtr) != 0)
+ Bs3TestFailedF("Mismatch (#22): expected %.*Rhxs, got %.*Rhxs\n", cbIdtr, pbExpected, cbIdtr, &pbTest[off]);
+ }
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ if (!ASMMemIsAllU8(&pbTest[off], cbIdtr, bFiller))
+ Bs3TestFailedF("Mismatch (#22): touched base %.*Rhxs, got %.*Rhxs\n",
+ cbIdtr, &pbExpected[off], cbIdtr, &pbTest[off]);
+ }
+ if (!ASMMemIsAllU8(&pbTest[off - 16], 16, bFiller))
+ Bs3TestFailedF("Leading bytes touched (#22): bFiller=%#x, got %.16Rhxs\n", bFiller, &pbTest[off]);
+ if (!ASMMemIsAllU8(&pbTest[off + cbIdtr], 16, bFiller))
+ Bs3TestFailedF("Trailing bytes touched (#22): bFiller=%#x, got %.16Rhxs\n", bFiller, &pbTest[off + cbIdtr]);
+ }
+
+ }
+}
+
+
+static void bs3CpuBasic2_sidt_sgdt_Common(uint8_t bTestMode, BS3CB2SIDTSGDT const BS3_FAR *paWorkers, unsigned cWorkers,
+ uint8_t const *pbExpected)
+{
+ unsigned idx;
+ unsigned bRing;
+ unsigned iStep = 0;
+
+ /* Note! We skip the SS checks for ring-0 since we badly mess up SS in the
+ test and don't want to bother with double faults. */
+ for (bRing = 0; bRing <= 3; bRing++)
+ {
+ for (idx = 0; idx < cWorkers; idx++)
+ if ( (paWorkers[idx].bMode & (bTestMode & BS3_MODE_CODE_MASK))
+ && (!paWorkers[idx].fSs || bRing != 0 /** @todo || BS3_MODE_IS_64BIT_SYS(bTestMode)*/ ))
+ {
+ g_usBs3TestStep = iStep;
+ bs3CpuBasic2_sidt_sgdt_One(&paWorkers[idx], bTestMode, bRing, pbExpected);
+ iStep += 1000;
+ }
+ if (BS3_MODE_IS_RM_OR_V86(bTestMode))
+ break;
+ }
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_sidt)(uint8_t bMode)
+{
+ union
+ {
+ RTIDTR Idtr;
+ uint8_t ab[16];
+ } Expected;
+
+ //if (bMode != BS3_MODE_LM64) return BS3TESTDOMODE_SKIPPED;
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Pass to common worker which is only compiled once per mode.
+ */
+ Bs3MemZero(&Expected, sizeof(Expected));
+ ASMGetIDTR(&Expected.Idtr);
+ bs3CpuBasic2_sidt_sgdt_Common(bMode, g_aSidtWorkers, RT_ELEMENTS(g_aSidtWorkers), Expected.ab);
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapReInit();
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_sgdt)(uint8_t bMode)
+{
+ uint64_t const uOrgAddr = Bs3Lgdt_Gdt.uAddr;
+ uint64_t uNew = 0;
+ union
+ {
+ RTGDTR Gdtr;
+ uint8_t ab[16];
+ } Expected;
+
+ //if (bMode != BS3_MODE_LM64) return BS3TESTDOMODE_SKIPPED;
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * If paged mode, try push the GDT way up.
+ */
+ Bs3MemZero(&Expected, sizeof(Expected));
+ ASMGetGDTR(&Expected.Gdtr);
+ if (BS3_MODE_IS_PAGED(bMode))
+ {
+/** @todo loading non-canonical base addresses. */
+ int rc;
+ uNew = BS3_MODE_IS_64BIT_SYS(bMode) ? UINT64_C(0xffff80fedcb70000) : UINT64_C(0xc2d28000);
+ uNew |= uOrgAddr & X86_PAGE_OFFSET_MASK;
+ rc = Bs3PagingAlias(uNew, uOrgAddr, Bs3Lgdt_Gdt.cb, X86_PTE_P | X86_PTE_RW | X86_PTE_US | X86_PTE_D | X86_PTE_A);
+ if (RT_SUCCESS(rc))
+ {
+ Bs3Lgdt_Gdt.uAddr = uNew;
+ Bs3UtilSetFullGdtr(Bs3Lgdt_Gdt.cb, uNew);
+ ASMGetGDTR(&Expected.Gdtr);
+ if (BS3_MODE_IS_64BIT_SYS(bMode) && ARCH_BITS != 64)
+ *(uint32_t *)&Expected.ab[6] = (uint32_t)(uNew >> 32);
+ }
+ }
+
+ /*
+ * Pass to common worker which is only compiled once per mode.
+ */
+ bs3CpuBasic2_sidt_sgdt_Common(bMode, g_aSgdtWorkers, RT_ELEMENTS(g_aSgdtWorkers), Expected.ab);
+
+ /*
+ * Unalias the GDT.
+ */
+ if (uNew != 0)
+ {
+ Bs3Lgdt_Gdt.uAddr = uOrgAddr;
+ Bs3UtilSetFullGdtr(Bs3Lgdt_Gdt.cb, uOrgAddr);
+ Bs3PagingUnalias(uNew, Bs3Lgdt_Gdt.cb);
+ }
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapReInit();
+ return 0;
+}
+
+
+
+/*
+ * LIDT & LGDT
+ */
+
+/**
+ * Executes one round of LIDT and LGDT tests using one assembly worker.
+ *
+ * This is written with driving everything from the 16-bit or 32-bit worker in
+ * mind, i.e. not assuming the test bitcount is the same as the current.
+ */
+static void bs3CpuBasic2_lidt_lgdt_One(BS3CB2SIDTSGDT const BS3_FAR *pWorker, uint8_t bTestMode, uint8_t bRing,
+ uint8_t const *pbRestore, size_t cbRestore, uint8_t const *pbExpected)
+{
+ static const struct
+ {
+ bool fGP;
+ uint16_t cbLimit;
+ uint64_t u64Base;
+ } s_aValues64[] =
+ {
+ { false, 0x0000, UINT64_C(0x0000000000000000) },
+ { false, 0x0001, UINT64_C(0x0000000000000001) },
+ { false, 0x0002, UINT64_C(0x0000000000000010) },
+ { false, 0x0003, UINT64_C(0x0000000000000123) },
+ { false, 0x0004, UINT64_C(0x0000000000001234) },
+ { false, 0x0005, UINT64_C(0x0000000000012345) },
+ { false, 0x0006, UINT64_C(0x0000000000123456) },
+ { false, 0x0007, UINT64_C(0x0000000001234567) },
+ { false, 0x0008, UINT64_C(0x0000000012345678) },
+ { false, 0x0009, UINT64_C(0x0000000123456789) },
+ { false, 0x000a, UINT64_C(0x000000123456789a) },
+ { false, 0x000b, UINT64_C(0x00000123456789ab) },
+ { false, 0x000c, UINT64_C(0x0000123456789abc) },
+ { false, 0x001c, UINT64_C(0x00007ffffeefefef) },
+ { false, 0xffff, UINT64_C(0x00007fffffffffff) },
+ { true, 0xf3f1, UINT64_C(0x0000800000000000) },
+ { true, 0x0000, UINT64_C(0x0000800000000000) },
+ { true, 0x0000, UINT64_C(0x0000800000000333) },
+ { true, 0x00f0, UINT64_C(0x0001000000000000) },
+ { true, 0x0ff0, UINT64_C(0x0012000000000000) },
+ { true, 0x0eff, UINT64_C(0x0123000000000000) },
+ { true, 0xe0fe, UINT64_C(0x1234000000000000) },
+ { true, 0x00ad, UINT64_C(0xffff300000000000) },
+ { true, 0x0000, UINT64_C(0xffff7fffffffffff) },
+ { true, 0x00f0, UINT64_C(0xffff7fffffffffff) },
+ { false, 0x5678, UINT64_C(0xffff800000000000) },
+ { false, 0x2969, UINT64_C(0xffffffffffeefefe) },
+ { false, 0x1221, UINT64_C(0xffffffffffffffff) },
+ { false, 0x1221, UINT64_C(0xffffffffffffffff) },
+ };
+ static const struct
+ {
+ uint16_t cbLimit;
+ uint32_t u32Base;
+ } s_aValues32[] =
+ {
+ { 0xdfdf, UINT32_C(0xefefefef) },
+ { 0x0000, UINT32_C(0x00000000) },
+ { 0x0001, UINT32_C(0x00000001) },
+ { 0x0002, UINT32_C(0x00000012) },
+ { 0x0003, UINT32_C(0x00000123) },
+ { 0x0004, UINT32_C(0x00001234) },
+ { 0x0005, UINT32_C(0x00012345) },
+ { 0x0006, UINT32_C(0x00123456) },
+ { 0x0007, UINT32_C(0x01234567) },
+ { 0x0008, UINT32_C(0x12345678) },
+ { 0x0009, UINT32_C(0x80204060) },
+ { 0x000a, UINT32_C(0xddeeffaa) },
+ { 0x000b, UINT32_C(0xfdecdbca) },
+ { 0x000c, UINT32_C(0x6098456b) },
+ { 0x000d, UINT32_C(0x98506099) },
+ { 0x000e, UINT32_C(0x206950bc) },
+ { 0x000f, UINT32_C(0x9740395d) },
+ { 0x0334, UINT32_C(0x64a9455e) },
+ { 0xb423, UINT32_C(0xd20b6eff) },
+ { 0x4955, UINT32_C(0x85296d46) },
+ { 0xffff, UINT32_C(0x07000039) },
+ { 0xefe1, UINT32_C(0x0007fe00) },
+ };
+
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxUdExpected;
+ BS3REGCTX TmpCtx;
+ uint8_t abBufLoad[40]; /* Test buffer w/ misalignment test space and some (cbIdtr) extra guard. */
+ uint8_t abBufSave[32]; /* For saving the result after loading. */
+ uint8_t abBufRestore[24]; /* For restoring sane value (same seg as abBufSave!). */
+ uint8_t abExpectedFilled[32]; /* Same as pbExpected, except it's filled with bFiller2 instead of zeros. */
+ uint8_t BS3_FAR *pbBufSave; /* Correctly aligned pointer into abBufSave. */
+ uint8_t BS3_FAR *pbBufRestore; /* Correctly aligned pointer into abBufRestore. */
+ uint8_t const cbIdtr = BS3_MODE_IS_64BIT_CODE(bTestMode) ? 2+8 : 2+4;
+ uint8_t const cbBaseLoaded = BS3_MODE_IS_64BIT_CODE(bTestMode) ? 8
+ : BS3_MODE_IS_16BIT_CODE(bTestMode) == !(pWorker->fFlags & BS3CB2SIDTSGDT_F_OPSIZE)
+ ? 3 : 4;
+ bool const f286 = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) == BS3CPU_80286;
+ uint8_t const bTop16BitBase = f286 ? 0xff : 0x00;
+ uint8_t bFiller1; /* For filling abBufLoad. */
+ uint8_t bFiller2; /* For filling abBufSave and expectations. */
+ int off;
+ uint8_t BS3_FAR *pbTest;
+ unsigned i;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxUdExpected, sizeof(CtxUdExpected));
+ Bs3MemZero(&TmpCtx, sizeof(TmpCtx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(abBufSave, sizeof(abBufSave));
+ Bs3MemZero(abBufLoad, sizeof(abBufLoad));
+ Bs3MemZero(abBufRestore, sizeof(abBufRestore));
+
+ /*
+ * Create a context, giving this routine some more stack space.
+ * - Point the context at our LIDT [xBX] + SIDT [xDI] + LIDT [xSI] + UD2 combo.
+ * - Point DS/SS:xBX at abBufLoad.
+ * - Point ES:xDI at abBufSave.
+ * - Point ES:xSI at abBufRestore.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bTestMode, 256 /*cbExtraStack*/);
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, pWorker->fpfnWorker);
+ if (BS3_MODE_IS_16BIT_SYS(bTestMode))
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+ Ctx.rflags.u16 &= ~X86_EFL_IF;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBufLoad);
+
+ pbBufSave = abBufSave;
+ if ((BS3_FP_OFF(pbBufSave) + 2) & 7)
+ pbBufSave += 8 - ((BS3_FP_OFF(pbBufSave) + 2) & 7);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rdi, &Ctx.es, pbBufSave);
+
+ pbBufRestore = abBufRestore;
+ if ((BS3_FP_OFF(pbBufRestore) + 2) & 7)
+ pbBufRestore += 8 - ((BS3_FP_OFF(pbBufRestore) + 2) & 7);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rsi, &Ctx.es, pbBufRestore);
+ Bs3MemCpy(pbBufRestore, pbRestore, cbRestore);
+
+ if (!BS3_MODE_IS_RM_OR_V86(bTestMode))
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+
+ /* For successful SIDT attempts, we'll stop at the UD2. */
+ Bs3MemCpy(&CtxUdExpected, &Ctx, sizeof(Ctx));
+ CtxUdExpected.rip.u += pWorker->cbInstr;
+
+ /*
+ * Check that it works at all.
+ */
+ Bs3MemZero(abBufLoad, sizeof(abBufLoad));
+ Bs3MemCpy(abBufLoad, pbBufRestore, cbIdtr);
+ Bs3MemZero(abBufSave, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, pbExpected, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #1): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, pbExpected, cbIdtr*2, pbBufSave);
+ }
+ g_usBs3TestStep++;
+
+ /* Determine two filler bytes that doesn't appear in the previous result or our expectations. */
+ bFiller1 = ~0x55;
+ while ( Bs3MemChr(pbBufSave, bFiller1, cbIdtr) != NULL
+ || Bs3MemChr(pbRestore, bFiller1, cbRestore) != NULL
+ || bFiller1 == 0xff)
+ bFiller1++;
+ bFiller2 = 0x33;
+ while ( Bs3MemChr(pbBufSave, bFiller2, cbIdtr) != NULL
+ || Bs3MemChr(pbRestore, bFiller2, cbRestore) != NULL
+ || bFiller2 == 0xff
+ || bFiller2 == bFiller1)
+ bFiller2++;
+ Bs3MemSet(abExpectedFilled, bFiller2, sizeof(abExpectedFilled));
+ Bs3MemCpy(abExpectedFilled, pbExpected, cbIdtr);
+
+ /* Again with a buffer filled with a byte not occuring in the previous result. */
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(abBufLoad, pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #2): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ g_usBs3TestStep++;
+
+ /*
+ * Try loading a bunch of different limit+base value to check what happens,
+ * especially what happens wrt the top part of the base in 16-bit mode.
+ */
+ if (BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aValues64); i++)
+ {
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(&abBufLoad[0], &s_aValues64[i].cbLimit, 2);
+ Bs3MemCpy(&abBufLoad[2], &s_aValues64[i].u64Base, 8);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0 || s_aValues64[i].fGP)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if ( Bs3MemCmp(&pbBufSave[0], &s_aValues64[i].cbLimit, 2) != 0
+ || Bs3MemCmp(&pbBufSave[2], &s_aValues64[i].u64Base, 8) != 0
+ || !ASMMemIsAllU8(&pbBufSave[10], cbIdtr, bFiller2))
+ Bs3TestFailedF("Mismatch (%s, #2): expected %04RX16:%016RX64, fillers %#x %#x, got %.*Rhxs\n",
+ pWorker->pszDesc, s_aValues64[i].cbLimit, s_aValues64[i].u64Base,
+ bFiller1, bFiller2, cbIdtr*2, pbBufSave);
+ }
+ g_usBs3TestStep++;
+ }
+ }
+ else
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aValues32); i++)
+ {
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(&abBufLoad[0], &s_aValues32[i].cbLimit, 2);
+ Bs3MemCpy(&abBufLoad[2], &s_aValues32[i].u32Base, cbBaseLoaded);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if ( Bs3MemCmp(&pbBufSave[0], &s_aValues32[i].cbLimit, 2) != 0
+ || Bs3MemCmp(&pbBufSave[2], &s_aValues32[i].u32Base, cbBaseLoaded) != 0
+ || ( cbBaseLoaded != 4
+ && pbBufSave[2+3] != bTop16BitBase)
+ || !ASMMemIsAllU8(&pbBufSave[8], cbIdtr, bFiller2))
+ Bs3TestFailedF("Mismatch (%s,#3): loaded %04RX16:%08RX32, fillers %#x %#x%s, got %.*Rhxs\n",
+ pWorker->pszDesc, s_aValues32[i].cbLimit, s_aValues32[i].u32Base, bFiller1, bFiller2,
+ f286 ? ", 286" : "", cbIdtr*2, pbBufSave);
+ }
+ g_usBs3TestStep++;
+ }
+ }
+
+ /*
+ * Slide the buffer along 8 bytes to cover misalignment.
+ */
+ for (off = 0; off < 8; off++)
+ {
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &abBufLoad[off]);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(&abBufLoad[off], pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #4): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ g_usBs3TestStep++;
+ }
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBufLoad);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+
+ /*
+ * Play with the selector limit if the target mode supports limit checking
+ * We use BS3_SEL_TEST_PAGE_00 for this
+ */
+ if ( !BS3_MODE_IS_RM_OR_V86(bTestMode)
+ && !BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ uint16_t cbLimit;
+ uint32_t uFlatBuf = Bs3SelPtrToFlat(abBufLoad);
+ Bs3GdteTestPage00 = Bs3Gdte_DATA16;
+ Bs3GdteTestPage00.Gen.u2Dpl = bRing;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatBuf;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatBuf >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatBuf >> 24);
+
+ if (pWorker->fSs)
+ CtxUdExpected.ss = Ctx.ss = BS3_SEL_TEST_PAGE_00 | bRing;
+ else
+ CtxUdExpected.ds = Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+
+ /* Expand up (normal). */
+ for (off = 0; off < 8; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = 0; cbLimit < cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(&abBufLoad[off], pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off + cbIdtr <= cbLimit + 1)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #5): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ g_usBs3TestStep++;
+
+ /* Again with zero limit and messed up base (should trigger tripple fault if partially loaded). */
+ abBufLoad[off] = abBufLoad[off + 1] = 0;
+ abBufLoad[off + 2] |= 1;
+ abBufLoad[off + cbIdtr - 2] ^= 0x5a;
+ abBufLoad[off + cbIdtr - 1] ^= 0xa5;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off + cbIdtr <= cbLimit + 1)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ }
+ }
+
+ /* Expand down (weird). Inverted valid area compared to expand up,
+ so a limit of zero give us a valid range for 0001..0ffffh (instead of
+ a segment with one valid byte at 0000h). Whereas a limit of 0fffeh
+ means one valid byte at 0ffffh, and a limit of 0ffffh means none
+ (because in a normal expand up the 0ffffh means all 64KB are
+ accessible). */
+ Bs3GdteTestPage00.Gen.u4Type = X86_SEL_TYPE_RW_DOWN_ACC;
+ for (off = 0; off < 8; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = 0; cbLimit < cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+
+ Bs3MemSet(abBufLoad, bFiller1, sizeof(abBufLoad));
+ Bs3MemCpy(&abBufLoad[off], pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off > cbLimit)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #6): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ g_usBs3TestStep++;
+
+ /* Again with zero limit and messed up base (should trigger triple fault if partially loaded). */
+ abBufLoad[off] = abBufLoad[off + 1] = 0;
+ abBufLoad[off + 2] |= 3;
+ abBufLoad[off + cbIdtr - 2] ^= 0x55;
+ abBufLoad[off + cbIdtr - 1] ^= 0xaa;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off > cbLimit)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ }
+ }
+
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, abBufLoad);
+ CtxUdExpected.rbx.u = Ctx.rbx.u;
+ CtxUdExpected.ss = Ctx.ss;
+ CtxUdExpected.ds = Ctx.ds;
+ }
+
+ /*
+ * Play with the paging.
+ */
+ if ( BS3_MODE_IS_PAGED(bTestMode)
+ && (!pWorker->fSs || bRing == 3) /* SS.DPL == CPL, we'll get some tiled ring-3 selector here. */
+ && (pbTest = (uint8_t BS3_FAR *)Bs3MemGuardedTestPageAlloc(BS3MEMKIND_TILED)) != NULL)
+ {
+ RTCCUINTXREG uFlatTest = Bs3SelPtrToFlat(pbTest);
+
+ /*
+ * Slide the load buffer towards the trailing guard page.
+ */
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &pbTest[X86_PAGE_SIZE]);
+ CtxUdExpected.ss = Ctx.ss;
+ CtxUdExpected.ds = Ctx.ds;
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE - cbIdtr * 2], bFiller1, cbIdtr*2);
+ if (off < X86_PAGE_SIZE)
+ Bs3MemCpy(&pbTest[off], pbBufRestore, RT_MIN(X86_PAGE_SIZE - off, cbIdtr));
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &pbTest[off]);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off + cbIdtr <= X86_PAGE_SIZE)
+ {
+ CtxUdExpected.rbx = Ctx.rbx;
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr*2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #7): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ g_usBs3TestStep++;
+
+ /* Again with zero limit and maybe messed up base as well (triple fault if buggy).
+ The 386DX-40 here triple faults (or something) with off == 0xffe, nothing else. */
+ if ( off < X86_PAGE_SIZE && off + cbIdtr > X86_PAGE_SIZE
+ && ( off != X86_PAGE_SIZE - 2
+ || (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) != BS3CPU_80386)
+ )
+ {
+ pbTest[off] = 0;
+ if (off + 1 < X86_PAGE_SIZE)
+ pbTest[off + 1] = 0;
+ if (off + 2 < X86_PAGE_SIZE)
+ pbTest[off + 2] |= 7;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ g_usBs3TestStep++;
+ }
+ }
+
+ /*
+ * Now, do it the other way around. It should look normal now since writing
+ * the limit will #PF first and nothing should be written.
+ */
+ for (off = cbIdtr + 4; off >= -cbIdtr - 4; off--)
+ {
+ Bs3MemSet(pbTest, bFiller1, 48);
+ if (off >= 0)
+ Bs3MemCpy(&pbTest[off], pbBufRestore, cbIdtr);
+ else if (off + cbIdtr > 0)
+ Bs3MemCpy(pbTest, &pbBufRestore[-off], cbIdtr + off);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, pWorker->fSs ? &Ctx.ss : &Ctx.ds, &pbTest[off]);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off >= 0)
+ {
+ CtxUdExpected.rbx = Ctx.rbx;
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr*2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #8): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + off, 0 /*cbIpAdjust*/);
+ g_usBs3TestStep++;
+
+ /* Again with messed up base as well (triple fault if buggy). */
+ if (off < 0 && off > -cbIdtr)
+ {
+ if (off + 2 >= 0)
+ pbTest[off + 2] |= 15;
+ pbTest[off + cbIdtr - 1] ^= 0xaa;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + off, 0 /*cbIpAdjust*/);
+ g_usBs3TestStep++;
+ }
+ }
+
+ /*
+ * Combine paging and segment limit and check ordering.
+ * This is kind of interesting here since it the instruction seems to
+ * actually be doing two separate read, just like it's S[IG]DT counterpart.
+ *
+ * Note! My 486DX4 does a DWORD limit read when the operand size is 32-bit,
+ * that's what f486Weirdness deals with.
+ */
+ if ( !BS3_MODE_IS_RM_OR_V86(bTestMode)
+ && !BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ bool const f486Weirdness = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) == BS3CPU_80486
+ && BS3_MODE_IS_32BIT_CODE(bTestMode) == !(pWorker->fFlags & BS3CB2SIDTSGDT_F_OPSIZE);
+ uint16_t cbLimit;
+
+ Bs3GdteTestPage00 = Bs3Gdte_DATA16;
+ Bs3GdteTestPage00.Gen.u2Dpl = bRing;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatTest;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatTest >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatTest >> 24);
+
+ if (pWorker->fSs)
+ CtxUdExpected.ss = Ctx.ss = BS3_SEL_TEST_PAGE_00 | bRing;
+ else
+ CtxUdExpected.ds = Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+
+ /* Expand up (normal), approaching tail guard page. */
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = X86_PAGE_SIZE - cbIdtr*2; cbLimit < X86_PAGE_SIZE + cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE - cbIdtr * 2], bFiller1, cbIdtr * 2);
+ if (off < X86_PAGE_SIZE)
+ Bs3MemCpy(&pbTest[off], pbBufRestore, RT_MIN(cbIdtr, X86_PAGE_SIZE - off));
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (off + cbIdtr <= cbLimit + 1)
+ {
+ /* No #GP, but maybe #PF. */
+ if (off + cbIdtr <= X86_PAGE_SIZE)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #9): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ }
+ /* No #GP/#SS on limit, but instead #PF? */
+ else if ( !f486Weirdness
+ ? off < cbLimit && off >= 0xfff
+ : off + 2 < cbLimit && off >= 0xffd)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + RT_MAX(off, X86_PAGE_SIZE), 0 /*cbIpAdjust*/);
+ /* #GP/#SS on limit or base. */
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+
+ g_usBs3TestStep++;
+
+ /* Set DS to 0 and check that we get #GP(0). */
+ if (!pWorker->fSs)
+ {
+ Ctx.ds = 0;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ Ctx.ds = BS3_SEL_TEST_PAGE_00 | bRing;
+ g_usBs3TestStep++;
+ }
+ }
+ }
+
+ /* Expand down. */
+ pbTest -= X86_PAGE_SIZE; /* Note! we're backing up a page to simplify things */
+ uFlatTest -= X86_PAGE_SIZE;
+
+ Bs3GdteTestPage00.Gen.u4Type = X86_SEL_TYPE_RW_DOWN_ACC;
+ Bs3GdteTestPage00.Gen.u16BaseLow = (uint16_t)uFlatTest;
+ Bs3GdteTestPage00.Gen.u8BaseHigh1 = (uint8_t)(uFlatTest >> 16);
+ Bs3GdteTestPage00.Gen.u8BaseHigh2 = (uint8_t)(uFlatTest >> 24);
+
+ for (off = X86_PAGE_SIZE - cbIdtr - 4; off < X86_PAGE_SIZE + 4; off++)
+ {
+ CtxUdExpected.rbx.u = Ctx.rbx.u = off;
+ for (cbLimit = X86_PAGE_SIZE - cbIdtr*2; cbLimit < X86_PAGE_SIZE + cbIdtr*2; cbLimit++)
+ {
+ Bs3GdteTestPage00.Gen.u16LimitLow = cbLimit;
+ Bs3MemSet(&pbTest[X86_PAGE_SIZE], bFiller1, cbIdtr * 2);
+ if (off >= X86_PAGE_SIZE)
+ Bs3MemCpy(&pbTest[off], pbBufRestore, cbIdtr);
+ else if (off > X86_PAGE_SIZE - cbIdtr)
+ Bs3MemCpy(&pbTest[X86_PAGE_SIZE], &pbBufRestore[X86_PAGE_SIZE - off], cbIdtr - (X86_PAGE_SIZE - off));
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else if (cbLimit < off && off >= X86_PAGE_SIZE)
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #10): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ else if (cbLimit < off && off < X86_PAGE_SIZE)
+ bs3CpuBasic2_ComparePfCtx(&TrapCtx, &Ctx, 0, uFlatTest + off, 0 /*cbIpAdjust*/);
+ else if (pWorker->fSs)
+ bs3CpuBasic2_CompareSsCtx(&TrapCtx, &Ctx, 0, false /*f486ResumeFlagHint*/);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ g_usBs3TestStep++;
+ }
+ }
+
+ pbTest += X86_PAGE_SIZE;
+ uFlatTest += X86_PAGE_SIZE;
+ }
+
+ Bs3MemGuardedTestPageFree(pbTest);
+ }
+
+ /*
+ * Check non-canonical 64-bit space.
+ */
+ if ( BS3_MODE_IS_64BIT_CODE(bTestMode)
+ && (pbTest = (uint8_t BS3_FAR *)Bs3PagingSetupCanonicalTraps()) != NULL)
+ {
+ /* Make our references relative to the gap. */
+ pbTest += g_cbBs3PagingOneCanonicalTrap;
+
+ /* Hit it from below. */
+ for (off = -cbIdtr - 8; off < cbIdtr + 8; off++)
+ {
+ Ctx.rbx.u = CtxUdExpected.rbx.u = UINT64_C(0x0000800000000000) + off;
+ Bs3MemSet(&pbTest[-64], bFiller1, 64*2);
+ Bs3MemCpy(&pbTest[off], pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off + cbIdtr > 0 || bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #11): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ }
+
+ /* Hit it from above. */
+ for (off = -cbIdtr - 8; off < cbIdtr + 8; off++)
+ {
+ Ctx.rbx.u = CtxUdExpected.rbx.u = UINT64_C(0xffff800000000000) + off;
+ Bs3MemSet(&pbTest[-64], bFiller1, 64*2);
+ Bs3MemCpy(&pbTest[off], pbBufRestore, cbIdtr);
+ Bs3MemSet(abBufSave, bFiller2, sizeof(abBufSave));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (off < 0 || bRing != 0)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ else
+ {
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ if (Bs3MemCmp(pbBufSave, abExpectedFilled, cbIdtr * 2) != 0)
+ Bs3TestFailedF("Mismatch (%s, #19): expected %.*Rhxs, got %.*Rhxs\n",
+ pWorker->pszDesc, cbIdtr*2, abExpectedFilled, cbIdtr*2, pbBufSave);
+ }
+ }
+
+ }
+}
+
+
+static void bs3CpuBasic2_lidt_lgdt_Common(uint8_t bTestMode, BS3CB2SIDTSGDT const BS3_FAR *paWorkers, unsigned cWorkers,
+ void const *pvRestore, size_t cbRestore, uint8_t const *pbExpected)
+{
+ unsigned idx;
+ unsigned bRing;
+ unsigned iStep = 0;
+
+ /* Note! We skip the SS checks for ring-0 since we badly mess up SS in the
+ test and don't want to bother with double faults. */
+ for (bRing = BS3_MODE_IS_V86(bTestMode) ? 3 : 0; bRing <= 3; bRing++)
+ {
+ for (idx = 0; idx < cWorkers; idx++)
+ if ( (paWorkers[idx].bMode & (bTestMode & BS3_MODE_CODE_MASK))
+ && (!paWorkers[idx].fSs || bRing != 0 /** @todo || BS3_MODE_IS_64BIT_SYS(bTestMode)*/ )
+ && ( !(paWorkers[idx].fFlags & BS3CB2SIDTSGDT_F_386PLUS)
+ || ( bTestMode > BS3_MODE_PE16
+ || ( bTestMode == BS3_MODE_PE16
+ && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)) ) )
+ {
+ //Bs3TestPrintf("idx=%-2d fpfnWorker=%p fSs=%d cbInstr=%d\n",
+ // idx, paWorkers[idx].fpfnWorker, paWorkers[idx].fSs, paWorkers[idx].cbInstr);
+ g_usBs3TestStep = iStep;
+ bs3CpuBasic2_lidt_lgdt_One(&paWorkers[idx], bTestMode, bRing, pvRestore, cbRestore, pbExpected);
+ iStep += 1000;
+ }
+ if (BS3_MODE_IS_RM_SYS(bTestMode))
+ break;
+ }
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_lidt)(uint8_t bMode)
+{
+ union
+ {
+ RTIDTR Idtr;
+ uint8_t ab[32]; /* At least cbIdtr*2! */
+ } Expected;
+
+ //if (bMode != BS3_MODE_LM64) return 0;
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Pass to common worker which is only compiled once per mode.
+ */
+ Bs3MemZero(&Expected, sizeof(Expected));
+ ASMGetIDTR(&Expected.Idtr);
+
+ if (BS3_MODE_IS_RM_SYS(bMode))
+ bs3CpuBasic2_lidt_lgdt_Common(bMode, g_aLidtWorkers, RT_ELEMENTS(g_aLidtWorkers),
+ &Bs3Lidt_Ivt, sizeof(Bs3Lidt_Ivt), Expected.ab);
+ else if (BS3_MODE_IS_16BIT_SYS(bMode))
+ bs3CpuBasic2_lidt_lgdt_Common(bMode, g_aLidtWorkers, RT_ELEMENTS(g_aLidtWorkers),
+ &Bs3Lidt_Idt16, sizeof(Bs3Lidt_Idt16), Expected.ab);
+ else if (BS3_MODE_IS_32BIT_SYS(bMode))
+ bs3CpuBasic2_lidt_lgdt_Common(bMode, g_aLidtWorkers, RT_ELEMENTS(g_aLidtWorkers),
+ &Bs3Lidt_Idt32, sizeof(Bs3Lidt_Idt32), Expected.ab);
+ else
+ bs3CpuBasic2_lidt_lgdt_Common(bMode, g_aLidtWorkers, RT_ELEMENTS(g_aLidtWorkers),
+ &Bs3Lidt_Idt64, sizeof(Bs3Lidt_Idt64), Expected.ab);
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapReInit();
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_lgdt)(uint8_t bMode)
+{
+ union
+ {
+ RTGDTR Gdtr;
+ uint8_t ab[32]; /* At least cbIdtr*2! */
+ } Expected;
+
+ //if (!BS3_MODE_IS_64BIT_SYS(bMode)) return 0;
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Pass to common worker which is only compiled once per mode.
+ */
+ if (BS3_MODE_IS_RM_SYS(bMode))
+ ASMSetGDTR((PRTGDTR)&Bs3LgdtDef_Gdt);
+ Bs3MemZero(&Expected, sizeof(Expected));
+ ASMGetGDTR(&Expected.Gdtr);
+
+ bs3CpuBasic2_lidt_lgdt_Common(bMode, g_aLgdtWorkers, RT_ELEMENTS(g_aLgdtWorkers),
+ &Bs3LgdtDef_Gdt, sizeof(Bs3LgdtDef_Gdt), Expected.ab);
+
+ /*
+ * Re-initialize the IDT.
+ */
+ Bs3TrapReInit();
+ return 0;
+}
+
+typedef union IRETBUF
+{
+ uint64_t au64[6]; /* max req is 5 */
+ uint32_t au32[12]; /* max req is 9 */
+ uint16_t au16[24]; /* max req is 5 */
+ uint8_t ab[48];
+} IRETBUF;
+typedef IRETBUF BS3_FAR *PIRETBUF;
+
+
+static void iretbuf_SetupFrame(PIRETBUF pIretBuf, unsigned const cbPop,
+ uint16_t uCS, uint64_t uPC, uint32_t fEfl, uint16_t uSS, uint64_t uSP)
+{
+ if (cbPop == 2)
+ {
+ pIretBuf->au16[0] = (uint16_t)uPC;
+ pIretBuf->au16[1] = uCS;
+ pIretBuf->au16[2] = (uint16_t)fEfl;
+ pIretBuf->au16[3] = (uint16_t)uSP;
+ pIretBuf->au16[4] = uSS;
+ }
+ else if (cbPop != 8)
+ {
+ pIretBuf->au32[0] = (uint32_t)uPC;
+ pIretBuf->au16[1*2] = uCS;
+ pIretBuf->au32[2] = (uint32_t)fEfl;
+ pIretBuf->au32[3] = (uint32_t)uSP;
+ pIretBuf->au16[4*2] = uSS;
+ }
+ else
+ {
+ pIretBuf->au64[0] = uPC;
+ pIretBuf->au16[1*4] = uCS;
+ pIretBuf->au64[2] = fEfl;
+ pIretBuf->au64[3] = uSP;
+ pIretBuf->au16[4*4] = uSS;
+ }
+}
+
+
+static void bs3CpuBasic2_iret_Worker(uint8_t bTestMode, FPFNBS3FAR pfnIret, unsigned const cbPop,
+ PIRETBUF pIretBuf, const char BS3_FAR *pszDesc)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxUdExpected;
+ BS3REGCTX TmpCtx;
+ BS3REGCTX TmpCtxExpected;
+ uint8_t abLowUd[8];
+ uint8_t abLowIret[8];
+ FPFNBS3FAR pfnUdLow = (FPFNBS3FAR)abLowUd;
+ FPFNBS3FAR pfnIretLow = (FPFNBS3FAR)abLowIret;
+ unsigned const cbSameCplFrame = BS3_MODE_IS_64BIT_CODE(bTestMode) ? 5*cbPop : 3*cbPop;
+ bool const fUseLowCode = cbPop == 2 && !BS3_MODE_IS_16BIT_CODE(bTestMode);
+ int iRingDst;
+ int iRingSrc;
+ uint16_t uDplSs;
+ uint16_t uRplCs;
+ uint16_t uRplSs;
+// int i;
+ uint8_t BS3_FAR *pbTest;
+
+ NOREF(abLowUd);
+#define IRETBUF_SET_SEL(a_idx, a_uValue) \
+ do { *(uint16_t)&pIretBuf->ab[a_idx * cbPop] = (a_uValue); } while (0)
+#define IRETBUF_SET_REG(a_idx, a_uValue) \
+ do { uint8_t const BS3_FAR *pbTmp = &pIretBuf->ab[a_idx * cbPop]; \
+ if (cbPop == 2) *(uint16_t)pbTmp = (uint16_t)(a_uValue); \
+ else if (cbPop != 8) *(uint32_t)pbTmp = (uint32_t)(a_uValue); \
+ else *(uint64_t)pbTmp = (a_uValue); \
+ } while (0)
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxUdExpected, sizeof(CtxUdExpected));
+ Bs3MemZero(&TmpCtx, sizeof(TmpCtx));
+ Bs3MemZero(&TmpCtxExpected, sizeof(TmpCtxExpected));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ /*
+ * When dealing with 16-bit irets in 32-bit or 64-bit mode, we must have
+ * copies of both iret and ud in the first 64KB of memory. The stack is
+ * below 64KB, so we'll just copy the instructions onto the stack.
+ */
+ Bs3MemCpy(abLowUd, bs3CpuBasic2_ud2, 4);
+ Bs3MemCpy(abLowIret, pfnIret, 4);
+
+ /*
+ * Create a context (stack is irrelevant, we'll mainly be using pIretBuf).
+ * - Point the context at our iret instruction.
+ * - Point SS:xSP at pIretBuf.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bTestMode, 0);
+ if (!fUseLowCode)
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, pfnIret);
+ else
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, pfnIretLow);
+ if (BS3_MODE_IS_16BIT_SYS(bTestMode))
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rsp, &Ctx.ss, pIretBuf);
+
+ /*
+ * The first success (UD) context keeps the same code bit-count as the iret.
+ */
+ Bs3MemCpy(&CtxUdExpected, &Ctx, sizeof(Ctx));
+ if (!fUseLowCode)
+ Bs3RegCtxSetRipCsFromLnkPtr(&CtxUdExpected, bs3CpuBasic2_ud2);
+ else
+ Bs3RegCtxSetRipCsFromCurPtr(&CtxUdExpected, pfnUdLow);
+ CtxUdExpected.rsp.u += cbSameCplFrame;
+
+ /*
+ * Check that it works at all.
+ */
+ iretbuf_SetupFrame(pIretBuf, cbPop, CtxUdExpected.cs, CtxUdExpected.rip.u,
+ CtxUdExpected.rflags.u32, CtxUdExpected.ss, CtxUdExpected.rsp.u);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ g_usBs3TestStep++;
+
+ if (!BS3_MODE_IS_RM_OR_V86(bTestMode))
+ {
+ /* Selectors are modified when switching rings, so we need to know
+ what we're dealing with there. */
+ if ( !BS3_SEL_IS_IN_R0_RANGE(Ctx.cs) || !BS3_SEL_IS_IN_R0_RANGE(Ctx.ss)
+ || !BS3_SEL_IS_IN_R0_RANGE(Ctx.ds) || !BS3_SEL_IS_IN_R0_RANGE(Ctx.es))
+ Bs3TestFailedF("Expected R0 CS, SS, DS and ES; not %#x, %#x, %#x and %#x\n", Ctx.cs, Ctx.ss, Ctx.ds, Ctx.es);
+ if (Ctx.fs || Ctx.gs)
+ Bs3TestFailed("Expected R0 FS and GS to be 0!\n");
+
+ /*
+ * Test returning to outer rings if protected mode.
+ */
+ Bs3MemCpy(&TmpCtx, &Ctx, sizeof(TmpCtx));
+ Bs3MemCpy(&TmpCtxExpected, &CtxUdExpected, sizeof(TmpCtxExpected));
+ for (iRingDst = 3; iRingDst >= 0; iRingDst--)
+ {
+ Bs3RegCtxConvertToRingX(&TmpCtxExpected, iRingDst);
+ TmpCtxExpected.ds = iRingDst ? 0 : TmpCtx.ds;
+ TmpCtx.es = TmpCtxExpected.es;
+ iretbuf_SetupFrame(pIretBuf, cbPop, TmpCtxExpected.cs, TmpCtxExpected.rip.u,
+ TmpCtxExpected.rflags.u32, TmpCtxExpected.ss, TmpCtxExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&TmpCtx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &TmpCtxExpected);
+ g_usBs3TestStep++;
+ }
+
+ /*
+ * Check CS.RPL and SS.RPL.
+ */
+ for (iRingDst = 3; iRingDst >= 0; iRingDst--)
+ {
+ uint16_t const uDstSsR0 = (CtxUdExpected.ss & BS3_SEL_RING_SUB_MASK) + BS3_SEL_R0_FIRST;
+ Bs3MemCpy(&TmpCtxExpected, &CtxUdExpected, sizeof(TmpCtxExpected));
+ Bs3RegCtxConvertToRingX(&TmpCtxExpected, iRingDst);
+ for (iRingSrc = 3; iRingSrc >= 0; iRingSrc--)
+ {
+ Bs3MemCpy(&TmpCtx, &Ctx, sizeof(TmpCtx));
+ Bs3RegCtxConvertToRingX(&TmpCtx, iRingSrc);
+ TmpCtx.es = TmpCtxExpected.es;
+ TmpCtxExpected.ds = iRingDst != iRingSrc ? 0 : TmpCtx.ds;
+ for (uRplCs = 0; uRplCs <= 3; uRplCs++)
+ {
+ uint16_t const uSrcEs = TmpCtx.es;
+ uint16_t const uDstCs = (TmpCtxExpected.cs & X86_SEL_MASK_OFF_RPL) | uRplCs;
+ //Bs3TestPrintf("dst=%d src=%d rplCS=%d\n", iRingDst, iRingSrc, uRplCs);
+
+ /* CS.RPL */
+ iretbuf_SetupFrame(pIretBuf, cbPop, uDstCs, TmpCtxExpected.rip.u, TmpCtxExpected.rflags.u32,
+ TmpCtxExpected.ss, TmpCtxExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&TmpCtx, &TrapCtx);
+ if (uRplCs == iRingDst && iRingDst >= iRingSrc)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &TmpCtxExpected);
+ else
+ {
+ if (iRingDst < iRingSrc)
+ TmpCtx.es = 0;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &TmpCtx, uDstCs & X86_SEL_MASK_OFF_RPL);
+ TmpCtx.es = uSrcEs;
+ }
+ g_usBs3TestStep++;
+
+ /* SS.RPL */
+ if (iRingDst != iRingSrc || BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ uint16_t uSavedDstSs = TmpCtxExpected.ss;
+ for (uRplSs = 0; uRplSs <= 3; uRplSs++)
+ {
+ /* SS.DPL (iRingDst == CS.DPL) */
+ for (uDplSs = 0; uDplSs <= 3; uDplSs++)
+ {
+ uint16_t const uDstSs = ((uDplSs << BS3_SEL_RING_SHIFT) | uRplSs) + uDstSsR0;
+ //Bs3TestPrintf("dst=%d src=%d rplCS=%d rplSS=%d dplSS=%d dst %04x:%08RX64 %08RX32 %04x:%08RX64\n",
+ // iRingDst, iRingSrc, uRplCs, uRplSs, uDplSs, uDstCs, TmpCtxExpected.rip.u,
+ // TmpCtxExpected.rflags.u32, uDstSs, TmpCtxExpected.rsp.u);
+
+ iretbuf_SetupFrame(pIretBuf, cbPop, uDstCs, TmpCtxExpected.rip.u,
+ TmpCtxExpected.rflags.u32, uDstSs, TmpCtxExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&TmpCtx, &TrapCtx);
+ if (uRplCs != iRingDst || iRingDst < iRingSrc)
+ {
+ if (iRingDst < iRingSrc)
+ TmpCtx.es = 0;
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &TmpCtx, uDstCs & X86_SEL_MASK_OFF_RPL);
+ }
+ else if (uRplSs != iRingDst || uDplSs != iRingDst)
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &TmpCtx, uDstSs & X86_SEL_MASK_OFF_RPL);
+ else
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &TmpCtxExpected);
+ TmpCtx.es = uSrcEs;
+ g_usBs3TestStep++;
+ }
+ }
+
+ TmpCtxExpected.ss = uSavedDstSs;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Special 64-bit checks.
+ */
+ if (BS3_MODE_IS_64BIT_CODE(bTestMode))
+ {
+ /* The VM flag is completely ignored. */
+ iretbuf_SetupFrame(pIretBuf, cbPop, CtxUdExpected.cs, CtxUdExpected.rip.u,
+ CtxUdExpected.rflags.u32 | X86_EFL_VM, CtxUdExpected.ss, CtxUdExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ g_usBs3TestStep++;
+
+ /* The NT flag can be loaded just fine. */
+ CtxUdExpected.rflags.u32 |= X86_EFL_NT;
+ iretbuf_SetupFrame(pIretBuf, cbPop, CtxUdExpected.cs, CtxUdExpected.rip.u,
+ CtxUdExpected.rflags.u32, CtxUdExpected.ss, CtxUdExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxUdExpected);
+ CtxUdExpected.rflags.u32 &= ~X86_EFL_NT;
+ g_usBs3TestStep++;
+
+ /* However, we'll #GP(0) if it's already set (in RFLAGS) when executing IRET. */
+ Ctx.rflags.u32 |= X86_EFL_NT;
+ iretbuf_SetupFrame(pIretBuf, cbPop, CtxUdExpected.cs, CtxUdExpected.rip.u,
+ CtxUdExpected.rflags.u32, CtxUdExpected.ss, CtxUdExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ g_usBs3TestStep++;
+
+ /* The NT flag #GP(0) should trump all other exceptions - pit it against #PF. */
+ pbTest = (uint8_t BS3_FAR *)Bs3MemGuardedTestPageAlloc(BS3MEMKIND_TILED);
+ if (pbTest != NULL)
+ {
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rsp, &Ctx.ss, &pbTest[X86_PAGE_SIZE]);
+ iretbuf_SetupFrame(pIretBuf, cbPop, CtxUdExpected.cs, CtxUdExpected.rip.u,
+ CtxUdExpected.rflags.u32, CtxUdExpected.ss, CtxUdExpected.rsp.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &Ctx, 0);
+ g_usBs3TestStep++;
+
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rsp, &Ctx.ss, pIretBuf);
+ Bs3MemGuardedTestPageFree(pbTest);
+ }
+ Ctx.rflags.u32 &= ~X86_EFL_NT;
+ }
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_iret)(uint8_t bMode)
+{
+ struct
+ {
+ uint8_t abExtraStack[4096]; /**< we've got ~30KB of stack, so 4KB for the trap handlers++ is not a problem. */
+ IRETBUF IRetBuf;
+ uint8_t abGuard[32];
+ } uBuf;
+ size_t cbUnused;
+
+ //if (bMode != BS3_MODE_LM64) return BS3TESTDOMODE_SKIPPED;
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Primary instruction form.
+ */
+ Bs3MemSet(&uBuf, 0xaa, sizeof(uBuf));
+ Bs3MemSet(uBuf.abGuard, 0x88, sizeof(uBuf.abGuard));
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret, 2, &uBuf.IRetBuf, "iret");
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret, 4, &uBuf.IRetBuf, "iretd");
+ else
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret_rexw, 8, &uBuf.IRetBuf, "o64 iret");
+
+ BS3_ASSERT(ASMMemIsAllU8(uBuf.abGuard, sizeof(uBuf.abGuard), 0x88));
+ cbUnused = (uintptr_t)ASMMemFirstMismatchingU8(uBuf.abExtraStack, sizeof(uBuf.abExtraStack) + sizeof(uBuf.IRetBuf), 0xaa)
+ - (uintptr_t)uBuf.abExtraStack;
+ if (cbUnused < 2048)
+ Bs3TestFailedF("cbUnused=%u #%u\n", cbUnused, 1);
+
+ /*
+ * Secondary variation: opsize prefixed.
+ */
+ Bs3MemSet(&uBuf, 0xaa, sizeof(uBuf));
+ Bs3MemSet(uBuf.abGuard, 0x88, sizeof(uBuf.abGuard));
+ if (BS3_MODE_IS_16BIT_CODE(bMode) && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret_opsize, 4, &uBuf.IRetBuf, "o32 iret");
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret_opsize, 2, &uBuf.IRetBuf, "o16 iret");
+ else if (BS3_MODE_IS_64BIT_CODE(bMode))
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret, 4, &uBuf.IRetBuf, "iretd");
+ BS3_ASSERT(ASMMemIsAllU8(uBuf.abGuard, sizeof(uBuf.abGuard), 0x88));
+ cbUnused = (uintptr_t)ASMMemFirstMismatchingU8(uBuf.abExtraStack, sizeof(uBuf.abExtraStack) + sizeof(uBuf.IRetBuf), 0xaa)
+ - (uintptr_t)uBuf.abExtraStack;
+ if (cbUnused < 2048)
+ Bs3TestFailedF("cbUnused=%u #%u\n", cbUnused, 2);
+
+ /*
+ * Third variation: 16-bit in 64-bit mode (truly unlikely)
+ */
+ if (BS3_MODE_IS_64BIT_CODE(bMode))
+ {
+ Bs3MemSet(&uBuf, 0xaa, sizeof(uBuf));
+ Bs3MemSet(uBuf.abGuard, 0x88, sizeof(uBuf.abGuard));
+ bs3CpuBasic2_iret_Worker(bMode, bs3CpuBasic2_iret_opsize, 2, &uBuf.IRetBuf, "o16 iret");
+ BS3_ASSERT(ASMMemIsAllU8(uBuf.abGuard, sizeof(uBuf.abGuard), 0x88));
+ cbUnused = (uintptr_t)ASMMemFirstMismatchingU8(uBuf.abExtraStack, sizeof(uBuf.abExtraStack) + sizeof(uBuf.IRetBuf), 0xaa)
+ - (uintptr_t)uBuf.abExtraStack;
+ if (cbUnused < 2048)
+ Bs3TestFailedF("cbUnused=%u #%u\n", cbUnused, 3);
+ }
+
+ return 0;
+}
+
+
+
+/*********************************************************************************************************************************
+* Non-far JMP & CALL Tests *
+*********************************************************************************************************************************/
+#define PROTO_ALL(a_Template) \
+ FNBS3FAR a_Template ## _c16, \
+ a_Template ## _c32, \
+ a_Template ## _c64
+PROTO_ALL(bs3CpuBasic2_jmp_jb__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jb_back__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jv__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jv_back__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_ind_mem__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_ind_xAX__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_ind_xDI__ud2);
+FNBS3FAR bs3CpuBasic2_jmp_ind_r9__ud2_c64;
+PROTO_ALL(bs3CpuBasic2_call_jv__ud2);
+PROTO_ALL(bs3CpuBasic2_call_jv_back__ud2);
+PROTO_ALL(bs3CpuBasic2_call_ind_mem__ud2);
+PROTO_ALL(bs3CpuBasic2_call_ind_xAX__ud2);
+PROTO_ALL(bs3CpuBasic2_call_ind_xDI__ud2);
+FNBS3FAR bs3CpuBasic2_call_ind_r9__ud2_c64;
+
+PROTO_ALL(bs3CpuBasic2_jmp_opsize_begin);
+PROTO_ALL(bs3CpuBasic2_jmp_jb_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jb_opsize_back__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jv_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_jv_opsize_back__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_ind_mem_opsize__ud2);
+FNBS3FAR bs3CpuBasic2_jmp_ind_mem_opsize__ud2__intel_c64;
+PROTO_ALL(bs3CpuBasic2_jmp_ind_xAX_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_call_jv_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_call_jv_opsize_back__ud2);
+PROTO_ALL(bs3CpuBasic2_call_ind_mem_opsize__ud2);
+FNBS3FAR bs3CpuBasic2_call_ind_mem_opsize__ud2__intel_c64;
+PROTO_ALL(bs3CpuBasic2_call_ind_xAX_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_jmp_opsize_end);
+#undef PROTO_ALL
+
+FNBS3FAR bs3CpuBasic2_jmptext16_start;
+
+FNBS3FAR bs3CpuBasic2_jmp_target_wrap_forward;
+FNBS3FAR bs3CpuBasic2_jmp_jb_wrap_forward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jb_opsize_wrap_forward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jv16_wrap_forward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jv16_opsize_wrap_forward__ud2;
+FNBS3FAR bs3CpuBasic2_call_jv16_wrap_forward__ud2;
+FNBS3FAR bs3CpuBasic2_call_jv16_opsize_wrap_forward__ud2;
+
+FNBS3FAR bs3CpuBasic2_jmp_target_wrap_backward;
+FNBS3FAR bs3CpuBasic2_jmp_jb_wrap_backward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jb_opsize_wrap_backward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jv16_wrap_backward__ud2;
+FNBS3FAR bs3CpuBasic2_jmp_jv16_opsize_wrap_backward__ud2;
+FNBS3FAR bs3CpuBasic2_call_jv16_wrap_backward__ud2;
+FNBS3FAR bs3CpuBasic2_call_jv16_opsize_wrap_backward__ud2;
+
+
+
+/**
+ * Entrypoint for non-far JMP & CALL tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ *
+ * @note When testing v8086 code, we'll be running in v8086 mode. So, careful
+ * with control registers and such.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_jmp_call)(uint8_t bMode)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxExpected;
+ unsigned iTest;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxExpected, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Create a context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 768);
+ Bs3MemCpy(&CtxExpected, &Ctx, sizeof(CtxExpected));
+
+ /*
+ * 16-bit tests.
+ *
+ * When opsize is 16-bit relative jumps will do 16-bit calculations and
+ * modify IP. This means that it is not possible to trigger a segment
+ * limit #GP(0) when the limit is set to 0xffff.
+ */
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ static struct
+ {
+ int8_t iWrap;
+ bool fOpSizePfx;
+ int8_t iGprIndirect;
+ bool fCall;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { 0, false, -1, false, bs3CpuBasic2_jmp_jb__ud2_c16, },
+ { 0, false, -1, false, bs3CpuBasic2_jmp_jb_back__ud2_c16, },
+ { 0, true, -1, false, bs3CpuBasic2_jmp_jb_opsize__ud2_c16, },
+ { 0, true, -1, false, bs3CpuBasic2_jmp_jb_opsize_back__ud2_c16, },
+ { 0, false, -1, false, bs3CpuBasic2_jmp_jv__ud2_c16, },
+ { 0, false, -1, false, bs3CpuBasic2_jmp_jv_back__ud2_c16, },
+ { 0, true, -1, false, bs3CpuBasic2_jmp_jv_opsize__ud2_c16, },
+ { 0, true, -1, false, bs3CpuBasic2_jmp_jv_opsize_back__ud2_c16, },
+ { 0, false, -1, false, bs3CpuBasic2_jmp_ind_mem__ud2_c16, },
+ { 0, true, -1, false, bs3CpuBasic2_jmp_ind_mem_opsize__ud2_c16, },
+ { 0, false, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX__ud2_c16, },
+ { 0, false, X86_GREG_xDI, false, bs3CpuBasic2_jmp_ind_xDI__ud2_c16, },
+ { 0, true, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX_opsize__ud2_c16, },
+ { 0, false, -1, true, bs3CpuBasic2_call_jv__ud2_c16, },
+ { 0, false, -1, true, bs3CpuBasic2_call_jv_back__ud2_c16, },
+ { 0, true, -1, true, bs3CpuBasic2_call_jv_opsize__ud2_c16, },
+ { 0, true, -1, true, bs3CpuBasic2_call_jv_opsize_back__ud2_c16, },
+ { 0, false, -1, true, bs3CpuBasic2_call_ind_mem__ud2_c16, },
+ { 0, true, -1, true, bs3CpuBasic2_call_ind_mem_opsize__ud2_c16, },
+ { 0, false, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX__ud2_c16, },
+ { 0, false, X86_GREG_xDI, true, bs3CpuBasic2_call_ind_xDI__ud2_c16, },
+ { 0, true, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX_opsize__ud2_c16, },
+
+ { -1, false, -1, false, bs3CpuBasic2_jmp_jb_wrap_backward__ud2, },
+ { +1, false, -1, false, bs3CpuBasic2_jmp_jb_wrap_forward__ud2, },
+ { -1, true, -1, false, bs3CpuBasic2_jmp_jb_opsize_wrap_backward__ud2, },
+ { +1, true, -1, false, bs3CpuBasic2_jmp_jb_opsize_wrap_forward__ud2, },
+
+ { -1, false, -1, false, bs3CpuBasic2_jmp_jv16_wrap_backward__ud2, },
+ { +1, false, -1, false, bs3CpuBasic2_jmp_jv16_wrap_forward__ud2, },
+ { -1, true, -1, false, bs3CpuBasic2_jmp_jv16_opsize_wrap_backward__ud2, },
+ { +1, true, -1, false, bs3CpuBasic2_jmp_jv16_opsize_wrap_forward__ud2, },
+ { -1, false, -1, true, bs3CpuBasic2_call_jv16_wrap_backward__ud2, },
+ { +1, false, -1, true, bs3CpuBasic2_call_jv16_wrap_forward__ud2, },
+ { -1, true, -1, true, bs3CpuBasic2_call_jv16_opsize_wrap_backward__ud2, },
+ { +1, true, -1, true, bs3CpuBasic2_call_jv16_opsize_wrap_forward__ud2, },
+ };
+
+ if (!BS3_MODE_IS_RM_OR_V86(bMode))
+ Bs3SelSetup16BitCode(&Bs3GdteSpare03, Bs3SelLnkPtrToFlat(bs3CpuBasic2_jmptext16_start), 0);
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint64_t uGprSaved;
+ if (s_aTests[iTest].iWrap == 0)
+ {
+ uint8_t const BS3_FAR *fpbCode;
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+ fpbCode = (uint8_t const BS3_FAR *)BS3_FP_MAKE(Ctx.cs, Ctx.rip.u16);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ }
+ else
+ {
+ if (BS3_MODE_IS_RM_OR_V86(bMode))
+ Ctx.cs = BS3_FP_SEG(s_aTests[iTest].pfnTest);
+ else
+ Ctx.cs = BS3_SEL_SPARE_03;
+ Ctx.rip.u = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ if (s_aTests[iTest].fOpSizePfx)
+ CtxExpected.rip.u = Ctx.rip.u;
+ else if (s_aTests[iTest].iWrap < 0)
+ CtxExpected.rip.u = BS3_FP_OFF(bs3CpuBasic2_jmp_target_wrap_backward);
+ else
+ CtxExpected.rip.u = BS3_FP_OFF(bs3CpuBasic2_jmp_target_wrap_forward);
+ }
+ CtxExpected.cs = Ctx.cs;
+ if (s_aTests[iTest].iGprIndirect >= 0)
+ {
+ uGprSaved = (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u;
+ (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u
+ = (&CtxExpected.rax)[s_aTests[iTest].iGprIndirect].u = CtxExpected.rip.u;
+ }
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall && (s_aTests[iTest].iWrap == 0 || !s_aTests[iTest].fOpSizePfx))
+ CtxExpected.rsp.u -= s_aTests[iTest].fOpSizePfx ? 4 : 2;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aTests[iTest].iWrap == 0 || !s_aTests[iTest].fOpSizePfx)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, 0);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(0);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aTests[iTest].iWrap == 0 || !s_aTests[iTest].fOpSizePfx)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, 0);
+ bs3CpuBasic2_CheckDr6InitVal();
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+
+ if (s_aTests[iTest].iGprIndirect >= 0)
+ (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u = (&CtxExpected.rax)[s_aTests[iTest].iGprIndirect].u = uGprSaved;
+ }
+
+ /* Limit the wraparound CS segment to exclude bs3CpuBasic2_jmp_target_wrap_backward
+ and run the backward wrapping tests. */
+ if (!BS3_MODE_IS_RM_OR_V86(bMode))
+ {
+ Bs3GdteSpare03.Gen.u16LimitLow = BS3_FP_OFF(bs3CpuBasic2_jmp_target_wrap_backward) - 1;
+ CtxExpected.cs = Ctx.cs = BS3_SEL_SPARE_03;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].iWrap < 0)
+ {
+ CtxExpected.rip.u = Ctx.rip.u = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 v1\n", Ctx.cs, Ctx.rip.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, 0);
+ g_usBs3TestStep++;
+ }
+
+ /* Do another round where we put the limit in the middle of the UD2
+ instruction we're jumping to: */
+ Bs3GdteSpare03.Gen.u16LimitLow = BS3_FP_OFF(bs3CpuBasic2_jmp_target_wrap_backward);
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].iWrap < 0)
+ {
+ Ctx.rip.u = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ if (s_aTests[iTest].fOpSizePfx)
+ CtxExpected.rip.u = Ctx.rip.u;
+ else
+ CtxExpected.rip.u = BS3_FP_OFF(bs3CpuBasic2_jmp_target_wrap_backward);
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall && (s_aTests[iTest].iWrap == 0 || !s_aTests[iTest].fOpSizePfx))
+ CtxExpected.rsp.u -= s_aTests[iTest].fOpSizePfx ? 4 : 2;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 v2\n", Ctx.cs, Ctx.rip.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, 0);
+ g_usBs3TestStep++;
+ }
+ }
+
+ }
+ /*
+ * 32-bit & 64-bit tests.
+ *
+ * When the opsize prefix is applied here, IP is updated and bits 63:16
+ * cleared. However in 64-bit mode, Intel ignores the opsize prefix
+ * whereas AMD doesn't and it works like you expect.
+ */
+ else
+ {
+ static struct
+ {
+ uint8_t cBits;
+ bool fOpSizePfx;
+ bool fIgnPfx;
+ int8_t iGprIndirect;
+ bool fCall;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { 32, false, false, -1, false, bs3CpuBasic2_jmp_jb__ud2_c32, },
+ { 32, false, false, -1, false, bs3CpuBasic2_jmp_jb_back__ud2_c32, },
+ { 32, true, false, -1, false, bs3CpuBasic2_jmp_jb_opsize__ud2_c32, },
+ { 32, true, false, -1, false, bs3CpuBasic2_jmp_jb_opsize_back__ud2_c32, },
+ { 32, false, false, -1, false, bs3CpuBasic2_jmp_jv__ud2_c32, },
+ { 32, false, false, -1, false, bs3CpuBasic2_jmp_jv_back__ud2_c32, },
+ { 32, true, false, -1, false, bs3CpuBasic2_jmp_jv_opsize__ud2_c32, },
+ { 32, true, false, -1, false, bs3CpuBasic2_jmp_jv_opsize_back__ud2_c32, },
+ { 32, false, false, -1, false, bs3CpuBasic2_jmp_ind_mem__ud2_c32, },
+ { 32, true, false, -1, false, bs3CpuBasic2_jmp_ind_mem_opsize__ud2_c32, },
+ { 32, false, false, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX__ud2_c32, },
+ { 32, false, false, X86_GREG_xDI, false, bs3CpuBasic2_jmp_ind_xDI__ud2_c32, },
+ { 32, true, false, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX_opsize__ud2_c32, },
+ { 32, false, false, -1, true, bs3CpuBasic2_call_jv__ud2_c32, },
+ { 32, false, false, -1, true, bs3CpuBasic2_call_jv_back__ud2_c32, },
+ { 32, true, false, -1, true, bs3CpuBasic2_call_jv_opsize__ud2_c32, },
+ { 32, true, false, -1, true, bs3CpuBasic2_call_jv_opsize_back__ud2_c32, },
+ { 32, false, false, -1, true, bs3CpuBasic2_call_ind_mem__ud2_c32, },
+ { 32, true, false, -1, true, bs3CpuBasic2_call_ind_mem_opsize__ud2_c32, },
+ { 32, false, false, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX__ud2_c32, },
+ { 32, false, false, X86_GREG_xDI, true, bs3CpuBasic2_call_ind_xDI__ud2_c32, },
+ { 32, true, false, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX_opsize__ud2_c32, },
+ /* 64bit/Intel: Use the _c64 tests, which are written to ignore the o16 prefix. */
+ { 64, false, true, -1, false, bs3CpuBasic2_jmp_jb__ud2_c64, },
+ { 64, false, true, -1, false, bs3CpuBasic2_jmp_jb_back__ud2_c64, },
+ { 64, true, true, -1, false, bs3CpuBasic2_jmp_jb_opsize__ud2_c64, },
+ { 64, true, true, -1, false, bs3CpuBasic2_jmp_jb_opsize_back__ud2_c64, },
+ { 64, false, true, -1, false, bs3CpuBasic2_jmp_jv__ud2_c64, },
+ { 64, false, true, -1, false, bs3CpuBasic2_jmp_jv_back__ud2_c64, },
+ { 64, true, true, -1, false, bs3CpuBasic2_jmp_jv_opsize__ud2_c64, },
+ { 64, true, true, -1, false, bs3CpuBasic2_jmp_jv_opsize_back__ud2_c64, },
+ { 64, false, true, -1, false, bs3CpuBasic2_jmp_ind_mem__ud2_c64, },
+ { 64, true, true, -1, false, bs3CpuBasic2_jmp_ind_mem_opsize__ud2__intel_c64, },
+ { 64, false, true, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX__ud2_c64, },
+ { 64, false, true, X86_GREG_xDI, false, bs3CpuBasic2_jmp_ind_xDI__ud2_c64, },
+ { 64, false, true, X86_GREG_x9, false, bs3CpuBasic2_jmp_ind_r9__ud2_c64, },
+ { 64, true, true, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX_opsize__ud2_c64, }, /* no intel version needed */
+ { 64, false, true, -1, true, bs3CpuBasic2_call_jv__ud2_c64, },
+ { 64, false, true, -1, true, bs3CpuBasic2_call_jv_back__ud2_c64, },
+ { 64, true, true, -1, true, bs3CpuBasic2_call_jv_opsize__ud2_c64, },
+ { 64, true, true, -1, true, bs3CpuBasic2_call_jv_opsize_back__ud2_c64, },
+ { 64, false, true, -1, true, bs3CpuBasic2_call_ind_mem__ud2_c64, },
+ { 64, true, true, -1, true, bs3CpuBasic2_call_ind_mem_opsize__ud2__intel_c64,},
+ { 64, false, true, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX__ud2_c64, },
+ { 64, false, true, X86_GREG_xDI, true, bs3CpuBasic2_call_ind_xDI__ud2_c64, },
+ { 64, false, true, X86_GREG_x9, true, bs3CpuBasic2_call_ind_r9__ud2_c64, },
+ { 64, true, true, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX_opsize__ud2_c64, }, /* no intel version needed */
+ /* 64bit/AMD: Use the _c32 tests. */
+ { 64, false, false, -1, false, bs3CpuBasic2_jmp_jb__ud2_c32, },
+ { 64, false, false, -1, false, bs3CpuBasic2_jmp_jb_back__ud2_c32, },
+ { 64, true, false, -1, false, bs3CpuBasic2_jmp_jb_opsize__ud2_c32, },
+ { 64, true, false, -1, false, bs3CpuBasic2_jmp_jb_opsize_back__ud2_c32, },
+ { 64, false, false, -1, false, bs3CpuBasic2_jmp_jv__ud2_c32, },
+ { 64, false, false, -1, false, bs3CpuBasic2_jmp_jv_back__ud2_c32, },
+ { 64, true, false, -1, false, bs3CpuBasic2_jmp_jv_opsize__ud2_c32, },
+ { 64, true, false, -1, false, bs3CpuBasic2_jmp_jv_opsize_back__ud2_c32, },
+ { 64, false, false, -1, false, bs3CpuBasic2_jmp_ind_mem__ud2_c64, }, /* using c64 here */
+ { 64, true, false, -1, false, bs3CpuBasic2_jmp_ind_mem_opsize__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_xDI, false, bs3CpuBasic2_jmp_ind_xDI__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_x9, false, bs3CpuBasic2_jmp_ind_r9__ud2_c64, }, /* ditto */
+ { 64, true, false, X86_GREG_xAX, false, bs3CpuBasic2_jmp_ind_xAX_opsize__ud2_c64, }, /* ditto */
+ { 64, false, false, -1, true, bs3CpuBasic2_call_jv__ud2_c32, }, /* using c32 again */
+ { 64, false, false, -1, true, bs3CpuBasic2_call_jv_back__ud2_c32, },
+ { 64, true, false, -1, true, bs3CpuBasic2_call_jv_opsize__ud2_c32, },
+ { 64, true, false, -1, true, bs3CpuBasic2_call_jv_opsize_back__ud2_c32, },
+ { 64, false, false, -1, true, bs3CpuBasic2_call_ind_mem__ud2_c64, }, /* using c64 here */
+ { 64, true, false, -1, true, bs3CpuBasic2_call_ind_mem_opsize__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_xDI, true, bs3CpuBasic2_call_ind_xDI__ud2_c64, }, /* ditto */
+ { 64, false, false, X86_GREG_x9, true, bs3CpuBasic2_call_ind_r9__ud2_c64, }, /* ditto */
+ { 64, true, false, X86_GREG_xAX, true, bs3CpuBasic2_call_ind_xAX_opsize__ud2_c64, }, /* ditto */
+ };
+ uint8_t const cBits = BS3_MODE_IS_64BIT_CODE(bMode) ? 64 : 32;
+ BS3CPUVENDOR const enmCpuVendor = Bs3GetCpuVendor();
+ bool const fIgnPfx = cBits == 64 && enmCpuVendor == BS3CPUVENDOR_INTEL; /** @todo what does VIA do? */
+
+ /* Prepare a copy of the UD2 instructions in low memory for opsize prefixed tests. */
+ uint16_t const offLow = BS3_FP_OFF(bs3CpuBasic2_jmp_opsize_begin_c32);
+ uint16_t const cbLow = BS3_FP_OFF(bs3CpuBasic2_jmp_opsize_end_c64) - offLow;
+ uint8_t BS3_FAR * const pbCode16 = BS3_MAKE_PROT_R0PTR_FROM_FLAT(BS3_ADDR_BS3TEXT16);
+ uint8_t BS3_FAR * const pbLow = BS3_FP_MAKE(BS3_SEL_TILED_R0, 0);
+ if (offLow < 0x600 || offLow + cbLow >= BS3_ADDR_STACK_R2)
+ Bs3TestFailedF("Opsize overriden jumps are out of place: %#x LB %#x\n", offLow, cbLow);
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ if (!fIgnPfx)
+ {
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].fOpSizePfx && s_aTests[iTest].cBits == cBits && s_aTests[iTest].fIgnPfx == fIgnPfx)
+ {
+ uint16_t const offFn = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ uint16_t const offUd = offFn + (int16_t)(int8_t)pbCode16[offFn - 1];
+ BS3_ASSERT(offUd - offLow + 1 < cbLow);
+ pbCode16[offUd] = 0xf1; /* replace original ud2 with icebp */
+ pbCode16[offUd + 1] = 0xf1;
+ pbLow[offUd] = 0x0f; /* plant ud2 in low memory */
+ pbLow[offUd + 1] = 0x0b;
+ }
+ }
+
+ /* Run the tests. */
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ if (s_aTests[iTest].cBits == cBits && s_aTests[iTest].fIgnPfx == fIgnPfx)
+ {
+ uint64_t uGprSaved;
+ uint8_t const BS3_FAR *fpbCode = Bs3SelLnkPtrToCurPtr(s_aTests[iTest].pfnTest);
+ Ctx.rip.u = Bs3SelLnkPtrToFlat(s_aTests[iTest].pfnTest);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ if (s_aTests[iTest].iGprIndirect >= 0)
+ {
+ uGprSaved = (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u;
+ (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u
+ = (&CtxExpected.rax)[s_aTests[iTest].iGprIndirect].u = CtxExpected.rip.u;
+ }
+ if (s_aTests[iTest].fOpSizePfx && !fIgnPfx)
+ CtxExpected.rip.u &= UINT16_MAX;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall)
+ CtxExpected.rsp.u -= s_aTests[iTest].cBits == 64 ? 8
+ : !s_aTests[iTest].fOpSizePfx ? 4 : 2;
+
+ //Bs3TestPrintf("cs:rip=%04RX16:%08RX64\n", Ctx.cs, Ctx.rip.u);
+
+ if (BS3_MODE_IS_16BIT_SYS(bMode))
+ g_uBs3TrapEipHint = s_aTests[iTest].fOpSizePfx ? 0 : Ctx.rip.u32;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(0);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+
+ if (s_aTests[iTest].iGprIndirect >= 0)
+ (&Ctx.rax)[s_aTests[iTest].iGprIndirect].u = (&CtxExpected.rax)[s_aTests[iTest].iGprIndirect].u = uGprSaved;
+ }
+ }
+
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ }
+
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* FAR JMP & FAR CALL Tests *
+*********************************************************************************************************************************/
+#define PROTO_ALL(a_Template) \
+ FNBS3FAR a_Template ## _c16, \
+ a_Template ## _c32, \
+ a_Template ## _c64
+PROTO_ALL(bs3CpuBasic2_far_jmp_call_opsize_begin);
+
+FNBS3FAR bs3CpuBasic2_jmpf_ptr_rm__ud2_c16;
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_same_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_same_r1__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_same_r2__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_same_r3__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2);
+
+FNBS3FAR bs3CpuBasic2_callf_ptr_rm__ud2_c16;
+PROTO_ALL(bs3CpuBasic2_callf_ptr_same_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_same_r1__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_same_r2__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_same_r3__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_r0_cs64__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_ptr_r0_cs16l__ud2);
+
+FNBS3FAR bs3CpuBasic2_jmpf_mem_rm__ud2_c16;
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_same_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_same_r1__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_same_r2__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_same_r3__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_r0_cs16__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_r0_cs32__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_r0_cs64__ud2);
+PROTO_ALL(bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2);
+
+FNBS3FAR bs3CpuBasic2_jmpf_mem_same_r0__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_same_r1__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_same_r2__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_same_r3__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_r0_cs16__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_r0_cs32__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_r0_cs64__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2_intel_c64;
+
+FNBS3FAR bs3CpuBasic2_callf_mem_rm__ud2_c16;
+PROTO_ALL(bs3CpuBasic2_callf_mem_same_r0__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_same_r1__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_same_r2__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_same_r3__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_r0_cs16__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_r0_cs32__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_r0_cs64__ud2);
+PROTO_ALL(bs3CpuBasic2_callf_mem_r0_cs16l__ud2);
+
+FNBS3FAR bs3CpuBasic2_callf_mem_same_r0__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_same_r1__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_same_r2__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_same_r3__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_r0_cs16__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_r0_cs32__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_r0_cs64__ud2_intel_c64;
+FNBS3FAR bs3CpuBasic2_callf_mem_r0_cs16l__ud2_intel_c64;
+
+PROTO_ALL(bs3CpuBasic2_far_jmp_call_opsize_end);
+#undef PROTO_ALL
+
+
+
+/**
+ * Entrypoint for FAR JMP & FAR CALL tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ *
+ * @note When testing v8086 code, we'll be running in v8086 mode. So, careful
+ * with control registers and such.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_far_jmp_call)(uint8_t bMode)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxExpected;
+ unsigned iTest;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxExpected, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Create a context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 768);
+ Bs3MemCpy(&CtxExpected, &Ctx, sizeof(CtxExpected));
+
+ if (Ctx.rax.u8 == 0 || Ctx.rax.u8 == 0xff) /* for salc & the 64-bit detection */
+ CtxExpected.rax.u8 = Ctx.rax.u8 = 0x42;
+
+ /*
+ * Set up spare selectors.
+ */
+ Bs3GdteSpare00 = Bs3Gdte_CODE16;
+ Bs3GdteSpare00.Gen.u1Long = 1;
+
+ /*
+ * 16-bit tests.
+ */
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fRmOrV86;
+ bool fCall;
+ uint16_t uDstSel;
+ uint8_t uDstBits;
+ bool fOpSizePfx;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { true, false, BS3_SEL_TEXT16, 16, false, bs3CpuBasic2_jmpf_ptr_rm__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_jmpf_ptr_same_r0__ud2_c16, },
+ { false, false, BS3_SEL_R1_CS16 | 1, 16, false, bs3CpuBasic2_jmpf_ptr_same_r1__ud2_c16, },
+ { false, false, BS3_SEL_R2_CS16 | 2, 16, false, bs3CpuBasic2_jmpf_ptr_same_r2__ud2_c16, },
+ { false, false, BS3_SEL_R3_CS16 | 3, 16, false, bs3CpuBasic2_jmpf_ptr_same_r3__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS32, 32, true, bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS64, 64, true, bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2_c16, }, /* 16-bit CS, except in LM. */
+ { false, false, BS3_SEL_SPARE_00, 64, false, bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2_c16, }, /* 16-bit CS, except in LM. */
+
+ { true, true, BS3_SEL_TEXT16, 16, false, bs3CpuBasic2_callf_ptr_rm__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_callf_ptr_same_r0__ud2_c16, },
+ { false, true, BS3_SEL_R1_CS16 | 1, 16, false, bs3CpuBasic2_callf_ptr_same_r1__ud2_c16, },
+ { false, true, BS3_SEL_R2_CS16 | 2, 16, false, bs3CpuBasic2_callf_ptr_same_r2__ud2_c16, },
+ { false, true, BS3_SEL_R3_CS16 | 3, 16, false, bs3CpuBasic2_callf_ptr_same_r3__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS32, 32, true, bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS64, 64, true, bs3CpuBasic2_callf_ptr_r0_cs64__ud2_c16, }, /* 16-bit CS, except in LM. */
+ { false, true, BS3_SEL_SPARE_00, 64, false, bs3CpuBasic2_callf_ptr_r0_cs16l__ud2_c16, }, /* 16-bit CS, except in LM. */
+
+ { true, false, BS3_SEL_TEXT16, 16, false, bs3CpuBasic2_jmpf_mem_rm__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_jmpf_mem_same_r0__ud2_c16, },
+ { false, false, BS3_SEL_R1_CS16 | 1, 16, false, bs3CpuBasic2_jmpf_mem_same_r1__ud2_c16, },
+ { false, false, BS3_SEL_R2_CS16 | 2, 16, false, bs3CpuBasic2_jmpf_mem_same_r2__ud2_c16, },
+ { false, false, BS3_SEL_R3_CS16 | 3, 16, false, bs3CpuBasic2_jmpf_mem_same_r3__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_jmpf_mem_r0_cs16__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS32, 32, true, bs3CpuBasic2_jmpf_mem_r0_cs32__ud2_c16, },
+ { false, false, BS3_SEL_R0_CS64, 64, true, bs3CpuBasic2_jmpf_mem_r0_cs64__ud2_c16, }, /* 16-bit CS, except in LM. */
+ { false, false, BS3_SEL_SPARE_00, 64, false, bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2_c16, }, /* 16-bit CS, except in LM. */
+
+ { true, true, BS3_SEL_TEXT16, 16, false, bs3CpuBasic2_callf_mem_rm__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_callf_mem_same_r0__ud2_c16, },
+ { false, true, BS3_SEL_R1_CS16 | 1, 16, false, bs3CpuBasic2_callf_mem_same_r1__ud2_c16, },
+ { false, true, BS3_SEL_R2_CS16 | 2, 16, false, bs3CpuBasic2_callf_mem_same_r2__ud2_c16, },
+ { false, true, BS3_SEL_R3_CS16 | 3, 16, false, bs3CpuBasic2_callf_mem_same_r3__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS16, 16, false, bs3CpuBasic2_callf_mem_r0_cs16__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS32, 32, true, bs3CpuBasic2_callf_mem_r0_cs32__ud2_c16, },
+ { false, true, BS3_SEL_R0_CS64, 64, true, bs3CpuBasic2_callf_mem_r0_cs64__ud2_c16, }, /* 16-bit CS, except in LM. */
+ { false, true, BS3_SEL_SPARE_00, 64, false, bs3CpuBasic2_callf_mem_r0_cs16l__ud2_c16, }, /* 16-bit CS, except in LM. */
+ };
+ bool const fRmOrV86 = BS3_MODE_IS_RM_OR_V86(bMode);
+
+ /* Prepare a copy of the SALC & UD2 instructions in low memory for opsize
+ prefixed tests jumping to BS3_SEL_SPARE_00 when in 64-bit mode, because
+ it'll be a 64-bit CS then with base=0 instead of a CS16 with base=0x10000. */
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ uint16_t const offLow = BS3_FP_OFF(bs3CpuBasic2_far_jmp_call_opsize_begin_c16);
+ uint16_t const cbLow = BS3_FP_OFF(bs3CpuBasic2_far_jmp_call_opsize_end_c16) - offLow;
+ uint8_t BS3_FAR * const pbLow = BS3_FP_MAKE(BS3_SEL_TILED_R0, 0);
+ uint8_t BS3_FAR * const pbCode16 = BS3_MAKE_PROT_R0PTR_FROM_FLAT(BS3_ADDR_BS3TEXT16);
+ if (offLow < 0x600 || offLow + cbLow >= BS3_ADDR_STACK_R2)
+ Bs3TestFailedF("Opsize overriden jumps/calls are out of place: %#x LB %#x\n", offLow, cbLow);
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].uDstSel == BS3_SEL_SPARE_00 && s_aTests[iTest].uDstBits == 64)
+ {
+ uint16_t const offFn = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ uint16_t const offUd = offFn + (int16_t)(int8_t)pbCode16[offFn - 1];
+ BS3_ASSERT(offUd - offLow + 1 < cbLow);
+ pbLow[offUd - 1] = 0xd6; /* plant salc + ud2 in low memory */
+ pbLow[offUd] = 0x0f;
+ pbLow[offUd + 1] = 0x0b;
+ }
+ }
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].fRmOrV86 == fRmOrV86)
+ {
+ uint64_t const uSavedRsp = Ctx.rsp.u;
+ bool const fGp = (s_aTests[iTest].uDstSel & X86_SEL_RPL) != 0;
+ uint8_t const BS3_FAR *fpbCode;
+
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+ fpbCode = (uint8_t const BS3_FAR *)BS3_FP_MAKE(Ctx.cs, Ctx.rip.u16);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ if ( s_aTests[iTest].uDstBits == 32
+ || ( s_aTests[iTest].uDstBits == 64
+ && !BS3_MODE_IS_16BIT_SYS(bMode)
+ && s_aTests[iTest].uDstSel != BS3_SEL_SPARE_00))
+ CtxExpected.rip.u += BS3_ADDR_BS3TEXT16;
+ if (s_aTests[iTest].uDstSel == BS3_SEL_SPARE_00 && s_aTests[iTest].uDstBits == 64 && BS3_MODE_IS_64BIT_SYS(bMode))
+ CtxExpected.rip.u &= UINT16_MAX;
+ CtxExpected.cs = s_aTests[iTest].uDstSel;
+ if (fGp)
+ {
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.cs = Ctx.cs;
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall && !fGp)
+ CtxExpected.rsp.u -= s_aTests[iTest].fOpSizePfx ? 8 : 4;
+ if (s_aTests[iTest].uDstBits == 64 && !fGp)
+ {
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ CtxExpected.rip.u -= 1;
+ else
+ CtxExpected.rax.u8 = CtxExpected.rflags.u & X86_EFL_CF ? 0xff : 0x00;
+ }
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!fGp)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ CtxExpected.rax.u = Ctx.rax.u;
+ if (s_aTests[iTest].uDstBits == 64 && !fGp && !BS3_MODE_IS_64BIT_SYS(bMode))
+ CtxExpected.rip.u -= 1;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!fGp)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ bs3CpuBasic2_CheckDr6InitVal();
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+ }
+ }
+ /*
+ * 32-bit tests.
+ */
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fCall;
+ uint16_t uDstSel;
+ uint8_t uDstBits;
+ bool fOpSizePfx;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { false, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_jmpf_ptr_same_r0__ud2_c32, },
+ { false, BS3_SEL_R1_CS32 | 1, 32, false, bs3CpuBasic2_jmpf_ptr_same_r1__ud2_c32, },
+ { false, BS3_SEL_R2_CS32 | 2, 32, false, bs3CpuBasic2_jmpf_ptr_same_r2__ud2_c32, },
+ { false, BS3_SEL_R3_CS32 | 3, 32, false, bs3CpuBasic2_jmpf_ptr_same_r3__ud2_c32, },
+ { false, BS3_SEL_R0_CS16, 16, true, bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2_c32, },
+ { false, BS3_SEL_R0_CS64, 64, false, bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2_c32, }, /* 16-bit CS, except in LM. */
+ { false, BS3_SEL_SPARE_00, 64, true, bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2_c32, }, /* 16-bit CS, except in LM. */
+
+ { true, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_callf_ptr_same_r0__ud2_c32, },
+ { true, BS3_SEL_R1_CS32 | 1, 32, false, bs3CpuBasic2_callf_ptr_same_r1__ud2_c32, },
+ { true, BS3_SEL_R2_CS32 | 2, 32, false, bs3CpuBasic2_callf_ptr_same_r2__ud2_c32, },
+ { true, BS3_SEL_R3_CS32 | 3, 32, false, bs3CpuBasic2_callf_ptr_same_r3__ud2_c32, },
+ { true, BS3_SEL_R0_CS16, 16, true, bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2_c32, },
+ { true, BS3_SEL_R0_CS64, 64, false, bs3CpuBasic2_callf_ptr_r0_cs64__ud2_c32, }, /* 16-bit CS, except in LM. */
+ { true, BS3_SEL_SPARE_00, 64, true, bs3CpuBasic2_callf_ptr_r0_cs16l__ud2_c32, }, /* 16-bit CS, except in LM. */
+
+ { false, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_jmpf_mem_same_r0__ud2_c32, },
+ { false, BS3_SEL_R1_CS32 | 1, 32, false, bs3CpuBasic2_jmpf_mem_same_r1__ud2_c32, },
+ { false, BS3_SEL_R2_CS32 | 2, 32, false, bs3CpuBasic2_jmpf_mem_same_r2__ud2_c32, },
+ { false, BS3_SEL_R3_CS32 | 3, 32, false, bs3CpuBasic2_jmpf_mem_same_r3__ud2_c32, },
+ { false, BS3_SEL_R0_CS16, 16, true, bs3CpuBasic2_jmpf_mem_r0_cs16__ud2_c32, },
+ { false, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_jmpf_mem_r0_cs32__ud2_c32, },
+ { false, BS3_SEL_R0_CS64, 64, false, bs3CpuBasic2_jmpf_mem_r0_cs64__ud2_c32, }, /* 16-bit CS, except in LM. */
+ { false, BS3_SEL_SPARE_00, 64, true, bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2_c32, }, /* 16-bit CS, except in LM. */
+
+ { true, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_callf_mem_same_r0__ud2_c32, },
+ { true, BS3_SEL_R1_CS32 | 1, 32, false, bs3CpuBasic2_callf_mem_same_r1__ud2_c32, },
+ { true, BS3_SEL_R2_CS32 | 2, 32, false, bs3CpuBasic2_callf_mem_same_r2__ud2_c32, },
+ { true, BS3_SEL_R3_CS32 | 3, 32, false, bs3CpuBasic2_callf_mem_same_r3__ud2_c32, },
+ { true, BS3_SEL_R0_CS16, 16, true, bs3CpuBasic2_callf_mem_r0_cs16__ud2_c32, },
+ { true, BS3_SEL_R0_CS32, 32, false, bs3CpuBasic2_callf_mem_r0_cs32__ud2_c32, },
+ { true, BS3_SEL_R0_CS64, 64, false, bs3CpuBasic2_callf_mem_r0_cs64__ud2_c32, }, /* 16-bit CS, except in LM. */
+ { true, BS3_SEL_SPARE_00, 64, true, bs3CpuBasic2_callf_mem_r0_cs16l__ud2_c32, }, /* 16-bit CS, except in LM. */
+ };
+
+ /* Prepare a copy of the SALC & UD2 instructions in low memory for opsize
+ prefixed tests jumping to BS3_SEL_SPARE_00 when in 64-bit mode, because
+ it'll be a 64-bit CS then with base=0 instead of a CS16 with base=0x10000. */
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ uint16_t const offLow = BS3_FP_OFF(bs3CpuBasic2_far_jmp_call_opsize_begin_c32);
+ uint16_t const cbLow = BS3_FP_OFF(bs3CpuBasic2_far_jmp_call_opsize_end_c32) - offLow;
+ uint8_t BS3_FAR * const pbLow = BS3_FP_MAKE(BS3_SEL_TILED_R0, 0);
+ uint8_t BS3_FAR * const pbCode16 = BS3_MAKE_PROT_R0PTR_FROM_FLAT(BS3_ADDR_BS3TEXT16);
+ if (offLow < 0x600 || offLow + cbLow >= BS3_ADDR_STACK_R2)
+ Bs3TestFailedF("Opsize overriden jumps/calls are out of place: %#x LB %#x\n", offLow, cbLow);
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].uDstSel == BS3_SEL_SPARE_00 && s_aTests[iTest].uDstBits == 64)
+ {
+ uint16_t const offFn = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ uint16_t const offUd = offFn + (int16_t)(int8_t)pbCode16[offFn - 1];
+ BS3_ASSERT(offUd - offLow + 1 < cbLow);
+ pbLow[offUd - 1] = 0xd6; /* plant salc + ud2 in low memory */
+ pbLow[offUd] = 0x0f;
+ pbLow[offUd + 1] = 0x0b;
+ }
+ }
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint64_t const uSavedRsp = Ctx.rsp.u;
+ bool const fGp = (s_aTests[iTest].uDstSel & X86_SEL_RPL) != 0;
+ uint8_t const BS3_FAR *fpbCode = Bs3SelLnkPtrToCurPtr(s_aTests[iTest].pfnTest);
+
+ Ctx.rip.u = Bs3SelLnkPtrToFlat(s_aTests[iTest].pfnTest);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ if ( s_aTests[iTest].uDstBits == 16
+ || ( s_aTests[iTest].uDstBits == 64
+ && ( BS3_MODE_IS_16BIT_SYS(bMode))
+ || s_aTests[iTest].uDstSel == BS3_SEL_SPARE_00))
+ CtxExpected.rip.u &= UINT16_MAX;
+ CtxExpected.cs = s_aTests[iTest].uDstSel;
+ if (fGp)
+ {
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.cs = Ctx.cs;
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall && !fGp)
+ CtxExpected.rsp.u -= s_aTests[iTest].fOpSizePfx ? 4 : 8;
+ if (s_aTests[iTest].uDstBits == 64 && !fGp)
+ {
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ CtxExpected.rip.u -= 1;
+ else
+ CtxExpected.rax.u8 = CtxExpected.rflags.u & X86_EFL_CF ? 0xff : 0x00;
+ }
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!fGp)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ CtxExpected.rax.u = Ctx.rax.u;
+ if (s_aTests[iTest].uDstBits == 64 && !fGp && !BS3_MODE_IS_64BIT_SYS(bMode))
+ CtxExpected.rip.u -= 1;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!fGp)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ bs3CpuBasic2_CheckDr6InitVal();
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+ }
+ }
+ /*
+ * 64-bit tests.
+ */
+ else if (BS3_MODE_IS_64BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fInvalid;
+ bool fCall;
+ uint16_t uDstSel;
+ uint8_t uDstBits;
+ uint8_t fOpSizePfx; /**< 0: none, 1: 066h, 2: REX.W, 3: 066h REX.W */
+ int8_t fFix64OpSize;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ /* invalid opcodes: */
+ { true, false, BS3_SEL_R0_CS32, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_same_r0__ud2_c32, },
+ { true, false, BS3_SEL_R1_CS32 | 1, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_same_r1__ud2_c32, },
+ { true, false, BS3_SEL_R2_CS32 | 2, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_same_r2__ud2_c32, },
+ { true, false, BS3_SEL_R3_CS32 | 3, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_same_r3__ud2_c32, },
+ { true, false, BS3_SEL_R0_CS16, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_opsize_flipbit_r0__ud2_c32, },
+ { true, false, BS3_SEL_R0_CS64, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_r0_cs64__ud2_c32, },
+ { true, false, BS3_SEL_SPARE_00, 64, 0, -1, bs3CpuBasic2_jmpf_ptr_r0_cs16l__ud2_c32, },
+
+ { true, true, BS3_SEL_R0_CS32, 64, 0, -1, bs3CpuBasic2_callf_ptr_same_r0__ud2_c32, },
+ { true, true, BS3_SEL_R1_CS32 | 1, 64, 0, -1, bs3CpuBasic2_callf_ptr_same_r1__ud2_c32, },
+ { true, true, BS3_SEL_R2_CS32 | 2, 64, 0, -1, bs3CpuBasic2_callf_ptr_same_r2__ud2_c32, },
+ { true, true, BS3_SEL_R3_CS32 | 3, 64, 0, -1, bs3CpuBasic2_callf_ptr_same_r3__ud2_c32, },
+ { true, true, BS3_SEL_R0_CS16, 64, 0, -1, bs3CpuBasic2_callf_ptr_opsize_flipbit_r0__ud2_c32, },
+ { true, true, BS3_SEL_R0_CS64, 64, 0, -1, bs3CpuBasic2_callf_ptr_r0_cs64__ud2_c32, },
+ { true, true, BS3_SEL_SPARE_00, 64, 0, -1, bs3CpuBasic2_callf_ptr_r0_cs16l__ud2_c32, },
+
+ { false, false, BS3_SEL_R0_CS64, 64, 0, false, bs3CpuBasic2_jmpf_mem_same_r0__ud2_c64, },
+ { false, false, BS3_SEL_R1_CS64 | 1, 64, 0, false, bs3CpuBasic2_jmpf_mem_same_r1__ud2_c64, },
+ { false, false, BS3_SEL_R2_CS64 | 2, 64, 0, false, bs3CpuBasic2_jmpf_mem_same_r2__ud2_c64, },
+ { false, false, BS3_SEL_R3_CS64 | 3, 64, 0, false, bs3CpuBasic2_jmpf_mem_same_r3__ud2_c64, },
+ { false, false, BS3_SEL_R0_CS16, 16, 1, false, bs3CpuBasic2_jmpf_mem_r0_cs16__ud2_c64, },
+ { false, false, BS3_SEL_R0_CS32, 32, 0, false, bs3CpuBasic2_jmpf_mem_r0_cs32__ud2_c64, },
+ { false, false, BS3_SEL_R0_CS64, 64, 0, false, bs3CpuBasic2_jmpf_mem_r0_cs64__ud2_c64, }, /* 16-bit CS, except in LM. */
+ { false, false, BS3_SEL_SPARE_00, 64, 0, false, bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2_c64, }, /* 16-bit CS, except in LM. */
+
+ { false, false, BS3_SEL_R0_CS64, 64, 2, true, bs3CpuBasic2_jmpf_mem_same_r0__ud2_intel_c64, },
+ { false, false, BS3_SEL_R1_CS64 | 1, 64, 2, true, bs3CpuBasic2_jmpf_mem_same_r1__ud2_intel_c64, },
+ { false, false, BS3_SEL_R2_CS64 | 2, 64, 0, true, bs3CpuBasic2_jmpf_mem_same_r2__ud2_intel_c64, },
+ { false, false, BS3_SEL_R3_CS64 | 3, 64, 2, true, bs3CpuBasic2_jmpf_mem_same_r3__ud2_intel_c64, },
+ { false, false, BS3_SEL_R0_CS16, 16, 1, true, bs3CpuBasic2_jmpf_mem_r0_cs16__ud2_intel_c64, },
+ { false, false, BS3_SEL_R0_CS32, 32, 0, true, bs3CpuBasic2_jmpf_mem_r0_cs32__ud2_intel_c64, },
+ { false, false, BS3_SEL_R0_CS64, 64, 2, true, bs3CpuBasic2_jmpf_mem_r0_cs64__ud2_intel_c64, }, /* 16-bit CS, except in LM. */
+ { false, false, BS3_SEL_SPARE_00, 64, 0, true, bs3CpuBasic2_jmpf_mem_r0_cs16l__ud2_intel_c64, }, /* 16-bit CS, except in LM. */
+
+ { false, true, BS3_SEL_R0_CS64, 64, 2, false, bs3CpuBasic2_callf_mem_same_r0__ud2_c64, },
+ { false, true, BS3_SEL_R1_CS64 | 1, 64, 2, false, bs3CpuBasic2_callf_mem_same_r1__ud2_c64, },
+ { false, true, BS3_SEL_R2_CS64 | 2, 64, 0, false, bs3CpuBasic2_callf_mem_same_r2__ud2_c64, },
+ { false, true, BS3_SEL_R3_CS64 | 3, 64, 2, false, bs3CpuBasic2_callf_mem_same_r3__ud2_c64, },
+ { false, true, BS3_SEL_R0_CS16, 16, 1, false, bs3CpuBasic2_callf_mem_r0_cs16__ud2_c64, },
+ { false, true, BS3_SEL_R0_CS32, 32, 2, false, bs3CpuBasic2_callf_mem_r0_cs32__ud2_c64, },
+ { false, true, BS3_SEL_R0_CS64, 64, 0, false, bs3CpuBasic2_callf_mem_r0_cs64__ud2_c64, }, /* 16-bit CS, except in LM. */
+ { false, true, BS3_SEL_SPARE_00, 64, 0, false, bs3CpuBasic2_callf_mem_r0_cs16l__ud2_c64, }, /* 16-bit CS, except in LM. */
+
+ { false, true, BS3_SEL_R0_CS64, 64, 2, true, bs3CpuBasic2_callf_mem_same_r0__ud2_intel_c64, },
+ { false, true, BS3_SEL_R1_CS64 | 1, 64, 2, true, bs3CpuBasic2_callf_mem_same_r1__ud2_intel_c64, },
+ { false, true, BS3_SEL_R2_CS64 | 2, 64, 0, true, bs3CpuBasic2_callf_mem_same_r2__ud2_intel_c64, },
+ { false, true, BS3_SEL_R3_CS64 | 3, 64, 2, true, bs3CpuBasic2_callf_mem_same_r3__ud2_intel_c64, },
+ { false, true, BS3_SEL_R0_CS16, 16, 1, true, bs3CpuBasic2_callf_mem_r0_cs16__ud2_intel_c64, },
+ { false, true, BS3_SEL_R0_CS32, 32, 0, true, bs3CpuBasic2_callf_mem_r0_cs32__ud2_intel_c64, },
+ { false, true, BS3_SEL_R0_CS64, 64, 2, true, bs3CpuBasic2_callf_mem_r0_cs64__ud2_intel_c64, }, /* 16-bit CS, except in LM. */
+ { false, true, BS3_SEL_SPARE_00, 64, 0, true, bs3CpuBasic2_callf_mem_r0_cs16l__ud2_intel_c64, }, /* 16-bit CS, except in LM. */
+ };
+ BS3CPUVENDOR const enmCpuVendor = Bs3GetCpuVendor();
+ bool const fFix64OpSize = enmCpuVendor == BS3CPUVENDOR_INTEL; /** @todo what does VIA do? */
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint64_t const uSavedRsp = Ctx.rsp.u;
+ bool const fUd = s_aTests[iTest].fInvalid;
+ bool const fGp = (s_aTests[iTest].uDstSel & X86_SEL_RPL) != 0;
+ uint8_t const BS3_FAR *fpbCode = Bs3SelLnkPtrToCurPtr(s_aTests[iTest].pfnTest);
+
+ if (s_aTests[iTest].fFix64OpSize != fFix64OpSize && s_aTests[iTest].fFix64OpSize >= 0)
+ continue;
+
+ Ctx.rip.u = Bs3SelLnkPtrToFlat(s_aTests[iTest].pfnTest);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ CtxExpected.cs = s_aTests[iTest].uDstSel;
+ if (s_aTests[iTest].uDstBits == 16)
+ CtxExpected.rip.u &= UINT16_MAX;
+ else if (s_aTests[iTest].uDstBits == 64 && fFix64OpSize && s_aTests[iTest].uDstSel != BS3_SEL_SPARE_00)
+ CtxExpected.rip.u |= UINT64_C(0xfffff00000000000);
+
+ if (fGp || fUd)
+ {
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.cs = Ctx.cs;
+ }
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ if (s_aTests[iTest].fCall && !fGp && !fUd)
+ {
+ CtxExpected.rsp.u -= s_aTests[iTest].fOpSizePfx == 0 ? 8
+ : s_aTests[iTest].fOpSizePfx == 1 ? 4 : 16;
+ //Bs3TestPrintf("cs:rsp=%04RX16:%04RX64 -> %04RX64 (fOpSizePfx=%d)\n", Ctx.ss, Ctx.rsp.u, CtxExpected.rsp.u, s_aTests[iTest].fOpSizePfx);
+ }
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!fGp || fUd)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ CtxExpected.rax.u = Ctx.rax.u;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (fUd)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else if (!fGp)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ {
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aTests[iTest].uDstSel & X86_TRAP_ERR_SEL_MASK);
+ bs3CpuBasic2_CheckDr6InitVal();
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ Ctx.rsp.u = uSavedRsp;
+ g_usBs3TestStep++;
+ }
+ }
+ else
+ Bs3TestFailed("wtf?");
+
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Near RET *
+*********************************************************************************************************************************/
+#define PROTO_ALL(a_Template) \
+ FNBS3FAR a_Template ## _c16, \
+ a_Template ## _c32, \
+ a_Template ## _c64
+PROTO_ALL(bs3CpuBasic2_retn_opsize_begin);
+PROTO_ALL(bs3CpuBasic2_retn__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_i24__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_i24_opsize__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_i760__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_i0__ud2);
+PROTO_ALL(bs3CpuBasic2_retn_i0_opsize__ud2);
+FNBS3FAR bs3CpuBasic2_retn_rexw__ud2_c64;
+FNBS3FAR bs3CpuBasic2_retn_i24_rexw__ud2_c64;
+FNBS3FAR bs3CpuBasic2_retn_opsize_rexw__ud2_c64;
+FNBS3FAR bs3CpuBasic2_retn_rexw_opsize__ud2_c64;
+FNBS3FAR bs3CpuBasic2_retn_i24_opsize_rexw__ud2_c64;
+FNBS3FAR bs3CpuBasic2_retn_i24_rexw_opsize__ud2_c64;
+PROTO_ALL(bs3CpuBasic2_retn_opsize_end);
+#undef PROTO_ALL
+
+
+static void bs3CpuBasic2_retn_PrepStack(BS3PTRUNION StkPtr, PCBS3REGCTX pCtxExpected, uint8_t cbAddr)
+{
+ StkPtr.pu32[3] = UINT32_MAX;
+ StkPtr.pu32[2] = UINT32_MAX;
+ StkPtr.pu32[1] = UINT32_MAX;
+ StkPtr.pu32[0] = UINT32_MAX;
+ StkPtr.pu32[-1] = UINT32_MAX;
+ StkPtr.pu32[-2] = UINT32_MAX;
+ StkPtr.pu32[-3] = UINT32_MAX;
+ StkPtr.pu32[-4] = UINT32_MAX;
+ if (cbAddr == 2)
+ StkPtr.pu16[0] = pCtxExpected->rip.u16;
+ else if (cbAddr == 4)
+ StkPtr.pu32[0] = pCtxExpected->rip.u32;
+ else
+ StkPtr.pu64[0] = pCtxExpected->rip.u64;
+}
+
+
+/**
+ * Entrypoint for NEAR RET tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_near_ret)(uint8_t bMode)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxExpected;
+ unsigned iTest;
+ BS3PTRUNION StkPtr;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxExpected, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ /*
+ * Create a context.
+ *
+ * ASSUMES we're in on the ring-0 stack in ring-0 and using less than 16KB.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 1664);
+ Ctx.rsp.u = BS3_ADDR_STACK - _16K;
+ Bs3MemCpy(&CtxExpected, &Ctx, sizeof(CtxExpected));
+
+ StkPtr.pv = Bs3RegCtxGetRspSsAsCurPtr(&Ctx);
+ //Bs3TestPrintf("Stack=%p rsp=%RX64\n", StkPtr.pv, Ctx.rsp.u);
+
+ /*
+ * 16-bit tests.
+ */
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fOpSizePfx;
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { false, 0, bs3CpuBasic2_retn__ud2_c16, },
+ { true, 0, bs3CpuBasic2_retn_opsize__ud2_c16, },
+ { false, 24, bs3CpuBasic2_retn_i24__ud2_c16, },
+ { true, 24, bs3CpuBasic2_retn_i24_opsize__ud2_c16, },
+ { false, 0, bs3CpuBasic2_retn_i0__ud2_c16, },
+ { true, 0, bs3CpuBasic2_retn_i0_opsize__ud2_c16, },
+ { false,760, bs3CpuBasic2_retn_i760__ud2_c16, },
+ };
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint8_t const BS3_FAR *fpbCode;
+
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+ fpbCode = (uint8_t const BS3_FAR *)BS3_FP_MAKE(Ctx.cs, Ctx.rip.u16);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ CtxExpected.cs = Ctx.cs;
+ if (!s_aTests[iTest].fOpSizePfx)
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 2;
+ else
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 4;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64\n", Ctx.ss, Ctx.rsp.u);
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx ? 4 : 2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx ? 4 : 2);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+ }
+ }
+ /*
+ * 32-bit tests.
+ */
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ static struct
+ {
+ uint8_t cBits;
+ bool fOpSizePfx;
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { 32, false, 0, bs3CpuBasic2_retn__ud2_c32, },
+ { 32, true, 0, bs3CpuBasic2_retn_opsize__ud2_c32, },
+ { 32, false, 24, bs3CpuBasic2_retn_i24__ud2_c32, },
+ { 32, true, 24, bs3CpuBasic2_retn_i24_opsize__ud2_c32, },
+ { 32, false, 0, bs3CpuBasic2_retn_i0__ud2_c32, },
+ { 32, true, 0, bs3CpuBasic2_retn_i0_opsize__ud2_c32, },
+ { 32, false,760, bs3CpuBasic2_retn_i760__ud2_c32, },
+ };
+
+ /* Prepare a copy of the UD2 instructions in low memory for opsize prefixed tests. */
+ uint16_t const offLow = BS3_FP_OFF(bs3CpuBasic2_retn_opsize_begin_c32);
+ uint16_t const cbLow = BS3_FP_OFF(bs3CpuBasic2_retn_opsize_end_c32) - offLow;
+ uint8_t BS3_FAR * const pbLow = BS3_FP_MAKE(BS3_SEL_TILED_R0, 0);
+ uint8_t BS3_FAR * const pbCode16 = BS3_MAKE_PROT_R0PTR_FROM_FLAT(BS3_ADDR_BS3TEXT16);
+ if (offLow < 0x600 || offLow + cbLow >= BS3_ADDR_STACK_R2)
+ Bs3TestFailedF("Opsize overriden jumps/calls are out of place: %#x LB %#x\n", offLow, cbLow);
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].fOpSizePfx)
+ {
+ uint16_t const offFn = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ uint16_t const offUd = offFn + (int16_t)(int8_t)pbCode16[offFn - 1];
+ BS3_ASSERT(offUd - offLow + 1 < cbLow);
+ pbCode16[offUd] = 0xf1; /* replace original ud2 with icebp */
+ pbCode16[offUd + 1] = 0xf1;
+ pbLow[offUd] = 0x0f; /* plant ud2 in low memory */
+ pbLow[offUd + 1] = 0x0b;
+ }
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint8_t const BS3_FAR *fpbCode = Bs3SelLnkPtrToCurPtr(s_aTests[iTest].pfnTest);
+
+ Ctx.rip.u = Bs3SelLnkPtrToFlat(s_aTests[iTest].pfnTest);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ CtxExpected.cs = Ctx.cs;
+ if (!s_aTests[iTest].fOpSizePfx)
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 4;
+ else
+ {
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 2;
+ CtxExpected.rip.u &= UINT16_MAX;
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64\n", Ctx.ss, Ctx.rsp.u);
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx ? 2 : 4);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx ? 2 : 4);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+ }
+ }
+ /*
+ * 64-bit tests.
+ */
+ else if (BS3_MODE_IS_64BIT_CODE(bMode))
+ {
+ static struct
+ {
+ uint8_t cBits;
+ bool fOpSizePfx;
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ }
+ const s_aTests[] =
+ {
+ { 32, false, 0, bs3CpuBasic2_retn__ud2_c64, },
+ { 32, false, 0, bs3CpuBasic2_retn_rexw__ud2_c64, },
+ { 32, true, 0, bs3CpuBasic2_retn_opsize__ud2_c64, },
+ { 32, false, 0, bs3CpuBasic2_retn_opsize_rexw__ud2_c64, },
+ { 32, true, 0, bs3CpuBasic2_retn_rexw_opsize__ud2_c64, },
+ { 32, false, 24, bs3CpuBasic2_retn_i24__ud2_c64, },
+ { 32, false, 24, bs3CpuBasic2_retn_i24_rexw__ud2_c64, },
+ { 32, true, 24, bs3CpuBasic2_retn_i24_opsize__ud2_c64, },
+ { 32, false, 24, bs3CpuBasic2_retn_i24_opsize_rexw__ud2_c64, },
+ { 32, true, 24, bs3CpuBasic2_retn_i24_rexw_opsize__ud2_c64, },
+ { 32, false, 0, bs3CpuBasic2_retn_i0__ud2_c64, },
+ { 32, true, 0, bs3CpuBasic2_retn_i0_opsize__ud2_c64, },
+ { 32, false,760, bs3CpuBasic2_retn_i760__ud2_c64, },
+ };
+ BS3CPUVENDOR const enmCpuVendor = Bs3GetCpuVendor();
+ bool const fFix64OpSize = enmCpuVendor == BS3CPUVENDOR_INTEL; /** @todo what does VIA do? */
+
+ /* Prepare a copy of the UD2 instructions in low memory for opsize prefixed
+ tests, unless we're on intel where the opsize prefix is ignored. Here we
+ just fill low memory with int3's so we can detect non-intel behaviour. */
+ uint16_t const offLow = BS3_FP_OFF(bs3CpuBasic2_retn_opsize_begin_c64);
+ uint16_t const cbLow = BS3_FP_OFF(bs3CpuBasic2_retn_opsize_end_c64) - offLow;
+ uint8_t BS3_FAR * const pbLow = BS3_FP_MAKE(BS3_SEL_TILED_R0, 0);
+ uint8_t BS3_FAR * const pbCode16 = BS3_MAKE_PROT_R0PTR_FROM_FLAT(BS3_ADDR_BS3TEXT16);
+ if (offLow < 0x600 || offLow + cbLow >= BS3_ADDR_STACK_R2)
+ Bs3TestFailedF("Opsize overriden jumps/calls are out of place: %#x LB %#x\n", offLow, cbLow);
+ Bs3MemSet(&pbLow[offLow], 0xcc /*int3*/, cbLow);
+ if (!fFix64OpSize)
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ if (s_aTests[iTest].fOpSizePfx)
+ {
+ uint16_t const offFn = BS3_FP_OFF(s_aTests[iTest].pfnTest);
+ uint16_t const offUd = offFn + (int16_t)(int8_t)pbCode16[offFn - 1];
+ BS3_ASSERT(offUd - offLow + 1 < cbLow);
+ pbCode16[offUd] = 0xf1; /* replace original ud2 with icebp */
+ pbCode16[offUd + 1] = 0xf1;
+ pbLow[offUd] = 0x0f; /* plant ud2 in low memory */
+ pbLow[offUd + 1] = 0x0b;
+ }
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ uint8_t const BS3_FAR *fpbCode = Bs3SelLnkPtrToCurPtr(s_aTests[iTest].pfnTest);
+
+ Ctx.rip.u = Bs3SelLnkPtrToFlat(s_aTests[iTest].pfnTest);
+ CtxExpected.rip.u = Ctx.rip.u + (int64_t)(int8_t)fpbCode[-1];
+ CtxExpected.cs = Ctx.cs;
+ if (!s_aTests[iTest].fOpSizePfx || fFix64OpSize)
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 8;
+ else
+ {
+ CtxExpected.rsp.u = Ctx.rsp.u + s_aTests[iTest].cbImm + 2;
+ CtxExpected.rip.u &= UINT16_MAX;
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64\n", Ctx.ss, Ctx.rsp.u);
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx && !fFix64OpSize ? 2 : 8);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ bs3CpuBasic2_retn_PrepStack(StkPtr, &CtxExpected, s_aTests[iTest].fOpSizePfx && !fFix64OpSize ? 2 : 8);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+ }
+ }
+ else
+ Bs3TestFailed("wtf?");
+
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Far RET *
+*********************************************************************************************************************************/
+#define PROTO_ALL(a_Template) \
+ FNBS3FAR a_Template ## _c16, \
+ a_Template ## _c32, \
+ a_Template ## _c64
+PROTO_ALL(bs3CpuBasic2_retf);
+PROTO_ALL(bs3CpuBasic2_retf_opsize);
+FNBS3FAR bs3CpuBasic2_retf_rexw_c64;
+FNBS3FAR bs3CpuBasic2_retf_rexw_opsize_c64;
+FNBS3FAR bs3CpuBasic2_retf_opsize_rexw_c64;
+PROTO_ALL(bs3CpuBasic2_retf_i32);
+PROTO_ALL(bs3CpuBasic2_retf_i32_opsize);
+FNBS3FAR bs3CpuBasic2_retf_i24_rexw_c64;
+FNBS3FAR bs3CpuBasic2_retf_i24_rexw_opsize_c64;
+FNBS3FAR bs3CpuBasic2_retf_i24_opsize_rexw_c64;
+PROTO_ALL(bs3CpuBasic2_retf_i888);
+#undef PROTO_ALL
+
+
+static void bs3CpuBasic2_retf_PrepStack(BS3PTRUNION StkPtr, uint8_t cbStkItem, RTSEL uRetCs, uint64_t uRetRip,
+ bool fWithStack, uint16_t cbImm, RTSEL uRetSs, uint64_t uRetRsp)
+{
+ Bs3MemSet(&StkPtr.pu32[-4], 0xff, 96);
+ if (cbStkItem == 2)
+ {
+ StkPtr.pu16[0] = (uint16_t)uRetRip;
+ StkPtr.pu16[1] = uRetCs;
+ if (fWithStack)
+ {
+ StkPtr.pb += cbImm;
+ StkPtr.pu16[2] = (uint16_t)uRetRsp;
+ StkPtr.pu16[3] = uRetSs;
+ }
+ }
+ else if (cbStkItem == 4)
+ {
+ StkPtr.pu32[0] = (uint32_t)uRetRip;
+ StkPtr.pu16[2] = uRetCs;
+ if (fWithStack)
+ {
+ StkPtr.pb += cbImm;
+ StkPtr.pu32[2] = (uint32_t)uRetRsp;
+ StkPtr.pu16[6] = uRetSs;
+ }
+ }
+ else
+ {
+ StkPtr.pu64[0] = uRetRip;
+ StkPtr.pu16[4] = uRetCs;
+ if (fWithStack)
+ {
+ StkPtr.pb += cbImm;
+ StkPtr.pu64[2] = uRetRsp;
+ StkPtr.pu16[12] = uRetSs;
+ }
+ }
+}
+
+
+/**
+ * Entrypoint for FAR RET tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_far_ret)(uint8_t bMode)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX Ctx2;
+ BS3REGCTX CtxExpected;
+ unsigned iTest;
+ unsigned iSubTest;
+ BS3PTRUNION StkPtr;
+
+#define LOW_UD_ADDR 0x0609
+ uint8_t BS3_FAR * const pbLowUd = BS3_FP_MAKE(BS3_FP_SEG(&StkPtr), LOW_UD_ADDR);
+#define LOW_SALC_UD_ADDR 0x0611
+ uint8_t BS3_FAR * const pbLowSalcUd = BS3_FP_MAKE(BS3_FP_SEG(&StkPtr), LOW_SALC_UD_ADDR);
+#define LOW_SWAPGS_ADDR 0x061d
+ uint8_t BS3_FAR * const pbLowSwapGs = BS3_FP_MAKE(BS3_FP_SEG(&StkPtr), LOW_SWAPGS_ADDR);
+#define BS3TEXT16_ADDR_HI (BS3_ADDR_BS3TEXT16 >> 16)
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&Ctx2, sizeof(Ctx2));
+ Bs3MemZero(&CtxExpected, sizeof(CtxExpected));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ //if (!BS3_MODE_IS_64BIT_SYS(bMode) && bMode != BS3_MODE_PP32_16) return 0xff;
+ //if (bMode != BS3_MODE_PE32_16) return 0xff;
+
+ /*
+ * When dealing retf with 16-bit effective operand size to 32-bit or 64-bit
+ * code, we're restricted to a 16-bit address. So, we plant a UD
+ * instruction below 64KB that we can target with flat 32/64 code segments.
+ * (Putting it on the stack would be possible too, but we'd have to create
+ * the sub-test tables dynamically, which isn't necessary.)
+ */
+ Bs3MemSet(&pbLowUd[-9], 0xcc, 32);
+ Bs3MemSet(&pbLowSalcUd[-9], 0xcc, 32);
+ Bs3MemSet(&pbLowSwapGs[-9], 0xcc, 32);
+
+ pbLowUd[0] = 0x0f; /* ud2 */
+ pbLowUd[1] = 0x0b;
+
+ /* A variation to detect whether we're in 64-bit or 16-bit mode when
+ executing the code. */
+ pbLowSalcUd[0] = 0xd6; /* salc */
+ pbLowSalcUd[1] = 0x0f; /* ud2 */
+ pbLowSalcUd[2] = 0x0b;
+
+ /* A variation to check that we're not in 64-bit mode. */
+ pbLowSwapGs[0] = 0x0f; /* swapgs */
+ pbLowSwapGs[1] = 0x01;
+ pbLowSwapGs[2] = 0xf8;
+
+ /*
+ * Use separate stacks for all relevant CPU exceptions so we can put
+ * garbage in unused RSP bits w/o needing to care about where a long mode
+ * handler will end up when accessing the whole RSP. (Not an issue with
+ * 16-bit and 32-bit protected mode kernels, as here the weird SS based
+ * stack pointer handling is in effect and the exception handler code
+ * will just continue using the same SS and same portion of RSP.)
+ *
+ * See r154660.
+ */
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ Bs3Trap64InitEx(true);
+
+ /*
+ * Create some call gates and whatnot for the UD2 code using the spare selectors.
+ */
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ for (iTest = 0; iTest < 16; iTest++)
+ Bs3SelSetupGate64(&Bs3GdteSpare00 + iTest * 2, iTest /*bType*/, 3 /*bDpl*/,
+ BS3_SEL_R0_CS64, BS3_FP_OFF(bs3CpuBasic2_ud2) + BS3_ADDR_BS3TEXT16);
+ else
+ {
+ for (iTest = 0; iTest < 16; iTest++)
+ {
+ Bs3SelSetupGate(&Bs3GdteSpare00 + iTest, iTest /*bType*/, 3 /*bDpl*/,
+ BS3_SEL_R0_CS16, BS3_FP_OFF(bs3CpuBasic2_ud2), 0);
+ Bs3SelSetupGate(&Bs3GdteSpare00 + iTest + 16, iTest /*bType*/, 3 /*bDpl*/,
+ BS3_SEL_R0_CS32, BS3_FP_OFF(bs3CpuBasic2_ud2) + BS3_ADDR_BS3TEXT16, 0);
+ }
+ }
+
+ /*
+ * Create a context.
+ *
+ * ASSUMES we're in on the ring-0 stack in ring-0 and using less than 16KB.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 1728);
+ Ctx.rsp.u = BS3_ADDR_STACK - _16K;
+ Bs3MemCpy(&CtxExpected, &Ctx, sizeof(CtxExpected));
+
+ StkPtr.pv = Bs3RegCtxGetRspSsAsCurPtr(&Ctx);
+ //Bs3TestPrintf("Stack=%p rsp=%RX64\n", StkPtr.pv, Ctx.rsp.u);
+
+ /*
+ * 16-bit tests.
+ */
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fOpSizePfx;
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ } const s_aTests[] =
+ {
+ { false, 0, bs3CpuBasic2_retf_c16, },
+ { true, 0, bs3CpuBasic2_retf_opsize_c16, },
+ { false, 32, bs3CpuBasic2_retf_i32_c16, },
+ { true, 32, bs3CpuBasic2_retf_i32_opsize_c16, },
+ { false,888, bs3CpuBasic2_retf_i888_c16, },
+ };
+
+ static struct
+ {
+ bool fRmOrV86;
+ bool fInterPriv;
+ int8_t iXcpt;
+ RTSEL uStartSs;
+ uint8_t cDstBits;
+ RTSEL uDstCs;
+ union /* must use a union here as the compiler won't compile if uint16_t and will mess up fixups for uint32_t. */
+ {
+ uint32_t offDst;
+ struct
+ {
+ NPVOID pv;
+ uint16_t uHigh;
+ } s;
+ };
+ RTSEL uDstSs;
+ uint16_t uErrCd;
+ } const s_aSubTests[] =
+ { /* rm/v86, PriChg, Xcpt, uStartSs, => bits uDstCs offDst/pv uDstSs uErrCd */
+ { true, false, -1, 0, 16, BS3_SEL_TEXT16, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, 0, 0 },
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_TEXT16 | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, false, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* conforming stuff */
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 1, BS3_SEL_R0_SS16 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16_CNF | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16_CNF | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R1_CS16_CNF },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R1_CS16_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16_CNF | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16_CNF | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R2_CS16_CNF },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R2_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R2_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R2_CS16_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16_CNF | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R3_CS16_CNF },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 0, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R3_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R3_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 1, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R3_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS16_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 2, BS3_SEL_R3_CS16_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16_CNF | 3, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* returning to 32-bit code: */
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* returning to 32-bit conforming code: */
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 1, BS3_SEL_R0_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R0_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 1, BS3_SEL_R3_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, BS3_SEL_R3_SS16 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R1_CS32_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 1, BS3_SEL_R0_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R0_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 1, BS3_SEL_R3_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, BS3_SEL_R3_SS16 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R2_CS32_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R2_CS32_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R3_CS32_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R3_CS32_CNF },
+ { false, true, 42, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS32_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* returning to 64-bit code or 16-bit when not in long mode: */
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_DS64 | 1, BS3_SEL_R0_DS64 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_DS64 | 1, 0 },
+ { false, false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R2_CS64 },
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 3, BS3_SEL_R2_CS64 },
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 3, BS3_SEL_R1_SS32 },
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 2, BS3_SEL_R3_SS32 },
+ /* returning to 64-bit code or 16-bit when not in long mode, conforming code variant: */
+ { false, false, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R1_CS64_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 2, BS3_SEL_R1_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 1, BS3_SEL_R2_SS16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R2_SS16 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R2_CS64_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R2_CS64_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, false, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R3_CS64_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R3_CS64_CNF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS64_CNF },
+ { false, true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ /* some additional #GP variations */ /** @todo test all possible exceptions! */
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS16 },
+ { false, true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_TSS32_DF | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_TSS32_DF },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_00 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_00 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_01 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_01 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_02 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_02 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_03 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_03 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_04 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_04 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_05 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_05 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_06 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_06 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_07 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_07 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_08 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_08 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_09 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_09 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0a | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0a },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0b | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0b },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0c | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0c },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0d | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0d },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0e | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0e },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_SPARE_0f | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_0f },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_10 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_10 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_11 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_11 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_12 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_12 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_13 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_13 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_14 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_14 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_15 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_15 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_16 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_16 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_17 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_17 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_18 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_18 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_19 | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_19 },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1a | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1a },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1b | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1b },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1c | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1c },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1d | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1d },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1e | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1e },
+ { false, true, 14, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_SPARE_1f | 0, { .offDst = 0 }, BS3_SEL_R0_SS16 | 0, BS3_SEL_SPARE_1f },
+ };
+
+ bool const fRmOrV86 = BS3_MODE_IS_RM_OR_V86(bMode);
+ BS3CPUVENDOR const enmCpuVendor = Bs3GetCpuVendor();
+
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+
+ for (iSubTest = 0; iSubTest < RT_ELEMENTS(s_aSubTests); iSubTest++)
+ {
+ g_usBs3TestStep = (iTest << 12) | (iSubTest << 4);
+ if ( s_aSubTests[iSubTest].fRmOrV86 == fRmOrV86
+ && (s_aSubTests[iSubTest].offDst <= UINT16_MAX || s_aTests[iTest].fOpSizePfx))
+ {
+ uint16_t const cbFrmDisp = s_aSubTests[iSubTest].fInterPriv ? iSubTest % 7 : 0;
+ uint16_t const cbStkItem = s_aTests[iTest].fOpSizePfx ? 4 : 2;
+ uint16_t const cbFrame = (s_aSubTests[iSubTest].fInterPriv ? 4 : 2) * cbStkItem;
+ uint32_t const uFlatDst = Bs3SelFar32ToFlat32(s_aSubTests[iSubTest].offDst, s_aSubTests[iSubTest].uDstCs)
+ + (s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode));
+ RTSEL const uDstSs = s_aSubTests[iSubTest].uDstSs;
+ uint64_t uDstRspExpect, uDstRspPush;
+ uint16_t cErrors;
+
+ Ctx.ss = s_aSubTests[iSubTest].uStartSs;
+ if (Ctx.ss != BS3_SEL_R0_SS32)
+ Ctx.rsp.u32 |= UINT32_C(0xfffe0000);
+ else
+ Ctx.rsp.u32 &= UINT16_MAX;
+ uDstRspExpect = uDstRspPush = Ctx.rsp.u + s_aTests[iTest].cbImm + cbFrame + cbFrmDisp;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ if (s_aTests[iTest].fOpSizePfx)
+ uDstRspPush = (uDstRspPush & UINT16_MAX) | UINT32_C(0xacdc0000);
+ if ( uDstSs == (BS3_SEL_R1_SS32 | 1)
+ || uDstSs == (BS3_SEL_R2_SS32 | 2)
+ || uDstSs == (BS3_SEL_R3_SS32 | 3)
+ || (s_aSubTests[iSubTest].cDstBits == 64 && BS3_MODE_IS_64BIT_SYS(bMode)))
+ {
+ if (s_aTests[iTest].fOpSizePfx)
+ uDstRspExpect = uDstRspPush;
+ else
+ uDstRspExpect &= UINT16_MAX;
+ }
+ }
+
+ CtxExpected.bCpl = Ctx.bCpl;
+ CtxExpected.cs = Ctx.cs;
+ CtxExpected.ss = Ctx.ss;
+ CtxExpected.ds = Ctx.ds;
+ CtxExpected.es = Ctx.es;
+ CtxExpected.fs = Ctx.fs;
+ CtxExpected.gs = Ctx.gs;
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ CtxExpected.rax.u = Ctx.rax.u;
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ {
+ CtxExpected.cs = s_aSubTests[iSubTest].uDstCs;
+ CtxExpected.rip.u = s_aSubTests[iSubTest].offDst;
+ if (s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u += 1;
+ CtxExpected.rax.au8[0] = CtxExpected.rflags.u16 & X86_EFL_CF ? 0xff : 0;
+ }
+ CtxExpected.ss = uDstSs;
+ CtxExpected.rsp.u = uDstRspExpect;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ uint16_t BS3_FAR *puSel = &CtxExpected.ds; /* ASSUME member order! */
+ unsigned cSels = 4;
+ CtxExpected.bCpl = CtxExpected.ss & X86_SEL_RPL;
+ while (cSels-- > 0)
+ {
+ uint16_t uSel = *puSel;
+ if ( (uSel & X86_SEL_MASK_OFF_RPL)
+ && Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u2Dpl < CtxExpected.bCpl
+ && (Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ *puSel = 0;
+ puSel++;
+ }
+ CtxExpected.rsp.u += s_aTests[iTest].cbImm; /* arguments are dropped from both stacks. */
+ }
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ //Bs3TestPrintf("cs:rip=%04RX16:%04RX64 -> %04RX16:%04RX64\n", Ctx.cs, Ctx.rip.u, CtxExpected.cs, CtxExpected.rip.u);
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64 -> %04RX16:%04RX64 [pushed %#RX64]\n", Ctx.ss, Ctx.rsp.u, CtxExpected.ss, CtxExpected.rsp.u, uDstRspPush);
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ //Bs3TestPrintf("%p: %04RX16 %04RX16 %04RX16 %04RX16\n", StkPtr.pu16, StkPtr.pu16[0], StkPtr.pu16[1], StkPtr.pu16[2], StkPtr.pu16[3]);
+ //Bs3TestPrintf("%.48Rhxd\n", StkPtr.pu16);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++; /* 1 */
+
+ /* Bad hw bp: Setup DR0-3 but use invalid length encodings (non-byte) */
+ //Bs3TestPrintf("hw bp: bad len\n");
+ Bs3RegSetDr0(uFlatDst);
+ Bs3RegSetDr1(uFlatDst);
+ Bs3RegSetDr2(uFlatDst);
+ Bs3RegSetDr3(uFlatDst);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(1, X86_DR7_LEN_WORD) | X86_DR7_L_G(1)
+ | X86_DR7_RW(2, X86_DR7_RW_EO) | X86_DR7_LEN(2, X86_DR7_LEN_DWORD) | X86_DR7_L_G(2)
+ | ( BS3_MODE_IS_64BIT_SYS(bMode)
+ ? X86_DR7_RW(3, X86_DR7_RW_EO) | X86_DR7_LEN(3, X86_DR7_LEN_QWORD) | X86_DR7_L_G(3) : 0) );
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ bs3CpuBasic2_CheckDr6InitVal();
+ g_usBs3TestStep++; /* 2 */
+
+ /* Bad hw bp: setup DR0-3 but don't enable them */
+ //Bs3TestPrintf("hw bp: disabled\n");
+ //Bs3RegSetDr0(uFlatDst);
+ //Bs3RegSetDr1(uFlatDst);
+ //Bs3RegSetDr2(uFlatDst);
+ //Bs3RegSetDr3(uFlatDst);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ bs3CpuBasic2_CheckDr6InitVal();
+ g_usBs3TestStep++; /* 3 */
+
+ /* Bad hw bp: Points at 2nd byte in the UD2. Docs says it only works when pointing at first byte. */
+ //Bs3TestPrintf("hw bp: byte 2\n");
+ Bs3RegSetDr0(uFlatDst + 1);
+ Bs3RegSetDr1(uFlatDst + 1);
+ //Bs3RegSetDr2(uFlatDst);
+ //Bs3RegSetDr3(uFlatDst);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE) | X86_DR7_L_G(0)
+ | X86_DR7_RW(1, X86_DR7_RW_EO) | X86_DR7_LEN(1, X86_DR7_LEN_BYTE) | X86_DR7_L_G(1));
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ bs3CpuBasic2_CheckDr6InitVal();
+ g_usBs3TestStep++; /* 4 */
+
+ /* Again with two correctly hardware breakpoints and a disabled one that just matches the address: */
+ //Bs3TestPrintf("bp 1 + 3...\n");
+ Bs3RegSetDr0(uFlatDst);
+ Bs3RegSetDr1(uFlatDst);
+ Bs3RegSetDr2(0);
+ Bs3RegSetDr3(uFlatDst);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(1, X86_DR7_RW_EO) | X86_DR7_LEN(1, X86_DR7_LEN_BYTE) | X86_DR7_L_G(1)
+ | X86_DR7_RW(3, X86_DR7_RW_EO) | X86_DR7_LEN(3, X86_DR7_LEN_BYTE) | X86_DR7_L_G(3) );
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected,
+ enmCpuVendor == BS3CPUVENDOR_AMD ? X86_DR6_B1 | X86_DR6_B3 /* 3990x */
+ : X86_DR6_B0 | X86_DR6_B1 | X86_DR6_B3);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++; /* 5 */
+
+ /* Again with a single locally enabled breakpoint. */
+ //Bs3TestPrintf("bp 0/l...\n");
+ Bs3RegSetDr0(uFlatDst);
+ Bs3RegSetDr1(0);
+ Bs3RegSetDr2(0);
+ Bs3RegSetDr3(0);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL | X86_DR6_B1 | X86_DR6_B2 | X86_DR6_B3 | X86_DR6_BS);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE) | X86_DR7_L(0));
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_B0 | X86_DR6_BS); /* B0-B3 set, BS preserved */
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++; /* 6 */
+
+ /* Again with a single globally enabled breakpoint and serveral other types of breakpoints
+ configured but not enabled. */
+ //Bs3TestPrintf("bp 2/g+...\n");
+ cErrors = Bs3TestSubErrorCount();
+ Bs3RegSetDr0(uFlatDst);
+ Bs3RegSetDr1(uFlatDst);
+ Bs3RegSetDr2(uFlatDst);
+ Bs3RegSetDr3(uFlatDst);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL | X86_DR6_BS | X86_DR6_BD | X86_DR6_BT | X86_DR6_B2);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(0, X86_DR7_RW_RW) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE)
+ | X86_DR7_RW(1, X86_DR7_RW_RW) | X86_DR7_LEN(1, X86_DR7_LEN_BYTE) | X86_DR7_L_G(1)
+ | X86_DR7_RW(2, X86_DR7_RW_EO) | X86_DR7_LEN(2, X86_DR7_LEN_BYTE) | X86_DR7_G(2)
+ | X86_DR7_RW(3, X86_DR7_RW_WO) | X86_DR7_LEN(3, X86_DR7_LEN_BYTE) | X86_DR7_G(3)
+ );
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_B2 | X86_DR6_BS | X86_DR6_BD | X86_DR6_BT);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++; /* 7 */
+
+ /* Now resume it with lots of execution breakpoints configured. */
+ if (s_aSubTests[iSubTest].iXcpt < 0 && Bs3TestSubErrorCount() == cErrors)
+ {
+ Bs3MemCpy(&Ctx2, &TrapCtx.Ctx, sizeof(Ctx2));
+ Ctx2.rflags.u32 |= X86_EFL_RF;
+ //Bs3TestPrintf("bp 3/g+rf %04RX16:%04RX64 efl=%RX32 ds=%04RX16...\n", Ctx2.cs, Ctx2.rip.u, Ctx2.rflags.u32, Ctx2.ds);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL
+ | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE)
+ | X86_DR7_RW(1, X86_DR7_RW_EO) | X86_DR7_LEN(1, X86_DR7_LEN_BYTE) | X86_DR7_L_G(1)
+ | X86_DR7_RW(2, X86_DR7_RW_EO) | X86_DR7_LEN(2, X86_DR7_LEN_BYTE) | X86_DR7_G(2)
+ | X86_DR7_RW(3, X86_DR7_RW_EO) | X86_DR7_LEN(3, X86_DR7_LEN_BYTE) | X86_DR7_G(3)
+ );
+ Bs3TrapSetJmpAndRestore(&Ctx2, &TrapCtx);
+ Bs3RegSetDr7(X86_DR7_INIT_VAL);
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ bs3CpuBasic2_CheckDr6InitVal();
+ }
+ g_usBs3TestStep++; /* 8 */
+
+ /* Now do single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ if (s_aSubTests[iSubTest].iXcpt < 0 && s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u -= 1;
+ CtxExpected.rax.u = Ctx.rax.u;
+ }
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++; /* 9 */
+
+ /* Single step with B0-B3 set to check that they're not preserved
+ and with BD & BT to check that they are (checked on Intel 6700K): */
+ //Bs3TestPrintf("stepping b0-b3+bd+bt=1...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL | X86_DR6_B_MASK | X86_DR6_BD | X86_DR6_BT);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS | X86_DR6_BD | X86_DR6_BT);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++; /* 10 */
+
+ }
+ }
+ }
+ }
+ /*
+ * 32-bit tests.
+ */
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ static struct
+ {
+ bool fOpSizePfx;
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ } const s_aTests[] =
+ {
+ { false, 0, bs3CpuBasic2_retf_c32, },
+ { true, 0, bs3CpuBasic2_retf_opsize_c32, },
+ { false, 32, bs3CpuBasic2_retf_i32_c32, },
+ { true, 32, bs3CpuBasic2_retf_i32_opsize_c32, },
+ { false,888, bs3CpuBasic2_retf_i888_c32, },
+ };
+
+ static struct
+ {
+ bool fInterPriv;
+ int8_t iXcpt;
+ RTSEL uStartSs;
+ uint8_t cDstBits;
+ RTSEL uDstCs;
+ union /* must use a union here as the compiler won't compile if uint16_t and will mess up fixups for uint32_t. */
+ {
+ uint32_t offDst;
+ struct
+ {
+ NPVOID pv;
+ uint16_t uHigh;
+ } s;
+ };
+ RTSEL uDstSs;
+ uint16_t uErrCd;
+ } const s_aSubTests[] =
+ { /* PriChg, Xcpt, uStartSs, => bits uDstCs offDst/pv uDstSs uErrCd */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* same with 32-bit wide target addresses: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* conforming stuff */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R1_CS32_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R1_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS32_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS32_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 0, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 1, { .offDst = LOW_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R2_SS32 | 2, BS3_SEL_R3_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 2, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 2, BS3_SEL_R3_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 3, { .offDst = LOW_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 16-bit code: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 16-bit conforming code: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R1_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS16_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS16_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS16_CNF },
+ { true, 42, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R2_SS32 | 2, BS3_SEL_R3_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_ud2, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 64-bit code or 16-bit when not in long mode: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_DS64 | 1, BS3_SEL_R0_DS64 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_DS64 | 1, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R2_CS64 },
+ { true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 3, BS3_SEL_R2_CS64 },
+ { true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 3, BS3_SEL_R1_SS32 },
+ { true, 14, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 2, BS3_SEL_R3_SS32 },
+ /* returning to 64-bit code or 16-bit when not in long mode, conforming code variant: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R1_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 2, BS3_SEL_R1_SS16 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 1, BS3_SEL_R2_SS16 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R2_SS16 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R2_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R2_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS16 | 0, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+
+ /* some additional #GP variations */ /** @todo test all possible exceptions! */
+ { true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS16 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_00 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_00 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_01 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_01 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_02 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_02 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_03 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_03 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_04 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_04 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_05 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_05 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_06 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_06 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_07 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_07 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_08 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_08 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_09 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_09 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0a | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0a },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0b | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0b },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0c | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0c },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0d | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0d },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0e | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0e },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_SPARE_0f | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0f },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_10 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_10 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_11 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_11 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_12 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_12 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_13 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_13 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_14 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_14 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_15 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_15 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_16 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_16 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_17 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_17 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_18 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_18 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_19 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_19 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1a | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1a },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1b | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1b },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1c | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1c },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1d | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1d },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1e | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1e },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_SPARE_1f | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1f },
+ };
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+ //Bs3TestPrintf("-------------- #%u: cs:eip=%04RX16:%08RX64 imm=%u%s\n",
+ // iTest, Ctx.cs, Ctx.rip.u, s_aTests[iTest].cbImm, s_aTests[iTest].fOpSizePfx ? " o16" : "");
+
+ for (iSubTest = 0; iSubTest < RT_ELEMENTS(s_aSubTests); iSubTest++)
+ {
+ g_usBs3TestStep = (iTest << 12) | (iSubTest << 1);
+ if (!s_aTests[iTest].fOpSizePfx || s_aSubTests[iSubTest].offDst <= UINT16_MAX)
+ {
+ uint16_t const cbFrmDisp = s_aSubTests[iSubTest].fInterPriv ? iSubTest % 7 : 0;
+ uint16_t const cbStkItem = s_aTests[iTest].fOpSizePfx ? 2 : 4;
+ uint16_t const cbFrame = (s_aSubTests[iSubTest].fInterPriv ? 4 : 2) * cbStkItem;
+ RTSEL const uDstSs = s_aSubTests[iSubTest].uDstSs;
+ uint64_t uDstRspExpect, uDstRspPush;
+ //Bs3TestPrintf(" #%u: %s %d %#04RX16 -> %u %#04RX16:%#04RX32 %#04RX16 %#RX16\n", iSubTest, s_aSubTests[iSubTest].fInterPriv ? "priv" : "same", s_aSubTests[iSubTest].iXcpt, s_aSubTests[iSubTest].uStartSs,
+ // s_aSubTests[iSubTest].cDstBits, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst, s_aSubTests[iSubTest].uDstSs, s_aSubTests[iSubTest].uErrCd);
+
+ Ctx.ss = s_aSubTests[iSubTest].uStartSs;
+ if (Ctx.ss != BS3_SEL_R0_SS32)
+ Ctx.rsp.u32 |= UINT32_C(0xfffe0000);
+ else
+ Ctx.rsp.u32 &= UINT16_MAX;
+ uDstRspExpect = uDstRspPush = Ctx.rsp.u + s_aTests[iTest].cbImm + cbFrame + cbFrmDisp;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ if (!s_aTests[iTest].fOpSizePfx)
+ uDstRspPush = (uDstRspPush & UINT16_MAX) | UINT32_C(0xacdc0000);
+ if ( uDstSs == (BS3_SEL_R1_SS32 | 1)
+ || uDstSs == (BS3_SEL_R2_SS32 | 2)
+ || uDstSs == (BS3_SEL_R3_SS32 | 3)
+ || (s_aSubTests[iSubTest].cDstBits == 64 && BS3_MODE_IS_64BIT_SYS(bMode)))
+ {
+ if (!s_aTests[iTest].fOpSizePfx)
+ uDstRspExpect = uDstRspPush;
+ else
+ uDstRspExpect &= UINT16_MAX;
+ }
+ }
+
+ CtxExpected.bCpl = Ctx.bCpl;
+ CtxExpected.cs = Ctx.cs;
+ CtxExpected.ss = Ctx.ss;
+ CtxExpected.ds = Ctx.ds;
+ CtxExpected.es = Ctx.es;
+ CtxExpected.fs = Ctx.fs;
+ CtxExpected.gs = Ctx.gs;
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ CtxExpected.rax.u = Ctx.rax.u;
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ {
+ CtxExpected.cs = s_aSubTests[iSubTest].uDstCs;
+ CtxExpected.rip.u = s_aSubTests[iSubTest].offDst;
+ if (s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u += 1;
+ CtxExpected.rax.au8[0] = CtxExpected.rflags.u16 & X86_EFL_CF ? 0xff : 0;
+ }
+ CtxExpected.ss = uDstSs;
+ CtxExpected.rsp.u = uDstRspExpect;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ uint16_t BS3_FAR *puSel = &CtxExpected.ds; /* ASSUME member order! */
+ unsigned cSels = 4;
+ CtxExpected.bCpl = CtxExpected.ss & X86_SEL_RPL;
+ while (cSels-- > 0)
+ {
+ uint16_t uSel = *puSel;
+ if ( (uSel & X86_SEL_MASK_OFF_RPL)
+ && Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u2Dpl < CtxExpected.bCpl
+ && (Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ *puSel = 0;
+ puSel++;
+ }
+ CtxExpected.rsp.u += s_aTests[iTest].cbImm; /* arguments are dropped from both stacks. */
+ }
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64 -> %04RX16:%04RX64 [pushed %#RX64]; %04RX16:%04RX64\n",Ctx.ss, Ctx.rsp.u,
+ // CtxExpected.ss, CtxExpected.rsp.u, uDstRspPush, CtxExpected.cs, CtxExpected.rip.u);
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ //Bs3TestPrintf("%p: %04RX16 %04RX16 %04RX16 %04RX16\n", StkPtr.pu16, StkPtr.pu16[0], StkPtr.pu16[1], StkPtr.pu16[2], StkPtr.pu16[3]);
+ //Bs3TestPrintf("%.48Rhxd\n", StkPtr.pu16);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ if (s_aSubTests[iSubTest].iXcpt < 0 && s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u -= 1;
+ CtxExpected.rax.u = Ctx.rax.u;
+ }
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+ }
+ }
+ }
+ }
+ /*
+ * 64-bit tests.
+ */
+ else if (BS3_MODE_IS_64BIT_CODE(bMode))
+ {
+ static struct
+ {
+ uint8_t fOpSizePfx; /**< 0: none, 1: 066h, 2: REX.W; Effective op size prefix. */
+ uint16_t cbImm;
+ FPFNBS3FAR pfnTest;
+ } const s_aTests[] =
+ {
+ { 0, 0, bs3CpuBasic2_retf_c64, },
+ { 1, 0, bs3CpuBasic2_retf_opsize_c64, },
+ { 0, 32, bs3CpuBasic2_retf_i32_c64, },
+ { 1, 32, bs3CpuBasic2_retf_i32_opsize_c64, },
+ { 2, 0, bs3CpuBasic2_retf_rexw_c64, },
+ { 2, 0, bs3CpuBasic2_retf_opsize_rexw_c64, },
+ { 1, 0, bs3CpuBasic2_retf_rexw_opsize_c64, },
+ { 2, 24, bs3CpuBasic2_retf_i24_rexw_c64, },
+ { 2, 24, bs3CpuBasic2_retf_i24_opsize_rexw_c64, },
+ { 1, 24, bs3CpuBasic2_retf_i24_rexw_opsize_c64, },
+ { 0,888, bs3CpuBasic2_retf_i888_c64, },
+ };
+
+ static struct
+ {
+ bool fInterPriv;
+ int8_t iXcpt;
+ RTSEL uStartSs;
+ uint8_t cDstBits;
+ RTSEL uDstCs;
+ union /* must use a union here as the compiler won't compile if uint16_t and will mess up fixups for uint32_t. */
+ {
+ uint32_t offDst;
+ struct
+ {
+ NPVOID pv;
+ uint16_t uHigh;
+ } s;
+ };
+ RTSEL uDstSs;
+ uint16_t uErrCd;
+ } const s_aSubTests[] =
+ { /* PriChg, Xcpt, uStartSs, => bits uDstCs offDst/pv uDstSs uErrCd */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64 | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* same with 32-bit wide target addresses: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64 | 0, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R0_CS64 | 0, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64 | 1, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R1_CS64 | 1, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64 | 2, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R2_CS64 | 2, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64 | 3, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 64, BS3_SEL_R3_CS64 | 3, { .s = {(NPVOID)bs3CpuBasic2_salc_ud2, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ /* conforming stuff */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R0_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R1_CS64_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R1_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R1_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS64_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R2_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS64_CNF },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 0, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 1, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R2_SS32 | 2, BS3_SEL_R3_CS64_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 2, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 2, BS3_SEL_R3_CS64_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_R3_CS64_CNF | 3, { .offDst = LOW_SALC_UD_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 16-bit code: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R0_CS16 | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R1_CS16 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R2_CS16 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 16-bit conforming code: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R0_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R1_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R1_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS16_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R2_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS16_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS16_CNF },
+ { true, 42, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R2_SS32 | 2, BS3_SEL_R3_CS16_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 16, BS3_SEL_R3_CS16_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, 0 } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 32-bit code - narrow 16-bit target address: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 32-bit code - wider 32-bit target address: */
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R0_CS32 | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS16 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R1_CS32 | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R2_CS32 | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { true, -1, BS3_SEL_R0_SS16 | 0, 32, BS3_SEL_R3_CS32 | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ /* returning to 32-bit conforming code: */
+ { false, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS16 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R0_CS32_CNF | 3, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R1_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS16 | 1, 0 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 1, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R0_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 1, BS3_SEL_R3_SS32 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, BS3_SEL_R3_SS32 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R1_CS32_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R2_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R2_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 2, { .offDst = LOW_SWAPGS_ADDR }, BS3_SEL_R2_SS32 | 2, 0 },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R2_CS32_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS16 | 3, 0 },
+ { false, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 0, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R0_SS32 | 0, BS3_SEL_R3_CS32_CNF },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 1, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R1_SS32 | 1, BS3_SEL_R3_CS32_CNF },
+ { true, 42, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 2, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R2_SS32 | 2, BS3_SEL_R3_CS32_CNF },
+ { true, -1, BS3_SEL_R0_SS32 | 0, 32, BS3_SEL_R3_CS32_CNF | 3, { .s = {(NPVOID)bs3CpuBasic2_swapgs, BS3TEXT16_ADDR_HI } }, BS3_SEL_R3_SS32 | 3, 0 },
+
+ /* some additional #GP variations */ /** @todo test all possible exceptions! */
+ { true, 14, BS3_SEL_R0_SS16 | 0, 16, BS3_SEL_R3_CS16 | 2, { .s = { (NPVOID)bs3CpuBasic2_ud2 } }, BS3_SEL_R2_SS16 | 2, BS3_SEL_R3_CS16 },
+
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_00 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_00 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_02 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_02 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_04 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_04 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_06 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_06 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_08 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_08 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_0a | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0a },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_0c | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0c },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_0e | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_0e },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_10 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_10 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_12 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_12 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_14 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_14 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_16 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_16 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_18 | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_18 },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_1a | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1a },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_1c | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1c },
+ { true, 14, BS3_SEL_R0_SS32 | 0, 64, BS3_SEL_SPARE_1e | 0, { .offDst = 0 }, BS3_SEL_R0_SS32 | 0, BS3_SEL_SPARE_1e },
+ };
+
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, s_aTests[iTest].pfnTest);
+ //Bs3TestPrintf("-------------- #%u: cs:eip=%04RX16:%08RX64 imm=%u%s\n", iTest, Ctx.cs, Ctx.rip.u, s_aTests[iTest].cbImm,
+ // s_aTests[iTest].fOpSizePfx == 1 ? " o16" : s_aTests[iTest].fOpSizePfx == 2 ? " o64" : "");
+
+ for (iSubTest = 0; iSubTest < RT_ELEMENTS(s_aSubTests); iSubTest++)
+ {
+ g_usBs3TestStep = (iTest << 12) | (iSubTest << 1);
+ if (s_aTests[iTest].fOpSizePfx != 1 || s_aSubTests[iSubTest].offDst <= UINT16_MAX)
+ {
+ uint16_t const cbFrmDisp = s_aSubTests[iSubTest].fInterPriv ? iSubTest % 7 : 0;
+ uint16_t const cbStkItem = s_aTests[iTest].fOpSizePfx == 2 ? 8 : s_aTests[iTest].fOpSizePfx == 0 ? 4 : 2;
+ uint16_t const cbFrame = (s_aSubTests[iSubTest].fInterPriv ? 4 : 2) * cbStkItem;
+ RTSEL const uDstSs = s_aSubTests[iSubTest].uDstSs;
+ uint64_t uDstRspExpect, uDstRspPush;
+ //Bs3TestPrintf(" #%u: %s %d %#04RX16 -> %u %#04RX16:%#04RX32 %#04RX16 %#RX16\n", iSubTest, s_aSubTests[iSubTest].fInterPriv ? "priv" : "same", s_aSubTests[iSubTest].iXcpt, s_aSubTests[iSubTest].uStartSs,
+ // s_aSubTests[iSubTest].cDstBits, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst, s_aSubTests[iSubTest].uDstSs, s_aSubTests[iSubTest].uErrCd);
+
+ Ctx.ss = s_aSubTests[iSubTest].uStartSs;
+ uDstRspExpect = uDstRspPush = Ctx.rsp.u + s_aTests[iTest].cbImm + cbFrame + cbFrmDisp;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ if (s_aTests[iTest].fOpSizePfx != 1)
+ {
+ if (s_aTests[iTest].fOpSizePfx == 2)
+ uDstRspPush |= UINT64_C(0xf00dfaceacdc0000);
+ else
+ uDstRspPush |= UINT32_C(0xacdc0000);
+ if (s_aSubTests[iSubTest].cDstBits == 64)
+ uDstRspExpect = uDstRspPush;
+ else if (!BS3_SEL_IS_SS16(uDstSs))
+ uDstRspExpect = (uint32_t)uDstRspPush;
+ }
+ }
+
+ CtxExpected.bCpl = Ctx.bCpl;
+ CtxExpected.cs = Ctx.cs;
+ CtxExpected.ss = Ctx.ss;
+ CtxExpected.ds = Ctx.ds;
+ CtxExpected.es = Ctx.es;
+ CtxExpected.fs = Ctx.fs;
+ CtxExpected.gs = Ctx.gs;
+ CtxExpected.rip.u = Ctx.rip.u;
+ CtxExpected.rsp.u = Ctx.rsp.u;
+ CtxExpected.rax.u = Ctx.rax.u;
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ {
+ CtxExpected.cs = s_aSubTests[iSubTest].uDstCs;
+ CtxExpected.rip.u = s_aSubTests[iSubTest].offDst;
+ if (s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u += 1;
+ CtxExpected.rax.au8[0] = CtxExpected.rflags.u16 & X86_EFL_CF ? 0xff : 0;
+ }
+ CtxExpected.ss = uDstSs;
+ CtxExpected.rsp.u = uDstRspExpect;
+ if (s_aSubTests[iSubTest].fInterPriv)
+ {
+ uint16_t BS3_FAR *puSel = &CtxExpected.ds; /* ASSUME member order! */
+ unsigned cSels = 4;
+ CtxExpected.bCpl = CtxExpected.ss & X86_SEL_RPL;
+ while (cSels-- > 0)
+ {
+ uint16_t uSel = *puSel;
+ if ( (uSel & X86_SEL_MASK_OFF_RPL)
+ && Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u2Dpl < CtxExpected.bCpl
+ && (Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+ *puSel = 0;
+ puSel++;
+ }
+ CtxExpected.rsp.u += s_aTests[iTest].cbImm; /* arguments are dropped from both stacks. */
+ }
+ }
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ //Bs3TestPrintf("ss:rsp=%04RX16:%04RX64 -> %04RX16:%04RX64 [pushed %#RX64]; %04RX16:%04RX64\n",Ctx.ss, Ctx.rsp.u,
+ // CtxExpected.ss, CtxExpected.rsp.u, uDstRspPush, CtxExpected.cs, CtxExpected.rip.u);
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ //Bs3TestPrintf("%p: %04RX16 %04RX16 %04RX16 %04RX16\n", StkPtr.pu16, StkPtr.pu16[0], StkPtr.pu16[1], StkPtr.pu16[2], StkPtr.pu16[3]);
+ //Bs3TestPrintf("%.48Rhxd\n", StkPtr.pu16);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ g_usBs3TestStep++;
+
+ /* Again single stepping: */
+ //Bs3TestPrintf("stepping...\n");
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Ctx.rflags.u16 |= X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ if (s_aSubTests[iSubTest].iXcpt < 0 && s_aSubTests[iSubTest].cDstBits == 64 && !BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ CtxExpected.rip.u -= 1;
+ CtxExpected.rax.u = Ctx.rax.u;
+ }
+ bs3CpuBasic2_retf_PrepStack(StkPtr, cbStkItem, s_aSubTests[iSubTest].uDstCs, s_aSubTests[iSubTest].offDst,
+ s_aSubTests[iSubTest].fInterPriv, s_aTests[iTest].cbImm,
+ s_aSubTests[iSubTest].uDstSs, uDstRspPush);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (s_aSubTests[iSubTest].iXcpt < 0)
+ bs3CpuBasic2_CompareDbCtx(&TrapCtx, &CtxExpected, X86_DR6_BS);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, s_aSubTests[iSubTest].uErrCd);
+ Ctx.rflags.u16 &= ~X86_EFL_TF;
+ CtxExpected.rflags.u16 = Ctx.rflags.u16;
+ g_usBs3TestStep++;
+ }
+ }
+ }
+ }
+ else
+ Bs3TestFailed("wtf?");
+
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ Bs3TrapReInit();
+ return 0;
+}
+
+
+
+/*********************************************************************************************************************************
+* Instruction Length *
+*********************************************************************************************************************************/
+
+
+static uint8_t bs3CpuBasic2_instr_len_Worker(uint8_t bMode, uint8_t BS3_FAR *pbCodeBuf)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3REGCTX Ctx;
+ BS3REGCTX CtxExpected;
+ uint32_t uEipBase;
+ unsigned cbInstr;
+ unsigned off;
+
+ /* Make sure they're allocated and all zeroed. */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&CtxExpected, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+
+ /*
+ * Create a context.
+ *
+ * ASSUMES we're in on the ring-0 stack in ring-0 and using less than 16KB.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 768);
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, (FPFNBS3FAR)pbCodeBuf);
+ uEipBase = Ctx.rip.u32;
+
+ Bs3MemCpy(&CtxExpected, &Ctx, sizeof(CtxExpected));
+
+ /*
+ * Simple stuff crossing the page.
+ */
+ for (off = X86_PAGE_SIZE - 32; off <= X86_PAGE_SIZE + 16; off++)
+ {
+ Ctx.rip.u32 = uEipBase + off;
+ for (cbInstr = 0; cbInstr < 24; cbInstr++)
+ {
+ /*
+ * Generate the instructions:
+ * [es] nop
+ * ud2
+ */
+ if (cbInstr > 0)
+ {
+ Bs3MemSet(&pbCodeBuf[off], 0x26 /* es */, cbInstr);
+ pbCodeBuf[off + cbInstr - 1] = 0x90; /* nop */
+ }
+ pbCodeBuf[off + cbInstr + 0] = 0x0f; /* ud2 */
+ pbCodeBuf[off + cbInstr + 1] = 0x0b;
+
+ /*
+ * Test it.
+ */
+ if (cbInstr < 16)
+ CtxExpected.rip.u32 = Ctx.rip.u32 + cbInstr;
+ else
+ CtxExpected.rip.u32 = Ctx.rip.u32;
+ g_uBs3TrapEipHint = CtxExpected.rip.u32;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (cbInstr < 16)
+ bs3CpuBasic2_CompareUdCtx(&TrapCtx, &CtxExpected);
+ else
+ bs3CpuBasic2_CompareGpCtx(&TrapCtx, &CtxExpected, 0);
+ }
+ pbCodeBuf[off] = 0xf1; /* icebp */
+ }
+
+ /*
+ * Pit instruction length violations against the segment limit (#GP).
+ */
+ if (!BS3_MODE_IS_RM_OR_V86(bMode) && bMode != BS3_MODE_LM64)
+ {
+ /** @todo */
+ }
+
+ /*
+ * Pit instruction length violations against an invalid page (#PF).
+ */
+ if (BS3_MODE_IS_PAGED(bMode))
+ {
+ /** @todo */
+ }
+
+ return 0;
+}
+
+
+/**
+ * Entrypoint for FAR RET tests.
+ *
+ * @returns 0 or BS3TESTDOMODE_SKIPPED.
+ * @param bMode The CPU mode we're testing.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuBasic2_instr_len)(uint8_t bMode)
+{
+ /*
+ * Allocate three pages so we can straddle an instruction across the
+ * boundrary for testing special IEM cases, with the last page being
+ * made in accessible and useful for pitting #PF against #GP.
+ */
+ uint8_t BS3_FAR * const pbCodeBuf = (uint8_t BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_REAL, X86_PAGE_SIZE * 3);
+ //Bs3TestPrintf("pbCodeBuf=%p\n", pbCodeBuf);
+ if (pbCodeBuf)
+ {
+ Bs3MemSet(pbCodeBuf, 0xf1, X86_PAGE_SIZE * 3);
+ bs3CpuBasic2_SetGlobals(bMode);
+
+ if (!BS3_MODE_IS_PAGED(bMode))
+ bs3CpuBasic2_instr_len_Worker(bMode, pbCodeBuf);
+ else
+ {
+ uint32_t const uFlatLastPg = Bs3SelPtrToFlat(pbCodeBuf) + X86_PAGE_SIZE * 2;
+ int rc = Bs3PagingProtect(uFlatLastPg, X86_PAGE_SIZE, 0, X86_PTE_P);
+ if (RT_SUCCESS(rc))
+ {
+ bs3CpuBasic2_instr_len_Worker(bMode, pbCodeBuf);
+ Bs3PagingProtect(uFlatLastPg, X86_PAGE_SIZE, X86_PTE_P, 0);
+ }
+ else
+ Bs3TestFailed("Failed to allocate 3 code pages");
+ }
+
+ Bs3MemFree(pbCodeBuf, X86_PAGE_SIZE * 3);
+ }
+ else
+ Bs3TestFailed("Failed to allocate 3 code pages");
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2.c
new file mode 100644
index 00000000..9b654200
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2.c
@@ -0,0 +1,127 @@
+/* $Id: bs3-cpu-basic-2.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3TESTMODE_PROTOTYPES_MODE(bs3CpuBasic2_TssGateEsp);
+BS3TESTMODE_PROTOTYPES_MODE(bs3CpuBasic2_RaiseXcpt1);
+
+FNBS3TESTDOMODE bs3CpuBasic2_RaiseXcpt11_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_sidt_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_sgdt_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_lidt_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_lgdt_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_iret_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_jmp_call_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_far_jmp_call_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_near_ret_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_far_ret_f16;
+FNBS3TESTDOMODE bs3CpuBasic2_instr_len_f16;
+
+BS3_DECL_CALLBACK(void) bs3CpuBasic2_Do32BitTests_pe32();
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEENTRY g_aModeTest[] =
+{
+ BS3TESTMODEENTRY_MODE("tss / gate / esp", bs3CpuBasic2_TssGateEsp),
+#if 0 /** @todo The 'raise xcpt \#1' test doesn't work in IEM! */
+ BS3TESTMODEENTRY_MODE("raise xcpt #1", bs3CpuBasic2_RaiseXcpt1),
+#endif
+};
+
+static const BS3TESTMODEBYONEENTRY g_aModeByOneTests[] =
+{
+#if 1
+ { "#ac", bs3CpuBasic2_RaiseXcpt11_f16, 0 },
+#endif
+#if 1
+ { "iret", bs3CpuBasic2_iret_f16, 0 },
+ { "near jmp+call jb / jv / ind", bs3CpuBasic2_jmp_call_f16, 0 },
+ { "far jmp+call", bs3CpuBasic2_far_jmp_call_f16, 0 },
+ { "near ret", bs3CpuBasic2_near_ret_f16, 0 },
+ { "far ret", bs3CpuBasic2_far_ret_f16, 0 },
+#endif
+#if 1
+ { "sidt", bs3CpuBasic2_sidt_f16, 0 },
+ { "sgdt", bs3CpuBasic2_sgdt_f16, 0 },
+ { "lidt", bs3CpuBasic2_lidt_f16, 0 },
+ { "lgdt", bs3CpuBasic2_lgdt_f16, 0 },
+#endif
+#if 1
+ { "instr length", bs3CpuBasic2_instr_len_f16, 0 },
+#endif
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-cpu-basic-2");
+ Bs3TestPrintf("g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+
+ /*
+ * Do tests driven from 16-bit code.
+ */
+ NOREF(g_aModeTest); NOREF(g_aModeByOneTests); /* for when commenting out bits */
+#if 1
+ Bs3TestDoModes_rm(g_aModeTest, RT_ELEMENTS(g_aModeTest));
+#endif
+ Bs3TestDoModesByOne_rm(g_aModeByOneTests, RT_ELEMENTS(g_aModeByOneTests), 0);
+
+#if 0 /** @todo The '\#PF' test doesn't work right in IEM! */
+ /*
+ * Do tests driven from 32-bit code (bs3-cpu-basic-2-32.c32 via assembly).
+ */
+ Bs3SwitchTo32BitAndCallC_rm(bs3CpuBasic2_Do32BitTests_pe32, 0);
+#endif
+
+ Bs3TestTerm();
+ Bs3Shutdown();
+for (;;) { ASMHalt(); }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-asm.asm
new file mode 100644
index 00000000..4e0d649d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-asm.asm
@@ -0,0 +1,49 @@
+; $Id: bs3-cpu-decoding-1-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-decoding-1, assembly helpers and template instantiation.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;
+; Instantiate code templates.
+;
+BS3_INSTANTIATE_TEMPLATE_ESSENTIALS "bs3-cpu-decoding-1-template.mac"
+BS3_INSTANTIATE_COMMON_TEMPLATE "bs3-cpu-decoding-1-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.c
new file mode 100644
index 00000000..e57bde8a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.c
@@ -0,0 +1,68 @@
+/* $Id: bs3-cpu-decoding-1-template.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-decoding-1, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <VBox/VMMDevTesting.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/*
+ * Common code.
+ * Common code.
+ * Common code.
+ */
+#ifdef BS3_INSTANTIATING_CMN
+
+#endif /* BS3_INSTANTIATING_CMN */
+
+
+/*
+ * Mode specific code.
+ * Mode specific code.
+ * Mode specific code.
+ */
+#ifdef BS3_INSTANTIATING_MODE
+
+#endif /* BS3_INSTANTIATING_MODE */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.mac
new file mode 100644
index 00000000..8b6fd894
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1-template.mac
@@ -0,0 +1,124 @@
+; $Id: bs3-cpu-decoding-1-template.mac $
+;; @file
+; BS3Kit - bs3-cpu-decoding-1, assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+%ifdef BS3_INSTANTIATING_CMN
+
+BS3_PROC_BEGIN_CMN bs3CpuDecoding1_LoadXmm0, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ push es
+ push bx
+ les bx, [xBP + xCB + cbCurRetAddr]
+ movupd xmm0, [es:bx]
+ pop bx
+ pop es
+%else
+ mov xAX, [xBP + xCB + cbCurRetAddr]
+ movupd xmm0, [xAX]
+%endif
+
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN bs3CpuDecoding1_LoadXmm0
+
+
+BS3_PROC_BEGIN_CMN bs3CpuDecoding1_LoadXmm1, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ push es
+ push bx
+ les bx, [xBP + xCB + cbCurRetAddr]
+ movupd xmm1, [es:bx]
+ pop bx
+ pop es
+%else
+ mov xAX, [xBP + xCB + cbCurRetAddr]
+ movupd xmm1, [xAX]
+%endif
+
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN bs3CpuDecoding1_LoadXmm1
+
+
+BS3_PROC_BEGIN_CMN bs3CpuDecoding1_SaveXmm0, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ push es
+ push bx
+ les bx, [xBP + xCB + cbCurRetAddr]
+ movupd [es:bx], xmm0
+ pop bx
+ pop es
+%else
+ mov xAX, [xBP + xCB + cbCurRetAddr]
+ movupd [xAX], xmm0
+%endif
+
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN bs3CpuDecoding1_SaveXmm0
+
+
+%endif
+
+%include "bs3kit-template-footer.mac" ; reset environment
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1.c32 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1.c32
new file mode 100644
index 00000000..22001f16
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-decoding-1.c32
@@ -0,0 +1,1736 @@
+/* $Id: bs3-cpu-decoding-1.c32 $ */
+/** @file
+ * BS3Kit - bs3-cpu-decoding-1, 32-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/* bs3-cpu-decoding-1-template.mac: */
+BS3_DECL_NEAR(void) BS3_CMN_NM(bs3CpuDecoding1_LoadXmm0)(PCRTUINT128U);
+BS3_DECL_NEAR(void) BS3_CMN_NM(bs3CpuDecoding1_LoadXmm1)(PCRTUINT128U);
+BS3_DECL_NEAR(void) BS3_CMN_NM(bs3CpuDecoding1_SaveXmm0)(PRTUINT128U);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Simple test.
+ */
+typedef struct CPUDECODE1TST
+{
+ uint16_t fFlags;
+ uint8_t cbOpcodes;
+ uint8_t abOpcodes[20];
+ uint8_t cbUd;
+} CPUDECODE1TST;
+typedef CPUDECODE1TST BS3_FAR *PCPUDECODE1TST;
+
+#define P_CS X86_OP_PRF_CS
+#define P_SS X86_OP_PRF_SS
+#define P_DS X86_OP_PRF_DS
+#define P_ES X86_OP_PRF_ES
+#define P_FS X86_OP_PRF_FS
+#define P_GS X86_OP_PRF_GS
+#define P_OZ X86_OP_PRF_SIZE_OP
+#define P_AZ X86_OP_PRF_SIZE_ADDR
+#define P_LK X86_OP_PRF_LOCK
+#define P_RN X86_OP_PRF_REPNZ
+#define P_RZ X86_OP_PRF_REPZ
+
+#define RM_EAX_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_ECX_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_EDX_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_EBX_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_ESP_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_EBP_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_ESI_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+#define RM_EDI_EAX ((3 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | (X86_GREG_xAX))
+
+#define RM_EAX_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ECX_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDX_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBX_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESP_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBP_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESI_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDI_DEREF_EBX ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+
+#define RM_EAX_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ECX_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDX_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBX_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESP_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBP_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESI_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDI_DEREF_EBX_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+
+#define RM_EAX_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ECX_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDX_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBX_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESP_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EBP_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_ESI_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+#define RM_EDI_DEREF_EBX_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | (X86_GREG_xBX))
+
+#define RM_EAX_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ECX_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDX_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBX_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESP_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBP_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESI_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDI_SIB ((0 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | 4)
+
+#define RM_EAX_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ECX_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDX_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBX_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESP_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBP_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESI_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDI_SIB_DISP8 ((1 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | 4)
+
+#define RM_EAX_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xAX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ECX_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xCX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDX_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBX_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBX << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESP_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EBP_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xBP << X86_MODRM_REG_SHIFT) | 4)
+#define RM_ESI_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xSI << X86_MODRM_REG_SHIFT) | 4)
+#define RM_EDI_SIB_DISP32 ((2 << X86_MODRM_MOD_SHIFT) | (X86_GREG_xDI << X86_MODRM_REG_SHIFT) | 4)
+
+#define RM_XMM0_XMM1 ((3 << X86_MODRM_MOD_SHIFT) | (0 << X86_MODRM_REG_SHIFT) | 1)
+
+#define SIB_EBX_X1_NONE ((0 << X86_SIB_SCALE_SHIFT) | (4 << X86_SIB_INDEX_SHIFT) | (X86_GREG_xBX))
+#define SIB_EBX_X2_NONE ((1 << X86_SIB_SCALE_SHIFT) | (4 << X86_SIB_INDEX_SHIFT) | (X86_GREG_xBX))
+#define SIB_EBX_X4_NONE ((2 << X86_SIB_SCALE_SHIFT) | (4 << X86_SIB_INDEX_SHIFT) | (X86_GREG_xBX))
+#define SIB_EBX_X8_NONE ((3 << X86_SIB_SCALE_SHIFT) | (4 << X86_SIB_INDEX_SHIFT) | (X86_GREG_xBX))
+
+#define F_486 UINT16_C(0x0000)
+#define F_SSE2 UINT16_C(0x0001)
+#define F_SSE3 UINT16_C(0x0002)
+#define F_SSE42 UINT16_C(0x0004)
+#define F_MOVBE UINT16_C(0x0080)
+#define F_CBUD UINT16_C(0x4000)
+#define F_UD UINT16_C(0x8000)
+#define F_OK UINT16_C(0x0000)
+
+
+/**
+ * This is an exploratory testcase. It tries to figure out how exactly the
+ * different Intel and AMD CPUs implements SSE and similar instructions that
+ * uses the size, repz, repnz and lock prefixes in the encoding.
+ */
+CPUDECODE1TST const g_aSimpleTests[] =
+{
+ /*
+ * fFlags, cbUd, cbOpcodes, abOpcodes
+ */
+#if 0
+ /* Using currently undefined 0x0f 0x7a sequences. */
+ { F_UD, 3, { 0x0f, 0x7a, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_LK, 0x0f, 0x7a, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RZ, 0x0f, 0x7a, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RN, 0x0f, 0x7a, RM_EAX_EAX, } },
+ { F_UD, 3+2, { P_LK, P_LK, 0x0f, 0x7a, RM_EAX_EAX, } },
+ { F_UD, 4, { 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP8, 0 } },
+ { F_UD, 4+1, { P_LK, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP8, 0 } },
+ { F_UD, 4+1, { P_RZ, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP8, 0 } },
+ { F_UD, 4+1, { P_RN, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP8, 0 } },
+ { F_UD, 4+2, { P_LK, P_LK, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP8, 0 } },
+ { F_UD, 7, { 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 } },
+ { F_UD, 7+1, { P_LK, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 } },
+ { F_UD, 7+1, { P_RZ, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 } },
+ { F_UD, 7+1, { P_RN, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 } },
+ { F_UD, 7+2, { P_LK, P_LK, 0x0f, 0x7a, RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 } },
+#endif
+#if 0
+ /* Ditto for currently undefined sequence: 0x0f 0x7b */
+ { F_UD, 3, { 0x0f, 0x7b, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_LK, 0x0f, 0x7b, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RZ, 0x0f, 0x7b, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RN, 0x0f, 0x7b, RM_EAX_EAX, } },
+ { F_UD, 3+2, { P_LK, P_LK, 0x0f, 0x7b, RM_EAX_EAX, } },
+#endif
+#if 1
+ /* Ditto for currently undefined sequence: 0x0f 0x24 */
+ { F_UD, 3, { 0x0f, 0x24, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_LK, 0x0f, 0x24, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RZ, 0x0f, 0x24, RM_EAX_EAX, } },
+ { F_UD, 3+1, { P_RN, 0x0f, 0x24, RM_EAX_EAX, } },
+ { F_UD, 3+2, { P_LK, P_LK, 0x0f, 0x24, RM_EAX_EAX, } },
+#endif
+#if 0
+ /* The XADD instruction has empty lines for 66, f3 and f2 prefixes.
+ AMD doesn't do anything special for XADD Ev,Gv as the intel table would indicate. */
+ { F_486 | F_OK, 3, { 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 4, { P_OZ, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 4, { P_RZ, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 5, { P_OZ, P_RZ, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 5, { P_RZ, P_OZ, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 4, { P_RN, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 5, { P_OZ, P_RN, 0x0f, 0xc1, RM_EAX_EAX, } },
+ { F_486 | F_OK, 5, { P_RN, P_OZ, 0x0f, 0xc1, RM_EAX_EAX, } },
+#endif
+#if 0
+ /* The movnti instruction is confined to the unprefixed lined in the intel manuals. Check how the other lines work. */
+ { F_SSE2 | F_UD, 3, { 0x0f, 0xc3, RM_EAX_EAX, } }, /* invalid - reg,reg */
+ { F_SSE2 | F_OK, 3, { 0x0f, 0xc3, RM_EAX_DEREF_EBX, } },
+ { F_SSE2 | F_UD, 4, { P_OZ, 0x0f, 0xc3, RM_EAX_DEREF_EBX, } }, /* invalid */
+ { F_SSE2 | F_UD, 4, { P_RZ, 0x0f, 0xc3, RM_EAX_DEREF_EBX, } }, /* invalid */
+ { F_SSE2 | F_UD, 4, { P_RN, 0x0f, 0xc3, RM_EAX_DEREF_EBX, } }, /* invalid */
+ { F_SSE2 | F_UD, 4, { P_LK, 0x0f, 0xc3, RM_EAX_DEREF_EBX, } }, /* invalid */
+ { F_SSE2 | F_UD, 5, { P_RN, P_LK, 0x0f, 0xc3, RM_EAX_DEREF_EBX, } }, /* invalid */
+#endif
+#if 0
+ /* The lddqu instruction requires a 0xf2 prefix, intel only lists 0x66 and empty
+ prefix for it. Check what they really mean by that*/
+ { F_SSE3 | F_UD, 4, { P_RN, 0x0f, 0xf0, RM_EAX_EAX, } }, /* invalid - reg, reg */
+ { F_SSE3 | F_OK, 4, { P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 5, { P_RN, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 3, { 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 4, { P_RZ, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 4, { P_OZ, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 4, { P_LK, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 5, { P_RN, P_RZ, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 5, { P_RN, P_OZ, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } }, // AMD,why?
+ { F_SSE3 | F_UD, 5, { P_RN, P_LK, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 5, { P_RZ, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 5, { P_OZ, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_UD, 5, { P_LK, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 5, { P_OZ, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+ { F_SSE3 | F_OK, 6,{ P_OZ, P_RZ, P_RN, 0x0f, 0xf0, RM_EAX_DEREF_EBX, } },
+#endif
+#if 0
+ { F_SSE2 | F_OK, 3, { 0x0f, 0x7e, RM_EAX_EAX, } },
+ { F_SSE2 | F_OK, 4, { P_OZ, 0x0f, 0x7e, RM_EAX_EAX, } },
+ { F_SSE2 | F_UD, 5,{ P_RN, P_OZ, 0x0f, 0x7e, RM_EAX_EAX, } }, // WTF?
+ { F_SSE2 | F_UD, 5,{ P_OZ, P_RN, 0x0f, 0x7e, RM_EAX_EAX, } },
+ { F_SSE2 | F_OK, 5,{ P_RZ, P_OZ, 0x0f, 0x7e, RM_EAX_EAX, } },
+ { F_SSE2 | F_OK, 4, { P_RZ, 0x0f, 0x7e, RM_EAX_EAX, } },
+ { F_SSE2 | F_UD, 4, { P_RN, 0x0f, 0x7e, RM_EAX_EAX, } },
+#endif
+/** @todo crc32 / movbe */
+};
+
+void DecodeEdgeTest(void)
+{
+ /*
+ * Allocate and initialize a page pair
+ */
+ uint8_t BS3_FAR *pbPages;
+ pbPages = Bs3MemGuardedTestPageAlloc(BS3MEMKIND_FLAT32);
+ if (pbPages)
+ {
+ unsigned i;
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&TrapFrame, sizeof(TrapFrame));
+
+ ASMSetCR0((ASMGetCR0() & ~(X86_CR0_EM | X86_CR0_TS)) | X86_CR0_MP);
+ ASMSetCR4(ASMGetCR4() | X86_CR4_OSFXSR);
+
+ Bs3RegCtxSaveEx(&Ctx, BS3_MODE_CODE_32, 512);
+ Ctx.rbx.u64 = (uintptr_t)pbPages;
+
+ for (i = 0; i < RT_ELEMENTS(g_aSimpleTests); i++)
+ {
+ unsigned const cbOpcodes = g_aSimpleTests[i].cbOpcodes;
+ uint16_t const fFlags = g_aSimpleTests[i].fFlags;
+ unsigned cb;
+ /** @todo check if supported. */
+
+ /*
+ * Place the instruction exactly at the page boundrary and proceed to
+ * move it across it and check that we get #PFs then.
+ */
+ cb = cbOpcodes;
+ while (cb >= 1)
+ {
+ unsigned const cErrorsBefore = Bs3TestSubErrorCount();
+ uint8_t BS3_FAR *pbRip = &pbPages[X86_PAGE_SIZE - cb];
+ Bs3MemCpy(pbRip, &g_aSimpleTests[i].abOpcodes[0], cb);
+ Bs3RegCtxSetRipCsFromFlat(&Ctx, (uintptr_t)pbRip);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+#if 1
+ Bs3TestPrintf("\ni=%d cb=%#x (cbOpcodes=%#x fFlags=%#x)\n", i, cb, cbOpcodes, fFlags);
+// Bs3TrapPrintFrame(&TrapFrame);
+#endif
+ if (cb >= cbOpcodes && (g_aSimpleTests[i].fFlags & F_UD))
+ {
+ if (TrapFrame.bXcpt != X86_XCPT_UD)
+ Bs3TestFailedF("i=%d cb=%d cbOp=%d fFlags=%#x: expected #UD got %#x at %RX32\n",
+ i, cb, cbOpcodes, fFlags, TrapFrame.bXcpt, TrapFrame.Ctx.rip.u32);
+ }
+ else if (cb < cbOpcodes)
+ {
+ if (TrapFrame.bXcpt != X86_XCPT_PF)
+ Bs3TestFailedF("i=%d cb=%d cbOp=%d fFlags=%#x: expected #PF (on) got %#x at %RX32\n",
+ i, cb, cbOpcodes, fFlags, TrapFrame.bXcpt, TrapFrame.Ctx.rip.u32);
+ else if (TrapFrame.Ctx.rip.u32 != (uintptr_t)pbRip)
+ Bs3TestFailedF("i=%d cb=%d cbOp=%d fFlags=%#x: expected #PF rip of %p (on) got %#RX32\n",
+ i, cb, cbOpcodes, fFlags, pbRip, TrapFrame.Ctx.rip.u32);
+ }
+ else
+ {
+ if (TrapFrame.bXcpt != X86_XCPT_PF)
+ Bs3TestFailedF("i=%d cb=%d cbOp=%d fFlags=%#x: expected #PF (after) got %#x at %RX32\n",
+ i, cb, cbOpcodes, fFlags, TrapFrame.bXcpt, TrapFrame.Ctx.rip.u32);
+ else if (TrapFrame.Ctx.rip.u32 != (uintptr_t)&pbPages[X86_PAGE_SIZE])
+ Bs3TestFailedF("i=%d cb=%d cbOp=%d fFlags=%#x: expected #PF rip of %p (after) got %#RX32\n",
+ i, cb, cbOpcodes, fFlags, &pbPages[X86_PAGE_SIZE], TrapFrame.Ctx.rip.u32);
+ }
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TestPrintf(" %.*Rhxs", cb, &g_aSimpleTests[i].abOpcodes[0]);
+ if (cb < cbOpcodes)
+ Bs3TestPrintf("[%.*Rhxs]", cbOpcodes - cb, &g_aSimpleTests[i].abOpcodes[cb]);
+ Bs3TestPrintf("\n");
+ }
+
+ /* next */
+ cb--;
+ }
+ }
+
+ Bs3MemGuardedTestPageFree(pbPages);
+ }
+ else
+ Bs3TestFailed("Failed to allocate two pages!\n");
+
+ /*
+ * Test instruction sequences.
+ */
+
+
+}
+
+
+/**
+ * Undefined opcode test.
+ */
+typedef struct CPUDECODE1UDTST
+{
+ /** Type of undefined opcode decoding logic - UD_T_XXX. */
+ uint8_t enmType;
+ /** Core opcodes length. */
+ uint8_t cbOpcodes;
+ /** Core opcodes. */
+ uint8_t abOpcodes[5];
+ /** UD_F_XXX. */
+ uint8_t fFlags;
+} CPUDECODE1UDTST;
+typedef CPUDECODE1UDTST const BS3_FAR *PCCPUDECODE1UDTST;
+
+#define UD_T_EXACT 0
+#define UD_T_NOAMD 0x80 /**< AMD does not decode unnecessary bytes, Intel does. */
+#define UD_T_MODRM 1
+#define UD_T_MODRM_I8 2
+#define UD_T_MODRM_M 3
+#define UD_T_MODRM_M_I8 4
+#define UD_T_MODRM_RR0 0x10
+#define UD_T_MODRM_RR1 0x11
+#define UD_T_MODRM_RR2 0x12
+#define UD_T_MODRM_RR3 0x13
+#define UD_T_MODRM_RR4 0x14
+#define UD_T_MODRM_RR5 0x15
+#define UD_T_MODRM_RR6 0x16
+#define UD_T_MODRM_RR7 0x17
+#define UD_T_MODRM_RR0_I8 0x18
+#define UD_T_MODRM_RR1_I8 0x19
+#define UD_T_MODRM_RR2_I8 0x1a
+#define UD_T_MODRM_RR3_I8 0x1b
+#define UD_T_MODRM_RR4_I8 0x1c
+#define UD_T_MODRM_RR5_I8 0x1d
+#define UD_T_MODRM_RR6_I8 0x1e
+#define UD_T_MODRM_RR7_I8 0x1f
+#define UD_T_MODRM_MR0 0x20
+#define UD_T_MODRM_MR1 0x21
+#define UD_T_MODRM_MR2 0x22
+#define UD_T_MODRM_MR3 0x23
+#define UD_T_MODRM_MR4 0x24
+#define UD_T_MODRM_MR5 0x25
+#define UD_T_MODRM_MR6 0x26
+#define UD_T_MODRM_MR7 0x27
+#define UD_T_MODRM_MR0_I8 0x28
+#define UD_T_MODRM_MR1_I8 0x29
+#define UD_T_MODRM_MR2_I8 0x2a
+#define UD_T_MODRM_MR3_I8 0x2b
+#define UD_T_MODRM_MR4_I8 0x2c
+#define UD_T_MODRM_MR5_I8 0x2d
+#define UD_T_MODRM_MR6_I8 0x2e
+#define UD_T_MODRM_MR7_I8 0x2f
+
+#define UD_F_ANY_PFX 0
+#define UD_F_NOT_NO_PFX UINT8_C(0x01) /**< Must have some kind of prefix to be \#UD. */
+#define UD_F_NOT_OZ_PFX UINT8_C(0x02) /**< Skip the size prefix. */
+#define UD_F_NOT_RZ_PFX UINT8_C(0x04) /**< Skip the REPZ prefix. */
+#define UD_F_NOT_RN_PFX UINT8_C(0x08) /**< Skip the REPNZ prefix. */
+#define UD_F_NOT_LK_PFX UINT8_C(0x10) /**< Skip the LOCK prefix. */
+#define UD_F_3BYTE_ESC UINT8_C(0x20) /**< Unused 3 byte escape table. Test all 256 entries */
+
+/**
+ * Two byte opcodes.
+ */
+CPUDECODE1UDTST const g_aUdTest2Byte_0f[] =
+{
+#if 0
+ { UD_T_EXACT, 2, { 0x0f, 0x04 }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x0a }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x0c }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x0e }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x0f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x13 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x14 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x15 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x16 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x17 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ /** @todo figure when 0f 019 and 0f 0c-0f were made into NOPs. */
+ { UD_T_EXACT, 2, { 0x0f, 0x24 }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x25 }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x26 }, UD_F_ANY_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x27 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x28 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x29 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x2b }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x2e }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x2f }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_EXACT, 2, { 0x0f, 0x36 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x39, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM_I8, 3, { 0x0f, 0x3b, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM, 3, { 0x0f, 0x3c, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM, 3, { 0x0f, 0x3d, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM_I8, 3, { 0x0f, 0x3e, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM_I8, 3, { 0x0f, 0x3f, 0x00 }, UD_F_3BYTE_ESC | UD_F_ANY_PFX }, /* Three byte escape table, just unused. */
+ { UD_T_MODRM, 2, { 0x0f, 0x50 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x52 }, UD_F_NOT_NO_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x53 }, UD_F_NOT_NO_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x54 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x55 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x56 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x57 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x5b }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x60 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x61 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x62 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x63 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x64 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x65 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x66 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x67 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x68 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x69 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6a }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6b }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6d }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6e }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x6f }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM_M_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR0_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR1_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR2_I8, 2, { 0x0f, 0x71 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR3_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR4_I8, 2, { 0x0f, 0x71 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR5_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR6_I8, 2, { 0x0f, 0x71 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR7_I8, 2, { 0x0f, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_M_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR0_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR1_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR2_I8, 2, { 0x0f, 0x72 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR3_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR4_I8, 2, { 0x0f, 0x72 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR5_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR6_I8, 2, { 0x0f, 0x72 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR7_I8, 2, { 0x0f, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_M_I8, 2, { 0x0f, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR0_I8, 2, { 0x0f, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR1_I8, 2, { 0x0f, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR2_I8, 2, { 0x0f, 0x73 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR3_I8, 2, { 0x0f, 0x73 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR4_I8, 2, { 0x0f, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR5_I8, 2, { 0x0f, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR6_I8, 2, { 0x0f, 0x73 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_RR7_I8, 2, { 0x0f, 0x73 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x74 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x75 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x76 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ /* 0f 77: WTF? OZ, RZ and RN are all empty in the intel tables and LK isn't metnioned at all: */
+ { UD_T_MODRM, 2, { 0x0f, 0x77 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX | UD_F_NOT_RZ_PFX | UD_F_NOT_LK_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x78 }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x79 }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7a }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7c }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7d }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7e }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0x7f }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xa6 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xa7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR0, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* fxsave only checks REX.W */
+ { UD_T_MODRM_MR1, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* frstor ditto */
+ { UD_T_MODRM_MR2, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* ldmxcsr */
+ { UD_T_MODRM_MR3, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* stmxcsr */
+ { UD_T_MODRM_MR4, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* xsave */
+ { UD_T_MODRM_MR5, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* xrstor */
+ { UD_T_MODRM_MR6, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* xsaveopt */
+ { UD_T_MODRM_MR7, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX }, /* clflush (none) and clflushopt (66) */
+ { UD_T_MODRM_RR0, 2, { 0x0f, 0xae }, UD_F_ANY_PFX }, /* f3=rdfsbase is 64-bit */
+ { UD_T_MODRM_RR1, 2, { 0x0f, 0xae }, UD_F_ANY_PFX }, /* f3=rdfsbase is 64-bit */
+ { UD_T_MODRM_RR2, 2, { 0x0f, 0xae }, UD_F_ANY_PFX }, /* f3=rdfsbase is 64-bit */
+ { UD_T_MODRM_RR3, 2, { 0x0f, 0xae }, UD_F_ANY_PFX }, /* f3=rdfsbase is 64-bit */
+ { UD_T_MODRM_RR4, 2, { 0x0f, 0xae }, UD_F_ANY_PFX }, /* unused */
+ { UD_T_MODRM_RR5, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* 00=lfence */
+ { UD_T_MODRM_RR6, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* 00=mfence */
+ { UD_T_MODRM_RR7, 2, { 0x0f, 0xae }, UD_F_NOT_NO_PFX }, /* 00=sfence */
+ { UD_T_MODRM, 2, { 0x0f, 0xb8 }, UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM | UD_T_NOAMD, 2, { 0x0f, 0xb9 }, UD_F_ANY_PFX }, /* UD1 */
+ { UD_T_MODRM_MR0_I8, 2, { 0x0f, 0xba }, UD_F_ANY_PFX }, /* grp8 */
+ { UD_T_MODRM_MR1_I8, 2, { 0x0f, 0xba }, UD_F_ANY_PFX }, /* grp8 */
+ { UD_T_MODRM_MR2_I8, 2, { 0x0f, 0xba }, UD_F_ANY_PFX }, /* grp8 */
+ { UD_T_MODRM_MR3_I8, 2, { 0x0f, 0xba }, UD_F_ANY_PFX }, /* grp8 */
+ /** @todo f3 0f bb rm and f2 0f bb rm does stuff on skylake even if their are blank in intel and AMD tables! */
+ //{ UD_T_MODRM, 2, { 0x0f, 0xbb }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ /** @todo AMD tables indicates that f2 0f bc rm is invalid, but on skylake it works differently (BSF?) */
+ { UD_T_MODRM, 2, { 0x0f, 0xbc }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX /* figure: */ | UD_F_NOT_RN_PFX },
+ /** @todo AMD tables indicates that f3 0f bc rm is invalid, but on skylake it works differently (BSR?) */
+ { UD_T_MODRM, 2, { 0x0f, 0xbd }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX /* figure: */ | UD_F_NOT_RN_PFX },
+ /* Note! Intel incorrectly states that XADD (0f c0 and 0f c1) are sensitive to OZ, RN and RZ. AMD and skylake hw disagrees. */
+ { UD_T_MODRM, 2, { 0x0f, 0xc3 }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM_I8, 2, { 0x0f, 0xc4 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_I8, 2, { 0x0f, 0xc5 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM_I8, 2, { 0x0f, 0xc6 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+#endif
+ { UD_T_MODRM_MR0, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR0, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ //{ UD_T_MODRM_MR1, 2, { 0x0f, 0xc7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX | UD_F_NOT_RZ_PFX | UD_F_NOT_LK_PFX }, - cmpxchg8b ignores everything. @
+ { UD_T_MODRM_RR1, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR2, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR2, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR3, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR3, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR4, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR4, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR5, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_RR5, 2, { 0x0f, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM_MR6, 2, { 0x0f, 0xc7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX }, /* f2? */
+ { UD_T_MODRM_RR6, 2, { 0x0f, 0xc7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX }, /* (rdrand Rv) */
+ { UD_T_MODRM_MR7, 2, { 0x0f, 0xc7 }, UD_F_NOT_NO_PFX }, /* vmptrst Mq (f2?); */
+ { UD_T_MODRM_RR7, 2, { 0x0f, 0xc7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX }, /* rdrand Rv; rdpid Rd/q (f2,66??); */
+#if 0
+ { UD_T_MODRM, 2, { 0x0f, 0xd0 }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd1 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd2 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd3 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd4 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd5 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd6 }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd8 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xd9 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xda }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xdb }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xdc }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xdd }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xde }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xdf }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe0 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe1 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe2 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe3 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe4 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe5 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe6 }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX | UD_F_NOT_RZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe8 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xe9 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xea }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xeb }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xec }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xed }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xee }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xef }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf0 }, UD_F_NOT_RN_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf1 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf2 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf3 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf4 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf5 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf6 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf7 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf8 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xf9 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xfa }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xfb }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xfc }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xfd }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xfe }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 2, { 0x0f, 0xff }, UD_F_ANY_PFX },
+#endif
+};
+
+
+/**
+ * Three byte opcodes.
+ */
+CPUDECODE1UDTST const g_aUdTest3Byte_0f_38[] =
+{
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x00 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x01 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x02 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x03 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x04 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x05 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x06 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x07 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x08 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x09 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0a }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0b }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0d }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0e }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x0f }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x10 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x11 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x12 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x13 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x14 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x15 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x16 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x17 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x18 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x19 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1a }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1c }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1d }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1e }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x1f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x20 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x21 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x22 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x23 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x24 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x25 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x26 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x27 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x28 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x29 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2a }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2b }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2d }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2e }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x2f }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x30 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x31 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x32 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x33 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x34 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x35 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x36 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x37 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x38 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x39 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3a }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3b }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3d }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3e }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x3f }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x40 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x41 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x42 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x43 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x44 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x45 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x46 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x47 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x48 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x49 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4a }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4c }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4d }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4e }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x4f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x50 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x51 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x52 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x53 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x54 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x55 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x56 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x57 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x58 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x59 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5a }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5c }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5e }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5d }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x5f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x60 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x61 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x62 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x63 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x64 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x65 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x66 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x67 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x68 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x69 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6a }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6c }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6d }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6e }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x6f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x70 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x71 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x72 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x73 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x74 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x75 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x76 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x77 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x78 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x79 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7a }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7c }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7d }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7e }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x7f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x80 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x81 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x82 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x83 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x84 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x85 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x86 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x87 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x88 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x89 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8a }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8b }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8d }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8e }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x8f }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x90 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x91 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x92 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x93 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x94 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x95 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x96 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x97 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x98 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x99 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9a }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9b }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9c }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9d }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9e }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0x9f }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa0 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa1 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa2 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa3 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa6 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa7 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa8 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xa9 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xaa }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xab }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xac }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xad }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xae }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xaf }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb0 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb1 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb2 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb3 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb6 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb7 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb8 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xb9 }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xba }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xbb }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xbc }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xbd }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xbe }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xbf }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc0 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc1 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc2 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc3 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc6 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc8 }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xc9 }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xca }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xcb }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xcc }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xcd }, UD_F_NOT_NO_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xce }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xcf }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd0 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd1 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd2 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd3 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd6 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd8 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xd9 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xda }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xdb }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xdc }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xdd }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xde }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xdf }, UD_F_NOT_OZ_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe0 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe1 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe2 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe3 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe6 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe8 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xe9 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xea }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xeb }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xec }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xed }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xee }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xef }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf0 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX }, /// @todo crc32 weirdness
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf1 }, UD_F_NOT_NO_PFX | UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX }, /// @todo crc32 weirdness
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf2 }, UD_F_NOT_NO_PFX },
+
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf4 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf5 }, UD_F_NOT_NO_PFX | UD_F_NOT_RZ_PFX | UD_F_NOT_RN_PFX },
+
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf7 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf8 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xf9 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xfa }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xfb }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xfc }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xfd }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xfe }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 3, { 0x0f, 0x38, 0xff }, UD_F_ANY_PFX },
+
+ /* This is going to be interesting: */
+ { UD_T_MODRM, 5, { 0x66, 0xf2, 0x0f, 0x38, 0xf5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 5, { 0x66, 0xf3, 0x0f, 0x38, 0xf5 }, UD_F_ANY_PFX },
+ { UD_T_MODRM, 5, { 0x66, 0xf2, 0x0f, 0x38, 0xf6 }, UD_F_ANY_PFX },
+ //{ UD_T_MODRM, 5, { 0x66, 0xf3, 0x0f, 0x38, 0xf6 }, UD_F_ANY_PFX }, - not this one.
+};
+
+
+void DecodeUdEdgeTest(PCCPUDECODE1UDTST paTests, unsigned cTests)
+{
+ uint8_t BS3_FAR *pbPages;
+
+ /*
+ * Detect AMD.
+ */
+ bool fIsAmd = false;
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ fIsAmd = ASMIsAmdCpu() || ASMIsHygonCpu();
+ Bs3TestPrintf("fIsAmd=%d\n", fIsAmd);
+
+ /*
+ * Allocate and initialize a page pair
+ */
+ pbPages = Bs3MemGuardedTestPageAlloc(BS3MEMKIND_FLAT32);
+ if (pbPages)
+ {
+ unsigned iTest;
+ BS3REGCTX Ctx;
+ BS3REGCTX ExpectCtx;
+ BS3TRAPFRAME TrapFrame;
+ uint32_t iStep;
+
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&ExpectCtx, sizeof(ExpectCtx));
+ Bs3MemZero(&TrapFrame, sizeof(TrapFrame));
+
+ /* Enable SSE. */
+ ASMSetCR0((ASMGetCR0() & ~(X86_CR0_EM | X86_CR0_TS)) | X86_CR0_MP);
+ ASMSetCR4(ASMGetCR4() | X86_CR4_OSFXSR);
+
+ /* Create a test context. */
+ Bs3RegCtxSaveEx(&Ctx, BS3_MODE_CODE_32, 512);
+ Ctx.rbx.u = (uintptr_t)pbPages;
+ Ctx.rcx.u = (uintptr_t)pbPages;
+ Ctx.rdx.u = (uintptr_t)pbPages;
+ Ctx.rax.u = (uintptr_t)pbPages;
+ Ctx.rbp.u = (uintptr_t)pbPages;
+ Ctx.rsi.u = (uintptr_t)pbPages;
+ Ctx.rdi.u = (uintptr_t)pbPages;
+
+ Bs3MemCpy(&ExpectCtx, &Ctx, sizeof(ExpectCtx));
+ ExpectCtx.rflags.u32 |= X86_EFL_RF;
+
+ /* Loop thru the tests. */
+ iStep = g_usBs3TestStep = 0;
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ typedef struct CPUDECODE1UDSEQ
+ {
+ uint8_t cb;
+ uint8_t ab[10];
+ uint8_t fIncompatible;
+ } CPUDECODE1UDSEQ;
+ typedef CPUDECODE1UDSEQ const BS3_FAR *PCCPUDECODE1UDSEQ;
+
+ static CPUDECODE1UDSEQ const s_aPrefixes[] =
+ {
+ { 0, { 0 }, UD_F_NOT_NO_PFX },
+ { 1, { P_OZ }, UD_F_NOT_OZ_PFX },
+ { 1, { P_RN }, UD_F_NOT_RN_PFX },
+ { 1, { P_RZ }, UD_F_NOT_RZ_PFX },
+ { 1, { P_LK }, UD_F_NOT_LK_PFX },
+ { 2, { P_OZ, P_OZ }, UD_F_NOT_OZ_PFX | UD_F_NOT_OZ_PFX },
+ { 2, { P_RN, P_OZ }, UD_F_NOT_RN_PFX | UD_F_NOT_OZ_PFX },
+ { 2, { P_RZ, P_OZ }, UD_F_NOT_RZ_PFX | UD_F_NOT_OZ_PFX },
+ { 2, { P_LK, P_OZ }, UD_F_NOT_LK_PFX | UD_F_NOT_OZ_PFX },
+ { 2, { P_OZ, P_RN }, UD_F_NOT_OZ_PFX | UD_F_NOT_RN_PFX },
+ { 2, { P_RN, P_RN }, UD_F_NOT_RN_PFX | UD_F_NOT_RN_PFX },
+ { 2, { P_RZ, P_RN }, UD_F_NOT_RZ_PFX | UD_F_NOT_RN_PFX },
+ { 2, { P_LK, P_RN }, UD_F_NOT_LK_PFX | UD_F_NOT_RN_PFX },
+ { 2, { P_OZ, P_RZ }, UD_F_NOT_OZ_PFX | UD_F_NOT_RZ_PFX },
+ { 2, { P_RN, P_RZ }, UD_F_NOT_RN_PFX | UD_F_NOT_RZ_PFX },
+ { 2, { P_RZ, P_RZ }, UD_F_NOT_RZ_PFX | UD_F_NOT_RZ_PFX },
+ { 2, { P_LK, P_RZ }, UD_F_NOT_LK_PFX | UD_F_NOT_RZ_PFX },
+ { 2, { P_OZ, P_LK }, UD_F_NOT_OZ_PFX | UD_F_NOT_LK_PFX },
+ { 2, { P_RN, P_LK }, UD_F_NOT_RN_PFX | UD_F_NOT_LK_PFX },
+ { 2, { P_RZ, P_LK }, UD_F_NOT_RZ_PFX | UD_F_NOT_LK_PFX },
+ { 2, { P_LK, P_LK }, UD_F_NOT_LK_PFX | UD_F_NOT_LK_PFX },
+ };
+
+ static CPUDECODE1UDSEQ const s_aExact[] = { { 0, { 0 }, 0 } };
+ static CPUDECODE1UDSEQ const s_aModRm[] =
+ {
+ { 1, { RM_EAX_EAX, }, 0 },
+ /* Mem forms (hardcoded indexed later): */
+ { 2, { RM_EAX_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EAX_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EAX_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EAX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+ };
+ static CPUDECODE1UDSEQ const s_aModRmImm8[] =
+ {
+ { 1 + 1, { RM_EAX_EAX, 0x11 }, 0 },
+ /* Mem forms (hardcoded indexed later): */
+ { 2 + 1, { RM_EAX_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5 + 1, { RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2 + 1, { RM_EAX_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3 + 1, { RM_EAX_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6 + 1, { RM_EAX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+ };
+ static CPUDECODE1UDSEQ const s_aModRmRRx[] =
+ {
+ { 1, { RM_EAX_EAX, }, 0 },
+ { 1, { RM_ECX_EAX, }, 0 },
+ { 1, { RM_EDX_EAX, }, 0 },
+ { 1, { RM_EBX_EAX, }, 0 },
+ { 1, { RM_ESP_EAX, }, 0 },
+ { 1, { RM_EBP_EAX, }, 0 },
+ { 1, { RM_ESI_EAX, }, 0 },
+ { 1, { RM_EDI_EAX, }, 0 },
+ };
+ static CPUDECODE1UDSEQ const s_aModRmRRxImm8[] =
+ {
+ { 2, { RM_EAX_EAX, 0x11 }, 0 },
+ { 2, { RM_ECX_EAX, 0x11 }, 0 },
+ { 2, { RM_EDX_EAX, 0x11 }, 0 },
+ { 2, { RM_EBX_EAX, 0x11 }, 0 },
+ { 2, { RM_ESP_EAX, 0x11 }, 0 },
+ { 2, { RM_EBP_EAX, 0x11 }, 0 },
+ { 2, { RM_ESI_EAX, 0x11 }, 0 },
+ { 2, { RM_EDI_EAX, 0x11 }, 0 },
+ };
+ static CPUDECODE1UDSEQ const s_aModRmMRx[] = /* index*5 */
+ {
+ { 2, { RM_EAX_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EAX_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EAX_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EAX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_ECX_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_ECX_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_ECX_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_ECX_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_ECX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_EDX_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EDX_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EDX_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EDX_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EDX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_EBX_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EBX_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EBX_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EBX_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EBX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_ESP_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_ESP_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_ESP_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_ESP_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_ESP_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_EBP_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EBP_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EBP_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EBP_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EBP_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_ESI_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_ESI_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_ESI_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_ESI_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_ESI_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+
+ { 2, { RM_EDI_DEREF_EBX_DISP8, 0 }, 0 },
+ { 5, { RM_EDI_DEREF_EBX_DISP32, 0, 0, 0, 0 }, 0 },
+ { 2, { RM_EDI_SIB, SIB_EBX_X1_NONE, }, 0 },
+ { 3, { RM_EDI_SIB_DISP8, SIB_EBX_X1_NONE, 0 }, 0 },
+ { 6, { RM_EDI_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0 }, 0 },
+ };
+ static CPUDECODE1UDSEQ const s_aModRmMRxImm8[] = /* index*5 */
+ {
+ { 2+1, { RM_EAX_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_EAX_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_EAX_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_EAX_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_EAX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_ECX_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_ECX_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_ECX_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_ECX_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_ECX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_EDX_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_EDX_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_EDX_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_EDX_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_EDX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_EBX_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_EBX_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_EBX_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_EBX_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_EBX_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_ESP_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_ESP_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_ESP_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_ESP_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_ESP_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_EBP_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_EBP_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_EBP_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_EBP_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_EBP_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_ESI_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_ESI_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_ESI_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_ESI_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_ESI_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+
+ { 2+1, { RM_EDI_DEREF_EBX_DISP8, 0, 0x11 }, 0 },
+ { 5+1, { RM_EDI_DEREF_EBX_DISP32, 0, 0, 0, 0, 0x11 }, 0 },
+ { 2+1, { RM_EDI_SIB, SIB_EBX_X1_NONE, 0x11 }, 0 },
+ { 3+1, { RM_EDI_SIB_DISP8, SIB_EBX_X1_NONE, 0, 0x11 }, 0 },
+ { 6+1, { RM_EDI_SIB_DISP32, SIB_EBX_X1_NONE, 0, 0, 0, 0, 0x11 }, 0 },
+ };
+ unsigned iPrefix;
+ unsigned cSuffixes;
+ PCCPUDECODE1UDSEQ paSuffixes;
+ unsigned const cSubTabEntries = paTests[iTest].fFlags & UD_F_3BYTE_ESC ? 256 : 1;
+ unsigned cImmEntries = 1;
+
+ /*
+ * Skip if implemented.
+ */
+
+ /*
+ * Produce a number of opcode sequences by varying the prefixes and
+ * ModR/M parts. Each opcode sequence is then treated to the edge test.
+ */
+ switch (paTests[iTest].enmType)
+ {
+ case UD_T_EXACT:
+ l_case_exact:
+ cSuffixes = RT_ELEMENTS(s_aExact);
+ paSuffixes = s_aExact;
+ break;
+ case UD_T_MODRM | UD_T_NOAMD:
+ if (fIsAmd)
+ goto l_case_exact;
+ case UD_T_MODRM:
+ cSuffixes = RT_ELEMENTS(s_aModRm);
+ paSuffixes = s_aModRm;
+ break;
+ case UD_T_MODRM_I8:
+ cSuffixes = RT_ELEMENTS(s_aModRmImm8);
+ paSuffixes = s_aModRmImm8;
+ cImmEntries = 256;
+ break;
+ case UD_T_MODRM_M:
+ cSuffixes = RT_ELEMENTS(s_aModRm) - 1;
+ paSuffixes = &s_aModRm[1];
+ break;
+ case UD_T_MODRM_M_I8:
+ cSuffixes = RT_ELEMENTS(s_aModRmImm8) - 1;
+ paSuffixes = &s_aModRmImm8[1];
+ break;
+ case UD_T_MODRM_RR0:
+ case UD_T_MODRM_RR1:
+ case UD_T_MODRM_RR2:
+ case UD_T_MODRM_RR3:
+ case UD_T_MODRM_RR4:
+ case UD_T_MODRM_RR5:
+ case UD_T_MODRM_RR6:
+ case UD_T_MODRM_RR7:
+ cSuffixes = 1;
+ paSuffixes = &s_aModRmRRx[paTests[iTest].enmType - UD_T_MODRM_RR0];
+ break;
+ case UD_T_MODRM_RR0_I8:
+ case UD_T_MODRM_RR1_I8:
+ case UD_T_MODRM_RR2_I8:
+ case UD_T_MODRM_RR3_I8:
+ case UD_T_MODRM_RR4_I8:
+ case UD_T_MODRM_RR5_I8:
+ case UD_T_MODRM_RR6_I8:
+ case UD_T_MODRM_RR7_I8:
+ cSuffixes = 1;
+ paSuffixes = &s_aModRmRRxImm8[paTests[iTest].enmType - UD_T_MODRM_RR0_I8];
+ break;
+ case UD_T_MODRM_MR0:
+ case UD_T_MODRM_MR1:
+ case UD_T_MODRM_MR2:
+ case UD_T_MODRM_MR3:
+ case UD_T_MODRM_MR4:
+ case UD_T_MODRM_MR5:
+ case UD_T_MODRM_MR6:
+ case UD_T_MODRM_MR7:
+ cSuffixes = 5;
+ paSuffixes = &s_aModRmMRx[(paTests[iTest].enmType - UD_T_MODRM_MR0) * 5];
+ break;
+ case UD_T_MODRM_MR0_I8:
+ case UD_T_MODRM_MR1_I8:
+ case UD_T_MODRM_MR2_I8:
+ case UD_T_MODRM_MR3_I8:
+ case UD_T_MODRM_MR4_I8:
+ case UD_T_MODRM_MR5_I8:
+ case UD_T_MODRM_MR6_I8:
+ case UD_T_MODRM_MR7_I8:
+ cSuffixes = 5;
+ paSuffixes = &s_aModRmMRxImm8[(paTests[iTest].enmType - UD_T_MODRM_MR0_I8) * 5];
+ break;
+ default:
+ Bs3TestPrintf("#%u: enmType=%d\n", paTests[iTest].enmType);
+ continue;
+ }
+
+ for (iPrefix = 0; iPrefix < RT_ELEMENTS(s_aPrefixes); iPrefix++)
+ if (!(s_aPrefixes[iPrefix].fIncompatible & paTests[iTest].fFlags))
+ {
+ unsigned iSubTab;
+ unsigned cbOpcodesLead;
+ uint8_t abOpcodes[32];
+
+ Bs3MemCpy(&abOpcodes[0], &s_aPrefixes[iPrefix].ab[0], s_aPrefixes[iPrefix].cb);
+ cbOpcodesLead = s_aPrefixes[iPrefix].cb;
+ Bs3MemCpy(&abOpcodes[cbOpcodesLead], &paTests[iTest].abOpcodes[0], paTests[iTest].cbOpcodes);
+ cbOpcodesLead += paTests[iTest].cbOpcodes;
+
+ for (iSubTab = 0; iSubTab < cSubTabEntries; iSubTab++)
+ {
+ unsigned iSuffix;
+
+ if (cSubTabEntries > 1)
+ abOpcodes[cbOpcodesLead - 1] = iSubTab;
+
+ for (iSuffix = 0; iSuffix < cSuffixes; iSuffix++)
+ if (!(paSuffixes[iSuffix].fIncompatible & paTests[iTest].fFlags))
+ {
+ unsigned const cbOpcodes = cbOpcodesLead + paSuffixes[iSuffix].cb;
+ unsigned cbOpcodesMin = 1;
+ unsigned iImm;
+ Bs3MemCpy(&abOpcodes[cbOpcodesLead], paSuffixes[iSuffix].ab, paSuffixes[iSuffix].cb);
+
+ for (iImm = 0; iImm < cImmEntries; iImm++)
+ {
+ unsigned cb;
+
+ if (cImmEntries > 1)
+ abOpcodes[cbOpcodes - 1] = iImm;
+
+ /*
+ * Do the edge thing.
+ */
+ cb = cbOpcodes;
+ while (cb >= cbOpcodesMin)
+ {
+ uint8_t BS3_FAR *pbRip = &pbPages[X86_PAGE_SIZE - cb];
+ uint8_t bXcptExpected;
+
+ Bs3RegCtxSetRipCsFromFlat(&Ctx, (uintptr_t)pbRip);
+ ExpectCtx.rip = Ctx.rip;
+ ExpectCtx.cs = Ctx.cs;
+ if (cb >= cbOpcodes)
+ {
+ ExpectCtx.cr2 = Ctx.cr2;
+ bXcptExpected = X86_XCPT_UD;
+ }
+ else
+ {
+ ExpectCtx.cr2.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+ bXcptExpected = X86_XCPT_PF;
+ }
+
+ Bs3MemCpy(pbRip, &abOpcodes[0], cb);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+#if 0
+ Bs3TestPrintf("iTest=%d iPrefix=%d (%d/%#x) iSubTab=%d iSuffix=%d (%d/%#x) iImm=%d cb=%d cbOp=%d: %.*Rhxs\n",
+ iTest, iPrefix, s_aPrefixes[iPrefix].cb, s_aPrefixes[iPrefix].fIncompatible,
+ iSubTab, iSuffix, paSuffixes[iSuffix].cb, paSuffixes[iSuffix].fIncompatible, iImm,
+ cb, cbOpcodes,
+ cbOpcodes, abOpcodes);
+#endif
+
+ if ( !Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &ExpectCtx, 0 /*cbPcAdjust*/,
+ 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "mode", 0)
+ || TrapFrame.bXcpt != bXcptExpected)
+ {
+ Bs3TestFailedF("iTest=%d iPrefix=%d (%d/%#x) iSubTab=%u iSuffix=%d (%d/%#x) cb=%d cbOp=%d: %.*Rhxs\n",
+ iTest, iPrefix, s_aPrefixes[iPrefix].cb, s_aPrefixes[iPrefix].fIncompatible,
+ iSubTab, iSuffix, paSuffixes[iSuffix].cb, paSuffixes[iSuffix].fIncompatible,
+ cb, cbOpcodes,
+ cbOpcodes, abOpcodes);
+ if (TrapFrame.bXcpt != bXcptExpected)
+ Bs3TestFailedF("Expected bXcpt=%#x got %#x\n", bXcptExpected, TrapFrame.bXcpt);
+ Bs3TrapPrintFrame(&TrapFrame);
+ Bs3Shutdown();
+ }
+
+ /* next */
+ g_usBs3TestStep++;
+ iStep++;
+ cb--;
+ }
+
+ /* For iImm > 0 only test cb == cbOpcode since the byte isn't included when cb < cbOpcode. */
+ cbOpcodesMin = cbOpcodes;
+ }
+ }
+ }
+ }
+ }
+ Bs3TestPrintf("%RI32 (%#RX32) test steps\n", iStep, iStep);
+
+ Bs3MemGuardedTestPageFree(pbPages);
+ }
+ else
+ Bs3TestFailed("Failed to allocate two pages!\n");
+}
+
+
+#if 0
+/**
+ * Checks how prefixes affects cmpxchg8b and cmpxchg16b
+ *
+ * The thing here is that the intel opcode tables indicates that the 66 and f3
+ * prefixes encodings are reserved and causes \#UD, where AMD doesn't. Seems
+ * though that the f2, f3 and 66 prefixes are ignored on skylake intel. Need to
+ * make sure this is the case, also in 64-bit mode and for the 16b version.
+ */
+static void DecodeCmpXchg8bVs16b(void)
+{
+ uint8_t BS3_FAR *pbPages;
+
+ /* Check that the instructions are supported. */
+ if ( !(g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ || !(ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_CX8))
+ {
+ Bs3TestSkipped("not supported");
+ return;
+ }
+
+ /* Setup a guarded page. */
+ pbPages = Bs3MemGuardedTestPageAlloc(BS3MEMKIND_FLAT32);
+ if (pbPages)
+ {
+
+ Bs3MemGuardedTestPageFree(pbPages);
+ }
+ else
+ Bs3TestFailed("Failed to allocate two pages!\n");
+}
+#endif
+
+
+/**
+ * Checks various prefix encodings with the MOVBE and CRC32 instructions to try
+ * figure out how they are decoded.
+ *
+ * The issue here is that both MOVBE and CRC32 are sensitive to the operand size
+ * prefix, which helps us identify whether the F2h and F3h prefixes takes
+ * precedence over 66h in this case. (As it turned out they do and it order
+ * doesn't matter.)
+ */
+static void DecodeMovbeVsCrc32(void)
+{
+ uint8_t BS3_FAR *pbPages;
+
+ /* Check that the instructions are supported. */
+ if ( !(g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ || (ASMCpuId_ECX(1) & (X86_CPUID_FEATURE_ECX_MOVBE | X86_CPUID_FEATURE_ECX_SSE4_2))
+ != (X86_CPUID_FEATURE_ECX_MOVBE | X86_CPUID_FEATURE_ECX_SSE4_2) )
+ {
+ Bs3TestSkipped("not supported");
+ return;
+ }
+
+ /* Setup a guarded page. */
+ pbPages = Bs3MemGuardedTestPageAlloc(BS3MEMKIND_FLAT32);
+ if (pbPages)
+ {
+ unsigned iTest;
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ BS3REGCTX ExpectCtxMovbe_m32_eax; /* 0f 38 f1 /r */
+ BS3REGCTX ExpectCtxMovbe_m16_ax; /* 66 0f 38 f1 /r */
+ BS3REGCTX ExpectCtxCrc32_eax_m32; /* f2 0f 38 f1 /r */
+ BS3REGCTX ExpectCtxCrc32_eax_m16; /* 66 f2 0f 38 f1 /r */
+ BS3REGCTX ExpectCtxUd;
+ PBS3REGCTX apExpectCtxs[5];
+ static const struct
+ {
+ uint32_t u32Stored;
+ uint8_t iExpectCtx;
+ uint8_t bXcpt;
+ uint8_t cbOpcodes;
+ uint8_t abOpcodes[18];
+ } s_aTests[] =
+ {
+#define BECRC_EAX UINT32_C(0x11223344)
+#define BECRC_MEM_ORG UINT32_C(0x55667788)
+#define BECRC_MEM_BE16 UINT32_C(0x55664433)
+#define BECRC_MEM_BE32 UINT32_C(0x44332211)
+
+ /* base forms. */
+ { BECRC_MEM_BE32, 0, X86_XCPT_PF, 4, { 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_BE16, 1, X86_XCPT_PF, 5, { P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 2, X86_XCPT_PF, 5, { P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 6, { P_OZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 4, X86_XCPT_UD, 5, { P_RZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } }, /* undefined F3 (P_RZ) */
+ { BECRC_MEM_ORG, 4, X86_XCPT_UD, 6, { P_OZ, P_RZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } }, /* undefined F3 (P_RZ) */
+
+ /* CRC32 eax, [word ebx]: Simple variations showing it doesn't matter where the prefixes are placed. */
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 6, { P_RN, P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 7, { P_RN, P_OZ, P_ES, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_RN, P_SS, P_OZ, P_ES, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_RN, P_SS, P_ES, P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_SS, P_RN, P_ES, P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_SS, P_ES, P_RN, P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_SS, P_ES, P_OZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_SS, P_OZ, P_ES, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_OZ, P_SS, P_ES, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+
+ /* CRC32 eax, [word ebx]: Throw the F3h prefix into the mix. The last of F3 and F2 wins on skylake+jaguar. */
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 7, { P_RZ, P_OZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 7, { P_OZ, P_RZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 4, X86_XCPT_UD, 7, { P_OZ, P_RN, P_RZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_OZ, P_RN, P_RZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_RN, P_RZ, P_OZ, P_RN, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ { BECRC_MEM_ORG, 3, X86_XCPT_PF, 8, { P_RN, P_RZ, P_RN, P_OZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+
+ { BECRC_MEM_ORG, 4, X86_XCPT_UD, 7, { P_OZ, P_RN, P_RZ, 0x0f, 0x38, 0xf1, RM_EAX_DEREF_EBX } },
+ };
+
+ apExpectCtxs[0] = &ExpectCtxMovbe_m32_eax;
+ apExpectCtxs[1] = &ExpectCtxMovbe_m16_ax;
+ apExpectCtxs[2] = &ExpectCtxCrc32_eax_m32;
+ apExpectCtxs[3] = &ExpectCtxCrc32_eax_m16;
+ apExpectCtxs[4] = &ExpectCtxUd;
+
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&ExpectCtxMovbe_m32_eax, sizeof(ExpectCtxMovbe_m32_eax));
+ Bs3MemZero(&ExpectCtxMovbe_m16_ax, sizeof(ExpectCtxMovbe_m16_ax));
+ Bs3MemZero(&ExpectCtxCrc32_eax_m32, sizeof(ExpectCtxCrc32_eax_m32));
+ Bs3MemZero(&ExpectCtxCrc32_eax_m16, sizeof(ExpectCtxCrc32_eax_m16));
+ Bs3MemZero(&ExpectCtxUd, sizeof(ExpectCtxUd));
+ Bs3MemZero(&TrapFrame, sizeof(TrapFrame));
+
+ /* Create a test context. */
+ Bs3RegCtxSaveEx(&Ctx, BS3_MODE_CODE_32, 512);
+ Ctx.rax.u = BECRC_EAX;
+ Ctx.rbx.u = (uintptr_t)pbPages;
+
+ /* Create expected result contexts. */
+ Bs3MemCpy(&ExpectCtxMovbe_m32_eax, &Ctx, sizeof(ExpectCtxMovbe_m32_eax));
+ ExpectCtxMovbe_m32_eax.rflags.u32 |= X86_EFL_RF;
+ ExpectCtxMovbe_m32_eax.rip.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+ ExpectCtxMovbe_m32_eax.cr2.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+
+ Bs3MemCpy(&ExpectCtxMovbe_m16_ax, &ExpectCtxMovbe_m32_eax, sizeof(ExpectCtxMovbe_m16_ax));
+
+ Bs3MemCpy(&ExpectCtxCrc32_eax_m32, &Ctx, sizeof(ExpectCtxCrc32_eax_m32));
+ ExpectCtxCrc32_eax_m32.rflags.u32 |= X86_EFL_RF;
+ ExpectCtxCrc32_eax_m32.rip.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+ ExpectCtxCrc32_eax_m32.cr2.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+ ExpectCtxCrc32_eax_m32.rax.u32 = 0x1aa7cd75;
+ Bs3MemCpy(&ExpectCtxCrc32_eax_m16, &ExpectCtxCrc32_eax_m32, sizeof(ExpectCtxCrc32_eax_m16));
+ ExpectCtxCrc32_eax_m16.rax.u32 = 0x51ab0518;
+
+ Bs3MemCpy(&ExpectCtxUd, &Ctx, sizeof(ExpectCtxUd));
+ ExpectCtxUd.rflags.u32 |= X86_EFL_RF;
+
+ /* Loop thru the tests. */
+ g_usBs3TestStep = 0;
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ unsigned const cbOpcodes = s_aTests[iTest].cbOpcodes;
+ uint8_t BS3_FAR *pbRip = &pbPages[X86_PAGE_SIZE - cbOpcodes];
+
+ Bs3MemCpy(pbRip, s_aTests[iTest].abOpcodes, cbOpcodes);
+ Bs3RegCtxSetRipCsFromFlat(&Ctx, (uintptr_t)pbRip);
+ *(uint32_t *)pbPages = BECRC_MEM_ORG;
+
+#if 0
+ Bs3TestPrintf("iTest=%d pbRip=%p cbOpcodes=%d: %.*Rhxs\n",
+ iTest, pbRip, cbOpcodes, cbOpcodes, s_aTests[iTest].abOpcodes);
+ //Bs3RegCtxPrint(&Ctx);
+#endif
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ if (s_aTests[iTest].bXcpt == X86_XCPT_UD)
+ ExpectCtxUd.rip = Ctx.rip;
+ if ( !Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, apExpectCtxs[s_aTests[iTest].iExpectCtx],
+ 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "mode", iTest)
+ || TrapFrame.bXcpt != s_aTests[iTest].bXcpt
+ || *(uint32_t *)pbPages != s_aTests[iTest].u32Stored)
+ {
+ Bs3TestFailedF("iTest=%d cbOpcodes=%d: %.*Rhxs\n", iTest, cbOpcodes, cbOpcodes, s_aTests[iTest].abOpcodes);
+ if (TrapFrame.bXcpt != s_aTests[iTest].bXcpt)
+ Bs3TestFailedF("Expected bXcpt=%#x, got %#x\n", s_aTests[iTest].bXcpt, TrapFrame.bXcpt);
+ if (*(uint32_t *)pbPages != s_aTests[iTest].u32Stored)
+ Bs3TestFailedF("Expected %#RX32 stored at %p, found: %RX32\n",
+ s_aTests[iTest].u32Stored, pbPages, *(uint32_t *)pbPages);
+ }
+ }
+
+ Bs3MemGuardedTestPageFree(pbPages);
+ }
+ else
+ Bs3TestFailed("Failed to allocate two pages!\n");
+}
+
+
+
+/**
+ * Checks various prefix encodings with the CMPPS, CMPPD, CMPSS and CMPSD
+ * instructions to try figure out how they are decoded.
+ *
+ * The important thing to check here is that unlike CRC32/MOVBE the operand size
+ * prefix (66h) is ignored when the F2h and F3h prefixes are used. We also
+ * check that the prefix ordering is irrelevant and that the last one of F2h and
+ * F3h wins.
+ */
+static void DecodeCmppsCmppdCmpssCmpsd(void)
+{
+ uint8_t BS3_FAR *pbPages;
+
+ /* Check that the instructions are supported. */
+ if ( !(g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ || (ASMCpuId_EDX(1) & (X86_CPUID_FEATURE_EDX_SSE | X86_CPUID_FEATURE_EDX_SSE2))
+ != (X86_CPUID_FEATURE_EDX_SSE | X86_CPUID_FEATURE_EDX_SSE2) )
+ {
+ Bs3TestSkipped("SSE and/or SSE2 are not supported");
+ return;
+ }
+
+ /* Setup a guarded page. */
+ pbPages = Bs3MemGuardedTestPageAlloc(BS3MEMKIND_FLAT32);
+ if (pbPages)
+ {
+ unsigned iTest;
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ BS3REGCTX ExpectCtxPf;
+ BS3REGCTX ExpectCtxUd;
+ static const struct
+ {
+ RTUINT128U Xmm0Expect;
+ uint8_t bXcpt;
+ uint8_t cbOpcodes;
+ uint8_t abOpcodes[18];
+ } s_aTests[] =
+ {
+#define BECRC_IN_XMM1 RTUINT128_INIT_C(0x76547654bbaa9988, 0x7766554433221100)
+#define BECRC_IN_XMM0 RTUINT128_INIT_C(0x765476549988bbaa, 0x7766554400112233)
+#define BECRC_OUT_PS RTUINT128_INIT_C(0xffffffff00000000, 0xffffffff00000000) /* No prefix. */
+#define BECRC_OUT_PD RTUINT128_INIT_C(0x0000000000000000, 0x0000000000000000) /* P_OZ (66h) */
+#define BECRC_OUT_SS RTUINT128_INIT_C(0x765476549988bbaa, 0x7766554400000000) /* P_RZ (f3h) */
+#define BECRC_OUT_SD RTUINT128_INIT_C(0x765476549988bbaa, 0x0000000000000000) /* P_RN (f2h) */
+
+ /* We use imm8=0 which checks for equality, with the subvalue result being all
+ F's if equal and all zeros if not equal. The input values are choosen such
+ that the 4 variants produces different results in xmm0. */
+ /* CMPPS xmm0, xmm1, 0: 0f c2 /r ib ; Compares four 32-bit subvalues. */
+ /* CMPPD xmm0, xmm1, 0: 66 0f c2 /r ib ; Compares two 64-bit subvalues. */
+ /* CMPSS xmm0, xmm1, 0: f3 0f c2 /r ib ; Compares two 32-bit subvalues, top 64-bit remains unchanged. */
+ /* CMPSD xmm0, xmm1, 0: f2 0f c2 /r ib ; Compares one 64-bit subvalue, top 64-bit remains unchanged. */
+
+ /* base forms. */
+ { BECRC_OUT_PS, X86_XCPT_PF, 4, { 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 5, { P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 5, { P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 5, { P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ /* Skylake+jaguar ignores the 66h prefix with both f3h (P_RZ) and f2h (P_RN). */
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_OZ, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_RZ, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_OZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_RN, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ /* Throw in segment prefixes and address size prefixes. */
+ { BECRC_OUT_PS, X86_XCPT_PF, 5, { P_ES, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PS, X86_XCPT_PF, 6, { P_ES, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PS, X86_XCPT_PF, 5, { P_AZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PS, X86_XCPT_PF, 6, { P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ { BECRC_OUT_PD, X86_XCPT_PF, 6, { P_ES, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 6, { P_OZ, P_ES, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_ES, P_SS, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_ES, P_OZ, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_OZ, P_ES, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 6, { P_AZ, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 6, { P_OZ, P_AZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_AZ, P_CS, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_AZ, P_OZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_PD, X86_XCPT_PF, 7, { P_OZ, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_ES, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_RZ, P_ES, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_ES, P_SS, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_ES, P_RZ, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_RZ, P_ES, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_AZ, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_RZ, P_AZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_AZ, P_CS, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_AZ, P_RZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_RZ, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_OZ, P_RZ, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RZ, P_OZ, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RZ, P_AZ, P_OZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RZ, P_AZ, P_CS, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_ES, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_RN, P_ES, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_ES, P_SS, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_ES, P_RN, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_RN, P_ES, P_SS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_AZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_RN, P_AZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_AZ, P_CS, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_AZ, P_RN, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_RN, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_OZ, P_RN, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RN, P_OZ, P_AZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RN, P_AZ, P_OZ, P_CS, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RN, P_AZ, P_CS, P_OZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ /* Pit f2h against f3h, on skylake+jaguar the last prefix wins. */
+ { BECRC_OUT_SS, X86_XCPT_PF, 6, { P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_RN, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_RZ, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 7, { P_RN, P_RZ, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RN, P_RN, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RN, P_RN, P_RZ, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RN, P_RZ, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RZ, P_RN, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RZ, P_RZ, P_RN, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SS, X86_XCPT_PF, 8, { P_RN, P_RZ, P_RZ, P_RZ, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+
+ { BECRC_OUT_SD, X86_XCPT_PF, 6, { P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_RZ, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_RN, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 7, { P_RZ, P_RN, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RZ, P_RZ, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RZ, P_RZ, P_RN, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RZ, P_RN, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RN, P_RZ, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RN, P_RN, P_RZ, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ { BECRC_OUT_SD, X86_XCPT_PF, 8, { P_RZ, P_RN, P_RN, P_RN, 0x0f, 0xc2, RM_XMM0_XMM1, 0 } },
+ };
+ RTUINT128U InXmm0 = BECRC_IN_XMM0;
+ RTUINT128U InXmm1 = BECRC_IN_XMM1;
+ RTUINT128U OutXmm0 = RTUINT128_INIT_C(0xeeeeeeeeeeeeeeee, 0xcccccccccccccccc);
+
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&ExpectCtxPf, sizeof(ExpectCtxPf));
+ Bs3MemZero(&ExpectCtxUd, sizeof(ExpectCtxUd));
+ Bs3MemZero(&TrapFrame, sizeof(TrapFrame));
+
+ /* Enable SSE. */
+ ASMSetCR0((ASMGetCR0() & ~(X86_CR0_EM | X86_CR0_TS)) | X86_CR0_MP);
+ ASMSetCR4(ASMGetCR4() | X86_CR4_OSFXSR);
+
+ /* Create a test context. */
+ Bs3RegCtxSaveEx(&Ctx, BS3_MODE_CODE_32, 512);
+ Ctx.rax.u = BECRC_EAX;
+ Ctx.rbx.u = (uintptr_t)pbPages;
+
+ /* Create expected result contexts. */
+ Bs3MemCpy(&ExpectCtxPf, &Ctx, sizeof(ExpectCtxPf));
+ ExpectCtxPf.rflags.u32 |= X86_EFL_RF;
+ ExpectCtxPf.rip.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+ ExpectCtxPf.cr2.u = (uintptr_t)&pbPages[X86_PAGE_SIZE];
+
+ Bs3MemCpy(&ExpectCtxUd, &Ctx, sizeof(ExpectCtxUd));
+ ExpectCtxUd.rflags.u32 |= X86_EFL_RF;
+
+ /* Loop thru the tests. */
+ g_usBs3TestStep = 0;
+ for (iTest = 0; iTest < RT_ELEMENTS(s_aTests); iTest++)
+ {
+ unsigned const cbOpcodes = s_aTests[iTest].cbOpcodes;
+ uint8_t BS3_FAR *pbRip = &pbPages[X86_PAGE_SIZE - cbOpcodes];
+
+ Bs3MemCpy(pbRip, s_aTests[iTest].abOpcodes, cbOpcodes);
+ Bs3RegCtxSetRipCsFromFlat(&Ctx, (uintptr_t)pbRip);
+ ExpectCtxUd.rip = Ctx.rip;
+#if 0
+ Bs3TestPrintf("iTest=%d pbRip=%p cbOpcodes=%d: %.*Rhxs\n",
+ iTest, pbRip, cbOpcodes, cbOpcodes, s_aTests[iTest].abOpcodes);
+ //Bs3RegCtxPrint(&Ctx);
+#endif
+ BS3_CMN_NM(bs3CpuDecoding1_LoadXmm0)(&InXmm0);
+ BS3_CMN_NM(bs3CpuDecoding1_LoadXmm1)(&InXmm1);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ BS3_CMN_NM(bs3CpuDecoding1_SaveXmm0)(&OutXmm0);
+
+ if ( !Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, s_aTests[iTest].bXcpt == X86_XCPT_UD ? &ExpectCtxUd : &ExpectCtxPf,
+ 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "mode", iTest)
+ || TrapFrame.bXcpt != s_aTests[iTest].bXcpt
+ || OutXmm0.s.Lo != s_aTests[iTest].Xmm0Expect.s.Lo
+ || OutXmm0.s.Hi != s_aTests[iTest].Xmm0Expect.s.Hi)
+ {
+ Bs3TestFailedF("iTest=%d cbOpcodes=%d: %.*Rhxs\n", iTest, cbOpcodes, cbOpcodes, s_aTests[iTest].abOpcodes);
+ if (TrapFrame.bXcpt != s_aTests[iTest].bXcpt)
+ Bs3TestFailedF("Expected bXcpt=%#x, got %#x\n", s_aTests[iTest].bXcpt, TrapFrame.bXcpt);
+ if ( OutXmm0.s.Lo != s_aTests[iTest].Xmm0Expect.s.Lo
+ || OutXmm0.s.Hi != s_aTests[iTest].Xmm0Expect.s.Hi)
+ Bs3TestFailedF("Expected XMM0=%08RX32:%08RX32:%08RX32:%08RX32, not %08RX32:%08RX32:%08RX32:%08RX32\n",
+ s_aTests[iTest].Xmm0Expect.DWords.dw3, s_aTests[iTest].Xmm0Expect.DWords.dw2,
+ s_aTests[iTest].Xmm0Expect.DWords.dw1, s_aTests[iTest].Xmm0Expect.DWords.dw0,
+ OutXmm0.DWords.dw3, OutXmm0.DWords.dw2, OutXmm0.DWords.dw1, OutXmm0.DWords.dw0);
+ }
+ }
+
+ Bs3MemGuardedTestPageFree(pbPages);
+ }
+ else
+ Bs3TestFailed("Failed to allocate two pages!\n");
+}
+
+
+BS3_DECL(void) Main_pp32()
+{
+ Bs3TestInit("bs3-cpu-decoding-1");
+ Bs3TestPrintf("g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+
+#if 0
+ Bs3TestSub("CMPPS, CMPPD, CMPSS, CMPSD");
+ DecodeCmppsCmppdCmpssCmpsd();
+
+ Bs3TestSub("MOVBE vs CRC32");
+ DecodeMovbeVsCrc32();
+#endif
+
+ //Bs3TestSub("CMPXCHG8B/16B");
+ //DecodeCmpXchg8bVs16b();
+
+#if 1
+ Bs3TestSub("2 byte undefined opcodes 0f");
+ DecodeUdEdgeTest(g_aUdTest2Byte_0f, RT_ELEMENTS(g_aUdTest2Byte_0f));
+#endif
+#if 0
+ Bs3TestSub("3 byte undefined opcodes 0f 38");
+ DecodeUdEdgeTest(g_aUdTest3Byte_0f_38, RT_ELEMENTS(g_aUdTest3Byte_0f_38));
+#endif
+
+#if 0
+ Bs3TestSub("misc");
+ DecodeEdgeTest();
+#endif
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-asm.asm
new file mode 100644
index 00000000..c96a6615
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-asm.asm
@@ -0,0 +1,44 @@
+; $Id: bs3-cpu-generated-1-asm.asm $
+;; @file
+; BS3Kit - bs3-generated-1, assembly helpers and template instantiation.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+; later maybe.
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py
new file mode 100755
index 00000000..63210b48
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py
@@ -0,0 +1,665 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: bs3-cpu-generated-1-data.py $
+# pylint: disable=invalid-name
+
+"""
+Generates testcases from @optest specifications in IEM.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2017-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155250 $"
+
+# Standard python imports.
+import datetime;
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+g_ksVmmAllDir = os.path.join(os.path.dirname(g_ksValidationKitDir), 'VMM', 'VMMAll')
+sys.path.append(g_ksVmmAllDir);
+
+import IEMAllInstructionsPython as iai; # pylint: disable=import-error
+
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class Bs3Cg1TestEncoder(object):
+ """
+ Does the encoding of a single test.
+ """
+
+ def __init__(self, fLast):
+ self.fLast = fLast;
+ # Each list member (in all lists) are C expression of a byte.
+ self.asHdr = [];
+ self.asSelectors = [];
+ self.asInputs = [];
+ self.asOutputs = [];
+
+ @staticmethod
+ def _compileSelectors(aoSelectors): # (list(iai.TestSelector)) -> list(str)
+ """
+ Compiles a list of iai.TestSelector predicate checks.
+ Returns C byte expression strings.
+ """
+ asRet = [];
+ for oSelector in aoSelectors:
+ sConstant = oSelector.kdVariables[oSelector.sVariable][oSelector.sValue];
+ sConstant = sConstant.upper().replace('.', '_');
+ if oSelector.sOp == '==':
+ sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_TRUE' % (sConstant,);
+ elif oSelector.sOp == '!=':
+ sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_FALSE' % (sConstant,);
+ else:
+ raise Exception('Unknown selector operator: %s' % (oSelector.sOp,));
+ asRet.append(sByte);
+ return asRet;
+
+ kdSmallFields = {
+ 'op1': 'BS3CG1_CTXOP_OP1',
+ 'op2': 'BS3CG1_CTXOP_OP2',
+ 'efl': 'BS3CG1_CTXOP_EFL',
+ };
+ kdOperators = {
+ '=': 'BS3CG1_CTXOP_ASSIGN',
+ '|=': 'BS3CG1_CTXOP_OR',
+ '&=': 'BS3CG1_CTXOP_AND',
+ '&~=': 'BS3CG1_CTXOP_AND_INV',
+ };
+ kdSmallSizes = {
+ 1: 'BS3CG1_CTXOP_1_BYTE',
+ 2: 'BS3CG1_CTXOP_2_BYTES',
+ 4: 'BS3CG1_CTXOP_4_BYTES',
+ 8: 'BS3CG1_CTXOP_8_BYTES',
+ 16: 'BS3CG1_CTXOP_16_BYTES',
+ 32: 'BS3CG1_CTXOP_32_BYTES',
+ 12: 'BS3CG1_CTXOP_12_BYTES',
+ };
+
+ @staticmethod
+ def _amendOutputs(aoOutputs, oInstr): # type: (list(iai.TestInOut), iai.Instruction) -> list(iai.TestInOut)
+ """
+ Amends aoOutputs for instructions with special flag behaviour (undefined,
+ always set, always clear).
+
+ Undefined flags are copied from the result context as the very first
+ operation so they can be set to CPU vendor specific values later if
+ desired.
+
+ Always set or cleared flags are applied at the very end of the
+ modification operations so that we spot incorrect specifications.
+ """
+ if oInstr.asFlUndefined or oInstr.asFlClear or oInstr.asFlSet:
+ aoOutputs = list(aoOutputs);
+
+ if oInstr.asFlUndefined:
+ fFlags = oInstr.getUndefinedFlagsMask();
+ assert fFlags != 0;
+ aoOutputs.insert(0, iai.TestInOut('efl_undef', '=', str(fFlags), 'uint'));
+
+ if oInstr.asFlClear:
+ fFlags = oInstr.getClearedFlagsMask();
+ assert fFlags != 0;
+ aoOutputs.append(iai.TestInOut('efl', '&~=', str(fFlags), 'uint'));
+
+ if oInstr.asFlSet:
+ fFlags = oInstr.getSetFlagsMask();
+ assert fFlags != 0;
+ aoOutputs.append(iai.TestInOut('efl', '|=', str(fFlags), 'uint'));
+
+ return aoOutputs;
+
+ @staticmethod
+ def _compileContextModifers(aoOperations): # (list(iai.TestInOut))
+ """
+ Compile a list of iai.TestInOut context modifiers.
+ """
+ asRet = [];
+ for oOperation in aoOperations:
+ oType = iai.TestInOut.kdTypes[oOperation.sType];
+ aaoValues = oType.get(oOperation.sValue);
+ assert len(aaoValues) == 1 or len(aaoValues) == 2;
+
+ sOp = oOperation.sOp;
+ if sOp == '&|=':
+ sOp = '|=' if len(aaoValues) == 1 else '&~=';
+
+ for fSignExtend, abValue in aaoValues:
+ cbValue = len(abValue);
+
+ # The opcode byte.
+ sOpcode = Bs3Cg1TestEncoder.kdOperators[sOp];
+ sOpcode += ' | ';
+ if oOperation.sField in Bs3Cg1TestEncoder.kdSmallFields:
+ sOpcode += Bs3Cg1TestEncoder.kdSmallFields[oOperation.sField];
+ else:
+ sOpcode += 'BS3CG1_CTXOP_DST_ESC';
+ sOpcode += ' | ';
+ if cbValue in Bs3Cg1TestEncoder.kdSmallSizes:
+ sOpcode += Bs3Cg1TestEncoder.kdSmallSizes[cbValue];
+ else:
+ sOpcode += 'BS3CG1_CTXOP_SIZE_ESC';
+ if fSignExtend:
+ sOpcode += ' | BS3CG1_CTXOP_SIGN_EXT';
+ asRet.append(sOpcode);
+
+ # Escaped field identifier.
+ if oOperation.sField not in Bs3Cg1TestEncoder.kdSmallFields:
+ asRet.append('BS3CG1DST_%s' % (oOperation.sField.upper().replace('.', '_'),));
+
+ # Escaped size byte?
+ if cbValue not in Bs3Cg1TestEncoder.kdSmallSizes:
+ if cbValue >= 256 or cbValue not in [ 1, 2, 4, 6, 8, 12, 16, 32, 64, 128, ]:
+ raise Exception('Invalid value size: %s' % (cbValue,));
+ asRet.append('0x%02x' % (cbValue,));
+
+ # The value bytes.
+ for b in abValue:
+ asRet.append('0x%02x' % (b,));
+
+ sOp = '|=';
+
+ return asRet;
+
+ def _constructHeader(self):
+ """
+ Returns C byte expression strings for BS3CG1TESTHDR.
+ """
+ cbSelectors = len(self.asSelectors);
+ if cbSelectors >= 256:
+ raise Exception('Too many selectors: %s bytes, max 255 bytes' % (cbSelectors,))
+
+ cbInputs = len(self.asInputs);
+ if cbInputs >= 4096:
+ raise Exception('Too many input context modifiers: %s bytes, max 4095 bytes' % (cbInputs,))
+
+ cbOutputs = len(self.asOutputs);
+ if cbOutputs >= 2048:
+ raise Exception('Too many output context modifiers: %s bytes, max 2047 bytes' % (cbOutputs,))
+
+ return [
+ '%#04x' % (cbSelectors,), # 8-bit
+ '%#05x & 0xff' % (cbInputs,), # first 8 bits of cbInputs
+ '(%#05x >> 8) | ((%#05x & 0xf) << 4)' % (cbInputs, cbOutputs,), # last 4 bits of cbInputs, lower 4 bits of cbOutputs.
+ '(%#05x >> 4) | (%#05x << 7)' % (cbOutputs, self.fLast), # last 7 bits of cbOutputs and 1 bit fLast.
+ ];
+
+ def encodeTest(self, oTest): # type: (iai.InstructionTest)
+ """
+ Does the encoding.
+ """
+ self.asSelectors = self._compileSelectors(oTest.aoSelectors);
+ self.asInputs = self._compileContextModifers(oTest.aoInputs);
+ self.asOutputs = self._compileContextModifers(self._amendOutputs(oTest.aoOutputs, oTest.oInstr));
+ self.asHdr = self._constructHeader();
+
+
+class Bs3Cg1EncodedTests(object):
+ """
+ Encodes the tests for an instruction.
+ """
+
+ def __init__(self, oInstr):
+ self.offTests = -1;
+ self.cbTests = 0;
+ self.asLines = [] # type: list(str)
+ self.aoInstructions = [] # type: list(iai.Instruction)
+
+ # Encode the tests.
+ for iTest, oTest in enumerate(oInstr.aoTests):
+ oEncodedTest = Bs3Cg1TestEncoder(iTest + 1 == len(oInstr.aoTests));
+ oEncodedTest.encodeTest(oTest);
+
+ self.cbTests += len(oEncodedTest.asHdr) + len(oEncodedTest.asSelectors) \
+ + len(oEncodedTest.asInputs) + len(oEncodedTest.asOutputs);
+
+ self.asLines.append(' /* test #%s: %s */' % (iTest, oTest,));
+ self.asLines += self.bytesToLines(' ', oEncodedTest.asHdr);
+ if oEncodedTest.asSelectors:
+ self.asLines += self.bytesToLines(' /*sel:*/ ', oEncodedTest.asSelectors);
+ if oEncodedTest.asInputs:
+ self.asLines += self.bytesToLines(' /* in:*/ ', oEncodedTest.asInputs);
+ if oEncodedTest.asOutputs:
+ self.asLines += self.bytesToLines(' /*out:*/ ', oEncodedTest.asOutputs);
+
+ @staticmethod
+ def bytesToLines(sPrefix, asBytes):
+ """
+ Formats a series of bytes into one or more lines.
+ A byte ending with a newline indicates that we should start a new line,
+ and prefix it by len(sPrefix) spaces.
+
+ Returns list of lines.
+ """
+ asRet = [];
+ sLine = sPrefix;
+ for sByte in asBytes:
+ if sByte[-1] == '\n':
+ sLine += sByte[:-1] + ',';
+ asRet.append(sLine);
+ sLine = ' ' * len(sPrefix);
+ else:
+ if len(sLine) + 2 + len(sByte) > 132 and len(sLine) > len(sPrefix):
+ asRet.append(sLine[:-1]);
+ sLine = ' ' * len(sPrefix);
+ sLine += sByte + ', ';
+
+
+ if len(sLine) > len(sPrefix):
+ asRet.append(sLine);
+ return asRet;
+
+
+ def isEqual(self, oOther):
+ """ Compares two encoded tests. """
+ if self.cbTests != oOther.cbTests:
+ return False;
+ if len(self.asLines) != len(oOther.asLines):
+ return False;
+ for iLine, sLines in enumerate(self.asLines):
+ if sLines != oOther.asLines[iLine]:
+ return False;
+ return True;
+
+
+
+class Bs3Cg1Instruction(object):
+ """
+ An instruction with tests.
+ """
+
+ def __init__(self, oMap, oInstr, oTests):
+ self.oMap = oMap # type: iai.InstructionMap
+ self.oInstr = oInstr # type: iai.Instruction
+ self.oTests = oTests # type: Bs3Cg1EncodedTests
+
+ self.asOpcodes = oMap.asLeadOpcodes + [ '0x%02x' % (oInstr.getOpcodeByte(),) ];
+ self.sEncoding = iai.g_kdEncodings[oInstr.sEncoding][0];
+
+ for oOp in oInstr.aoOperands:
+ self.sEncoding += '_' + oOp.sType;
+ if oInstr.sSubOpcode and iai.g_kdSubOpcodes[oInstr.sSubOpcode][1]:
+ self.sEncoding += '_' + iai.g_kdSubOpcodes[oInstr.sSubOpcode][1];
+
+ if oInstr.fUnused:
+ if oInstr.sInvalidStyle == 'immediate' and oInstr.sSubOpcode:
+ self.sEncoding += '_MOD_EQ_3' if oInstr.sSubOpcode == '11 mr/reg' else '_MOD_NE_3';
+ elif oInstr.sInvalidStyle == 'intel-modrm':
+ if oInstr.sSubOpcode is None:
+ self.sEncoding = 'BS3CG1ENC_MODRM_Gv_Ev';
+ elif oInstr.sSubOpcode == '11 mr/reg':
+ self.sEncoding = 'BS3CG1ENC_MODRM_MOD_EQ_3';
+ elif oInstr.sSubOpcode == '!11 mr/reg':
+ self.sEncoding = 'BS3CG1ENC_MODRM_MOD_NE_3';
+ else:
+ raise Exception('Unhandled sSubOpcode=%s for sInvalidStyle=%s' % (oInstr.sSubOpcode, oInstr.sInvalidStyle));
+ elif oInstr.sInvalidStyle == 'vex.modrm':
+ self.sEncoding = 'BS3CG1ENC_VEX_MODRM';
+
+ self.asFlags = [];
+ if 'invalid_64' in oInstr.dHints:
+ self.asFlags.append('BS3CG1INSTR_F_INVALID_64BIT');
+ if oInstr.fUnused:
+ self.asFlags.append('BS3CG1INSTR_F_UNUSED');
+ elif oInstr.fInvalid:
+ self.asFlags.append('BS3CG1INSTR_F_INVALID');
+ if oInstr.sInvalidStyle and oInstr.sInvalidStyle.startswith('intel-'):
+ self.asFlags.append('BS3CG1INSTR_F_INTEL_DECODES_INVALID');
+ if 'vex_l_zero' in oInstr.dHints:
+ self.asFlags.append('BS3CG1INSTR_F_VEX_L_ZERO');
+ if 'vex_l_ignored' in oInstr.dHints:
+ self.asFlags.append('BS3CG1INSTR_F_VEX_L_IGNORED');
+
+ self.fAdvanceMnemonic = True; ##< Set by the caller.
+ if oInstr.sPrefix:
+ if oInstr.sPrefix == 'none':
+ self.sPfxKind = 'BS3CG1PFXKIND_NO_F2_F3_66';
+ else:
+ self.sPfxKind = 'BS3CG1PFXKIND_REQ_' + oInstr.sPrefix[-2:].upper();
+ elif oInstr.sEncoding == 'ModR/M':
+ if 'ignores_op_size' not in oInstr.dHints:
+ self.sPfxKind = 'BS3CG1PFXKIND_MODRM';
+ else:
+ self.sPfxKind = 'BS3CG1PFXKIND_MODRM_NO_OP_SIZES';
+ else:
+ self.sPfxKind = '0';
+
+ self.sCpu = 'BS3CG1CPU_';
+ assert len(oInstr.asCpuIds) in [0, 1], str(oInstr);
+ if oInstr.asCpuIds:
+ self.sCpu += oInstr.asCpuIds[0].upper().replace('.', '_');
+ elif oInstr.sMinCpu:
+ self.sCpu += 'GE_' + oInstr.sMinCpu;
+ else:
+ self.sCpu += 'ANY';
+
+ if oInstr.sXcptType:
+ self.sXcptType = 'BS3CG1XCPTTYPE_' + oInstr.sXcptType.upper();
+ else:
+ self.sXcptType = 'BS3CG1XCPTTYPE_NONE';
+
+ def getOperands(self):
+ """ Returns comma separated string of operand values for g_abBs3Cg1Operands. """
+ return ', '.join(['(uint8_t)BS3CG1OP_%s' % (oOp.sType,) for oOp in self.oInstr.aoOperands]);
+
+ def getOpcodeMap(self):
+ """ Returns the opcode map number for the BS3CG1INSTR structure. """
+ sEncoding = self.oInstr.aoMaps[0].sEncoding;
+ if sEncoding == 'legacy': return 0;
+ if sEncoding == 'vex1': return 1;
+ if sEncoding == 'vex2': return 2;
+ if sEncoding == 'vex3': return 3;
+ if sEncoding == 'xop8': return 8;
+ if sEncoding == 'xop9': return 9;
+ if sEncoding == 'xop10': return 10;
+ assert False, sEncoding;
+ return 3;
+
+ def getInstructionEntry(self):
+ """ Returns an array of BS3CG1INSTR member initializers. """
+ assert len(self.oInstr.sMnemonic) < 16;
+ sOperands = ', '.join([oOp.sType for oOp in self.oInstr.aoOperands]);
+ if sOperands:
+ sOperands = ' /* ' + sOperands + ' */';
+ return [
+ ' /* cbOpcodes = */ %s, /* %s */' % (len(self.asOpcodes), ' '.join(self.asOpcodes),),
+ ' /* cOperands = */ %s,%s' % (len(self.oInstr.aoOperands), sOperands,),
+ ' /* cchMnemonic = */ %s, /* %s */' % (len(self.oInstr.sMnemonic), self.oInstr.sMnemonic,),
+ ' /* fAdvanceMnemonic = */ %s,' % ('true' if self.fAdvanceMnemonic else 'false',),
+ ' /* offTests = */ %s,' % (self.oTests.offTests,),
+ ' /* enmEncoding = */ (unsigned)%s,' % (self.sEncoding,),
+ ' /* uOpcodeMap = */ (unsigned)%s,' % (self.getOpcodeMap(),),
+ ' /* enmPrefixKind = */ (unsigned)%s,' % (self.sPfxKind,),
+ ' /* enmCpuTest = */ (unsigned)%s,' % (self.sCpu,),
+ ' /* enmXcptType = */ (unsigned)%s,' % (self.sXcptType,),
+ ' /* uUnused = */ 0,',
+ ' /* fFlags = */ %s' % (' | '.join(self.asFlags) if self.asFlags else '0'),
+ ];
+
+
+class Bs3CpuGenerated1Generator(object):
+ """
+ The generator code for bs3-cpu-generated-1.
+ """
+
+ def __init__(self):
+ self.aoInstructions = [] # type: Bs3Cg1Instruction
+ self.aoTests = [] # type: Bs3Cg1EncodedTests
+ self.cbTests = 0;
+
+ def addTests(self, oTests, oInstr): # type: (Bs3Cg1EncodedTests, iai.Instruction) -> Bs3Cg1EncodedTests
+ """
+ Adds oTests to self.aoTests, setting the oTests.offTests member.
+ Checks for and eliminates duplicates.
+ Returns the tests to use.
+ """
+ # Check for duplicates.
+ for oExisting in self.aoTests:
+ if oTests.isEqual(oExisting):
+ oExisting.aoInstructions.append(oInstr);
+ return oExisting;
+
+ # New test, so add it.
+ oTests.offTests = self.cbTests;
+ self.aoTests.append(oTests);
+ self.cbTests += oTests.cbTests;
+
+ assert not oTests.aoInstructions;
+ oTests.aoInstructions.append(oInstr);
+
+ return oTests;
+
+ def processInstruction(self):
+ """
+ Processes the IEM specified instructions.
+ Returns success indicator.
+ """
+
+ #
+ # Group instructions by mnemonic to reduce the number of sub-tests.
+ #
+ for oInstr in sorted(iai.g_aoAllInstructions,
+ key = lambda oInstr: oInstr.sMnemonic + ''.join([oOp.sType for oOp in oInstr.aoOperands])
+ + (oInstr.sOpcode if oInstr.sOpcode else 'zz')):
+ if oInstr.aoTests:
+ oTests = Bs3Cg1EncodedTests(oInstr);
+ oTests = self.addTests(oTests, oInstr);
+
+ for oMap in oInstr.aoMaps:
+ self.aoInstructions.append(Bs3Cg1Instruction(oMap, oInstr, oTests));
+
+ # Set fAdvanceMnemonic.
+ for iInstr, oInstr in enumerate(self.aoInstructions):
+ oInstr.fAdvanceMnemonic = iInstr + 1 >= len(self.aoInstructions) \
+ or oInstr.oInstr.sMnemonic != self.aoInstructions[iInstr + 1].oInstr.sMnemonic;
+
+ return True;
+
+ def generateCode(self, oOut):
+ """
+ Generates the C code.
+ Returns success indicator.
+ """
+
+ # First, a file header.
+ asLines = [
+ '/*',
+ ' * Autogenerated by $Id: bs3-cpu-generated-1-data.py $ ',
+ ' * Do not edit!',
+ ' */',
+ '',
+ '/*',
+ ' * Copyright (C) 2017-' + str(datetime.date.today().year) + ' Oracle and/or its affiliates.',
+ ' *',
+ ' * This file is part of VirtualBox base platform packages, as',
+ ' * available from https://www.virtualbox.org.',
+ ' *',
+ ' * This program is free software; you can redistribute it and/or',
+ ' * modify it under the terms of the GNU General Public License',
+ ' * as published by the Free Software Foundation, in version 3 of the',
+ ' * License.',
+ ' *',
+ ' * This program is distributed in the hope that it will be useful, but',
+ ' * WITHOUT ANY WARRANTY; without even the implied warranty of',
+ ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU',
+ ' * General Public License for more details.',
+ ' *',
+ ' * You should have received a copy of the GNU General Public License',
+ ' * along with this program; if not, see <https://www.gnu.org/licenses>.',
+ ' *',
+ ' * The contents of this file may alternatively be used under the terms',
+ ' * of the Common Development and Distribution License Version 1.0',
+ ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included',
+ ' * in the VirtualBox distribution, in which case the provisions of the',
+ ' * CDDL are applicable instead of those of the GPL.',
+ ' *',
+ ' * You may elect to license modified versions of this file under the',
+ ' * terms and conditions of either the GPL or the CDDL or both.',
+ ' *',
+ ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0',
+ ' */',
+ '',
+ '',
+ '#include "bs3-cpu-generated-1.h"',
+ '',
+ '',
+ '#pragma data_seg ("BS3DATA16")',
+ ];
+
+ # Generate the g_achBs3Cg1Mnemonics array.
+ asLines += [
+ 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
+ '{',
+ ];
+ fAdvanceMnemonic = True;
+ for oInstr in self.aoInstructions:
+ if fAdvanceMnemonic:
+ asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
+ fAdvanceMnemonic = oInstr.fAdvanceMnemonic;
+ asLines += [
+ '};',
+ '',
+ '',
+ ];
+
+ # Generate the g_abBs3Cg1Opcodes array.
+ asLines += [
+ 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
+ '{',
+ ];
+ for oInstr in self.aoInstructions:
+ asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
+ asLines += [
+ '};',
+ '',
+ '',
+ ];
+
+ # Generate the g_abBs3Cg1Opcodes array.
+ asLines += [
+ 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
+ '{',
+ ];
+ cOperands = 0;
+ for oInstr in self.aoInstructions:
+ if oInstr.oInstr.aoOperands:
+ cOperands += len(oInstr.oInstr.aoOperands);
+ asLines.append(' ' + oInstr.getOperands() + ', /* %s */' % (oInstr.oInstr.sStats,));
+ else:
+ asLines.append(' /* none */');
+ if not cOperands:
+ asLines.append(' 0 /* dummy */');
+ asLines += [
+ '};',
+ '',
+ '',
+ ];
+
+ # Generate the g_abBs3Cg1Operands array.
+ asLines += [
+ 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
+ '{',
+ ];
+ for oInstr in self.aoInstructions:
+ asLines.append(' {');
+ asLines += oInstr.getInstructionEntry();
+ asLines.append(' },');
+ asLines += [
+ '};',
+ 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
+ '',
+ '',
+ ];
+
+ # Generate the g_abBs3Cg1Tests array.
+ asLines += [
+ 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
+ '{',
+ ];
+ for oTests in self.aoTests:
+ asLines.append(' /*');
+ asLines.append(' * offTests=%s' % (oTests.offTests,));
+ asLines.append(' * Instructions: %s' % (', '.join([oInstr.sStats for oInstr in oTests.aoInstructions]),));
+ asLines.append(' */');
+ asLines += oTests.asLines;
+ asLines += [
+ '};',
+ '',
+ ];
+
+
+ #/** The test data that BS3CG1INSTR.
+ # * In order to simplify generating these, we use a byte array. */
+ #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
+
+
+ oOut.write('\n'.join(asLines));
+ return True;
+
+
+ def usage(self):
+ """ Prints usage. """
+ print('usage: bs3-cpu-generated-1-data.py [output file|-]');
+ return 0;
+
+ def main(self, asArgs):
+ """
+ C-like main function.
+ Returns exit code.
+ """
+
+ #
+ # Quick argument parsing.
+ #
+ if len(asArgs) == 1:
+ sOutFile = '-';
+ elif len(asArgs) != 2:
+ print('syntax error! Expected exactly one argument.');
+ return 2;
+ elif asArgs[1] in [ '-h', '-?', '--help' ]:
+ return self.usage();
+ else:
+ sOutFile = asArgs[1];
+
+ #
+ # Process the instructions specified in the IEM sources.
+ #
+ if self.processInstruction():
+
+ #
+ # Open the output file and generate the code.
+ #
+ if sOutFile == '-':
+ oOut = sys.stdout;
+ else:
+ try:
+ oOut = open(sOutFile, 'w'); # pylint: disable=consider-using-with,unspecified-encoding
+ except Exception as oXcpt:
+ print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
+ return 1;
+ if self.generateCode(oOut):
+ return 0;
+
+ return 1;
+
+
+if __name__ == '__main__':
+ sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-template.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-template.c
new file mode 100644
index 00000000..30bbafa6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-template.c
@@ -0,0 +1,6152 @@
+/* $Id: bs3-cpu-generated-1-template.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-generated-1, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3_INSTANTIATING_CMN
+# error "BS3_INSTANTIATING_CMN not defined"
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+#include "bs3-cpu-generated-1.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define BS3CG1_WITH_VEX
+
+#define P_CS X86_OP_PRF_CS
+#define P_SS X86_OP_PRF_SS
+#define P_DS X86_OP_PRF_DS
+#define P_ES X86_OP_PRF_ES
+#define P_FS X86_OP_PRF_FS
+#define P_GS X86_OP_PRF_GS
+#define P_OZ X86_OP_PRF_SIZE_OP
+#define P_AZ X86_OP_PRF_SIZE_ADDR
+#define P_LK X86_OP_PRF_LOCK
+#define P_RN X86_OP_PRF_REPNZ
+#define P_RZ X86_OP_PRF_REPZ
+
+#define REX_WRBX (X86_OP_REX_W | X86_OP_REX_R | X86_OP_REX_B | X86_OP_REX_X)
+#define REX_W___ (X86_OP_REX_W)
+#define REX_WR__ (X86_OP_REX_W | X86_OP_REX_R)
+#define REX_W_B_ (X86_OP_REX_W | X86_OP_REX_B)
+#define REX_W__X (X86_OP_REX_W | X86_OP_REX_X)
+#define REX_WRB_ (X86_OP_REX_W | X86_OP_REX_R | X86_OP_REX_B)
+#define REX_WR_X (X86_OP_REX_W | X86_OP_REX_R | X86_OP_REX_X)
+#define REX_W_BX (X86_OP_REX_W | X86_OP_REX_B | X86_OP_REX_X)
+#define REX__R__ (X86_OP_REX_R)
+#define REX__RB_ (X86_OP_REX_R | X86_OP_REX_B)
+#define REX__R_X (X86_OP_REX_R | X86_OP_REX_X)
+#define REX__RBX (X86_OP_REX_R | X86_OP_REX_B | X86_OP_REX_X)
+#define REX___B_ (X86_OP_REX_B)
+#define REX___BX (X86_OP_REX_B | X86_OP_REX_X)
+#define REX____X (X86_OP_REX_X)
+#define REX_____ (0x40)
+
+
+/** @def BS3CG1_DPRINTF
+ * Debug print macro.
+ */
+#if 0
+# define BS3CG1_DPRINTF(a_ArgList) Bs3TestPrintf a_ArgList
+# define BS3CG1_DEBUG_CTX_MOD
+#else
+# define BS3CG1_DPRINTF(a_ArgList) do { } while (0)
+#endif
+
+/**
+ * Checks if this is a 64-bit test target or not.
+ * Helps avoid ifdefs or code bloat.
+ */
+#if ARCH_BITS == 64
+# define BS3CG1_IS_64BIT_TARGET(a_pThis) BS3_MODE_IS_64BIT_CODE((a_pThis)->bMode)
+#else
+# define BS3CG1_IS_64BIT_TARGET(a_pThis) (false)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Operand value location. */
+typedef enum BS3CG1OPLOC
+{
+ BS3CG1OPLOC_INVALID = 0,
+ BS3CG1OPLOC_CTX,
+ BS3CG1OPLOC_CTX_ZX_VLMAX,
+ BS3CG1OPLOC_IMM,
+ BS3CG1OPLOC_MEM,
+ BS3CG1OPLOC_MEM_RW,
+ BS3CG1OPLOC_MEM_WO,
+ BS3CG1OPLOC_END
+} BS3CG1OPLOC;
+AssertCompile(BS3CG1OPLOC_END <= 16);
+
+
+/** Pointer to the generated test state. */
+typedef struct BS3CG1STATE *PBS3CG1STATE;
+
+/**
+ * Encoder callback.
+ * @returns Next encoding. If equal or less to @a iEncoding, no
+ * further encodings are available for testing.
+ * @param pThis The state.
+ * @param iEncoding The encoding.
+ */
+typedef unsigned BS3_NEAR_CODE FNBS3CG1ENCODER(PBS3CG1STATE pThis, unsigned iEncoding);
+/** Pointer to a encoder callback. */
+typedef FNBS3CG1ENCODER *PFNBS3CG1ENCODER;
+
+
+/**
+ * The state.
+ */
+typedef struct BS3CG1STATE
+{
+ /** @name Instruction details (expanded from BS3CG1INSTR).
+ * @{ */
+ /** Pointer to the mnemonic string (not terminated) (g_achBs3Cg1Mnemonics). */
+ const char BS3_FAR *pchMnemonic;
+ /** Pointer to the test header. */
+ PCBS3CG1TESTHDR pTestHdr;
+ /** Pointer to the per operand flags (g_abBs3Cg1Operands). */
+ const uint8_t BS3_FAR *pabOperands;
+ /** Opcode bytes (g_abBs3Cg1Opcodes). */
+ const uint8_t BS3_FAR *pabOpcodes;
+ /** The current instruction number in the input array (for error reporting). */
+ uint32_t iInstr;
+
+ /** The instruction flags. */
+ uint32_t fFlags;
+ /** The encoding. */
+ BS3CG1ENC enmEncoding;
+ /** The non-invalid encoding. This may differ from enmEncoding when
+ * Bs3Cg1CalcNoneIntelInvalidEncoding has been called. */
+ BS3CG1ENC enmEncodingNonInvalid;
+ /** The CPU test / CPU ID. */
+ BS3CG1CPU enmCpuTest;
+ /** Prefix sensitivity and requirements. */
+ BS3CG1PFXKIND enmPrefixKind;
+ /** Exception type (SSE, AVX). */
+ BS3CG1XCPTTYPE enmXcptType;
+ /** Per operand flags. */
+ BS3CG1OP aenmOperands[4];
+ /** Opcode bytes. */
+ uint8_t abOpcodes[4];
+ /** The instruction encoder. */
+ PFNBS3CG1ENCODER pfnEncoder;
+
+ /** The length of the mnemonic. */
+ uint8_t cchMnemonic;
+ /** Whether to advance the mnemonic pointer or not. */
+ uint8_t fAdvanceMnemonic;
+ /** The opcode map number. */
+ uint8_t uOpcodeMap;
+ /** The number of opcode bytes. */
+ uint8_t cbOpcodes;
+ /** Number of operands. */
+ uint8_t cOperands;
+ /** @} */
+
+ /** Default operand size. */
+ uint8_t cbOpDefault;
+ /** Operand size when overridden by 066h. */
+ uint8_t cbOpOvrd66;
+ /** Operand size when overridden by REX.W. */
+ uint8_t cbOpOvrdRexW;
+
+ /** Operand size in bytes (0 if not applicable). */
+ uint8_t cbOperand;
+ /** Current VEX.L value (UINT8_MAX if not applicable). */
+ uint8_t uVexL;
+ /** Current target ring (0..3). */
+ uint8_t uCpl;
+
+ /** The current test number. */
+ uint8_t iTest;
+
+ /** Target mode (g_bBs3CurrentMode). */
+ uint8_t bMode;
+ /** The CPU vendor (BS3CPUVENDOR). */
+ uint8_t bCpuVendor;
+ /** First ring being tested. */
+ uint8_t iFirstRing;
+ /** End of rings being tested. */
+ uint8_t iEndRing;
+
+ /** @name Current encoded instruction.
+ * @{ */
+ /** The size of the current instruction that we're testing. */
+ uint8_t cbCurInstr;
+ /** The size the prefixes. */
+ uint8_t cbCurPrefix;
+ /** The offset into abCurInstr of the immediate. */
+ uint8_t offCurImm;
+ /** Buffer for assembling the current instruction. */
+ uint8_t abCurInstr[23];
+
+ /** Set if the encoding can't be tested in the same ring as this test code.
+ * This is used to deal with encodings modifying SP/ESP/RSP. */
+ bool fSameRingNotOkay;
+ /** Whether to work the extended context too. */
+ bool fWorkExtCtx;
+ /** The aOperands index of the modrm.reg operand (if applicable). */
+ uint8_t iRegOp;
+ /** The aOperands index of the modrm.rm operand (if applicable). */
+ uint8_t iRmOp;
+
+ /** Operands details. */
+ struct
+ {
+ uint8_t cbOp;
+ /** BS3CG1OPLOC_XXX. */
+ uint8_t enmLocation;
+ /** BS3CG1OPLOC_XXX for memory encodings (MODRM.rm field). */
+ uint8_t enmLocationMem : 4;
+ /** BS3CG1OPLOC_XXX for register encodings (MODRM.rm field). */
+ uint8_t enmLocationReg : 4;
+ /** The BS3CG1DST value for this field.
+ * Set to BS3CG1DST_INVALID if memory or immediate. */
+ uint8_t idxField;
+ /** The base BS3CG1DST value for this field.
+ * Used only by some generalized encoders when dealing with registers. */
+ uint8_t idxFieldBase;
+ /** Depends on enmLocation.
+ * - BS3CG1OPLOC_IMM: offset relative to start of the instruction.
+ * - BS3CG1OPLOC_MEM: offset should be subtracted from &pbDataPg[_4K].
+ * - BS3CG1OPLOC_MEM_RW: offset should be subtracted from &pbDataPg[_4K].
+ * - BS3CG1OPLOC_MEM_RO: offset should be subtracted from &pbDataPg[_4K].
+ * - BS3CG1OPLOC_CTX: not used (use idxField instead).
+ */
+ uint8_t off;
+ } aOperands[4];
+ /** @} */
+
+ /** Page to put code in. When paging is enabled, the page before and after
+ * are marked not-present. */
+ uint8_t BS3_FAR *pbCodePg;
+ /** The flat address corresponding to pbCodePg. */
+ uintptr_t uCodePgFlat;
+ /** The 16-bit address corresponding to pbCodePg if relevant for bMode. */
+ RTFAR16 CodePgFar;
+ /** The IP/EIP/RIP value for pbCodePg[0] relative to CS (bMode). */
+ uintptr_t CodePgRip;
+
+ /** Page for placing data operands in. When paging is enabled, the page before
+ * and after are marked not-present. */
+ uint8_t BS3_FAR *pbDataPg;
+ /** The flat address corresponding to pbDataPg. */
+ uintptr_t uDataPgFlat;
+ /** The 16-bit address corresponding to pbDataPg. */
+ RTFAR16 DataPgFar;
+
+ /** The name corresponding to bMode. */
+ const char BS3_FAR *pszMode;
+ /** The short name corresponding to bMode. */
+ const char BS3_FAR *pszModeShort;
+
+ /** @name Expected result (modifiable by output program).
+ * @{ */
+ /** The expected exception based on operand values or result.
+ * UINT8_MAX if no special exception expected. */
+ uint8_t bValueXcpt;
+ /** @} */
+ /** Alignment exception expected by the encoder.
+ * UINT8_MAX if no special exception expected. */
+ uint8_t bAlignmentXcpt;
+ /** Set by the encoding method to indicating invalid encoding. */
+ bool fInvalidEncoding;
+ /** The result of Bs3Cg1CpuSetupFirst(). */
+ bool fCpuSetupFirstResult;
+
+ /** The context we're working on. */
+ BS3REGCTX Ctx;
+ /** The trap context and frame. */
+ BS3TRAPFRAME TrapFrame;
+ /** Initial contexts, one for each ring. */
+ BS3REGCTX aInitialCtxs[4];
+
+ /** The extended context we're working on (input, expected output). */
+ PBS3EXTCTX pExtCtx;
+ /** The extended result context (analoguous to TrapFrame). */
+ PBS3EXTCTX pResultExtCtx;
+ /** The initial extended context. */
+ PBS3EXTCTX pInitialExtCtx;
+
+ /** Memory operand scratch space. */
+ union
+ {
+ uint8_t ab[128];
+ uint16_t au16[128 / sizeof(uint16_t)];
+ uint32_t au32[128 / sizeof(uint32_t)];
+ uint64_t au64[128 / sizeof(uint64_t)];
+ } MemOp;
+
+ /** Array parallel to aInitialCtxs for saving segment registers. */
+ struct
+ {
+ RTSEL ds;
+ } aSavedSegRegs[4];
+
+} BS3CG1STATE;
+
+
+#define BS3CG1_PF_OZ UINT16_C(0x0001)
+#define BS3CG1_PF_AZ UINT16_C(0x0002)
+#define BS3CG1_PF_CS UINT16_C(0x0004)
+#define BS3CG1_PF_DS UINT16_C(0x0008)
+#define BS3CG1_PF_ES UINT16_C(0x0010)
+#define BS3CG1_PF_FS UINT16_C(0x0020)
+#define BS3CG1_PF_GS UINT16_C(0x0040)
+#define BS3CG1_PF_SS UINT16_C(0x0080)
+#define BS3CG1_PF_SEGS (BS3CG1_PF_CS | BS3CG1_PF_DS | BS3CG1_PF_ES | BS3CG1_PF_FS | BS3CG1_PF_GS | BS3CG1_PF_SS)
+#define BS3CG1_PF_MEM (BS3CG1_PF_SEGS | BS3CG1_PF_AZ)
+#define BS3CG1_PF_LK UINT16_C(0x0100)
+#define BS3CG1_PF_RN UINT16_C(0x0200)
+#define BS3CG1_PF_RZ UINT16_C(0x0400)
+#define BS3CG1_PF_W UINT16_C(0x0800) /**< REX.W */
+#define BS3CG1_PF_R UINT16_C(0x1000) /**< REX.R */
+#define BS3CG1_PF_B UINT16_C(0x2000) /**< REX.B */
+#define BS3CG1_PF_X UINT16_C(0x4000) /**< REX.X */
+
+
+/** Used in g_cbBs3Cg1DstFields to indicate that it's one of the 4 operands. */
+#define BS3CG1DSTSIZE_OPERAND UINT8_C(255)
+/** Used in g_cbBs3Cg1DstFields to indicate that the operand size determins
+ * the field size (2, 4, or 8). */
+#define BS3CG1DSTSIZE_OPERAND_SIZE_GRP UINT8_C(254)
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Destination field sizes indexed by bBS3CG1DST.
+ * Zero means operand size sized. */
+static const uint8_t g_acbBs3Cg1DstFields[] =
+{
+ /* [BS3CG1DST_INVALID] = */ BS3CG1DSTSIZE_OPERAND,
+
+ /* [BS3CG1DST_OP1] = */ BS3CG1DSTSIZE_OPERAND,
+ /* [BS3CG1DST_OP2] = */ BS3CG1DSTSIZE_OPERAND,
+ /* [BS3CG1DST_OP3] = */ BS3CG1DSTSIZE_OPERAND,
+ /* [BS3CG1DST_OP4] = */ BS3CG1DSTSIZE_OPERAND,
+ /* [BS3CG1DST_EFL] = */ 4,
+ /* [BS3CG1DST_EFL_UNDEF]=*/ 4,
+
+ /* [BS3CG1DST_AL] = */ 1,
+ /* [BS3CG1DST_CL] = */ 1,
+ /* [BS3CG1DST_DL] = */ 1,
+ /* [BS3CG1DST_BL] = */ 1,
+ /* [BS3CG1DST_AH] = */ 1,
+ /* [BS3CG1DST_CH] = */ 1,
+ /* [BS3CG1DST_DH] = */ 1,
+ /* [BS3CG1DST_BH] = */ 1,
+ /* [BS3CG1DST_SPL] = */ 1,
+ /* [BS3CG1DST_BPL] = */ 1,
+ /* [BS3CG1DST_SIL] = */ 1,
+ /* [BS3CG1DST_DIL] = */ 1,
+ /* [BS3CG1DST_R8L] = */ 1,
+ /* [BS3CG1DST_R9L] = */ 1,
+ /* [BS3CG1DST_R10L] = */ 1,
+ /* [BS3CG1DST_R11L] = */ 1,
+ /* [BS3CG1DST_R12L] = */ 1,
+ /* [BS3CG1DST_R13L] = */ 1,
+ /* [BS3CG1DST_R14L] = */ 1,
+ /* [BS3CG1DST_R15L] = */ 1,
+
+ /* [BS3CG1DST_AX] = */ 2,
+ /* [BS3CG1DST_CX] = */ 2,
+ /* [BS3CG1DST_DX] = */ 2,
+ /* [BS3CG1DST_BX] = */ 2,
+ /* [BS3CG1DST_SP] = */ 2,
+ /* [BS3CG1DST_BP] = */ 2,
+ /* [BS3CG1DST_SI] = */ 2,
+ /* [BS3CG1DST_DI] = */ 2,
+ /* [BS3CG1DST_R8W] = */ 2,
+ /* [BS3CG1DST_R9W] = */ 2,
+ /* [BS3CG1DST_R10W] = */ 2,
+ /* [BS3CG1DST_R11W] = */ 2,
+ /* [BS3CG1DST_R12W] = */ 2,
+ /* [BS3CG1DST_R13W] = */ 2,
+ /* [BS3CG1DST_R14W] = */ 2,
+ /* [BS3CG1DST_R15W] = */ 2,
+
+ /* [BS3CG1DST_EAX] = */ 4,
+ /* [BS3CG1DST_ECX] = */ 4,
+ /* [BS3CG1DST_EDX] = */ 4,
+ /* [BS3CG1DST_EBX] = */ 4,
+ /* [BS3CG1DST_ESP] = */ 4,
+ /* [BS3CG1DST_EBP] = */ 4,
+ /* [BS3CG1DST_ESI] = */ 4,
+ /* [BS3CG1DST_EDI] = */ 4,
+ /* [BS3CG1DST_R8D] = */ 4,
+ /* [BS3CG1DST_R9D] = */ 4,
+ /* [BS3CG1DST_R10D] = */ 4,
+ /* [BS3CG1DST_R11D] = */ 4,
+ /* [BS3CG1DST_R12D] = */ 4,
+ /* [BS3CG1DST_R13D] = */ 4,
+ /* [BS3CG1DST_R14D] = */ 4,
+ /* [BS3CG1DST_R15D] = */ 4,
+
+ /* [BS3CG1DST_RAX] = */ 8,
+ /* [BS3CG1DST_RCX] = */ 8,
+ /* [BS3CG1DST_RDX] = */ 8,
+ /* [BS3CG1DST_RBX] = */ 8,
+ /* [BS3CG1DST_RSP] = */ 8,
+ /* [BS3CG1DST_RBP] = */ 8,
+ /* [BS3CG1DST_RSI] = */ 8,
+ /* [BS3CG1DST_RDI] = */ 8,
+ /* [BS3CG1DST_R8] = */ 8,
+ /* [BS3CG1DST_R9] = */ 8,
+ /* [BS3CG1DST_R10] = */ 8,
+ /* [BS3CG1DST_R11] = */ 8,
+ /* [BS3CG1DST_R12] = */ 8,
+ /* [BS3CG1DST_R13] = */ 8,
+ /* [BS3CG1DST_R14] = */ 8,
+ /* [BS3CG1DST_R15] = */ 8,
+
+ /* [BS3CG1DST_OZ_RAX] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RCX] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RDX] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RBX] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RSP] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RBP] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RSI] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_RDI] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R8] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R9] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R10] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R11] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R12] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R13] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R14] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+ /* [BS3CG1DST_OZ_R15] = */ BS3CG1DSTSIZE_OPERAND_SIZE_GRP,
+
+ /* [BS3CG1DST_CR0] = */ 4,
+ /* [BS3CG1DST_CR4] = */ 4,
+ /* [BS3CG1DST_XCR0] = */ 8,
+
+ /* [BS3CG1DST_FCW] = */ 2,
+ /* [BS3CG1DST_FSW] = */ 2,
+ /* [BS3CG1DST_FTW] = */ 2,
+ /* [BS3CG1DST_FOP] = */ 2,
+ /* [BS3CG1DST_FPUIP] = */ 2,
+ /* [BS3CG1DST_FPUCS] = */ 2,
+ /* [BS3CG1DST_FPUDP] = */ 2,
+ /* [BS3CG1DST_FPUDS] = */ 2,
+ /* [BS3CG1DST_MXCSR] = */ 4,
+ /* [BS3CG1DST_ST0] = */ 12,
+ /* [BS3CG1DST_ST1] = */ 12,
+ /* [BS3CG1DST_ST2] = */ 12,
+ /* [BS3CG1DST_ST3] = */ 12,
+ /* [BS3CG1DST_ST4] = */ 12,
+ /* [BS3CG1DST_ST5] = */ 12,
+ /* [BS3CG1DST_ST6] = */ 12,
+ /* [BS3CG1DST_ST7] = */ 12,
+ /* [BS3CG1DST_MM0] = */ 8,
+ /* [BS3CG1DST_MM1] = */ 8,
+ /* [BS3CG1DST_MM2] = */ 8,
+ /* [BS3CG1DST_MM3] = */ 8,
+ /* [BS3CG1DST_MM4] = */ 8,
+ /* [BS3CG1DST_MM5] = */ 8,
+ /* [BS3CG1DST_MM6] = */ 8,
+ /* [BS3CG1DST_MM7] = */ 8,
+ /* [BS3CG1DST_MM0_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM1_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM2_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM3_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM4_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM5_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM6_LO_ZX] = */ 4,
+ /* [BS3CG1DST_MM7_LO_ZX] = */ 4,
+ /* [BS3CG1DST_XMM0] = */ 16,
+ /* [BS3CG1DST_XMM1] = */ 16,
+ /* [BS3CG1DST_XMM2] = */ 16,
+ /* [BS3CG1DST_XMM3] = */ 16,
+ /* [BS3CG1DST_XMM4] = */ 16,
+ /* [BS3CG1DST_XMM5] = */ 16,
+ /* [BS3CG1DST_XMM6] = */ 16,
+ /* [BS3CG1DST_XMM7] = */ 16,
+ /* [BS3CG1DST_XMM8] = */ 16,
+ /* [BS3CG1DST_XMM9] = */ 16,
+ /* [BS3CG1DST_XMM10] = */ 16,
+ /* [BS3CG1DST_XMM11] = */ 16,
+ /* [BS3CG1DST_XMM12] = */ 16,
+ /* [BS3CG1DST_XMM13] = */ 16,
+ /* [BS3CG1DST_XMM14] = */ 16,
+ /* [BS3CG1DST_XMM15] = */ 16,
+ /* [BS3CG1DST_XMM0_LO] = */ 8,
+ /* [BS3CG1DST_XMM1_LO] = */ 8,
+ /* [BS3CG1DST_XMM2_LO] = */ 8,
+ /* [BS3CG1DST_XMM3_LO] = */ 8,
+ /* [BS3CG1DST_XMM4_LO] = */ 8,
+ /* [BS3CG1DST_XMM5_LO] = */ 8,
+ /* [BS3CG1DST_XMM6_LO] = */ 8,
+ /* [BS3CG1DST_XMM7_LO] = */ 8,
+ /* [BS3CG1DST_XMM8_LO] = */ 8,
+ /* [BS3CG1DST_XMM9_LO] = */ 8,
+ /* [BS3CG1DST_XMM10_LO] = */ 8,
+ /* [BS3CG1DST_XMM11_LO] = */ 8,
+ /* [BS3CG1DST_XMM12_LO] = */ 8,
+ /* [BS3CG1DST_XMM13_LO] = */ 8,
+ /* [BS3CG1DST_XMM14_LO] = */ 8,
+ /* [BS3CG1DST_XMM15_LO] = */ 8,
+ /* [BS3CG1DST_XMM0_HI] = */ 8,
+ /* [BS3CG1DST_XMM1_HI] = */ 8,
+ /* [BS3CG1DST_XMM2_HI] = */ 8,
+ /* [BS3CG1DST_XMM3_HI] = */ 8,
+ /* [BS3CG1DST_XMM4_HI] = */ 8,
+ /* [BS3CG1DST_XMM5_HI] = */ 8,
+ /* [BS3CG1DST_XMM6_HI] = */ 8,
+ /* [BS3CG1DST_XMM7_HI] = */ 8,
+ /* [BS3CG1DST_XMM8_HI] = */ 8,
+ /* [BS3CG1DST_XMM9_HI] = */ 8,
+ /* [BS3CG1DST_XMM10_HI] = */ 8,
+ /* [BS3CG1DST_XMM11_HI] = */ 8,
+ /* [BS3CG1DST_XMM12_HI] = */ 8,
+ /* [BS3CG1DST_XMM13_HI] = */ 8,
+ /* [BS3CG1DST_XMM14_HI] = */ 8,
+ /* [BS3CG1DST_XMM15_HI] = */ 8,
+ /* [BS3CG1DST_XMM0_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM1_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM2_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM3_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM4_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM5_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM6_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM7_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM8_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM9_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM10_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM11_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM12_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM13_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM14_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM15_LO_ZX] = */ 8,
+ /* [BS3CG1DST_XMM0_DW0] = */ 4,
+ /* [BS3CG1DST_XMM1_DW0] = */ 4,
+ /* [BS3CG1DST_XMM2_DW0] = */ 4,
+ /* [BS3CG1DST_XMM3_DW0] = */ 4,
+ /* [BS3CG1DST_XMM4_DW0] = */ 4,
+ /* [BS3CG1DST_XMM5_DW0] = */ 4,
+ /* [BS3CG1DST_XMM6_DW0] = */ 4,
+ /* [BS3CG1DST_XMM7_DW0] = */ 4,
+ /* [BS3CG1DST_XMM8_DW0] = */ 4,
+ /* [BS3CG1DST_XMM9_DW0] = */ 4,
+ /* [BS3CG1DST_XMM10_DW0] = */ 4,
+ /* [BS3CG1DST_XMM11_DW0] = */ 4,
+ /* [BS3CG1DST_XMM12_DW0] = */ 4,
+ /* [BS3CG1DST_XMM13_DW0] = */ 4,
+ /* [BS3CG1DST_XMM14_DW0] = */ 4,
+ /* [BS3CG1DST_XMM15_DW0] = */ 4,
+ /* [BS3CG1DST_XMM0_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM1_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM2_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM3_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM4_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM5_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM6_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM7_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM8_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM9_DW0_ZX] = */ 4,
+ /* [BS3CG1DST_XMM10_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM11_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM12_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM13_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM14_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM15_DW0_ZX] =*/ 4,
+ /* [BS3CG1DST_XMM0_HI96] = */ 12,
+ /* [BS3CG1DST_XMM1_HI96] = */ 12,
+ /* [BS3CG1DST_XMM2_HI96] = */ 12,
+ /* [BS3CG1DST_XMM3_HI96] = */ 12,
+ /* [BS3CG1DST_XMM4_HI96] = */ 12,
+ /* [BS3CG1DST_XMM5_HI96] = */ 12,
+ /* [BS3CG1DST_XMM6_HI96] = */ 12,
+ /* [BS3CG1DST_XMM7_HI96] = */ 12,
+ /* [BS3CG1DST_XMM8_HI96] = */ 12,
+ /* [BS3CG1DST_XMM9_HI96] = */ 12,
+ /* [BS3CG1DST_XMM10_HI96] =*/ 12,
+ /* [BS3CG1DST_XMM11_HI96] =*/ 12,
+ /* [BS3CG1DST_XMM12_HI96] =*/ 12,
+ /* [BS3CG1DST_XMM13_HI96] =*/ 12,
+ /* [BS3CG1DST_XMM14_HI96] =*/ 12,
+ /* [BS3CG1DST_XMM15_HI96] =*/ 12,
+ /* [BS3CG1DST_YMM0] = */ 32,
+ /* [BS3CG1DST_YMM1] = */ 32,
+ /* [BS3CG1DST_YMM2] = */ 32,
+ /* [BS3CG1DST_YMM3] = */ 32,
+ /* [BS3CG1DST_YMM4] = */ 32,
+ /* [BS3CG1DST_YMM5] = */ 32,
+ /* [BS3CG1DST_YMM6] = */ 32,
+ /* [BS3CG1DST_YMM7] = */ 32,
+ /* [BS3CG1DST_YMM8] = */ 32,
+ /* [BS3CG1DST_YMM9] = */ 32,
+ /* [BS3CG1DST_YMM10] = */ 32,
+ /* [BS3CG1DST_YMM11] = */ 32,
+ /* [BS3CG1DST_YMM12] = */ 32,
+ /* [BS3CG1DST_YMM13] = */ 32,
+ /* [BS3CG1DST_YMM14] = */ 32,
+ /* [BS3CG1DST_YMM15] = */ 32,
+
+ /* [BS3CG1DST_VALUE_XCPT] = */ 1,
+};
+AssertCompile(RT_ELEMENTS(g_acbBs3Cg1DstFields) == BS3CG1DST_END);
+
+/** Destination field offset indexed by bBS3CG1DST.
+ * Zero means operand size sized. */
+static const unsigned g_aoffBs3Cg1DstFields[] =
+{
+ /* [BS3CG1DST_INVALID] = */ ~0U,
+ /* [BS3CG1DST_OP1] = */ ~0U,
+ /* [BS3CG1DST_OP2] = */ ~0U,
+ /* [BS3CG1DST_OP3] = */ ~0U,
+ /* [BS3CG1DST_OP4] = */ ~0U,
+ /* [BS3CG1DST_EFL] = */ RT_OFFSETOF(BS3REGCTX, rflags),
+ /* [BS3CG1DST_EFL_UNDEF]=*/ ~0, /* special field */
+
+ /* [BS3CG1DST_AL] = */ RT_OFFSETOF(BS3REGCTX, rax.u8),
+ /* [BS3CG1DST_CL] = */ RT_OFFSETOF(BS3REGCTX, rcx.u8),
+ /* [BS3CG1DST_DL] = */ RT_OFFSETOF(BS3REGCTX, rdx.u8),
+ /* [BS3CG1DST_BL] = */ RT_OFFSETOF(BS3REGCTX, rbx.u8),
+ /* [BS3CG1DST_AH] = */ RT_OFFSETOF(BS3REGCTX, rax.b.bHi),
+ /* [BS3CG1DST_CH] = */ RT_OFFSETOF(BS3REGCTX, rcx.b.bHi),
+ /* [BS3CG1DST_DH] = */ RT_OFFSETOF(BS3REGCTX, rdx.b.bHi),
+ /* [BS3CG1DST_BH] = */ RT_OFFSETOF(BS3REGCTX, rbx.b.bHi),
+ /* [BS3CG1DST_SPL] = */ RT_OFFSETOF(BS3REGCTX, rsp.u8),
+ /* [BS3CG1DST_BPL] = */ RT_OFFSETOF(BS3REGCTX, rbp.u8),
+ /* [BS3CG1DST_SIL] = */ RT_OFFSETOF(BS3REGCTX, rsi.u8),
+ /* [BS3CG1DST_DIL] = */ RT_OFFSETOF(BS3REGCTX, rdi.u8),
+ /* [BS3CG1DST_R8L] = */ RT_OFFSETOF(BS3REGCTX, r8.u8),
+ /* [BS3CG1DST_R9L] = */ RT_OFFSETOF(BS3REGCTX, r9.u8),
+ /* [BS3CG1DST_R10L] = */ RT_OFFSETOF(BS3REGCTX, r10.u8),
+ /* [BS3CG1DST_R11L] = */ RT_OFFSETOF(BS3REGCTX, r11.u8),
+ /* [BS3CG1DST_R12L] = */ RT_OFFSETOF(BS3REGCTX, r12.u8),
+ /* [BS3CG1DST_R13L] = */ RT_OFFSETOF(BS3REGCTX, r13.u8),
+ /* [BS3CG1DST_R14L] = */ RT_OFFSETOF(BS3REGCTX, r14.u8),
+ /* [BS3CG1DST_R15L] = */ RT_OFFSETOF(BS3REGCTX, r15.u8),
+
+ /* [BS3CG1DST_AX] = */ RT_OFFSETOF(BS3REGCTX, rax.u16),
+ /* [BS3CG1DST_CX] = */ RT_OFFSETOF(BS3REGCTX, rcx.u16),
+ /* [BS3CG1DST_DX] = */ RT_OFFSETOF(BS3REGCTX, rdx.u16),
+ /* [BS3CG1DST_BX] = */ RT_OFFSETOF(BS3REGCTX, rbx.u16),
+ /* [BS3CG1DST_SP] = */ RT_OFFSETOF(BS3REGCTX, rsp.u16),
+ /* [BS3CG1DST_BP] = */ RT_OFFSETOF(BS3REGCTX, rbp.u16),
+ /* [BS3CG1DST_SI] = */ RT_OFFSETOF(BS3REGCTX, rsi.u16),
+ /* [BS3CG1DST_DI] = */ RT_OFFSETOF(BS3REGCTX, rdi.u16),
+ /* [BS3CG1DST_R8W] = */ RT_OFFSETOF(BS3REGCTX, r8.u16),
+ /* [BS3CG1DST_R9W] = */ RT_OFFSETOF(BS3REGCTX, r9.u16),
+ /* [BS3CG1DST_R10W] = */ RT_OFFSETOF(BS3REGCTX, r10.u16),
+ /* [BS3CG1DST_R11W] = */ RT_OFFSETOF(BS3REGCTX, r11.u16),
+ /* [BS3CG1DST_R12W] = */ RT_OFFSETOF(BS3REGCTX, r12.u16),
+ /* [BS3CG1DST_R13W] = */ RT_OFFSETOF(BS3REGCTX, r13.u16),
+ /* [BS3CG1DST_R14W] = */ RT_OFFSETOF(BS3REGCTX, r14.u16),
+ /* [BS3CG1DST_R15W] = */ RT_OFFSETOF(BS3REGCTX, r15.u16),
+
+ /* [BS3CG1DST_EAX] = */ RT_OFFSETOF(BS3REGCTX, rax.u32),
+ /* [BS3CG1DST_ECX] = */ RT_OFFSETOF(BS3REGCTX, rcx.u32),
+ /* [BS3CG1DST_EDX] = */ RT_OFFSETOF(BS3REGCTX, rdx.u32),
+ /* [BS3CG1DST_EBX] = */ RT_OFFSETOF(BS3REGCTX, rbx.u32),
+ /* [BS3CG1DST_ESP] = */ RT_OFFSETOF(BS3REGCTX, rsp.u32),
+ /* [BS3CG1DST_EBP] = */ RT_OFFSETOF(BS3REGCTX, rbp.u32),
+ /* [BS3CG1DST_ESI] = */ RT_OFFSETOF(BS3REGCTX, rsi.u32),
+ /* [BS3CG1DST_EDI] = */ RT_OFFSETOF(BS3REGCTX, rdi.u32),
+ /* [BS3CG1DST_R8D] = */ RT_OFFSETOF(BS3REGCTX, r8.u32),
+ /* [BS3CG1DST_R9D] = */ RT_OFFSETOF(BS3REGCTX, r9.u32),
+ /* [BS3CG1DST_R10D] = */ RT_OFFSETOF(BS3REGCTX, r10.u32),
+ /* [BS3CG1DST_R11D] = */ RT_OFFSETOF(BS3REGCTX, r11.u32),
+ /* [BS3CG1DST_R12D] = */ RT_OFFSETOF(BS3REGCTX, r12.u32),
+ /* [BS3CG1DST_R13D] = */ RT_OFFSETOF(BS3REGCTX, r13.u32),
+ /* [BS3CG1DST_R14D] = */ RT_OFFSETOF(BS3REGCTX, r14.u32),
+ /* [BS3CG1DST_R15D] = */ RT_OFFSETOF(BS3REGCTX, r15.u32),
+
+ /* [BS3CG1DST_RAX] = */ RT_OFFSETOF(BS3REGCTX, rax.u64),
+ /* [BS3CG1DST_RCX] = */ RT_OFFSETOF(BS3REGCTX, rcx.u64),
+ /* [BS3CG1DST_RDX] = */ RT_OFFSETOF(BS3REGCTX, rdx.u64),
+ /* [BS3CG1DST_RBX] = */ RT_OFFSETOF(BS3REGCTX, rbx.u64),
+ /* [BS3CG1DST_RSP] = */ RT_OFFSETOF(BS3REGCTX, rsp.u64),
+ /* [BS3CG1DST_RBP] = */ RT_OFFSETOF(BS3REGCTX, rbp.u64),
+ /* [BS3CG1DST_RSI] = */ RT_OFFSETOF(BS3REGCTX, rsi.u64),
+ /* [BS3CG1DST_RDI] = */ RT_OFFSETOF(BS3REGCTX, rdi.u64),
+ /* [BS3CG1DST_R8] = */ RT_OFFSETOF(BS3REGCTX, r8.u64),
+ /* [BS3CG1DST_R9] = */ RT_OFFSETOF(BS3REGCTX, r9.u64),
+ /* [BS3CG1DST_R10] = */ RT_OFFSETOF(BS3REGCTX, r10.u64),
+ /* [BS3CG1DST_R11] = */ RT_OFFSETOF(BS3REGCTX, r11.u64),
+ /* [BS3CG1DST_R12] = */ RT_OFFSETOF(BS3REGCTX, r12.u64),
+ /* [BS3CG1DST_R13] = */ RT_OFFSETOF(BS3REGCTX, r13.u64),
+ /* [BS3CG1DST_R14] = */ RT_OFFSETOF(BS3REGCTX, r14.u64),
+ /* [BS3CG1DST_R15] = */ RT_OFFSETOF(BS3REGCTX, r15.u64),
+
+ /* [BS3CG1DST_OZ_RAX] = */ RT_OFFSETOF(BS3REGCTX, rax),
+ /* [BS3CG1DST_OZ_RCX] = */ RT_OFFSETOF(BS3REGCTX, rcx),
+ /* [BS3CG1DST_OZ_RDX] = */ RT_OFFSETOF(BS3REGCTX, rdx),
+ /* [BS3CG1DST_OZ_RBX] = */ RT_OFFSETOF(BS3REGCTX, rbx),
+ /* [BS3CG1DST_OZ_RSP] = */ RT_OFFSETOF(BS3REGCTX, rsp),
+ /* [BS3CG1DST_OZ_RBP] = */ RT_OFFSETOF(BS3REGCTX, rbp),
+ /* [BS3CG1DST_OZ_RSI] = */ RT_OFFSETOF(BS3REGCTX, rsi),
+ /* [BS3CG1DST_OZ_RDI] = */ RT_OFFSETOF(BS3REGCTX, rdi),
+ /* [BS3CG1DST_OZ_R8] = */ RT_OFFSETOF(BS3REGCTX, r8),
+ /* [BS3CG1DST_OZ_R9] = */ RT_OFFSETOF(BS3REGCTX, r9),
+ /* [BS3CG1DST_OZ_R10] = */ RT_OFFSETOF(BS3REGCTX, r10),
+ /* [BS3CG1DST_OZ_R11] = */ RT_OFFSETOF(BS3REGCTX, r11),
+ /* [BS3CG1DST_OZ_R12] = */ RT_OFFSETOF(BS3REGCTX, r12),
+ /* [BS3CG1DST_OZ_R13] = */ RT_OFFSETOF(BS3REGCTX, r13),
+ /* [BS3CG1DST_OZ_R14] = */ RT_OFFSETOF(BS3REGCTX, r14),
+ /* [BS3CG1DST_OZ_R15] = */ RT_OFFSETOF(BS3REGCTX, r15),
+
+ /* [BS3CG1DST_CR0] = */ RT_OFFSETOF(BS3REGCTX, cr0),
+ /* [BS3CG1DST_CR4] = */ RT_OFFSETOF(BS3REGCTX, cr4),
+ /* [BS3CG1DST_XCR0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, fXcr0Saved),
+
+ /* [BS3CG1DST_FCW] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FCW),
+ /* [BS3CG1DST_FSW] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FSW),
+ /* [BS3CG1DST_FTW] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FTW),
+ /* [BS3CG1DST_FOP] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FOP),
+ /* [BS3CG1DST_FPUIP] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FPUIP),
+ /* [BS3CG1DST_FPUCS] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.CS),
+ /* [BS3CG1DST_FPUDP] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.FPUDP),
+ /* [BS3CG1DST_FPUDS] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.DS),
+ /* [BS3CG1DST_MXCSR] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.MXCSR),
+ /* [BS3CG1DST_ST0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[0]),
+ /* [BS3CG1DST_ST1] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[1]),
+ /* [BS3CG1DST_ST2] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[2]),
+ /* [BS3CG1DST_ST3] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[3]),
+ /* [BS3CG1DST_ST4] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[4]),
+ /* [BS3CG1DST_ST5] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[5]),
+ /* [BS3CG1DST_ST6] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[6]),
+ /* [BS3CG1DST_ST7] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[7]),
+ /* [BS3CG1DST_MM0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[0]),
+ /* [BS3CG1DST_MM1] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[1]),
+ /* [BS3CG1DST_MM2] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[2]),
+ /* [BS3CG1DST_MM3] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[3]),
+ /* [BS3CG1DST_MM4] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[4]),
+ /* [BS3CG1DST_MM5] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[5]),
+ /* [BS3CG1DST_MM6] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[6]),
+ /* [BS3CG1DST_MM7] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[7]),
+ /* [BS3CG1DST_MM0_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[0]),
+ /* [BS3CG1DST_MM1_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[1]),
+ /* [BS3CG1DST_MM2_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[2]),
+ /* [BS3CG1DST_MM3_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[3]),
+ /* [BS3CG1DST_MM4_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[4]),
+ /* [BS3CG1DST_MM5_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[5]),
+ /* [BS3CG1DST_MM6_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[6]),
+ /* [BS3CG1DST_MM7_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aRegs[7]),
+
+ /* [BS3CG1DST_XMM0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]),
+ /* [BS3CG1DST_XMM1] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]),
+ /* [BS3CG1DST_XMM2] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]),
+ /* [BS3CG1DST_XMM3] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]),
+ /* [BS3CG1DST_XMM4] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]),
+ /* [BS3CG1DST_XMM5] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]),
+ /* [BS3CG1DST_XMM6] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]),
+ /* [BS3CG1DST_XMM7] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]),
+ /* [BS3CG1DST_XMM8] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]),
+ /* [BS3CG1DST_XMM9] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]),
+ /* [BS3CG1DST_XMM10] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]),
+ /* [BS3CG1DST_XMM11] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]),
+ /* [BS3CG1DST_XMM12] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]),
+ /* [BS3CG1DST_XMM13] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]),
+ /* [BS3CG1DST_XMM14] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]),
+ /* [BS3CG1DST_XMM15] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]),
+ /* [BS3CG1DST_XMM0_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]),
+ /* [BS3CG1DST_XMM1_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]),
+ /* [BS3CG1DST_XMM2_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]),
+ /* [BS3CG1DST_XMM3_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]),
+ /* [BS3CG1DST_XMM4_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]),
+ /* [BS3CG1DST_XMM5_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]),
+ /* [BS3CG1DST_XMM6_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]),
+ /* [BS3CG1DST_XMM7_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]),
+ /* [BS3CG1DST_XMM8_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]),
+ /* [BS3CG1DST_XMM9_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]),
+ /* [BS3CG1DST_XMM10_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]),
+ /* [BS3CG1DST_XMM11_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]),
+ /* [BS3CG1DST_XMM12_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]),
+ /* [BS3CG1DST_XMM13_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]),
+ /* [BS3CG1DST_XMM14_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]),
+ /* [BS3CG1DST_XMM15_LO] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]),
+ /* [BS3CG1DST_XMM0_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM1_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM2_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM3_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM4_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM5_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM6_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM7_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM8_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM9_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM10_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM11_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM12_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM13_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM14_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM15_HI] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]) + sizeof(uint64_t),
+ /* [BS3CG1DST_XMM0_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]),
+ /* [BS3CG1DST_XMM1_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]),
+ /* [BS3CG1DST_XMM2_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]),
+ /* [BS3CG1DST_XMM3_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]),
+ /* [BS3CG1DST_XMM4_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]),
+ /* [BS3CG1DST_XMM5_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]),
+ /* [BS3CG1DST_XMM6_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]),
+ /* [BS3CG1DST_XMM7_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]),
+ /* [BS3CG1DST_XMM8_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]),
+ /* [BS3CG1DST_XMM9_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]),
+ /* [BS3CG1DST_XMM10_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]),
+ /* [BS3CG1DST_XMM11_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]),
+ /* [BS3CG1DST_XMM12_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]),
+ /* [BS3CG1DST_XMM13_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]),
+ /* [BS3CG1DST_XMM14_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]),
+ /* [BS3CG1DST_XMM15_LO_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]),
+ /* [BS3CG1DST_XMM0_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]),
+ /* [BS3CG1DST_XMM1_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]),
+ /* [BS3CG1DST_XMM2_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]),
+ /* [BS3CG1DST_XMM3_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]),
+ /* [BS3CG1DST_XMM4_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]),
+ /* [BS3CG1DST_XMM5_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]),
+ /* [BS3CG1DST_XMM6_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]),
+ /* [BS3CG1DST_XMM7_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]),
+ /* [BS3CG1DST_XMM8_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]),
+ /* [BS3CG1DST_XMM9_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]),
+ /* [BS3CG1DST_XMM10_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]),
+ /* [BS3CG1DST_XMM11_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]),
+ /* [BS3CG1DST_XMM12_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]),
+ /* [BS3CG1DST_XMM13_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]),
+ /* [BS3CG1DST_XMM14_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]),
+ /* [BS3CG1DST_XMM15_DW0] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]),
+ /* [BS3CG1DST_XMM0_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0]),
+ /* [BS3CG1DST_XMM1_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1]),
+ /* [BS3CG1DST_XMM2_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2]),
+ /* [BS3CG1DST_XMM3_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3]),
+ /* [BS3CG1DST_XMM4_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4]),
+ /* [BS3CG1DST_XMM5_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5]),
+ /* [BS3CG1DST_XMM6_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6]),
+ /* [BS3CG1DST_XMM7_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7]),
+ /* [BS3CG1DST_XMM8_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8]),
+ /* [BS3CG1DST_XMM9_DW0_ZX] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9]),
+ /* [BS3CG1DST_XMM10_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10]),
+ /* [BS3CG1DST_XMM11_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11]),
+ /* [BS3CG1DST_XMM12_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12]),
+ /* [BS3CG1DST_XMM13_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13]),
+ /* [BS3CG1DST_XMM14_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14]),
+ /* [BS3CG1DST_XMM15_DW0_ZX] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15]),
+ /* [BS3CG1DST_XMM0_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[0].au32[1]),
+ /* [BS3CG1DST_XMM1_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[1].au32[1]),
+ /* [BS3CG1DST_XMM2_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[2].au32[1]),
+ /* [BS3CG1DST_XMM3_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[3].au32[1]),
+ /* [BS3CG1DST_XMM4_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[4].au32[1]),
+ /* [BS3CG1DST_XMM5_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[5].au32[1]),
+ /* [BS3CG1DST_XMM6_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[6].au32[1]),
+ /* [BS3CG1DST_XMM7_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[7].au32[1]),
+ /* [BS3CG1DST_XMM8_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[8].au32[1]),
+ /* [BS3CG1DST_XMM9_HI96] = */ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[9].au32[1]),
+ /* [BS3CG1DST_XMM10_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[10].au32[1]),
+ /* [BS3CG1DST_XMM11_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[11].au32[1]),
+ /* [BS3CG1DST_XMM12_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[12].au32[1]),
+ /* [BS3CG1DST_XMM13_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[13].au32[1]),
+ /* [BS3CG1DST_XMM14_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[14].au32[1]),
+ /* [BS3CG1DST_XMM15_HI96] =*/ sizeof(BS3REGCTX) + RT_OFFSETOF(BS3EXTCTX, Ctx.x87.aXMM[15].au32[1]),
+
+ /* [BS3CG1DST_YMM0] = */ ~0U,
+ /* [BS3CG1DST_YMM1] = */ ~0U,
+ /* [BS3CG1DST_YMM2] = */ ~0U,
+ /* [BS3CG1DST_YMM3] = */ ~0U,
+ /* [BS3CG1DST_YMM4] = */ ~0U,
+ /* [BS3CG1DST_YMM5] = */ ~0U,
+ /* [BS3CG1DST_YMM6] = */ ~0U,
+ /* [BS3CG1DST_YMM7] = */ ~0U,
+ /* [BS3CG1DST_YMM8] = */ ~0U,
+ /* [BS3CG1DST_YMM9] = */ ~0U,
+ /* [BS3CG1DST_YMM10] = */ ~0U,
+ /* [BS3CG1DST_YMM11] = */ ~0U,
+ /* [BS3CG1DST_YMM12] = */ ~0U,
+ /* [BS3CG1DST_YMM13] = */ ~0U,
+ /* [BS3CG1DST_YMM14] = */ ~0U,
+ /* [BS3CG1DST_YMM15] = */ ~0U,
+
+ /* [BS3CG1DST_VALUE_XCPT] = */ ~0U,
+};
+AssertCompile(RT_ELEMENTS(g_aoffBs3Cg1DstFields) == BS3CG1DST_END);
+
+/** Destination field names. */
+static const struct { char sz[12]; } g_aszBs3Cg1DstFields[] =
+{
+ { "INVALID" },
+ { "OP1" },
+ { "OP2" },
+ { "OP3" },
+ { "OP4" },
+ { "EFL" },
+ { "EFL_UND" },
+
+ { "AL" },
+ { "CL" },
+ { "DL" },
+ { "BL" },
+ { "AH" },
+ { "CH" },
+ { "DH" },
+ { "BH" },
+ { "SPL" },
+ { "BPL" },
+ { "SIL" },
+ { "DIL" },
+ { "R8L" },
+ { "R9L" },
+ { "R10L" },
+ { "R11L" },
+ { "R12L" },
+ { "R13L" },
+ { "R14L" },
+ { "R15L" },
+
+ { "AX" },
+ { "CX" },
+ { "DX" },
+ { "BX" },
+ { "SP" },
+ { "BP" },
+ { "SI" },
+ { "DI" },
+ { "R8W" },
+ { "R9W" },
+ { "R10W" },
+ { "R11W" },
+ { "R12W" },
+ { "R13W" },
+ { "R14W" },
+ { "R15W" },
+
+ { "EAX" },
+ { "ECX" },
+ { "EDX" },
+ { "EBX" },
+ { "ESP" },
+ { "EBP" },
+ { "ESI" },
+ { "EDI" },
+ { "R8D" },
+ { "R9D" },
+ { "R10D" },
+ { "R11D" },
+ { "R12D" },
+ { "R13D" },
+ { "R14D" },
+ { "R15D" },
+
+ { "RAX" },
+ { "RCX" },
+ { "RDX" },
+ { "RBX" },
+ { "RSP" },
+ { "RBP" },
+ { "RSI" },
+ { "RDI" },
+ { "R8" },
+ { "R9" },
+ { "R10" },
+ { "R11" },
+ { "R12" },
+ { "R13" },
+ { "R14" },
+ { "R15" },
+
+ { "OZ_RAX" },
+ { "OZ_RCX" },
+ { "OZ_RDX" },
+ { "OZ_RBX" },
+ { "OZ_RSP" },
+ { "OZ_RBP" },
+ { "OZ_RSI" },
+ { "OZ_RDI" },
+ { "OZ_R8" },
+ { "OZ_R9" },
+ { "OZ_R10" },
+ { "OZ_R11" },
+ { "OZ_R12" },
+ { "OZ_R13" },
+ { "OZ_R14" },
+ { "OZ_R15" },
+
+ { "CR0" },
+ { "CR4" },
+ { "XCR0" },
+
+ { "FCW" },
+ { "FSW" },
+ { "FTW" },
+ { "FOP" },
+ { "FPUIP" },
+ { "FPUCS" },
+ { "FPUDP" },
+ { "FPUDS" },
+ { "MXCSR" },
+ { "ST0" },
+ { "ST1" },
+ { "ST2" },
+ { "ST3" },
+ { "ST4" },
+ { "ST5" },
+ { "ST6" },
+ { "ST7" },
+ { "MM0" },
+ { "MM1" },
+ { "MM2" },
+ { "MM3" },
+ { "MM4" },
+ { "MM5" },
+ { "MM6" },
+ { "MM7" },
+ { "MM0_LO_ZX" },
+ { "MM1_LO_ZX" },
+ { "MM2_LO_ZX" },
+ { "MM3_LO_ZX" },
+ { "MM4_LO_ZX" },
+ { "MM5_LO_ZX" },
+ { "MM6_LO_ZX" },
+ { "MM7_LO_ZX" },
+ { "XMM0" },
+ { "XMM1" },
+ { "XMM2" },
+ { "XMM3" },
+ { "XMM4" },
+ { "XMM5" },
+ { "XMM6" },
+ { "XMM7" },
+ { "XMM8" },
+ { "XMM9" },
+ { "XMM10" },
+ { "XMM11" },
+ { "XMM12" },
+ { "XMM13" },
+ { "XMM14" },
+ { "XMM15" },
+ { "XMM0_LO" },
+ { "XMM1_LO" },
+ { "XMM2_LO" },
+ { "XMM3_LO" },
+ { "XMM4_LO" },
+ { "XMM5_LO" },
+ { "XMM6_LO" },
+ { "XMM7_LO" },
+ { "XMM8_LO" },
+ { "XMM9_LO" },
+ { "XMM10_LO" },
+ { "XMM11_LO" },
+ { "XMM12_LO" },
+ { "XMM13_LO" },
+ { "XMM14_LO" },
+ { "XMM15_LO" },
+ { "XMM0_HI" },
+ { "XMM1_HI" },
+ { "XMM2_HI" },
+ { "XMM3_HI" },
+ { "XMM4_HI" },
+ { "XMM5_HI" },
+ { "XMM6_HI" },
+ { "XMM7_HI" },
+ { "XMM8_HI" },
+ { "XMM9_HI" },
+ { "XMM10_HI" },
+ { "XMM11_HI" },
+ { "XMM12_HI" },
+ { "XMM13_HI" },
+ { "XMM14_HI" },
+ { "XMM15_HI" },
+ { "XMM0_LO_ZX" },
+ { "XMM1_LO_ZX" },
+ { "XMM2_LO_ZX" },
+ { "XMM3_LO_ZX" },
+ { "XMM4_LO_ZX" },
+ { "XMM5_LO_ZX" },
+ { "XMM6_LO_ZX" },
+ { "XMM7_LO_ZX" },
+ { "XMM8_LO_ZX" },
+ { "XMM9_LO_ZX" },
+ { "XMM10_LO_ZX" },
+ { "XMM11_LO_ZX" },
+ { "XMM12_LO_ZX" },
+ { "XMM13_LO_ZX" },
+ { "XMM14_LO_ZX" },
+ { "XMM15_LO_ZX" },
+ { "XMM0_DW0" },
+ { "XMM1_DW0" },
+ { "XMM2_DW0" },
+ { "XMM3_DW0" },
+ { "XMM4_DW0" },
+ { "XMM5_DW0" },
+ { "XMM6_DW0" },
+ { "XMM7_DW0" },
+ { "XMM8_DW0" },
+ { "XMM9_DW0" },
+ { "XMM10_DW0" },
+ { "XMM11_DW0" },
+ { "XMM12_DW0" },
+ { "XMM13_DW0" },
+ { "XMM14_DW0" },
+ { "XMM15_DW0" },
+ { "XMM0_DW0_ZX" },
+ { "XMM1_DW0_ZX" },
+ { "XMM2_DW0_ZX" },
+ { "XMM3_DW0_ZX" },
+ { "XMM4_DW0_ZX" },
+ { "XMM5_DW0_ZX" },
+ { "XMM6_DW0_ZX" },
+ { "XMM7_DW0_ZX" },
+ { "XMM8_DW0_ZX" },
+ { "XMM9_DW0_ZX" },
+ { "XMM10_DW0_ZX" },
+ { "XMM11_DW0_ZX" },
+ { "XMM12_DW0_ZX" },
+ { "XMM13_DW0_ZX" },
+ { "XMM14_DW0_ZX" },
+ { "XMM15_DW0_ZX" },
+ { "XMM0_HI96" },
+ { "XMM1_HI96" },
+ { "XMM2_HI96" },
+ { "XMM3_HI96" },
+ { "XMM4_HI96" },
+ { "XMM5_HI96" },
+ { "XMM6_HI96" },
+ { "XMM7_HI96" },
+ { "XMM8_HI96" },
+ { "XMM9_HI96" },
+ { "XMM10_HI96" },
+ { "XMM11_HI96" },
+ { "XMM12_HI96" },
+ { "XMM13_HI96" },
+ { "XMM14_HI96" },
+ { "XMM15_HI96" },
+ { "YMM0" },
+ { "YMM1" },
+ { "YMM2" },
+ { "YMM3" },
+ { "YMM4" },
+ { "YMM5" },
+ { "YMM6" },
+ { "YMM7" },
+ { "YMM8" },
+ { "YMM9" },
+ { "YMM10" },
+ { "YMM11" },
+ { "YMM12" },
+ { "YMM13" },
+ { "YMM14" },
+ { "YMM15" },
+
+ { "VALXCPT" },
+};
+AssertCompile(RT_ELEMENTS(g_aszBs3Cg1DstFields) >= BS3CG1DST_END);
+AssertCompile(RT_ELEMENTS(g_aszBs3Cg1DstFields) == BS3CG1DST_END);
+
+
+#if 0
+static const struct
+{
+ uint8_t cbPrefixes;
+ uint8_t abPrefixes[14];
+ uint16_t fEffective;
+} g_aPrefixVariations[] =
+{
+ { 0, { 0x00 }, BS3CG1_PF_NONE },
+
+ { 1, { P_OZ }, BS3CG1_PF_OZ },
+ { 1, { P_CS }, BS3CG1_PF_CS },
+ { 1, { P_DS }, BS3CG1_PF_DS },
+ { 1, { P_ES }, BS3CG1_PF_ES },
+ { 1, { P_FS }, BS3CG1_PF_FS },
+ { 1, { P_GS }, BS3CG1_PF_GS },
+ { 1, { P_SS }, BS3CG1_PF_SS },
+ { 1, { P_LK }, BS3CG1_PF_LK },
+
+ { 2, { P_CS, P_OZ, }, BS3CG1_PF_CS | BS3CFG1_PF_OZ },
+ { 2, { P_DS, P_OZ, }, BS3CG1_PF_DS | BS3CFG1_PF_OZ },
+ { 2, { P_ES, P_OZ, }, BS3CG1_PF_ES | BS3CFG1_PF_OZ },
+ { 2, { P_FS, P_OZ, }, BS3CG1_PF_FS | BS3CFG1_PF_OZ },
+ { 2, { P_GS, P_OZ, }, BS3CG1_PF_GS | BS3CFG1_PF_OZ },
+ { 2, { P_GS, P_OZ, }, BS3CG1_PF_SS | BS3CFG1_PF_OZ },
+ { 2, { P_SS, P_OZ, }, BS3CG1_PF_SS | BS3CFG1_PF_OZ },
+
+ { 2, { P_OZ, P_CS, }, BS3CG1_PF_CS | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_DS, }, BS3CG1_PF_DS | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_ES, }, BS3CG1_PF_ES | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_FS, }, BS3CG1_PF_FS | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_GS, }, BS3CG1_PF_GS | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_GS, }, BS3CG1_PF_SS | BS3CFG1_PF_OZ },
+ { 2, { P_OZ, P_SS, }, BS3CG1_PF_SS | BS3CFG1_PF_OZ },
+};
+
+static const uint16_t g_afPfxKindToIgnoredFlags[BS3CG1PFXKIND_END] =
+{
+ /* [BS3CG1PFXKIND_INVALID] = */ UINT16_MAX,
+ /* [BS3CG1PFXKIND_MODRM] = */ 0,
+ /* [BS3CG1PFXKIND_MODRM_NO_OP_SIZES] = */ BS3CG1_PF_OZ | BS3CG1_PF_W,
+};
+
+#endif
+
+
+/**
+ * Checks if >= 16 byte SSE alignment are exempted for the exception type.
+ *
+ * @returns true / false.
+ * @param enmXcptType The type to check.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1XcptTypeIsUnaligned(BS3CG1XCPTTYPE enmXcptType)
+{
+ switch (enmXcptType)
+ {
+ case BS3CG1XCPTTYPE_1:
+ case BS3CG1XCPTTYPE_2:
+ case BS3CG1XCPTTYPE_4:
+ return false;
+ case BS3CG1XCPTTYPE_NONE:
+ case BS3CG1XCPTTYPE_3:
+ case BS3CG1XCPTTYPE_4UA:
+ case BS3CG1XCPTTYPE_5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+/**
+ * Checks if >= 16 byte AVX alignment are exempted for the exception type.
+ *
+ * @returns true / false.
+ * @param enmXcptType The type to check.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1XcptTypeIsVexUnaligned(BS3CG1XCPTTYPE enmXcptType)
+{
+ switch (enmXcptType)
+ {
+ case BS3CG1XCPTTYPE_1:
+ return false;
+
+ case BS3CG1XCPTTYPE_NONE:
+ case BS3CG1XCPTTYPE_2:
+ case BS3CG1XCPTTYPE_3:
+ case BS3CG1XCPTTYPE_4:
+ case BS3CG1XCPTTYPE_4UA:
+ case BS3CG1XCPTTYPE_5:
+ case BS3CG1XCPTTYPE_6:
+ case BS3CG1XCPTTYPE_11:
+ case BS3CG1XCPTTYPE_12:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+DECLINLINE(unsigned) BS3_NEAR_CODE Bs3Cg1InsertReqPrefix(PBS3CG1STATE pThis, unsigned offDst)
+{
+ switch (pThis->enmPrefixKind)
+ {
+ case BS3CG1PFXKIND_REQ_66:
+ pThis->abCurInstr[offDst] = 0x66;
+ break;
+ case BS3CG1PFXKIND_REQ_F2:
+ pThis->abCurInstr[offDst] = 0xf2;
+ break;
+ case BS3CG1PFXKIND_REQ_F3:
+ pThis->abCurInstr[offDst] = 0xf3;
+ break;
+ default:
+ return offDst;
+ }
+ return offDst + 1;
+}
+
+
+DECLINLINE(unsigned) BS3_NEAR_CODE Bs3Cg1InsertOpcodes(PBS3CG1STATE pThis, unsigned offDst)
+{
+ switch (pThis->cbOpcodes)
+ {
+ case 4: pThis->abCurInstr[offDst + 3] = pThis->abOpcodes[3];
+ case 3: pThis->abCurInstr[offDst + 2] = pThis->abOpcodes[2];
+ case 2: pThis->abCurInstr[offDst + 1] = pThis->abOpcodes[1];
+ case 1: pThis->abCurInstr[offDst] = pThis->abOpcodes[0];
+ return offDst + pThis->cbOpcodes;
+
+ default:
+ BS3_ASSERT(0);
+ return 0;
+ }
+}
+
+
+/**
+ * Inserts a ModR/M byte with mod=3 and set the two idxFields members.
+ *
+ * @returns off + 1.
+ * @param pThis The state.
+ * @param off Current instruction offset.
+ * @param uReg Register index for ModR/M.reg.
+ * @param uRegMem Register index for ModR/M.rm.
+ */
+static unsigned Bs3Cg1InsertModRmWithRegFields(PBS3CG1STATE pThis, unsigned off, uint8_t uReg, uint8_t uRegMem)
+{
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, uReg & 7, uRegMem & 7);
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + uReg;
+ pThis->aOperands[pThis->iRmOp ].idxField = pThis->aOperands[pThis->iRmOp ].idxFieldBase + uRegMem;
+ return off;
+}
+
+
+
+/**
+ * Cleans up state and context changes made by the encoder.
+ *
+ * @param pThis The state.
+ */
+static void BS3_NEAR_CODE Bs3Cg1EncodeCleanup(PBS3CG1STATE pThis)
+{
+ /* Restore the DS registers in the contexts. */
+ unsigned iRing = 4;
+ while (iRing-- > 0)
+ pThis->aInitialCtxs[iRing].ds = pThis->aSavedSegRegs[iRing].ds;
+
+ switch (pThis->enmEncoding)
+ {
+ /* Most encodings currently doesn't need any special cleaning up. */
+ default:
+ return;
+ }
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cfg1EncodeMemMod0Disp(PBS3CG1STATE pThis, bool fAddrOverride, unsigned off, uint8_t iReg,
+ uint8_t cbOp, uint8_t cbMisalign, BS3CG1OPLOC enmLocation)
+{
+ pThis->aOperands[pThis->iRmOp].idxField = BS3CG1DST_INVALID;
+ pThis->aOperands[pThis->iRmOp].enmLocation = enmLocation;
+ pThis->aOperands[pThis->iRmOp].cbOp = cbOp;
+ pThis->aOperands[pThis->iRmOp].off = cbOp + cbMisalign;
+
+ if ( BS3_MODE_IS_16BIT_CODE(pThis->bMode)
+ || (fAddrOverride && BS3_MODE_IS_32BIT_CODE(pThis->bMode)) )
+ {
+ /*
+ * 16-bit code doing 16-bit or 32-bit addressing,
+ * or 32-bit code doing 16-bit addressing.
+ */
+ unsigned iRing = 4;
+ if (BS3_MODE_IS_RM_OR_V86(pThis->bMode))
+ while (iRing-- > 0)
+ pThis->aInitialCtxs[iRing].ds = pThis->DataPgFar.sel;
+ else
+ while (iRing-- > 0)
+ pThis->aInitialCtxs[iRing].ds = pThis->DataPgFar.sel | iRing;
+ if (!fAddrOverride || BS3_MODE_IS_32BIT_CODE(pThis->bMode))
+ {
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(0, iReg, 6 /*disp16*/);
+ *(uint16_t *)&pThis->abCurInstr[off] = pThis->DataPgFar.off + X86_PAGE_SIZE - cbOp - cbMisalign;
+ off += 2;
+ }
+ else
+ {
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(0, iReg, 5 /*disp32*/);
+ *(uint32_t *)&pThis->abCurInstr[off] = pThis->DataPgFar.off + X86_PAGE_SIZE - cbOp - cbMisalign;
+ off += 4;
+ }
+ }
+ else
+ {
+ /*
+ * 32-bit code doing 32-bit addressing,
+ * or 64-bit code doing either 64-bit or 32-bit addressing.
+ */
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(0, iReg, 5 /*disp32*/);
+ *(uint32_t *)&pThis->abCurInstr[off] = BS3_FP_OFF(pThis->pbDataPg) + X86_PAGE_SIZE - cbOp - cbMisalign;
+
+#if ARCH_BITS == 64
+ /* In 64-bit mode we always have a rip relative encoding regardless of fAddrOverride. */
+ if (BS3CG1_IS_64BIT_TARGET(pThis))
+ *(uint32_t *)&pThis->abCurInstr[off] -= BS3_FP_OFF(&pThis->pbCodePg[X86_PAGE_SIZE]);
+#endif
+ off += 4;
+ }
+
+ /*
+ * Fill the memory with 0xcc.
+ */
+ switch (cbOp + cbMisalign)
+ {
+ case 8: pThis->pbDataPg[X86_PAGE_SIZE - 8] = 0xcc; RT_FALL_THRU();
+ case 7: pThis->pbDataPg[X86_PAGE_SIZE - 7] = 0xcc; RT_FALL_THRU();
+ case 6: pThis->pbDataPg[X86_PAGE_SIZE - 6] = 0xcc; RT_FALL_THRU();
+ case 5: pThis->pbDataPg[X86_PAGE_SIZE - 5] = 0xcc; RT_FALL_THRU();
+ case 4: pThis->pbDataPg[X86_PAGE_SIZE - 4] = 0xcc; RT_FALL_THRU();
+ case 3: pThis->pbDataPg[X86_PAGE_SIZE - 3] = 0xcc; RT_FALL_THRU();
+ case 2: pThis->pbDataPg[X86_PAGE_SIZE - 2] = 0xcc; RT_FALL_THRU();
+ case 1: pThis->pbDataPg[X86_PAGE_SIZE - 1] = 0xcc; RT_FALL_THRU();
+ case 0: break;
+ default:
+ {
+ BS3CG1_DPRINTF(("Bs3MemSet(%p,%#x,%#x)\n", &pThis->pbDataPg[X86_PAGE_SIZE - cbOp - cbMisalign], 0xcc, cbOp - cbMisalign));
+ Bs3MemSet(&pThis->pbDataPg[X86_PAGE_SIZE - cbOp - cbMisalign], 0xcc, cbOp - cbMisalign);
+ break;
+ }
+ }
+
+ return off;
+}
+
+
+#if 0 /* unused */
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegField(PBS3CG1STATE pThis, bool fAddrOverride, unsigned off, uint8_t iReg,
+ uint8_t cbOp, uint8_t cbMisalign, BS3CG1OPLOC enmLocation)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, fAddrOverride, off, iReg & 7, cbOp, cbMisalign, enmLocation);
+}
+#endif
+
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(PBS3CG1STATE pThis, unsigned off, uint8_t iReg)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, false /*fAddrOverride*/, off, iReg & 7,
+ pThis->aOperands[pThis->iRmOp].cbOp,
+ 0 /*cbMisalign*/,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsAddrOverride(PBS3CG1STATE pThis, unsigned off, uint8_t iReg)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, true /*fAddrOverride*/, off, iReg & 7,
+ pThis->aOperands[pThis->iRmOp].cbOp,
+ 0 /*cbMisalign*/,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(PBS3CG1STATE pThis, unsigned off, uint8_t iReg, uint8_t cbMisalign)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, false /*fAddrOverride*/, off, iReg & 7,
+ pThis->aOperands[pThis->iRmOp].cbOp,
+ cbMisalign,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaults(PBS3CG1STATE pThis, unsigned off, uint8_t iReg, uint8_t cbOp)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, false /*fAddrOverride*/, off, iReg & 7, cbOp, 0 /*cbMisalign*/,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+/** Also encodes idxField of the register operand using idxFieldBase. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaultsAddrOverride(PBS3CG1STATE pThis, unsigned off, uint8_t iReg, uint8_t cbOp)
+{
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + iReg;
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, true /*fAddrOverride*/, off, iReg & 7, cbOp, 0 /*cbMisalign*/,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+
+/** The modrm.reg value is taken from the instruction byte at @a off. */
+static unsigned BS3_NEAR_CODE
+Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(PBS3CG1STATE pThis, unsigned off)
+{
+ return Bs3Cfg1EncodeMemMod0Disp(pThis, false /*fAddrOverride*/, off,
+ (pThis->abCurInstr[off] & X86_MODRM_REG_MASK) >> X86_MODRM_REG_SHIFT,
+ pThis->aOperands[pThis->iRmOp].cbOp,
+ 0 /*cbMisalign*/,
+ pThis->aOperands[pThis->iRmOp].enmLocation);
+}
+
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Eb_Gb_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ /* Start by reg,reg encoding. */
+ case 0:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, X86_GREG_xAX, X86_GREG_xCX);
+ break;
+ case 1:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5 /*CH*/);
+ break;
+ case 2:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80386)
+ return 0;
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 6 /*DH*/);
+ break;
+ /* Tests with address overrides go last! */
+ case 3:
+ pThis->abCurInstr[0] = P_AZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsAddrOverride(pThis, off, 7 /*BH*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Gv_Ev__OR__MODRM_Ev_Gv(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ unsigned cbOp;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, X86_GREG_xBX, X86_GREG_xDX);
+ cbOp = pThis->cbOpDefault;
+ break;
+ case 1:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ cbOp = pThis->cbOpDefault;
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaults(pThis, off, X86_GREG_xBP, cbOp);
+ break;
+ case 2:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80386)
+ return 0;
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, X86_GREG_xAX, X86_GREG_xCX);
+ cbOp = pThis->cbOpOvrd66;
+ break;
+ case 3:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ cbOp = pThis->cbOpOvrd66;
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaults(pThis, off, X86_GREG_xSI, cbOp);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 2 : 0;
+ break;
+ case 4:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, X86_GREG_xBX, X86_GREG_xDX);
+ cbOp = pThis->cbOpOvrdRexW;
+ break;
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RB_;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, X86_GREG_x14, X86_GREG_x12);
+ cbOp = pThis->cbOpDefault;
+ break;
+ /* Tests with address overrides go last!*/
+ case 6:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ pThis->abCurInstr[0] = P_AZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ cbOp = pThis->cbOpDefault;
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaultsAddrOverride(pThis, off, X86_GREG_xDI, cbOp);
+ break;
+ case 7:
+ pThis->abCurInstr[0] = P_OZ;
+ pThis->abCurInstr[1] = P_AZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 2));
+ cbOp = pThis->cbOpOvrd66;
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaultsAddrOverride(pThis, off, X86_GREG_xDI, cbOp);
+ break;
+ default:
+ return 0;
+ }
+ pThis->aOperands[0].cbOp = cbOp;
+ pThis->aOperands[1].cbOp = cbOp;
+ pThis->cbOperand = cbOp;
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Pq_WO_Qq(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 7);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6 /*no +8*/, 2 /*no +8*/);
+ break;
+#endif
+ case 3:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 4:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 /*iReg - no +8*/);
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Pq_WO_Uq(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6 /*no+8*/, 2 + 8);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_PdZx_WO_Ed_WZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6 /*no +8*/, 2+8);
+ break;
+#endif
+ case 3:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 4:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 /*iReg*/);
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Pq_WO_Eq_WNZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+#if ARCH_BITS == 64
+ if (BS3CG1_IS_64BIT_TARGET(pThis))
+ {
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_WRBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6 /*no +8*/, 2+8);
+ break;
+ case 3:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 4:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ break;
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_WRBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 /*iReg*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+ }
+#endif
+ return 0;
+}
+
+
+/* Differs from Bs3Cg1EncodeNext_MODRM_PdZx_WO_Ed_WZ in that REX.R isn't ignored. */
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Vd_WO_Ed_WZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6+8, 2+8);
+ break;
+#endif
+ case 3:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 4:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7+8 /*iReg*/);
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/* Differs from Bs3Cg1EncodeNext_MODRM_Pq_WO_Eq_WNZ in that REX.R isn't ignored. */
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Vq_WO_Eq_WNZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+#if ARCH_BITS == 64
+ if (BS3CG1_IS_64BIT_TARGET(pThis))
+ {
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_WRBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6+8, 2+8);
+ break;
+ case 4:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 5:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ break;
+ case 6:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_WRBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7+8 /*iReg*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+ }
+#endif
+ return 0;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Vsomething_Usomething_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 2, 2);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 3+8, 7+8);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0)), 1, 0);
+ break;
+ case 1:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2 /*iReg*/);
+ break;
+ case 2:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 3 /*iReg*/, 1 /*cbMisalign*/);
+ if (!Bs3Cg1XcptTypeIsUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Vsomething_Nsomething(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 7);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_WRBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6 + 8, 7 /*no +8*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Gv_RO_Ma(PBS3CG1STATE pThis, unsigned iEncoding) /* bound instr */
+{
+ unsigned off;
+ unsigned cbOp = BS3_MODE_IS_16BIT_CODE(pThis->bMode) ? 2 : 4;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaults(pThis, off, X86_GREG_xBP, cbOp * 2);
+ break;
+ case 1:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80386)
+ return 0;
+ cbOp = cbOp == 2 ? 4 : 2;
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaults(pThis, off, X86_GREG_xBP, cbOp * 2);
+ break;
+ case 2:
+ pThis->abCurInstr[0] = P_AZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaultsAddrOverride(pThis, off, X86_GREG_xBP, cbOp * 2);
+ break;
+ case 3:
+ cbOp = cbOp == 2 ? 4 : 2;
+ pThis->abCurInstr[0] = P_AZ;
+ pThis->abCurInstr[1] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 2));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndSizeAndDefaultsAddrOverride(pThis, off, X86_GREG_xBP, cbOp * 2);
+ break;
+ default:
+ return 0;
+ }
+ pThis->aOperands[pThis->iRegOp].cbOp = cbOp;
+ pThis->cbOperand = cbOp;
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Msomething(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0)) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Msomething_Psomething(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 7 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__RBX;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 /*iReg - no +8*/);
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2 /*iReg*/);
+ break;
+ case 1:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 2 /*iReg*/, 1 /*cbMisalign*/ );
+ if (!Bs3Cg1XcptTypeIsUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX__R__;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2+8 /*iReg*/);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_FIXED(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ pThis->cbCurInstr = off;
+ break;
+ default:
+ return 0;
+ }
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_FIXED_AL_Ib(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ pThis->aOperands[1].off = (uint8_t)off;
+ pThis->abCurInstr[off++] = 0xff;
+ pThis->cbCurInstr = off;
+ break;
+ default:
+ return 0;
+ }
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_FIXED_rAX_Iz(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ unsigned cbOp;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 0));
+ pThis->aOperands[1].off = (uint8_t)off;
+ cbOp = pThis->cbOpDefault;
+ if (cbOp == 2)
+ *(uint16_t *)&pThis->abCurInstr[off] = UINT16_MAX;
+ else
+ *(uint32_t *)&pThis->abCurInstr[off] = UINT32_MAX;
+ off += cbOp;
+ pThis->aOperands[0].cbOp = cbOp;
+ pThis->aOperands[1].cbOp = cbOp;
+ pThis->cbOperand = cbOp;
+ break;
+ case 1:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80386)
+ return 0;
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertOpcodes(pThis, Bs3Cg1InsertReqPrefix(pThis, 1));
+ pThis->aOperands[1].off = (uint8_t)off;
+ cbOp = pThis->cbOpOvrd66;
+ if (cbOp == 2)
+ *(uint16_t *)&pThis->abCurInstr[off] = UINT16_MAX;
+ else
+ *(uint32_t *)&pThis->abCurInstr[off] = UINT32_MAX;
+ off += cbOp;
+ pThis->aOperands[0].cbOp = cbOp;
+ pThis->aOperands[1].cbOp = cbOp;
+ pThis->cbOperand = cbOp;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ pThis->abCurInstr[off++] = REX_W___;
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->aOperands[1].off = (uint8_t)off;
+ *(uint32_t *)&pThis->abCurInstr[off] = UINT32_MAX;
+ off += 4;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->cbOperand = 8;
+ break;
+ default:
+ return 0;
+
+ /* IMAGE PADDING - workaround for "rd err" - remove later! */
+ case 4:
+ ASMHalt();
+ ASMHalt();
+ ASMHalt();
+ return 0;
+
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_MOD_EQ_3(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ if (iEncoding < 8)
+ {
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, iEncoding, 1);
+ }
+ else if (iEncoding < 16)
+ {
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, 0, iEncoding & 7);
+ }
+ else
+ return 0;
+ pThis->cbCurInstr = off;
+
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_MODRM_MOD_NE_3(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ if (iEncoding < 3)
+ {
+ off = Bs3Cg1InsertReqPrefix(pThis, 0);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(iEncoding, 0, 1);
+ if (iEncoding >= 1)
+ pThis->abCurInstr[off++] = 0x7f;
+ if (iEncoding == 2)
+ {
+ pThis->abCurInstr[off++] = 0x5f;
+ if (!BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ {
+ pThis->abCurInstr[off++] = 0x3f;
+ pThis->abCurInstr[off++] = 0x1f;
+ }
+ }
+ }
+ else
+ return 0;
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/*
+ *
+ * VEX
+ * VEX
+ * VEX
+ *
+ */
+#ifdef BS3CG1_WITH_VEX
+
+/**
+ * Inserts a 3-byte VEX prefix.
+ *
+ * @returns New offDst value.
+ * @param pThis The state.
+ * @param offDst The current instruction offset.
+ * @param uVexL The VEX.L value.
+ * @param uVexV The VEX.V value (caller inverted it already).
+ * @param uVexR The VEX.R value (caller inverted it already).
+ * @param uVexX The VEX.X value (caller inverted it already).
+ * @param uVexB The VEX.B value (caller inverted it already).
+ * @param uVexW The VEX.W value (straight).
+ */
+DECLINLINE(unsigned) BS3_NEAR_CODE Bs3Cg1InsertVex3bPrefix(PBS3CG1STATE pThis, unsigned offDst, uint8_t uVexV, uint8_t uVexL,
+ uint8_t uVexR, uint8_t uVexX, uint8_t uVexB, uint8_t uVexW)
+{
+ uint8_t b1;
+ uint8_t b2;
+ b1 = uVexR << 7;
+ b1 |= uVexX << 6;
+ b1 |= uVexB << 5;
+ b1 |= pThis->uOpcodeMap;
+ b2 = uVexV << 3;
+ b2 |= uVexW << 7;
+ b2 |= uVexL << 2;
+ switch (pThis->enmPrefixKind)
+ {
+ case BS3CG1PFXKIND_NO_F2_F3_66: b2 |= 0; break;
+ case BS3CG1PFXKIND_REQ_66: b2 |= 1; break;
+ case BS3CG1PFXKIND_REQ_F3: b2 |= 2; break;
+ case BS3CG1PFXKIND_REQ_F2: b2 |= 3; break;
+ default:
+ Bs3TestFailedF("enmPrefixKind=%d not supported for VEX!\n", pThis->enmPrefixKind);
+ break;
+ }
+
+ pThis->abCurInstr[offDst] = 0xc4; /* vex3 */
+ pThis->abCurInstr[offDst + 1] = b1;
+ pThis->abCurInstr[offDst + 2] = b2;
+ pThis->uVexL = uVexL;
+ return offDst + 3;
+}
+
+
+/**
+ * Inserts a 2-byte VEX prefix.
+ *
+ * @note Will switch to 3-byte VEX prefix if uOpcodeMap isn't one.
+ *
+ * @returns New offDst value.
+ * @param pThis The state.
+ * @param offDst The current instruction offset.
+ * @param uVexL The VEX.L value.
+ * @param uVexV The VEX.V value (caller inverted it already).
+ * @param uVexR The VEX.R value (caller inverted it already).
+ */
+DECLINLINE(unsigned) BS3_NEAR_CODE Bs3Cg1InsertVex2bPrefix(PBS3CG1STATE pThis, unsigned offDst,
+ uint8_t uVexV, uint8_t uVexL, uint8_t uVexR)
+{
+ if (pThis->uOpcodeMap == 1)
+ {
+ uint8_t b = uVexR << 7;
+ b |= uVexV << 3;
+ b |= uVexL << 2;
+ switch (pThis->enmPrefixKind)
+ {
+ case BS3CG1PFXKIND_NO_F2_F3_66: b |= 0; break;
+ case BS3CG1PFXKIND_REQ_66: b |= 1; break;
+ case BS3CG1PFXKIND_REQ_F3: b |= 2; break;
+ case BS3CG1PFXKIND_REQ_F2: b |= 3; break;
+ default:
+ Bs3TestFailedF("enmPrefixKind=%d not supported for VEX!\n");
+ break;
+ }
+
+ pThis->abCurInstr[offDst] = 0xc5; /* vex2 */
+ pThis->abCurInstr[offDst + 1] = b;
+ pThis->uVexL = uVexL;
+ return offDst + 2;
+ }
+ return Bs3Cg1InsertVex3bPrefix(pThis, offDst, uVexV, uVexL, uVexR, 1 /*uVexX*/, 1 /*uVexB*/, 0/*uVexW*/);
+}
+
+
+/**
+ * Inserts a ModR/M byte with mod=3 and set the two idxFields members.
+ *
+ * @returns off + 1.
+ * @param pThis The state.
+ * @param off Current instruction offset.
+ * @param uReg Register index for ModR/M.reg.
+ * @param uRegMem Register index for ModR/M.rm.
+ * @param uVexVvvv The VEX.vvvv register.
+ */
+static unsigned Bs3Cg1InsertModRmWithRegFieldsAndVvvv(PBS3CG1STATE pThis, unsigned off,
+ uint8_t uReg, uint8_t uRegMem, uint8_t uVexVvvv)
+{
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, uReg & 7, uRegMem & 7);
+ pThis->aOperands[pThis->iRegOp].idxField = pThis->aOperands[pThis->iRegOp].idxFieldBase + uReg;
+ pThis->aOperands[1 ].idxField = pThis->aOperands[1 ].idxFieldBase + uVexVvvv;
+ pThis->aOperands[pThis->iRmOp ].idxField = pThis->aOperands[pThis->iRmOp ].idxFieldBase + uRegMem;
+ return off;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM_Vd_WO_Ed_WZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ break;
+ case 2:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L-invalid*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xe /*~V-invalid*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ pThis->fInvalidEncoding = true;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 0 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6+8, 2+8);
+ break;
+#endif
+ case 5:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 4 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 2 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 8:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4+8 /*iReg*/);
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8 /*iReg*/);
+ iEncoding += 2;
+ break;
+#endif
+ case 10: /* VEX.W is ignored in 32-bit mode. flag? */
+ BS3_ASSERT(!BS3CG1_IS_64BIT_TARGET(pThis));
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/* Differs from Bs3Cg1EncodeNext_MODRM_Pq_WO_Eq_WNZ in that REX.R isn't ignored. */
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM_Vq_WO_Eq_WNZ(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+#if ARCH_BITS == 64
+ if (BS3CG1_IS_64BIT_TARGET(pThis))
+ {
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L-invalid*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 2:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xe /*~V-invalid*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 0 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 6+8, 2+8);
+ break;
+ case 4:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4 /*iReg*/);
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 4 /*iReg*/, 1 /*cbMisalign*/);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 2 : 0;
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 4+8 /*iReg*/);
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+ }
+#endif
+ return 0;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ * Lig - VEX.L ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Usomething_Lip_Wip_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0x8 /*~V*/, 1 /*L-ignored*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 3, 1, 7);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+#if ARCH_BITS == 64
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 3+8, 2, 15);
+ break;
+#endif
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - ignored*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xc /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 3);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, BS3CG1_IS_64BIT_TARGET(pThis) ? 15 : 7);
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, BS3CG1_IS_64BIT_TARGET(pThis) ? 15 : 7);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_HdqCsomething_Usomething_Wip_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0x8 /*~V*/, 1 /*L-ignored*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 3, 1, 7);
+ pThis->fInvalidEncoding = true;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+ case 2:
+#if ARCH_BITS == 64
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 3+8, 2, 15);
+ break;
+#endif
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - ignored*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xc /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, 3);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, BS3CG1_IS_64BIT_TARGET(pThis) ? 15 : 7);
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFieldsAndVvvv(pThis, off, 2, 1, BS3CG1_IS_64BIT_TARGET(pThis) ? 15 : 7);
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 20: /* Switch to 256-bit operands. */
+ pThis->aOperands[pThis->iRegOp].idxFieldBase = BS3CG1DST_YMM0;
+ pThis->aOperands[pThis->iRegOp].cbOp = 32;
+ pThis->aOperands[pThis->iRmOp ].cbOp = 32;
+ RT_FALL_THRU();
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 1:
+ case 21:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 + 8);
+ break;
+#endif
+ case 2:
+ case 22:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 3:
+ case 23:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 4:
+ case 24:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 3 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 5:
+ case 25:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8);
+ break;
+ case 6:
+ case 26:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 0 /*~B-ignored*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 7:
+ case 27:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 0 /*~X-ignored*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ break;
+#endif
+ case 8:
+ case 28:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 9:
+ case 29:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 7 /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ pThis->fInvalidEncoding = true;
+ iEncoding += 10;
+ break;
+
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+
+/**
+ * Wip - VEX.W ignored.
+ * Lig - VEX.L ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - ignored*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - ignored*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 + 8);
+ break;
+#endif
+ case 3:
+ iEncoding = 3;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L-ignored*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 3 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8);
+ break;
+ case 8:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 0 /*~B-ignored*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 0 /*~X-ignored*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ break;
+#endif
+ case 10:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 11:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 7 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ * L0 - VEX.L must be zero.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lmbz_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - invalid*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7);
+ pThis->fInvalidEncoding = true;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 2 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 + 8);
+ break;
+ case 3:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - invalid*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5 + 8);
+ pThis->fInvalidEncoding = true;
+ break;
+#endif
+ case 4:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L - invalid*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 3 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 8:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8);
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 0 /*~B-ignored*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 10:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 0 /*~X-ignored*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ break;
+#endif
+ case 11:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 12:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 7 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lxx_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding, uint8_t uVexL)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 + 8);
+ break;
+#endif
+ case 2:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, uVexL, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 3 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 1 /*~R*/, 1 /*~X*/, 0 /*~B-ignored*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, uVexL, 1 /*~R*/, 0 /*~X-ignored*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ break;
+#endif
+ case 8:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, uVexL, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 7 /*~V*/, uVexL, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ pThis->fInvalidEncoding = true;
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ * L0 - VEX.L is zero (encoding may exist where it isn't).
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_L0_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ return Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lxx_OR_ViceVersa(pThis, iEncoding, 0 /*uVexL*/);
+}
+
+
+/**
+ * Wip - VEX.W ignored.
+ * L1 - VEX.L is one (encoding may exist where it isn't).
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_L1_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ return Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lxx_OR_ViceVersa(pThis, iEncoding, 1 /*uVexL*/);
+}
+
+
+
+/**
+ * Wip - VEX.W ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Msomething_Wip_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xc /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 3;
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ pThis->fInvalidEncoding = true;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 2:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0x1 /*~V*/, 0 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 7 + 8);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 14;
+ break;
+#endif
+ case 3:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 0);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 1;
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L-ignored*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ pThis->fInvalidEncoding = true;
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W-ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 3 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 0 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5+8);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ break;
+ case 8:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 0 /*~B-ignored*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 1);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 0 /*~X-ignored*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + 0;
+ break;
+#endif
+ case 10:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 1 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 5);
+ pThis->aOperands[1].idxField = pThis->aOperands[1].idxFieldBase + (BS3CG1_IS_64BIT_TARGET(pThis) ? 15 : 7);
+ pThis->fInvalidEncoding = true;
+ break;
+ default:
+ return 0;
+ }
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM_Md_WO(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ case 0:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ break;
+ case 1:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ break;
+ case 2:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0x7 /*~V-invalid*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 3:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 4:
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 5:
+ pThis->abCurInstr[0] = P_RZ;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 6:
+ pThis->abCurInstr[0] = P_RN;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 1 : 0;
+ break;
+#if ARCH_BITS == 64
+ case 8:
+ pThis->abCurInstr[0] = REX_____;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off) - 1;
+ off = Bs3Cfg1EncodeMemMod0DispWithDefaultsAndNoReg(pThis, off);
+ pThis->fInvalidEncoding = true;
+ break;
+#endif
+ default:
+ return 0;
+ }
+
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip = VEX.W ignored.
+ * Lmbz = VEX.L must be zero.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_Lmbz_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ switch (iEncoding)
+ {
+ /* 128-bit wide stuff goes first, then we'll update the operand widths afterwards. */
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+
+ case 1:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ break;
+ case 2:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W - ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 5, 4);
+ break;
+ case 3:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2 /*iReg*/);
+ break;
+ case 4:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 3 /*iReg*/);
+ break;
+ case 5:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W - ignored */);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 3 /*iReg*/);
+ break;
+ case 6:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 3 /*iReg*/, 1 /*cbMisalign*/);
+ if (!Bs3Cg1XcptTypeIsVexUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ break;
+ case 7:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 3 /*iReg*/, 1 /*cbMisalign*/);
+ if (!Bs3Cg1XcptTypeIsVexUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ break;
+ /* 128-bit invalid encodings: */
+ case 8:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, 0 /*L*/, 1 /*~R*/); /* Bad V value */
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 9:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ iEncoding = 20-1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+/**
+ * Wip = VEX.W ignored.
+ */
+static unsigned BS3_NEAR_CODE
+Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_OR_ViceVersa(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+
+ switch (iEncoding)
+ {
+ case 20: /* switch to 256-bit */
+ pThis->aOperands[pThis->iRmOp ].cbOp = 32;
+ pThis->aOperands[pThis->iRmOp ].idxFieldBase = BS3CG1DST_YMM0;
+ pThis->aOperands[pThis->iRegOp].cbOp = 32;
+ pThis->aOperands[pThis->iRegOp].idxFieldBase = BS3CG1DST_YMM0;
+ RT_FALL_THRU();
+ case 0:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ break;
+
+ case 1:
+ case 21:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ break;
+ case 2:
+ case 22:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W - ignored*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 5, 4);
+ break;
+ case 3:
+ case 23:
+ pThis->aOperands[pThis->iRmOp].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationMem;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 2 /*iReg*/);
+ break;
+ case 4:
+ case 24:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 3 /*iReg*/);
+ break;
+ case 5:
+ case 25:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 1 /*W - ignored */);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaults(pThis, off, 3 /*iReg*/);
+ break;
+ case 6:
+ case 26:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 3 /*iReg*/, 1 /*cbMisalign*/);
+ if (!Bs3Cg1XcptTypeIsVexUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ break;
+ case 7:
+ case 27:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cfg1EncodeMemMod0DispWithRegFieldAndDefaultsMisaligned(pThis, off, 3 /*iReg*/, 1 /*cbMisalign*/);
+ if (!Bs3Cg1XcptTypeIsVexUnaligned(pThis->enmXcptType))
+ pThis->bAlignmentXcpt = X86_XCPT_GP;
+ break;
+ /* invalid encodings: */
+ case 8:
+ case 28:
+ pThis->aOperands[pThis->iRmOp ].enmLocation = pThis->aOperands[pThis->iRmOp].enmLocationReg;
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xe /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/); /* Bad V value */
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1, 0);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 9:
+ case 29:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0 /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+
+ case 10:
+ case 30:
+ pThis->abCurInstr[0] = P_RN;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 11:
+ case 31:
+ pThis->abCurInstr[0] = P_RZ;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 12:
+ case 32:
+ pThis->abCurInstr[0] = P_OZ;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ break;
+ case 13:
+ case 33:
+ pThis->abCurInstr[0] = P_LK;
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 1 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 4, 5);
+ pThis->fInvalidEncoding = true;
+ iEncoding += !BS3CG1_IS_64BIT_TARGET(pThis) ? 2 + 4 : 0;
+ break;
+
+#if ARCH_BITS == 64
+ /* 64-bit mode registers */
+ case 14:
+ case 34:
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 0 /*~R*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 3+8, 4);
+ break;
+ case 15:
+ case 35:
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, iEncoding >= 20 /*L*/, 0 /*~R*/, 1 /*~X*/, 0 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ off = Bs3Cg1InsertModRmWithRegFields(pThis, off, 1+8, 4+8);
+ iEncoding += 4;
+ break;
+#endif
+ default:
+ return 0;
+ }
+
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+//static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_FIXED(PBS3CG1STATE pThis, unsigned iEncoding)
+//{
+// unsigned off;
+// if (iEncoding == 0)
+// off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+// else if (iEncoding == 0)
+// off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+// else
+// return 0;
+// pThis->cbCurInstr = off;
+// return iEncoding + 1;
+//}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM_MOD_EQ_3(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ if (iEncoding < 8)
+ {
+ if (iEncoding & 1)
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ else
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, iEncoding, 1);
+ }
+ else if (iEncoding < 16)
+ {
+ if (iEncoding & 1)
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L*/, 1 /*~R*/);
+ else
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 1 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, iEncoding & 7, 1);
+ }
+ else if (iEncoding < 24)
+ {
+ if (iEncoding & 1)
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/);
+ else
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, 0 /*L*/, 1 /*~R*/, 1 /*~X*/, 1 /*~B*/, 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, 0, iEncoding & 7);
+ }
+ else if (iEncoding < 32)
+ {
+ if (iEncoding & 1)
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, (iEncoding & 3) != 0 /*L*/, 1 /*~R*/);
+ else
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, (iEncoding & 2) != 0 /*L*/, 1 /*~R*/, 1 /*~X*/,
+ 1 /*~B*/, (iEncoding & 4) != 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(3, 0, iEncoding & 7);
+ }
+ else
+ return 0;
+ pThis->cbCurInstr = off;
+
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM_MOD_NE_3(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ unsigned off;
+ if (iEncoding < 8)
+ {
+ unsigned iMod = iEncoding % 3;
+ if (iEncoding & 1)
+ off = Bs3Cg1InsertVex2bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, (iEncoding & 2) != 0 /*L*/, 1 /*~R*/);
+ else
+ off = Bs3Cg1InsertVex3bPrefix(pThis, 0 /*offDst*/, 0xf /*~V*/, (iEncoding & 2) != 0 /*L*/, 1 /*~R*/,
+ 1 /*~X*/, 1 /*~B*/, (iEncoding & 4) != 0 /*W*/);
+ off = Bs3Cg1InsertOpcodes(pThis, off);
+ pThis->abCurInstr[off++] = X86_MODRM_MAKE(iMod, 0, 1);
+ if (iMod >= 1)
+ pThis->abCurInstr[off++] = 0x7f;
+ if (iMod == 2)
+ {
+ pThis->abCurInstr[off++] = 0x5f;
+ if (!BS3_MODE_IS_16BIT_CODE(pThis->bMode))
+ {
+ pThis->abCurInstr[off++] = 0x3f;
+ pThis->abCurInstr[off++] = 0x1f;
+ }
+ }
+ }
+ else
+ return 0;
+ pThis->cbCurInstr = off;
+ return iEncoding + 1;
+}
+
+
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext_VEX_MODRM(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ const unsigned cFirstEncodings = 32;
+ if (iEncoding < cFirstEncodings)
+ {
+ unsigned iRet = Bs3Cg1EncodeNext_VEX_MODRM_MOD_EQ_3(pThis, iEncoding);
+ BS3_ASSERT(iRet > iEncoding);
+ return iRet;
+ }
+ return Bs3Cg1EncodeNext_VEX_MODRM_MOD_NE_3(pThis, iEncoding - cFirstEncodings) + cFirstEncodings;
+}
+
+#endif /* BS3CG1_WITH_VEX */
+
+
+/**
+ * Encodes the next instruction.
+ *
+ * @returns Next iEncoding value. Returns @a iEncoding unchanged to indicate
+ * that there are no more encodings to test.
+ * @param pThis The state.
+ * @param iEncoding The encoding to produce. Meaning is specific to
+ * each BS3CG1ENC_XXX value and should be considered
+ * internal.
+ */
+static unsigned BS3_NEAR_CODE Bs3Cg1EncodeNext(PBS3CG1STATE pThis, unsigned iEncoding)
+{
+ pThis->bAlignmentXcpt = UINT8_MAX;
+ pThis->uVexL = UINT8_MAX;
+ if (pThis->pfnEncoder)
+ return pThis->pfnEncoder(pThis, iEncoding);
+
+ Bs3TestFailedF("Internal error! BS3CG1ENC_XXX = %u not implemented", pThis->enmEncoding);
+ return iEncoding;
+}
+
+
+/**
+ * Prepares doing instruction encodings.
+ *
+ * This is in part specific to how the instruction is encoded, but generally it
+ * sets up basic operand values that doesn't change (much) when Bs3Cg1EncodeNext
+ * is called from within the loop.
+ *
+ * @returns Success indicator (true/false).
+ * @param pThis The state.
+ */
+#define Bs3Cg1EncodePrep BS3_CMN_NM(Bs3Cg1EncodePrep)
+bool BS3_NEAR_CODE Bs3Cg1EncodePrep(PBS3CG1STATE pThis)
+{
+ unsigned i = 4;
+ while (i-- > 0)
+ pThis->aSavedSegRegs[i].ds = pThis->aInitialCtxs[i].ds;
+
+ i = RT_ELEMENTS(pThis->aOperands);
+ while (i-- > 0)
+ {
+ pThis->aOperands[i].enmLocationReg = BS3CG1OPLOC_INVALID;
+ pThis->aOperands[i].enmLocationMem = BS3CG1OPLOC_INVALID;
+ pThis->aOperands[i].idxFieldBase = BS3CG1DST_INVALID;
+ }
+
+ pThis->iRmOp = RT_ELEMENTS(pThis->aOperands) - 1;
+ pThis->iRegOp = RT_ELEMENTS(pThis->aOperands) - 1;
+ pThis->fSameRingNotOkay = false;
+ pThis->cbOperand = 0;
+ pThis->pfnEncoder = NULL;
+
+ switch (pThis->enmEncoding)
+ {
+ case BS3CG1ENC_MODRM_Eb_Gb:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Eb_Gb_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 1;
+ pThis->aOperands[1].cbOp = 1;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_AL;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_AL;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_RW;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Ev_Gv:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Gv_Ev__OR__MODRM_Ev_Gv;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->cbOperand = 2;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_OZ_RAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_OZ_RAX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_RW;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Ed_WO_Pd_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_PdZx_WO_Ed_WZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Eq_WO_Pq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Pq_WO_Eq_WNZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Ed_WO_Vd_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vd_WO_Ed_WZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Eq_WO_Vq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vq_WO_Eq_WNZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Gb_Eb:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Eb_Gb_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 1;
+ pThis->aOperands[1].cbOp = 1;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_AL;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_AL;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Gv_Ev:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Gv_Ev__OR__MODRM_Ev_Gv;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->cbOperand = 2;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_OZ_RAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_OZ_RAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Gv_RO_Ma: /* bound instr */
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Gv_RO_Ma;
+ pThis->iRmOp = 1;
+ pThis->iRegOp = 0;
+ pThis->cbOperand = 2;
+ pThis->aOperands[0].cbOp = 2;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_OZ_RAX;
+ break;
+
+ case BS3CG1ENC_MODRM_Wss_WO_Vss:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Wsd_WO_Vsd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_WqZxReg_WO_Vq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Wps_WO_Vps:
+ case BS3CG1ENC_MODRM_Wpd_WO_Vpd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Vdq_WO_Mdq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_MODRM_Vdq_WO_Wdq:
+ case BS3CG1ENC_MODRM_Vpd_WO_Wpd:
+ case BS3CG1ENC_MODRM_Vps_WO_Wps:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Pq_WO_Qq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Pq_WO_Qq;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Pq_WO_Uq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Pq_WO_Uq;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX; /* reg only */
+ break;
+
+ case BS3CG1ENC_MODRM_PdZx_WO_Ed_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_PdZx_WO_Ed_WZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_MM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Pq_WO_Eq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Pq_WO_Eq_WNZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_VdZx_WO_Ed_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vd_WO_Ed_WZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_DW0_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_VqZx_WO_Eq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vq_WO_Eq_WNZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Vq_WO_UqHi:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Usomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_VqHi_WO_Uq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Usomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_VqHi_WO_Mq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Vq_WO_Mq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_VssZx_WO_Wss:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_DW0_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_MODRM_VqZx_WO_Nq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Nsomething;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_MM0;
+ break;
+
+ case BS3CG1ENC_MODRM_VsdZx_WO_Wsd:
+ case BS3CG1ENC_MODRM_VqZx_WO_Wq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Vsomething_Wsomething_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_MODRM_Mb_RO:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 1;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Md_RO:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_MODRM_Md_WO:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ break;
+
+ case BS3CG1ENC_MODRM_Mdq_WO_Vdq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_MODRM_Mq_WO_Pq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Psomething;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_MM0;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_MODRM_Mq_WO_Vq:
+ case BS3CG1ENC_MODRM_Mq_WO_VqHi:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].idxFieldBase = pThis->enmEncoding == BS3CG1ENC_MODRM_Mq_WO_Vq
+ ? BS3CG1DST_XMM0_LO : BS3CG1DST_XMM0_HI;
+ break;
+
+ case BS3CG1ENC_MODRM_Mps_WO_Vps:
+ case BS3CG1ENC_MODRM_Mpd_WO_Vpd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_Msomething_Vsomething_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_FIXED:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_FIXED;
+ break;
+
+ case BS3CG1ENC_FIXED_AL_Ib:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_FIXED_AL_Ib;
+ pThis->aOperands[0].cbOp = 1;
+ pThis->aOperands[1].cbOp = 1;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_IMM;
+ pThis->aOperands[0].idxField = BS3CG1DST_AL;
+ pThis->aOperands[1].idxField = BS3CG1DST_INVALID;
+ break;
+
+ case BS3CG1ENC_FIXED_rAX_Iz:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_FIXED_rAX_Iz;
+ pThis->aOperands[0].cbOp = 2;
+ pThis->aOperands[1].cbOp = 2;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_IMM;
+ pThis->aOperands[0].idxField = BS3CG1DST_OZ_RAX;
+ pThis->aOperands[1].idxField = BS3CG1DST_INVALID;
+ break;
+
+ /* Unused or invalid instructions mostly. */
+ case BS3CG1ENC_MODRM_MOD_EQ_3:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_MOD_EQ_3;
+ break;
+ case BS3CG1ENC_MODRM_MOD_NE_3:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_MODRM_MOD_NE_3;
+ break;
+
+#ifdef BS3CG1_WITH_VEX
+
+ case BS3CG1ENC_VEX_MODRM_Vd_WO_Ed_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_Vd_WO_Ed_WZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_DW0_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vq_WO_Eq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_Vq_WO_Eq_WNZ;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vps_WO_Wps:
+ case BS3CG1ENC_VEX_MODRM_Vpd_WO_Wpd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_VssZx_WO_Md:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa;
+ pThis->iRmOp = 1;
+ pThis->iRegOp = 0;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_INVALID;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vss_WO_HssHi_Uss:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Usomething_Lip_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 2;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 12;
+ pThis->aOperands[2].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI96;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_VsdZx_WO_Mq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa;
+ pThis->iRmOp = 1;
+ pThis->iRegOp = 0;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_INVALID;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vx_WO_Mx_L0:
+ BS3_ASSERT(!(pThis->fFlags & BS3CG1INSTR_F_VEX_L_ZERO));
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_L0_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vx_WO_Mx_L1:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_L1_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 32;
+ pThis->aOperands[1].cbOp = 32;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_YMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vsd_WO_HsdHi_Usd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Usomething_Lip_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 2;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[2].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vq_WO_HqHi_UqHi:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_HdqCsomething_Usomething_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 2;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[2].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_XMM0_HI;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vq_WO_HqHi_Mq:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Msomething_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 2;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[2].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_INVALID;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vq_WO_Wq:
+ BS3_ASSERT(pThis->fFlags & BS3CG1INSTR_F_VEX_L_ZERO);
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_Lmbz_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Vx_WO_Wx:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_OR_ViceVersa;
+ pThis->iRegOp = 0;
+ pThis->iRmOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].enmLocationMem = BS3CG1OPLOC_MEM;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Ed_WO_Vd_WZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_Vd_WO_Ed_WZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_EAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_DW0_ZX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Eq_WO_Vq_WNZ:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_Vq_WO_Eq_WNZ;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_RAX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO_ZX;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Md_WO:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_Md_WO;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Md_WO_Vss:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 4;
+ pThis->aOperands[1].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_INVALID;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Mq_WO_Vq:
+ BS3_ASSERT(pThis->fFlags & (BS3CG1INSTR_F_VEX_L_ZERO | BS3CG1INSTR_F_VEX_L_IGNORED));
+ pThis->pfnEncoder = pThis->fFlags & BS3CG1INSTR_F_VEX_L_ZERO
+ ? Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lmbz_OR_ViceVersa
+ : Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Mq_WO_Vsd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_Lig_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_INVALID;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Mps_WO_Vps:
+ case BS3CG1ENC_VEX_MODRM_Mpd_WO_Vpd:
+ case BS3CG1ENC_VEX_MODRM_Mx_WO_Vx:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Msomething_Wip_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Uss_WO_HssHi_Vss:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Usomething_Lip_Wip_OR_ViceVersa;
+ pThis->iRegOp = 2;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 96;
+ pThis->aOperands[2].cbOp = 4;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI96;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_XMM0_DW0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Usd_WO_HsdHi_Vsd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_VsomethingWO_Hsomething_Usomething_Lip_Wip_OR_ViceVersa;
+ pThis->iRegOp = 2;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[2].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[2].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_HI;
+ pThis->aOperands[2].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Wps_WO_Vps:
+ case BS3CG1ENC_VEX_MODRM_Wpd_WO_Vpd:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Wq_WO_Vq:
+ BS3_ASSERT(pThis->fFlags & BS3CG1INSTR_F_VEX_L_ZERO);
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_Lmbz_OR_ViceVersa;
+ pThis->iRegOp = 1;
+ pThis->iRmOp = 0;
+ pThis->aOperands[0].cbOp = 8;
+ pThis->aOperands[1].cbOp = 8;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0_LO;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0_LO;
+ break;
+
+ case BS3CG1ENC_VEX_MODRM_Wx_WO_Vx:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_WsomethingWO_Vsomething_Wip_OR_ViceVersa;
+ pThis->iRmOp = 0;
+ pThis->iRegOp = 1;
+ pThis->aOperands[0].cbOp = 16;
+ pThis->aOperands[1].cbOp = 16;
+ pThis->aOperands[0].enmLocation = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationReg = BS3CG1OPLOC_CTX_ZX_VLMAX;
+ pThis->aOperands[0].enmLocationMem = BS3CG1OPLOC_MEM_WO;
+ pThis->aOperands[1].enmLocation = BS3CG1OPLOC_CTX;
+ pThis->aOperands[0].idxFieldBase = BS3CG1DST_XMM0;
+ pThis->aOperands[1].idxFieldBase = BS3CG1DST_XMM0;
+ break;
+
+
+ /* Unused or invalid instructions mostly. */
+ //case BS3CG1ENC_VEX_FIXED:
+ // pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_FIXED;
+ // break;
+ case BS3CG1ENC_VEX_MODRM_MOD_EQ_3:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_MOD_EQ_3;
+ break;
+ case BS3CG1ENC_VEX_MODRM_MOD_NE_3:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM_MOD_NE_3;
+ break;
+ case BS3CG1ENC_VEX_MODRM:
+ pThis->pfnEncoder = Bs3Cg1EncodeNext_VEX_MODRM;
+ break;
+
+#endif /* BS3CG1_WITH_VEX */
+
+ default:
+ Bs3TestFailedF("Invalid/unimplemented enmEncoding for instruction #%RU32 (%.*s): %d",
+ pThis->iInstr, pThis->cchMnemonic, pThis->pchMnemonic, pThis->enmEncoding);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Calculates the appropriate non-intel invalid instruction encoding.
+ *
+ * @returns the encoding to use instead.
+ * @param enmEncoding The intel invalid instruction encoding.
+ */
+static BS3CG1ENC Bs3Cg1CalcNoneIntelInvalidEncoding(BS3CG1ENC enmEncoding)
+{
+ switch (enmEncoding)
+ {
+ case BS3CG1ENC_MODRM_Gb_Eb:
+ case BS3CG1ENC_MODRM_Gv_RO_Ma:
+ case BS3CG1ENC_FIXED:
+ return BS3CG1ENC_FIXED;
+ default:
+ Bs3TestFailedF("Bs3Cg1CalcNoneIntelInvalidEncoding: Unsupported encoding: %d\n", enmEncoding);
+ return BS3CG1ENC_FIXED;
+ }
+}
+
+
+/**
+ * Sets cbOpDefault, cbOpOvrd66 and cbOpOvrdRexW.
+ *
+ * @param pThis The state.
+ * @param bMode The mode (only code part is used).
+ */
+static void Bs3Cg1SetOpSizes(PBS3CG1STATE pThis, uint8_t bMode)
+{
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ pThis->cbOpDefault = 2;
+ pThis->cbOpOvrd66 = 4;
+ pThis->cbOpOvrdRexW = 0;
+ }
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ pThis->cbOpDefault = 4;
+ pThis->cbOpOvrd66 = 2;
+ pThis->cbOpOvrdRexW = 0;
+ }
+ else
+ {
+ pThis->cbOpDefault = 4;
+ pThis->cbOpOvrd66 = 2;
+ pThis->cbOpOvrdRexW = 8;
+ }
+}
+
+
+/**
+ * Sets up SSE and maybe AVX.
+ *
+ * @returns true (if successful, false if not and the SSE instructions ends up
+ * being invalid).
+ * @param pThis The state.
+ */
+static bool BS3_NEAR_CODE Bs3Cg3SetupSseAndAvx(PBS3CG1STATE pThis)
+{
+ if (!pThis->fWorkExtCtx)
+ {
+ unsigned i;
+ uint32_t cr0 = ASMGetCR0();
+ uint32_t cr4 = ASMGetCR4();
+
+ cr0 &= ~(X86_CR0_TS | X86_CR0_MP | X86_CR0_EM);
+ cr0 |= X86_CR0_NE;
+ ASMSetCR0(cr0);
+ if (pThis->pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ {
+ cr4 |= X86_CR4_OSFXSR | X86_CR4_OSXMMEEXCPT | X86_CR4_OSXSAVE;
+ ASMSetCR4(cr4);
+ ASMSetXcr0(pThis->pExtCtx->fXcr0Nominal);
+ }
+ else
+ {
+ cr4 |= X86_CR4_OSFXSR | X86_CR4_OSXMMEEXCPT;
+ ASMSetCR4(cr4);
+ }
+
+ for (i = 0; i < RT_ELEMENTS(pThis->aInitialCtxs); i++)
+ {
+ pThis->aInitialCtxs[i].cr0.u32 = cr0;
+ pThis->aInitialCtxs[i].cr4.u32 = cr4;
+ }
+ pThis->fWorkExtCtx = true;
+ }
+
+ return true;
+}
+
+
+/**
+ * Next CPU configuration to test the current instruction in.
+ *
+ * This is for testing FPU, SSE and AVX instructions with the various lazy state
+ * load and enable bits in different configurations to ensure we're getting the
+ * right response.
+ *
+ * This also cleans up the CPU and test driver state.
+ *
+ * @returns true if we're to do another round, false if we're done.
+ * @param pThis The state.
+ * @param iCpuSetup The current CPU setup number.
+ * @param pfInvalidInstr Where to indicate whether the setup causes an
+ * invalid instruction or not. This is also used as
+ * input to avoid unnecessary CPUID work.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1CpuSetupNext(PBS3CG1STATE pThis, unsigned iCpuSetup, bool BS3_FAR *pfInvalidInstr)
+{
+ if ( (pThis->fFlags & BS3CG1INSTR_F_INVALID_64BIT)
+ && BS3CG1_IS_64BIT_TARGET(pThis))
+ return false;
+
+ switch (pThis->enmCpuTest)
+ {
+ case BS3CG1CPU_ANY:
+ case BS3CG1CPU_GE_80186:
+ case BS3CG1CPU_GE_80286:
+ case BS3CG1CPU_GE_80386:
+ case BS3CG1CPU_GE_80486:
+ case BS3CG1CPU_GE_Pentium:
+ case BS3CG1CPU_CLFSH:
+ case BS3CG1CPU_CLFLUSHOPT:
+ return false;
+
+ case BS3CG1CPU_MMX:
+ return false;
+
+ case BS3CG1CPU_SSE:
+ case BS3CG1CPU_SSE2:
+ case BS3CG1CPU_SSE3:
+ case BS3CG1CPU_SSE4_1:
+ case BS3CG1CPU_AVX:
+ case BS3CG1CPU_AVX2:
+ if (iCpuSetup > 0 || *pfInvalidInstr)
+ {
+ /** @todo do more configs here. */
+ pThis->fWorkExtCtx = false;
+ ASMSetCR0(ASMGetCR0() | X86_CR0_EM | X86_CR0_MP);
+ ASMSetCR4(ASMGetCR4() & ~(X86_CR4_OSFXSR | X86_CR4_OSXMMEEXCPT | X86_CR4_OSXSAVE));
+ return false;
+ }
+ return false;
+
+ default:
+ Bs3TestFailedF("Invalid enmCpuTest value: %d", pThis->enmCpuTest);
+ return false;
+ }
+}
+
+
+/**
+ * Check if the instruction is supported by the CPU, possibly making state
+ * adjustments to enable support for it.
+ *
+ * @returns true if supported, false if not.
+ * @param pThis The state.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1CpuSetupFirst(PBS3CG1STATE pThis)
+{
+ uint32_t fEax;
+ uint32_t fEbx;
+ uint32_t fEcx;
+ uint32_t fEdx;
+
+ if ( (pThis->fFlags & BS3CG1INSTR_F_INVALID_64BIT)
+ && BS3CG1_IS_64BIT_TARGET(pThis))
+ return false;
+
+ switch (pThis->enmCpuTest)
+ {
+ case BS3CG1CPU_ANY:
+ return true;
+
+ case BS3CG1CPU_GE_80186:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80186)
+ return true;
+ return false;
+
+ case BS3CG1CPU_GE_80286:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80286)
+ return true;
+ return false;
+
+ case BS3CG1CPU_GE_80386:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ return true;
+ return false;
+
+ case BS3CG1CPU_GE_80486:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ return true;
+ return false;
+
+ case BS3CG1CPU_GE_Pentium:
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_Pentium)
+ return true;
+ return false;
+
+ case BS3CG1CPU_MMX:
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ ASMCpuIdExSlow(1, 0, 0, 0, NULL, NULL, NULL, &fEdx);
+ if (fEdx & X86_CPUID_FEATURE_EDX_MMX)
+ return Bs3Cg3SetupSseAndAvx(pThis); /** @todo only do FNSAVE/FXSAVE here? */
+ }
+ return false;
+
+ case BS3CG1CPU_SSE:
+ case BS3CG1CPU_SSE2:
+ case BS3CG1CPU_SSE3:
+ case BS3CG1CPU_SSE4_1:
+ case BS3CG1CPU_AVX:
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ ASMCpuIdExSlow(1, 0, 0, 0, NULL, NULL, &fEcx, &fEdx);
+ switch (pThis->enmCpuTest)
+ {
+ case BS3CG1CPU_SSE:
+ if (fEdx & X86_CPUID_FEATURE_EDX_SSE)
+ return Bs3Cg3SetupSseAndAvx(pThis);
+ return false;
+ case BS3CG1CPU_SSE2:
+ if (fEdx & X86_CPUID_FEATURE_EDX_SSE2)
+ return Bs3Cg3SetupSseAndAvx(pThis);
+ return false;
+ case BS3CG1CPU_SSE3:
+ if (fEcx & X86_CPUID_FEATURE_ECX_SSE3)
+ return Bs3Cg3SetupSseAndAvx(pThis);
+ return false;
+ case BS3CG1CPU_SSE4_1:
+ if (fEcx & X86_CPUID_FEATURE_ECX_SSE4_1)
+ return Bs3Cg3SetupSseAndAvx(pThis);
+ return false;
+ case BS3CG1CPU_AVX:
+ if (fEcx & X86_CPUID_FEATURE_ECX_AVX)
+ return Bs3Cg3SetupSseAndAvx(pThis) && !BS3_MODE_IS_RM_OR_V86(pThis->bMode);
+ return false;
+ default: BS3_ASSERT(0); /* impossible */
+ }
+ }
+ return false;
+
+ case BS3CG1CPU_AVX2:
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ ASMCpuIdExSlow(7, 0, 0/*leaf*/, 0, &fEax, &fEbx, &fEcx, &fEdx);
+ switch (pThis->enmCpuTest)
+ {
+ case BS3CG1CPU_AVX2:
+ if (fEbx & X86_CPUID_STEXT_FEATURE_EBX_AVX2)
+ return Bs3Cg3SetupSseAndAvx(pThis) && !BS3_MODE_IS_RM_OR_V86(pThis->bMode);
+ return false;
+ default: BS3_ASSERT(0); return false; /* impossible */
+ }
+ }
+ return false;
+
+ case BS3CG1CPU_CLFSH:
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ ASMCpuIdExSlow(1, 0, 0, 0, NULL, NULL, NULL, &fEdx);
+ if (fEdx & X86_CPUID_FEATURE_EDX_CLFSH)
+ return true;
+ }
+ return false;
+
+ case BS3CG1CPU_CLFLUSHOPT:
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ ASMCpuIdExSlow(7, 0, 0/*leaf*/, 0, NULL, &fEbx, NULL, NULL);
+ if (fEbx & X86_CPUID_STEXT_FEATURE_EBX_CLFLUSHOPT)
+ return true;
+ }
+ return false;
+
+ default:
+ Bs3TestFailedF("Invalid enmCpuTest value: %d", pThis->enmCpuTest);
+ return false;
+ }
+}
+
+
+
+/**
+ * Checks the preconditions for a test.
+ *
+ * @returns true if the test be executed, false if not.
+ * @param pThis The state.
+ * @param pHdr The test header.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1RunSelector(PBS3CG1STATE pThis, PCBS3CG1TESTHDR pHdr)
+{
+
+ uint8_t const BS3_FAR *pbCode = (uint8_t const BS3_FAR *)(pHdr + 1);
+ unsigned cbLeft = pHdr->cbSelector;
+ while (cbLeft-- > 0)
+ {
+ switch (*pbCode++)
+ {
+#define CASE_PRED(a_Pred, a_Expr) \
+ case ((a_Pred) << BS3CG1SEL_OP_KIND_MASK) | BS3CG1SEL_OP_IS_TRUE: \
+ if (!(a_Expr)) return false; \
+ break; \
+ case ((a_Pred) << BS3CG1SEL_OP_KIND_MASK) | BS3CG1SEL_OP_IS_FALSE: \
+ if (a_Expr) return false; \
+ break
+ CASE_PRED(BS3CG1PRED_SIZE_O16, pThis->cbOperand == 2);
+ CASE_PRED(BS3CG1PRED_SIZE_O32, pThis->cbOperand == 4);
+ CASE_PRED(BS3CG1PRED_SIZE_O64, pThis->cbOperand == 8);
+ CASE_PRED(BS3CG1PRED_VEXL_0, pThis->uVexL == 0);
+ CASE_PRED(BS3CG1PRED_VEXL_1, pThis->uVexL == 1);
+ CASE_PRED(BS3CG1PRED_RING_0, pThis->uCpl == 0);
+ CASE_PRED(BS3CG1PRED_RING_1, pThis->uCpl == 1);
+ CASE_PRED(BS3CG1PRED_RING_2, pThis->uCpl == 2);
+ CASE_PRED(BS3CG1PRED_RING_3, pThis->uCpl == 3);
+ CASE_PRED(BS3CG1PRED_RING_0_THRU_2, pThis->uCpl <= 2);
+ CASE_PRED(BS3CG1PRED_RING_1_THRU_3, pThis->uCpl >= 1);
+ CASE_PRED(BS3CG1PRED_CODE_64BIT, BS3CG1_IS_64BIT_TARGET(pThis));
+ CASE_PRED(BS3CG1PRED_CODE_32BIT, BS3_MODE_IS_32BIT_CODE(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_CODE_16BIT, BS3_MODE_IS_16BIT_CODE(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_MODE_REAL, BS3_MODE_IS_RM_SYS(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_MODE_PROT, BS3_MODE_IS_PM_SYS(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_MODE_LONG, BS3_MODE_IS_64BIT_SYS(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_MODE_SMM, false);
+ CASE_PRED(BS3CG1PRED_MODE_VMX, false);
+ CASE_PRED(BS3CG1PRED_MODE_SVM, false);
+ CASE_PRED(BS3CG1PRED_PAGING_ON, BS3_MODE_IS_PAGED(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_PAGING_OFF, !BS3_MODE_IS_PAGED(pThis->bMode));
+ CASE_PRED(BS3CG1PRED_VENDOR_AMD, pThis->bCpuVendor == BS3CPUVENDOR_AMD);
+ CASE_PRED(BS3CG1PRED_VENDOR_INTEL, pThis->bCpuVendor == BS3CPUVENDOR_INTEL);
+ CASE_PRED(BS3CG1PRED_VENDOR_VIA, pThis->bCpuVendor == BS3CPUVENDOR_VIA);
+ CASE_PRED(BS3CG1PRED_VENDOR_SHANGHAI, pThis->bCpuVendor == BS3CPUVENDOR_SHANGHAI);
+ CASE_PRED(BS3CG1PRED_VENDOR_HYGON, pThis->bCpuVendor == BS3CPUVENDOR_HYGON);
+
+#undef CASE_PRED
+ default:
+ return Bs3TestFailedF("Invalid selector opcode %#x!", pbCode[-1]);
+ }
+ }
+
+ return true;
+}
+
+
+#ifdef BS3CG1_DEBUG_CTX_MOD
+/**
+ * Translates the operator into a string.
+ *
+ * @returns Read-only string pointer.
+ * @param bOpcode The context modifier program opcode.
+ */
+static const char BS3_FAR * BS3_NEAR_CODE Bs3Cg1CtxOpToString(uint8_t bOpcode)
+{
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: return "=";
+ case BS3CG1_CTXOP_OR: return "|=";
+ case BS3CG1_CTXOP_AND: return "&=";
+ case BS3CG1_CTXOP_AND_INV: return "&~=";
+ default: return "?WTF?";
+ }
+}
+#endif
+
+
+/**
+ * Runs a context modifier program.
+ *
+ * @returns Success indicator (true/false).
+ * @param pThis The state.
+ * @param pCtx The context.
+ * @param pHdr The program header.
+ * @param off The program offset relative to the end of the header.
+ * @param cb The program size.
+ * @param pEflCtx The context to take undefined EFLAGS from. (This is NULL
+ * if we're processing a input context modifier program.)
+ * @param pbInstr Points to the first instruction byte. For storing
+ * immediate operands during input context modification.
+ * NULL for output contexts.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1RunContextModifier(PBS3CG1STATE pThis, PBS3REGCTX pCtx, PCBS3CG1TESTHDR pHdr,
+ unsigned off, unsigned cb,
+ PCBS3REGCTX pEflCtx, uint8_t BS3_FAR *pbInstr)
+{
+ uint8_t const BS3_FAR *pbCode = (uint8_t const BS3_FAR *)(pHdr + 1) + off;
+ int cbLeft = cb;
+ while (cbLeft-- > 0)
+ {
+ /*
+ * Decode the instruction.
+ */
+ uint8_t const bOpcode = *pbCode++;
+ unsigned cbValue;
+ unsigned cbDst;
+ BS3CG1DST idxField;
+ BS3PTRUNION PtrField;
+ uint8_t BS3_FAR *pbMemCopy = NULL;
+ bool fZxVlMax;
+
+ /* Expand the destiation field (can be escaped). Set fZxVlMax. */
+ switch (bOpcode & BS3CG1_CTXOP_DST_MASK)
+ {
+ case BS3CG1_CTXOP_OP1:
+ idxField = pThis->aOperands[0].idxField;
+ if (idxField == BS3CG1DST_INVALID)
+ idxField = BS3CG1DST_OP1;
+ fZxVlMax = pEflCtx != NULL && pThis->aOperands[0].enmLocation == BS3CG1OPLOC_CTX_ZX_VLMAX;
+ break;
+
+ case BS3CG1_CTXOP_OP2:
+ idxField = pThis->aOperands[1].idxField;
+ if (idxField == BS3CG1DST_INVALID)
+ idxField = BS3CG1DST_OP2;
+ fZxVlMax = pEflCtx != NULL && pThis->aOperands[1].enmLocation == BS3CG1OPLOC_CTX_ZX_VLMAX;
+ break;
+
+ case BS3CG1_CTXOP_EFL:
+ idxField = BS3CG1DST_EFL;
+ fZxVlMax = false;
+ break;
+
+ case BS3CG1_CTXOP_DST_ESC:
+ if (cbLeft-- > 0)
+ {
+ idxField = (BS3CG1DST)*pbCode++;
+ if (idxField <= BS3CG1DST_OP4)
+ {
+ if (idxField > BS3CG1DST_INVALID)
+ {
+ unsigned idxOp = idxField - BS3CG1DST_OP1;
+ uint8_t idxField2 = pThis->aOperands[idxOp].idxField;
+ if (idxField2 != BS3CG1DST_INVALID)
+ idxField = idxField2;
+ fZxVlMax = pEflCtx != NULL && pThis->aOperands[idxOp].enmLocation == BS3CG1OPLOC_CTX_ZX_VLMAX;
+ break;
+ }
+ }
+ else if (idxField < BS3CG1DST_END)
+ {
+ fZxVlMax = false;
+ break;
+ }
+ return Bs3TestFailedF("Malformed context instruction: idxField=%d", idxField);
+ }
+ RT_FALL_THRU();
+ default:
+ return Bs3TestFailed("Malformed context instruction: Destination");
+ }
+
+ /* Expand value size (can be escaped). */
+ switch (bOpcode & BS3CG1_CTXOP_SIZE_MASK)
+ {
+ case BS3CG1_CTXOP_1_BYTE: cbValue = 1; break;
+ case BS3CG1_CTXOP_2_BYTES: cbValue = 2; break;
+ case BS3CG1_CTXOP_4_BYTES: cbValue = 4; break;
+ case BS3CG1_CTXOP_8_BYTES: cbValue = 8; break;
+ case BS3CG1_CTXOP_16_BYTES: cbValue = 16; break;
+ case BS3CG1_CTXOP_32_BYTES: cbValue = 32; break;
+ case BS3CG1_CTXOP_12_BYTES: cbValue = 12; break;
+ case BS3CG1_CTXOP_SIZE_ESC:
+ if (cbLeft-- > 0)
+ {
+ cbValue = *pbCode++;
+ if (cbValue)
+ break;
+ }
+ RT_FALL_THRU();
+ default:
+ return Bs3TestFailed("Malformed context instruction: size");
+ }
+
+ /* Make sure there is enough instruction bytes for the value. */
+ if (cbValue <= cbLeft)
+ { /* likely */ }
+ else
+ return Bs3TestFailedF("Malformed context instruction: %u bytes value, %u bytes left", cbValue, cbLeft);
+
+ /*
+ * Do value processing specific to the target field size.
+ */
+ cbDst = g_acbBs3Cg1DstFields[idxField];
+ if (cbDst == BS3CG1DSTSIZE_OPERAND)
+ cbDst = pThis->aOperands[idxField - BS3CG1DST_OP1].cbOp;
+ else if (cbDst == BS3CG1DSTSIZE_OPERAND_SIZE_GRP)
+ cbDst = pThis->cbOperand;
+ if (cbDst <= 8)
+ {
+ unsigned const offField = g_aoffBs3Cg1DstFields[idxField];
+
+ /*
+ * Deal with fields up to 8-byte wide.
+ */
+
+ /* Get the value. */
+ uint64_t uValue;
+ if ((bOpcode & BS3CG1_CTXOP_SIGN_EXT))
+ switch (cbValue)
+ {
+ case 1: uValue = *(int8_t const BS3_FAR *)pbCode; break;
+ case 2: uValue = *(int16_t const BS3_FAR *)pbCode; break;
+ case 4: uValue = *(int32_t const BS3_FAR *)pbCode; break;
+ default:
+ if (cbValue >= 8)
+ {
+ uValue = *(uint64_t const BS3_FAR *)pbCode;
+ break;
+ }
+ return Bs3TestFailedF("Malformed context instruction: %u bytes value (%u dst)", cbValue, cbDst);
+ }
+ else
+ switch (cbValue)
+ {
+ case 1: uValue = *(uint8_t const BS3_FAR *)pbCode; break;
+ case 2: uValue = *(uint16_t const BS3_FAR *)pbCode; break;
+ case 4: uValue = *(uint32_t const BS3_FAR *)pbCode; break;
+ default:
+ if (cbValue >= 8)
+ {
+ uValue = *(uint64_t const BS3_FAR *)pbCode;
+ break;
+ }
+ return Bs3TestFailedF("Malformed context instruction: %u bytes value (%u dst)", cbValue, cbDst);
+ }
+
+ /* Find the field. */
+ if (offField < sizeof(BS3REGCTX))
+ PtrField.pu8 = (uint8_t BS3_FAR *)pCtx + offField;
+ /* Non-register operands: */
+ else if ((unsigned)(idxField - BS3CG1DST_OP1) < 4U)
+ {
+ unsigned const idxOp = idxField - BS3CG1DST_OP1;
+
+ switch (pThis->aOperands[idxOp].enmLocation)
+ {
+ case BS3CG1OPLOC_IMM:
+ if (pbInstr)
+ PtrField.pu8 = &pbInstr[pThis->aOperands[idxOp].off];
+ else
+ return Bs3TestFailedF("Immediate operand referenced in output context!");
+ break;
+
+ case BS3CG1OPLOC_MEM:
+ if (!pbInstr)
+ return Bs3TestFailedF("Read only operand specified in output!");
+ PtrField.pu8 = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[idxOp].off];
+ break;
+
+ case BS3CG1OPLOC_MEM_RW:
+ case BS3CG1OPLOC_MEM_WO:
+ if (pbInstr)
+ {
+ PtrField.pu8 = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[idxOp].off];
+ pbMemCopy = pThis->MemOp.ab;
+ }
+ else
+ PtrField.pu8 = pThis->MemOp.ab;
+ break;
+
+ default:
+ if (pThis->enmEncoding != pThis->enmEncodingNonInvalid)
+ goto l_advance_to_next;
+ return Bs3TestFailedF("Internal error: cbDst=%u idxField=%d (%d) offField=%#x: enmLocation=%u off=%#x idxField=%u",
+ cbDst, idxField, idxOp, offField, pThis->aOperands[idxOp].enmLocation,
+ pThis->aOperands[idxOp].off, pThis->aOperands[idxOp].idxField);
+ }
+ }
+ /* Special field: Copying in undefined EFLAGS from the result context. */
+ else if (idxField == BS3CG1DST_EFL_UNDEF)
+ {
+ if (!pEflCtx || (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK) != BS3CG1_CTXOP_ASSIGN)
+ return Bs3TestFailed("Invalid BS3CG1DST_EFL_UNDEF usage");
+ PtrField.pu32 = &pCtx->rflags.u32;
+ uValue = (*PtrField.pu32 & ~(uint32_t)uValue) | (pEflCtx->rflags.u32 & (uint32_t)uValue);
+ }
+ /* Special field: Expected value (in/result) exception. */
+ else if (idxField == BS3CG1DST_VALUE_XCPT)
+ {
+ if (!pEflCtx || (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK) != BS3CG1_CTXOP_ASSIGN || cbDst != 1)
+ return Bs3TestFailed("Invalid BS3CG1DST_VALUE_XCPT usage");
+ PtrField.pu8 = &pThis->bValueXcpt;
+ }
+ /* FPU and FXSAVE format. */
+ else if ( pThis->pExtCtx->enmMethod != BS3EXTCTXMETHOD_ANCIENT
+ && offField - sizeof(BS3REGCTX) < RT_UOFFSET_AFTER(BS3EXTCTX, Ctx.x87.aXMM[15]))
+ {
+ if (pThis->fWorkExtCtx)
+ PtrField.pb = (uint8_t *)pThis->pExtCtx + offField - sizeof(BS3REGCTX);
+ else if (!pThis->fCpuSetupFirstResult)
+ {
+ BS3CG1_DPRINTF(("dbg: Extended context disabled: skipping modification (<=8)\n"));
+ goto l_advance_to_next;
+ }
+ else
+ return Bs3TestFailedF("Extended context disabled: Field %d (%s) @ %#x LB %u\n",
+ idxField, g_aszBs3Cg1DstFields[idxField].sz, offField, cbDst);
+ }
+ /** @todo other FPU fields and FPU state formats. */
+ else
+ return Bs3TestFailedF("Todo implement me: cbDst=%u idxField=%d %s offField=%#x (<= 8)",
+ cbDst, idxField, g_aszBs3Cg1DstFields[idxField].sz, offField);
+
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ switch (cbDst)
+ {
+ case 1:
+ BS3CG1_DPRINTF(("dbg: modify %s: %#04RX8 (LB %u) %s %#RX64 (LB %u)\n", g_aszBs3Cg1DstFields[idxField].sz,
+ *PtrField.pu8, cbDst, Bs3Cg1CtxOpToString(bOpcode), uValue, cbValue));
+ break;
+ case 2:
+ BS3CG1_DPRINTF(("dbg: modify %s: %#06RX16 (LB %u) %s %#RX64 (LB %u)\n", g_aszBs3Cg1DstFields[idxField].sz,
+ *PtrField.pu16, cbDst, Bs3Cg1CtxOpToString(bOpcode), uValue, cbValue));
+ break;
+ case 4:
+ BS3CG1_DPRINTF(("dbg: modify %s: %#010RX32 (LB %u) %s %#RX64 (LB %u)\n", g_aszBs3Cg1DstFields[idxField].sz,
+ *PtrField.pu32, cbDst, Bs3Cg1CtxOpToString(bOpcode), uValue, cbValue));
+ break;
+ default:
+ BS3CG1_DPRINTF(("dbg: modify %s: %#018RX64 (LB %u) %s %#RX64 (LB %u)\n", g_aszBs3Cg1DstFields[idxField].sz,
+ *PtrField.pu64, cbDst, Bs3Cg1CtxOpToString(bOpcode), uValue, cbValue));
+ break;
+ }
+#endif
+
+ /* Modify the field. */
+ switch (cbDst)
+ {
+ case 1:
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: *PtrField.pu8 = (uint8_t)uValue; break;
+ case BS3CG1_CTXOP_OR: *PtrField.pu8 |= (uint8_t)uValue; break;
+ case BS3CG1_CTXOP_AND: *PtrField.pu8 &= (uint8_t)uValue; break;
+ case BS3CG1_CTXOP_AND_INV: *PtrField.pu8 &= ~(uint8_t)uValue; break;
+ }
+ break;
+
+ case 2:
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: *PtrField.pu16 = (uint16_t)uValue; break;
+ case BS3CG1_CTXOP_OR: *PtrField.pu16 |= (uint16_t)uValue; break;
+ case BS3CG1_CTXOP_AND: *PtrField.pu16 &= (uint16_t)uValue; break;
+ case BS3CG1_CTXOP_AND_INV: *PtrField.pu16 &= ~(uint16_t)uValue; break;
+ }
+ break;
+
+ case 4:
+ if ( (unsigned)(idxField - BS3CG1DST_XMM0_DW0_ZX) <= (unsigned)(BS3CG1DST_XMM15_DW0_ZX - BS3CG1DST_XMM0_DW0_ZX)
+ || fZxVlMax)
+ {
+ PtrField.pu32[1] = 0;
+ PtrField.pu64[1] = 0;
+ }
+ else if (offField <= RT_UOFFSETOF(BS3REGCTX, r15) /* Clear the top dword. */)
+ PtrField.pu32[1] = 0;
+ else if ((unsigned)(idxField - BS3CG1DST_MM0_LO_ZX) <= (unsigned)(BS3CG1DST_MM7_LO_ZX - BS3CG1DST_MM0_LO_ZX))
+ {
+ PtrField.pu32[1] = 0;
+ PtrField.pu32[2] = 0xffff; /* observed on skylake */
+ }
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: *PtrField.pu32 = (uint32_t)uValue; break;
+ case BS3CG1_CTXOP_OR: *PtrField.pu32 |= (uint32_t)uValue; break;
+ case BS3CG1_CTXOP_AND: *PtrField.pu32 &= (uint32_t)uValue; break;
+ case BS3CG1_CTXOP_AND_INV: *PtrField.pu32 &= ~(uint32_t)uValue; break;
+ }
+ break;
+
+ case 8:
+ if ( (unsigned)(idxField - BS3CG1DST_XMM0_LO_ZX) <= (unsigned)(BS3CG1DST_XMM15_LO_ZX - BS3CG1DST_XMM0_LO_ZX)
+ || fZxVlMax)
+ PtrField.pu64[1] = 0;
+ else if ((unsigned)(idxField - BS3CG1DST_MM0) <= (unsigned)(BS3CG1DST_MM7 - BS3CG1DST_MM0))
+ PtrField.pu32[2] = 0xffff; /* observed on skylake */
+
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: *PtrField.pu64 = (uint64_t)uValue; break;
+ case BS3CG1_CTXOP_OR: *PtrField.pu64 |= (uint64_t)uValue; break;
+ case BS3CG1_CTXOP_AND: *PtrField.pu64 &= (uint64_t)uValue; break;
+ case BS3CG1_CTXOP_AND_INV: *PtrField.pu64 &= ~(uint64_t)uValue; break;
+ }
+ break;
+
+ default:
+ return Bs3TestFailedF("Malformed context instruction: cbDst=%u, expected 1, 2, 4, or 8", cbDst);
+ }
+
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ switch (cbDst)
+ {
+ case 1: BS3CG1_DPRINTF(("dbg: --> %s: %#04RX8\n", g_aszBs3Cg1DstFields[idxField].sz, *PtrField.pu8)); break;
+ case 2: BS3CG1_DPRINTF(("dbg: --> %s: %#06RX16\n", g_aszBs3Cg1DstFields[idxField].sz, *PtrField.pu16)); break;
+ case 4: BS3CG1_DPRINTF(("dbg: --> %s: %#010RX32\n", g_aszBs3Cg1DstFields[idxField].sz, *PtrField.pu32)); break;
+ default: BS3CG1_DPRINTF(("dbg: --> %s: %#018RX64\n", g_aszBs3Cg1DstFields[idxField].sz, *PtrField.pu64)); break;
+ }
+#endif
+ if (fZxVlMax)
+ {
+ uintptr_t iReg = ((uintptr_t)PtrField.pu8 - (uintptr_t)&pThis->pExtCtx->Ctx.x87.aXMM[0])
+ / sizeof(pThis->pExtCtx->Ctx.x87.aXMM[0]);
+ pThis->pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[0] = 0;
+ pThis->pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[1] = 0;
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ BS3CG1_DPRINTF(("dbg: --> cleared YMM%u_HI\n", iReg));
+#endif
+ }
+ }
+ /*
+ * Deal with larger field (FPU, SSE, AVX, ...).
+ */
+ else if (pThis->fWorkExtCtx)
+ {
+ union
+ {
+ X86FPUREG FpuReg;
+ X86XMMREG XmmReg;
+ X86YMMREG YmmReg;
+ X86ZMMREG ZmmReg;
+ uint8_t ab[sizeof(X86ZMMREG)];
+ uint32_t au32[sizeof(X86ZMMREG) / sizeof(uint32_t)];
+ uint64_t au64[sizeof(X86ZMMREG) / sizeof(uint64_t)];
+ } Value;
+ unsigned const offField = g_aoffBs3Cg1DstFields[idxField];
+ unsigned iReg;
+
+ /* Copy the value into the union, doing the zero padding / extending. */
+ Bs3MemCpy(&Value, pbCode, cbValue);
+ if (cbValue < sizeof(Value))
+ {
+ if ((bOpcode & BS3CG1_CTXOP_SIGN_EXT) && (Value.ab[cbValue - 1] & 0x80))
+ Bs3MemSet(&Value.ab[cbValue], 0xff, sizeof(Value) - cbValue);
+ else
+ Bs3MemSet(&Value.ab[cbValue], 0x00, sizeof(Value) - cbValue);
+ }
+
+ /* Optimized access to XMM and STx registers. */
+ if ( pThis->pExtCtx->enmMethod != BS3EXTCTXMETHOD_ANCIENT
+ && offField - sizeof(BS3REGCTX) < RT_UOFFSET_AFTER(BS3EXTCTX, Ctx.x87.aXMM[15]) )
+ PtrField.pb = (uint8_t *)pThis->pExtCtx + offField - sizeof(BS3REGCTX);
+ /* Non-register operands: */
+ else if ((unsigned)(idxField - BS3CG1DST_OP1) < 4U)
+ {
+ unsigned const idxOp = idxField - BS3CG1DST_OP1;
+ switch (pThis->aOperands[idxOp].enmLocation)
+ {
+ case BS3CG1OPLOC_MEM:
+ if (!pbInstr)
+ return Bs3TestFailedF("Read only operand specified in output!");
+ PtrField.pu8 = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[idxOp].off];
+ break;
+
+ case BS3CG1OPLOC_MEM_RW:
+ case BS3CG1OPLOC_MEM_WO:
+ if (pbInstr)
+ {
+ PtrField.pu8 = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[idxOp].off];
+ pbMemCopy = pThis->MemOp.ab;
+ }
+ else
+ PtrField.pu8 = pThis->MemOp.ab;
+ break;
+
+ default:
+ return Bs3TestFailedF("Internal error: Field %d (%d) @ %#x LB %u: enmLocation=%u off=%#x idxField=%u",
+ idxField, idxOp, offField, cbDst, pThis->aOperands[idxOp].enmLocation,
+ pThis->aOperands[idxOp].off, pThis->aOperands[idxOp].idxField);
+ }
+ }
+ /* The YMM (AVX) registers have split storage in the state, so they need special handling. */
+ else if ((iReg = idxField - BS3CG1DST_YMM0) < 16U)
+ {
+ /* The first 128-bits in XMM land. */
+ PtrField.pu64 = &pThis->pExtCtx->Ctx.x87.aXMM[iReg].au64[0];
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN:
+ PtrField.pu64[0] = Value.au64[0];
+ PtrField.pu64[1] = Value.au64[1];
+ break;
+ case BS3CG1_CTXOP_OR:
+ PtrField.pu64[0] |= Value.au64[0];
+ PtrField.pu64[1] |= Value.au64[1];
+ break;
+ case BS3CG1_CTXOP_AND:
+ PtrField.pu64[0] &= Value.au64[0];
+ PtrField.pu64[1] &= Value.au64[1];
+ break;
+ case BS3CG1_CTXOP_AND_INV:
+ PtrField.pu64[0] &= ~Value.au64[0];
+ PtrField.pu64[1] &= ~Value.au64[1];
+ break;
+ }
+
+ /* The second 128-bit in YMM_HI land. */
+ PtrField.pu64 = &pThis->pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[0];
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN:
+ PtrField.pu64[0] = Value.au64[2];
+ PtrField.pu64[1] = Value.au64[3];
+ break;
+ case BS3CG1_CTXOP_OR:
+ PtrField.pu64[0] |= Value.au64[2];
+ PtrField.pu64[1] |= Value.au64[3];
+ break;
+ case BS3CG1_CTXOP_AND:
+ PtrField.pu64[0] &= Value.au64[2];
+ PtrField.pu64[1] &= Value.au64[3];
+ break;
+ case BS3CG1_CTXOP_AND_INV:
+ PtrField.pu64[0] &= ~Value.au64[2];
+ PtrField.pu64[1] &= ~Value.au64[3];
+ break;
+ }
+ PtrField.pb = NULL;
+ }
+ /* AVX512 needs handling like above, but more complicated. */
+ else
+ return Bs3TestFailedF("TODO: implement me: cbDst=%d idxField=%d (AVX and other weird state)", cbDst, idxField);
+
+ if (PtrField.pb)
+ {
+ /* Modify the field / memory. */
+ unsigned i;
+ if (cbDst & 3)
+ return Bs3TestFailedF("Malformed context instruction: cbDst=%u, multiple of 4", cbDst);
+
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ BS3CG1_DPRINTF(("dbg: modify %s: %.*Rhxs (LB %u) %s %.*Rhxs (LB %u)\n", g_aszBs3Cg1DstFields[idxField].sz,
+ cbDst, PtrField.pb, cbDst, Bs3Cg1CtxOpToString(bOpcode), cbValue, Value.ab, cbValue));
+#endif
+
+ i = cbDst / 4;
+ while (i-- > 0)
+ {
+ switch (bOpcode & BS3CG1_CTXOP_OPERATOR_MASK)
+ {
+ case BS3CG1_CTXOP_ASSIGN: PtrField.pu32[i] = Value.au32[i]; break;
+ case BS3CG1_CTXOP_OR: PtrField.pu32[i] |= Value.au32[i]; break;
+ case BS3CG1_CTXOP_AND: PtrField.pu32[i] &= Value.au32[i]; break;
+ case BS3CG1_CTXOP_AND_INV: PtrField.pu32[i] &= ~Value.au32[i]; break;
+ }
+ }
+
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ BS3CG1_DPRINTF(("dbg: --> %s: %.*Rhxs\n", g_aszBs3Cg1DstFields[idxField].sz, cbDst, PtrField.pb));
+#endif
+
+ if (fZxVlMax)
+ {
+ uintptr_t iReg = ((uintptr_t)PtrField.pu8 - (uintptr_t)&pThis->pExtCtx->Ctx.x87.aXMM[0])
+ / sizeof(pThis->pExtCtx->Ctx.x87.aXMM[0]);
+ if (cbDst < 16)
+ {
+ for (i = cbDst / 4; i < 4; i++)
+ PtrField.pu32[i++] = 0;
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ BS3CG1_DPRINTF(("dbg: --> cleared high %u bytes of XMM%u\n", 16 - cbDst, iReg));
+#endif
+ }
+ pThis->pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[0] = 0;
+ pThis->pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[1] = 0;
+#ifdef BS3CG1_DEBUG_CTX_MOD
+ BS3CG1_DPRINTF(("dbg: --> cleared YMM%u_HI\n", iReg));
+#endif
+ }
+ }
+
+ /*
+ * Hack! Update pThis->MemOp when setting up the inputs so we can
+ * correctly validate value and alignment exceptions.
+ */
+ if (pbMemCopy && PtrField.pv)
+ Bs3MemCpy(pbMemCopy, PtrField.pv, cbDst);
+ }
+ /* !pThis->fWorkExtCtx: */
+ else if (pThis->fCpuSetupFirstResult)
+ return Bs3TestFailedF("Extended context disabled: Field %d (%s) @ %#x LB %u\n",
+ idxField, g_aszBs3Cg1DstFields[idxField].sz, g_aoffBs3Cg1DstFields[idxField], cbDst);
+ else
+ BS3CG1_DPRINTF(("dbg: Extended context disabled: skipping modification [> 8]\n"));
+
+ /*
+ * Advance to the next instruction.
+ */
+l_advance_to_next:
+ pbCode += cbValue;
+ cbLeft -= cbValue;
+ }
+
+ return true;
+}
+
+
+/**
+ * Checks the result of a run.
+ *
+ * @returns true if successful, false if not.
+ * @param pThis The state.
+ * @param bTestXcptExpected The exception causing the test code to stop
+ * executing.
+ * @param fInvalidEncodingPgFault Set if we've cut the instruction a byte
+ * short and is expecting a \#PF on the page
+ * boundrary rather than a \#UD. Only set if
+ * fInvalidEncoding is also set.
+ * @param iEncoding For error reporting.
+ */
+static bool BS3_NEAR_CODE Bs3Cg1CheckResult(PBS3CG1STATE pThis, uint8_t bTestXcptExpected,
+ bool fInvalidEncodingPgFault, unsigned iEncoding)
+{
+ unsigned iOperand;
+
+ /*
+ * Check the exception state first.
+ */
+ uint8_t bExpectedXcpt;
+ uint8_t cbAdjustPc;
+ if (!pThis->fInvalidEncoding)
+ {
+ bExpectedXcpt = pThis->bAlignmentXcpt;
+ if (bExpectedXcpt == UINT8_MAX)
+ bExpectedXcpt = pThis->bValueXcpt;
+ if (bExpectedXcpt == UINT8_MAX)
+ {
+ cbAdjustPc = pThis->cbCurInstr;
+ bExpectedXcpt = bTestXcptExpected;
+ if (bTestXcptExpected == X86_XCPT_PF)
+ pThis->Ctx.cr2.u = pThis->uCodePgFlat + X86_PAGE_SIZE;
+ }
+ else
+ cbAdjustPc = 0;
+ }
+ else
+ {
+ cbAdjustPc = 0;
+ if (!fInvalidEncodingPgFault)
+ bExpectedXcpt = X86_XCPT_UD;
+ else
+ {
+ bExpectedXcpt = X86_XCPT_PF;
+ pThis->Ctx.cr2.u = pThis->uCodePgFlat + X86_PAGE_SIZE;
+ }
+ }
+ if (RT_LIKELY( pThis->TrapFrame.bXcpt == bExpectedXcpt
+ && pThis->TrapFrame.Ctx.rip.u == pThis->Ctx.rip.u + cbAdjustPc))
+ {
+ /*
+ * Check the register content.
+ */
+ bool fOkay = Bs3TestCheckRegCtxEx(&pThis->TrapFrame.Ctx, &pThis->Ctx,
+ cbAdjustPc, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/,
+ pThis->pszMode, iEncoding);
+
+ /*
+ * Check memory output operands.
+ */
+ if (!pThis->fInvalidEncoding)
+ {
+ iOperand = pThis->cOperands;
+ while (iOperand-- > 0)
+ if ( pThis->aOperands[iOperand].enmLocation == BS3CG1OPLOC_MEM_RW
+ || pThis->aOperands[iOperand].enmLocation == BS3CG1OPLOC_MEM_WO)
+ {
+ if (pThis->aOperands[iOperand].off)
+ {
+ BS3PTRUNION PtrUnion;
+ PtrUnion.pb = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[iOperand].off];
+ switch (pThis->aOperands[iOperand].cbOp)
+ {
+ case 1:
+ if (*PtrUnion.pu8 == pThis->MemOp.ab[0])
+ continue;
+ Bs3TestFailedF("op%u: Wrote %#04RX8, expected %#04RX8",
+ iOperand, *PtrUnion.pu8, pThis->MemOp.ab[0]);
+ break;
+ case 2:
+ if (*PtrUnion.pu16 == pThis->MemOp.au16[0])
+ continue;
+ Bs3TestFailedF("op%u: Wrote %#06RX16, expected %#06RX16",
+ iOperand, *PtrUnion.pu16, pThis->MemOp.au16[0]);
+ break;
+ case 4:
+ if (*PtrUnion.pu32 == pThis->MemOp.au32[0])
+ continue;
+ Bs3TestFailedF("op%u: Wrote %#010RX32, expected %#010RX32",
+ iOperand, *PtrUnion.pu32, pThis->MemOp.au32[0]);
+ break;
+ case 8:
+ if (*PtrUnion.pu64 == pThis->MemOp.au64[0])
+ continue;
+ Bs3TestFailedF("op%u: Wrote %#018RX64, expected %#018RX64",
+ iOperand, *PtrUnion.pu64, pThis->MemOp.au64[0]);
+ break;
+ default:
+ if (Bs3MemCmp(PtrUnion.pb, pThis->MemOp.ab, pThis->aOperands[iOperand].cbOp) == 0)
+ continue;
+ Bs3TestFailedF("op%u: Wrote %.*Rhxs, expected %.*Rhxs",
+ iOperand,
+ pThis->aOperands[iOperand].cbOp, PtrUnion.pb,
+ pThis->aOperands[iOperand].cbOp, pThis->MemOp.ab);
+ break;
+ }
+ }
+ else
+ Bs3TestFailedF("op%u: off is zero\n", iOperand);
+ fOkay = false;
+ }
+ }
+
+ /*
+ * Check extended context if enabled.
+ */
+ if (pThis->fWorkExtCtx)
+ {
+ PBS3EXTCTX pExpect = pThis->pExtCtx;
+ PBS3EXTCTX pResult = pThis->pResultExtCtx;
+ unsigned i;
+ if ( pExpect->enmMethod == BS3EXTCTXMETHOD_XSAVE
+ || pExpect->enmMethod == BS3EXTCTXMETHOD_FXSAVE)
+ {
+ /* Compare the x87 state, ASSUMING XCR0 bit 1 is set. */
+#define CHECK_FIELD(a_Field, a_szFmt) \
+ if (pResult->Ctx.a_Field != pExpect->Ctx.a_Field) fOkay = Bs3TestFailedF(a_szFmt, pResult->Ctx.a_Field, pExpect->Ctx.a_Field)
+ CHECK_FIELD(x87.FCW, "FCW: %#06x, expected %#06x");
+ CHECK_FIELD(x87.FSW, "FSW: %#06x, expected %#06x");
+ CHECK_FIELD(x87.FTW, "FTW: %#06x, expected %#06x");
+ //CHECK_FIELD(x87.FOP, "FOP: %#06x, expected %#06x");
+ //CHECK_FIELD(x87.FPUIP, "FPUIP: %#010RX32, expected %#010RX32");
+ //CHECK_FIELD(x87.CS, "FPUCS: %#06x, expected %#06x");
+ //CHECK_FIELD(x87.Rsrvd1, "Rsrvd1: %#06x, expected %#06x");
+ //CHECK_FIELD(x87.DP, "FPUDP: %#010RX32, expected %#010RX32");
+ //CHECK_FIELD(x87.DS, "FPUDS: %#06x, expected %#06x");
+ //CHECK_FIELD(x87.Rsrvd2, "Rsrvd2: %#06x, expected %#06x");
+ CHECK_FIELD(x87.MXCSR, "MXCSR: %#010RX32, expected %#010RX32");
+#undef CHECK_FIELD
+ for (i = 0; i < RT_ELEMENTS(pExpect->Ctx.x87.aRegs); i++)
+ if ( pResult->Ctx.x87.aRegs[i].au64[0] != pExpect->Ctx.x87.aRegs[i].au64[0]
+ || pResult->Ctx.x87.aRegs[i].au16[4] != pExpect->Ctx.x87.aRegs[i].au16[4])
+ fOkay = Bs3TestFailedF("ST[%u]: %c m=%#RX64 e=%d, expected %c m=%#RX64 e=%d", i,
+ pResult->Ctx.x87.aRegs[i].r80Ex.s.fSign ? '-' : '+',
+ pResult->Ctx.x87.aRegs[i].r80Ex.s.uMantissa,
+ pResult->Ctx.x87.aRegs[i].r80Ex.s.uExponent,
+ pExpect->Ctx.x87.aRegs[i].r80Ex.s.fSign ? '-' : '+',
+ pExpect->Ctx.x87.aRegs[i].r80Ex.s.uMantissa,
+ pExpect->Ctx.x87.aRegs[i].r80Ex.s.uExponent);
+ for (i = 0; i < (ARCH_BITS == 64 ? 16 : 8); i++)
+ if ( pResult->Ctx.x87.aXMM[i].au64[0] != pExpect->Ctx.x87.aXMM[i].au64[0]
+ || pResult->Ctx.x87.aXMM[i].au64[1] != pExpect->Ctx.x87.aXMM[i].au64[1])
+ fOkay = Bs3TestFailedF("XMM%u: %#010RX64'%016RX64, expected %#010RX64'%08RX64", i,
+ pResult->Ctx.x87.aXMM[i].au64[1],
+ pResult->Ctx.x87.aXMM[i].au64[0],
+ pExpect->Ctx.x87.aXMM[i].au64[1],
+ pExpect->Ctx.x87.aXMM[i].au64[0]);
+ if (pExpect->fXcr0Saved & XSAVE_C_YMM)
+ for (i = 0; i < (ARCH_BITS == 64 ? 16 : 8); i++)
+ if ( pResult->Ctx.x.u.YmmHi.aYmmHi[i].au64[0] != pExpect->Ctx.x.u.YmmHi.aYmmHi[i].au64[0]
+ || pResult->Ctx.x.u.YmmHi.aYmmHi[i].au64[1] != pExpect->Ctx.x.u.YmmHi.aYmmHi[i].au64[1])
+ fOkay = Bs3TestFailedF("YMM%u_HI: %#010RX64'%016RX64, expected %#010RX64'%08RX64", i,
+ pResult->Ctx.x.u.YmmHi.aYmmHi[i].au64[1],
+ pResult->Ctx.x.u.YmmHi.aYmmHi[i].au64[0],
+ pExpect->Ctx.x.u.YmmHi.aYmmHi[i].au64[1],
+ pExpect->Ctx.x.u.YmmHi.aYmmHi[i].au64[0]);
+ }
+ else
+ fOkay = Bs3TestFailedF("Unsupported extended CPU context method: %d", pExpect->enmMethod);
+ }
+
+ /*
+ * Done.
+ */
+ if (fOkay)
+ return true;
+
+ /*
+ * Report failure.
+ */
+ Bs3TestFailedF("ins#%RU32/test#%u: encoding #%u: %.*Rhxs%s",
+ pThis->iInstr, pThis->iTest, iEncoding, pThis->cbCurInstr, pThis->abCurInstr,
+ fInvalidEncodingPgFault ? " (cut short)" : "");
+ }
+ else
+ Bs3TestFailedF("ins#%RU32/test#%u: bXcpt=%#x expected %#x; rip=%RX64 expected %RX64; encoding#%u: %.*Rhxs%s",
+ pThis->iInstr, pThis->iTest,
+ pThis->TrapFrame.bXcpt, bExpectedXcpt,
+ pThis->TrapFrame.Ctx.rip.u, pThis->Ctx.rip.u + cbAdjustPc,
+ iEncoding, pThis->cbCurInstr, pThis->abCurInstr, fInvalidEncodingPgFault ? " (cut short)" : "");
+ Bs3TestPrintf("cpl=%u cbOperands=%u\n", pThis->uCpl, pThis->cbOperand);
+
+ /*
+ * Display memory operands.
+ */
+ for (iOperand = 0; iOperand < pThis->cOperands; iOperand++)
+ {
+ BS3PTRUNION PtrUnion;
+ switch (pThis->aOperands[iOperand].enmLocation)
+ {
+ case BS3CG1OPLOC_CTX:
+ {
+ uint8_t idxField = pThis->aOperands[iOperand].idxField;
+ unsigned offField = g_aoffBs3Cg1DstFields[idxField];
+ if (offField <= sizeof(BS3REGCTX))
+ PtrUnion.pb = (uint8_t BS3_FAR *)&pThis->Ctx + offField;
+ else
+ {
+ Bs3TestPrintf("op%u: ctx%u: xxxx\n", iOperand, pThis->aOperands[iOperand].cbOp * 8);
+ break;
+ }
+ switch (pThis->aOperands[iOperand].cbOp)
+ {
+ case 1: Bs3TestPrintf("op%u: ctx08: %#04RX8\n", iOperand, *PtrUnion.pu8); break;
+ case 2: Bs3TestPrintf("op%u: ctx16: %#06RX16\n", iOperand, *PtrUnion.pu16); break;
+ case 4: Bs3TestPrintf("op%u: ctx32: %#010RX32\n", iOperand, *PtrUnion.pu32); break;
+ case 8: Bs3TestPrintf("op%u: ctx64: %#018RX64\n", iOperand, *PtrUnion.pu64); break;
+ default:
+ Bs3TestPrintf("op%u: ctx%u: %.*Rhxs\n", iOperand, pThis->aOperands[iOperand].cbOp * 8,
+ pThis->aOperands[iOperand].cbOp, PtrUnion.pb);
+ break;
+ }
+ break;
+ }
+
+ case BS3CG1OPLOC_IMM:
+ PtrUnion.pb = &pThis->pbCodePg[pThis->aOperands[iOperand].off];
+ switch (pThis->aOperands[iOperand].cbOp)
+ {
+ case 1: Bs3TestPrintf("op%u: imm08: %#04RX8\n", iOperand, *PtrUnion.pu8); break;
+ case 2: Bs3TestPrintf("op%u: imm16: %#06RX16\n", iOperand, *PtrUnion.pu16); break;
+ case 4: Bs3TestPrintf("op%u: imm32: %#010RX32\n", iOperand, *PtrUnion.pu32); break;
+ case 8: Bs3TestPrintf("op%u: imm64: %#018RX64\n", iOperand, *PtrUnion.pu64); break;
+ default:
+ Bs3TestPrintf("op%u: imm%u: %.*Rhxs\n", iOperand, pThis->aOperands[iOperand].cbOp * 8,
+ pThis->aOperands[iOperand].cbOp, PtrUnion.pb);
+ break;
+ }
+ break;
+
+ case BS3CG1OPLOC_MEM:
+ case BS3CG1OPLOC_MEM_RW:
+ case BS3CG1OPLOC_MEM_WO:
+ if (pThis->aOperands[iOperand].off)
+ {
+ PtrUnion.pb = &pThis->pbDataPg[X86_PAGE_SIZE - pThis->aOperands[iOperand].off];
+ switch (pThis->aOperands[iOperand].cbOp)
+ {
+ case 1: Bs3TestPrintf("op%u: result mem08: %#04RX8\n", iOperand, *PtrUnion.pu8); break;
+ case 2: Bs3TestPrintf("op%u: result mem16: %#06RX16\n", iOperand, *PtrUnion.pu16); break;
+ case 4: Bs3TestPrintf("op%u: result mem32: %#010RX32\n", iOperand, *PtrUnion.pu32); break;
+ case 8: Bs3TestPrintf("op%u: result mem64: %#018RX64\n", iOperand, *PtrUnion.pu64); break;
+ default:
+ Bs3TestPrintf("op%u: result mem%u: %.*Rhxs\n", iOperand, pThis->aOperands[iOperand].cbOp * 8,
+ pThis->aOperands[iOperand].cbOp, PtrUnion.pb);
+ break;
+ }
+ if ( pThis->aOperands[iOperand].enmLocation == BS3CG1OPLOC_MEM_WO
+ || pThis->aOperands[iOperand].enmLocation == BS3CG1OPLOC_MEM_RW)
+ {
+ PtrUnion.pb = pThis->MemOp.ab;
+ switch (pThis->aOperands[iOperand].cbOp)
+ {
+ case 1: Bs3TestPrintf("op%u: expect mem08: %#04RX8\n", iOperand, *PtrUnion.pu8); break;
+ case 2: Bs3TestPrintf("op%u: expect mem16: %#06RX16\n", iOperand, *PtrUnion.pu16); break;
+ case 4: Bs3TestPrintf("op%u: expect mem32: %#010RX32\n", iOperand, *PtrUnion.pu32); break;
+ case 8: Bs3TestPrintf("op%u: expect mem64: %#018RX64\n", iOperand, *PtrUnion.pu64); break;
+ default:
+ Bs3TestPrintf("op%u: expect mem%u: %.*Rhxs\n", iOperand, pThis->aOperands[iOperand].cbOp * 8,
+ pThis->aOperands[iOperand].cbOp, PtrUnion.pb);
+ break;
+ }
+ }
+ }
+ else
+ Bs3TestPrintf("op%u: mem%u: zero off value!!\n", iOperand, pThis->aOperands[iOperand].cbOp * 8);
+ break;
+ }
+ }
+
+ /*
+ * Display contexts.
+ */
+ Bs3TestPrintf("-- Expected context:\n");
+ Bs3RegCtxPrint(&pThis->Ctx);
+ if (pThis->fWorkExtCtx)
+ Bs3TestPrintf("xcr0=%RX64\n", pThis->pExtCtx->fXcr0Saved);
+ Bs3TestPrintf("-- Actual context:\n");
+ Bs3TrapPrintFrame(&pThis->TrapFrame);
+ if (pThis->fWorkExtCtx)
+ Bs3TestPrintf("xcr0=%RX64\n", pThis->pResultExtCtx->fXcr0Saved);
+ Bs3TestPrintf("\n");
+ASMHalt();
+ return false;
+}
+
+
+/**
+ * Destroys the state, freeing all allocations and such.
+ *
+ * @param pThis The state.
+ */
+static void BS3_NEAR_CODE Bs3Cg1Destroy(PBS3CG1STATE pThis)
+{
+ if (BS3_MODE_IS_PAGED(pThis->bMode))
+ {
+#if ARCH_BITS != 16
+ Bs3MemGuardedTestPageFree(pThis->pbCodePg);
+ Bs3MemGuardedTestPageFree(pThis->pbDataPg);
+#endif
+ }
+ else
+ {
+ Bs3MemFree(pThis->pbCodePg, X86_PAGE_SIZE);
+ Bs3MemFree(pThis->pbDataPg, X86_PAGE_SIZE);
+ }
+
+ if (pThis->pExtCtx)
+ Bs3MemFree(pThis->pExtCtx, pThis->pExtCtx->cb * 3);
+
+ pThis->pbCodePg = NULL;
+ pThis->pbDataPg = NULL;
+ pThis->pExtCtx = NULL;
+ pThis->pResultExtCtx = NULL;
+ pThis->pInitialExtCtx = NULL;
+}
+
+
+/**
+ * Initializes the state.
+ *
+ * @returns Success indicator (true/false)
+ * @param pThis The state.
+ * @param bMode The mode being tested.
+ */
+bool BS3_NEAR_CODE BS3_CMN_NM(Bs3Cg1Init)(PBS3CG1STATE pThis, uint8_t bMode)
+{
+ BS3MEMKIND const enmMemKind = BS3_MODE_IS_RM_OR_V86(bMode) ? BS3MEMKIND_REAL
+ : !BS3_MODE_IS_64BIT_CODE(bMode) ? BS3MEMKIND_TILED : BS3MEMKIND_FLAT32;
+ unsigned iRing;
+ unsigned cb;
+ unsigned i;
+ uint64_t fFlags;
+ PBS3EXTCTX pExtCtx;
+
+ Bs3MemSet(pThis, 0, sizeof(*pThis));
+
+ pThis->iFirstRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ pThis->iEndRing = BS3_MODE_IS_RM_SYS(bMode) ? 1 : 4;
+ pThis->bMode = bMode;
+ pThis->pszMode = Bs3GetModeName(bMode);
+ pThis->pszModeShort = Bs3GetModeNameShortLower(bMode);
+ pThis->bCpuVendor = Bs3GetCpuVendor();
+ pThis->pchMnemonic = g_achBs3Cg1Mnemonics;
+ pThis->pabOperands = g_abBs3Cg1Operands;
+ pThis->pabOpcodes = g_abBs3Cg1Opcodes;
+ pThis->fAdvanceMnemonic = 1;
+
+ /* Allocate extended context structures. */
+ cb = Bs3ExtCtxGetSize(&fFlags);
+ pExtCtx = Bs3MemAlloc(BS3MEMKIND_TILED, cb * 3);
+ if (!pExtCtx)
+ return Bs3TestFailedF("Bs3MemAlloc(tiled,%#x)", cb * 3);
+ pThis->pExtCtx = pExtCtx;
+ pThis->pResultExtCtx = (PBS3EXTCTX)((uint8_t BS3_FAR *)pExtCtx + cb);
+ pThis->pInitialExtCtx = (PBS3EXTCTX)((uint8_t BS3_FAR *)pExtCtx + cb + cb);
+
+ Bs3ExtCtxInit(pThis->pExtCtx, cb, fFlags);
+ Bs3ExtCtxInit(pThis->pResultExtCtx, cb, fFlags);
+ Bs3ExtCtxInit(pThis->pInitialExtCtx, cb, fFlags);
+ //Bs3TestPrintf("fCR0=%RX64 cbExtCtx=%#x method=%d\n", fFlags, cb, pExtCtx->enmMethod);
+
+ /* Allocate guarded exectuable and data memory. */
+ if (BS3_MODE_IS_PAGED(bMode))
+ {
+#if ARCH_BITS != 16
+ pThis->pbCodePg = Bs3MemGuardedTestPageAlloc(enmMemKind);
+ pThis->pbDataPg = Bs3MemGuardedTestPageAlloc(enmMemKind);
+ if (!pThis->pbCodePg || !pThis->pbDataPg)
+ {
+ Bs3TestFailedF("Bs3MemGuardedTestPageAlloc(%d) failed", enmMemKind);
+ Bs3MemPrintInfo();
+ Bs3Shutdown();
+ return Bs3TestFailedF("Bs3MemGuardedTestPageAlloc(%d) failed", enmMemKind);
+ }
+ if ( BS3_MODE_IS_64BIT_CODE(bMode)
+ && (uintptr_t)pThis->pbDataPg >= _2G)
+ return Bs3TestFailedF("pbDataPg=%p is above 2GB and not simple to address from 64-bit code", pThis->pbDataPg);
+#else
+ return Bs3TestFailed("WTF?! #1");
+#endif
+ }
+ else
+ {
+ pThis->pbCodePg = Bs3MemAlloc(enmMemKind, X86_PAGE_SIZE);
+ pThis->pbDataPg = Bs3MemAlloc(enmMemKind, X86_PAGE_SIZE);
+ if (!pThis->pbCodePg || !pThis->pbDataPg)
+ {
+ Bs3MemPrintInfo();
+ return Bs3TestFailedF("Bs3MemAlloc(%d,Pg) failed", enmMemKind);
+ }
+ }
+ pThis->uCodePgFlat = Bs3SelPtrToFlat(pThis->pbCodePg);
+ pThis->uDataPgFlat = Bs3SelPtrToFlat(pThis->pbDataPg);
+#if ARCH_BITS == 16
+ pThis->CodePgFar.sel = BS3_FP_SEG(pThis->pbCodePg);
+ pThis->CodePgFar.off = BS3_FP_OFF(pThis->pbCodePg);
+ pThis->CodePgRip = BS3_FP_OFF(pThis->pbCodePg);
+ pThis->DataPgFar.sel = BS3_FP_SEG(pThis->pbDataPg);
+ pThis->DataPgFar.off = BS3_FP_OFF(pThis->pbDataPg);
+#else
+ if (BS3_MODE_IS_RM_OR_V86(bMode))
+ {
+ *(uint32_t *)&pThis->DataPgFar = Bs3SelFlatDataToRealMode(pThis->uDataPgFlat);
+ ASMCompilerBarrier();
+ pThis->CodePgFar.off = 0;
+ pThis->CodePgFar.sel = pThis->uCodePgFlat >> 4;
+ pThis->CodePgRip = pThis->CodePgFar.off;
+ }
+ else if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ *(uint32_t *)&pThis->DataPgFar = Bs3SelFlatDataToProtFar16(pThis->uDataPgFlat);
+ ASMCompilerBarrier();
+ pThis->CodePgFar.sel = BS3_SEL_SPARE_00;
+ pThis->CodePgFar.off = 0;
+ pThis->CodePgRip = 0;
+ }
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ *(uint32_t *)&pThis->DataPgFar = Bs3SelFlatDataToProtFar16(pThis->uDataPgFlat);
+ ASMCompilerBarrier();
+ pThis->CodePgFar.sel = 0;
+ pThis->CodePgFar.off = 0;
+ pThis->CodePgRip = (uintptr_t)pThis->pbCodePg;
+ }
+ else
+ {
+ pThis->DataPgFar.off = 0;
+ pThis->DataPgFar.sel = 0;
+ pThis->CodePgFar.off = 0;
+ pThis->CodePgFar.sel = 0;
+ pThis->CodePgRip = (uintptr_t)pThis->pbCodePg;
+ }
+#endif
+ BS3CG1_DPRINTF(("pbDataPg=%p %04x:%04x pbCodePg=%p %04x:%04x\n",
+ pThis->pbDataPg, pThis->DataPgFar.sel, pThis->DataPgFar.off,
+ pThis->pbCodePg, pThis->CodePgFar.sel, pThis->CodePgFar.off));
+
+ /*
+ * Create basic context for each target ring.
+ *
+ * In protected 16-bit code we need set up code selectors that can access
+ * pbCodePg.
+ *
+ * In long mode we make sure the high 32-bits of GPRs (sans RSP) have some
+ * bits set so we can check that the implicit clearing is tested.
+ */
+ Bs3RegCtxSaveEx(&pThis->aInitialCtxs[pThis->iFirstRing], bMode, 1024 * 3);
+#if ARCH_BITS == 64
+ pThis->aInitialCtxs[pThis->iFirstRing].rax.u |= UINT64_C(0x0101010100000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rbx.u |= UINT64_C(0x0202020200000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rcx.u |= UINT64_C(0x0303030300000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rdx.u |= UINT64_C(0x0404040400000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rbp.u |= UINT64_C(0x0505050500000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rdi.u |= UINT64_C(0x0606060600000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].rsi.u |= UINT64_C(0x0707070700000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r8.u |= UINT64_C(0x0808080800000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r9.u |= UINT64_C(0x0909090900000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r10.u |= UINT64_C(0x1010101000000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r11.u |= UINT64_C(0x1111111100000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r12.u |= UINT64_C(0x1212121200000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r13.u |= UINT64_C(0x1313131300000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r14.u |= UINT64_C(0x1414141400000000);
+ pThis->aInitialCtxs[pThis->iFirstRing].r15.u |= UINT64_C(0x1515151500000000);
+#endif
+
+ if (BS3_MODE_IS_RM_OR_V86(bMode))
+ {
+ pThis->aInitialCtxs[pThis->iFirstRing].cs = pThis->CodePgFar.sel;
+ BS3_ASSERT(pThis->iFirstRing + 1 == pThis->iEndRing);
+ }
+ else if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+#if ARCH_BITS == 16
+ uintptr_t const uFlatCodePgSeg = Bs3SelPtrToFlat(BS3_FP_MAKE(BS3_FP_SEG(pThis->pbCodePg), 0));
+#else
+ uintptr_t const uFlatCodePgSeg = (uintptr_t)pThis->pbCodePg;
+#endif
+ for (iRing = pThis->iFirstRing + 1; iRing < pThis->iEndRing; iRing++)
+ {
+ Bs3MemCpy(&pThis->aInitialCtxs[iRing], &pThis->aInitialCtxs[pThis->iFirstRing], sizeof(pThis->aInitialCtxs[iRing]));
+ Bs3RegCtxConvertToRingX(&pThis->aInitialCtxs[iRing], iRing);
+ }
+ for (iRing = pThis->iFirstRing; iRing < pThis->iEndRing; iRing++)
+ {
+ pThis->aInitialCtxs[iRing].cs = BS3_SEL_SPARE_00 + iRing * 8 + iRing;
+ Bs3SelSetup16BitCode(&Bs3GdteSpare00 + iRing, uFlatCodePgSeg, iRing);
+ }
+ }
+ else
+ {
+ Bs3RegCtxSetRipCsFromCurPtr(&pThis->aInitialCtxs[pThis->iFirstRing], (FPFNBS3FAR)pThis->pbCodePg);
+ for (iRing = pThis->iFirstRing + 1; iRing < pThis->iEndRing; iRing++)
+ {
+ Bs3MemCpy(&pThis->aInitialCtxs[iRing], &pThis->aInitialCtxs[pThis->iFirstRing], sizeof(pThis->aInitialCtxs[iRing]));
+ Bs3RegCtxConvertToRingX(&pThis->aInitialCtxs[iRing], iRing);
+ }
+ }
+
+ /*
+ * Create an initial extended CPU context.
+ */
+ pExtCtx = pThis->pInitialExtCtx;
+ if ( pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE
+ || pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ {
+ pExtCtx->Ctx.x87.FCW = X86_FCW_MASK_ALL | X86_FCW_PC_64 | X86_FCW_RC_NEAREST;
+ pExtCtx->Ctx.x87.FSW = 0;
+ pExtCtx->Ctx.x87.MXCSR = X86_MXCSR_IM | X86_MXCSR_DM | X86_MXCSR_RC_NEAREST;
+ pExtCtx->Ctx.x87.MXCSR_MASK = 0;
+ for (i = 0; i < RT_ELEMENTS(pExtCtx->Ctx.x87.aRegs); i++)
+ {
+ pExtCtx->Ctx.x87.aRegs[i].au16[0] = i << 4;
+ pExtCtx->Ctx.x87.aRegs[i].au16[1] = i << 4;
+ pExtCtx->Ctx.x87.aRegs[i].au16[2] = i << 4;
+ pExtCtx->Ctx.x87.aRegs[i].au16[3] = i << 4;
+ }
+ for (i = 0; i < RT_ELEMENTS(pExtCtx->Ctx.x87.aXMM); i++)
+ {
+ pExtCtx->Ctx.x87.aXMM[i].au16[0] = i | UINT16_C(0x8f00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[1] = i | UINT16_C(0x8e00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[2] = i | UINT16_C(0x8d00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[3] = i | UINT16_C(0x8c00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[4] = i | UINT16_C(0x8b00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[5] = i | UINT16_C(0x8a00);
+ pExtCtx->Ctx.x87.aXMM[i].au16[6] = i | UINT16_C(0x8900);
+ pExtCtx->Ctx.x87.aXMM[i].au16[7] = i | UINT16_C(0x8800);
+ }
+ if (pExtCtx->fXcr0Nominal & XSAVE_C_YMM)
+ for (i = 0; i < RT_ELEMENTS(pExtCtx->Ctx.x.u.YmmHi.aYmmHi); i++)
+ {
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[0] = (i << 8) | (i << 12) | 0xff;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[1] = (i << 8) | (i << 12) | 0xfe;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[2] = (i << 8) | (i << 12) | 0xfd;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[3] = (i << 8) | (i << 12) | 0xfc;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[4] = (i << 8) | (i << 12) | 0xfb;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[5] = (i << 8) | (i << 12) | 0xfa;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[6] = (i << 8) | (i << 12) | 0xf9;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[i].au16[7] = (i << 8) | (i << 12) | 0xf8;
+ }
+
+ }
+ //else if (pExtCtx->enmMethod == BS3EXTCTXMETHOD_ANCIENT)
+ else
+ return Bs3TestFailedF("Unsupported extended CPU context method: %d", pExtCtx->enmMethod);
+
+ return true;
+}
+
+
+static uint8_t BS3_NEAR_CODE BS3_CMN_NM(Bs3Cg1WorkerInner)(PBS3CG1STATE pThis)
+{
+ uint8_t iRing;
+ unsigned iInstr;
+
+ /*
+ * Test the instructions.
+ */
+ for (iInstr = 0; iInstr < g_cBs3Cg1Instructions;
+ iInstr++,
+ pThis->pchMnemonic += pThis->fAdvanceMnemonic * pThis->cchMnemonic,
+ pThis->pabOperands += pThis->cOperands,
+ pThis->pabOpcodes += pThis->cbOpcodes)
+ {
+ uint8_t const bTestXcptExpected = BS3_MODE_IS_PAGED(pThis->bMode) ? X86_XCPT_PF : X86_XCPT_UD;
+ bool fOuterInvalidInstr = false;
+ unsigned iCpuSetup;
+
+ /*
+ * Expand the instruction information into the state.
+ * Note! 16-bit will switch to a two level test header lookup once we exceed 64KB.
+ */
+ PCBS3CG1INSTR pInstr = &g_aBs3Cg1Instructions[iInstr];
+ pThis->iInstr = iInstr;
+ pThis->pTestHdr = (PCBS3CG1TESTHDR)&g_abBs3Cg1Tests[pInstr->offTests];
+ pThis->fFlags = pInstr->fFlags;
+ pThis->enmEncoding = (BS3CG1ENC)pInstr->enmEncoding;
+ pThis->enmEncodingNonInvalid = (BS3CG1ENC)pInstr->enmEncoding;
+ pThis->enmCpuTest = (BS3CG1CPU)pInstr->enmCpuTest;
+ pThis->enmPrefixKind = (BS3CG1PFXKIND)pInstr->enmPrefixKind;
+ pThis->enmXcptType = (BS3CG1XCPTTYPE)pInstr->enmXcptType;
+ pThis->cchMnemonic = pInstr->cchMnemonic;
+ if (pThis->fAdvanceMnemonic)
+ Bs3TestSubF("%s / %.*s", pThis->pszModeShort, pThis->cchMnemonic, pThis->pchMnemonic);
+ pThis->fAdvanceMnemonic = pInstr->fAdvanceMnemonic;
+ pThis->uOpcodeMap = pInstr->uOpcodeMap;
+ pThis->cOperands = pInstr->cOperands;
+ pThis->cbOpcodes = pInstr->cbOpcodes;
+ switch (pThis->cOperands)
+ {
+ case 4: pThis->aenmOperands[3] = (BS3CG1OP)pThis->pabOperands[3];
+ case 3: pThis->aenmOperands[2] = (BS3CG1OP)pThis->pabOperands[2];
+ case 2: pThis->aenmOperands[1] = (BS3CG1OP)pThis->pabOperands[1];
+ case 1: pThis->aenmOperands[0] = (BS3CG1OP)pThis->pabOperands[0];
+ }
+ switch (pThis->cbOpcodes)
+ {
+ case 4: pThis->abOpcodes[3] = pThis->pabOpcodes[3];
+ case 3: pThis->abOpcodes[2] = pThis->pabOpcodes[2];
+ case 2: pThis->abOpcodes[1] = pThis->pabOpcodes[1];
+ case 1: pThis->abOpcodes[0] = pThis->pabOpcodes[0];
+ }
+
+ /*
+ * Check if the CPU supports the instruction.
+ */
+ pThis->fCpuSetupFirstResult = Bs3Cg1CpuSetupFirst(pThis);
+ if ( !pThis->fCpuSetupFirstResult
+ || (pThis->fFlags & (BS3CG1INSTR_F_UNUSED | BS3CG1INSTR_F_INVALID)))
+ fOuterInvalidInstr = true;
+
+ /* Switch the encoder for some of the invalid instructions on non-intel CPUs. */
+ if ( (pThis->fFlags & BS3CG1INSTR_F_INTEL_DECODES_INVALID)
+ && pThis->bCpuVendor != BS3CPUVENDOR_INTEL
+ && ( (pThis->fFlags & (BS3CG1INSTR_F_UNUSED | BS3CG1INSTR_F_INVALID))
+ || (BS3CG1_IS_64BIT_TARGET(pThis) && (pThis->fFlags & BS3CG1INSTR_F_INVALID_64BIT))
+ || fOuterInvalidInstr ) )
+ pThis->enmEncoding = Bs3Cg1CalcNoneIntelInvalidEncoding(pThis->enmEncoding);
+
+ for (iCpuSetup = 0;; iCpuSetup++)
+ {
+ unsigned iEncoding;
+ unsigned iEncodingNext;
+
+ /*
+ * Prep the operands and encoding handling.
+ */
+ Bs3Cg1SetOpSizes(pThis, pThis->bMode);
+ if (!Bs3Cg1EncodePrep(pThis))
+ break;
+
+ /*
+ * Encode the instruction in various ways and check out the test values.
+ */
+ for (iEncoding = 0;; iEncoding = iEncodingNext)
+ {
+ /*
+ * Encode the next instruction variation.
+ */
+ pThis->fInvalidEncoding = fOuterInvalidInstr;
+ iEncodingNext = Bs3Cg1EncodeNext(pThis, iEncoding);
+ if (iEncodingNext <= iEncoding)
+ break;
+ BS3CG1_DPRINTF(("\ndbg: Encoding #%u: cbCurInst=%u: %.*Rhxs fInvalidEncoding=%d\n",
+ iEncoding, pThis->cbCurInstr, pThis->cbCurInstr, pThis->abCurInstr, pThis->fInvalidEncoding));
+
+ /*
+ * Do the rings.
+ */
+ for (iRing = pThis->iFirstRing + pThis->fSameRingNotOkay; iRing < pThis->iEndRing; iRing++)
+ {
+ PCBS3CG1TESTHDR pHdr;
+
+ pThis->uCpl = iRing;
+ BS3CG1_DPRINTF(("dbg: Ring %u\n", iRing));
+
+ /*
+ * Do the tests one by one.
+ */
+ pHdr = pThis->pTestHdr;
+ for (pThis->iTest = 0;; pThis->iTest++)
+ {
+ if (Bs3Cg1RunSelector(pThis, pHdr))
+ {
+ /* Okay, set up the execution context. */
+ unsigned offCode;
+ uint8_t BS3_FAR *pbCode;
+
+ Bs3MemCpy(&pThis->Ctx, &pThis->aInitialCtxs[iRing], sizeof(pThis->Ctx));
+ if (pThis->fWorkExtCtx)
+ Bs3ExtCtxCopy(pThis->pExtCtx, pThis->pInitialExtCtx);
+ if (BS3_MODE_IS_PAGED(pThis->bMode))
+ {
+ offCode = X86_PAGE_SIZE - pThis->cbCurInstr;
+ pbCode = &pThis->pbCodePg[offCode];
+ //if (iEncoding > 0) { pbCode[-1] = 0xf4; offCode--; }
+ }
+ else
+ {
+ pbCode = pThis->pbCodePg;
+ pbCode[pThis->cbCurInstr] = 0x0f; /* UD2 */
+ pbCode[pThis->cbCurInstr + 1] = 0x0b;
+ offCode = 0;
+ }
+ pThis->Ctx.rip.u = pThis->CodePgRip + offCode;
+ Bs3MemCpy(pbCode, pThis->abCurInstr, pThis->cbCurInstr);
+
+ if (Bs3Cg1RunContextModifier(pThis, &pThis->Ctx, pHdr, pHdr->cbSelector, pHdr->cbInput, NULL, pbCode))
+ {
+ /* Run the instruction. */
+ BS3CG1_DPRINTF(("dbg: Running test #%u\n", pThis->iTest));
+ //Bs3RegCtxPrint(&pThis->Ctx);
+ if (pThis->fWorkExtCtx)
+ Bs3ExtCtxRestore(pThis->pExtCtx);
+ Bs3TrapSetJmpAndRestore(&pThis->Ctx, &pThis->TrapFrame);
+ if (pThis->fWorkExtCtx)
+ Bs3ExtCtxSave(pThis->pResultExtCtx);
+ BS3CG1_DPRINTF(("dbg: bXcpt=%#x rip=%RX64 -> %RX64\n",
+ pThis->TrapFrame.bXcpt, pThis->Ctx.rip.u, pThis->TrapFrame.Ctx.rip.u));
+
+ /*
+ * Apply the output modification program to the context.
+ */
+ pThis->Ctx.rflags.u32 &= ~X86_EFL_RF;
+ pThis->Ctx.rflags.u32 |= pThis->TrapFrame.Ctx.rflags.u32 & X86_EFL_RF;
+ pThis->bValueXcpt = UINT8_MAX; //???
+ if ( pThis->fInvalidEncoding
+ || pThis->bAlignmentXcpt != UINT8_MAX
+ || pThis->bValueXcpt != UINT8_MAX
+ || Bs3Cg1RunContextModifier(pThis, &pThis->Ctx, pHdr,
+ pHdr->cbSelector + pHdr->cbInput, pHdr->cbOutput,
+ &pThis->TrapFrame.Ctx, NULL /*pbCode*/))
+ Bs3Cg1CheckResult(pThis, bTestXcptExpected, false /*fInvalidEncodingPgFault*/, iEncoding);
+ else
+ {
+ Bs3TestPrintf("Bs3Cg1RunContextModifier(out): iEncoding=%u iTest=%RU32 iInstr=%u %.*s\n",
+ iEncoding, pThis->iTest, pThis->iInstr, pThis->cchMnemonic, pThis->pchMnemonic);
+ ASMHalt();
+ }
+
+ /*
+ * If this is an invalid encoding or instruction, check that we
+ * get a page fault when shortening it by one byte.
+ * (Since we didn't execute the output context modifier, we don't
+ * need to re-initialize the start context.)
+ */
+ if ( pThis->fInvalidEncoding
+ && BS3_MODE_IS_PAGED(pThis->bMode)
+ && pThis->cbCurInstr)
+ {
+ pbCode += 1;
+ offCode += 1;
+ pThis->Ctx.rip.u = pThis->CodePgRip + offCode;
+ Bs3MemCpy(pbCode, pThis->abCurInstr, pThis->cbCurInstr - 1);
+
+ /* Run the instruction. */
+ BS3CG1_DPRINTF(("dbg: Running test #%u (cut short #PF)\n", pThis->iTest));
+ //Bs3RegCtxPrint(&pThis->Ctx);
+ if (pThis->fWorkExtCtx)
+ Bs3ExtCtxRestore(pThis->pExtCtx);
+ Bs3TrapSetJmpAndRestore(&pThis->Ctx, &pThis->TrapFrame);
+ if (pThis->fWorkExtCtx)
+ Bs3ExtCtxSave(pThis->pResultExtCtx);
+ BS3CG1_DPRINTF(("dbg: bXcpt=%#x rip=%RX64 -> %RX64 (cut short #PF)\n",
+ pThis->TrapFrame.bXcpt, pThis->Ctx.rip.u, pThis->TrapFrame.Ctx.rip.u));
+
+ /* Check it */
+ pThis->Ctx.rflags.u32 &= ~X86_EFL_RF;
+ pThis->Ctx.rflags.u32 |= pThis->TrapFrame.Ctx.rflags.u32 & X86_EFL_RF;
+ Bs3Cg1CheckResult(pThis, X86_XCPT_PF, true /*fInvalidEncodingPgFault*/, iEncoding);
+ }
+ }
+ else
+ {
+ Bs3TestPrintf("Bs3Cg1RunContextModifier(in): iEncoding=%u iTest=%u iInstr=%RU32 %.*s\n",
+ iEncoding, pThis->iTest, pThis->iInstr, pThis->cchMnemonic, pThis->pchMnemonic);
+ ASMHalt();
+ }
+ }
+ else
+ BS3CG1_DPRINTF(("dbg: Skipping #%u\n", pThis->iTest));
+
+ /* advance */
+ if (pHdr->fLast)
+ {
+ BS3CG1_DPRINTF(("dbg: Last\n\n"));
+ break;
+ }
+ pHdr = (PCBS3CG1TESTHDR)((uint8_t BS3_FAR *)(pHdr + 1) + pHdr->cbInput + pHdr->cbOutput + pHdr->cbSelector);
+ }
+ }
+ }
+
+ /*
+ * Clean up (segment registers, etc) and get the next CPU config.
+ */
+ Bs3Cg1EncodeCleanup(pThis);
+ if (!Bs3Cg1CpuSetupNext(pThis, iCpuSetup, &fOuterInvalidInstr))
+ break;
+ if (pThis->fFlags & (BS3CG1INSTR_F_UNUSED | BS3CG1INSTR_F_INVALID))
+ fOuterInvalidInstr = true;
+ }
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(Bs3Cg1Worker)(uint8_t bMode)
+{
+ uint8_t bRet = 1;
+ BS3CG1STATE This;
+
+#if 0
+ /* (for debugging) */
+ if (bMode != BS3_MODE_LM64)
+ return BS3TESTDOMODE_SKIPPED;
+#endif
+
+ if (BS3_CMN_NM(Bs3Cg1Init)(&This, bMode))
+ {
+ bRet = BS3_CMN_NM(Bs3Cg1WorkerInner)(&This);
+ Bs3TestSubDone();
+ }
+ Bs3Cg1Destroy(&This);
+
+#if 0
+ /* (for debugging) */
+ if (bMode == BS3_MODE_PPV86)
+ {
+ Bs3TestTerm();
+ Bs3Shutdown();
+ }
+#endif
+ return bRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.c
new file mode 100644
index 00000000..7361dfc7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.c
@@ -0,0 +1,69 @@
+/* $Id: bs3-cpu-generated-1.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-generated-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include "bs3-cpu-generated-1.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3TESTMODEBYMAX_PROTOTYPES_CMN(Bs3Cg1Worker);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEBYMAXENTRY g_aModeTest[] =
+{
+ BS3TESTMODEBYMAXENTRY_CMN(NULL, Bs3Cg1Worker),
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-cpu-generated-1");
+
+ Bs3TestDoModesByMax_rm(g_aModeTest, RT_ELEMENTS(g_aModeTest));
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.h b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.h
new file mode 100644
index 00000000..a6dce53d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1.h
@@ -0,0 +1,817 @@
+/* $Id: bs3-cpu-generated-1.h $ */
+/** @file
+ * BS3Kit - bs3-cpu-generated-1, common header file.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_bootsectors_bs3_cpu_generated_1_h
+#define VBOX_INCLUDED_SRC_bootsectors_bs3_cpu_generated_1_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <bs3kit.h>
+#include <iprt/assert.h>
+
+
+/**
+ * Operand details.
+ *
+ * Currently simply using the encoding from the reference manuals.
+ */
+typedef enum BS3CG1OP
+{
+ BS3CG1OP_INVALID = 0,
+
+ BS3CG1OP_Eb,
+ BS3CG1OP_Ed,
+ BS3CG1OP_Ed_WO,
+ BS3CG1OP_Eq,
+ BS3CG1OP_Eq_WO,
+ BS3CG1OP_Ev,
+ BS3CG1OP_Qq,
+ BS3CG1OP_Qq_WO,
+ BS3CG1OP_Wss,
+ BS3CG1OP_Wss_WO,
+ BS3CG1OP_Wsd,
+ BS3CG1OP_Wsd_WO,
+ BS3CG1OP_Wps,
+ BS3CG1OP_Wps_WO,
+ BS3CG1OP_Wpd,
+ BS3CG1OP_Wpd_WO,
+ BS3CG1OP_Wdq,
+ BS3CG1OP_Wdq_WO,
+ BS3CG1OP_Wq,
+ BS3CG1OP_Wq_WO,
+ BS3CG1OP_WqZxReg_WO,
+ BS3CG1OP_Wx,
+ BS3CG1OP_Wx_WO,
+
+ BS3CG1OP_Gb,
+ BS3CG1OP_Gv,
+ BS3CG1OP_Gv_RO,
+ BS3CG1OP_HssHi,
+ BS3CG1OP_HsdHi,
+ BS3CG1OP_HqHi,
+ BS3CG1OP_Nq,
+ BS3CG1OP_Pd,
+ BS3CG1OP_PdZx_WO,
+ BS3CG1OP_Pq,
+ BS3CG1OP_Pq_WO,
+ BS3CG1OP_Uq,
+ BS3CG1OP_UqHi,
+ BS3CG1OP_Uss,
+ BS3CG1OP_Uss_WO,
+ BS3CG1OP_Usd,
+ BS3CG1OP_Usd_WO,
+ BS3CG1OP_Vd,
+ BS3CG1OP_Vd_WO,
+ BS3CG1OP_VdZx_WO,
+ BS3CG1OP_Vss,
+ BS3CG1OP_Vss_WO,
+ BS3CG1OP_VssZx_WO,
+ BS3CG1OP_Vsd,
+ BS3CG1OP_Vsd_WO,
+ BS3CG1OP_VsdZx_WO,
+ BS3CG1OP_Vps,
+ BS3CG1OP_Vps_WO,
+ BS3CG1OP_Vpd,
+ BS3CG1OP_Vpd_WO,
+ BS3CG1OP_Vq,
+ BS3CG1OP_Vq_WO,
+ BS3CG1OP_Vdq,
+ BS3CG1OP_Vdq_WO,
+ BS3CG1OP_VqHi,
+ BS3CG1OP_VqHi_WO,
+ BS3CG1OP_VqZx_WO,
+ BS3CG1OP_Vx,
+ BS3CG1OP_Vx_WO,
+
+ BS3CG1OP_Ib,
+ BS3CG1OP_Iz,
+
+ BS3CG1OP_AL,
+ BS3CG1OP_rAX,
+
+ BS3CG1OP_Ma,
+ BS3CG1OP_Mb_RO,
+ BS3CG1OP_Md,
+ BS3CG1OP_Md_RO,
+ BS3CG1OP_Md_WO,
+ BS3CG1OP_Mdq,
+ BS3CG1OP_Mdq_WO,
+ BS3CG1OP_Mq,
+ BS3CG1OP_Mq_WO,
+ BS3CG1OP_Mps_WO,
+ BS3CG1OP_Mpd_WO,
+ BS3CG1OP_Mx,
+ BS3CG1OP_Mx_WO,
+
+ BS3CG1OP_END
+} BS3CG1OP;
+/** Pointer to a const operand enum. */
+typedef const BS3CG1OP BS3_FAR *PCBS3CG1OP;
+
+
+/**
+ * Instruction encoding format.
+ *
+ * This duplicates some of the info in the operand array, however it makes it
+ * easier to figure out encoding variations.
+ */
+typedef enum BS3CG1ENC
+{
+ BS3CG1ENC_INVALID = 0,
+
+ BS3CG1ENC_MODRM_Eb_Gb,
+ BS3CG1ENC_MODRM_Ev_Gv,
+ BS3CG1ENC_MODRM_Ed_WO_Pd_WZ,
+ BS3CG1ENC_MODRM_Eq_WO_Pq_WNZ,
+ BS3CG1ENC_MODRM_Ed_WO_Vd_WZ,
+ BS3CG1ENC_MODRM_Eq_WO_Vq_WNZ,
+ BS3CG1ENC_MODRM_Pq_WO_Qq,
+ BS3CG1ENC_MODRM_Wss_WO_Vss,
+ BS3CG1ENC_MODRM_Wsd_WO_Vsd,
+ BS3CG1ENC_MODRM_Wps_WO_Vps,
+ BS3CG1ENC_MODRM_Wpd_WO_Vpd,
+ BS3CG1ENC_MODRM_WqZxReg_WO_Vq,
+
+ BS3CG1ENC_MODRM_Gb_Eb,
+ BS3CG1ENC_MODRM_Gv_Ev,
+ BS3CG1ENC_MODRM_Gv_RO_Ma, /**< bound instruction */
+ BS3CG1ENC_MODRM_Pq_WO_Uq,
+ BS3CG1ENC_MODRM_PdZx_WO_Ed_WZ,
+ BS3CG1ENC_MODRM_Pq_WO_Eq_WNZ,
+ BS3CG1ENC_MODRM_VdZx_WO_Ed_WZ,
+ BS3CG1ENC_MODRM_Vq_Mq,
+ BS3CG1ENC_MODRM_Vq_WO_UqHi,
+ BS3CG1ENC_MODRM_Vq_WO_Mq,
+ BS3CG1ENC_MODRM_VqHi_WO_Uq,
+ BS3CG1ENC_MODRM_VqHi_WO_Mq,
+ BS3CG1ENC_MODRM_VqZx_WO_Eq_WNZ,
+ BS3CG1ENC_MODRM_Vdq_WO_Mdq,
+ BS3CG1ENC_MODRM_Vdq_WO_Wdq,
+ BS3CG1ENC_MODRM_Vpd_WO_Wpd,
+ BS3CG1ENC_MODRM_Vps_WO_Wps,
+ BS3CG1ENC_MODRM_VssZx_WO_Wss,
+ BS3CG1ENC_MODRM_VsdZx_WO_Wsd,
+ BS3CG1ENC_MODRM_VqZx_WO_Wq,
+ BS3CG1ENC_MODRM_VqZx_WO_Nq,
+ BS3CG1ENC_MODRM_Mb_RO,
+ BS3CG1ENC_MODRM_Md_RO,
+ BS3CG1ENC_MODRM_Md_WO,
+ BS3CG1ENC_MODRM_Mdq_WO_Vdq,
+ BS3CG1ENC_MODRM_Mq_WO_Pq,
+ BS3CG1ENC_MODRM_Mq_WO_Vq,
+ BS3CG1ENC_MODRM_Mq_WO_VqHi,
+ BS3CG1ENC_MODRM_Mps_WO_Vps,
+ BS3CG1ENC_MODRM_Mpd_WO_Vpd,
+
+ BS3CG1ENC_VEX_MODRM_Vd_WO_Ed_WZ,
+ BS3CG1ENC_VEX_MODRM_Vps_WO_Wps,
+ BS3CG1ENC_VEX_MODRM_Vpd_WO_Wpd,
+ BS3CG1ENC_VEX_MODRM_Vss_WO_HssHi_Uss,
+ BS3CG1ENC_VEX_MODRM_Vsd_WO_HsdHi_Usd,
+ BS3CG1ENC_VEX_MODRM_Vq_WO_Eq_WNZ,
+ BS3CG1ENC_VEX_MODRM_Vq_WO_HqHi_UqHi,
+ BS3CG1ENC_VEX_MODRM_Vq_WO_HqHi_Mq,
+ BS3CG1ENC_VEX_MODRM_Vq_WO_Wq,
+ BS3CG1ENC_VEX_MODRM_VssZx_WO_Md,
+ BS3CG1ENC_VEX_MODRM_VsdZx_WO_Mq,
+ BS3CG1ENC_VEX_MODRM_Vx_WO_Mx_L0,
+ BS3CG1ENC_VEX_MODRM_Vx_WO_Mx_L1,
+ BS3CG1ENC_VEX_MODRM_Vx_WO_Wx,
+ BS3CG1ENC_VEX_MODRM_Ed_WO_Vd_WZ,
+ BS3CG1ENC_VEX_MODRM_Eq_WO_Vq_WNZ,
+ BS3CG1ENC_VEX_MODRM_Md_WO,
+ BS3CG1ENC_VEX_MODRM_Mq_WO_Vq,
+ BS3CG1ENC_VEX_MODRM_Md_WO_Vss,
+ BS3CG1ENC_VEX_MODRM_Mq_WO_Vsd,
+ BS3CG1ENC_VEX_MODRM_Mps_WO_Vps,
+ BS3CG1ENC_VEX_MODRM_Mpd_WO_Vpd,
+ BS3CG1ENC_VEX_MODRM_Mx_WO_Vx,
+ BS3CG1ENC_VEX_MODRM_Uss_WO_HssHi_Vss,
+ BS3CG1ENC_VEX_MODRM_Usd_WO_HsdHi_Vsd,
+ BS3CG1ENC_VEX_MODRM_Wps_WO_Vps,
+ BS3CG1ENC_VEX_MODRM_Wpd_WO_Vpd,
+ BS3CG1ENC_VEX_MODRM_Wq_WO_Vq,
+ BS3CG1ENC_VEX_MODRM_Wx_WO_Vx,
+
+ BS3CG1ENC_FIXED,
+ BS3CG1ENC_FIXED_AL_Ib,
+ BS3CG1ENC_FIXED_rAX_Iz,
+
+
+ BS3CG1ENC_MODRM_MOD_EQ_3, /**< Unused or invalid instruction. */
+ BS3CG1ENC_MODRM_MOD_NE_3, /**< Unused or invalid instruction. */
+ //BS3CG1ENC_VEX_FIXED, /**< Unused or invalid instruction. */
+ BS3CG1ENC_VEX_MODRM_MOD_EQ_3, /**< Unused or invalid instruction. */
+ BS3CG1ENC_VEX_MODRM_MOD_NE_3, /**< Unused or invalid instruction. */
+ BS3CG1ENC_VEX_MODRM, /**< Unused or invalid instruction. */
+
+ BS3CG1ENC_END
+} BS3CG1ENC;
+
+
+/**
+ * Prefix sensitivitiy kind.
+ */
+typedef enum BS3CG1PFXKIND
+{
+ BS3CG1PFXKIND_INVALID = 0,
+
+ BS3CG1PFXKIND_NO_F2_F3_66, /**< No 66, F2 or F3 prefixes allowed as that would alter the meaning. */
+ BS3CG1PFXKIND_REQ_F2, /**< Requires F2 (REPNE) prefix as part of the instr encoding. */
+ BS3CG1PFXKIND_REQ_F3, /**< Requires F3 (REPE) prefix as part of the instr encoding. */
+ BS3CG1PFXKIND_REQ_66, /**< Requires 66 (OP SIZE) prefix as part of the instr encoding. */
+
+ /** @todo more work to be done here... */
+ BS3CG1PFXKIND_MODRM,
+ BS3CG1PFXKIND_MODRM_NO_OP_SIZES,
+
+ BS3CG1PFXKIND_END
+} BS3CG1PFXKIND;
+
+/**
+ * CPU selection or CPU ID.
+ */
+typedef enum BS3CG1CPU
+{
+ /** Works with an CPU. */
+ BS3CG1CPU_ANY = 0,
+ BS3CG1CPU_GE_80186,
+ BS3CG1CPU_GE_80286,
+ BS3CG1CPU_GE_80386,
+ BS3CG1CPU_GE_80486,
+ BS3CG1CPU_GE_Pentium,
+
+ BS3CG1CPU_MMX,
+ BS3CG1CPU_SSE,
+ BS3CG1CPU_SSE2,
+ BS3CG1CPU_SSE3,
+ BS3CG1CPU_SSE4_1,
+ BS3CG1CPU_AVX,
+ BS3CG1CPU_AVX2,
+ BS3CG1CPU_CLFSH,
+ BS3CG1CPU_CLFLUSHOPT,
+
+ BS3CG1CPU_END
+} BS3CG1CPU;
+
+
+/**
+ * SSE & AVX exception types.
+ */
+typedef enum BS3CG1XCPTTYPE
+{
+ BS3CG1XCPTTYPE_NONE = 0,
+ /* SSE: */
+ BS3CG1XCPTTYPE_1,
+ BS3CG1XCPTTYPE_2,
+ BS3CG1XCPTTYPE_3,
+ BS3CG1XCPTTYPE_4,
+ BS3CG1XCPTTYPE_4UA,
+ BS3CG1XCPTTYPE_5,
+ BS3CG1XCPTTYPE_5LZ,
+ BS3CG1XCPTTYPE_6,
+ BS3CG1XCPTTYPE_7,
+ BS3CG1XCPTTYPE_7LZ,
+ BS3CG1XCPTTYPE_8,
+ BS3CG1XCPTTYPE_11,
+ BS3CG1XCPTTYPE_12,
+ /* EVEX: */
+ BS3CG1XCPTTYPE_E1,
+ BS3CG1XCPTTYPE_E1NF,
+ BS3CG1XCPTTYPE_E2,
+ BS3CG1XCPTTYPE_E3,
+ BS3CG1XCPTTYPE_E3NF,
+ BS3CG1XCPTTYPE_E4,
+ BS3CG1XCPTTYPE_E4NF,
+ BS3CG1XCPTTYPE_E5,
+ BS3CG1XCPTTYPE_E5NF,
+ BS3CG1XCPTTYPE_E6,
+ BS3CG1XCPTTYPE_E6NF,
+ BS3CG1XCPTTYPE_E7NF,
+ BS3CG1XCPTTYPE_E9,
+ BS3CG1XCPTTYPE_E9NF,
+ BS3CG1XCPTTYPE_E10,
+ BS3CG1XCPTTYPE_E11,
+ BS3CG1XCPTTYPE_E12,
+ BS3CG1XCPTTYPE_E12NF,
+ BS3CG1XCPTTYPE_END
+} BS3CG1XCPTTYPE;
+AssertCompile(BS3CG1XCPTTYPE_END <= 32);
+
+
+/**
+ * Generated instruction info.
+ */
+typedef struct BS3CG1INSTR
+{
+ /** The opcode size. */
+ uint32_t cbOpcodes : 2;
+ /** The number of operands. */
+ uint32_t cOperands : 2;
+ /** The length of the mnemonic. */
+ uint32_t cchMnemonic : 4;
+ /** Whether to advance the mnemonic array pointer. */
+ uint32_t fAdvanceMnemonic : 1;
+ /** Offset into g_abBs3Cg1Tests of the first test. */
+ uint32_t offTests : 23;
+ /** BS3CG1ENC values. */
+ uint32_t enmEncoding : 10;
+ /** The VEX, EVEX or XOP opcode map number (VEX.mmmm). */
+ uint32_t uOpcodeMap : 4;
+ /** BS3CG1PFXKIND values. */
+ uint32_t enmPrefixKind : 4;
+ /** CPU test / CPU ID bit test (BS3CG1CPU). */
+ uint32_t enmCpuTest : 6;
+ /** Exception type (BS3CG1XCPTTYPE) */
+ uint32_t enmXcptType : 5;
+ /** Currently unused bits. */
+ uint32_t uUnused : 3;
+ /** BS3CG1INSTR_F_XXX. */
+ uint32_t fFlags;
+} BS3CG1INSTR;
+AssertCompileSize(BS3CG1INSTR, 12);
+/** Pointer to a const instruction. */
+typedef BS3CG1INSTR const BS3_FAR *PCBS3CG1INSTR;
+
+
+/** @name BS3CG1INSTR_F_XXX
+ * @{ */
+/** Defaults to SS rather than DS. */
+#define BS3CG1INSTR_F_DEF_SS UINT32_C(0x00000001)
+/** Invalid instruction in 64-bit mode. */
+#define BS3CG1INSTR_F_INVALID_64BIT UINT32_C(0x00000002)
+/** Unused instruction. */
+#define BS3CG1INSTR_F_UNUSED UINT32_C(0x00000004)
+/** Invalid instruction. */
+#define BS3CG1INSTR_F_INVALID UINT32_C(0x00000008)
+/** Only intel does full ModR/M(, ++) decoding for invalid instruction.
+ * Always used with BS3CG1INSTR_F_INVALID or BS3CG1INSTR_F_UNUSED. */
+#define BS3CG1INSTR_F_INTEL_DECODES_INVALID UINT32_C(0x00000010)
+/** VEX.L must be zero (IEMOPHINT_VEX_L_ZERO). */
+#define BS3CG1INSTR_F_VEX_L_ZERO UINT32_C(0x00000020)
+/** VEX.L is ignored (IEMOPHINT_VEX_L_IGNORED). */
+#define BS3CG1INSTR_F_VEX_L_IGNORED UINT32_C(0x00000040)
+/** @} */
+
+
+/**
+ * Test header.
+ */
+typedef struct BS3CG1TESTHDR
+{
+ /** The size of the selector program in bytes.
+ * This is also the offset of the input context modification program. */
+ uint32_t cbSelector : 8;
+ /** The size of the input context modification program in bytes.
+ * This immediately follows the selector program. */
+ uint32_t cbInput : 12;
+ /** The size of the output context modification program in bytes.
+ * This immediately follows the input context modification program. The
+ * program takes the result of the input program as starting point. */
+ uint32_t cbOutput : 11;
+ /** Indicates whether this is the last test or not. */
+ uint32_t fLast : 1;
+} BS3CG1TESTHDR;
+AssertCompileSize(BS3CG1TESTHDR, 4);
+/** Pointer to a const test header. */
+typedef BS3CG1TESTHDR const BS3_FAR *PCBS3CG1TESTHDR;
+
+/** @name Opcode format for the BS3CG1 context modifier.
+ *
+ * Used by both the input and output context programs.
+ *
+ * The most common operations are encoded as a single byte opcode followed by
+ * one or more immediate bytes with data.
+ *
+ * @{ */
+#define BS3CG1_CTXOP_SIZE_MASK UINT8_C(0x07)
+#define BS3CG1_CTXOP_1_BYTE UINT8_C(0x00)
+#define BS3CG1_CTXOP_2_BYTES UINT8_C(0x01)
+#define BS3CG1_CTXOP_4_BYTES UINT8_C(0x02)
+#define BS3CG1_CTXOP_8_BYTES UINT8_C(0x03)
+#define BS3CG1_CTXOP_16_BYTES UINT8_C(0x04)
+#define BS3CG1_CTXOP_32_BYTES UINT8_C(0x05)
+#define BS3CG1_CTXOP_12_BYTES UINT8_C(0x06)
+#define BS3CG1_CTXOP_SIZE_ESC UINT8_C(0x07) /**< Separate byte encoding the value size following any destination escape byte. */
+
+#define BS3CG1_CTXOP_DST_MASK UINT8_C(0x18)
+#define BS3CG1_CTXOP_OP1 UINT8_C(0x00)
+#define BS3CG1_CTXOP_OP2 UINT8_C(0x08)
+#define BS3CG1_CTXOP_EFL UINT8_C(0x10)
+#define BS3CG1_CTXOP_DST_ESC UINT8_C(0x18) /**< Separate byte giving the destination follows immediately. */
+
+#define BS3CG1_CTXOP_SIGN_EXT UINT8_C(0x20) /**< Whether to sign-extend (set) the immediate value. */
+
+#define BS3CG1_CTXOP_OPERATOR_MASK UINT8_C(0xc0)
+#define BS3CG1_CTXOP_ASSIGN UINT8_C(0x00) /**< Simple assignment operator (=) */
+#define BS3CG1_CTXOP_OR UINT8_C(0x40) /**< OR assignment operator (|=). */
+#define BS3CG1_CTXOP_AND UINT8_C(0x80) /**< AND assignment operator (&=). */
+#define BS3CG1_CTXOP_AND_INV UINT8_C(0xc0) /**< AND assignment operator of the inverted value (&~=). */
+/** @} */
+
+/**
+ * Escaped destination values
+ *
+ * These are just uppercased versions of TestInOut.kdFields, where dots are
+ * replaced by underscores.
+ */
+typedef enum BS3CG1DST
+{
+ BS3CG1DST_INVALID = 0,
+ /* Operands. */
+ BS3CG1DST_OP1,
+ BS3CG1DST_OP2,
+ BS3CG1DST_OP3,
+ BS3CG1DST_OP4,
+ /* Flags. */
+ BS3CG1DST_EFL,
+ BS3CG1DST_EFL_UNDEF, /**< Special field only valid in output context modifiers: EFLAGS |= Value & Ouput.EFLAGS; */
+ /* 8-bit GPRs. */
+ BS3CG1DST_AL,
+ BS3CG1DST_CL,
+ BS3CG1DST_DL,
+ BS3CG1DST_BL,
+ BS3CG1DST_AH,
+ BS3CG1DST_CH,
+ BS3CG1DST_DH,
+ BS3CG1DST_BH,
+ BS3CG1DST_SPL,
+ BS3CG1DST_BPL,
+ BS3CG1DST_SIL,
+ BS3CG1DST_DIL,
+ BS3CG1DST_R8L,
+ BS3CG1DST_R9L,
+ BS3CG1DST_R10L,
+ BS3CG1DST_R11L,
+ BS3CG1DST_R12L,
+ BS3CG1DST_R13L,
+ BS3CG1DST_R14L,
+ BS3CG1DST_R15L,
+ /* 16-bit GPRs. */
+ BS3CG1DST_AX,
+ BS3CG1DST_CX,
+ BS3CG1DST_DX,
+ BS3CG1DST_BX,
+ BS3CG1DST_SP,
+ BS3CG1DST_BP,
+ BS3CG1DST_SI,
+ BS3CG1DST_DI,
+ BS3CG1DST_R8W,
+ BS3CG1DST_R9W,
+ BS3CG1DST_R10W,
+ BS3CG1DST_R11W,
+ BS3CG1DST_R12W,
+ BS3CG1DST_R13W,
+ BS3CG1DST_R14W,
+ BS3CG1DST_R15W,
+ /* 32-bit GPRs. */
+ BS3CG1DST_EAX,
+ BS3CG1DST_ECX,
+ BS3CG1DST_EDX,
+ BS3CG1DST_EBX,
+ BS3CG1DST_ESP,
+ BS3CG1DST_EBP,
+ BS3CG1DST_ESI,
+ BS3CG1DST_EDI,
+ BS3CG1DST_R8D,
+ BS3CG1DST_R9D,
+ BS3CG1DST_R10D,
+ BS3CG1DST_R11D,
+ BS3CG1DST_R12D,
+ BS3CG1DST_R13D,
+ BS3CG1DST_R14D,
+ BS3CG1DST_R15D,
+ /* 64-bit GPRs. */
+ BS3CG1DST_RAX,
+ BS3CG1DST_RCX,
+ BS3CG1DST_RDX,
+ BS3CG1DST_RBX,
+ BS3CG1DST_RSP,
+ BS3CG1DST_RBP,
+ BS3CG1DST_RSI,
+ BS3CG1DST_RDI,
+ BS3CG1DST_R8,
+ BS3CG1DST_R9,
+ BS3CG1DST_R10,
+ BS3CG1DST_R11,
+ BS3CG1DST_R12,
+ BS3CG1DST_R13,
+ BS3CG1DST_R14,
+ BS3CG1DST_R15,
+ /* 16-bit, 32-bit or 64-bit registers according to operand size. */
+ BS3CG1DST_OZ_RAX,
+ BS3CG1DST_OZ_RCX,
+ BS3CG1DST_OZ_RDX,
+ BS3CG1DST_OZ_RBX,
+ BS3CG1DST_OZ_RSP,
+ BS3CG1DST_OZ_RBP,
+ BS3CG1DST_OZ_RSI,
+ BS3CG1DST_OZ_RDI,
+ BS3CG1DST_OZ_R8,
+ BS3CG1DST_OZ_R9,
+ BS3CG1DST_OZ_R10,
+ BS3CG1DST_OZ_R11,
+ BS3CG1DST_OZ_R12,
+ BS3CG1DST_OZ_R13,
+ BS3CG1DST_OZ_R14,
+ BS3CG1DST_OZ_R15,
+
+ /* Control registers.*/
+ BS3CG1DST_CR0,
+ BS3CG1DST_CR4,
+ BS3CG1DST_XCR0,
+
+ /* FPU registers. */
+ BS3CG1DST_FPU_FIRST,
+ BS3CG1DST_FCW = BS3CG1DST_FPU_FIRST,
+ BS3CG1DST_FSW,
+ BS3CG1DST_FTW,
+ BS3CG1DST_FOP,
+ BS3CG1DST_FPUIP,
+ BS3CG1DST_FPUCS,
+ BS3CG1DST_FPUDP,
+ BS3CG1DST_FPUDS,
+ BS3CG1DST_MXCSR,
+ BS3CG1DST_ST0,
+ BS3CG1DST_ST1,
+ BS3CG1DST_ST2,
+ BS3CG1DST_ST3,
+ BS3CG1DST_ST4,
+ BS3CG1DST_ST5,
+ BS3CG1DST_ST6,
+ BS3CG1DST_ST7,
+ /* MMX registers. */
+ BS3CG1DST_MM0,
+ BS3CG1DST_MM1,
+ BS3CG1DST_MM2,
+ BS3CG1DST_MM3,
+ BS3CG1DST_MM4,
+ BS3CG1DST_MM5,
+ BS3CG1DST_MM6,
+ BS3CG1DST_MM7,
+ BS3CG1DST_MM0_LO_ZX,
+ BS3CG1DST_MM1_LO_ZX,
+ BS3CG1DST_MM2_LO_ZX,
+ BS3CG1DST_MM3_LO_ZX,
+ BS3CG1DST_MM4_LO_ZX,
+ BS3CG1DST_MM5_LO_ZX,
+ BS3CG1DST_MM6_LO_ZX,
+ BS3CG1DST_MM7_LO_ZX,
+ /* SSE registers. */
+ BS3CG1DST_XMM0,
+ BS3CG1DST_XMM1,
+ BS3CG1DST_XMM2,
+ BS3CG1DST_XMM3,
+ BS3CG1DST_XMM4,
+ BS3CG1DST_XMM5,
+ BS3CG1DST_XMM6,
+ BS3CG1DST_XMM7,
+ BS3CG1DST_XMM8,
+ BS3CG1DST_XMM9,
+ BS3CG1DST_XMM10,
+ BS3CG1DST_XMM11,
+ BS3CG1DST_XMM12,
+ BS3CG1DST_XMM13,
+ BS3CG1DST_XMM14,
+ BS3CG1DST_XMM15,
+ BS3CG1DST_XMM0_LO,
+ BS3CG1DST_XMM1_LO,
+ BS3CG1DST_XMM2_LO,
+ BS3CG1DST_XMM3_LO,
+ BS3CG1DST_XMM4_LO,
+ BS3CG1DST_XMM5_LO,
+ BS3CG1DST_XMM6_LO,
+ BS3CG1DST_XMM7_LO,
+ BS3CG1DST_XMM8_LO,
+ BS3CG1DST_XMM9_LO,
+ BS3CG1DST_XMM10_LO,
+ BS3CG1DST_XMM11_LO,
+ BS3CG1DST_XMM12_LO,
+ BS3CG1DST_XMM13_LO,
+ BS3CG1DST_XMM14_LO,
+ BS3CG1DST_XMM15_LO,
+ BS3CG1DST_XMM0_HI,
+ BS3CG1DST_XMM1_HI,
+ BS3CG1DST_XMM2_HI,
+ BS3CG1DST_XMM3_HI,
+ BS3CG1DST_XMM4_HI,
+ BS3CG1DST_XMM5_HI,
+ BS3CG1DST_XMM6_HI,
+ BS3CG1DST_XMM7_HI,
+ BS3CG1DST_XMM8_HI,
+ BS3CG1DST_XMM9_HI,
+ BS3CG1DST_XMM10_HI,
+ BS3CG1DST_XMM11_HI,
+ BS3CG1DST_XMM12_HI,
+ BS3CG1DST_XMM13_HI,
+ BS3CG1DST_XMM14_HI,
+ BS3CG1DST_XMM15_HI,
+ BS3CG1DST_XMM0_LO_ZX,
+ BS3CG1DST_XMM1_LO_ZX,
+ BS3CG1DST_XMM2_LO_ZX,
+ BS3CG1DST_XMM3_LO_ZX,
+ BS3CG1DST_XMM4_LO_ZX,
+ BS3CG1DST_XMM5_LO_ZX,
+ BS3CG1DST_XMM6_LO_ZX,
+ BS3CG1DST_XMM7_LO_ZX,
+ BS3CG1DST_XMM8_LO_ZX,
+ BS3CG1DST_XMM9_LO_ZX,
+ BS3CG1DST_XMM10_LO_ZX,
+ BS3CG1DST_XMM11_LO_ZX,
+ BS3CG1DST_XMM12_LO_ZX,
+ BS3CG1DST_XMM13_LO_ZX,
+ BS3CG1DST_XMM14_LO_ZX,
+ BS3CG1DST_XMM15_LO_ZX,
+ BS3CG1DST_XMM0_DW0,
+ BS3CG1DST_XMM1_DW0,
+ BS3CG1DST_XMM2_DW0,
+ BS3CG1DST_XMM3_DW0,
+ BS3CG1DST_XMM4_DW0,
+ BS3CG1DST_XMM5_DW0,
+ BS3CG1DST_XMM6_DW0,
+ BS3CG1DST_XMM7_DW0,
+ BS3CG1DST_XMM8_DW0,
+ BS3CG1DST_XMM9_DW0,
+ BS3CG1DST_XMM10_DW0,
+ BS3CG1DST_XMM11_DW0,
+ BS3CG1DST_XMM12_DW0,
+ BS3CG1DST_XMM13_DW0,
+ BS3CG1DST_XMM14_DW0,
+ BS3CG1DST_XMM15_DW0,
+ BS3CG1DST_XMM0_DW0_ZX,
+ BS3CG1DST_XMM1_DW0_ZX,
+ BS3CG1DST_XMM2_DW0_ZX,
+ BS3CG1DST_XMM3_DW0_ZX,
+ BS3CG1DST_XMM4_DW0_ZX,
+ BS3CG1DST_XMM5_DW0_ZX,
+ BS3CG1DST_XMM6_DW0_ZX,
+ BS3CG1DST_XMM7_DW0_ZX,
+ BS3CG1DST_XMM8_DW0_ZX,
+ BS3CG1DST_XMM9_DW0_ZX,
+ BS3CG1DST_XMM10_DW0_ZX,
+ BS3CG1DST_XMM11_DW0_ZX,
+ BS3CG1DST_XMM12_DW0_ZX,
+ BS3CG1DST_XMM13_DW0_ZX,
+ BS3CG1DST_XMM14_DW0_ZX,
+ BS3CG1DST_XMM15_DW0_ZX,
+ BS3CG1DST_XMM0_HI96,
+ BS3CG1DST_XMM1_HI96,
+ BS3CG1DST_XMM2_HI96,
+ BS3CG1DST_XMM3_HI96,
+ BS3CG1DST_XMM4_HI96,
+ BS3CG1DST_XMM5_HI96,
+ BS3CG1DST_XMM6_HI96,
+ BS3CG1DST_XMM7_HI96,
+ BS3CG1DST_XMM8_HI96,
+ BS3CG1DST_XMM9_HI96,
+ BS3CG1DST_XMM10_HI96,
+ BS3CG1DST_XMM11_HI96,
+ BS3CG1DST_XMM12_HI96,
+ BS3CG1DST_XMM13_HI96,
+ BS3CG1DST_XMM14_HI96,
+ BS3CG1DST_XMM15_HI96,
+ /* AVX registers. */
+ BS3CG1DST_YMM0,
+ BS3CG1DST_YMM1,
+ BS3CG1DST_YMM2,
+ BS3CG1DST_YMM3,
+ BS3CG1DST_YMM4,
+ BS3CG1DST_YMM5,
+ BS3CG1DST_YMM6,
+ BS3CG1DST_YMM7,
+ BS3CG1DST_YMM8,
+ BS3CG1DST_YMM9,
+ BS3CG1DST_YMM10,
+ BS3CG1DST_YMM11,
+ BS3CG1DST_YMM12,
+ BS3CG1DST_YMM13,
+ BS3CG1DST_YMM14,
+ BS3CG1DST_YMM15,
+
+ /* Special fields: */
+ BS3CG1DST_SPECIAL_START,
+ BS3CG1DST_VALUE_XCPT = BS3CG1DST_SPECIAL_START, /**< Expected exception based on input or result. */
+
+ BS3CG1DST_END
+} BS3CG1DST;
+AssertCompile(BS3CG1DST_END <= 256);
+
+/** @name Selector opcode definitions.
+ *
+ * Selector programs are very simple, they are zero or more predicate tests
+ * that are ANDed together. If a predicate test fails, the test is skipped.
+ *
+ * One instruction is encoded as byte, where the first bit indicates what kind
+ * of test and the 7 remaining bits indicates which predicate to check.
+ *
+ * @{ */
+#define BS3CG1SEL_OP_KIND_MASK UINT8_C(0x01) /**< The operator part (put in lower bit to reduce switch value range). */
+#define BS3CG1SEL_OP_IS_TRUE UINT8_C(0x00) /**< Check that the predicate is true. */
+#define BS3CG1SEL_OP_IS_FALSE UINT8_C(0x01) /**< Check that the predicate is false. */
+#define BS3CG1SEL_OP_PRED_SHIFT 1 /**< Shift factor for getting/putting a BS3CG1PRED value into/from a byte. */
+/** @} */
+
+/**
+ * Test selector predicates (values are shifted by BS3CG1SEL_OP_PRED_SHIFT).
+ */
+typedef enum BS3CG1PRED
+{
+ BS3CG1PRED_INVALID = 0,
+
+ /* Operand size. */
+ BS3CG1PRED_SIZE_O16,
+ BS3CG1PRED_SIZE_O32,
+ BS3CG1PRED_SIZE_O64,
+ /* VEX.L values. */
+ BS3CG1PRED_VEXL_0,
+ BS3CG1PRED_VEXL_1,
+ /* Execution ring. */
+ BS3CG1PRED_RING_0,
+ BS3CG1PRED_RING_1,
+ BS3CG1PRED_RING_2,
+ BS3CG1PRED_RING_3,
+ BS3CG1PRED_RING_0_THRU_2,
+ BS3CG1PRED_RING_1_THRU_3,
+ /* Basic code mode. */
+ BS3CG1PRED_CODE_64BIT,
+ BS3CG1PRED_CODE_32BIT,
+ BS3CG1PRED_CODE_16BIT,
+ /* CPU modes. */
+ BS3CG1PRED_MODE_REAL,
+ BS3CG1PRED_MODE_PROT,
+ BS3CG1PRED_MODE_LONG,
+ BS3CG1PRED_MODE_V86,
+ BS3CG1PRED_MODE_SMM,
+ BS3CG1PRED_MODE_VMX,
+ BS3CG1PRED_MODE_SVM,
+ /* Paging on/off */
+ BS3CG1PRED_PAGING_ON,
+ BS3CG1PRED_PAGING_OFF,
+ /* CPU Vendors. */
+ BS3CG1PRED_VENDOR_AMD,
+ BS3CG1PRED_VENDOR_INTEL,
+ BS3CG1PRED_VENDOR_VIA,
+ BS3CG1PRED_VENDOR_SHANGHAI,
+ BS3CG1PRED_VENDOR_HYGON,
+
+ BS3CG1PRED_END
+} BS3CG1PRED;
+
+
+/** The test instructions (generated). */
+extern const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[];
+/** The number of test instructions (generated). */
+extern const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions;
+/** The mnemonics (generated).
+ * Variable length sequence of mnemonics that runs in parallel to
+ * g_aBs3Cg1Instructions. */
+extern const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[];
+/** The opcodes (generated).
+ * Variable length sequence of opcode bytes that runs in parallel to
+ * g_aBs3Cg1Instructions, advancing by BS3CG1INSTR::cbOpcodes each time. */
+extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[];
+/** The operands (generated).
+ * Variable length sequence of opcode values (BS3CG1OP) that runs in
+ * parallel to g_aBs3Cg1Instructions, advancing by BS3CG1INSTR::cOperands. */
+extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[];
+/** The test data that BS3CG1INSTR.
+ * In order to simplify generating these, we use a byte array. */
+extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
+
+
+#endif /* !VBOX_INCLUDED_SRC_bootsectors_bs3_cpu_generated_1_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-asm.asm
new file mode 100644
index 00000000..17498b1e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-asm.asm
@@ -0,0 +1,72 @@
+; $Id: bs3-cpu-instr-2-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-instr-2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+;BS3_BEGIN_DATA16
+;BS3_GLOBAL_DATA g_bs3CpuBasic2_ud2_FlatAddr, 4
+; dd _bs3CpuBasic2_ud2 wrt FLAT
+
+
+
+;
+; CPU mode agnostic test code snippets.
+;
+BS3_BEGIN_TEXT16
+
+BS3_PROC_BEGIN _bs3CpuInstr2_imul_bl_ud2
+ imul bl
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END _bs3CpuInstr2_imul_bl_ud2
+
+
+
+;
+; Instantiate code templates.
+;
+BS3_INSTANTIATE_COMMON_TEMPLATE "bs3-cpu-instr-2-template.mac"
+BS3_INSTANTIATE_TEMPLATE_WITH_WEIRD_ONES "bs3-cpu-instr-2-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.c
new file mode 100644
index 00000000..adaf1c48
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.c
@@ -0,0 +1,3196 @@
+/* $Id: bs3-cpu-instr-2-template.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-instr-2, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+# if ARCH_BITS == 64
+typedef struct BS3CI2FSGSBASE
+{
+ const char *pszDesc;
+ bool f64BitOperand;
+ FPFNBS3FAR pfnWorker;
+ uint8_t offWorkerUd2;
+ FPFNBS3FAR pfnVerifyWorker;
+ uint8_t offVerifyWorkerUd2;
+} BS3CI2FSGSBASE;
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mul_xBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_imul_xBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_imul_xCX_xBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_div_xBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_idiv_xBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsf_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsf_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_tzcnt_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bsr_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_bsr_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lzcnt_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_AX_BX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_EAX_EBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_RAX_RBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_AX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_EAX_FSxBX_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_RAX_FSxBX_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_RDX_2_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_L1);
+# if ARCH_BITS == 64
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_X1);
+# endif
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V1);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V15);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_DSxDI_68_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_DSxDI_36_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bextr_RAX_RBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bextr_RAX_FSxBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bextr_EAX_EBX_ECX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bextr_EAX_FSxBX_ECX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bzhi_RAX_RBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bzhi_RAX_FSxBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bzhi_EAX_EBX_ECX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_bzhi_EAX_FSxBX_ECX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pdep_RAX_RCX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pdep_RAX_RCX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pdep_EAX_ECX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pdep_EAX_ECX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pext_RAX_RCX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pext_RAX_RCX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pext_EAX_ECX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_pext_EAX_ECX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shlx_RAX_RBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shlx_RAX_FSxBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shlx_EAX_EBX_ECX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shlx_EAX_FSxBX_ECX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_sarx_RAX_RBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_sarx_RAX_FSxBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_sarx_EAX_EBX_ECX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_sarx_EAX_FSxBX_ECX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shrx_RAX_RBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shrx_RAX_FSxBX_RCX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shrx_EAX_EBX_ECX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_shrx_EAX_FSxBX_ECX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsr_RAX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsr_RAX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsr_EAX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsr_EAX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsmsk_RAX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsmsk_RAX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsmsk_EAX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsmsk_EAX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsi_RAX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsi_RAX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsi_EAX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_blsi_EAX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_RCX_RCX_RBX_RDX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_ECX_ECX_EBX_EDX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_BX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_FSxBX_icebp);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_BL_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_byte_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_BX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_word_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_EBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_dword_FSxBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_RBX_icebp);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_qword_FSxBX_icebp);
+
+# if ARCH_BITS == 64
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lock_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_o16_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lock_o16_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_repz_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lock_repz_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_repnz_cmpxchg16b_rdi_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_lock_repnz_cmpxchg16b_rdi_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrfsbase_rbx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrfsbase_ebx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrfsbase_rbx_rdfsbase_rcx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrfsbase_ebx_rdfsbase_ecx_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrgsbase_rbx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrgsbase_ebx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrgsbase_rbx_rdgsbase_rcx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_wrgsbase_ebx_rdgsbase_ecx_ud2);
+
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rdfsbase_rbx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rdfsbase_ebx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rdgsbase_rbx_ud2);
+extern FNBS3FAR BS3_CMN_NM(bs3CpuInstr2_rdgsbase_ebx_ud2);
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef BS3_INSTANTIATING_CMN
+# if ARCH_BITS == 64
+static BS3CI2FSGSBASE const s_aWrFsBaseWorkers[] =
+{
+ { "wrfsbase rbx", true, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_rbx_ud2), 5, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_rbx_rdfsbase_rcx_ud2), 15 },
+ { "wrfsbase ebx", false, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_ebx_ud2), 4, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_ebx_rdfsbase_ecx_ud2), 13 },
+};
+
+static BS3CI2FSGSBASE const s_aWrGsBaseWorkers[] =
+{
+ { "wrgsbase rbx", true, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_rbx_ud2), 5, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_rbx_rdgsbase_rcx_ud2), 15 },
+ { "wrgsbase ebx", false, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_ebx_ud2), 4, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_ebx_rdgsbase_ecx_ud2), 13 },
+};
+
+static BS3CI2FSGSBASE const s_aRdFsBaseWorkers[] =
+{
+ { "rdfsbase rbx", true, BS3_CMN_NM(bs3CpuInstr2_rdfsbase_rbx_ud2), 5, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_rbx_rdfsbase_rcx_ud2), 15 },
+ { "rdfsbase ebx", false, BS3_CMN_NM(bs3CpuInstr2_rdfsbase_ebx_ud2), 4, BS3_CMN_NM(bs3CpuInstr2_wrfsbase_ebx_rdfsbase_ecx_ud2), 13 },
+};
+
+static BS3CI2FSGSBASE const s_aRdGsBaseWorkers[] =
+{
+ { "rdgsbase rbx", true, BS3_CMN_NM(bs3CpuInstr2_rdgsbase_rbx_ud2), 5, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_rbx_rdgsbase_rcx_ud2), 15 },
+ { "rdgsbase ebx", false, BS3_CMN_NM(bs3CpuInstr2_rdgsbase_ebx_ud2), 4, BS3_CMN_NM(bs3CpuInstr2_wrgsbase_ebx_rdgsbase_ecx_ud2), 13 },
+};
+# endif
+#endif /* BS3_INSTANTIATING_CMN - global */
+
+
+/*
+ * Common code.
+ * Common code.
+ * Common code.
+ */
+#ifdef BS3_INSTANTIATING_CMN
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_mul)(uint8_t bMode)
+{
+#define MUL_CHECK_EFLAGS_ZERO (uint16_t)(X86_EFL_AF | X86_EFL_ZF)
+#define MUL_CHECK_EFLAGS (uint16_t)(X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF)
+
+ static const struct
+ {
+ RTCCUINTREG uInAX;
+ RTCCUINTREG uInBX;
+ RTCCUINTREG uOutDX;
+ RTCCUINTREG uOutAX;
+ uint16_t fFlags;
+ } s_aTests[] =
+ {
+ { 1, 1,
+ 0, 1, 0 },
+ { 2, 2,
+ 0, 4, 0 },
+ { RTCCUINTREG_MAX, RTCCUINTREG_MAX,
+ RTCCUINTREG_MAX-1, 1, X86_EFL_CF | X86_EFL_OF },
+ { RTCCINTREG_MAX, RTCCINTREG_MAX,
+ RTCCINTREG_MAX / 2, 1, X86_EFL_CF | X86_EFL_OF },
+ { 1, RTCCUINTREG_MAX,
+ 0, RTCCUINTREG_MAX, X86_EFL_PF | X86_EFL_SF },
+ { 1, RTCCINTREG_MAX,
+ 0, RTCCINTREG_MAX, X86_EFL_PF },
+ { 2, RTCCINTREG_MAX,
+ 0, RTCCUINTREG_MAX - 1, X86_EFL_SF },
+ { (RTCCUINTREG)RTCCINTREG_MAX + 1, 2,
+ 1, 0, X86_EFL_PF | X86_EFL_CF | X86_EFL_OF },
+ { (RTCCUINTREG)RTCCINTREG_MAX / 2 + 1, 3,
+ 0, ((RTCCUINTREG)RTCCINTREG_MAX / 2 + 1) * 3, X86_EFL_PF | X86_EFL_SF },
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j, k;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, BS3_CMN_NM(bs3CpuInstr2_mul_xBX_ud2));
+ for (k = 0; k < 2; k++)
+ {
+ Ctx.rflags.u16 |= MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ if (k == 0)
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ }
+ else
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ }
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ if (TrapFrame.bXcpt != X86_XCPT_UD)
+ Bs3TestFailedF("Expected #UD got %#x", TrapFrame.bXcpt);
+ else if ( TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX
+ || TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX
+ || (TrapFrame.Ctx.rflags.u16 & (MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & MUL_CHECK_EFLAGS) )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTREG_XFMT " * %#" RTCCUINTREG_XFMT,
+ i, s_aTests[i].uInAX, s_aTests[i].uInBX);
+
+ if (TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX)
+ Bs3TestFailedF("Expected xAX = %#RX" RT_XSTR(ARCH_BITS) " got %#RX" RT_XSTR(ARCH_BITS),
+ s_aTests[i].uOutAX, TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS));
+ if (TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX)
+ Bs3TestFailedF("Expected xDX = %#RX" RT_XSTR(ARCH_BITS) " got %#RX" RT_XSTR(ARCH_BITS),
+ s_aTests[i].uOutDX, TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS));
+ if ( (TrapFrame.Ctx.rflags.u16 & (MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & MUL_CHECK_EFLAGS) )
+ Bs3TestFailedF("Expected EFLAGS = %#06RX16, got %#06RX16", s_aTests[i].fFlags & MUL_CHECK_EFLAGS,
+ TrapFrame.Ctx.rflags.u16 & (MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO));
+ }
+ }
+ Ctx.rflags.u16 &= ~(MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO);
+ }
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_imul)(uint8_t bMode)
+{
+#define IMUL_CHECK_EFLAGS_ZERO (uint16_t)(X86_EFL_AF | X86_EFL_ZF)
+#define IMUL_CHECK_EFLAGS (uint16_t)(X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF)
+ static const struct
+ {
+ RTCCUINTREG uInAX;
+ RTCCUINTREG uInBX;
+ RTCCUINTREG uOutDX;
+ RTCCUINTREG uOutAX;
+ uint16_t fFlags;
+ } s_aTests[] =
+ {
+ /* two positive values. */
+ { 1, 1,
+ 0, 1, 0 },
+ { 2, 2,
+ 0, 4, 0 },
+ { RTCCINTREG_MAX, RTCCINTREG_MAX,
+ RTCCINTREG_MAX/2, 1, X86_EFL_CF | X86_EFL_OF },
+ { 1, RTCCINTREG_MAX,
+ 0, RTCCINTREG_MAX, X86_EFL_PF },
+ { 2, RTCCINTREG_MAX,
+ 0, RTCCUINTREG_MAX - 1U, X86_EFL_CF | X86_EFL_OF | X86_EFL_SF },
+ { 2, RTCCINTREG_MAX / 2,
+ 0, RTCCINTREG_MAX - 1U, 0 },
+ { 2, (RTCCINTREG_MAX / 2 + 1),
+ 0, (RTCCUINTREG)RTCCINTREG_MAX + 1U, X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF },
+ { 4, (RTCCINTREG_MAX / 2 + 1),
+ 1, 0, X86_EFL_CF | X86_EFL_OF | X86_EFL_PF },
+
+ /* negative and positive */
+ { -4, 3,
+ -1, -12, X86_EFL_SF },
+ { 32, -127,
+ -1, -4064, X86_EFL_SF },
+ { RTCCINTREG_MIN, 1,
+ -1, RTCCINTREG_MIN, X86_EFL_SF | X86_EFL_PF },
+ { RTCCINTREG_MIN, 2,
+ -1, 0, X86_EFL_CF | X86_EFL_OF | X86_EFL_PF },
+ { RTCCINTREG_MIN, 3,
+ -2, RTCCINTREG_MIN, X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF },
+ { RTCCINTREG_MIN, 4,
+ -2, 0, X86_EFL_CF | X86_EFL_OF | X86_EFL_PF },
+ { RTCCINTREG_MIN, RTCCINTREG_MAX,
+ RTCCINTREG_MIN / 2, RTCCINTREG_MIN, X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF },
+ { RTCCINTREG_MIN, RTCCINTREG_MAX - 1,
+ RTCCINTREG_MIN / 2 + 1, 0, X86_EFL_CF | X86_EFL_OF | X86_EFL_PF },
+
+ /* two negative values. */
+ { -4, -63,
+ 0, 252, X86_EFL_PF },
+ { RTCCINTREG_MIN, RTCCINTREG_MIN,
+ RTCCUINTREG_MAX / 4 + 1, 0, X86_EFL_CF | X86_EFL_OF | X86_EFL_PF },
+ { RTCCINTREG_MIN, RTCCINTREG_MIN + 1,
+ RTCCUINTREG_MAX / 4, RTCCINTREG_MIN, X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_PF},
+ { RTCCINTREG_MIN + 1, RTCCINTREG_MIN + 1,
+ RTCCUINTREG_MAX / 4, 1, X86_EFL_CF | X86_EFL_OF },
+
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j, k;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, BS3_CMN_NM(bs3CpuInstr2_imul_xBX_ud2));
+
+ for (k = 0; k < 2; k++)
+ {
+ Ctx.rflags.u16 |= MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ if (k == 0)
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ }
+ else
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ }
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ if (TrapFrame.bXcpt != X86_XCPT_UD)
+ Bs3TestFailedF("Expected #UD got %#x", TrapFrame.bXcpt);
+ else if ( TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX
+ || TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX
+ || (TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & IMUL_CHECK_EFLAGS) )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTREG_XFMT " * %#" RTCCUINTREG_XFMT,
+ i, s_aTests[i].uInAX, s_aTests[i].uInBX);
+
+ if (TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX)
+ Bs3TestFailedF("Expected xAX = %#RX" RT_XSTR(ARCH_BITS) " got %#RX" RT_XSTR(ARCH_BITS),
+ s_aTests[i].uOutAX, TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS));
+ if (TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX)
+ Bs3TestFailedF("Expected xDX = %#RX" RT_XSTR(ARCH_BITS) " got %#RX" RT_XSTR(ARCH_BITS),
+ s_aTests[i].uOutDX, TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS));
+ if ( (TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & IMUL_CHECK_EFLAGS) )
+ Bs3TestFailedF("Expected EFLAGS = %#06RX16, got %#06RX16", s_aTests[i].fFlags & IMUL_CHECK_EFLAGS,
+ TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO));
+ }
+ }
+ }
+ }
+
+ /*
+ * Repeat for the truncating two operand version.
+ */
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, BS3_CMN_NM(bs3CpuInstr2_imul_xCX_xBX_ud2));
+
+ for (k = 0; k < 2; k++)
+ {
+ Ctx.rflags.u16 |= MUL_CHECK_EFLAGS | MUL_CHECK_EFLAGS_ZERO;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ if (k == 0)
+ {
+ Ctx.rcx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ }
+ else
+ {
+ Ctx.rcx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ }
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ if (TrapFrame.bXcpt != X86_XCPT_UD)
+ Bs3TestFailedF("Expected #UD got %#x", TrapFrame.bXcpt);
+ else if ( TrapFrame.Ctx.rcx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || (TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & IMUL_CHECK_EFLAGS) )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTREG_XFMT " * %#" RTCCUINTREG_XFMT,
+ i, s_aTests[i].uInAX, s_aTests[i].uInBX);
+
+ if (TrapFrame.Ctx.rcx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX)
+ Bs3TestFailedF("Expected xAX = %#RX" RT_XSTR(ARCH_BITS) " got %#RX" RT_XSTR(ARCH_BITS),
+ s_aTests[i].uOutAX, TrapFrame.Ctx.rcx.RT_CONCAT(u,ARCH_BITS));
+ if ( (TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO))
+ != (s_aTests[i].fFlags & IMUL_CHECK_EFLAGS) )
+ Bs3TestFailedF("Expected EFLAGS = %#06RX16, got %#06RX16", s_aTests[i].fFlags & IMUL_CHECK_EFLAGS,
+ TrapFrame.Ctx.rflags.u16 & (IMUL_CHECK_EFLAGS | IMUL_CHECK_EFLAGS_ZERO));
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_div)(uint8_t bMode)
+{
+#define DIV_CHECK_EFLAGS (uint16_t)(X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_ZF | X86_EFL_AF | X86_EFL_PF)
+ static const struct
+ {
+ RTCCUINTREG uInDX;
+ RTCCUINTREG uInAX;
+ RTCCUINTREG uInBX;
+ RTCCUINTREG uOutAX;
+ RTCCUINTREG uOutDX;
+ uint8_t bXcpt;
+ } s_aTests[] =
+ {
+ { 0, 1, 1,
+ 1, 0, X86_XCPT_UD },
+ { 0, 5, 2,
+ 2, 1, X86_XCPT_UD },
+ { 0, 0, 0,
+ 0, 0, X86_XCPT_DE },
+ { RTCCUINTREG_MAX, RTCCUINTREG_MAX, 0,
+ 0, 0, X86_XCPT_DE },
+ { RTCCUINTREG_MAX, RTCCUINTREG_MAX, 1,
+ 0, 0, X86_XCPT_DE },
+ { RTCCUINTREG_MAX, RTCCUINTREG_MAX, RTCCUINTREG_MAX,
+ 0, 0, X86_XCPT_DE },
+ { RTCCUINTREG_MAX - 1, RTCCUINTREG_MAX, RTCCUINTREG_MAX,
+ RTCCUINTREG_MAX, RTCCUINTREG_MAX - 1, X86_XCPT_UD },
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, BS3_CMN_NM(bs3CpuInstr2_div_xBX_ud2));
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not touched by my intel skylake CPU.
+ */
+ Ctx.rflags.u16 |= DIV_CHECK_EFLAGS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ Ctx.rdx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInDX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != s_aTests[i].bXcpt
+ || ( s_aTests[i].bXcpt == X86_XCPT_UD
+ ? TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX
+ || TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX
+ || (TrapFrame.Ctx.rflags.u16 & DIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & DIV_CHECK_EFLAGS)
+ : TrapFrame.Ctx.rax.u != Ctx.rax.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || (TrapFrame.Ctx.rflags.u16 & DIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & DIV_CHECK_EFLAGS) ) )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTREG_XFMT ":%" RTCCUINTREG_XFMT " / %#" RTCCUINTREG_XFMT,
+ i, s_aTests[i].uInDX, s_aTests[i].uInAX, s_aTests[i].uInBX);
+ if (TrapFrame.bXcpt != s_aTests[i].bXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", s_aTests[i].bXcpt, TrapFrame.bXcpt);
+ if (s_aTests[i].bXcpt == X86_XCPT_UD)
+ {
+ if (TrapFrame.Ctx.rax.RT_CONCAT(u, ARCH_BITS) != s_aTests[i].uOutAX)
+ Bs3TestFailedF("Expected xAX = %#" RTCCUINTREG_XFMT ", got %#" RTCCUINTREG_XFMT,
+ s_aTests[i].uOutAX, TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS));
+ if (TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX)
+ Bs3TestFailedF("Expected xDX = %#" RTCCUINTREG_XFMT ", got %#" RTCCUINTREG_XFMT,
+ s_aTests[i].uOutDX, TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS));
+ if ((TrapFrame.Ctx.rflags.u16 & DIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & DIV_CHECK_EFLAGS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX16, got %#06RX16",
+ Ctx.rflags.u16 & DIV_CHECK_EFLAGS, TrapFrame.Ctx.rflags.u16 & DIV_CHECK_EFLAGS);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~DIV_CHECK_EFLAGS;
+ }
+
+ return 0;
+}
+
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_idiv)(uint8_t bMode)
+{
+#define IDIV_CHECK_EFLAGS (uint16_t)(X86_EFL_CF | X86_EFL_OF | X86_EFL_SF | X86_EFL_ZF | X86_EFL_AF | X86_EFL_PF)
+ static const struct
+ {
+ RTCCUINTREG uInDX;
+ RTCCUINTREG uInAX;
+ RTCCUINTREG uInBX;
+ RTCCUINTREG uOutAX;
+ RTCCUINTREG uOutDX;
+ uint8_t bXcpt;
+ } s_aTests[] =
+ {
+ { 0, 0, 0,
+ 0, 0, X86_XCPT_DE },
+ { RTCCINTREG_MAX, RTCCINTREG_MAX, 0,
+ 0, 0, X86_XCPT_DE },
+ /* two positive values. */
+ { 0, 1, 1,
+ 1, 0, X86_XCPT_UD },
+ { 0, 5, 2,
+ 2, 1, X86_XCPT_UD },
+ { RTCCINTREG_MAX / 2, RTCCUINTREG_MAX / 2, RTCCINTREG_MAX,
+ RTCCINTREG_MAX, RTCCINTREG_MAX - 1, X86_XCPT_UD },
+ { RTCCINTREG_MAX / 2, RTCCUINTREG_MAX / 2 + 1, RTCCINTREG_MAX,
+ RTCCINTREG_MAX, RTCCINTREG_MAX - 1, X86_XCPT_DE },
+ /* negative dividend, positive divisor. */
+ { -1, -7, 2,
+ -3, -1, X86_XCPT_UD },
+ { RTCCINTREG_MIN / 2 + 1, 0, RTCCINTREG_MAX,
+ RTCCINTREG_MIN + 2, RTCCINTREG_MIN + 2, X86_XCPT_UD },
+ { RTCCINTREG_MIN / 2, 0, RTCCINTREG_MAX,
+ 0, 0, X86_XCPT_DE },
+ /* positive dividend, negative divisor. */
+ { 0, 7, -2,
+ -3, 1, X86_XCPT_UD },
+ { RTCCINTREG_MAX / 2 + 1, RTCCINTREG_MAX, RTCCINTREG_MIN,
+ RTCCINTREG_MIN, RTCCINTREG_MAX, X86_XCPT_UD },
+ { RTCCINTREG_MAX / 2 + 1, (RTCCUINTREG)RTCCINTREG_MAX+1, RTCCINTREG_MIN,
+ 0, 0, X86_XCPT_DE },
+ /* negative dividend, negative divisor. */
+ { -1, -7, -2,
+ 3, -1, X86_XCPT_UD },
+ { RTCCINTREG_MIN / 2, 1, RTCCINTREG_MIN,
+ RTCCINTREG_MAX, RTCCINTREG_MIN + 1, X86_XCPT_UD },
+ { RTCCINTREG_MIN / 2, 2, RTCCINTREG_MIN,
+ RTCCINTREG_MAX, RTCCINTREG_MIN + 2, X86_XCPT_UD },
+ { RTCCINTREG_MIN / 2, 0, RTCCINTREG_MIN,
+ 0, 0, X86_XCPT_DE },
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, BS3_CMN_NM(bs3CpuInstr2_idiv_xBX_ud2));
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not touched by my intel skylake CPU.
+ */
+ Ctx.rflags.u16 |= IDIV_CHECK_EFLAGS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ Ctx.rax.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInAX;
+ Ctx.rdx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInDX;
+ Ctx.rbx.RT_CONCAT(u,ARCH_BITS) = s_aTests[i].uInBX;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != s_aTests[i].bXcpt
+ || ( s_aTests[i].bXcpt == X86_XCPT_UD
+ ? TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutAX
+ || TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX
+ || (TrapFrame.Ctx.rflags.u16 & IDIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & IDIV_CHECK_EFLAGS)
+ : TrapFrame.Ctx.rax.u != Ctx.rax.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || (TrapFrame.Ctx.rflags.u16 & IDIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & IDIV_CHECK_EFLAGS) ) )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTREG_XFMT ":%" RTCCUINTREG_XFMT " / %#" RTCCUINTREG_XFMT,
+ i, s_aTests[i].uInDX, s_aTests[i].uInAX, s_aTests[i].uInBX);
+ if (TrapFrame.bXcpt != s_aTests[i].bXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", s_aTests[i].bXcpt, TrapFrame.bXcpt);
+ if (s_aTests[i].bXcpt == X86_XCPT_UD)
+ {
+ if (TrapFrame.Ctx.rax.RT_CONCAT(u, ARCH_BITS) != s_aTests[i].uOutAX)
+ Bs3TestFailedF("Expected xAX = %#" RTCCUINTREG_XFMT ", got %#" RTCCUINTREG_XFMT,
+ s_aTests[i].uOutAX, TrapFrame.Ctx.rax.RT_CONCAT(u,ARCH_BITS));
+ if (TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS) != s_aTests[i].uOutDX)
+ Bs3TestFailedF("Expected xDX = %#" RTCCUINTREG_XFMT ", got %#" RTCCUINTREG_XFMT,
+ s_aTests[i].uOutDX, TrapFrame.Ctx.rdx.RT_CONCAT(u,ARCH_BITS));
+ if ((TrapFrame.Ctx.rflags.u16 & IDIV_CHECK_EFLAGS) != (Ctx.rflags.u16 & IDIV_CHECK_EFLAGS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX16, got %#06RX16",
+ Ctx.rflags.u16 & IDIV_CHECK_EFLAGS, TrapFrame.Ctx.rflags.u16 & IDIV_CHECK_EFLAGS);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~IDIV_CHECK_EFLAGS;
+ }
+
+ return 0;
+}
+
+
+/*
+ * BSF/BSR (386+) & TZCNT/LZCNT (BMI1,ABM)
+ */
+
+typedef struct BS3CPUINSTR2_SUBTEST_BITSCAN_T
+{
+ RTCCUINTXREG uSrc;
+ RTCCUINTXREG uOut;
+ bool fOutNotSet;
+ uint16_t fEflOut;
+} BS3CPUINSTR2_SUBTEST_BITSCAN_T;
+
+typedef struct BS3CPUINSTR2_TEST_BITSCAN_T
+{
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cbInstr;
+ uint8_t cOpBits;
+ uint16_t fEflCheck;
+ uint8_t cSubTests;
+ BS3CPUINSTR2_SUBTEST_BITSCAN_T const *paSubTests;
+} BS3CPUINSTR2_TEST_BITSCAN_T;
+
+static uint8_t bs3CpuInstr2_BitScan(uint8_t bMode, BS3CPUINSTR2_TEST_BITSCAN_T const *paTests, unsigned cTests)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j, k;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < cTests; i++)
+ {
+ for (k = 0; k < paTests[i].cSubTests; k++)
+ {
+ uint64_t uExpectRax, uExpectRip;
+ RTCCUINTXREG uMemSrc, uMemSrcExpect;
+
+ Ctx.rax.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ if (!paTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = paTests[i].paSubTests[k].uSrc;
+ uMemSrcExpect = uMemSrc = ~paTests[i].paSubTests[k].uSrc;
+ }
+ else
+ {
+ uMemSrcExpect = uMemSrc = paTests[i].paSubTests[k].uSrc;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[i].pfnWorker);
+ if (paTests[i].paSubTests[k].fOutNotSet)
+ uExpectRax = Ctx.rax.u;
+ else if (paTests[i].cOpBits != 16)
+ uExpectRax = paTests[i].paSubTests[k].uOut;
+ else
+ uExpectRax = paTests[i].paSubTests[k].uOut | (Ctx.rax.u & UINT64_C(0xffffffffffff0000));
+ uExpectRip = Ctx.rip.u + paTests[i].cbInstr;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != X86_XCPT_UD
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ || (TrapFrame.Ctx.rflags.u16 & paTests[i].fEflCheck)
+ != (paTests[i].paSubTests[k].fEflOut & paTests[i].fEflCheck)
+ /* check that nothing else really changed: */
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc != uMemSrcExpect
+ )
+ {
+ Bs3TestFailedF("test #%i/%i failed: input %#" RTCCUINTXREG_XFMT,
+ i, k, paTests[i].paSubTests[k].uSrc);
+ if (TrapFrame.bXcpt != X86_XCPT_UD)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", X86_XCPT_UD, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if ( (TrapFrame.Ctx.rflags.u16 & paTests[i].fEflCheck)
+ != (paTests[i].paSubTests[k].fEflOut & paTests[i].fEflCheck))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (output)",
+ paTests[i].paSubTests[k].fEflOut & paTests[i].fEflCheck,
+ TrapFrame.Ctx.rflags.u16 & paTests[i].fEflCheck);
+
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc != uMemSrcExpect)
+ Bs3TestFailedF("Expected uMemSrc = %#06RX64, got %#06RX64", (uint64_t)uMemSrcExpect, (uint64_t)uMemSrc);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_bsf_tzcnt)(uint8_t bMode)
+{
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsf16[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)UINT16_MAX, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsTzCnt16[] =
+ {
+ { 0, /* -> */ 16, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)UINT16_MAX, /* -> */ 16, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsf32[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+#if ARCH_BITS == 64
+ { ~(RTCCUINTXREG)UINT32_MAX, /* -> */ 0, true, X86_EFL_ZF },
+#endif
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 21, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsTzCnt32[] =
+ {
+ { 0, /* -> */ 32, false, X86_EFL_CF },
+#if ARCH_BITS == 64
+ { ~(RTCCUINTXREG)UINT32_MAX, /* -> */ 32, false, X86_EFL_CF },
+#endif
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 21, false, 0 },
+ };
+#if ARCH_BITS == 64
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsf64[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 21, false, 0 },
+ { UINT64_C(0x8000000000000000), /* -> */ 63, false, 0 },
+ { UINT64_C(0x4560000000000000), /* -> */ 53, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsTzCnt64[] =
+ {
+ { 0, /* -> */ 64, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 5, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 21, false, 0 },
+ { UINT64_C(0x8000000000000000), /* -> */ 63, false, 0 },
+ { UINT64_C(0x4560000000000000), /* -> */ 53, false, 0 },
+ };
+#endif
+ static BS3CPUINSTR2_TEST_BITSCAN_T s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_AX_BX_ud2), false, 3 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf16), s_aSubTestsBsf16 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_AX_FSxBX_ud2), true, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf16), s_aSubTestsBsf16 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_EAX_EBX_ud2), false, 3 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf32), s_aSubTestsBsf32 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_EAX_FSxBX_ud2), true, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf32), s_aSubTestsBsf32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_RAX_RBX_ud2), false, 4, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf64), s_aSubTestsBsf64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsf_RAX_FSxBX_ud2), true, 5, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf64), s_aSubTestsBsf64 },
+#endif
+ /* f2 prefixed variant: */
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_AX_BX_ud2), false, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf16), s_aSubTestsBsf16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_AX_FSxBX_ud2), true, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf16), s_aSubTestsBsf16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_EAX_EBX_ud2), false, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf32), s_aSubTestsBsf32 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_EAX_FSxBX_ud2), true, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf32), s_aSubTestsBsf32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_RAX_RBX_ud2), false, 5, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf64), s_aSubTestsBsf64 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsf_RAX_FSxBX_ud2), true, 6, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsf64), s_aSubTestsBsf64 },
+#endif
+
+ /* tzcnt: */
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_AX_BX_ud2), false, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt16), s_aSubTestsTzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_AX_FSxBX_ud2), true, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt16), s_aSubTestsTzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_EAX_EBX_ud2), false, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt32), s_aSubTestsTzCnt32 },
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_EAX_FSxBX_ud2), true, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt32), s_aSubTestsTzCnt32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_RAX_RBX_ud2), false, 5, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt64), s_aSubTestsTzCnt64 },
+ { BS3_CMN_NM(bs3CpuInstr2_tzcnt_RAX_FSxBX_ud2), true, 6, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt64), s_aSubTestsTzCnt64 },
+#endif
+ /* f2 prefixed tzcnt variant (last prefix (f3) should prevail): */
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_AX_BX_ud2), false, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt16), s_aSubTestsTzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_AX_FSxBX_ud2), true, 6 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt16), s_aSubTestsTzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_EAX_EBX_ud2), false, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt32), s_aSubTestsTzCnt32 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_EAX_FSxBX_ud2),true, 6 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt32), s_aSubTestsTzCnt32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_RAX_RBX_ud2), false, 6, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt64), s_aSubTestsTzCnt64 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_tzcnt_RAX_FSxBX_ud2),true, 7, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsTzCnt64), s_aSubTestsTzCnt64 },
+#endif
+ };
+
+ uint32_t uStdExtFeatEbx = 0;
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ if (!(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_BMI1))
+ {
+ unsigned i = RT_ELEMENTS(s_aTests);
+ while (i-- > 0)
+ if (s_aTests[i].fEflCheck & X86_EFL_CF)
+ {
+ s_aTests[i].fEflCheck = X86_EFL_ZF;
+ switch (s_aTests[i].cOpBits)
+ {
+ case 16:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsf16);
+ s_aTests[i].paSubTests = s_aSubTestsBsf16;
+ break;
+ case 32:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsf32);
+ s_aTests[i].paSubTests = s_aSubTestsBsf32;
+ break;
+#if ARCH_BITS == 64
+ case 64:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsf64);
+ s_aTests[i].paSubTests = s_aSubTestsBsf64;
+ break;
+#endif
+ }
+ }
+ Bs3TestPrintf("tzcnt not supported\n");
+ }
+
+ return bs3CpuInstr2_BitScan(bMode, s_aTests, RT_ELEMENTS(s_aTests));
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_bsr_lzcnt)(uint8_t bMode)
+{
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsr16[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)UINT16_MAX, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)0, /* -> */ 15, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 15, false, 0 },
+ { UINT16_C(0x0001), /* -> */ 0, false, 0 },
+ { UINT16_C(0x0002), /* -> */ 1, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 14, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsLzCnt16[] =
+ {
+ { 0, /* -> */ 16, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)UINT16_MAX, /* -> */ 16, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 0, false, X86_EFL_ZF },
+ { UINT16_C(0x8000), /* -> */ 0, false, X86_EFL_ZF },
+ { UINT16_C(0x4560), /* -> */ 1, false, 0 },
+ { UINT16_C(0x003f), /* -> */ 10, false, 0 },
+ { UINT16_C(0x0001), /* -> */ 15, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsr32[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+#if ARCH_BITS == 64
+ { ~(RTCCUINTXREG)UINT32_MAX, /* -> */ 0, true, X86_EFL_ZF },
+#endif
+ { ~(RTCCUINTXREG)0, /* -> */ 31, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 31, false, 0 },
+ { 1, /* -> */ 0, false, 0 },
+ { 2, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 14, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 30, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsLzCnt32[] =
+ {
+ { 0, /* -> */ 32, false, X86_EFL_CF },
+#if ARCH_BITS == 64
+ { ~(RTCCUINTXREG)UINT32_MAX, /* -> */ 32, false, X86_EFL_CF },
+#endif
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 0, false, X86_EFL_ZF },
+ { 1, /* -> */ 31, false, 0 },
+ { 2, /* -> */ 30, false, 0},
+ { UINT16_C(0x8000), /* -> */ 16, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 17, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 0, false, X86_EFL_ZF },
+ { UINT32_C(0x45600000), /* -> */ 1, false, 0 },
+ { UINT32_C(0x0000ffff), /* -> */ 16, false, 0 },
+ };
+#if ARCH_BITS == 64
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsBsr64[] =
+ {
+ { 0, /* -> */ 0, true, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)0, /* -> */ 63, false, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 63, false, 0 },
+ { 1, /* -> */ 0, false, 0 },
+ { 2, /* -> */ 1, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 15, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 14, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 31, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 30, false, 0 },
+ { UINT64_C(0x8000000000000000), /* -> */ 63, false, 0 },
+ { UINT64_C(0x0045600000000000), /* -> */ 54, false, 0 },
+ };
+ static BS3CPUINSTR2_SUBTEST_BITSCAN_T const s_aSubTestsLzCnt64[] =
+ {
+ { 0, /* -> */ 64, false, X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, /* -> */ 0, false, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ 0, false, X86_EFL_ZF },
+ { 1, /* -> */ 63, false, 0 },
+ { 2, /* -> */ 62, false, 0 },
+ { UINT16_C(0x8000), /* -> */ 48, false, 0 },
+ { UINT16_C(0x4560), /* -> */ 49, false, 0 },
+ { UINT32_C(0x80000000), /* -> */ 32, false, 0 },
+ { UINT32_C(0x45600000), /* -> */ 33, false, 0 },
+ { UINT64_C(0x8000000000000000), /* -> */ 0, false, X86_EFL_ZF },
+ { UINT64_C(0x4560000000000000), /* -> */ 1, false, 0 },
+ { UINT64_C(0x0045600000000000), /* -> */ 9, false, 0 },
+ };
+#endif
+ static BS3CPUINSTR2_TEST_BITSCAN_T s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_AX_BX_ud2), false, 3 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr16), s_aSubTestsBsr16 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_AX_FSxBX_ud2), true, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr16), s_aSubTestsBsr16 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_EAX_EBX_ud2), false, 3 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr32), s_aSubTestsBsr32 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_EAX_FSxBX_ud2), true, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr32), s_aSubTestsBsr32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_RAX_RBX_ud2), false, 4, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr64), s_aSubTestsBsr64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bsr_RAX_FSxBX_ud2), true, 5, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr64), s_aSubTestsBsr64 },
+#endif
+ /* f2 prefixed variant: */
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_AX_BX_ud2), false, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr16), s_aSubTestsBsr16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_AX_FSxBX_ud2), true, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr16), s_aSubTestsBsr16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_EAX_EBX_ud2), false, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr32), s_aSubTestsBsr32 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_EAX_FSxBX_ud2), true, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr32), s_aSubTestsBsr32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_RAX_RBX_ud2), false, 5, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr64), s_aSubTestsBsr64 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_bsr_RAX_FSxBX_ud2), true, 6, 64, X86_EFL_ZF,
+ RT_ELEMENTS(s_aSubTestsBsr64), s_aSubTestsBsr64 },
+#endif
+
+ /* lzcnt: */
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_AX_BX_ud2), false, 4 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt16), s_aSubTestsLzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_AX_FSxBX_ud2), true, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt16), s_aSubTestsLzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_EAX_EBX_ud2), false, 4 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt32), s_aSubTestsLzCnt32 },
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_EAX_FSxBX_ud2), true, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt32), s_aSubTestsLzCnt32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_RAX_RBX_ud2), false, 5, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt64), s_aSubTestsLzCnt64 },
+ { BS3_CMN_NM(bs3CpuInstr2_lzcnt_RAX_FSxBX_ud2), true, 6, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt64), s_aSubTestsLzCnt64 },
+#endif
+ /* f2 prefixed lzcnt variant (last prefix (f3) should prevail): */
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_AX_BX_ud2), false, 5 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt16), s_aSubTestsLzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_AX_FSxBX_ud2), true, 6 + (ARCH_BITS != 16), 16, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt16), s_aSubTestsLzCnt16 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_EAX_EBX_ud2), false, 5 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt32), s_aSubTestsLzCnt32 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_EAX_FSxBX_ud2),true, 6 + (ARCH_BITS == 16), 32, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt32), s_aSubTestsLzCnt32 },
+#if ARCH_BITS == 64
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_RAX_RBX_ud2), false, 6, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt64), s_aSubTestsLzCnt64 },
+ { BS3_CMN_NM(bs3CpuInstr2_f2_lzcnt_RAX_FSxBX_ud2),true, 7, 64, X86_EFL_ZF | X86_EFL_CF,
+ RT_ELEMENTS(s_aSubTestsLzCnt64), s_aSubTestsLzCnt64 },
+#endif
+ };
+
+ uint32_t uExtFeatEcx = 0;
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID_EXT_LEAVES)
+ ASMCpuIdExSlow(UINT32_C(0x80000001), 0, 0, 0, NULL, NULL, &uExtFeatEcx, NULL);
+ if (!(uExtFeatEcx & X86_CPUID_AMD_FEATURE_ECX_ABM))
+ {
+ unsigned i = RT_ELEMENTS(s_aTests);
+ while (i-- > 0)
+ if (s_aTests[i].fEflCheck & X86_EFL_CF)
+ {
+ s_aTests[i].fEflCheck = X86_EFL_ZF;
+ switch (s_aTests[i].cOpBits)
+ {
+ case 16:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsr16);
+ s_aTests[i].paSubTests = s_aSubTestsBsr16;
+ break;
+ case 32:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsr32);
+ s_aTests[i].paSubTests = s_aSubTestsBsr32;
+ break;
+#if ARCH_BITS == 64
+ case 64:
+ s_aTests[i].cSubTests = RT_ELEMENTS(s_aSubTestsBsr64);
+ s_aTests[i].paSubTests = s_aSubTestsBsr64;
+ break;
+#endif
+ }
+ }
+ Bs3TestPrintf("lzcnt not supported\n");
+ }
+
+ return bs3CpuInstr2_BitScan(bMode, s_aTests, RT_ELEMENTS(s_aTests));
+}
+
+
+/**
+ * RORX
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_rorx)(uint8_t bMode)
+{
+ static const struct
+ {
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ bool fOkay;
+ RTCCUINTXREG uIn;
+ RTCCUINTXREG uOut;
+ } s_aTests[] =
+ {
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_RDX_2_icebp), false, true, // #0
+ 0, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_RDX_2_icebp), false, true, // #1
+ ~(RTCCUINTXREG)2, /* -> */ ~(RTCCUINTXREG)0 >> 1 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_DSxDI_68_icebp), true, true, // #2
+ 0, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_RBX_DSxDI_68_icebp), true, true, // #3
+ ~(RTCCUINTXREG)2, /* -> */ (RTCCUINTXREG_MAX >> 4) | (~(RTCCUINTXREG)2 << (sizeof(RTCCUINTXREG) * 8 - 4)) },
+
+ /* 32 bits register width: */
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp), false, true, // #4
+ 0, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp), false, true, // #5
+ ~(RTCCUINTXREG)2, /* -> */ (RTCCUINTXREG)(~(uint32_t)0 >> 1) },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_DSxDI_36_icebp), true, true, // #6
+ 0, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_DSxDI_36_icebp), true, true, // #7
+ ~(RTCCUINTXREG)2, /* -> */ (RTCCUINTXREG)UINT32_C(0xdfffffff) },
+
+ /* encoding tests: */
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_L1), false, false, // #8
+ RTCCUINTXREG_MAX, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V1), false, false, // #9
+ RTCCUINTXREG_MAX, /* -> */ 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V15), false, false, // #10
+ RTCCUINTXREG_MAX, /* -> */ 0 },
+# if ARCH_BITS == 64 /* The VEX.X=0 encoding mean LES instruction in 32-bit and 16-bit mode. */
+ { BS3_CMN_NM(bs3CpuInstr2_rorx_EBX_EDX_2_icebp_X1), false, true, // #11
+ UINT32_C(0xf1e2d3c5), /* -> */ (RTCCUINTXREG)UINT32_C(0x7c78b4f1) },
+# endif
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+ uint32_t uStdExtFeatEbx = 0;
+ bool fSupportsRorX;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ fSupportsRorX = RT_BOOL(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_BMI2);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ bool const fOkay = !BS3_MODE_IS_RM_OR_V86(bMode) && s_aTests[i].fOkay && fSupportsRorX;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRbx, uExpectRip;
+ RTCCUINTXREG uMemSrc, uMemSrcExpect;
+ Ctx.rbx.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ if (!s_aTests[i].fMemSrc)
+ {
+ Ctx.rdx.uCcXReg = s_aTests[i].uIn;
+ uMemSrcExpect = uMemSrc = ~s_aTests[i].uIn;
+ }
+ else
+ {
+ Ctx.rdx.uCcXReg = ~s_aTests[i].uIn;
+ uMemSrcExpect = uMemSrc = s_aTests[i].uIn;
+ Bs3RegCtxSetGrpDsFromCurPtr(&Ctx, &Ctx.rdi, &uMemSrc);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aTests[i].pfnWorker);
+ uExpectRbx = fOkay ? s_aTests[i].uOut : Ctx.rbx.u;
+ uExpectRip = Ctx.rip.u + (fOkay ? 6 + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rbx.u != uExpectRbx
+ /* check that nothing else really changed: */
+ || (TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (Ctx.rflags.u16 & X86_EFL_STATUS_BITS)
+ || TrapFrame.Ctx.rax.u != Ctx.rax.u
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc != uMemSrcExpect
+ )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTXREG_XFMT, i, s_aTests[i].uIn);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rbx.u != uExpectRbx)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", uExpectRbx, TrapFrame.Ctx.rbx.u);
+
+ if ((TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (Ctx.rflags.u16 & X86_EFL_STATUS_BITS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX64, got %#06RX64",
+ Ctx.rflags.u16 & X86_EFL_STATUS_BITS, TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS);
+ if (TrapFrame.Ctx.rax.u != Ctx.rax.u)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", Ctx.rax.u, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc != uMemSrcExpect)
+ Bs3TestFailedF("Expected uMemSrc = %#06RX64, got %#06RX64", (uint64_t)uMemSrcExpect, (uint64_t)uMemSrc);
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_andn)(uint8_t bMode)
+{
+#define ANDN_CHECK_EFLAGS (uint16_t)(X86_EFL_CF | X86_EFL_ZF | X86_EFL_OF | X86_EFL_SF)
+#define ANDN_IGNORE_EFLAGS (uint16_t)(X86_EFL_AF | X86_EFL_PF) /* undefined, ignoring for now */
+ static const struct
+ {
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cbInstr;
+ RTCCUINTXREG uSrc1;
+ RTCCUINTXREG uSrc2;
+ RTCCUINTXREG uOut;
+ uint16_t fEFlags;
+ } s_aTests[] =
+ {
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ { BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_RBX_icebp), false, 5, // #0
+ 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_RBX_icebp), false, 5, // #1
+ 2, ~(RTCCUINTXREG)3, /* -> */ ~(RTCCUINTXREG)3, X86_EFL_SF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_FSxBX_icebp), true, 6, // #2
+ 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_RAX_RCX_FSxBX_icebp), true, 6, // #3
+ 2, ~(RTCCUINTXREG)3, /* -> */ ~(RTCCUINTXREG)3, X86_EFL_SF },
+
+ /* 32-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_EBX_icebp), false, 5, // #4
+ 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_EBX_icebp), false, 5, // #5
+ 2, ~(RTCCUINTXREG)7, /* -> */ ~(uint32_t)7, X86_EFL_SF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_FSxBX_icebp), true, 6, // #6
+ 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_andn_EAX_ECX_FSxBX_icebp), true, 6, // #7
+ 2, ~(RTCCUINTXREG)7, /* -> */ ~(uint32_t)7, X86_EFL_SF },
+
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+ uint32_t uStdExtFeatEbx = 0;
+ bool fSupportsAndN;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ fSupportsAndN = RT_BOOL(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_BMI1);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ bool const fOkay = !BS3_MODE_IS_RM_OR_V86(bMode) && fSupportsAndN;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRax, uExpectRip;
+ RTCCUINTXREG uMemSrc2, uMemSrc2Expect;
+
+ Ctx.rax.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ Ctx.rcx.uCcXReg = s_aTests[i].uSrc1;
+ if (!s_aTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = s_aTests[i].uSrc2;
+ uMemSrc2Expect = uMemSrc2 = ~s_aTests[i].uSrc2;
+ }
+ else
+ {
+ uMemSrc2Expect = uMemSrc2 = s_aTests[i].uSrc2;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc2);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aTests[i].pfnWorker);
+ uExpectRax = fOkay ? s_aTests[i].uOut : Ctx.rax.u;
+ uExpectRip = Ctx.rip.u + (fOkay ? s_aTests[i].cbInstr + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ /* check that nothing else really changed: */
+ || (TrapFrame.Ctx.rflags.u16 & ANDN_CHECK_EFLAGS)
+ != ((fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16) & ANDN_CHECK_EFLAGS)
+ || (TrapFrame.Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS)
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc2 != uMemSrc2Expect
+ )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTXREG_XFMT ", %#" RTCCUINTXREG_XFMT, i, s_aTests[i].uSrc1, s_aTests[i].uSrc2);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if ( (TrapFrame.Ctx.rflags.u16 & ANDN_CHECK_EFLAGS)
+ != ((fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16) & ANDN_CHECK_EFLAGS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (output)",
+ (fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16) & ANDN_CHECK_EFLAGS, TrapFrame.Ctx.rflags.u16 & ANDN_CHECK_EFLAGS);
+ if ( (TrapFrame.Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (immutable)",
+ Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS,
+ TrapFrame.Ctx.rflags.u16 & ~(ANDN_CHECK_EFLAGS | ANDN_IGNORE_EFLAGS) & X86_EFL_STATUS_BITS);
+
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc2 != uMemSrc2Expect)
+ Bs3TestFailedF("Expected uMemSrc2 = %#06RX64, got %#06RX64", (uint64_t)uMemSrc2Expect, (uint64_t)uMemSrc2);
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+/*
+ * For testing BEXTR, SHLX SARX & SHRX.
+ */
+typedef struct BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T
+{
+ RTCCUINTXREG uSrc1;
+ RTCCUINTXREG uSrc2;
+ RTCCUINTXREG uOut;
+ uint16_t fEflOut;
+} BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T;
+
+typedef struct BS3CPUINSTR2_TEST_Gy_Ey_By_T
+{
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cbInstr;
+ uint8_t cSubTests;
+ BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const *paSubTests;
+} BS3CPUINSTR2_TEST_Gy_Ey_By_T;
+
+static uint8_t bs3CpuInstr2_Common_Gy_Ey_By(uint8_t bMode, BS3CPUINSTR2_TEST_Gy_Ey_By_T const *paTests, unsigned cTests,
+ uint32_t fStdExtFeatEbx, uint16_t fEflCheck, uint16_t fEflIgnore)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j, k;
+ uint32_t uStdExtFeatEbx = 0;
+ bool fSupportsInstr;
+
+ fEflCheck &= ~fEflIgnore;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ fSupportsInstr = RT_BOOL(uStdExtFeatEbx & fStdExtFeatEbx);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < cTests; i++)
+ {
+ for (k = 0; k < paTests[i].cSubTests; k++)
+ {
+ bool const fOkay = !BS3_MODE_IS_RM_OR_V86(bMode) && fSupportsInstr;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRax, uExpectRip;
+ RTCCUINTXREG uMemSrc1, uMemSrc1Expect;
+
+ Ctx.rax.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ Ctx.rcx.uCcXReg = paTests[i].paSubTests[k].uSrc2;
+ if (!paTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = paTests[i].paSubTests[k].uSrc1;
+ uMemSrc1Expect = uMemSrc1 = ~paTests[i].paSubTests[k].uSrc1;
+ }
+ else
+ {
+ uMemSrc1Expect = uMemSrc1 = paTests[i].paSubTests[k].uSrc1;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc1);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[i].pfnWorker);
+ uExpectRax = fOkay ? paTests[i].paSubTests[k].uOut : Ctx.rax.u;
+ uExpectRip = Ctx.rip.u + (fOkay ? paTests[i].cbInstr + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ /* check that nothing else really changed: */
+ || (TrapFrame.Ctx.rflags.u16 & fEflCheck)
+ != ((fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck)
+ || (TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc1 != uMemSrc1Expect
+ )
+ {
+ Bs3TestFailedF("test #%i/%i failed: input %#" RTCCUINTXREG_XFMT ", %#" RTCCUINTXREG_XFMT,
+ i, k, paTests[i].paSubTests[k].uSrc1, paTests[i].paSubTests[k].uSrc2);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if ( (TrapFrame.Ctx.rflags.u16 & fEflCheck)
+ != ((fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (output)",
+ (fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck,
+ TrapFrame.Ctx.rflags.u16 & fEflCheck);
+ if ( (TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (immutable)",
+ Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS,
+ TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS);
+
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc1 != uMemSrc1Expect)
+ Bs3TestFailedF("Expected uMemSrc1 = %#06RX64, got %#06RX64", (uint64_t)uMemSrc1Expect, (uint64_t)uMemSrc1);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_bextr)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ {
+ { 0, RT_MAKE_U16(0, 0), /* -> */ 0, X86_EFL_ZF },
+ { 0, RT_MAKE_U16(16, 33), /* -> */ 0, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(2, 4), /* -> */ 0xe, 0},
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(40, 8), /* -> */ ARCH_BITS == 64 ? 0xff : 0x00, ARCH_BITS == 64 ? 0 : X86_EFL_ZF },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ {
+ { 0, RT_MAKE_U16(0, 0), /* -> */ 0, X86_EFL_ZF },
+ { 0, RT_MAKE_U16(16, 18), /* -> */ 0, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(2, 4), /* -> */ 0xe, 0 },
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(24, 8), /* -> */ 0xff, 0 },
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(31, 9), /* -> */ 1, 0 },
+ { ~(RTCCUINTXREG)7, RT_MAKE_U16(42, 8), /* -> */ 0, X86_EFL_ZF },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_bextr_RAX_RBX_RCX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bextr_RAX_FSxBX_RCX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bextr_EAX_EBX_ECX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_bextr_EAX_FSxBX_ECX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ X86_EFL_STATUS_BITS, X86_EFL_AF | X86_EFL_SF | X86_EFL_PF);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_bzhi)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ {
+ { 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { 0, ~(RTCCUINTXREG)255, /* -> */ 0, X86_EFL_ZF },
+ { 0, 64, /* -> */ 0, X86_EFL_ZF | X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, 64, /* -> */ ~(RTCCUINTXREG)0, X86_EFL_CF | X86_EFL_SF },
+ { ~(RTCCUINTXREG)0, 63,
+ /* -> */ ARCH_BITS >= 64 ? ~(RTCCUINTXREG)0 >> 1 : ~(RTCCUINTXREG)0, ARCH_BITS >= 64 ? 0 : X86_EFL_CF | X86_EFL_SF },
+ { ~(RTCCUINTXREG)0 << 31 | UINT32_C(0x63849607), 24, /* -> */ UINT32_C(0x00849607), 0 },
+ { ~(RTCCUINTXREG)0 << 31 | UINT32_C(0x63849607), 33,
+ /* -> */ ARCH_BITS >= 64 ? UINT64_C(0x1e3849607) : UINT32_C(0xe3849607), ARCH_BITS >= 64 ? 0 : X86_EFL_CF | X86_EFL_SF },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ {
+ { 0, 0, /* -> */ 0, X86_EFL_ZF },
+ { 0, ~(RTCCUINTXREG)255, /* -> */ 0, X86_EFL_ZF },
+ { 0, 32, /* -> */ 0, X86_EFL_ZF | X86_EFL_CF },
+ { ~(RTCCUINTXREG)0, 32, /* -> */ UINT32_MAX, X86_EFL_CF | X86_EFL_SF },
+ { ~(RTCCUINTXREG)0, 31, /* -> */ UINT32_MAX >> 1, 0 },
+ { UINT32_C(0x1230fd34), 15, /* -> */ UINT32_C(0x00007d34), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_bzhi_RAX_RBX_RCX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bzhi_RAX_FSxBX_RCX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_bzhi_EAX_EBX_ECX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_bzhi_EAX_FSxBX_ECX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI2,
+ X86_EFL_STATUS_BITS, 0);
+}
+
+
+/** @note This is a Gy_By_Ey format instruction, so we're switching the two
+ * source registers around when calling bs3CpuInstr2_Common_Gy_Ey_By.
+ * Sorry for the confusion, but it saves some unnecessary code dup. */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_pdep)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ { /* Mask (RBX/[FS:xBX]), source=RCX */
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, 0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(RTCCUINTXREG)0, 0 },
+#if ARCH_BITS >= 64
+ { UINT64_C(0x3586049947589201), ~(RTCCUINTXREG)0, /* -> */ UINT64_C(0x3586049947589201), 0 },
+ { UINT64_C(0x3586049947589201), ~(RTCCUINTXREG)7, /* -> */ UINT64_C(0x3586049947588000), 0 },
+#endif
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x47589201), 0 },
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)7, /* -> */ UINT32_C(0x47588000), 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ { /* Mask (EBX/[FS:xBX]), source=ECX */
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, 0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ UINT32_MAX, 0 },
+ { UINT32_C(0x01010101), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x01010101), 0 },
+ { UINT32_C(0x01010101), ~(RTCCUINTXREG)3, /* -> */ UINT32_C(0x01010000), 0 },
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x47589201), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_pdep_RAX_RCX_RBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_pdep_RAX_RCX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_pdep_EAX_ECX_EBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_pdep_EAX_ECX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI2, 0, 0);
+}
+
+
+/** @note Same note as for bs3CpuInstr2_pdep */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_pext)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ { /* Mask (RBX/[FS:xBX]), source=RCX */
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, 0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(RTCCUINTXREG)0, 0 },
+#if ARCH_BITS >= 64
+ { UINT64_C(0x3586049947589201), ~(RTCCUINTXREG)0, /* -> */ UINT64_C(0x00000000007fffff), 0 },
+ { UINT64_C(0x3586049947589201), ~(RTCCUINTXREG)7, /* -> */ UINT64_C(0x00000000007ffffe), 0 },
+#endif
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x000007ff), 0 },
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)7, /* -> */ UINT32_C(0x000007fe), 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ { /* Mask (EBX/[FS:xBX]), source=ECX */
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, 0, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ UINT32_MAX, 0 },
+ { UINT32_C(0x01010101), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x0000000f), 0 },
+ { UINT32_C(0x01010101), ~(RTCCUINTXREG)3, /* -> */ UINT32_C(0x0000000e), 0 },
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)0, /* -> */ UINT32_C(0x000007ff), 0 },
+ { UINT32_C(0x47589201), ~(RTCCUINTXREG)7, /* -> */ UINT32_C(0x000007fe), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_pext_RAX_RCX_RBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_pext_RAX_RCX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_pext_EAX_ECX_EBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_pext_EAX_ECX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI2, 0, 0);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_shlx)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)3, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)7, 8, /* -> */ ~(RTCCUINTXREG)0x7ff, 0},
+ { ~(RTCCUINTXREG)7, 40, /* -> */ ~(RTCCUINTXREG)7 << (ARCH_BITS == 64 ? 40 : 8), 0 },
+ { ~(RTCCUINTXREG)7, 72, /* -> */ ~(RTCCUINTXREG)7 << 8, 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)9, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)7, 8, /* -> */ UINT32_C(0xfffff800), 0 },
+ { ~(RTCCUINTXREG)7, 8, /* -> */ UINT32_C(0xfffff800), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_shlx_RAX_RBX_RCX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_shlx_RAX_FSxBX_RCX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_shlx_EAX_EBX_ECX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_shlx_EAX_FSxBX_ECX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ 0, 0);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_sarx)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)3, /* -> */ 0, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 1), RTCCINTXREG_BITS - 1, /* -> */ ~(RTCCUINTXREG)0, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 1), RTCCINTXREG_BITS - 1 + 64, /* -> */ ~(RTCCUINTXREG)0, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 2), RTCCINTXREG_BITS - 3, /* -> */ 2, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 2), RTCCINTXREG_BITS - 3 + 64, /* -> */ 2, 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)9, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0x7fffffff), 24, /* -> */ UINT32_C(0xffffff80), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0x7fffffff), 24+32, /* -> */ UINT32_C(0xffffff80), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0xbfffffff), 24, /* -> */ UINT32_C(0x40), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0xbfffffff), 24+32, /* -> */ UINT32_C(0x40), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_sarx_RAX_RBX_RCX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_sarx_RAX_FSxBX_RCX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_sarx_EAX_EBX_ECX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_sarx_EAX_FSxBX_ECX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ 0, 0);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_shrx)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests64[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)3, /* -> */ 0, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 1), RTCCINTXREG_BITS - 1, /* -> */ 1, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 1), RTCCINTXREG_BITS - 1 + 64, /* -> */ 1, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 2), RTCCINTXREG_BITS - 3, /* -> */ 2, 0 },
+ { (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 2), RTCCINTXREG_BITS - 3 + 64, /* -> */ 2, 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_Gy_Ey_By_T const s_aSubTests32[] =
+ {
+ { 0, 0, /* -> */ 0, 0 },
+ { 0, ~(RTCCUINTXREG)9, /* -> */ 0, 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0x7fffffff), 24, /* -> */ UINT32_C(0x80), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0x7fffffff), 24+32, /* -> */ UINT32_C(0x80), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0xbfffffff), 24, /* -> */ UINT32_C(0x40), 0 },
+ { ~(RTCCUINTXREG)UINT32_C(0xbfffffff), 24+32, /* -> */ UINT32_C(0x40), 0 },
+ };
+
+ static BS3CPUINSTR2_TEST_Gy_Ey_By_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_shrx_RAX_RBX_RCX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_shrx_RAX_FSxBX_RCX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_shrx_EAX_EBX_ECX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_shrx_EAX_FSxBX_ECX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_Gy_Ey_By(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ 0, 0);
+}
+
+
+/*
+ * For testing BLSR, BLSMSK, and BLSI.
+ */
+typedef struct BS3CPUINSTR2_SUBTEST_By_Ey_T
+{
+ RTCCUINTXREG uSrc;
+ RTCCUINTXREG uDst;
+ uint16_t fEflOut;
+} BS3CPUINSTR2_SUBTEST_By_Ey_T;
+
+typedef struct BS3CPUINSTR2_TEST_By_Ey_T
+{
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cbInstr;
+ uint8_t cSubTests;
+ BS3CPUINSTR2_SUBTEST_By_Ey_T const *paSubTests;
+} BS3CPUINSTR2_TEST_By_Ey_T;
+
+static uint8_t bs3CpuInstr2_Common_By_Ey(uint8_t bMode, BS3CPUINSTR2_TEST_By_Ey_T const *paTests, unsigned cTests,
+ uint32_t fStdExtFeatEbx, uint16_t fEflCheck, uint16_t fEflIgnore)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j, k;
+ uint32_t uStdExtFeatEbx = 0;
+ bool fSupportsInstr;
+
+ fEflCheck &= ~fEflIgnore;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ fSupportsInstr = RT_BOOL(uStdExtFeatEbx & fStdExtFeatEbx);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < cTests; i++)
+ {
+ for (k = 0; k < paTests[i].cSubTests; k++)
+ {
+ bool const fOkay = !BS3_MODE_IS_RM_OR_V86(bMode) && fSupportsInstr;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRax, uExpectRip;
+ RTCCUINTXREG uMemSrc, uMemSrcExpect;
+
+ Ctx.rax.uCcXReg = ~paTests[i].paSubTests[k].uSrc ^ 0x593e7591;
+ if (!paTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = paTests[i].paSubTests[k].uSrc;
+ uMemSrcExpect = uMemSrc = ~paTests[i].paSubTests[k].uSrc;
+ }
+ else
+ {
+ uMemSrcExpect = uMemSrc = paTests[i].paSubTests[k].uSrc;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[i].pfnWorker);
+ uExpectRax = fOkay ? paTests[i].paSubTests[k].uDst : Ctx.rax.u;
+ uExpectRip = Ctx.rip.u + (fOkay ? paTests[i].cbInstr + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ /* check that nothing else really changed: */
+ || (TrapFrame.Ctx.rflags.u16 & fEflCheck)
+ != ((fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck)
+ || (TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc != uMemSrcExpect
+ )
+ {
+ Bs3TestFailedF("test #%i/%i failed: input %#" RTCCUINTXREG_XFMT,
+ i, k, paTests[i].paSubTests[k].uSrc);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if ( (TrapFrame.Ctx.rflags.u16 & fEflCheck)
+ != ((fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (output)",
+ (fOkay ? paTests[i].paSubTests[k].fEflOut : Ctx.rflags.u16) & fEflCheck,
+ TrapFrame.Ctx.rflags.u16 & fEflCheck);
+ if ( (TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS)
+ != (Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (immutable)",
+ Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS,
+ TrapFrame.Ctx.rflags.u16 & ~(fEflCheck | fEflIgnore) & X86_EFL_STATUS_BITS);
+
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc != uMemSrcExpect)
+ Bs3TestFailedF("Expected uMemSrc = %#06RX64, got %#06RX64", (uint64_t)uMemSrcExpect, (uint64_t)uMemSrc);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_blsr)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests64[] =
+ {
+ { 0, /* -> */ 0, X86_EFL_ZF | X86_EFL_CF },
+ { 1, /* -> */ 0, X86_EFL_ZF },
+ { 2, /* -> */ 0, X86_EFL_ZF },
+ { 3, /* -> */ 2, 0 },
+ { 5, /* -> */ 4, 0 },
+ { 6, /* -> */ 4, 0 },
+ { 7, /* -> */ 6, 0 },
+ { 9, /* -> */ 8, 0 },
+ { 10, /* -> */ 8, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ ~(RTCCUINTXREG)3, X86_EFL_SF },
+ { (RTCCUINTXREG)3 << (RTCCINTXREG_BITS - 2), /* -> */ (RTCCUINTXREG)2 << (RTCCINTXREG_BITS - 2), X86_EFL_SF },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests32[] =
+ {
+ { 0, /* -> */ 0, X86_EFL_ZF | X86_EFL_CF },
+ { 1, /* -> */ 0, X86_EFL_ZF },
+ { ~(RTCCUINTXREG)1, /* -> */ UINT32_C(0xfffffffc), X86_EFL_SF },
+ { ~(RTCCUINTXREG)0 << 30, /* -> */ UINT32_C(0x80000000), X86_EFL_SF },
+ };
+
+ static BS3CPUINSTR2_TEST_By_Ey_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_blsr_RAX_RBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsr_RAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsr_EAX_EBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsr_EAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_By_Ey(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ X86_EFL_STATUS_BITS, 0);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_blsmsk)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests64[] =
+ {
+ { 0, /* -> */ ~(RTCCUINTXREG)0, X86_EFL_CF | X86_EFL_SF },
+ { 1, /* -> */ 1, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 3, 0 },
+ { (RTCCUINTXREG)3 << (RTCCINTXREG_BITS - 2), /* -> */ ~((RTCCUINTXREG)2 << (RTCCINTXREG_BITS - 2)), 0 },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests32[] =
+ {
+ { 0, /* -> */ UINT32_MAX, X86_EFL_CF | X86_EFL_SF },
+ { 1, /* -> */ 1, 0 },
+ { ~(RTCCUINTXREG)1, /* -> */ 3, 0 },
+ { ~(RTCCUINTXREG)0 << 30, /* -> */ UINT32_C(0x7fffffff), 0},
+ };
+
+ static BS3CPUINSTR2_TEST_By_Ey_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_blsmsk_RAX_RBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsmsk_RAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsmsk_EAX_EBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsmsk_EAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_By_Ey(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ X86_EFL_STATUS_BITS, 0);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_blsi)(uint8_t bMode)
+{
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests64[] =
+ {
+ { 0, /* -> */ 0, X86_EFL_ZF },
+ { 1, /* -> */ 1, X86_EFL_CF },
+ { ~(RTCCUINTXREG)1, /* -> */ 2, X86_EFL_CF },
+ { (RTCCUINTXREG)3 << (RTCCINTXREG_BITS - 2), /* -> */ (RTCCUINTXREG)1 << (RTCCINTXREG_BITS - 2), X86_EFL_CF },
+ };
+
+ /* 32-bit register width */
+ static BS3CPUINSTR2_SUBTEST_By_Ey_T const s_aSubTests32[] =
+ {
+ { 0, /* -> */ 0, X86_EFL_ZF },
+ { 1, /* -> */ 1, X86_EFL_CF },
+ { ~(RTCCUINTXREG)1, /* -> */ 2, X86_EFL_CF },
+ { ~(RTCCUINTXREG)0 << 30, /* -> */ UINT32_C(0x40000000), X86_EFL_CF },
+ };
+
+ static BS3CPUINSTR2_TEST_By_Ey_T const s_aTests[] =
+ {
+ { BS3_CMN_NM(bs3CpuInstr2_blsi_RAX_RBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsi_RAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests64), s_aSubTests64 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsi_EAX_EBX_icebp), false, 5, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ { BS3_CMN_NM(bs3CpuInstr2_blsi_EAX_FSxBX_icebp), true, 6, RT_ELEMENTS(s_aSubTests32), s_aSubTests32 },
+ };
+ return bs3CpuInstr2_Common_By_Ey(bMode, s_aTests, RT_ELEMENTS(s_aTests), X86_CPUID_STEXT_FEATURE_EBX_BMI1,
+ X86_EFL_STATUS_BITS, 0);
+}
+
+
+/*
+ * MULX (BMI2) - destination registers (/r & vvvv) = r/m * rDX
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_mulx)(uint8_t bMode)
+{
+ static const struct
+ {
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ bool fSameDst;
+ uint8_t cbInstr;
+ RTCCUINTXREG uSrc1;
+ RTCCUINTXREG uSrc2;
+ RTCCUINTXREG uDst1;
+ RTCCUINTXREG uDst2;
+ } s_aTests[] =
+ {
+ /* 64 bits register width (32 bits in 32- and 16-bit modes): */
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp), false, false, 5, // #0
+ 0, 0, /* -> */ 0, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp), false, false, 5, // #1
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(RTCCUINTXREG)1, 1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RCX_RCX_RBX_RDX_icebp), false, true, 5, // #2
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(RTCCUINTXREG)1, ~(RTCCUINTXREG)1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp), false, false, 5, // #3
+ 2, 2, /* -> */ 0, 4 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp), false, false, 5, // #4
+ ~(RTCCUINTXREG)0, 42, /* -> */ 0x29, ~(RTCCUINTXREG)41 },
+
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp), true, false, 6, // #5
+ 0, 0, /* -> */ 0, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp), true, false, 6, // #6
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(RTCCUINTXREG)1, 1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp), true, false, 6, // #7
+ ~(RTCCUINTXREG)0, 42, /* -> */ 0x29, ~(RTCCUINTXREG)41 },
+
+ /* 32-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp), false, false, 5, // #8
+ 0, 0, /* -> */ 0, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp), false, false, 5, // #9
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(uint32_t)1, 1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_ECX_ECX_EBX_EDX_icebp), false, true, 5, // #10
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(uint32_t)1, ~(uint32_t)1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp), false, false, 5, // #11
+ 2, 2, /* -> */ 0, 4 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp), false, false, 5, // #12
+ ~(RTCCUINTXREG)0, 42, /* -> */ 0x29, ~(uint32_t)41 },
+
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp), true, false, 6, // #13
+ 0, 0, /* -> */ 0, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp), true, false, 6, // #14
+ ~(RTCCUINTXREG)0, ~(RTCCUINTXREG)0, /* -> */ ~(uint32_t)1, 1 },
+ { BS3_CMN_NM(bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp), true, false, 6, // #15
+ ~(RTCCUINTXREG)0, 42, /* -> */ 0x29, ~(uint32_t)41 },
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+ uint32_t uStdExtFeatEbx = 0;
+ bool fSupportsAndN;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &uStdExtFeatEbx, NULL, NULL);
+ fSupportsAndN = RT_BOOL(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_BMI2);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ bool const fOkay = !BS3_MODE_IS_RM_OR_V86(bMode) && fSupportsAndN;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRax, uExpectRcx, uExpectRip;
+ RTCCUINTXREG uMemSrc1, uMemSrc1Expect;
+
+ Ctx.rax.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ Ctx.rcx.uCcXReg = RTCCUINTXREG_MAX * 4095;
+ Ctx.rdx.uCcXReg = s_aTests[i].uSrc2;
+ if (!s_aTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = s_aTests[i].uSrc1;
+ uMemSrc1Expect = uMemSrc1 = ~s_aTests[i].uSrc1;
+ }
+ else
+ {
+ uMemSrc1Expect = uMemSrc1 = s_aTests[i].uSrc1;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc1);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aTests[i].pfnWorker);
+ uExpectRax = fOkay && !s_aTests[i].fSameDst ? s_aTests[i].uDst1 : Ctx.rax.u;
+ uExpectRcx = fOkay ? s_aTests[i].uDst2 : Ctx.rcx.u;
+ uExpectRip = Ctx.rip.u + (fOkay ? s_aTests[i].cbInstr + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ || TrapFrame.Ctx.rcx.u != uExpectRcx
+ /* check that nothing else really changed: */
+ || (TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (Ctx.rflags.u16 & X86_EFL_STATUS_BITS)
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc1 != uMemSrc1Expect
+ )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTXREG_XFMT ", %#" RTCCUINTXREG_XFMT, i, s_aTests[i].uSrc1, s_aTests[i].uSrc2);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rcx.u != uExpectRcx)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", uExpectRcx, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+
+ if ( (TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (Ctx.rflags.u16 & X86_EFL_STATUS_BITS))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32 (immutable)",
+ Ctx.rflags.u16 & X86_EFL_STATUS_BITS, TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc1 != uMemSrc1Expect)
+ Bs3TestFailedF("Expected uMemSrc1 = %#06RX64, got %#06RX64", (uint64_t)uMemSrc1Expect, (uint64_t)uMemSrc1);
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+
+/*
+ * POPCNT - Intel: POPCNT; AMD: ABM.
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_popcnt)(uint8_t bMode)
+{
+ static const struct
+ {
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cWidth;
+ uint8_t cbInstr;
+ RTCCUINTXREG uSrc;
+ RTCCUINTXREG uDst;
+ uint16_t fEFlags;
+ } s_aTests[] =
+ {
+ /* 16-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_BX_icebp), false, 16, 4 + (ARCH_BITS != 16), // #0
+ 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_BX_icebp), false, 16, 4 + (ARCH_BITS != 16), // #1
+ ~(RTCCUINTXREG)0, /* -> */ 16, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_BX_icebp), false, 16, 4 + (ARCH_BITS != 16), // #2
+ UINT16_C(0xffff), /* -> */ 16, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_BX_icebp), false, 16, 4 + (ARCH_BITS != 16), // #3
+ UINT16_C(0x0304), /* -> */ 3, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_FSxBX_icebp), true, 16, 5 + (ARCH_BITS != 16), // #4
+ UINT16_C(0xd569), /* -> */ 9, 0},
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_AX_FSxBX_icebp), true, 16, 5 + (ARCH_BITS != 16), // #5
+ 0, /* -> */ 0, X86_EFL_ZF },
+
+ /* 32-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_EBX_icebp), false, 32, 4 + (ARCH_BITS == 16), // #6
+ 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_EBX_icebp), false, 32, 4 + (ARCH_BITS == 16), // #7
+ ~(RTCCUINTXREG)0, /* -> */ 32, 0},
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_EBX_icebp), false, 32, 4 + (ARCH_BITS == 16), // #8
+ UINT32_C(0x01020304), /* -> */ 5, 0},
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_FSxBX_icebp), true, 32, 5 + (ARCH_BITS == 16), // #9
+ 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_EAX_FSxBX_icebp), true, 32, 5 + (ARCH_BITS == 16), // #10
+ UINT32_C(0x49760948), /* -> */ 12, 0 },
+
+#if ARCH_BITS == 64
+ /* 64-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_RBX_icebp), false, 64, 5, // #11
+ 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_RBX_icebp), false, 64, 5, // #12
+ ~(RTCCUINTXREG)0, /* -> */ 64, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_RBX_icebp), false, 64, 5, // #13
+ UINT64_C(0x1234123412341234), /* -> */ 5*4, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_FSxBX_icebp), true, 64, 6, // #14
+ 0, /* -> */ 0, X86_EFL_ZF },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_FSxBX_icebp), true, 64, 6, // #15
+ ~(RTCCUINTXREG)0, /* -> */ 64, 0 },
+ { BS3_CMN_NM(bs3CpuInstr2_popcnt_RAX_FSxBX_icebp), true, 64, 6, // #16
+ UINT64_C(0x5908760293769087), /* -> */ 26, 0 },
+#endif
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+ bool const fSupportsPopCnt = (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ && (ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_POPCNT);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ bool const fOkay = fSupportsPopCnt;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t uExpectRax, uExpectRip;
+ RTCCUINTXREG uMemSrc, uMemSrcExpect;
+
+ Ctx.rax.uCcXReg = RTCCUINTXREG_MAX * 1019;
+ if (!s_aTests[i].fMemSrc)
+ {
+ Ctx.rbx.uCcXReg = s_aTests[i].uSrc;
+ uMemSrcExpect = uMemSrc = ~s_aTests[i].uSrc;
+ }
+ else
+ {
+ uMemSrcExpect = uMemSrc = s_aTests[i].uSrc;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc);
+ }
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aTests[i].pfnWorker);
+ uExpectRax = fOkay ? s_aTests[i].uDst : Ctx.rax.u;
+ if (s_aTests[i].cWidth == 16)
+ uExpectRax = (uExpectRax & UINT16_MAX) | (Ctx.rax.u & ~(uint64_t)UINT16_MAX);
+
+ uExpectRip = Ctx.rip.u + (fOkay ? s_aTests[i].cbInstr + 1 : 0);
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ || (TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16)
+ /* check that nothing else really changed: */
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc != uMemSrcExpect
+ )
+ {
+ Bs3TestFailedF("test #%i failed: input %#" RTCCUINTXREG_XFMT, i, s_aTests[i].uSrc);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#06RX64, got %#06RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+ if ((TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS) != (fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16))
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32",
+ fOkay ? s_aTests[i].fEFlags : Ctx.rflags.u16, TrapFrame.Ctx.rflags.u16 & X86_EFL_STATUS_BITS);
+
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc != uMemSrcExpect)
+ Bs3TestFailedF("Expected uMemSrc = %#06RX64, got %#06RX64", (uint64_t)uMemSrcExpect, (uint64_t)uMemSrc);
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+/*
+ * CRC32 - SSE4.2
+ */
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_crc32)(uint8_t bMode)
+{
+ typedef struct BS3CPUINSTR2_CRC32_VALUES_T
+ {
+ uint32_t uDstIn;
+ uint32_t uDstOut;
+ uint64_t uSrc;
+ } BS3CPUINSTR2_CRC32_VALUES_T;
+ static const BS3CPUINSTR2_CRC32_VALUES_T s_aValues1[] =
+ {
+ { UINT32_C(0000000000), UINT32_C(0000000000), UINT8_C(0000) },
+ { UINT32_C(0xffffffff), UINT32_C(0x25502c8c), UINT8_C(0xea) },
+ { UINT32_C(0x25502c8c), UINT32_C(0x474224a6), UINT8_C(0xea) },
+ { UINT32_C(0x474224a6), UINT32_C(0x0c7f9048), UINT8_C(0xea) },
+ { UINT32_C(0x0c7f9048), UINT32_C(0x39c5b9e0), UINT8_C(0x01) },
+ { UINT32_C(0x39c5b9e0), UINT32_C(0x2493fabc), UINT8_C(0x04) },
+ { UINT32_C(0x2493fabc), UINT32_C(0x0b05c4d6), UINT8_C(0x27) },
+ { UINT32_C(0x0b05c4d6), UINT32_C(0xbe26a561), UINT8_C(0x2a) },
+ { UINT32_C(0xbe26a561), UINT32_C(0xe1855652), UINT8_C(0x63) },
+ { UINT32_C(0xe1855652), UINT32_C(0xc67efe3f), UINT8_C(0xa7) },
+ { UINT32_C(0xc67efe3f), UINT32_C(0x227028cd), UINT8_C(0xfd) },
+ { UINT32_C(0x227028cd), UINT32_C(0xf4559a1d), UINT8_C(0xea) },
+ };
+ static const BS3CPUINSTR2_CRC32_VALUES_T s_aValues2[] =
+ {
+ { UINT32_C(0000000000), UINT32_C(0000000000), UINT16_C(000000) },
+ { UINT32_C(0xffffffff), UINT32_C(0xd550e2a0), UINT16_C(0x04d2) },
+ { UINT32_C(0xd550e2a0), UINT32_C(0x38e07a0a), UINT16_C(0xe8cc) },
+ { UINT32_C(0x38e07a0a), UINT32_C(0x60ebd519), UINT16_C(0x82a2) },
+ { UINT32_C(0x60ebd519), UINT32_C(0xaaa127b5), UINT16_C(0x0fff) },
+ { UINT32_C(0xaaa127b5), UINT32_C(0xb13175c6), UINT16_C(0x00ff) },
+ { UINT32_C(0xb13175c6), UINT32_C(0x3a226f1b), UINT16_C(0x0300) },
+ { UINT32_C(0x3a226f1b), UINT32_C(0xbaedef0c), UINT16_C(0x270f) },
+ { UINT32_C(0xbaedef0c), UINT32_C(0x2d18866e), UINT16_C(0x3ff6) },
+ { UINT32_C(0x2d18866e), UINT32_C(0x07e2e954), UINT16_C(0x9316) },
+ { UINT32_C(0x07e2e954), UINT32_C(0x95f82acb), UINT16_C(0xa59c) },
+ };
+ static const BS3CPUINSTR2_CRC32_VALUES_T s_aValues4[] =
+ {
+ { UINT32_C(0000000000), UINT32_C(0000000000), UINT32_C(0000000000) },
+ { UINT32_C(0xffffffff), UINT32_C(0xc9a7250e), UINT32_C(0x0270fa68) },
+ { UINT32_C(0xc9a7250e), UINT32_C(0x7340d175), UINT32_C(0x23729736) },
+ { UINT32_C(0x7340d175), UINT32_C(0x7e17b67d), UINT32_C(0x8bc75d35) },
+ { UINT32_C(0x7e17b67d), UINT32_C(0x5028eb71), UINT32_C(0x0e9bebf2) },
+ { UINT32_C(0x5028eb71), UINT32_C(0xc0a7f45a), UINT32_C(0x000001bc) },
+ { UINT32_C(0xc0a7f45a), UINT32_C(0xa96f4012), UINT32_C(0x0034ba02) },
+ { UINT32_C(0xa96f4012), UINT32_C(0xb27c0718), UINT32_C(0x0000002a) },
+ { UINT32_C(0xb27c0718), UINT32_C(0x79fb2d35), UINT32_C(0x0153158e) },
+ { UINT32_C(0x79fb2d35), UINT32_C(0x23434fc9), UINT32_C(0x02594882) },
+ { UINT32_C(0x23434fc9), UINT32_C(0x354bf3b6), UINT32_C(0xb230b8f3) },
+ };
+#if ARCH_BITS >= 64
+ static const BS3CPUINSTR2_CRC32_VALUES_T s_aValues8[] =
+ {
+ { UINT32_C(0000000000), UINT32_C(0000000000), UINT64_C(000000000000000000) },
+ { UINT32_C(0xffffffff), UINT32_C(0xadc36834), UINT64_C(0x02b0b5e2a975c1cc) },
+ { UINT32_C(0xadc36834), UINT32_C(0xf0e893c9), UINT64_C(0x823d386bf7517583) },
+ { UINT32_C(0xf0e893c9), UINT32_C(0x1a22a837), UINT64_C(0x0481f5311fa061d0) },
+ { UINT32_C(0x1a22a837), UINT32_C(0xcf8b6d61), UINT64_C(0x13fa70f64d52a92d) },
+ { UINT32_C(0xcf8b6d61), UINT32_C(0xc7dde203), UINT64_C(0x3ccc8b035903d3e1) },
+ { UINT32_C(0xc7dde203), UINT32_C(0xd42b5823), UINT64_C(0x0000011850ec2fac) },
+ { UINT32_C(0xd42b5823), UINT32_C(0x8b1ce49e), UINT64_C(0x0000000000001364) },
+ { UINT32_C(0x8b1ce49e), UINT32_C(0x1af31710), UINT64_C(0x000000057840205a) },
+ { UINT32_C(0x1af31710), UINT32_C(0xdea35e8b), UINT64_C(0x2e5d93688d9a0bfa) },
+ { UINT32_C(0xdea35e8b), UINT32_C(0x594c013a), UINT64_C(0x8ac7230489e7ffff) },
+ { UINT32_C(0x594c013a), UINT32_C(0x27b061e5), UINT64_C(0x6bf037ae325f1c71) },
+ { UINT32_C(0x27b061e5), UINT32_C(0x3120b5f7), UINT64_C(0x0fffffff34503556) },
+ };
+#endif
+ static const struct
+ {
+ FPFNBS3FAR pfnWorker;
+ bool fMemSrc;
+ uint8_t cbOp;
+ uint8_t cValues;
+ BS3CPUINSTR2_CRC32_VALUES_T const BS3_FAR *paValues;
+ } s_aTests[] =
+ {
+ /* 8-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_BL_icebp), false, 1, RT_ELEMENTS(s_aValues1), s_aValues1 },
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_byte_FSxBX_icebp), true, 1, RT_ELEMENTS(s_aValues1), s_aValues1 },
+
+ /* 16-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_BX_icebp), false, 2, RT_ELEMENTS(s_aValues2), s_aValues2 },
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_word_FSxBX_icebp), true, 2, RT_ELEMENTS(s_aValues2), s_aValues2 },
+
+ /* 32-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_EBX_icebp), false, 4, RT_ELEMENTS(s_aValues4), s_aValues4 },
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_dword_FSxBX_icebp), true, 4, RT_ELEMENTS(s_aValues4), s_aValues4 },
+#if ARCH_BITS >= 64
+ /* 32-bit register width */
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_RBX_icebp), false, 8, RT_ELEMENTS(s_aValues8), s_aValues8 },
+ { BS3_CMN_NM(bs3CpuInstr2_crc32_EAX_qword_FSxBX_icebp), true, 8, RT_ELEMENTS(s_aValues8), s_aValues8 },
+#endif
+ };
+
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned i, j;
+ bool const fSupportsCrc32 = (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ && (ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_SSE4_2);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ /*
+ * Do the tests twice, first with all flags set, then once again with
+ * flags cleared. The flags are not supposed to be touched at all.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < RT_ELEMENTS(s_aTests); i++)
+ {
+ uint8_t const cbOp = s_aTests[i].cbOp;
+ unsigned const cValues = s_aTests[i].cValues;
+ BS3CPUINSTR2_CRC32_VALUES_T const BS3_FAR *paValues = s_aTests[i].paValues;
+ unsigned iValue;
+ bool const fOkay = fSupportsCrc32;
+ uint8_t const bExpectXcpt = fOkay ? X86_XCPT_DB : X86_XCPT_UD;
+ uint64_t const uSrcGarbage = ( cbOp == 1 ? UINT64_C(0x03948314d0f03400)
+ : cbOp == 2 ? UINT64_C(0x03948314d0f00000)
+ : cbOp == 4 ? UINT64_C(0x0394831000000000) : 0)
+ & (ARCH_BITS >= 64 ? UINT64_MAX : UINT32_MAX);
+ uint64_t uExpectRip;
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aTests[i].pfnWorker);
+ uExpectRip = Ctx.rip.u + (fOkay ? ((uint8_t const BS3_FAR *)s_aTests[i].pfnWorker)[-1] + 1 : 0);
+
+ for (iValue = 0; iValue < cValues; iValue++)
+ {
+ uint64_t const uExpectRax = fOkay ? paValues[iValue].uDstOut : paValues[iValue].uDstIn;
+ uint64_t uMemSrc, uMemSrcExpect;
+
+ Ctx.rax.uCcXReg = paValues[iValue].uDstIn;
+ if (!s_aTests[i].fMemSrc)
+ {
+ Ctx.rbx.u64 = paValues[iValue].uSrc | uSrcGarbage;
+ uMemSrcExpect = uMemSrc = ~(paValues[iValue].uSrc | uSrcGarbage);
+ }
+ else
+ {
+ uMemSrcExpect = uMemSrc = paValues[iValue].uSrc | uSrcGarbage;
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, &uMemSrc);
+ }
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+
+ if ( TrapFrame.bXcpt != bExpectXcpt
+ || TrapFrame.Ctx.rip.u != uExpectRip
+ || TrapFrame.Ctx.rbx.u != Ctx.rbx.u
+ || TrapFrame.Ctx.rax.u != uExpectRax
+ /* check that nothing else really changed: */
+ || TrapFrame.Ctx.rflags.u16 != Ctx.rflags.u16
+ || TrapFrame.Ctx.rcx.u != Ctx.rcx.u
+ || TrapFrame.Ctx.rdx.u != Ctx.rdx.u
+ || TrapFrame.Ctx.rsp.u != Ctx.rsp.u
+ || TrapFrame.Ctx.rbp.u != Ctx.rbp.u
+ || TrapFrame.Ctx.rsi.u != Ctx.rsi.u
+ || TrapFrame.Ctx.rdi.u != Ctx.rdi.u
+ || uMemSrc != uMemSrcExpect
+ )
+ {
+ Bs3TestFailedF("test #%i value #%i failed: input %#RX32, %#RX64",
+ i, iValue, paValues[iValue].uDstIn, paValues[iValue].uSrc);
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bExpectXcpt, TrapFrame.bXcpt);
+ if (TrapFrame.Ctx.rip.u != uExpectRip)
+ Bs3TestFailedF("Expected RIP = %#06RX64, got %#06RX64", uExpectRip, TrapFrame.Ctx.rip.u);
+ if (TrapFrame.Ctx.rax.u != uExpectRax)
+ Bs3TestFailedF("Expected RAX = %#010RX64, got %#010RX64", uExpectRax, TrapFrame.Ctx.rax.u);
+ if (TrapFrame.Ctx.rbx.u != Ctx.rbx.u)
+ Bs3TestFailedF("Expected RBX = %#06RX64, got %#06RX64 (dst)", Ctx.rbx.u, TrapFrame.Ctx.rbx.u);
+
+ if (TrapFrame.Ctx.rflags.u16 != Ctx.rflags.u16)
+ Bs3TestFailedF("Expected EFLAGS = %#06RX32, got %#06RX32", Ctx.rflags.u16, TrapFrame.Ctx.rflags.u16);
+ if (TrapFrame.Ctx.rcx.u != Ctx.rcx.u)
+ Bs3TestFailedF("Expected RCX = %#06RX64, got %#06RX64", Ctx.rcx.u, TrapFrame.Ctx.rcx.u);
+ if (TrapFrame.Ctx.rdx.u != Ctx.rdx.u)
+ Bs3TestFailedF("Expected RDX = %#06RX64, got %#06RX64 (src)", Ctx.rdx.u, TrapFrame.Ctx.rdx.u);
+ if (TrapFrame.Ctx.rsp.u != Ctx.rsp.u)
+ Bs3TestFailedF("Expected RSP = %#06RX64, got %#06RX64", Ctx.rsp.u, TrapFrame.Ctx.rsp.u);
+ if (TrapFrame.Ctx.rbp.u != Ctx.rbp.u)
+ Bs3TestFailedF("Expected RBP = %#06RX64, got %#06RX64", Ctx.rbp.u, TrapFrame.Ctx.rbp.u);
+ if (TrapFrame.Ctx.rsi.u != Ctx.rsi.u)
+ Bs3TestFailedF("Expected RSI = %#06RX64, got %#06RX64", Ctx.rsi.u, TrapFrame.Ctx.rsi.u);
+ if (TrapFrame.Ctx.rdi.u != Ctx.rdi.u)
+ Bs3TestFailedF("Expected RDI = %#06RX64, got %#06RX64", Ctx.rdi.u, TrapFrame.Ctx.rdi.u);
+ if (uMemSrc != uMemSrcExpect)
+ Bs3TestFailedF("Expected uMemSrc = %#06RX64, got %#06RX64", (uint64_t)uMemSrcExpect, (uint64_t)uMemSrc);
+ }
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+
+ return 0;
+}
+
+#if 0 /* Program for generating CRC32 value sets: */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ int cbOp = atoi(argv[1]);
+ uint32_t uBefore = atoi(argv[2]);
+ int i = 3;
+ while (i < argc)
+ {
+ unsigned long long uValue = strtoull(argv[i], NULL, 0);
+ uint32_t uAfter = uBefore;
+ switch (cbOp)
+ {
+ case 1:
+ __asm__ __volatile__("crc32b %2, %0" : "=r" (uAfter) : "0" (uAfter), "r" ((uint8_t)uValue));
+ printf(" { UINT32_C(%#010x), UINT32_C(%#010x), UINT8_C(%#04x) },\n",
+ uBefore, uAfter, (unsigned)(uint8_t)uValue);
+ break;
+ case 2:
+ __asm__ __volatile__("crc32w %2, %0" : "=r" (uAfter) : "0" (uAfter), "r" ((uint16_t)uValue));
+ printf(" { UINT32_C(%#010x), UINT32_C(%#010x), UINT16_C(%#06x) },\n",
+ uBefore, uAfter, (unsigned)(uint16_t)uValue);
+ break;
+ case 4:
+ __asm__ __volatile__("crc32l %2, %0" : "=r" (uAfter) : "0" (uAfter), "r" ((uint32_t)uValue));
+ printf(" { UINT32_C(%#010x), UINT32_C(%#010x), UINT32_C(%#010x) },\n",
+ uBefore, uAfter, (uint32_t)uValue);
+ break;
+ case 8:
+ {
+ uint64_t u64After = uBefore;
+ __asm__ __volatile__("crc32q %2, %0" : "=r" (u64After) : "0" (u64After), "r" (uValue));
+ uAfter = (uint32_t)u64After;
+ printf(" { UINT32_C(%#010x), UINT32_C(%#010x), UINT64_C(%#018llx) },\n", uBefore, uAfter, uValue);
+ break;
+ }
+ }
+
+ /* next */
+ uBefore = uAfter;
+ i++;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ *
+ */
+# if ARCH_BITS == 64
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_cmpxchg16b)(uint8_t bMode)
+{
+ BS3REGCTX Ctx;
+ BS3REGCTX ExpectCtx;
+ BS3TRAPFRAME TrapFrame;
+ RTUINT128U au128[3];
+ PRTUINT128U pau128 = RT_ALIGN_PT(&au128[0], sizeof(RTUINT128U), PRTUINT128U);
+ bool const fSupportCX16 = RT_BOOL(ASMCpuId_ECX(1) & X86_CPUID_FEATURE_ECX_CX16);
+ unsigned iFlags;
+ unsigned offBuf;
+ unsigned iMatch;
+ unsigned iWorker;
+ static struct
+ {
+ bool fLocked;
+ uint8_t offUd2;
+ FNBS3FAR *pfnWorker;
+ } const s_aWorkers[] =
+ {
+ { false, 4, BS3_CMN_NM(bs3CpuInstr2_cmpxchg16b_rdi_ud2) },
+ { false, 5, BS3_CMN_NM(bs3CpuInstr2_o16_cmpxchg16b_rdi_ud2) },
+ { false, 5, BS3_CMN_NM(bs3CpuInstr2_repz_cmpxchg16b_rdi_ud2) },
+ { false, 5, BS3_CMN_NM(bs3CpuInstr2_repnz_cmpxchg16b_rdi_ud2) },
+ { true, 1+4, BS3_CMN_NM(bs3CpuInstr2_lock_cmpxchg16b_rdi_ud2) },
+ { true, 1+5, BS3_CMN_NM(bs3CpuInstr2_lock_o16_cmpxchg16b_rdi_ud2) },
+ { true, 1+5, BS3_CMN_NM(bs3CpuInstr2_lock_repz_cmpxchg16b_rdi_ud2) },
+ { true, 1+5, BS3_CMN_NM(bs3CpuInstr2_lock_repnz_cmpxchg16b_rdi_ud2) },
+ };
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&ExpectCtx, 0, sizeof(ExpectCtx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+ Bs3MemSet(pau128, 0, sizeof(pau128[0]) * 2);
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+ if (!fSupportCX16)
+ Bs3TestPrintf("Note! CMPXCHG16B is not supported by the CPU!\n");
+
+ /*
+ * One loop with the normal variant and one with the locked one
+ */
+ g_usBs3TestStep = 0;
+ for (iWorker = 0; iWorker < RT_ELEMENTS(s_aWorkers); iWorker++)
+ {
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, s_aWorkers[iWorker].pfnWorker);
+
+ /*
+ * One loop with all status flags set, and one with them clear.
+ */
+ Ctx.rflags.u16 |= X86_EFL_STATUS_BITS;
+ for (iFlags = 0; iFlags < 2; iFlags++)
+ {
+ Bs3MemCpy(&ExpectCtx, &Ctx, sizeof(ExpectCtx));
+
+ for (offBuf = 0; offBuf < sizeof(RTUINT128U); offBuf++)
+ {
+# define CX16_OLD_LO UINT64_C(0xabb6345dcc9c4bbd)
+# define CX16_OLD_HI UINT64_C(0x7b06ea35749549ab)
+# define CX16_MISMATCH_LO UINT64_C(0xbace3e3590f18981)
+# define CX16_MISMATCH_HI UINT64_C(0x9b385e8bfd5b4000)
+# define CX16_STORE_LO UINT64_C(0x5cbd27d251f6559b)
+# define CX16_STORE_HI UINT64_C(0x17ff434ed1b54963)
+
+ PRTUINT128U pBuf = (PRTUINT128U)&pau128->au8[offBuf];
+
+ ExpectCtx.rax.u = Ctx.rax.u = CX16_MISMATCH_LO;
+ ExpectCtx.rdx.u = Ctx.rdx.u = CX16_MISMATCH_HI;
+ for (iMatch = 0; iMatch < 2; iMatch++)
+ {
+ uint8_t bExpectXcpt;
+ pBuf->s.Lo = CX16_OLD_LO;
+ pBuf->s.Hi = CX16_OLD_HI;
+ ExpectCtx.rdi.u = Ctx.rdi.u = (uintptr_t)pBuf;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ g_usBs3TestStep++;
+ //Bs3TestPrintf("Test: iFlags=%d offBuf=%d iMatch=%u iWorker=%u\n", iFlags, offBuf, iMatch, iWorker);
+ bExpectXcpt = X86_XCPT_UD;
+ if (fSupportCX16)
+ {
+ if (offBuf & 15)
+ {
+ bExpectXcpt = X86_XCPT_GP;
+ ExpectCtx.rip.u = Ctx.rip.u;
+ ExpectCtx.rflags.u32 = Ctx.rflags.u32;
+ }
+ else
+ {
+ ExpectCtx.rax.u = CX16_OLD_LO;
+ ExpectCtx.rdx.u = CX16_OLD_HI;
+ if (iMatch & 1)
+ ExpectCtx.rflags.u32 = Ctx.rflags.u32 | X86_EFL_ZF;
+ else
+ ExpectCtx.rflags.u32 = Ctx.rflags.u32 & ~X86_EFL_ZF;
+ ExpectCtx.rip.u = Ctx.rip.u + s_aWorkers[iWorker].offUd2;
+ }
+ ExpectCtx.rflags.u32 |= X86_EFL_RF;
+ }
+ if ( !Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &ExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/,
+ 0 /*fExtraEfl*/, "lm64", 0 /*idTestStep*/)
+ || TrapFrame.bXcpt != bExpectXcpt)
+ {
+ if (TrapFrame.bXcpt != bExpectXcpt)
+ Bs3TestFailedF("Expected bXcpt=#%x, got %#x (%#x)", bExpectXcpt, TrapFrame.bXcpt, TrapFrame.uErrCd);
+ Bs3TestFailedF("^^^ iWorker=%d iFlags=%d offBuf=%d iMatch=%u\n", iWorker, iFlags, offBuf, iMatch);
+ ASMHalt();
+ }
+
+ ExpectCtx.rax.u = Ctx.rax.u = CX16_OLD_LO;
+ ExpectCtx.rdx.u = Ctx.rdx.u = CX16_OLD_HI;
+ }
+ }
+ Ctx.rflags.u16 &= ~X86_EFL_STATUS_BITS;
+ }
+ }
+
+ return 0;
+}
+
+
+static void bs3CpuInstr2_fsgsbase_ExpectUD(uint8_t bMode, PBS3REGCTX pCtx, PBS3REGCTX pExpectCtx, PBS3TRAPFRAME pTrapFrame)
+{
+ pCtx->rbx.u = 0;
+ Bs3MemCpy(pExpectCtx, pCtx, sizeof(*pExpectCtx));
+ Bs3TrapSetJmpAndRestore(pCtx, pTrapFrame);
+ pExpectCtx->rip.u = pCtx->rip.u;
+ pExpectCtx->rflags.u32 |= X86_EFL_RF;
+ if ( !Bs3TestCheckRegCtxEx(&pTrapFrame->Ctx, pExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "lm64",
+ 0 /*idTestStep*/)
+ || pTrapFrame->bXcpt != X86_XCPT_UD)
+ {
+ Bs3TestFailedF("Expected #UD, got %#x (%#x)", pTrapFrame->bXcpt, pTrapFrame->uErrCd);
+ ASMHalt();
+ }
+}
+
+
+static bool bs3CpuInstr2_fsgsbase_VerifyWorker(uint8_t bMode, PBS3REGCTX pCtx, PBS3REGCTX pExpectCtx, PBS3TRAPFRAME pTrapFrame,
+ BS3CI2FSGSBASE const *pFsGsBaseWorker, unsigned *puIter)
+{
+ bool fPassed = true;
+ unsigned iValue = 0;
+ static const struct
+ {
+ bool fGP;
+ uint64_t u64Base;
+ } s_aValues64[] =
+ {
+ { false, UINT64_C(0x0000000000000000) },
+ { false, UINT64_C(0x0000000000000001) },
+ { false, UINT64_C(0x0000000000000010) },
+ { false, UINT64_C(0x0000000000000123) },
+ { false, UINT64_C(0x0000000000001234) },
+ { false, UINT64_C(0x0000000000012345) },
+ { false, UINT64_C(0x0000000000123456) },
+ { false, UINT64_C(0x0000000001234567) },
+ { false, UINT64_C(0x0000000012345678) },
+ { false, UINT64_C(0x0000000123456789) },
+ { false, UINT64_C(0x000000123456789a) },
+ { false, UINT64_C(0x00000123456789ab) },
+ { false, UINT64_C(0x0000123456789abc) },
+ { false, UINT64_C(0x00007ffffeefefef) },
+ { false, UINT64_C(0x00007fffffffffff) },
+ { true, UINT64_C(0x0000800000000000) },
+ { true, UINT64_C(0x0000800000000000) },
+ { true, UINT64_C(0x0000800000000333) },
+ { true, UINT64_C(0x0001000000000000) },
+ { true, UINT64_C(0x0012000000000000) },
+ { true, UINT64_C(0x0123000000000000) },
+ { true, UINT64_C(0x1234000000000000) },
+ { true, UINT64_C(0xffff300000000000) },
+ { true, UINT64_C(0xffff7fffffffffff) },
+ { true, UINT64_C(0xffff7fffffffffff) },
+ { false, UINT64_C(0xffff800000000000) },
+ { false, UINT64_C(0xffffffffffeefefe) },
+ { false, UINT64_C(0xffffffffffffffff) },
+ { false, UINT64_C(0xffffffffffffffff) },
+ { false, UINT64_C(0x00000000efefefef) },
+ { false, UINT64_C(0x0000000080204060) },
+ { false, UINT64_C(0x00000000ddeeffaa) },
+ { false, UINT64_C(0x00000000fdecdbca) },
+ { false, UINT64_C(0x000000006098456b) },
+ { false, UINT64_C(0x0000000098506099) },
+ { false, UINT64_C(0x00000000206950bc) },
+ { false, UINT64_C(0x000000009740395d) },
+ { false, UINT64_C(0x0000000064a9455e) },
+ { false, UINT64_C(0x00000000d20b6eff) },
+ { false, UINT64_C(0x0000000085296d46) },
+ { false, UINT64_C(0x0000000007000039) },
+ { false, UINT64_C(0x000000000007fe00) },
+ };
+
+ Bs3RegCtxSetRipCsFromCurPtr(pCtx, pFsGsBaseWorker->pfnVerifyWorker);
+ if (pFsGsBaseWorker->f64BitOperand)
+ {
+ for (iValue = 0; iValue < RT_ELEMENTS(s_aValues64); iValue++)
+ {
+ bool const fGP = s_aValues64[iValue].fGP;
+
+ pCtx->rbx.u = s_aValues64[iValue].u64Base;
+ pCtx->rcx.u = 0;
+ pCtx->cr4.u |= X86_CR4_FSGSBASE;
+ Bs3MemCpy(pExpectCtx, pCtx, sizeof(*pExpectCtx));
+ Bs3TrapSetJmpAndRestore(pCtx, pTrapFrame);
+ pExpectCtx->rip.u = pCtx->rip.u + (!fGP ? pFsGsBaseWorker->offVerifyWorkerUd2 : 0);
+ pExpectCtx->rbx.u = !fGP ? 0 : s_aValues64[iValue].u64Base;
+ pExpectCtx->rcx.u = !fGP ? s_aValues64[iValue].u64Base : 0;
+ pExpectCtx->rflags.u32 |= X86_EFL_RF;
+ if ( !Bs3TestCheckRegCtxEx(&pTrapFrame->Ctx, pExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/,
+ 0 /*fExtraEfl*/, "lm64", 0 /*idTestStep*/)
+ || (fGP && pTrapFrame->bXcpt != X86_XCPT_GP))
+ {
+ if (fGP && pTrapFrame->bXcpt != X86_XCPT_GP)
+ Bs3TestFailedF("Expected #GP, got %#x (%#x)", pTrapFrame->bXcpt, pTrapFrame->uErrCd);
+ else
+ Bs3TestFailedF("iValue=%u\n", iValue);
+ fPassed = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (iValue = 0; iValue < RT_ELEMENTS(s_aValues64); iValue++)
+ {
+ pCtx->rbx.u = s_aValues64[iValue].u64Base;
+ pCtx->rcx.u = ~s_aValues64[iValue].u64Base;
+ pCtx->cr4.u |= X86_CR4_FSGSBASE;
+ Bs3MemCpy(pExpectCtx, pCtx, sizeof(*pExpectCtx));
+ Bs3TrapSetJmpAndRestore(pCtx, pTrapFrame);
+ pExpectCtx->rip.u = pCtx->rip.u + pFsGsBaseWorker->offVerifyWorkerUd2;
+ pExpectCtx->rbx.u = 0;
+ pExpectCtx->rcx.u = s_aValues64[iValue].u64Base & UINT64_C(0x00000000ffffffff);
+ pExpectCtx->rflags.u32 |= X86_EFL_RF;
+ if (!Bs3TestCheckRegCtxEx(&pTrapFrame->Ctx, pExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/,
+ 0 /*fExtraEfl*/, "lm64", 0 /*idTestStep*/))
+ {
+ Bs3TestFailedF("iValue=%u\n", iValue);
+ fPassed = false;
+ break;
+ }
+ }
+ }
+
+ *puIter = iValue;
+ return fPassed;
+}
+
+
+static void bs3CpuInstr2_rdfsbase_rdgsbase_Common(uint8_t bMode, BS3CI2FSGSBASE const *paFsGsBaseWorkers,
+ unsigned cFsGsBaseWorkers, uint32_t idxFsGsBaseMsr)
+{
+ BS3REGCTX Ctx;
+ BS3REGCTX ExpectCtx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned iWorker;
+ unsigned iIter;
+ uint32_t uDummy;
+ uint32_t uStdExtFeatEbx;
+ bool fSupportsFsGsBase;
+
+ ASMCpuId_Idx_ECX(7, 0, &uDummy, &uStdExtFeatEbx, &uDummy, &uDummy);
+ fSupportsFsGsBase = RT_BOOL(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_FSGSBASE);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&ExpectCtx, 0, sizeof(ExpectCtx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ for (iWorker = 0; iWorker < cFsGsBaseWorkers; iWorker++)
+ {
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paFsGsBaseWorkers[iWorker].pfnWorker);
+ if (fSupportsFsGsBase)
+ {
+ uint64_t const uBaseAddr = ASMRdMsr(idxFsGsBaseMsr);
+
+ /* CR4.FSGSBASE disabled -> #UD. */
+ Ctx.cr4.u &= ~X86_CR4_FSGSBASE;
+ bs3CpuInstr2_fsgsbase_ExpectUD(bMode, &Ctx, &ExpectCtx, &TrapFrame);
+
+ /* Read and verify existing base address. */
+ Ctx.rbx.u = 0;
+ Ctx.cr4.u |= X86_CR4_FSGSBASE;
+ Bs3MemCpy(&ExpectCtx, &Ctx, sizeof(ExpectCtx));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ ExpectCtx.rip.u = Ctx.rip.u + paFsGsBaseWorkers[iWorker].offWorkerUd2;
+ ExpectCtx.rbx.u = uBaseAddr;
+ ExpectCtx.rflags.u32 |= X86_EFL_RF;
+ if (!Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &ExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "lm64",
+ 0 /*idTestStep*/))
+ {
+ ASMHalt();
+ }
+
+ /* Write, read and verify series of base addresses. */
+ if (!bs3CpuInstr2_fsgsbase_VerifyWorker(bMode, &Ctx, &ExpectCtx, &TrapFrame, &paFsGsBaseWorkers[iWorker], &iIter))
+ {
+ Bs3TestFailedF("^^^ %s: iWorker=%u iIter=%u\n", paFsGsBaseWorkers[iWorker].pszDesc, iWorker, iIter);
+ ASMHalt();
+ }
+
+ /* Restore original base address. */
+ ASMWrMsr(idxFsGsBaseMsr, uBaseAddr);
+
+ /* Clean used GPRs. */
+ Ctx.rbx.u = 0;
+ Ctx.rcx.u = 0;
+ }
+ else
+ {
+ /* Unsupported by CPUID -> #UD. */
+ Bs3TestPrintf("Note! FSGSBASE is not supported by the CPU!\n");
+ bs3CpuInstr2_fsgsbase_ExpectUD(bMode, &Ctx, &ExpectCtx, &TrapFrame);
+ }
+ }
+}
+
+
+static void bs3CpuInstr2_wrfsbase_wrgsbase_Common(uint8_t bMode, BS3CI2FSGSBASE const *paFsGsBaseWorkers,
+ unsigned cFsGsBaseWorkers, uint32_t idxFsGsBaseMsr)
+{
+ BS3REGCTX Ctx;
+ BS3REGCTX ExpectCtx;
+ BS3TRAPFRAME TrapFrame;
+ unsigned iWorker;
+ unsigned iIter;
+ uint32_t uDummy;
+ uint32_t uStdExtFeatEbx;
+ bool fSupportsFsGsBase;
+
+ ASMCpuId_Idx_ECX(7, 0, &uDummy, &uStdExtFeatEbx, &uDummy, &uDummy);
+ fSupportsFsGsBase = RT_BOOL(uStdExtFeatEbx & X86_CPUID_STEXT_FEATURE_EBX_FSGSBASE);
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&ExpectCtx, 0, sizeof(ExpectCtx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveEx(&Ctx, bMode, 512);
+
+ for (iWorker = 0; iWorker < cFsGsBaseWorkers; iWorker++)
+ {
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paFsGsBaseWorkers[iWorker].pfnWorker);
+ if (fSupportsFsGsBase)
+ {
+ uint64_t const uBaseAddr = ASMRdMsr(idxFsGsBaseMsr);
+
+ /* CR4.FSGSBASE disabled -> #UD. */
+ Ctx.cr4.u &= ~X86_CR4_FSGSBASE;
+ bs3CpuInstr2_fsgsbase_ExpectUD(bMode, &Ctx, &ExpectCtx, &TrapFrame);
+
+ /* Write a base address. */
+ Ctx.rbx.u = 0xa0000;
+ Ctx.cr4.u |= X86_CR4_FSGSBASE;
+ Bs3MemCpy(&ExpectCtx, &Ctx, sizeof(ExpectCtx));
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapFrame);
+ ExpectCtx.rip.u = Ctx.rip.u + paFsGsBaseWorkers[iWorker].offWorkerUd2;
+ ExpectCtx.rflags.u32 |= X86_EFL_RF;
+ if (!Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &ExpectCtx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/, "lm64",
+ 0 /*idTestStep*/))
+ {
+ ASMHalt();
+ }
+
+ /* Write and read back series of base addresses. */
+ if (!bs3CpuInstr2_fsgsbase_VerifyWorker(bMode, &Ctx, &ExpectCtx, &TrapFrame, &paFsGsBaseWorkers[iWorker], &iIter))
+ {
+ Bs3TestFailedF("^^^ %s: iWorker=%u iIter=%u\n", paFsGsBaseWorkers[iWorker].pszDesc, iWorker, iIter);
+ ASMHalt();
+ }
+
+ /* Restore original base address. */
+ ASMWrMsr(idxFsGsBaseMsr, uBaseAddr);
+
+ /* Clean used GPRs. */
+ Ctx.rbx.u = 0;
+ Ctx.rcx.u = 0;
+ }
+ else
+ {
+ /* Unsupported by CPUID -> #UD. */
+ Bs3TestPrintf("Note! FSGSBASE is not supported by the CPU!\n");
+ bs3CpuInstr2_fsgsbase_ExpectUD(bMode, &Ctx, &ExpectCtx, &TrapFrame);
+ }
+ }
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_wrfsbase)(uint8_t bMode)
+{
+ bs3CpuInstr2_wrfsbase_wrgsbase_Common(bMode, s_aWrFsBaseWorkers, RT_ELEMENTS(s_aWrFsBaseWorkers), MSR_K8_FS_BASE);
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_wrgsbase)(uint8_t bMode)
+{
+ bs3CpuInstr2_wrfsbase_wrgsbase_Common(bMode, s_aWrGsBaseWorkers, RT_ELEMENTS(s_aWrGsBaseWorkers), MSR_K8_GS_BASE);
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_rdfsbase)(uint8_t bMode)
+{
+ bs3CpuInstr2_rdfsbase_rdgsbase_Common(bMode, s_aRdFsBaseWorkers, RT_ELEMENTS(s_aRdFsBaseWorkers), MSR_K8_FS_BASE);
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_NM(bs3CpuInstr2_rdgsbase)(uint8_t bMode)
+{
+ bs3CpuInstr2_rdfsbase_rdgsbase_Common(bMode, s_aRdGsBaseWorkers, RT_ELEMENTS(s_aRdGsBaseWorkers), MSR_K8_GS_BASE);
+ return 0;
+}
+
+# endif /* ARCH_BITS == 64 */
+
+#endif /* BS3_INSTANTIATING_CMN */
+
+
+
+/*
+ * Mode specific code.
+ * Mode specific code.
+ * Mode specific code.
+ */
+#ifdef BS3_INSTANTIATING_MODE
+
+
+#endif /* BS3_INSTANTIATING_MODE */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.mac
new file mode 100644
index 00000000..acb23b2d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2-template.mac
@@ -0,0 +1,845 @@
+; $Id: bs3-cpu-instr-2-template.mac $
+;; @file
+; BS3Kit - bs3-cpu-instr-2 assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+;;
+; Variant on BS3_PROC_BEGIN_CMN w/ BS3_PBC_NEAR that prefixes the function
+; with an instruction length byte.
+;
+; ASSUMES the length is between the start of the function and the .again label.
+;
+%ifndef BS3CPUINSTR2_PROC_BEGIN_CMN_DEFINED
+ %define BS3CPUINSTR2_PROC_BEGIN_CMN_DEFINED
+ %macro BS3CPUINSTR2_PROC_BEGIN_CMN 1
+ align 8, db 0cch
+ db BS3_CMN_NM(%1).again - BS3_CMN_NM(%1)
+BS3_PROC_BEGIN_CMN %1, BS3_PBC_NEAR
+ %endmacro
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+;
+; Test code snippets containing code which differs between 16-bit, 32-bit
+; and 64-bit CPUs modes.
+;
+%ifdef BS3_INSTANTIATING_CMN
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mul_xBX_ud2, BS3_PBC_NEAR
+ mul xBX
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mul_xBX_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_imul_xBX_ud2, BS3_PBC_NEAR
+ imul xBX
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_imul_xBX_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_imul_xCX_xBX_ud2, BS3_PBC_NEAR
+ imul xCX, xBX
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_imul_xCX_xBX_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_div_xBX_ud2, BS3_PBC_NEAR
+ div xBX
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_div_xBX_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_idiv_xBX_ud2, BS3_PBC_NEAR
+ idiv xBX
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_idiv_xBX_ud2
+
+
+;
+; BSF / BSR / TZCNT / LZCNT
+;
+%ifndef EMIT_BITSCAN_DEFINED
+%define EMIT_BITSCAN_DEFINED
+%macro EMIT_BITSCAN 3
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _AX_BX_ud2, BS3_PBC_NEAR
+ %2
+ %1 ax, bx
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _AX_BX_ud2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _AX_FSxBX_ud2, BS3_PBC_NEAR
+ %2
+ %1 ax, [fs:xBX]
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _AX_FSxBX_ud2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _EAX_EBX_ud2, BS3_PBC_NEAR
+ %2
+ %1 eax, ebx
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _EAX_EBX_ud2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _EAX_FSxBX_ud2, BS3_PBC_NEAR
+ %2
+ %1 eax, [fs:xBX]
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _EAX_FSxBX_ud2
+
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _RAX_RBX_ud2, BS3_PBC_NEAR
+ %2
+ %1 rax, rbx
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _RAX_RBX_ud2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %3 %+ _RAX_FSxBX_ud2, BS3_PBC_NEAR
+ %2
+ %1 rax, [fs:xBX]
+.again:
+ ud2
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %3 %+ _RAX_FSxBX_ud2
+ %endif
+%endmacro
+%endif
+
+EMIT_BITSCAN bsf, .ignored:, bsf
+EMIT_BITSCAN bsr, .ignored:, bsr
+EMIT_BITSCAN tzcnt, .ignored:, tzcnt
+EMIT_BITSCAN lzcnt, .ignored:, lzcnt
+EMIT_BITSCAN bsf, db 0f2h, f2_bsf
+EMIT_BITSCAN bsr, db 0f2h, f2_bsr
+EMIT_BITSCAN tzcnt, db 0f2h, f2_tzcnt
+EMIT_BITSCAN lzcnt, db 0f2h, f2_lzcnt
+
+
+;
+; RORX - VEX instruction with a couple of questions about non-standard encodings.
+;
+;;%define icebp ud2
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp, BS3_PBC_NEAR
+ rorx ebx, edx, 2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_RBX_RDX_2_icebp, BS3_PBC_NEAR
+%if TMPL_BITS == 64
+ rorx rbx, rdx, 2
+%else
+ db 0C4h,0E3h,0FBh,0F0h,0DAh,002h ; 32-bit ignores VEX.W=1 (10980xe)
+%endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_RBX_RDX_2_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_L1, BS3_PBC_NEAR
+ db 0C4h, 0E3h, 07Bh | 4h, 0F0h, 0DAh, 002h ; VEX.L=1 should #UD according to the docs
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_L1
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V1, BS3_PBC_NEAR
+ db 0C4h, 0E3h, 003h | ~(1 << 3), 0F0h, 0DAh, 002h ; VEX.VVVV=1 - behaviour is undocumented - 10980xe #UD
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V1
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V15, BS3_PBC_NEAR
+ db 0C4h, 0E3h, 003h | ~(15 << 3), 0F0h, 0DAh, 002h ; VEX.VVVV=15 - behaviour is not documented - 10980xe #UD
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_V15
+
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_X1, BS3_PBC_NEAR
+ db 0C4h, 0E3h & ~40h, 07Bh, 0F0h, 0DAh, 002h ; VEX.X=0 - behaviour is not documented - ignored by 10980xe
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_EDX_2_icebp_X1
+ %endif
+
+; A couple of memory variants
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_EBX_DSxDI_36_icebp, BS3_PBC_NEAR
+ rorx ebx, [xDI], 36
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_EBX_DSxDI_36_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rorx_RBX_DSxDI_68_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ rorx rbx, [xDI], 68
+ %elif TMPL_BITS == 32
+ db 0C4h,0E3h,07Bh,0F0h,01Fh,044h ; 16-bit ignores VEX.W=1 (10980xe)
+ %else
+ db 0C4h,0E3h,0FBh,0F0h,01Dh,044h ; 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_rorx_RBX_DSxDI_68_icebp
+
+;
+; ANDN (BMI1)
+;
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_andn_RAX_RCX_RBX_icebp, BS3_PBC_NEAR
+%if TMPL_BITS == 64
+ andn rax, rcx, rbx
+%else
+ db 0C4h,0E2h,0F0h,0F2h,0C3h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+%endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_andn_RAX_RCX_RBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_andn_EAX_ECX_EBX_icebp, BS3_PBC_NEAR
+ andn eax, ecx, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_andn_EAX_ECX_EBX_icebp
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_andn_RAX_RCX_FSxBX_icebp, BS3_PBC_NEAR
+%if TMPL_BITS == 64
+ andn rax, rcx, [fs:rbx]
+%elif TMPL_BITS == 32
+ db 064h,0C4h,0E2h,0F0h,0F2h,003h ; andn rax, rcx, [fs:ebx]
+%else
+ db 064h,0C4h,0E2h,0F0h,0F2h,007h ; andn rax, rcx, [fs:bx]
+%endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_andn_RAX_RCX_FSxBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_andn_EAX_ECX_FSxBX_icebp, BS3_PBC_NEAR
+ andn eax, ecx, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_andn_EAX_ECX_FSxBX_icebp
+
+
+;
+; BEXTR / SHLX / SARX / SHRX - BMI1 (opcode f7h)
+; BZHI - BMI2 (opcode f5h)
+;
+; @param %1 instruction
+; @param %2 opcode
+; @param %3 prefix
+;
+%ifndef SHLX_SARX_SHRX_DEFINED
+%define SHLX_SARX_SHRX_DEFINED
+%macro SHLX_SARX_SHRX 3
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RBX_RCX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, rbx, rcx ; SHLX=C4E2F1F7C3
+ %else
+ db 0C4h,0E2h,0F0h|%3,%2,0C3h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RBX_RCX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_EBX_ECX_icebp, BS3_PBC_NEAR
+ %1 eax, ebx, ecx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_EBX_ECX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_FSxBX_RCX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, [fs:rbx], rcx ; SHLX=64C4E2F1F703
+ %elif TMPL_BITS == 32
+ db 064h,0C4h,0E2h,0F0h|%3,%2,003h
+ %else
+ db 064h,0C4h,0E2h,0F0h|%3,%2,007h
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_FSxBX_RCX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_FSxBX_ECX_icebp, BS3_PBC_NEAR
+ %1 eax, [fs:xBX], ecx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_FSxBX_ECX_icebp
+
+%endmacro
+%endif
+
+SHLX_SARX_SHRX bextr, 0f7h, 0 ; none
+SHLX_SARX_SHRX shlx, 0f7h, 1 ; 66h
+SHLX_SARX_SHRX sarx, 0f7h, 2 ; f3h
+SHLX_SARX_SHRX shrx, 0f7h, 3 ; f2h
+SHLX_SARX_SHRX bzhi, 0f5h, 0 ; none
+
+;
+; PPEP / PEXT - BMI2 (opcode f5h)
+;
+; @param %1 instruction
+; @param %2 opcode
+; @param %3 prefix
+;
+%ifndef PDEP_PEXT_DEFINED
+%define PDEP_PEXT_DEFINED
+%macro PDEP_PEXT_ 3
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RCX_RBX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, rcx, rbx
+ %else
+ db 0C4h,0E2h,0F0h|%3,%2,0C3h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RCX_RBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_ECX_EBX_icebp, BS3_PBC_NEAR
+ %1 eax, ecx, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_ECX_EBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RCX_FSxBX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, rcx, [fs:rbx]
+ %elif TMPL_BITS == 32
+ db 064h,0C4h,0E2h,0F0h|%3,%2,003h
+ %else
+ db 064h,0C4h,0E2h,0F0h|%3,%2,007h
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RCX_FSxBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_ECX_FSxBX_icebp, BS3_PBC_NEAR
+ %1 eax, ecx, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_ECX_FSxBX_icebp
+
+%endmacro
+%endif
+
+PDEP_PEXT_ pext, 0f5h, 2 ; f3h
+PDEP_PEXT_ pdep, 0f5h, 3 ; f2h
+
+;
+; BLSR / BLSMSK / BLSI
+; These are encoded in the exact same way, only the /r differs (%2).
+;
+%ifndef BLSR_BLSMSK_BLSI_DEFINED
+%define BLSR_BLSMSK_BLSI_DEFINED
+%macro BLSR_BLSMSK_BLSI 2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RBX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, rbx ; BLSR=C4E2F8F3CB
+ %else
+ db 0C4h,0E2h,0F8h,0F3h,0C3h | (%2 << 3) ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_RBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_EBX_icebp, BS3_PBC_NEAR
+ %1 eax, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_EBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_FSxBX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ %1 rax, [fs:rbx] ; BSLR=64C4E2F8F30B
+ %elif TMPL_BITS == 32
+ db 064h,0C4h,0E2h,0F8h,0F3h,003h | (%2 << 3)
+ %else
+ db 064h,0C4h,0E2h,0F8h,0F3h,007h | (%2 << 3)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _RAX_FSxBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_FSxBX_icebp, BS3_PBC_NEAR
+ %1 eax, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_ %+ %1 %+ _EAX_FSxBX_icebp
+
+%endmacro
+%endif
+
+BLSR_BLSMSK_BLSI blsr, 1
+BLSR_BLSMSK_BLSI blsmsk, 2
+BLSR_BLSMSK_BLSI blsi, 3
+
+;
+; MULX
+;
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ mulx rax, rcx, rbx ; C4E2F3F6C3
+ %else
+ db 0C4h,0E2h,0F3h,0F6h,0C3h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_RAX_RCX_RBX_RDX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_RCX_RCX_RBX_RDX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ mulx rcx, rcx, rbx ; C4E2F3F6CB
+ %else
+ db 0C4h,0E2h,0F3h,0F6h,0CBh ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_RCX_RCX_RBX_RDX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ mulx rax, rcx, [fs:rbx] ; 64C4E2F3F603
+ %elif TMPL_BITS == 32
+ db 064h,0C4h,0E2h,0F3h,0F6h,003h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %else
+ db 064h,0C4h,0E2h,0F3h,0F6h,007h ; 32-bit & 16-bit ignores VEX.W=1 (10980xe)
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_RAX_RCX_FSxBX_RDX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp, BS3_PBC_NEAR
+ mulx eax, ecx, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_EAX_ECX_EBX_EDX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_ECX_ECX_EBX_EDX_icebp, BS3_PBC_NEAR
+ mulx ecx, ecx, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_ECX_ECX_EBX_EDX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp, BS3_PBC_NEAR
+ mulx eax, ecx, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_mulx_EAX_ECX_FSxBX_EDX_icebp
+
+
+;
+; POPCNT
+;
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_AX_BX_icebp, BS3_PBC_NEAR
+ popcnt ax, bx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_AX_BX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_EAX_EBX_icebp, BS3_PBC_NEAR
+ popcnt eax, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_EAX_EBX_icebp
+
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_RAX_RBX_icebp, BS3_PBC_NEAR
+ popcnt rax, rbx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_RAX_RBX_icebp
+ %endif
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_AX_FSxBX_icebp, BS3_PBC_NEAR
+ popcnt ax, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_AX_FSxBX_icebp
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_EAX_FSxBX_icebp, BS3_PBC_NEAR
+ popcnt eax, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_EAX_FSxBX_icebp
+
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_popcnt_RAX_FSxBX_icebp, BS3_PBC_NEAR
+ popcnt rax, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_popcnt_RAX_FSxBX_icebp
+ %endif
+
+
+;
+; CRC32
+;
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_BL_icebp
+ crc32 eax, bl
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_BL_icebp
+
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_BX_icebp
+ crc32 eax, bx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_BX_icebp
+
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_EBX_icebp
+ crc32 eax, ebx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_EBX_icebp
+
+ %if TMPL_BITS == 64
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_RBX_icebp
+ crc32 rax, rbx
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_RBX_icebp
+ %endif
+
+
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_byte_FSxBX_icebp
+ crc32 eax, byte [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_byte_FSxBX_icebp
+
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_word_FSxBX_icebp
+ crc32 eax, word [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_word_FSxBX_icebp
+
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_dword_FSxBX_icebp
+ crc32 eax, dword [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_dword_FSxBX_icebp
+
+ %if TMPL_BITS == 64
+BS3CPUINSTR2_PROC_BEGIN_CMN bs3CpuInstr2_crc32_EAX_qword_FSxBX_icebp
+ crc32 rax, qword [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr2_crc32_EAX_qword_FSxBX_icebp
+ %endif
+
+
+;
+; CMPXCHG16B
+;
+ %if TMPL_BITS == 64
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuInstr2_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_lock_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ lock cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_lock_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_o16_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ o16 cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_o16_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_lock_o16_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ db 0f0h, 066h
+ cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 6)
+BS3_PROC_END_CMN bs3CpuInstr2_lock_o16_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_repz_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ repz cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_repz_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_lock_repz_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ db 0f0h, 0f3h
+ cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 6)
+BS3_PROC_END_CMN bs3CpuInstr2_lock_repz_cmpxchg16b_rdi_ud2
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_repnz_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ repnz cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_repnz_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_lock_repnz_cmpxchg16b_rdi_ud2, BS3_PBC_NEAR
+ db 0f0h, 0f2h
+ cmpxchg16b [rdi]
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 6)
+BS3_PROC_END_CMN bs3CpuInstr2_lock_repnz_cmpxchg16b_rdi_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrfsbase_rbx_ud2, BS3_PBC_NEAR
+ wrfsbase rbx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_wrfsbase_rbx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrfsbase_ebx_ud2, BS3_PBC_NEAR
+ wrfsbase ebx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuInstr2_wrfsbase_ebx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrgsbase_rbx_ud2, BS3_PBC_NEAR
+ wrgsbase rbx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_wrgsbase_rbx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrgsbase_ebx_ud2, BS3_PBC_NEAR
+ wrgsbase ebx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuInstr2_wrgsbase_ebx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrfsbase_rbx_rdfsbase_rcx_ud2, BS3_PBC_NEAR
+ wrfsbase rbx
+ mov ebx, 0
+ rdfsbase rcx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 15)
+BS3_PROC_END_CMN bs3CpuInstr2_wrfsbase_rbx_rdfsbase_rcx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrfsbase_ebx_rdfsbase_ecx_ud2, BS3_PBC_NEAR
+ wrfsbase ebx
+ mov ebx, 0
+ rdfsbase ecx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 13)
+BS3_PROC_END_CMN bs3CpuInstr2_wrfsbase_ebx_rdfsbase_ecx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrgsbase_rbx_rdgsbase_rcx_ud2, BS3_PBC_NEAR
+ wrgsbase rbx
+ mov ebx, 0
+ rdgsbase rcx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 15)
+BS3_PROC_END_CMN bs3CpuInstr2_wrgsbase_rbx_rdgsbase_rcx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_wrgsbase_ebx_rdgsbase_ecx_ud2, BS3_PBC_NEAR
+ wrgsbase ebx
+ mov ebx, 0
+ rdgsbase ecx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 13)
+BS3_PROC_END_CMN bs3CpuInstr2_wrfgbase_ebx_rdgsbase_ecx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rdfsbase_rbx_ud2, BS3_PBC_NEAR
+ rdfsbase rbx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_rdfsbase_rbx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rdfsbase_ebx_ud2, BS3_PBC_NEAR
+ rdfsbase ebx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuInstr2_rdfsbase_ebx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rdgsbase_rbx_ud2, BS3_PBC_NEAR
+ rdgsbase rbx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 5)
+BS3_PROC_END_CMN bs3CpuInstr2_rdgsbase_rbx_ud2
+
+
+BS3_PROC_BEGIN_CMN bs3CpuInstr2_rdgsbase_ebx_ud2, BS3_PBC_NEAR
+ rdgsbase ebx
+.again:
+ ud2
+ jmp .again
+AssertCompile(.again - BS3_LAST_LABEL == 4)
+BS3_PROC_END_CMN bs3CpuInstr2_rdgsbase_ebx_ud2
+
+
+;; @todo figure out this fudge. sigh.
+times (348) db 0cch ; fudge to avoid 'rderr' during boot.
+
+ %endif ; TMPL_BITS == 64
+
+
+%endif ; BS3_INSTANTIATING_CMN
+
+%include "bs3kit-template-footer.mac" ; reset environment
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2.c
new file mode 100644
index 00000000..4cf22406
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-2.c
@@ -0,0 +1,130 @@
+/* $Id: bs3-cpu-instr-2.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-instr-2, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_mul);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_imul);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_div);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_idiv);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_bsf_tzcnt);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_bsr_lzcnt);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_andn);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_bextr);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_blsr);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_blsmsk);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_blsi);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_bzhi);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_pdep);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_pext);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_rorx);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_shlx);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_sarx);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_shrx);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_mulx);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_popcnt);
+BS3TESTMODE_PROTOTYPES_CMN(bs3CpuInstr2_crc32);
+BS3TESTMODE_PROTOTYPES_CMN_64(bs3CpuInstr2_cmpxchg16b);
+BS3TESTMODE_PROTOTYPES_CMN_64(bs3CpuInstr2_wrfsbase);
+BS3TESTMODE_PROTOTYPES_CMN_64(bs3CpuInstr2_wrgsbase);
+BS3TESTMODE_PROTOTYPES_CMN_64(bs3CpuInstr2_rdfsbase);
+BS3TESTMODE_PROTOTYPES_CMN_64(bs3CpuInstr2_rdgsbase);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEENTRY g_aModeTests[] =
+{
+#if 1
+ BS3TESTMODEENTRY_CMN("mul", bs3CpuInstr2_mul),
+ BS3TESTMODEENTRY_CMN("imul", bs3CpuInstr2_imul),
+ BS3TESTMODEENTRY_CMN("div", bs3CpuInstr2_div),
+ BS3TESTMODEENTRY_CMN("idiv", bs3CpuInstr2_idiv),
+#endif
+#if 1 /* BSF/BSR (386+) & TZCNT/LZCNT (BMI1,ABM) */
+ BS3TESTMODEENTRY_CMN("bsf/tzcnt", bs3CpuInstr2_bsf_tzcnt),
+ BS3TESTMODEENTRY_CMN("bsr/lzcnt", bs3CpuInstr2_bsr_lzcnt),
+#endif
+#if 1 /* BMI1 */
+ BS3TESTMODEENTRY_CMN("andn", bs3CpuInstr2_andn),
+ BS3TESTMODEENTRY_CMN("bextr", bs3CpuInstr2_bextr),
+ BS3TESTMODEENTRY_CMN("blsr", bs3CpuInstr2_blsr),
+ BS3TESTMODEENTRY_CMN("blsmsk", bs3CpuInstr2_blsmsk),
+ BS3TESTMODEENTRY_CMN("blsi", bs3CpuInstr2_blsi),
+#endif
+#if 1 /* BMI2 */
+ BS3TESTMODEENTRY_CMN("bzhi", bs3CpuInstr2_bzhi),
+ BS3TESTMODEENTRY_CMN("pdep", bs3CpuInstr2_pdep),
+ BS3TESTMODEENTRY_CMN("pext", bs3CpuInstr2_pext),
+ BS3TESTMODEENTRY_CMN("rorx", bs3CpuInstr2_rorx),
+ BS3TESTMODEENTRY_CMN("shlx", bs3CpuInstr2_shlx),
+ BS3TESTMODEENTRY_CMN("sarx", bs3CpuInstr2_sarx),
+ BS3TESTMODEENTRY_CMN("shrx", bs3CpuInstr2_shrx),
+ BS3TESTMODEENTRY_CMN("mulx", bs3CpuInstr2_mulx),
+#endif
+#if 1
+ BS3TESTMODEENTRY_CMN("popcnt", bs3CpuInstr2_popcnt), /* Intel: POPCNT; AMD: ABM */
+ BS3TESTMODEENTRY_CMN("crc32", bs3CpuInstr2_crc32), /* SSE4.2 */
+#endif
+#if 1
+ BS3TESTMODEENTRY_CMN_64("cmpxchg16b", bs3CpuInstr2_cmpxchg16b),
+ BS3TESTMODEENTRY_CMN_64("wrfsbase", bs3CpuInstr2_wrfsbase),
+ BS3TESTMODEENTRY_CMN_64("wrgsbase", bs3CpuInstr2_wrgsbase),
+ BS3TESTMODEENTRY_CMN_64("rdfsbase", bs3CpuInstr2_rdfsbase),
+ BS3TESTMODEENTRY_CMN_64("rdgsbase", bs3CpuInstr2_rdgsbase),
+#endif
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-cpu-instr-2");
+
+ Bs3TestDoModes_rm(g_aModeTests, RT_ELEMENTS(g_aModeTests));
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-asm.asm
new file mode 100644
index 00000000..eea2b6a6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-asm.asm
@@ -0,0 +1,48 @@
+; $Id: bs3-cpu-instr-3-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-instr-3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+;
+; Instantiate code templates.
+;
+BS3_INSTANTIATE_COMMON_TEMPLATE "bs3-cpu-instr-3-template.mac"
+BS3_INSTANTIATE_TEMPLATE_WITH_WEIRD_ONES "bs3-cpu-instr-3-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-template.mac
new file mode 100644
index 00000000..3ab84931
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3-template.mac
@@ -0,0 +1,2963 @@
+; $Id: bs3-cpu-instr-3-template.mac $
+;; @file
+; BS3Kit - bs3-cpu-instr-3 - MMX, SSE and AVX instructions, assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+;
+; Test code snippets containing code which differs between 16-bit, 32-bit
+; and 64-bit CPUs modes.
+;
+%ifdef BS3_INSTANTIATING_CMN
+
+
+;;
+; Variant on BS3_PROC_BEGIN_CMN w/ BS3_PBC_NEAR that prefixes the function
+; with an instruction length byte.
+;
+; ASSUMES the length is between the start of the function and the .again label.
+;
+ %ifndef BS3CPUINSTR3_PROC_BEGIN_CMN_DEFINED
+ %define BS3CPUINSTR3_PROC_BEGIN_CMN_DEFINED
+ %macro BS3CPUINSTR3_PROC_BEGIN_CMN 1
+ align 8, db 0cch
+ db BS3_CMN_NM(%1).again - BS3_CMN_NM(%1)
+BS3_PROC_BEGIN_CMN %1, BS3_PBC_NEAR
+ %endmacro
+ %endif
+
+;;
+; The EMIT_INSTR_PLUS_ICEBP macros is for creating a common function for and
+; named after a single instruction, followed by a looping ICEBP.
+;
+; This works like a prefix to the instruction invocation, only exception is that
+; instead of [fs:xBX] you write FSxBS as that's what is wanted in the name.
+;
+ %ifndef EMIT_INSTR_PLUS_ICEBP_DEFINED
+ %define EMIT_INSTR_PLUS_ICEBP_DEFINED
+
+ %macro EMIT_INSTR_PLUS_ICEBP 2
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _icebp
+ %define FSxBX [fs:xBX]
+ %1 %2
+ %undef FSxBX
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _icebp
+ %endmacro
+
+ %macro EMIT_INSTR_PLUS_ICEBP 3
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _icebp
+ %define FSxBX [fs:xBX]
+ %1 %2, %3
+ %undef FSxBX
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _icebp
+ %endmacro
+
+ %macro EMIT_INSTR_PLUS_ICEBP 4
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _ %+ %4 %+ _icebp
+ %define FSxBX [fs:xBX]
+ %1 %2, %3, %4
+ %undef FSxBX
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _ %+ %4 %+ _icebp
+ %endmacro
+
+ %macro EMIT_INSTR_PLUS_ICEBP 5
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _ %+ %4 %+ _ %+ %5 %+ _icebp
+ %define FSxBX [fs:xBX]
+ %1 %2, %3, %4, %5
+ %undef FSxBX
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _ %+ %2 %+ _ %+ %3 %+ _ %+ %4 %+ _ %+ %5 %+ _icebp
+ %endmacro
+
+ %endif
+
+;;
+; Companion to EMIT_INSTR_PLUS_ICEBP for dealing stuff that the assmbler does
+; not want to emit.
+;
+; @param 1 The function name (omitting bs3CpuInstr3_ and _icebp).
+; @param 2+ The opcode bytes. FSxBX_PFX and FSxBX_MODRM are defined locally.
+;
+ %ifndef EMIT_INSTR_PLUS_ICEBP_BYTES_DEFINED
+ %define EMIT_INSTR_PLUS_ICEBP_BYTES_DEFINED
+
+ %macro EMIT_INSTR_PLUS_ICEBP_BYTES 2+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _icebp
+ %define FSxBX_PFX 64h
+ %if TMPL_BITS == 16
+ %define FSxBX_MODRM 07h
+ %else
+ %define FSxBX_MODRM 03h
+ %endif
+ db %2
+ %undef FSxBX_MODRM
+ %undef FSxBX_PFX
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _icebp
+ %endmacro
+ %endif
+
+
+
+%ifndef EMIT_TYPE1_INSTR_DEFINED
+ %define EMIT_TYPE1_INSTR_DEFINED
+ ;; @param 7 Indicates whether the 2nd and 3rd pair has MMX variants.
+ %macro EMIT_TYPE1_INSTR 7
+;
+; PXOR (SSE2) & VPXOR (AVX2)
+;
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_MM2_icebp
+ %1 mm1, mm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_MM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_FSxBX_icebp
+ %1 mm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_XMM2_icebp
+ %1 xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_FSxBX_icebp
+ %1 xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _XMM1_XMM1_XMM2_icebp
+ %2 xmm1, xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _XMM1_XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _XMM1_XMM1_FSxBX_icebp
+ %2 xmm1, xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _XMM1_XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _YMM7_YMM2_YMM3_icebp
+ %2 ymm7, ymm2, ymm3
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _YMM7_YMM2_YMM3_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _YMM7_YMM2_FSxBX_icebp
+ %2 ymm7, ymm2, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _YMM7_YMM2_FSxBX_icebp
+
+
+;
+; XORPS (SSE2) & VXORPS (AVX)
+;
+ %if %7 != 0
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %3 %+ _MM1_MM2_icebp
+ %3 mm1, mm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %3 %+ _MM1_MM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %3 %+ _MM1_FSxBX_icebp
+ %3 mm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %3 %+ _MM1_FSxBX_icebp
+ %endif
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %3 %+ _XMM1_XMM2_icebp
+ %3 xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %3 %+ _XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %3 %+ _XMM1_FSxBX_icebp
+ %3 xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %3 %+ _XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %4 %+ _XMM1_XMM1_XMM2_icebp
+ %4 xmm1, xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %4 %+ _XMM1_XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %4 %+ _XMM1_XMM1_FSxBX_icebp
+ %4 xmm1, xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %4 %+ _XMM1_XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %4 %+ _YMM1_YMM1_YMM2_icebp
+ %4 ymm1, ymm1, ymm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %4 %+ _YMM1_YMM1_YMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %4 %+ _YMM1_YMM1_FSxBX_icebp
+ %4 ymm1, ymm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %4 %+ _YMM1_YMM1_FSxBX_icebp
+
+
+
+;
+; XORPD (SSE2) & VXORPD (AVX)
+;
+ %if %7 != 0
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %5 %+ _MM1_MM2_icebp
+ %5 mm1, mm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %5 %+ _MM1_MM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %5 %+ _MM1_FSxBX_icebp
+ %5 mm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %5 %+ _MM1_FSxBX_icebp
+ %endif
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %5 %+ _XMM1_XMM2_icebp
+ %5 xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %5 %+ _XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %5 %+ _XMM1_FSxBX_icebp
+ %5 xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %5 %+ _XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %6 %+ _XMM2_XMM1_XMM0_icebp
+ %6 xmm2, xmm1, xmm0
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %6 %+ _XMM2_XMM1_XMM0_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %6 %+ _XMM2_XMM1_FSxBX_icebp
+ %6 xmm2, xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %6 %+ _XMM2_XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %6 %+ _YMM2_YMM1_YMM0_icebp
+ %6 ymm2, ymm1, ymm0
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %6 %+ _YMM2_YMM1_YMM0_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %6 %+ _YMM2_YMM1_FSxBX_icebp
+ %6 ymm2, ymm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %6 %+ _YMM2_YMM1_FSxBX_icebp
+
+ %if TMPL_BITS == 64
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %6 %+ _YMM10_YMM8_YMM15_icebp
+ %6 ymm10, ymm8, ymm15
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %6 %+ _YMM10_YMM8_YMM15_icebp
+ %endif
+
+ %endmacro ; EMIT_TYPE1_INSTR
+
+ %macro EMIT_TYPE1_ONE_INSTR 3
+ %if %3 != 0
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_MM2_icebp
+ %1 mm1, mm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_MM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_FSxBX_icebp
+ %1 mm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _MM1_FSxBX_icebp
+ %endif
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_XMM2_icebp
+ %1 xmm1, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_FSxBX_icebp
+ %1 xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _XMM2_XMM1_XMM0_icebp
+ %2 xmm2, xmm1, xmm0
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _XMM2_XMM1_XMM0_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _XMM2_XMM1_FSxBX_icebp
+ %2 xmm2, xmm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _XMM2_XMM1_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _YMM2_YMM1_YMM0_icebp
+ %2 ymm2, ymm1, ymm0
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _YMM2_YMM1_YMM0_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _YMM2_YMM1_FSxBX_icebp
+ %2 ymm2, ymm1, [fs:xBX]
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _YMM2_YMM1_FSxBX_icebp
+
+ %if TMPL_BITS == 64
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _YMM10_YMM8_YMM15_icebp
+ %2 ymm10, ymm8, ymm15
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _YMM10_YMM8_YMM15_icebp
+ %endif
+ %endmacro ; EMIT_TYPE1_ONE_INSTR
+
+%endif
+
+EMIT_TYPE1_INSTR pand, vpand, andps, vandps, andpd, vandpd, 0
+EMIT_TYPE1_INSTR pandn, vpandn, andnps, vandnps, andnpd, vandnpd, 0
+EMIT_TYPE1_INSTR por, vpor, orps, vorps, orpd, vorpd, 0
+EMIT_TYPE1_INSTR pxor, vpxor, xorps, vxorps, xorpd, vxorpd, 0
+
+EMIT_TYPE1_INSTR pcmpgtb, vpcmpgtb, pcmpgtw, vpcmpgtw, pcmpgtd, vpcmpgtd, 1
+EMIT_TYPE1_ONE_INSTR pcmpgtq, vpcmpgtq, 0
+EMIT_TYPE1_INSTR pcmpeqb, vpcmpeqb, pcmpeqw, vpcmpeqw, pcmpeqd, vpcmpeqd, 1
+EMIT_TYPE1_ONE_INSTR pcmpeqq, vpcmpeqq, 0
+
+EMIT_TYPE1_INSTR paddb, vpaddb, paddw, vpaddw, paddd, vpaddd, 1
+EMIT_TYPE1_ONE_INSTR paddq, vpaddq, 1
+
+EMIT_TYPE1_INSTR psubb, vpsubb, psubw, vpsubw, psubd, vpsubd, 1
+EMIT_TYPE1_ONE_INSTR psubq, vpsubq, 1
+
+
+;
+; Type 2 instructions. On the form: pxxxx sAX, [zy]mm0
+;
+%ifndef EMIT_TYPE2_ONE_INSTR_DEFINED
+ %define EMIT_TYPE2_ONE_INSTR_DEFINED
+ ;; @param 1 MMX/SSE instruction name
+ ;; @param 2 AVX instruction name
+ ;; @param 3 Whether to emit MMX function
+ ;; @param 4 The opcode byte. (assuming two byte / vex map 1)
+ %macro EMIT_TYPE2_ONE_INSTR 4
+ %if %3 != 0
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_MM2_icebp
+ %1 eax, mm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_MM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_qword_FSxBX_icebp
+ %if TMPL_BITS == 16
+ db 64h, 0fh, %4, 7 ; %1 eax, qword [fs:xBX]
+ %else
+ db 64h, 0fh, %4, 3 ; %1 eax, qword [fs:xBX]
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_qword_FSxBX_icebp
+ %endif
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_XMM2_icebp
+ %1 eax, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_dqword_FSxBX_icebp
+ %if TMPL_BITS == 16
+ db 64h, 66h, 0fh, %4, 7 ; %1 eax, dqword [fs:xBX]
+ %else
+ db 64h, 66h, 0fh, %4, 3 ; %1 eax, dqword [fs:xBX]
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %1 %+ _EAX_dqword_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_XMM2_icebp
+ %2 eax, xmm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_XMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_dqword_FSxBX_icebp
+ %if TMPL_BITS == 16
+ db 64h, 0c4h, 0e0h, 071h, %4, 7 ; %2 eax, dqword [fs:xBX]
+ %else
+ db 64h, 0c4h, 0e0h, 071h, %4, 3 ; %2 eax, dqword [fs:xBX]
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_dqword_FSxBX_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_YMM2_icebp
+ %2 eax, ymm2
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_YMM2_icebp
+
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_qqword_FSxBX_icebp
+ %if TMPL_BITS == 16
+ db 64h, 0c4h, 0e0h, 075h, %4, 7 ; %2 eax, qqword [fs:xBX]
+ %else
+ db 64h, 0c4h, 0e0h, 075h, %4, 3 ; %2 eax, qqword [fs:xBX]
+ %endif
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _EAX_qqword_FSxBX_icebp
+
+ %if TMPL_BITS == 64
+BS3CPUINSTR3_PROC_BEGIN_CMN bs3CpuInstr3_ %+ %2 %+ _RAX_YMM9_icebp
+ %2 rax, ymm9
+.again:
+ icebp
+ jmp .again
+BS3_PROC_END_CMN bs3CpuInstr3_ %+ %2 %+ _RAX_YMM9_icebp
+ %endif
+ %endmacro ; EMIT_TYPE2_ONE_INSTR
+%endif
+
+EMIT_TYPE2_ONE_INSTR pmovmskb, vpmovmskb, 1, 0d7h
+
+;
+; [V]PMULLW
+;
+EMIT_INSTR_PLUS_ICEBP pmullw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmullw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pmullw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmullw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmullw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmullw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmullw, XMM1, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmullw, XMM1, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmullw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmullw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmullw, YMM1, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpmullw, YMM1, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmullw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmullw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMULLD
+;
+EMIT_INSTR_PLUS_ICEBP pmulld, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmulld, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmulld, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmulld, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulld, XMM2, XMM1, XMM0
+EMIT_INSTR_PLUS_ICEBP vpmulld, XMM2, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulld, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmulld, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulld, YMM2, YMM1, YMM0
+EMIT_INSTR_PLUS_ICEBP vpmulld, YMM2, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulld, YMM10, YMM8, YMM15
+EMIT_INSTR_PLUS_ICEBP vpmulld, YMM10, YMM8, FSxBX
+ %endif
+
+;
+; [V]PMULHW
+;
+EMIT_INSTR_PLUS_ICEBP pmulhw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmulhw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pmulhw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmulhw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmulhw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmulhw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulhw, XMM1, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmulhw, XMM1, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulhw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulhw, YMM1, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpmulhw, YMM1, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulhw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMULHUW
+;
+EMIT_INSTR_PLUS_ICEBP pmulhuw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmulhuw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pmulhuw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmulhuw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmulhuw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmulhuw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, XMM1, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, XMM1, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, YMM1, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, YMM1, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhuw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PSHUFB
+;
+EMIT_INSTR_PLUS_ICEBP pshufb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pshufb, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pshufb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pshufb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pshufb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pshufb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpshufb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpshufb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpshufb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpshufb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpshufb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpshufb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpshufb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpshufb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; PSHUFW
+;
+EMIT_INSTR_PLUS_ICEBP pshufw, MM1, MM2, 0FFh ; FF = top src word in all destination words
+EMIT_INSTR_PLUS_ICEBP pshufw, MM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshufw, MM1, MM2, 01Bh ; 1B = word swap (like bswap but for words)
+EMIT_INSTR_PLUS_ICEBP pshufw, MM1, FSxBX, 01Bh
+
+;
+; [V]PSHUFHW
+;
+EMIT_INSTR_PLUS_ICEBP pshufhw, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshufhw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshufhw, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP pshufhw, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshufhw, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM1, YMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM1, YMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM1, FSxBX, 01Bh
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM12, YMM7, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufhw, YMM9, YMM12, 01Bh
+ %endif
+
+;
+; [V]PSHUFLW
+;
+EMIT_INSTR_PLUS_ICEBP pshuflw, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshuflw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshuflw, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP pshuflw, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshuflw, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM1, YMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM1, YMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM1, FSxBX, 01Bh
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM12, YMM7, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshuflw, YMM9, YMM12, 01Bh
+ %endif
+
+;
+; [V]PSHUFD
+;
+EMIT_INSTR_PLUS_ICEBP pshufd, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshufd, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pshufd, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP pshufd, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshufd, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufd, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufd, XMM1, XMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshufd, XMM1, FSxBX, 01Bh
+
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM1, YMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM1, YMM2, 01Bh
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM1, FSxBX, 01Bh
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM12, YMM7, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpshufd, YMM9, YMM12, 01Bh
+ %endif
+
+;
+; [V]PUNPCKHBW
+;
+EMIT_INSTR_PLUS_ICEBP punpckhbw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpckhbw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpckhbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpckhbw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpckhbw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpckhbw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhbw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKHWD
+;
+EMIT_INSTR_PLUS_ICEBP punpckhwd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpckhwd, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpckhwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpckhwd, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpckhwd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpckhwd, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhwd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKHDQ
+;
+EMIT_INSTR_PLUS_ICEBP punpckhdq, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpckhdq, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpckhdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpckhdq, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpckhdq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpckhdq, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhdq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKHQDQ (no MMX)
+;
+EMIT_INSTR_PLUS_ICEBP punpckhqdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpckhqdq, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpckhqdq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpckhqdq, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckhqdq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLBW
+;
+EMIT_INSTR_PLUS_ICEBP punpcklbw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpcklbw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpcklbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpcklbw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpcklbw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpcklbw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklbw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLWD
+;
+EMIT_INSTR_PLUS_ICEBP punpcklwd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpcklwd, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpcklwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpcklwd, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpcklwd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpcklwd, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklwd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLDQ
+;
+EMIT_INSTR_PLUS_ICEBP punpckldq, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP punpckldq, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP punpckldq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpckldq, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpckldq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpckldq, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpckldq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLQDQ (no MMX)
+;
+EMIT_INSTR_PLUS_ICEBP punpcklqdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP punpcklqdq, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP punpcklqdq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP punpcklqdq, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpunpcklqdq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PACKSSWB
+;
+EMIT_INSTR_PLUS_ICEBP packsswb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP packsswb, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP packsswb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP packsswb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP packsswb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP packsswb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpacksswb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpacksswb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpacksswb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpacksswb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpacksswb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpacksswb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpacksswb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpacksswb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PACKSSWD
+;
+EMIT_INSTR_PLUS_ICEBP packssdw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP packssdw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP packssdw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP packssdw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP packssdw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP packssdw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackssdw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpackssdw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackssdw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpackssdw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackssdw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpackssdw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackssdw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpackssdw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PACKUSWB
+;
+EMIT_INSTR_PLUS_ICEBP packuswb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP packuswb, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP packuswb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP packuswb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP packuswb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP packuswb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackuswb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpackuswb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackuswb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpackuswb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackuswb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpackuswb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackuswb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpackuswb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PACKUSWD (no MMX)
+;
+EMIT_INSTR_PLUS_ICEBP packusdw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP packusdw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP packusdw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP packusdw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackusdw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpackusdw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackusdw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpackusdw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpackusdw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpackusdw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpackusdw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpackusdw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXUB
+;
+EMIT_INSTR_PLUS_ICEBP pmaxub, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmaxub, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pmaxub, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxub, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxub, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxub, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxub, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxub, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxub, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxub, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxub, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxub, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxub, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxub, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXUW
+;
+EMIT_INSTR_PLUS_ICEBP pmaxuw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxuw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxuw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxuw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxuw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXUD
+;
+EMIT_INSTR_PLUS_ICEBP pmaxud, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxud, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxud, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxud, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxud, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxud, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxud, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxud, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxud, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxud, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxud, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxud, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXSB
+;
+EMIT_INSTR_PLUS_ICEBP pmaxsb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxsb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxsb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxsb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXSW
+;
+EMIT_INSTR_PLUS_ICEBP pmaxsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmaxsw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pmaxsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxsw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxsw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMAXSD
+;
+EMIT_INSTR_PLUS_ICEBP pmaxsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaxsd, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaxsd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaxsd, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaxsd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINUB
+;
+EMIT_INSTR_PLUS_ICEBP pminub, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pminub, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pminub, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminub, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminub, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminub, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminub, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminub, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminub, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminub, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminub, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminub, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminub, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminub, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINUW
+;
+EMIT_INSTR_PLUS_ICEBP pminuw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminuw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminuw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminuw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminuw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminuw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminuw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminuw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminuw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminuw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminuw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminuw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINUD
+;
+EMIT_INSTR_PLUS_ICEBP pminud, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminud, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminud, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminud, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminud, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminud, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminud, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminud, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminud, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminud, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminud, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminud, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINSB
+;
+EMIT_INSTR_PLUS_ICEBP pminsb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminsb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminsb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminsb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminsb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminsb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminsb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminsb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINSW
+;
+EMIT_INSTR_PLUS_ICEBP pminsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pminsw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pminsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminsw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminsw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminsw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminsw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMINSD
+;
+EMIT_INSTR_PLUS_ICEBP pminsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pminsd, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pminsd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pminsd, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpminsd, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpminsd, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpminsd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpminsd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpminsd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpminsd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]MOVNTDQA
+;
+EMIT_INSTR_PLUS_ICEBP movntdqa, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovntdqa, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovntdqa, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movntdqa, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovntdqa, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovntdqa, YMM12, FSxBX
+ %endif
+
+;
+; [V]MOVNTDQ
+;
+EMIT_INSTR_PLUS_ICEBP movntdq, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntdq, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntdq, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movntdq, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovntdq, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovntdq, FSxBX, YMM10
+ %endif
+
+
+;
+; [V]MOVNTPS
+;
+EMIT_INSTR_PLUS_ICEBP movntps, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntps, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntps, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movntps, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovntps, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovntps, FSxBX, YMM12
+ %endif
+
+;
+; [V]MOVNTPD
+;
+EMIT_INSTR_PLUS_ICEBP movntpd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntpd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovntpd, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movntpd, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovntpd, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovntpd, FSxBX, YMM12
+ %endif
+
+;
+; [V]MOVUPS - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movups, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movups, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movups, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovups, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovups, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovups, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovups, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovups, YMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovups, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movups, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movups, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP movups, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovups, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovups, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovups, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovups, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovups, YMM12, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovups, FSxBX, YMM12
+ %endif
+
+;
+; [V]MOVUPD - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movupd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movupd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movupd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovupd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovupd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovupd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovupd, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovupd, YMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovupd, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movupd, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movupd, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP movupd, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovupd, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovupd, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovupd, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovupd, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovupd, YMM12, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovupd, FSxBX, YMM12
+ %endif
+
+;
+; [V]MOVSS - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movss, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movss, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movss, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovss, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovss, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovss, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movss, XMM11, XMM8
+EMIT_INSTR_PLUS_ICEBP movss, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movss, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovss, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovss, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovss, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVSD - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movsd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movsd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovsd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsd, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movsd, XMM11, XMM8
+EMIT_INSTR_PLUS_ICEBP movsd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movsd, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovsd, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovsd, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsd, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVLPS
+;
+EMIT_INSTR_PLUS_ICEBP movlps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movlps, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovlps, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovlps, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movlps, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movlps, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovlps, XMM10, XMM14, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovlps, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVLPD
+;
+EMIT_INSTR_PLUS_ICEBP movlpd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movlpd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovlpd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovlpd, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movlpd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movlpd, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovlpd, XMM10, XMM14, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovlpd, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVHPS
+;
+EMIT_INSTR_PLUS_ICEBP movhps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movhps, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovhps, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovhps, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movhps, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movhps, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovhps, XMM10, XMM14, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovhps, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVHPD
+;
+EMIT_INSTR_PLUS_ICEBP movhpd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movhpd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovhpd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovhpd, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movhpd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP movhpd, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovhpd, XMM10, XMM14, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovhpd, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVHLPS
+;
+EMIT_INSTR_PLUS_ICEBP movhlps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovhlps, XMM1, XMM2, XMM3
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movhlps, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP vmovhlps, XMM10, XMM14, XMM12
+ %endif
+
+;
+; [V]MOVSLDUP
+;
+EMIT_INSTR_PLUS_ICEBP movsldup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movsldup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsldup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovsldup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsldup, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovsldup, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movsldup, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movsldup, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsldup, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovsldup, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovsldup, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovsldup, YMM12, FSxBX
+ %endif
+
+;
+; [V]MOVSHDUP
+;
+EMIT_INSTR_PLUS_ICEBP movshdup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movshdup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovshdup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovshdup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovshdup, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovshdup, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movshdup, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movshdup, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovshdup, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovshdup, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovshdup, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovshdup, YMM12, FSxBX
+ %endif
+
+;
+; [V]MOVDDUP
+;
+EMIT_INSTR_PLUS_ICEBP movddup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movddup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovddup, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovddup, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovddup, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovddup, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movddup, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movddup, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovddup, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovddup, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovddup, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovddup, YMM12, FSxBX
+ %endif
+
+;
+; [V]MOVAPS
+;
+EMIT_INSTR_PLUS_ICEBP movaps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movaps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovaps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovaps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovaps, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovaps, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movaps, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movaps, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovaps, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovaps, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovaps, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovaps, YMM12, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP movapd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movapd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovapd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovapd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovapd, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vmovapd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movapd, XMM8, XMM12
+EMIT_INSTR_PLUS_ICEBP movapd, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovapd, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovapd, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovapd, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovapd, YMM12, FSxBX
+ %endif
+
+;
+; [V]MOVD
+;
+EMIT_INSTR_PLUS_ICEBP movd, MM1, EDX
+EMIT_INSTR_PLUS_ICEBP movd, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movd, EAX, MM1
+EMIT_INSTR_PLUS_ICEBP movd, FSxBX, MM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movd, MM1, R9D
+EMIT_INSTR_PLUS_ICEBP movd, R10D, MM0
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP movd, XMM1, EAX
+EMIT_INSTR_PLUS_ICEBP movd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP movd, EAX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movd, XMM9, R8D
+EMIT_INSTR_PLUS_ICEBP movd, R8D, XMM9
+EMIT_INSTR_PLUS_ICEBP movd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP movd, FSxBX, XMM9
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vmovd, XMM1, EAX
+EMIT_INSTR_PLUS_ICEBP vmovd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovd, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovd, EDX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vmovd, XMM9, R9D
+EMIT_INSTR_PLUS_ICEBP vmovd, R8D, XMM9
+EMIT_INSTR_PLUS_ICEBP vmovd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovd, FSxBX, XMM9
+ %endif
+
+;
+; [V]MOVQ - some hand coded stuff here as the assembler prefers the 7f/6f variants.
+;
+EMIT_INSTR_PLUS_ICEBP movq, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP movq, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movq, FSxBX, MM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movq, R9, MM1
+EMIT_INSTR_PLUS_ICEBP movq, MM1, R9
+EMIT_INSTR_PLUS_ICEBP_BYTES 06e_movq_MM1_FSxBX, FSxBX_PFX, 48h, 0fh, 06eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP_BYTES 07e_movq_FSxBX_MM1, FSxBX_PFX, 48h, 0fh, 07eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP movq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP movq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movq, FSxBX, XMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movq, XMM9, R8
+EMIT_INSTR_PLUS_ICEBP movq, R8, XMM9
+EMIT_INSTR_PLUS_ICEBP movq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP movq, FSxBX, XMM9
+EMIT_INSTR_PLUS_ICEBP_BYTES 06e_movq_XMM1_FSxBX, FSxBX_PFX, 66h, 48h, 0fh, 06eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP_BYTES 06e_movq_XMM9_FSxBX, FSxBX_PFX, 66h, 4ch, 0fh, 06eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP_BYTES 07e_movq_FSxBX_XMM1, FSxBX_PFX, 66h, 48h, 0fh, 07eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP_BYTES 07e_movq_FSxBX_XMM9, FSxBX_PFX, 66h, 4ch, 0fh, 07eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vmovq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vmovq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP_BYTES 06e_vmovq_XMM1_FSxBX, FSxBX_PFX, 0c4h, 0e1h, 0f9h, 06eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP vmovq, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP_BYTES 07e_vmovq_FSxBX_XMM1, FSxBX_PFX, 0c4h, 0e1h, 0f9h, 07eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vmovq, XMM9, R8
+EMIT_INSTR_PLUS_ICEBP vmovq, R8, XMM9
+EMIT_INSTR_PLUS_ICEBP vmovq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovq, FSxBX, XMM9
+EMIT_INSTR_PLUS_ICEBP_BYTES 06e_vmovq_XMM9_FSxBX, FSxBX_PFX, 0c4h, 061h, 0f9h, 06eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+EMIT_INSTR_PLUS_ICEBP_BYTES 07e_vmovq_FSxBX_XMM9, FSxBX_PFX, 0c4h, 061h, 0f9h, 07eh, FSxBX_MODRM | (1 << X86_MODRM_REG_SHIFT)
+ %endif
+
+;
+; [V]MOVDQU - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movdqu, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_movdqu_XMM1_XMM2, 0f3h, 00fh, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP movdqu, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movdqu, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovdqu, XMM1, XMM2 ; C5 FA 6F CA
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqu_XMM1_XMM2, 0c5h, 0fah, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP vmovdqu, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqu, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovdqu, YMM1, YMM2 ; C5 FE 6F CA
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqu_YMM1_YMM2, 0c5h, 0feh, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP vmovdqu, YMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqu, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movdqu, XMM8, XMM12 ; F3 45 0F 6F C4
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_movdqu_XMM8_XMM12, 0f3h, 045h, 00fh, 07fh, X86_MODRM_MAKE(3, 4, 0)
+EMIT_INSTR_PLUS_ICEBP movdqu, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP movdqu, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovdqu, XMM7, XMM14
+EMIT_INSTR_PLUS_ICEBP vmovdqu, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqu, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovdqu, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovdqu, YMM12, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqu, FSxBX, YMM12
+ %endif
+
+;
+; [V]MOVDQA - not testing the 2nd register variant.
+;
+EMIT_INSTR_PLUS_ICEBP movdqa, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_movdqa_XMM1_XMM2, 066h, 00fh, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP movdqa, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP movdqa, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovdqa, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqa_XMM1_XMM2, 0c5h, 0f9h, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP vmovdqa, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqa, FSxBX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovdqa, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqa_YMM1_YMM2, 0c5h, 0fdh, 07fh, X86_MODRM_MAKE(3, 2, 1)
+EMIT_INSTR_PLUS_ICEBP vmovdqa, YMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqa, FSxBX, YMM1
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movdqa, XMM8, XMM12 ; 66 45 0F 6F C4
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_movdqa_XMM8_XMM12, 066h, 045h, 00fh, 07fh, X86_MODRM_MAKE(3, 4, 0)
+EMIT_INSTR_PLUS_ICEBP movdqa, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP movdqa, FSxBX, XMM10
+EMIT_INSTR_PLUS_ICEBP vmovdqa, XMM8, XMM14 ; C4 C1 79 6F FE
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqa_XMM8_XMM14, 0c4h, 041h, 79h, 07fh, X86_MODRM_MAKE(3, 6, 0)
+EMIT_INSTR_PLUS_ICEBP vmovdqa, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqa, FSxBX, XMM11
+EMIT_INSTR_PLUS_ICEBP vmovdqa, YMM12, YMM8
+EMIT_INSTR_PLUS_ICEBP_BYTES 07f_vmovdqa_YMM12_YMM8, 0c4h, 041h, 7dh, 07fh, X86_MODRM_MAKE(3, 0, 4)
+EMIT_INSTR_PLUS_ICEBP vmovdqa, YMM12, FSxBX
+EMIT_INSTR_PLUS_ICEBP vmovdqa, FSxBX, YMM12
+ %endif
+
+;
+; [V]PTEST
+;
+EMIT_INSTR_PLUS_ICEBP ptest, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP ptest, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vptest, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vptest, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vptest, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vptest, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP ptest, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP ptest, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vptest, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vptest, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vptest, YMM9, YMM8
+EMIT_INSTR_PLUS_ICEBP vptest, YMM9, FSxBX
+ %endif
+
+;
+; [V]PAVGB
+;
+EMIT_INSTR_PLUS_ICEBP pavgb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pavgb, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pavgb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pavgb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pavgb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pavgb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpavgb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpavgb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpavgb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpavgb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpavgb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpavgb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpavgb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpavgb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PAVGW
+;
+EMIT_INSTR_PLUS_ICEBP pavgw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pavgw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP pavgw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pavgw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pavgw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pavgw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpavgw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpavgw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpavgw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpavgw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpavgw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpavgw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpavgw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpavgw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PSIGNB
+;
+EMIT_INSTR_PLUS_ICEBP psignb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP psignb, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP psignb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP psignb, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP psignb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP psignb, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignb, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpsignb, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignb, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpsignb, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignb, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpsignb, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignb, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpsignb, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PSIGNW
+;
+EMIT_INSTR_PLUS_ICEBP psignw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP psignw, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP psignw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP psignw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP psignw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP psignw, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpsignw, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpsignw, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpsignw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpsignw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PSIGND
+;
+EMIT_INSTR_PLUS_ICEBP psignd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP psignd, MM1, FSxBX
+
+EMIT_INSTR_PLUS_ICEBP psignd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP psignd, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP psignd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP psignd, XMM8, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpsignd, XMM1, XMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpsignd, XMM8, XMM9, FSxBX
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpsignd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpsignd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpsignd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpsignd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]ABSB
+;
+EMIT_INSTR_PLUS_ICEBP pabsb, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pabsb, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pabsb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pabsb, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpabsb, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsb, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpabsb, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pabsb, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pabsb, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsb, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpabsb, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsb, YMM9, YMM8
+EMIT_INSTR_PLUS_ICEBP vpabsb, YMM9, FSxBX
+ %endif
+
+;
+; [V]ABSW
+;
+EMIT_INSTR_PLUS_ICEBP pabsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pabsw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pabsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pabsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpabsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsw, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpabsw, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pabsw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pabsw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpabsw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsw, YMM9, YMM8
+EMIT_INSTR_PLUS_ICEBP vpabsw, YMM9, FSxBX
+ %endif
+
+;
+; [V]ABSD
+;
+EMIT_INSTR_PLUS_ICEBP pabsd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pabsd, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pabsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pabsd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpabsd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsd, YMM1, YMM2
+EMIT_INSTR_PLUS_ICEBP vpabsd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pabsd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pabsd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpabsd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpabsd, YMM9, YMM8
+EMIT_INSTR_PLUS_ICEBP vpabsd, YMM9, FSxBX
+ %endif
+
+;
+; [V]PHADDW
+;
+EMIT_INSTR_PLUS_ICEBP phaddw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phaddw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phaddw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phaddw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphaddw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphaddw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phaddw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phaddw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphaddw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphaddw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PHADDD
+;
+EMIT_INSTR_PLUS_ICEBP phaddd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phaddd, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phaddd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phaddd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphaddd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphaddd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phaddd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phaddd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphaddd, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphaddd, YMM8, YMM9, FSxBX
+ %endif
+
+
+;
+; [V]PHSUBW
+;
+EMIT_INSTR_PLUS_ICEBP phsubw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phsubw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phsubw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phsubw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphsubw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphsubw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phsubw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phsubw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphsubw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphsubw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PHSUBD
+;
+EMIT_INSTR_PLUS_ICEBP phsubd, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phsubd, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phsubd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phsubd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphsubd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphsubd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phsubd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phsubd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphsubd, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphsubd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PHADDSW
+;
+EMIT_INSTR_PLUS_ICEBP phaddsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phaddsw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phaddsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phaddsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphaddsw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphaddsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phaddsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phaddsw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphaddsw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphaddsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphaddsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PHSUBSW
+;
+EMIT_INSTR_PLUS_ICEBP phsubsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP phsubsw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP phsubsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phsubsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vphsubsw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vphsubsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phsubsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP phsubsw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vphsubsw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphsubsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vphsubsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMADDUBSW
+;
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmaddubsw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmaddubsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMULHRSW
+;
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmulhrsw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmulhrsw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PSADBW
+;
+EMIT_INSTR_PLUS_ICEBP psadbw, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP psadbw, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP psadbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP psadbw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpsadbw, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpsadbw, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpsadbw, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpsadbw, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP psadbw, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP psadbw, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpsadbw, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpsadbw, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpsadbw, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpsadbw, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMULDQ
+;
+EMIT_INSTR_PLUS_ICEBP pmuldq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmuldq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuldq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmuldq, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuldq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmuldq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmuldq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmuldq, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuldq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmuldq, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuldq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmuldq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMULUDQ
+;
+EMIT_INSTR_PLUS_ICEBP pmuludq, MM1, MM2
+EMIT_INSTR_PLUS_ICEBP pmuludq, MM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP pmuludq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmuludq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuludq, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vpmuludq, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuludq, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vpmuludq, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmuludq, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pmuludq, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuludq, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vpmuludq, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmuludq, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vpmuludq, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLPS
+;
+EMIT_INSTR_PLUS_ICEBP unpcklps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP unpcklps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklps, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vunpcklps, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklps, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vunpcklps, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP unpcklps, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP unpcklps, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklps, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vunpcklps, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklps, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vunpcklps, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKLPD
+;
+EMIT_INSTR_PLUS_ICEBP unpcklpd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP unpcklpd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP unpcklpd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP unpcklpd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vunpcklpd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKHPS
+;
+EMIT_INSTR_PLUS_ICEBP unpckhps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP unpckhps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhps, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vunpckhps, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhps, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vunpckhps, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP unpckhps, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP unpckhps, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhps, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vunpckhps, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhps, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vunpckhps, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PUNPCKHPD
+;
+EMIT_INSTR_PLUS_ICEBP unpckhpd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP unpckhpd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, XMM1, XMM2, XMM3
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, XMM1, XMM2, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, YMM1, YMM2, YMM3
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, YMM1, YMM2, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP unpckhpd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP unpckhpd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, XMM8, XMM9, XMM10
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, XMM8, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, YMM8, YMM9, YMM10
+EMIT_INSTR_PLUS_ICEBP vunpckhpd, YMM8, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXBW
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxbw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxbw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxbw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbw, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXBD
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxbd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxbd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxbd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxbd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbd, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXBQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxbq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxbq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxbq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxbq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxbq, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXWD
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxwd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxwd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxwd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxwd, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXWQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxwq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxwq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxwq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxwq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxwq, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVSXDQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovsxdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovsxdq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovsxdq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovsxdq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovsxdq, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXBW
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxbw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxbw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxbw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbw, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXBD
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxbd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxbd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxbd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxbd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbd, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXBQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxbq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxbq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxbq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxbq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxbq, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXWD
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxwd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxwd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxwd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxwd, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXWQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxwq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxwq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxwq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxwq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxwq, YMM9, FSxBX
+ %endif
+
+;
+; [V]PMOVZXDQ
+;
+EMIT_INSTR_PLUS_ICEBP pmovzxdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pmovzxdq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, YMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pmovzxdq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP pmovzxdq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, YMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vpmovzxdq, YMM9, FSxBX
+ %endif
+
+;
+; [V]SHUFPS
+;
+EMIT_INSTR_PLUS_ICEBP shufps, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufps, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufps, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP shufps, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM1, XMM2, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM1, YMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP shufps, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufps, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufps, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP shufps, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vshufps, XMM8, XMM9, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vshufps, YMM8, YMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]SHUFPD
+;
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM1, XMM2, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM1, YMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP shufpd, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vshufpd, XMM8, XMM9, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vshufpd, YMM8, YMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]LDDQU
+;
+EMIT_INSTR_PLUS_ICEBP lddqu, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vlddqu, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vlddqu, YMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP lddqu, XMM10, FSxBX
+EMIT_INSTR_PLUS_ICEBP vlddqu, XMM11, FSxBX
+EMIT_INSTR_PLUS_ICEBP vlddqu, YMM12, FSxBX
+ %endif
+
+;
+; [V]PHMINPOSUW
+;
+EMIT_INSTR_PLUS_ICEBP phminposuw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP phminposuw, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphminposuw, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP vphminposuw, XMM1, FSxBX
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP phminposuw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP phminposuw, XMM9, FSxBX
+EMIT_INSTR_PLUS_ICEBP vphminposuw, XMM9, XMM8
+EMIT_INSTR_PLUS_ICEBP vphminposuw, XMM9, FSxBX
+ %endif
+
+;
+; [V]PBLENDVB
+;
+EMIT_INSTR_PLUS_ICEBP pblendvb, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP pblendvb, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpblendvb, XMM1, XMM2, XMM3, XMM4
+EMIT_INSTR_PLUS_ICEBP vpblendvb, XMM1, XMM2, FSxBX, XMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pblendvb, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP pblendvb, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vpblendvb, XMM8, XMM9, XMM10, XMM11
+EMIT_INSTR_PLUS_ICEBP vpblendvb, XMM8, XMM9, FSxBX, XMM11
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vpblendvb, YMM1, YMM2, YMM3, YMM4
+EMIT_INSTR_PLUS_ICEBP vpblendvb, YMM1, YMM2, FSxBX, YMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vpblendvb, YMM8, YMM9, YMM10, YMM11
+EMIT_INSTR_PLUS_ICEBP vpblendvb, YMM8, YMM9, FSxBX, YMM11
+ %endif
+
+;
+; [V]BLENDVPS
+;
+EMIT_INSTR_PLUS_ICEBP blendvps, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP blendvps, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vblendvps, XMM1, XMM2, XMM3, XMM4
+EMIT_INSTR_PLUS_ICEBP vblendvps, XMM1, XMM2, FSxBX, XMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP blendvps, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP blendvps, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vblendvps, XMM8, XMM9, XMM10, XMM11
+EMIT_INSTR_PLUS_ICEBP vblendvps, XMM8, XMM9, FSxBX, XMM11
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vblendvps, YMM1, YMM2, YMM3, YMM4
+EMIT_INSTR_PLUS_ICEBP vblendvps, YMM1, YMM2, FSxBX, YMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vblendvps, YMM8, YMM9, YMM10, YMM11
+EMIT_INSTR_PLUS_ICEBP vblendvps, YMM8, YMM9, FSxBX, YMM11
+ %endif
+
+;
+; [V]BLENDVPD
+;
+EMIT_INSTR_PLUS_ICEBP blendvpd, XMM1, XMM2
+EMIT_INSTR_PLUS_ICEBP blendvpd, XMM1, FSxBX
+EMIT_INSTR_PLUS_ICEBP vblendvpd, XMM1, XMM2, XMM3, XMM4
+EMIT_INSTR_PLUS_ICEBP vblendvpd, XMM1, XMM2, FSxBX, XMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP blendvpd, XMM8, XMM9
+EMIT_INSTR_PLUS_ICEBP blendvpd, XMM8, FSxBX
+EMIT_INSTR_PLUS_ICEBP vblendvpd, XMM8, XMM9, XMM10, XMM11
+EMIT_INSTR_PLUS_ICEBP vblendvpd, XMM8, XMM9, FSxBX, XMM11
+ %endif
+
+EMIT_INSTR_PLUS_ICEBP vblendvpd, YMM1, YMM2, YMM3, YMM4
+EMIT_INSTR_PLUS_ICEBP vblendvpd, YMM1, YMM2, FSxBX, YMM4
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP vblendvpd, YMM8, YMM9, YMM10, YMM11
+EMIT_INSTR_PLUS_ICEBP vblendvpd, YMM8, YMM9, FSxBX, YMM11
+ %endif
+
+;
+; [V]PALIGNR
+;
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, MM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, MM2, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, MM2, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, MM2, 009h
+EMIT_INSTR_PLUS_ICEBP palignr, MM1, FSxBX, 009h
+
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, XMM2, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, XMM2, 013h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM1, FSxBX, 013h
+
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, XMM3, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, XMM3, 013h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM1, XMM2, FSxBX, 013h
+
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, YMM3, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, YMM3, 013h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM1, YMM2, FSxBX, 013h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, XMM9, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, XMM9, 013h
+EMIT_INSTR_PLUS_ICEBP palignr, XMM8, FSxBX, 013h
+
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, XMM10, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, XMM10, 013h
+EMIT_INSTR_PLUS_ICEBP vpalignr, XMM8, XMM9, FSxBX, 013h
+
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, FSxBX, 000h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, YMM10, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, FSxBX, 003h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, YMM10, 013h
+EMIT_INSTR_PLUS_ICEBP vpalignr, YMM8, YMM9, FSxBX, 013h
+ %endif
+
+;
+; [V]PBLENDW
+;
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM1, XMM2, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM1, YMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP pblendw, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vpblendw, XMM8, XMM9, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vpblendw, YMM8, YMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]BLENDPS
+;
+EMIT_INSTR_PLUS_ICEBP blendps, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendps, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendps, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP blendps, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM1, XMM2, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM1, YMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP blendps, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendps, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendps, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP blendps, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vblendps, XMM8, XMM9, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vblendps, YMM8, YMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]BLENDPD
+;
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM1, XMM2, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM1, YMM2, YMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM1, YMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM1, YMM2, YMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM1, YMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP blendpd, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vblendpd, XMM8, XMM9, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM8, YMM9, YMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM8, YMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM8, YMM9, YMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vblendpd, YMM8, YMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]PCLMULQDQ
+;
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM1, XMM2, 0FFh
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM1, XMM2, 000h
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM1, XMM2, XMM3, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM1, XMM2, XMM3, 000h
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM1, XMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM8, XMM9, 0FFh
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM8, XMM9, 000h
+EMIT_INSTR_PLUS_ICEBP pclmulqdq, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM8, XMM9, XMM10, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM8, XMM9, XMM10, 000h
+EMIT_INSTR_PLUS_ICEBP vpclmulqdq, XMM8, XMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]PINSRW
+;
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, EDX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, EDX, 000h
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM1, EDX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM1, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM1, EDX, 000h
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM1, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM1, XMM2, EDX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM1, XMM2, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM1, XMM2, EDX, 000h
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM1, XMM2, FSxBX, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, R9D, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, MM1, R9D, 000h
+
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM8, R9D, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM8, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM8, R9D, 000h
+EMIT_INSTR_PLUS_ICEBP pinsrw, XMM8, FSxBX, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM8, XMM9, R9D, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM8, XMM9, FSxBX, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM8, XMM9, R9D, 000h
+EMIT_INSTR_PLUS_ICEBP vpinsrw, XMM8, XMM9, FSxBX, 000h
+ %endif
+
+;
+; [V]PEXTRW
+;
+EMIT_INSTR_PLUS_ICEBP pextrw, EDX, MM1, 0FFh
+EMIT_INSTR_PLUS_ICEBP pextrw, EDX, MM1, 000h
+
+EMIT_INSTR_PLUS_ICEBP pextrw, EDX, XMM1, 0FFh
+EMIT_INSTR_PLUS_ICEBP pextrw, EDX, XMM1, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpextrw, EDX, XMM1, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpextrw, EDX, XMM1, 000h
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP pextrw, R9D, MM1, 0FFh
+EMIT_INSTR_PLUS_ICEBP pextrw, R9D, MM1, 000h
+
+; @todo Emits the SSE4.1 0f3a variant EMIT_INSTR_PLUS_ICEBP pextrw, RDX, XMM1, 0FFh
+; @todo Emits the SSE4.1 0f3a variant EMIT_INSTR_PLUS_ICEBP pextrw, RDX, XMM1, 000h
+
+EMIT_INSTR_PLUS_ICEBP pextrw, R9D, XMM8, 0FFh
+EMIT_INSTR_PLUS_ICEBP pextrw, R9D, XMM8, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpextrw, R9D, XMM8, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpextrw, R9D, XMM8, 000h
+
+EMIT_INSTR_PLUS_ICEBP vpextrw, RDX, XMM1, 0FFh
+EMIT_INSTR_PLUS_ICEBP vpextrw, RDX, XMM1, 000h
+ %endif
+
+;
+; [V]MOVMSKPS
+;
+EMIT_INSTR_PLUS_ICEBP movmskps, EDX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovmskps, EDX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovmskps, EDX, YMM1
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movmskps, R9D, XMM8
+EMIT_INSTR_PLUS_ICEBP movmskps, RDX, XMM1
+
+EMIT_INSTR_PLUS_ICEBP vmovmskps, R9D, XMM8
+EMIT_INSTR_PLUS_ICEBP vmovmskps, RDX, XMM1
+
+EMIT_INSTR_PLUS_ICEBP vmovmskps, R9D, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovmskps, RDX, YMM1
+ %endif
+
+;
+; [V]MOVMSKPD
+;
+EMIT_INSTR_PLUS_ICEBP movmskpd, EDX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, EDX, XMM1
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, EDX, YMM1
+
+ %if TMPL_BITS == 64
+EMIT_INSTR_PLUS_ICEBP movmskpd, R9D, XMM8
+EMIT_INSTR_PLUS_ICEBP movmskpd, RDX, XMM1
+
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, R9D, XMM8
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, RDX, XMM1
+
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, R9D, YMM8
+EMIT_INSTR_PLUS_ICEBP vmovmskpd, RDX, YMM1
+ %endif
+
+
+%endif ; BS3_INSTANTIATING_CMN
+
+%include "bs3kit-template-footer.mac" ; reset environment
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3.c32 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3.c32
new file mode 100644
index 00000000..2d1e7c28
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-instr-3.c32
@@ -0,0 +1,12210 @@
+/* $Id: bs3-cpu-instr-3.c32 $ */
+/** @file
+ * BS3Kit - bs3-cpu-instr-3 - MMX, SSE and AVX instructions, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define BS3_FNBS3FAR_PROTOTYPES_CMN(a_BaseNm) \
+ extern FNBS3FAR RT_CONCAT(a_BaseNm, _c16); \
+ extern FNBS3FAR RT_CONCAT(a_BaseNm, _c32); \
+ extern FNBS3FAR RT_CONCAT(a_BaseNm, _c64)
+
+
+/** Converts an execution mode (BS3_MODE_XXX) into an index into an array
+ * initialized by BS3CPUINSTR3_TEST1_MODES_INIT,
+ * BS3CPUINSTR3_TEST2_MODES_INIT, BS3CPUINSTR3_TEST3_MODES_INIT, ... . */
+#define BS3CPUINSTR3_TEST_MODES_INDEX(a_bMode) (BS3_MODE_IS_16BIT_CODE(bMode) ? 0 : BS3_MODE_IS_32BIT_CODE(bMode) ? 1 : 2)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Instruction set type and operand width. */
+typedef enum
+{
+ T_INVALID,
+ T_MMX,
+ T_MMX_SSE, /**< MMX instruction, but require the SSE CPUID to work. */
+ T_MMX_SSE2, /**< MMX instruction, but require the SSE2 CPUID to work. */
+ T_MMX_SSSE3, /**< MMX instruction, but require the SSSE3 CPUID to work. */
+ T_AXMMX,
+ T_AXMMX_OR_SSE,
+ T_SSE,
+ T_128BITS = T_SSE,
+ T_SSE2,
+ T_SSE3,
+ T_SSSE3,
+ T_SSE4_1,
+ T_SSE4_2,
+ T_SSE4A,
+ T_PCLMUL,
+ T_AVX_128,
+ T_AVX2_128,
+ T_AVX_PCLMUL,
+ T_AVX_256,
+ T_256BITS = T_AVX_256,
+ T_AVX2_256,
+ T_MAX
+} INPUT_TYPE_T;
+
+/** Memory or register rm variant. */
+enum {
+ RM_REG = 0,
+ RM_MEM,
+ RM_MEM32, /**< Memory operand is 32 bits. Hack for movss and similar. */
+ RM_MEM64 /**< Memory operand is 64 bits. Hack for movss and similar. */
+};
+
+/**
+ * Execution environment configuration.
+ */
+typedef struct BS3CPUINSTR3_CONFIG_T
+{
+ uint16_t fCr0Mp : 1;
+ uint16_t fCr0Em : 1;
+ uint16_t fCr0Ts : 1;
+ uint16_t fCr4OsFxSR : 1;
+ uint16_t fCr4OsXSave : 1;
+ uint16_t fXcr0Sse : 1;
+ uint16_t fXcr0Avx : 1;
+ /** x87 exception pending (IE + something unmasked). */
+ uint16_t fX87XcptPending : 1;
+ /** Aligned memory operands. If zero, they will be misaligned and tests w/o memory ops skipped. */
+ uint16_t fAligned : 1;
+ uint16_t fAlignCheck : 1;
+ uint16_t fMxCsrMM : 1; /**< AMD only */
+ uint8_t bXcptMmx;
+ uint8_t bXcptSse;
+ uint8_t bXcptAvx;
+} BS3CPUINSTR3_CONFIG_T;
+/** Pointer to an execution environment configuration. */
+typedef BS3CPUINSTR3_CONFIG_T const BS3_FAR *PCBS3CPUINSTR3_CONFIG_T;
+
+/** State saved by bs3CpuInstr3ConfigReconfigure. */
+typedef struct BS3CPUINSTR3_CONFIG_SAVED_T
+{
+ uint32_t uCr0;
+ uint32_t uCr4;
+ uint32_t uEfl;
+ uint16_t uFcw;
+ uint16_t uFsw;
+ uint32_t uMxCsr;
+} BS3CPUINSTR3_CONFIG_SAVED_T;
+typedef BS3CPUINSTR3_CONFIG_SAVED_T BS3_FAR *PBS3CPUINSTR3_CONFIG_SAVED_T;
+typedef BS3CPUINSTR3_CONFIG_SAVED_T const BS3_FAR *PCBS3CPUINSTR3_CONFIG_SAVED_T;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static bool g_afTypeSupports[T_MAX] = { false, false, false, false, false, false, false, false, false };
+static bool g_fAmdMisalignedSse = false;
+
+/** Size of g_pbBuf - at least three pages. */
+static uint32_t g_cbBuf;
+/** Buffer of g_cbBuf size. */
+static uint8_t BS3_FAR *g_pbBuf;
+
+/** Exception type \#1 test configurations, 16 & 32 bytes strictly aligned. */
+static const BS3CPUINSTR3_CONFIG_T g_aXcptConfig1[] =
+{
+/*
+ * X87 SSE SSE SSE AVX AVX AVX MMX MMX+SSE MMX+AVX AMD/SSE <-- applies to
+ * +AVX +AMD/SSE
+ * CR0 CR0 CR0 CR4 CR4 XCR0 XCR0 FCW MXCSR
+ * MP, EM, TS, OSFXSR, OSXSAVE, SSE, AVX, ES+, fAligned, AC/AM, MM, bXcptMmx, bXcptSse, bXcptAvx */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #0 */
+ { 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #1 */
+ { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_DB }, /* #2 */
+ { 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_NM, X86_XCPT_NM, X86_XCPT_NM }, /* #3 */
+ { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_NM }, /* #4 */
+ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_UD, X86_XCPT_DB }, /* #5 */
+ { 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #6 */
+ { 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #7 */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #8 */
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, X86_XCPT_MF, X86_XCPT_DB, X86_XCPT_DB }, /* #9 - pending x87 exception */
+ /* Memory misalignment and alignment checks: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, X86_XCPT_DB, X86_XCPT_GP, X86_XCPT_GP }, /* #10 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, X86_XCPT_AC, X86_XCPT_GP, X86_XCPT_GP }, /* #11 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #12 */
+ /* AMD only: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_GP }, /* #13 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, X86_XCPT_AC, X86_XCPT_AC, X86_XCPT_GP }, /* #14 */
+};
+
+/** Exception type \#4 test configurations, 16 & 32 byte not strictly aligned. */
+static const BS3CPUINSTR3_CONFIG_T g_aXcptConfig4[] =
+{
+/*
+ * X87 SSE SSE SSE AVX AVX AVX MMX MMX+SSE MMX+AVX AMD/SSE <-- applies to
+ * +AVX +AMD/SSE
+ * CR0 CR0 CR0 CR4 CR4 XCR0 XCR0 FCW MXCSR
+ * MP, EM, TS, OSFXSR, OSXSAVE, SSE, AVX, ES+, fAligned, AC/AM, MM, bXcptMmx, bXcptSse, bXcptAvx */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #0 */
+ { 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #1 */
+ { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_DB }, /* #2 */
+ { 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_NM, X86_XCPT_NM, X86_XCPT_NM }, /* #3 */
+ { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_NM }, /* #4 */
+ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_UD, X86_XCPT_DB }, /* #5 */
+ { 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #6 */
+ { 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #7 */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #8 */
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, X86_XCPT_MF, X86_XCPT_DB, X86_XCPT_DB }, /* #9 - pending x87 exception */
+ /* Memory misalignment and alignment checks: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, X86_XCPT_DB, X86_XCPT_GP, X86_XCPT_DB }, /* #10 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, X86_XCPT_AC, X86_XCPT_GP, X86_XCPT_AC }, /* #11 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #12 */
+ /* AMD only: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #13 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, X86_XCPT_AC, X86_XCPT_AC, X86_XCPT_AC }, /* #14 */
+};
+
+/** Exception type \#4 test configurations, for the SSE version of movups. */
+static const BS3CPUINSTR3_CONFIG_T g_aXcptConfig4Unaligned[] =
+{
+/*
+ * X87 SSE SSE SSE AVX AVX AVX MMX MMX+SSE MMX+AVX AMD/SSE <-- applies to
+ * +AVX +AMD/SSE
+ * CR0 CR0 CR0 CR4 CR4 XCR0 XCR0 FCW MXCSR
+ * MP, EM, TS, OSFXSR, OSXSAVE, SSE, AVX, ES+, fAligned, AC/AM, MM, bXcptMmx, bXcptSse, bXcptAvx */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #0 */
+ { 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #1 */
+ { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_DB }, /* #2 */
+ { 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_NM, X86_XCPT_NM, X86_XCPT_NM }, /* #3 */
+ { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_NM }, /* #4 */
+ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_UD, X86_XCPT_DB }, /* #5 */
+ { 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #6 */
+ { 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #7 */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #8 */
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, X86_XCPT_MF, X86_XCPT_DB, X86_XCPT_DB }, /* #9 - pending x87 exception */
+ /* Memory misalignment and alignment checks: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #10 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, X86_XCPT_AC, X86_XCPT_DB, X86_XCPT_AC }, /* #11 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #12 */
+ /* AMD only: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #13 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, X86_XCPT_AC, X86_XCPT_AC, X86_XCPT_AC }, /* #14 */
+};
+
+/** Exception type \#5 test configurations, less than 16 byte operands. */
+static const BS3CPUINSTR3_CONFIG_T g_aXcptConfig5[] =
+{
+/*
+ * X87 SSE SSE SSE AVX AVX AVX MMX MMX+SSE MMX+AVX AMD/SSE <-- applies to
+ * +AVX +AMD/SSE
+ * CR0 CR0 CR0 CR4 CR4 XCR0 XCR0 FCW MXCSR
+ * MP, EM, TS, OSFXSR, OSXSAVE, SSE, AVX, ES+, fAligned, AC/AM, MM, bXcptMmx, bXcptSse, bXcptAvx */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #0 */
+ { 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #1 */
+ { 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_DB }, /* #2 */
+ { 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_NM, X86_XCPT_NM, X86_XCPT_NM }, /* #3 */
+ { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_UD, X86_XCPT_UD, X86_XCPT_NM }, /* #4 */
+ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_UD, X86_XCPT_DB }, /* #5 */
+ { 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #6 */
+ { 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #7 */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_UD }, /* #8 */
+ { 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, X86_XCPT_MF, X86_XCPT_DB, X86_XCPT_DB }, /* #9 - pending x87 exception */
+ /* Memory misalignment and alignment checks: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #10 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, X86_XCPT_AC, X86_XCPT_AC, X86_XCPT_AC }, /* #11 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #12 */
+ /* AMD only: */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, X86_XCPT_DB, X86_XCPT_DB, X86_XCPT_DB }, /* #13 */
+ { 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, X86_XCPT_AC, X86_XCPT_AC, X86_XCPT_AC }, /* #14 */
+};
+
+
+
+/**
+ * Reconfigures the execution environment according to @a pConfig.
+ *
+ * Call bs3CpuInstr3ConfigRestore to undo the changes.
+ *
+ * @returns true on success, false if the configuration cannot be applied. In
+ * the latter case, no context changes are made.
+ * @param pSavedCfg Where to save state we modify.
+ * @param pCtx The register context to modify.
+ * @param pExtCtx The extended register context to modify.
+ * @param pConfig The configuration to apply.
+ * @param bMode The target mode.
+ */
+static bool bs3CpuInstr3ConfigReconfigure(PBS3CPUINSTR3_CONFIG_SAVED_T pSavedCfg, PBS3REGCTX pCtx, PBS3EXTCTX pExtCtx,
+ PCBS3CPUINSTR3_CONFIG_T pConfig, uint8_t bMode)
+{
+ /*
+ * Save context bits we may change here
+ */
+ pSavedCfg->uCr0 = pCtx->cr0.u32;
+ pSavedCfg->uCr4 = pCtx->cr4.u32;
+ pSavedCfg->uEfl = pCtx->rflags.u32;
+ pSavedCfg->uFcw = Bs3ExtCtxGetFcw(pExtCtx);
+ pSavedCfg->uFsw = Bs3ExtCtxGetFsw(pExtCtx);
+ pSavedCfg->uMxCsr = Bs3ExtCtxGetMxCsr(pExtCtx);
+
+ /*
+ * Can we make these changes?
+ */
+ if (pConfig->fMxCsrMM && !g_fAmdMisalignedSse)
+ return false;
+
+ /* Currently we skip pending x87 exceptions in real mode as they cannot be
+ caught, given that we preserve the bios int10h. */
+ if (pConfig->fX87XcptPending && BS3_MODE_IS_RM_OR_V86(bMode))
+ return false;
+
+ /*
+ * Modify the test context.
+ */
+ if (pConfig->fCr0Mp)
+ pCtx->cr0.u32 |= X86_CR0_MP;
+ else
+ pCtx->cr0.u32 &= ~X86_CR0_MP;
+ if (pConfig->fCr0Em)
+ pCtx->cr0.u32 |= X86_CR0_EM;
+ else
+ pCtx->cr0.u32 &= ~X86_CR0_EM;
+ if (pConfig->fCr0Ts)
+ pCtx->cr0.u32 |= X86_CR0_TS;
+ else
+ pCtx->cr0.u32 &= ~X86_CR0_TS;
+
+ if (pConfig->fCr4OsFxSR)
+ pCtx->cr4.u32 |= X86_CR4_OSFXSR;
+ else
+ pCtx->cr4.u32 &= ~X86_CR4_OSFXSR;
+ /** @todo X86_CR4_OSXMMEEXCPT? */
+ if (pConfig->fCr4OsXSave)
+ pCtx->cr4.u32 |= X86_CR4_OSXSAVE;
+ else
+ pCtx->cr4.u32 &= ~X86_CR4_OSXSAVE;
+
+ if (pConfig->fXcr0Sse)
+ pExtCtx->fXcr0Saved |= XSAVE_C_SSE;
+ else
+ pExtCtx->fXcr0Saved &= ~XSAVE_C_SSE;
+ if (pConfig->fXcr0Avx)
+ pExtCtx->fXcr0Saved |= XSAVE_C_YMM;
+ else
+ pExtCtx->fXcr0Saved &= ~XSAVE_C_YMM;
+
+ if (pConfig->fAlignCheck)
+ {
+ pCtx->rflags.u32 |= X86_EFL_AC;
+ pCtx->cr0.u32 |= X86_CR0_AM;
+ }
+ else
+ {
+ pCtx->rflags.u32 &= ~X86_EFL_AC;
+ pCtx->cr0.u32 &= ~X86_CR0_AM;
+ }
+
+ if (!pConfig->fX87XcptPending)
+ Bs3ExtCtxSetFsw(pExtCtx, pSavedCfg->uFsw & ~(X86_FSW_ES | X86_FSW_B));
+ else
+ {
+ Bs3ExtCtxSetFcw(pExtCtx, pSavedCfg->uFcw & ~X86_FCW_ZM);
+ Bs3ExtCtxSetFsw(pExtCtx, pSavedCfg->uFsw | X86_FSW_ZE | X86_FSW_ES | X86_FSW_B);
+ pCtx->cr0.u32 |= X86_CR0_NE;
+ }
+
+ if (pConfig->fMxCsrMM)
+ Bs3ExtCtxSetMxCsr(pExtCtx, pSavedCfg->uMxCsr | X86_MXCSR_MM);
+ else
+ Bs3ExtCtxSetMxCsr(pExtCtx, pSavedCfg->uMxCsr & ~X86_MXCSR_MM);
+ return true;
+}
+
+
+/**
+ * Undoes changes made by bs3CpuInstr3ConfigReconfigure.
+ */
+static void bs3CpuInstr3ConfigRestore(PCBS3CPUINSTR3_CONFIG_SAVED_T pSavedCfg, PBS3REGCTX pCtx, PBS3EXTCTX pExtCtx)
+{
+ pCtx->cr0.u32 = pSavedCfg->uCr0;
+ pCtx->cr4.u32 = pSavedCfg->uCr4;
+ pCtx->rflags.u32 = pSavedCfg->uEfl;
+ pExtCtx->fXcr0Saved = pExtCtx->fXcr0Nominal;
+ Bs3ExtCtxSetFcw(pExtCtx, pSavedCfg->uFcw);
+ Bs3ExtCtxSetFsw(pExtCtx, pSavedCfg->uFsw);
+ Bs3ExtCtxSetMxCsr(pExtCtx, pSavedCfg->uMxCsr);
+}
+
+
+/**
+ * Allocates two extended CPU contexts and initializes the first one
+ * with random data.
+ * @returns First extended context, initialized with randomish data. NULL on
+ * failure (complained).
+ * @param ppExtCtx2 Where to return the 2nd context.
+ */
+static PBS3EXTCTX bs3CpuInstr3AllocExtCtxs(PBS3EXTCTX BS3_FAR *ppExtCtx2)
+{
+ /* Allocate extended context structures. */
+ uint64_t fFlags;
+ uint16_t cb = Bs3ExtCtxGetSize(&fFlags);
+ PBS3EXTCTX pExtCtx1 = Bs3MemAlloc(BS3MEMKIND_TILED, cb * 2);
+ PBS3EXTCTX pExtCtx2 = (PBS3EXTCTX)((uint8_t BS3_FAR *)pExtCtx1 + cb);
+ if (pExtCtx1)
+ {
+ Bs3ExtCtxInit(pExtCtx1, cb, fFlags);
+ /** @todo populate with semi-random stuff. */
+
+ Bs3ExtCtxInit(pExtCtx2, cb, fFlags);
+ *ppExtCtx2 = pExtCtx2;
+ return pExtCtx1;
+ }
+ Bs3TestFailedF("Bs3MemAlloc(tiled,%#x)", cb * 2);
+ *ppExtCtx2 = NULL;
+ return NULL;
+}
+
+static void bs3CpuInstr3FreeExtCtxs(PBS3EXTCTX pExtCtx1, PBS3EXTCTX BS3_FAR pExtCtx2)
+{
+ RT_NOREF_PV(pExtCtx2);
+ Bs3MemFree(pExtCtx1, pExtCtx1->cb * 2);
+}
+
+/**
+ * Sets up SSE and maybe AVX.
+ */
+static void bs3CpuInstr3SetupSseAndAvx(PBS3REGCTX pCtx, PCBS3EXTCTX pExtCtx)
+{
+ /* CR0: */
+ uint32_t cr0 = Bs3RegGetCr0();
+ cr0 &= ~(X86_CR0_TS | X86_CR0_MP | X86_CR0_EM);
+ cr0 |= X86_CR0_NE;
+ Bs3RegSetCr0(cr0);
+
+ /* If real mode context, the cr0 value will differ from the current one (we're in PE32 mode). */
+ pCtx->cr0.u32 &= ~(X86_CR0_TS | X86_CR0_MP | X86_CR0_EM);
+ pCtx->cr0.u32 |= X86_CR0_NE;
+
+ /* CR4: */
+ if (pExtCtx->enmMethod != BS3EXTCTXMETHOD_ANCIENT)
+ {
+ uint32_t cr4 = Bs3RegGetCr4();
+ if (pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ {
+ cr4 |= X86_CR4_OSFXSR | X86_CR4_OSXMMEEXCPT | X86_CR4_OSXSAVE;
+ Bs3RegSetCr4(cr4);
+ Bs3RegSetXcr0(pExtCtx->fXcr0Nominal);
+ }
+ else if (pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE)
+ {
+ cr4 |= X86_CR4_OSFXSR | X86_CR4_OSXMMEEXCPT;
+ Bs3RegSetCr4(cr4);
+ }
+ pCtx->cr4.u32 = cr4;
+ }
+}
+
+
+
+/**
+ * Configures the buffer with electrict fences in paged modes.
+ *
+ * @returns Adjusted buffer pointer.
+ * @param pbBuf The buffer pointer.
+ * @param pcbBuf Pointer to the buffer size (input & output).
+ * @param bMode The testing target mode.
+ */
+DECLINLINE(uint8_t BS3_FAR *) bs3CpuInstr3BufSetup(uint8_t BS3_FAR *pbBuf, uint32_t *pcbBuf, uint8_t bMode)
+{
+ if (BS3_MODE_IS_PAGED(bMode))
+ {
+ uint32_t cbBuf = *pcbBuf;
+ Bs3PagingProtectPtr(&pbBuf[0], X86_PAGE_SIZE, 0, X86_PTE_P);
+ Bs3PagingProtectPtr(&pbBuf[cbBuf - X86_PAGE_SIZE], X86_PAGE_SIZE, 0, X86_PTE_P);
+ pbBuf += X86_PAGE_SIZE;
+ cbBuf -= X86_PAGE_SIZE * 2;
+ *pcbBuf = cbBuf;
+ }
+ return pbBuf;
+}
+
+
+/**
+ * Undoes what bs3CpuInstr3BufSetup did.
+ *
+ * @param pbBuf The buffer pointer.
+ * @param cbBuf The buffer size.
+ * @param bMode The testing target mode.
+ */
+DECLINLINE(void) bs3CpuInstr3BufCleanup(uint8_t BS3_FAR *pbBuf, uint32_t cbBuf, uint8_t bMode)
+{
+ if (BS3_MODE_IS_PAGED(bMode))
+ {
+ Bs3PagingProtectPtr(&pbBuf[-X86_PAGE_SIZE], X86_PAGE_SIZE, X86_PTE_P, 0);
+ Bs3PagingProtectPtr(&pbBuf[cbBuf], X86_PAGE_SIZE, X86_PTE_P, 0);
+ }
+}
+
+
+/**
+ * Gets a buffer of a @a cbMemOp sized operand according to the given
+ * configuration and alignment restrictions.
+ *
+ * @returns Pointer to the buffer.
+ * @param pbBuf The buffer pointer.
+ * @param cbBuf The buffer size.
+ * @param cbMemOp The operand size.
+ * @param cbAlign The operand alignment restriction.
+ * @param pConfig The configuration.
+ */
+DECLINLINE(PRTUINT256U) bs3CpuInstr3BufForOperand(uint8_t BS3_FAR *pbBuf, uint32_t cbBuf, uint8_t cbMemOp, uint8_t cbAlign,
+ PCBS3CPUINSTR3_CONFIG_T pConfig)
+{
+ if (pConfig->fAligned)
+ {
+ if (!pConfig->fAlignCheck)
+ return (PRTUINT256U)&pbBuf[cbBuf - cbMemOp];
+ return (PRTUINT256U)&pbBuf[cbBuf - cbMemOp - cbAlign];
+ }
+ return (PRTUINT256U)&pbBuf[cbBuf - cbMemOp - 1];
+}
+
+
+/**
+ * Determins the size of memory operands.
+ */
+DECLINLINE(uint8_t) bs3CpuInstr3MemOpSize(uint8_t cbOperand, uint8_t enmRm)
+{
+ if (enmRm <= RM_MEM)
+ return cbOperand;
+ if (enmRm == RM_MEM32)
+ return sizeof(uint32_t);
+ if (enmRm == RM_MEM64)
+ return sizeof(uint64_t);
+ BS3_ASSERT(0);
+ return cbOperand;
+}
+
+
+/*
+ * Test type #1.
+ */
+
+typedef struct BS3CPUINSTR3_TEST1_VALUES_T
+{
+ RTUINT256U uSrc2;
+ RTUINT256U uSrc1; /**< uDstIn for MMX & SSE */
+ RTUINT256U uDstOut;
+} BS3CPUINSTR3_TEST1_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST1_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t iRegDst;
+ uint8_t iRegSrc1;
+ uint8_t iRegSrc2;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST1_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST1_T;
+
+typedef struct BS3CPUINSTR3_TEST1_MODE_T
+{
+ BS3CPUINSTR3_TEST1_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST1_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST1_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST1_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #1 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType1(uint8_t bMode, BS3CPUINSTR3_TEST1_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+ //Bs3TestPrintf("FTW=%#x mm1/st1=%.16Rhxs\n", pExtCtx->Ctx.x87.FTW, &pExtCtx->Ctx.x87.aRegs[1]);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST1_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = bs3CpuInstr3MemOpSize(cbOperand, paTests[iTest].enmRm);
+ uint8_t const cbAlign = cbMemOp;
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType] ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ /* dest */
+ if (paTests[iTest].iRegDst == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(puMemOp, 0xcc, cbMemOp);
+ if (bXcptExpect == X86_XCPT_DB)
+ uMemOpExpect = paValues[iVal].uDstOut;
+ else
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc2, ~paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+
+ /* source #1 (/ destination for MMX and SSE) */
+ if (paTests[iTest].iRegSrc1 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc1, cbMemOp);
+ if (paTests[iTest].iRegDst == UINT8_MAX)
+ BS3_ASSERT(fSseInstr);
+ else
+ uMemOpExpect = paValues[iVal].uSrc1;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc1, paValues[iVal].uSrc1.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1, 32);
+
+ /* source #2 */
+ if (paTests[iTest].iRegSrc2 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegDst != UINT8_MAX && paTests[iTest].iRegSrc1 != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc2, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc2;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc2, paValues[iVal].uSrc2.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2, 32);
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT( paTests[iTest].iRegDst == UINT8_MAX
+ || paTests[iTest].iRegSrc1 == UINT8_MAX
+ || paTests[iTest].iRegSrc2 == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (fMmxInstr && bXcptExpect == X86_XCPT_DB)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff); /* Observed on 10980xe after pxor mm1, mm2. */
+ }
+ if (bXcptExpect == X86_XCPT_DB && paTests[iTest].iRegDst != UINT8_MAX)
+ {
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegDst, paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_SET);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut, cbOperand);
+ }
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, bXcptExpect, puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * PAND, VPAND, ANDPS, VANDPS, ANDPD, VANDPD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pand_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pand_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pand_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pand_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpand_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpand_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpand_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpand_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandps_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandps_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandps_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandps_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandpd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandpd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandpd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandpd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vandpd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_andps_andpd_pand(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ^ */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ^ */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x5555666677770000, 0x1111222233334444, 0x1111222233334444, 0x5555666677770000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ^ */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x0c09d02808403294, 0x385406c840621622, 0x8000290816080282, 0x0050c020030090b9) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pand_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pand_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pand_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pand_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpand_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandps_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandpd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PANDN, VPANDN, ANDNPS, VANDNPS, ANDNPD, VANDNPD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pandn_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pandn_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pandn_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pandn_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpandn_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpandn_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpandn_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpandn_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andnps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andnps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnps_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnps_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnps_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnps_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andnpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_andnpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnpd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnpd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnpd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vandnpd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vandnpd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_andnps_andnpd_pandn(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ^ */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ^ */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000008888, 0x0000000000000000, 0x0000000000000000, 0x0000000000008888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ^ */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x41002002649c4141, 0x06a01100260929c4, 0x342106a040449920, 0x9c0c205390090602) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pandn_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pandn_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pandn_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pandn_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpandn_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnps_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_andnpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_andnpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vandnpd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+
+/*
+ * POR, VPOR, PORPS, VORPS, PORPD, VPORPD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_por_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_por_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_por_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_por_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpor_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpor_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpor_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpor_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_orps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_orps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorps_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorps_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorps_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorps_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_orpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_orpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorpd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorpd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorpd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vorpd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vorpd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_orps_orpd_por(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ^ */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ^ */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0xddddeeeeffff8888, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff8888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ^ */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x5fddfdae6dff73d5, 0xfffc9fec667b7ff7, 0xbc21effbffddfbe3, 0xdfdfedf3b38d9fff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_por_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_por_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_por_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_por_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpor_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorps_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_orpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_orpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vorpd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PXOR, VPXOR, XORPS, VXORPS, XORPD, VXORPD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pxor_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pxor_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pxor_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pxor_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpxor_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpxor_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpxor_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpxor_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_xorps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_xorps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorps_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorps_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorps_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorps_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_xorpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_xorpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorpd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorpd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorpd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vxorpd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vxorpd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_xorps_xorpd_pxor(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ^ */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ^ */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x8888888888888888, 0x8888888888888888, 0x8888888888888888, 0x8888888888888888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ^ */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x53d42d8665bf4141, 0xc7a89924261969d5, 0x3c21c6f3e9d5f961, 0xdf8f2dd3b08d0f46) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pxor_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pxor_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pxor_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pxor_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpxor_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorps_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_xorpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_xorpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vxorpd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+
+/*
+ * PCMPGTB, VPCMPGTB, PCMPGTW, VPCMPGTW, PCMPGTD, VPCMPGTD, PCMPGTQ, VPCMPGTQ.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtb_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpcmpgtd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpgtq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpcmpgtq_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pcmpgtb_pcmpgtw_pcmpgtd_pcmpgtq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* < */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* < */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x000000000000ffff, 0x0000000000000000, 0x0000000000000000, 0x000000000000ffff) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* < */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x0000000000ff0000, 0x00ff00ff00ffffff, 0x000000ff0000ffff, 0xff000000ff00ffff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* < */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* < */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x000000000000ffff, 0x0000000000000000, 0x0000000000000000, 0x000000000000ffff) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ^ */ RTUINT256_INIT_C(0x1eddddac77733294, 0xf95c8eec40725633, 0x3333e95bbf9962c3, 0x43d3cda0238499fd), /* modified 1st and 3rd value */
+ /* = */ RTUINT256_INIT_C(0x00000000ffff0000, 0x000000000000ffff, 0xffff00000000ffff, 0xffff0000ffffffff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* < */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* < */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* < */ RTUINT256_INIT_C(0x555dddac09633294, 0xf95c8eec77725633, 0x7770e95bbf9962c3, 0x43d3cda0238499fd), /* modified 1st, 2nd and 3rd value */
+ /* = */ RTUINT256_INIT_C(0xffffffff00000000, 0x00000000ffffffff, 0xffffffff00000000, 0xffffffffffffffff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* < */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* < */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* < */ RTUINT256_INIT_C(0x77ddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd), /* modified 1st value */
+ /* = */ RTUINT256_INIT_C(0xffffffffffffffff, 0x0000000000000000, 0x0000000000000000, 0xffffffffffffffff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pcmpgtb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpgtw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpgtd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpgtq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_pcmpgtq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pcmpgtb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpgtw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpgtd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpgtq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_pcmpgtq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pcmpgtb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpgtb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpgtb_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpgtw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpgtw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpgtw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpgtd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpgtd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpgtd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpgtq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_pcmpgtq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpgtq_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PCMPEQB, VPCMPEQB, PCMPEQW, VPCMPEQW, PCMPEQD, VPCMPEQD, PCMPEQQ, VPCMPEQQ.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqb_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpcmpeqd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pcmpeqq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpcmpeqq_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pcmpeqb_pcmpeqw_pcmpeqd_pcmpeqq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ==*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ==*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4dddf02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ==*/ RTUINT256_INIT_C(0x1eddddac09dc3294, 0xf95c17ec667256e6, 0xb400e95bbf999bc3, 0x9cd3cda0230999fd), /* modified all to get some matches */
+ /* = */ RTUINT256_INIT_C(0x00ff000000ff0000, 0x0000ff00ff0000ff, 0xff0000000000ff00, 0xff00000000ff0000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ==*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ==*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ==*/ RTUINT256_INIT_C(0x1eddf02a6cdc3294, 0x3ef48eec666b5633, 0x88002fa8bf999ba2, 0x9c5ccda0238496bb), /* modified all to get some matches */
+ /* = */ RTUINT256_INIT_C(0x0000ffffffff0000, 0xffff0000ffff0000, 0x0000ffff0000ffff, 0xffff00000000ffff) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ==*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ==*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ==*/ RTUINT256_INIT_C(0x4d09f02a09633294, 0x3ef417c8666b3fe6, 0x8800e95b564c9ba2, 0x9c5ce073238499fd), /* modified all to get some matches */
+ /* = */ RTUINT256_INIT_C(0xffffffff00000000, 0xffffffffffffffff, 0x00000000ffffffff, 0xffffffff00000000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* ==*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* ==*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* ==*/ RTUINT256_INIT_C(0x1eddddac09633294, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x43d3cda0238499fd), /* modified 2nd and 3rd to get some matches */
+ /* = */ RTUINT256_INIT_C(0x0000000000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pcmpeqb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpeqw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpeqd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpeqq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_pcmpeqq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pcmpeqb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpeqw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpeqd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpeqq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_pcmpeqq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pcmpeqb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pcmpeqb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpcmpeqb_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pcmpeqw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pcmpeqw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpcmpeqw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pcmpeqd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pcmpeqd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpcmpeqd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_pcmpeqq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_pcmpeqq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpcmpeqq_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PADDB, VPADDB, PADDW, VPADDW, PADDD, VPADDD, PADDQ, VPADDQ.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddb_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddb_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddb_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddb_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddb_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpaddd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_paddq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddq_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddq_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddq_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpaddq_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpaddq_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_paddb_paddw_paddd_paddq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x3232545476768888, 0xaaaacccceeee1010, 0xaaaacccceeee1010, 0x3232545476768888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x6be6cdd6753fa569, 0x3750a5b4a6dd9519, 0x3c21180315e5fd65, 0xdf2fad13b68d2fb8) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x3332555477768888, 0xaaaacccceeee1110, 0xaaaacccceeee1110, 0x3332555477768888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x6be6cdd6763fA669, 0x3850A6B4A6DD9619, 0x3C21190315E5FE65, 0xE02FAE13B68D30B8) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x3333555477768888, 0xAAAACCCCEEEF1110, 0xAAAACCCCEEEF1110, 0x3333555477768888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x6BE7CDD6763FA669, 0x3850A6B4A6DD9619, 0x3C22190315E5FE65, 0xE030AE13B68E30B8) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x3333555577768888, 0xAAAACCCCEEEF1110, 0xAAAACCCCEEEF1110, 0x3333555577768888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x6BE7CDD6763FA669, 0x3850A6B4A6DD9619, 0x3C22190415E5FE65, 0xE030AE13B68E30B8) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_paddb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_paddw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_paddd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_paddq_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_paddb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_paddw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_paddd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_paddq_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_paddb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_paddb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpaddb_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_paddw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_paddw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpaddw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_paddd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_paddd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpaddd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_paddq_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_paddq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpaddq_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PSUBB, VPSUBB, PSUBW, VPSUBW, PSUBD, VPSUBD, PSUBQ, VPSUBQ.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubb_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubb_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubb_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubb_YMM7_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubb_YMM7_YMM2_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubd_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubd_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubd_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubd_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsubd_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psubq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubq_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubq_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubq_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsubq_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsubq_YMM10_YMM8_YMM15_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_psubb_psubw_psubd_psubq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x8888888888887878, 0x8888888888888888, 0x8888888888888888, 0x8888888888887878) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0xd1d4ed829d87bfbf, 0xbb687724da07174d, 0xd4dfbab3694dc721, 0xa777ed2d907b0342) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x8888888888887778, 0x8888888888888888, 0x8888888888888888, 0x8888888888887778) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0xd1d4ed829c87bebf, 0xba687724da07164d, 0xd3dfb9b3694dc721, 0xa777ed2d907b0342) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x8888888888877778, 0x8888888888888888, 0x8888888888888888, 0x8888888888877778) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0xd1d3ed829c86bebf, 0xba687724da07164d, 0xd3dfb9b3694cc721, 0xa776ed2d907b0342) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* + */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* + */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x8888888888877778, 0x8888888888888888, 0x8888888888888888, 0x8888888888877778) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* + */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0xd1d3ed819c86bebf, 0xba687723da07164d, 0xd3dfb9b3694cc721, 0xa776ed2c907b0342) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_psubb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psubw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psubd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_psubq_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_psubb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psubw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psubd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_psubq_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_psubb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psubb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX_256, 7, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsubb_YMM7_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 7, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psubw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psubw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsubw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psubd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psubd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsubd_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+
+ { bs3CpuInstr3_psubq_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_psubq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ { bs3CpuInstr3_vpsubq_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesQ), s_aValuesQ },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PMULLW, VPMULLW, PMULLD, VPMULLD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmullw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmullw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmullw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmullw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmullw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmullw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmullw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmullw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulld_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulld_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulld_XMM2_XMM1_XMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulld_XMM2_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulld_YMM2_YMM1_YMM0_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulld_YMM2_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmulld_YMM10_YMM8_YMM15_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmulld_YMM10_YMM8_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmullw_pmulld(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* * */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* * */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x0b6106d488890000, 0x5c293e94a7419630, 0x5c293e94a7419630, 0x0b6106d488890000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* * */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x8ec59e38d5149124, 0xf3b0dc605ba6fed2, 0x8800d8b8476c9066, 0xf3d45ee00ba4b9cf) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* * */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* * */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x2ea606d477780000, 0x6e5d3e9430ec9630, 0x6e5d3e9430ec9630, 0x2ea606d477780000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* * */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x97439e3846719124, 0x8216dc606340fed2, 0x7c2bd8b8f1c09066, 0x31915ee054fbb9cf) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmullw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmulld_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmulld_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_XMM0_icebp_c16, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_YMM0_icebp_c16, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmullw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmulld_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmulld_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_XMM0_icebp_c32, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_YMM0_icebp_c32, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmullw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmullw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmullw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmulld_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmulld_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_XMM0_icebp_c64, 255, RM_REG, T_AVX_128, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_XMM2_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_YMM0_icebp_c64, 255, RM_REG, T_AVX_256, 2, 1, 0, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM2_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 2, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM10_YMM8_YMM15_icebp_c64, 255, RM_REG, T_AVX_256, 10, 8, 15, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmulld_YMM10_YMM8_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 10, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PMULHW, VPMULHW.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmulhw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* * */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* * */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0xf49ff92cffff0000, 0xf92cf49ff258f258, 0xf92cf49ff258f258, 0xf49ff92cffff0000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* * */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x0949021f03fd16e2, 0xfe5df57e19c81583, 0x2390fbc8ea4ad947, 0xe5990635f0e229f2) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmulhw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmulhw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmulhw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PMULHUW, VPMULHUW.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhuw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhuw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhuw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhuw_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhuw_XMM1_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhuw_XMM1_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhuw_YMM1_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhuw_YMM1_YMM1_FSxBX_icebp);
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmulhuw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* * */ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* = */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* * */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* = */ RTUINT256_INIT_C(0x49f45f9277760000, 0x0a3d16c1258b369c, 0x0a3d16c1258b369c, 0x49f45f9277760000) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* * */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* = */ RTUINT256_INIT_C(0x0949cff503fd16e2, 0x3d510d4619c81583, 0x5fb12b7040963c0a, 0x296cb44814665aaa) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmulhuw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmulhuw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmulhuw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmulhuw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_XMM1_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmulhuw_YMM1_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * PSHUFB
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pshufb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pshufb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpshufb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpshufb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpshufb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpshufb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_pshufb(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*mask*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*val*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*mask*/ RTUINT256_INIT_C( 1, 2, 3, 0xffffffffffffffff),
+ /*val*/ RTUINT256_INIT_C( 1, 2, 3, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x0000000000000000) },
+ { /*mask*/ RTUINT256_INIT_C( 1, 2, 3, 0x7f7f7f7f7f7f7f7f),
+ /*val*/ RTUINT256_INIT_C( 1, 2, 3, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xffffffffffffffff) },
+ { /*mask*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*val*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0xeeeedddddddd0000) },
+ { /*mask*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*val*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x00a0002300990000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*mask*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*val*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*mask*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*val*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { /*mask*/ RTUINT256_INIT_C(0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f),
+ /*val*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { /*mask*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*val*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xaaaa999999990000, 0xccccbbbbbbbbaaaa, 0x0000ffffffffeeee, 0xeeeedddddddd0000) },
+ { /*mask*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*val*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xdd320063ac004000, 0xdd00f9005c091e00, 0x00998800d35b0000, 0x005b002300620000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pshufb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_pshufb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pshufb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_pshufb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pshufb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_pshufb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_pshufb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_pshufb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_pshufb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpshufb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKHBW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhbw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhbw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpckhbw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpckhbw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhbw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhbw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhbw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhbw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckhbw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf1e1f2e2f3e3f4e4) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x55dd55dd66ee66ee) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9c435cd3e0cd73a0) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1b1f2b2f3b3f4b4, 0xf5b5f6b6f7b7f8b8, 0xd191d292d393d494, 0xd595d696d797d898) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x55dd55dd66ee66ee, 0x77ff77ff88008800, 0x1199119922aa22aa, 0x33bb33bb44cc44cc) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d1e09ddf0dd2aac, 0x6c09dc637332d594, 0xb48821002fe9a85b, 0x56bf4c999b62a2c3) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpckhbw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhbw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpckhbw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhbw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpckhbw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhbw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhbw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhbw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhbw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKHWD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhwd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhwd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpckhwd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpckhwd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhwd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhwd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhwd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhwd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckhwd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2e1e2f3f4e3e4) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x5555dddd6666eeee) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9c5c43d3e073cda0) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2b1b2f3f4b3b4, 0xf5f6b5b6f7f8b7b8, 0xd1d29192d3d49394, 0xd5d69596d7d89798) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x5555dddd6666eeee, 0x7777ffff88880000, 0x111199992222aaaa, 0x3333bbbb4444cccc) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d091eddf02addac, 0x6cdc096373d53294, 0xb42188002fa8e95b, 0x564cbf999ba262c3) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpckhwd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhwd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpckhwd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhwd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpckhwd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhwd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhwd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhwd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhwd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKHDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhdq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhdq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpckhdq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpckhdq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhdq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhdq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhdq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhdq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckhdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4e1e2e3e4) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x55556666ddddeeee) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9c5ce07343d3cda0) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4b1b2b3b4, 0xf5f6f7f8b5b6b7b8, 0xd1d2d3d491929394, 0xd5d6d7d895969798) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x55556666ddddeeee, 0x77778888ffff0000, 0x111122229999aaaa, 0x33334444bbbbcccc) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a1eddddac, 0x6cdc73d509633294, 0xb4212fa88800e95b, 0x564c9ba2bf9962c3) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpckhdq_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhdq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpckhdq_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhdq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpckhdq_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckhdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhdq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhdq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhdq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhdq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKHQDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhqdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckhqdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpckhqdq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpckhqdq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhqdq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhqdq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckhqdq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckhqdq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckhqdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xb1b2b3b4b5b6b7b8, 0xd1d2d3d4d5d6d7d8, 0x9192939495969798) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x5555666677778888, 0xddddeeeeffff0000, 0x1111222233334444, 0x9999aaaabbbbcccc) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x1eddddac09633294, 0xb4212fa8564c9ba2, 0x8800e95bbf9962c3) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpckhqdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhqdq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpckhqdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhqdq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpckhqdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhqdq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhqdq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckhqdq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckhqdq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKLBW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklbw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklbw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpcklbw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpcklbw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklbw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklbw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklbw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklbw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpcklbw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf5e5f6e6f7e7f8e8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x77ff77ff88008800) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x932309849699bbfd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1a1e2a2e3a3e4a4, 0xe5a5e6a6e7a7e8a8, 0xc181c282c383c484, 0xc585c686c787c888) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x1199119922aa22aa, 0x33bb33bb44cc44cc, 0x55dd55dd66ee66ee, 0x77ff77ff88008800) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef9f45c178ec8ec, 0x66406b723f56e633, 0x9c435cd3e0cd73a0, 0x932309849699bbfd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpcklbw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklbw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpcklbw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklbw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpcklbw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklbw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklbw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklbw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklbw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKLWD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklwd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklwd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpcklwd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpcklwd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklwd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklwd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklwd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklwd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpcklwd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf5f6e5e6f7f8e7e8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x7777ffff88880000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9309238496bb99fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2a1a2e3e4a3a4, 0xe5e6a5a6e7e8a7a8, 0xc1c28182c3c48384, 0xc5c68586c7c88788) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x111199992222aaaa, 0x3333bbbb4444cccc, 0x5555dddd6666eeee, 0x7777ffff88880000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef4f95c17c88eec, 0x666b40723fe65633, 0x9c5c43d3e073cda0, 0x9309238496bb99fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpcklwd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklwd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpcklwd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklwd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpcklwd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpcklwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklwd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklwd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklwd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklwd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKLDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckldq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckldq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckldq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpckldq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpckldq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpckldq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckldq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckldq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckldq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckldq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckldq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpckldq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpckldq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpckldq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckldq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf5f6f7f8e5e6e7e8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x77778888ffff0000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x930996bb238499fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2e3e4a1a2a3a4, 0xe5e6e7e8a5a6a7a8, 0xc1c2c3c481828384, 0xc5c6c7c885868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x111122229999aaaa, 0x33334444bbbbcccc, 0x55556666ddddeeee, 0x77778888ffff0000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8f95c8eec, 0x666b3fe640725633, 0x9c5ce07343d3cda0, 0x930996bb238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpckldq_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckldq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpckldq_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckldq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpckldq_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_punpckldq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckldq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckldq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpckldq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpckldq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKLQDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklqdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_punpcklqdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_punpcklqdq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_punpcklqdq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklqdq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklqdq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpunpcklqdq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpunpcklqdq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpcklqdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2e3e4e5e6e7e8, 0xa1a2a3a4a5a6a7a8, 0xc1c2c3c4c5c6c7c8, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x1111222233334444, 0x9999aaaabbbbcccc, 0x5555666677778888, 0xddddeeeeffff0000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8666b3fe6, 0xf95c8eec40725633, 0x9c5ce073930996bb, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_punpcklqdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklqdq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_punpcklqdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklqdq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_punpcklqdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklqdq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklqdq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_punpcklqdq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpunpcklqdq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PACKSSWB
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packsswb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packsswb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packsswb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packsswb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_packsswb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_packsswb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpacksswb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpacksswb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpacksswb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpacksswb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpacksswb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpacksswb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpacksswb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpacksswb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_packsswb(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x8080808080808080) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x7f7f7f808080ff00) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x808080807f807f80) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xFF820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x8264fe4222808081) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x7f7f7f807f7f7f7f, 0x8080ff0080808080, 0x7f7f7f7f7f7f7f80, 0x808080808080ff00) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x7f807f7f7f7f7f7f, 0x7f807f7f80807f7f, 0x807f7f8080808080, 0x8080807f7f807f80) },
+ { /*src2*/ RTUINT256_INIT_C(0x002200250079007e, 0xfffffffeff88ff7f, 0x0064003200160008, 0x0042004600880080),
+ /*src1*/ RTUINT256_INIT_C(0x0001000200030005, 0x0007000b000d0011, 0x00130017001d0025, 0x0029002b002f0035),
+ /* => */ RTUINT256_INIT_C(0x2225797efffe8880, 0x01020305070b0d11, 0x6432160842467f7f, 0x13171d25292b2f35) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_packsswb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packsswb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_packsswb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packsswb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_packsswb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packsswb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packsswb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packsswb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packsswb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpacksswb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PACKSSDW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packssdw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packssdw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packssdw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packssdw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_packssdw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_packssdw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackssdw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackssdw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackssdw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackssdw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackssdw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackssdw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackssdw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackssdw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_packssdw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x8000800080008000) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0x7fff7fff80008000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x800080007fff7fff) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xffff898400007495),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x00002222ffff9485),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x8984749522229485) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x8000800080008000, 0x8000800080008000, 0x8000800080008000, 0x8000800080008000) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x7fff7fff7fff7fff, 0x8000800080008000, 0x7fff7fff7fff7fff, 0x8000800080008000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x7fff7fff7fff7fff, 0x7fff7fff80007fff, 0x80007fff80008000, 0x800080007fff7fff) },
+ { /*src2*/ RTUINT256_INIT_C(0x0000349000002349, 0xffffa230ffffe384, 0xffff348300007ffe, 0x00008000ffff7fff),
+ /*src1*/ RTUINT256_INIT_C(0xffff800100007ffe, 0xffffcbaffffffffe, 0x0000643200001608, 0xffffffe0ffffffc0),
+ /* => */ RTUINT256_INIT_C(0x34902349a230e384, 0x80017ffecbaffffe, 0x80007ffe7fff8000, 0x64321608ffe0ffc0) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_packssdw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packssdw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_packssdw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packssdw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_packssdw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packssdw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packssdw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packssdw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packssdw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackssdw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PACKUSWB
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packuswb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packuswb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packuswb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packuswb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_packuswb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_packuswb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackuswb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackuswb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackuswb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackuswb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackuswb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackuswb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackuswb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackuswb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_packuswb(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x0000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 8, 10, 11, 0xffffff0000000000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x00000000ff00ff00) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xFF820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x0064004222000000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xffffff00ffffffff, 0x0000000000000000, 0xffffffffffffff00, 0x000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xff00ffffffffffff, 0xff00ffff0000ffff, 0x00ffff0000000000, 0x000000ffff00ff00) },
+ { /*src2*/ RTUINT256_INIT_C(0x002200250079007e, 0xfffffffeff88ff7f, 0x0064003200160008, 0x0042004600880080),
+ /*src1*/ RTUINT256_INIT_C(0x0001000200030005, 0x0007000b000d0011, 0x00130017001d0025, 0x0029002b002f0035),
+ /* => */ RTUINT256_INIT_C(0x2225797e00000000, 0x01020305070b0d11, 0x6432160842468880, 0x13171d25292b2f35) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_packuswb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packuswb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_packuswb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packuswb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_packuswb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX, 1, 1, 2, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX, 1, 1, 255, RT_ELEMENTS(s_aValues64), s_aValues64 },
+ { bs3CpuInstr3_packuswb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packuswb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packuswb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packuswb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackuswb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PACKUSDW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packusdw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_packusdw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_packusdw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_packusdw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackusdw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackusdw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackusdw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackusdw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackusdw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpackusdw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpackusdw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpackusdw_YMM8_YMM9_FSxBX_icebp_c64;
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_packusdw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesOthers[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0x0000000000000000, 0xffffffffffffffff, 0x0000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffff0000ffff, 0x0000ffff00000000, 0x00000000ffffffff) },
+ { /*src2*/ RTUINT256_INIT_C(0x0000349000002349, 0xffffa230ffffe384, 0xffff348300007ffe, 0x00008000ffff7fff),
+ /*src1*/ RTUINT256_INIT_C(0xffff800100007ffe, 0xffffcbaffffffffe, 0x0000643200001608, 0xffffffe0ffffffc0),
+ /* => */ RTUINT256_INIT_C(0x3490234900000000, 0x00007ffe00000000, 0x00007ffe80000000, 0x6432160800000000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_packusdw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packusdw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_packusdw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packusdw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_packusdw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packusdw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packusdw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_packusdw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ { bs3CpuInstr3_vpackusdw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesOthers), s_aValuesOthers },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMAXUB - Compare unsigned byte integers and returns maximum values.
+ * [V]PMAXUW - Compare unsigned word integers and returns maximum values.
+ * [V]PMAXUD - Compare unsigned double word integers and returns maximum values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxub_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxub_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxub_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxub_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxub_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxub_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxub_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxub_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxub_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxub_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxub_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxub_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxub_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxub_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxuw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxuw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxuw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxuw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxuw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxuw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxuw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxuw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxuw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxuw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxuw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxuw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxud_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxud_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxud_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxud_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxud_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxud_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxud_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxud_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxud_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxud_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxud_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxud_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmaxub_pmaxuw_pmaxud(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff8888) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9cd3e0a0938499fd) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xff820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0xff82fe64fffeff81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4dddf0ac6cdc73d5, 0xf9f48eec667256e6, 0xb421e9a8bf999bc3, 0x9cd3e0a0938499fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0xf95c8eec666b5633, 0xb421e95bbf999ba2, 0x9c5ce073930999fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0xf95c8eec666b3fe6, 0xb4212fa8bf9962c3, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmaxub_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxub_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxuw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxuw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxud_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxud_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmaxub_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxub_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxuw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxuw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxud_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxud_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmaxub_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxub_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxub_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxub_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxub_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxub_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxuw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxuw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxuw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxuw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxuw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxud_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxud_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxud_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxud_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxud_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMAXSB - Compare signed byte integers and returns maximum values.
+ * [V]PMAXSW - Compare signed word integers and returns maximum values.
+ * [V]PMAXSD - Compare signed double word integers and returns maximum values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxsb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxsb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaxsd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaxsd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaxsd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaxsd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaxsd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaxsd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmaxsb_pmaxsw_pmaxsd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 4, 6, 7, 0x5555666677770000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x43d3e073238499fd) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xff820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x00220064fffe0042) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6c6373d5, 0x3e5c17ec66725633, 0xb4212f5b564c62c3, 0x435ce073230999fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b5633, 0xb4212fa8564c62c3, 0x43d3e073238499fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmaxsb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxsb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxsd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmaxsb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxsb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxsd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmaxsb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxsb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxsb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pmaxsb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpmaxsb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pmaxsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pmaxsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pmaxsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpmaxsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pmaxsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxsd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxsd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pmaxsd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpmaxsd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMINUB - Compare unsigned byte integers and returns minimum values.
+ * [V]PMINUW - Compare unsigned word integers and returns minimum values.
+ * [V]PMINUD - Compare unsigned double word integers and returns minimum values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminub_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminub_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminub_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminub_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminub_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminub_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminub_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminub_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminub_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminub_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminub_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminub_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminub_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminub_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminuw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminuw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminuw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminuw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminuw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminuw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminuw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminuw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminuw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminuw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminuw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminuw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminud_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminud_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminud_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminud_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminud_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminud_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminud_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminud_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminud_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminud_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminud_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminud_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pminub_pminuw_pminud(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 4, 6, 7, 0x5555666677770000) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x435ccd73230996bb) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xff820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x00220000ff800042) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1e09dd2a09633294, 0x3e5c17c8406b3f33, 0x88002f5b564c62a2, 0x435ccd73230996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0x3ef417c840723fe6, 0x88002fa8564c62c3, 0x43d3cda0238496bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0x3ef417c840725633, 0x8800e95b564c9ba2, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pminub_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminub_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminuw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminuw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminud_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminud_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pminub_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminub_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminuw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminuw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminud_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminud_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pminub_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminub_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminub_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminub_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminub_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminub_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminuw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminuw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminuw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminuw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminuw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminud_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminud_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminud_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminud_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminud_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMINSB - Compare signed byte integers and returns minimum values.
+ * [V]PMINSW - Compare signed word integers and returns minimum values.
+ * [V]PMINSD - Compare signed double word integers and returns minimum values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminsb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminsb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pminsd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pminsd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pminsd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpminsd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpminsd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpminsd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pminsb_pminsw_pminsd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB64[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C( 0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C( 0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C( 1, 2, 3, 0xf1f2f3f4f5f6f7f8),
+ /*src1*/ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xe1e2e3e4e5e6e7e8) },
+ { /*src2*/ RTUINT256_INIT_C( 4, 5, 7, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C( 4, 6, 7, 0xddddeeeeffff8888) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0x9c5ccda0930996bb) },
+ { /*src2*/ RTUINT256_INIT_C( 8, 10, 11, 0xff820064fffe0042),
+ /*src1*/ RTUINT256_INIT_C(12, 13, 14, 0x0022fe00ff80ff81),
+ /* => */ RTUINT256_INIT_C(12, 13, 14, 0xff82fe00ff80ff81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09dc3294, 0xf9f48ec8406b3fe6, 0x8800e9a8bf999ba2, 0x9cd3cda0938496bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40723fe6, 0x8800e95bbf999ba2, 0x9c5ccda0930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pminsb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminsb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminsd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pminsb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminsb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminsd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pminsb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminsb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminsb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pminsb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpminsb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pminsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB64), s_aValuesB64 },
+ { bs3CpuInstr3_pminsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pminsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpminsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pminsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminsd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminsd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pminsd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpminsd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]MOVSS - move (mem) or merge (reg) scalar single-precision floating-point value.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movss_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movss_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movss_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovss_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovss_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovss_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movss_XMM11_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movss_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movss_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovss_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovss_XMM10_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovss_FSxBX_XMM9_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movss(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesR[] =
+ {
+ { /*src*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*dst-in*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*dst-in*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x81828384c5c6c7c8) },
+ { /*src*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*dst-in*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeee77778888) },
+ { /*src*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*dst-in*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesM[] =
+ {
+ { /*src*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*dst-in*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*dst-in*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00000000c5c6c7c8) },
+ { /*src*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*dst-in*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000077778888) },
+ { /*src*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*dst-in*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x00000000930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movss_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movss_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM32, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movss_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM32, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovss_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovss_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovss_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movss_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movss_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM32, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movss_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM32, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovss_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovss_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovss_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movss_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movss_XMM11_XMM8_icebp_c64, 255, RM_REG, T_SSE, 11, 11, 8, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movss_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movss_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movss_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM32, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movss_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM32, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovss_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovss_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 9, 9, 10, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovss_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovss_XMM10_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 10, 10, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovss_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovss_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]MOVSD - move (mem) or merge (reg) scalar single-precision floating-point value.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movsd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movsd_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movsd_XMM11_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movsd_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movsd_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovsd_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovsd_XMM10_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovsd_FSxBX_XMM9_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movsd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesR[] =
+ {
+ { /*src*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*dst-in*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*dst-in*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*src*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x81828384c5c6c7c8),
+ /*dst-in*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0x81828384c5c6c7c8) },
+ { /*src*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*dst-in*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesM[] =
+ {
+ { /*src*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*dst-in*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*dst-in*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0xc1c2c3c4c5c6c7c8) },
+ { /*src*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*dst-in*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x5555666677778888) },
+ { /*src*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*dst-in*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movsd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movsd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovsd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovsd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movsd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movsd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovsd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovsd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movsd_XMM11_XMM8_icebp_c64, 255, RM_REG, T_SSE, 11, 11, 8, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_movsd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movsd_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movsd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_movsd_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+
+ { bs3CpuInstr3_vmovsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 1, 2, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovsd_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 9, 9, 10, RT_ELEMENTS(s_aValuesR), s_aValuesR },
+ { bs3CpuInstr3_vmovsd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 1, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovsd_XMM10_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 10, 10, 255, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovsd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ { bs3CpuInstr3_vmovsd_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesM), s_aValuesM },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]MOVLPS - Merge a low qword (two single precision floating-point values)
+ * from memory with the high qword from a register (SSE destination
+ * or VEX 2nd source).
+ * The store variant just stores the high qword.
+ * [V]MOVLPD - Same, just using double precision floating-point unit.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movlps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movlps_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovlps_XMM1_XMM2_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovlps_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movlps_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movlps_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovlps_XMM10_XMM14_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovlps_FSxBX_XMM9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movlpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movlpd_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovlpd_XMM1_XMM2_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovlpd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movlpd_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movlpd_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovlpd_XMM10_XMM14_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovlpd_FSxBX_XMM9_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movlps_movlpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesLd[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x81828384c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9999aaaabbbbcccc, 0x81828384c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x8800e95bbf9962c3, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesSt[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x81828384c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9999aaaabbbbcccc, 0x81828384c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x8800e95bbf9962c3, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movlps_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlps_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movlpd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlpd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovlps_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlps_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovlpd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlpd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movlps_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlps_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movlpd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlpd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovlps_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlps_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovlpd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlpd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movlps_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlps_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlps_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movlps_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movlpd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlpd_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movlpd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movlpd_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovlps_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlps_XMM10_XMM14_FSxBX_icebp_c64,X86_XCPT_AC, RM_MEM64, T_AVX_128, 10, 14, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlps_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovlps_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovlpd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlpd_XMM10_XMM14_FSxBX_icebp_c64,X86_XCPT_AC, RM_MEM64, T_AVX_128, 10, 14, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovlpd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovlpd_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]MOVHPS - Merge a high qword (two single precision floating-point values)
+ * from memory with the low qword from a register (SSE destination
+ * or VEX 2nd source).
+ * The store variant just stores the high qword.
+ * [V]MOVHPD - Same, just using double precision floating-point unit.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movhps_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movhps_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovhps_XMM1_XMM2_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovhps_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movhps_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movhps_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovhps_XMM10_XMM14_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovhps_FSxBX_XMM9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movhpd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movhpd_FSxBX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovhpd_XMM1_XMM2_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovhpd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movhpd_XMM8_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movhpd_FSxBX_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovhpd_XMM10_XMM14_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovhpd_FSxBX_XMM9_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movhps_movhpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesLd[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0xc1c2c3c4c5c6c7c8, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x5555666677778888, 0xddddeeeeffff0000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9c5ce073930996bb, 0x43d3cda0238499fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesSt[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*ign*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*ign*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0xd1d2d3d4d5d6d7d8) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x81828384c5c6c7c8),
+ /*ign*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1111222233334444) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*ign*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0xb4212fa8564c9ba2) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movhps_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhps_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movhpd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhpd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovhps_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhps_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovhpd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhpd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movhps_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhps_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movhpd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhpd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovhps_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhps_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovhpd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhpd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movhps_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhps_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhps_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movhps_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movhpd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhpd_XMM8_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_movhpd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+ { bs3CpuInstr3_movhpd_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM64, T_SSE, 255, 128, 11, RT_ELEMENTS(s_aValuesSt), s_aValuesSt },
+
+ { bs3CpuInstr3_vmovhps_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhps_XMM10_XMM14_FSxBX_icebp_c64,X86_XCPT_AC, RM_MEM64, T_AVX_128, 10, 14, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhps_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovhps_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovhpd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhpd_XMM10_XMM14_FSxBX_icebp_c64,X86_XCPT_AC, RM_MEM64, T_AVX_128, 10, 14, 255, RT_ELEMENTS(s_aValuesLd), s_aValuesLd },
+ { bs3CpuInstr3_vmovhpd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 1, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ { bs3CpuInstr3_vmovhpd_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 255, 128, 9, RT_ELEMENTS(s_aValuesSt), s_aValuesSt},
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]MOVHLPS - Move high qword in source (2) to low qword in destination, leaving
+ * the high qword in the destination as it was. The VEX variant
+ * takes the high qword from the first source operand.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movhlps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovhlps_XMM1_XMM2_XMM3_icebp);
+extern FNBS3FAR bs3CpuInstr3_movhlps_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovhlps_XMM10_XMM14_XMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movhlps(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9192939495969798, 0xd1d2d3d4d5d6d7d8) },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x81828384c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x9999aaaabbbbcccc, 0x1111222233334444) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x8800e95bbf9962c3, 0xb4212fa8564c9ba2) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movhlps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovhlps_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movhlps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovhlps_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movhlps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movhlps_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 8, 12, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovhlps_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovhlps_XMM10_XMM14_XMM12_icebp_c64, 255, RM_REG, T_AVX_128, 10, 14, 12, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]PAVGB - Average unsigned packed byte integers with rounding.
+ * [V]PAVGW - Average unsigned packed word integers with rounding.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pavgb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pavgb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpavgb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpavgb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpavgb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpavgb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pavgw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pavgw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pavgw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpavgw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpavgw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpavgw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpavgw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpavgw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pavgb_pavgw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8, 0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3673e76b3ba053b5, 0x9ca853da536f4b8d, 0x9e118c828b737fb3, 0x7098d78a5b4798dc) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8, 0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x35f3e6eb3b205335, 0x9c28535a536f4b0d, 0x9e118c828af37f33, 0x7018d70a5b47985c) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pavgb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pavgw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pavgb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pavgw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pavgb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pavgb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpavgb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pavgw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pavgw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpavgw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PSIGNB - Negate/Zero/Keep the destination packed byte integers based on the sign of the corresponding source operand.
+ * [V]PSIGNW - Negate/Zero/Keep the destination packed word integers based on the sign of the corresponding source operand.
+ * [V]PSIGND - Negate/Zero/Keep the destination packed doubleword integers based on the sign of the corresponding source operand.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_psignb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_psignb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignb_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignb_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignb_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignb_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignb_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignb_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignb_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignb_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_psignw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_psignw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psignd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_psignd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_psignd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsignd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsignd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsignd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_psignb_psignw_psignd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesB[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x4f4e4d4c4b4a4948, 0x5f5e5d5c5b5a5958, 0x6f6e6d6c6b6a6968, 0x7f7e7d7c7b7a7978) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1edd23ac099d326c, 0xf9a48e14407256cd, 0x7800e9a5bf999e3d, 0xbdd333a0dd846703) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesW[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x4e4e4c4c4a4a4848, 0x5e5e5c5c5a5a5858, 0x6e6e6c6c6a6a6868, 0x7e7e7c7c7a7a7878) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1edd225409633294, 0xf95c8eec40725633, 0x7800e95bbf999d3d, 0xbc2d3260dc7c6603) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x4e4d4c4c4a494848, 0x5e5d5c5c5a595858, 0x6e6d6c6c6a696868, 0x7e7d7c7c7a797878) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x77ff16a5bf9962c3, 0xbc2c3260dc7b6603) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_psignb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psignw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psignd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_psignb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psignw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psignd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_psignb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_psignb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpsignb_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_psignw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_psignw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpsignw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_psignd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_psignd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpsignd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PHADDW - Horizontally add word sized signed integers.
+ * [V]PHADDD - Horizontally add doubleword sized signed integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phaddw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phaddw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phaddd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phaddd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_phaddw_phaddd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x85868d8e05060d0e) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x7ccf29c41173bd81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0xa5a6adae85868d8e, 0x25262d2e05060d0e) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0xe3c9f1ee7ccf29c4, 0x715b225c1173bd81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe5e6edeec5c6cdce, 0x65666d6e45464d4e, 0xa5a6adae85868d8e, 0x25262d2e05060d0e) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3d33e0b156bca651, 0xfc893bf7884896a5, 0xe3c9f1ee7ccf29c4, 0x715b225c1173bd81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x87898b8c07090b0c) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x2f66772e6758679d) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0xa7a9abac87898b8c, 0x27292b2c07090b0c) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0x0a6dcb4a2f66772e, 0x479a4c1e6758679d) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe7e9ebecc7c9cbcc, 0x67696b6c47494b4c, 0xa7a9abac87898b8c, 0x27292b2c07090b0c) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xb9e663ffa55f57ae, 0x2841104039cee51f, 0x0a6dcb4a2f66772e, 0x479a4c1e6758679d) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_phaddw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phaddd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phaddd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_phaddw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phaddd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phaddd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_phaddw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phaddd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phaddd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phaddd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phaddd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phaddd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphaddd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphaddd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphaddd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PHSUBW - Horizontally subtract word sized signed integers.
+ * [V]PHSUBD - Horizontally subtract doubleword sized signed integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phsubw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phsubw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phsubd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phsubd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_phsubw_phsubd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x441703b289cd7679) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0x0202020202020202, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0x7b874556441703b2, 0x615ba32a89cd7679) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0202020202020202, 0x0202020202020202, 0x0202020202020202, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xa32106f9d8d4d97b, 0xbecf2931959015c1, 0x7b874556441703b2, 0x615ba32a89cd7679) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x0404040404040404) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0xf6acb648dfb0cc5d) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0x0404040404040404, 0x0404040404040404) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0xa22b6bfaf6acb648, 0x37987968dfb0cc5d) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256D[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0404040404040404, 0x0404040404040404, 0x0404040404040404, 0x0404040404040404) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1fd283ab2777281e, 0xea8554e84715c747, 0xa22b6bfaf6acb648, 0x37987968dfb0cc5d) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_phsubw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phsubd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phsubd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_phsubw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phsubd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phsubd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_phsubw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+
+ { bs3CpuInstr3_phsubd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64D), s_aValues64D },
+ { bs3CpuInstr3_phsubd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phsubd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phsubd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_phsubd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128D), s_aValues128D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphsubd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphsubd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ { bs3CpuInstr3_vphsubd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256D), s_aValues256D },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PHADDSW - Horizontally add and saturate word sized signed integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phaddsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phaddsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phaddsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphaddsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphaddsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphaddsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_phaddsw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x85868d8e80008000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x800080001173bd81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0xa5a6adae85868d8e, 0x8000800080008000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0xe3c9f1ee80008000, 0x8000225c1173bd81) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe5e6edeec5c6cdce, 0x8000800080008000, 0xa5a6adae85868d8e, 0x8000800080008000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3d337fff56bc7fff, 0xfc893bf788487fff, 0xe3c9f1ee80008000, 0x8000225c1173bd81) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_phaddsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_phaddsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_phaddsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phaddsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phaddsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphaddsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PHSUBSW - Horizontally subtract and saturate word sized signed integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phsubsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phsubsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phsubsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphsubsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphsubsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphsubsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_phsubsw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x441703b289cd8000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0x0202020202020202, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0x7b878000441703b2, 0x615b7fff89cd8000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0202020202020202, 0x0202020202020202, 0x0202020202020202, 0x0202020202020202) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0xa32106f9d8d4d97b, 0xbecf2931959015c1, 0x7b878000441703b2, 0x615b7fff89cd8000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_phsubsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_phsubsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_phsubsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_phsubsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_phsubsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vphsubsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMADDUBSW - Horizontally subtract and saturate word sized signed integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaddubsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaddubsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaddubsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmaddubsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmaddubsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmaddubsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaddubsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaddubsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmaddubsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmaddubsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmaddubsw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 3, 0xc0c5c1d9c2fdc431) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 7, 0x31a82e40f5bd8000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0xcb25ccb9ce5dd011, 0xc0c5c1d9c2fdc431) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0xd7a00b7f6d9691bc, 0x31a82e40f5bd8000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256W[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xebe5ee79f11df3d1, 0xd985db99ddbddff1, 0xcb25ccb9ce5dd011, 0xc0c5c1d9c2fdc431) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x10cb0e68f5e0fd9a, 0x37fed92249260ffc, 0xd7a00b7f6d9691bc, 0x31a82e40f5bd8000) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmaddubsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmaddubsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmaddubsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64W), s_aValues64W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_pmaddubsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_pmaddubsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_pmaddubsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128W), s_aValues128W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vpmaddubsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vpmaddubsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ { bs3CpuInstr3_vpmaddubsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256W), s_aValues256W },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMULHRSW - Vertically multiply, round and scale word sized signed integers and extract the high 16-bits.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhrsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhrsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhrsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmulhrsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmulhrsw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmulhrsw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmulhrsw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmulhrsw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmulhrsw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmulhrsw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmulhrsw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0899072e05d40489, 0x16341448126d10a1, 0x27d7256a230e20c1, 0x3d823a9437b734e9) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1293043f07fc2dc5, 0xfcbceafe33912b08, 0x4721f792d495b28f, 0xcb340c6be1c453e5) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmulhrsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmulhrsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmulhrsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmulhrsw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmulhrsw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PSADBW - Compute sum of absolute differences of packed unsigned byte integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psadbw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psadbw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psadbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_psadbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_psadbw_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_psadbw_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsadbw_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsadbw_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsadbw_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsadbw_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsadbw_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpsadbw_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpsadbw_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpsadbw_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_psadbw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000200, 0x0000000000000200, 0x0000000000000200, 0x0000000000000200) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x00000000000002f6, 0x00000000000002e5, 0x0000000000000264, 0x0000000000000240) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_psadbw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_psadbw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_psadbw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_psadbw_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpsadbw_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMULDQ - Multiply packed signed double word integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuldq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuldq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmuldq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmuldq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuldq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuldq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmuldq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmuldq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuldq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuldq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmuldq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmuldq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmuldq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x02e97bbaf7148240, 0x0935ee3369408840, 0x118666b3e1709040, 0x1bdae53c5fa49a40) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x03fdeed546719124, 0x19c88e386340fed2, 0xea4a418ff1c09066, 0xf0e1df0254fbb9cf) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmuldq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuldq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmuldq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuldq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmuldq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuldq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuldq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuldq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuldq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PMULUDQ - Multiply packed unsigned double word integers.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuludq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuludq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuludq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmuludq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmuludq_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmuludq_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuludq_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuludq_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmuludq_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmuludq_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuludq_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmuludq_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmuludq_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmuludq_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmuludq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xae972b6af7148240, 0x94c37dc369408840, 0x7cf3d623e1709040, 0x6728348c5fa49a40) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x03fdeed546719124, 0x19c88e386340fed2, 0x4096dd31f1c09066, 0x146678ff54fbb9cf) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmuludq_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmuludq_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmuludq_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmuludq_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmuludq_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKLPS - Unpack and interleave low packed single precision FP values.
+ * [V]PUNPCKLPD - Unpack and interleave low packed double precision FP values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpcklps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpcklps_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_unpcklps_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_unpcklps_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklps_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklps_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpcklps_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpcklps_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklps_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklps_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpcklps_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpcklps_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpcklpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpcklpd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_unpcklpd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_unpcklpd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklpd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklpd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpcklpd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpcklpd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklpd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpcklpd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpcklpd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpcklpd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpcklps_punpcklpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesS[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2e3e4a1a2a3a4, 0xe5e6e7e8a5a6a7a8, 0xc1c2c3c481828384, 0xc5c6c7c885868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8f95c8eec, 0x666b3fe640725633, 0x9c5ce07343d3cda0, 0x930996bb238499fd) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2e3e4e5e6e7e8, 0xa1a2a3a4a5a6a7a8, 0xc1c2c3c4c5c6c7c8, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8666b3fe6, 0xf95c8eec40725633, 0x9c5ce073930996bb, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_unpcklps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpcklps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpcklpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpcklpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_unpcklps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpcklps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpcklpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpcklpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_unpcklps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpcklps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpcklps_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE, 8, 8, 9, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpcklps_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpcklps_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpcklpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpcklpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpcklpd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpcklpd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpcklpd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PUNPCKHPS - Unpack and interleave low packed single precision FP values.
+ * [V]PUNPCKHPD - Unpack and interleave low packed double precision FP values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpckhps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpckhps_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_unpckhps_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_unpckhps_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhps_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhps_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpckhps_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpckhps_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhps_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhps_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpckhps_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpckhps_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpckhpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_unpckhpd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_unpckhpd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_unpckhpd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhpd_XMM1_XMM2_XMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhpd_XMM1_XMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpckhpd_XMM8_XMM9_XMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpckhpd_XMM8_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhpd_YMM1_YMM2_YMM3_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vunpckhpd_YMM1_YMM2_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vunpckhpd_YMM8_YMM9_YMM10_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vunpckhpd_YMM8_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_punpckhps_punpckhpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesS[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4b1b2b3b4, 0xf5f6f7f8b5b6b7b8, 0xd1d2d3d491929394, 0xd5d6d7d895969798) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a1eddddac, 0x6cdc73d509633294, 0xb4212fa88800e95b, 0x564c9ba2bf9962c3) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesD[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xb1b2b3b4b5b6b7b8, 0xd1d2d3d4d5d6d7d8, 0x9192939495969798) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x1eddddac09633294, 0xb4212fa8564c9ba2, 0x8800e95bbf9962c3) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_unpckhps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpckhps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpckhpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpckhpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_XMM3_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_YMM3_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_unpckhps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpckhps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpckhpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpckhpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_XMM3_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_YMM3_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_unpckhps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpckhps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpckhps_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE, 8, 8, 9, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_unpckhps_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+ { bs3CpuInstr3_vunpckhps_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesS), s_aValuesS },
+
+ { bs3CpuInstr3_unpckhpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpckhpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 1, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpckhpd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE, 8, 8, 9, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_unpckhpd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 8, 8, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_XMM3_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM1_XMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM8_XMM9_XMM10_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_XMM8_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_YMM3_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM1_YMM2_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM8_YMM9_YMM10_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vunpckhpd_YMM8_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]SHUFPS - Shuffle two pairs of single precision floating point values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufps_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufps_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufps_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufps_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_shufps_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufps_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufps_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufps_XMM8_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vshufps_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_XMM8_XMM9_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vshufps_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufps_YMM8_YMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_shufps(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f1f2f3f4, 0xb1b2b3b4b1b2b3b4, 0xd1d2d3d4d1d2d3d4, 0x9192939491929394) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a4d09f02a, 0x1eddddac1eddddac, 0xb4212fa8b4212fa8, 0x8800e95b8800e95b) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe5e6e7e8e5e6e7e8, 0xa5a6a7a8a5a6a7a8, 0xc5c6c7c8c5c6c7c8, 0x8586878885868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x666b3fe6666b3fe6, 0x4072563340725633, 0x930996bb930996bb, 0x238499fd238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_shufps_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_shufps_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_shufps_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufps_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufps_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufps_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufps_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufps_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]SHUFPD - Shuffle two pairs of double precision floating point values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufpd_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufpd_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufpd_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_shufpd_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_shufpd_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufpd_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufpd_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_shufpd_XMM8_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vshufpd_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_XMM8_XMM9_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vshufpd_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vshufpd_YMM8_YMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_shufpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xb1b2b3b4b5b6b7b8, 0xd1d2d3d4d5d6d7d8, 0x9192939495969798) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x1eddddac09633294, 0xb4212fa8564c9ba2, 0x8800e95bbf9962c3) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xe1e2e3e4e5e6e7e8, 0xa1a2a3a4a5a6a7a8, 0xc1c2c3c4c5c6c7c8, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8666b3fe6, 0xf95c8eec40725633, 0x9c5ce073930996bb, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_shufpd_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSE2, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufpd_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE2, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufpd_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSE2, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_shufpd_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE2, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vshufpd_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PALIGNR - Concatenate and align source operands to the right.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_MM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_MM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_FSxBX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_MM2_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_FSxBX_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_MM2_009h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_MM1_FSxBX_009h_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_FSxBX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_XMM2_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_FSxBX_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_XMM2_013h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_palignr_XMM1_FSxBX_013h_icebp);
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_FSxBX_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_XMM9_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_FSxBX_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_XMM9_013h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_palignr_XMM8_FSxBX_013h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_013h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_013h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_013h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_013h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_003h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_013h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_013h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_003h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_013h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_013h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_palignr(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64B_03[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 17, 18, 19, 0x868788c1c2c3c4c5) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 21, 22, 23, 0x8499fd9c5ce07393) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues64B_09[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 17, 18, 19, 0x0081828384858687) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 21, 22, 23, 0x0043d3cda0238499) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128B_03[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 17, 18, 0x868788d1d2d3d4d5, 0xd6d7d8c1c2c3c4c5) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 21, 22, 0x8499fdb4212fa856, 0x4c9ba29c5ce07393) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues128B_13[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 17, 18, 0x0000009192939495, 0x9697988182838485) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 21, 22, 0x0000008800e95bbf, 0x9962c343d3cda023) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256B_03[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xa6a7a8f1f2f3f4f5, 0xf6f7f8e1e2e3e4e5, 0x868788d1d2d3d4d5, 0xd6d7d8c1c2c3c4c5) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x7256334d09f02a6c, 0xdc73d53ef417c866, 0x8499fdb4212fa856, 0x4c9ba29c5ce07393) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues256B_13[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0x000000b1b2b3b4b5, 0xb6b7b8a1a2a3a4a5, 0x0000009192939495, 0x9697988182838485) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x0000001eddddac09, 0x633294f95c8eec40, 0x0000008800e95bbf, 0x9962c343d3cda023) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_palignr_MM1_MM2_0FFh_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_MM2_000h_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_MM2_003h_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_003h_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_MM2_009h_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_009h_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_003h_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_003h_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_013h_icebp_c16, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_013h_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_003h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_003h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_013h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_013h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_003h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_003h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_013h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_013h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_palignr_MM1_MM2_0FFh_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_MM2_000h_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_MM2_003h_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_003h_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_MM2_009h_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_009h_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_003h_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_003h_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM1_XMM2_013h_icebp_c32, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_013h_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_003h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_003h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_013h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_013h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_003h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_003h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_013h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_013h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_palignr_MM1_MM2_0FFh_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_MM1_MM2_000h_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_MM1_MM2_003h_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_003h_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_03), s_aValues64B_03 },
+ { bs3CpuInstr3_palignr_MM1_MM2_009h_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+ { bs3CpuInstr3_palignr_MM1_FSxBX_009h_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues64B_09), s_aValues64B_09 },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_palignr_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_palignr_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_003h_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_003h_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM8_XMM9_003h_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_palignr_XMM8_FSxBX_003h_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+
+ { bs3CpuInstr3_palignr_XMM1_XMM2_013h_icebp_c64, 255, RM_REG, T_SSSE3, 1, 1, 2, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_palignr_XMM1_FSxBX_013h_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 1, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_palignr_XMM8_XMM9_013h_icebp_c64, 255, RM_REG, T_SSSE3, 8, 8, 9, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_palignr_XMM8_FSxBX_013h_icebp_c64, 255, RM_MEM, T_SSSE3, 8, 8, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_003h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_003h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_003h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_003h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128B_03), s_aValues128B_03 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_XMM3_013h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_XMM1_XMM2_FSxBX_013h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_XMM10_013h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+ { bs3CpuInstr3_vpalignr_XMM8_XMM9_FSxBX_013h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues128B_13), s_aValues128B_13 },
+
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_003h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_003h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_003h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_003h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256B_03), s_aValues256B_03 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_YMM3_013h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ { bs3CpuInstr3_vpalignr_YMM1_YMM2_FSxBX_013h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_YMM10_013h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ { bs3CpuInstr3_vpalignr_YMM8_YMM9_FSxBX_013h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues256B_13), s_aValues256B_13 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PBLENDW - Blend packed words based on an 8-bit immediate.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendw_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendw_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendw_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendw_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_pblendw_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pblendw_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pblendw_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pblendw_XMM8_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpblendw_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_XMM8_XMM9_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpblendw_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendw_YMM8_YMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pblendw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pblendw_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_pblendw_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pblendw_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pblendw_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pblendw_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpblendw_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpblendw_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]BLENDPS - Blend packed single precision floating point values based on an 8-bit immediate.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendps_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendps_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendps_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendps_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_blendps_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendps_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendps_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendps_XMM8_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendps_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_XMM8_XMM9_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendps_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendps_YMM8_YMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_blendps(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_blendps_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_blendps_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_blendps_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendps_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_blendps_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendps_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendps_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendps_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendps_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendps_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]BLENDPD - Blend packed double precision floating point values based on an 8-bit immediate.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendpd_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendpd_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendpd_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendpd_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_blendpd_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendpd_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendpd_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendpd_XMM8_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendpd_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_XMM8_XMM9_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendpd_YMM8_YMM9_YMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_YMM8_YMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_YMM8_YMM9_YMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendpd_YMM8_YMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_blendpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_000h_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_000h_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_blendpd_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_blendpd_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendpd_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendpd_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_blendpd_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM8_YMM9_YMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vblendpd_YMM8_YMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_YMM3_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM1_YMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM8_YMM9_YMM10_000h_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vblendpd_YMM8_YMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]PCLMULQDQ - Carry-less multiplication of a quadword.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pclmulqdq_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pclmulqdq_XMM1_XMM2_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_pclmulqdq_XMM8_XMM9_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pclmulqdq_XMM8_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pclmulqdq_XMM8_XMM9_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pclmulqdq_XMM8_FSxBX_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_XMM10_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_XMM10_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_FSxBX_000h_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pclmulqdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValuesFF[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 1, 2, 0x6541a5056544a512, 0x6753a7176756a740) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 5, 6, 0x5fb1fa02ce11d9e9, 0x547462ca50871166) },
+ };
+ static BS3CPUINSTR3_TEST1_VALUES_T const s_aValues00[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C( 9, 10, 0x6041a0056044a012, 0x6253a2176256a240) },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ RTUINT256_INIT_C( 13, 14, 0x26d2ea34453fe24f, 0x2592f499ed1f651f) },
+ };
+
+ static BS3CPUINSTR3_TEST1_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_000h_icebp_c16, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_0FFh_icebp_c16, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_000h_icebp_c16, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_000h_icebp_c32, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_0FFh_icebp_c32, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_000h_icebp_c32, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM8_XMM9_0FFh_icebp_c64, 255, RM_REG, T_PCLMUL, 8, 8, 9, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pclmulqdq_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_PCLMUL, 8, 8, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_pclmulqdq_XMM1_XMM2_000h_icebp_c64, 255, RM_REG, T_PCLMUL, 1, 1, 2, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pclmulqdq_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM, T_PCLMUL, 1, 1, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pclmulqdq_XMM8_XMM9_000h_icebp_c64, 255, RM_REG, T_PCLMUL, 8, 8, 9, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pclmulqdq_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM, T_PCLMUL, 8, 8, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_0FFh_icebp_c64, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_XMM10_0FFh_icebp_c64, 255, RM_REG, T_AVX_PCLMUL, 8, 9, 10, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 8, 9, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_XMM3_000h_icebp_c64, 255, RM_REG, T_AVX_PCLMUL, 1, 2, 3, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpclmulqdq_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 1, 2, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_XMM10_000h_icebp_c64, 255, RM_REG, T_AVX_PCLMUL, 8, 9, 10, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpclmulqdq_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_PCLMUL, 8, 9, 255, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ };
+ static BS3CPUINSTR3_TEST1_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST1_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType1(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * Test type #2 - GPR <- MM/XMM/YMM, no VVVV.
+ */
+
+typedef struct BS3CPUINSTR3_TEST2_VALUES_T
+{
+ RTUINT256U uMedia;
+ uint64_t uGpr;
+} BS3CPUINSTR3_TEST2_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST2_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t cbGpr;
+ uint8_t cBitsGprValMask;
+ bool fInvalidEncoding;
+ bool fGprDst;
+ uint8_t iGprReg;
+ uint8_t iMediaReg;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST2_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST2_T;
+
+typedef struct BS3CPUINSTR3_TEST2_MODE_T
+{
+ BS3CPUINSTR3_TEST2_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST2_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST2_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST2_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #2 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType2(uint8_t bMode, BS3CPUINSTR3_TEST2_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST2_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fGprDst = paTests[iTest].fGprDst;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = bs3CpuInstr3MemOpSize(cbOperand, paTests[iTest].enmRm);
+ uint8_t const cbAlign = RT_MIN(cbOperand, 16);
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType]
+ || paTests[iTest].fInvalidEncoding ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint64_t const fGprValMask = paTests[iTest].cBitsGprValMask == 64 ? UINT64_MAX
+ : RT_BIT_64(paTests[iTest].cBitsGprValMask) - 1;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ if (fGprDst)
+ {
+ /* dest - gpr/mem */
+ if (paTests[iTest].iGprReg == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(puMemOp, 0xcc, cbMemOp);
+ if (bXcptExpect != X86_XCPT_DB)
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ else
+ {
+ Bs3MemSet(&uMemOpExpect, 0xaa, sizeof(uMemOpExpect));
+ switch (paTests[iTest].cbGpr)
+ {
+ case 1: uMemOpExpect.au8[0] = (uint8_t) (paValues[iVal].uGpr & fGprValMask); break;
+ case 2: uMemOpExpect.au16[0] = (uint16_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 4: uMemOpExpect.au32[0] = (uint32_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 8: uMemOpExpect.au64[0] = (paValues[iVal].uGpr & fGprValMask); break;
+ default: BS3_ASSERT(0);
+ }
+ }
+ }
+ else
+ Bs3RegCtxSetGpr(&Ctx, paTests[iTest].iGprReg, UINT64_C(0xcccccccccccccccc),
+ BS3_MODE_IS_64BIT_CODE(bMode) ? 8 : 4); /* we only restore 63:32 when bMode==LM64 */
+
+ /* source - media/mem */
+ if (paTests[iTest].iMediaReg == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iGprReg != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uMedia, cbMemOp);
+ uMemOpExpect = paValues[iVal].uMedia;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iMediaReg, paValues[iVal].uMedia.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iMediaReg, &paValues[iVal].uMedia.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iMediaReg, &paValues[iVal].uMedia, 32);
+ }
+ else
+ {
+ /* dest - media */
+ if (paTests[iTest].iMediaReg == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(puMemOp, 0xcc, cbMemOp);
+ if (bXcptExpect == X86_XCPT_DB)
+ uMemOpExpect = paValues[iVal].uMedia;
+ else
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ }
+
+ /* source - gpr/mem */
+ if (paTests[iTest].iGprReg == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ if (bXcptExpect == X86_XCPT_DB)
+ switch (paTests[iTest].cbGpr)
+ {
+ case 1: uMemOpExpect.au8[0] = (uint8_t) (paValues[iVal].uGpr & fGprValMask); break;
+ case 2: uMemOpExpect.au16[0] = (uint16_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 4: uMemOpExpect.au32[0] = (uint32_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 8: uMemOpExpect.au64[0] = (paValues[iVal].uGpr & fGprValMask); break;
+ default: BS3_ASSERT(0);
+ }
+ Bs3MemCpy(puMemOp, &uMemOpExpect, cbMemOp);
+ }
+ else
+ Bs3RegCtxSetGpr(&Ctx, paTests[iTest].iGprReg, paValues[iVal].uGpr & fGprValMask, paTests[iTest].cbGpr);
+ }
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT(paTests[iTest].iGprReg == UINT8_MAX || paTests[iTest].iMediaReg == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (fMmxInstr && bXcptExpect == X86_XCPT_DB)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff);
+ }
+ if (!fGprDst && bXcptExpect == X86_XCPT_DB && paTests[iTest].iMediaReg != UINT8_MAX)
+ {
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iMediaReg, paValues[iVal].uMedia.QWords.qw0, BS3EXTCTXTOPMM_SET);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iMediaReg, &paValues[iVal].uMedia.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iMediaReg, &paValues[iVal].uMedia, 32);
+ }
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ if (fGprDst && bXcptExpect == X86_XCPT_DB && paTests[iTest].iGprReg != UINT8_MAX)
+ Bs3RegCtxSetGpr(&Ctx, paTests[iTest].iGprReg, paValues[iVal].uGpr & fGprValMask,
+ paTests[iTest].cbGpr >= 4 ? 8 : paTests[iTest].cbGpr);
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, bXcptExpect, puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * PMOVMSKB, VPMOVMSKB.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovmskb_EAX_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovmskb_EAX_qword_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovmskb_EAX_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovmskb_EAX_dqword_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovmskb_EAX_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovmskb_EAX_dqword_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovmskb_EAX_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovmskb_EAX_qqword_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovmskb_RAX_YMM9_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmovmskb(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xffffffff) },
+ { RTUINT256_INIT_C(0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x00000000) },
+ { RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080), UINT64_C(0xffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x03000003) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0x255193ab) },
+ };
+
+ static BS3CPUINSTR3_TEST2_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmovmskb_EAX_MM2_icebp_c16, 255, RM_REG, T_AXMMX_OR_SSE, 4, 8, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_qword_FSxBX_icebp_c16, 255, RM_MEM, T_AXMMX_OR_SSE, 4, 8, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_XMM2_icebp_c16, 255, RM_REG, T_SSE2, 4, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_dqword_FSxBX_icebp_c16, 255, RM_MEM, T_SSE2, 4, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 4, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_dqword_FSxBX_icebp_c16, 255, RM_MEM, T_AVX_128, 4, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_YMM2_icebp_c16, 255, RM_REG, T_AVX2_256, 4, 32, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_qqword_FSxBX_icebp_c16, 255, RM_MEM, T_AVX2_256, 4, 32, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmovmskb_EAX_MM2_icebp_c32, 255, RM_REG, T_AXMMX_OR_SSE, 4, 8, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_qword_FSxBX_icebp_c32, 255, RM_MEM, T_AXMMX_OR_SSE, 4, 8, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_XMM2_icebp_c32, 255, RM_REG, T_SSE2, 4, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_dqword_FSxBX_icebp_c32, 255, RM_MEM, T_SSE2, 4, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 4, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_dqword_FSxBX_icebp_c32, 255, RM_MEM, T_AVX_128, 4, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_YMM2_icebp_c32, 255, RM_REG, T_AVX2_256, 4, 32, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_qqword_FSxBX_icebp_c32, 255, RM_MEM, T_AVX2_256, 4, 32, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmovmskb_EAX_MM2_icebp_c64, 255, RM_REG, T_AXMMX_OR_SSE, 8, 8, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_qword_FSxBX_icebp_c64, 255, RM_MEM, T_AXMMX_OR_SSE, 8, 8, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_XMM2_icebp_c64, 255, RM_REG, T_SSE2, 8, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pmovmskb_EAX_dqword_FSxBX_icebp_c64, 255, RM_MEM, T_SSE2, 8, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 8, 16, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_dqword_FSxBX_icebp_c64, 255, RM_MEM, T_AVX_128, 8, 16, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_YMM2_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 32, false, true, 0, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_EAX_qqword_FSxBX_icebp_c64, 255, RM_MEM, T_AVX2_256, 8, 32, true, true, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpmovmskb_RAX_YMM9_icebp_c64, 255, RM_REG, T_AVX2_256, 8, 32, false, true, 0, 9, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST2_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST2_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType2(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * [V]MOVD / [V]MOVQ - greg/mem variants only.
+ */
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_MM1_EDX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_EAX_MM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_FSxBX_MM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movd_MM1_R9D_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movd_R10D_MM0_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_XMM1_EAX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_EAX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movd_XMM9_R8D_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movd_R8D_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movd_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movd_FSxBX_XMM9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovd_XMM1_EAX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovd_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovd_EDX_XMM1_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovd_XMM9_R9D_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovd_R8D_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovd_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovd_FSxBX_XMM9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_FSxBX_MM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movq_R9_MM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_06e_movq_MM1_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07e_movq_FSxBX_MM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movq_MM1_R9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movq_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movq_XMM9_R8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movq_R8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movq_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movq_FSxBX_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_06e_movq_XMM1_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_06e_movq_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07e_movq_FSxBX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07e_movq_FSxBX_XMM9_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovq_XMM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovq_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovq_XMM9_R8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovq_R8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovq_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovq_FSxBX_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_06e_vmovq_XMM1_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_06e_vmovq_XMM9_FSxBX_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07e_vmovq_FSxBX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07e_vmovq_FSxBX_XMM9_icebp_c64;
+
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movd_movq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesRm[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x7f7f7f7f7f7f7f7f) },
+ { RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080), UINT64_C(0x8080808080808080) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesMediaD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x00000000ffffffff), UINT64_C(0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x000000007f7f7f7f), UINT64_C(0x7f7f7f7f7f7f7f7f) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x0000000080808080), UINT64_C(0x8080808080808080) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x0000000077778888), UINT64_C(0x5555666677778888) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x00000000930996bb), UINT64_C(0x9c5ce073930996bb) },
+ };
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesMediaQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0) },
+ { RTUINT256_INIT_C(0, 0, 0, 0xffffffffffffffff), UINT64_C(0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x7f7f7f7f7f7f7f7f) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x8080808080808080), UINT64_C(0x8080808080808080) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x5555666677778888), UINT64_C(0x5555666677778888) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x9c5ce073930996bb), UINT64_C(0x9c5ce073930996bb) },
+ };
+
+ /* Note! Seems the 256-bit variants doesn't generate \#ACs on a 10980XE. WEIRD! */
+ static BS3CPUINSTR3_TEST2_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movd_MM1_EDX_icebp_c16, 255, RM_REG, T_MMX, 4, 32, false, false, 2, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_MM1_FSxBX_icebp_c16, 255, RM_MEM32, T_MMX, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_MM1_icebp_c16, 255, RM_REG, T_MMX, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_MM1_icebp_c16, 255, RM_MEM32, T_MMX, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movd_XMM1_EAX_icebp_c16, 255, RM_REG, T_SSE2, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_XMM1_FSxBX_icebp_c16, 255, RM_MEM32, T_SSE2, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_XMM1_icebp_c16, 255, RM_REG, T_SSE2, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_XMM1_icebp_c16, 255, RM_MEM32, T_SSE2, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovd_XMM1_EAX_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_EDX_XMM1_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovd_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_MM1_FSxBX_icebp_c16, 255, RM_MEM64, T_MMX, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_FSxBX_MM1_icebp_c16, 255, RM_MEM64, T_MMX, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_XMM1_FSxBX_icebp_c16, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_FSxBX_XMM1_icebp_c16, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_vmovq_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movd_MM1_EDX_icebp_c32, 255, RM_REG, T_MMX, 4, 32, false, false, 2, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_MM1_FSxBX_icebp_c32, 255, RM_MEM32, T_MMX, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_MM1_icebp_c32, 255, RM_REG, T_MMX, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_MM1_icebp_c32, 255, RM_MEM32, T_MMX, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movd_XMM1_EAX_icebp_c32, 255, RM_REG, T_SSE2, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_XMM1_FSxBX_icebp_c32, 255, RM_MEM32, T_SSE2, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_XMM1_icebp_c32, 255, RM_REG, T_SSE2, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_XMM1_icebp_c32, 255, RM_MEM32, T_SSE2, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovd_XMM1_EAX_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_EDX_XMM1_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovd_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movq_MM1_FSxBX_icebp_c32, 255, RM_MEM64, T_MMX, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_FSxBX_MM1_icebp_c32, 255, RM_MEM64, T_MMX, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movq_XMM1_FSxBX_icebp_c32, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_FSxBX_XMM1_icebp_c32, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_vmovq_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movd_MM1_EDX_icebp_c64, 255, RM_REG, T_MMX, 4, 32, false, false, 2, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_MM1_R9D_icebp_c64, 255, RM_REG, T_MMX, 4, 32, false, false, 9, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_MM1_FSxBX_icebp_c64, 255, RM_MEM32, T_MMX, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_MM1_icebp_c64, 255, RM_REG, T_MMX, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_R10D_MM0_icebp_c64, 255, RM_REG, T_MMX, 4, 32, false, true, 10, 0, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_MM1_icebp_c64, 255, RM_MEM32, T_MMX, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movd_XMM1_EAX_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_XMM9_R8D_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, false, 8, 9, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_XMM1_FSxBX_icebp_c64, 255, RM_MEM32, T_SSE2, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_XMM9_FSxBX_icebp_c64, 255, RM_MEM32, T_SSE2, 4, 32, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_movd_EAX_XMM1_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 0, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_R8D_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 8, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movd_FSxBX_XMM1_icebp_c64, 255, RM_MEM32, T_SSE2, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovd_XMM1_EAX_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, false, 0, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_XMM9_R9D_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, false, 9, 9, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaD), s_aValuesMediaD },
+ { bs3CpuInstr3_vmovd_EDX_XMM1_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovd_R8D_XMM9_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 8, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovd_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovd_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, false, true, 255, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movq_MM1_R9_icebp_c64, 255, RM_REG, T_MMX, 8, 64, false, false, 9, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_MM1_FSxBX_icebp_c64, 255, RM_MEM64, T_MMX, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_06e_movq_MM1_FSxBX_icebp_c64, 255, RM_MEM64, T_MMX, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_R9_MM1_icebp_c64, 255, RM_REG, T_MMX, 8, 64, false, true, 9, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_FSxBX_MM1_icebp_c64, 255, RM_MEM64, T_MMX, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_07e_movq_FSxBX_MM1_icebp_c64, 255, RM_MEM64, T_MMX, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_movq_XMM9_R8_icebp_c64, 255, RM_REG, T_SSE2, 8, 64, false, false, 8, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_XMM1_FSxBX_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_06e_movq_XMM1_FSxBX_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_XMM9_FSxBX_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_06e_movq_XMM9_FSxBX_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_movq_R8_XMM9_icebp_c64, 255, RM_REG, T_SSE2, 8, 64, false, true, 8, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_FSxBX_XMM1_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_07e_movq_FSxBX_XMM1_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_movq_FSxBX_XMM9_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_07e_movq_FSxBX_XMM9_icebp_c64, 255, RM_MEM64, T_SSE2, 8, 64, false, true, 255, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+
+ { bs3CpuInstr3_vmovq_XMM9_R8_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, false, 8, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_vmovq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_06e_vmovq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 1, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_vmovq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_06e_vmovq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, false, 255, 9, RT_ELEMENTS(s_aValuesMediaQ), s_aValuesMediaQ },
+ { bs3CpuInstr3_vmovq_R8_XMM9_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, true, 8, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovq_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_07e_vmovq_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 1, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_vmovq_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ { bs3CpuInstr3_07e_vmovq_FSxBX_XMM9_icebp_c64, X86_XCPT_AC, RM_MEM64, T_AVX_128, 8, 64, false, true, 255, 9, RT_ELEMENTS(s_aValuesRm), s_aValuesRm },
+ };
+ static BS3CPUINSTR3_TEST2_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST2_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType2(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]PEXTRW.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pextrw_EDX_MM1_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pextrw_EDX_XMM1_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_pextrw_RDX_XMM1_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pextrw_R9D_MM1_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pextrw_R9D_XMM8_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pextrw_EDX_MM1_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pextrw_EDX_XMM1_0FFh_icebp);
+extern FNBS3FAR bs3CpuInstr3_pextrw_RDX_XMM1_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pextrw_R9D_MM1_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pextrw_R9D_XMM8_0FFh_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpextrw_EDX_XMM1_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpextrw_RDX_XMM1_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpextrw_EDX_XMM8_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpextrw_R9D_XMM8_000h_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpextrw_EDX_XMM1_0FFh_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpextrw_RDX_XMM1_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpextrw_EDX_XMM8_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpextrw_R9D_XMM8_0FFh_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pextrw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValues00[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0x1234), /*->*/ UINT64_C(0x1234) },
+ { RTUINT256_INIT_C(1, 2, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xffff) },
+ { RTUINT256_INIT_C(3, 4, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x7f7f) },
+ { RTUINT256_INIT_C(5, 6, 0x8080808080808080, 0x8080808080808080), UINT64_C(0x8080) },
+ { RTUINT256_INIT_C(7, 8, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x8888) },
+ { RTUINT256_INIT_C(9, 10, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0x96bb) },
+ };
+
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesFF[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0x1234000000000000, 0), UINT64_C(0x1234) },
+ { RTUINT256_INIT_C(1, 2, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xffff) },
+ { RTUINT256_INIT_C(3, 4, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x7f7f) },
+ { RTUINT256_INIT_C(5, 6, 0x8080808080808080, 0x8080808080808080), UINT64_C(0x8080) },
+ { RTUINT256_INIT_C(7, 8, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x1111) },
+ { RTUINT256_INIT_C(9, 10, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0xb421) },
+ };
+
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesFF_64[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0x1234000000000000), UINT64_C(0x1234) },
+ { RTUINT256_INIT_C(1, 2, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xffff) },
+ { RTUINT256_INIT_C(3, 4, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x7f7f) },
+ { RTUINT256_INIT_C(5, 6, 0x8080808080808080, 0x8080808080808080), UINT64_C(0x8080) },
+ { RTUINT256_INIT_C(7, 8, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x5555) },
+ { RTUINT256_INIT_C(9, 10, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0x9c5c) },
+ };
+
+ static BS3CPUINSTR3_TEST2_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pextrw_EDX_MM1_000h_icebp_c16, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_000h_icebp_c16, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_000h_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_pextrw_EDX_MM1_0FFh_icebp_c16, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pextrw_EDX_MM1_000h_icebp_c32, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_000h_icebp_c32, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_000h_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_pextrw_EDX_MM1_0FFh_icebp_c32, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pextrw_EDX_MM1_000h_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pextrw_R9D_MM1_000h_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 9, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_000h_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ /// @todo Emits the SSE4.1 0f3a variant { bs3CpuInstr3_pextrw_RDX_XMM1_000h_icebp_c64, 255, RM_REG, T_SSE2, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pextrw_R9D_XMM8_000h_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_000h_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpextrw_RDX_XMM1_000h_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpextrw_R9D_XMM8_000h_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+
+ { bs3CpuInstr3_pextrw_EDX_MM1_0FFh_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pextrw_R9D_MM1_0FFh_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, false, true, 9, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pextrw_EDX_XMM1_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ /// @todo Emits the SSE4.1 0f3a variant { bs3CpuInstr3_pextrw_RDX_XMM1_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pextrw_R9D_XMM8_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpextrw_EDX_XMM1_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpextrw_RDX_XMM1_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpextrw_R9D_XMM8_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST2_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST2_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType2(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+/*
+ * [V]MOVMSKPS / [V]MOVMSKPD.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movmskps_EDX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movmskps_RDX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movmskps_R9D_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movmskps_RDX_XMM8_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovmskps_EDX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_RDX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_EDX_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_R9D_XMM8_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovmskps_EDX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_RDX_YMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_EDX_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskps_R9D_YMM8_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movmskpd_EDX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movmskpd_RDX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movmskpd_R9D_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movmskpd_RDX_XMM8_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovmskpd_EDX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_RDX_XMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_EDX_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_R9D_XMM8_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovmskpd_EDX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_RDX_YMM1_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_EDX_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovmskpd_R9D_YMM8_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movmskps_movmskpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesR32_128[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0x0) },
+ { RTUINT256_INIT_C(1, 2, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xf) },
+ { RTUINT256_INIT_C(3, 4, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(5, 6, 0x8080808080808080, 0x8080808080808080), UINT64_C(0xf) },
+ { RTUINT256_INIT_C(7, 8, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(9, 10, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0xb) },
+ };
+
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesR32_256[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), /*->*/ UINT64_C(0x00) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xff) },
+ { RTUINT256_INIT_C(0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x00) },
+ { RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080), UINT64_C(0xff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff0000, 0x1111222233334444, 0x5555666677778888), UINT64_C(0xf0) },
+ { RTUINT256_INIT_C(0x9c5ce073930996bb, 0xb4212fa8564c9ba2, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0xeb) },
+ };
+
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesR64_128[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(1, 2, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0x3) },
+ { RTUINT256_INIT_C(3, 4, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(5, 6, 0x8080808080808080, 0x8080808080808080), UINT64_C(0x3) },
+ { RTUINT256_INIT_C(7, 8, 0x1111222233334444, 0x5555666677778888), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(9, 10, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0x3) },
+ };
+
+ static BS3CPUINSTR3_TEST2_VALUES_T const s_aValuesR64_256[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff), UINT64_C(0xf) },
+ { RTUINT256_INIT_C(0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f, 0x7f7f7f7f7f7f7f7f), UINT64_C(0x0) },
+ { RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080), UINT64_C(0xf) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff0000, 0x1111222233334444, 0x5555666677778888), UINT64_C(0xc) },
+ { RTUINT256_INIT_C(0x9c5ce073930996bb, 0xb4212fa8564c9ba2, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb), UINT64_C(0xf) },
+ };
+
+ static BS3CPUINSTR3_TEST2_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movmskps_EDX_XMM1_icebp_c16, 255, RM_REG, T_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_EDX_XMM1_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_EDX_YMM1_icebp_c16, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_256), s_aValuesR32_256 },
+
+ { bs3CpuInstr3_movmskpd_EDX_XMM1_icebp_c16, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_EDX_XMM1_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_EDX_YMM1_icebp_c16, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_256), s_aValuesR64_256 },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movmskps_EDX_XMM1_icebp_c32, 255, RM_REG, T_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_EDX_XMM1_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_EDX_YMM1_icebp_c32, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_256), s_aValuesR32_256 },
+
+ { bs3CpuInstr3_movmskpd_EDX_XMM1_icebp_c32, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_EDX_XMM1_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_EDX_YMM1_icebp_c32, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_256), s_aValuesR64_256 },
+ };
+ static BS3CPUINSTR3_TEST2_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movmskps_EDX_XMM1_icebp_c64, 255, RM_REG, T_SSE, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_movmskps_R9D_XMM8_icebp_c64, 255, RM_REG, T_SSE, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_movmskps_RDX_XMM1_icebp_c64, 255, RM_REG, T_SSE, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_EDX_XMM1_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_RDX_XMM1_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+ { bs3CpuInstr3_vmovmskps_R9D_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR32_128), s_aValuesR32_128 },
+
+ { bs3CpuInstr3_vmovmskps_EDX_YMM1_icebp_c64, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_256), s_aValuesR32_256 },
+ { bs3CpuInstr3_vmovmskps_RDX_YMM1_icebp_c64, 255, RM_REG, T_AVX_256, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR32_256), s_aValuesR32_256 },
+ { bs3CpuInstr3_vmovmskps_R9D_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR32_256), s_aValuesR32_256 },
+
+ { bs3CpuInstr3_movmskpd_EDX_XMM1_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_movmskpd_R9D_XMM8_icebp_c64, 255, RM_REG, T_SSE2, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_movmskpd_RDX_XMM1_icebp_c64, 255, RM_REG, T_SSE2, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_EDX_XMM1_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_RDX_XMM1_icebp_c64, 255, RM_REG, T_AVX_128, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+ { bs3CpuInstr3_vmovmskpd_R9D_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR64_128), s_aValuesR64_128 },
+
+ { bs3CpuInstr3_vmovmskpd_EDX_YMM1_icebp_c64, 255, RM_REG, T_AVX_256, 4, 32, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_256), s_aValuesR64_256 },
+ { bs3CpuInstr3_vmovmskpd_RDX_YMM1_icebp_c64, 255, RM_REG, T_AVX_256, 8, 64, false, true, 2, 1, RT_ELEMENTS(s_aValuesR64_256), s_aValuesR64_256 },
+ { bs3CpuInstr3_vmovmskpd_R9D_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 4, 32, false, true, 9, 8, RT_ELEMENTS(s_aValuesR64_256), s_aValuesR64_256 },
+ };
+ static BS3CPUINSTR3_TEST2_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST2_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType2(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+
+
+/*
+ * Test type #3 - two MM/XMM/YMM operands, no flags.
+ */
+
+typedef struct BS3CPUINSTR3_TEST3_VALUES_T
+{
+ RTUINT256U uSrc;
+ RTUINT256U uDstOut;
+} BS3CPUINSTR3_TEST3_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST3_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t iRegDst;
+ uint8_t iRegSrc;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST3_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST3_T;
+
+typedef struct BS3CPUINSTR3_TEST3_MODE_T
+{
+ BS3CPUINSTR3_TEST3_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST3_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST3_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST3_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #1 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType3(uint8_t bMode, BS3CPUINSTR3_TEST3_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs, uint8_t cbMaxAlign)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST3_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = cbOperand;
+ uint8_t const cbAlign = RT_MIN(cbOperand, !cbMaxAlign ? 16 : cbMaxAlign);
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType] ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ /* dest */
+ if (paTests[iTest].iRegDst == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(puMemOp, 0xcc, cbMemOp);
+ if (bXcptExpect == X86_XCPT_DB)
+ uMemOpExpect = paValues[iVal].uDstOut;
+ else
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc, ~paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+
+ /* source */
+ if (paTests[iTest].iRegSrc == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegDst != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc, paValues[iVal].uSrc.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc, &paValues[iVal].uSrc.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc, &paValues[iVal].uSrc, 32);
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT( paTests[iTest].iRegDst == UINT8_MAX
+ || paTests[iTest].iRegSrc == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (bXcptExpect == X86_XCPT_DB && fMmxInstr)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff);
+ }
+ if (bXcptExpect == X86_XCPT_DB && paTests[iTest].iRegDst != UINT8_MAX)
+ {
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegDst, paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_SET);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut, cbOperand);
+ }
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, bXcptExpect, puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * PSHUFW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufw_MM1_MM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufw_MM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufw_MM1_MM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufw_MM1_FSxBX_01Bh_icebp);
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_pshufw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesFF[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0, 0, 0, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0x5555555555555555) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0x9c5c9c5c9c5c9c5c) },
+ };
+
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues1B[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0, 0, 0, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0x8888777766665555) },
+ { RTUINT256_INIT_C(0, 0, 0, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0x96bb9309e0739c5c) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pshufw_MM1_MM2_0FFh_icebp_c16, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_MM2_01Bh_icebp_c16, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_01Bh_icebp_c16, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pshufw_MM1_MM2_0FFh_icebp_c32, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_MM2_01Bh_icebp_c32, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_01Bh_icebp_c32, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pshufw_MM1_MM2_0FFh_icebp_c64, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufw_MM1_MM2_01Bh_icebp_c64, 255, RM_REG, T_AXMMX_OR_SSE, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufw_MM1_FSxBX_01Bh_icebp_c64, 255, RM_MEM, T_AXMMX_OR_SSE, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]PSHUFHW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufhw_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufhw_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufhw_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufhw_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_YMM1_YMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_YMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_YMM1_YMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufhw_YMM1_FSxBX_01Bh_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpshufhw_YMM12_YMM7_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpshufhw_YMM9_YMM12_01Bh_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pshufhw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesFF[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x5555555555555555, 0x1111222233334444, 0x1111111111111111, 0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d094d094d094d09, 0x3ef417c8666b3fe6, 0xb421b421b421b421, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues1B[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x8888777766665555, 0x1111222233334444, 0x4444333322221111, 0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x73d56cdcf02a4d09, 0x3ef417c8666b3fe6, 0x9ba2564c2fa8b421, 0x9c5ce073930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_01Bh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_01Bh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufhw_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufhw_XMM1_FSxBX_01Bh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_XMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM1_YMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_YMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufhw_YMM12_YMM7_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 12, 7, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufhw_YMM9_YMM12_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 9, 12, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]PSHUFLW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshuflw_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshuflw_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshuflw_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshuflw_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_YMM1_YMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_YMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_YMM1_YMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshuflw_YMM1_FSxBX_01Bh_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpshuflw_YMM12_YMM7_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpshuflw_YMM9_YMM12_01Bh_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pshuflw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesFF[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x5555666677778888, 0x1111111111111111, 0x1111222233334444, 0x5555555555555555) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef43ef43ef43ef4, 0xb4212fa8564c9ba2, 0x9c5c9c5c9c5c9c5c) },
+ };
+
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues1B[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x5555666677778888, 0x4444333322221111, 0x1111222233334444, 0x8888777766665555) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3fe6666b17c83ef4, 0xb4212fa8564c9ba2, 0x96bb9309e0739c5c) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_01Bh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_01Bh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshuflw_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshuflw_XMM1_FSxBX_01Bh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_XMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM1_YMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_YMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshuflw_YMM12_YMM7_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 12, 7, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshuflw_YMM9_YMM12_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 9, 12, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]PSHUFD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufd_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufd_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufd_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pshufd_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_XMM1_XMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_XMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_XMM1_XMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_XMM1_FSxBX_01Bh_icebp);
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_YMM1_YMM2_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_YMM1_FSxBX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_YMM1_YMM2_01Bh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpshufd_YMM1_FSxBX_01Bh_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpshufd_YMM12_YMM7_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpshufd_YMM9_YMM12_01Bh_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pshufd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesFF[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x5555666655556666, 0x5555666655556666, 0x1111222211112222, 0x1111222211112222) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a4d09f02a, 0x4d09f02a4d09f02a, 0xb4212fa8b4212fa8, 0xb4212fa8b4212fa8) },
+ };
+
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues1B[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x3333444411112222, 0x7777888855556666, 0x7777888855556666, 0x3333444411112222) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x666b3fe63ef417c8, 0x6cdc73d54d09f02a, 0x930996bb9c5ce073, 0x564c9ba2b4212fa8) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_01Bh_icebp_c16, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_0FFh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_0FFh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_01Bh_icebp_c16, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_01Bh_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_01Bh_icebp_c32, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_0FFh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_0FFh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_01Bh_icebp_c32, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_01Bh_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pshufd_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_SSE2, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_pshufd_XMM1_FSxBX_01Bh_icebp_c64, 255, RM_MEM, T_SSE2, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_XMM1_XMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_XMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_0FFh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM1_YMM2_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 1, 2, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_YMM1_FSxBX_01Bh_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ { bs3CpuInstr3_vpshufd_YMM12_YMM7_0FFh_icebp_c64, 255, RM_REG, T_AVX2_256, 12, 7, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpshufd_YMM9_YMM12_01Bh_icebp_c64, 255, RM_REG, T_AVX2_256, 9, 12, RT_ELEMENTS(s_aValues1B), s_aValues1B },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/**
+ * Values shared by the move functions (same input as output).
+ */
+static BS3CPUINSTR3_TEST3_VALUES_T const g_aMoveValues3[] =
+{
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb) },
+};
+
+
+/*
+ * MOVNTDQA - load double qword, strictly aligned, with non-temporal hint.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movntdqa_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movntdqa_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntdqa_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntdqa_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntdqa_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntdqa_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movntdqa(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movntdqa_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_YMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movntdqa_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_YMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movntdqa_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movntdqa_XMM10_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE4_1, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_XMM11_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_YMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX2_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdqa_YMM12_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX2_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig1, RT_ELEMENTS(g_aXcptConfig1), 255 /*cbMaxAlign*/);
+}
+
+
+/*
+ * MOVNTDQ - store double qword, strictly aligned, with non-temporal hint.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movntdq_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movntdq_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntdq_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntdq_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntdq_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntdq_FSxBX_YMM10_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movntdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movntdq_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE2, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_YMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movntdq_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE2, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_YMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movntdq_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE2, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movntdq_FSxBX_XMM10_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE2, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_XMM10_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_YMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntdq_FSxBX_YMM10_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig1, RT_ELEMENTS(g_aXcptConfig1), 255 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVNPS / [V]MOVNTPD - load single/double precision floating-point, aligned,
+ * with non-temporal hint. Only difference is the unit.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movntps_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movntps_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntps_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntps_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntps_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntps_FSxBX_YMM12_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movntpd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movntpd_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntpd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntpd_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovntpd_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovntpd_FSxBX_YMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movntps_movntpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movntps_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_YMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_movntpd_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_XMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_YMM1_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movntps_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_YMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_movntpd_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_XMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_YMM1_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movntps_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movntps_FSxBX_XMM10_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_XMM11_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_YMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntps_FSxBX_YMM12_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_movntpd_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movntpd_FSxBX_XMM10_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_XMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_XMM11_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_YMM1_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovntpd_FSxBX_YMM12_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig1, RT_ELEMENTS(g_aXcptConfig1), 255 /*cbMaxAlign*/);
+}
+
+
+/*
+ * MOVUPS - packed single-precision floating point, unaligned.
+ *
+ * Note! We only cover one of the two register<->register variants here
+ * thanks to the assembler (probably the one with the smaller opcode).
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movups_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movups_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movups_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movups_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovups_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovups_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovups_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovups_YMM12_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movups_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movups_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovups_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovups_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovups_FSxBX_YMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movups(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movups_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_YMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movups_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_YMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movups_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movups_FSxBX_XMM10_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovups_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_YMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovups_FSxBX_YMM12_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4Unaligned, RT_ELEMENTS(g_aXcptConfig4Unaligned), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * MOVUPD - packed double-precision floating point, unaligned.
+ *
+ * Note! We only cover one of the two register<->register variants here
+ * thanks to the assembler (probably the one with the smaller opcode).
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movupd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movupd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movupd_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movupd_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovupd_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovupd_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovupd_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovupd_YMM12_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movupd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movupd_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovupd_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovupd_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovupd_FSxBX_YMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movupd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movupd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_YMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movupd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_YMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movupd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movupd_FSxBX_XMM10_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovupd_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_YMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovupd_FSxBX_YMM12_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4Unaligned, RT_ELEMENTS(g_aXcptConfig4Unaligned), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVSLDUP - Duplicate even single precision floating-point values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movsldup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movsldup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movsldup_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movsldup_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsldup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsldup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovsldup_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovsldup_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsldup_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovsldup_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovsldup_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovsldup_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movsldup(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0xbbbbccccbbbbcccc, 0xffff2121ffff2121, 0x3333444433334444, 0x7777888877778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x6cdc73d56cdc73d5, 0x666b3fe6666b3fe6, 0x564c9ba2564c9ba2, 0x930996bb930996bb) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movsldup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movsldup_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movsldup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movsldup_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movsldup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movsldup_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE3, 8, 12, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movsldup_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movsldup_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 10, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovsldup_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovsldup_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVSHDUP - Duplicate even single precision floating-point values.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movshdup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movshdup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movshdup_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movshdup_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovshdup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovshdup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovshdup_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovshdup_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovshdup_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovshdup_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovshdup_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovshdup_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movshdup(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x9999aaaa9999aaaa, 0xddddeeeeddddeeee, 0x1111222211112222, 0x5555666655556666) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a4d09f02a, 0x3ef417c83ef417c8, 0xb4212fa8b4212fa8, 0x9c5ce0739c5ce073) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movshdup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movshdup_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movshdup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movshdup_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movshdup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movshdup_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE3, 8, 12, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movshdup_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movshdup_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 10, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovshdup_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovshdup_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVDDUP - Duplicate even single precision floating-point values.
+ *
+ * Similar to MOVSLDUP, but different exception class and unit size.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movddup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movddup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movddup_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movddup_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovddup_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovddup_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovddup_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovddup_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovddup_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovddup_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovddup_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovddup_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movddup(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0xddddeeeeffff2121, 0xddddeeeeffff2121, 0x5555666677778888, 0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x3ef417c8666b3fe6, 0x3ef417c8666b3fe6, 0x9c5ce073930996bb, 0x9c5ce073930996bb) },
+ };
+
+ /* Note! Seems the 256-bit variants doesn't generate \#ACs on a 10980XE. WEIRD! */
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movddup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movddup_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movddup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movddup_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movddup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE3, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movddup_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE3, 8, 12, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movddup_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_movddup_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 10, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_XMM11_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vmovddup_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vmovddup_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVAPS / [V]MOVAPD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movaps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movaps_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movaps_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movaps_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovaps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovaps_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovaps_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovaps_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovaps_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovaps_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovaps_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovaps_YMM12_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movapd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movapd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movapd_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movapd_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovapd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovapd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovapd_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovapd_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovapd_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovapd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovapd_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovapd_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movaps_movapd(uint8_t bMode)
+{
+ /* Note! Seems the 256-bit variants doesn't generate \#ACs on a 10980XE. WEIRD! */
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movaps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movaps_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_YMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_FSxBX_icebp_c16, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movaps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movaps_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_YMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_FSxBX_icebp_c32, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movaps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movaps_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movaps_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movaps_XMM10_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movapd_XMM10_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_XMM11_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_XMM11_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovaps_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_YMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovaps_YMM12_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM1_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovapd_YMM12_FSxBX_icebp_c64, X86_XCPT_GP, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig1, RT_ELEMENTS(g_aXcptConfig1), 255 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVDQU - move unaligned packed qwords.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqu_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_movdqu_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqu_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movdqu_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07f_movdqu_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movdqu_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_vmovdqu_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_XMM7_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_vmovdqu_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_YMM12_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqu_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movdqu_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqu_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqu_FSxBX_YMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movdqu(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movdqu_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqu_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_XMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_YMM1_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movdqu_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqu_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_XMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_YMM1_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movdqu_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqu_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqu_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqu_FSxBX_XMM10_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_XMM7_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 7, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_XMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqu_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqu_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_YMM1_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqu_FSxBX_YMM12_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4Unaligned, RT_ELEMENTS(g_aXcptConfig4Unaligned), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]MOVDQA - move aligned packed qwords.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqa_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_movdqa_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqa_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_movdqa_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07f_movdqa_XMM8_XMM12_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_movdqa_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_vmovdqa_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_XMM8_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07f_vmovdqa_XMM8_XMM14_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_07f_vmovdqa_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_07f_vmovdqa_YMM12_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_YMM12_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_movdqa_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_movdqa_FSxBX_XMM10_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_FSxBX_XMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vmovdqa_FSxBX_YMM1_icebp);
+extern FNBS3FAR bs3CpuInstr3_vmovdqa_FSxBX_YMM12_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_movdqa(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_movdqa_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqa_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_FSxBX_XMM1_icebp_c16, 255, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_XMM1_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_YMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_YMM1_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_movdqa_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqa_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_FSxBX_XMM1_icebp_c32, 255, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_XMM1_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_YMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_YMM1_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_movdqa_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqa_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_movdqa_XMM8_XMM12_icebp_c64, 255, RM_REG, T_SSE, 8, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_XMM10_FSxBX_icebp_c64, 255, RM_MEM, T_SSE, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_FSxBX_XMM1_icebp_c64, 255, RM_MEM, T_SSE, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_movdqa_FSxBX_XMM10_icebp_c64, 255, RM_MEM, T_SSE, 255, 10, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_XMM8_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 8, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_XMM8_XMM14_icebp_c64, 255, RM_REG, T_AVX_128, 8, 14, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_XMM11_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_XMM1_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_XMM11_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 255, 11, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vmovdqa_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_07f_vmovdqa_YMM12_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 12, 8, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_YMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_YMM12_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_YMM1_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_256, 255, 1, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vmovdqa_FSxBX_YMM12_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_256, 255, 12, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig1, RT_ELEMENTS(g_aXcptConfig1), 255 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]PABSB / [V]PABSW / [V]PABSD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsb_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsb_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pabsb_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pabsb_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsb_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsb_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsb_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsb_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsb_YMM9_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsb_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsw_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsw_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pabsw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pabsw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsw_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsw_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsw_YMM9_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsw_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsd_MM1_MM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsd_MM1_FSxBX_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pabsd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pabsd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pabsd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsd_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpabsd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpabsd_YMM9_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpabsd_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pabsb_pabsw_pabsd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesB[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x0101010101010101, 0x0101010101010101, 0x0101010101010101, 0x0101010101010101) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x6767565645453434, 0x2323121201012121, 0x1111222233334444, 0x5555666677777878) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09102a6c24732b, 0x3e0c1738666b3f1a, 0x4c212f58564c655e, 0x645c20736d096a45) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x0001000100010001, 0x0001000100010001, 0x0001000100010001, 0x0001000100010001) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x6667555644453334, 0x2223111200012121, 0x1111222233334444, 0x5555666677777778) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d090fd66cdc73d5, 0x3ef417c8666b3fe6, 0x4bdf2fa8564c645e, 0x63a41f8d6cf76945) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x0000000100000001, 0x0000000100000001, 0x0000000100000001, 0x0000000100000001) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x6666555644443334, 0x222211120000dedf, 0x1111222233334444, 0x5555666677778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0x4bded058564c9ba2, 0x63a31f8d6cf66945) },
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pabsb_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pabsw_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pabsd_MM1_MM2_icebp_c16, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_MM1_FSxBX_icebp_c16, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pabsb_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pabsw_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pabsd_MM1_MM2_icebp_c32, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_MM1_FSxBX_icebp_c32, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pabsb_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSSE3, 9, 8, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_pabsb_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM9_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+ { bs3CpuInstr3_vpabsb_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesB), s_aValuesB },
+
+ { bs3CpuInstr3_pabsw_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSSE3, 9, 8, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_pabsw_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM9_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+ { bs3CpuInstr3_vpabsw_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesW), s_aValuesW },
+
+ { bs3CpuInstr3_pabsd_MM1_MM2_icebp_c64, 255, RM_REG, T_MMX_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_MM1_FSxBX_icebp_c64, 255, RM_MEM, T_MMX_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSSE3, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSSE3, 9, 8, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_pabsd_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSSE3, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM9_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ { bs3CpuInstr3_vpabsd_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesD), s_aValuesD },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), X86_EFL_STATUS_BITS);
+}
+
+
+/*
+ * [V]PMOVSXBW / [V]PMOVSXBD / [V]PMOVSXBQ / [V]PMOVSXWD / [V]PMOVSXWQ / [V]PMOVSXDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxbw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxbw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbw_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbw_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbw_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbw_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxbd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxbd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbd_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbd_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbd_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxbq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxbq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxbq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxbq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxbq_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxwd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxwd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwd_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwd_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwd_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxwq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxwq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxwq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxwq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxwq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxwq_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovsxdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovsxdq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovsxdq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxdq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxdq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxdq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovsxdq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovsxdq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovsxdq_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmovsxbw_pmovsxbd_pmovsxbq_pmovsxwd_pmovsxwq_pmovsxdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0011001100220022, 0x0033003300440044, 0x0055005500660066, 0x00770077ff88ff88) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffb40021002fffa8, 0x0056004cff9bffa2, 0xff9c005cffe00073, 0xff930009ff96ffbb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000005500000055, 0x0000006600000066, 0x0000007700000077, 0xffffff88ffffff88) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffffff9c0000005c, 0xffffffe000000073, 0xffffff9300000009, 0xffffff96ffffffbb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000000000077, 0x0000000000000077, 0xffffffffffffff88, 0xffffffffffffff88) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffff93, 0x0000000000000009, 0xffffffffffffff96, 0xffffffffffffffbb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesWD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000111100002222, 0x0000333300004444, 0x0000555500006666, 0x00007777ffff8888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffffb42100002fa8, 0x0000564cffff9ba2, 0xffff9c5cffffe073, 0xffff9309ffff96bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesWQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000000005555, 0x0000000000006666, 0x0000000000007777, 0xffffffffffff8888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffffffffffff9c5c, 0xffffffffffffe073, 0xffffffffffff9309, 0xffffffffffff96bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesDQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000011112222, 0x0000000033334444, 0x0000000055556666, 0x0000000077778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0xffffffffb4212fa8, 0x00000000564c9ba2, 0xffffffff9c5ce073, 0xffffffff930996bb) },
+ };
+
+ /** @todo Some variants produce different results wrt. to #DB vs #AC exceptions on real hardware (i7-6700K) and in a VM.
+ * The exception encountered on real hardware is put in the comment, the X86_XCPT_DB is when emulating the instruction using IEM.
+ * Needs investigation
+ */
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmovsxbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovsxbw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovsxbd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovsxbd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovsxbq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovsxbq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovsxwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovsxwd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovsxwq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovsxwq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovsxdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovsxdq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmovsxbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovsxbw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovsxbd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovsxbd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovsxbq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovsxbq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovsxwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovsxwd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovsxwq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovsxwq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovsxdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovsxdq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmovsxbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovsxbw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovsxbw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovsxbw_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovsxbw_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovsxbd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovsxbd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovsxbd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovsxbd_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovsxbd_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovsxbq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovsxbq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovsxbq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovsxbq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovsxbq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovsxwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovsxwd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovsxwd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovsxwd_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovsxwd_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovsxwq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovsxwq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovsxwq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovsxwq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovsxwq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovsxdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovsxdq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovsxdq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovsxdq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovsxdq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5), X86_EFL_STATUS_BITS);
+}
+
+
+/*
+ * [V]PMOVZXBW / [V]PMOVZXBD / [V]PMOVZXBQ / [V]PMOVZXWD / [V]PMOVZXWQ / [V]PMOVZXDQ
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxbw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxbw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbw_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbw_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbw_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbw_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxbd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxbd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbd_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbd_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbd_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxbq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxbq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxbq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxbq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxbq_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxwd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxwd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwd_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwd_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwd_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwd_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwd_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwd_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxwq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxwq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxwq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxwq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxwq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxwq_YMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pmovzxdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pmovzxdq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pmovzxdq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxdq_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxdq_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxdq_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxdq_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxdq_YMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpmovzxdq_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpmovzxdq_YMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpmovzxdq_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pmovzxbw_pmovzxbd_pmovzxbq_pmovzxwd_pmovzxwq_pmovzxdq(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBW[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x00ff00ff00ff00ff, 0x00ff00ff00ff00ff, 0x00ff00ff00ff00ff, 0x00ff00ff00ff00ff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0011001100220022, 0x0033003300440044, 0x0055005500660066, 0x0077007700880088) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x00b40021002f00a8, 0x0056004c009b00a2, 0x009c005c00e00073, 0x00930009009600bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x000000ff000000ff, 0x000000ff000000ff, 0x000000ff000000ff, 0x000000ff000000ff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000005500000055, 0x0000006600000066, 0x0000007700000077, 0x0000008800000088) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x0000009c0000005c, 0x000000e000000073, 0x0000009300000009, 0x00000096000000bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesBQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x00000000000000ff, 0x00000000000000ff, 0x00000000000000ff, 0x00000000000000ff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000000000077, 0x0000000000000077, 0x0000000000000088, 0x0000000000000088) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x0000000000000093, 0x0000000000000009, 0x0000000000000096, 0x00000000000000bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesWD[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x0000ffff0000ffff, 0x0000ffff0000ffff, 0x0000ffff0000ffff, 0x0000ffff0000ffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000111100002222, 0x0000333300004444, 0x0000555500006666, 0x0000777700008888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x0000b42100002fa8, 0x0000564c00009ba2, 0x00009c5c0000e073, 0x00009309000096bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesWQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x000000000000ffff, 0x000000000000ffff, 0x000000000000ffff, 0x000000000000ffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000000005555, 0x0000000000006666, 0x0000000000007777, 0x0000000000008888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x0000000000009c5c, 0x000000000000e073, 0x0000000000009309, 0x00000000000096bb) },
+ };
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValuesDQ[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(0x00000000ffffffff, 0x00000000ffffffff, 0x00000000ffffffff, 0x00000000ffffffff) },
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(0x0000000011112222, 0x0000000033334444, 0x0000000055556666, 0x0000000077778888) },
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(0x00000000b4212fa8, 0x00000000564c9ba2, 0x000000009c5ce073, 0x00000000930996bb) },
+ };
+
+ /** @todo Some variants produce different results wrt. to #DB vs #AC exceptions on real hardware (i7-6700K) and in a VM.
+ * The exception encountered on real hardware is put in the comment, the X86_XCPT_DB is when emulating the instruction using IEM.
+ * Needs investigation
+ */
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pmovzxbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovzxbw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovzxbd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovzxbd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovzxbq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovzxbq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovzxwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovzxwd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovzxwq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovzxwq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovzxdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovzxdq_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_FSxBX_icebp_c16, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_FSxBX_icebp_c16, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pmovzxbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovzxbw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovzxbd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovzxbd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovzxbq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovzxbq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovzxwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovzxwd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovzxwq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovzxwq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovzxdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovzxdq_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_FSxBX_icebp_c32, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pmovzxbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovzxbw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovzxbw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_pmovzxbw_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+ { bs3CpuInstr3_vpmovzxbw_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBW), s_aValuesBW },
+
+ { bs3CpuInstr3_pmovzxbd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovzxbd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovzxbd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_pmovzxbd_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+ { bs3CpuInstr3_vpmovzxbd_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBD), s_aValuesBD },
+
+ { bs3CpuInstr3_pmovzxbq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovzxbq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovzxbq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_pmovzxbq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+ { bs3CpuInstr3_vpmovzxbq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesBQ), s_aValuesBQ },
+
+ { bs3CpuInstr3_pmovzxwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovzxwd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovzxwd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_pmovzxwd_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+ { bs3CpuInstr3_vpmovzxwd_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesWD), s_aValuesWD },
+
+ { bs3CpuInstr3_pmovzxwq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovzxwq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovzxwq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_pmovzxwq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+ { bs3CpuInstr3_vpmovzxwq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB /*AC*/, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesWQ), s_aValuesWQ },
+
+ { bs3CpuInstr3_pmovzxdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovzxdq_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovzxdq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_pmovzxdq_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM1_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_XMM9_FSxBX_icebp_c64, X86_XCPT_AC, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ { bs3CpuInstr3_vpmovzxdq_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValuesDQ), s_aValuesDQ },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5), X86_EFL_STATUS_BITS);
+}
+
+
+/*
+ * [V]LDDQU - Load unaligned integer from memory.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_lddqu_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_lddqu_XMM10_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vlddqu_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vlddqu_XMM11_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vlddqu_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vlddqu_YMM12_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_lddqu(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_lddqu_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vlddqu_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vlddqu_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_lddqu_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vlddqu_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vlddqu_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_lddqu_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_lddqu_XMM10_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_SSE3, 10, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vlddqu_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vlddqu_XMM11_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 11, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+
+ { bs3CpuInstr3_vlddqu_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ { bs3CpuInstr3_vlddqu_YMM12_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 12, 255, RT_ELEMENTS(g_aMoveValues3), g_aMoveValues3 },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4Unaligned, RT_ELEMENTS(g_aXcptConfig4Unaligned), 0 /*cbMaxAlign*/);
+}
+
+
+/*
+ * [V]PHMINPOSUW
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phminposuw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_phminposuw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_phminposuw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_phminposuw_XMM9_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphminposuw_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vphminposuw_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vphminposuw_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vphminposuw_XMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_phminposuw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST3_VALUES_T const s_aValues[] =
+ {
+ { RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ RTUINT256_INIT_C(1, 2, 0x0000000000000000, 0x000000000000ffff) }, /* No 256-bit variant */
+ { RTUINT256_INIT_C(0x9999aaaabbbbcccc, 0xddddeeeeffff2121, 0x1111222233334444, 0x5555666677778888),
+ /* => */ RTUINT256_INIT_C(5, 6, 0x0000000000000000, 0x0000000000071111) }, /* No 256-bit variant */
+ { RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /* => */ RTUINT256_INIT_C(9, 10, 0x0000000000000000, 0x0000000000062fa8) }, /* No 256-bit variant */
+ };
+
+ static BS3CPUINSTR3_TEST3_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_phminposuw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_phminposuw_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_phminposuw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_phminposuw_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_phminposuw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_phminposuw_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_phminposuw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_phminposuw_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vphminposuw_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST3_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST3_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType3(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), X86_EFL_STATUS_BITS);
+}
+
+
+/*
+ * Test type #4 - two source MM/XMM/YMM operands, outputs only eflags.
+ *
+ * Probably only used by the PTEST instruction.
+ */
+
+typedef struct BS3CPUINSTR3_TEST4_VALUES_T
+{
+ RTUINT256U uSrc2;
+ RTUINT256U uSrc1;
+ uint16_t afEflOut[3]; /* [0]=MM result, [1]=XMM result, [2]=YMM result */
+} BS3CPUINSTR3_TEST4_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST4_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t iRegSrc1;
+ uint8_t iRegSrc2;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST4_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST4_T;
+
+typedef struct BS3CPUINSTR3_TEST4_MODE_T
+{
+ BS3CPUINSTR3_TEST4_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST4_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST4_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST4_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #4 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType4(uint8_t bMode, BS3CPUINSTR3_TEST4_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs, uint32_t fEflCheck)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST4_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = cbOperand;
+ uint8_t const cbAlign = RT_MIN(cbOperand, 16);
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t const idxEflOut = cbOperand == 32 ? 2 : cbOperand == 16 ? 1 : 0;
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType] ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ unsigned iEflVariation;
+ uint32_t const fSavedEfl = Ctx.rflags.u32;
+ for (iEflVariation = 0; iEflVariation < 2; iEflVariation++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ /* eflags */
+ if (iEflVariation)
+ Ctx.rflags.u32 = fSavedEfl | X86_EFL_STATUS_BITS;
+ else
+ Ctx.rflags.u32 = fSavedEfl & ~X86_EFL_STATUS_BITS;
+
+ /* source1 */
+ if (paTests[iTest].iRegSrc1 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegSrc2 != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc1, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc1;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc1, paValues[iVal].uSrc1.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1, 32);
+
+ /* source2 */
+ if (paTests[iTest].iRegSrc2 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegSrc1 != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc2, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc2;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc2, paValues[iVal].uSrc2.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2, 32);
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT( paTests[iTest].iRegSrc1 == UINT8_MAX
+ || paTests[iTest].iRegSrc2 == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (bXcptExpect == X86_XCPT_DB && fMmxInstr)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff);
+ }
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ if (bXcptExpect == X86_XCPT_DB)
+ Ctx.rflags.u32 = (Ctx.rflags.u32 & ~fEflCheck) | paValues[iVal].afEflOut[idxEflOut];
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u/efl#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, iEflVariation, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u/efl#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, iEflVariation, bXcptExpect,
+ puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ Ctx.rflags.u32 = fSavedEfl;
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * PTEST
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_ptest_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_ptest_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_ptest_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_ptest_XMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vptest_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vptest_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vptest_XMM9_XMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vptest_XMM9_FSxBX_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vptest_YMM1_YMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vptest_YMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_vptest_YMM9_YMM8_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vptest_YMM9_FSxBX_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_ptest(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST4_VALUES_T const s_aValues[] =
+ {
+ { /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ { 0, X86_EFL_ZF | X86_EFL_CF, X86_EFL_ZF | X86_EFL_CF } },
+ { /*src2*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src1*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /* => */ { 0, X86_EFL_CF, X86_EFL_CF } },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ { 0, 0, 0 } },
+ { /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ { 0, 0, 0 } },
+ { /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ { 0, 0, 0 } },
+ { /*src2*/ RTUINT256_INIT_C(0x4d09f02a6cdc73d5, 0x3ef417c8666b3fe6, 0xb4212fa8564c9ba2, 0x9c5ce073930996bb),
+ /*src1*/ RTUINT256_INIT_C(0x1eddddac09633294, 0xf95c8eec40725633, 0x8800e95bbf9962c3, 0x43d3cda0238499fd),
+ /* => */ { 0, 0, 0 } },
+ };
+
+ static BS3CPUINSTR3_TEST4_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_ptest_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_ptest_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_XMM2_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_YMM2_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_FSxBX_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST4_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_ptest_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_ptest_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_XMM2_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_YMM2_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_FSxBX_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST4_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_ptest_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_ptest_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_ptest_XMM9_XMM8_icebp_c64, 255, RM_REG, T_SSE4_1, 9, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_ptest_XMM9_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_XMM2_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM9_XMM8_icebp_c64, 255, RM_REG, T_AVX_128, 9, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_XMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_YMM2_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM1_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM9_YMM8_icebp_c64, 255, RM_REG, T_AVX_256, 9, 8, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vptest_YMM9_FSxBX_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 9, 255, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST4_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST4_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType4(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4), X86_EFL_STATUS_BITS);
+}
+
+
+/*
+ * Test type #5 - three source MM/XMM/YMM operands.
+ *
+ * Probably only used by the [P]BLEND instruction.
+ */
+
+typedef struct BS3CPUINSTR3_TEST5_VALUES_T
+{
+ RTUINT256U uSrc3;
+ RTUINT256U uSrc2;
+ RTUINT256U uSrc1;
+ RTUINT256U uDstOut;
+} BS3CPUINSTR3_TEST5_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST5_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t iRegDst;
+ uint8_t iRegSrc1;
+ uint8_t iRegSrc2;
+ uint8_t iRegSrc3;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST5_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST5_T;
+
+typedef struct BS3CPUINSTR3_TEST5_MODE_T
+{
+ BS3CPUINSTR3_TEST5_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST5_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST5_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST5_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #5 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType5(uint8_t bMode, BS3CPUINSTR3_TEST5_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+ //Bs3TestPrintf("FTW=%#x mm1/st1=%.16Rhxs\n", pExtCtx->Ctx.x87.FTW, &pExtCtx->Ctx.x87.aRegs[1]);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST5_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = bs3CpuInstr3MemOpSize(cbOperand, paTests[iTest].enmRm);
+ uint8_t const cbAlign = cbMemOp;
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType] ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ /* dest */
+ if (paTests[iTest].iRegDst == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(puMemOp, 0xcc, cbMemOp);
+ if (bXcptExpect == X86_XCPT_DB)
+ uMemOpExpect = paValues[iVal].uDstOut;
+ else
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc2, ~paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+
+ /* source #1 (/ destination for MMX and SSE) */
+ if (paTests[iTest].iRegSrc1 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc1, cbMemOp);
+ if (paTests[iTest].iRegDst == UINT8_MAX)
+ BS3_ASSERT(fSseInstr);
+ else
+ uMemOpExpect = paValues[iVal].uSrc1;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc1, paValues[iVal].uSrc1.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc1, &paValues[iVal].uSrc1, 32);
+
+ /* source #2 */
+ if (paTests[iTest].iRegSrc2 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegDst != UINT8_MAX && paTests[iTest].iRegSrc1 != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc2, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc2;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc2, paValues[iVal].uSrc2.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc2, &paValues[iVal].uSrc2, 32);
+
+ /* source #3 */
+ if (paTests[iTest].iRegSrc3 == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ BS3_ASSERT(paTests[iTest].iRegDst != UINT8_MAX && paTests[iTest].iRegSrc1 != UINT8_MAX);
+ Bs3MemCpy(puMemOp, &paValues[iVal].uSrc3, cbMemOp);
+ uMemOpExpect = paValues[iVal].uSrc3;
+ }
+ else if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegSrc3, paValues[iVal].uSrc3.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegSrc3, &paValues[iVal].uSrc3.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegSrc3, &paValues[iVal].uSrc3, 32);
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT( paTests[iTest].iRegDst == UINT8_MAX
+ || paTests[iTest].iRegSrc1 == UINT8_MAX
+ || paTests[iTest].iRegSrc2 == UINT8_MAX
+ || paTests[iTest].iRegSrc3 == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (fMmxInstr && bXcptExpect == X86_XCPT_DB)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff); /* Observed on 10980xe after pxor mm1, mm2. */
+ }
+ if (bXcptExpect == X86_XCPT_DB && paTests[iTest].iRegDst != UINT8_MAX)
+ {
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iRegDst, paValues[iVal].uDstOut.QWords.qw0, BS3EXTCTXTOPMM_SET);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iRegDst, &paValues[iVal].uDstOut, cbOperand);
+ }
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, bXcptExpect, puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * PBLENDVB
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendvb_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pblendvb_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_pblendvb_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pblendvb_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendvb_XMM1_XMM2_XMM3_XMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendvb_XMM1_XMM2_FSxBX_XMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpblendvb_XMM8_XMM9_XMM10_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendvb_XMM8_XMM9_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendvb_YMM1_YMM2_YMM3_YMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpblendvb_YMM1_YMM2_FSxBX_YMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpblendvb_YMM8_YMM9_YMM10_YMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpblendvb_YMM8_YMM9_FSxBX_YMM11_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pblendvb(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST5_VALUES_T const s_aValues[] =
+ {
+ { /*mask*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*mask*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src2*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { /*mask*/ RTUINT256_INIT_C(0x0000008000000000, 0x0000091000007f00, 0, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3f4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x1234567890abcdef, 0xfedcba0987654321, 0xfedcba0987654321, 0x1234567890abcdef),
+ /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xddddeeee77778888, 0x111122aa33bbcccc, 0x111122aa33bbcccc, 0xddddeeee77778888) },
+ };
+
+ static BS3CPUINSTR3_TEST5_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pblendvb_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pblendvb_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_XMM3_XMM4_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_FSxBX_XMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_YMM3_YMM4_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_FSxBX_YMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pblendvb_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pblendvb_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_XMM3_XMM4_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_FSxBX_XMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_YMM3_YMM4_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_FSxBX_YMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pblendvb_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pblendvb_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pblendvb_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_pblendvb_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_XMM3_XMM4_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_XMM1_XMM2_FSxBX_XMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_XMM8_XMM9_XMM10_XMM11_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_XMM8_XMM9_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_YMM3_YMM4_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM1_YMM2_FSxBX_YMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM8_YMM9_YMM10_YMM11_icebp_c64, 255, RM_REG, T_AVX_256, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vpblendvb_YMM8_YMM9_FSxBX_YMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST5_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType5(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * BLENDPS
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendvps_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendvps_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_blendvps_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendvps_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvps_XMM1_XMM2_XMM3_XMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvps_XMM1_XMM2_FSxBX_XMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendvps_XMM8_XMM9_XMM10_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendvps_XMM8_XMM9_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvps_YMM1_YMM2_YMM3_YMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvps_YMM1_YMM2_FSxBX_YMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendvps_YMM8_YMM9_YMM10_YMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendvps_YMM8_YMM9_FSxBX_YMM11_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_blendvps(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST5_VALUES_T const s_aValues[] =
+ {
+ { /*mask*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*mask*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src2*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { /*mask*/ RTUINT256_INIT_C(0x0000008000000000, 0x0000091000007f00, 0, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x1234567890abcdef, 0xfedcba0987654321, 0xfedcba0987654321, 0x1234567890abcdef),
+ /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xddddeeee77778888, 0x1111222233334444, 0x1111222233334444, 0xddddeeee77778888) },
+ };
+
+ static BS3CPUINSTR3_TEST5_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_blendvps_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvps_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_XMM3_XMM4_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_FSxBX_XMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_YMM3_YMM4_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_FSxBX_YMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_blendvps_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvps_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_XMM3_XMM4_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_FSxBX_XMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_YMM3_YMM4_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_FSxBX_YMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_blendvps_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvps_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvps_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvps_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_XMM3_XMM4_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_XMM1_XMM2_FSxBX_XMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_XMM8_XMM9_XMM10_XMM11_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_XMM8_XMM9_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_YMM3_YMM4_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM1_YMM2_FSxBX_YMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM8_YMM9_YMM10_YMM11_icebp_c64, 255, RM_REG, T_AVX_256, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvps_YMM8_YMM9_FSxBX_YMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST5_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType5(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * BLENDPD
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendvpd_XMM1_XMM2_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_blendvpd_XMM1_FSxBX_icebp);
+extern FNBS3FAR bs3CpuInstr3_blendvpd_XMM8_XMM9_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_blendvpd_XMM8_FSxBX_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvpd_XMM1_XMM2_XMM3_XMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvpd_XMM1_XMM2_FSxBX_XMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendvpd_XMM8_XMM9_XMM10_XMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendvpd_XMM8_XMM9_FSxBX_XMM11_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvpd_YMM1_YMM2_YMM3_YMM4_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vblendvpd_YMM1_YMM2_FSxBX_YMM4_icebp);
+extern FNBS3FAR bs3CpuInstr3_vblendvpd_YMM8_YMM9_YMM10_YMM11_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vblendvpd_YMM8_YMM9_FSxBX_YMM11_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_blendvpd(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST5_VALUES_T const s_aValues[] =
+ {
+ { /*mask*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src2*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0, 0, 0, 0) },
+ { /*mask*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src2*/ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff),
+ /*src1*/ RTUINT256_INIT_C(0, 0, 0, 0),
+ /* => */ RTUINT256_INIT_C(0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff) },
+ { /*mask*/ RTUINT256_INIT_C(0x0000008000000000, 0x0000091000007f00, 0, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x8080808080808080, 0x8080808080808080, 0x8080808080808080, 0x8080808080808080),
+ /*src2*/ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8),
+ /*src1*/ RTUINT256_INIT_C(0xb1b2b3b4b5b6b7b8, 0xa1a2a3a4a5a6a7a8, 0x9192939495969798, 0x8182838485868788),
+ /* => */ RTUINT256_INIT_C(0xf1f2f3f4f5f6f7f8, 0xe1e2e3e4e5e6e7e8, 0xd1d2d3d4d5d6d7d8, 0xc1c2c3c4c5c6c7c8) },
+ { /*mask*/ RTUINT256_INIT_C(0x1234567890abcdef, 0xfedcba0987654321, 0xfedcba0987654321, 0x1234567890abcdef),
+ /*src2*/ RTUINT256_INIT_C(0x5555666677778888, 0x1111222233334444, 0x1111222233334444, 0x5555666677778888),
+ /*src1*/ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x9999aaaabbbbcccc, 0x9999aaaabbbbcccc, 0xddddeeeeffff0000),
+ /* => */ RTUINT256_INIT_C(0xddddeeeeffff0000, 0x1111222233334444, 0x1111222233334444, 0xddddeeeeffff0000) },
+ };
+
+ static BS3CPUINSTR3_TEST5_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_blendvpd_XMM1_XMM2_icebp_c16, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvpd_XMM1_FSxBX_icebp_c16, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_XMM3_XMM4_icebp_c16, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_FSxBX_XMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_YMM3_YMM4_icebp_c16, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_FSxBX_YMM4_icebp_c16, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_blendvpd_XMM1_XMM2_icebp_c32, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvpd_XMM1_FSxBX_icebp_c32, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_XMM3_XMM4_icebp_c32, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_FSxBX_XMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_YMM3_YMM4_icebp_c32, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_FSxBX_YMM4_icebp_c32, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_blendvpd_XMM1_XMM2_icebp_c64, 255, RM_REG, T_SSE4_1, 1, 1, 2, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvpd_XMM1_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 1, 1, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvpd_XMM8_XMM9_icebp_c64, 255, RM_REG, T_SSE4_1, 8, 8, 9, 0, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_blendvpd_XMM8_FSxBX_icebp_c64, 255, RM_MEM, T_SSE4_1, 8, 8, 255, 0, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_XMM3_XMM4_icebp_c64, 255, RM_REG, T_AVX_128, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_XMM1_XMM2_FSxBX_XMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_XMM8_XMM9_XMM10_XMM11_icebp_c64, 255, RM_REG, T_AVX_128, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_XMM8_XMM9_FSxBX_XMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_128, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_YMM3_YMM4_icebp_c64, 255, RM_REG, T_AVX_256, 1, 2, 3, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM1_YMM2_FSxBX_YMM4_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 1, 2, 255, 4, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM8_YMM9_YMM10_YMM11_icebp_c64, 255, RM_REG, T_AVX_256, 8, 9, 10, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ { bs3CpuInstr3_vblendvpd_YMM8_YMM9_FSxBX_YMM11_icebp_c64, X86_XCPT_DB, RM_MEM, T_AVX_256, 8, 9, 255, 11, RT_ELEMENTS(s_aValues), s_aValues },
+ };
+ static BS3CPUINSTR3_TEST5_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST5_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType5(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig4, RT_ELEMENTS(g_aXcptConfig4));
+}
+
+
+/*
+ * Test type #6 - MM/XMM/YMM <- GPR, no VVVV.
+ *
+ * Used probably only by the PINSRW testcases
+ */
+
+typedef struct BS3CPUINSTR3_TEST6_VALUES_T
+{
+ RTUINT256U uMediaSrc;
+ uint64_t uGpr;
+ RTUINT256U uMediaDst;
+} BS3CPUINSTR3_TEST6_VALUES_T;
+
+typedef struct BS3CPUINSTR3_TEST6_T
+{
+ FPFNBS3FAR pfnWorker;
+ uint8_t bAvxMisalignXcpt;
+ uint8_t enmRm;
+ uint8_t enmType;
+ uint8_t cbGpr;
+ uint8_t cBitsGprValMask;
+ uint8_t iGprReg;
+ uint8_t iMediaRegSrc;
+ uint8_t iMediaRegDst;
+ uint8_t cValues;
+ BS3CPUINSTR3_TEST6_VALUES_T const BS3_FAR *paValues;
+} BS3CPUINSTR3_TEST6_T;
+
+typedef struct BS3CPUINSTR3_TEST6_MODE_T
+{
+ BS3CPUINSTR3_TEST6_T const BS3_FAR *paTests;
+ unsigned cTests;
+} BS3CPUINSTR3_TEST6_MODE_T;
+
+/** Initializer for a BS3CPUINSTR3_TEST6_MODE_T array (three entries). */
+#define BS3CPUINSTR3_TEST6_MODES_INIT(a_aTests16, a_aTests32, a_aTests64) \
+ { { a_aTests16, RT_ELEMENTS(a_aTests16) }, { a_aTests32, RT_ELEMENTS(a_aTests32) }, { a_aTests64, RT_ELEMENTS(a_aTests64) } }
+
+
+/**
+ * Test type #6 worker.
+ */
+static uint8_t bs3CpuInstr3_WorkerTestType6(uint8_t bMode, BS3CPUINSTR3_TEST6_T const BS3_FAR *paTests, unsigned cTests,
+ PCBS3CPUINSTR3_CONFIG_T paConfigs, unsigned cConfigs)
+{
+ BS3REGCTX Ctx;
+ BS3TRAPFRAME TrapFrame;
+ const char BS3_FAR * const pszMode = Bs3GetModeName(bMode);
+ uint8_t BS3_FAR *pbBuf = g_pbBuf;
+ uint32_t cbBuf = g_cbBuf;
+ uint8_t bRing = BS3_MODE_IS_V86(bMode) ? 3 : 0;
+ PBS3EXTCTX pExtCtxOut;
+ PBS3EXTCTX pExtCtx = bs3CpuInstr3AllocExtCtxs(&pExtCtxOut);
+ if (!pExtCtx)
+ return 0;
+
+ /* Ensure the structures are allocated before we sample the stack pointer. */
+ Bs3MemSet(&Ctx, 0, sizeof(Ctx));
+ Bs3MemSet(&TrapFrame, 0, sizeof(TrapFrame));
+
+ /*
+ * Create test context.
+ */
+ pbBuf = bs3CpuInstr3BufSetup(pbBuf, &cbBuf, bMode);
+ Bs3RegCtxSaveForMode(&Ctx, bMode, 1024);
+ bs3CpuInstr3SetupSseAndAvx(&Ctx, pExtCtx);
+
+ /*
+ * Run the tests in all rings since alignment issues may behave
+ * differently in ring-3 compared to ring-0.
+ */
+ for (;;)
+ {
+ unsigned iCfg;
+ for (iCfg = 0; iCfg < cConfigs; iCfg++)
+ {
+ unsigned iTest;
+ BS3CPUINSTR3_CONFIG_SAVED_T SavedCfg;
+ if (!bs3CpuInstr3ConfigReconfigure(&SavedCfg, &Ctx, pExtCtx, &paConfigs[iCfg], bMode))
+ continue; /* unsupported config */
+
+ /*
+ * Iterate the tests.
+ */
+ for (iTest = 0; iTest < cTests; iTest++)
+ {
+ BS3CPUINSTR3_TEST6_VALUES_T const BS3_FAR *paValues = paTests[iTest].paValues;
+ uint8_t const cbInstr = ((uint8_t const BS3_FAR *)(uintptr_t)paTests[iTest].pfnWorker)[-1];
+ unsigned const cValues = paTests[iTest].cValues;
+ bool const fMmxInstr = paTests[iTest].enmType < T_SSE;
+ bool const fSseInstr = paTests[iTest].enmType >= T_SSE && paTests[iTest].enmType < T_AVX_128;
+ bool const fAvxInstr = paTests[iTest].enmType >= T_AVX_128;
+ uint8_t const cbOperand = paTests[iTest].enmType < T_128BITS ? 64/8
+ : paTests[iTest].enmType < T_256BITS ? 128/8 : 256/8;
+ uint8_t const cbMemOp = bs3CpuInstr3MemOpSize(cbOperand, paTests[iTest].enmRm);
+ uint8_t const cbAlign = RT_MIN(cbOperand, 16);
+ PRTUINT256U puMemOp = bs3CpuInstr3BufForOperand(pbBuf, cbBuf, cbMemOp, cbAlign, &paConfigs[iCfg]);
+ uint8_t bXcptExpect = !g_afTypeSupports[paTests[iTest].enmType] ? X86_XCPT_UD
+ : fMmxInstr ? paConfigs[iCfg].bXcptMmx
+ : fSseInstr ? paConfigs[iCfg].bXcptSse
+ : BS3_MODE_IS_RM_OR_V86(bMode) ? X86_XCPT_UD : paConfigs[iCfg].bXcptAvx;
+ uint64_t const fGprValMask = paTests[iTest].cBitsGprValMask == 64 ? UINT64_MAX
+ : RT_BIT_64(paTests[iTest].cBitsGprValMask) - 1;
+ uint16_t idTestStep = bRing * 10000 + iCfg * 100 + iTest * 10;
+ unsigned iVal;
+
+ /* If testing unaligned memory accesses, skip register-only tests. This allows
+ setting bXcptMmx, bXcptSse and bXcptAvx to reflect the misaligned exceptions. */
+ if (paTests[iTest].enmRm == RM_REG && (!paConfigs[iCfg].fAligned || paConfigs[iCfg].fAlignCheck))
+ continue;
+
+ /* #AC is only raised in ring-3.: */
+ if (bXcptExpect == X86_XCPT_AC)
+ {
+ if (bRing != 3)
+ bXcptExpect = X86_XCPT_DB;
+ else if (fAvxInstr)
+ bXcptExpect = paTests[iTest].bAvxMisalignXcpt; /* they generally don't raise #AC */
+ }
+
+ Bs3RegCtxSetRipCsFromCurPtr(&Ctx, paTests[iTest].pfnWorker);
+
+ /*
+ * Iterate the test values and do the actual testing.
+ */
+ for (iVal = 0; iVal < cValues; iVal++, idTestStep++)
+ {
+ uint16_t cErrors;
+ uint16_t uSavedFtw = 0xff;
+ RTUINT256U uMemOpExpect;
+
+ /*
+ * Set up the context and some expectations.
+ */
+ /* source - media */
+ BS3_ASSERT(paTests[iTest].iMediaRegSrc != UINT8_MAX);
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iMediaRegSrc, paValues[iVal].uMediaSrc.QWords.qw0, BS3EXTCTXTOPMM_ZERO);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iMediaRegSrc, &paValues[iVal].uMediaSrc.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iMediaRegSrc, &paValues[iVal].uMediaSrc, 32);
+
+ /* source - gpr/mem */
+ if (paTests[iTest].iGprReg == UINT8_MAX)
+ {
+ BS3_ASSERT(paTests[iTest].enmRm >= RM_MEM);
+ Bs3MemSet(&uMemOpExpect, 0xcc, sizeof(uMemOpExpect));
+ if (bXcptExpect == X86_XCPT_DB)
+ switch (paTests[iTest].cbGpr)
+ {
+ case 1: uMemOpExpect.au8[0] = (uint8_t) (paValues[iVal].uGpr & fGprValMask); break;
+ case 2: uMemOpExpect.au16[0] = (uint16_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 4: uMemOpExpect.au32[0] = (uint32_t)(paValues[iVal].uGpr & fGprValMask); break;
+ case 8: uMemOpExpect.au64[0] = (paValues[iVal].uGpr & fGprValMask); break;
+ default: BS3_ASSERT(0);
+ }
+ Bs3MemCpy(puMemOp, &uMemOpExpect, cbMemOp);
+ }
+ else
+ Bs3RegCtxSetGpr(&Ctx, paTests[iTest].iGprReg, paValues[iVal].uGpr & fGprValMask, paTests[iTest].cbGpr);
+
+ /* Memory pointer. */
+ if (paTests[iTest].enmRm >= RM_MEM)
+ {
+ BS3_ASSERT(paTests[iTest].iGprReg == UINT8_MAX || paTests[iTest].iMediaRegSrc == UINT8_MAX);
+ Bs3RegCtxSetGrpSegFromCurPtr(&Ctx, &Ctx.rbx, &Ctx.fs, puMemOp);
+ }
+
+ /*
+ * Execute.
+ */
+ Bs3TrapSetJmpAndRestoreWithExtCtxAndRm(&Ctx, pExtCtx, &TrapFrame, pExtCtxOut);
+
+ /*
+ * Check the result:
+ */
+ cErrors = Bs3TestSubErrorCount();
+
+ if (fMmxInstr && bXcptExpect == X86_XCPT_DB)
+ {
+ uSavedFtw = Bs3ExtCtxGetAbridgedFtw(pExtCtx);
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, 0xff);
+ }
+
+ if (bXcptExpect == X86_XCPT_DB)
+ {
+ if (fMmxInstr)
+ Bs3ExtCtxSetMm(pExtCtx, paTests[iTest].iMediaRegDst, paValues[iVal].uMediaDst.QWords.qw0, BS3EXTCTXTOPMM_SET);
+ else if (fSseInstr)
+ Bs3ExtCtxSetXmm(pExtCtx, paTests[iTest].iMediaRegDst, &paValues[iVal].uMediaDst.DQWords.dqw0);
+ else
+ Bs3ExtCtxSetYmm(pExtCtx, paTests[iTest].iMediaRegDst, &paValues[iVal].uMediaDst, 32);
+ }
+
+ Bs3TestCheckExtCtx(pExtCtxOut, pExtCtx, 0 /*fFlags*/, pszMode, idTestStep);
+
+ if (TrapFrame.bXcpt != bXcptExpect)
+ Bs3TestFailedF("Expected bXcpt = %#x, got %#x", bXcptExpect, TrapFrame.bXcpt);
+
+ /* Kludge! Looks like EFLAGS.AC is cleared when raising #GP in real mode on the 10980XE. WEIRD! */
+ if (bMode == BS3_MODE_RM && (Ctx.rflags.u32 & X86_EFL_AC))
+ {
+ if (TrapFrame.Ctx.rflags.u32 & X86_EFL_AC)
+ Bs3TestFailedF("Expected EFLAGS.AC to be cleared (bXcpt=%d)", TrapFrame.bXcpt);
+ TrapFrame.Ctx.rflags.u32 |= X86_EFL_AC;
+ }
+ Bs3TestCheckRegCtxEx(&TrapFrame.Ctx, &Ctx, bXcptExpect == X86_XCPT_DB ? cbInstr + 1 : 0, 0,
+ bXcptExpect == X86_XCPT_DB || BS3_MODE_IS_16BIT_SYS(bMode) ? 0 : X86_EFL_RF,
+ pszMode, idTestStep);
+
+ if ( paTests[iTest].enmRm >= RM_MEM
+ && Bs3MemCmp(puMemOp, &uMemOpExpect, cbMemOp) != 0)
+ Bs3TestFailedF("Expected uMemOp %.*Rhxs, got %.*Rhxs", cbMemOp, &uMemOpExpect, cbMemOp, puMemOp);
+
+ if (cErrors != Bs3TestSubErrorCount())
+ {
+ if (paConfigs[iCfg].fAligned)
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x)",
+ bRing, iCfg, iTest, iVal, bXcptExpect);
+ else
+ Bs3TestFailedF("ring-%d/cfg#%u/test#%u/value#%u failed (bXcptExpect=%#x, puMemOp=%p, EFLAGS=%#RX32, CR0=%#RX32)",
+ bRing, iCfg, iTest, iVal, bXcptExpect, puMemOp, TrapFrame.Ctx.rflags.u32, TrapFrame.Ctx.cr0);
+ Bs3TestPrintf("\n");
+ }
+
+ if (uSavedFtw != 0xff)
+ Bs3ExtCtxSetAbridgedFtw(pExtCtx, uSavedFtw);
+ }
+ }
+
+ bs3CpuInstr3ConfigRestore(&SavedCfg, &Ctx, pExtCtx);
+ }
+
+ /*
+ * Next ring.
+ */
+ bRing++;
+ if (bRing > 3 || bMode == BS3_MODE_RM)
+ break;
+ Bs3RegCtxConvertToRingX(&Ctx, bRing);
+ }
+
+ /*
+ * Cleanup.
+ */
+ bs3CpuInstr3BufCleanup(pbBuf, cbBuf, bMode);
+ bs3CpuInstr3FreeExtCtxs(pExtCtx, pExtCtxOut);
+ return 0;
+}
+
+
+/*
+ * [V]PINSRW.
+ */
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_MM1_EDX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_MM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_pinsrw_MM1_R9D_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_MM1_EDX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_MM1_FSxBX_0FFh_icebp);
+extern FNBS3FAR bs3CpuInstr3_pinsrw_MM1_R9D_0FFh_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_XMM1_EDX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_XMM1_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_pinsrw_XMM8_R9D_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pinsrw_XMM8_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_XMM1_EDX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_pinsrw_XMM1_FSxBX_0FFh_icebp);
+extern FNBS3FAR bs3CpuInstr3_pinsrw_XMM8_R9D_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_pinsrw_XMM8_FSxBX_0FFh_icebp_c64;
+
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_000h_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_000h_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpinsrw_XMM8_XMM9_R9D_000h_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpinsrw_XMM8_XMM9_FSxBX_000h_icebp_c64;
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_0FFh_icebp);
+BS3_FNBS3FAR_PROTOTYPES_CMN(bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_0FFh_icebp);
+extern FNBS3FAR bs3CpuInstr3_vpinsrw_XMM8_XMM9_R9D_0FFh_icebp_c64;
+extern FNBS3FAR bs3CpuInstr3_vpinsrw_XMM8_XMM9_FSxBX_0FFh_icebp_c64;
+
+BS3_DECL_FAR(uint8_t) bs3CpuInstr3_v_pinsrw(uint8_t bMode)
+{
+ static BS3CPUINSTR3_TEST6_VALUES_T const s_aValues00[] =
+ {
+ /* Media source GPR word Media dest. */
+ { RTUINT256_INIT_C(0, 0, 0, 0), UINT64_C(0x1234), RTUINT256_INIT_C(0, 0, 0, 0x0000000000001234) },
+ };
+ static BS3CPUINSTR3_TEST6_VALUES_T const s_aValuesFF_64[] =
+ {
+ /* Media source GPR word Media dest. */
+ { RTUINT256_INIT_C(0, 0, 0, 0), UINT64_C(0x1234), RTUINT256_INIT_C(0, 0, 0, 0x1234000000000000) },
+ };
+ static BS3CPUINSTR3_TEST6_VALUES_T const s_aValuesFF[] =
+ {
+ /* Media source GPR word Media dest. */
+ { RTUINT256_INIT_C(0, 0, 0, 0), UINT64_C(0x1234), RTUINT256_INIT_C(0, 0, 0x1234000000000000, 0) },
+ };
+
+ static BS3CPUINSTR3_TEST6_T const s_aTests16[] =
+ {
+ { bs3CpuInstr3_pinsrw_MM1_EDX_000h_icebp_c16, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_000h_icebp_c16, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_EDX_0FFh_icebp_c16, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_000h_icebp_c16, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_000h_icebp_c16, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_0FFh_icebp_c16, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_0FFh_icebp_c16, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_000h_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_000h_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_0FFh_icebp_c16, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_0FFh_icebp_c16, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST6_T const s_aTests32[] =
+ {
+ { bs3CpuInstr3_pinsrw_MM1_EDX_000h_icebp_c32, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_000h_icebp_c32, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_EDX_0FFh_icebp_c32, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_000h_icebp_c32, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_000h_icebp_c32, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_0FFh_icebp_c32, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_0FFh_icebp_c32, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_000h_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_000h_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_0FFh_icebp_c32, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_0FFh_icebp_c32, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST6_T const s_aTests64[] =
+ {
+ { bs3CpuInstr3_pinsrw_MM1_EDX_000h_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_R9D_000h_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, 9, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_000h_icebp_c64, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_MM1_EDX_0FFh_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pinsrw_MM1_R9D_0FFh_icebp_c64, 255, RM_REG, T_MMX_SSE, 4, 32, 9, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+ { bs3CpuInstr3_pinsrw_MM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM32, T_MMX_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF_64), s_aValuesFF_64 },
+
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_000h_icebp_c64, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_000h_icebp_c64, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM1_EDX_0FFh_icebp_c64, 255, RM_REG, T_SSE, 4, 32, 2, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pinsrw_XMM1_FSxBX_0FFh_icebp_c64, 255, RM_MEM32, T_SSE, 4, 32, 255, 1, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_pinsrw_XMM8_R9D_000h_icebp_c64, 255, RM_REG, T_SSE, 4, 32, 9, 8, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM8_FSxBX_000h_icebp_c64, 255, RM_MEM32, T_SSE, 4, 32, 255, 8, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_pinsrw_XMM8_R9D_0FFh_icebp_c64, 255, RM_REG, T_SSE, 4, 32, 9, 8, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_pinsrw_XMM8_FSxBX_0FFh_icebp_c64, 255, RM_MEM32, T_SSE, 4, 32, 255, 8, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_000h_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_000h_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_EDX_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, 2, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpinsrw_XMM1_XMM2_FSxBX_0FFh_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 2, 1, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+
+ { bs3CpuInstr3_vpinsrw_XMM8_XMM9_R9D_000h_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, 9, 9, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM8_XMM9_FSxBX_000h_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 9, 8, RT_ELEMENTS(s_aValues00), s_aValues00 },
+ { bs3CpuInstr3_vpinsrw_XMM8_XMM9_R9D_0FFh_icebp_c64, 255, RM_REG, T_AVX_128, 4, 32, 9, 9, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ { bs3CpuInstr3_vpinsrw_XMM8_XMM9_FSxBX_0FFh_icebp_c64, X86_XCPT_AC, RM_MEM32, T_AVX_128, 4, 32, 255, 9, 8, RT_ELEMENTS(s_aValuesFF), s_aValuesFF },
+ };
+ static BS3CPUINSTR3_TEST6_MODE_T const s_aTests[3] = BS3CPUINSTR3_TEST6_MODES_INIT(s_aTests16, s_aTests32, s_aTests64);
+ unsigned const iTest = BS3CPUINSTR3_TEST_MODES_INDEX(bMode);
+ return bs3CpuInstr3_WorkerTestType6(bMode, s_aTests[iTest].paTests, s_aTests[iTest].cTests,
+ g_aXcptConfig5, RT_ELEMENTS(g_aXcptConfig5));
+}
+
+
+
+
+/**
+ * The 32-bit protected mode main function.
+ *
+ * The tests a driven by 32-bit test drivers, even for real-mode tests (though
+ * we'll switch between PE32 and RM for each test step we perform). Given that
+ * we test MMX, SSE and AVX here, we don't need to worry about 286 or 8086.
+ *
+ * Some extra steps needs to be taken to properly handle extended state in LM64
+ * (Bs3ExtCtxRestoreEx & Bs3ExtCtxSaveEx) and when testing real mode
+ * (Bs3RegCtxSaveForMode & Bs3TrapSetJmpAndRestoreWithExtCtxAndRm).
+ */
+BS3_DECL(void) Main_pe32()
+{
+ static const BS3TESTMODEBYONEENTRY g_aTests[] =
+ {
+#if 1 /*ndef DEBUG_bird*/
+# define ALL_TESTS
+#endif
+#if defined(ALL_TESTS)
+ { "[v]andps/[v]andpd/[v]pand", bs3CpuInstr3_v_andps_andpd_pand, 0 },
+ { "[v]andnps/[v]andnpd/[v]pandn", bs3CpuInstr3_v_andnps_andnpd_pandn, 0 },
+ { "[v]orps/[v]orpd/[v]or", bs3CpuInstr3_v_orps_orpd_por, 0 },
+ { "[v]xorps/[v]xorpd/[v]pxor", bs3CpuInstr3_v_xorps_xorpd_pxor, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pcmpgtb/[v]pcmpgtw/[v]pcmpgtd/[v]pcmpgtq", bs3CpuInstr3_v_pcmpgtb_pcmpgtw_pcmpgtd_pcmpgtq, 0 },
+ { "[v]pcmpeqb/[v]pcmpeqw/[v]pcmpeqd/[v]pcmpeqq", bs3CpuInstr3_v_pcmpeqb_pcmpeqw_pcmpeqd_pcmpeqq, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]paddb/[v]paddw/[v]paddd/[v]paddq", bs3CpuInstr3_v_paddb_paddw_paddd_paddq, 0 },
+ { "[v]psubb/[v]psubw/[v]psubd/[v]psubq", bs3CpuInstr3_v_psubb_psubw_psubd_psubq, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pmullw/[v]pmulld", bs3CpuInstr3_v_pmullw_pmulld, 0 },
+ { "[v]pmulhw", bs3CpuInstr3_v_pmulhw, 0 },
+ { "[v]pmulhuw", bs3CpuInstr3_v_pmulhuw, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pmovmskb", bs3CpuInstr3_v_pmovmskb, 0 },
+ { "pshufb", bs3CpuInstr3_pshufb, 0 },
+ { "pshufw", bs3CpuInstr3_pshufw, 0 },
+ { "[v]pshufhw", bs3CpuInstr3_v_pshufhw, 0 },
+ { "[v]pshuflw", bs3CpuInstr3_v_pshuflw, 0 },
+ { "[v]pshufd", bs3CpuInstr3_v_pshufd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]punpckhbw", bs3CpuInstr3_v_punpckhbw, 0 },
+ { "[v]punpckhwd", bs3CpuInstr3_v_punpckhwd, 0 },
+ { "[v]punpckhdq", bs3CpuInstr3_v_punpckhdq, 0 },
+ { "[v]punpckhqdq", bs3CpuInstr3_v_punpckhqdq, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]punpcklbw", bs3CpuInstr3_v_punpcklbw, 0 },
+ { "[v]punpcklwd", bs3CpuInstr3_v_punpcklwd, 0 },
+ { "[v]punpckldq", bs3CpuInstr3_v_punpckldq, 0 },
+ { "[v]punpcklqdq", bs3CpuInstr3_v_punpcklqdq, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]packsswb", bs3CpuInstr3_v_packsswb, 0 },
+ { "[v]packssdw", bs3CpuInstr3_v_packssdw, 0 },
+ { "[v]packuswb", bs3CpuInstr3_v_packuswb, 0 },
+ { "[v]packusdw", bs3CpuInstr3_v_packusdw, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pmaxub/[v]pmaxuw/[v]pmaxud", bs3CpuInstr3_v_pmaxub_pmaxuw_pmaxud, 0 },
+ { "[v]pmaxsb/[v]pmaxsw/[v]pmaxsd", bs3CpuInstr3_v_pmaxsb_pmaxsw_pmaxsd, 0 },
+ { "[v]pminub/[v]pminuw/[v]pminud", bs3CpuInstr3_v_pminub_pminuw_pminud, 0 },
+ { "[v]pminsb/[v]pminsw/[v]pminsd", bs3CpuInstr3_v_pminsb_pminsw_pminsd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]movntdqa", bs3CpuInstr3_v_movntdqa, 0 },
+ { "[v]movntdq", bs3CpuInstr3_v_movntdq, 0 },
+ { "[v]movntps_movntpd", bs3CpuInstr3_v_movntps_movntpd, 0 },
+ { "[v]movups", bs3CpuInstr3_v_movups, 0 },
+ { "[v]movupd", bs3CpuInstr3_v_movupd, 0 },
+ { "[v]movss", bs3CpuInstr3_v_movss, 0 },
+ { "[v]movsd", bs3CpuInstr3_v_movsd, 0 },
+ { "[v]movhlps", bs3CpuInstr3_v_movhlps, 0 },
+ { "[v]movlps/[v]movlpd", bs3CpuInstr3_v_movlps_movlpd, 0 },
+ { "[v]movhps/[v]movhpd", bs3CpuInstr3_v_movhps_movhpd, 0 },
+ { "[v]movsldup", bs3CpuInstr3_v_movsldup, 0 },
+ { "[v]movshdup", bs3CpuInstr3_v_movshdup, 0 },
+ { "[v]movddup", bs3CpuInstr3_v_movddup, 0 },
+ { "[v]movaps_movapd", bs3CpuInstr3_v_movaps_movapd, 0 },
+ { "[v]movd_movq", bs3CpuInstr3_v_movd_movq, 0 },
+ { "[v]movdqu", bs3CpuInstr3_v_movdqu, 0 },
+ { "[v]movdqa", bs3CpuInstr3_v_movdqa, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]ptest", bs3CpuInstr3_v_ptest, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pavgb/[v]pavgw", bs3CpuInstr3_v_pavgb_pavgw, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pabsb/[v]pabsw/[v]pabsd", bs3CpuInstr3_v_pabsb_pabsw_pabsd, 0 },
+ { "[v]psignb/[v]psignw/[v]psignd", bs3CpuInstr3_v_psignb_psignw_psignd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]phaddw/[v]phaddd", bs3CpuInstr3_v_phaddw_phaddd, 0 },
+ { "[v]phsubw/[v]phsubd", bs3CpuInstr3_v_phsubw_phsubd, 0 },
+ { "[v]phaddsw", bs3CpuInstr3_v_phaddsw, 0 },
+ { "[v]phsubsw", bs3CpuInstr3_v_phsubsw, 0 },
+ { "[v]pmaddubsw", bs3CpuInstr3_v_pmaddubsw, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pmulhrsw", bs3CpuInstr3_v_pmulhrsw, 0 },
+ { "[v]psadbw", bs3CpuInstr3_v_psadbw, 0 },
+ { "[v]pmuldq", bs3CpuInstr3_v_pmuldq, 0 },
+ { "[v]pmuludq", bs3CpuInstr3_v_pmuludq, 0 },
+ { "[v]punpcklps/[v]punpcklpd", bs3CpuInstr3_v_punpcklps_punpcklpd, 0 },
+ { "[v]punpckhps/[v]punpckhpd", bs3CpuInstr3_v_punpckhps_punpckhpd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pmovsxbw/[v]pmovsxbd/[v]pmovsxbq/[v]pmovsxwd/[v]pmovsxwq/[v]pmovsxdq",
+ bs3CpuInstr3_v_pmovsxbw_pmovsxbd_pmovsxbq_pmovsxwd_pmovsxwq_pmovsxdq, 0 },
+ { "[v]pmovzxbw/[v]pmovzxbd/[v]pmovzxbq/[v]pmovzxwd/[v]pmovzxwq/[v]pmovzxdq",
+ bs3CpuInstr3_v_pmovzxbw_pmovzxbd_pmovzxbq_pmovzxwd_pmovzxwq_pmovzxdq, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]shufps", bs3CpuInstr3_v_shufps, 0 },
+ { "[v]shufpd", bs3CpuInstr3_v_shufpd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]lddqu", bs3CpuInstr3_v_lddqu, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]phminposuw", bs3CpuInstr3_v_phminposuw, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pblendvb", bs3CpuInstr3_v_pblendvb, 0 },
+ { "[v]blendvps", bs3CpuInstr3_v_blendvps, 0 },
+ { "[v]blendvpd", bs3CpuInstr3_v_blendvpd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]palignr", bs3CpuInstr3_v_palignr, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pblendw", bs3CpuInstr3_v_pblendw, 0 },
+ { "[v]blendps", bs3CpuInstr3_v_blendps, 0 },
+ { "[v]blendpd", bs3CpuInstr3_v_blendpd, 0 },
+#endif
+#if defined(ALL_TESTS)
+ { "[v]pclmulqdq", bs3CpuInstr3_v_pclmulqdq, 0 },
+ { "[v]pinsrw", bs3CpuInstr3_v_pinsrw, 0 },
+ { "[v]pextrw", bs3CpuInstr3_v_pextrw, 0 },
+ { "[v]movmskps/[v]movmskpd", bs3CpuInstr3_v_movmskps_movmskpd, 0 },
+
+#endif
+ };
+ Bs3TestInit("bs3-cpu-instr-3");
+
+ /*
+ * Initialize globals.
+ */
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ uint32_t fEbx, fEcx, fEdx;
+ ASMCpuIdExSlow(1, 0, 0, 0, NULL, NULL, &fEcx, &fEdx);
+ g_afTypeSupports[T_MMX] = RT_BOOL(fEdx & X86_CPUID_FEATURE_EDX_MMX);
+ g_afTypeSupports[T_MMX_SSE] = RT_BOOL(fEdx & X86_CPUID_FEATURE_EDX_SSE);
+ g_afTypeSupports[T_MMX_SSE2] = RT_BOOL(fEdx & X86_CPUID_FEATURE_EDX_SSE2);
+ g_afTypeSupports[T_MMX_SSSE3] = RT_BOOL(fEdx & X86_CPUID_FEATURE_ECX_SSSE3);
+ g_afTypeSupports[T_SSE] = RT_BOOL(fEdx & X86_CPUID_FEATURE_EDX_SSE);
+ g_afTypeSupports[T_SSE2] = RT_BOOL(fEdx & X86_CPUID_FEATURE_EDX_SSE2);
+ g_afTypeSupports[T_SSE3] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_SSE3);
+ g_afTypeSupports[T_SSSE3] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_SSSE3);
+ g_afTypeSupports[T_SSE4_1] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_SSE4_1);
+ g_afTypeSupports[T_SSE4_2] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_SSE4_2);
+ g_afTypeSupports[T_PCLMUL] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_PCLMUL);
+ g_afTypeSupports[T_AVX_128] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_AVX);
+ g_afTypeSupports[T_AVX_256] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_AVX);
+ g_afTypeSupports[T_AVX_PCLMUL] = RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_PCLMUL)
+ && RT_BOOL(fEcx & X86_CPUID_FEATURE_ECX_AVX);
+
+ if (ASMCpuId_EAX(0) >= 7)
+ {
+ ASMCpuIdExSlow(7, 0, 0, 0, NULL, &fEbx, NULL, NULL);
+ g_afTypeSupports[T_AVX2_128] = RT_BOOL(fEbx & X86_CPUID_STEXT_FEATURE_EBX_AVX2);
+ g_afTypeSupports[T_AVX2_256] = RT_BOOL(fEbx & X86_CPUID_STEXT_FEATURE_EBX_AVX2);
+ }
+
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID_EXT_LEAVES)
+ {
+ ASMCpuIdExSlow(UINT32_C(0x80000001), 0, 0, 0, NULL, NULL, &fEcx, &fEdx);
+ g_afTypeSupports[T_AXMMX] = RT_BOOL(fEcx & X86_CPUID_AMD_FEATURE_EDX_AXMMX);
+ g_afTypeSupports[T_SSE4A] = RT_BOOL(fEcx & X86_CPUID_AMD_FEATURE_ECX_SSE4A);
+ g_fAmdMisalignedSse = RT_BOOL(fEcx & X86_CPUID_AMD_FEATURE_ECX_MISALNSSE);
+ }
+ g_afTypeSupports[T_AXMMX_OR_SSE] = g_afTypeSupports[T_AXMMX] || g_afTypeSupports[T_SSE];
+ }
+
+ /*
+ * Allocate a buffer for testing.
+ */
+ g_cbBuf = X86_PAGE_SIZE * 4;
+ g_pbBuf = (uint8_t BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_REAL, g_cbBuf);
+ if (g_pbBuf)
+ {
+ /*
+ * Do the tests.
+ */
+ Bs3TestDoModesByOne_pe32(g_aTests, RT_ELEMENTS(g_aTests), BS3TESTMODEBYONEENTRY_F_REAL_MODE_READY);
+ }
+ else
+ Bs3TestFailed("Failed to allocate 16K buffer");
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1-asm.asm
new file mode 100644
index 00000000..49d95f3f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1-asm.asm
@@ -0,0 +1,242 @@
+; $Id: bs3-cpu-state64-1-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-state64-1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+BS3_GLOBAL_DATA g_bs3CpuState64CtxCaller, BS3REGCTX_size
+ resb BS3REGCTX_size
+BS3_GLOBAL_DATA g_bs3CpuState64CtxToLoad, BS3REGCTX_size
+ resb BS3REGCTX_size
+BS3_GLOBAL_DATA g_bs3CpuState64CtxSaved, BS3REGCTX_size
+ resb BS3REGCTX_size
+
+BS3_GLOBAL_DATA g_bs3CpuState64RCX, 8
+ dq 1
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_BEGIN_TEXT64
+EXTERN Bs3RegCtxRestore_c64
+EXTERN Bs3RegCtxSave_c64
+
+
+BS3_BEGIN_TEXT64
+ BS3_SET_BITS 64
+
+;;
+;; Test worker that switches between 64-bit and 16-bit real mode,
+;; only trashing RAX, BX, DS, RSP (preseved) and RIP.
+;;
+;; Caller puts the state to load in g_bs3CpuState64CtxToLoad, this function alters
+;; the BX and RIP values before loading it. It then switches to 16-bit real mode,
+;; executes the worker given as input, re-enters long mode and saves the state to
+;; g_bs3CpuState64CtxSaved.
+;;
+;; @param rcx Address of worker (16-bit) to invoke while in real-mode.
+;;
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker)
+ push rbp
+ mov rbp, rsp
+ sub rsp, 40h
+ mov [rbp + 16], rcx
+
+ ;
+ ; Save the current register state so we can return with the exact state we entered.
+ ;
+ lea rcx, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64CtxCaller)) wrt FLAT]
+ mov [rsp], rcx
+ call NAME(Bs3RegCtxSave_c64)
+
+ ;
+ ; Load the context. We modify the state to be loaded so that it fits
+ ; into the code flow here..
+ ;
+ lea rcx, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64CtxToLoad)) wrt FLAT]
+ mov [rcx + BS3REGCTX.rsp], rsp
+ ;lea rdx, [BS3_WRT_RIP(.ctx_loaded) wrt FLAT] - absolute address cannot be relative. wtf?
+ mov edx, .ctx_loaded wrt FLAT
+ mov [rcx + BS3REGCTX.rip], rdx
+ mov edx, [rbp + 16] ; Worker address. Putting it in the BX register relative to 16-bit CS.
+ sub edx, BS3_ADDR_BS3TEXT16
+ mov [rcx + BS3REGCTX.rbx], dx
+ mov edx, 0 ; fFlags
+ mov [rsp], rcx
+ mov [rsp + 8], rdx
+ call NAME(Bs3RegCtxRestore_c64)
+.ctx_loaded:
+
+ ;
+ ; Disable long mode.
+ ;
+
+ ; Construct a far return for switching to 16-bit code.
+ push BS3_SEL_R0_CS16
+ push .sixteen_bit_segment wrt CGROUP16
+ xRETF
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS 16
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ ; Make the DS usable from real mode.
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+
+ ; Exit to real mode.
+ mov eax, cr0
+ and eax, X86_CR0_NO_PE_NO_PG
+ mov cr0, eax
+ jmp CGROUP16:.reload_cs16
+BS3_GLOBAL_LOCAL_LABEL .reload_cs16
+
+ ;
+ ; Jump to the 16-bit worker function that will make state modifications.
+ ;
+ jmp bx
+BS3_GLOBAL_LOCAL_LABEL .resume16
+
+ ;
+ ; Re-enter long mode.
+ ;
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp CGROUP16:.reload_cs_long_mode
+BS3_GLOBAL_LOCAL_LABEL .reload_cs_long_mode
+ ; Construct a far return for switching to 64-bit code.
+ push dword BS3_SEL_R0_CS64
+ push dword .sixtyfour_bit_segment wrt FLAT
+ o32 retf
+BS3_BEGIN_TEXT64
+BS3_GLOBAL_LOCAL_LABEL .sixtyfour_bit_segment
+ BS3_SET_BITS 64
+
+ ;
+ ; We're back in long mode, save the context.
+ ;
+ mov [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64RCX)) wrt FLAT], rcx
+ lea rcx, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64CtxSaved)) wrt FLAT]
+ mov [rsp], rcx
+ call NAME(Bs3RegCtxSave_c64)
+ lea rcx, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64CtxSaved)) wrt FLAT]
+ mov rax, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64RCX)) wrt FLAT]
+ mov [rcx + BS3REGCTX.rcx], rax
+
+ ;
+ ; Load the caller's context.
+ ;
+ lea rcx, [BS3_WRT_RIP(BS3_DATA_NM(g_bs3CpuState64CtxCaller)) wrt FLAT]
+ ;lea rdx, [BS3_WRT_RIP(.return_sequence) wrt FLAT] - absolute address cannot be relative. wtf?
+ mov edx, .return_sequence wrt FLAT
+ mov [rcx + BS3REGCTX.rip], rdx
+ mov edx, 0
+ mov [rsp], rcx
+ mov [rsp + 8], rdx
+ call NAME(Bs3RegCtxRestore_c64)
+.return_sequence:
+
+ add rsp, 40h
+ pop rbp
+ ret
+BS3_PROC_END NAME(bs3CpuState64Worker)
+
+
+BS3_BEGIN_TEXT16
+;
+; Real-mod modification workers for bs3CpuState64Worker.
+;
+
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker_Nop)
+ nop
+ jmp NAME(bs3CpuState64Worker.resume16)
+BS3_PROC_END NAME(bs3CpuState64Worker_Nop)
+
+
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker_ModAll32BitGrps)
+ mov eax, 0xc0ffee0d ; C code hardcodes these values too.
+ mov ecx, 0xc0ffee1d
+ mov edx, 0xc0ffee2d
+ mov ebx, 0xc0ffee3d
+ ; leave esp alone for now.
+ mov ebp, 0xc0ffee5d
+ mov esi, 0xc0ffee6d
+ mov edi, 0xc0ffee7d
+ jmp NAME(bs3CpuState64Worker.resume16)
+BS3_PROC_END NAME(bs3CpuState64Worker_ModAll32BitGrps)
+
+
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker_ModAll16BitGrps)
+ mov ax, 0xfad0 ; C code hardcodes these values too.
+ mov cx, 0xfad1
+ mov dx, 0xfad2
+ mov bx, 0xfad3
+ ; leave esp alone for now.
+ mov bp, 0xfad5
+ mov si, 0xfad6
+ mov di, 0xfad7
+ jmp NAME(bs3CpuState64Worker.resume16)
+BS3_PROC_END NAME(bs3CpuState64Worker_ModAll16BitGrps)
+
+
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker_ModAll8BitGrps)
+ mov al, 0x10 ; C code hardcodes these values too.
+ mov ah, 0x11
+ mov cl, 0x20
+ mov ch, 0x21
+ mov dl, 0x30
+ mov dh, 0x31
+ mov bl, 0x40
+ mov bh, 0x41
+ jmp NAME(bs3CpuState64Worker.resume16)
+BS3_PROC_END NAME(bs3CpuState64Worker_ModAll8BitGrps)
+
+BS3_PROC_BEGIN NAME(bs3CpuState64Worker_ModCr2)
+ mov eax, 0xf00dface ; C code hardcodes this value too.
+ mov cr2, eax
+ jmp NAME(bs3CpuState64Worker.resume16)
+BS3_PROC_END NAME(bs3CpuState64Worker_ModCr2)
+
+;; @todo drX registers.
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1.c64 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1.c64
new file mode 100644
index 00000000..c2645f59
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-state64-1.c64
@@ -0,0 +1,166 @@
+/* $Id: bs3-cpu-state64-1.c64 $ */
+/** @file
+ * BS3Kit - bs3-cpu-state64-1, 64-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3_DECL(void) bs3CpuState64Worker_Nop(void);
+BS3_DECL(void) bs3CpuState64Worker_ModAll32BitGrps(void);
+BS3_DECL(void) bs3CpuState64Worker_ModAll16BitGrps(void);
+BS3_DECL(void) bs3CpuState64Worker_ModAll8BitGrps(void);
+BS3_DECL(void) bs3CpuState64Worker_ModCr2(void);
+BS3_DECL(void) bs3CpuState64Worker(PFNBS3NEAR pfnWorker);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern BS3REGCTX g_bs3CpuState64CtxToLoad;
+extern BS3REGCTX const g_bs3CpuState64CtxSaved;
+
+
+static void bs3CpuState64InitCtx(uint64_t uTweak)
+{
+ Bs3RegCtxSave(&g_bs3CpuState64CtxToLoad);
+ g_bs3CpuState64CtxToLoad.rax.u = UINT64_C(0x8877f055443322e0) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rcx.u = UINT64_C(0x8877f155443322e1) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rdx.u = UINT64_C(0x8877f255443322e2) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rbx.u = UINT64_C(0x8877f355443322e3) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rsp.u = UINT64_C(0x8877f455443322e4) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rbp.u = UINT64_C(0x8877f555443322e5) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rsi.u = UINT64_C(0x8877f655443322e6) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.rdi.u = UINT64_C(0x8877f755443322e7) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r8.u = UINT64_C(0x8877f855443322e8) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r9.u = UINT64_C(0x8877f955443322e9) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r10.u = UINT64_C(0x8877fa55443322ea) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r11.u = UINT64_C(0x8877fb55443322eb) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r12.u = UINT64_C(0x8877fc55443322ec) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r13.u = UINT64_C(0x8877fd55443322ed) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r14.u = UINT64_C(0x8877fe55443322ee) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.r15.u = UINT64_C(0x8877ff55443322ef) ^ uTweak;
+ g_bs3CpuState64CtxToLoad.cr2.u = UINT64_C(0x89abcdef76543210) ^ uTweak;
+ //Bs3TestPrintf("** Input:\n");
+ //Bs3RegCtxPrint(&g_bs3CpuState64CtxToLoad);
+}
+
+
+static void bs3CpuState64Comp(bool fModRbx)
+{
+ //Bs3TestPrintf("** Result:\n");
+ //Bs3RegCtxPrint(&g_bs3CpuState64CtxSaved);
+ g_bs3CpuState64CtxToLoad.rax = g_bs3CpuState64CtxSaved.rax;
+ g_bs3CpuState64CtxToLoad.rip = g_bs3CpuState64CtxSaved.rip;
+ g_bs3CpuState64CtxToLoad.rflags = g_bs3CpuState64CtxSaved.rflags;
+ g_bs3CpuState64CtxToLoad.rsp = g_bs3CpuState64CtxSaved.rsp;
+ if (!fModRbx)
+ g_bs3CpuState64CtxToLoad.rbx.au16[0] = g_bs3CpuState64CtxSaved.rbx.au16[0];
+ if (1)
+ g_bs3CpuState64CtxToLoad.ds = g_bs3CpuState64CtxSaved.ds;
+
+ Bs3TestCheckRegCtxEx(&g_bs3CpuState64CtxSaved, &g_bs3CpuState64CtxToLoad, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/,
+ 0 /*fExtraEfl*/, "lm64", 0 /*idTestStep*/);
+}
+
+BS3_DECL(void) Main_lm64()
+{
+ Bs3TestInit("bs3-cpu-state64-1");
+ /*
+ * Switch to 64-bit mode and back to rm.
+ */
+ Bs3TestSub("noop");
+ bs3CpuState64InitCtx(0);
+ bs3CpuState64Worker(bs3CpuState64Worker_Nop);
+ bs3CpuState64Comp(false /*fModRbx*/);
+
+ Bs3TestSub("Modify all 32-bit GPRs");
+ bs3CpuState64InitCtx(0);
+ bs3CpuState64Worker(bs3CpuState64Worker_ModAll32BitGrps);
+ g_bs3CpuState64CtxToLoad.rax.u = UINT64_C(0xc0ffee0d);
+ g_bs3CpuState64CtxToLoad.rcx.u = UINT64_C(0xc0ffee1d);
+ g_bs3CpuState64CtxToLoad.rdx.u = UINT64_C(0xc0ffee2d);
+ g_bs3CpuState64CtxToLoad.rbx.u = UINT64_C(0xc0ffee3d);
+ g_bs3CpuState64CtxToLoad.rsp.u = UINT64_C(0xc0ffee4d);
+ g_bs3CpuState64CtxToLoad.rbp.u = UINT64_C(0xc0ffee5d);
+ g_bs3CpuState64CtxToLoad.rsi.u = UINT64_C(0xc0ffee6d);
+ g_bs3CpuState64CtxToLoad.rdi.u = UINT64_C(0xc0ffee7d);
+ bs3CpuState64Comp(true /*fModRbx*/);
+
+ Bs3TestSub("Modify all 16-bit GPRs");
+ bs3CpuState64InitCtx(0);
+ bs3CpuState64Worker(bs3CpuState64Worker_ModAll16BitGrps);
+ g_bs3CpuState64CtxToLoad.rax.au16[0] = UINT16_C(0xfad0);
+ g_bs3CpuState64CtxToLoad.rcx.au16[0] = UINT16_C(0xfad1);
+ g_bs3CpuState64CtxToLoad.rdx.au16[0] = UINT16_C(0xfad2);
+ g_bs3CpuState64CtxToLoad.rbx.au16[0] = UINT16_C(0xfad3);
+ g_bs3CpuState64CtxToLoad.rsp.au16[0] = UINT16_C(0xfad4);
+ g_bs3CpuState64CtxToLoad.rbp.au16[0] = UINT16_C(0xfad5);
+ g_bs3CpuState64CtxToLoad.rsi.au16[0] = UINT16_C(0xfad6);
+ g_bs3CpuState64CtxToLoad.rdi.au16[0] = UINT16_C(0xfad7);
+ bs3CpuState64Comp(true /*fModRbx*/);
+
+ Bs3TestSub("Modify all 8-bit GPRs");
+ bs3CpuState64InitCtx(0);
+ bs3CpuState64Worker(bs3CpuState64Worker_ModAll8BitGrps);
+ g_bs3CpuState64CtxToLoad.rax.au8[0] = 0x10;
+ g_bs3CpuState64CtxToLoad.rax.au8[1] = 0x11;
+ g_bs3CpuState64CtxToLoad.rcx.au8[0] = 0x20;
+ g_bs3CpuState64CtxToLoad.rcx.au8[1] = 0x21;
+ g_bs3CpuState64CtxToLoad.rdx.au8[0] = 0x30;
+ g_bs3CpuState64CtxToLoad.rdx.au8[1] = 0x31;
+ g_bs3CpuState64CtxToLoad.rbx.au8[0] = 0x40;
+ g_bs3CpuState64CtxToLoad.rbx.au8[1] = 0x41;
+ bs3CpuState64Comp(true /*fModRbx*/);
+
+ Bs3TestSub("Modify CR2");
+ bs3CpuState64InitCtx(0);
+ bs3CpuState64Worker(bs3CpuState64Worker_ModCr2);
+ g_bs3CpuState64CtxToLoad.cr2.u = 0xf00dface;
+ bs3CpuState64Comp(true /*fModRbx*/);
+
+ /** @todo DRx */
+ Bs3TestTerm();
+ for (;;)
+ ASMHalt();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-asm.asm
new file mode 100644
index 00000000..7886c2a8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-asm.asm
@@ -0,0 +1,69 @@
+; $Id: bs3-cpu-weird-1-asm.asm $
+;; @file
+; BS3Kit - bs3-cpu-weird-1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+
+
+;
+; CPU mode agnostic test code snippets.
+;
+BS3_BEGIN_TEXT16
+
+
+;
+; CPU mode agnostic test code snippets.
+;
+BS3_BEGIN_TEXT32
+
+
+BS3_BEGIN_TEXT16
+
+;;
+;; Instantiate code templates.
+;;
+BS3_INSTANTIATE_COMMON_TEMPLATE "bs3-cpu-weird-1-template.mac"
+; BS3_INSTANTIATE_TEMPLATE_WITH_WEIRD_ONES "bs3-cpu-weird-1-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-template.mac
new file mode 100644
index 00000000..9247db83
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-template.mac
@@ -0,0 +1,167 @@
+; $Id: bs3-cpu-weird-1-template.mac $
+;; @file
+; BS3Kit - bs3-cpu-weird-1 assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+;
+; Test code snippets containing code which differs between 16-bit, 32-bit
+; and 64-bit CPUs modes.
+;
+%ifdef BS3_INSTANTIATING_CMN
+
+
+;
+; Inhibited int 80h.
+;
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_InhibitedInt80, BS3_PBC_NEAR
+ ; Load SS from stack. This instruction causes fusing.
+%if TMPL_BITS != 64
+ pop ss
+%else
+ mov ss, [rsp]
+%endif
+ ; The ring transition instruction.
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuWeird1_InhibitedInt80_int80), , 0
+ int 80h
+ ; We shouldn't get here!
+.ud2_again:
+ ud2
+ jmp .ud2_again
+BS3_PROC_END_CMN bs3CpuWeird1_InhibitedInt80
+
+;
+; Inhibited int 3.
+;
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_InhibitedInt3, BS3_PBC_NEAR
+ ; Load SS from stack. This instruction causes fusing.
+%if TMPL_BITS != 64
+ pop ss
+%else
+ mov ss, [rsp]
+%endif
+ ; The ring transition instruction.
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuWeird1_InhibitedInt3_int3), , 0
+ int 3
+ ; We shouldn't get here!
+.ud2_again:
+ ud2
+ jmp .ud2_again
+AssertCompile(.ud2_again - BS3_CMN_NM(bs3CpuWeird1_InhibitedInt3_int3) == 2)
+BS3_PROC_END_CMN bs3CpuWeird1_InhibitedInt3
+
+
+;
+; Inhibited int3.
+;
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_InhibitedBp, BS3_PBC_NEAR
+ ; Load SS from stack. This instruction causes fusing.
+%if TMPL_BITS != 64
+ pop ss
+%else
+ mov ss, [rsp]
+%endif
+ ; The ring transition instruction.
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(bs3CpuWeird1_InhibitedBp_int3), , 0
+ int3
+ ; We shouldn't get here!
+.ud2_again:
+ ud2
+ jmp .ud2_again
+AssertCompile(.ud2_again - BS3_CMN_NM(bs3CpuWeird1_InhibitedBp_int3) == 1)
+BS3_PROC_END_CMN bs3CpuWeird1_InhibitedBp
+
+
+;
+; PC (IP/EIP) wrapper templates.
+; These will potentially trigger VM exits, except for the benign one.
+;
+; Note! Single instructions as the testcase will shift multibyte variations
+; across the wrap-around boundary and that would cause unpredictable
+; results for the 16-bit if there is more than one instruction.
+;
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapBenign1, BS3_PBC_NEAR
+ nop
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapBenign1
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapBenign2, BS3_PBC_NEAR
+ xor xDX, xAX
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapBenign2
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapCpuId, BS3_PBC_NEAR
+ cpuid
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapCpuId
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapIn80, BS3_PBC_NEAR
+ in al, 80h
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapIn80
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapOut80, BS3_PBC_NEAR
+ out 80h, al
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapOut80
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapSmsw, BS3_PBC_NEAR
+ smsw si
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapSmsw
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapRdCr0, BS3_PBC_NEAR
+ mov sAX, cr0
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapRdCr0
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapRdDr0, BS3_PBC_NEAR
+ mov sAX, dr0
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapRdDr0
+
+BS3_PROC_BEGIN_CMN bs3CpuWeird1_PcWrapWrDr0, BS3_PBC_NEAR
+ mov dr0, sAX
+BS3_PROC_END_CMN bs3CpuWeird1_PcWrapWrDr0
+
+
+%endif ; BS3_INSTANTIATING_CMN
+
+%include "bs3kit-template-footer.mac" ; reset environment
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-x0.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-x0.c
new file mode 100644
index 00000000..4d85fdb2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1-x0.c
@@ -0,0 +1,1086 @@
+/* $Id: bs3-cpu-weird-1-x0.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-weird-2, C test driver code (16-bit).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define BS3_USE_X0_TEXT_SEG
+#include <bs3kit.h>
+#include <bs3-cmn-memory.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#undef CHECK_MEMBER
+#define CHECK_MEMBER(a_szName, a_szFmt, a_Actual, a_Expected) \
+ do \
+ { \
+ if ((a_Actual) == (a_Expected)) { /* likely */ } \
+ else bs3CpuWeird1_FailedF(a_szName "=" a_szFmt " expected " a_szFmt, (a_Actual), (a_Expected)); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_c64;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_int80_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_int80_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt80_int80_c64;
+
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_c64;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_int3_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_int3_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedInt3_int3_c64;
+
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_c64;
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_int3_c16;
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_int3_c32;
+extern FNBS3FAR bs3CpuWeird1_InhibitedBp_int3_c64;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const char BS3_FAR *g_pszTestMode = (const char *)1;
+static BS3CPUVENDOR g_enmCpuVendor = BS3CPUVENDOR_INTEL;
+static bool g_fVME = false;
+//static uint8_t g_bTestMode = 1;
+//static bool g_f16BitSys = 1;
+
+
+
+/**
+ * Sets globals according to the mode.
+ *
+ * @param bTestMode The test mode.
+ */
+static void bs3CpuWeird1_SetGlobals(uint8_t bTestMode)
+{
+// g_bTestMode = bTestMode;
+ g_pszTestMode = Bs3GetModeName(bTestMode);
+// g_f16BitSys = BS3_MODE_IS_16BIT_SYS(bTestMode);
+ g_usBs3TestStep = 0;
+ g_enmCpuVendor = Bs3GetCpuVendor();
+ g_fVME = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486
+ && (Bs3RegGetCr4() & X86_CR4_VME);
+}
+
+
+/**
+ * Wrapper around Bs3TestFailedF that prefixes the error with g_usBs3TestStep
+ * and g_pszTestMode.
+ */
+static void bs3CpuWeird1_FailedF(const char *pszFormat, ...)
+{
+ va_list va;
+
+ char szTmp[168];
+ va_start(va, pszFormat);
+ Bs3StrPrintfV(szTmp, sizeof(szTmp), pszFormat, va);
+ va_end(va);
+
+ Bs3TestFailedF("%u - %s: %s", g_usBs3TestStep, g_pszTestMode, szTmp);
+}
+
+
+/**
+ * Compares interrupt stuff.
+ */
+static void bs3CpuWeird1_CompareDbgInhibitRingXfer(PCBS3TRAPFRAME pTrapCtx, PCBS3REGCTX pStartCtx, uint8_t bXcpt,
+ int8_t cbPcAdjust, int8_t cbSpAdjust, uint32_t uDr6Expected,
+ uint8_t cbIretFrame, uint64_t uHandlerRsp)
+{
+ uint32_t uDr6 = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386 ? Bs3RegGetDr6() : X86_DR6_INIT_VAL;
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, 0);
+ CHECK_MEMBER("cbIretFrame", "%#04x", pTrapCtx->cbIretFrame, cbIretFrame);
+ CHECK_MEMBER("uHandlerRsp", "%#06RX64", pTrapCtx->uHandlerRsp, uHandlerRsp);
+ if (uDr6 != uDr6Expected)
+ bs3CpuWeird1_FailedF("dr6=%#010RX32 expected %#010RX32", uDr6, uDr6Expected);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, pStartCtx, cbPcAdjust, cbSpAdjust, 0 /*fExtraEfl*/, g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+ Bs3TestPrintf("DR6=%#RX32; Handler: CS=%04RX16 SS:ESP=%04RX16:%08RX64 EFL=%RX64 cbIret=%#x\n",
+ uDr6, pTrapCtx->uHandlerCs, pTrapCtx->uHandlerSs, pTrapCtx->uHandlerRsp,
+ pTrapCtx->fHandlerRfl, pTrapCtx->cbIretFrame);
+#if 0
+ Bs3TestPrintf("Halting in CompareIntCtx: bXcpt=%#x\n", bXcpt);
+ ASMHalt();
+#endif
+ }
+}
+
+static uint64_t bs3CpuWeird1_GetTrapHandlerEIP(uint8_t bXcpt, uint8_t bMode, bool fV86)
+{
+ if ( BS3_MODE_IS_RM_SYS(bMode)
+ || (fV86 && BS3_MODE_IS_V86(bMode)))
+ {
+ PRTFAR16 paIvt = (PRTFAR16)Bs3XptrFlatToCurrent(0);
+ return paIvt[bXcpt].off;
+ }
+ if (BS3_MODE_IS_16BIT_SYS(bMode))
+ return Bs3Idt16[bXcpt].Gate.u16OffsetLow;
+ if (BS3_MODE_IS_32BIT_SYS(bMode))
+ return RT_MAKE_U32(Bs3Idt32[bXcpt].Gate.u16OffsetLow, Bs3Idt32[bXcpt].Gate.u16OffsetHigh);
+ return RT_MAKE_U64(RT_MAKE_U32(Bs3Idt64[bXcpt].Gate.u16OffsetLow, Bs3Idt32[bXcpt].Gate.u16OffsetHigh),
+ Bs3Idt64[bXcpt].Gate.u32OffsetTop);
+}
+
+
+static int bs3CpuWeird1_DbgInhibitRingXfer_Worker(uint8_t bTestMode, uint8_t bIntGate, uint8_t cbRingInstr, int8_t cbSpAdjust,
+ FPFNBS3FAR pfnTestCode, FPFNBS3FAR pfnTestLabel)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3TRAPFRAME TrapExpect;
+ BS3REGCTX Ctx;
+ uint8_t bSavedDpl;
+ uint8_t const offTestLabel = BS3_FP_OFF(pfnTestLabel) - BS3_FP_OFF(pfnTestCode);
+ //uint8_t const cbIretFrameSame = BS3_MODE_IS_RM_SYS(bTestMode) ? 6
+ // : BS3_MODE_IS_16BIT_SYS(bTestMode) ? 12
+ // : BS3_MODE_IS_64BIT_SYS(bTestMode) ? 40 : 12;
+ uint8_t cbIretFrameInt;
+ uint8_t cbIretFrameIntDb;
+ uint8_t const cbIretFrameSame = BS3_MODE_IS_16BIT_SYS(bTestMode) ? 6
+ : BS3_MODE_IS_32BIT_SYS(bTestMode) ? 12 : 40;
+ uint8_t const cbSpAdjSame = BS3_MODE_IS_64BIT_SYS(bTestMode) ? 48 : cbIretFrameSame;
+ uint8_t bVmeMethod = 0;
+ uint64_t uHandlerRspInt;
+ uint64_t uHandlerRspIntDb;
+ BS3_XPTR_AUTO(uint32_t, StackXptr);
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&TrapExpect, sizeof(TrapExpect));
+
+ /*
+ * Make INT xx accessible from DPL 3 and create a ring-3 context that we can work with.
+ */
+ bSavedDpl = Bs3TrapSetDpl(bIntGate, 3);
+
+ Bs3RegCtxSaveEx(&Ctx, bTestMode, 1024);
+ Bs3RegCtxSetRipCsFromLnkPtr(&Ctx, pfnTestCode);
+ if (BS3_MODE_IS_16BIT_SYS(bTestMode))
+ g_uBs3TrapEipHint = Ctx.rip.u32;
+ Ctx.rflags.u32 &= ~X86_EFL_RF;
+
+ /* Raw-mode enablers. */
+ Ctx.rflags.u32 |= X86_EFL_IF;
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486)
+ Ctx.cr0.u32 |= X86_CR0_WP;
+
+ /* We put the SS value on the stack so we can easily set breakpoints there. */
+ Ctx.rsp.u32 -= 8;
+ BS3_XPTR_SET_FLAT(uint32_t, StackXptr, Ctx.rsp.u32); /* ASSUMES SS.BASE == 0!! */
+
+ /* ring-3 */
+ if (!BS3_MODE_IS_RM_OR_V86(bTestMode))
+ Bs3RegCtxConvertToRingX(&Ctx, 3);
+
+ /* V8086: Set IOPL to 3. */
+ if (BS3_MODE_IS_V86(bTestMode))
+ {
+ Ctx.rflags.u32 |= X86_EFL_IOPL;
+ if (g_fVME)
+ {
+ Bs3RegSetTr(BS3_SEL_TSS32_IRB);
+#if 0
+ /* SDMv3b, 20.3.3 method 5: */
+ ASMBitClear(&Bs3SharedIntRedirBm, bIntGate);
+ bVmeMethod = 5;
+#else
+ /* SDMv3b, 20.3.3 method 4 (similar to non-VME): */
+ ASMBitSet(&Bs3SharedIntRedirBm, bIntGate);
+ bVmeMethod = 4;
+ }
+#endif
+ }
+
+ /*
+ * Test #0: Test run. Calc expected delayed #DB from it.
+ */
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ {
+ Bs3RegSetDr7(0);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ }
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapExpect);
+ if (TrapExpect.bXcpt != bIntGate)
+ {
+
+ Bs3TestFailedF("%u: bXcpt is %#x, expected %#x!\n", g_usBs3TestStep, TrapExpect.bXcpt, bIntGate);
+ Bs3TrapPrintFrame(&TrapExpect);
+ return 1;
+ }
+
+ cbIretFrameInt = TrapExpect.cbIretFrame;
+ cbIretFrameIntDb = cbIretFrameInt + cbIretFrameSame;
+ uHandlerRspInt = TrapExpect.uHandlerRsp;
+ uHandlerRspIntDb = uHandlerRspInt - cbSpAdjSame;
+
+ TrapExpect.Ctx.bCpl = 0;
+ TrapExpect.Ctx.cs = TrapExpect.uHandlerCs;
+ TrapExpect.Ctx.ss = TrapExpect.uHandlerSs;
+ TrapExpect.Ctx.rsp.u64 = TrapExpect.uHandlerRsp;
+ TrapExpect.Ctx.rflags.u64 = TrapExpect.fHandlerRfl;
+ if (BS3_MODE_IS_V86(bTestMode))
+ {
+ if (bVmeMethod >= 5)
+ {
+ TrapExpect.Ctx.rflags.u32 |= X86_EFL_VM;
+ TrapExpect.Ctx.bCpl = 3;
+ TrapExpect.Ctx.rip.u64 = bs3CpuWeird1_GetTrapHandlerEIP(bIntGate, bTestMode, true);
+ cbIretFrameIntDb = 36;
+ if (BS3_MODE_IS_16BIT_SYS(bTestMode))
+ uHandlerRspIntDb = Bs3Tss16.sp0 - cbIretFrameIntDb;
+ else
+ uHandlerRspIntDb = Bs3Tss32.esp0 - cbIretFrameIntDb;
+ }
+ else
+ {
+ TrapExpect.Ctx.ds = 0;
+ TrapExpect.Ctx.es = 0;
+ TrapExpect.Ctx.fs = 0;
+ TrapExpect.Ctx.gs = 0;
+ }
+ }
+
+ /*
+ * Test #1: Single stepping ring-3. Ignored except for V8086 w/ VME.
+ */
+ g_usBs3TestStep++;
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ {
+ Bs3RegSetDr7(0);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ }
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+ Ctx.rflags.u32 |= X86_EFL_TF;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if ( !BS3_MODE_IS_V86(bTestMode)
+ || bVmeMethod < 5)
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, bIntGate, offTestLabel + cbRingInstr, cbSpAdjust,
+ X86_DR6_INIT_VAL, cbIretFrameInt, uHandlerRspInt);
+ else
+ {
+ TrapExpect.Ctx.rflags.u32 |= X86_EFL_TF;
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &TrapExpect.Ctx, X86_XCPT_DB, offTestLabel, -2,
+ X86_DR6_INIT_VAL | X86_DR6_BS, cbIretFrameIntDb, uHandlerRspIntDb);
+ TrapExpect.Ctx.rflags.u32 &= ~X86_EFL_TF;
+ }
+
+ Ctx.rflags.u32 &= ~X86_EFL_TF;
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ {
+ uint32_t uDr6Expect;
+
+ /*
+ * Test #2: Execution breakpoint on ring transition instruction.
+ * This hits on AMD-V (threadripper) but not on VT-x (skylake).
+ */
+ g_usBs3TestStep++;
+ Bs3RegSetDr0(Bs3SelRealModeCodeToFlat(pfnTestLabel));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE));
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ Bs3RegSetDr7(0);
+ if (g_enmCpuVendor == BS3CPUVENDOR_AMD || g_enmCpuVendor == BS3CPUVENDOR_HYGON)
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, X86_XCPT_DB, offTestLabel, cbSpAdjust,
+ X86_DR6_INIT_VAL | X86_DR6_B0, cbIretFrameInt, uHandlerRspInt);
+ else
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, bIntGate, offTestLabel + cbRingInstr, cbSpAdjust,
+ X86_DR6_INIT_VAL, cbIretFrameInt, uHandlerRspInt);
+
+ /*
+ * Test #3: Same as above, but with the LE and GE flags set.
+ */
+ g_usBs3TestStep++;
+ Bs3RegSetDr0(Bs3SelRealModeCodeToFlat(pfnTestLabel));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE) | X86_DR7_LE | X86_DR7_GE);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (g_enmCpuVendor == BS3CPUVENDOR_AMD || g_enmCpuVendor == BS3CPUVENDOR_HYGON)
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, X86_XCPT_DB, offTestLabel, cbSpAdjust,
+ X86_DR6_INIT_VAL | X86_DR6_B0, cbIretFrameInt, uHandlerRspInt);
+ else
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, bIntGate, offTestLabel + cbRingInstr, cbSpAdjust,
+ X86_DR6_INIT_VAL, cbIretFrameInt, uHandlerRspInt);
+
+ /*
+ * Test #4: Execution breakpoint on pop ss / mov ss. Hits.
+ * Note! In real mode AMD-V updates the stack pointer, or something else is busted. Totally weird!
+ */
+ g_usBs3TestStep++;
+ Bs3RegSetDr0(Bs3SelRealModeCodeToFlat(pfnTestCode));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE));
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, X86_XCPT_DB, 0, 0, X86_DR6_INIT_VAL | X86_DR6_B0,
+ cbIretFrameInt,
+ uHandlerRspInt - (BS3_MODE_IS_RM_SYS(bTestMode) ? 2 : 0) );
+
+ /*
+ * Test #5: Same as above, but with the LE and GE flags set.
+ */
+ g_usBs3TestStep++;
+ Bs3RegSetDr0(Bs3SelRealModeCodeToFlat(pfnTestCode));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_EO) | X86_DR7_LEN(0, X86_DR7_LEN_BYTE) | X86_DR7_LE | X86_DR7_GE);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &Ctx, X86_XCPT_DB, 0, 0, X86_DR6_INIT_VAL | X86_DR6_B0,
+ cbIretFrameInt,
+ uHandlerRspInt - (BS3_MODE_IS_RM_SYS(bTestMode) ? 2 : 0) );
+
+ /*
+ * Test #6: Data breakpoint on SS load. The #DB is delivered after ring transition. Weird!
+ *
+ * Note! Intel loses the B0 status, probably for reasons similar to Pentium Pro errata 3. Similar
+ * erratum is seen with virtually every march since, e.g. skylake SKL009 & SKL111.
+ * Weirdly enougth, they seem to get this right in real mode. Go figure.
+ */
+ g_usBs3TestStep++;
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+ Bs3RegSetDr0(BS3_XPTR_GET_FLAT(uint32_t, StackXptr));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_RW) | X86_DR7_LEN(0, X86_DR7_LEN_WORD));
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ TrapExpect.Ctx.rip = TrapCtx.Ctx.rip; /// @todo fixme
+ Bs3RegSetDr7(0);
+ uDr6Expect = X86_DR6_INIT_VAL | X86_DR6_B0;
+ if (g_enmCpuVendor == BS3CPUVENDOR_INTEL && bTestMode != BS3_MODE_RM)
+ uDr6Expect = X86_DR6_INIT_VAL;
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &TrapExpect.Ctx, X86_XCPT_DB, 0, 0, uDr6Expect,
+ cbIretFrameSame, uHandlerRspIntDb);
+
+ /*
+ * Test #7: Same as above, but with the LE and GE flags set.
+ */
+ g_usBs3TestStep++;
+ *BS3_XPTR_GET(uint32_t, StackXptr) = Ctx.ss;
+ Bs3RegSetDr0(BS3_XPTR_GET_FLAT(uint32_t, StackXptr));
+ Bs3RegSetDr7(X86_DR7_L0 | X86_DR7_G0 | X86_DR7_RW(0, X86_DR7_RW_RW) | X86_DR7_LEN(0, X86_DR7_LEN_WORD) | X86_DR7_LE | X86_DR7_GE);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ TrapExpect.Ctx.rip = TrapCtx.Ctx.rip; /// @todo fixme
+ Bs3RegSetDr7(0);
+ uDr6Expect = X86_DR6_INIT_VAL | X86_DR6_B0;
+ if (g_enmCpuVendor == BS3CPUVENDOR_INTEL && bTestMode != BS3_MODE_RM)
+ uDr6Expect = X86_DR6_INIT_VAL;
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &TrapExpect.Ctx, X86_XCPT_DB, 0, 0, uDr6Expect,
+ cbIretFrameSame, uHandlerRspIntDb);
+
+ if (!BS3_MODE_IS_RM_OR_V86(bTestMode))
+ {
+ /*
+ * Test #8: Data breakpoint on SS GDT entry. Half weird!
+ * Note! Intel loses the B1 status, see test #6.
+ */
+ g_usBs3TestStep++;
+ *BS3_XPTR_GET(uint32_t, StackXptr) = (Ctx.ss & X86_SEL_RPL) | BS3_SEL_SPARE_00;
+ Bs3GdteSpare00 = Bs3Gdt[Ctx.ss / sizeof(Bs3Gdt[0])];
+
+ Bs3RegSetDr1(Bs3SelPtrToFlat(&Bs3GdteSpare00));
+ Bs3RegSetDr7(X86_DR7_L1 | X86_DR7_G1 | X86_DR7_RW(1, X86_DR7_RW_RW) | X86_DR7_LEN(1, X86_DR7_LEN_DWORD));
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ TrapExpect.Ctx.rip = TrapCtx.Ctx.rip; /// @todo fixme
+ Bs3RegSetDr7(0);
+ uDr6Expect = g_enmCpuVendor == BS3CPUVENDOR_INTEL ? X86_DR6_INIT_VAL : X86_DR6_INIT_VAL | X86_DR6_B1;
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &TrapExpect.Ctx, X86_XCPT_DB, 0, 0, uDr6Expect,
+ cbIretFrameSame, uHandlerRspIntDb);
+
+ /*
+ * Test #9: Same as above, but with the LE and GE flags set.
+ */
+ g_usBs3TestStep++;
+ *BS3_XPTR_GET(uint32_t, StackXptr) = (Ctx.ss & X86_SEL_RPL) | BS3_SEL_SPARE_00;
+ Bs3GdteSpare00 = Bs3Gdt[Ctx.ss / sizeof(Bs3Gdt[0])];
+
+ Bs3RegSetDr1(Bs3SelPtrToFlat(&Bs3GdteSpare00));
+ Bs3RegSetDr7(X86_DR7_L1 | X86_DR7_G1 | X86_DR7_RW(1, X86_DR7_RW_RW) | X86_DR7_LEN(1, X86_DR7_LEN_DWORD) | X86_DR7_LE | X86_DR7_GE);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ TrapExpect.Ctx.rip = TrapCtx.Ctx.rip; /// @todo fixme
+ Bs3RegSetDr7(0);
+ uDr6Expect = g_enmCpuVendor == BS3CPUVENDOR_INTEL ? X86_DR6_INIT_VAL : X86_DR6_INIT_VAL | X86_DR6_B1;
+ bs3CpuWeird1_CompareDbgInhibitRingXfer(&TrapCtx, &TrapExpect.Ctx, X86_XCPT_DB, 0, 0, uDr6Expect,
+ cbIretFrameSame, uHandlerRspIntDb);
+ }
+
+ /*
+ * Cleanup.
+ */
+ Bs3RegSetDr0(0);
+ Bs3RegSetDr1(0);
+ Bs3RegSetDr2(0);
+ Bs3RegSetDr3(0);
+ Bs3RegSetDr6(X86_DR6_INIT_VAL);
+ Bs3RegSetDr7(0);
+ }
+ Bs3TrapSetDpl(bIntGate, bSavedDpl);
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuWeird1_DbgInhibitRingXfer)(uint8_t bMode)
+{
+ if (BS3_MODE_IS_V86(bMode))
+ switch (bMode)
+ {
+ /** @todo some busted stack stuff with the 16-bit guys. Also, if VME is
+ * enabled, we're probably not able to do any sensible testing. */
+ case BS3_MODE_PP16_V86:
+ case BS3_MODE_PE16_V86:
+ case BS3_MODE_PAE16_V86:
+ return BS3TESTDOMODE_SKIPPED;
+ }
+ //if (bMode != BS3_MODE_PE16_V86) return BS3TESTDOMODE_SKIPPED;
+ //if (bMode != BS3_MODE_PAEV86) return BS3TESTDOMODE_SKIPPED;
+
+ bs3CpuWeird1_SetGlobals(bMode);
+
+ /** @todo test sysenter and syscall too. */
+ /** @todo test INTO. */
+ /** @todo test all V8086 software INT delivery modes (currently only 4 and 1). */
+
+ /* Note! Both ICEBP and BOUND has be checked cursorily and found not to be affected. */
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x80, 2, 2, bs3CpuWeird1_InhibitedInt80_c16, bs3CpuWeird1_InhibitedInt80_int80_c16);
+ if (!BS3_MODE_IS_V86(bMode) || !g_fVME)
+ {
+ /** @todo explain why these GURU */
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 2, 2, bs3CpuWeird1_InhibitedInt3_c16, bs3CpuWeird1_InhibitedInt3_int3_c16);
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 1, 2, bs3CpuWeird1_InhibitedBp_c16, bs3CpuWeird1_InhibitedBp_int3_c16);
+ }
+ }
+ else if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x80, 2, 4, bs3CpuWeird1_InhibitedInt80_c32, bs3CpuWeird1_InhibitedInt80_int80_c32);
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 2, 4, bs3CpuWeird1_InhibitedInt3_c32, bs3CpuWeird1_InhibitedInt3_int3_c32);
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 1, 4, bs3CpuWeird1_InhibitedBp_c32, bs3CpuWeird1_InhibitedBp_int3_c32);
+ }
+ else
+ {
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x80, 2, 0, bs3CpuWeird1_InhibitedInt80_c64, bs3CpuWeird1_InhibitedInt80_int80_c64);
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 2, 0, bs3CpuWeird1_InhibitedInt3_c64, bs3CpuWeird1_InhibitedInt3_int3_c64);
+ bs3CpuWeird1_DbgInhibitRingXfer_Worker(bMode, 0x03, 1, 0, bs3CpuWeird1_InhibitedBp_c64, bs3CpuWeird1_InhibitedBp_int3_c64);
+ }
+
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* IP / EIP Wrapping *
+*********************************************************************************************************************************/
+#define PROTO_ALL(a_Template) \
+ FNBS3FAR a_Template ## _c16, a_Template ## _c16_EndProc, \
+ a_Template ## _c32, a_Template ## _c32_EndProc, \
+ a_Template ## _c64, a_Template ## _c64_EndProc
+PROTO_ALL(bs3CpuWeird1_PcWrapBenign1);
+PROTO_ALL(bs3CpuWeird1_PcWrapBenign2);
+PROTO_ALL(bs3CpuWeird1_PcWrapCpuId);
+PROTO_ALL(bs3CpuWeird1_PcWrapIn80);
+PROTO_ALL(bs3CpuWeird1_PcWrapOut80);
+PROTO_ALL(bs3CpuWeird1_PcWrapSmsw);
+PROTO_ALL(bs3CpuWeird1_PcWrapRdCr0);
+PROTO_ALL(bs3CpuWeird1_PcWrapRdDr0);
+PROTO_ALL(bs3CpuWeird1_PcWrapWrDr0);
+#undef PROTO_ALL
+
+typedef enum { kPcWrapSetup_None, kPcWrapSetup_ZeroRax } PCWRAPSETUP;
+
+/**
+ * Compares pc wraparound result.
+ */
+static uint8_t bs3CpuWeird1_ComparePcWrap(PCBS3TRAPFRAME pTrapCtx, PCBS3TRAPFRAME pTrapExpect)
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ CHECK_MEMBER("bXcpt", "%#04x", pTrapCtx->bXcpt, pTrapExpect->bXcpt);
+ CHECK_MEMBER("bErrCd", "%#06RX64", pTrapCtx->uErrCd, pTrapExpect->uErrCd);
+ Bs3TestCheckRegCtxEx(&pTrapCtx->Ctx, &pTrapExpect->Ctx, 0 /*cbPcAdjust*/, 0 /*cbSpAdjust*/, 0 /*fExtraEfl*/,
+ g_pszTestMode, g_usBs3TestStep);
+ if (Bs3TestSubErrorCount() != cErrorsBefore)
+ {
+ Bs3TrapPrintFrame(pTrapCtx);
+ Bs3TestPrintf("CS=%04RX16 SS:ESP=%04RX16:%08RX64 EFL=%RX64 cbIret=%#x\n",
+ pTrapCtx->uHandlerCs, pTrapCtx->uHandlerSs, pTrapCtx->uHandlerRsp,
+ pTrapCtx->fHandlerRfl, pTrapCtx->cbIretFrame);
+#if 0
+ Bs3TestPrintf("Halting in ComparePcWrap: bXcpt=%#x\n", pTrapCtx->bXcpt);
+ ASMHalt();
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+
+static uint8_t bs3CpuWeird1_PcWrapping_Worker16(uint8_t bMode, RTSEL SelCode, uint8_t BS3_FAR *pbHead,
+ uint8_t BS3_FAR *pbTail, uint8_t BS3_FAR *pbAfter,
+ void const BS3_FAR *pvTemplate, size_t cbTemplate, PCWRAPSETUP enmSetup)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3TRAPFRAME TrapExpect;
+ BS3REGCTX Ctx;
+ uint8_t bXcpt;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&TrapExpect, sizeof(TrapExpect));
+
+ /*
+ * Create the expected result by first placing the code template
+ * at the start of the buffer and giving it a quick run.
+ *
+ * I cannot think of any instruction always causing #GP(0) right now, so
+ * we generate a ud2 and modify it instead.
+ */
+ Bs3MemCpy(pbHead, pvTemplate, cbTemplate);
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) <= BS3CPU_80286)
+ {
+ pbHead[cbTemplate] = 0xcc; /* int3 */
+ bXcpt = X86_XCPT_BP;
+ }
+ else
+ {
+ pbHead[cbTemplate] = 0x0f; /* ud2 */
+ pbHead[cbTemplate + 1] = 0x0b;
+ bXcpt = X86_XCPT_UD;
+ }
+
+ Bs3RegCtxSaveEx(&Ctx, bMode, 1024);
+
+ Ctx.cs = SelCode;
+ Ctx.rip.u = 0;
+ switch (enmSetup)
+ {
+ case kPcWrapSetup_None:
+ break;
+ case kPcWrapSetup_ZeroRax:
+ Ctx.rax.u = 0;
+ break;
+ }
+
+ /* V8086: Set IOPL to 3. */
+ if (BS3_MODE_IS_V86(bMode))
+ Ctx.rflags.u32 |= X86_EFL_IOPL;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapExpect);
+ if (TrapExpect.bXcpt != bXcpt)
+ {
+
+ Bs3TestFailedF("%u: Setup: bXcpt is %#x, expected %#x!\n", g_usBs3TestStep, TrapExpect.bXcpt, bXcpt);
+ Bs3TrapPrintFrame(&TrapExpect);
+ return 1;
+ }
+
+ /*
+ * Adjust the contexts for the real test.
+ */
+ Ctx.cs = SelCode;
+ Ctx.rip.u = (uint32_t)_64K - cbTemplate;
+
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) <= BS3CPU_80286)
+ TrapExpect.Ctx.rip.u = 1;
+ else
+ {
+ if (BS3_MODE_IS_16BIT_SYS(bMode))
+ TrapExpect.Ctx.rip.u = 0;
+ else
+ TrapExpect.Ctx.rip.u = UINT32_C(0x10000);
+ TrapExpect.bXcpt = X86_XCPT_GP;
+ TrapExpect.uErrCd = 0;
+ }
+
+ /*
+ * Prepare the buffer for 16-bit wrap around.
+ */
+ Bs3MemSet(pbHead, 0xcc, 64); /* int3 */
+ if (bXcpt == X86_XCPT_UD)
+ {
+ pbHead[0] = 0x0f; /* ud2 */
+ pbHead[1] = 0x0b;
+ }
+ Bs3MemCpy(&pbTail[_4K - cbTemplate], pvTemplate, cbTemplate);
+ Bs3MemSet(pbAfter, 0xf1, 64); /* icebp / int1 */
+
+ /*
+ * Do a test run.
+ */
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (!bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ {
+#if 0 /* needs more work */
+ /*
+ * Slide the instruction template across the boundrary byte-by-byte and
+ * check that it triggers #GP on the initial instruction on 386+.
+ */
+ unsigned cbTail;
+ unsigned cbHead;
+ g_usBs3TestStep++;
+ for (cbTail = cbTemplate - 1, cbHead = 1; cbTail > 0; cbTail--, cbHead++, g_usBs3TestStep++)
+ {
+ pbTail[X86_PAGE_SIZE - cbTail - 1] = 0xcc;
+ Bs3MemCpy(&pbTail[X86_PAGE_SIZE - cbTail], pvTemplate, cbTail);
+ Bs3MemCpy(pbHead, &((uint8_t const *)pvTemplate)[cbTail], cbHead);
+ if (bXcpt == X86_XCPT_BP)
+ pbHead[cbHead] = 0xcc; /* int3 */
+ else
+ {
+ pbHead[cbHead] = 0x0f; /* ud2 */
+ pbHead[cbHead + 1] = 0x0b;
+ }
+
+ Ctx.rip.u = (uint32_t)_64K - cbTail;
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) <= BS3CPU_80286)
+ TrapExpect.Ctx.rip.u = cbHead + 1;
+ else
+ {
+ TrapExpect.Ctx.rip.u = Ctx.rip.u;
+ }
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ return 1;
+ }
+#endif
+ }
+ return 0;
+}
+
+
+static uint8_t bs3CpuWeird1_PcWrapping_Worker32(uint8_t bMode, RTSEL SelCode, uint8_t BS3_FAR *pbPage1,
+ uint8_t BS3_FAR *pbPage2, uint32_t uFlatPage2,
+ void const BS3_FAR *pvTemplate, size_t cbTemplate, PCWRAPSETUP enmSetup)
+{
+ BS3TRAPFRAME TrapCtx;
+ BS3TRAPFRAME TrapExpect;
+ BS3REGCTX Ctx;
+ unsigned cbPage1;
+ unsigned cbPage2;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&TrapExpect, sizeof(TrapExpect));
+
+ //Bs3TestPrintf("SelCode=%#x pbPage1=%p pbPage2=%p uFlatPage2=%RX32 pvTemplate=%p cbTemplate\n",
+ // SelCode, pbPage1, pbPage2, uFlatPage2, pvTemplate, cbTemplate);
+
+ /*
+ * Create the expected result by first placing the code template
+ * at the start of the buffer and giving it a quick run.
+ */
+ Bs3MemSet(pbPage1, 0xcc, _4K);
+ Bs3MemSet(pbPage2, 0xcc, _4K);
+ Bs3MemCpy(&pbPage1[_4K - cbTemplate], pvTemplate, cbTemplate);
+ pbPage2[0] = 0x0f; /* ud2 */
+ pbPage2[1] = 0x0b;
+
+ Bs3RegCtxSaveEx(&Ctx, bMode, 1024);
+
+ Ctx.cs = BS3_SEL_R0_CS32;
+ Ctx.rip.u = uFlatPage2 - cbTemplate;
+ switch (enmSetup)
+ {
+ case kPcWrapSetup_None:
+ break;
+ case kPcWrapSetup_ZeroRax:
+ Ctx.rax.u = 0;
+ break;
+ }
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapExpect);
+ if (TrapExpect.bXcpt != X86_XCPT_UD)
+ {
+
+ Bs3TestFailedF("%u: Setup: bXcpt is %#x, expected %#x!\n", g_usBs3TestStep, TrapExpect.bXcpt, X86_XCPT_UD);
+ Bs3TrapPrintFrame(&TrapExpect);
+ return 1;
+ }
+
+ /*
+ * The real test uses the special CS selector.
+ */
+ Ctx.cs = SelCode;
+ TrapExpect.Ctx.cs = SelCode;
+
+ /*
+ * Unlike 16-bit mode, the instruction may cross the wraparound boundary,
+ * so we test by advancing the template across byte-by-byte.
+ */
+ for (cbPage1 = cbTemplate, cbPage2 = 0; cbPage1 > 0; cbPage1--, cbPage2++, g_usBs3TestStep++)
+ {
+ pbPage1[X86_PAGE_SIZE - cbPage1 - 1] = 0xcc;
+ Bs3MemCpy(&pbPage1[X86_PAGE_SIZE - cbPage1], pvTemplate, cbPage1);
+ Bs3MemCpy(pbPage2, &((uint8_t const *)pvTemplate)[cbPage1], cbPage2);
+ pbPage2[cbPage2] = 0x0f; /* ud2 */
+ pbPage2[cbPage2 + 1] = 0x0b;
+
+ Ctx.rip.u = UINT32_MAX - cbPage1 + 1;
+ TrapExpect.Ctx.rip.u = cbPage2;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ return 1;
+ }
+ return 0;
+}
+
+
+static uint8_t bs3CpuWeird1_PcWrapping_Worker64(uint8_t bMode, uint8_t BS3_FAR *pbBuf, uint32_t uFlatBuf,
+ void const BS3_FAR *pvTemplate, size_t cbTemplate, PCWRAPSETUP enmSetup)
+{
+ uint8_t BS3_FAR * const pbPage1 = pbBuf; /* mapped at 0, 4G and 8G */
+ uint8_t BS3_FAR * const pbPage2 = &pbBuf[X86_PAGE_SIZE]; /* mapped at -4K, 4G-4K and 8G-4K. */
+ BS3TRAPFRAME TrapCtx;
+ BS3TRAPFRAME TrapExpect;
+ BS3REGCTX Ctx;
+ unsigned cbStart;
+ unsigned cbEnd;
+
+ /* make sure they're allocated */
+ Bs3MemZero(&Ctx, sizeof(Ctx));
+ Bs3MemZero(&TrapCtx, sizeof(TrapCtx));
+ Bs3MemZero(&TrapExpect, sizeof(TrapExpect));
+
+ /*
+ * Create the expected result by first placing the code template
+ * at the start of the buffer and giving it a quick run.
+ */
+ Bs3MemCpy(pbPage1, pvTemplate, cbTemplate);
+ pbPage1[cbTemplate] = 0x0f; /* ud2 */
+ pbPage1[cbTemplate + 1] = 0x0b;
+
+ Bs3RegCtxSaveEx(&Ctx, bMode, 1024);
+
+ Ctx.rip.u = uFlatBuf;
+ switch (enmSetup)
+ {
+ case kPcWrapSetup_None:
+ break;
+ case kPcWrapSetup_ZeroRax:
+ Ctx.rax.u = 0;
+ break;
+ }
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapExpect);
+ if (TrapExpect.bXcpt != X86_XCPT_UD)
+ {
+
+ Bs3TestFailedF("%u: Setup: bXcpt is %#x, expected %#x!\n", g_usBs3TestStep, TrapExpect.bXcpt, X86_XCPT_UD);
+ Bs3TrapPrintFrame(&TrapExpect);
+ return 1;
+ }
+
+ /*
+ * Unlike 16-bit mode, the instruction may cross the wraparound boundary,
+ * so we test by advancing the template across byte-by-byte.
+ *
+ * Page #1 is mapped at address zero and Page #2 as the last one.
+ */
+ Bs3MemSet(pbBuf, 0xf1, X86_PAGE_SIZE * 2);
+ for (cbStart = cbTemplate, cbEnd = 0; cbStart > 0; cbStart--, cbEnd++)
+ {
+ pbPage2[X86_PAGE_SIZE - cbStart - 1] = 0xf1;
+ Bs3MemCpy(&pbPage2[X86_PAGE_SIZE - cbStart], pvTemplate, cbStart);
+ Bs3MemCpy(pbPage1, &((uint8_t const *)pvTemplate)[cbStart], cbEnd);
+ pbPage1[cbEnd] = 0x0f; /* ud2 */
+ pbPage1[cbEnd + 1] = 0x0b;
+
+ Ctx.rip.u = UINT64_MAX - cbStart + 1;
+ TrapExpect.Ctx.rip.u = cbEnd;
+
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ return 1;
+ g_usBs3TestStep++;
+
+ /* Also check that crossing 4G isn't buggered up in our code by
+ 32-bit and 16-bit mode support.*/
+ Ctx.rip.u = _4G - cbStart;
+ TrapExpect.Ctx.rip.u = _4G + cbEnd;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ return 1;
+ g_usBs3TestStep++;
+
+ Ctx.rip.u = _4G*2 - cbStart;
+ TrapExpect.Ctx.rip.u = _4G*2 + cbEnd;
+ Bs3TrapSetJmpAndRestore(&Ctx, &TrapCtx);
+ if (bs3CpuWeird1_ComparePcWrap(&TrapCtx, &TrapExpect))
+ return 1;
+ g_usBs3TestStep += 2;
+ }
+ return 0;
+}
+
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3CpuWeird1_PcWrapping)(uint8_t bMode)
+{
+ uint8_t bRet = 1;
+ size_t i;
+
+ bs3CpuWeird1_SetGlobals(bMode);
+
+ if (BS3_MODE_IS_16BIT_CODE(bMode))
+ {
+ /*
+ * For 16-bit testing, we need a 68 KB buffer.
+ *
+ * This is a little annoying to work with from 16-bit bit, so we use
+ * separate pointers to each interesting bit of it.
+ */
+ /** @todo add api for doing this, so we don't need to include bs3-cmn-memory.h. */
+ uint8_t BS3_FAR *pbBuf = (uint8_t BS3_FAR *)Bs3SlabAllocEx(&g_Bs3Mem4KLow.Core, 17 /*cPages*/, 0 /*fFlags*/);
+ if (pbBuf != NULL)
+ {
+ uint32_t const uFlatBuf = Bs3SelPtrToFlat(pbBuf);
+ uint8_t BS3_FAR *pbTail = Bs3XptrFlatToCurrent(uFlatBuf + 0x0f000);
+ uint8_t BS3_FAR *pbAfter = Bs3XptrFlatToCurrent(uFlatBuf + UINT32_C(0x10000));
+ RTSEL SelCode;
+ uint32_t off;
+ static struct { FPFNBS3FAR pfnStart, pfnEnd; PCWRAPSETUP enmSetup; unsigned fNoV86 : 1; }
+ const s_aTemplates16[] =
+ {
+#define ENTRY16(a_Template, a_enmSetup, a_fNoV86) { a_Template ## _c16, a_Template ## _c16_EndProc, a_enmSetup, a_fNoV86 }
+ ENTRY16(bs3CpuWeird1_PcWrapBenign1, kPcWrapSetup_None, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapBenign2, kPcWrapSetup_None, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapCpuId, kPcWrapSetup_ZeroRax, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapIn80, kPcWrapSetup_None, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapOut80, kPcWrapSetup_None, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapSmsw, kPcWrapSetup_None, 0),
+ ENTRY16(bs3CpuWeird1_PcWrapRdCr0, kPcWrapSetup_None, 1),
+ ENTRY16(bs3CpuWeird1_PcWrapRdDr0, kPcWrapSetup_None, 1),
+ ENTRY16(bs3CpuWeird1_PcWrapWrDr0, kPcWrapSetup_ZeroRax, 1),
+#undef ENTRY16
+ };
+
+ /* Fill the buffer with int1 instructions: */
+ for (off = 0; off < UINT32_C(0x11000); off += _4K)
+ {
+ uint8_t BS3_FAR *pbPage = Bs3XptrFlatToCurrent(uFlatBuf + off);
+ Bs3MemSet(pbPage, 0xf1, _4K);
+ }
+
+ /* Setup the CS for it. */
+ SelCode = (uint16_t)(uFlatBuf >> 4);
+ if (!BS3_MODE_IS_RM_OR_V86(bMode))
+ {
+ Bs3SelSetup16BitCode(&Bs3GdteSpare00, uFlatBuf, 0);
+ SelCode = BS3_SEL_SPARE_00;
+ }
+
+ /* Allow IN and OUT to port 80h from V8086 mode. */
+ if (BS3_MODE_IS_V86(bMode))
+ {
+ Bs3RegSetTr(BS3_SEL_TSS32_IOBP_IRB);
+ ASMBitClear(Bs3SharedIobp, 0x80);
+ }
+
+ for (i = 0; i < RT_ELEMENTS(s_aTemplates16); i++)
+ {
+ if (!s_aTemplates16[i].fNoV86 || !BS3_MODE_IS_V86(bMode))
+ bs3CpuWeird1_PcWrapping_Worker16(bMode, SelCode, pbBuf, pbTail, pbAfter, s_aTemplates16[i].pfnStart,
+ (uintptr_t)s_aTemplates16[i].pfnEnd - (uintptr_t)s_aTemplates16[i].pfnStart,
+ s_aTemplates16[i].enmSetup);
+ g_usBs3TestStep = i * 256;
+ }
+
+ if (BS3_MODE_IS_V86(bMode))
+ ASMBitSet(Bs3SharedIobp, 0x80);
+
+ Bs3SlabFree(&g_Bs3Mem4KLow.Core, uFlatBuf, 17);
+
+ bRet = 0;
+ }
+ else
+ Bs3TestFailed("Failed to allocate 17 pages (68KB)");
+ }
+ else
+ {
+ /*
+ * For 32-bit and 64-bit mode we just need two pages.
+ */
+ size_t const cbBuf = X86_PAGE_SIZE * 2;
+ uint8_t BS3_FAR *pbBuf = (uint8_t BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, cbBuf);
+ if (pbBuf)
+ {
+ uint32_t const uFlatBuf = Bs3SelPtrToFlat(pbBuf);
+ Bs3MemSet(pbBuf, 0xf1, cbBuf);
+
+ /*
+ * For 32-bit we set up a CS that starts with the 2nd page and
+ * ends with the first.
+ */
+ if (BS3_MODE_IS_32BIT_CODE(bMode))
+ {
+ static struct { FPFNBS3FAR pfnStart, pfnEnd; PCWRAPSETUP enmSetup; } const s_aTemplates32[] =
+ {
+#define ENTRY32(a_Template, a_enmSetup) { a_Template ## _c32, a_Template ## _c32_EndProc, a_enmSetup }
+ ENTRY32(bs3CpuWeird1_PcWrapBenign1, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapBenign2, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapCpuId, kPcWrapSetup_ZeroRax),
+ ENTRY32(bs3CpuWeird1_PcWrapIn80, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapOut80, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapSmsw, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapRdCr0, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapRdDr0, kPcWrapSetup_None),
+ ENTRY32(bs3CpuWeird1_PcWrapWrDr0, kPcWrapSetup_ZeroRax),
+#undef ENTRY32
+ };
+
+ Bs3SelSetup32BitCode(&Bs3GdteSpare00, uFlatBuf + X86_PAGE_SIZE, UINT32_MAX, 0);
+
+ for (i = 0; i < RT_ELEMENTS(s_aTemplates32); i++)
+ {
+ //Bs3TestPrintf("pfnStart=%p pfnEnd=%p\n", s_aTemplates32[i].pfnStart, s_aTemplates32[i].pfnEnd);
+ bs3CpuWeird1_PcWrapping_Worker32(bMode, BS3_SEL_SPARE_00, pbBuf, &pbBuf[X86_PAGE_SIZE],
+ uFlatBuf + X86_PAGE_SIZE, Bs3SelLnkPtrToCurPtr(s_aTemplates32[i].pfnStart),
+ (uintptr_t)s_aTemplates32[i].pfnEnd - (uintptr_t)s_aTemplates32[i].pfnStart,
+ s_aTemplates32[i].enmSetup);
+ g_usBs3TestStep = i * 256;
+ }
+
+ bRet = 0;
+ }
+ /*
+ * For 64-bit we have to alias the two buffer pages to the first and
+ * last page in the address space. To test that the 32-bit 4G rollover
+ * isn't incorrectly applied to LM64, we repeat this mappingfor the 4G
+ * and 8G boundaries too.
+ *
+ * This ASSUMES there is nothing important in page 0 when in LM64.
+ */
+ else
+ {
+ static const struct { uint64_t uDst; uint16_t off; } s_aMappings[] =
+ {
+ { UINT64_MAX - X86_PAGE_SIZE + 1, X86_PAGE_SIZE * 1 },
+ { UINT64_C(0), X86_PAGE_SIZE * 0 },
+#if 1 /* technically not required as we just repeat the same 4G address space in long mode: */
+ { _4G - X86_PAGE_SIZE, X86_PAGE_SIZE * 1 },
+ { _4G, X86_PAGE_SIZE * 0 },
+ { _4G*2 - X86_PAGE_SIZE, X86_PAGE_SIZE * 1 },
+ { _4G*2, X86_PAGE_SIZE * 0 },
+#endif
+ };
+ int rc = VINF_SUCCESS;
+ unsigned iMap;
+ BS3_ASSERT(bMode == BS3_MODE_LM64);
+ for (iMap = 0; iMap < RT_ELEMENTS(s_aMappings) && RT_SUCCESS(rc); iMap++)
+ {
+ rc = Bs3PagingAlias(s_aMappings[iMap].uDst, uFlatBuf + s_aMappings[iMap].off, X86_PAGE_SIZE,
+ X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
+ if (RT_FAILURE(rc))
+ Bs3TestFailedF("Bs3PagingAlias(%#RX64,...) failed: %d", s_aMappings[iMap].uDst, rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ static struct { FPFNBS3FAR pfnStart, pfnEnd; PCWRAPSETUP enmSetup; } const s_aTemplates64[] =
+ {
+#define ENTRY64(a_Template, a_enmSetup) { a_Template ## _c64, a_Template ## _c64_EndProc, a_enmSetup }
+ ENTRY64(bs3CpuWeird1_PcWrapBenign1, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapBenign2, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapCpuId, kPcWrapSetup_ZeroRax),
+ ENTRY64(bs3CpuWeird1_PcWrapIn80, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapOut80, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapSmsw, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapRdCr0, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapRdDr0, kPcWrapSetup_None),
+ ENTRY64(bs3CpuWeird1_PcWrapWrDr0, kPcWrapSetup_ZeroRax),
+#undef ENTRY64
+ };
+
+ for (i = 0; i < RT_ELEMENTS(s_aTemplates64); i++)
+ {
+ bs3CpuWeird1_PcWrapping_Worker64(bMode, pbBuf, uFlatBuf,
+ Bs3SelLnkPtrToCurPtr(s_aTemplates64[i].pfnStart),
+ (uintptr_t)s_aTemplates64[i].pfnEnd
+ - (uintptr_t)s_aTemplates64[i].pfnStart,
+ s_aTemplates64[i].enmSetup);
+ g_usBs3TestStep = i * 256;
+ }
+
+ bRet = 0;
+
+ Bs3PagingUnalias(UINT64_C(0), X86_PAGE_SIZE);
+ }
+
+ while (iMap-- > 0)
+ Bs3PagingUnalias(s_aMappings[iMap].uDst, X86_PAGE_SIZE);
+ }
+ Bs3MemFree(pbBuf, cbBuf);
+ }
+ else
+ Bs3TestFailed("Failed to allocate 2-3 pages for tests.");
+ }
+
+ return bRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1.c b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1.c
new file mode 100644
index 00000000..9eb55c1d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-weird-1.c
@@ -0,0 +1,76 @@
+/* $Id: bs3-cpu-weird-1.c $ */
+/** @file
+ * BS3Kit - bs3-cpu-weird-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+FNBS3TESTDOMODE bs3CpuWeird1_DbgInhibitRingXfer_f16;
+FNBS3TESTDOMODE bs3CpuWeird1_PcWrapping_f16;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEBYONEENTRY g_aModeByOneTests[] =
+{
+ { "dbg+inhibit+ringxfer", bs3CpuWeird1_DbgInhibitRingXfer_f16, 0 },
+ { "pc wrapping", bs3CpuWeird1_PcWrapping_f16, 0 },
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-cpu-weird-1");
+ Bs3TestPrintf("g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+
+ /*
+ * Do tests driven from 16-bit code.
+ */
+ Bs3TestDoModesByOne_rm(g_aModeByOneTests, RT_ELEMENTS(g_aModeByOneTests), 0);
+
+ Bs3TestTerm();
+ Bs3Shutdown();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-asm.asm b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-asm.asm
new file mode 100644
index 00000000..8575883a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-asm.asm
@@ -0,0 +1,172 @@
+; $Id: bs3-fpustate-1-asm.asm $
+;; @file
+; BS3Kit - bs3-fpustate-1, assembly helpers and template instantiation.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+;; @name Floating point constants.
+; @{
+g_r32_0dot1: dd 0.1
+g_r32_3dot2: dd 3.2
+g_r32_Zero: dd 0.0
+g_r32_One: dd 1.0
+g_r32_Two: dd 2.0
+g_r32_Three: dd 3.0
+g_r32_Ten: dd 10.0
+g_r32_Eleven: dd 11.0
+g_r32_ThirtyTwo:dd 32.0
+g_r32_Min: dd 000800000h
+g_r32_Max: dd 07f7fffffh
+g_r32_Inf: dd 07f800000h
+g_r32_SNaN: dd 07f800001h
+g_r32_SNaNMax: dd 07fbfffffh
+g_r32_QNaN: dd 07fc00000h
+g_r32_QNaNMax: dd 07fffffffh
+g_r32_NegQNaN: dd 0ffc00000h
+
+g_r64_0dot1: dq 0.1
+g_r64_6dot9: dq 6.9
+g_r64_Zero: dq 0.0
+g_r64_One: dq 1.0
+g_r64_Two: dq 2.0
+g_r64_Three: dq 3.0
+g_r64_Ten: dq 10.0
+g_r64_Eleven: dq 11.0
+g_r64_ThirtyTwo:dq 32.0
+g_r64_Min: dq 00010000000000000h
+g_r64_Max: dq 07fefffffffffffffh
+g_r64_Inf: dq 07ff0000000000000h
+g_r64_SNaN: dq 07ff0000000000001h
+g_r64_SNaNMax: dq 07ff7ffffffffffffh
+g_r64_NegQNaN: dq 0fff8000000000000h
+g_r64_QNaN: dq 07ff8000000000000h
+g_r64_QNaNMax: dq 07fffffffffffffffh
+g_r64_DnMin: dq 00000000000000001h
+g_r64_DnMax: dq 0000fffffffffffffh
+
+
+g_r80_0dot1: dt 0.1
+g_r80_3dot2: dt 3.2
+g_r80_Zero: dt 0.0
+g_r80_One: dt 1.0
+g_r80_Two: dt 2.0
+g_r80_Three: dt 3.0
+g_r80_Ten: dt 10.0
+g_r80_Eleven: dt 11.0
+g_r80_ThirtyTwo:dt 32.0
+%ifdef __NASM__
+g_r80_Min: dq 08000000000000000h
+ dw 00001h
+g_r80_Max: dq 0ffffffffffffffffh
+ dw 07ffeh
+g_r80_Inf: dq 08000000000000000h
+ dw 07fffh
+g_r80_QNaN: dq 0c000000000000000h
+ dw 07fffh
+g_r80_QNaNMax: dq 0ffffffffffffffffh
+ dw 07fffh
+g_r80_NegQNaN: dq 0c000000000000000h
+ dw 0ffffh
+g_r80_SNaN: dq 08000000000000001h
+ dw 07fffh
+g_r80_SNaNMax: dq 0bfffffffffffffffh
+ dw 07fffh
+g_r80_DnMin: dq 00000000000000001h
+ dw 00000h
+g_r80_DnMax: dq 07fffffffffffffffh
+ dw 00000h
+%else
+g_r80_Min: dt 000018000000000000000h
+g_r80_Max: dt 07ffeffffffffffffffffh
+g_r80_Inf: dt 07fff8000000000000000h
+g_r80_QNaN: dt 07fffc000000000000000h
+g_r80_QNaNMax: dt 07fffffffffffffffffffh
+g_r80_NegQNaN: dt 0ffffc000000000000000h
+g_r80_SNaN: dt 07fff8000000000000001h
+g_r80_SNaNMax: dt 07fffbfffffffffffffffh
+g_r80_DnMin: dt 000000000000000000001h
+g_r80_DnMax: dt 000007fffffffffffffffh
+%endif
+
+g_r32V1: dd 3.2
+g_r32V2: dd -1.9
+g_r64V1: dq 6.4
+g_r80V1: dt 8.0
+
+; Denormal numbers.
+g_r32D0: dd 000200000h
+;; @}
+
+;; @name Upconverted Floating point constants
+; @{
+;g_r80_r32_0dot1: dt 0.1
+%ifdef __NASM__
+g_r80_r32_3dot2: dq 0cccccd0000000000h
+ dw 04000h
+%else
+g_r80_r32_3dot2: dt 04000cccccd0000000000h
+%endif
+;g_r80_r32_Zero: dt 0.0
+;g_r80_r32_One: dt 1.0
+;g_r80_r32_Two: dt 2.0
+;g_r80_r32_Three: dt 3.0
+;g_r80_r32_Ten: dt 10.0
+;g_r80_r32_Eleven: dt 11.0
+;g_r80_r32_ThirtyTwo: dt 32.0
+;; @}
+
+;; @name Decimal constants.
+; @{
+g_u64Zero: dd 0
+g_u32Zero: dw 0
+g_u64Two: dd 2
+g_u32Two: dw 2
+;; @}
+
+
+;
+; Instantiate code templates.
+;
+BS3_INSTANTIATE_TEMPLATE_ESSENTIALS "bs3-fpustate-1-template.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.c b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.c
new file mode 100644
index 00000000..0604432a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.c
@@ -0,0 +1,409 @@
+/* $Id: bs3-fpustate-1-template.c $ */
+/** @file
+ * BS3Kit - bs3-fpustate-1, C code template.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <VBox/VMMDevTesting.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+#ifdef BS3_INSTANTIATING_CMN
+
+/**
+ * Displays the differences between the two states.
+ */
+# define bs3FpuState1_Diff BS3_CMN_NM(bs3FpuState1_Diff)
+BS3_DECL_NEAR(void) bs3FpuState1_Diff(X86FXSTATE const BS3_FAR *pExpected, X86FXSTATE const BS3_FAR *pChecking)
+{
+ unsigned i;
+
+# define CHECK(a_Member, a_Fmt) \
+ if (pExpected->a_Member != pChecking->a_Member) \
+ Bs3TestPrintf(" " #a_Member ": " a_Fmt ", expected " a_Fmt "\n", pChecking->a_Member, pExpected->a_Member); \
+ else do { } while (0)
+ CHECK(FCW, "%#RX16");
+ CHECK(FSW, "%#RX16");
+ CHECK(FTW, "%#RX16");
+ CHECK(FOP, "%#RX16");
+ CHECK(FPUIP, "%#RX32");
+ CHECK(CS, "%#RX16");
+ CHECK(Rsrvd1, "%#RX16");
+ CHECK(FPUDP, "%#RX32");
+ CHECK(DS, "%#RX16");
+ CHECK(Rsrvd2, "%#RX16");
+ CHECK(MXCSR, "%#RX32");
+ CHECK(MXCSR_MASK, "%#RX32");
+# undef CHECK
+ for (i = 0; i < RT_ELEMENTS(pExpected->aRegs); i++)
+ if ( pChecking->aRegs[i].au64[0] != pExpected->aRegs[i].au64[0]
+ || pChecking->aRegs[i].au64[1] != pExpected->aRegs[i].au64[1])
+ Bs3TestPrintf("st%u: %.16Rhxs\n"
+ "exp: %.16Rhxs\n",
+ i, &pChecking->aRegs[i], &pExpected->aRegs[i]);
+ for (i = 0; i < RT_ELEMENTS(pExpected->aXMM); i++)
+ if ( pChecking->aXMM[i].au64[0] != pExpected->aXMM[i].au64[0]
+ || pChecking->aXMM[i].au64[1] != pExpected->aXMM[i].au64[1])
+ Bs3TestPrintf("xmm%u: %.16Rhxs\n"
+ " %sexp: %.16Rhxs\n",
+ i, &pChecking->aRegs[i], &pExpected->aRegs[i], i >= 10 ? " " : "");
+}
+
+
+#endif /* BS3_INSTANTIATING_CMN */
+
+
+/*
+ * Mode specific code.
+ * Mode specific code.
+ * Mode specific code.
+ */
+#ifdef BS3_INSTANTIATING_MODE
+# if TMPL_MODE == BS3_MODE_PE32 \
+ || TMPL_MODE == BS3_MODE_PP32 \
+ || TMPL_MODE == BS3_MODE_PAE32 \
+ || TMPL_MODE == BS3_MODE_LM64 \
+ || TMPL_MODE == BS3_MODE_RM
+
+/* Assembly helpers: */
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_InitState)(X86FXSTATE BS3_FAR *pFxState, void BS3_FAR *pvMmioReg);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_Restore)(X86FXSTATE const BS3_FAR *pFxState);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_Save)(X86FXSTATE BS3_FAR *pFxState);
+
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_FNStEnv)(void BS3_FAR *pvMmioReg);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovDQU_Read)(void BS3_FAR *pvMmioReg, void BS3_FAR *pvResult);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovDQU_Write)(void BS3_FAR *pvMmioReg);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovUPS_Read)(void BS3_FAR *pvMmioReg, void BS3_FAR *pvResult);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovUPS_Write)(void BS3_FAR *pvMmioReg);
+BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_FMul)(void BS3_FAR *pvMmioReg, void BS3_FAR *pvNoResult);
+
+
+/**
+ * Checks if we're seeing a problem with fnstenv saving zero selectors when
+ * running on the compare area.
+ *
+ * This triggers in NEM mode if the native hypervisor doesn't do a good enough
+ * job at save the FPU state for 16-bit and 32-bit guests. We have heuristics
+ * in CPUMInternal.mac (SAVE_32_OR_64_FPU) for this.
+ *
+ * @returns true if this the zero selector issue.
+ * @param pabReadback The MMIO read buffer containing the fnstenv result
+ * typically produced by IEM.
+ * @param pabCompare The buffer containing the fnstenv result typcially
+ * produced by the CPU itself.
+ */
+static bool TMPL_NM(bs3FpuState1_IsZeroFnStEnvSelectorsProblem)(const uint8_t BS3_FAR *pabReadback,
+ const uint8_t BS3_FAR *pabCompare)
+{
+ unsigned const offCs = ARCH_BITS == 16 ? 8 : 16;
+ unsigned const offDs = ARCH_BITS == 16 ? 12 : 24;
+ if ( *(const uint16_t BS3_FAR *)&pabCompare[offCs] == 0
+ && *(const uint16_t BS3_FAR *)&pabCompare[offDs] == 0)
+ {
+ /* Check the stuff before the CS register: */
+ if (Bs3MemCmp(pabReadback, pabCompare, offCs) == 0)
+ {
+ /* Check the stuff between the DS and CS registers:*/
+ if (Bs3MemCmp(&pabReadback[offCs + 2], &pabCompare[offCs + 2], offDs - offCs - 2) == 0)
+ {
+#if ARCH_BITS != 16
+ /* Check the stuff after the DS register if 32-bit mode: */
+ if ( *(const uint16_t BS3_FAR *)&pabReadback[offDs + 2]
+ == *(const uint16_t BS3_FAR *)&pabCompare[offDs + 2])
+#endif
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Tests for FPU state corruption.
+ *
+ * First we don't do anything to quit guest context for a while.
+ * Then we start testing weird MMIO accesses, some which amonger other things
+ * forces the use of the FPU state or host FPU to do the emulation. Both are a
+ * little complicated in raw-mode and ring-0 contexts.
+ *
+ * We ASSUME FXSAVE/FXRSTOR support here.
+ */
+BS3_DECL_FAR(uint8_t) TMPL_NM(bs3FpuState1_Corruption)(uint8_t bMode)
+{
+ /* We don't need to test that many modes, probably. */
+
+ uint8_t abBuf[sizeof(X86FXSTATE)*2 + 32];
+ uint8_t BS3_FAR *pbTmp = &abBuf[0x10 - (((uintptr_t)abBuf) & 0x0f)];
+ X86FXSTATE BS3_FAR *pExpected = (X86FXSTATE BS3_FAR *)pbTmp;
+ X86FXSTATE BS3_FAR *pChecking = pExpected + 1;
+ uint32_t iLoop;
+ uint32_t uStartTick;
+ bool fMmioReadback;
+ bool fReadBackError = false;
+ bool fReadError = false;
+ uint32_t cFnStEnvSelectorsZero = 0;
+ BS3PTRUNION MmioReg;
+ BS3CPUVENDOR const enmCpuVendor = Bs3GetCpuVendor();
+ bool const fSkipStorIdt = Bs3TestQueryCfgBool(VMMDEV_TESTING_CFG_IS_NEM_LINUX);
+ bool const fMayHaveZeroStEnvSels = Bs3TestQueryCfgBool(VMMDEV_TESTING_CFG_IS_NEM_LINUX);
+ bool const fFastFxSaveRestore = RT_BOOL(ASMCpuId_EDX(0x80000001) & X86_CPUID_AMD_FEATURE_EDX_FFXSR);
+ //bool const fFdpXcptOnly = (ASMCpuIdEx_EBX(7, 0) & X86_CPUID_STEXT_FEATURE_EBX_FDP_EXCPTN_ONLY)
+ // && ASMCpuId_EAX(0) >= 7;
+ RT_NOREF(bMode);
+
+ if (fSkipStorIdt)
+ Bs3TestPrintf("NEM/linux - skipping SIDT\n");
+
+# undef CHECK_STATE
+# define CHECK_STATE(a_Instr, a_fIsFnStEnv) \
+ do { \
+ TMPL_NM(bs3FpuState1_Save)(pChecking); \
+ if (Bs3MemCmp(pExpected, pChecking, sizeof(*pExpected)) != 0) \
+ { \
+ Bs3TestFailedF("State differs after " #a_Instr " (write) in loop #%RU32\n", iLoop); \
+ bs3FpuState1_Diff(pExpected, pChecking); \
+ Bs3PitDisable(); \
+ return 1; \
+ } \
+ } while (0)
+
+
+ /* Make this code executable in raw-mode. A bit tricky. */
+ ASMSetCR0(ASMGetCR0() | X86_CR0_WP);
+ Bs3PitSetupAndEnablePeriodTimer(20);
+ ASMIntEnable();
+# if ARCH_BITS != 64
+ ASMHalt();
+# endif
+
+ /* Figure out which MMIO region we'll be using so we can correctly initialize FPUDS. */
+# if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ MmioReg.pv = BS3_FP_MAKE(VMMDEV_TESTING_MMIO_RM_SEL, VMMDEV_TESTING_MMIO_RM_OFF2(0));
+# elif BS3_MODE_IS_16BIT_CODE(TMPL_MODE)
+ MmioReg.pv = BS3_FP_MAKE(BS3_SEL_VMMDEV_MMIO16, 0);
+# else
+ MmioReg.pv = (uint8_t *)VMMDEV_TESTING_MMIO_BASE;
+# endif
+ if (MmioReg.pu32[VMMDEV_TESTING_MMIO_OFF_NOP / sizeof(uint32_t)] == VMMDEV_TESTING_NOP_RET)
+ {
+ fMmioReadback = true;
+ MmioReg.pb += VMMDEV_TESTING_MMIO_OFF_READBACK;
+ }
+ else
+ {
+ Bs3TestPrintf("VMMDev MMIO not found, using VGA instead\n");
+ fMmioReadback = false;
+ MmioReg.pv = Bs3XptrFlatToCurrent(0xa7800);
+ }
+
+ /* Make 100% sure we don't trap accessing the FPU state and that we can use fxsave/fxrstor. */
+ g_usBs3TestStep = 1;
+ ASMSetCR0((ASMGetCR0() & ~(X86_CR0_TS | X86_CR0_EM)) | X86_CR0_MP);
+ ASMSetCR4(ASMGetCR4() | X86_CR4_OSFXSR /*| X86_CR4_OSXMMEEXCPT*/);
+
+ /* Come up with a distinct state. We do that from assembly (will do FPU in R0/RC). */
+ g_usBs3TestStep = 2;
+ Bs3MemSet(abBuf, 0x42, sizeof(abBuf));
+ TMPL_NM(bs3FpuState1_InitState)(pExpected, MmioReg.pb);
+
+
+ /*
+ * Test #1: Check that we can keep it consistent for a while.
+ */
+ g_usBs3TestStep = 3;
+ uStartTick = g_cBs3PitTicks;
+ for (iLoop = 0; iLoop < _16M; iLoop++)
+ {
+ CHECK_STATE(nop, false);
+ if ( (iLoop & 0xffff) == 0xffff
+ && g_cBs3PitTicks - uStartTick >= 20 * 20) /* 20 seconds*/
+ break;
+ }
+
+ /*
+ * Test #2: Use various FPU, SSE and weird instructions to do MMIO writes.
+ *
+ * We'll use the VMMDev readback register if possible, but make do
+ * with VGA if not configured.
+ */
+# ifdef __WATCOMC__
+# pragma DISABLE_MESSAGE(201) /* Warning! W201: Unreachable code */
+# endif
+ g_usBs3TestStep = 4;
+ uStartTick = g_cBs3PitTicks;
+ for (iLoop = 0; iLoop < _1M; iLoop++)
+ {
+ unsigned off;
+ uint8_t abCompare[64];
+ uint8_t abReadback[64];
+
+ /* Macros */
+# undef CHECK_READBACK_WRITE_RUN
+# define CHECK_READBACK_WRITE_RUN(a_Instr, a_Worker, a_Type, a_fIsFnStEnv) \
+ do { \
+ off = (unsigned)(iLoop & (VMMDEV_TESTING_READBACK_SIZE / 2 - 1)); \
+ if (off + sizeof(a_Type) > VMMDEV_TESTING_READBACK_SIZE) \
+ off = VMMDEV_TESTING_READBACK_SIZE - sizeof(a_Type); \
+ a_Worker((a_Type *)&MmioReg.pb[off]); \
+ if (fMmioReadback && (!fReadBackError || iLoop == 0)) \
+ { \
+ a_Worker((a_Type *)&abCompare[0]); \
+ Bs3MemCpy(abReadback, &MmioReg.pb[off], sizeof(a_Type)); \
+ if (Bs3MemCmp(abReadback, abCompare, sizeof(a_Type)) == 0) \
+ { /* likely */ } \
+ else if ( (a_fIsFnStEnv) \
+ && fMayHaveZeroStEnvSels \
+ && TMPL_NM(bs3FpuState1_IsZeroFnStEnvSelectorsProblem)(abReadback, abCompare)) \
+ cFnStEnvSelectorsZero += 1; \
+ else \
+ { \
+ Bs3TestFailedF("Read back error for " #a_Instr " in loop #%RU32:\n%.*Rhxs expected:\n%.*Rhxs\n", \
+ iLoop, sizeof(a_Type), abReadback, sizeof(a_Type), abCompare); \
+ fReadBackError = true; \
+ } \
+ } \
+ } while (0)
+
+# undef CHECK_READBACK_WRITE
+# define CHECK_READBACK_WRITE(a_Instr, a_Worker, a_Type, a_fIsFnStEnv) \
+ CHECK_READBACK_WRITE_RUN(a_Instr, a_Worker, a_Type, a_fIsFnStEnv); \
+ CHECK_STATE(a_Instr, a_fIsFnStEnv)
+# undef CHECK_READBACK_WRITE_Z
+# define CHECK_READBACK_WRITE_Z(a_Instr, a_Worker, a_Type, a_fIsFnStEnv) \
+ do { \
+ if (fMmioReadback && (!fReadBackError || iLoop == 0)) \
+ { \
+ Bs3MemZero(&abCompare[0], sizeof(a_Type)); \
+ off = (unsigned)(iLoop & (VMMDEV_TESTING_READBACK_SIZE / 2 - 1)); \
+ if (off + sizeof(a_Type) > VMMDEV_TESTING_READBACK_SIZE) \
+ off = VMMDEV_TESTING_READBACK_SIZE - sizeof(a_Type); \
+ Bs3MemZero(&MmioReg.pb[off], sizeof(a_Type)); \
+ } \
+ CHECK_READBACK_WRITE(a_Instr, a_Worker, a_Type, a_fIsFnStEnv); \
+ } while (0)
+
+# undef CHECK_READBACK_READ_RUN
+#define CHECK_READBACK_READ_RUN(a_Instr, a_Worker, a_Type) \
+ do { \
+ off = (unsigned)(iLoop & (VMMDEV_TESTING_READBACK_SIZE / 2 - 1)); \
+ if (off + sizeof(a_Type) > VMMDEV_TESTING_READBACK_SIZE) \
+ off = VMMDEV_TESTING_READBACK_SIZE - sizeof(a_Type); \
+ a_Worker((a_Type *)&MmioReg.pb[off], (a_Type *)&abReadback[0]); \
+ TMPL_NM(bs3FpuState1_Save)(pChecking); \
+ } while (0)
+# undef CHECK_READBACK_READ
+# define CHECK_READBACK_READ(a_Instr, a_Worker, a_Type) \
+ do { \
+ Bs3MemSet(&abReadback[0], 0xcc, sizeof(abReadback)); \
+ CHECK_READBACK_READ_RUN(a_Instr, a_Worker, a_Type); \
+ CHECK_STATE(a_Instr, false); \
+ if (!fReadError || iLoop == 0) \
+ { \
+ Bs3MemZero(&abCompare[0], sizeof(abCompare)); \
+ Bs3MemCpy(&abCompare[0], &MmioReg.pb[off], sizeof(a_Type)); \
+ if (Bs3MemCmp(abReadback, abCompare, sizeof(a_Type)) != 0) \
+ { \
+ Bs3TestFailedF("Read result check for " #a_Instr " in loop #%RU32:\n%.*Rhxs expected:\n%.*Rhxs\n", \
+ iLoop, sizeof(a_Type), abReadback, sizeof(a_Type), abCompare); \
+ fReadError = true; \
+ } \
+ } \
+ } while (0)
+
+ /* The tests. */
+ if (!fSkipStorIdt) /* KVM doesn't advance RIP executing a SIDT [MMIO-memory], it seems. (Linux 5.13.1) */
+ CHECK_READBACK_WRITE_Z(SIDT, ASMGetIDTR, RTIDTR, false);
+ CHECK_READBACK_WRITE_Z(FNSTENV, TMPL_NM(bs3FpuState1_FNStEnv), X86FSTENV32P, true); /** @todo x86.h is missing types */
+ CHECK_READBACK_WRITE( MOVDQU, TMPL_NM(bs3FpuState1_MovDQU_Write), X86XMMREG, false);
+ CHECK_READBACK_READ( MOVDQU, TMPL_NM(bs3FpuState1_MovDQU_Read), X86XMMREG);
+ CHECK_READBACK_WRITE( MOVUPS, TMPL_NM(bs3FpuState1_MovUPS_Write), X86XMMREG, false);
+ CHECK_READBACK_READ( MOVUPS, TMPL_NM(bs3FpuState1_MovUPS_Read), X86XMMREG);
+
+ /* Using the FPU is a little complicated, but we really need to check these things. */
+ CHECK_READBACK_READ_RUN(FMUL, TMPL_NM(bs3FpuState1_FMul), uint64_t);
+ if (enmCpuVendor == BS3CPUVENDOR_INTEL)
+# if BS3_MODE_IS_16BIT_CODE(TMPL_MODE)
+ pExpected->FOP = 0x040f; // skylake 6700k
+# else
+ pExpected->FOP = 0x040b; // skylake 6700k
+# endif
+ else if (enmCpuVendor == BS3CPUVENDOR_AMD && fFastFxSaveRestore)
+ pExpected->FOP = 0x0000; // Zen2 (3990x)
+ else
+ pExpected->FOP = 0x07dc; // dunno where we got this.
+# if ARCH_BITS == 64
+ pExpected->FPUDP = (uint32_t) (uintptr_t)&MmioReg.pb[off];
+ pExpected->DS = (uint16_t)((uintptr_t)&MmioReg.pb[off] >> 32);
+ pExpected->Rsrvd2 = (uint16_t)((uintptr_t)&MmioReg.pb[off] >> 48);
+# elif BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ pExpected->FPUDP = Bs3SelPtrToFlat(&MmioReg.pb[off]);
+# else
+ pExpected->FPUDP = BS3_FP_OFF(&MmioReg.pb[off]);
+# endif
+ if (enmCpuVendor == BS3CPUVENDOR_AMD && fFastFxSaveRestore)
+ pExpected->FPUDP = 0; // Zen2 (3990x)
+ CHECK_STATE(FMUL, false);
+
+ /* check for timeout every now an then. */
+ if ( (iLoop & 0xfff) == 0xfff
+ && g_cBs3PitTicks - uStartTick >= 20 * 20) /* 20 seconds*/
+ break;
+ }
+
+ Bs3PitDisable();
+
+# ifdef __WATCOMC__
+# pragma ENABLE_MESSAGE(201) /* Warning! W201: Unreachable code */
+# endif
+
+ /*
+ * Warn if selectors are borked (for real VBox we'll fail and not warn).
+ */
+ if (cFnStEnvSelectorsZero > 0)
+ Bs3TestPrintf("Warning! NEM borked the FPU selectors %u times.\n", cFnStEnvSelectorsZero);
+ return 0;
+}
+# endif
+#endif /* BS3_INSTANTIATING_MODE */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.mac b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.mac
new file mode 100644
index 00000000..eafa95b5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1-template.mac
@@ -0,0 +1,418 @@
+; $Id: bs3-fpustate-1-template.mac $
+;; @file
+; BS3Kit - bs3-fpustate-1, assembly template.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac" ; setup environment
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+
+
+;;
+; Initializes the FPU state and saves it to pFxState.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_InitState)(X86FXSTATE BS3_FAR *pFxState, void *pvMmioReg);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_InitState, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+ pushf
+TONLY64 sub xSP, 20h
+
+ ;
+ ; x87 state.
+ ;
+ fninit
+ fld dword [TMPL_DATA16_WRT(g_r32V1)]
+ fld qword [TMPL_DATA16_WRT(g_r64V1)]
+ fld tword [TMPL_DATA16_WRT(g_r80V1)]
+ fld qword [TMPL_DATA16_WRT(g_r64V1)]
+ fld dword [TMPL_DATA16_WRT(g_r32V2)]
+ fld dword [TMPL_DATA16_WRT(g_r80_QNaNMax)]
+ fld tword [TMPL_DATA16_WRT(g_r80_SNaNMax)]
+ fld tword [TMPL_DATA16_WRT(g_r80_ThirtyTwo)]
+
+ ;
+ ; We'll later be using FMUL to test actually using the FPU in RC & R0,
+ ; so for everything to line up correctly with FPU CS:IP and FPU DS:DP,
+ ; we'll call the function here too. This has the benefitial side effect
+ ; of loading correct FPU DS/DS values so we can check that they don't
+ ; get lost either. Also, we now don't have to guess whether the CPU
+ ; emulation sets CS/DS or not.
+ ;
+TONLY16 push xPRE [xBP + xCB + cbCurRetAddr + sCB + 2]
+ push xPRE [xBP + xCB + cbCurRetAddr + sCB]
+ BS3_CALL TMPL_NM(bs3FpuState1_FMul), 1
+ add xSP, sCB
+
+ ;
+ ; SSE state
+ ;
+ movdqu xmm0, [TMPL_DATA16_WRT(g_r32_0dot1)]
+ movdqu xmm1, [TMPL_DATA16_WRT(g_r32_Two)]
+ movdqu xmm2, [TMPL_DATA16_WRT(g_r32_ThirtyTwo)]
+ movdqu xmm3, [TMPL_DATA16_WRT(g_r32_SNaN)]
+ movdqu xmm4, [TMPL_DATA16_WRT(g_r80_ThirtyTwo)]
+ movdqu xmm5, [TMPL_DATA16_WRT(g_r32_NegQNaN)]
+ movdqu xmm6, [TMPL_DATA16_WRT(g_r64_Zero)]
+ movdqu xmm7, [TMPL_DATA16_WRT(g_r64_Two)]
+%if TMPL_BITS == 64
+ movdqu xmm8, [TMPL_DATA16_WRT(g_r64_Ten)]
+ movdqu xmm9, [TMPL_DATA16_WRT(g_r64_ThirtyTwo)]
+ movdqu xmm10, [TMPL_DATA16_WRT(g_r64_Max)]
+ movdqu xmm11, [TMPL_DATA16_WRT(g_r64_SNaN)]
+ movdqu xmm12, [TMPL_DATA16_WRT(g_r64_NegQNaN)]
+ movdqu xmm13, [TMPL_DATA16_WRT(g_r64_QNaNMax)]
+ movdqu xmm14, [TMPL_DATA16_WRT(g_r64_DnMax)]
+ movdqu xmm15, [TMPL_DATA16_WRT(g_r80_Eleven)]
+%endif
+
+ ;; @todo status regs
+
+ ;
+ ; Save it. Note that DS is no longer valid in 16-bit code.
+ ; To be on the safe side, we load and save the state once again.
+ ;
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+ cli
+%if TMPL_BITS == 64
+ o64 fxsave [xBX]
+ fninit
+ o64 fxrstor [xBX]
+ o64 fxsave [xBX]
+%else
+ fxsave [xBX]
+ fninit
+ fxrstor [xBX]
+ fxsave [xBX]
+%endif
+
+.return:
+TONLY64 add xSP, 20h
+ popf
+TONLY16 pop ds
+ pop xBX
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_InitState
+
+
+;;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_Restore)(X86FXSTATE const BS3_FAR *pFxState);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_Restore, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 64
+ o64 fxrstor [rcx]
+
+%elif TMPL_BITS == 32
+ mov eax, [xBP + xCB*2]
+ fxrstor [eax]
+
+%elif TMPL_BITS == 16
+ mov ax, ds
+ mov ds, [xBP + xCB + cbCurRetAddr + 2]
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+ fxrstor [bx]
+ mov ds, ax
+%else
+ %error TMPL_BITS
+%endif
+
+ mov xSP, xBP
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_Restore
+
+;;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_Save)(X86FXSTATE BS3_FAR *pFxState);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_Save, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 64
+ o64 fxsave [rcx]
+
+%elif TMPL_BITS == 32
+ mov eax, [xBP + xCB*2]
+ fxsave [eax]
+
+%elif TMPL_BITS == 16
+ push bx
+ push ds
+ mov ds, [xBP + xCB + cbCurRetAddr + 2]
+ mov bx, [xBP + xCB + cbCurRetAddr]
+ fxsave [bx]
+ pop ds
+ pop bx
+%else
+ %error TMPL_BITS
+%endif
+
+ mov xSP, xBP
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_Save
+
+
+;;
+; Performs a MOVDQU write on the specified memory.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovDQU_Write)(void *pvMmioReg);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_MovDQU_Write, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Do read.
+ movdqu [xBX], xmm0
+
+TONLY16 pop ds
+ pop xBX
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_MovDQU_Write
+
+
+;;
+; Performs a MOVDQU write to the specified memory.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovDQU_Read)(void *pvMmioReg);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_MovDQU_Read, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+ sub xSP, 20h
+%if TMPL_BITS == 16
+ movdqu [xBP - xCB - xCB - 2 - 18h], xmm2
+%else
+ movdqu [xSP], xmm2
+%endif
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+
+ ; Do read.
+ movdqu xmm2, [xBX]
+
+ ; Save the result.
+ mov xBX, [xBP + xCB + cbCurRetAddr + sCB]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + sCB + 2]
+ movups [xBX], xmm2
+
+%if TMPL_BITS == 16
+ movdqu xmm2, [xBP - xCB - xCB - 2 - 18h]
+%else
+ movdqu xmm2, [xSP]
+%endif
+ add xSP, 20h
+TONLY16 pop ds
+ pop xBX
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_MovDQU_Read
+
+
+;;
+; Performs a MOVUPS write on the specified memory.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovUPS_Write)(void *pvMmioReg);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_MovUPS_Write, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Do read.
+ movups [xBX], xmm3
+
+TONLY16 pop ds
+ pop xBX
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_MovUPS_Write
+
+
+;;
+; Performs a MOVUPS write to the specified memory.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_MovUPS_Read)(void *pvMmioReg, void *pvResult);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_MovUPS_Read, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+ sub xSP, 20h
+%if TMPL_BITS == 16
+ movups [xBP - xCB - xCB - 2 - 18h], xmm1
+%else
+ movups [xSP], xmm1
+%endif
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+
+ ; Do read.
+ movups xmm1, [xBX]
+
+ ; Save the result.
+ mov xBX, [xBP + xCB + cbCurRetAddr + sCB]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + sCB + 2]
+ movups [xBX], xmm1
+
+%if TMPL_BITS == 16
+ movups xmm1, [xBP - xCB - xCB - 2 - 18h]
+%else
+ movups xmm1, [xSP]
+%endif
+ add xSP, 20h
+TONLY16 pop ds
+ pop xBX
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_MovUPS_Read
+
+
+;;
+; Performs a FNSTENV write on the specified memory.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_FNStEnv)(void *pvMmioReg);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_FNStEnv, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Just write.
+ fnstenv [xBX]
+
+TONLY16 pop ds
+ pop xBX
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE bs3FpuState1_FNStEnv
+
+
+;;
+; Performs a FMUL on the specified memory, after writing a 64-bit value to it first.
+;
+; BS3_DECL_NEAR(void) TMPL_NM(bs3FpuState1_FMul)(void *pvMmioReg, void *pvResultIgnored);
+;
+BS3_PROC_BEGIN_MODE bs3FpuState1_FMul, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xBX
+TONLY16 push ds
+
+ ; Load the value we'll be multiplying with into register(s) while ds is DATA16.
+ mov sAX, [TMPL_DATA16_WRT(g_r64_One)]
+TNOT64 mov edx, [4 + TMPL_DATA16_WRT(g_r64_One)]
+
+ ; Load the register pointer.
+ mov xBX, [xBP + xCB + cbCurRetAddr]
+TONLY16 mov ds, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Just write.
+ mov [xBX], sAX
+TNOT64 mov [xBX + 4], edx
+ call .do_it
+
+TONLY16 pop ds
+ pop xBX
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+.do_it:
+ fmul qword [xBX]
+ ret
+BS3_PROC_END_MODE bs3FpuState1_FMul
+
+
+%include "bs3kit-template-footer.mac" ; reset environment
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1.c b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1.c
new file mode 100644
index 00000000..2548544b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-fpustate-1.c
@@ -0,0 +1,94 @@
+/* $Id: bs3-fpustate-1.c $ */
+/** @file
+ * BS3Kit - bs3-fpustate-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3TESTMODE_PROTOTYPES_MODE(bs3FpuState1_Corruption);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEENTRY g_aModeTest[] =
+{
+ {
+ /*pszSubTest =*/ "corruption",
+ /*RM*/ bs3FpuState1_Corruption_rm,
+ /*PE16*/ NULL, //bs3FpuState1_Corruption_pe16,
+ /*PE16_32*/ NULL, //bs3FpuState1_Corruption_pe16_32,
+ /*PE16_V86*/ NULL, //bs3FpuState1_Corruption_pe16_v86,
+ /*PE32*/ bs3FpuState1_Corruption_pe32,
+ /*PE32_16*/ NULL, //bs3FpuState1_Corruption_pe32_16,
+ /*PEV86*/ NULL, //bs3FpuState1_Corruption_pev86,
+ /*PP16*/ NULL, //bs3FpuState1_Corruption_pp16,
+ /*PP16_32*/ NULL, //bs3FpuState1_Corruption_pp16_32,
+ /*PP16_V86*/ NULL, //bs3FpuState1_Corruption_pp16_v86,
+ /*PP32*/ bs3FpuState1_Corruption_pp32,
+ /*PP32_16*/ NULL, //bs3FpuState1_Corruption_pp32_16,
+ /*PPV86*/ NULL, //bs3FpuState1_Corruption_ppv86,
+ /*PAE16*/ NULL, //bs3FpuState1_Corruption_pae16,
+ /*PAE16_32*/ NULL, //bs3FpuState1_Corruption_pae16_32,
+ /*PAE16_V86*/ NULL, //bs3FpuState1_Corruption_pae16_v86,
+ /*PAE32*/ bs3FpuState1_Corruption_pae32,
+ /*PAE32_16*/ NULL, //bs3FpuState1_Corruption_pae32_16,
+ /*PAEV86*/ NULL, //bs3FpuState1_Corruption_paev86,
+ /*LM16*/ NULL, //bs3FpuState1_Corruption_lm16,
+ /*LM32*/ NULL, //bs3FpuState1_Corruption_lm32,
+ /*LM64*/ bs3FpuState1_Corruption_lm64,
+ }
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-fpustate-1");
+ Bs3TestPrintf("g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected);
+
+ Bs3TestDoModes_rm(g_aModeTest, RT_ELEMENTS(g_aModeTest));
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-locking-1.c b/src/VBox/ValidationKit/bootsectors/bs3-locking-1.c
new file mode 100644
index 00000000..111d2971
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-locking-1.c
@@ -0,0 +1,246 @@
+/* $Id: bs3-locking-1.c $ */
+/** @file
+ * BS3Kit - bs3-locking-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+#include <VBox/VMMDevTesting.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static struct
+{
+ const char * BS3_FAR pszName;
+ uint32_t cInnerLoops;
+ uint32_t uCtrlLo;
+ uint32_t uCtrlHi;
+} g_aLockingTests[] =
+{
+#if 1
+# if 1 /* no contention benchmark */
+ {
+ "None 0us/inf/0k",
+ _32K,
+ 0,
+ 0,
+ },
+ {
+ "RW None Exl 0us/inf/0k",
+ _32K,
+ 0,
+ 0 | VMMDEV_TESTING_LOCKED_HI_TYPE_RW,
+ },
+# endif
+ {
+ "RW None Shr 0us/inf/0k",
+ _32K,
+ 0,
+ 0 | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_EMT_SHARED,
+ },
+# if 1
+ {
+ "Contention 500us/250us/64k",
+ 2000 + 16384,
+ 500 | (UINT32_C(250) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 64 | VMMDEV_TESTING_LOCKED_HI_ENABLED,
+ },
+ {
+ "Contention 100us/50us/8k",
+ 10000 + 4096,
+ 100 | (UINT32_C(50) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 8 | VMMDEV_TESTING_LOCKED_HI_ENABLED,
+ },
+ {
+ "Contention 10us/1us/0k",
+ 16384 + 4096,
+ 10 | (UINT32_C(1) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 0 | VMMDEV_TESTING_LOCKED_HI_ENABLED,
+ },
+ {
+ "Contention 500us/250us/64k poke",
+ 2000 + 16384,
+ 500 | (UINT32_C(250) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 64 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_POKE,
+ },
+ {
+ "Contention 100us/50us/1k poke",
+ 10000 + 4096,
+ 100 | (UINT32_C(50) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 1 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_POKE,
+ },
+ {
+ "Contention 500us/250us/64k poke void",
+ 2000 + 16384,
+ 500 | (UINT32_C(250) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 64 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_POKE | VMMDEV_TESTING_LOCKED_HI_BUSY_SUCCESS
+ },
+ {
+ "Contention 50us/25us/8k poke void",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 1 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_POKE | VMMDEV_TESTING_LOCKED_HI_BUSY_SUCCESS
+ },
+# endif
+# if 1
+ {
+ "RW Contention Exl/Exl 50us/25us/16k",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW
+ },
+# endif
+ {
+ "RW Contention Shr/Exl 50us/25us/16k",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED
+ },
+# if 1
+ {
+ "RW Contention Exl/Exl 50us/25us/16k poke",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_POKE
+ },
+# endif
+ {
+ "RW Contention Shr/Exl 50us/25us/16k poke",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED
+ | VMMDEV_TESTING_LOCKED_HI_POKE | VMMDEV_TESTING_LOCKED_HI_BUSY_SUCCESS
+ },
+# if 1
+ {
+ "RW Contention Exl/Exl 50us/25us/16k poke void",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_POKE
+ },
+# endif
+ {
+ "RW Contention Shr/Exl 50us/25us/16k poke void",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED
+ | VMMDEV_TESTING_LOCKED_HI_POKE | VMMDEV_TESTING_LOCKED_HI_BUSY_SUCCESS
+ },
+#endif
+
+ {
+ "RW Contention Exl/Shr 50us/25us/16k",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_EMT_SHARED
+ },
+ {
+ "RW Contention Exl/Shr poke 250us/25us/16k",
+ 10000 + 4096,
+ 250 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_EMT_SHARED
+ | VMMDEV_TESTING_LOCKED_HI_POKE
+ },
+ {
+ "RW Contention Exl/Shr poke void 250us/25us/16k",
+ 10000 + 4096,
+ 250 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW | VMMDEV_TESTING_LOCKED_HI_EMT_SHARED
+ | VMMDEV_TESTING_LOCKED_HI_POKE | VMMDEV_TESTING_LOCKED_HI_BUSY_SUCCESS
+ },
+ {
+ "RW None Shr/Shr 50us/25us/16k",
+ 20000 + 4096,
+ 50 | (UINT32_C(25) << VMMDEV_TESTING_LOCKED_LO_WAIT_SHIFT),
+ 16 | VMMDEV_TESTING_LOCKED_HI_ENABLED | VMMDEV_TESTING_LOCKED_HI_TYPE_RW
+ | VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED | VMMDEV_TESTING_LOCKED_HI_EMT_SHARED
+ },
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ uint64_t const cNsPerTest = RT_NS_15SEC;
+ unsigned i;
+
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-locking-1");
+
+ /*
+ * Since this is a host-side test and we don't have raw-mode any more, we
+ * just stay in raw-mode when doing the test.
+ */
+ for (i = 0; i < RT_ELEMENTS(g_aLockingTests); i++)
+ {
+ uint64_t const nsStart = Bs3TestNow();
+ uint64_t cNsElapsed = 0;
+ uint32_t cTotal = 0;
+ uint32_t j;
+
+ Bs3TestSub(g_aLockingTests[i].pszName);
+ ASMOutU32(VMMDEV_TESTING_IOPORT_LOCKED_LO, g_aLockingTests[i].uCtrlLo);
+ ASMOutU32(VMMDEV_TESTING_IOPORT_LOCKED_HI, g_aLockingTests[i].uCtrlHi);
+
+ for (j = 0; j < _2M && cTotal < _1G; j++)
+ {
+
+ /* The inner loop should avoid calling Bs3TestNow too often, while not overshooting the . */
+ unsigned iInner = (unsigned)g_aLockingTests[i].cInnerLoops;
+ cTotal += iInner;
+ while (iInner-- > 0)
+ ASMInU32(VMMDEV_TESTING_IOPORT_LOCKED_LO);
+
+ cNsElapsed = Bs3TestNow() - nsStart;
+ if (cNsElapsed >= cNsPerTest)
+ break;
+ }
+
+ /* Disable locking. */
+ ASMOutU32(VMMDEV_TESTING_IOPORT_LOCKED_HI, 0);
+
+ Bs3TestValue("Loops", cTotal, VMMDEV_TESTING_UNIT_OCCURRENCES);
+ Bs3TestValue("Elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
+ Bs3TestValue("PerLoop", cNsElapsed / cTotal, VMMDEV_TESTING_UNIT_NS_PER_OCCURRENCE);
+ }
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-memalloc-1.c64 b/src/VBox/ValidationKit/bootsectors/bs3-memalloc-1.c64
new file mode 100644
index 00000000..3a3235b5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-memalloc-1.c64
@@ -0,0 +1,282 @@
+/* $Id: bs3-memalloc-1.c64 $ */
+/** @file
+ * BS3Kit - bs3-timers-1, 64-bit C code.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+#include <VBox/VMMDevTesting.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Copy of interesting E820 entries. */
+static INT15E820ENTRY g_aEntries[16];
+/** Number of interesting entires. */
+static unsigned g_cEntries = 0;
+/** Number of intersting bytes found. */
+static uint64_t g_cbInteresting = 0;
+/** Lowest interesting address. */
+static uint64_t g_uInterestingStart = UINT64_MAX;
+/** End of interesting addresses. */
+static uint64_t g_uInterestingEnd = 0;
+
+
+/**
+ * For subsequence touch iterations that doesn't allocate any RAM.
+ *
+ * This may cause page pool activitiy if we've got more memory than we have room
+ * for in the pool. This depends on amount of guest RAM and how much could be
+ * backed by large pages.
+ */
+static uint64_t CheckTouchedMemory(void)
+{
+ unsigned iEntry;
+ uint64_t iPage = 0;
+ uint64_t cErrors = 0;
+ for (iEntry = 0; iEntry < g_cEntries; iEntry++)
+ {
+ uint64_t volatile *pu64Cur = (uint64_t *)g_aEntries[iEntry].uBaseAddr;
+ uint64_t cbLeft = g_aEntries[iEntry].cbRange;
+ while (cbLeft >= X86_PAGE_SIZE)
+ {
+ /* Check first. */
+ if (RT_LIKELY( pu64Cur[0] == iPage
+ && pu64Cur[1] == iPage))
+ { /* likely */ }
+ else
+ {
+ Bs3TestFailedF("%p: %#llx + %#llx, expected twice %#llx\n", pu64Cur, pu64Cur[0], pu64Cur[1], iPage);
+ cErrors++;
+ }
+
+ /* Then write again. */
+ pu64Cur[0] = iPage;
+ pu64Cur[1] = iPage;
+
+ /* Advance. */
+ iPage++;
+ pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
+ cbLeft -= X86_PAGE_SIZE;
+ }
+ }
+ return cErrors;
+}
+
+
+/**
+ * First touching of memory, assuming content is ZERO.
+ */
+static uint64_t FirstTouchMemory(void)
+{
+ unsigned iEntry;
+ uint64_t iPage = 0;
+ for (iEntry = 0; iEntry < g_cEntries; iEntry++)
+ {
+ uint64_t volatile *pu64Cur = (uint64_t volatile *)g_aEntries[iEntry].uBaseAddr;
+ uint64_t cbLeft = g_aEntries[iEntry].cbRange;
+ while (cbLeft >= X86_PAGE_SIZE)
+ {
+ /*
+ * Write to the page first so we won't waste time mapping the zero
+ * page and get straight to the actual page allocation.
+ */
+ pu64Cur[0] = iPage;
+
+ /* Then check that the 2nd qword is zero before writing it. */
+ if (RT_LIKELY(pu64Cur[1] == 0))
+ { /* likely */ }
+ else
+ Bs3TestFailedF("%p: %#llx, expected zero\n", pu64Cur, pu64Cur[1]);
+ pu64Cur[1] = iPage;
+
+ /* Advance. */
+ iPage++;
+ pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
+ cbLeft -= X86_PAGE_SIZE;
+ }
+ }
+ return iPage;
+}
+
+
+/**
+ * Translates a E820 entry type to a string.
+ */
+static const char *getEntryTypeName(uint32_t uType)
+{
+ switch (uType)
+ {
+ case INT15E820_TYPE_USABLE: return "USABLE";
+ case INT15E820_TYPE_RESERVED: return "RESERVED";
+ case INT15E820_TYPE_ACPI_RECLAIMABLE: return "ACPI_RECLAIMABLE";
+ case INT15E820_TYPE_ACPI_NVS: return "ACPI_NVS";
+ case INT15E820_TYPE_BAD: return "BAD";
+ default: return "unknown";
+ }
+}
+
+BS3_DECL(void) Main_lm64()
+{
+ uint32_t uCont;
+ unsigned i;
+
+ Bs3TestInit("bs3-memalloc-1");
+
+ /*
+ * Get the E820 memory descriptors and pick out those describing memory not
+ * already used by the Bs3Kit.
+ */
+ Bs3TestSub("INT15h/E820");
+ for (uCont = i = 0; i < 2048; i++)
+ {
+ uint32_t const uEbxCur = uCont;
+ INT15E820ENTRY Entry = { 0, 0, 0, 0 };
+ uint32_t cbEntry = sizeof(Entry);
+ if (!Bs3BiosInt15hE820_lm64(&Entry, &cbEntry, &uCont))
+ {
+ Bs3TestFailedF("int15h/E820 failed i=%u", i);
+ break;
+ }
+ Bs3TestPrintf("#%u/%#x: %#018llx LB %#018llx %s (%d)\n",
+ i, uEbxCur, Entry.uBaseAddr, Entry.cbRange, getEntryTypeName(Entry.uType), Entry.uType);
+ if (Entry.uType == INT15E820_TYPE_USABLE)
+ {
+ if (Entry.uBaseAddr >= _4G)
+ {
+ if (g_cEntries < RT_ELEMENTS(g_aEntries))
+ {
+ g_cbInteresting += Entry.cbRange;
+ if (g_uInterestingStart > Entry.uBaseAddr)
+ g_uInterestingStart = Entry.uBaseAddr;
+ if (g_uInterestingEnd < Entry.uBaseAddr + Entry.cbRange)
+ g_uInterestingEnd = Entry.uBaseAddr + Entry.cbRange;
+ Bs3MemCpy(&g_aEntries[g_cEntries++], &Entry, sizeof(Entry));
+ }
+ else
+ Bs3TestFailedF("Too many interesting E820 entries! Extend g_aEntries!\n");
+ }
+ }
+
+ /* Done? */
+ if (uCont == 0)
+ break;
+ }
+ if (g_cEntries == 0)
+ Bs3TestFailedF("No interesting E820 entries! Make sure you've assigned more than 4GB to the VM!\n");
+ else
+ {
+ uint64_t uFailurePoint = 0;
+ int rc;
+ Bs3TestPrintf("Found %u interesting entries covering %#llx bytes (%u GB).\n"
+ "From %#llx to %#llx\n",
+ g_cEntries, g_cbInteresting, (unsigned)(g_cbInteresting / _1G), g_uInterestingStart, g_uInterestingEnd);
+
+ if (g_uBs3EndOfRamAbove4G < g_uInterestingEnd)
+ Bs3TestFailedF("g_uBs3EndOfRamAbove4G (%#llx) is lower than g_uInterestingEnd (%#llx)!\n",
+ g_uBs3EndOfRamAbove4G, g_uInterestingEnd);
+
+
+ /*
+ * Map all the memory (Bs3Kit only maps memory below 4G).
+ */
+ Bs3TestSub("Mapping memory above 4GB");
+ if (!(g_uBs3CpuDetected & BS3CPU_F_PSE))
+ Bs3TestFailedF("PSE was not detected!\n");
+ else if (!(ASMGetCR4() & X86_CR4_PSE))
+ Bs3TestFailedF("PSE was not enabled!\n");
+ else if (RT_SUCCESS(rc = Bs3PagingMapRamAbove4GForLM(&uFailurePoint)))
+ {
+#define PAGES_2_MB(a_cPages) ((a_cPages) / (_1M / X86_PAGE_SIZE))
+ uint64_t cTotalPages;
+ unsigned iLoop;
+
+ /*
+ * Time touching all the memory.
+ */
+ Bs3TestSub("Allocation speed");
+ {
+ uint64_t const nsStart = Bs3TestNow();
+ uint64_t const uTscStart = ASMReadTSC();
+ uint64_t const cPages = FirstTouchMemory();
+ uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
+ uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
+ uint64_t uThruput;
+ Bs3TestValue("Pages", cPages, VMMDEV_TESTING_UNIT_PAGES);
+ Bs3TestValue("MiBs", PAGES_2_MB(cPages), VMMDEV_TESTING_UNIT_MEGABYTES);
+ Bs3TestValue("Alloc elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
+ Bs3TestValue("Alloc elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
+ Bs3TestValue("Page alloc time", cNsElapsed / cPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
+ Bs3TestValue("Page alloc time in ticks", cTicksElapsed / cPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
+ uThruput = cPages * RT_NS_1SEC / cNsElapsed;
+ Bs3TestValue("Alloc thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
+ Bs3TestValue("Alloc thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
+ cTotalPages = cPages;
+ }
+
+ /*
+ * Time accessing all the memory again. This might give a clue as to page pool performance.
+ */
+ for (iLoop = 0; iLoop < 2; iLoop++)
+ {
+ Bs3TestSub(iLoop == 0 ? "2nd access" : "3rd access");
+ {
+ uint64_t const nsStart = Bs3TestNow();
+ uint64_t const uTscStart = ASMReadTSC();
+ uint64_t const cErrors = CheckTouchedMemory();
+ uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
+ uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
+ uint64_t uThruput;
+ Bs3TestValue("Access elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
+ Bs3TestValue("Access elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
+ Bs3TestValue("Page access time", cNsElapsed / cTotalPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
+ Bs3TestValue("Page access time in ticks", cTicksElapsed / cTotalPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
+ uThruput = cTotalPages * RT_NS_1SEC / cNsElapsed;
+ Bs3TestValue("Access thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
+ Bs3TestValue("Access thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
+ }
+ }
+ }
+ else
+ Bs3TestFailedF("Bs3PagingMapRamAbove4GForLM failed at %#llx: %d", uFailurePoint, rc);
+ }
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-timers-1-x0.c b/src/VBox/ValidationKit/bootsectors/bs3-timers-1-x0.c
new file mode 100644
index 00000000..d3dfdb2d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-timers-1-x0.c
@@ -0,0 +1,102 @@
+/* $Id: bs3-timers-1-x0.c $ */
+/** @file
+ * BS3Kit - bs3-timers-1, C test driver code (16-bit).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define BS3_USE_X0_TEXT_SEG
+#include <bs3kit.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+static uint8_t bs3Timers1_Pit(uint8_t bMode, uint16_t uHz, uint32_t cNsMaxDiviation)
+{
+ uint32_t const cTargetTicks = uHz * 3;
+ uint16_t uActualHz;
+ uint64_t cNsElapsed;
+ int64_t cNsDelta;
+ uint64_t cNsDeltaAbs;
+
+ Bs3PitSetupAndEnablePeriodTimer(uHz);
+ cNsElapsed = Bs3TestNow();
+ ASMIntEnable();
+ uActualHz = g_cBs3PitIntervalHz;
+
+ while (g_cBs3PitTicks < cTargetTicks)
+ ASMHalt();
+
+ Bs3PitDisable();
+ ASMIntDisable();
+ cNsElapsed = Bs3TestNow() - cNsElapsed;
+
+ /* Calculate the absolute delta and fail the test if the diviation is too high... */
+ cNsDelta = cNsElapsed - RT_NS_1SEC * 3;
+ cNsDeltaAbs = RT_ABS(cNsDelta);
+ /*Bs3TestPrintf("cNsElapsed=%RU64 g_cBs3PitTicks=%RU32 uHz=%u -> %RU64ns\n",
+ cNsElapsed, g_cBs3PitTicks, uActualHz, cNsDeltaAbs);*/
+ if (cNsDeltaAbs > cNsMaxDiviation)
+ Bs3TestFailedF("delta %c%RU64 ns (%RI32 ms), max %RU32 ns", cNsDelta < 0 ? '-' : '+', cNsDeltaAbs,
+ (int32_t)((uint64_t)cNsDelta / RT_NS_1MS), cNsMaxDiviation);
+
+ return 0;
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3Timers1_Pit_100Hz)(uint8_t bMode)
+{
+ return bs3Timers1_Pit(bMode, 100, RT_NS_10MS);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3Timers1_Pit_1000Hz)(uint8_t bMode)
+{
+ return bs3Timers1_Pit(bMode, 1000, RT_NS_10MS);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3Timers1_Pit_2000Hz)(uint8_t bMode)
+{
+ return bs3Timers1_Pit(bMode, 2000, RT_NS_10MS*2);
+}
+
+
+BS3_DECL_FAR(uint8_t) BS3_CMN_FAR_NM(bs3Timers1_Pit_4000Hz)(uint8_t bMode)
+{
+ return bs3Timers1_Pit(bMode, 4000, RT_NS_10MS*4);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-timers-1.c b/src/VBox/ValidationKit/bootsectors/bs3-timers-1.c
new file mode 100644
index 00000000..c613180d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-timers-1.c
@@ -0,0 +1,75 @@
+/* $Id: bs3-timers-1.c $ */
+/** @file
+ * BS3Kit - bs3-timers-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+FNBS3TESTDOMODE bs3Timers1_Pit_100Hz_f16;
+FNBS3TESTDOMODE bs3Timers1_Pit_1000Hz_f16;
+FNBS3TESTDOMODE bs3Timers1_Pit_2000Hz_f16;
+FNBS3TESTDOMODE bs3Timers1_Pit_4000Hz_f16;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const BS3TESTMODEBYONEENTRY g_aModeByOneTests[] =
+{
+ { "pit-100Hz", bs3Timers1_Pit_100Hz_f16, BS3TESTMODEBYONEENTRY_F_MINIMAL },
+ { "pit-1000Hz", bs3Timers1_Pit_1000Hz_f16, BS3TESTMODEBYONEENTRY_F_MINIMAL },
+ { "pit-2000Hz", bs3Timers1_Pit_2000Hz_f16, BS3TESTMODEBYONEENTRY_F_MINIMAL },
+ { "pit-4000Hz", bs3Timers1_Pit_4000Hz_f16, BS3TESTMODEBYONEENTRY_F_MINIMAL },
+};
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-timers-1");
+
+ Bs3TestDoModesByOne_rm(g_aModeByOneTests, RT_ELEMENTS(g_aModeByOneTests), 0);
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-timing-1-32.c32 b/src/VBox/ValidationKit/bootsectors/bs3-timing-1-32.c32
new file mode 100644
index 00000000..95befae9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-timing-1-32.c32
@@ -0,0 +1,363 @@
+/* $Id: bs3-timing-1-32.c32 $ */
+/** @file
+ * BS3Kit - bs3-timinig-1, 32-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef STANDALONE_EXECUTABLE
+# include <bs3kit.h>
+#endif
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/asm-math.h>
+#include <iprt/asm.h>
+#include <iprt/uint128.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** TSC timing results. */
+typedef struct BS3TIMING1RESULT
+{
+ /** Number of nanoseconds elapsed while testing. */
+ uint64_t cNsElapsed;
+ /** Number of CPU ticks elapsed while testing. */
+ uint64_t cTicksElapsed;
+ /** The minium number of ticks between TSC reads. */
+ uint64_t cTicksMin;
+ /** The maximum number of ticks between TSC reads. */
+ uint64_t cTicksMax;
+ /** The sum of all TSC read deltas. */
+ uint64_t cTicksSum;
+ /** Number of loops (TSC read deltas). */
+ uint64_t cTotalLoops;
+ /** Number of times TSC moved backwards. */
+ uint64_t cBackwards;
+ /** Approx log2(cTicks) distribution. */
+ uint64_t aDistribution[65];
+} BS3TIMING1RESULT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The total result. */
+static BS3TIMING1RESULT g_TotalResult;
+
+/** Set if history wrapped (i.e. table is full). */
+static bool g_fBigHistoryWrapped = false;
+/** The next history entry. */
+static uint32_t g_iBigHistory;
+/** History of large gaps. */
+static struct { uint64_t uTsc, cTicksDelta; } g_aBigHistory[384];
+
+
+
+/**
+ * Pretty prints
+ */
+static void bs3Timing1_PrintTicks(uint64_t cTicks, uint64_t uCpuFreq)
+{
+ if (uCpuFreq > _128M)
+ {
+ if (cTicks >= uCpuFreq * 1000)
+ Bs3TestPrintf("%'RU64s", cTicks / uCpuFreq);
+ else
+ {
+ const char *pszUnit;
+ uint64_t uValue;
+ if (cTicks >= uCpuFreq)
+ {
+ pszUnit = "s";
+ uValue = (cTicks * RT_MS_1SEC) / uCpuFreq;
+ }
+ else if (cTicks * RT_MS_1SEC >= uCpuFreq)
+ {
+ pszUnit = "ms";
+ uValue = (cTicks * RT_US_1SEC) / uCpuFreq;
+ }
+ else if (cTicks * RT_US_1SEC >= uCpuFreq)
+ {
+ pszUnit = "us";
+ uValue = (cTicks * RT_NS_1SEC) / uCpuFreq;
+ }
+ else if (cTicks * RT_NS_1SEC >= uCpuFreq)
+ {
+ pszUnit = "ns";
+ uValue = (cTicks * UINT64_C(1000000000000)) / uCpuFreq;
+ }
+ else
+ {
+ Bs3TestPrintf("%'RU64ps", (cTicks * UINT64_C(1000000000000)) / uCpuFreq);
+ return;
+ }
+ Bs3TestPrintf("%u.%03u%s (%'RU64 ticks)", (uint32_t)uValue / 1000, (uint32_t)uValue % 1000, pszUnit, cTicks);
+ }
+ }
+ else
+ Bs3TestPrintf("%'RU64 ticks", cTicks);
+}
+
+
+/**
+ * Prints a result.
+ *
+ * @param pResult The result to print.
+ * @param iRun The run (loop in qpc parlance).
+ * @param uVerbosity The verbosity level.
+ */
+static void bs3Timing1_PrintResult(BS3TIMING1RESULT const *pResult, unsigned iRun, unsigned uVerbosity)
+{
+ uint64_t uCpuFreq;
+
+ /*
+ * Calc CPU frequency.
+ */
+ if (pResult->cNsElapsed > 0 && pResult->cTicksElapsed > 0)
+ {
+#if 1
+ RTUINT128U Tmp1, Divisor, Result;
+ RTUInt128Div(&Result,
+ RTUInt128MulU64ByU64(&Tmp1, pResult->cTicksElapsed, RT_NS_1SEC),
+ RTUInt128AssignU64(&Divisor, pResult->cNsElapsed));
+ uCpuFreq = Result.s.Lo;
+#else
+ unsigned const cShift = pResult->cTicksElapsed < UINT64_C(0x000225C17D04) ? 0
+ : pResult->cTicksElapsed < UINT64_C(0x00225C17D04D) ? 4
+ : pResult->cTicksElapsed < UINT64_C(0x0225C17D04DA) ? 8
+ : pResult->cTicksElapsed < UINT64_C(0x225C1D940BF6) ? 12
+ : 16;
+ uCpuFreq = pResult->cTicksElapsed * ((uint64_t)RT_NS_1SEC >> cShift) / (pResult->cNsElapsed >> cShift);
+#endif
+ }
+ else
+ uCpuFreq = 1;
+
+ /*
+ * Report results.
+ *
+ * Note! in 32-bit and 16-bit mode, values 4G or higher gets formatted as
+ * hexadecimal to avoid 64-bit division.
+ */
+ Bs3TestPrintf("Loop #%u: %'RU64 tests: ", iRun, pResult->cTotalLoops);
+
+ Bs3TestPrintf("average ");
+ bs3Timing1_PrintTicks(pResult->cTicksSum / pResult->cTotalLoops, uCpuFreq);
+ Bs3TestPrintf(", min ");
+ bs3Timing1_PrintTicks(pResult->cTicksMin, uCpuFreq);
+ Bs3TestPrintf(", max ");
+ bs3Timing1_PrintTicks(pResult->cTicksMax, uCpuFreq);
+ Bs3TestPrintf("\n");
+
+ /* Distribution (tick delta log2-ish). */
+ if (uVerbosity > 0)
+ {
+ unsigned iItem = 0;
+ unsigned i;
+ for (i = uVerbosity > 1 ? 0 : 5; i < RT_ELEMENTS(pResult->aDistribution); i++)
+ if (pResult->aDistribution[i] != 0)
+ {
+ if (iItem >= 6)
+ {
+ iItem = 0;
+ Bs3TestPrintf("\n");
+ }
+ iItem++;
+ Bs3TestPrintf(" %'11RU64|2^%-2u", pResult->aDistribution[i], i);
+ }
+ if (uVerbosity > 1)
+ Bs3TestPrintf(iItem < 6 ? " (%'RU64 Hz)\n" : "\n (%'RU64 Hz)\n", uCpuFreq);
+ else
+ Bs3TestPrintf("\n");
+ }
+ if (pResult->cBackwards != 0)
+ Bs3TestFailedF("TSC went backwards %'RU64 time(s)", pResult->cBackwards);
+}
+
+
+/**
+ * Do one TSC timing iteration.
+ *
+ * @param iRun The iteration number (loop).
+ * @param cSecs The number of seconds to sample TSCs.
+ * @param uVerbosity The noise level.
+ * @param iMinHistory The threshold level to put stuff in g_auTscHistory.
+ */
+static void bs3Timing1_Tsc_One(unsigned iRun, uint32_t cSecs, unsigned uVerbosity, unsigned iMinHistory)
+{
+ uint64_t const nsStart = Bs3TestNow();
+ uint64_t const uTscStart = ASMReadTSC();
+ uint64_t const nsDeadline = nsStart + cSecs * RT_NS_1SEC_64;
+ uint64_t cNsElapsed;
+ BS3TIMING1RESULT Result;
+ unsigned i;
+
+ Bs3MemZero(&Result, sizeof(Result));
+ Result.cTicksMin = UINT64_MAX;
+
+ /*
+ * Test loop.
+ */
+ do
+ {
+ unsigned cLoops = 100000 + 1;
+ Result.cTotalLoops += cLoops - 1;
+ while (--cLoops != 0)
+ {
+ uint64_t uTscPrev = ASMReadTSC();
+ uint64_t uTscNow = ASMReadTSC();
+ uint64_t cTicks = uTscNow - uTscPrev;
+ unsigned iBit;
+
+ /* check that it doesn't go backwards*/
+ if ((int64_t)cTicks < 0)
+ Result.cBackwards++;
+
+ /* min/max/avg */
+ Result.cTicksSum += cTicks;
+ if (cTicks < Result.cTicksMin)
+ Result.cTicksMin = cTicks;
+ if (cTicks > Result.cTicksMax)
+ Result.cTicksMax = cTicks;
+
+ /* result distribution by most significant bit. */
+ iBit = ASMBitLastSetU64(cTicks);
+ Result.aDistribution[iBit] += 1;
+ if (iBit < iMinHistory)
+ { /* likely */ }
+ else
+ {
+ g_aBigHistory[g_iBigHistory].uTsc = uTscPrev;
+ g_aBigHistory[g_iBigHistory].cTicksDelta = cTicks;
+ if (++g_iBigHistory >= RT_ELEMENTS(g_aBigHistory))
+ {
+ g_iBigHistory = 0;
+ g_fBigHistoryWrapped = true;
+ }
+ }
+ }
+ } while ((cNsElapsed = Bs3TestNow()) < nsDeadline);
+
+ Result.cTicksElapsed = ASMReadTSC() - uTscStart;
+ Result.cNsElapsed = cNsElapsed - nsStart;
+
+ bs3Timing1_PrintResult(&Result, iRun, uVerbosity);
+
+ /* Add to total. */
+ g_TotalResult.cNsElapsed += Result.cNsElapsed;
+ g_TotalResult.cTicksElapsed += Result.cTicksElapsed;
+ if (Result.cTicksMin < g_TotalResult.cTicksMin || g_TotalResult.cTicksMin == 0)
+ g_TotalResult.cTicksMin = Result.cTicksMin;
+ if (Result.cTicksMax > g_TotalResult.cTicksMax)
+ g_TotalResult.cTicksMax += Result.cTicksMax;
+ g_TotalResult.cTicksSum += Result.cTicksSum;
+ g_TotalResult.cTotalLoops += Result.cTotalLoops;
+ g_TotalResult.cBackwards += Result.cBackwards;
+ for (i = 0; i < RT_ELEMENTS(Result.aDistribution); i++)
+ g_TotalResult.aDistribution[i] += Result.aDistribution[i];
+}
+
+
+/**
+ * The TSC test driver.
+ *
+ * @param cLoops Number of test iterations.
+ * @param cSecs The number of seconds per iteration.
+ * @param uVerbosity How noisy we should be.
+ * @param iMinHistory The threshold for big gap history.
+ */
+static void bs3Timing1_Tsc_Driver(unsigned cLoops, unsigned cSecs, unsigned uVerbosity, unsigned iMinHistory)
+{
+ unsigned iLoop;
+
+#if 1
+ /*
+ * Verify that the first/last bit in U64 works (didn't).
+ */
+ iLoop = ASMBitLastSetU64( UINT64_C(0x1000100010001000)); if (iLoop != 61) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
+ iLoop = ASMBitFirstSetU64(UINT64_C(0x1000100010001000)); if (iLoop != 13) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
+ iLoop = ASMBitLastSetU64( UINT64_C(0x000ffff000000000)); if (iLoop != 52) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
+ iLoop = ASMBitFirstSetU64(UINT64_C(0x000ffff000000000)); if (iLoop != 37) Bs3TestFailedF("%d: iLoop=%d\n", __LINE__, iLoop);
+#endif
+
+ /*
+ * Do the work.
+ */
+ Bs3TestPrintf("Running %u loops, %u second%s each...\n", cLoops, cSecs, cSecs != 1 ? "s" : "");
+ for (iLoop = 1; iLoop <= cLoops; iLoop++)
+ bs3Timing1_Tsc_One(iLoop, cSecs, uVerbosity, iMinHistory);
+
+ /*
+ * Report the total.
+ */
+ Bs3TestPrintf("Total:\n");
+ bs3Timing1_PrintResult(&g_TotalResult, iLoop, uVerbosity + 1);
+
+ /*
+ * Dump the large gap history, if any.
+ */
+ if (g_fBigHistoryWrapped || g_iBigHistory > 0)
+ {
+ uint32_t const iFirst = g_fBigHistoryWrapped ? g_iBigHistory : 0;
+ uint32_t const iEnd = g_iBigHistory;
+ uint64_t uTscPrev = g_aBigHistory[iFirst].uTsc;
+ uint32_t i = iFirst;
+ Bs3TestPrintf("Big gap history (TSC, prev delta, test delta|level):\n");
+ do
+ {
+ Bs3TestPrintf(" %'RU64: %'14RU64 - %'14RU64|%u\n", g_aBigHistory[i].uTsc, g_aBigHistory[i].uTsc - uTscPrev,
+ g_aBigHistory[i].cTicksDelta, ASMBitLastSetU64(g_aBigHistory[i].cTicksDelta));
+ uTscPrev = g_aBigHistory[i].uTsc;
+ if (++i >= RT_ELEMENTS(g_aBigHistory))
+ i = 0;
+ } while (i != iEnd);
+ }
+ else
+ Bs3TestPrintf("No big gap history.\n");
+}
+
+
+#ifndef STANDALONE_EXECUTABLE
+BS3_DECL(void) bs3Timing1_Tsc_pe32(void)
+{
+ Bs3TestPrintf("bs3Timing1_Tsc_pe32\n");
+ bs3Timing1_Tsc_Driver(60, 10 /*sec*/, 1 /*uVerbosity*/, 17);
+}
+#endif
+
+/* P.S. don't forget: VBoxManage setextradata bs3-timing-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1 */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-timing-1-exe.c b/src/VBox/ValidationKit/bootsectors/bs3-timing-1-exe.c
new file mode 100644
index 00000000..5d802f7f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-timing-1-exe.c
@@ -0,0 +1,126 @@
+/* $Id: bs3-timing-1-exe.c $ */
+/** @file
+ * BS3Kit - bs3-timing-1, regular executable version of the TSC test.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+/* Fake bs3kit.h bits: */
+#define Bs3TestNow RTTimeNanoTS
+#define Bs3TestPrintf RTPrintf
+#define Bs3TestFailedF RTMsgError
+#define Bs3MemZero RT_BZERO
+#define BS3_DECL(t) t
+
+#define STANDALONE_EXECUTABLE
+#include "bs3-timing-1-32.c32"
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--loops", 'l', RTGETOPT_REQ_UINT32 },
+ { "--seconds", 's', RTGETOPT_REQ_UINT32 },
+ { "--min-history", 'm', RTGETOPT_REQ_UINT32 },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ };
+ uint32_t cLoops = 5;
+ uint32_t cSecs = 10;
+ unsigned uVerbosity = 1;
+ unsigned iMinHistory = 17;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRC(rc);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'l':
+ cLoops = ValueUnion.u32;
+ break;
+ case 'm':
+ iMinHistory = ValueUnion.u32;
+ break;
+ case 's':
+ cSecs = ValueUnion.u32;
+ if (cSecs == 0 || cSecs > RT_SEC_1DAY / 2)
+ return RTMsgErrorExitFailure("Seconds value is out of range: %u (min 1, max %u)", cSecs, RT_SEC_1DAY / 2);
+ break;
+ case 'q':
+ uVerbosity = 0;
+ break;
+ case 'v':
+ uVerbosity++;
+ break;
+ case 'V':
+ RTPrintf("v0.1.0\n");
+ return RTEXITCODE_SUCCESS;
+ case 'h':
+ RTPrintf("usage: %Rbn [-q|-v] [-l <iterations>] [-s <seconds-per-iteration>] [-m <log2-big-gap>]\n", argv[0]);
+ return RTEXITCODE_SUCCESS;
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Run the test.
+ */
+ bs3Timing1_Tsc_Driver(cLoops, cSecs, uVerbosity, iMinHistory);
+ return RTEXITCODE_SUCCESS;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-timing-1.c b/src/VBox/ValidationKit/bootsectors/bs3-timing-1.c
new file mode 100644
index 00000000..6404e25a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-timing-1.c
@@ -0,0 +1,63 @@
+/* $Id: bs3-timing-1.c $ */
+/** @file
+ * BS3Kit - bs3-timing-1, 16-bit C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+BS3_DECL_CALLBACK(void) bs3Timing1_Tsc_pe32();
+
+
+
+BS3_DECL(void) Main_rm()
+{
+ Bs3InitAll_rm();
+ Bs3TestInit("bs3-timing-1");
+
+ /* Switch to PE32 and do the work from there, all the 64-bit integer
+ handling should be a little more efficient in 32-bit mode. */
+ Bs3SwitchTo32BitAndCallC_rm(bs3Timing1_Tsc_pe32, 0);
+
+ Bs3TestTerm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/Makefile.kmk b/src/VBox/ValidationKit/bootsectors/bs3kit/Makefile.kmk
new file mode 100644
index 00000000..5a7b77bd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/Makefile.kmk
@@ -0,0 +1,760 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Bootsector Kit v3
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+
+# Boot Sector post-link tool (used via the parent Config.kmk).
+BLDPROGS += VBoxBs3Linker
+VBoxBs3Linker_TEMPLATE = VBoxBldProg
+VBoxBs3Linker_SOURCES = $(VBOX_PATH_BS3KIT_SRC)/VBoxBs3Linker.cpp
+
+
+# 64-bit relocation conversion tool (used via the parent Config.kmk).
+BLDPROGS += VBoxBs3ObjConverter
+VBoxBs3ObjConverter_TEMPLATE = VBoxBldProg
+VBoxBs3ObjConverter_DEFS = BS3KIT_BS3CLASS16CODE=$(BS3KIT_BS3CLASS16CODE) IN_RT_R3
+VBoxBs3ObjConverter_SOURCES = \
+ $(VBOX_PATH_BS3KIT_SRC)/VBoxBs3ObjConverter.cpp \
+ $(PATH_ROOT)/src/VBox/Runtime/common/sort/shellsort.cpp
+
+
+# The boot sector.
+MISCBINS += bs3-bootsector
+bs3-bootsector_TEMPLATE = VBoxBS3KitBS
+bs3-bootsector_SOURCES = bs3-bootsector.asm
+
+
+#
+# Common sources to be compiled into _p16, _p32 and _p64 versions.
+#
+VBOX_BS3KIT_COMMON_SOURCES = \
+ bs3-cmn-A20Disable.asm \
+ bs3-cmn-A20Enable.asm \
+ bs3-cmn-GetCpuVendor.c \
+ bs3-cmn-GetModeName.c \
+ bs3-cmn-GetModeNameShortLower.c \
+ bs3-cmn-KbdRead.asm \
+ bs3-cmn-KbdWait.asm \
+ bs3-cmn-KbdWrite.asm \
+ bs3-cmn-Shutdown.asm \
+ bs3-cmn-Panic.asm \
+ bs3-cmn-PrintChr.asm \
+ bs3-cmn-Printf.c \
+ bs3-cmn-PrintU32.asm \
+ bs3-cmn-PrintX32.asm \
+ bs3-cmn-PrintStr.c \
+ bs3-cmn-PrintStrN.asm \
+ bs3-cmn-StrFormatV.c \
+ bs3-cmn-StrPrintf.c \
+ bs3-cmn-StrLen.c \
+ bs3-cmn-StrNLen.c \
+ bs3-cmn-StrCpy.c \
+ bs3-cmn-MemChr.asm \
+ bs3-cmn-MemCmp.asm \
+ bs3-cmn-MemCpy.c \
+ bs3-cmn-MemPCpy.c \
+ bs3-cmn-MemMove.c \
+ bs3-cmn-MemSet.asm \
+ bs3-cmn-MemZero.asm \
+ bs3-cmn-MemAlloc.c \
+ bs3-cmn-MemAllocZ.c \
+ bs3-cmn-MemFree.c \
+ bs3-cmn-MemGuardedTestPage.c \
+ bs3-cmn-MemPrintInfo.c \
+ bs3-cmn-PagingData.c \
+ bs3-cmn-PagingInitRootForPP.c \
+ bs3-cmn-PagingInitRootForPAE.c \
+ bs3-cmn-PagingInitRootForLM.c \
+ bs3-cmn-PagingAlias.c \
+ bs3-cmn-PagingProtect.c \
+ bs3-cmn-PagingQueryAddressInfo.c \
+ bs3-cmn-PagingSetupCanonicalTraps.c \
+ bs3-cmn-pic-data.c \
+ bs3-cmn-PicMaskAll.c \
+ bs3-cmn-PicUpdateMask.c \
+ bs3-cmn-PicSetup.c \
+ bs3-cmn-pit.c \
+ bs3-cmn-PitIrqHandler.c \
+ bs3-cmn-RegCtxRestore.asm \
+ bs3-cmn-RegCtxConvertToRingX.c \
+ bs3-cmn-RegCtxConvertV86ToRm.c \
+ bs3-cmn-RegCtxPrint.c \
+ bs3-cmn-RegCtxGetRspSsAsCurPtr.c \
+ bs3-cmn-RegCtxSave.asm \
+ bs3-cmn-RegCtxSaveEx.asm \
+ bs3-cmn-RegCtxSaveForMode.c \
+ bs3-cmn-RegCtxSetGrpSegFromCurPtr.c \
+ bs3-cmn-RegCtxSetGrpSegFromFlat.c \
+ bs3-cmn-RegCtxSetRipCsFromCurPtr.c \
+ bs3-cmn-RegCtxSetRipCsFromFlat.c \
+ bs3-cmn-RegCtxSetRipCsFromLnkPtr.c \
+ bs3-cmn-RegCtxSetGpr.c \
+ bs3-cmn-RegGetCr0.asm \
+ bs3-cmn-RegGetCr2.asm \
+ bs3-cmn-RegGetCr3.asm \
+ bs3-cmn-RegGetCr4.asm \
+ bs3-cmn-RegSetCr0.asm \
+ bs3-cmn-RegSetCr2.asm \
+ bs3-cmn-RegSetCr3.asm \
+ bs3-cmn-RegSetCr4.asm \
+ bs3-cmn-RegGetDr0.asm \
+ bs3-cmn-RegGetDr1.asm \
+ bs3-cmn-RegGetDr2.asm \
+ bs3-cmn-RegGetDr3.asm \
+ bs3-cmn-RegGetDr6.asm \
+ bs3-cmn-RegGetDr7.asm \
+ bs3-cmn-RegGetDrX.asm \
+ bs3-cmn-RegSetDr0.asm \
+ bs3-cmn-RegSetDr1.asm \
+ bs3-cmn-RegSetDr2.asm \
+ bs3-cmn-RegSetDr3.asm \
+ bs3-cmn-RegSetDr6.asm \
+ bs3-cmn-RegSetDr7.asm \
+ bs3-cmn-RegSetDrX.asm \
+ bs3-cmn-RegGetTr.asm \
+ bs3-cmn-RegSetTr.asm \
+ bs3-cmn-RegGetLdtr.asm \
+ bs3-cmn-RegSetLdtr.asm \
+ bs3-cmn-RegGetXcr0.asm \
+ bs3-cmn-RegSetXcr0.asm \
+ bs3-cmn-ExtCtxInit.c \
+ bs3-cmn-ExtCtxSave.asm \
+ bs3-cmn-ExtCtxSaveEx.asm \
+ bs3-cmn-ExtCtxRestore.asm \
+ bs3-cmn-ExtCtxRestoreEx.asm \
+ bs3-cmn-ExtCtxGetSize.c \
+ bs3-cmn-ExtCtxAlloc.c \
+ bs3-cmn-ExtCtxFree.c \
+ bs3-cmn-ExtCtxCopy.c \
+ bs3-cmn-ExtCtxGetFcw.c \
+ bs3-cmn-ExtCtxSetFcw.c \
+ bs3-cmn-ExtCtxGetFsw.c \
+ bs3-cmn-ExtCtxSetFsw.c \
+ bs3-cmn-ExtCtxGetAbridgedFtw.c \
+ bs3-cmn-ExtCtxSetAbridgedFtw.c \
+ bs3-cmn-ExtCtxGetMxCsr.c \
+ bs3-cmn-ExtCtxSetMxCsr.c \
+ bs3-cmn-ExtCtxGetMxCsrMask.c \
+ bs3-cmn-ExtCtxSetMxCsrMask.c \
+ bs3-cmn-ExtCtxGetMm.c \
+ bs3-cmn-ExtCtxSetMm.c \
+ bs3-cmn-ExtCtxGetXmm.c \
+ bs3-cmn-ExtCtxSetXmm.c \
+ bs3-cmn-ExtCtxGetYmm.c \
+ bs3-cmn-ExtCtxSetYmm.c \
+ bs3-cmn-SelFar32ToFlat32.c \
+ bs3-cmn-SelFar32ToFlat32NoClobber.asm \
+ bs3-cmn-SelProtFar32ToFlat32.c \
+ bs3-cmn-SelProtModeCodeToRealMode.asm \
+ bs3-cmn-SelRealModeCodeToProtMode.asm \
+ bs3-cmn-SelFlatCodeToRealMode.asm \
+ bs3-cmn-SelFlatCodeToProtFar16.asm \
+ bs3-cmn-SelRealModeDataToProtFar16.asm \
+ bs3-cmn-SelProtFar16DataToRealMode.asm \
+ bs3-cmn-SelRealModeDataToFlat.asm \
+ bs3-cmn-SelProtFar16DataToFlat.asm \
+ bs3-cmn-SelFlatDataToProtFar16.asm \
+ bs3-cmn-SelFlatDataToRealMode.asm \
+ bs3-cmn-SelLnkPtrToCurPtr.c \
+ bs3-cmn-SelLnkPtrToFlat.c \
+ bs3-cmn-SelSetup16BitData.c \
+ bs3-cmn-SelSetup16BitCode.c \
+ bs3-cmn-SelSetup32BitCode.c \
+ bs3-cmn-SelSetupGate.c \
+ bs3-cmn-SelSetupGate64.c \
+ bs3-cmn-SlabInit.c \
+ bs3-cmn-SlabAlloc.c \
+ bs3-cmn-SlabAllocEx.c \
+ bs3-cmn-SlabFree.c \
+ bs3-cmn-SlabListInit.c \
+ bs3-cmn-SlabListAdd.c \
+ bs3-cmn-SlabListAlloc.c \
+ bs3-cmn-SlabListAllocEx.c \
+ bs3-cmn-SlabListFree.c \
+ bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm \
+ bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm \
+ bs3-cmn-SwitchToRing0.asm \
+ bs3-cmn-SwitchToRing1.asm \
+ bs3-cmn-SwitchToRing2.asm \
+ bs3-cmn-SwitchToRing3.asm \
+ bs3-cmn-SwitchToRingX.asm \
+ bs3-cmn-SwitchTo16Bit.asm \
+ bs3-cmn-SwitchTo16BitV86.asm \
+ bs3-cmn-SwitchTo32Bit.asm \
+ bs3-cmn-SwitchTo64Bit.asm \
+ bs3-cmn-Syscall.asm \
+ bs3-cmn-TestData.c \
+ bs3-cmn-TestInit.c \
+ bs3-cmn-TestFailed.c \
+ bs3-cmn-TestNow.asm \
+ bs3-cmn-TestSkipped.c \
+ bs3-cmn-TestSub.c \
+ bs3-cmn-TestSubDone.c \
+ bs3-cmn-TestSubErrorCount.c \
+ bs3-cmn-TestTerm.c \
+ bs3-cmn-TestSendCmdWithStr.asm \
+ bs3-cmn-TestSendCmdWithU32.asm \
+ bs3-cmn-TestIsVmmDevTestingPresent.asm \
+ bs3-cmn-TestCheckRegCtxEx.c \
+ bs3-cmn-TestCheckExtCtx.c \
+ bs3-cmn-TestQueryCfgU8.asm \
+ bs3-cmn-TestQueryCfgU32.asm \
+ bs3-cmn-TestHostPrintf.c \
+ bs3-cmn-TestPrintf.c \
+ bs3-cmn-TestValue.c \
+ bs3-cmn-TrapReInit.c \
+ bs3-cmn-TrapRmV86Init.c \
+ bs3-cmn-TrapRmV86SetGate.c \
+ bs3-cmn-Trap16Init.c \
+ bs3-cmn-Trap16SetGate.c \
+ bs3-cmn-Trap32Init.c \
+ bs3-cmn-Trap32SetGate.c \
+ bs3-cmn-Trap64Init.c \
+ bs3-cmn-Trap64SetGate.c \
+ bs3-cmn-TrapSetDpl.c \
+ bs3-cmn-TrapDefaultHandler.c \
+ bs3-cmn-TrapHandlersData.asm \
+ bs3-cmn-TrapPrintFrame.c \
+ bs3-cmn-TrapSetHandler.c \
+ bs3-cmn-TrapSetHandlerEx.c \
+ bs3-cmn-TrapSetJmp.asm \
+ bs3-cmn-TrapSetJmpAndRestore.c \
+ bs3-cmn-TrapSetJmpAndRestoreInRm.c \
+ bs3-cmn-TrapSetJmpAndRestoreWithRm.c \
+ bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c \
+ bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c \
+ bs3-cmn-TrapUnsetJmp.c \
+ bs3-cmn-UtilSetFullGdtr.asm \
+ bs3-cmn-UtilSetFullIdtr.asm \
+ bs3-cmn-TestDoModesByOneHlp.asm \
+ ../../../Runtime/common/asm/ASMBitFirstClear.asm \
+ ../../../Runtime/common/asm/ASMBitFirstSet.asm \
+ ../../../Runtime/common/asm/ASMBitNextClear.asm \
+ ../../../Runtime/common/asm/ASMBitNextSet.asm \
+ ../../../Runtime/common/asm/ASMBitFirstSetU16.asm \
+ ../../../Runtime/common/asm/ASMBitFirstSetU32.asm \
+ ../../../Runtime/common/asm/ASMBitFirstSetU64.asm \
+ ../../../Runtime/common/asm/ASMBitLastSetU16.asm \
+ ../../../Runtime/common/asm/ASMBitLastSetU32.asm \
+ ../../../Runtime/common/asm/ASMBitLastSetU64.asm \
+ ../../../Runtime/common/asm/ASMMemFirstMismatchingU8.asm \
+ ../../../Runtime/common/asm/ASMSerializeInstruction-cpuid.asm \
+ ../../../Runtime/common/asm/ASMSerializeInstruction-iret.asm \
+ ../../../Runtime/common/asm/ASMSerializeInstruction-rdtscp.asm \
+ ../../../Runtime/common/asm/ASMCpuIdExSlow.asm \
+ ../../../Runtime/common/asm/ASMCpuId.asm \
+ ../../../Runtime/common/asm/ASMCpuId_Idx_ECX.asm \
+ ../../../Runtime/common/asm/ASMWrMsr.asm \
+ ../../../Runtime/common/asm/ASMGetXcr0.asm \
+ ../../../Runtime/common/asm/ASMSetXcr0.asm \
+ ../../../Runtime/common/asm/ASMSetFlags.asm \
+ ../../../Runtime/common/asm/ASMGetFlags.asm \
+ ../../../Runtime/common/asm/ASMMultU64ByU32DivByU32.asm
+
+# The 16-bit BS3Kit library.
+LIBRARIES += bs3kit-common-16
+bs3kit-common-16_TEMPLATE = VBoxBS3KitImg
+bs3kit-common-16_INSTTYPE = none
+bs3kit-common-16_DEFS = TMPL_PE16 BS3_CMN_ONLY
+bs3kit-common-16_ASDEFS = RT_ASMDEFS_INC_FIRST_FILE
+bs3kit-common-16_SOURCES = $(VBOX_BS3KIT_COMMON_SOURCES) \
+ bs3-system-data.asm \
+ bs3-rm-InitAll.c \
+ bs3-rm-InitMemory.c \
+ bs3-rm-InitGdt.c \
+ bs3-cmn-hexdigits.c \
+ bs3-cmn-CpuDetectData.c \
+ bs3-cmn-PerCpuData.c \
+ bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm \
+ bs3-cmn-UInt64Div.c \
+ bs3-cmn-UInt32Div.c \
+ bs3-wc16-U8DR.asm \
+ bs3-wc16-U8DQ.asm \
+ bs3-wc16-I8DR.asm \
+ bs3-wc16-I8DQ.asm \
+ bs3-wc16-I8RS.asm \
+ bs3-wc16-U8RS.asm \
+ bs3-wc16-U8LS.asm \
+ bs3-wc16-U4D.asm \
+ bs3-wc16-I4D.asm \
+ bs3-c16-SwitchFromV86To16BitAndCallC.asm \
+ bs3-c16-Trap16Generic.asm \
+ bs3-c16-TrapRmV86Generic.asm \
+ bs3-c16-TrapRmV86Data.c \
+ bs3-c16-CreateHybridFarRet.asm
+bs3kit-common-16_bs3-cmn-UInt64Div.c_CFLAGS = -oh -d0 # -d1+ vs -d0 saves 0x6a3-0x577 = 0x12C (300)!
+
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMMemFirstMismatchingU8,8)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMMemFirstNonZero,6)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMCpuIdExSlow,32)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMCpuId,20)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMWrMsr,12)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMSetXcr0,8)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,ASMGetXcr0,0)
+-include $(PATH_SUB_CURRENT)/bs3kit-autostubs.kmk # manually generated from headers, see bottom of this file.
+
+# The 32-bit BS3Kit library.
+LIBRARIES += bs3kit-common-32
+bs3kit-common-32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-common-32_INSTTYPE = none
+bs3kit-common-32_DEFS = TMPL_PE32 BS3_CMN_ONLY
+bs3kit-common-32_ASDEFS = RT_ASMDEFS_INC_FIRST_FILE
+bs3kit-common-32_SOURCES = $(VBOX_BS3KIT_COMMON_SOURCES) \
+ bs3-cmn-PagingMapRamAbove4GForLM.c \
+ bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm \
+ bs3-cmn-UInt64Div.c \
+ bs3-wc32-U8D.asm \
+ bs3-wc32-I8D.asm \
+ bs3-wc32-I8RS.asm \
+ bs3-wc32-U8RS.asm \
+ bs3-wc32-U8LS.asm \
+ bs3-wc32-U8M.asm \
+ bs3-c32-Trap32Generic.asm
+
+# The 64-bit BS3Kit library.
+LIBRARIES += bs3kit-common-64
+bs3kit-common-64_TEMPLATE = VBoxBS3KitImg64
+bs3kit-common-64_INSTTYPE = none
+bs3kit-common-64_DEFS = TMPL_LM64 BS3_CMN_ONLY
+bs3kit-common-64_ASDEFS = RT_ASMDEFS_INC_FIRST_FILE
+bs3kit-common-64_SOURCES = $(VBOX_BS3KIT_COMMON_SOURCES) \
+ bs3-cmn-PagingMapRamAbove4GForLM.c \
+ bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm \
+ bs3-c64-Trap64Generic.asm \
+ ../../../Runtime/common/asm/ASMGetIDTR.asm \
+ ../../../Runtime/common/asm/ASMSetIDTR.asm \
+ ../../../Runtime/common/asm/ASMGetGDTR.asm \
+ ../../../Runtime/common/asm/ASMSetGDTR.asm
+
+
+#
+# Common sources to be compiled for each CPU mode.
+#
+VBOX_BS3KIT_MODE_SOURCES = \
+ bs3-mode-Name.asm \
+ bs3-mode-NameShortLower.asm \
+ bs3-mode-SwitchToRM.asm \
+ bs3-mode-SwitchToPE16.asm \
+ bs3-mode-SwitchToPE16_32.asm \
+ bs3-mode-SwitchToPE16_V86.asm \
+ bs3-mode-SwitchToPE32.asm \
+ bs3-mode-SwitchToPE32_16.asm \
+ bs3-mode-SwitchToPEV86.asm \
+ bs3-mode-SwitchToPP16.asm \
+ bs3-mode-SwitchToPP16_32.asm \
+ bs3-mode-SwitchToPP16_V86.asm \
+ bs3-mode-SwitchToPP32.asm \
+ bs3-mode-SwitchToPP32_16.asm \
+ bs3-mode-SwitchToPPV86.asm \
+ bs3-mode-SwitchToPAE16.asm \
+ bs3-mode-SwitchToPAE16_32.asm \
+ bs3-mode-SwitchToPAE16_V86.asm \
+ bs3-mode-SwitchToPAE32.asm \
+ bs3-mode-SwitchToPAE32_16.asm \
+ bs3-mode-SwitchToPAEV86.asm \
+ bs3-mode-SwitchToLM64.asm \
+ bs3-mode-SwitchToLM32.asm \
+ bs3-mode-SwitchToLM16.asm \
+ bs3-mode-SwitchTo32BitAndCallC.asm \
+ bs3-mode-EnteredMode.asm \
+ bs3-mode-PagingGetRootForPP16.asm \
+ bs3-mode-PagingGetRootForPP32.asm \
+ bs3-mode-PagingGetRootForPAE16.asm \
+ bs3-mode-PagingGetRootForPAE32.asm \
+ bs3-mode-PagingGetRootForLM64.asm \
+ bs3-mode-TrapInit.c \
+ bs3-mode-TrapSystemCallHandler.asm \
+ bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm \
+ bs3-mode-TestDoModes.c \
+ bs3-mode-TestDoModesByOne.c \
+ bs3-mode-TestDoModesByMax.c \
+ bs3-mode-TestDoModesHlp.asm \
+ bs3-mode-BiosInt15hE820.asm
+
+# The 16-bit real mode BS3Kit library.
+LIBRARIES += bs3kit-rm
+bs3kit-rm_TEMPLATE = VBoxBS3KitImg
+bs3kit-rm_INSTTYPE = none
+bs3kit-rm_DEFS = TMPL_MODE=BS3_MODE_RM
+bs3kit-rm_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-first-rm.asm \
+ bs3-mode-CpuDetect.asm \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+
+# The 16-bit BS3Kit library for 16-bit protected kernel+tss.
+LIBRARIES += bs3kit-pe16
+bs3kit-pe16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pe16_INSTTYPE = none
+bs3kit-pe16_DEFS = TMPL_MODE=BS3_MODE_PE16
+bs3kit-pe16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-first-pe16.asm \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+# bs3-mode-CpuDetect.asm
+
+# The 32-bit BS3Kit library for 16-bit protected kernel+tss.
+LIBRARIES += bs3kit-pe16_32
+bs3kit-pe16_32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pe16_32_INSTTYPE = none
+bs3kit-pe16_32_DEFS = TMPL_MODE=BS3_MODE_PE16_32
+bs3kit-pe16_32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The v86 BS3Kit library for 16-bit protected kernel+tss.
+LIBRARIES += bs3kit-pe16_v86
+bs3kit-pe16_v86_TEMPLATE = VBoxBS3KitImg
+bs3kit-pe16_v86_INSTTYPE = none
+bs3kit-pe16_v86_DEFS = TMPL_MODE=BS3_MODE_PE16_V86
+bs3kit-pe16_v86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The 32-bit BS3Kit library for 32-bit protected kernel+tss.
+LIBRARIES += bs3kit-pe32
+bs3kit-pe32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pe32_INSTTYPE = none
+bs3kit-pe32_DEFS = TMPL_MODE=BS3_MODE_PE32
+bs3kit-pe32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-first-init-all-pe32.asm
+
+# The 16-bit BS3Kit library for 32-bit protected kernel+tss.
+LIBRARIES += bs3kit-pe32_16
+bs3kit-pe32_16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pe32_16_INSTTYPE = none
+bs3kit-pe32_16_DEFS = TMPL_MODE=BS3_MODE_PE32_16
+bs3kit-pe32_16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The v8086 BS3Kit library for 32-bit protected kernel+tss.
+LIBRARIES += bs3kit-pev86
+bs3kit-pev86_TEMPLATE = VBoxBS3KitImg
+bs3kit-pev86_INSTTYPE = none
+bs3kit-pev86_DEFS = TMPL_MODE=BS3_MODE_PEV86
+bs3kit-pev86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The 16-bit BS3Kit library for 16-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-pp16
+bs3kit-pp16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pp16_INSTTYPE = none
+bs3kit-pp16_DEFS = TMPL_MODE=BS3_MODE_PP16
+bs3kit-pp16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-CpuDetect.asm \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The 32-bit BS3Kit library for 16-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-pp16_32
+bs3kit-pp16_32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pp16_32_INSTTYPE = none
+bs3kit-pp16_32_DEFS = TMPL_MODE=BS3_MODE_PP16_32
+bs3kit-pp16_32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The v8086 BS3Kit library for 16-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-pp16_v86
+bs3kit-pp16_v86_TEMPLATE = VBoxBS3KitImg
+bs3kit-pp16_v86_INSTTYPE = none
+bs3kit-pp16_v86_DEFS = TMPL_MODE=BS3_MODE_PP16_V86
+bs3kit-pp16_v86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The 32-bit BS3Kit library for 32-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-pp32
+bs3kit-pp32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pp32_INSTTYPE = none
+bs3kit-pp32_DEFS = TMPL_MODE=BS3_MODE_PP32
+bs3kit-pp32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-first-init-all-pp32.asm
+
+# The 16-bit BS3Kit library for 32-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-pp32_16
+bs3kit-pp32_16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pp32_16_INSTTYPE = none
+bs3kit-pp32_16_DEFS = TMPL_MODE=BS3_MODE_PP32_16
+bs3kit-pp32_16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The v8086 BS3Kit library for 32-bit paged protected kernel+tss.
+LIBRARIES += bs3kit-ppv86
+bs3kit-ppv86_TEMPLATE = VBoxBS3KitImg
+bs3kit-ppv86_INSTTYPE = none
+bs3kit-ppv86_DEFS = TMPL_MODE=BS3_MODE_PPV86
+bs3kit-ppv86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+
+# The 16-bit BS3Kit library for 16-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-pae16
+bs3kit-pae16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pae16_INSTTYPE = none
+bs3kit-pae16_DEFS = TMPL_MODE=BS3_MODE_PAE16
+bs3kit-pae16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-CpuDetect.asm \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The 16-bit BS3Kit library for 16-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-pae16_32
+bs3kit-pae16_32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pae16_32_INSTTYPE = none
+bs3kit-pae16_32_DEFS = TMPL_MODE=BS3_MODE_PAE16_32
+bs3kit-pae16_32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The v8086 BS3Kit library for 16-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-pae16_v86
+bs3kit-pae16_v86_TEMPLATE = VBoxBS3KitImg
+bs3kit-pae16_v86_INSTTYPE = none
+bs3kit-pae16_v86_DEFS = TMPL_MODE=BS3_MODE_PAE16_V86
+bs3kit-pae16_v86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The 32-bit BS3Kit library for 32-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-pae32
+bs3kit-pae32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-pae32_INSTTYPE = none
+bs3kit-pae32_DEFS = TMPL_MODE=BS3_MODE_PAE32
+bs3kit-pae32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The 16-bit BS3Kit library for 32-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-pae32_16
+bs3kit-pae32_16_TEMPLATE = VBoxBS3KitImg
+bs3kit-pae32_16_INSTTYPE = none
+bs3kit-pae32_16_DEFS = TMPL_MODE=BS3_MODE_PAE32_16
+bs3kit-pae32_16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The v8086 BS3Kit library for 32-bit PAE paged protected kernel+tss.
+LIBRARIES += bs3kit-paev86
+bs3kit-paev86_TEMPLATE = VBoxBS3KitImg
+bs3kit-paev86_INSTTYPE = none
+bs3kit-paev86_DEFS = TMPL_MODE=BS3_MODE_PAEV86
+bs3kit-paev86_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+
+# The 16-bit long mode BS3Kit library.
+LIBRARIES += bs3kit-lm16
+bs3kit-lm16_TEMPLATE = VBoxBS3KitImg
+bs3kit-lm16_INSTTYPE = none
+bs3kit-lm16_DEFS = TMPL_MODE=BS3_MODE_LM16
+bs3kit-lm16_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-mode-TestDoModesStub.asm \
+ bs3-mode-TestDoModesByOneStub.asm \
+ bs3-mode-TestDoModesByMaxStub.asm
+
+# The 32-bit long mode BS3Kit library.
+LIBRARIES += bs3kit-lm32
+bs3kit-lm32_TEMPLATE = VBoxBS3KitImg32
+bs3kit-lm32_INSTTYPE = none
+bs3kit-lm32_DEFS = TMPL_MODE=BS3_MODE_LM32
+bs3kit-lm32_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES)
+
+# The 64-bit long mode BS3Kit library.
+LIBRARIES += bs3kit-lm64
+bs3kit-lm64_TEMPLATE = VBoxBS3KitImg64
+bs3kit-lm64_INSTTYPE = none
+bs3kit-lm64_DEFS = TMPL_MODE=BS3_MODE_LM64
+bs3kit-lm64_SOURCES = $(VBOX_BS3KIT_MODE_SOURCES) \
+ bs3-first-init-all-lm64.asm
+
+
+#
+# shutdown example.
+#
+MISCBINS += bs3-shutdown
+bs3-shutdown_TEMPLATE = VBoxBS3KitImg
+bs3-shutdown_SOURCES = \
+ bs3-first-pe16.asm \
+ bs3-shutdown.c
+
+
+#
+# DOS Utilities / Testcases.
+#
+MISCBINS += bs3cpudt
+bs3cpudt_TEMPLATE = VBoxBS3KitUtil
+bs3cpudt_SOURCES = \
+ bs3-first-dosexe.asm \
+ bs3cpudt.c
+
+
+#
+# Rule for regenerating bs3kit-mangling-functions-undef.h.
+#
+bs3kit-mangling-code-undef.h: $(PATH_SUB_CURRENT)/bs3kit-mangling-code-define.h $(MAKEFILE)
+ $(SED) \
+ -e 's/#\( *\)define \([a-zA-Z_][a-zA-Z0-9_]*\) .*$(DOLLAR)/#\1undef \2/' \
+ -e 's/Function needing mangling.*$(DOLLAR)/Undefining function mangling - automatically generated by the $@ makefile rule./' \
+ --output $(dir $<)bs3kit-mangling-code-undef.h \
+ $<
+
+#
+# Rule for regenerating bs3kit-mangling-functions-define.h.
+#
+bs3kit-mangling-code-define.h: \
+ $(PATH_SUB_CURRENT)/bs3kit.h \
+ $(PATH_SUB_CURRENT)/bs3-cmn-paging.h \
+ $(PATH_SUB_CURRENT)/bs3-cmn-test.h \
+ $(MAKEFILE)
+ $(APPEND) -tn "$(dir $<)$@" \
+ '/* $(DOLLAR)Id: $(DOLLAR) */' \
+ '/** @file' \
+ ' * BS3Kit - Function needing mangling - generated by the $@ makefile rule.' \
+ ' */' \
+ '' \
+ '/*' \
+ ' * Copyright (C) 2007-$(VBOX_C_YEAR) Oracle and/or its affiliates.' \
+ ' *' \
+ ' * This file is part of VirtualBox base platform packages, as' \
+ ' * available from https://www.virtualbox.org.' \
+ ' *' \
+ ' * This program is free software; you can redistribute it and/or' \
+ ' * modify it under the terms of the GNU General Public License' \
+ ' * as published by the Free Software Foundation, in version 3 of the' \
+ ' * License.' \
+ ' *' \
+ ' * This program is distributed in the hope that it will be useful, but' \
+ ' * WITHOUT ANY WARRANTY; without even the implied warranty of' \
+ ' * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU' \
+ ' * General Public License for more details.' \
+ ' *' \
+ ' * You should have received a copy of the GNU General Public License' \
+ ' * along with this program; if not, see <https://www.gnu.org/licenses>.' \
+ ' *' \
+ ' * The contents of this file may alternatively be used under the terms' \
+ ' * of the Common Development and Distribution License Version 1.0' \
+ ' * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included' \
+ ' * in the VirtualBox distribution, in which case the provisions of the' \
+ ' * CDDL are applicable instead of those of the GPL.' \
+ ' *' \
+ ' * You may elect to license modified versions of this file under the' \
+ ' * terms and conditions of either the GPL or the CDDL or both.' \
+ ' *' \
+ ' * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0' \
+ ' */' \
+ ''
+ $(SED) -n \
+ -e 's/^ *BS3_CMN_PROTO_STUB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/#define \1 BS3_CMN_MANGLER(\1)/p' \
+ -e 's/^ *BS3_CMN_PROTO_NOSB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/#define \1 BS3_CMN_MANGLER(\1)/p' \
+ -e 's/^ *BS3_CMN_PROTO_FARSTUB([^,]*,[^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/#define \1 BS3_CMN_MANGLER(\1)/p' \
+ $(filter %.h,$^) | sort >> "$(dir $<)bs3kit-mangling-code-define.h"
+ $(APPEND) -n "$(dir $<)$@" '#ifndef BS3_CMN_ONLY'
+ $(SED) -n \
+ -e 's/^ *BS3_MODE_PROTO_STUB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/# define \1 BS3_MODE_MANGLER(\1)/p' \
+ -e 's/^ *BS3_MODE_PROTO_NOSB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/# define \1 BS3_MODE_MANGLER(\1)/p' \
+ -e 's/^ *BS3_MODE_PROTO_FARSTUB([^,]*,[^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/# define \1 BS3_MODE_MANGLER(\1)/p' \
+ $(filter %.h,$^) | sort >> "$(dir $<)bs3kit-mangling-code-define.h"
+ $(APPEND) -n "$(dir $<)$@" '#endif /* !BS3_CMN_ONLY */'
+
+#
+# Rule for regenerating bs3kit-autostubs.kmk.
+#
+bs3kit-autostubs.kmk: \
+ $(PATH_SUB_CURRENT)/bs3kit.h \
+ $(PATH_SUB_CURRENT)/bs3-cmn-memory.h \
+ $(PATH_SUB_CURRENT)/bs3-cmn-paging.h \
+ $(PATH_SUB_CURRENT)/bs3-cmn-test.h \
+ $(MAKEFILE)
+ $(APPEND) -tn "$(dir $<)$@" \
+ '# $(DOLLAR)Id: $(DOLLAR)' \
+ '## @file' \
+ '# BS3Kit - Automatic near/far stubs - generated by the $@ makefile rule.' \
+ '#' \
+ '' \
+ '#' \
+ '# Copyright (C) 2007-$(VBOX_C_YEAR) Oracle and/or its affiliates.' \
+ '#' \
+ '# This file is part of VirtualBox base platform packages, as' \
+ '# available from https://www.virtualbox.org.' \
+ '#' \
+ '# This program is free software; you can redistribute it and/or' \
+ '# modify it under the terms of the GNU General Public License' \
+ '# as published by the Free Software Foundation, in version 3 of the' \
+ '# License.' \
+ '#' \
+ '# This program is distributed in the hope that it will be useful, but' \
+ '# WITHOUT ANY WARRANTY; without even the implied warranty of' \
+ '# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU' \
+ '# General Public License for more details.' \
+ '#' \
+ '# You should have received a copy of the GNU General Public License' \
+ '# along with this program; if not, see <https://www.gnu.org/licenses>.' \
+ '#' \
+ '# The contents of this file may alternatively be used under the terms' \
+ '# of the Common Development and Distribution License Version 1.0' \
+ '# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included' \
+ '# in the VirtualBox distribution, in which case the provisions of the' \
+ '# CDDL are applicable instead of those of the GPL.' \
+ '#' \
+ '# You may elect to license modified versions of this file under the' \
+ '# terms and conditions of either the GPL or the CDDL or both.' \
+ '#' \
+ '# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0' \
+ '#' \
+ ''
+ $(SED) -n \
+ -e '/^ *BS3_CMN_PROTO_STUB/p' \
+ -e '/^ *BS3_CMN_PROTO_FARSTUB/p' \
+ -e '/^ *BS3_MODE_PROTO_STUB/p' \
+ -e '/^ *BS3_MODE_PROTO_FARSTUB/p' \
+ $(filter %.h,$^) \
+ | sort \
+ | $(SED) -n \
+ -e 's/^ *BS3_CMN_PROTO_STUB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/\$(DOLLAR)(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,\1)/p' \
+ -e 's/^ *BS3_MODE_PROTO_STUB([^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/\$(DOLLAR)(call BS3KIT_FN_GEN_MODE_NEARSTUB,bs3kit-common-16,\1)/p' \
+ -e 's/^ *BS3_CMN_PROTO_FARSTUB( *\([^,]*\),[^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/\$(DOLLAR)(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,\2,\1)/p' \
+ -e 's/^ *BS3_MODE_PROTO_FARSTUB( *\([^,]*\),[^,]*, *\([a-zA-Z_][a-zA-Z0-9_]*\) *,.*$(DOLLAR)/\$(DOLLAR)(call BS3KIT_FN_GEN_MODE_FARSTUB,bs3kit-common-16,\2,\1)/p' \
+ --append "$(dir $<)$@"
+
+bs3kit-update:: bs3kit-autostubs.kmk bs3kit-mangling-code-define.h bs3kit-mangling-code-undef.h
+.NOTPARALLEL: bs3kit-autostubs.kmk bs3kit-mangling-code-define.h bs3kit-mangling-code-undef.h
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp b/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp
new file mode 100644
index 00000000..0855c35f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp
@@ -0,0 +1,362 @@
+/* $Id: VBoxBs3Linker.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - Boot Sector 3 "linker".
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <iprt/types.h>
+#include <iprt/assert.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#pragma pack(1)
+typedef struct BS3BOOTSECTOR
+{
+ uint8_t abJmp[3];
+ char abOemId[8];
+ /** @name EBPB, DOS 4.0 style.
+ * @{ */
+ uint16_t cBytesPerSector; /**< 00bh */
+ uint8_t cSectorsPerCluster; /**< 00dh */
+ uint16_t cReservedSectors; /**< 00eh */
+ uint8_t cFATs; /**< 010h */
+ uint16_t cRootDirEntries; /**< 011h */
+ uint16_t cTotalSectors; /**< 013h */
+ uint8_t bMediaDescriptor; /**< 015h */
+ uint16_t cSectorsPerFAT; /**< 016h */
+ uint16_t cPhysSectorsPerTrack; /**< 018h */
+ uint16_t cHeads; /**< 01ah */
+ uint32_t cHiddentSectors; /**< 01ch */
+ uint32_t cLargeTotalSectors; /**< 020h - We (ab)use this to indicate the number of sectors to load. */
+ uint8_t bBootDrv; /**< 024h */
+ uint8_t bFlagsEtc; /**< 025h */
+ uint8_t bExtendedSignature; /**< 026h */
+ uint32_t dwSerialNumber; /**< 027h */
+ char abLabel[11]; /**< 02bh */
+ char abFSType[8]; /**< 036h */
+ /** @} */
+} BS3BOOTSECTOR;
+#pragma pack()
+typedef BS3BOOTSECTOR *PBS3BOOTSECTOR;
+
+AssertCompileMemberOffset(BS3BOOTSECTOR, cLargeTotalSectors, 0x20);
+AssertCompileMemberOffset(BS3BOOTSECTOR, abLabel, 0x2b);
+AssertCompileMemberOffset(BS3BOOTSECTOR, abFSType, 0x36);
+
+#define BS3_OEMID "BS3Kit\n\n"
+#define BS3_FSTYPE "RawCode\n"
+#define BS3_LABEL "VirtualBox\n"
+#define BS3_MAX_SIZE UINT32_C(491520) /* 480KB */
+
+
+int main(int argc, char **argv)
+{
+ const char *pszOutput = NULL;
+ struct BS3LNKINPUT
+ {
+ const char *pszFile;
+ FILE *pFile;
+ uint32_t cbFile;
+ } *paInputs = (struct BS3LNKINPUT *)calloc(sizeof(paInputs[0]), argc);
+ unsigned cInputs = 0;
+ uint32_t cSectors = 0;
+
+ /*
+ * Scan the arguments.
+ */
+ for (int i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ const char *pszOpt = &argv[i][1];
+ if (*pszOpt == '-')
+ {
+ /* Convert long options to short ones. */
+ pszOpt--;
+ if (!strcmp(pszOpt, "--output"))
+ pszOpt = "o";
+ else if (!strcmp(pszOpt, "--version"))
+ pszOpt = "V";
+ else if (!strcmp(pszOpt, "--help"))
+ pszOpt = "h";
+ else
+ {
+ fprintf(stderr, "syntax errro: Unknown options '%s'\n", pszOpt);
+ free(paInputs);
+ return 2;
+ }
+ }
+
+ /* Process the list of short options. */
+ while (*pszOpt)
+ {
+ switch (*pszOpt++)
+ {
+ case 'o':
+ {
+ const char *pszValue = pszOpt;
+ pszOpt = strchr(pszOpt, '\0');
+ if (*pszValue == '=')
+ pszValue++;
+ else if (!*pszValue)
+ {
+ if (i + 1 >= argc)
+ {
+ fprintf(stderr, "syntax error: The --output option expects a filename.\n");
+ free(paInputs);
+ return 12;
+ }
+ pszValue = argv[++i];
+ }
+ if (pszOutput)
+ {
+ fprintf(stderr, "Only one output file is allowed. You've specified '%s' and '%s'\n",
+ pszOutput, pszValue);
+ free(paInputs);
+ return 2;
+ }
+ pszOutput = pszValue;
+ pszOpt = "";
+ break;
+ }
+
+ case 'V':
+ printf("%s\n", "$Revision: 155244 $");
+ free(paInputs);
+ return 0;
+
+ case '?':
+ case 'h':
+ printf("usage: %s [options] -o <output> <input1> [input2 ... [inputN]]\n",
+ argv[0]);
+ free(paInputs);
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Add to input file collection.
+ */
+ paInputs[cInputs].pszFile = argv[i];
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ FILE *pFile = fopen(paInputs[cInputs].pszFile, "rb");
+#else
+ FILE *pFile = fopen(paInputs[cInputs].pszFile, "r");
+#endif
+ if (pFile)
+ {
+ if (fseek(pFile, 0, SEEK_END) == 0)
+ {
+ paInputs[cInputs].cbFile = (uint32_t)ftell(pFile);
+ if (fseek(pFile, 0, SEEK_SET) == 0)
+ {
+ if (cInputs != 0 || paInputs[cInputs].cbFile == 512)
+ {
+ cSectors += RT_ALIGN_32(paInputs[cInputs].cbFile, 512) / 512;
+ if (cSectors <= BS3_MAX_SIZE / 512)
+ {
+ if (cSectors > 0)
+ {
+ paInputs[cInputs].pFile = pFile;
+ pFile = NULL;
+ }
+ else
+ fprintf(stderr, "error: empty input file: '%s'\n", paInputs[cInputs].pszFile);
+ }
+ else
+ fprintf(stderr, "error: input is too big: %u bytes, %u sectors (max %u bytes, %u sectors)\n"
+ "info: detected loading '%s'\n",
+ cSectors * 512, cSectors, BS3_MAX_SIZE, BS3_MAX_SIZE / 512,
+ paInputs[cInputs].pszFile);
+ }
+ else
+ fprintf(stderr, "error: first input file (%s) must be exactly 512 bytes\n", paInputs[cInputs].pszFile);
+ }
+ else
+ fprintf(stderr, "error: seeking to start of '%s' failed\n", paInputs[cInputs].pszFile);
+ }
+ else
+ fprintf(stderr, "error: seeking to end of '%s' failed\n", paInputs[cInputs].pszFile);
+ }
+ else
+ fprintf(stderr, "error: Failed to open input file '%s' for reading\n", paInputs[cInputs].pszFile);
+ if (pFile)
+ {
+ free(paInputs);
+ return 1;
+ }
+ cInputs++;
+ }
+ }
+
+ if (!pszOutput)
+ {
+ fprintf(stderr, "syntax error: No output file was specified (-o or --output).\n");
+ free(paInputs);
+ return 2;
+ }
+ if (cInputs == 0)
+ {
+ fprintf(stderr, "syntax error: No input files was specified.\n");
+ free(paInputs);
+ return 2;
+ }
+
+ /*
+ * Do the job.
+ */
+ /* Open the output file. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ FILE *pOutput = fopen(pszOutput, "wb");
+#else
+ FILE *pOutput = fopen(pszOutput, "w");
+#endif
+ if (!pOutput)
+ {
+ fprintf(stderr, "error: Failed to open output file '%s' for writing\n", pszOutput);
+ free(paInputs);
+ return 1;
+ }
+
+ /* Copy the input files to the output file, with sector padding applied. */
+ int rcExit = 0;
+ size_t off = 0;
+ for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
+ {
+ uint8_t abBuf[4096]; /* Must be multiple of 512! */
+ uint32_t cbToRead = paInputs[i].cbFile;
+ while (cbToRead > 0)
+ {
+ /* Read a block from the input file. */
+ uint32_t const cbThisRead = RT_MIN(cbToRead, sizeof(abBuf));
+ size_t cbRead = fread(abBuf, sizeof(uint8_t), cbThisRead, paInputs[i].pFile);
+ if (cbRead != cbThisRead)
+ {
+ fprintf(stderr, "error: Error reading '%s' (got %d bytes, wanted %u).\n",
+ paInputs[i].pszFile, (int)cbRead, (unsigned)cbThisRead);
+ rcExit = 1;
+ break;
+ }
+ cbToRead -= cbThisRead;
+
+ /* Padd the end of the file if necessary. */
+ if ((cbRead & 0x1ff) != 0)
+ {
+ memset(&abBuf[cbRead], 0, 4096 - cbRead);
+ cbRead = (cbRead + 0x1ff) & ~0x1ffU;
+ }
+
+ /* Patch the BPB of the first file. */
+ if (off == 0)
+ {
+ PBS3BOOTSECTOR pBs = (PBS3BOOTSECTOR)&abBuf[0];
+ if ( memcmp(pBs->abLabel, RT_STR_TUPLE(BS3_LABEL)) == 0
+ && memcmp(pBs->abFSType, RT_STR_TUPLE(BS3_FSTYPE)) == 0
+ && memcmp(pBs->abOemId, RT_STR_TUPLE(BS3_OEMID)) == 0)
+ pBs->cLargeTotalSectors = cSectors;
+ else
+ {
+ fprintf(stderr, "error: Didn't find magic strings in the first file (%s).\n", paInputs[i].pszFile);
+ rcExit = 1;
+ }
+ }
+
+ /* Write the block to the output file. */
+ if (fwrite(abBuf, sizeof(uint8_t), cbRead, pOutput) == cbRead)
+ off += cbRead;
+ else
+ {
+ fprintf(stderr, "error: fwrite failed\n");
+ rcExit = 1;
+ break;
+ }
+ }
+
+ if (ferror(paInputs[i].pFile))
+ {
+ fprintf(stderr, "error: Error reading '%s'.\n", paInputs[i].pszFile);
+ rcExit = 1;
+ }
+ }
+
+ /* Close the input files. */
+ for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
+ fclose(paInputs[i].pFile);
+ free(paInputs);
+
+ /* Avoid output sizes that makes the FDC code think it's a single sided
+ floppy. The BIOS always report double sided floppies, and even if we
+ the bootsector adjust it's bMaxHeads value when getting a 20h error
+ we end up with a garbaged image (seems somewhere in the BIOS/FDC it is
+ still treated as a double sided floppy and we get half the data we want
+ and with gaps).
+
+ Similarly, if the size is 320KB or 360KB the FDC detects it as a double
+ sided 5.25" floppy with 40 tracks, while the BIOS keeps reporting a
+ 1.44MB 3.5" floppy. So, just avoid those sizes too. */
+ uint32_t cbOutput = ftell(pOutput);
+ if ( cbOutput == 512 * 8 * 40 * 1 /* 160kB 5"1/4 SS */
+ || cbOutput == 512 * 9 * 40 * 1 /* 180kB 5"1/4 SS */
+ || cbOutput == 512 * 8 * 40 * 2 /* 320kB 5"1/4 DS */
+ || cbOutput == 512 * 9 * 40 * 2 /* 360kB 5"1/4 DS */ )
+ {
+ static uint8_t const s_abZeroSector[512] = { 0 };
+ if (fwrite(s_abZeroSector, sizeof(uint8_t), sizeof(s_abZeroSector), pOutput) != sizeof(s_abZeroSector))
+ {
+ fprintf(stderr, "error: fwrite failed (padding)\n");
+ rcExit = 1;
+ }
+ }
+
+ /* Finally, close the output file (can fail because of buffered data). */
+ if (fclose(pOutput) != 0)
+ {
+ fprintf(stderr, "error: Error closing '%s'.\n", pszOutput);
+ rcExit = 1;
+ }
+
+ fclose(stderr);
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3ObjConverter.cpp b/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3ObjConverter.cpp
new file mode 100644
index 00000000..ce789b21
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3ObjConverter.cpp
@@ -0,0 +1,5530 @@
+/* $Id: VBoxBs3ObjConverter.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - Boot Sector 3 object file convert.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <iprt/types.h>
+#include <iprt/ctype.h>
+#include <iprt/assert.h>
+#include <iprt/sort.h>
+#include <iprt/x86.h>
+
+#include <iprt/formats/elf64.h>
+#include <iprt/formats/elf-amd64.h>
+#include <iprt/formats/pecoff.h>
+#include <iprt/formats/omf.h>
+#include <iprt/formats/codeview.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 64 && !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
+# define ELF_FMT_X64 "lx"
+# define ELF_FMT_D64 "ld"
+#else
+# define ELF_FMT_X64 "llx"
+# define ELF_FMT_D64 "lld"
+#endif
+
+/** Compares an OMF string with a constant string. */
+#define IS_OMF_STR_EQUAL_EX(a_cch1, a_pch1, a_szConst2) \
+ ( (a_cch1) == sizeof(a_szConst2) - 1 && memcmp(a_pch1, a_szConst2, sizeof(a_szConst2) - 1) == 0 )
+
+/** Compares an OMF string with a constant string. */
+#define IS_OMF_STR_EQUAL(a_pchZeroPrefixed, a_szConst2) \
+ IS_OMF_STR_EQUAL_EX((uint8_t)((a_pchZeroPrefixed)[0]), &((a_pchZeroPrefixed)[1]), a_szConst2)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Verbosity level. */
+static unsigned g_cVerbose = 0;
+/** Indicates that it's output from the 16-bit watcom C or C++ compiler.
+ * We will do some massaging for fixup records when this is used. */
+static bool g_f16BitWatcomC = false;
+
+
+/*
+ * Minimal assertion support.
+ */
+
+RTDECL(bool) RTAssertShouldPanic(void)
+{
+ return true;
+}
+
+
+RTDECL(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ fprintf(stderr,
+ "VBoxBs3ObjConverter: assertion failed in %s (%s:%u)!\n"
+ "VBoxBs3ObjConverter: %s\n",
+ pszFunction, pszFile, uLine, pszExpr);
+}
+
+
+/**
+ * Opens a file for binary reading or writing.
+ *
+ * @returns File stream handle.
+ * @param pszFile The name of the file.
+ * @param fWrite Whether to open for writing or reading.
+ */
+static FILE *openfile(const char *pszFile, bool fWrite)
+{
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ FILE *pFile = fopen(pszFile, fWrite ? "wb" : "rb");
+#else
+ FILE *pFile = fopen(pszFile, fWrite ? "w" : "r");
+#endif
+ if (!pFile)
+ fprintf(stderr, "error: Failed to open '%s' for %s: %s (%d)\n",
+ pszFile, fWrite ? "writing" : "reading", strerror(errno), errno);
+ return pFile;
+}
+
+
+/**
+ * Read the given file into memory.
+ *
+ * @returns true on success, false on failure.
+ * @param pszFile The file to read.
+ * @param ppvFile Where to return the memory.
+ * @param pcbFile Where to return the size.
+ */
+static bool readfile(const char *pszFile, void **ppvFile, size_t *pcbFile)
+{
+ FILE *pFile = openfile(pszFile, false);
+ if (pFile)
+ {
+ /*
+ * Figure the size.
+ */
+ if (fseek(pFile, 0, SEEK_END) == 0)
+ {
+ long cbFile = ftell(pFile);
+ if (cbFile > 0)
+ {
+ if (fseek(pFile, SEEK_SET, 0) == 0)
+ {
+ /*
+ * Allocate and read content.
+ */
+ void *pvFile = malloc((size_t)cbFile);
+ if (pvFile)
+ {
+ if (fread(pvFile, cbFile, 1, pFile) == 1)
+ {
+ *ppvFile = pvFile;
+ *pcbFile = (size_t)cbFile;
+ fclose(pFile);
+ return true;
+ }
+ free(pvFile);
+ fprintf(stderr, "error: fread failed in '%s': %s (%d)\n", pszFile, strerror(errno), errno);
+ }
+ else
+ fprintf(stderr, "error: failed to allocate %ld bytes of memory for '%s'\n", cbFile, pszFile);
+ }
+ else
+ fprintf(stderr, "error: fseek #2 failed in '%s': %s (%d)\n", pszFile, strerror(errno), errno);
+ }
+ else
+ fprintf(stderr, "error: ftell failed in '%s': %s (%d)\n", pszFile, strerror(errno), errno);
+ }
+ else
+ fprintf(stderr, "error: fseek #1 failed in '%s': %s (%d)\n", pszFile, strerror(errno), errno);
+ fclose(pFile);
+ }
+ return false;
+}
+
+
+/**
+ * Write the given file into memory.
+ *
+ * @returns true on success, false on failure.
+ * @param pszFile The file to write.
+ * @param pvFile Where to return the memory.
+ * @param cbFile Where to return the size.
+ */
+static bool writefile(const char *pszFile, void const *pvFile, size_t cbFile)
+{
+ remove(pszFile);
+
+ FILE *pFile = openfile(pszFile, true);
+ if (pFile)
+ {
+ if (fwrite(pvFile, cbFile, 1, pFile) == 1)
+ {
+ fclose(pFile);
+ return true;
+ }
+ fprintf(stderr, "error: fwrite failed in '%s': %s (%d)\n", pszFile, strerror(errno), errno);
+ fclose(pFile);
+ }
+ return false;
+}
+
+
+/**
+ * Reports an error and returns false.
+ *
+ * @returns false
+ * @param pszFile The filename.
+ * @param pszFormat The message format string.
+ * @param ... Format arguments.
+ */
+static bool error(const char *pszFile, const char *pszFormat, ...)
+{
+ fflush(stdout);
+ fprintf(stderr, "error: %s: ", pszFile);
+ va_list va;
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+ return false;
+}
+
+
+
+/*********************************************************************************************************************************
+* Common OMF Writer *
+*********************************************************************************************************************************/
+
+/** Entry for each segment/section in the source format for mapping it to a
+ * segment defintion. */
+typedef struct OMFTOSEGDEF
+{
+ /** The segment defintion index of the section, UINT16_MAX if not translated. */
+ uint16_t iSegDef;
+ /** The group index for this segment, UINT16_MAX if not applicable. */
+ uint16_t iGrpDef;
+ /** The class name table entry, UINT16_MAX if not applicable. */
+ uint16_t iClassNm;
+ /** The group name for this segment, UINT16_MAX if not applicable. */
+ uint16_t iGrpNm;
+ /** The group name for this segment, UINT16_MAX if not applicable. */
+ uint16_t iSegNm;
+ /** The number of public definitions for this segment. */
+ uint32_t cPubDefs;
+ /** The segment name (OMF). */
+ char *pszName;
+} OMFTOSEGDEF;
+/** Pointer to a segment/section to segdef mapping. */
+typedef OMFTOSEGDEF *POMFTOSEGDEF;
+
+/** Symbol table translation type. */
+typedef enum OMFSYMTYPE
+{
+ /** Invalid symbol table entry (aux sym). */
+ OMFSYMTYPE_INVALID = 0,
+ /** Ignored. */
+ OMFSYMTYPE_IGNORED,
+ /** A public defintion. */
+ OMFSYMTYPE_PUBDEF,
+ /** An external definition. */
+ OMFSYMTYPE_EXTDEF,
+ /** A segment reference for fixups. */
+ OMFSYMTYPE_SEGDEF,
+ /** Internal symbol that may be used for fixups. */
+ OMFSYMTYPE_INTERNAL
+} OMFSYMTYPE;
+
+/** Symbol table translation. */
+typedef struct OMFSYMBOL
+{
+ /** What this source symbol table entry should be translated into. */
+ OMFSYMTYPE enmType;
+ /** The OMF table index. UINT16_MAX if not applicable. */
+ uint16_t idx;
+ /** The OMF segment definition index. */
+ uint16_t idxSegDef;
+ /** The OMF group definition index. */
+ uint16_t idxGrpDef;
+} OMFSYMBOL;
+/** Pointer to an source symbol table translation entry. */
+typedef OMFSYMBOL *POMFSYMBOL;
+
+/** OMF Writer LNAME lookup record. */
+typedef struct OMFWRLNAME
+{
+ /** Pointer to the next entry with the name hash. */
+ struct OMFWRLNAME *pNext;
+ /** The LNAMES index number. */
+ uint16_t idxName;
+ /** The name length. */
+ uint8_t cchName;
+ /** The name (variable size). */
+ char szName[1];
+} OMFWRLNAME;
+/** Pointer to the a OMF writer LNAME lookup record. */
+typedef OMFWRLNAME *POMFWRLNAME;
+
+/**
+ * OMF converter & writer instance.
+ */
+typedef struct OMFWRITER
+{
+ /** The source file name (for bitching). */
+ const char *pszSrc;
+ /** The destination output file. */
+ FILE *pDst;
+
+ /** Pointer to the table mapping from source segments/section to segdefs. */
+ POMFTOSEGDEF paSegments;
+ /** Number of source segments/sections. */
+ uint32_t cSegments;
+
+ /** Number of entries in the source symbol table. */
+ uint32_t cSymbols;
+ /** Pointer to the table mapping from source symbols to OMF stuff. */
+ POMFSYMBOL paSymbols;
+
+ /** LEDATA segment offset. */
+ uint32_t offSeg;
+ /** Start of the current LEDATA record. */
+ uint32_t offSegRec;
+ /** The LEDATA end segment offset. */
+ uint32_t offSegEnd;
+ /** The current LEDATA segment. */
+ uint16_t idx;
+
+ /** The index of the next list of names entry. */
+ uint16_t idxNextName;
+
+ /** The current record size. */
+ uint16_t cbRec;
+ /** The current record type */
+ uint8_t bType;
+ /** The record data buffer (too large, but whatever). */
+ uint8_t abData[_1K + 64];
+
+ /** Current FIXUPP entry. */
+ uint8_t iFixupp;
+ /** FIXUPP records being prepared for LEDATA currently stashed in abData.
+ * We may have to adjust addend values in the LEDATA when converting to OMF
+ * fixups. */
+ struct
+ {
+ uint16_t cbRec;
+ uint8_t abData[_1K + 64];
+ uint8_t abAlign[2]; /**< Alignment padding. */
+ } aFixupps[3];
+
+ /** The index of the FLAT group. */
+ uint16_t idxGrpFlat;
+ /** The EXTDEF index of the __ImageBase symbol. */
+ uint16_t idxExtImageBase;
+
+ /** LNAME lookup hash table. To avoid too many duplicates. */
+ POMFWRLNAME apNameLookup[63];
+} OMFWRITE;
+/** Pointer to an OMF writer. */
+typedef OMFWRITE *POMFWRITER;
+
+
+/**
+ * Creates an OMF writer instance.
+ */
+static POMFWRITER omfWriter_Create(const char *pszSrc, uint32_t cSegments, uint32_t cSymbols, FILE *pDst)
+{
+ POMFWRITER pThis = (POMFWRITER)calloc(sizeof(OMFWRITER), 1);
+ if (pThis)
+ {
+ pThis->pszSrc = pszSrc;
+ pThis->idxNextName = 1; /* We start counting at 1. */
+ pThis->cSegments = cSegments;
+ pThis->paSegments = (POMFTOSEGDEF)calloc(sizeof(OMFTOSEGDEF), cSegments);
+ if (pThis->paSegments)
+ {
+ pThis->cSymbols = cSymbols;
+ pThis->paSymbols = (POMFSYMBOL)calloc(sizeof(OMFSYMBOL), cSymbols);
+ if (pThis->paSymbols)
+ {
+ pThis->pDst = pDst;
+ return pThis;
+ }
+ free(pThis->paSegments);
+ }
+ free(pThis);
+ }
+ error(pszSrc, "Out of memory!\n");
+ return NULL;
+}
+
+/**
+ * Destroys the given OMF writer instance.
+ * @param pThis OMF writer instance.
+ */
+static void omfWriter_Destroy(POMFWRITER pThis)
+{
+ free(pThis->paSymbols);
+
+ for (uint32_t i = 0; i < pThis->cSegments; i++)
+ if (pThis->paSegments[i].pszName)
+ free(pThis->paSegments[i].pszName);
+
+ free(pThis->paSegments);
+
+ uint32_t i = RT_ELEMENTS(pThis->apNameLookup);
+ while (i-- > 0)
+ {
+ POMFWRLNAME pNext = pThis->apNameLookup[i];
+ pThis->apNameLookup[i] = NULL;
+ while (pNext)
+ {
+ POMFWRLNAME pFree = pNext;
+ pNext = pNext->pNext;
+ free(pFree);
+ }
+ }
+
+ free(pThis);
+}
+
+static bool omfWriter_RecBegin(POMFWRITER pThis, uint8_t bType)
+{
+ pThis->bType = bType;
+ pThis->cbRec = 0;
+ return true;
+}
+
+static bool omfWriter_RecAddU8(POMFWRITER pThis, uint8_t b)
+{
+ if (pThis->cbRec < OMF_MAX_RECORD_PAYLOAD)
+ {
+ pThis->abData[pThis->cbRec++] = b;
+ return true;
+ }
+ return error(pThis->pszSrc, "Exceeded max OMF record length (bType=%#x)!\n", pThis->bType);
+}
+
+static bool omfWriter_RecAddU16(POMFWRITER pThis, uint16_t u16)
+{
+ if (pThis->cbRec + 2U <= OMF_MAX_RECORD_PAYLOAD)
+ {
+ pThis->abData[pThis->cbRec++] = (uint8_t)u16;
+ pThis->abData[pThis->cbRec++] = (uint8_t)(u16 >> 8);
+ return true;
+ }
+ return error(pThis->pszSrc, "Exceeded max OMF record length (bType=%#x)!\n", pThis->bType);
+}
+
+static bool omfWriter_RecAddU32(POMFWRITER pThis, uint32_t u32)
+{
+ if (pThis->cbRec + 4U <= OMF_MAX_RECORD_PAYLOAD)
+ {
+ pThis->abData[pThis->cbRec++] = (uint8_t)u32;
+ pThis->abData[pThis->cbRec++] = (uint8_t)(u32 >> 8);
+ pThis->abData[pThis->cbRec++] = (uint8_t)(u32 >> 16);
+ pThis->abData[pThis->cbRec++] = (uint8_t)(u32 >> 24);
+ return true;
+ }
+ return error(pThis->pszSrc, "Exceeded max OMF record length (bType=%#x)!\n", pThis->bType);
+}
+
+static bool omfWriter_RecAddIdx(POMFWRITER pThis, uint16_t idx)
+{
+ if (idx < 128)
+ return omfWriter_RecAddU8(pThis, (uint8_t)idx);
+ if (idx < _32K)
+ return omfWriter_RecAddU8(pThis, (uint8_t)(idx >> 8) | 0x80)
+ && omfWriter_RecAddU8(pThis, (uint8_t)idx);
+ return error(pThis->pszSrc, "Index out of range %#x\n", idx);
+}
+
+static bool omfWriter_RecAddBytes(POMFWRITER pThis, const void *pvData, size_t cbData)
+{
+ const uint16_t cbNasmHack = OMF_MAX_RECORD_PAYLOAD + 1;
+ if (cbData + pThis->cbRec <= cbNasmHack)
+ {
+ memcpy(&pThis->abData[pThis->cbRec], pvData, cbData);
+ pThis->cbRec += (uint16_t)cbData;
+ return true;
+ }
+ return error(pThis->pszSrc, "Exceeded max OMF record length (bType=%#x, cbData=%#x, cbRec=%#x, max=%#x)!\n",
+ pThis->bType, (unsigned)cbData, pThis->cbRec, OMF_MAX_RECORD_PAYLOAD);
+}
+
+static bool omfWriter_RecAddStringNEx(POMFWRITER pThis, const char *pchString, size_t cchString, bool fPrependUnderscore)
+{
+ if (cchString < 256)
+ {
+ return omfWriter_RecAddU8(pThis, (uint8_t)cchString + fPrependUnderscore)
+ && (!fPrependUnderscore || omfWriter_RecAddU8(pThis, '_'))
+ && omfWriter_RecAddBytes(pThis, pchString, cchString);
+ }
+ return error(pThis->pszSrc, "String too long (%u bytes): '%*.*s'\n",
+ (unsigned)cchString, (int)cchString, (int)cchString, pchString);
+}
+
+static bool omfWriter_RecAddStringN(POMFWRITER pThis, const char *pchString, size_t cchString)
+{
+ return omfWriter_RecAddStringNEx(pThis, pchString, cchString, false /*fPrependUnderscore*/);
+}
+
+static bool omfWriter_RecAddString(POMFWRITER pThis, const char *pszString)
+{
+ return omfWriter_RecAddStringNEx(pThis, pszString, strlen(pszString), false /*fPrependUnderscore*/);
+}
+
+static bool omfWriter_RecEnd(POMFWRITER pThis, bool fAddCrc)
+{
+ if ( !fAddCrc
+ || omfWriter_RecAddU8(pThis, 0))
+ {
+ OMFRECHDR RecHdr = { pThis->bType, RT_H2LE_U16(pThis->cbRec) };
+ if ( fwrite(&RecHdr, sizeof(RecHdr), 1, pThis->pDst) == 1
+ && fwrite(pThis->abData, pThis->cbRec, 1, pThis->pDst) == 1)
+ {
+ pThis->bType = 0;
+ pThis->cbRec = 0;
+ return true;
+ }
+ return error(pThis->pszSrc, "Write error\n");
+ }
+ return false;
+}
+
+static bool omfWriter_RecEndWithCrc(POMFWRITER pThis)
+{
+ return omfWriter_RecEnd(pThis, true /*fAddCrc*/);
+}
+
+
+static bool omfWriter_BeginModule(POMFWRITER pThis, const char *pszFile)
+{
+ return omfWriter_RecBegin(pThis, OMF_THEADR)
+ && omfWriter_RecAddString(pThis, pszFile)
+ && omfWriter_RecEndWithCrc(pThis);
+}
+
+
+/**
+ * Simple stupid string hashing function (for LNAMES)
+ * @returns 8-bit hash.
+ * @param pchName The string.
+ * @param cchName The string length.
+ */
+DECLINLINE(uint8_t) omfWriter_HashStrU8(const char *pchName, size_t cchName)
+{
+ if (cchName)
+ return (uint8_t)(cchName + pchName[cchName >> 1]);
+ return 0;
+}
+
+/**
+ * Looks up a LNAME.
+ *
+ * @returns Index (0..32K) if found, UINT16_MAX if not found.
+ * @param pThis The OMF writer.
+ * @param pchName The name to look up.
+ * @param cchName The length of the name.
+ */
+static uint16_t omfWriter_LNamesLookupN(POMFWRITER pThis, const char *pchName, size_t cchName)
+{
+ uint8_t uHash = omfWriter_HashStrU8(pchName, cchName);
+ uHash %= RT_ELEMENTS(pThis->apNameLookup);
+
+ POMFWRLNAME pCur = pThis->apNameLookup[uHash];
+ while (pCur)
+ {
+ if ( pCur->cchName == cchName
+ && memcmp(pCur->szName, pchName, cchName) == 0)
+ return pCur->idxName;
+ pCur = pCur->pNext;
+ }
+
+ return UINT16_MAX;
+}
+
+/**
+ * Add a LNAME lookup record.
+ *
+ * @returns success indicator.
+ * @param pThis The OMF writer.
+ * @param pchName The name to look up.
+ * @param cchName The length of the name.
+ * @param idxName The name index.
+ */
+static bool omfWriter_LNamesAddLookup(POMFWRITER pThis, const char *pchName, size_t cchName, uint16_t idxName)
+{
+ POMFWRLNAME pCur = (POMFWRLNAME)malloc(sizeof(*pCur) + cchName);
+ if (!pCur)
+ return error("???", "Out of memory!\n");
+
+ pCur->idxName = idxName;
+ pCur->cchName = (uint8_t)cchName;
+ memcpy(pCur->szName, pchName, cchName);
+ pCur->szName[cchName] = '\0';
+
+ uint8_t uHash = omfWriter_HashStrU8(pchName, cchName);
+ uHash %= RT_ELEMENTS(pThis->apNameLookup);
+ pCur->pNext = pThis->apNameLookup[uHash];
+ pThis->apNameLookup[uHash] = pCur;
+
+ return true;
+}
+
+
+static bool omfWriter_LNamesAddN(POMFWRITER pThis, const char *pchName, size_t cchName, uint16_t *pidxName)
+{
+ /* See if we've already got that name in the list. */
+ uint16_t idxName;
+ if (pidxName) /* If pidxName is NULL, we assume the caller might just be passing stuff thru. */
+ {
+ idxName = omfWriter_LNamesLookupN(pThis, pchName, cchName);
+ if (idxName != UINT16_MAX)
+ {
+ *pidxName = idxName;
+ return true;
+ }
+ }
+
+ /* split? */
+ if (pThis->cbRec + 1 /*len*/ + cchName + 1 /*crc*/ > OMF_MAX_RECORD_PAYLOAD)
+ {
+ if (pThis->cbRec == 0)
+ return error(pThis->pszSrc, "Too long LNAME '%*.*s'\n", (int)cchName, (int)cchName, pchName);
+ if ( !omfWriter_RecEndWithCrc(pThis)
+ || !omfWriter_RecBegin(pThis, OMF_LNAMES))
+ return false;
+ }
+
+ idxName = pThis->idxNextName++;
+ if (pidxName)
+ *pidxName = idxName;
+ return omfWriter_RecAddStringN(pThis, pchName, cchName)
+ && omfWriter_LNamesAddLookup(pThis, pchName, cchName, idxName);
+}
+
+static bool omfWriter_LNamesAdd(POMFWRITER pThis, const char *pszName, uint16_t *pidxName)
+{
+ return omfWriter_LNamesAddN(pThis, pszName, strlen(pszName), pidxName);
+}
+
+static bool omfWriter_LNamesBegin(POMFWRITER pThis, bool fAddZeroEntry)
+{
+ /* First entry is an empty string. */
+ return omfWriter_RecBegin(pThis, OMF_LNAMES)
+ && ( pThis->idxNextName > 1
+ || !fAddZeroEntry
+ || omfWriter_LNamesAddN(pThis, "", 0, NULL));
+}
+
+static bool omfWriter_LNamesEnd(POMFWRITER pThis)
+{
+ return omfWriter_RecEndWithCrc(pThis);
+}
+
+
+static bool omfWriter_SegDef(POMFWRITER pThis, uint8_t bSegAttr, uint32_t cbSeg, uint16_t idxSegName, uint16_t idxSegClass,
+ uint16_t idxOverlay = 1 /* NULL entry */)
+{
+ return omfWriter_RecBegin(pThis, OMF_SEGDEF32)
+ && omfWriter_RecAddU8(pThis, bSegAttr)
+ && omfWriter_RecAddU32(pThis, cbSeg)
+ && omfWriter_RecAddIdx(pThis, idxSegName)
+ && omfWriter_RecAddIdx(pThis, idxSegClass)
+ && omfWriter_RecAddIdx(pThis, idxOverlay)
+ && omfWriter_RecEndWithCrc(pThis);
+}
+
+static bool omfWriter_SegDef16(POMFWRITER pThis, uint8_t bSegAttr, uint32_t cbSeg, uint16_t idxSegName, uint16_t idxSegClass,
+ uint16_t idxOverlay = 1 /* NULL entry */)
+{
+ Assert(cbSeg <= UINT16_MAX);
+ return omfWriter_RecBegin(pThis, OMF_SEGDEF16)
+ && omfWriter_RecAddU8(pThis, bSegAttr)
+ && omfWriter_RecAddU16(pThis, cbSeg)
+ && omfWriter_RecAddIdx(pThis, idxSegName)
+ && omfWriter_RecAddIdx(pThis, idxSegClass)
+ && omfWriter_RecAddIdx(pThis, idxOverlay)
+ && omfWriter_RecEndWithCrc(pThis);
+}
+
+static bool omfWriter_GrpDefBegin(POMFWRITER pThis, uint16_t idxGrpName)
+{
+ return omfWriter_RecBegin(pThis, OMF_GRPDEF)
+ && omfWriter_RecAddIdx(pThis, idxGrpName);
+}
+
+static bool omfWriter_GrpDefAddSegDef(POMFWRITER pThis, uint16_t idxSegDef)
+{
+ return omfWriter_RecAddU8(pThis, 0xff)
+ && omfWriter_RecAddIdx(pThis, idxSegDef);
+}
+
+static bool omfWriter_GrpDefEnd(POMFWRITER pThis)
+{
+ return omfWriter_RecEndWithCrc(pThis);
+}
+
+
+static bool omfWriter_PubDefBegin(POMFWRITER pThis, uint16_t idxGrpDef, uint16_t idxSegDef)
+{
+ return omfWriter_RecBegin(pThis, OMF_PUBDEF32)
+ && omfWriter_RecAddIdx(pThis, idxGrpDef)
+ && omfWriter_RecAddIdx(pThis, idxSegDef)
+ && ( idxSegDef != 0
+ || omfWriter_RecAddU16(pThis, 0));
+
+}
+
+static bool omfWriter_PubDefAddN(POMFWRITER pThis, uint32_t uValue, const char *pchString, size_t cchString,
+ bool fPrependUnderscore)
+{
+ /* Split? */
+ if (pThis->cbRec + 1 + cchString + 4 + 1 + 1 + fPrependUnderscore > OMF_MAX_RECORD_PAYLOAD)
+ {
+ if (cchString >= 256)
+ return error(pThis->pszSrc, "PUBDEF string too long %u ('%s')\n",
+ (unsigned)cchString, (int)cchString, (int)cchString, pchString);
+ if (!omfWriter_RecEndWithCrc(pThis))
+ return false;
+
+ /* Figure out the initial data length. */
+ pThis->cbRec = 1 + ((pThis->abData[0] & 0x80) != 0);
+ if (pThis->abData[pThis->cbRec] != 0)
+ pThis->cbRec += 1 + ((pThis->abData[pThis->cbRec] & 0x80) != 0);
+ else
+ pThis->cbRec += 3;
+ pThis->bType = OMF_PUBDEF32;
+ }
+
+ return omfWriter_RecAddStringNEx(pThis, pchString, cchString, fPrependUnderscore)
+ && omfWriter_RecAddU32(pThis, uValue)
+ && omfWriter_RecAddIdx(pThis, 0); /* type */
+}
+
+static bool omfWriter_PubDefAdd(POMFWRITER pThis, uint32_t uValue, const char *pszString, bool fPrependUnderscore)
+{
+ return omfWriter_PubDefAddN(pThis, uValue, pszString, strlen(pszString), fPrependUnderscore);
+}
+
+static bool omfWriter_PubDefEnd(POMFWRITER pThis)
+{
+ return omfWriter_RecEndWithCrc(pThis);
+}
+
+/**
+ * EXTDEF - Begin record.
+ */
+static bool omfWriter_ExtDefBegin(POMFWRITER pThis)
+{
+ return omfWriter_RecBegin(pThis, OMF_EXTDEF);
+
+}
+
+/**
+ * EXTDEF - Add an entry, split record if necessary.
+ */
+static bool omfWriter_ExtDefAddN(POMFWRITER pThis, const char *pchString, size_t cchString, uint16_t idxType,
+ bool fPrependUnderscore)
+{
+ /* Split? */
+ if (pThis->cbRec + 1 + cchString + 1 + 1 + fPrependUnderscore > OMF_MAX_RECORD_PAYLOAD)
+ {
+ if (cchString >= 256)
+ return error(pThis->pszSrc, "EXTDEF string too long %u ('%s')\n",
+ (unsigned)cchString, (int)cchString, (int)cchString, pchString);
+ if ( !omfWriter_RecEndWithCrc(pThis)
+ || !omfWriter_RecBegin(pThis, OMF_EXTDEF))
+ return false;
+ }
+
+ return omfWriter_RecAddStringNEx(pThis, pchString, cchString, fPrependUnderscore)
+ && omfWriter_RecAddIdx(pThis, idxType); /* type */
+}
+
+/**
+ * EXTDEF - Add an entry, split record if necessary.
+ */
+static bool omfWriter_ExtDefAdd(POMFWRITER pThis, const char *pszString, bool fPrependUnderscore)
+{
+ return omfWriter_ExtDefAddN(pThis, pszString, strlen(pszString), 0, fPrependUnderscore);
+}
+
+/**
+ * EXTDEF - End of record.
+ */
+static bool omfWriter_ExtDefEnd(POMFWRITER pThis)
+{
+ return omfWriter_RecEndWithCrc(pThis);
+}
+
+/**
+ * COMENT/LINK_PASS_SEP - Add a link pass separator comment.
+ */
+static bool omfWriter_LinkPassSeparator(POMFWRITER pThis)
+{
+ return omfWriter_RecBegin(pThis, OMF_COMENT)
+ && omfWriter_RecAddU8(pThis, OMF_CTYP_NO_LIST)
+ && omfWriter_RecAddU8(pThis, OMF_CCLS_LINK_PASS_SEP)
+ && omfWriter_RecAddU8(pThis, 1)
+ && omfWriter_RecEndWithCrc(pThis);
+}
+
+
+/**
+ * LEDATA + FIXUPP - Begin records.
+ */
+static bool omfWriter_LEDataBegin(POMFWRITER pThis, uint16_t idxSeg, uint32_t offSeg)
+{
+ if ( omfWriter_RecBegin(pThis, OMF_LEDATA32)
+ && omfWriter_RecAddIdx(pThis, idxSeg)
+ && omfWriter_RecAddU32(pThis, offSeg))
+ {
+ pThis->idx = idxSeg;
+ pThis->offSeg = offSeg;
+ pThis->offSegRec = offSeg;
+ pThis->offSegEnd = offSeg + OMF_MAX_RECORD_PAYLOAD - 1 /*CRC*/ - pThis->cbRec;
+ pThis->offSegEnd &= ~(uint32_t)7; /* qword align. */
+
+ /* Reset the associated FIXUPP records. */
+ pThis->iFixupp = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aFixupps); i++)
+ pThis->aFixupps[i].cbRec = 0;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * LEDATA + FIXUPP - Begin records.
+ */
+static bool omfWriter_LEDataBeginEx(POMFWRITER pThis, uint16_t idxSeg, uint32_t offSeg,
+ uint32_t cbData, uint32_t cbRawData, void const *pbRawData, uint8_t **ppbData)
+{
+ if ( omfWriter_RecBegin(pThis, OMF_LEDATA32)
+ && omfWriter_RecAddIdx(pThis, idxSeg)
+ && omfWriter_RecAddU32(pThis, offSeg))
+ {
+ if ( cbData <= _1K
+ && pThis->cbRec + cbData + 1 <= OMF_MAX_RECORD_PAYLOAD)
+ {
+ uint8_t *pbDst = &pThis->abData[pThis->cbRec];
+ if (ppbData)
+ *ppbData = pbDst;
+
+ if (cbRawData)
+ memcpy(pbDst, pbRawData, RT_MIN(cbData, cbRawData));
+ if (cbData > cbRawData)
+ memset(&pbDst[cbRawData], 0, cbData - cbRawData);
+
+ pThis->cbRec += cbData;
+ pThis->idx = idxSeg;
+ pThis->offSegRec = offSeg;
+ pThis->offSeg = offSeg + cbData;
+ pThis->offSegEnd = offSeg + cbData;
+
+ /* Reset the associated FIXUPP records. */
+ pThis->iFixupp = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aFixupps); i++)
+ pThis->aFixupps[i].cbRec = 0;
+ return true;
+ }
+ error(pThis->pszSrc, "Too much data for LEDATA record! (%#x)\n", (unsigned)cbData);
+ }
+ return false;
+}
+
+/**
+ * LEDATA + FIXUPP - Add FIXUPP subrecord bytes, split if necessary.
+ */
+static bool omfWriter_LEDataAddFixuppBytes(POMFWRITER pThis, void *pvSubRec, size_t cbSubRec)
+{
+ /* Split? */
+ unsigned iFixupp = pThis->iFixupp;
+ if (pThis->aFixupps[iFixupp].cbRec + cbSubRec >= OMF_MAX_RECORD_PAYLOAD)
+ {
+ if (g_cVerbose >= 2)
+ printf("debug: FIXUPP split\n");
+ iFixupp++;
+ if (iFixupp >= RT_ELEMENTS(pThis->aFixupps))
+ return error(pThis->pszSrc, "Out of FIXUPP records\n");
+ pThis->iFixupp = iFixupp;
+ pThis->aFixupps[iFixupp].cbRec = 0; /* paranoia */
+ }
+
+ /* Append the sub-record data. */
+ memcpy(&pThis->aFixupps[iFixupp].abData[pThis->aFixupps[iFixupp].cbRec], pvSubRec, cbSubRec);
+ pThis->aFixupps[iFixupp].cbRec += (uint16_t)cbSubRec;
+ return true;
+}
+
+/**
+ * LEDATA + FIXUPP - Add fixup, split if necessary.
+ */
+static bool omfWriter_LEDataAddFixup(POMFWRITER pThis, uint16_t offDataRec, bool fSelfRel, uint8_t bLocation,
+ uint8_t bFrame, uint16_t idxFrame,
+ uint8_t bTarget, uint16_t idxTarget, bool fTargetDisp, uint32_t offTargetDisp)
+{
+ if (g_cVerbose >= 2)
+ printf("debug: FIXUP[%#x]: off=%#x frame=%u:%#x target=%u:%#x disp=%d:%#x\n", pThis->aFixupps[pThis->iFixupp].cbRec,
+ offDataRec, bFrame, idxFrame, bTarget, idxTarget, fTargetDisp, offTargetDisp);
+
+ if ( offDataRec >= _1K
+ || bFrame >= 6
+ || bTarget > 6
+ || idxFrame >= _32K
+ || idxTarget >= _32K
+ || fTargetDisp != (bTarget <= OMF_FIX_T_FRAME_NO) )
+ return error(pThis->pszSrc,
+ "Internal error: offDataRec=%#x bFrame=%u idxFrame=%#x bTarget=%u idxTarget=%#x fTargetDisp=%d offTargetDisp=%#x\n",
+ offDataRec, bFrame, idxFrame, bTarget, idxTarget, fTargetDisp, offTargetDisp);
+
+
+ /*
+ * Encode the FIXUP subrecord.
+ */
+ uint8_t abFixup[16];
+ uint8_t off = 0;
+ /* Location */
+ abFixup[off++] = (offDataRec >> 8) | (bLocation << 2) | ((uint8_t)!fSelfRel << 6) | 0x80;
+ abFixup[off++] = (uint8_t)offDataRec;
+ /* Fix Data */
+ abFixup[off++] = 0x00 /*F=0*/ | (bFrame << 4) | 0x00 /*T=0*/ | bTarget;
+ /* Frame Datum */
+ if (bFrame <= OMF_FIX_F_FRAME_NO)
+ {
+ if (idxFrame >= 128)
+ abFixup[off++] = (uint8_t)(idxFrame >> 8) | 0x80;
+ abFixup[off++] = (uint8_t)idxFrame;
+ }
+ /* Target Datum */
+ if (idxTarget >= 128)
+ abFixup[off++] = (uint8_t)(idxTarget >> 8) | 0x80;
+ abFixup[off++] = (uint8_t)idxTarget;
+ /* Target Displacement */
+ if (fTargetDisp)
+ {
+ abFixup[off++] = RT_BYTE1(offTargetDisp);
+ abFixup[off++] = RT_BYTE2(offTargetDisp);
+ abFixup[off++] = RT_BYTE3(offTargetDisp);
+ abFixup[off++] = RT_BYTE4(offTargetDisp);
+ }
+
+ return omfWriter_LEDataAddFixuppBytes(pThis, abFixup, off);
+}
+
+/**
+ * LEDATA + FIXUPP - Add simple fixup, split if necessary.
+ */
+static bool omfWriter_LEDataAddFixupNoDisp(POMFWRITER pThis, uint16_t offDataRec, uint8_t bLocation,
+ uint8_t bFrame, uint16_t idxFrame, uint8_t bTarget, uint16_t idxTarget)
+{
+ return omfWriter_LEDataAddFixup(pThis, offDataRec, false /*fSelfRel*/, bLocation, bFrame, idxFrame, bTarget, idxTarget,
+ false /*fTargetDisp*/, 0 /*offTargetDisp*/);
+}
+
+
+/**
+ * LEDATA + FIXUPP - End of records.
+ */
+static bool omfWriter_LEDataEnd(POMFWRITER pThis)
+{
+ if (omfWriter_RecEndWithCrc(pThis))
+ {
+ for (unsigned iFixupp = 0; iFixupp <= pThis->iFixupp; iFixupp++)
+ {
+ uint16_t const cbRec = pThis->aFixupps[iFixupp].cbRec;
+ if (!cbRec)
+ break;
+ if (g_cVerbose >= 3)
+ printf("debug: FIXUPP32 #%u cbRec=%#x\n", iFixupp, cbRec);
+ if ( !omfWriter_RecBegin(pThis, OMF_FIXUPP32)
+ || !omfWriter_RecAddBytes(pThis, pThis->aFixupps[iFixupp].abData, cbRec)
+ || !omfWriter_RecEndWithCrc(pThis))
+ return false;
+ }
+ pThis->iFixupp = 0;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * LEDATA + FIXUPP - Splits the LEDATA record.
+ */
+static bool omfWriter_LEDataSplit(POMFWRITER pThis)
+{
+ return omfWriter_LEDataEnd(pThis)
+ && omfWriter_LEDataBegin(pThis, pThis->idx, pThis->offSeg);
+}
+
+/**
+ * LEDATA + FIXUPP - Returns available space in current LEDATA record.
+ */
+static uint32_t omfWriter_LEDataAvailable(POMFWRITER pThis)
+{
+ if (pThis->offSeg < pThis->offSegEnd)
+ return pThis->offSegEnd - pThis->offSeg;
+ return 0;
+}
+
+/**
+ * LEDATA + FIXUPP - Splits LEDATA record if less than @a cb bytes available.
+ */
+static bool omfWriter_LEDataEnsureSpace(POMFWRITER pThis, uint32_t cb)
+{
+ if ( omfWriter_LEDataAvailable(pThis) >= cb
+ || omfWriter_LEDataSplit(pThis))
+ return true;
+ return false;
+}
+
+/**
+ * LEDATA + FIXUPP - Adds data to the LEDATA record, splitting it if needed.
+ */
+static bool omfWriter_LEDataAddBytes(POMFWRITER pThis, void const *pvData, size_t cbData)
+{
+ while (cbData > 0)
+ {
+ uint32_t cbAvail = omfWriter_LEDataAvailable(pThis);
+ if (cbAvail >= cbData)
+ {
+ if (omfWriter_RecAddBytes(pThis, pvData, cbData))
+ {
+ pThis->offSeg += (uint32_t)cbData;
+ break;
+ }
+ return false;
+ }
+ if (!omfWriter_RecAddBytes(pThis, pvData, cbAvail))
+ return false;
+ pThis->offSeg += cbAvail;
+ pvData = (uint8_t const *)pvData + cbAvail;
+ cbData -= cbAvail;
+ if (!omfWriter_LEDataSplit(pThis))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * LEDATA + FIXUPP - Adds a U32 to the LEDATA record, splitting if needed.
+ */
+static bool omfWriter_LEDataAddU32(POMFWRITER pThis, uint32_t u32)
+{
+ if ( omfWriter_LEDataEnsureSpace(pThis, 4)
+ && omfWriter_RecAddU32(pThis, u32))
+ {
+ pThis->offSeg += 4;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * LEDATA + FIXUPP - Adds a U16 to the LEDATA record, splitting if needed.
+ */
+static bool omfWriter_LEDataAddU16(POMFWRITER pThis, uint16_t u16)
+{
+ if ( omfWriter_LEDataEnsureSpace(pThis, 2)
+ && omfWriter_RecAddU16(pThis, u16))
+ {
+ pThis->offSeg += 2;
+ return true;
+ }
+ return false;
+}
+
+#if 0 /* unused */
+/**
+ * LEDATA + FIXUPP - Adds a byte to the LEDATA record, splitting if needed.
+ */
+static bool omfWriter_LEDataAddU8(POMFWRITER pThis, uint8_t b)
+{
+ if ( omfWriter_LEDataEnsureSpace(pThis, 1)
+ && omfWriter_RecAddU8(pThis, b))
+ {
+ pThis->offSeg += 1;
+ return true;
+ }
+ return false;
+}
+#endif
+
+/**
+ * MODEND - End of module, simple variant.
+ */
+static bool omfWriter_EndModule(POMFWRITER pThis)
+{
+ return omfWriter_RecBegin(pThis, OMF_MODEND32)
+ && omfWriter_RecAddU8(pThis, 0)
+ && omfWriter_RecEndWithCrc(pThis);
+}
+
+
+
+
+/*********************************************************************************************************************************
+* ELF64/AMD64 -> ELF64/i386 Converter *
+*********************************************************************************************************************************/
+
+/** AMD64 relocation type names for ELF. */
+static const char * const g_apszElfAmd64RelTypes[] =
+{
+ "R_X86_64_NONE",
+ "R_X86_64_64",
+ "R_X86_64_PC32",
+ "R_X86_64_GOT32",
+ "R_X86_64_PLT32",
+ "R_X86_64_COPY",
+ "R_X86_64_GLOB_DAT",
+ "R_X86_64_JMP_SLOT",
+ "R_X86_64_RELATIVE",
+ "R_X86_64_GOTPCREL",
+ "R_X86_64_32",
+ "R_X86_64_32S",
+ "R_X86_64_16",
+ "R_X86_64_PC16",
+ "R_X86_64_8",
+ "R_X86_64_PC8",
+ "R_X86_64_DTPMOD64",
+ "R_X86_64_DTPOFF64",
+ "R_X86_64_TPOFF64",
+ "R_X86_64_TLSGD",
+ "R_X86_64_TLSLD",
+ "R_X86_64_DTPOFF32",
+ "R_X86_64_GOTTPOFF",
+ "R_X86_64_TPOFF32",
+};
+
+/** AMD64 relocation type sizes for ELF. */
+static uint8_t const g_acbElfAmd64RelTypes[] =
+{
+ 0, /* R_X86_64_NONE */
+ 8, /* R_X86_64_64 */
+ 4, /* R_X86_64_PC32 */
+ 4, /* R_X86_64_GOT32 */
+ 4, /* R_X86_64_PLT32 */
+ 0, /* R_X86_64_COPY */
+ 0, /* R_X86_64_GLOB_DAT */
+ 0, /* R_X86_64_JMP_SLOT */
+ 0, /* R_X86_64_RELATIVE */
+ 0, /* R_X86_64_GOTPCREL */
+ 4, /* R_X86_64_32 */
+ 4, /* R_X86_64_32S */
+ 2, /* R_X86_64_16 */
+ 2, /* R_X86_64_PC16 */
+ 1, /* R_X86_64_8 */
+ 1, /* R_X86_64_PC8 */
+ 0, /* R_X86_64_DTPMOD64 */
+ 0, /* R_X86_64_DTPOFF64 */
+ 0, /* R_X86_64_TPOFF64 */
+ 0, /* R_X86_64_TLSGD */
+ 0, /* R_X86_64_TLSLD */
+ 0, /* R_X86_64_DTPOFF32 */
+ 0, /* R_X86_64_GOTTPOFF */
+ 0, /* R_X86_64_TPOFF32 */
+};
+
+/** Macro for getting the size of a AMD64 ELF relocation. */
+#define ELF_AMD64_RELOC_SIZE(a_Type) ( (a_Type) < RT_ELEMENTS(g_acbElfAmd64RelTypes) ? g_acbElfAmd64RelTypes[(a_Type)] : 1)
+
+
+typedef struct ELFDETAILS
+{
+ /** The ELF header. */
+ Elf64_Ehdr const *pEhdr;
+ /** The section header table. */
+ Elf64_Shdr const *paShdrs;
+ /** The string table for the section names. */
+ const char *pchShStrTab;
+
+ /** The symbol table section number. UINT16_MAX if not found. */
+ uint16_t iSymSh;
+ /** The string table section number. UINT16_MAX if not found. */
+ uint16_t iStrSh;
+
+ /** The symbol table. */
+ Elf64_Sym const *paSymbols;
+ /** The number of symbols in the symbol table. */
+ uint32_t cSymbols;
+
+ /** Pointer to the (symbol) string table if found. */
+ const char *pchStrTab;
+ /** The string table size. */
+ size_t cbStrTab;
+
+} ELFDETAILS;
+typedef ELFDETAILS *PELFDETAILS;
+typedef ELFDETAILS const *PCELFDETAILS;
+
+
+static bool validateElf(const char *pszFile, uint8_t const *pbFile, size_t cbFile, PELFDETAILS pElfStuff)
+{
+ /*
+ * Initialize the ELF details structure.
+ */
+ memset(pElfStuff, 0, sizeof(*pElfStuff));
+ pElfStuff->iSymSh = UINT16_MAX;
+ pElfStuff->iStrSh = UINT16_MAX;
+
+ /*
+ * Validate the header and our other expectations.
+ */
+ Elf64_Ehdr const *pEhdr = (Elf64_Ehdr const *)pbFile;
+ pElfStuff->pEhdr = pEhdr;
+ if ( pEhdr->e_ident[EI_CLASS] != ELFCLASS64
+ || pEhdr->e_ident[EI_DATA] != ELFDATA2LSB
+ || pEhdr->e_ehsize != sizeof(Elf64_Ehdr)
+ || pEhdr->e_shentsize != sizeof(Elf64_Shdr)
+ || pEhdr->e_version != EV_CURRENT )
+ return error(pszFile, "Unsupported ELF config\n");
+ if (pEhdr->e_type != ET_REL)
+ return error(pszFile, "Expected relocatable ELF file (e_type=%d)\n", pEhdr->e_type);
+ if (pEhdr->e_machine != EM_X86_64)
+ return error(pszFile, "Expected relocatable ELF file (e_type=%d)\n", pEhdr->e_machine);
+ if (pEhdr->e_phnum != 0)
+ return error(pszFile, "Expected e_phnum to be zero not %u\n", pEhdr->e_phnum);
+ if (pEhdr->e_shnum < 2)
+ return error(pszFile, "Expected e_shnum to be two or higher\n");
+ if (pEhdr->e_shstrndx >= pEhdr->e_shnum || pEhdr->e_shstrndx == 0)
+ return error(pszFile, "Bad e_shstrndx=%u (e_shnum=%u)\n", pEhdr->e_shstrndx, pEhdr->e_shnum);
+ if ( pEhdr->e_shoff >= cbFile
+ || pEhdr->e_shoff + pEhdr->e_shnum * sizeof(Elf64_Shdr) > cbFile)
+ return error(pszFile, "Section table is outside the file (e_shoff=%#llx, e_shnum=%u, cbFile=%#llx)\n",
+ pEhdr->e_shstrndx, pEhdr->e_shnum, (uint64_t)cbFile);
+
+ /*
+ * Locate the section name string table.
+ * We assume it's okay as we only reference it in verbose mode.
+ */
+ Elf64_Shdr const *paShdrs = (Elf64_Shdr const *)&pbFile[pEhdr->e_shoff];
+ pElfStuff->paShdrs = paShdrs;
+
+ Elf64_Xword const cbShStrTab = paShdrs[pEhdr->e_shstrndx].sh_size;
+ if ( paShdrs[pEhdr->e_shstrndx].sh_offset > cbFile
+ || cbShStrTab > cbFile
+ || paShdrs[pEhdr->e_shstrndx].sh_offset + cbShStrTab > cbFile)
+ return error(pszFile,
+ "Section string table is outside the file (sh_offset=%#" ELF_FMT_X64 " sh_size=%#" ELF_FMT_X64 " cbFile=%#" ELF_FMT_X64 ")\n",
+ paShdrs[pEhdr->e_shstrndx].sh_offset, paShdrs[pEhdr->e_shstrndx].sh_size, (Elf64_Xword)cbFile);
+ const char *pchShStrTab = (const char *)&pbFile[paShdrs[pEhdr->e_shstrndx].sh_offset];
+ pElfStuff->pchShStrTab = pchShStrTab;
+
+ /*
+ * Work the section table.
+ */
+ bool fRet = true;
+ for (uint32_t i = 1; i < pEhdr->e_shnum; i++)
+ {
+ if (paShdrs[i].sh_name >= cbShStrTab)
+ return error(pszFile, "Invalid sh_name value (%#x) for section #%u\n", paShdrs[i].sh_name, i);
+ const char *pszShNm = &pchShStrTab[paShdrs[i].sh_name];
+
+ if ( paShdrs[i].sh_offset > cbFile
+ || paShdrs[i].sh_size > cbFile
+ || paShdrs[i].sh_offset + paShdrs[i].sh_size > cbFile)
+ return error(pszFile, "Section #%u '%s' has data outside the file: %#" ELF_FMT_X64 " LB %#" ELF_FMT_X64 " (cbFile=%#" ELF_FMT_X64 ")\n",
+ i, pszShNm, paShdrs[i].sh_offset, paShdrs[i].sh_size, (Elf64_Xword)cbFile);
+ if (g_cVerbose)
+ printf("shdr[%u]: name=%#x '%s' type=%#x flags=%#" ELF_FMT_X64 " addr=%#" ELF_FMT_X64 " off=%#" ELF_FMT_X64 " size=%#" ELF_FMT_X64 "\n"
+ " link=%u info=%#x align=%#" ELF_FMT_X64 " entsize=%#" ELF_FMT_X64 "\n",
+ i, paShdrs[i].sh_name, pszShNm, paShdrs[i].sh_type, paShdrs[i].sh_flags,
+ paShdrs[i].sh_addr, paShdrs[i].sh_offset, paShdrs[i].sh_size,
+ paShdrs[i].sh_link, paShdrs[i].sh_info, paShdrs[i].sh_addralign, paShdrs[i].sh_entsize);
+
+ if (paShdrs[i].sh_link >= pEhdr->e_shnum)
+ return error(pszFile, "Section #%u '%s' links to a section outside the section table: %#x, max %#x\n",
+ i, pszShNm, paShdrs[i].sh_link, pEhdr->e_shnum);
+ if (!RT_IS_POWER_OF_TWO(paShdrs[i].sh_addralign))
+ return error(pszFile, "Section #%u '%s' alignment value is not a power of two: %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_addralign);
+ if (!RT_IS_POWER_OF_TWO(paShdrs[i].sh_addralign))
+ return error(pszFile, "Section #%u '%s' alignment value is not a power of two: %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_addralign);
+ if (paShdrs[i].sh_addr != 0)
+ return error(pszFile, "Section #%u '%s' has non-zero address: %#" ELF_FMT_X64 "\n", i, pszShNm, paShdrs[i].sh_addr);
+
+ if (paShdrs[i].sh_type == SHT_RELA)
+ {
+ if (paShdrs[i].sh_entsize != sizeof(Elf64_Rela))
+ return error(pszFile, "Expected sh_entsize to be %u not %u for section #%u (%s)\n", (unsigned)sizeof(Elf64_Rela),
+ paShdrs[i].sh_entsize, i, pszShNm);
+ uint32_t const cRelocs = paShdrs[i].sh_size / sizeof(Elf64_Rela);
+ if (cRelocs * sizeof(Elf64_Rela) != paShdrs[i].sh_size)
+ return error(pszFile, "Uneven relocation entry count in #%u (%s): sh_size=%#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size);
+ if ( paShdrs[i].sh_offset > cbFile
+ || paShdrs[i].sh_size >= cbFile
+ || paShdrs[i].sh_offset + paShdrs[i].sh_size > cbFile)
+ return error(pszFile, "The content of section #%u '%s' is outside the file (%#" ELF_FMT_X64 " LB %#" ELF_FMT_X64 ", cbFile=%#lx)\n",
+ i, pszShNm, paShdrs[i].sh_offset, paShdrs[i].sh_size, (unsigned long)cbFile);
+ if (paShdrs[i].sh_info != i - 1)
+ return error(pszFile, "Expected relocation section #%u (%s) to link to previous section: sh_info=%#u\n",
+ i, pszShNm, (unsigned)paShdrs[i].sh_link);
+ if (paShdrs[paShdrs[i].sh_link].sh_type != SHT_SYMTAB)
+ return error(pszFile, "Expected relocation section #%u (%s) to link to symbol table: sh_link=%#u -> sh_type=%#x\n",
+ i, pszShNm, (unsigned)paShdrs[i].sh_link, (unsigned)paShdrs[paShdrs[i].sh_link].sh_type);
+ uint32_t cSymbols = paShdrs[paShdrs[i].sh_link].sh_size / paShdrs[paShdrs[i].sh_link].sh_entsize;
+
+ Elf64_Rela const *paRelocs = (Elf64_Rela *)&pbFile[paShdrs[i].sh_offset];
+ for (uint32_t j = 0; j < cRelocs; j++)
+ {
+ uint8_t const bType = ELF64_R_TYPE(paRelocs[j].r_info);
+ if (RT_UNLIKELY(bType >= R_X86_64_COUNT))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": unknown fix up %#x (%+" ELF_FMT_D64 ")\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, bType, paRelocs[j].r_addend);
+ if (RT_UNLIKELY( paRelocs[j].r_offset > paShdrs[i - 1].sh_size
+ || paRelocs[j].r_offset + ELF_AMD64_RELOC_SIZE(ELF64_R_TYPE(paRelocs[j].r_info))
+ > paShdrs[i - 1].sh_size))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": out of bounds (sh_size %" ELF_FMT_X64 ")\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, paShdrs[i - 1].sh_size);
+
+ uint32_t const iSymbol = ELF64_R_SYM(paRelocs[j].r_info);
+ if (RT_UNLIKELY(iSymbol >= cSymbols))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": symbol index (%#x) out of bounds (%#x)\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, iSymbol, cSymbols);
+ }
+ }
+ else if (paShdrs[i].sh_type == SHT_REL)
+ fRet = error(pszFile, "Section #%u '%s': Unexpected SHT_REL section\n", i, pszShNm);
+ else if (paShdrs[i].sh_type == SHT_SYMTAB)
+ {
+ if (paShdrs[i].sh_entsize != sizeof(Elf64_Sym))
+ fRet = error(pszFile, "Section #%u '%s': Unsupported symbol table entry size in : #%u (expected #%u)\n",
+ i, pszShNm, paShdrs[i].sh_entsize, sizeof(Elf64_Sym));
+ Elf64_Xword const cSymbols = paShdrs[i].sh_size / paShdrs[i].sh_entsize;
+ if (cSymbols * paShdrs[i].sh_entsize != paShdrs[i].sh_size)
+ fRet = error(pszFile, "Section #%u '%s': Size not a multiple of entry size: %#" ELF_FMT_X64 " %% %#" ELF_FMT_X64 " = %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size, paShdrs[i].sh_entsize, paShdrs[i].sh_size % paShdrs[i].sh_entsize);
+ if (cSymbols > UINT32_MAX)
+ fRet = error(pszFile, "Section #%u '%s': too many symbols: %" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size, cSymbols);
+
+ if (pElfStuff->iSymSh == UINT16_MAX)
+ {
+ pElfStuff->iSymSh = (uint16_t)i;
+ pElfStuff->paSymbols = (Elf64_Sym const *)&pbFile[paShdrs[i].sh_offset];
+ pElfStuff->cSymbols = cSymbols;
+
+ if (paShdrs[i].sh_link != 0)
+ {
+ /* Note! The symbol string table section header may not have been validated yet! */
+ Elf64_Shdr const *pStrTabShdr = &paShdrs[paShdrs[i].sh_link];
+ pElfStuff->iStrSh = paShdrs[i].sh_link;
+ pElfStuff->pchStrTab = (const char *)&pbFile[pStrTabShdr->sh_offset];
+ pElfStuff->cbStrTab = (size_t)pStrTabShdr->sh_size;
+ }
+ else
+ fRet = error(pszFile, "Section #%u '%s': String table link is out of bounds (%#x)\n",
+ i, pszShNm, paShdrs[i].sh_link);
+ }
+ else
+ fRet = error(pszFile, "Section #%u '%s': Found additonal symbol table, previous in #%u\n",
+ i, pszShNm, pElfStuff->iSymSh);
+ }
+ }
+ return fRet;
+}
+
+
+static bool convertElfSectionsToSegDefsAndGrpDefs(POMFWRITER pThis, PCELFDETAILS pElfStuff)
+{
+ /*
+ * Do the list of names pass.
+ */
+ uint16_t idxGrpFlat, idxGrpData;
+ uint16_t idxClassCode, idxClassData, idxClassDwarf;
+ if ( !omfWriter_LNamesBegin(pThis, true /*fAddZeroEntry*/)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FLAT"), &idxGrpFlat)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3DATA64_GROUP"), &idxGrpData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3CLASS64CODE"), &idxClassCode)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FAR_DATA"), &idxClassData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("DWARF"), &idxClassDwarf)
+ )
+ return false;
+
+ bool fHaveData = false;
+ Elf64_Shdr const *pShdr = &pElfStuff->paShdrs[1];
+ Elf64_Half const cSections = pElfStuff->pEhdr->e_shnum;
+ for (Elf64_Half i = 1; i < cSections; i++, pShdr++)
+ {
+ const char *pszName = &pElfStuff->pchShStrTab[pShdr->sh_name];
+ if (*pszName == '\0')
+ return error(pThis->pszSrc, "Section #%u has an empty name!\n", i);
+
+ switch (pShdr->sh_type)
+ {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ /* We drop a few sections we don't want:. */
+ if ( strcmp(pszName, ".comment") != 0 /* compiler info */
+ && strcmp(pszName, ".note.GNU-stack") != 0 /* some empty section for hinting the linker/whatever */
+ && strcmp(pszName, ".eh_frame") != 0 /* unwind / exception info */
+ )
+ {
+ pThis->paSegments[i].iSegDef = UINT16_MAX;
+ pThis->paSegments[i].iGrpDef = UINT16_MAX;
+
+ /* Translate the name and determine group and class.
+ Note! We currently strip sub-sections. */
+ if ( strcmp(pszName, ".text") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".text.")) == 0)
+ {
+ pszName = "BS3TEXT64";
+ pThis->paSegments[i].iGrpNm = idxGrpFlat;
+ pThis->paSegments[i].iClassNm = idxClassCode;
+ }
+ else if ( strcmp(pszName, ".data") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".data.")) == 0)
+ {
+ pszName = "BS3DATA64";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strcmp(pszName, ".bss") == 0)
+ {
+ pszName = "BS3BSS64";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if ( strcmp(pszName, ".rodata") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".rodata.")) == 0)
+ {
+ pszName = "BS3DATA64CONST";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strncmp(pszName, RT_STR_TUPLE(".debug_")) == 0)
+ {
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = idxClassDwarf;
+ }
+ else
+ {
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ error(pThis->pszSrc, "Unknown data (?) segment: '%s'\n", pszName);
+ }
+
+ /* Save the name. */
+ pThis->paSegments[i].pszName = strdup(pszName);
+ if (!pThis->paSegments[i].pszName)
+ return error(pThis->pszSrc, "Out of memory!\n");
+
+ /* Add the section name. */
+ if (!omfWriter_LNamesAdd(pThis, pThis->paSegments[i].pszName, &pThis->paSegments[i].iSegNm))
+ return false;
+
+ fHaveData |= pThis->paSegments[i].iGrpNm == idxGrpData;
+ break;
+ }
+ RT_FALL_THRU();
+
+ default:
+ pThis->paSegments[i].iSegDef = UINT16_MAX;
+ pThis->paSegments[i].iGrpDef = UINT16_MAX;
+ pThis->paSegments[i].iSegNm = UINT16_MAX;
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = UINT16_MAX;
+ pThis->paSegments[i].pszName = NULL;
+ break;
+ }
+ }
+
+ if (!omfWriter_LNamesEnd(pThis))
+ return false;
+
+ /*
+ * Emit segment definitions.
+ */
+ uint16_t iSegDef = 1; /* Start counting at 1. */
+ pShdr = &pElfStuff->paShdrs[1];
+ for (Elf64_Half i = 1; i < cSections; i++, pShdr++)
+ {
+ if (pThis->paSegments[i].iSegNm == UINT16_MAX)
+ continue;
+
+ uint8_t bSegAttr = 0;
+
+ /* The A field. */
+ switch (pShdr->sh_addralign)
+ {
+ case 0:
+ case 1:
+ bSegAttr |= 1 << 5;
+ break;
+ case 2:
+ bSegAttr |= 2 << 5;
+ break;
+ case 4:
+ bSegAttr |= 5 << 5;
+ break;
+ case 8:
+ case 16:
+ bSegAttr |= 3 << 5;
+ break;
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ bSegAttr |= 4 << 5;
+ break;
+ default:
+ bSegAttr |= 6 << 5; /* page aligned, pharlabs extension. */
+ break;
+ }
+
+ /* The C field. */
+ bSegAttr |= 2 << 2; /* public */
+
+ /* The B field. We don't have 4GB segments, so leave it as zero. */
+
+ /* The D field shall be set as we're doing USE32. */
+ bSegAttr |= 1;
+
+
+ /* Done. */
+ if (!omfWriter_SegDef(pThis, bSegAttr, (uint32_t)pShdr->sh_size,
+ pThis->paSegments[i].iSegNm,
+ pThis->paSegments[i].iClassNm))
+ return false;
+ pThis->paSegments[i].iSegDef = iSegDef++;
+ }
+
+ /*
+ * Flat group definition (#1) - special, no members.
+ */
+ uint16_t iGrpDef = 1;
+ if ( !omfWriter_GrpDefBegin(pThis, idxGrpFlat)
+ || !omfWriter_GrpDefEnd(pThis))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == idxGrpFlat)
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ pThis->idxGrpFlat = iGrpDef++;
+
+ /*
+ * Data group definition (#2).
+ */
+ /** @todo do we need to consider missing segments and ordering? */
+ uint16_t cGrpNms = 0;
+ uint16_t aiGrpNms[2] = { 0, 0 }; /* Shut up, GCC. */
+ if (fHaveData)
+ aiGrpNms[cGrpNms++] = idxGrpData;
+ for (uint32_t iGrpNm = 0; iGrpNm < cGrpNms; iGrpNm++)
+ {
+ if (!omfWriter_GrpDefBegin(pThis, aiGrpNms[iGrpNm]))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == aiGrpNms[iGrpNm])
+ {
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ if (!omfWriter_GrpDefAddSegDef(pThis, pThis->paSegments[i].iSegDef))
+ return false;
+ }
+ if (!omfWriter_GrpDefEnd(pThis))
+ return false;
+ iGrpDef++;
+ }
+
+ return true;
+}
+
+static bool convertElfSymbolsToPubDefsAndExtDefs(POMFWRITER pThis, PCELFDETAILS pElfStuff)
+{
+ if (!pElfStuff->cSymbols)
+ return true;
+
+ /*
+ * Process the symbols the first.
+ */
+ uint32_t cAbsSyms = 0;
+ uint32_t cExtSyms = 0;
+ uint32_t cPubSyms = 0;
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ pThis->paSegments[iSeg].cPubDefs = 0;
+
+ uint32_t const cSections = pElfStuff->pEhdr->e_shnum;
+ uint32_t const cSymbols = pElfStuff->cSymbols;
+ Elf64_Sym const * const paSymbols = pElfStuff->paSymbols;
+ for (uint32_t iSym = 0; iSym < cSymbols; iSym++)
+ {
+ const uint8_t bBind = ELF64_ST_BIND(paSymbols[iSym].st_info);
+ const uint8_t bType = ELF64_ST_TYPE(paSymbols[iSym].st_info);
+ const char *pszSymName = &pElfStuff->pchStrTab[paSymbols[iSym].st_name];
+ if ( *pszSymName == '\0'
+ && bType == STT_SECTION
+ && paSymbols[iSym].st_shndx < cSections)
+ pszSymName = &pElfStuff->pchShStrTab[pElfStuff->paShdrs[paSymbols[iSym].st_shndx].sh_name];
+
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_IGNORED;
+ pThis->paSymbols[iSym].idx = UINT16_MAX;
+ pThis->paSymbols[iSym].idxSegDef = UINT16_MAX;
+ pThis->paSymbols[iSym].idxGrpDef = UINT16_MAX;
+
+ uint32_t const idxSection = paSymbols[iSym].st_shndx;
+ if (idxSection == SHN_UNDEF)
+ {
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_EXTDEF;
+ cExtSyms++;
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "External symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else if (bBind != STB_LOCAL || iSym != 0) /* Entry zero is usually a dummy. */
+ return error(pThis->pszSrc, "Unsupported or invalid bind type %#x for undefined symbol #%u (%s)\n",
+ bBind, iSym, pszSymName);
+ }
+ else if (idxSection < cSections)
+ {
+ pThis->paSymbols[iSym].idxSegDef = pThis->paSegments[idxSection].iSegDef;
+ pThis->paSymbols[iSym].idxGrpDef = pThis->paSegments[idxSection].iGrpDef;
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSegments[idxSection].cPubDefs++;
+ cPubSyms++;
+ if (bType == STT_SECTION)
+ return error(pThis->pszSrc, "Don't know how to export STT_SECTION symbol #%u (%s)\n", iSym, pszSymName);
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "Public symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else if (bType == STT_SECTION)
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_SEGDEF;
+ else
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_INTERNAL;
+ }
+ else if (idxSection == SHN_ABS)
+ {
+ if (bType != STT_FILE)
+ {
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSymbols[iSym].idxSegDef = 0;
+ pThis->paSymbols[iSym].idxGrpDef = 0;
+ cAbsSyms++;
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "Public absolute symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else
+ return error(pThis->pszSrc, "Unsupported or invalid bind type %#x for absolute symbol #%u (%s)\n",
+ bBind, iSym, pszSymName);
+ }
+ }
+ else if (idxSection == SHN_COMMON)
+ return error(pThis->pszSrc, "Symbol #%u (%s) is in the unsupported 'common' section.\n", iSym, pszSymName);
+ else
+ return error(pThis->pszSrc, "Unsupported or invalid section number %#x for symbol #%u (%s)\n",
+ idxSection, iSym, pszSymName);
+ }
+
+ /*
+ * Emit the PUBDEFs the first time around (see order of records in TIS spec).
+ */
+ uint16_t idxPubDef = 1;
+ if (cPubSyms)
+ {
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ if (pThis->paSegments[iSeg].cPubDefs > 0)
+ {
+ uint16_t const idxSegDef = pThis->paSegments[iSeg].iSegDef;
+ if (!omfWriter_PubDefBegin(pThis, pThis->paSegments[iSeg].iGrpDef, idxSegDef))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == idxSegDef
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ /* Underscore prefix all names not already underscored/mangled. */
+ const char *pszName = &pElfStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].st_value, pszName, pszName[0] != '_'))
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+ }
+
+ if (cAbsSyms > 0)
+ {
+ if (!omfWriter_PubDefBegin(pThis, 0, 0))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == 0
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ /* Underscore prefix all names not already underscored/mangled. */
+ const char *pszName = &pElfStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].st_value, pszName, pszName[0] != '_'))
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+
+ /*
+ * Go over the symbol table and emit external definition records.
+ */
+ if (!omfWriter_ExtDefBegin(pThis))
+ return false;
+ uint16_t idxExtDef = 1;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if (pThis->paSymbols[iSym].enmType == OMFSYMTYPE_EXTDEF)
+ {
+ /* Underscore prefix all names not already underscored/mangled. */
+ const char *pszName = &pElfStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_ExtDefAdd(pThis, pszName, *pszName != '_'))
+ return false;
+ pThis->paSymbols[iSym].idx = idxExtDef++;
+ }
+
+ if (!omfWriter_ExtDefEnd(pThis))
+ return false;
+
+ return true;
+}
+
+/**
+ * @callback_method_impl{FNRTSORTCMP, For Elf64_Rela tables.}
+ */
+static DECLCALLBACK(int) convertElfCompareRelA(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ Elf64_Rela const *pReloc1 = (Elf64_Rela const *)pvElement1;
+ Elf64_Rela const *pReloc2 = (Elf64_Rela const *)pvElement2;
+ if (pReloc1->r_offset < pReloc2->r_offset)
+ return -1;
+ if (pReloc1->r_offset > pReloc2->r_offset)
+ return 1;
+ RT_NOREF_PV(pvUser);
+ return 0;
+}
+
+static bool convertElfSectionsToLeDataAndFixupps(POMFWRITER pThis, PCELFDETAILS pElfStuff, uint8_t const *pbFile, size_t cbFile)
+{
+ Elf64_Sym const *paSymbols = pElfStuff->paSymbols;
+ Elf64_Shdr const *paShdrs = pElfStuff->paShdrs;
+ bool fRet = true;
+ RT_NOREF_PV(cbFile);
+
+ for (uint32_t i = 1; i < pThis->cSegments; i++)
+ {
+ if (pThis->paSegments[i].iSegDef == UINT16_MAX)
+ continue;
+
+ const char *pszSegNm = &pElfStuff->pchShStrTab[paShdrs[i].sh_name];
+ bool const fRelocs = i + 1 < pThis->cSegments && paShdrs[i + 1].sh_type == SHT_RELA;
+ uint32_t cRelocs = fRelocs ? paShdrs[i + 1].sh_size / sizeof(Elf64_Rela) : 0;
+ Elf64_Rela const *paRelocs = fRelocs ? (Elf64_Rela *)&pbFile[paShdrs[i + 1].sh_offset] : NULL;
+ Elf64_Xword cbVirtData = paShdrs[i].sh_size;
+ Elf64_Xword cbData = paShdrs[i].sh_type == SHT_NOBITS ? 0 : cbVirtData;
+ uint8_t const *pbData = &pbFile[paShdrs[i].sh_offset];
+ uint32_t off = 0;
+
+ /* We sort fixups by r_offset in order to more easily split them into chunks. */
+ RTSortShell((void *)paRelocs, cRelocs, sizeof(paRelocs[0]), convertElfCompareRelA, NULL);
+
+ /* The OMF record size requires us to split larger sections up. To make
+ life simple, we fill zeros for unitialized (BSS) stuff. */
+ const uint32_t cbMaxData = RT_MIN(OMF_MAX_RECORD_PAYLOAD - 1 - (pThis->paSegments[i].iSegDef >= 128) - 4 - 1, _1K);
+ while (cbVirtData > 0)
+ {
+ /* Figure out how many bytes to put out in this chunk. Must make sure
+ fixups doesn't cross chunk boundraries. ASSUMES sorted relocs. */
+ uint32_t cChunkRelocs = cRelocs;
+ uint32_t cbChunk = cbVirtData;
+ uint32_t offEnd = off + cbChunk;
+ if (cbChunk > cbMaxData)
+ {
+ cbChunk = cbMaxData;
+ offEnd = off + cbChunk;
+ cChunkRelocs = 0;
+
+ /* Quickly determin the reloc range. */
+ while ( cChunkRelocs < cRelocs
+ && paRelocs[cChunkRelocs].r_offset < offEnd)
+ cChunkRelocs++;
+
+ /* Ensure final reloc doesn't go beyond chunk. */
+ while ( cChunkRelocs > 0
+ && paRelocs[cChunkRelocs - 1].r_offset
+ + ELF_AMD64_RELOC_SIZE(ELF64_R_TYPE(paRelocs[cChunkRelocs - 1].r_info))
+ > offEnd)
+ {
+ uint32_t cbDrop = offEnd - paRelocs[cChunkRelocs - 1].r_offset;
+ cbChunk -= cbDrop;
+ offEnd -= cbDrop;
+ cChunkRelocs--;
+ }
+
+ if (!cbVirtData)
+ return error(pThis->pszSrc, "Wtf? cbVirtData is zero!\n");
+ }
+ if (g_cVerbose >= 2)
+ printf("debug: LEDATA off=%#x cb=%#x cRelocs=%#x sect=#%u segdef=%#x grpdef=%#x '%s'\n",
+ off, cbChunk, cRelocs, i, pThis->paSegments[i].iSegDef, pThis->paSegments[i].iGrpDef, pszSegNm);
+
+ /*
+ * We stash the bytes into the OMF writer record buffer, receiving a
+ * pointer to the start of it so we can make adjustments if necessary.
+ */
+ uint8_t *pbCopy;
+ if (!omfWriter_LEDataBeginEx(pThis, pThis->paSegments[i].iSegDef, off, cbChunk, cbData, pbData, &pbCopy))
+ return false;
+
+ /*
+ * Convert fiuxps.
+ */
+ for (uint32_t iReloc = 0; iReloc < cChunkRelocs; iReloc++)
+ {
+ /* Get the OMF and ELF data for the symbol the reloc references. */
+ uint32_t const uType = ELF64_R_TYPE(paRelocs[iReloc].r_info);
+ uint32_t const iSymbol = ELF64_R_SYM(paRelocs[iReloc].r_info);
+ Elf64_Sym const * const pElfSym = &paSymbols[iSymbol];
+ POMFSYMBOL const pOmfSym = &pThis->paSymbols[iSymbol];
+ const char * const pszSymName = &pElfStuff->pchStrTab[pElfSym->st_name];
+
+ /* Calc fixup location in the pending chunk and setup a flexible pointer to it. */
+ uint16_t offDataRec = (uint16_t)(paRelocs[iReloc].r_offset - off);
+ RTPTRUNION uLoc;
+ uLoc.pu8 = &pbCopy[offDataRec];
+
+ /* OMF fixup data initialized with typical defaults. */
+ bool fSelfRel = true;
+ uint8_t bLocation = OMF_FIX_LOC_32BIT_OFFSET;
+ uint8_t bFrame = OMF_FIX_F_GRPDEF;
+ uint16_t idxFrame = pThis->idxGrpFlat;
+ uint8_t bTarget;
+ uint16_t idxTarget;
+ bool fTargetDisp;
+ uint32_t offTargetDisp;
+ switch (pOmfSym->enmType)
+ {
+ case OMFSYMTYPE_INTERNAL:
+ case OMFSYMTYPE_PUBDEF:
+ bTarget = OMF_FIX_T_SEGDEF;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = true;
+ offTargetDisp = pElfSym->st_value;
+ break;
+
+ case OMFSYMTYPE_SEGDEF:
+ bTarget = OMF_FIX_T_SEGDEF_NO_DISP;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ case OMFSYMTYPE_EXTDEF:
+ bTarget = OMF_FIX_T_EXTDEF_NO_DISP;
+ idxTarget = pOmfSym->idx;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ default:
+ return error(pThis->pszSrc, "Relocation in segment #%u '%s' references ignored or invalid symbol (%s)\n",
+ i, pszSegNm, pszSymName);
+ }
+
+ /* Do COFF relocation type conversion. */
+ switch (uType)
+ {
+ case R_X86_64_64:
+ {
+ int64_t iAddend = paRelocs[iReloc].r_addend;
+ if (iAddend > _1G || iAddend < -_1G)
+ fRet = error(pThis->pszSrc, "R_X86_64_64 with large addend (%" ELF_FMT_D64 ") at %#x in segment #%u '%s'\n",
+ iAddend, paRelocs[iReloc].r_offset, i, pszSegNm);
+ *uLoc.pu64 = iAddend;
+ fSelfRel = false;
+ break;
+ }
+
+ case R_X86_64_32:
+ case R_X86_64_32S: /* signed, unsigned, whatever. */
+ fSelfRel = false;
+ RT_FALL_THRU();
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32: /* binutils commit 451875b4f976a527395e9303224c7881b65e12ed feature/regression. */
+ {
+ /* defaults are ok, just handle the addend. */
+ int32_t iAddend = paRelocs[iReloc].r_addend;
+ if (iAddend != paRelocs[iReloc].r_addend)
+ fRet = error(pThis->pszSrc, "R_X86_64_PC32 with large addend (%d) at %#x in segment #%u '%s'\n",
+ iAddend, paRelocs[iReloc].r_offset, i, pszSegNm);
+ if (fSelfRel)
+ *uLoc.pu32 = iAddend + 4;
+ else
+ *uLoc.pu32 = iAddend;
+ break;
+ }
+
+ case R_X86_64_NONE:
+ continue; /* Ignore this one */
+
+ case R_X86_64_GOT32:
+ case R_X86_64_COPY:
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_JMP_SLOT:
+ case R_X86_64_RELATIVE:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_16:
+ case R_X86_64_PC16:
+ case R_X86_64_8:
+ case R_X86_64_PC8:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TLSGD:
+ case R_X86_64_TLSLD:
+ case R_X86_64_DTPOFF32:
+ case R_X86_64_GOTTPOFF:
+ case R_X86_64_TPOFF32:
+ default:
+ return error(pThis->pszSrc, "Unsupported fixup type %#x (%s) at rva=%#x in section #%u '%s' against '%s'\n",
+ uType, g_apszElfAmd64RelTypes[uType], paRelocs[iReloc].r_offset, i, pszSegNm, pszSymName);
+ }
+
+ /* Add the fixup. */
+ if (idxFrame == UINT16_MAX)
+ error(pThis->pszSrc, "idxFrame=UINT16_MAX for %s type=%s\n", pszSymName, g_apszElfAmd64RelTypes[uType]);
+ fRet = omfWriter_LEDataAddFixup(pThis, offDataRec, fSelfRel, bLocation, bFrame, idxFrame,
+ bTarget, idxTarget, fTargetDisp, offTargetDisp) && fRet;
+ }
+
+ /*
+ * Write the LEDATA and associated FIXUPPs.
+ */
+ if (!omfWriter_LEDataEnd(pThis))
+ return false;
+
+ /*
+ * Advance.
+ */
+ paRelocs += cChunkRelocs;
+ cRelocs -= cChunkRelocs;
+ if (cbData > cbChunk)
+ {
+ cbData -= cbChunk;
+ pbData += cbChunk;
+ }
+ else
+ cbData = 0;
+ off += cbChunk;
+ cbVirtData -= cbChunk;
+ }
+ }
+
+ return fRet;
+}
+
+
+static bool convertElfToOmf(const char *pszFile, uint8_t const *pbFile, size_t cbFile, FILE *pDst)
+{
+ /*
+ * Validate the source file a little.
+ */
+ ELFDETAILS ElfStuff;
+ if (!validateElf(pszFile, pbFile, cbFile, &ElfStuff))
+ return false;
+
+ /*
+ * Instantiate the OMF writer.
+ */
+ POMFWRITER pThis = omfWriter_Create(pszFile, ElfStuff.pEhdr->e_shnum, ElfStuff.cSymbols, pDst);
+ if (!pThis)
+ return false;
+
+ /*
+ * Write the OMF object file.
+ */
+ if (omfWriter_BeginModule(pThis, pszFile))
+ {
+ if ( convertElfSectionsToSegDefsAndGrpDefs(pThis, &ElfStuff)
+ && convertElfSymbolsToPubDefsAndExtDefs(pThis, &ElfStuff)
+ && omfWriter_LinkPassSeparator(pThis)
+ && convertElfSectionsToLeDataAndFixupps(pThis, &ElfStuff, pbFile, cbFile)
+ && omfWriter_EndModule(pThis) )
+ {
+
+ omfWriter_Destroy(pThis);
+ return true;
+ }
+ }
+
+ omfWriter_Destroy(pThis);
+ return false;
+}
+
+
+
+/*********************************************************************************************************************************
+* COFF -> OMF Converter *
+*********************************************************************************************************************************/
+
+/** AMD64 relocation type names for (Microsoft) COFF. */
+static const char * const g_apszCoffAmd64RelTypes[] =
+{
+ "ABSOLUTE",
+ "ADDR64",
+ "ADDR32",
+ "ADDR32NB",
+ "REL32",
+ "REL32_1",
+ "REL32_2",
+ "REL32_3",
+ "REL32_4",
+ "REL32_5",
+ "SECTION",
+ "SECREL",
+ "SECREL7",
+ "TOKEN",
+ "SREL32",
+ "PAIR",
+ "SSPAN32"
+};
+
+/** AMD64 relocation type sizes for (Microsoft) COFF. */
+static uint8_t const g_acbCoffAmd64RelTypes[] =
+{
+ 8, /* ABSOLUTE */
+ 8, /* ADDR64 */
+ 4, /* ADDR32 */
+ 4, /* ADDR32NB */
+ 4, /* REL32 */
+ 4, /* REL32_1 */
+ 4, /* REL32_2 */
+ 4, /* REL32_3 */
+ 4, /* REL32_4 */
+ 4, /* REL32_5 */
+ 2, /* SECTION */
+ 4, /* SECREL */
+ 1, /* SECREL7 */
+ 0, /* TOKEN */
+ 4, /* SREL32 */
+ 0, /* PAIR */
+ 4, /* SSPAN32 */
+};
+
+/** Macro for getting the size of a AMD64 COFF relocation. */
+#define COFF_AMD64_RELOC_SIZE(a_Type) ( (a_Type) < RT_ELEMENTS(g_acbCoffAmd64RelTypes) ? g_acbCoffAmd64RelTypes[(a_Type)] : 1)
+
+
+static const char *coffGetSymbolName(PCIMAGE_SYMBOL pSym, const char *pchStrTab, uint32_t cbStrTab, char pszShortName[16])
+{
+ if (pSym->N.Name.Short != 0)
+ {
+ memcpy(pszShortName, pSym->N.ShortName, 8);
+ pszShortName[8] = '\0';
+ return pszShortName;
+ }
+ if (pSym->N.Name.Long < cbStrTab)
+ {
+ uint32_t const cbLeft = cbStrTab - pSym->N.Name.Long;
+ const char *pszRet = pchStrTab + pSym->N.Name.Long;
+ if (memchr(pszRet, '\0', cbLeft) != NULL)
+ return pszRet;
+ }
+ error("<null>", "Invalid string table index %#x!\n", pSym->N.Name.Long);
+ return "Invalid Symbol Table Entry";
+}
+
+static bool validateCoff(const char *pszFile, uint8_t const *pbFile, size_t cbFile)
+{
+ /*
+ * Validate the header and our other expectations.
+ */
+ PIMAGE_FILE_HEADER pHdr = (PIMAGE_FILE_HEADER)pbFile;
+ if (pHdr->Machine != IMAGE_FILE_MACHINE_AMD64)
+ return error(pszFile, "Expected IMAGE_FILE_MACHINE_AMD64 not %#x\n", pHdr->Machine);
+ if (pHdr->SizeOfOptionalHeader != 0)
+ return error(pszFile, "Expected SizeOfOptionalHeader to be zero, not %#x\n", pHdr->SizeOfOptionalHeader);
+ if (pHdr->NumberOfSections == 0)
+ return error(pszFile, "Expected NumberOfSections to be non-zero\n");
+ uint32_t const cbHeaders = pHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + sizeof(*pHdr);
+ if (cbHeaders > cbFile)
+ return error(pszFile, "Section table goes beyond the end of the of the file (cSections=%#x)\n", pHdr->NumberOfSections);
+ if (pHdr->NumberOfSymbols)
+ {
+ if ( pHdr->PointerToSymbolTable >= cbFile
+ || pHdr->NumberOfSymbols * (uint64_t)IMAGE_SIZE_OF_SYMBOL > cbFile)
+ return error(pszFile, "Symbol table goes beyond the end of the of the file (cSyms=%#x, offFile=%#x)\n",
+ pHdr->NumberOfSymbols, pHdr->PointerToSymbolTable);
+ }
+
+ return true;
+}
+
+
+static bool convertCoffSectionsToSegDefsAndGrpDefs(POMFWRITER pThis, PCIMAGE_SECTION_HEADER paShdrs, uint16_t cSections)
+{
+ /*
+ * Do the list of names pass.
+ */
+ uint16_t idxGrpFlat, idxGrpData;
+ uint16_t idxClassCode, idxClassData, idxClassDebugSymbols, idxClassDebugTypes;
+ if ( !omfWriter_LNamesBegin(pThis, true /*fAddZeroEntry*/)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FLAT"), &idxGrpFlat)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3DATA64_GROUP"), &idxGrpData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3CLASS64CODE"), &idxClassCode)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FAR_DATA"), &idxClassData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("DEBSYM"), &idxClassDebugSymbols)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("DEBTYP"), &idxClassDebugTypes)
+ )
+ return false;
+
+ bool fHaveData = false;
+ for (uint16_t i = 0; i < cSections; i++)
+ {
+ /* Copy the name and terminate it. */
+ char szName[32];
+ memcpy(szName, paShdrs[i].Name, sizeof(paShdrs[i].Name));
+ unsigned cchName = sizeof(paShdrs[i].Name);
+ while (cchName > 0 && RT_C_IS_SPACE(szName[cchName - 1]))
+ cchName--;
+ if (cchName == 0)
+ return error(pThis->pszSrc, "Section #%u has an empty name!\n", i);
+ szName[cchName] = '\0';
+
+ if ( (paShdrs[i].Characteristics & (IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_LNK_INFO))
+ || strcmp(szName, ".pdata") == 0 /* Exception stuff, I think, so discard it. */
+ || strcmp(szName, ".xdata") == 0 /* Ditto. */ )
+ {
+ pThis->paSegments[i].iSegDef = UINT16_MAX;
+ pThis->paSegments[i].iGrpDef = UINT16_MAX;
+ pThis->paSegments[i].iSegNm = UINT16_MAX;
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = UINT16_MAX;
+ pThis->paSegments[i].pszName = NULL;
+ }
+ else
+ {
+ /* Translate the name, group and class. */
+ if ( strcmp(szName, ".text") == 0
+ || strcmp(szName, ".text$mn") == 0 /* Seen first in VC++ 14.1 (could be older). */)
+ {
+ strcpy(szName, "BS3TEXT64");
+ pThis->paSegments[i].iGrpNm = idxGrpFlat;
+ pThis->paSegments[i].iClassNm = idxClassCode;
+ }
+ else if (strcmp(szName, ".data") == 0)
+ {
+ strcpy(szName, "BS3DATA64");
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strcmp(szName, ".bss") == 0)
+ {
+ strcpy(szName, "BS3BSS64");
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strcmp(szName, ".rdata") == 0)
+ {
+ strcpy(szName, "BS3DATA64CONST");
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strcmp(szName, ".debug$S") == 0)
+ {
+ strcpy(szName, "$$SYMBOLS");
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = idxClassDebugSymbols;
+ }
+ else if (strcmp(szName, ".debug$T") == 0)
+ {
+ strcpy(szName, "$$TYPES");
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = idxClassDebugTypes;
+ }
+ else if (paShdrs[i].Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE))
+ {
+ pThis->paSegments[i].iGrpNm = idxGrpFlat;
+ pThis->paSegments[i].iClassNm = idxClassCode;
+ error(pThis->pszSrc, "Unknown code segment: '%s'\n", szName);
+ }
+ else
+ {
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ error(pThis->pszSrc, "Unknown data (?) segment: '%s'\n", szName);
+ }
+
+ /* Save the name. */
+ pThis->paSegments[i].pszName = strdup(szName);
+ if (!pThis->paSegments[i].pszName)
+ return error(pThis->pszSrc, "Out of memory!\n");
+
+ /* Add the section name. */
+ if (!omfWriter_LNamesAdd(pThis, pThis->paSegments[i].pszName, &pThis->paSegments[i].iSegNm))
+ return false;
+
+ fHaveData |= pThis->paSegments[i].iGrpNm == idxGrpData;
+ }
+ }
+
+ if (!omfWriter_LNamesEnd(pThis))
+ return false;
+
+ /*
+ * Emit segment definitions.
+ */
+ uint16_t iSegDef = 1; /* Start counting at 1. */
+ for (uint16_t i = 0; i < cSections; i++)
+ {
+ if (pThis->paSegments[i].iSegDef == UINT16_MAX)
+ continue;
+
+ uint8_t bSegAttr = 0;
+
+ /* The A field. */
+ switch (paShdrs[i].Characteristics & IMAGE_SCN_ALIGN_MASK)
+ {
+ default:
+ case IMAGE_SCN_ALIGN_1BYTES:
+ bSegAttr |= 1 << 5;
+ break;
+ case IMAGE_SCN_ALIGN_2BYTES:
+ bSegAttr |= 2 << 5;
+ break;
+ case IMAGE_SCN_ALIGN_4BYTES:
+ bSegAttr |= 5 << 5;
+ break;
+ case IMAGE_SCN_ALIGN_8BYTES:
+ case IMAGE_SCN_ALIGN_16BYTES:
+ bSegAttr |= 3 << 5;
+ break;
+ case IMAGE_SCN_ALIGN_32BYTES:
+ case IMAGE_SCN_ALIGN_64BYTES:
+ case IMAGE_SCN_ALIGN_128BYTES:
+ case IMAGE_SCN_ALIGN_256BYTES:
+ bSegAttr |= 4 << 5;
+ break;
+ case IMAGE_SCN_ALIGN_512BYTES:
+ case IMAGE_SCN_ALIGN_1024BYTES:
+ case IMAGE_SCN_ALIGN_2048BYTES:
+ case IMAGE_SCN_ALIGN_4096BYTES:
+ case IMAGE_SCN_ALIGN_8192BYTES:
+ bSegAttr |= 6 << 5; /* page aligned, pharlabs extension. */
+ break;
+ }
+
+ /* The C field. */
+ bSegAttr |= 2 << 2; /* public */
+
+ /* The B field. We don't have 4GB segments, so leave it as zero. */
+
+ /* The D field shall be set as we're doing USE32. */
+ bSegAttr |= 1;
+
+
+ /* Done. */
+ if (!omfWriter_SegDef(pThis, bSegAttr, paShdrs[i].SizeOfRawData,
+ pThis->paSegments[i].iSegNm,
+ pThis->paSegments[i].iClassNm))
+ return false;
+ pThis->paSegments[i].iSegDef = iSegDef++;
+ }
+
+ /*
+ * Flat group definition (#1) - special, no members.
+ */
+ uint16_t iGrpDef = 1;
+ if ( !omfWriter_GrpDefBegin(pThis, idxGrpFlat)
+ || !omfWriter_GrpDefEnd(pThis))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == idxGrpFlat)
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ pThis->idxGrpFlat = iGrpDef++;
+
+ /*
+ * Data group definition (#2).
+ */
+ /** @todo do we need to consider missing segments and ordering? */
+ uint16_t cGrpNms = 0;
+ uint16_t aiGrpNms[2] = { 0, 0 }; /* Shut up, GCC. */
+ if (fHaveData)
+ aiGrpNms[cGrpNms++] = idxGrpData;
+ for (uint32_t iGrpNm = 0; iGrpNm < cGrpNms; iGrpNm++)
+ {
+ if (!omfWriter_GrpDefBegin(pThis, aiGrpNms[iGrpNm]))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == aiGrpNms[iGrpNm])
+ {
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ if (!omfWriter_GrpDefAddSegDef(pThis, pThis->paSegments[i].iSegDef))
+ return false;
+ }
+ if (!omfWriter_GrpDefEnd(pThis))
+ return false;
+ iGrpDef++;
+ }
+
+ return true;
+}
+
+/**
+ * This is for matching STATIC symbols with value 0 against the section name,
+ * to see if it's a section reference or symbol at offset 0 reference.
+ *
+ * @returns true / false.
+ * @param pszSymbol The symbol name.
+ * @param pachSectName8 The section name (8-bytes).
+ */
+static bool isCoffSymbolMatchingSectionName(const char *pszSymbol, uint8_t const pachSectName8[8])
+{
+ uint32_t off = 0;
+ char ch;
+ while (off < 8 && (ch = pszSymbol[off]) != '\0')
+ {
+ if (ch != pachSectName8[off])
+ return false;
+ off++;
+ }
+ while (off < 8)
+ {
+ if (!RT_C_IS_SPACE((ch = pachSectName8[off])))
+ return ch == '\0';
+ off++;
+ }
+ return true;
+}
+
+static bool convertCoffSymbolsToPubDefsAndExtDefs(POMFWRITER pThis, PCIMAGE_SYMBOL paSymbols, uint16_t cSymbols,
+ const char *pchStrTab, PCIMAGE_SECTION_HEADER paShdrs)
+{
+
+ if (!cSymbols)
+ return true;
+ uint32_t const cbStrTab = *(uint32_t const *)pchStrTab;
+ char szShort[16];
+
+ /*
+ * Process the symbols the first.
+ */
+ uint32_t iSymImageBase = UINT32_MAX;
+ uint32_t cAbsSyms = 0;
+ uint32_t cExtSyms = 0;
+ uint32_t cPubSyms = 0;
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ pThis->paSegments[iSeg].cPubDefs = 0;
+
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ {
+ const char *pszSymName = coffGetSymbolName(&paSymbols[iSym], pchStrTab, cbStrTab, szShort);
+
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_IGNORED;
+ pThis->paSymbols[iSym].idx = UINT16_MAX;
+ pThis->paSymbols[iSym].idxSegDef = UINT16_MAX;
+ pThis->paSymbols[iSym].idxGrpDef = UINT16_MAX;
+
+ int16_t const idxSection = paSymbols[iSym].SectionNumber;
+ if ( (idxSection >= 1 && idxSection <= (int32_t)pThis->cSegments)
+ || idxSection == IMAGE_SYM_ABSOLUTE)
+ {
+ switch (paSymbols[iSym].StorageClass)
+ {
+ case IMAGE_SYM_CLASS_EXTERNAL:
+ if (idxSection != IMAGE_SYM_ABSOLUTE)
+ {
+ if (pThis->paSegments[idxSection - 1].iSegDef != UINT16_MAX)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSymbols[iSym].idxSegDef = pThis->paSegments[idxSection - 1].iSegDef;
+ pThis->paSymbols[iSym].idxGrpDef = pThis->paSegments[idxSection - 1].iGrpDef;
+ pThis->paSegments[idxSection - 1].cPubDefs++;
+ cPubSyms++;
+ }
+ }
+ else
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSymbols[iSym].idxSegDef = 0;
+ pThis->paSymbols[iSym].idxGrpDef = 0;
+ cAbsSyms++;
+ }
+ break;
+
+ case IMAGE_SYM_CLASS_STATIC:
+ if ( paSymbols[iSym].Value == 0
+ && idxSection != IMAGE_SYM_ABSOLUTE
+ && isCoffSymbolMatchingSectionName(pszSymName, paShdrs[idxSection - 1].Name) )
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_SEGDEF;
+ pThis->paSymbols[iSym].idxSegDef = pThis->paSegments[idxSection - 1].iSegDef;
+ pThis->paSymbols[iSym].idxGrpDef = pThis->paSegments[idxSection - 1].iGrpDef;
+ break;
+ }
+ RT_FALL_THRU();
+
+ case IMAGE_SYM_CLASS_END_OF_FUNCTION:
+ case IMAGE_SYM_CLASS_AUTOMATIC:
+ case IMAGE_SYM_CLASS_REGISTER:
+ case IMAGE_SYM_CLASS_LABEL:
+ case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT:
+ case IMAGE_SYM_CLASS_ARGUMENT:
+ case IMAGE_SYM_CLASS_STRUCT_TAG:
+ case IMAGE_SYM_CLASS_MEMBER_OF_UNION:
+ case IMAGE_SYM_CLASS_UNION_TAG:
+ case IMAGE_SYM_CLASS_TYPE_DEFINITION:
+ case IMAGE_SYM_CLASS_ENUM_TAG:
+ case IMAGE_SYM_CLASS_MEMBER_OF_ENUM:
+ case IMAGE_SYM_CLASS_REGISTER_PARAM:
+ case IMAGE_SYM_CLASS_BIT_FIELD:
+ case IMAGE_SYM_CLASS_BLOCK:
+ case IMAGE_SYM_CLASS_FUNCTION:
+ case IMAGE_SYM_CLASS_END_OF_STRUCT:
+ case IMAGE_SYM_CLASS_FILE:
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_INTERNAL;
+ if (idxSection != IMAGE_SYM_ABSOLUTE)
+ {
+ pThis->paSymbols[iSym].idxSegDef = pThis->paSegments[idxSection - 1].iSegDef;
+ pThis->paSymbols[iSym].idxGrpDef = pThis->paSegments[idxSection - 1].iGrpDef;
+ }
+ else
+ {
+ pThis->paSymbols[iSym].idxSegDef = 0;
+ pThis->paSymbols[iSym].idxGrpDef = 0;
+ }
+ break;
+
+ case IMAGE_SYM_CLASS_SECTION:
+ case IMAGE_SYM_CLASS_EXTERNAL_DEF:
+ case IMAGE_SYM_CLASS_NULL:
+ case IMAGE_SYM_CLASS_UNDEFINED_LABEL:
+ case IMAGE_SYM_CLASS_UNDEFINED_STATIC:
+ case IMAGE_SYM_CLASS_CLR_TOKEN:
+ case IMAGE_SYM_CLASS_FAR_EXTERNAL:
+ case IMAGE_SYM_CLASS_WEAK_EXTERNAL:
+ return error(pThis->pszSrc, "Unsupported storage class value %#x for symbol #%u (%s)\n",
+ paSymbols[iSym].StorageClass, iSym, pszSymName);
+
+ default:
+ return error(pThis->pszSrc, "Unknown storage class value %#x for symbol #%u (%s)\n",
+ paSymbols[iSym].StorageClass, iSym, pszSymName);
+ }
+ }
+ else if (idxSection == IMAGE_SYM_UNDEFINED)
+ {
+ if ( paSymbols[iSym].StorageClass == IMAGE_SYM_CLASS_EXTERNAL
+ || paSymbols[iSym].StorageClass == IMAGE_SYM_CLASS_EXTERNAL_DEF)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_EXTDEF;
+ cExtSyms++;
+ if (iSymImageBase == UINT32_MAX && strcmp(pszSymName, "__ImageBase") == 0)
+ iSymImageBase = iSym;
+ }
+ else
+ return error(pThis->pszSrc, "Unknown/unknown storage class value %#x for undefined symbol #%u (%s)\n",
+ paSymbols[iSym].StorageClass, iSym, pszSymName);
+ }
+ else if (idxSection != IMAGE_SYM_DEBUG)
+ return error(pThis->pszSrc, "Invalid section number %#x for symbol #%u (%s)\n", idxSection, iSym, pszSymName);
+
+ /* Skip AUX symbols. */
+ uint8_t cAuxSyms = paSymbols[iSym].NumberOfAuxSymbols;
+ while (cAuxSyms-- > 0)
+ {
+ iSym++;
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_INVALID;
+ pThis->paSymbols[iSym].idx = UINT16_MAX;
+ }
+ }
+
+ /*
+ * Emit the PUBDEFs the first time around (see order of records in TIS spec).
+ */
+ uint16_t idxPubDef = 1;
+ if (cPubSyms)
+ {
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ if (pThis->paSegments[iSeg].cPubDefs > 0)
+ {
+ uint16_t const idxSegDef = pThis->paSegments[iSeg].iSegDef;
+ if (!omfWriter_PubDefBegin(pThis, pThis->paSegments[iSeg].iGrpDef, idxSegDef))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == idxSegDef
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ /* Underscore prefix all symbols not already underscored or mangled. */
+ const char *pszName = coffGetSymbolName(&paSymbols[iSym], pchStrTab, cbStrTab, szShort);
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].Value, pszName, pszName[0] != '_' && pszName[0] != '?'))
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+ }
+
+ if (cAbsSyms > 0)
+ {
+ if (!omfWriter_PubDefBegin(pThis, 0, 0))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == 0
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ /* Underscore prefix all symbols not already underscored or mangled. */
+ const char *pszName = coffGetSymbolName(&paSymbols[iSym], pchStrTab, cbStrTab, szShort);
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].Value, pszName, pszName[0] != '_' && pszName[0] != '?') )
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+
+ /*
+ * Go over the symbol table and emit external definition records.
+ */
+ if (!omfWriter_ExtDefBegin(pThis))
+ return false;
+ uint16_t idxExtDef = 1;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if (pThis->paSymbols[iSym].enmType == OMFSYMTYPE_EXTDEF)
+ {
+ /* Underscore prefix all symbols not already underscored or mangled. */
+ const char *pszName = coffGetSymbolName(&paSymbols[iSym], pchStrTab, cbStrTab, szShort);
+ if (!omfWriter_ExtDefAdd(pThis, pszName, pszName[0] != '_' && pszName[0] != '?'))
+ return false;
+ pThis->paSymbols[iSym].idx = idxExtDef++;
+ }
+
+ /* Always add an __ImageBase reference, in case we need it to deal with ADDR32NB fixups. */
+ /** @todo maybe we don't actually need this and could use FLAT instead? */
+ if (iSymImageBase != UINT32_MAX)
+ pThis->idxExtImageBase = pThis->paSymbols[iSymImageBase].idx;
+ else if (omfWriter_ExtDefAdd(pThis, "__ImageBase", false /*fPrependUnderscore*/))
+ pThis->idxExtImageBase = idxExtDef;
+ else
+ return false;
+
+ if (!omfWriter_ExtDefEnd(pThis))
+ return false;
+
+ return true;
+}
+
+
+static bool convertCoffSectionsToLeDataAndFixupps(POMFWRITER pThis, uint8_t const *pbFile, size_t cbFile,
+ PCIMAGE_SECTION_HEADER paShdrs, uint16_t cSections,
+ PCIMAGE_SYMBOL paSymbols, uint16_t cSymbols, const char *pchStrTab)
+{
+ RT_NOREF_PV(cbFile);
+ RT_NOREF_PV(cSections);
+ RT_NOREF_PV(cSymbols);
+
+ uint32_t const cbStrTab = *(uint32_t const *)pchStrTab;
+ bool fRet = true;
+ for (uint32_t i = 0; i < pThis->cSegments; i++)
+ {
+ if (pThis->paSegments[i].iSegDef == UINT16_MAX)
+ continue;
+
+ char szShortName[16];
+ const char *pszSegNm = pThis->paSegments[i].pszName;
+ uint16_t cRelocs = paShdrs[i].NumberOfRelocations;
+ PCIMAGE_RELOCATION paRelocs = (PCIMAGE_RELOCATION)&pbFile[paShdrs[i].PointerToRelocations];
+ uint32_t cbVirtData = paShdrs[i].SizeOfRawData;
+ uint32_t cbData = paShdrs[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ? 0 : cbVirtData;
+ uint8_t const *pbData = &pbFile[paShdrs[i].PointerToRawData];
+ uint32_t off = 0;
+
+ /* Check that the relocations are sorted and within the section. */
+ for (uint32_t iReloc = 1; iReloc < cRelocs; iReloc++)
+ if (paRelocs[iReloc - 1].u.VirtualAddress >= paRelocs[iReloc].u.VirtualAddress)
+ return error(pThis->pszSrc, "Section #%u (%s) relocations aren't sorted\n", i, pszSegNm);
+ if ( cRelocs > 0
+ && paRelocs[cRelocs - 1].u.VirtualAddress - paShdrs[i].VirtualAddress
+ + COFF_AMD64_RELOC_SIZE(paRelocs[cRelocs - 1].Type) > cbVirtData)
+ return error(pThis->pszSrc,
+ "Section #%u (%s) relocations beyond section data! cbVirtData=%#x RvaFix=%#x RVASeg=%#x type=%#x\n",
+ i, pszSegNm, cbVirtData, paRelocs[cRelocs - 1].u.VirtualAddress, paShdrs[i].VirtualAddress,
+ paRelocs[cRelocs - 1].Type);
+
+ /* The OMF record size requires us to split larger sections up. To make
+ life simple, we fill zeros for unitialized (BSS) stuff. */
+ const uint32_t cbMaxData = RT_MIN(OMF_MAX_RECORD_PAYLOAD - 1 - (pThis->paSegments[i].iSegDef >= 128) - 4 - 1, _1K);
+ while (cbVirtData > 0)
+ {
+ /* Figure out how many bytes to put out in this chunk. Must make sure
+ fixups doesn't cross chunk boundraries. ASSUMES sorted relocs. */
+ uint32_t cChunkRelocs = cRelocs;
+ uint32_t cbChunk = cbVirtData;
+ uint32_t uRvaEnd = paShdrs[i].VirtualAddress + off + cbChunk;
+ if (cbChunk > cbMaxData)
+ {
+ cbChunk = cbMaxData;
+ uRvaEnd = paShdrs[i].VirtualAddress + off + cbChunk;
+ cChunkRelocs = 0;
+
+ /* Quickly determin the reloc range. */
+ while ( cChunkRelocs < cRelocs
+ && paRelocs[cChunkRelocs].u.VirtualAddress < uRvaEnd)
+ cChunkRelocs++;
+
+ /* Ensure final reloc doesn't go beyond chunk. */
+ while ( cChunkRelocs > 0
+ && paRelocs[cChunkRelocs - 1].u.VirtualAddress + COFF_AMD64_RELOC_SIZE(paRelocs[cChunkRelocs - 1].Type)
+ > uRvaEnd)
+ {
+ uint32_t cbDrop = uRvaEnd - paRelocs[cChunkRelocs - 1].u.VirtualAddress;
+ cbChunk -= cbDrop;
+ uRvaEnd -= cbDrop;
+ cChunkRelocs--;
+ }
+
+ if (!cbVirtData)
+ return error(pThis->pszSrc, "Wtf? cbVirtData is zero!\n");
+ }
+
+ /*
+ * We stash the bytes into the OMF writer record buffer, receiving a
+ * pointer to the start of it so we can make adjustments if necessary.
+ */
+ uint8_t *pbCopy;
+ if (!omfWriter_LEDataBeginEx(pThis, pThis->paSegments[i].iSegDef, off, cbChunk, cbData, pbData, &pbCopy))
+ return false;
+
+ /*
+ * Convert fiuxps.
+ */
+ uint32_t const uRvaChunk = paShdrs[i].VirtualAddress + off;
+ for (uint32_t iReloc = 0; iReloc < cChunkRelocs; iReloc++)
+ {
+ /* Get the OMF and COFF data for the symbol the reloc references. */
+ if (paRelocs[iReloc].SymbolTableIndex >= pThis->cSymbols)
+ return error(pThis->pszSrc, "Relocation symtab index (%#x) is out of range in segment #%u '%s'\n",
+ paRelocs[iReloc].SymbolTableIndex, i, pszSegNm);
+ PCIMAGE_SYMBOL pCoffSym = &paSymbols[paRelocs[iReloc].SymbolTableIndex];
+ POMFSYMBOL pOmfSym = &pThis->paSymbols[paRelocs[iReloc].SymbolTableIndex];
+
+ /* Calc fixup location in the pending chunk and setup a flexible pointer to it. */
+ uint16_t offDataRec = (uint16_t)(paRelocs[iReloc].u.VirtualAddress - uRvaChunk);
+ RTPTRUNION uLoc;
+ uLoc.pu8 = &pbCopy[offDataRec];
+
+ /* OMF fixup data initialized with typical defaults. */
+ bool fSelfRel = true;
+ uint8_t bLocation = OMF_FIX_LOC_32BIT_OFFSET;
+ uint8_t bFrame = OMF_FIX_F_GRPDEF;
+ uint16_t idxFrame = pThis->idxGrpFlat;
+ uint8_t bTarget;
+ uint16_t idxTarget;
+ bool fTargetDisp;
+ uint32_t offTargetDisp;
+ switch (pOmfSym->enmType)
+ {
+ case OMFSYMTYPE_INTERNAL:
+ case OMFSYMTYPE_PUBDEF:
+ bTarget = OMF_FIX_T_SEGDEF;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = true;
+ offTargetDisp = pCoffSym->Value;
+ break;
+
+ case OMFSYMTYPE_SEGDEF:
+ bTarget = OMF_FIX_T_SEGDEF_NO_DISP;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ case OMFSYMTYPE_EXTDEF:
+ bTarget = OMF_FIX_T_EXTDEF_NO_DISP;
+ idxTarget = pOmfSym->idx;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ default:
+ return error(pThis->pszSrc, "Relocation in segment #%u '%s' references ignored or invalid symbol (%s)\n",
+ i, pszSegNm, coffGetSymbolName(pCoffSym, pchStrTab, cbStrTab, szShortName));
+ }
+
+ /* Do COFF relocation type conversion. */
+ switch (paRelocs[iReloc].Type)
+ {
+ case IMAGE_REL_AMD64_ADDR64:
+ {
+ uint64_t uAddend = *uLoc.pu64;
+ if (uAddend > _1G)
+ fRet = error(pThis->pszSrc, "ADDR64 with large addend (%#llx) at %#x in segment #%u '%s'\n",
+ uAddend, paRelocs[iReloc].u.VirtualAddress, i, pszSegNm);
+ fSelfRel = false;
+ break;
+ }
+
+ case IMAGE_REL_AMD64_REL32_1:
+ case IMAGE_REL_AMD64_REL32_2:
+ case IMAGE_REL_AMD64_REL32_3:
+ case IMAGE_REL_AMD64_REL32_4:
+ case IMAGE_REL_AMD64_REL32_5:
+ /** @todo Check whether OMF read addends from the data or relies on the
+ * displacement. Also, check what it's relative to. */
+ *uLoc.pu32 -= paRelocs[iReloc].Type - IMAGE_REL_AMD64_REL32;
+ break;
+
+ case IMAGE_REL_AMD64_ADDR32:
+ fSelfRel = false;
+ break;
+
+ case IMAGE_REL_AMD64_ADDR32NB:
+ fSelfRel = false;
+ bFrame = OMF_FIX_F_EXTDEF;
+ idxFrame = pThis->idxExtImageBase;
+ break;
+
+ case IMAGE_REL_AMD64_REL32:
+ /* defaults are ok. */
+ break;
+
+ case IMAGE_REL_AMD64_SECTION:
+ bLocation = OMF_FIX_LOC_16BIT_SEGMENT;
+ RT_FALL_THRU();
+
+ case IMAGE_REL_AMD64_SECREL:
+ fSelfRel = false;
+ if (pOmfSym->enmType == OMFSYMTYPE_EXTDEF)
+ {
+ bFrame = OMF_FIX_F_EXTDEF;
+ idxFrame = pOmfSym->idx;
+ }
+ else
+ {
+ bFrame = OMF_FIX_F_SEGDEF;
+ idxFrame = pOmfSym->idxSegDef;
+ }
+ break;
+
+ case IMAGE_REL_AMD64_ABSOLUTE:
+ continue; /* Ignore it like the PECOFF.DOC says we should. */
+
+ case IMAGE_REL_AMD64_SECREL7:
+ default:
+ return error(pThis->pszSrc, "Unsupported fixup type %#x (%s) at rva=%#x in section #%u '%-8.8s'\n",
+ paRelocs[iReloc].Type,
+ paRelocs[iReloc].Type < RT_ELEMENTS(g_apszCoffAmd64RelTypes)
+ ? g_apszCoffAmd64RelTypes[paRelocs[iReloc].Type] : "unknown",
+ paRelocs[iReloc].u.VirtualAddress, i, paShdrs[i].Name);
+ }
+
+ /* Add the fixup. */
+ if (idxFrame == UINT16_MAX)
+ error(pThis->pszSrc, "idxFrame=UINT16_MAX for %s type=%s\n",
+ coffGetSymbolName(pCoffSym, pchStrTab, cbStrTab, szShortName),
+ g_apszCoffAmd64RelTypes[paRelocs[iReloc].Type]);
+ fRet = omfWriter_LEDataAddFixup(pThis, offDataRec, fSelfRel, bLocation, bFrame, idxFrame,
+ bTarget, idxTarget, fTargetDisp, offTargetDisp) && fRet;
+ }
+
+ /*
+ * Write the LEDATA and associated FIXUPPs.
+ */
+ if (!omfWriter_LEDataEnd(pThis))
+ return false;
+
+ /*
+ * Advance.
+ */
+ paRelocs += cChunkRelocs;
+ cRelocs -= cChunkRelocs;
+ if (cbData > cbChunk)
+ {
+ cbData -= cbChunk;
+ pbData += cbChunk;
+ }
+ else
+ cbData = 0;
+ off += cbChunk;
+ cbVirtData -= cbChunk;
+ }
+ }
+
+ return fRet;
+}
+
+
+static bool convertCoffToOmf(const char *pszFile, uint8_t const *pbFile, size_t cbFile, FILE *pDst)
+{
+ /*
+ * Validate the source file a little.
+ */
+ if (!validateCoff(pszFile, pbFile, cbFile))
+ return false;
+
+ /*
+ * Instantiate the OMF writer.
+ */
+ PIMAGE_FILE_HEADER pHdr = (PIMAGE_FILE_HEADER)pbFile;
+ POMFWRITER pThis = omfWriter_Create(pszFile, pHdr->NumberOfSections, pHdr->NumberOfSymbols, pDst);
+ if (!pThis)
+ return false;
+
+ /*
+ * Write the OMF object file.
+ */
+ if (omfWriter_BeginModule(pThis, pszFile))
+ {
+ PCIMAGE_SECTION_HEADER paShdrs = (PCIMAGE_SECTION_HEADER)(pHdr + 1);
+ PCIMAGE_SYMBOL paSymTab = (PCIMAGE_SYMBOL)&pbFile[pHdr->PointerToSymbolTable];
+ const char *pchStrTab = (const char *)&paSymTab[pHdr->NumberOfSymbols];
+ if ( convertCoffSectionsToSegDefsAndGrpDefs(pThis, paShdrs, pHdr->NumberOfSections)
+ && convertCoffSymbolsToPubDefsAndExtDefs(pThis, paSymTab, pHdr->NumberOfSymbols, pchStrTab, paShdrs)
+ && omfWriter_LinkPassSeparator(pThis)
+ && convertCoffSectionsToLeDataAndFixupps(pThis, pbFile, cbFile, paShdrs, pHdr->NumberOfSections,
+ paSymTab, pHdr->NumberOfSymbols, pchStrTab)
+ && omfWriter_EndModule(pThis) )
+ {
+
+ omfWriter_Destroy(pThis);
+ return true;
+ }
+ }
+
+ omfWriter_Destroy(pThis);
+ return false;
+}
+
+
+/*********************************************************************************************************************************
+* Mach-O/AMD64 -> OMF/i386 Converter *
+*********************************************************************************************************************************/
+
+//#define MACHO_TO_OMF_CONVERSION
+#ifdef MACHO_TO_OMF_CONVERSION
+
+/** AMD64 relocation type names for Mach-O. */
+static const char * const g_apszMachOAmd64RelTypes[] =
+{
+ "X86_64_RELOC_UNSIGNED",
+ "X86_64_RELOC_SIGNED",
+ "X86_64_RELOC_BRANCH",
+ "X86_64_RELOC_GOT_LOAD",
+ "X86_64_RELOC_GOT",
+ "X86_64_RELOC_SUBTRACTOR",
+ "X86_64_RELOC_SIGNED_1",
+ "X86_64_RELOC_SIGNED_2",
+ "X86_64_RELOC_SIGNED_4"
+};
+
+/** AMD64 relocation type sizes for Mach-O. */
+static uint8_t const g_acbMachOAmd64RelTypes[] =
+{
+ 8, /* X86_64_RELOC_UNSIGNED */
+ 4, /* X86_64_RELOC_SIGNED */
+ 4, /* X86_64_RELOC_BRANCH */
+ 4, /* X86_64_RELOC_GOT_LOAD */
+ 4, /* X86_64_RELOC_GOT */
+ 8, /* X86_64_RELOC_SUBTRACTOR */
+ 4, /* X86_64_RELOC_SIGNED_1 */
+ 4, /* X86_64_RELOC_SIGNED_2 */
+ 4, /* X86_64_RELOC_SIGNED_4 */
+};
+
+/** Macro for getting the size of a AMD64 Mach-O relocation. */
+#define MACHO_AMD64_RELOC_SIZE(a_Type) ( (a_Type) < RT_ELEMENTS(g_acbMachOAmd64RelTypes) ? g_acbMachOAmd64RelTypes[(a_Type)] : 1)
+
+
+typedef struct MACHODETAILS
+{
+ /** The ELF header. */
+ Elf64_Ehdr const *pEhdr;
+ /** The section header table. */
+ Elf64_Shdr const *paShdrs;
+ /** The string table for the section names. */
+ const char *pchShStrTab;
+
+ /** The symbol table section number. UINT16_MAX if not found. */
+ uint16_t iSymSh;
+ /** The string table section number. UINT16_MAX if not found. */
+ uint16_t iStrSh;
+
+ /** The symbol table. */
+ Elf64_Sym const *paSymbols;
+ /** The number of symbols in the symbol table. */
+ uint32_t cSymbols;
+
+ /** Pointer to the (symbol) string table if found. */
+ const char *pchStrTab;
+ /** The string table size. */
+ size_t cbStrTab;
+
+} MACHODETAILS;
+typedef MACHODETAILS *PMACHODETAILS;
+typedef MACHODETAILS const *PCMACHODETAILS;
+
+
+static bool validateMacho(const char *pszFile, uint8_t const *pbFile, size_t cbFile, PMACHODETAILS pMachOStuff)
+{
+ /*
+ * Initialize the Mach-O details structure.
+ */
+ memset(pMachOStuff, 0, sizeof(*pMachOStuff));
+ pMachOStuff->iSymSh = UINT16_MAX;
+ pMachOStuff->iStrSh = UINT16_MAX;
+
+ /*
+ * Validate the header and our other expectations.
+ */
+ Elf64_Ehdr const *pEhdr = (Elf64_Ehdr const *)pbFile;
+ pMachOStuff->pEhdr = pEhdr;
+ if ( pEhdr->e_ident[EI_CLASS] != ELFCLASS64
+ || pEhdr->e_ident[EI_DATA] != ELFDATA2LSB
+ || pEhdr->e_ehsize != sizeof(Elf64_Ehdr)
+ || pEhdr->e_shentsize != sizeof(Elf64_Shdr)
+ || pEhdr->e_version != EV_CURRENT )
+ return error(pszFile, "Unsupported ELF config\n");
+ if (pEhdr->e_type != ET_REL)
+ return error(pszFile, "Expected relocatable ELF file (e_type=%d)\n", pEhdr->e_type);
+ if (pEhdr->e_machine != EM_X86_64)
+ return error(pszFile, "Expected relocatable ELF file (e_type=%d)\n", pEhdr->e_machine);
+ if (pEhdr->e_phnum != 0)
+ return error(pszFile, "Expected e_phnum to be zero not %u\n", pEhdr->e_phnum);
+ if (pEhdr->e_shnum < 2)
+ return error(pszFile, "Expected e_shnum to be two or higher\n");
+ if (pEhdr->e_shstrndx >= pEhdr->e_shnum || pEhdr->e_shstrndx == 0)
+ return error(pszFile, "Bad e_shstrndx=%u (e_shnum=%u)\n", pEhdr->e_shstrndx, pEhdr->e_shnum);
+ if ( pEhdr->e_shoff >= cbFile
+ || pEhdr->e_shoff + pEhdr->e_shnum * sizeof(Elf64_Shdr) > cbFile)
+ return error(pszFile, "Section table is outside the file (e_shoff=%#llx, e_shnum=%u, cbFile=%#llx)\n",
+ pEhdr->e_shstrndx, pEhdr->e_shnum, (uint64_t)cbFile);
+
+ /*
+ * Locate the section name string table.
+ * We assume it's okay as we only reference it in verbose mode.
+ */
+ Elf64_Shdr const *paShdrs = (Elf64_Shdr const *)&pbFile[pEhdr->e_shoff];
+ pMachOStuff->paShdrs = paShdrs;
+
+ Elf64_Xword const cbShStrTab = paShdrs[pEhdr->e_shstrndx].sh_size;
+ if ( paShdrs[pEhdr->e_shstrndx].sh_offset > cbFile
+ || cbShStrTab > cbFile
+ || paShdrs[pEhdr->e_shstrndx].sh_offset + cbShStrTab > cbFile)
+ return error(pszFile,
+ "Section string table is outside the file (sh_offset=%#" ELF_FMT_X64 " sh_size=%#" ELF_FMT_X64 " cbFile=%#" ELF_FMT_X64 ")\n",
+ paShdrs[pEhdr->e_shstrndx].sh_offset, paShdrs[pEhdr->e_shstrndx].sh_size, (Elf64_Xword)cbFile);
+ const char *pchShStrTab = (const char *)&pbFile[paShdrs[pEhdr->e_shstrndx].sh_offset];
+ pMachOStuff->pchShStrTab = pchShStrTab;
+
+ /*
+ * Work the section table.
+ */
+ bool fRet = true;
+ for (uint32_t i = 1; i < pEhdr->e_shnum; i++)
+ {
+ if (paShdrs[i].sh_name >= cbShStrTab)
+ return error(pszFile, "Invalid sh_name value (%#x) for section #%u\n", paShdrs[i].sh_name, i);
+ const char *pszShNm = &pchShStrTab[paShdrs[i].sh_name];
+
+ if ( paShdrs[i].sh_offset > cbFile
+ || paShdrs[i].sh_size > cbFile
+ || paShdrs[i].sh_offset + paShdrs[i].sh_size > cbFile)
+ return error(pszFile, "Section #%u '%s' has data outside the file: %#" ELF_FMT_X64 " LB %#" ELF_FMT_X64 " (cbFile=%#" ELF_FMT_X64 ")\n",
+ i, pszShNm, paShdrs[i].sh_offset, paShdrs[i].sh_size, (Elf64_Xword)cbFile);
+ if (g_cVerbose)
+ printf("shdr[%u]: name=%#x '%s' type=%#x flags=%#" ELF_FMT_X64 " addr=%#" ELF_FMT_X64 " off=%#" ELF_FMT_X64 " size=%#" ELF_FMT_X64 "\n"
+ " link=%u info=%#x align=%#" ELF_FMT_X64 " entsize=%#" ELF_FMT_X64 "\n",
+ i, paShdrs[i].sh_name, pszShNm, paShdrs[i].sh_type, paShdrs[i].sh_flags,
+ paShdrs[i].sh_addr, paShdrs[i].sh_offset, paShdrs[i].sh_size,
+ paShdrs[i].sh_link, paShdrs[i].sh_info, paShdrs[i].sh_addralign, paShdrs[i].sh_entsize);
+
+ if (paShdrs[i].sh_link >= pEhdr->e_shnum)
+ return error(pszFile, "Section #%u '%s' links to a section outside the section table: %#x, max %#x\n",
+ i, pszShNm, paShdrs[i].sh_link, pEhdr->e_shnum);
+ if (!RT_IS_POWER_OF_TWO(paShdrs[i].sh_addralign))
+ return error(pszFile, "Section #%u '%s' alignment value is not a power of two: %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_addralign);
+ if (!RT_IS_POWER_OF_TWO(paShdrs[i].sh_addralign))
+ return error(pszFile, "Section #%u '%s' alignment value is not a power of two: %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_addralign);
+ if (paShdrs[i].sh_addr != 0)
+ return error(pszFile, "Section #%u '%s' has non-zero address: %#" ELF_FMT_X64 "\n", i, pszShNm, paShdrs[i].sh_addr);
+
+ if (paShdrs[i].sh_type == SHT_RELA)
+ {
+ if (paShdrs[i].sh_entsize != sizeof(Elf64_Rela))
+ return error(pszFile, "Expected sh_entsize to be %u not %u for section #%u (%s)\n", (unsigned)sizeof(Elf64_Rela),
+ paShdrs[i].sh_entsize, i, pszShNm);
+ uint32_t const cRelocs = paShdrs[i].sh_size / sizeof(Elf64_Rela);
+ if (cRelocs * sizeof(Elf64_Rela) != paShdrs[i].sh_size)
+ return error(pszFile, "Uneven relocation entry count in #%u (%s): sh_size=%#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size);
+ if ( paShdrs[i].sh_offset > cbFile
+ || paShdrs[i].sh_size >= cbFile
+ || paShdrs[i].sh_offset + paShdrs[i].sh_size > cbFile)
+ return error(pszFile, "The content of section #%u '%s' is outside the file (%#" ELF_FMT_X64 " LB %#" ELF_FMT_X64 ", cbFile=%#lx)\n",
+ i, pszShNm, paShdrs[i].sh_offset, paShdrs[i].sh_size, (unsigned long)cbFile);
+ if (paShdrs[i].sh_info != i - 1)
+ return error(pszFile, "Expected relocation section #%u (%s) to link to previous section: sh_info=%#u\n",
+ i, pszShNm, (unsigned)paShdrs[i].sh_link);
+ if (paShdrs[paShdrs[i].sh_link].sh_type != SHT_SYMTAB)
+ return error(pszFile, "Expected relocation section #%u (%s) to link to symbol table: sh_link=%#u -> sh_type=%#x\n",
+ i, pszShNm, (unsigned)paShdrs[i].sh_link, (unsigned)paShdrs[paShdrs[i].sh_link].sh_type);
+ uint32_t cSymbols = paShdrs[paShdrs[i].sh_link].sh_size / paShdrs[paShdrs[i].sh_link].sh_entsize;
+
+ Elf64_Rela const *paRelocs = (Elf64_Rela *)&pbFile[paShdrs[i].sh_offset];
+ for (uint32_t j = 0; j < cRelocs; j++)
+ {
+ uint8_t const bType = ELF64_R_TYPE(paRelocs[j].r_info);
+ if (RT_UNLIKELY(bType >= R_X86_64_COUNT))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": unknown fix up %#x (%+" ELF_FMT_D64 ")\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, bType, paRelocs[j].r_addend);
+ if (RT_UNLIKELY( j > 1
+ && paRelocs[j].r_offset <= paRelocs[j - 1].r_offset
+ && paRelocs[j].r_offset + ELF_AMD64_RELOC_SIZE(ELF64_R_TYPE(paRelocs[j].r_info))
+ < paRelocs[j - 1].r_offset ))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": out of offset order (prev %" ELF_FMT_X64 ")\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, paRelocs[j - 1].r_offset);
+ uint32_t const iSymbol = ELF64_R_SYM(paRelocs[j].r_info);
+ if (RT_UNLIKELY(iSymbol >= cSymbols))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": symbol index (%#x) out of bounds (%#x)\n",
+ paRelocs[j].r_offset, paRelocs[j].r_info, iSymbol, cSymbols);
+ }
+ if (RT_UNLIKELY( cRelocs > 0
+ && fRet
+ && ( paRelocs[cRelocs - 1].r_offset > paShdrs[i - 1].sh_size
+ || paRelocs[cRelocs - 1].r_offset + ELF_AMD64_RELOC_SIZE(ELF64_R_TYPE(paRelocs[cRelocs-1].r_info))
+ > paShdrs[i - 1].sh_size )))
+ fRet = error(pszFile,
+ "%#018" ELF_FMT_X64 " %#018" ELF_FMT_X64 ": out of bounds (sh_size %" ELF_FMT_X64 ")\n",
+ paRelocs[cRelocs - 1].r_offset, paRelocs[cRelocs - 1].r_info, paShdrs[i - 1].sh_size);
+
+ }
+ else if (paShdrs[i].sh_type == SHT_REL)
+ fRet = error(pszFile, "Section #%u '%s': Unexpected SHT_REL section\n", i, pszShNm);
+ else if (paShdrs[i].sh_type == SHT_SYMTAB)
+ {
+ if (paShdrs[i].sh_entsize != sizeof(Elf64_Sym))
+ fRet = error(pszFile, "Section #%u '%s': Unsupported symbol table entry size in : #%u (expected #%u)\n",
+ i, pszShNm, paShdrs[i].sh_entsize, sizeof(Elf64_Sym));
+ Elf64_Xword const cSymbols = paShdrs[i].sh_size / paShdrs[i].sh_entsize;
+ if (cSymbols * paShdrs[i].sh_entsize != paShdrs[i].sh_size)
+ fRet = error(pszFile, "Section #%u '%s': Size not a multiple of entry size: %#" ELF_FMT_X64 " %% %#" ELF_FMT_X64 " = %#" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size, paShdrs[i].sh_entsize, paShdrs[i].sh_size % paShdrs[i].sh_entsize);
+ if (cSymbols > UINT32_MAX)
+ fRet = error(pszFile, "Section #%u '%s': too many symbols: %" ELF_FMT_X64 "\n",
+ i, pszShNm, paShdrs[i].sh_size, cSymbols);
+
+ if (pMachOStuff->iSymSh == UINT16_MAX)
+ {
+ pMachOStuff->iSymSh = (uint16_t)i;
+ pMachOStuff->paSymbols = (Elf64_Sym const *)&pbFile[paShdrs[i].sh_offset];
+ pMachOStuff->cSymbols = cSymbols;
+
+ if (paShdrs[i].sh_link != 0)
+ {
+ /* Note! The symbol string table section header may not have been validated yet! */
+ Elf64_Shdr const *pStrTabShdr = &paShdrs[paShdrs[i].sh_link];
+ pMachOStuff->iStrSh = paShdrs[i].sh_link;
+ pMachOStuff->pchStrTab = (const char *)&pbFile[pStrTabShdr->sh_offset];
+ pMachOStuff->cbStrTab = (size_t)pStrTabShdr->sh_size;
+ }
+ else
+ fRet = error(pszFile, "Section #%u '%s': String table link is out of bounds (%#x)\n",
+ i, pszShNm, paShdrs[i].sh_link);
+ }
+ else
+ fRet = error(pszFile, "Section #%u '%s': Found additonal symbol table, previous in #%u\n",
+ i, pszShNm, pMachOStuff->iSymSh);
+ }
+ }
+ return fRet;
+}
+
+static bool convertMachoSectionsToSegDefsAndGrpDefs(POMFWRITER pThis, PCMACHODETAILS pMachOStuff)
+{
+ /*
+ * Do the list of names pass.
+ */
+ uint16_t idxGrpFlat, idxGrpData;
+ uint16_t idxClassCode, idxClassData, idxClassDwarf;
+ if ( !omfWriter_LNamesBegin(pThis, true /*fAddZeroEntry*/)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FLAT"), &idxGrpFlat)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3DATA64_GROUP"), &idxGrpData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("BS3CLASS64CODE"), &idxClassCode)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("FAR_DATA"), &idxClassData)
+ || !omfWriter_LNamesAddN(pThis, RT_STR_TUPLE("DWARF"), &idxClassDwarf)
+ )
+ return false;
+
+ bool fHaveData = false;
+ Elf64_Shdr const *pShdr = &pMachOStuff->paShdrs[1];
+ Elf64_Half const cSections = pMachOStuff->pEhdr->e_shnum;
+ for (Elf64_Half i = 1; i < cSections; i++, pShdr++)
+ {
+ const char *pszName = &pMachOStuff->pchShStrTab[pShdr->sh_name];
+ if (*pszName == '\0')
+ return error(pThis->pszSrc, "Section #%u has an empty name!\n", i);
+
+ switch (pShdr->sh_type)
+ {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ /* We drop a few sections we don't want:. */
+ if ( strcmp(pszName, ".comment") != 0 /* compiler info */
+ && strcmp(pszName, ".note.GNU-stack") != 0 /* some empty section for hinting the linker/whatever */
+ && strcmp(pszName, ".eh_frame") != 0 /* unwind / exception info */
+ )
+ {
+ pThis->paSegments[i].iSegDef = UINT16_MAX;
+ pThis->paSegments[i].iGrpDef = UINT16_MAX;
+
+ /* Translate the name and determine group and class.
+ Note! We currently strip sub-sections. */
+ if ( strcmp(pszName, ".text") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".text.")) == 0)
+ {
+ pszName = "BS3TEXT64";
+ pThis->paSegments[i].iGrpNm = idxGrpFlat;
+ pThis->paSegments[i].iClassNm = idxClassCode;
+ }
+ else if ( strcmp(pszName, ".data") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".data.")) == 0)
+ {
+ pszName = "BS3DATA64";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strcmp(pszName, ".bss") == 0)
+ {
+ pszName = "BS3BSS64";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if ( strcmp(pszName, ".rodata") == 0
+ || strncmp(pszName, RT_STR_TUPLE(".rodata.")) == 0)
+ {
+ pszName = "BS3DATA64CONST";
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ }
+ else if (strncmp(pszName, RT_STR_TUPLE(".debug_")) == 0)
+ {
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = idxClassDwarf;
+ }
+ else
+ {
+ pThis->paSegments[i].iGrpNm = idxGrpData;
+ pThis->paSegments[i].iClassNm = idxClassData;
+ error(pThis->pszSrc, "Unknown data (?) segment: '%s'\n", pszName);
+ }
+
+ /* Save the name. */
+ pThis->paSegments[i].pszName = strdup(pszName);
+ if (!pThis->paSegments[i].pszName)
+ return error(pThis->pszSrc, "Out of memory!\n");
+
+ /* Add the section name. */
+ if (!omfWriter_LNamesAdd(pThis, pThis->paSegments[i].pszName, &pThis->paSegments[i].iSegNm))
+ return false;
+
+ fHaveData |= pThis->paSegments[i].iGrpDef == idxGrpData;
+ break;
+ }
+ RT_FALL_THRU();
+
+ default:
+ pThis->paSegments[i].iSegDef = UINT16_MAX;
+ pThis->paSegments[i].iGrpDef = UINT16_MAX;
+ pThis->paSegments[i].iSegNm = UINT16_MAX;
+ pThis->paSegments[i].iGrpNm = UINT16_MAX;
+ pThis->paSegments[i].iClassNm = UINT16_MAX;
+ pThis->paSegments[i].pszName = NULL;
+ break;
+ }
+ }
+
+ if (!omfWriter_LNamesEnd(pThis))
+ return false;
+
+ /*
+ * Emit segment definitions.
+ */
+ uint16_t iSegDef = 1; /* Start counting at 1. */
+ pShdr = &pMachOStuff->paShdrs[1];
+ for (Elf64_Half i = 1; i < cSections; i++, pShdr++)
+ {
+ if (pThis->paSegments[i].iSegNm == UINT16_MAX)
+ continue;
+
+ uint8_t bSegAttr = 0;
+
+ /* The A field. */
+ switch (pShdr->sh_addralign)
+ {
+ case 0:
+ case 1:
+ bSegAttr |= 1 << 5;
+ break;
+ case 2:
+ bSegAttr |= 2 << 5;
+ break;
+ case 4:
+ bSegAttr |= 5 << 5;
+ break;
+ case 8:
+ case 16:
+ bSegAttr |= 3 << 5;
+ break;
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ bSegAttr |= 4 << 5;
+ break;
+ default:
+ bSegAttr |= 6 << 5; /* page aligned, pharlabs extension. */
+ break;
+ }
+
+ /* The C field. */
+ bSegAttr |= 2 << 2; /* public */
+
+ /* The B field. We don't have 4GB segments, so leave it as zero. */
+
+ /* The D field shall be set as we're doing USE32. */
+ bSegAttr |= 1;
+
+
+ /* Done. */
+ if (!omfWriter_SegDef(pThis, bSegAttr, (uint32_t)pShdr->sh_size,
+ pThis->paSegments[i].iSegNm,
+ pThis->paSegments[i].iClassNm))
+ return false;
+ pThis->paSegments[i].iSegDef = iSegDef++;
+ }
+
+ /*
+ * Flat group definition (#1) - special, no members.
+ */
+ uint16_t iGrpDef = 1;
+ if ( !omfWriter_GrpDefBegin(pThis, idxGrpFlat)
+ || !omfWriter_GrpDefEnd(pThis))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == idxGrpFlat)
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ pThis->idxGrpFlat = iGrpDef++;
+
+ /*
+ * Data group definition (#2).
+ */
+ /** @todo do we need to consider missing segments and ordering? */
+ uint16_t cGrpNms = 0;
+ uint16_t aiGrpNms[2] = { 0, 0 }; /* Shut up, GCC. */
+ if (fHaveData)
+ aiGrpNms[cGrpNms++] = idxGrpData;
+ for (uint32_t iGrpNm = 0; iGrpNm < cGrpNms; iGrpNm++)
+ {
+ if (!omfWriter_GrpDefBegin(pThis, aiGrpNms[iGrpNm]))
+ return false;
+ for (uint16_t i = 0; i < cSections; i++)
+ if (pThis->paSegments[i].iGrpNm == aiGrpNms[iGrpNm])
+ {
+ pThis->paSegments[i].iGrpDef = iGrpDef;
+ if (!omfWriter_GrpDefAddSegDef(pThis, pThis->paSegments[i].iSegDef))
+ return false;
+ }
+ if (!omfWriter_GrpDefEnd(pThis))
+ return false;
+ iGrpDef++;
+ }
+
+ return true;
+}
+
+static bool convertMachOSymbolsToPubDefsAndExtDefs(POMFWRITER pThis, PCMACHODETAILS pMachOStuff)
+{
+ if (!pMachOStuff->cSymbols)
+ return true;
+
+ /*
+ * Process the symbols the first.
+ */
+ uint32_t cAbsSyms = 0;
+ uint32_t cExtSyms = 0;
+ uint32_t cPubSyms = 0;
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ pThis->paSegments[iSeg].cPubDefs = 0;
+
+ uint32_t const cSections = pMachOStuff->pEhdr->e_shnum;
+ uint32_t const cSymbols = pMachOStuff->cSymbols;
+ Elf64_Sym const * const paSymbols = pMachOStuff->paSymbols;
+ for (uint32_t iSym = 0; iSym < cSymbols; iSym++)
+ {
+ const uint8_t bBind = ELF64_ST_BIND(paSymbols[iSym].st_info);
+ const uint8_t bType = ELF64_ST_TYPE(paSymbols[iSym].st_info);
+ const char *pszSymName = &pMachOStuff->pchStrTab[paSymbols[iSym].st_name];
+ if ( *pszSymName == '\0'
+ && bType == STT_SECTION
+ && paSymbols[iSym].st_shndx < cSections)
+ pszSymName = &pMachOStuff->pchShStrTab[pMachOStuff->paShdrs[paSymbols[iSym].st_shndx].sh_name];
+
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_IGNORED;
+ pThis->paSymbols[iSym].idx = UINT16_MAX;
+ pThis->paSymbols[iSym].idxSegDef = UINT16_MAX;
+ pThis->paSymbols[iSym].idxGrpDef = UINT16_MAX;
+
+ uint32_t const idxSection = paSymbols[iSym].st_shndx;
+ if (idxSection == SHN_UNDEF)
+ {
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_EXTDEF;
+ cExtSyms++;
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "External symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else if (bBind != STB_LOCAL || iSym != 0) /* Entry zero is usually a dummy. */
+ return error(pThis->pszSrc, "Unsupported or invalid bind type %#x for undefined symbol #%u (%s)\n",
+ bBind, iSym, pszSymName);
+ }
+ else if (idxSection < cSections)
+ {
+ pThis->paSymbols[iSym].idxSegDef = pThis->paSegments[idxSection].iSegDef;
+ pThis->paSymbols[iSym].idxGrpDef = pThis->paSegments[idxSection].iGrpDef;
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSegments[idxSection].cPubDefs++;
+ cPubSyms++;
+ if (bType == STT_SECTION)
+ return error(pThis->pszSrc, "Don't know how to export STT_SECTION symbol #%u (%s)\n", iSym, pszSymName);
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "Public symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else if (bType == STT_SECTION)
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_SEGDEF;
+ else
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_INTERNAL;
+ }
+ else if (idxSection == SHN_ABS)
+ {
+ if (bType != STT_FILE)
+ {
+ if (bBind == STB_GLOBAL)
+ {
+ pThis->paSymbols[iSym].enmType = OMFSYMTYPE_PUBDEF;
+ pThis->paSymbols[iSym].idxSegDef = 0;
+ pThis->paSymbols[iSym].idxGrpDef = 0;
+ cAbsSyms++;
+ if (*pszSymName == '\0')
+ return error(pThis->pszSrc, "Public absolute symbol #%u (%s) has an empty name.\n", iSym, pszSymName);
+ }
+ else
+ return error(pThis->pszSrc, "Unsupported or invalid bind type %#x for absolute symbol #%u (%s)\n",
+ bBind, iSym, pszSymName);
+ }
+ }
+ else
+ return error(pThis->pszSrc, "Unsupported or invalid section number %#x for symbol #%u (%s)\n",
+ idxSection, iSym, pszSymName);
+ }
+
+ /*
+ * Emit the PUBDEFs the first time around (see order of records in TIS spec).
+ * Note! We expect the os x compiler to always underscore symbols, so unlike the
+ * other 64-bit converters we don't need to check for underscores and add them.
+ */
+ uint16_t idxPubDef = 1;
+ if (cPubSyms)
+ {
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ if (pThis->paSegments[iSeg].cPubDefs > 0)
+ {
+ uint16_t const idxSegDef = pThis->paSegments[iSeg].iSegDef;
+ if (!omfWriter_PubDefBegin(pThis, pThis->paSegments[iSeg].iGrpDef, idxSegDef))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == idxSegDef
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ const char *pszName = &pMachOStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].st_value, pszName, false /*fPrependUnderscore*/))
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+ }
+
+ if (cAbsSyms > 0)
+ {
+ if (!omfWriter_PubDefBegin(pThis, 0, 0))
+ return false;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if ( pThis->paSymbols[iSym].idxSegDef == 0
+ && pThis->paSymbols[iSym].enmType == OMFSYMTYPE_PUBDEF)
+ {
+ const char *pszName = &pMachOStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_PubDefAdd(pThis, paSymbols[iSym].st_value, pszName, false /*fPrependUnderscore*/))
+ return false;
+ pThis->paSymbols[iSym].idx = idxPubDef++;
+ }
+ if (!omfWriter_PubDefEnd(pThis))
+ return false;
+ }
+
+ /*
+ * Go over the symbol table and emit external definition records.
+ */
+ if (!omfWriter_ExtDefBegin(pThis))
+ return false;
+ uint16_t idxExtDef = 1;
+ for (uint16_t iSym = 0; iSym < cSymbols; iSym++)
+ if (pThis->paSymbols[iSym].enmType == OMFSYMTYPE_EXTDEF)
+ {
+ const char *pszName = &pMachOStuff->pchStrTab[paSymbols[iSym].st_name];
+ if (!omfWriter_ExtDefAdd(pThis, pszName, false /*fPrependUnderscore*/))
+ return false;
+ pThis->paSymbols[iSym].idx = idxExtDef++;
+ }
+
+ if (!omfWriter_ExtDefEnd(pThis))
+ return false;
+
+ return true;
+}
+
+static bool convertMachOSectionsToLeDataAndFixupps(POMFWRITER pThis, PCMACHODETAILS pMachOStuff,
+ uint8_t const *pbFile, size_t cbFile)
+{
+ Elf64_Sym const *paSymbols = pMachOStuff->paSymbols;
+ Elf64_Shdr const *paShdrs = pMachOStuff->paShdrs;
+ bool fRet = true;
+ for (uint32_t i = 1; i < pThis->cSegments; i++)
+ {
+ if (pThis->paSegments[i].iSegDef == UINT16_MAX)
+ continue;
+
+ const char *pszSegNm = &pMachOStuff->pchShStrTab[paShdrs[i].sh_name];
+ bool const fRelocs = i + 1 < pThis->cSegments && paShdrs[i + 1].sh_type == SHT_RELA;
+ uint32_t cRelocs = fRelocs ? paShdrs[i + 1].sh_size / sizeof(Elf64_Rela) : 0;
+ Elf64_Rela const *paRelocs = fRelocs ? (Elf64_Rela *)&pbFile[paShdrs[i + 1].sh_offset] : NULL;
+ Elf64_Xword cbVirtData = paShdrs[i].sh_size;
+ Elf64_Xword cbData = paShdrs[i].sh_type == SHT_NOBITS ? 0 : cbVirtData;
+ uint8_t const *pbData = &pbFile[paShdrs[i].sh_offset];
+ uint32_t off = 0;
+
+ /* The OMF record size requires us to split larger sections up. To make
+ life simple, we fill zeros for unitialized (BSS) stuff. */
+ const uint32_t cbMaxData = RT_MIN(OMF_MAX_RECORD_PAYLOAD - 1 - (pThis->paSegments[i].iSegDef >= 128) - 4 - 1, _1K);
+ while (cbVirtData > 0)
+ {
+ /* Figure out how many bytes to put out in this chunk. Must make sure
+ fixups doesn't cross chunk boundraries. ASSUMES sorted relocs. */
+ uint32_t cChunkRelocs = cRelocs;
+ uint32_t cbChunk = cbVirtData;
+ uint32_t offEnd = off + cbChunk;
+ if (cbChunk > cbMaxData)
+ {
+ cbChunk = cbMaxData;
+ offEnd = off + cbChunk;
+ cChunkRelocs = 0;
+
+ /* Quickly determin the reloc range. */
+ while ( cChunkRelocs < cRelocs
+ && paRelocs[cChunkRelocs].r_offset < offEnd)
+ cChunkRelocs++;
+
+ /* Ensure final reloc doesn't go beyond chunk. */
+ while ( cChunkRelocs > 0
+ && paRelocs[cChunkRelocs - 1].r_offset
+ + ELF_AMD64_RELOC_SIZE(ELF64_R_TYPE(paRelocs[cChunkRelocs - 1].r_info))
+ > offEnd)
+ {
+ uint32_t cbDrop = offEnd - paRelocs[cChunkRelocs - 1].r_offset;
+ cbChunk -= cbDrop;
+ offEnd -= cbDrop;
+ cChunkRelocs--;
+ }
+
+ if (!cbVirtData)
+ return error(pThis->pszSrc, "Wtf? cbVirtData is zero!\n");
+ }
+
+ /*
+ * We stash the bytes into the OMF writer record buffer, receiving a
+ * pointer to the start of it so we can make adjustments if necessary.
+ */
+ uint8_t *pbCopy;
+ if (!omfWriter_LEDataBeginEx(pThis, pThis->paSegments[i].iSegDef, off, cbChunk, cbData, pbData, &pbCopy))
+ return false;
+
+ /*
+ * Convert fiuxps.
+ */
+ for (uint32_t iReloc = 0; iReloc < cChunkRelocs; iReloc++)
+ {
+ /* Get the OMF and ELF data for the symbol the reloc references. */
+ uint32_t const uType = ELF64_R_TYPE(paRelocs[iReloc].r_info);
+ uint32_t const iSymbol = ELF64_R_SYM(paRelocs[iReloc].r_info);
+ Elf64_Sym const * const pElfSym = &paSymbols[iSymbol];
+ POMFSYMBOL const pOmfSym = &pThis->paSymbols[iSymbol];
+ const char * const pszSymName = &pMachOStuff->pchStrTab[pElfSym->st_name];
+
+ /* Calc fixup location in the pending chunk and setup a flexible pointer to it. */
+ uint16_t offDataRec = (uint16_t)(paRelocs[iReloc].r_offset - off);
+ RTPTRUNION uLoc;
+ uLoc.pu8 = &pbCopy[offDataRec];
+
+ /* OMF fixup data initialized with typical defaults. */
+ bool fSelfRel = true;
+ uint8_t bLocation = OMF_FIX_LOC_32BIT_OFFSET;
+ uint8_t bFrame = OMF_FIX_F_GRPDEF;
+ uint16_t idxFrame = pThis->idxGrpFlat;
+ uint8_t bTarget;
+ uint16_t idxTarget;
+ bool fTargetDisp;
+ uint32_t offTargetDisp;
+ switch (pOmfSym->enmType)
+ {
+ case OMFSYMTYPE_INTERNAL:
+ case OMFSYMTYPE_PUBDEF:
+ bTarget = OMF_FIX_T_SEGDEF;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = true;
+ offTargetDisp = pElfSym->st_value;
+ break;
+
+ case OMFSYMTYPE_SEGDEF:
+ bTarget = OMF_FIX_T_SEGDEF_NO_DISP;
+ idxTarget = pOmfSym->idxSegDef;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ case OMFSYMTYPE_EXTDEF:
+ bTarget = OMF_FIX_T_EXTDEF_NO_DISP;
+ idxTarget = pOmfSym->idx;
+ fTargetDisp = false;
+ offTargetDisp = 0;
+ break;
+
+ default:
+ return error(pThis->pszSrc, "Relocation in segment #%u '%s' references ignored or invalid symbol (%s)\n",
+ i, pszSegNm, pszSymName);
+ }
+
+ /* Do COFF relocation type conversion. */
+ switch (uType)
+ {
+ case R_X86_64_64:
+ {
+ int64_t iAddend = paRelocs[iReloc].r_addend;
+ if (iAddend > _1G || iAddend < -_1G)
+ fRet = error(pThis->pszSrc, "R_X86_64_64 with large addend (%" ELF_FMT_D64 ") at %#x in segment #%u '%s'\n",
+ iAddend, paRelocs[iReloc].r_offset, i, pszSegNm);
+ *uLoc.pu64 = iAddend;
+ fSelfRel = false;
+ break;
+ }
+
+ case R_X86_64_32:
+ case R_X86_64_32S: /* signed, unsigned, whatever. */
+ fSelfRel = false;
+ RT_FALL_THRU();
+ case R_X86_64_PC32:
+ {
+ /* defaults are ok, just handle the addend. */
+ int32_t iAddend = paRelocs[iReloc].r_addend;
+ if (iAddend != paRelocs[iReloc].r_addend)
+ fRet = error(pThis->pszSrc, "R_X86_64_PC32 with large addend (%d) at %#x in segment #%u '%s'\n",
+ iAddend, paRelocs[iReloc].r_offset, i, pszSegNm);
+ *uLoc.pu32 = iAddend;
+ break;
+ }
+
+ case R_X86_64_NONE:
+ continue; /* Ignore this one */
+
+ case R_X86_64_GOT32:
+ case R_X86_64_PLT32:
+ case R_X86_64_COPY:
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_JMP_SLOT:
+ case R_X86_64_RELATIVE:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_16:
+ case R_X86_64_PC16:
+ case R_X86_64_8:
+ case R_X86_64_PC8:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TLSGD:
+ case R_X86_64_TLSLD:
+ case R_X86_64_DTPOFF32:
+ case R_X86_64_GOTTPOFF:
+ case R_X86_64_TPOFF32:
+ default:
+ return error(pThis->pszSrc, "Unsupported fixup type %#x (%s) at rva=%#x in section #%u '%s' against '%s'\n",
+ uType, g_apszElfAmd64RelTypes[uType], paRelocs[iReloc].r_offset, i, pszSegNm, pszSymName);
+ }
+
+ /* Add the fixup. */
+ if (idxFrame == UINT16_MAX)
+ error(pThis->pszSrc, "idxFrame=UINT16_MAX for %s type=%s\n", pszSymName, g_apszElfAmd64RelTypes[uType]);
+ fRet = omfWriter_LEDataAddFixup(pThis, offDataRec, fSelfRel, bLocation, bFrame, idxFrame,
+ bTarget, idxTarget, fTargetDisp, offTargetDisp) && fRet;
+ }
+
+ /*
+ * Write the LEDATA and associated FIXUPPs.
+ */
+ if (!omfWriter_LEDataEnd(pThis))
+ return false;
+
+ /*
+ * Advance.
+ */
+ paRelocs += cChunkRelocs;
+ cRelocs -= cChunkRelocs;
+ if (cbData > cbChunk)
+ {
+ cbData -= cbChunk;
+ pbData += cbChunk;
+ }
+ else
+ cbData = 0;
+ off += cbChunk;
+ cbVirtData -= cbChunk;
+ }
+ }
+
+ return fRet;
+}
+
+
+static bool convertMachoToOmf(const char *pszFile, uint8_t const *pbFile, size_t cbFile, FILE *pDst)
+{
+ /*
+ * Validate the source file a little.
+ */
+ MACHODETAILS MachOStuff;
+ if (!validateMachO(pszFile, pbFile, cbFile, &MachOStuff))
+ return false;
+
+ /*
+ * Instantiate the OMF writer.
+ */
+ POMFWRITER pThis = omfWriter_Create(pszFile, MachOStuff.pEhdr->e_shnum, MachOStuff.cSymbols, pDst);
+ if (!pThis)
+ return false;
+
+ /*
+ * Write the OMF object file.
+ */
+ if (omfWriter_BeginModule(pThis, pszFile))
+ {
+ Elf64_Ehdr const *pEhdr = (Elf64_Ehdr const *)pbFile;
+
+ if ( convertMachOSectionsToSegDefsAndGrpDefs(pThis, &MachOStuff)
+ && convertMachOSymbolsToPubDefsAndExtDefs(pThis, &MachOStuff)
+ && omfWriter_LinkPassSeparator(pThis)
+ && convertMachOSectionsToLeDataAndFixupps(pThis, &MachOStuff, pbFile, cbFile)
+ && omfWriter_EndModule(pThis) )
+ {
+
+ omfWriter_Destroy(pThis);
+ return true;
+ }
+ }
+
+ omfWriter_Destroy(pThis);
+ return false;
+}
+
+#endif /* !MACHO_TO_OMF_CONVERSION */
+
+
+/*********************************************************************************************************************************
+* OMF Converter/Tweaker *
+*********************************************************************************************************************************/
+
+/** Watcom intrinsics we need to modify so we can mix 32-bit and 16-bit
+ * code, since the 16 and 32 bit compilers share several names.
+ * The names are length prefixed.
+ */
+static const char * const g_apszExtDefRenames[] =
+{
+ "\x05" "__I4D",
+ "\x05" "__I4M",
+ "\x05" "__I8D",
+ "\x06" "__I8DQ",
+ "\x07" "__I8DQE",
+ "\x06" "__I8DR",
+ "\x07" "__I8DRE",
+ "\x06" "__I8LS",
+ "\x05" "__I8M",
+ "\x06" "__I8ME",
+ "\x06" "__I8RS",
+ "\x05" "__PIA",
+ "\x05" "__PIS",
+ "\x05" "__PTC",
+ "\x05" "__PTS",
+ "\x05" "__U4D",
+ "\x05" "__U4M",
+ "\x05" "__U8D",
+ "\x06" "__U8DQ",
+ "\x07" "__U8DQE",
+ "\x06" "__U8DR",
+ "\x07" "__U8DRE",
+ "\x06" "__U8LS",
+ "\x05" "__U8M",
+ "\x06" "__U8ME",
+ "\x06" "__U8RS",
+};
+
+/**
+ * Segment definition.
+ */
+typedef struct OMFSEGDEF
+{
+ uint32_t cbSeg;
+ uint8_t bSegAttr;
+ uint16_t idxName;
+ uint16_t idxClass;
+ uint16_t idxOverlay;
+ uint8_t cchName;
+ uint8_t cchClass;
+ uint8_t cchOverlay;
+ const char *pchName;
+ const char *pchClass;
+ const char *pchOverlay;
+ bool fUse32;
+ bool f32bitRec;
+} OMFSEGDEF;
+typedef OMFSEGDEF *POMFSEGDEF;
+
+/**
+ * Group definition.
+ */
+typedef struct OMFGRPDEF
+{
+ const char *pchName;
+ uint16_t idxName;
+ uint8_t cchName;
+ uint16_t cSegDefs;
+ uint16_t *paidxSegDefs;
+} OMFGRPDEF;
+typedef OMFGRPDEF *POMFGRPDEF;
+
+/**
+ * Records line number information for a file in a segment (for CV8 debug info).
+ */
+typedef struct OMFFILELINES
+{
+ /** The source info offset. */
+ uint32_t offSrcInfo;
+ /** Number of line/offset pairs. */
+ uint32_t cPairs;
+ /** Number of pairs allocated. */
+ uint32_t cPairsAlloc;
+ /** Table with line number and offset pairs, ordered by offset. */
+ PRTCV8LINEPAIR paPairs;
+} OMFFILEINES;
+typedef OMFFILEINES *POMFFILEINES;
+
+/**
+ * Records line number information for a segment (for CV8 debug info).
+ */
+typedef struct OMFSEGLINES
+{
+ /** Number of files. */
+ uint32_t cFiles;
+ /** Number of bytes we need. */
+ uint32_t cb;
+ /** The segment index. */
+ uint16_t idxSeg;
+ /** The group index for this segment. Initially OMF_REPLACE_GRP_XXX values,
+ * later convertOmfWriteDebugGrpDefs replaces them with actual values. */
+ uint16_t idxGrp;
+ /** File table. */
+ POMFFILEINES paFiles;
+} OMFSEGLINES;
+typedef OMFSEGLINES *POMFSEGLINES;
+
+/** @name OMF_REPLACE_GRP_XXX - Special OMFSEGLINES::idxGrp values.
+ * @{ */
+#define OMF_REPLACE_GRP_CGROUP16 UINT16_C(0xffe0)
+#define OMF_REPLACE_GRP_RMCODE UINT16_C(0xffe1)
+#define OMF_REPLACE_GRP_X0CODE UINT16_C(0xffe2)
+#define OMF_REPLACE_GRP_X1CODE UINT16_C(0xffe3)
+/** @} */
+
+
+/**
+ * OMF details allocation that needs to be freed when done.
+ */
+typedef struct OMFDETAILSALLOC
+{
+ /** Pointer to the next allocation. */
+ struct OMFDETAILSALLOC *pNext;
+ /** The allocated bytes. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ uint8_t abData[RT_FLEXIBLE_ARRAY];
+} OMFDETAILSALLOC;
+typedef OMFDETAILSALLOC *POMFDETAILSALLOC;
+
+/**
+ * OMF conversion details.
+ *
+ * Keeps information relevant to the conversion and CV8 debug info.
+ */
+typedef struct OMFDETAILS
+{
+ /** The input file name. */
+ const char *pszFile;
+
+ /** Set if it has line numbers. */
+ bool fLineNumbers;
+ /** Set if we think this may be a 32-bit OMF file. */
+ bool fProbably32bit;
+ /** Set if this module may need mangling. */
+ bool fMayNeedMangling;
+ /** The LNAME index of '$$SYMBOLS' or UINT16_MAX it not found. */
+ uint16_t iSymbolsNm;
+ /** The LNAME index of 'DEBSYM' or UINT16_MAX it not found. */
+ uint16_t iDebSymNm;
+ /** The '$$SYMBOLS' segment index. */
+ uint16_t iSymbolsSeg;
+
+ /** Number of SEGDEFs records. */
+ uint16_t cSegDefs;
+ /** Number of GRPDEFs records. */
+ uint16_t cGrpDefs;
+ /** Number of listed names. */
+ uint16_t cLNames;
+
+ /** Segment defintions. */
+ POMFSEGDEF paSegDefs;
+ /** Group defintions. */
+ POMFGRPDEF paGrpDefs;
+ /** Name list. Points to the size repfix. */
+ char **papchLNames;
+
+ /** Code groups we need to keep an eye on for line number fixup purposes. */
+ struct OMFLINEGROUPS
+ {
+ /** The name. */
+ const char *pszName;
+ /** The primary class name. */
+ const char *pszClass1;
+ /** The secondary class name. */
+ const char *pszClass2;
+ /** The main segment name, NULL if not applicable (CGROUP16). */
+ const char *pszSeg;
+ /** The name length. */
+ uint8_t cchName;
+ /** The primary class name length. */
+ uint8_t cchClass1;
+ /** The secondary class name length. */
+ uint8_t cchClass2;
+ /** Whether this group is needed. */
+ bool fNeeded;
+ /** The group index (UINT16_MAX if not found). */
+ uint16_t idxGroup;
+ /** The group name. */
+ uint16_t idxName;
+ /** The OMF_REPLACE_GRP_XXX value. */
+ uint16_t idxReplaceGrp;
+ } aGroups[4];
+
+ /** CV8: Filename string table size. */
+ uint32_t cbStrTab;
+ /** CV8: Filename string table allocation size (always multiple of dword,
+ * zero initialized). */
+ uint32_t cbStrTabAlloc;
+ /** CV8: Filename String table. */
+ char *pchStrTab;
+ /** CV8: Elements in the source info table. */
+ uint16_t cSrcInfo;
+ /** CV8: Source info table. */
+ PRTCV8SRCINFO paSrcInfo;
+
+ /** Number of entries in the paSegLines table. */
+ uint32_t cSegLines;
+ /** Segment line numbers, indexed by segment number. */
+ POMFSEGLINES paSegLines;
+
+ /** List of allocations that needs freeing. */
+ POMFDETAILSALLOC pAllocHead;
+} OMFDETAILS;
+typedef OMFDETAILS *POMFDETAILS;
+typedef OMFDETAILS const *PCOMFDETAILS;
+
+
+/** Grows a table to a given size (a_cNewEntries). */
+#define OMF_GROW_TABLE_EX_RET_ERR(a_EntryType, a_paTable, a_cEntries, a_cNewEntries) \
+ do\
+ { \
+ size_t cbOld = (a_cEntries) * sizeof(a_EntryType); \
+ size_t cbNew = (a_cNewEntries) * sizeof(a_EntryType); \
+ void *pvNew = realloc(a_paTable, cbNew); \
+ if (pvNew) \
+ { \
+ memset((uint8_t *)pvNew + cbOld, 0, cbNew - cbOld); \
+ (a_paTable) = (a_EntryType *)pvNew; \
+ } \
+ else return error("???", "Out of memory!\n"); \
+ } while (0)
+
+/** Grows a table. */
+#define OMF_GROW_TABLE_RET_ERR(a_EntryType, a_paTable, a_cEntries, a_cEvery) \
+ if ((a_cEntries) % (a_cEvery) != 0) { /* likely */ } \
+ else do\
+ { \
+ size_t cbOld = (a_cEntries) * sizeof(a_EntryType); \
+ size_t cbNew = cbOld + (a_cEvery) * sizeof(a_EntryType); \
+ void *pvNew = realloc(a_paTable, cbNew); \
+ if (pvNew) \
+ { \
+ memset((uint8_t *)pvNew + cbOld, 0, (a_cEvery) * sizeof(a_EntryType)); \
+ (a_paTable) = (a_EntryType *)pvNew; \
+ } \
+ else return error("???", "Out of memory!\n"); \
+ } while (0)
+
+#define OMF_EXPLODE_LNAME(a_pOmfStuff, a_idxName, a_pchName, a_cchName, a_Name) \
+ do { \
+ if ((a_idxName) < (a_pOmfStuff)->cLNames) \
+ { \
+ a_cchName = (uint8_t)*(a_pOmfStuff)->papchLNames[(a_idxName)]; \
+ a_pchName = (a_pOmfStuff)->papchLNames[(a_idxName)] + 1; \
+ } \
+ else return error((a_pOmfStuff)->pszFile, "Invalid LNAME reference %#x in " #a_Name "!\n", a_idxName); \
+ } while (0)
+
+
+/**
+ * Allocates memory that will be freed when we're done converting.
+ *
+ * @returns Pointer tot he memory.
+ * @param pOmfStuff The OMF details data.
+ * @param cbNeeded The amount of memory required.
+ */
+static void *omfDetails_Alloc(POMFDETAILS pOmfStuff, size_t cbNeeded)
+{
+ POMFDETAILSALLOC pAlloc = (POMFDETAILSALLOC)malloc(RT_UOFFSETOF_DYN(OMFDETAILSALLOC, abData[cbNeeded]));
+ if (pAlloc)
+ {
+ pAlloc->pNext = pOmfStuff->pAllocHead;
+ pOmfStuff->pAllocHead = pAlloc;
+ return &pAlloc->abData[0];
+ }
+ return NULL;
+}
+
+/**
+ * Adds a line number to the CV8 debug info.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff Where to collect CV8 debug info.
+ * @param cchSrcFile The length of the source file name.
+ * @param pchSrcFile The source file name, not terminated.
+ * @param poffFile Where to return the source file information table
+ * offset (for use in the line number tables).
+ */
+static bool collectOmfAddFile(POMFDETAILS pOmfStuff, uint8_t cchSrcFile, const char *pchSrcFile, uint32_t *poffFile)
+{
+ /*
+ * Do lookup first.
+ */
+ uint32_t i = pOmfStuff->cSrcInfo;
+ while (i-- > 0)
+ {
+ const char *pszCur = &pOmfStuff->pchStrTab[pOmfStuff->paSrcInfo[i].offSourceName];
+ if ( strncmp(pszCur, pchSrcFile, cchSrcFile) == 0
+ && pszCur[cchSrcFile] == '\0')
+ {
+ *poffFile = i * sizeof(pOmfStuff->paSrcInfo[0]);
+ return true;
+ }
+ }
+
+ /*
+ * Add it to the string table (dword aligned and zero padded).
+ */
+ uint32_t offSrcTab = pOmfStuff->cbStrTab;
+ if (offSrcTab + cchSrcFile + 1 > pOmfStuff->cbStrTabAlloc)
+ {
+ uint32_t cbNew = (offSrcTab == 0) + offSrcTab + cchSrcFile + 1;
+ cbNew = RT_ALIGN(cbNew, 256);
+ void *pvNew = realloc(pOmfStuff->pchStrTab, cbNew);
+ if (!pvNew)
+ return error("???", "out of memory");
+ pOmfStuff->pchStrTab = (char *)pvNew;
+ pOmfStuff->cbStrTabAlloc = cbNew;
+ memset(&pOmfStuff->pchStrTab[offSrcTab], 0, cbNew - offSrcTab);
+
+ if (!offSrcTab)
+ offSrcTab++;
+ }
+
+ memcpy(&pOmfStuff->pchStrTab[offSrcTab], pchSrcFile, cchSrcFile);
+ pOmfStuff->pchStrTab[offSrcTab + cchSrcFile] = '\0';
+ pOmfStuff->cbStrTab = offSrcTab + cchSrcFile + 1;
+
+ /*
+ * Add it to the filename info table.
+ */
+ if ((pOmfStuff->cSrcInfo % 8) == 0)
+ {
+ void *pvNew = realloc(pOmfStuff->paSrcInfo, sizeof(pOmfStuff->paSrcInfo[0]) * (pOmfStuff->cSrcInfo + 8));
+ if (!pvNew)
+ return error("???", "out of memory");
+ pOmfStuff->paSrcInfo = (PRTCV8SRCINFO)pvNew;
+ }
+
+ PRTCV8SRCINFO pSrcInfo = &pOmfStuff->paSrcInfo[pOmfStuff->cSrcInfo++];
+ pSrcInfo->offSourceName = offSrcTab;
+ pSrcInfo->uDigestType = RTCV8SRCINFO_DIGEST_TYPE_MD5;
+ memset(&pSrcInfo->Digest, 0, sizeof(pSrcInfo->Digest));
+
+ *poffFile = (uint32_t)((uintptr_t)pSrcInfo - (uintptr_t)pOmfStuff->paSrcInfo);
+ return true;
+}
+
+
+/**
+ * Adds a line number to the CV8 debug info.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff Where to collect CV8 debug info.
+ * @param idxSeg The segment index.
+ * @param off The segment offset.
+ * @param uLine The line number.
+ * @param offSrcInfo The source file info table offset.
+ */
+static bool collectOmfAddLine(POMFDETAILS pOmfStuff, uint16_t idxSeg, uint32_t off, uint16_t uLine, uint32_t offSrcInfo)
+{
+ /*
+ * Get/add the segment line structure.
+ */
+ if (idxSeg >= pOmfStuff->cSegLines)
+ {
+ OMF_GROW_TABLE_EX_RET_ERR(OMFSEGLINES, pOmfStuff->paSegLines, pOmfStuff->cSegLines, idxSeg + 1);
+ for (uint32_t i = pOmfStuff->cSegLines; i <= idxSeg; i++)
+ {
+ pOmfStuff->paSegLines[i].idxSeg = i;
+ pOmfStuff->paSegLines[i].idxGrp = UINT16_MAX;
+ pOmfStuff->paSegLines[i].cb = sizeof(RTCV8LINESHDR);
+ }
+ pOmfStuff->cSegLines = idxSeg + 1;
+ }
+ POMFSEGLINES pSegLines = &pOmfStuff->paSegLines[idxSeg];
+
+ /*
+ * Get/add the file structure with the segment.
+ */
+ POMFFILEINES pFileLines = NULL;
+ uint32_t i = pSegLines->cFiles;
+ while (i-- > 0)
+ if (pSegLines->paFiles[i].offSrcInfo == offSrcInfo)
+ {
+ pFileLines = &pSegLines->paFiles[i];
+ break;
+ }
+ if (!pFileLines)
+ {
+ i = pSegLines->cFiles;
+ OMF_GROW_TABLE_RET_ERR(OMFFILEINES, pSegLines->paFiles, pSegLines->cFiles, 4);
+ pSegLines->cFiles = i + 1;
+ pSegLines->cb += sizeof(RTCV8LINESSRCMAP);
+
+ pFileLines = &pSegLines->paFiles[i];
+ pFileLines->offSrcInfo = offSrcInfo;
+ pFileLines->cPairs = 0;
+ pFileLines->cPairsAlloc = 0;
+ pFileLines->paPairs = NULL;
+
+ /*
+ * Check for segment group requirements the first time a segment is used.
+ */
+ if (i == 0)
+ {
+ if (idxSeg >= pOmfStuff->cSegDefs)
+ return error("???", "collectOmfAddLine: idxSeg=%#x is out of bounds (%#x)!\n", idxSeg, pOmfStuff->cSegDefs);
+ POMFSEGDEF pSegDef = &pOmfStuff->paSegDefs[idxSeg];
+ unsigned j = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (j-- > 0)
+ if ( ( pSegDef->cchClass == pOmfStuff->aGroups[j].cchClass1
+ && memcmp(pSegDef->pchClass, pOmfStuff->aGroups[j].pszClass1, pSegDef->cchClass) == 0)
+ || ( pSegDef->cchClass == pOmfStuff->aGroups[j].cchClass2
+ && memcmp(pSegDef->pchClass, pOmfStuff->aGroups[j].pszClass2, pSegDef->cchClass) == 0))
+ {
+ pOmfStuff->aGroups[j].fNeeded = true;
+ pSegLines->idxGrp = pOmfStuff->aGroups[j].idxReplaceGrp;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Add the line number (sorted, duplicates removed).
+ */
+ if (pFileLines->cPairs + 1 > pFileLines->cPairsAlloc)
+ {
+ void *pvNew = realloc(pFileLines->paPairs, (pFileLines->cPairsAlloc + 16) * sizeof(pFileLines->paPairs[0]));
+ if (!pvNew)
+ return error("???", "out of memory");
+ pFileLines->paPairs = (PRTCV8LINEPAIR)pvNew;
+ pFileLines->cPairsAlloc += 16;
+ }
+
+ i = pFileLines->cPairs;
+ while (i > 0 && ( off < pFileLines->paPairs[i - 1].offSection
+ || ( off == pFileLines->paPairs[i - 1].offSection
+ && uLine < pFileLines->paPairs[i - 1].uLineNumber)) )
+ i--;
+ if ( i == pFileLines->cPairs
+ || off != pFileLines->paPairs[i].offSection
+ || uLine != pFileLines->paPairs[i].uLineNumber)
+ {
+ if (i < pFileLines->cPairs)
+ memmove(&pFileLines->paPairs[i + 1], &pFileLines->paPairs[i],
+ (pFileLines->cPairs - i) * sizeof(pFileLines->paPairs));
+ pFileLines->paPairs[i].offSection = off;
+ pFileLines->paPairs[i].uLineNumber = uLine;
+ pFileLines->paPairs[i].fEndOfStatement = true;
+ pFileLines->cPairs++;
+ pSegLines->cb += sizeof(pFileLines->paPairs[0]);
+ }
+
+ return true;
+}
+
+
+/**
+ * Parses OMF file gathering line numbers (for CV8 debug info) and checking out
+ * external defintions for mangling work (compiler instrinsics).
+ *
+ * @returns success indicator.
+ * @param pszFile The name of the OMF file.
+ * @param pbFile The file content.
+ * @param cbFile The size of the file content.
+ * @param pOmfStuff Where to collect CV8 debug info and anything else we
+ * find out about the OMF file.
+ */
+static bool collectOmfDetails(const char *pszFile, uint8_t const *pbFile, size_t cbFile, POMFDETAILS pOmfStuff)
+{
+ uint32_t cExtDefs = 0;
+ uint32_t cPubDefs = 0;
+ uint32_t off = 0;
+ uint8_t cchSrcFile = 0;
+ const char *pchSrcFile = NULL;
+ uint32_t offSrcInfo = UINT32_MAX;
+
+ memset(pOmfStuff, 0, sizeof(*pOmfStuff));
+ pOmfStuff->pszFile = pszFile;
+ pOmfStuff->iDebSymNm = UINT16_MAX;
+ pOmfStuff->iSymbolsNm = UINT16_MAX;
+ pOmfStuff->iSymbolsSeg = UINT16_MAX;
+
+ /* Dummy entries. */
+ OMF_GROW_TABLE_RET_ERR(char *, pOmfStuff->papchLNames, pOmfStuff->cLNames, 16);
+ pOmfStuff->papchLNames[0] = (char *)"";
+ pOmfStuff->cLNames = 1;
+
+ OMF_GROW_TABLE_RET_ERR(OMFSEGDEF, pOmfStuff->paSegDefs, pOmfStuff->cSegDefs, 16);
+ pOmfStuff->cSegDefs = 1;
+
+ OMF_GROW_TABLE_RET_ERR(OMFGRPDEF, pOmfStuff->paGrpDefs, pOmfStuff->cGrpDefs, 16);
+ pOmfStuff->cGrpDefs = 1;
+
+ /* Groups we seek. */
+#define OMF_INIT_WANTED_GROUP(a_idx, a_szName, a_szClass1, a_szClass2, a_pszSeg, a_idxReplace) \
+ pOmfStuff->aGroups[a_idx].pszName = a_szName; \
+ pOmfStuff->aGroups[a_idx].cchName = sizeof(a_szName) - 1; \
+ pOmfStuff->aGroups[a_idx].pszClass1 = a_szClass1; \
+ pOmfStuff->aGroups[a_idx].cchClass1 = sizeof(a_szClass1) - 1; \
+ pOmfStuff->aGroups[a_idx].pszClass2 = a_szClass2; \
+ pOmfStuff->aGroups[a_idx].cchClass2 = sizeof(a_szClass2) - 1; \
+ pOmfStuff->aGroups[a_idx].pszSeg = a_pszSeg; \
+ pOmfStuff->aGroups[a_idx].fNeeded = false; \
+ pOmfStuff->aGroups[a_idx].idxGroup = UINT16_MAX; \
+ pOmfStuff->aGroups[a_idx].idxName = UINT16_MAX; \
+ pOmfStuff->aGroups[a_idx].idxReplaceGrp = a_idxReplace
+ OMF_INIT_WANTED_GROUP(0, "CGROUP16", "BS3CLASS16CODE", "CODE", NULL, OMF_REPLACE_GRP_CGROUP16);
+ OMF_INIT_WANTED_GROUP(1, "BS3GROUPRMTEXT16", "BS3CLASS16RMCODE", "", "BS3RMTEXT16", OMF_REPLACE_GRP_RMCODE);
+ OMF_INIT_WANTED_GROUP(2, "BS3GROUPX0TEXT16", "BS3CLASS16X0CODE", "", "BS3X0TEXT16", OMF_REPLACE_GRP_X0CODE);
+ OMF_INIT_WANTED_GROUP(3, "BS3GROUPX1TEXT16", "BS3CLASS16X1CODE", "", "BS3X1TEXT16", OMF_REPLACE_GRP_X1CODE);
+
+ /*
+ * Process the OMF records.
+ */
+ while (off + 3 < cbFile)
+ {
+ uint8_t bRecType = pbFile[off];
+ uint16_t cbRec = RT_MAKE_U16(pbFile[off + 1], pbFile[off + 2]);
+ if (g_cVerbose > 2)
+ printf( "%#07x: type=%#04x len=%#06x\n", off, bRecType, cbRec);
+ if (off + cbRec > cbFile)
+ return error(pszFile, "Invalid record length at %#x: %#x (cbFile=%#lx)\n", off, cbRec, (unsigned long)cbFile);
+
+ uint32_t offRec = 0;
+ uint8_t const *pbRec = &pbFile[off + 3];
+#define OMF_CHECK_RET(a_cbReq, a_Name) /* Not taking the checksum into account, so we're good with 1 or 2 byte fields. */ \
+ if (offRec + (a_cbReq) <= cbRec) {/*likely*/} \
+ else return error(pszFile, "Malformed " #a_Name "! off=%#x offRec=%#x cbRec=%#x cbNeeded=%#x line=%d\n", \
+ off, offRec, cbRec, (a_cbReq), __LINE__)
+#define OMF_READ_IDX(a_idx, a_Name) \
+ do { \
+ OMF_CHECK_RET(2, a_Name); \
+ a_idx = pbRec[offRec++]; \
+ if ((a_idx) & 0x80) \
+ a_idx = (((a_idx) & 0x7f) << 8) | pbRec[offRec++]; \
+ } while (0)
+
+#define OMF_READ_U16(a_u16, a_Name) \
+ do { \
+ OMF_CHECK_RET(4, a_Name); \
+ a_u16 = RT_MAKE_U16(pbRec[offRec], pbRec[offRec + 1]); \
+ offRec += 2; \
+ } while (0)
+#define OMF_READ_U32(a_u32, a_Name) \
+ do { \
+ OMF_CHECK_RET(4, a_Name); \
+ a_u32 = RT_MAKE_U32_FROM_U8(pbRec[offRec], pbRec[offRec + 1], pbRec[offRec + 2], pbRec[offRec + 3]); \
+ offRec += 4; \
+ } while (0)
+
+ switch (bRecType)
+ {
+ /*
+ * Record LNAME records, scanning for FLAT.
+ */
+ case OMF_LNAMES:
+ while (offRec + 1 < cbRec)
+ {
+ uint8_t cch = pbRec[offRec];
+ if (offRec + 1 + cch >= cbRec)
+ return error(pszFile, "Invalid LNAME string length at %#x+3+%#x: %#x (cbFile=%#lx)\n",
+ off, offRec, cch, (unsigned long)cbFile);
+
+ if (g_cVerbose > 2)
+ printf(" LNAME[%u]: %-*.*s\n", pOmfStuff->cLNames, cch, cch, &pbRec[offRec + 1]);
+
+ OMF_GROW_TABLE_RET_ERR(char *, pOmfStuff->papchLNames, pOmfStuff->cLNames, 16);
+ pOmfStuff->papchLNames[pOmfStuff->cLNames] = (char *)&pbRec[offRec];
+
+ if (IS_OMF_STR_EQUAL_EX(cch, &pbRec[offRec + 1], "FLAT"))
+ pOmfStuff->fProbably32bit = true;
+
+ if (IS_OMF_STR_EQUAL_EX(cch, &pbRec[offRec + 1], "DEBSYM"))
+ pOmfStuff->iDebSymNm = pOmfStuff->cLNames;
+ if (IS_OMF_STR_EQUAL_EX(cch, &pbRec[offRec + 1], "$$SYMBOLS"))
+ pOmfStuff->iSymbolsNm = pOmfStuff->cLNames;
+
+ unsigned j = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (j-- > 0)
+ if ( cch == pOmfStuff->aGroups[j].cchName
+ && memcmp(&pbRec[offRec + 1], pOmfStuff->aGroups[j].pszName, pOmfStuff->aGroups[j].cchName) == 0)
+ {
+ pOmfStuff->aGroups[j].idxName = pOmfStuff->cLNames;
+ break;
+ }
+
+ pOmfStuff->cLNames++;
+ offRec += cch + 1;
+ }
+ break;
+
+ /*
+ * Display external definitions if -v is specified, also check if anything needs mangling.
+ */
+ case OMF_EXTDEF:
+ while (offRec + 1 < cbRec)
+ {
+ uint8_t cch = pbRec[offRec++];
+ OMF_CHECK_RET(cch, EXTDEF);
+ char *pchName = (char *)&pbRec[offRec];
+ offRec += cch;
+
+ uint16_t idxType;
+ OMF_READ_IDX(idxType, EXTDEF);
+
+ if (g_cVerbose > 2)
+ printf(" EXTDEF [%u]: %-*.*s type=%#x\n", cExtDefs, cch, cch, pchName, idxType);
+ else if (g_cVerbose > 0)
+ printf(" U %-*.*s\n", cch, cch, pchName);
+
+ /* Look for g_apszExtDefRenames entries that requires changing. */
+ if ( !pOmfStuff->fMayNeedMangling
+ && cch >= 5
+ && cch <= 7
+ && pchName[0] == '_'
+ && pchName[1] == '_'
+ && ( pchName[2] == 'U'
+ || pchName[2] == 'I'
+ || pchName[2] == 'P')
+ && ( pchName[3] == '4'
+ || pchName[3] == '8'
+ || pchName[3] == 'I'
+ || pchName[3] == 'T') )
+ {
+ pOmfStuff->fMayNeedMangling = true;
+ }
+ }
+ break;
+
+ /*
+ * Display public names if -v is specified.
+ */
+ case OMF_PUBDEF32:
+ case OMF_LPUBDEF32:
+ pOmfStuff->fProbably32bit = true;
+ RT_FALL_THRU();
+ case OMF_PUBDEF16:
+ case OMF_LPUBDEF16:
+ if (g_cVerbose > 0)
+ {
+ char const chType = bRecType == OMF_PUBDEF16 || bRecType == OMF_PUBDEF32 ? 'T' : 't';
+ const char *pszRec = "LPUBDEF";
+ if (chType == 'T')
+ pszRec++;
+
+ uint16_t idxGrp;
+ OMF_READ_IDX(idxGrp, [L]PUBDEF);
+
+ uint16_t idxSeg;
+ OMF_READ_IDX(idxSeg, [L]PUBDEF);
+
+ uint16_t uFrameBase = 0;
+ if (idxSeg == 0)
+ {
+ OMF_CHECK_RET(2, [L]PUBDEF);
+ uFrameBase = RT_MAKE_U16(pbRec[offRec], pbRec[offRec + 1]);
+ offRec += 2;
+ }
+ if (g_cVerbose > 2)
+ printf(" %s: idxGrp=%#x idxSeg=%#x uFrameBase=%#x\n", pszRec, idxGrp, idxSeg, uFrameBase);
+ uint16_t const uSeg = idxSeg ? idxSeg : uFrameBase;
+
+ while (offRec + 1 < cbRec)
+ {
+ uint8_t cch = pbRec[offRec++];
+ OMF_CHECK_RET(cch, [L]PUBDEF);
+ const char *pchName = (const char *)&pbRec[offRec];
+ offRec += cch;
+
+ uint32_t offSeg;
+ if (bRecType & OMF_REC32)
+ {
+ OMF_CHECK_RET(4, [L]PUBDEF);
+ offSeg = RT_MAKE_U32_FROM_U8(pbRec[offRec], pbRec[offRec + 1], pbRec[offRec + 2], pbRec[offRec + 3]);
+ offRec += 4;
+ }
+ else
+ {
+ OMF_CHECK_RET(2, [L]PUBDEF);
+ offSeg = RT_MAKE_U16(pbRec[offRec], pbRec[offRec + 1]);
+ offRec += 2;
+ }
+
+ uint16_t idxType;
+ OMF_READ_IDX(idxType, [L]PUBDEF);
+
+ if (g_cVerbose > 2)
+ printf(" %s[%u]: off=%#010x type=%#x %-*.*s\n", pszRec, cPubDefs, offSeg, idxType, cch, cch, pchName);
+ else if (g_cVerbose > 0)
+ printf("%04x:%08x %c %-*.*s\n", uSeg, offSeg, chType, cch, cch, pchName);
+ }
+ }
+ break;
+
+ /*
+ * Must count segment definitions to figure the index of our segment.
+ */
+ case OMF_SEGDEF16:
+ case OMF_SEGDEF32:
+ {
+ OMF_GROW_TABLE_RET_ERR(OMFSEGDEF, pOmfStuff->paSegDefs, pOmfStuff->cSegDefs, 16);
+ POMFSEGDEF pSegDef = &pOmfStuff->paSegDefs[pOmfStuff->cSegDefs++];
+
+ OMF_CHECK_RET(1 + (bRecType == OMF_SEGDEF16 ? 2 : 4) + 1 + 1 + 1, SEGDEF);
+ pSegDef->f32bitRec = bRecType == OMF_SEGDEF32;
+ pSegDef->bSegAttr = pbRec[offRec++];
+ pSegDef->fUse32 = pSegDef->bSegAttr & 1;
+ if ((pSegDef->bSegAttr >> 5) == 0)
+ {
+ /* A=0: skip frame number of offset. */
+ OMF_CHECK_RET(3, SEGDEF);
+ offRec += 3;
+ }
+ if (bRecType == OMF_SEGDEF16)
+ OMF_READ_U16(pSegDef->cbSeg, SEGDEF16);
+ else
+ OMF_READ_U32(pSegDef->cbSeg, SEGDEF32);
+ OMF_READ_IDX(pSegDef->idxName, SEGDEF);
+ OMF_READ_IDX(pSegDef->idxClass, SEGDEF);
+ OMF_READ_IDX(pSegDef->idxOverlay, SEGDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxName, pSegDef->pchName, pSegDef->cchName, SEGDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxClass, pSegDef->pchClass, pSegDef->cchClass, SEGDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxOverlay, pSegDef->pchOverlay, pSegDef->cchOverlay, SEGDEF);
+ break;
+ }
+
+ /*
+ * Must count segment definitions to figure the index of our group.
+ */
+ case OMF_GRPDEF:
+ {
+ OMF_GROW_TABLE_RET_ERR(OMFGRPDEF, pOmfStuff->paGrpDefs, pOmfStuff->cGrpDefs, 8);
+ POMFGRPDEF pGrpDef = &pOmfStuff->paGrpDefs[pOmfStuff->cGrpDefs];
+
+ OMF_READ_IDX(pGrpDef->idxName, GRPDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pGrpDef->idxName, pGrpDef->pchName, pGrpDef->cchName, GRPDEF);
+
+ unsigned j = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (j-- > 0)
+ if (pGrpDef->idxName == pOmfStuff->aGroups[j].idxName)
+ {
+ pOmfStuff->aGroups[j].idxGroup = pOmfStuff->cGrpDefs;
+ break;
+ }
+
+ pGrpDef->cSegDefs = 0;
+ pGrpDef->paidxSegDefs = NULL;
+ while (offRec + 2 + 1 <= cbRec)
+ {
+ if (pbRec[offRec] != 0xff)
+ return error(pszFile, "Unsupported GRPDEF member type: %#x\n", pbRec[offRec]);
+ offRec++;
+ OMF_GROW_TABLE_RET_ERR(uint16_t, pGrpDef->paidxSegDefs, pGrpDef->cSegDefs, 16);
+ OMF_READ_IDX(pGrpDef->paidxSegDefs[pGrpDef->cSegDefs], GRPDEF);
+ pGrpDef->cSegDefs++;
+ }
+ pOmfStuff->cGrpDefs++;
+ break;
+ }
+
+ /*
+ * Gather file names.
+ */
+ case OMF_THEADR: /* watcom */
+ cchSrcFile = pbRec[offRec++];
+ OMF_CHECK_RET(cchSrcFile, OMF_THEADR);
+ pchSrcFile = (const char *)&pbRec[offRec];
+ if (!collectOmfAddFile(pOmfStuff, cchSrcFile, pchSrcFile, &offSrcInfo))
+ return false;
+ break;
+
+ case OMF_COMENT:
+ {
+ OMF_CHECK_RET(2, COMENT);
+ offRec++; /* skip the type (flags) */
+ uint8_t bClass = pbRec[offRec++];
+ if (bClass == OMF_CCLS_BORLAND_SRC_FILE) /* nasm */
+ {
+ OMF_CHECK_RET(1+1+4, BORLAND_SRC_FILE);
+ offRec++; /* skip unknown byte */
+ cchSrcFile = pbRec[offRec++];
+ OMF_CHECK_RET(cchSrcFile + 4, BORLAND_SRC_FILE);
+ pchSrcFile = (const char *)&pbRec[offRec];
+ offRec += cchSrcFile;
+ if (offRec + 4 + 1 != cbRec)
+ return error(pszFile, "BAD BORLAND_SRC_FILE record at %#x: %d bytes left\n",
+ off, cbRec - offRec - 4 - 1);
+ if (!collectOmfAddFile(pOmfStuff, cchSrcFile, pchSrcFile, &offSrcInfo))
+ return false;
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Line number conversion.
+ */
+ case OMF_LINNUM16:
+ case OMF_LINNUM32:
+ {
+ uint16_t idxGrp;
+ OMF_READ_IDX(idxGrp, LINNUM);
+ uint16_t idxSeg;
+ OMF_READ_IDX(idxSeg, LINNUM);
+
+ uint16_t iLine;
+ uint32_t offSeg;
+ if (bRecType == OMF_LINNUM16)
+ while (offRec + 4 < cbRec)
+ {
+ iLine = RT_MAKE_U16(pbRec[offRec + 0], pbRec[offRec + 1]);
+ offSeg = RT_MAKE_U16(pbRec[offRec + 2], pbRec[offRec + 3]);
+ if (!collectOmfAddLine(pOmfStuff, idxSeg, offSeg, iLine, offSrcInfo))
+ return false;
+ offRec += 4;
+ }
+ else
+ while (offRec + 6 < cbRec)
+ {
+ iLine = RT_MAKE_U16(pbRec[offRec + 0], pbRec[offRec + 1]);
+ offSeg = RT_MAKE_U32_FROM_U8(pbRec[offRec + 2], pbRec[offRec + 3], pbRec[offRec + 4], pbRec[offRec + 5]);
+ if (!collectOmfAddLine(pOmfStuff, idxSeg, offSeg, iLine, offSrcInfo))
+ return false;
+ offRec += 6;
+ }
+ if (offRec + 1 != cbRec)
+ return error(pszFile, "BAD LINNUM record at %#x: %d bytes left\n", off, cbRec - offRec - 1);
+ break;
+ }
+ }
+
+ /* advance */
+ off += cbRec + 3;
+ }
+
+ return true;
+#undef OMF_READ_IDX
+#undef OMF_CHECK_RET
+}
+
+
+/**
+ * Adds a LNAMES entry (returns existing).
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ * @param pszName The name to add.
+ * @param pidxName Where to return the name index.
+ */
+static bool omfDetails_AddLName(POMFDETAILS pOmfStuff, const char *pszName, uint16_t *pidxName)
+{
+ size_t const cchName = strlen(pszName);
+
+ /*
+ * Check if we've already got the name.
+ */
+ for (unsigned iName = 1; iName < pOmfStuff->cLNames; iName++)
+ if ( (unsigned char)pOmfStuff->papchLNames[iName][0] == cchName
+ && memcmp(pOmfStuff->papchLNames[iName] + 1, pszName, cchName) == 0)
+ {
+ *pidxName = iName;
+ return true;
+ }
+
+ /*
+ * Not found, append it.
+ */
+ char *pszCopy = (char *)omfDetails_Alloc(pOmfStuff, cchName + 2);
+ if (!pszCopy)
+ return false;
+ *(unsigned char *)&pszCopy[0] = (unsigned char)cchName;
+ memcpy(pszCopy + 1, pszName, cchName + 1);
+
+ OMF_GROW_TABLE_RET_ERR(char *, pOmfStuff->papchLNames, pOmfStuff->cLNames, 16);
+ pOmfStuff->papchLNames[pOmfStuff->cLNames] = (char *)pszCopy;
+ *pidxName = pOmfStuff->cLNames;
+ pOmfStuff->cLNames++;
+ return true;
+}
+
+
+/**
+ * Adds a SEGDEF (always adds a new one).
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ * @param bSegAttr The OMF segment attributes.
+ * @param cbSeg The segment size.
+ * @param idxSegName The LNAMES index of the segment name.
+ * @param idxSegClas The LNAMES index of the segment class.
+ * @param idxOverlay The LNAMES index of the overlay name; pass 1.
+ * @param fRec32 Set if SEGDEF32 should be emitted, clear for SEGDEF16.
+ * @param pidxSeg Where to return the segment index.
+ */
+static bool omfDetails_AddSegDef(POMFDETAILS pOmfStuff, uint8_t bSegAttr, uint32_t cbSeg, uint16_t idxSegName,
+ uint16_t idxSegClass, uint16_t idxOverlay, bool fRec32, uint16_t *pidxSeg)
+{
+ Assert(cbSeg <= UINT16_MAX || fRec32);
+ Assert(idxSegName < pOmfStuff->cLNames);
+ Assert(idxSegClass < pOmfStuff->cLNames);
+
+ OMF_GROW_TABLE_RET_ERR(OMFSEGDEF, pOmfStuff->paSegDefs, pOmfStuff->cSegDefs, 16);
+ POMFSEGDEF pSegDef = &pOmfStuff->paSegDefs[pOmfStuff->cSegDefs];
+
+ pSegDef->bSegAttr = bSegAttr;
+ pSegDef->fUse32 = bSegAttr & 1;
+ pSegDef->f32bitRec = fRec32;
+ pSegDef->cbSeg = cbSeg;
+ pSegDef->idxName = idxSegName;
+ pSegDef->idxClass = idxSegClass;
+ pSegDef->idxOverlay = idxOverlay;
+
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxName, pSegDef->pchName, pSegDef->cchName, SEGDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxClass, pSegDef->pchClass, pSegDef->cchClass, SEGDEF);
+ OMF_EXPLODE_LNAME(pOmfStuff, pSegDef->idxOverlay, pSegDef->pchOverlay, pSegDef->cchOverlay, SEGDEF);
+
+ *pidxSeg = pOmfStuff->cSegDefs;
+ pOmfStuff->cSegDefs++;
+ return true;
+}
+
+
+/**
+ * Adds a SEGDEF if not found.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ * @param bSegAttr The OMF segment attributes.
+ * @param cbSeg The segment size.
+ * @param idxSegName The LNAMES index of the segment name.
+ * @param idxSegClas The LNAMES index of the segment class.
+ * @param idxOverlay The LNAMES index of the overlay name; pass 1.
+ * @param fRec32 Set if SEGDEF32 should be emitted, clear for SEGDEF16.
+ * @param pidxSeg Where to return the segment index.
+ */
+static bool omfDetails_AddSegDefIfNeeded(POMFDETAILS pOmfStuff, uint8_t bSegAttr, uint32_t cbSeg, uint16_t idxSegName,
+ uint16_t idxSegClass, uint16_t idxOverlay, bool fRec32, uint16_t *pidxSeg)
+{
+ /* Search for name */
+ for (unsigned iSegDef = 1; iSegDef < pOmfStuff->cSegDefs; iSegDef++)
+ {
+ POMFSEGDEF pSegDef = &pOmfStuff->paSegDefs[iSegDef];
+ if (pSegDef->idxName == idxSegName)
+ {
+ if ( pSegDef->bSegAttr != bSegAttr
+ || pSegDef->f32bitRec != fRec32
+ || pSegDef->idxName != idxSegName
+ || pSegDef->idxClass != idxSegClass
+ || pSegDef->idxOverlay != idxOverlay)
+ return error(pOmfStuff->pszFile,
+ "Existing SEGDEF differs: bSegAttr=%#x vs %#x, f32bitRec=%d vs %d, idxName=%#x vs %#x, idxClass=%#x vs %#x, idxOverlay=%#x vs %#x\n",
+ pSegDef->bSegAttr, bSegAttr,
+ pSegDef->f32bitRec, fRec32,
+ pSegDef->idxName, idxSegName,
+ pSegDef->idxClass, idxSegClass,
+ pSegDef->idxOverlay, idxOverlay);
+ *pidxSeg = iSegDef;
+ return true;
+ }
+ }
+ return omfDetails_AddSegDef(pOmfStuff, bSegAttr, cbSeg, idxSegName, idxSegClass, idxOverlay, fRec32, pidxSeg);
+}
+
+
+#if 0 /* unused */
+/**
+ * Looks up a GRPDEF in the .
+ *
+ * @returns Index (0..32K) if found, UINT16_MAX if not found.
+ * @param pOmfStuff The OMF stuff.
+ * @param pchName The name to look up.
+ * @param cchName The length of the name.
+ */
+static uint16_t omfDetails_GrpDefLookupN(POMFDETAILS pOmfStuff, const char *pchName, size_t cchName)
+{
+ unsigned iGrpDef = pOmfStuff->cGrpDefs;
+ while (iGrpDef-- > 0)
+ {
+ if ( pOmfStuff->paGrpDefs[iGrpDef].cchName == cchName
+ && memcmp(pOmfStuff->paGrpDefs[iGrpDef].pchName, pchName, cchName) == 0)
+ return iGrpDef;
+ }
+ return UINT16_MAX;
+}
+#endif
+
+
+/**
+ * Adds an empty GRPDEF (always adds a new one).
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ * @param idxGrpName The LNAMES index of the group name.
+ * @param pidxGrp Where to return the group index.
+ */
+static bool omfDetails_AddGrpDef(POMFDETAILS pOmfStuff, uint16_t idxGrpName, uint16_t *pidxGrp)
+{
+ Assert(idxGrpName < pOmfStuff->cLNames);
+
+ OMF_GROW_TABLE_RET_ERR(OMFGRPDEF, pOmfStuff->paGrpDefs, pOmfStuff->cGrpDefs, 8);
+ POMFGRPDEF pGrpDef = &pOmfStuff->paGrpDefs[pOmfStuff->cGrpDefs];
+
+ pGrpDef->idxName = idxGrpName;
+ pGrpDef->cSegDefs = 0;
+ pGrpDef->paidxSegDefs = NULL;
+
+ *pidxGrp = pOmfStuff->cGrpDefs;
+ pOmfStuff->cGrpDefs++;
+ return true;
+}
+
+
+/**
+ * Adds a segment to an existing GRPDEF.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ * @param idxGrp The GRPDEF index of the group to append a member to.
+ * @param idxSeg The SEGDEF index of the segment name.
+ */
+static bool omfDetails_AddSegToGrpDef(POMFDETAILS pOmfStuff, uint16_t idxGrp, uint16_t idxSeg)
+{
+ Assert(idxGrp < pOmfStuff->cGrpDefs && idxGrp > 0);
+ Assert(idxSeg < pOmfStuff->cSegDefs && idxSeg > 0);
+
+ POMFGRPDEF pGrpDef = &pOmfStuff->paGrpDefs[idxGrp];
+ OMF_GROW_TABLE_RET_ERR(uint16_t, pGrpDef->paidxSegDefs, pGrpDef->cSegDefs, 16);
+ pGrpDef->paidxSegDefs[pGrpDef->cSegDefs] = idxSeg;
+ pGrpDef->cSegDefs++;
+
+ return true;
+}
+
+
+/**
+ * Marks 16-bit code segment groups that is used in the object file as needed.
+ *
+ * @param pOmfStuff The OMF stuff.
+ */
+static void convertOmfLookForNeededGroups(POMFDETAILS pOmfStuff)
+{
+ /*
+ * Consult the groups in question. We mark the groups which segments are
+ * included in the segment definitions as needed.
+ */
+ unsigned i = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (i-- > 0)
+ if (pOmfStuff->aGroups[i].pszSeg)
+ {
+ const char * const pszSegNm = pOmfStuff->aGroups[i].pszSeg;
+ size_t const cchSegNm = strlen(pszSegNm);
+ for (unsigned iSegDef = 0; iSegDef < pOmfStuff->cSegDefs; iSegDef++)
+ if ( pOmfStuff->paSegDefs[iSegDef].cchName == cchSegNm
+ && memcmp(pOmfStuff->paSegDefs[iSegDef].pchName, pszSegNm, cchSegNm) == 0)
+ {
+ pOmfStuff->aGroups[i].fNeeded = true;
+ break;
+ }
+ }
+}
+
+
+/**
+ * Adds necessary group and segment definitions.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff.
+ */
+static bool convertOmfAddNeededGrpDefs(POMFDETAILS pOmfStuff)
+{
+ /*
+ * Process the groups.
+ */
+ unsigned j = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (j-- > 0)
+ if (pOmfStuff->aGroups[j].fNeeded)
+ {
+ if (pOmfStuff->aGroups[j].idxName == UINT16_MAX)
+ {
+ Assert(pOmfStuff->aGroups[j].idxGroup == UINT16_MAX);
+ if (!omfDetails_AddLName(pOmfStuff, pOmfStuff->aGroups[j].pszName, &pOmfStuff->aGroups[j].idxName))
+ return false;
+ }
+ if (pOmfStuff->aGroups[j].idxGroup == UINT16_MAX)
+ {
+ if (!omfDetails_AddGrpDef(pOmfStuff, pOmfStuff->aGroups[j].idxName, &pOmfStuff->aGroups[j].idxGroup))
+ return false;
+
+ if (pOmfStuff->aGroups[j].pszSeg)
+ {
+ /* We need the segment class name. */
+ uint16_t idxSegClass;
+ if (!omfDetails_AddLName(pOmfStuff, pOmfStuff->aGroups[j].pszClass1, &idxSegClass))
+ return false;
+
+ /* Prep segment name buffer. */
+ size_t cchSegNm = strlen(pOmfStuff->aGroups[j].pszSeg);
+ char szSegNm[256+16];
+ Assert(cchSegNm < 256);
+ memcpy(szSegNm, pOmfStuff->aGroups[j].pszSeg, cchSegNm);
+
+ /* Add the three segments. */
+ static RTSTRTUPLE const s_aSuffixes[3] = { {RT_STR_TUPLE("_START")}, {RT_STR_TUPLE("")}, {RT_STR_TUPLE("_END")}, };
+ for (unsigned iSuffix = 0; iSuffix < RT_ELEMENTS(s_aSuffixes); iSuffix++)
+ {
+ uint16_t idxSegNm;
+ memcpy(&szSegNm[cchSegNm], s_aSuffixes[iSuffix].psz, s_aSuffixes[iSuffix].cch + 1);
+ if (!omfDetails_AddLName(pOmfStuff, szSegNm, &idxSegNm))
+ return false;
+ uint8_t const fAlign = iSuffix == 1 ? OMF_SEG_ATTR_ALIGN_BYTE : OMF_SEG_ATTR_ALIGN_PARA;
+ uint16_t idxSeg;
+ if (!omfDetails_AddSegDefIfNeeded(pOmfStuff, fAlign | OMF_SEG_ATTR_COMB_PUBLIC | OMF_SEG_ATTR_USE16,
+ 0, idxSegNm, idxSegClass, 1, false /*fRec*/, &idxSeg))
+ return false;
+ if (!omfDetails_AddSegToGrpDef(pOmfStuff, pOmfStuff->aGroups[j].idxGroup, idxSeg))
+ return false;
+ }
+ }
+ }
+ }
+
+ /*
+ * Replace group references in the segment lines table.
+ */
+ j = RT_ELEMENTS(pOmfStuff->aGroups);
+ while (j-- > 0)
+ if (pOmfStuff->aGroups[j].fNeeded)
+ for (unsigned i = 0; i < pOmfStuff->cSegLines; i++)
+ if (pOmfStuff->paSegLines[i].idxGrp == pOmfStuff->aGroups[j].idxReplaceGrp)
+ pOmfStuff->paSegLines[i].idxGrp = pOmfStuff->aGroups[j].idxGroup;
+ return true;
+}
+
+
+/**
+ * Adds the debug segment definitions (names too) to the OMF state.
+ *
+ * @returns success indicator.
+ * @param pOmfStuff The OMF stuff with CV8 line number info.
+ */
+static bool convertOmfAddDebugSegDefs(POMFDETAILS pOmfStuff)
+{
+ if ( pOmfStuff->cSegLines == 0
+ || pOmfStuff->iSymbolsSeg != UINT16_MAX)
+ return true;
+
+ /*
+ * Add the names we need.
+ */
+ if ( pOmfStuff->iSymbolsNm == UINT16_MAX
+ && !omfDetails_AddLName(pOmfStuff, "$$SYMBOLS", &pOmfStuff->iSymbolsNm))
+ return false;
+ if ( pOmfStuff->iDebSymNm == UINT16_MAX
+ && !omfDetails_AddLName(pOmfStuff, "DEBSYM", &pOmfStuff->iDebSymNm))
+ return false;
+
+ /*
+ * Add the segment definition.
+ */
+ uint8_t bSegAttr = 0;
+ bSegAttr |= 5 << 5; /* A: dword alignment */
+ bSegAttr |= 0 << 2; /* C: private */
+ bSegAttr |= 0 << 1; /* B: not big */
+ bSegAttr |= 1; /* D: use32 */
+
+ /* calc the segment size. */
+ uint32_t cbSeg = 4; /* dword 4 */
+ cbSeg += 4 + 4 + RT_ALIGN_32(pOmfStuff->cbStrTab, 4);
+ cbSeg += 4 + 4 + pOmfStuff->cSrcInfo * sizeof(pOmfStuff->paSrcInfo[0]);
+ uint32_t i = pOmfStuff->cSegLines;
+ while (i-- > 0)
+ if (pOmfStuff->paSegLines[i].cFiles > 0)
+ cbSeg += 4 + 4 + pOmfStuff->paSegLines[i].cb;
+ return omfDetails_AddSegDef(pOmfStuff, bSegAttr, cbSeg, pOmfStuff->iSymbolsNm, pOmfStuff->iDebSymNm, 1 /*idxOverlay*/,
+ true /*fRec32*/, &pOmfStuff->iSymbolsSeg);
+}
+
+
+/**
+ * Writes the debug segment data.
+ *
+ * @returns success indicator.
+ * @param pThis The OMF writer.
+ * @param pOmfStuff The OMF stuff with CV8 line number info.
+ */
+static bool convertOmfWriteDebugData(POMFWRITER pThis, POMFDETAILS pOmfStuff)
+{
+ if (pOmfStuff->cSegLines == 0)
+ return true;
+ Assert(pOmfStuff->iSymbolsSeg != UINT16_MAX);
+
+ /* Begin and write the CV version signature. */
+ if ( !omfWriter_LEDataBegin(pThis, pOmfStuff->iSymbolsSeg, 0)
+ || !omfWriter_LEDataAddU32(pThis, RTCVSYMBOLS_SIGNATURE_CV8))
+ return false;
+
+ /*
+ * Emit the string table (no fixups).
+ */
+ uint32_t cbLeft = pOmfStuff->cbStrTab;
+ if ( !omfWriter_LEDataAddU32(pThis, RTCV8SYMBLOCK_TYPE_SRC_STR)
+ || !omfWriter_LEDataAddU32(pThis, cbLeft)
+ || !omfWriter_LEDataAddBytes(pThis, pOmfStuff->pchStrTab, RT_ALIGN_32(cbLeft, 4)) ) /* table is zero padded to nearest dword */
+ return false;
+
+ /*
+ * Emit the source file info table (no fixups).
+ */
+ cbLeft = pOmfStuff->cSrcInfo * sizeof(pOmfStuff->paSrcInfo[0]);
+ if ( !omfWriter_LEDataAddU32(pThis, RTCV8SYMBLOCK_TYPE_SRC_INFO)
+ || !omfWriter_LEDataAddU32(pThis, cbLeft)
+ || !omfWriter_LEDataAddBytes(pThis, pOmfStuff->paSrcInfo, cbLeft) )
+ return false;
+
+ /*
+ * Emit the segment line numbers. There are two fixups here at the start
+ * of each chunk.
+ */
+ POMFSEGLINES pSegLines = pOmfStuff->paSegLines;
+ uint32_t i = pOmfStuff->cSegLines;
+ while (i-- > 0)
+ {
+ if (pSegLines->cFiles)
+ {
+ /* Calc covered area. */
+ uint32_t cbSectionCovered = 0;
+ uint32_t j = pSegLines->cFiles;
+ while (j-- > 0)
+ {
+ uint32_t offLast = pSegLines->paFiles[j].paPairs[pSegLines->paFiles[j].cPairs - 1].offSection;
+ if (offLast > cbSectionCovered)
+ offLast = cbSectionCovered;
+ }
+
+ /* For simplicity and debuggability, just split the LEDATA here. */
+ if ( !omfWriter_LEDataSplit(pThis)
+ || !omfWriter_LEDataAddU32(pThis, RTCV8SYMBLOCK_TYPE_SECT_LINES)
+ || !omfWriter_LEDataAddU32(pThis, pSegLines->cb)
+ || !omfWriter_LEDataAddU32(pThis, 0) /*RTCV8LINESHDR::offSection*/
+ || !omfWriter_LEDataAddU16(pThis, 0) /*RTCV8LINESHDR::iSection*/
+ || !omfWriter_LEDataAddU16(pThis, 0) /*RTCV8LINESHDR::u16Padding*/
+ || !omfWriter_LEDataAddU32(pThis, cbSectionCovered) /*RTCV8LINESHDR::cbSectionCovered*/ )
+ return false;
+
+ /* Default to the segment (BS3TEXT32, BS3TEXT64) or the group (CGROUP16,
+ RMGROUP16, etc). The important thing is that we're framing the fixups
+ using a segment or group which ends up in the codeview segment map. */
+ uint16_t idxFrame = pSegLines->idxSeg;
+ uint8_t bFrame = OMF_FIX_F_SEGDEF;
+ if (pSegLines->idxGrp != UINT16_MAX)
+ {
+ idxFrame = pSegLines->idxGrp;
+ bFrame = OMF_FIX_F_GRPDEF;
+ }
+
+ /* Fixup #1: segment offset - IMAGE_REL_AMD64_SECREL. */
+ if (!omfWriter_LEDataAddFixupNoDisp(pThis, 4 + 4 + RT_UOFFSETOF(RTCV8LINESHDR, offSection), OMF_FIX_LOC_32BIT_OFFSET,
+ bFrame, idxFrame, OMF_FIX_T_SEGDEF_NO_DISP, pSegLines->idxSeg))
+ return false;
+
+
+ /* Fixup #2: segment number - IMAGE_REL_AMD64_SECTION. */
+ if (!omfWriter_LEDataAddFixupNoDisp(pThis, 4 + 4 + RT_UOFFSETOF(RTCV8LINESHDR, iSection), OMF_FIX_LOC_16BIT_SEGMENT,
+ bFrame, idxFrame, OMF_FIX_T_SEGDEF_NO_DISP, pSegLines->idxSeg))
+ return false;
+
+ /* Emit data for each source file. */
+ for (j = 0; j < pSegLines->cFiles; j++)
+ {
+ uint32_t const cbPairs = pSegLines->paFiles[j].cPairs * sizeof(RTCV8LINEPAIR);
+ if ( !omfWriter_LEDataAddU32(pThis, pSegLines->paFiles[j].offSrcInfo) /*RTCV8LINESSRCMAP::offSourceInfo*/
+ || !omfWriter_LEDataAddU32(pThis, pSegLines->paFiles[j].cPairs) /*RTCV8LINESSRCMAP::cLines*/
+ || !omfWriter_LEDataAddU32(pThis, cbPairs + sizeof(RTCV8LINESSRCMAP)) /*RTCV8LINESSRCMAP::cb*/
+ || !omfWriter_LEDataAddBytes(pThis, pSegLines->paFiles[j].paPairs, cbPairs))
+ return false;
+ }
+ }
+ pSegLines++;
+ }
+
+ return omfWriter_LEDataEnd(pThis);
+}
+
+
+/**
+ * Writes out all the segment group definitions.
+ *
+ * @returns success indicator.
+ * @param pThis The OMF writer.
+ * @param pOmfStuff The OMF stuff containing the segment defs.
+ * @param pfFlushState Pointer to the flush state variable.
+ */
+static bool convertOmfWriteAllSegDefs(POMFWRITER pThis, POMFDETAILS pOmfStuff, int *pfFlushState)
+{
+ if (*pfFlushState > 0)
+ {
+ for (unsigned iSegDef = 1; iSegDef < pOmfStuff->cSegDefs; iSegDef++)
+ {
+ if (!(pOmfStuff->paSegDefs[iSegDef].f32bitRec
+ ? omfWriter_SegDef : omfWriter_SegDef16)(pThis, pOmfStuff->paSegDefs[iSegDef].bSegAttr,
+ pOmfStuff->paSegDefs[iSegDef].cbSeg,
+ pOmfStuff->paSegDefs[iSegDef].idxName,
+ pOmfStuff->paSegDefs[iSegDef].idxClass,
+ pOmfStuff->paSegDefs[iSegDef].idxOverlay))
+ return false;
+ }
+ *pfFlushState = -1;
+ }
+ return true;
+}
+
+
+/**
+ * Writes out all the segment group definitions.
+ *
+ * @returns success indicator.
+ * @param pThis The OMF writer.
+ * @param pOmfStuff The OMF stuff containing the group defs.
+ * @param pfFlushState Pointer to the flush state variable.
+ */
+static bool convertOmfWriteAllGrpDefs(POMFWRITER pThis, POMFDETAILS pOmfStuff, int *pfFlushState)
+{
+ if (*pfFlushState > 0)
+ {
+ for (unsigned iGrpDef = 1; iGrpDef < pOmfStuff->cGrpDefs; iGrpDef++)
+ {
+ if (!omfWriter_GrpDefBegin(pThis, pOmfStuff->paGrpDefs[iGrpDef].idxName))
+ return false;
+ for (unsigned iSegDef = 0; iSegDef < pOmfStuff->paGrpDefs[iGrpDef].cSegDefs; iSegDef++)
+ if (!omfWriter_GrpDefAddSegDef(pThis, pOmfStuff->paGrpDefs[iGrpDef].paidxSegDefs[iSegDef]))
+ return false;
+ if (!omfWriter_GrpDefEnd(pThis))
+ return false;
+ }
+ *pfFlushState = -1;
+ }
+ return true;
+}
+
+
+/**
+ * This does the actual converting, passthru style.
+ *
+ * It only modifies, removes and inserts stuff it care about, the rest is passed
+ * thru as-is.
+ *
+ * @returns success indicator.
+ * @param pThis The OMF writer.
+ * @param pbFile The original file content.
+ * @param cbFile The size of the original file.
+ * @param pOmfStuff The OMF stuff we've gathered during the first pass,
+ * contains CV8 line number info if we converted anything.
+ * @param fConvertLineNumbers Whether we're converting line numbers and stuff.
+ */
+static bool convertOmfPassthru(POMFWRITER pThis, uint8_t const *pbFile, size_t cbFile, POMFDETAILS pOmfStuff,
+ bool fConvertLineNumbers)
+{
+ int fFlushLNames = 1;
+ int fFlushSegDefs = 1;
+ int fFlushGrpDefs = 1;
+ bool fSeenTheAdr = false;
+ bool fConvertFixupp = false;
+
+ uint32_t off = 0;
+ while (off + 3 < cbFile)
+ {
+ uint8_t bRecType = pbFile[off];
+ uint16_t cbRec = RT_MAKE_U16(pbFile[off + 1], pbFile[off + 2]);
+ uint32_t offRec = 0;
+ uint8_t const *pbRec = &pbFile[off + 3];
+
+#define OMF_READ_IDX(a_idx, a_Name) \
+ do { \
+ a_idx = pbRec[offRec++]; \
+ if ((a_idx) & 0x80) \
+ a_idx = (((a_idx) & 0x7f) << 8) | pbRec[offRec++]; \
+ } while (0)
+
+#define OMF_PEEK_IDX(a_idx, a_offRec) \
+ do { \
+ a_idx = pbRec[a_offRec]; \
+ if ((a_idx) & 0x80) \
+ a_idx = (((a_idx) & 0x7f) << 8) | pbRec[(a_offRec) + 1]; \
+ } while (0)
+
+ /*
+ * Remove/insert switch. will
+ */
+ bool fSkip = false;
+ switch (bRecType)
+ {
+ /*
+ * Mangle watcom intrinsics if necessary.
+ */
+ case OMF_EXTDEF:
+ if (pOmfStuff->fMayNeedMangling)
+ {
+ if (!omfWriter_ExtDefBegin(pThis))
+ return false;
+ while (offRec + 1 < cbRec)
+ {
+ uint8_t cchName = pbRec[offRec++];
+ char *pchName = (char *)&pbRec[offRec];
+ offRec += cchName;
+
+ uint16_t idxType;
+ OMF_READ_IDX(idxType, EXTDEF);
+
+ /* Look for g_apszExtDefRenames entries that requires changing. */
+ if ( cchName >= 5
+ && cchName <= 7
+ && pchName[0] == '_'
+ && pchName[1] == '_'
+ && ( pchName[2] == 'U'
+ || pchName[2] == 'I'
+ || pchName[2] == 'P')
+ && ( pchName[3] == '4'
+ || pchName[3] == '8'
+ || pchName[3] == 'I'
+ || pchName[3] == 'T') )
+ {
+ char szName[12];
+ memcpy(szName, pchName, cchName);
+ szName[cchName] = '\0';
+
+ uint32_t i = RT_ELEMENTS(g_apszExtDefRenames);
+ while (i-- > 0)
+ if ( cchName == (uint8_t)g_apszExtDefRenames[i][0]
+ && memcmp(&g_apszExtDefRenames[i][1], szName, cchName) == 0)
+ {
+ szName[0] = pOmfStuff->fProbably32bit ? '?' : '_';
+ szName[1] = '?';
+ break;
+ }
+
+ if (!omfWriter_ExtDefAddN(pThis, szName, cchName, idxType, false /*fPrependUnderscore*/))
+ return false;
+ }
+ else if (!omfWriter_ExtDefAddN(pThis, pchName, cchName, idxType, false /*fPrependUnderscore*/))
+ return false;
+ }
+ if (!omfWriter_ExtDefEnd(pThis))
+ return false;
+ fSkip = true;
+ }
+ break;
+
+ /*
+ * Remove line number records.
+ */
+ case OMF_LINNUM16:
+ case OMF_LINNUM32:
+ fSkip = fConvertLineNumbers;
+ break;
+
+ /*
+ * Remove all but the first OMF_THEADR.
+ */
+ case OMF_THEADR:
+ fSkip = fSeenTheAdr && fConvertLineNumbers;
+ fSeenTheAdr = true;
+ break;
+
+ /*
+ * Remove borland source file changes. Also, make sure the group
+ * definitions are written out.
+ */
+ case OMF_COMENT:
+ if (pbRec[1] == OMF_CCLS_LINK_PASS_SEP)
+ {
+ Assert(fFlushSegDefs <= 0);
+ if ( fFlushGrpDefs > 0
+ && !convertOmfWriteAllGrpDefs(pThis, pOmfStuff, &fFlushGrpDefs))
+ return false;
+ }
+ if (fConvertLineNumbers)
+ fSkip = pbRec[1] == OMF_CCLS_BORLAND_SRC_FILE;
+ break;
+
+ /*
+ * Redo these so the OMF writer is on top of the index thing.
+ */
+ case OMF_LNAMES:
+ if (fFlushLNames >= 0)
+ {
+ if (!omfWriter_LNamesBegin(pThis, false /*fAddZeroEntry*/))
+ return false;
+ if (!fFlushLNames)
+ {
+ while (offRec + 1 < cbRec)
+ {
+ uint8_t cch = pbRec[offRec];
+ const char *pch = (const char *)&pbRec[offRec + 1];
+ if (!omfWriter_LNamesAddN(pThis, pch, cch, NULL))
+ return false;
+ offRec += cch + 1;
+ }
+ }
+ else
+ {
+ /* Flush all LNAMES in one go. */
+ for (unsigned i = 1; i < pOmfStuff->cLNames; i++)
+ if (!omfWriter_LNamesAddN(pThis, pOmfStuff->papchLNames[i] + 1, *pOmfStuff->papchLNames[i], NULL))
+ return false;
+ fFlushLNames = -1;
+ }
+ if (!omfWriter_LNamesEnd(pThis))
+ return false;
+ }
+ fSkip = true;
+ break;
+
+ /*
+ * We may want to flush all the segments when we see the first one.
+ */
+ case OMF_SEGDEF16:
+ case OMF_SEGDEF32:
+ fSkip = fFlushSegDefs != 0;
+ if (!convertOmfWriteAllSegDefs(pThis, pOmfStuff, &fFlushSegDefs))
+ return false;
+ break;
+
+ /*
+ * We may want to flush all the groups when we see the first one.
+ */
+ case OMF_GRPDEF:
+ fSkip = fFlushGrpDefs != 0;
+ if (!convertOmfWriteAllGrpDefs(pThis, pOmfStuff, &fFlushGrpDefs))
+ return false;
+ break;
+
+ /*
+ * Hook LEDATA to flush groups and figure out when to convert FIXUPP records.
+ */
+ case OMF_LEDATA16:
+ case OMF_LEDATA32:
+ if ( fFlushGrpDefs > 0
+ && !convertOmfWriteAllGrpDefs(pThis, pOmfStuff, &fFlushGrpDefs))
+ return false;
+ fConvertFixupp = false;
+#if 0
+ if ( g_f16BitWatcomC
+ && bRecType == OMF_LEDATA16)
+ {
+ /* Check if this is a code segment. */
+ uint16_t idxSeg;
+ OMF_PEEK_IDX(idxSeg, offRec);
+
+ }
+#endif
+ break;
+
+
+ /*
+ * Convert fixups for 16-bit code segments to groups.
+ * Deals with switch table trouble.
+ */
+ case OMF_FIXUPP16:
+ if (fConvertFixupp)
+ {
+ /* Gave up on this for now, easier to drop the eyecatcher in the _START segments. */
+ }
+ break;
+
+ /*
+ * Upon seeing MODEND we write out the debug info.
+ */
+ case OMF_MODEND16:
+ case OMF_MODEND32:
+ if (fConvertLineNumbers)
+ if (!convertOmfWriteDebugData(pThis, pOmfStuff))
+ return false;
+ break;
+ }
+
+ /*
+ * Pass the record thru, if so was decided.
+ */
+ if (!fSkip)
+ {
+ if ( omfWriter_RecBegin(pThis, bRecType)
+ && omfWriter_RecAddBytes(pThis, pbRec, cbRec)
+ && omfWriter_RecEnd(pThis, false))
+ { /* likely */ }
+ else return false;
+ }
+
+ /* advance */
+ off += cbRec + 3;
+ }
+
+ return true;
+}
+
+
+/**
+ * Converts LINNUMs and compiler intrinsics in an OMF object file.
+ *
+ * Wlink does a cheesy (to use their own term) job of generating the
+ * sstSrcModule subsection. It is limited to one file and cannot deal with line
+ * numbers in different segment. The latter is very annoying in assembly files
+ * that jumps between segments, these a frequent on crash stacks.
+ *
+ * The solution is to convert to the same line number tables that cl.exe /Z7
+ * generates for our 64-bit C code, we named that format codeview v8, or CV8.
+ * Our code codeview debug info reader can deal with this already because of the
+ * 64-bit code, so Bob's your uncle.
+ *
+ * @returns success indicator.
+ * @param pszFile The name of the file being converted.
+ * @param pbFile The file content.
+ * @param cbFile The size of the file content.
+ * @param pDst The destiation (output) file.
+ */
+static bool convertOmfToOmf(const char *pszFile, uint8_t const *pbFile, size_t cbFile, FILE *pDst)
+{
+ bool const fConvertLineNumbers = true;
+
+ /*
+ * Collect line number information, names, segment defintions, groups definitions and such.
+ */
+ OMFDETAILS OmfStuff;
+ if (!collectOmfDetails(pszFile, pbFile, cbFile, &OmfStuff))
+ return false;
+
+ /* Mark groups for 16-bit code segments used by this object file as needed
+ so we can reframe fixups to these segments correctly. */
+ convertOmfLookForNeededGroups(&OmfStuff);
+
+ /* Add debug segments definitions. */
+ bool fRc = true;
+ if (fConvertLineNumbers)
+ fRc = convertOmfAddDebugSegDefs(&OmfStuff);
+
+ /* Add any additional group defintions we may need (for 16-bit code segs). */
+ if (fRc)
+ fRc = convertOmfAddNeededGrpDefs(&OmfStuff);
+ if (fRc)
+ {
+ /*
+ * Instantiate the OMF writer and do pass-thru modifications.
+ */
+ POMFWRITER pThis = omfWriter_Create(pszFile, 0, 0, pDst);
+ if (pThis)
+ {
+ fRc = convertOmfPassthru(pThis, pbFile, cbFile, &OmfStuff, fConvertLineNumbers);
+ omfWriter_Destroy(pThis);
+ }
+ else
+ fRc = false;
+ }
+
+ /*
+ * Cleanup OmfStuff.
+ */
+ uint32_t i = OmfStuff.cSegLines;
+ while (i-- >0)
+ {
+ uint32_t j = OmfStuff.paSegLines[i].cFiles;
+ while (j-- > 0)
+ free(OmfStuff.paSegLines[i].paFiles[j].paPairs);
+ free(OmfStuff.paSegLines[i].paFiles);
+ }
+ free(OmfStuff.paSegLines);
+ free(OmfStuff.paSrcInfo);
+ free(OmfStuff.pchStrTab);
+
+ while (OmfStuff.pAllocHead)
+ {
+ POMFDETAILSALLOC pFreeMe = OmfStuff.pAllocHead;
+ OmfStuff.pAllocHead = OmfStuff.pAllocHead->pNext;
+ free(pFreeMe);
+ }
+
+ return fRc;
+}
+
+
+/**
+ * Does the convertion using convertelf and convertcoff.
+ *
+ * @returns exit code (0 on success, non-zero on failure)
+ * @param pszFile The file to convert.
+ */
+static int convertit(const char *pszFile)
+{
+ /* Construct the filename for saving the unmodified file. */
+ char szOrgFile[_4K];
+ size_t cchFile = strlen(pszFile);
+ if (cchFile + sizeof(".original") > sizeof(szOrgFile))
+ {
+ error(pszFile, "Filename too long!\n");
+ return RTEXITCODE_FAILURE;
+ }
+ memcpy(szOrgFile, pszFile, cchFile);
+ memcpy(&szOrgFile[cchFile], ".original", sizeof(".original"));
+
+ /* Read the whole file. */
+ void *pvFile;
+ size_t cbFile;
+ if (readfile(pszFile, &pvFile, &cbFile))
+ {
+ /*
+ * Do format conversions / adjustments.
+ */
+ bool fRc = false;
+ uint8_t *pbFile = (uint8_t *)pvFile;
+ if ( cbFile > sizeof(Elf64_Ehdr)
+ && pbFile[0] == ELFMAG0
+ && pbFile[1] == ELFMAG1
+ && pbFile[2] == ELFMAG2
+ && pbFile[3] == ELFMAG3)
+ {
+ if (writefile(szOrgFile, pvFile, cbFile))
+ {
+ FILE *pDst = openfile(pszFile, true /*fWrite*/);
+ if (pDst)
+ {
+ fRc = convertElfToOmf(pszFile, pbFile, cbFile, pDst);
+ fRc = fclose(pDst) == 0 && fRc;
+ }
+ }
+ }
+ else if ( cbFile > sizeof(IMAGE_FILE_HEADER)
+ && RT_MAKE_U16(pbFile[0], pbFile[1]) == IMAGE_FILE_MACHINE_AMD64
+ && RT_MAKE_U16(pbFile[2], pbFile[3]) * sizeof(IMAGE_SECTION_HEADER) + sizeof(IMAGE_FILE_HEADER)
+ < cbFile
+ && RT_MAKE_U16(pbFile[2], pbFile[3]) > 0)
+ {
+ if (writefile(szOrgFile, pvFile, cbFile))
+ {
+ FILE *pDst = openfile(pszFile, true /*fWrite*/);
+ if (pDst)
+ {
+ fRc = convertCoffToOmf(pszFile, pbFile, cbFile, pDst);
+ fRc = fclose(pDst) == 0 && fRc;
+ }
+ }
+ }
+ else if ( cbFile >= 8
+ && pbFile[0] == OMF_THEADR
+ && RT_MAKE_U16(pbFile[1], pbFile[2]) < cbFile)
+ {
+ if (writefile(szOrgFile, pvFile, cbFile))
+ {
+ FILE *pDst = openfile(pszFile, true /*fWrite*/);
+ if (pDst)
+ {
+ fRc = convertOmfToOmf(pszFile, pbFile, cbFile, pDst);
+ fRc = fclose(pDst) == 0 && fRc;
+ }
+ }
+ }
+ else
+ fprintf(stderr, "error: Don't recognize format of '%s' (%#x %#x %#x %#x, cbFile=%lu)\n",
+ pszFile, pbFile[0], pbFile[1], pbFile[2], pbFile[3], (unsigned long)cbFile);
+ free(pvFile);
+ if (fRc)
+ return 0;
+ }
+ return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+ int rcExit = 0;
+
+ /*
+ * Scan the arguments.
+ */
+ for (int i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ const char *pszOpt = &argv[i][1];
+ if (*pszOpt == '-')
+ {
+ /* Convert long options to short ones. */
+ pszOpt--;
+ if (!strcmp(pszOpt, "--wcc"))
+ pszOpt = "w";
+ else if (!strcmp(pszOpt, "--verbose"))
+ pszOpt = "v";
+ else if (!strcmp(pszOpt, "--version"))
+ pszOpt = "V";
+ else if (!strcmp(pszOpt, "--help"))
+ pszOpt = "h";
+ else
+ {
+ fprintf(stderr, "syntax errro: Unknown options '%s'\n", pszOpt);
+ return 2;
+ }
+ }
+
+ /* Process the list of short options. */
+ while (*pszOpt)
+ {
+ switch (*pszOpt++)
+ {
+ case 'w':
+ g_f16BitWatcomC = true;
+ break;
+
+ case 'v':
+ g_cVerbose++;
+ break;
+
+ case 'V':
+ printf("%s\n", "$Revision: 155244 $");
+ return 0;
+
+ case '?':
+ case 'h':
+ printf("usage: %s [options] -o <output> <input1> [input2 ... [inputN]]\n",
+ argv[0]);
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * File to convert. Do the job right away.
+ */
+ rcExit = convertit(argv[i]);
+ if (rcExit != 0)
+ break;
+ }
+ }
+
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/asmdefs-first.mac b/src/VBox/ValidationKit/bootsectors/bs3kit/asmdefs-first.mac
new file mode 100644
index 00000000..54c545fe
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/asmdefs-first.mac
@@ -0,0 +1,62 @@
+; $Id: asmdefs-first.mac $
+;; @file
+; BS3Kit - Included by asmdefs.mac when assembling IPRT code.
+;
+; This will only be included if asmdefs.mac is included before bs3kit.mac, so
+; it will not be used for bs3*.asm files, only IPRT ones.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%ifndef ___asmdefs_first_mac
+%define ___asmdefs_first_mac
+
+%include "bs3kit-template-header.mac"
+
+;
+; Redefine some macros to suite us.
+;
+; We do near 16-bit code and produce far stubs separately as needed.
+;
+%define BEGINCODE TMPL_BEGIN_TEXT
+%define BEGINPROC_EXPORTED BS3_BEGINPROC_EXPORTED_WRAPPER
+%define ENDPROC BS3_PROC_END_CMN
+%undef NAME
+%define NAME(a) BS3_CMN_NM(a)
+
+%macro BS3_BEGINPROC_EXPORTED_WRAPPER 1-2 0 ; %2 is ignored as we don't do endbr32/64
+BS3_PROC_BEGIN_CMN %1, BS3_PBC_NEAR
+%endmacro
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm
new file mode 100644
index 00000000..edd26f4d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm
@@ -0,0 +1,594 @@
+; $Id: bs3-bootsector.asm $
+;; @file
+; Generic bootsector for BS3.
+;
+; This sets up stack at %fff0 and loads the next sectors from the floppy at
+; %10000 (1000:0000 in real mode), then starts executing at cs:ip=1000:0000.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+;; Enabled faster loading.
+%define BS3KIT_BOOTSECTOR_FASTER_LOAD
+;; Enabled load progress dots.
+%define BS3KIT_BOOTSECTOR_LOAD_DOTS
+
+;; Halts on failure location. For debugging.
+;%define HLT_ON_FAILURE 1
+
+;; Enables saving of initial register state.
+;; Dropping this is useful for making more room for debugging.
+%define BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+
+
+%ifdef __YASM__
+[map all]
+%endif
+
+;
+; Start with a jump just to follow the convention.
+; Also declare all segments/sections to establish them and their order.
+;
+ ORG 07c00h
+
+BITS 16
+CPU 8086
+start:
+ jmp short bs3InitCode
+ db 0ah ; Should be nop, but this looks better.
+g_OemId: ; 003h
+ db 'BS3Kit', 0ah, 0ah
+
+;
+; DOS 4.0 Extended Bios Parameter Block:
+;
+g_cBytesPerSector: ; 00bh
+ dw 512
+g_cSectorsPerCluster: ; 00dh
+ db 1
+g_cReservedSectors: ; 00eh
+ dw 1
+g_cFATs: ; 010h
+ db 0
+g_cRootDirEntries: ; 011h
+ dw 0
+g_cTotalSectors: ; 013h
+ dw 0
+g_bMediaDescriptor: ; 015h
+ db 0
+g_cSectorsPerFAT: ; 016h
+ dw 0
+g_cPhysSectorsPerTrack: ; 018h
+ dw 18
+g_cHeads: ; 01ah
+ dw 2
+g_cHiddentSectors: ; 01ch
+ dd 1
+g_cLargeTotalSectors: ; 020h - We (ab)use this to indicate the number of sectors to load.
+ dd 0
+g_bBootDrv: ; 024h
+ db 80h
+g_bFlagsEtc: ; 025h
+ db 0
+g_bExtendedSignature: ; 026h
+ db 0x29
+g_dwSerialNumber: ; 027h
+ dd 0x0a458634
+g_abLabel: ; 02bh
+ db 'VirtualBox', 0ah
+g_abFSType: ; 036h
+ db 'RawCode', 0ah
+g_BpbEnd: ; 03ch
+
+
+;
+; Where to real init code starts.
+;
+bs3InitCode:
+ cli
+
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ ; save the registers.
+ mov [cs:BS3_ADDR_REG_SAVE + BS3REGCTX.rax], ax
+ mov [cs:BS3_ADDR_REG_SAVE + BS3REGCTX.ds], ds
+%endif
+
+ ; set up the DS segment reister so we can skip the CS prefix when saving more prefixes..
+ mov ax, 0
+ mov ds, ax
+
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [BS3_ADDR_REG_SAVE + BS3REGCTX.rdi], di
+ mov di, BS3_ADDR_REG_SAVE
+ mov [di + BS3REGCTX.rsp], sp
+ mov [di + BS3REGCTX.ss], ss
+ mov [di + BS3REGCTX.rcx], cx
+ mov [di + BS3REGCTX.es], es
+ mov [di + BS3REGCTX.rbp], bp
+%endif
+
+ ; set up the stack.
+ mov ss, ax
+ mov sp, BS3_ADDR_STACK
+
+ ; Load es and setup bp frame.
+ mov es, ax
+ mov bp, sp
+%if 0
+ mov [bp], ax ; clear the first 8 bytes (terminates the ebp chain)
+ mov [bp + 02h], ax
+ mov [bp + 04h], ax
+ mov [bp + 06h], ax
+%else
+ mov di, sp ; Combine clearing the rbp chain and register save area.
+%endif
+
+ ; Save flags now that we know that there's a valid stack.
+ pushf
+
+ ;
+ ; Clear the register area.
+ ;
+%if 0
+ mov di, BS3_ADDR_REG_SAVE
+ mov cx, BS3REGCTX_size/2
+%else
+ mov cx, (BS3_ADDR_LOAD - BS3_ADDR_STACK) / 2
+%endif
+ cld
+ rep stosw
+
+ ;
+ ; Do basic CPU detection.
+ ;
+
+ ; 0. Load the register save area address into DI to avoid absolute addressing
+ ; when saving additional state. To avoid disp16, offset the address.
+ mov di, BS3_ADDR_REG_SAVE + 0x70
+
+ ; 1. bit 15-bit was fixed to 1 in pre-286 CPUs, and fixed to 0 in 286+.
+ mov ax, [bp - 2]
+ test ah, 080h ; always set on pre 286, clear on 286 and later
+ jnz .pre_80286
+
+ ; 2. On a 286 you cannot popf IOPL and NT from real mode.
+.detect_286_or_386plus:
+CPU 286
+ mov ah, (X86_EFL_IOPL | X86_EFL_NT) >> 8
+ push ax
+ popf
+ pushf
+ cmp ah, [bp - 3]
+ pop ax
+ je .is_386plus
+.is_80286:
+CPU 286
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ smsw [di + BS3REGCTX.cr0 - 0x70]
+%endif
+.pre_80286:
+CPU 8086
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [di - 0x70 + BS3REGCTX.rbx], bx
+ mov [di - 0x70 + BS3REGCTX.rdx], dx
+ mov [di - 0x70 + BS3REGCTX.rsi], si
+%endif
+ jmp .do_load
+
+ ; Save 386 registers. We can now skip the CS prefix as DS is flat.
+CPU 386
+.is_386plus:
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rax+2], ax
+ mov eax, esp
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rsp+2], ax
+ mov eax, ebp
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rbp+2], ax
+ mov eax, edi
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rdi+2], ax
+ shr ecx, 16
+ mov [di - 0x70 + BS3REGCTX.rcx+2], cx
+ mov [di - 0x70 + BS3REGCTX.fs], fs
+ mov [di - 0x70 + BS3REGCTX.gs], gs
+ mov [di - 0x70 + BS3REGCTX.rbx], ebx
+ mov [di - 0x70 + BS3REGCTX.rdx], edx
+ mov [di - 0x70 + BS3REGCTX.rsi], esi
+ mov eax, cr2
+ mov [di - 0x70 + BS3REGCTX.cr2], eax
+ mov eax, cr3
+ mov [di - 0x70 + BS3REGCTX.cr3], eax
+ mov byte [di - 0x70 + BS3REGCTX.bMode], BS3_MODE_RM
+ mov [di - 0x70 + BS3REGCTX.cs], cs
+ xor eax, eax
+ mov ax, start
+ mov [di - 0x70 + BS3REGCTX.rip], eax
+
+ ; Pentium/486+: CR4 requires VME/CPUID, so we need to detect that before accessing it.
+ mov [di - 0x70 + BS3REGCTX.cr4], eax
+ popf ; (restores IOPL+NT)
+ pushfd
+ pop eax
+ mov [di - 0x70 + BS3REGCTX.rflags], eax
+ xor eax, X86_EFL_ID
+ push eax
+ popfd
+ pushfd
+ pop ebx
+ cmp ebx, eax
+ jne .no_cr4
+ mov eax, cr4
+ mov [di - 0x70 + BS3REGCTX.cr4], eax
+.no_cr4:
+%endif
+ ; Make sure caching is enabled and alignment is off.
+ mov eax, cr0
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [di - 0x70 + BS3REGCTX.cr0], eax
+%endif
+ and eax, ~(X86_CR0_NW | X86_CR0_CD | X86_CR0_AM)
+ mov cr0, eax
+
+ ; Load all the code.
+.do_load
+ mov [g_bBootDrv], dl
+ call NAME(bs3InitLoadImage)
+%if 0
+ mov al, '='
+ call bs3PrintChrInAl
+%endif
+
+ ;
+ ; Call the user 'main' procedure (shouldn't return).
+ ;
+ cld
+ call BS3_SEL_TEXT16:0000h
+
+ ; Panic/hang.
+Bs3Panic:
+ cli
+ jmp Bs3Panic
+
+
+;; For debug and error handling.
+; @uses ax
+bs3PrintHexInAl:
+CPU 286
+ push ax
+ shr al, 4
+ call bs3PrintHexDigitInAl
+ pop ax
+bs3PrintHexDigitInAl:
+ and al, 0fh
+ cmp al, 10
+ jb .decimal
+ add al, 'a' - '0' - 10
+.decimal:
+ add al, '0'
+bs3PrintChrInAl:
+ push bx
+ mov ah, 0eh
+ mov bx, 0ff00h
+ int 10h
+ pop bx
+ ret
+
+
+;;
+; Loads the image off the floppy.
+;
+; This uses g_cLargeTotalSectors to figure out how much to load.
+;
+; Clobbers everything except ebp and esp. Panics on failure.
+;
+; @param dl The boot drive number (from BIOS).
+; @uses ax, cx, bx, esi, di
+;
+BEGINPROC bs3InitLoadImage
+ push bp
+ mov bp, sp
+ push es
+%define bSavedDiskNo byte [bp - 04h]
+ push dx
+%define bMaxSector byte [bp - 06h]
+%define wMaxSector word [bp - 06h]
+ xor ax, ax
+ push ax
+%define bMaxHead byte [bp - 08h]
+ push ax
+
+ ;
+ ; Try figure the geometry.
+ ;
+ mov ah, 08h
+ int 13h
+%ifndef HLT_ON_FAILURE
+ jc .failure
+%else
+ jnc .ok_geometry_call
+ cli
+ hlt
+.ok_geometry_call:
+%endif
+ and cl, 63 ; only the sector count.
+ mov bMaxSector, cl
+ mov bMaxHead, dh
+ mov dl, bSavedDiskNo
+
+%if 0 ; bMaxSector=0x12 (18); bMaxHead=0x01; bMaxCylinder=0x4f (79)
+ mov al, 'S'
+ call bs3PrintChrInAl
+ mov al, bMaxSector
+ call bs3PrintHexInAl
+ mov al, 'H'
+ call bs3PrintChrInAl
+ mov al, bMaxHead
+ call bs3PrintHexInAl
+ mov al, 'C'
+ call bs3PrintChrInAl
+ mov al, ch ; first 8-bit of cylinder count.
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+%endif
+
+%ifndef BS3KIT_BOOTSECTOR_FASTER_LOAD
+ ;
+ ; Load the sectors following the boot sector one at a time (avoids problems).
+ ;
+ mov si, [g_cLargeTotalSectors] ; 16-bit sector count ==> max 512 * 65 535 = 33 553 920 bytes.
+ dec si ; Practically max: ca 575 KB, or 1150 sectors. Linker set BS3_MAX_SIZE to 480KB.
+
+ mov di, BS3_ADDR_LOAD / 16 ; The current load segment.
+ mov cx, 0002h ; ch/cylinder=0 (0-based); cl/sector=2 (1-based)
+ xor dh, dh ; dh/head=0
+.the_load_loop:
+ %if 0
+ mov al, 'c'
+ call bs3PrintChrInAl
+ mov al, ch
+ call bs3PrintHexInAl
+ mov al, 's'
+ call bs3PrintChrInAl
+ mov al, cl
+ call bs3PrintHexInAl
+ mov al, 'h'
+ call bs3PrintChrInAl
+ mov al, dh
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+ %elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, '.'
+ call bs3PrintChrInAl
+ %endif
+ xor bx, bx
+ mov es, di ; es:bx -> buffer
+ mov ax, 0201h ; al=1 sector; ah=read function
+ int 13h
+ %ifndef HLT_ON_FAILURE
+ jc .failure
+ %else
+ jnc .read_ok
+ cli
+ hlt
+.read_ok:
+ %endif
+
+ ; advance to the next sector/head/cylinder.
+ inc cl
+ cmp cl, bMaxSector
+ jbe .adv_addr
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead
+ jbe .adv_addr
+
+ mov dh, 0
+ inc ch
+
+.adv_addr:
+ add di, 512 / 16
+ dec si
+ jnz .the_load_loop
+
+%else ; BS3KIT_BOOTSECTOR_FASTER_LOAD
+ ;
+ ; Load the sectors following the boot sector, trying to load a whole
+ ; side in each bios call, falling back on single sector reads if we
+ ; run into DMA 64KB boundrary issues (BIOS must tell us).
+ ;
+ mov si, [g_cLargeTotalSectors] ; 16-bit sector count ==> max 512 * 65 535 = 33 553 920 bytes.
+ dec si ; Skip the boot sector, it's not part of the test image we execute.
+ mov di, BS3_ADDR_LOAD / 16 ; The current load segment.
+ mov cx, 0002h ; ch/cylinder=0 (0-based); cl/sector=0 (1-based)
+ xor dh, dh ; dh/head=0
+.the_load_loop:
+ %if 0
+ mov al, 'c'
+ call bs3PrintChrInAl
+ mov al, ch
+ call bs3PrintHexInAl
+ mov al, 's'
+ call bs3PrintChrInAl
+ mov al, cl
+ call bs3PrintHexInAl
+ mov al, 'h'
+ call bs3PrintChrInAl
+ mov al, dh
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+ %elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, '.'
+ call bs3PrintChrInAl
+ %endif
+ mov ax, wMaxSector ; read to the end of the side by default.
+ sub al, cl
+ inc al
+.read_again:
+ cmp si, ax
+ jae .do_read
+ mov ax, si
+.do_read:
+ mov ah, 02h ; ah=read function
+ xor bx, bx
+ mov es, di ; es:bx -> buffer
+ int 13h
+ jnc .advance_sector
+
+ cmp ah, 9 ; DMA 64KB crossing error
+%if 0 ; This hack doesn't work. If the FDC is in single sided mode we end up with a garbled image. Probably "missing" sides.
+ je .read_one
+
+ cmp ah, 20h ; Controller error, probably because we're reading side 1 on a single sided floppy
+ jne .failure
+ cmp bMaxHead, 0
+ je .failure
+ cmp dh, 1
+ jne .failure
+ xor dh, dh
+ mov bMaxHead, dh
+ inc ch
+ jmp .the_load_loop
+.read_one:
+%elifdef HLT_ON_FAILURE
+ je .read_one_ok
+ cli
+ hlt
+.read_one_ok:
+%else
+ jne .failure
+%endif
+ mov ax, 1 ; Retry reading a single sector.
+ jmp .read_again
+
+ ; advance to the next sector/head/cylinder and address.
+.advance_sector:
+ inc cl
+ cmp cl, bMaxSector
+ jbe .adv_addr
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead
+ jbe .adv_addr
+
+ mov dh, 0
+ inc ch
+
+.adv_addr:
+ dec si
+ jz .done_reading
+ add di, 512 / 16
+ dec al
+ jnz .advance_sector
+ jmp .the_load_loop
+
+.done_reading:
+%endif ; BS3KIT_BOOTSECTOR_FASTER_LOAD
+%if 0
+ mov al, 'D'
+ call bs3PrintChrInAl
+%elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, 13
+ call bs3PrintChrInAl
+ mov al, 10
+ call bs3PrintChrInAl
+%endif
+
+ add sp, 2*2
+ pop dx
+ pop es
+ pop bp
+ ret
+
+%ifndef HLT_ON_FAILURE
+ ;
+ ; Something went wrong, display a message.
+ ;
+.failure:
+ %if 1 ; Disable to save space for debugging.
+ %if 1
+ push ax
+ %endif
+
+ ; print message
+ mov si, .s_szErrMsg
+.failure_next_char:
+ lodsb
+ call bs3PrintChrInAl
+ cmp si, .s_szErrMsgEnd
+ jb .failure_next_char
+
+ ; panic
+ %if 1
+ pop ax
+ mov al, ah
+ push bs3PrintHexInAl
+ %endif
+ call Bs3Panic
+.s_szErrMsg:
+ db 13, 10, 'rd err! '
+ %else
+ hlt
+ jmp .failure
+ %endif
+%endif
+.s_szErrMsgEnd:
+;ENDPROC bs3InitLoadImage - don't want the padding.
+
+
+;
+; Pad the remainder of the sector with int3's and end it with the DOS signature.
+;
+bs3Padding:
+ times ( 510 - ( (bs3Padding - start) % 512 ) ) db 0cch
+ db 055h, 0aah
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-CreateHybridFarRet.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-CreateHybridFarRet.asm
new file mode 100644
index 00000000..ca372f66
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-CreateHybridFarRet.asm
@@ -0,0 +1,63 @@
+; $Id: bs3-c16-CreateHybridFarRet.asm $
+;; @file
+; BS3Kit - Bs3A20Disable.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "bs3kit.mac"
+
+
+;;
+; Worker for BS3_PROC_BEGIN_CMN
+; @uses nothing
+BS3_PROC_BEGIN Bs3CreateHybridFarRet_c16
+ push ax ; reserve space
+ push bp
+ mov bp, sp
+ push ax ; save it
+
+ ; Move the return address up a word.
+ mov ax, [bp + 4]
+ mov [bp + 2], ax
+ ; Move the caller's return address up a word.
+ mov ax, [bp + 6]
+ mov [bp + 4], ax
+ ; Add CS to the caller's far return address.
+ mov [bp + 6], cs
+
+ pop ax
+ pop bp
+ ret
+BS3_PROC_END Bs3CreateHybridFarRet_c16
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-SwitchFromV86To16BitAndCallC.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-SwitchFromV86To16BitAndCallC.asm
new file mode 100644
index 00000000..5e0fcb90
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-SwitchFromV86To16BitAndCallC.asm
@@ -0,0 +1,109 @@
+; $Id: bs3-c16-SwitchFromV86To16BitAndCallC.asm $
+;; @file
+; BS3Kit - Bs3SwitchFromV86To16BitAndCallC
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+%if TMPL_BITS != 16
+ %error "16-bit only"
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%ifdef BS3_STRICT
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3Panic
+%endif
+BS3_EXTERN_CMN Bs3SwitchTo16Bit
+BS3_EXTERN_CMN Bs3SwitchTo16BitV86
+BS3_EXTERN_CMN Bs3SelRealModeCodeToProtMode
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(int, Bs3SwitchFromV86To16BitAndCallC,(FPFNBS3FAR fpfnCall, unsigned cbParams, ...));
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchFromV86To16BitAndCallC, BS3_PBC_HYBRID
+ inc bp
+ push bp
+ mov bp, sp
+
+ ;
+ ; Push the arguments first.
+ ;
+ mov ax, si ; save si
+ mov si, [bp + 2 + cbCurRetAddr + 4]
+%ifdef BS3_STRICT
+ test si, 1
+ jz .cbParams_ok
+ call Bs3Panic
+.cbParams_ok:
+ test byte [g_bBs3CurrentMode], BS3_MODE_CODE_V86
+ jnz .mode_ok
+ call Bs3Panic
+.mode_ok:
+%endif
+
+.push_more:
+ push word [bp + 2 + cbCurRetAddr + 4 + 2 + si - 2]
+ sub si, 2
+ jnz .push_more
+ mov si, ax ; restore si
+
+ ;
+ ; Convert the code segment to a 16-bit prot mode selector
+ ;
+ push word [bp + 2 + cbCurRetAddr + 2]
+ call Bs3SelRealModeCodeToProtMode
+ mov [bp + 2 + cbCurRetAddr + 2], ax
+ add sp, 2
+
+ ;
+ ; Switch mode.
+ ;
+ call Bs3SwitchTo16Bit
+ call far [bp + 2 + cbCurRetAddr]
+ call Bs3SwitchTo16BitV86
+
+ mov sp, bp
+ pop bp
+ dec bp
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SwitchFromV86To16BitAndCallC
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-Trap16Generic.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-Trap16Generic.asm
new file mode 100644
index 00000000..3db838a2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-Trap16Generic.asm
@@ -0,0 +1,720 @@
+; $Id: bs3-c16-Trap16Generic.asm $
+;; @file
+; BS3Kit - Trap, 16-bit assembly handlers.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+%ifndef TMPL_16BIT
+ %error "16-bit only template"
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_DATA16 g_uBs3TrapEipHint
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_EXTERN_DATA16 g_apfnBs3TrapHandlers_c16
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3TrapDefaultHandler
+BS3_EXTERN_CMN Bs3RegCtxRestore
+TMPL_BEGIN_TEXT
+
+
+;;
+; Generic entry points for IDT handlers, 8 byte spacing.
+;
+BS3_PROC_BEGIN _Bs3Trap16GenericEntries
+BS3_PROC_BEGIN Bs3Trap16GenericEntries
+%macro Bs3Trap16GenericEntryNoErr 1
+ push byte 0 ; 2 byte: fake error code
+ db 06ah, i ; 2 byte: push imm8 - note that this is a signextended value.
+ jmp %1 ; 3 byte
+ ALIGNCODE(8)
+%assign i i+1
+%endmacro
+
+%macro Bs3Trap16GenericEntryErrCd 1
+ db 06ah, i ; 2 byte: push imm8 - note that this is a signextended value.
+ jmp %1 ; 3 byte
+ ALIGNCODE(8)
+%assign i i+1
+%endmacro
+
+%assign i 0 ; start counter.
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 0
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 2
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 3
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 4
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 5
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 6
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 7
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; 8
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 9
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; a
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; b
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; c
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; d
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; e
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; f (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 10
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; 11
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 12
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 13
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 14
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 15 (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 16 (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 17 (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 18 (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 19 (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1a (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1b (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1c (reserved)
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1d (reserved)
+ Bs3Trap16GenericEntryErrCd bs3Trap16GenericTrapOrInt ; 1e
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt ; 1f (reserved)
+%rep 224
+ Bs3Trap16GenericEntryNoErr bs3Trap16GenericTrapOrInt
+%endrep
+BS3_PROC_END Bs3Trap16GenericEntries
+AssertCompile(Bs3Trap16GenericEntries_EndProc - Bs3Trap16GenericEntries == 8*256)
+
+
+;;
+; Trap or interrupt with error code, faked if necessary.
+;
+; Note! This code is going to "misbehave" if the high word of ESP is not cleared.
+;
+BS3_PROC_BEGIN _bs3Trap16GenericTrapOrInt
+BS3_PROC_BEGIN bs3Trap16GenericTrapOrInt
+CPU 386
+ jmp near bs3Trap16GenericTrapErrCode80286 ; Bs3Trap16Init adjusts this on 80386+
+ push ebp
+ movzx ebp, sp
+ push ebx ; BP - 04h
+ pushfd ; BP - 08h
+ cld
+ push edx ; BP - 0ch
+ push ss ; BP - 0eh
+ push esp ; BP - 12h
+
+ ;
+ ; We may be comming from 32-bit code where SS is flat and ESP has a non-
+ ; zero high word. We need to thunk it for C code to work correctly with
+ ; [BP+xx] and [SS:BX+xx] style addressing that leaves out the high word.
+ ;
+ ; Note! Require ring-0 handler for non-standard stacks (SS.DPL must equal CPL).
+ ;
+ mov bx, ss
+ lar ebx, bx
+ test ebx, X86LAR_F_D
+ jz .stack_fine
+ test esp, 0ffff0000h
+ jnz .stack_thunk
+.stack_load_r0_ss16:
+ mov bx, ss
+ and bl, 3
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov bh, bl
+ add bx, BS3_SEL_R0_SS16
+ jmp .stack_load_bx_into_ss
+.stack_thunk:
+ mov ebx, esp
+ shr ebx, 16
+ shl ebx, X86_SEL_SHIFT
+ add ebx, BS3_SEL_TILED_R0
+ cmp ebx, BS3_SEL_TILED_R0_LAST
+ ja .stack_esp_out_of_bounds
+.stack_load_bx_into_ss:
+ mov ss, bx
+.stack_fine:
+ movzx esp, sp
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 7) / 8
+.more_zeroed_space:
+ push 0
+ push 0
+ push 0
+ push 0
+ dec bx
+ jnz .more_zeroed_space
+ movzx ebx, sp
+
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], eax
+ mov edx, [bp - 12h] ; This isn't quite right for wrap arounds, but close enough for now
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], edx ; high bits
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], edx ; high bits
+ mov dx, [bp - 0eh]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], dx
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], dx
+ mov edx, [bp - 0ch]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], edx
+ mov edx, [bp - 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], edx ; high bits
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], edx
+ mov edx, [bp - 4]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], edx
+ mov edx, [bp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], edx
+
+ mov dl, [bp + 4]
+ mov [ss:bx + BS3TRAPFRAME.bXcpt], dl
+
+ mov dx, [bp + 6]
+;; @todo Do voodoo checks for 'int xx' or misguided hardware interrupts.
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], dx
+
+ add bp, 6 ; adjust so it points to the word before the iret frame.
+ xor dx, dx
+ jmp bs3Trap16GenericCommon
+
+.stack_esp_out_of_bounds:
+%ifdef BS3_STRICT
+ int3
+%endif
+ jmp .stack_esp_out_of_bounds
+BS3_PROC_END bs3Trap16GenericTrapErrCode
+
+;;
+; Trap with error code - 80286 code variant.
+;
+BS3_PROC_BEGIN bs3Trap16GenericTrapErrCode80286
+CPU 286
+ push bp
+ mov bp, sp
+ push bx
+ pushf
+ cld
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 7) / 8
+.more_zeroed_space:
+ push 0
+ push 0
+ push 0
+ push 0
+ dec bx
+ jnz .more_zeroed_space
+ mov bx, sp
+
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], ax
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], ss
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], ss
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], dx
+ mov dx, [bp - 4]
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], dx
+ mov dx, [bp - 2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], dx
+ mov dx, [bp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], dx
+
+ mov dl, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.bXcpt], dl
+
+ mov dx, [bp + 4]
+;; @todo Do voodoo checks for 'int xx' or misguided hardware interrupts.
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], dx
+
+ add bp, 4 ; adjust so it points to the word before the iret frame.
+ mov dl, 1
+ jmp bs3Trap16GenericCommon
+BS3_PROC_END bs3Trap16GenericTrapErrCode80286
+
+
+;;
+; Common context saving code and dispatching.
+;
+; @param bx Pointer to the trap frame, zero filled. The following members
+; have been filled in by the previous code:
+; - bXcpt
+; - uErrCd
+; - fHandlerRFL
+; - Ctx.eax
+; - Ctx.edx
+; - Ctx.ebx
+; - Ctx.ebp
+; - Ctx.rflags - high bits only.
+; - Ctx.esp - high bits only.
+; - Ctx.ss - for same cpl frames
+; - All other bytes are zeroed.
+;
+; @param bp Pointer to the word before the iret frame, i.e. where bp
+; would be saved if this was a normal near call.
+; @param dx One (1) if 286, zero (0) if 386+.
+;
+BS3_PROC_BEGIN bs3Trap16GenericCommon
+CPU 286
+ ;
+ ; Fake EBP frame.
+ ;
+ mov ax, [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp]
+ mov [bp], ax
+
+ ;
+ ; Save the remaining GPRs and segment registers.
+ ;
+ test dx, dx
+ jnz .save_word_grps
+CPU 386
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], edi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], esi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
+ jmp .save_segment_registers
+.save_word_grps:
+CPU 286
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], cx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], di
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], si
+.save_segment_registers:
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], ds
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.es], es
+ mov [ss:bx + BS3TRAPFRAME.uHandlerCs], cs
+
+ ;
+ ; Load 16-bit data selector for the DPL we're executing at into DS and ES.
+ ;
+ mov ax, ss
+ and ax, 3
+ mov cx, ax
+ shl ax, BS3_SEL_RING_SHIFT
+ or ax, cx
+ add ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+
+ ;
+ ; Copy and update the mode now that we've got a flat DS.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], al
+ mov cl, al
+ and cl, ~BS3_MODE_CODE_MASK
+ or cl, BS3_MODE_CODE_16
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], cl
+
+ ;
+ ; Copy iret info.
+ ;
+ lea cx, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], cx
+ mov cx, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], cx
+ mov cx, [bp + 6]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], cx
+ mov cx, [bp + 4]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+
+ test al, BS3_MODE_CODE_V86
+ jnz .iret_frame_v8086
+
+ mov ax, ss
+ and al, 3
+ and cl, 3
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], cl
+ cmp cl, al
+ je .iret_frame_same_cpl
+
+.ret_frame_different_cpl:
+ mov cx, [bp + 10]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov cx, [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
+ mov byte [ss:bx + BS3TRAPFRAME.cbIretFrame], 5*2
+ test dx, dx
+ jnz .iret_frame_done
+ jmp .iret_frame_seed_high_eip_word
+
+.iret_frame_same_cpl: ; (ss and high bits was saved by CPU specific part)
+ lea cx, [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
+ mov byte [ss:bx + BS3TRAPFRAME.cbIretFrame], 3*2
+ test dx, dx
+ jnz .iret_frame_done
+ jmp .iret_frame_seed_high_eip_word
+
+.iret_frame_v8086:
+CPU 386
+ or dword [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], X86_EFL_VM
+ mov byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], 3
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], BS3_MODE_CODE_V86 ; paranoia ^ 2
+%if 0 ;; @todo testcase: high ESP word from V86 mode, 16-bit TSS.
+ movzx ecx, word [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], ecx
+%else
+ mov cx, word [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
+%endif
+ mov cx, [bp + 10]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov cx, [bp + 12]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.es], cx
+ mov cx, [bp + 14]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], cx
+ mov cx, [bp + 16]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], cx
+ mov cx, [bp + 18]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], cx
+ mov byte [ss:bx + BS3TRAPFRAME.cbIretFrame], 9*2
+ jmp .iret_frame_done
+
+ ;
+ ; For 386 we do special tricks to supply the high word of EIP when
+ ; arriving here from 32-bit code. (ESP was seeded earlier.)
+ ;
+.iret_frame_seed_high_eip_word:
+ lar eax, [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cs]
+ jnz .iret_frame_done
+ test eax, X86LAR_F_D
+ jz .iret_frame_done
+ mov ax, [g_uBs3TrapEipHint+2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rip + 2], ax
+
+.iret_frame_done:
+ ;
+ ; Control registers.
+ ;
+ str [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.tr]
+ sldt [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ldtr]
+ test dx, dx
+ jnz .save_286_control_registers
+.save_386_control_registers:
+CPU 386
+ mov ax, ss
+ test al, 3
+ jnz .skip_crX_because_cpl_not_0
+ mov eax, cr0
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], eax
+ mov eax, cr2
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], eax
+ mov eax, cr3
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], eax
+
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8) ; CR4 first appeared in later 486es.
+ jz .skip_cr4_because_not_there
+ mov eax, cr4
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], eax
+ jmp .set_flags
+
+.skip_cr4_because_not_there:
+ mov byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+ jmp .set_flags
+
+.skip_crX_because_cpl_not_0:
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], \
+ BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4 | BS3REG_CTX_F_NO_CR0_IS_MSW
+
+CPU 286
+.save_286_control_registers:
+ smsw [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0]
+
+.set_flags: ; The double fault code joins us here.
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
+
+ ;
+ ; Dispatch it to C code.
+ ;
+.dispatch_to_handler:
+ mov di, bx
+ mov bl, byte [ss:bx + BS3TRAPFRAME.bXcpt]
+ mov bh, 0
+ shl bx, 1
+ mov bx, [bx + BS3_DATA16_WRT(_g_apfnBs3TrapHandlers_c16)]
+ or bx, bx
+ jnz .call_handler
+ mov bx, Bs3TrapDefaultHandler
+.call_handler:
+ push ss
+ push di
+ call bx
+
+ ;
+ ; Resume execution using trap frame.
+ ;
+ push 0
+ push ss
+ add di, BS3TRAPFRAME.Ctx
+ push di
+ call Bs3RegCtxRestore
+.panic:
+ hlt
+ jmp .panic
+BS3_PROC_END bs3Trap16GenericCommon
+
+
+;;
+; Helper.
+;
+; @retruns Flat address in es:di.
+; @param di
+; @uses eax
+;
+bs3Trap16TssInDiToFar1616InEsDi:
+CPU 286
+ push ax
+
+ ; ASSUME Bs3Gdt is being used.
+ push BS3_SEL_SYSTEM16
+ pop es
+ and di, 0fff8h
+ add di, Bs3Gdt wrt BS3SYSTEM16
+
+ ; Load the TSS base into ax:di (di is low, ax high)
+ mov al, [es:di + (X86DESCGENERIC_BIT_OFF_BASE_HIGH1 / 8)]
+ mov ah, [es:di + (X86DESCGENERIC_BIT_OFF_BASE_HIGH2 / 8)]
+ mov di, [es:di + (X86DESCGENERIC_BIT_OFF_BASE_LOW / 8)]
+
+ ; Convert ax to tiled selector, if not within the tiling area we read
+ ; random BS3SYSTEM16 bits as that's preferable to #GP'ing.
+ shl ax, X86_SEL_SHIFT
+ cmp ax, BS3_SEL_TILED_LAST - BS3_SEL_TILED
+%ifdef BS3_STRICT
+ jbe .tiled
+ int3
+%endif
+ ja .return ; don't crash again.
+.tiled:
+ add ax, BS3_SEL_TILED
+ mov es, ax
+.return:
+ pop ax
+ ret
+
+
+;;
+; Double fault handler.
+;
+; We don't have to load any selectors or clear anything in EFLAGS because the
+; TSS specified sane values which got loaded during the task switch.
+;
+; @param dx Zero (0) for indicating 386+ to the common code.
+;
+BS3_PROC_BEGIN _Bs3Trap16DoubleFaultHandler80386
+BS3_PROC_BEGIN Bs3Trap16DoubleFaultHandler80386
+CPU 386
+ push 0 ; We'll copy the rip from the other TSS here later to create a more sensible call chain.
+ push ebp
+ mov bp, sp
+ pushfd ; Handler flags.
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 15) / 16
+.more_zeroed_space:
+ push dword 0
+ push dword 0
+ push dword 0
+ push dword 0
+ dec bx
+ jz .more_zeroed_space
+ mov bx, sp
+
+ ;
+ ; Fill in the high GRP register words before we mess them up.
+ ;
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], eax
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], ebx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], edx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], esi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], edi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], ebp
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], esp
+
+ ;
+ ; FS and GS are not part of the 16-bit TSS because they are 386+ specfic.
+ ;
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
+
+ ;
+ ; Fill in the non-context trap frame bits.
+ ;
+ mov ecx, [bp - 4]
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], ecx
+ mov byte [ss:bx + BS3TRAPFRAME.bXcpt], X86_XCPT_DF
+ mov [ss:bx + BS3TRAPFRAME.uHandlerCs], cs
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], ss
+ mov ecx, esp
+ lea cx, [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], ecx
+ mov cx, [bp + 6]
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], cx
+
+ ;
+ ; Copy 80386+ control registers.
+ ;
+ mov ecx, cr0
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], ecx
+ mov ecx, cr2
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], ecx
+ mov ecx, cr3
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], ecx
+
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8) ; CR4 first appeared in later 486es.
+ jz .skip_cr4_because_not_there
+ mov ecx, cr4
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], ecx
+ jmp .common
+
+.skip_cr4_because_not_there:
+ mov byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+
+ ;
+ ; Copy the register state from the previous task segment.
+ ; The 80286 code with join us here.
+ ;
+.common:
+CPU 286
+ ; Find our TSS.
+ str di
+ call bs3Trap16TssInDiToFar1616InEsDi
+
+ ; Find the previous TSS.
+ mov di, [es:di + X86TSS32.selPrev]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.tr], ax
+ call bs3Trap16TssInDiToFar1616InEsDi
+
+ ; Do the copying.
+ mov cx, [es:di + X86TSS16.ax]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], cx
+ mov cx, [es:di + X86TSS16.cx]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], cx
+ mov cx, [es:di + X86TSS16.dx]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], cx
+ mov cx, [es:di + X86TSS16.bx]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], cx
+ mov cx, [es:di + X86TSS16.sp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
+ mov cx, [es:di + X86TSS16.bp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], cx
+ mov [bp], cx ; For better call stacks.
+ mov cx, [es:di + X86TSS16.si]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], cx
+ mov cx, [es:di + X86TSS16.di]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], cx
+ mov cx, [es:di + X86TSS16.si]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], cx
+ mov cx, [es:di + X86TSS16.flags]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], cx
+ mov cx, [es:di + X86TSS16.ip]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], cx
+ mov [bp + 2], cx ; For better call stacks.
+ mov cx, [es:di + X86TSS16.cs]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+ mov cx, [es:di + X86TSS16.ds]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], cx
+ mov cx, [es:di + X86TSS16.es]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.es], cx
+ mov cx, [es:di + X86TSS16.ss]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov cx, [es:di + X86TSS16.selLdt] ; Note! This isn't necessarily the ldtr at the time of the fault.
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ldtr], cx
+
+ ;
+ ; Set CPL; copy and update mode.
+ ;
+ mov cl, [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss]
+ and cl, 3
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], cl
+
+ mov cl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], cl
+ and cl, ~BS3_MODE_CODE_MASK
+ or cl, BS3_MODE_CODE_16
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], cl
+
+ ;
+ ; Join code paths with the generic handler code.
+ ;
+ jmp bs3Trap16GenericCommon.set_flags
+BS3_PROC_END Bs3Trap16DoubleFaultHandler
+
+
+;;
+; Double fault handler.
+;
+; We don't have to load any selectors or clear anything in EFLAGS because the
+; TSS specified sane values which got loaded during the task switch.
+;
+; @param dx One (1) for indicating 386+ to the common code.
+;
+BS3_PROC_BEGIN _Bs3Trap16DoubleFaultHandler80286
+BS3_PROC_BEGIN Bs3Trap16DoubleFaultHandler80286
+CPU 286
+ push 0 ; We'll copy the rip from the other TSS here later to create a more sensible call chain.
+ push bp
+ mov bp, sp
+ pushf ; Handler flags.
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 7) / 8
+.more_zeroed_space:
+ push 0
+ push 0
+ push 0
+ push 0
+ dec bx
+ jz .more_zeroed_space
+ mov bx, sp
+
+ ;
+ ; Fill in the non-context trap frame bits.
+ ;
+ mov cx, [bp - 2]
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], cx
+ mov byte [ss:bx + BS3TRAPFRAME.bXcpt], X86_XCPT_DF
+ mov [ss:bx + BS3TRAPFRAME.uHandlerCs], cs
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], ss
+ lea cx, [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], cx
+ mov cx, [bp + 6]
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], cx
+
+ ;
+ ; Copy 80286 specific control register.
+ ;
+ smsw [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0]
+
+ jmp Bs3Trap16DoubleFaultHandler80386.common
+BS3_PROC_END Bs3Trap16DoubleFaultHandler80286
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Data.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Data.c
new file mode 100644
index 00000000..6398157f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Data.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-c16-TrapRmV86Data.c $ */
+/** @file
+ * BS3Kit - Real mode and V86 trap data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+/** Copy of the original real-mode interrupt vector table. */
+RTFAR16 g_aBs3RmIvtOriginal[256];
+/** Indicates whether we've copied the real-mode IVT or not. */
+bool g_fBs3RmIvtCopied = false;
+#endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Generic.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Generic.asm
new file mode 100644
index 00000000..017f112a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c16-TrapRmV86Generic.asm
@@ -0,0 +1,401 @@
+; $Id: bs3-c16-TrapRmV86Generic.asm $
+;; @file
+; BS3Kit - Trap, 16-bit assembly handlers for real mode and v8086.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+%ifndef TMPL_16BIT
+ %error "16-bit only template"
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_DATA16 g_uBs3TrapEipHint
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_EXTERN_DATA16 g_apfnBs3TrapHandlers_c16
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3TrapDefaultHandler
+BS3_EXTERN_CMN Bs3RegCtxRestore
+TMPL_BEGIN_TEXT
+
+
+;;
+; Generic entry points for IDT handlers, 8 byte spacing.
+;
+BS3_PROC_BEGIN _Bs3TrapRmV86GenericEntries
+BS3_PROC_BEGIN Bs3TrapRmV86GenericEntries
+%macro Bs3TrapRmV86GenericEntryNoErr 1
+ push ax ; 1 byte: Reserve space for fake error cd. (BP(+2) + 4)
+ push ax ; 1 byte: Save AX (BP(+2) + 2)
+ mov ax, i | 00000h ; 2 bytes: AL = trap/interrupt number; AH=indicate no error code
+ jmp %1 ; 3 bytes: Jump to handler code
+ ALIGNCODE(8)
+%assign i i+1
+%endmacro
+
+%macro Bs3TrapRmV86GenericEntryErrCd 1
+ Bs3TrapRmV86GenericEntryNoErr %1 ; No error code pushed in real mode or V86 mode.
+%endmacro
+
+%assign i 0 ; start counter.
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 0
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 2
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 3
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 4
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 5
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 6
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 7
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; 8
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 9
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; a
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; b
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; c
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; d
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; e
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; f (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 10
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; 11
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 12
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 13
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 14
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 15 (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 16 (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 17 (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 18 (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 19 (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1a (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1b (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1c (reserved)
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1d (reserved)
+ Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt ; 1e
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt ; 1f (reserved)
+%rep 224
+ Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt
+%endrep
+BS3_PROC_END Bs3TrapRmV86GenericEntries
+AssertCompile(Bs3TrapRmV86GenericEntries_EndProc - Bs3TrapRmV86GenericEntries == 8*256)
+
+
+;;
+; Trap or interrupt with error code, faked if necessary.
+;
+; early 386+ stack (movzx ebp, sp):
+; [bp + 000h] ebp
+; [bp + 004h] ax
+; [bp + 006h] errcd [bp'+0] <--- bp at jmp to common code.
+; [bp + 008h] cs [bp'+2]
+; [bp + 00ah] ip [bp'+4]
+; [bp + 00ch] flags [bp'+6]
+; ([bp + 00eh] post-iret sp value) [bp'+8]
+;
+BS3_PROC_BEGIN _bs3TrapRmV86GenericTrapOrInt
+BS3_PROC_BEGIN bs3TrapRmV86GenericTrapOrInt
+CPU 386
+ jmp near bs3TrapRmV86GenericTrapErrCode8086 ; Bs3TrapRmV86Init adjusts this on 80386+
+ push ebp
+ movzx ebp, sp
+ push ebx ; BP - 04h
+ pushfd ; BP - 08h
+ cld
+ push edx ; BP - 0ch
+ push ss ; BP - 0eh
+ push esp ; BP - 12h
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 7) / 8
+.more_zeroed_space:
+ push 0
+ push 0
+ push 0
+ push 0
+ dec bx
+ jnz .more_zeroed_space
+ movzx ebx, sp
+
+
+ mov edx, [bp - 12h]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], edx ; high bits
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], edx ; high bits
+ mov dx, [bp - 0eh]
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], dx
+ mov edx, [bp - 0ch]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], edx
+ mov edx, [bp - 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], edx ; high bits
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], edx
+ mov edx, [bp - 4]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], edx
+ mov edx, [bp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], edx
+ mov edx, eax ; high bits
+ mov dx, [bp + 4]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], edx
+
+ mov [ss:bx + BS3TRAPFRAME.bXcpt], al
+
+ test ah, 0ffh
+ jz .no_error_code
+ mov dx, [bp + 6]
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], dx
+.no_error_code:
+
+ add bp, 6 ; adjust so it points to the word before the iret frame.
+ xor dx, dx
+ jmp bs3TrapRmV86GenericCommon
+BS3_PROC_END bs3TrapRmV86GenericTrapErrCode
+
+;;
+; Trap with error code - 8086/V20/80186/80286 code variant.
+;
+BS3_PROC_BEGIN bs3TrapRmV86GenericTrapErrCode8086
+CPU 8086
+ push bp
+ mov bp, sp
+ push bx ; BP - 2
+ pushf ; BP - 4
+ push ax ; BP - 6
+ cld
+
+ ; Reserve space for the register and trap frame.
+ mov bx, (BS3TRAPFRAME_size + 7) / 8
+ xor ax, ax
+.more_zeroed_space:
+ push ax
+ push ax
+ push ax
+ push ax
+ dec bx
+ jnz .more_zeroed_space
+ mov bx, sp
+
+ mov [ss:bx + BS3TRAPFRAME.uHandlerSs], ss
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], dx
+ mov dx, [bp - 4]
+ mov [ss:bx + BS3TRAPFRAME.fHandlerRfl], dx
+ mov dx, [bp - 2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], dx
+ mov dx, [bp]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], dx
+
+ mov dx, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], dx
+
+ mov ax, [bp - 6]
+ mov [ss:bx + BS3TRAPFRAME.bXcpt], al
+
+ test ah, 0ffh
+ jz .no_error_code
+ mov dx, [bp + 4]
+ mov [ss:bx + BS3TRAPFRAME.uErrCd], dx
+.no_error_code:
+
+ add bp, 4 ; adjust so it points to the word before the iret frame.
+ mov dl, 1
+ jmp bs3TrapRmV86GenericCommon
+BS3_PROC_END bs3TrapRmV86GenericTrapErrCode8086
+
+
+;;
+; Common context saving code and dispatching.
+;
+; @param ss:bx Pointer to the trap frame, zero filled. The following members
+; have been filled in by the previous code:
+; - bXcpt
+; - uErrCd
+; - fHandlerRFL
+; - Ctx.eax
+; - Ctx.edx
+; - Ctx.ebx
+; - Ctx.ebp
+; - Ctx.rflags - high bits only.
+; - Ctx.esp - high bits only.
+; - All other bytes are zeroed.
+;
+; @param bp Pointer to the word before the iret frame, i.e. where bp
+; would be saved if this was a normal near call.
+; @param dx One (1) if 286, zero (0) if 386+.
+;
+BS3_PROC_BEGIN bs3TrapRmV86GenericCommon
+CPU 8086
+ ;
+ ; Fake EBP frame.
+ ;
+ mov ax, [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp]
+ mov [bp], ax
+
+ ;
+ ; Save the remaining GPRs and segment registers.
+ ;
+ test dx, dx
+ jnz .save_word_grps
+CPU 386
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], edi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], esi
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
+ jmp .save_segment_registers
+.save_word_grps:
+CPU 8086
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], cx
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], di
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], si
+.save_segment_registers:
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], ds
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.es], es
+ mov [ss:bx + BS3TRAPFRAME.uHandlerCs], cs
+
+ ;
+ ; Load 16-bit BS3KIT_GRPNM_DATA16 into DS and ES so we can access globals.
+ ;
+ mov ax, BS3KIT_GRPNM_DATA16
+ mov ds, ax
+ mov es, ax
+
+ ;
+ ; Copy the mode now that we've got a flat DS. We don't need to update
+ ; it as it didn't change.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], al
+
+ ;
+ ; Copy iret info.
+ ;
+ lea cx, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.uHandlerRsp], cx
+ mov cx, [bp + 2]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], cx
+ mov cx, [bp + 6]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], cx
+ mov cx, [bp + 4]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+ mov cx, ss
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ lea cx, [bp + 8]
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
+ mov byte [ss:bx + BS3TRAPFRAME.cbIretFrame], 3*2
+
+ ; The VM flag and CPL.
+ test al, BS3_MODE_CODE_V86
+ jz .dont_set_vm
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags + 2], X86_EFL_VM >> 16
+ mov byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], 3
+.dont_set_vm:
+
+
+ ;
+ ; Control registers.
+ ;
+ ; Since we're in real or v8086 here, we cannot save TR and LDTR.
+ ; But get MSW (CR0) first since that's always accessible and we
+ ; need it even on a 386 to check whether we're in v8086 mode or not.
+ ;
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jb .skip_control_registers_because_80186_or_older
+CPU 286
+ smsw ax
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], ax
+
+ test dx, dx
+ jnz .set_flags
+.save_386_control_registers:
+CPU 386
+ ; 386 control registers are not accessible from virtual 8086 mode.
+ test al, X86_CR0_PE
+ jnz .skip_crX_because_v8086
+ mov eax, cr0
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], eax
+ mov eax, cr2
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], eax
+ mov eax, cr3
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], eax
+
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8) ; CR4 first appeared in later 486es.
+ jz .skip_cr4_because_not_there
+ mov eax, cr4
+ mov [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], eax
+ jmp .set_flags
+
+.skip_cr4_because_not_there:
+ mov byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+ jmp .set_flags
+
+CPU 8086
+.skip_control_registers_because_80186_or_older:
+.skip_crX_because_v8086:
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], \
+ BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
+.set_flags: ; The double fault code joins us here.
+ or byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64 | BS3REG_CTX_F_NO_TR_LDTR
+
+ ;
+ ; Dispatch it to C code.
+ ;
+.dispatch_to_handler:
+ mov di, bx
+ mov bl, byte [ss:bx + BS3TRAPFRAME.bXcpt]
+ mov bh, 0
+ shl bx, 1
+ mov bx, [bx + BS3_DATA16_WRT(_g_apfnBs3TrapHandlers_c16)]
+ or bx, bx
+ jnz .call_handler
+ mov bx, Bs3TrapDefaultHandler
+.call_handler:
+ push ss
+ push di
+ call bx
+
+ ;
+ ; Resume execution using trap frame.
+ ;
+ xor ax, ax
+ push ax
+ push ss
+ add di, BS3TRAPFRAME.Ctx
+ push di
+ call Bs3RegCtxRestore
+.panic:
+ hlt
+ jmp .panic
+BS3_PROC_END bs3TrapRmV86GenericCommon
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c32-Trap32Generic.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c32-Trap32Generic.asm
new file mode 100644
index 00000000..3f25a5f2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c32-Trap32Generic.asm
@@ -0,0 +1,546 @@
+; $Id: bs3-c32-Trap32Generic.asm $
+;; @file
+; BS3Kit - Trap, 32-bit assembly handlers.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+%ifndef TMPL_32BIT
+ %error "32-bit only template"
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_EXTERN_DATA16 g_apfnBs3TrapHandlers_c32
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3TrapDefaultHandler
+BS3_EXTERN_CMN Bs3RegCtxRestore
+TMPL_BEGIN_TEXT
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+;; Easy to access flat address of Bs3Trap32GenericEntries.
+BS3_GLOBAL_DATA g_Bs3Trap32GenericEntriesFlatAddr, 4
+ dd Bs3Trap32GenericEntries wrt FLAT
+;; Easy to access flat address of Bs3Trap32DoubleFaultHandler.
+BS3_GLOBAL_DATA g_Bs3Trap32DoubleFaultHandlerFlatAddr, 4
+ dd Bs3Trap32DoubleFaultHandler wrt FLAT
+
+
+TMPL_BEGIN_TEXT
+
+;;
+; Generic entry points for IDT handlers, 8 byte spacing.
+;
+BS3_PROC_BEGIN Bs3Trap32GenericEntries
+%macro Bs3Trap32GenericEntryNoErr 1
+ push byte 0 ; 2 byte: fake error code.
+ db 06ah, i ; 2 byte: push imm8 - note that this is a signextended value.
+ jmp near %1 ; 5 byte
+ ALIGNCODE(2)
+%assign i i+1
+%endmacro
+
+%macro Bs3Trap32GenericEntryErrCd 1
+ db 06ah, i ; 2 byte: push imm8 - note that this is a signextended value.
+ jmp near %1 ; 5 byte
+ db 0cch, 0cch ; 2 byte: padding.
+ ALIGNCODE(2)
+%assign i i+1
+%endmacro
+
+%assign i 0 ; start counter.
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 0
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 2
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 3
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 4
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 5
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 6
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 7
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; 8
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 9
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; a
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; b
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; c
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; d
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; e
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; f (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 10
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; 11
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 12
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 13
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 14
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 15 (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 16 (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 17 (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 18 (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 19 (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1a (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1b (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1c (reserved)
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1d (reserved)
+ Bs3Trap32GenericEntryErrCd bs3Trap32GenericTrapOrInt ; 1e
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt ; 1f (reserved)
+%rep 224
+ Bs3Trap32GenericEntryNoErr bs3Trap32GenericTrapOrInt
+%endrep
+BS3_PROC_END Bs3Trap32GenericEntries
+AssertCompile(Bs3Trap32GenericEntries_EndProc - Bs3Trap32GenericEntries == 10*256)
+
+
+;;
+; Trap or interrupt with error code, faked if necessary.
+;
+BS3_PROC_BEGIN bs3Trap32GenericTrapOrInt
+ push ebp ; 0
+ mov ebp, esp
+ pushfd ; -04h
+ cld
+ push eax ; -08h
+ push edi ; -0ch
+ lea eax, [esp + (4+1+1)*4] ; 4 pushes above, 1 exception number push, 1 error code.
+ push eax ; -10h = handler ESP
+ add eax, 3*4 ; 3 dword iret frame
+ push eax ; -14h = caller ESP if same CPL
+ push ss ; -18h
+ push ds ; -1ch
+
+ ; Make sure we've got a flat DS. It makes everything so much simpler.
+ mov ax, ss
+ and al, 3
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov ah, al
+ add ax, BS3_SEL_R0_DS32
+ mov ds, ax
+
+ ;
+ ; We may be comming from 16-bit code with a 16-bit SS. Thunk it as
+ ; the C code may assume flat SS and we'll mess up by using EBP/ESP/EDI
+ ; instead of BP/SP/SS:DI. ASSUMES standard GDT selector.
+ ;
+ mov ax, ss
+ lar eax, ax
+ test eax, X86LAR_F_D
+ jz .stack_thunk
+ mov ax, ss
+ and al, 3
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov ah, al
+ add ax, BS3_SEL_R0_SS32
+ mov ss, ax
+ jmp .stack_flat
+.stack_thunk:
+ mov di, ss
+ and edi, X86_SEL_MASK_OFF_RPL
+ mov al, [X86DESCGENERIC_BIT_OFF_BASE_HIGH1 / 8 + edi + Bs3Gdt wrt FLAT]
+ mov ah, [X86DESCGENERIC_BIT_OFF_BASE_HIGH2 / 8 + edi + Bs3Gdt wrt FLAT]
+ shl eax, 16
+ mov ax, [X86DESCGENERIC_BIT_OFF_BASE_LOW / 8 + edi + Bs3Gdt wrt FLAT] ; eax = SS.base
+ movzx ebp, bp ; SS:BP -> flat EBP.
+ add ebp, eax
+ movzx edi, sp ; SS:SP -> flat ESP in EAX.
+ add edi, eax
+ mov ax, ss
+ and al, 3
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov ah, al
+ add ax, BS3_SEL_R0_SS32
+ mov ss, ax
+ mov esp, edi
+ sub dword [ebp - 10h], (4+1)*4 ; Recalc handler ESP in case of wraparound.
+ add word [ebp - 10h], (4+1)*4
+ sub dword [ebp - 10h], (4+1+3)*4 ; Recalc caller ESP in case of wraparound.
+ add word [ebp - 10h], (4+1+3)*4
+.stack_flat:
+
+ ; Reserve space for the register and trap frame.
+ mov eax, (BS3TRAPFRAME_size + 7) / 8
+AssertCompileSizeAlignment(BS3TRAPFRAME, 8)
+.more_zeroed_space:
+ push dword 0
+ push dword 0
+ dec eax
+ jnz .more_zeroed_space
+ mov edi, esp ; edi points to trapframe structure.
+
+ ; Copy stuff from the stack over.
+ mov eax, [ebp + 8]
+;; @todo Do voodoo checks for 'int xx' or misguided hardware interrupts.
+ mov [edi + BS3TRAPFRAME.uErrCd], eax
+ mov al, [ebp + 4]
+ mov [edi + BS3TRAPFRAME.bXcpt], al
+ mov eax, [ebp]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], eax
+ mov eax, [ebp - 04h]
+ mov [edi + BS3TRAPFRAME.fHandlerRfl], eax
+ mov eax, [ebp - 08h]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], eax
+ mov eax, [ebp - 0ch]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], eax
+ mov eax, [ebp - 10h]
+ mov [edi + BS3TRAPFRAME.uHandlerRsp], eax
+ mov eax, [ebp - 14h]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], eax
+ mov ax, [ebp - 18h]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], ax
+ mov [edi + BS3TRAPFRAME.uHandlerSs], ax
+ mov ax, [ebp - 1ch]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], ax
+
+ lea ebp, [ebp + 8] ; iret - 4 (i.e. ebp frame chain location)
+ jmp bs3Trap32GenericCommon
+BS3_PROC_END bs3Trap32GenericTrapErrCode
+
+
+;;
+; Common context saving code and dispatching.
+;
+; @param edi Pointer to the trap frame. The following members have been
+; filled in by the previous code:
+; - bXcpt
+; - uErrCd
+; - fHandlerRfl
+; - uHandlerRsp
+; - uHandlerSs
+; - Ctx.rax
+; - Ctx.rbp
+; - Ctx.rdi
+; - Ctx.rsp - assuming same CPL
+; - Ctx.ds
+; - Ctx.ss
+;
+; @param ebp Pointer to the dword before the iret frame, i.e. where ebp
+; would be saved if this was a normal call.
+;
+; @remarks This is a separate function for hysterical raisins.
+;
+BS3_PROC_BEGIN bs3Trap32GenericCommon
+ ;
+ ; Fake EBP frame.
+ ;
+ mov eax, [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp]
+ mov [ebp], eax
+
+ ;
+ ; Save the remaining GPRs and segment registers.
+ ;
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], edx
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], ebx
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], esi
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.es], es
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
+
+ ;
+ ; Load 32-bit data selector for the DPL we're executing at into DS and ES.
+ ; Save the handler CS value first.
+ ;
+ mov ax, cs
+ mov [edi + BS3TRAPFRAME.uHandlerCs], ax
+ and al, 3
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov ah, al
+ add ax, BS3_SEL_R0_DS32
+ mov ds, ax
+ mov es, ax
+
+ ;
+ ; Copy and update the mode now that we've got a flat DS.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], al
+ and al, ~BS3_MODE_CODE_MASK
+ or al, BS3_MODE_CODE_32
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], al
+
+ ;
+ ; Copy iret info.
+ ;
+ mov ecx, [ebp + 4]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], ecx
+ mov ecx, [ebp + 12]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], ecx
+ mov cx, [ebp + 8]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+ test dword [ebp + 12], X86_EFL_VM
+ jnz .iret_frame_v8086
+ mov ax, ss
+ and al, 3
+ and cl, 3
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], cl
+ cmp cl, al
+ je .iret_frame_same_cpl
+
+.iret_frame_different_cpl:
+ mov ecx, [ebp + 16]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], ecx
+ mov cx, [ebp + 20]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov byte [edi + BS3TRAPFRAME.cbIretFrame], 5*4
+ jmp .iret_frame_done
+
+.iret_frame_v8086:
+ mov byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], 3
+ or byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], BS3_MODE_CODE_V86 ; paranoia ^ 2
+ mov ecx, [ebp + 16]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], ecx
+ mov cx, [ebp + 20]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov cx, [ebp + 24]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.es], cx
+ mov cx, [ebp + 28]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], cx
+ mov cx, [ebp + 32]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], cx
+ mov cx, [ebp + 36]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], cx
+ mov byte [edi + BS3TRAPFRAME.cbIretFrame], 9*4
+ jmp .iret_frame_done
+
+.iret_frame_same_cpl: ; (caller already set SS:RSP and uHandlerRsp for same CPL iret frames)
+ mov byte [edi + BS3TRAPFRAME.cbIretFrame], 3*4
+
+.iret_frame_done:
+ ;
+ ; Control registers.
+ ;
+ str ax
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.tr], ax
+ sldt ax
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ldtr], ax
+
+ mov ax, ss
+ test al, 3
+ jnz .skip_crX_because_cpl_not_0
+
+ mov eax, cr3
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], eax
+.save_cr0_cr2_cr4: ; The double fault code joins us here.
+ mov eax, cr0
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], eax
+ mov eax, cr2
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], eax
+
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8) ; CR4 first appeared in later 486es.
+ jz .skip_cr4_because_not_there
+ mov eax, cr4
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], eax
+ jmp .set_flags
+
+.skip_cr4_because_not_there:
+ mov byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+ jmp .set_flags
+
+.skip_crX_because_cpl_not_0:
+ or byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], \
+ BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
+ smsw [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0]
+.set_flags:
+ or byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
+
+ ;
+ ; Dispatch it to C code.
+ ;
+.dispatch_to_handler:
+ movzx ebx, byte [edi + BS3TRAPFRAME.bXcpt]
+ mov eax, [ebx * 4 + BS3_DATA16_WRT(_g_apfnBs3TrapHandlers_c32)]
+ or eax, eax
+ jnz .call_handler
+ mov eax, Bs3TrapDefaultHandler
+.call_handler:
+ push edi
+ call eax
+
+ ;
+ ; Resume execution using trap frame.
+ ;
+ push 0
+ add edi, BS3TRAPFRAME.Ctx
+ push edi
+ call Bs3RegCtxRestore
+.panic:
+ hlt
+ jmp .panic
+BS3_PROC_END bs3Trap32GenericCommon
+
+
+;;
+; Helper.
+;
+; @retruns Flat address in eax.
+; @param ax
+; @uses eax
+;
+bs3Trap32TssInAxToFlatInEax:
+ ; Get the GDT base address and find the descriptor address (EAX)
+ sub esp, 8+2
+ sgdt [esp]
+ and eax, 0fff8h
+ add eax, [esp + 2] ; GDT base address.
+ add esp, 8+2
+
+ ; Get the flat TSS address from the descriptor.
+ mov al, [eax + (X86DESCGENERIC_BIT_OFF_BASE_HIGH1 / 8)]
+ mov ah, [eax + (X86DESCGENERIC_BIT_OFF_BASE_HIGH2 / 8)]
+ shl eax, 16
+ mov ax, [eax + (X86DESCGENERIC_BIT_OFF_BASE_LOW / 8)]
+ ret
+
+;;
+; Double fault handler.
+;
+; We don't have to load any selectors or clear anything in EFLAGS because the
+; TSS specified sane values which got loaded during the task switch.
+;
+BS3_PROC_BEGIN Bs3Trap32DoubleFaultHandler
+ push 0 ; We'll copy the rip from the other TSS here later to create a more sensible call chain.
+ push ebp
+ mov ebp, esp
+
+ pushfd ; Get handler flags.
+ pop ecx
+
+ xor edx, edx ; NULL register.
+
+ ;
+ ; Allocate a zero filled trap frame.
+ ;
+ mov eax, (BS3TRAPFRAME_size + 7) / 8
+AssertCompileSizeAlignment(BS3TRAPFRAME, 8)
+.more_zeroed_space:
+ push edx
+ push edx
+ dec eax
+ jz .more_zeroed_space
+ mov edi, esp
+
+ ;
+ ; Fill in the non-context trap frame bits.
+ ;
+ mov [edi + BS3TRAPFRAME.fHandlerRfl], ecx
+ mov word [edi + BS3TRAPFRAME.bXcpt], X86_XCPT_DF
+ mov [edi + BS3TRAPFRAME.uHandlerCs], cs
+ mov [edi + BS3TRAPFRAME.uHandlerSs], ss
+ lea ecx, [ebp + 3*4] ; two pushes, one error code.
+ mov [edi + BS3TRAPFRAME.uHandlerRsp], ecx
+ mov ecx, [ebp + 8]
+ mov [edi + BS3TRAPFRAME.uErrCd], ecx
+
+ ;
+ ; Copy the register state from the previous task segment.
+ ;
+
+ ; Find our TSS.
+ str ax
+ call bs3Trap32TssInAxToFlatInEax
+
+ ; Find the previous TSS.
+ mov ax, [eax + X86TSS32.selPrev]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.tr], ax
+ call bs3Trap32TssInAxToFlatInEax
+
+ ; Do the copying.
+ mov ecx, [eax + X86TSS32.eax]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], ecx
+ mov ecx, [eax + X86TSS32.ecx]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
+ mov ecx, [eax + X86TSS32.edx]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], ecx
+ mov ecx, [eax + X86TSS32.ebx]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], ecx
+ mov ecx, [eax + X86TSS32.esp]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], ecx
+ mov ecx, [eax + X86TSS32.ebp]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], ecx
+ mov [ebp], ecx ; For better call stacks.
+ mov ecx, [eax + X86TSS32.esi]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], ecx
+ mov ecx, [eax + X86TSS32.edi]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], ecx
+ mov ecx, [eax + X86TSS32.esi]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], ecx
+ mov ecx, [eax + X86TSS32.eflags]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], ecx
+ mov ecx, [eax + X86TSS32.eip]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], ecx
+ mov [ebp + 4], ecx ; For better call stacks.
+ mov cx, [eax + X86TSS32.cs]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+ mov cx, [eax + X86TSS32.ds]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], cx
+ mov cx, [eax + X86TSS32.es]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.es], cx
+ mov cx, [eax + X86TSS32.fs]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], cx
+ mov cx, [eax + X86TSS32.gs]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], cx
+ mov cx, [eax + X86TSS32.ss]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov cx, [eax + X86TSS32.selLdt] ; Note! This isn't necessarily the ldtr at the time of the fault.
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ldtr], cx
+ mov cx, [eax + X86TSS32.cr3] ; Note! This isn't necessarily the cr3 at the time of the fault.
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], ecx
+
+ ;
+ ; Set CPL; copy and update mode.
+ ;
+ mov cl, [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss]
+ and cl, 3
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], cl
+
+ mov cl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], cl
+ and cl, ~BS3_MODE_CODE_MASK
+ or cl, BS3_MODE_CODE_32
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], cl
+
+ ;
+ ; Join code paths with the generic handler code.
+ ;
+ jmp bs3Trap32GenericCommon.save_cr0_cr2_cr4
+BS3_PROC_END Bs3Trap32DoubleFaultHandler
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c64-Trap64Generic.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c64-Trap64Generic.asm
new file mode 100644
index 00000000..13e0ea27
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-c64-Trap64Generic.asm
@@ -0,0 +1,337 @@
+; $Id: bs3-c64-Trap64Generic.asm $
+;; @file
+; BS3Kit - Trap, 64-bit assembly handlers.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+%ifndef TMPL_64BIT
+ %error "64-bit only template"
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_DATA16 g_apfnBs3TrapHandlers_c64
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3TrapDefaultHandler
+BS3_EXTERN_CMN Bs3RegCtxRestore
+TMPL_BEGIN_TEXT
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+;; Easy to access flat address of Bs3Trap64GenericEntries.
+BS3_GLOBAL_DATA g_Bs3Trap64GenericEntriesFlatAddr, 4
+ dd Bs3Trap64GenericEntries wrt FLAT
+
+
+TMPL_BEGIN_TEXT
+
+;;
+; Generic entry points for IDT handlers, 8 byte spacing.
+;
+BS3_PROC_BEGIN Bs3Trap64GenericEntries
+%macro Bs3Trap64GenericEntry 1
+ db 06ah, i ; push imm8 - note that this is a signextended value.
+ jmp %1
+ ALIGNCODE(8)
+%assign i i+1
+%endmacro
+
+%assign i 0 ; start counter.
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 0
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 2
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 3
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 4
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 5
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 6
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 7
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; 8
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 9
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; a
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; b
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; c
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; d
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; e
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; f (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 10
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; 11
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 12
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 13
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 14
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 15 (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 16 (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 17 (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 18 (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 19 (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1a (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1b (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1c (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1d (reserved)
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapErrCode ; 1e
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt ; 1f (reserved)
+%rep 224
+ Bs3Trap64GenericEntry Bs3Trap64GenericTrapOrInt
+%endrep
+BS3_PROC_END Bs3Trap64GenericEntries
+
+
+
+
+;;
+; Trap or interrupt (no error code).
+;
+BS3_PROC_BEGIN Bs3Trap64GenericTrapOrInt
+ push rbp ; 0
+ mov rbp, rsp
+ pushfq ; -08h
+ cld
+ push rdi
+
+ ; Reserve space for the register and trap frame.
+ mov edi, (BS3TRAPFRAME_size + 15) / 16
+.more_zeroed_space:
+ push qword 0
+ push qword 0
+ dec edi
+ jnz .more_zeroed_space
+ mov rdi, rsp ; rdi points to trapframe structure.
+
+ ; Free up rax.
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], rax
+
+ ; Copy stuff from the stack over.
+ mov al, [rbp + 08h]
+ mov [rdi + BS3TRAPFRAME.bXcpt], al
+ mov rax, [rbp]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], rax
+ mov rax, [rbp - 08h]
+ mov [rdi + BS3TRAPFRAME.fHandlerRfl], rax
+ mov rax, [rbp - 10h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], rax
+
+ lea rbp, [rbp + 08h] ; iret - 8 (i.e. rbp frame chain location)
+ jmp Bs3Trap64GenericCommon
+BS3_PROC_END Bs3Trap64GenericTrapOrInt
+
+
+;;
+; Trap with error code.
+;
+BS3_PROC_BEGIN Bs3Trap64GenericTrapErrCode
+ push rbp ; 0
+ mov rbp, rsp
+ pushfq ; -08h
+ cld
+ push rdi
+
+ ; Reserve space for the register and trap frame.
+ mov edi, (BS3TRAPFRAME_size + 15) / 16
+.more_zeroed_space:
+ push qword 0
+ push qword 0
+ dec edi
+ jnz .more_zeroed_space
+ mov rdi, rsp ; rdi points to trapframe structure.
+
+ ; Free up rax.
+ mov [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], rax
+
+ ; Copy stuff from the stack over.
+ mov rax, [rbp + 10h]
+ mov [rdi + BS3TRAPFRAME.uErrCd], rax
+ mov al, [rbp + 08h]
+ mov [rdi + BS3TRAPFRAME.bXcpt], al
+ mov rax, [rbp]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], rax
+ mov rax, [rbp - 08h]
+ mov [rdi + BS3TRAPFRAME.fHandlerRfl], rax
+ mov rax, [rbp - 10h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], rax
+
+ lea rbp, [rbp + 10h] ; iret - 8 (i.e. rbp frame chain location)
+ jmp Bs3Trap64GenericCommon
+BS3_PROC_END Bs3Trap64GenericTrapErrCode
+
+
+;;
+; Common context saving code and dispatching.
+;
+; @param rdi Pointer to the trap frame. The following members have been
+; filled in by the previous code:
+; - bXcpt
+; - uErrCd
+; - fHandlerRfl
+; - Ctx.rax
+; - Ctx.rbp
+; - Ctx.rdi
+;
+; @param rbp Pointer to the dword before the iret frame, i.e. where rbp
+; would be saved if this was a normal call.
+;
+BS3_PROC_BEGIN Bs3Trap64GenericCommon
+ ;
+ ; Fake RBP frame.
+ ;
+ mov rax, [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp]
+ mov [rbp], rax
+
+ ;
+ ; Save the remaining GPRs and segment registers.
+ ;
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], rcx
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], rdx
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], rbx
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], rsi
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r8 ], r8
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r9 ], r9
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r10], r10
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r11], r11
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r12], r12
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r13], r13
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r14], r14
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.r15], r15
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], ds
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.es], es
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
+ lea rax, [rbp + 8h]
+ mov [rdi + BS3TRAPFRAME.uHandlerRsp], rax
+ mov [rdi + BS3TRAPFRAME.uHandlerSs], ss
+
+ ;
+ ; Load 32-bit data selector for the DPL we're executing at into DS, ES and SS.
+ ; Save the handler CS value first.
+ ;
+ mov ax, cs
+ mov [rdi + BS3TRAPFRAME.uHandlerCs], ax
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ and al, 3
+ mov ah, al
+ add ax, BS3_SEL_R0_DS64
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+
+ ;
+ ; Copy and update the mode.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], al
+ and al, ~BS3_MODE_CODE_MASK
+ or al, BS3_MODE_CODE_64
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], al
+
+ ;
+ ; Copy iret info. Bless AMD for only doing one 64-bit iret frame layout.
+ ;
+ mov rcx, [rbp + 08]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], rcx
+ mov cx, [rbp + 10h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
+ and cl, 3
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], cl
+ mov rcx, [rbp + 18h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], rcx
+ mov rcx, [rbp + 20h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], rcx
+ mov cx, [rbp + 28h]
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
+ mov byte [rdi + BS3TRAPFRAME.cbIretFrame], 5*8
+
+ ;
+ ; Control registers.
+ ;
+ str ax
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.tr], ax
+ sldt ax
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.ldtr], ax
+
+ mov ax, ss
+ test al, 3
+ jnz .skip_crX_because_cpl_not_0
+
+ mov rax, cr0
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], rax
+ mov rax, cr2
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], rax
+ mov rax, cr3
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], rax
+ mov rax, cr4
+ mov [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], rax
+ jmp .dispatch_to_handler
+
+.skip_crX_because_cpl_not_0:
+ or byte [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], \
+ BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
+ smsw [rdi + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0]
+
+ ;
+ ; Dispatch it to C code.
+ ;
+.dispatch_to_handler: ; The double fault code joins us here.
+ movzx ebx, byte [rdi + BS3TRAPFRAME.bXcpt]
+ lea rax, [BS3_DATA16_WRT(_g_apfnBs3TrapHandlers_c64)]
+ mov rax, [rax + rbx * 8]
+ or rax, rax
+ jnz .call_handler
+ lea rax, [BS3_WRT_RIP(Bs3TrapDefaultHandler)]
+.call_handler:
+ sub rsp, 20h
+ mov [rsp], rdi
+ mov rcx, rdi
+ call rax
+
+ ;
+ ; Resume execution using trap frame.
+ ;
+ xor edx, edx ; fFlags
+ mov [rsp + 8], rdx
+ lea rcx, [rdi + BS3TRAPFRAME.Ctx] ; pCtx
+ mov [rsp], rcx
+ call Bs3RegCtxRestore
+.panic:
+ hlt
+ jmp .panic
+BS3_PROC_END Bs3Trap64GenericCommon
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Disable.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Disable.asm
new file mode 100644
index 00000000..f4caca1d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Disable.asm
@@ -0,0 +1,115 @@
+; $Id: bs3-cmn-A20Disable.asm $
+;; @file
+; BS3Kit - Bs3A20Disable.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3KbdWait
+BS3_EXTERN_CMN Bs3KbdRead
+BS3_EXTERN_CMN Bs3KbdWrite
+
+
+;;
+; Disables the A20 gate.
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20Disable, BS3_PBC_HYBRID_0_ARGS
+ ; Must call both because they may be ORed together on real HW.
+BONLY64 sub rsp, 20h
+ call BS3_CMN_NM(Bs3A20DisableViaKbd)
+ call BS3_CMN_NM(Bs3A20DisableViaPortA)
+BONLY64 add rsp, 20h
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20Disable
+
+
+;;
+; Disables the A20 gate via control port A (PS/2 style).
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20DisableViaPortA, BS3_PBC_HYBRID_0_ARGS
+ push xAX
+
+ ; Use Control port A, assuming a PS/2 style system.
+ in al, 092h
+ test al, 02h
+ jz .done ; avoid trouble writing back the same value.
+ and al, 0fdh ; disable the A20 gate.
+ out 092h, al
+
+.done:
+ pop xAX
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20DisableViaPortA
+
+
+;;
+; Disables the A20 gate via the keyboard controller.
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20DisableViaKbd, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+ push xAX
+ pushf
+ cli
+BONLY64 sub rsp, 20h
+
+ call Bs3KbdWait
+ push 0d0h ; KBD_CCMD_READ_OUTPORT
+ call Bs3KbdRead
+
+ and al, 0fdh ; ~2
+ push xAX
+ push 0d1h ; KBD_CCMD_WRITE_OUTPORT
+ call Bs3KbdWrite
+
+ add xSP, xCB*3 ; Clean up both the above calls.
+
+ mov al, 0ffh ; KBD_CMD_RESET
+ out 64h, al
+ call Bs3KbdWait
+
+BONLY64 add rsp, 20h
+ popf
+ pop xAX
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20DisableViaKbd
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Enable.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Enable.asm
new file mode 100644
index 00000000..bd4406d1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-A20Enable.asm
@@ -0,0 +1,122 @@
+; $Id: bs3-cmn-A20Enable.asm $
+;; @file
+; BS3Kit - Bs3A20Enable.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3KbdWait
+BS3_EXTERN_CMN Bs3KbdRead
+BS3_EXTERN_CMN Bs3KbdWrite
+
+
+;;
+; Enables the A20 gate.
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20Enable, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+BONLY64 sub rsp, 20h
+
+ call BS3_CMN_NM(Bs3A20EnableViaPortA)
+;; @todo real 286 support
+; call BS3_CMN_NM(Bs3A20EnableViaKbd)
+
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20Enable
+
+
+;;
+; Enables the A20 gate via the keyboard controller.
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20EnableViaKbd, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+ push xAX
+ pushf
+ cli
+BONLY64 sub rsp, 20h
+
+ call Bs3KbdWait
+ push 0d0h ; KBD_CCMD_READ_OUTPORT
+ call Bs3KbdRead
+
+ or al, 002h
+ push xAX
+ push 0d1h ; KBD_CCMD_WRITE_OUTPORT
+ call Bs3KbdWrite
+
+ add xSP, xCB*3 ; both the above calls
+
+ mov al, 0ffh ; KBD_CMD_RESET
+ out 64h, al
+ call Bs3KbdWait
+
+BONLY64 add rsp, 20h
+ popf
+ pop xAX
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20EnableViaKbd
+
+
+;;
+; Enables the A20 gate via control port A (PS/2 style).
+;
+; @uses Nothing.
+;
+BS3_PROC_BEGIN_CMN Bs3A20EnableViaPortA, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+ push xAX
+
+ ; Use Control port A, assuming a PS/2 style system.
+ in al, 092h
+ test al, 02h
+ jnz .done ; avoid trouble writing back the same value.
+ or al, 2 ; enable the A20 gate.
+ out 092h, al
+
+.done:
+ pop xAX
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3A20EnableViaPortA
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm
new file mode 100644
index 00000000..d12f0f4b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm
@@ -0,0 +1,88 @@
+; $Id: bs3-cmn-ConvertRMStackToP16UsingCxReturnToAx.asm $
+;; @file
+; BS3Kit - Bs3ConvertRMStackToP16UsingCxReturnToAx.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%if TMPL_BITS != 16
+ %error 16-bit only!
+%endif
+
+;;
+; An internal helper for converting a real-mode stack into a 16-bit protected
+; mode stack.
+;
+; This is used by the mode switchers that ends up in 16-bit mode. It is
+; assumed that we're in ring-0.
+;
+; @param ax The return address.
+;
+; @uses cx, ss, esp
+;
+BS3_PROC_BEGIN_CMN Bs3ConvertRMStackToP16UsingCxReturnToAx, BS3_PBC_NEAR
+
+ ;
+ ; Check if it looks like the normal stack, if use BS3_SEL_R0_SS16.
+ ;
+ mov cx, ss
+ cmp cx, 0
+ jne .stack_tiled
+ mov cx, BS3_SEL_R0_SS16
+ mov ss, cx
+ jmp ax
+
+ ;
+ ; Some custom stack address, just use the 16-bit tiled mappings
+ ;
+.stack_tiled:
+int3 ; debug this, shouldn't happen yet. Bs3EnteredMode_xxx isn't prepared.
+ shl cx, 4
+ add sp, cx
+ mov cx, ss
+ jc .stack_carry
+ shr cx, 12
+ jmp .stack_join_up_again
+.stack_carry:
+ shr cx, 12
+ inc cx
+.stack_join_up_again:
+ shl cx, 3
+ adc cx, BS3_SEL_TILED
+ mov ss, cx
+ jmp ax
+
+BS3_PROC_END_CMN Bs3ConvertRMStackToP16UsingCxReturnToAx
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-CpuDetectData.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-CpuDetectData.c
new file mode 100644
index 00000000..11cc2b7a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-CpuDetectData.c
@@ -0,0 +1,54 @@
+/* $Id: bs3-cmn-CpuDetectData.c $ */
+/** @file
+ * BS3Kit - Detected CPU data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+
+uint16_t g_uBs3CpuDetected = BS3CPU_TYPE_MASK | BS3CPU_F_CPUID | BS3CPU_F_CPUID_EXT_LEAVES
+ | BS3CPU_F_PAE | BS3CPU_F_PSE | BS3CPU_F_LONG_MODE;
+
+#endif /* ARCH_BITS == 16 */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxAlloc.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxAlloc.c
new file mode 100644
index 00000000..2f648134
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxAlloc.c
@@ -0,0 +1,54 @@
+/* $Id: bs3-cmn-ExtCtxAlloc.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxAlloc
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxAlloc
+BS3_CMN_DEF(PBS3EXTCTX, Bs3ExtCtxAlloc,(BS3MEMKIND enmKind))
+{
+ uint64_t fFlags;
+ uint16_t cbExtCtx = Bs3ExtCtxGetSize(&fFlags);
+ PBS3EXTCTX pExtCtx = (PBS3EXTCTX)Bs3MemAlloc(enmKind, cbExtCtx);
+ if (pExtCtx)
+ return Bs3ExtCtxInit(pExtCtx, cbExtCtx, fFlags);
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxCopy.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxCopy.c
new file mode 100644
index 00000000..4eff8574
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxCopy.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-ExtCtxCopy.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxCopy
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3ExtCtxCopy
+BS3_CMN_DEF(PBS3EXTCTX, Bs3ExtCtxCopy,(PBS3EXTCTX pDst, PCBS3EXTCTX pSrc))
+{
+ BS3_ASSERT(pDst->cb == pSrc->cb && pDst->enmMethod == pSrc->enmMethod && pDst->fXcr0Nominal == pSrc->fXcr0Nominal);
+ Bs3MemCpy(&pDst->Ctx, &pSrc->Ctx, pDst->cb - RT_UOFFSETOF(BS3EXTCTX, Ctx));
+ pDst->fXcr0Saved = pSrc->fXcr0Saved;
+ return pDst;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxFree.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxFree.c
new file mode 100644
index 00000000..fcc35c78
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxFree.c
@@ -0,0 +1,56 @@
+/* $Id: bs3-cmn-ExtCtxFree.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxFree
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxFree
+BS3_CMN_DEF(void, Bs3ExtCtxFree,(PBS3EXTCTX pExtCtx))
+{
+ if (pExtCtx)
+ {
+ if (pExtCtx->u16Magic == BS3EXTCTX_MAGIC)
+ {
+ pExtCtx->u16Magic = ~BS3EXTCTX_MAGIC;
+ Bs3MemFree(pExtCtx, pExtCtx->cb);
+ }
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetAbridgedFtw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetAbridgedFtw.c
new file mode 100644
index 00000000..6092f88b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetAbridgedFtw.c
@@ -0,0 +1,71 @@
+/* $Id: bs3-cmn-ExtCtxGetAbridgedFtw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetAbridgedFtw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetAbridgedFtw
+BS3_CMN_DEF(uint16_t, Bs3ExtCtxGetAbridgedFtw,(PCBS3EXTCTX pExtCtx))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FTW, BS3EXTCTX, Ctx.x.x87.FTW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ return pExtCtx->Ctx.x87.FTW;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ {
+ /* iemFpuCompressFtw: */
+ uint16_t u16FullFtw = pExtCtx->Ctx.Ancient.FTW;
+ uint8_t u8Ftw = 0;
+ unsigned i;
+ for (i = 0; i < 8; i++)
+ {
+ if ((u16FullFtw & 3) != 3 /*empty*/)
+ u8Ftw |= RT_BIT(i);
+ u16FullFtw >>= 2;
+ }
+ return u8Ftw;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFcw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFcw.c
new file mode 100644
index 00000000..efbbf2ec
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFcw.c
@@ -0,0 +1,59 @@
+/* $Id: bs3-cmn-ExtCtxGetFcw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetFcw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetFcw
+BS3_CMN_DEF(uint16_t, Bs3ExtCtxGetFcw,(PCBS3EXTCTX pExtCtx))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FCW, BS3EXTCTX, Ctx.x.x87.FCW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ return pExtCtx->Ctx.x87.FCW;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ return pExtCtx->Ctx.Ancient.FCW;
+ }
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFsw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFsw.c
new file mode 100644
index 00000000..a7ebff46
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetFsw.c
@@ -0,0 +1,59 @@
+/* $Id: bs3-cmn-ExtCtxGetFsw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetFsw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetFsw
+BS3_CMN_DEF(uint16_t, Bs3ExtCtxGetFsw,(PCBS3EXTCTX pExtCtx))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FSW, BS3EXTCTX, Ctx.x.x87.FSW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ return pExtCtx->Ctx.x87.FSW;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ return pExtCtx->Ctx.Ancient.FSW;
+ }
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMm.c
new file mode 100644
index 00000000..7ad104a6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMm.c
@@ -0,0 +1,60 @@
+/* $Id: bs3-cmn-ExtCtxGetMm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetMm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetMm
+BS3_CMN_DEF(uint64_t, Bs3ExtCtxGetMm,(PCBS3EXTCTX pExtCtx, uint8_t iReg))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.aRegs, BS3EXTCTX, Ctx.x.x87.aRegs);
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aRegs))
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ return pExtCtx->Ctx.x87.aRegs[iReg].mmx;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ return pExtCtx->Ctx.Ancient.regs[iReg].mmx;
+ }
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsr.c
new file mode 100644
index 00000000..72c78be5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsr.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-ExtCtxGetMxCsr.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetMxCsr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetMxCsr
+BS3_CMN_DEF(uint32_t, Bs3ExtCtxGetMxCsr,(PCBS3EXTCTX pExtCtx))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.MXCSR, BS3EXTCTX, Ctx.x.x87.MXCSR);
+ if ( pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE
+ || pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ return pExtCtx->Ctx.x87.MXCSR;
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsrMask.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsrMask.c
new file mode 100644
index 00000000..ead905d2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetMxCsrMask.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-ExtCtxGetMxCsrMask.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetMxCsrMask
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetMxCsrMask
+BS3_CMN_DEF(uint32_t, Bs3ExtCtxGetMxCsrMask,(PCBS3EXTCTX pExtCtx))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.MXCSR_MASK, BS3EXTCTX, Ctx.x.x87.MXCSR_MASK);
+ if ( pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE
+ || pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ return pExtCtx->Ctx.x87.MXCSR_MASK;
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetSize.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetSize.c
new file mode 100644
index 00000000..69b9bf46
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetSize.c
@@ -0,0 +1,69 @@
+/* $Id: bs3-cmn-ExtCtxGetSize.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetSize
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3ExtCtxGetSize
+BS3_CMN_DEF(uint16_t, Bs3ExtCtxGetSize,(uint64_t BS3_FAR *pfFlags))
+{
+ uint32_t fEcx, fEdx;
+ *pfFlags = 0;
+
+ ASMCpuIdExSlow(1, 0, 0, 0, NULL, NULL, &fEcx, &fEdx);
+#if 1 /* To disable xsave/xrstor till IEM groks it... */
+ if (fEcx & X86_CPUID_FEATURE_ECX_XSAVE)
+ {
+ uint32_t fEax;
+ ASMCpuIdExSlow(13, 0, 0, 0, &fEax, NULL, &fEcx, &fEdx);
+ if ( fEcx >= sizeof(X86FXSTATE) + sizeof(X86XSAVEHDR)
+ && fEcx < _32K)
+ {
+ *pfFlags = fEax | ((uint64_t)fEdx << 32);
+ return RT_UOFFSETOF(BS3EXTCTX, Ctx) + RT_ALIGN(fEcx, 256);
+ }
+ }
+#endif
+ if (fEdx & X86_CPUID_FEATURE_EDX_FXSR)
+ return RT_UOFFSETOF(BS3EXTCTX, Ctx) + sizeof(X86FXSTATE);
+ return RT_UOFFSETOF(BS3EXTCTX, Ctx) + sizeof(X86FPUSTATE);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetXmm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetXmm.c
new file mode 100644
index 00000000..99b37e97
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetXmm.c
@@ -0,0 +1,64 @@
+/* $Id: bs3-cmn-ExtCtxGetXmm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetXmm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetXmm
+BS3_CMN_DEF(PRTUINT128U, Bs3ExtCtxGetXmm,(PCBS3EXTCTX pExtCtx, uint8_t iReg, PRTUINT128U pValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.aXMM, BS3EXTCTX, Ctx.x.x87.aXMM);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aXMM))
+ {
+ pValue->u = pExtCtx->Ctx.x87.aXMM[iReg].xmm;
+ return pValue;
+ }
+ break;
+ }
+
+ pValue->au64[0] = 0;
+ pValue->au64[1] = 0;
+ return pValue;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetYmm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetYmm.c
new file mode 100644
index 00000000..10e1066a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxGetYmm.c
@@ -0,0 +1,70 @@
+/* $Id: bs3-cmn-ExtCtxGetYmm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxGetYmm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxGetYmm
+BS3_CMN_DEF(PRTUINT256U, Bs3ExtCtxGetYmm,(PCBS3EXTCTX pExtCtx, uint8_t iReg, PRTUINT256U pValue))
+{
+ pValue->au128[0].au64[0] = 0;
+ pValue->au128[0].au64[1] = 0;
+ pValue->au128[1].au64[0] = 0;
+ pValue->au128[1].au64[1] = 0;
+
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aXMM))
+ pValue->au128[0] = pExtCtx->Ctx.x87.aXMM[iReg].uXmm;
+ break;
+
+ case BS3EXTCTXMETHOD_XSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x.x87.aXMM))
+ {
+ pValue->au128[0] = pExtCtx->Ctx.x87.aXMM[iReg].uXmm;
+ if (pExtCtx->fXcr0Nominal & XSAVE_C_YMM)
+ pValue->au128[1] = pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].uXmm;
+ }
+ break;
+ }
+ return pValue;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxInit.c
new file mode 100644
index 00000000..944f6d45
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxInit.c
@@ -0,0 +1,84 @@
+/* $Id: bs3-cmn-ExtCtxInit.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3ExtCtxInit
+BS3_CMN_DEF(PBS3EXTCTX, Bs3ExtCtxInit,(PBS3EXTCTX pExtCtx, uint16_t cbExtCtx, uint64_t fFlags))
+{
+ Bs3MemSet(pExtCtx, 0, cbExtCtx);
+
+ if (cbExtCtx >= RT_UOFFSETOF(BS3EXTCTX, Ctx) + sizeof(X86FXSTATE) + sizeof(X86XSAVEHDR))
+ {
+ BS3_ASSERT(fFlags & XSAVE_C_X87);
+ pExtCtx->enmMethod = BS3EXTCTXMETHOD_XSAVE;
+ pExtCtx->Ctx.x.Hdr.bmXState = fFlags;
+
+ /* Setting bit 6 (0x40) here as it kept sneaking in when loading/saving state in 16-bit and v8086 mode. */
+ pExtCtx->Ctx.x.x87.FCW = X86_FCW_RC_NEAREST | X86_FCW_PC_64 /* go figure:*/ | RT_BIT(6);
+ pExtCtx->Ctx.x.x87.MXCSR = X86_MXCSR_RC_NEAREST;
+ pExtCtx->Ctx.x.x87.MXCSR_MASK = 0xffff;
+ }
+ else if (cbExtCtx >= RT_UOFFSETOF(BS3EXTCTX, Ctx) + sizeof(X86FXSTATE))
+ {
+ BS3_ASSERT(fFlags == 0);
+ pExtCtx->enmMethod = BS3EXTCTXMETHOD_FXSAVE;
+ pExtCtx->Ctx.x87.FCW = X86_FCW_RC_NEAREST | X86_FCW_PC_64 /* go figure:*/ | RT_BIT(6);
+ pExtCtx->Ctx.x87.MXCSR = X86_MXCSR_RC_NEAREST;
+ pExtCtx->Ctx.x87.MXCSR_MASK = 0xffff;
+ }
+ else
+ {
+ BS3_ASSERT(fFlags == 0);
+ BS3_ASSERT(cbExtCtx >= RT_UOFFSETOF(BS3EXTCTX, Ctx) + sizeof(X86FPUSTATE));
+ pExtCtx->enmMethod = BS3EXTCTXMETHOD_ANCIENT;
+ pExtCtx->Ctx.Ancient.FCW = X86_FCW_RC_NEAREST | X86_FCW_PC_64 /* go figure:*/ | RT_BIT(6);
+ pExtCtx->Ctx.Ancient.FTW = UINT16_MAX; /* all registers empty */
+ }
+
+ pExtCtx->cb = cbExtCtx;
+ pExtCtx->u16Magic = BS3EXTCTX_MAGIC;
+ pExtCtx->fXcr0Nominal = fFlags;
+ pExtCtx->fXcr0Saved = fFlags;
+ return pExtCtx;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestore.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestore.asm
new file mode 100644
index 00000000..a24dedd4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestore.asm
@@ -0,0 +1,155 @@
+; $Id: bs3-cmn-ExtCtxRestore.asm $
+;; @file
+; BS3Kit - Bs3ExtCtxRestore.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+extern BS3_CMN_NM(Bs3RegSetXcr0)
+
+
+;;
+; Restores the extended CPU context (FPU, SSE, AVX, ++).
+;
+; @param pExtCtx
+;
+BS3_PROC_BEGIN_CMN Bs3ExtCtxRestore, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDX
+ push xBX
+BONLY16 push es
+
+%if ARCH_BITS == 16
+ les bx, [xBP + xCB + cbCurRetAddr]
+ mov al, [es:bx + BS3EXTCTX.enmMethod]
+ cmp al, BS3EXTCTXMETHOD_XSAVE
+ je .do_16_xsave
+ cmp al, BS3EXTCTXMETHOD_FXSAVE
+ je .do_16_fxsave
+ cmp al, BS3EXTCTXMETHOD_ANCIENT
+ je .do_16_ancient
+ int3
+
+.do_16_ancient:
+ frstor [es:bx + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_16_fxsave:
+ fxrstor [es:bx + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_16_xsave:
+ push dword [es:bx + BS3EXTCTX.fXcr0Nominal + 4]
+ push dword [es:bx + BS3EXTCTX.fXcr0Nominal]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [es:bx + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [es:bx + BS3EXTCTX.fXcr0Nominal + 4]
+ xrstor [es:bx + BS3EXTCTX.Ctx]
+
+ push dword [es:bx + BS3EXTCTX.fXcr0Saved + 4]
+ push dword [es:bx + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 4 * 2 * 2 ; clean up both calls
+ ;jmp .return
+
+%else
+BONLY32 mov ebx, [xBP + xCB + cbCurRetAddr]
+BONLY64 mov rbx, rcx
+
+ mov al, [xBX + BS3EXTCTX.enmMethod]
+ cmp al, BS3EXTCTXMETHOD_XSAVE
+ je .do_xsave
+ cmp al, BS3EXTCTXMETHOD_FXSAVE
+ je .do_fxsave
+ cmp al, BS3EXTCTXMETHOD_ANCIENT
+ je .do_ancient
+ int3
+
+.do_ancient:
+ frstor [xBX + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_fxsave:
+BONLY32 fxrstor [xBX + BS3EXTCTX.Ctx]
+BONLY64 fxrstor64 [xBX + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_xsave:
+ %if ARCH_BITS == 32
+ push dword [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ push dword [xBX + BS3EXTCTX.fXcr0Nominal]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [xBX + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ xrstor [xBX + BS3EXTCTX.Ctx]
+
+ push dword [xBX + BS3EXTCTX.fXcr0Saved + 4]
+ push dword [xBX + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 4 * 2 * 2 ; clean up both calls
+ %else
+ mov rcx, [xBX + BS3EXTCTX.fXcr0Nominal]
+ push rcx ; just for reserving parameter dumping space needed by Bs3RegSetXcr0
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [xBX + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ xrstor64 [xBX + BS3EXTCTX.Ctx]
+
+ mov rcx, [xBX + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 8 ; clean up parameter space
+ ;jmp .return
+ %endif
+%endif
+
+.return:
+BONLY16 pop es
+ pop xBX
+ pop sDX
+ pop sCX
+ pop sAX
+ mov xSP, xBP
+ pop xBP
+ ret
+BS3_PROC_END_CMN Bs3ExtCtxRestore
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestoreEx.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestoreEx.asm
new file mode 100644
index 00000000..46f86ced
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxRestoreEx.asm
@@ -0,0 +1,136 @@
+; $Id: bs3-cmn-ExtCtxRestoreEx.asm $
+;; @file
+; BS3Kit - Bs3ExtCtxRestoreEx.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%if ARCH_BITS != 64
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+
+BS3_BEGIN_TEXT64
+extern _Bs3ExtCtxRestore_c64
+ %if ARCH_BITS == 16
+extern BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+extern _Bs3SwitchTo16Bit_c64
+ %else
+extern _Bs3SwitchTo32Bit_c64
+ %endif
+
+TMPL_BEGIN_TEXT
+extern BS3_CMN_NM(Bs3SwitchTo64Bit)
+ %if ARCH_BITS == 16
+extern BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+ %endif
+%endif
+
+extern BS3_CMN_NM(Bs3ExtCtxRestore)
+
+
+
+;;
+; Restores the extended CPU context (FPU, SSE, AVX, ++), full 64-bit
+; when in long mode.
+;
+; @param pExtCtx
+;
+BS3_PROC_BEGIN_CMN Bs3ExtCtxRestoreEx, BS3_PBC_NEAR
+%if ARCH_BITS == 64
+ jmp BS3_CMN_NM(Bs3ExtCtxRestore)
+%else
+ push xBP
+ mov xBP, xSP
+ push sAX
+
+ ;
+ ; Check if we're in long mode.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .in_long_mode
+
+ ;
+ ; Not in long mode, so do normal restore.
+ ;
+ pop sAX
+ leave
+ jmp BS3_CMN_NM(Bs3ExtCtxRestore)
+
+ ;
+ ; Switch to 64-bit to do the restoring so we can restore 64-bit only state.
+ ;
+.in_long_mode:
+ push sCX
+BONLY16 push sDX
+
+ ; Load ecx with the flat pExtCtx address.
+ mov ecx, [xBP + xCB + cbCurRetAddr]
+
+ %if ARCH_BITS == 16
+ push ecx
+ call BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+ mov ecx, edx
+ shl ecx, 16
+ mov cx, ax
+ %endif
+
+ ; Switch to 64-bit mode.
+ call BS3_CMN_NM(Bs3SwitchTo64Bit)
+ BITS 64
+
+ ; Do the restore.
+ sub rsp, 20h
+ call _Bs3ExtCtxRestore_c64
+ add rsp, 20h
+
+ ; Switch back to the original mode.
+ %if ARCH_BITS == 16
+ call _Bs3SwitchTo16Bit_c64
+ %else
+ call _Bs3SwitchTo32Bit_c64
+ %endif
+ BITS ARCH_BITS
+
+ ; Restore context and return.
+BONLY16 pop sDX
+ pop sCX
+
+ pop sAX
+ leave
+ ret
+%endif
+BS3_PROC_END_CMN Bs3ExtCtxRestoreEx
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSave.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSave.asm
new file mode 100644
index 00000000..e6cbbd67
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSave.asm
@@ -0,0 +1,166 @@
+; $Id: bs3-cmn-ExtCtxSave.asm $
+;; @file
+; BS3Kit - Bs3ExtCtxSave.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+extern BS3_CMN_NM(Bs3RegSetXcr0)
+
+;;
+; Saves the extended CPU context (FPU, SSE, AVX, ++).
+;
+; @param pExtCtx
+;
+BS3_PROC_BEGIN_CMN Bs3ExtCtxSave, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDX
+ push xBX
+BONLY16 push es
+
+%if ARCH_BITS == 16
+ les bx, [xBP + xCB + cbCurRetAddr]
+ mov al, [es:bx + BS3EXTCTX.enmMethod]
+ cmp al, BS3EXTCTXMETHOD_XSAVE
+ je .do_16_xsave
+ cmp al, BS3EXTCTXMETHOD_FXSAVE
+ je .do_16_fxsave
+ cmp al, BS3EXTCTXMETHOD_ANCIENT
+ je .do_16_ancient
+ int3
+
+.do_16_ancient:
+ fnsave [es:bx + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_16_fxsave:
+ fxsave [es:bx + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_16_xsave:
+ ; xgetbv can be used in any ring!
+ xor ecx, ecx
+ xgetbv
+ mov [es:bx + BS3EXTCTX.fXcr0Saved], eax
+ mov [es:bx + BS3EXTCTX.fXcr0Saved + 4], edx
+
+ push dword [es:bx + BS3EXTCTX.fXcr0Nominal + 4]
+ push dword [es:bx + BS3EXTCTX.fXcr0Nominal]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [es:bx + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [es:bx + BS3EXTCTX.fXcr0Nominal + 4]
+ xsave [es:bx + BS3EXTCTX.Ctx]
+
+ push dword [es:bx + BS3EXTCTX.fXcr0Saved + 4]
+ push dword [es:bx + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 4 * 2 * 2 ; clean up both calls
+ ;jmp .return
+
+%else
+BONLY32 mov ebx, [xBP + xCB + cbCurRetAddr]
+BONLY64 mov rbx, rcx
+
+ mov al, [xBX + BS3EXTCTX.enmMethod]
+ cmp al, BS3EXTCTXMETHOD_XSAVE
+ je .do_xsave
+ cmp al, BS3EXTCTXMETHOD_FXSAVE
+ je .do_fxsave
+ cmp al, BS3EXTCTXMETHOD_ANCIENT
+ je .do_ancient
+ int3
+
+.do_ancient:
+ fnsave [xBX + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_fxsave:
+BONLY32 fxsave [xBX + BS3EXTCTX.Ctx]
+BONLY64 fxsave64 [xBX + BS3EXTCTX.Ctx]
+ jmp .return
+
+.do_xsave:
+ xor ecx, ecx
+ xgetbv
+ mov [xBX + BS3EXTCTX.fXcr0Saved], eax
+ mov [xBX + BS3EXTCTX.fXcr0Saved + 4], edx
+
+ %if ARCH_BITS == 32
+ push dword [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ push dword [xBX + BS3EXTCTX.fXcr0Nominal]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [xBX + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ xsave [xBX + BS3EXTCTX.Ctx]
+
+ push dword [xBX + BS3EXTCTX.fXcr0Saved + 4]
+ push dword [xBX + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 4 * 2 * 2 ; clean up both calls
+ %else
+ mov rcx, [xBX + BS3EXTCTX.fXcr0Nominal]
+ push rcx ; only to reserve necessary stack space for the Bs3RegSetXcr0 param dump.
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ mov eax, [xBX + BS3EXTCTX.fXcr0Nominal]
+ mov edx, [xBX + BS3EXTCTX.fXcr0Nominal + 4]
+ xsave64 [xBX + BS3EXTCTX.Ctx]
+
+ mov rcx, [xBX + BS3EXTCTX.fXcr0Saved]
+ call BS3_CMN_NM(Bs3RegSetXcr0)
+
+ add xSP, 8h ; clean up
+ %endif
+ ;jmp .return
+
+%endif
+
+.return:
+BONLY16 pop es
+ pop xBX
+ pop sDX
+ pop sCX
+ pop sAX
+ mov xSP, xBP
+ pop xBP
+ ret
+BS3_PROC_END_CMN Bs3ExtCtxSave
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSaveEx.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSaveEx.asm
new file mode 100644
index 00000000..ca1cbe9f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSaveEx.asm
@@ -0,0 +1,136 @@
+; $Id: bs3-cmn-ExtCtxSaveEx.asm $
+;; @file
+; BS3Kit - Bs3ExtCtxSaveEx.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%if ARCH_BITS != 64
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+
+BS3_BEGIN_TEXT64
+extern _Bs3ExtCtxSave_c64
+ %if ARCH_BITS == 16
+extern BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+extern _Bs3SwitchTo16Bit_c64
+ %else
+extern _Bs3SwitchTo32Bit_c64
+ %endif
+
+TMPL_BEGIN_TEXT
+extern BS3_CMN_NM(Bs3SwitchTo64Bit)
+ %if ARCH_BITS == 16
+extern BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+ %endif
+%endif
+
+extern BS3_CMN_NM(Bs3ExtCtxSave)
+
+
+
+;;
+; Saves the extended CPU context (FPU, SSE, AVX, ++), full 64-bit
+; when in long mode.
+;
+; @param pExtCtx
+;
+BS3_PROC_BEGIN_CMN Bs3ExtCtxSaveEx, BS3_PBC_NEAR
+%if ARCH_BITS == 64
+ jmp BS3_CMN_NM(Bs3ExtCtxSave)
+%else
+ push xBP
+ mov xBP, xSP
+ push sAX
+
+ ;
+ ; Check if we're in long mode.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .in_long_mode
+
+ ;
+ ; Not in long mode, so do normal save.
+ ;
+ pop sAX
+ leave
+ jmp BS3_CMN_NM(Bs3ExtCtxSave)
+
+ ;
+ ; Switch to 64-bit to do the restoring so we can save the 64-bit only state.
+ ;
+.in_long_mode:
+ push sCX
+BONLY16 push sDX
+
+ ; Load ecx with the flat pExtCtx address.
+ mov ecx, [xBP + xCB + cbCurRetAddr]
+
+ %if ARCH_BITS == 16
+ push ecx
+ call BS3_CMN_NM(Bs3SelProtFar16DataToFlat)
+ mov ecx, edx
+ shl ecx, 16
+ mov cx, ax
+ %endif
+
+ ; Switch to 64-bit mode.
+ call BS3_CMN_NM(Bs3SwitchTo64Bit)
+ BITS 64
+
+ ; Do the save.
+ sub rsp, 20h
+ call _Bs3ExtCtxSave_c64
+ add rsp, 20h
+
+ ; Switch back to the original mode.
+ %if ARCH_BITS == 16
+ call _Bs3SwitchTo16Bit_c64
+ %else
+ call _Bs3SwitchTo32Bit_c64
+ %endif
+ BITS ARCH_BITS
+
+ ; Save context and return.
+BONLY16 pop sDX
+ pop sCX
+
+ pop sAX
+ leave
+ ret
+%endif
+BS3_PROC_END_CMN Bs3ExtCtxSaveEx
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetAbridgedFtw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetAbridgedFtw.c
new file mode 100644
index 00000000..263817d0
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetAbridgedFtw.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-ExtCtxSetAbridgedFtw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetAbridgedFtw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetAbridgedFtw
+BS3_CMN_DEF(bool, Bs3ExtCtxSetAbridgedFtw,(PBS3EXTCTX pExtCtx, uint16_t uValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FTW, BS3EXTCTX, Ctx.x.x87.FTW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ pExtCtx->Ctx.x87.FTW = uValue;
+ return true;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ /* Could do iemFpuCalcFullFtw here, but too much work for now... */
+ break;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFcw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFcw.c
new file mode 100644
index 00000000..a51259be
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFcw.c
@@ -0,0 +1,60 @@
+/* $Id: bs3-cmn-ExtCtxSetFcw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetFcw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetFcw
+BS3_CMN_DEF(void, Bs3ExtCtxSetFcw,(PBS3EXTCTX pExtCtx, uint16_t uValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FCW, BS3EXTCTX, Ctx.x.x87.FCW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ pExtCtx->Ctx.x87.FCW = uValue;
+ break;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ pExtCtx->Ctx.Ancient.FCW = uValue;
+ break;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFsw.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFsw.c
new file mode 100644
index 00000000..e49e9781
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetFsw.c
@@ -0,0 +1,60 @@
+/* $Id: bs3-cmn-ExtCtxSetFsw.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetFsw
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetFsw
+BS3_CMN_DEF(void, Bs3ExtCtxSetFsw,(PBS3EXTCTX pExtCtx, uint16_t uValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.FSW, BS3EXTCTX, Ctx.x.x87.FSW);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ pExtCtx->Ctx.x87.FSW = uValue;
+ break;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ pExtCtx->Ctx.Ancient.FSW = uValue;
+ break;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMm.c
new file mode 100644
index 00000000..17dc583a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMm.c
@@ -0,0 +1,66 @@
+/* $Id: bs3-cmn-ExtCtxSetMm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetMm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetMm
+BS3_CMN_DEF(bool, Bs3ExtCtxSetMm,(PBS3EXTCTX pExtCtx, uint8_t iReg, uint64_t uValue, BS3EXTCTXTOPMM enmTop))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.aRegs, BS3EXTCTX, Ctx.x.x87.aRegs);
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aRegs))
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ pExtCtx->Ctx.x87.aRegs[iReg].mmx = uValue;
+ if (enmTop == BS3EXTCTXTOPMM_SET || enmTop == BS3EXTCTXTOPMM_ZERO)
+ pExtCtx->Ctx.x87.aRegs[iReg].au16[4] = enmTop == BS3EXTCTXTOPMM_SET ? UINT16_MAX : 0;
+ return true;
+
+ case BS3EXTCTXMETHOD_ANCIENT:
+ pExtCtx->Ctx.Ancient.regs[iReg].mmx = uValue;
+ if (enmTop == BS3EXTCTXTOPMM_SET || enmTop == BS3EXTCTXTOPMM_ZERO)
+ pExtCtx->Ctx.Ancient.regs[iReg].au16[4] = enmTop == BS3EXTCTXTOPMM_SET ? UINT16_MAX : 0;
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsr.c
new file mode 100644
index 00000000..15184a1b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsr.c
@@ -0,0 +1,56 @@
+/* $Id: bs3-cmn-ExtCtxSetMxCsr.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetMxCsr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetMxCsr
+BS3_CMN_DEF(bool, Bs3ExtCtxSetMxCsr,(PBS3EXTCTX pExtCtx, uint32_t uValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.MXCSR, BS3EXTCTX, Ctx.x.x87.MXCSR);
+ if ( pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE
+ || pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ {
+ pExtCtx->Ctx.x87.MXCSR = uValue;
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsrMask.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsrMask.c
new file mode 100644
index 00000000..64079d07
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetMxCsrMask.c
@@ -0,0 +1,56 @@
+/* $Id: bs3-cmn-ExtCtxSetMxCsrMask.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetMxCsrMask
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetMxCsrMask
+BS3_CMN_DEF(bool, Bs3ExtCtxSetMxCsrMask,(PBS3EXTCTX pExtCtx, uint32_t uValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.MXCSR, BS3EXTCTX, Ctx.x.x87.MXCSR);
+ if ( pExtCtx->enmMethod == BS3EXTCTXMETHOD_FXSAVE
+ || pExtCtx->enmMethod == BS3EXTCTXMETHOD_XSAVE)
+ {
+ pExtCtx->Ctx.x87.MXCSR_MASK = uValue;
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetXmm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetXmm.c
new file mode 100644
index 00000000..d758cc91
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetXmm.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-ExtCtxSetXmm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetXmm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetXmm
+BS3_CMN_DEF(bool, Bs3ExtCtxSetXmm,(PBS3EXTCTX pExtCtx, uint8_t iReg, PCRTUINT128U pValue))
+{
+ AssertCompileMembersAtSameOffset(BS3EXTCTX, Ctx.x87.aXMM, BS3EXTCTX, Ctx.x.x87.aXMM);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ case BS3EXTCTXMETHOD_XSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aXMM))
+ {
+ pExtCtx->Ctx.x87.aXMM[iReg].xmm = pValue->u;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetYmm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetYmm.c
new file mode 100644
index 00000000..7ff2732f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-ExtCtxSetYmm.c
@@ -0,0 +1,79 @@
+/* $Id: bs3-cmn-ExtCtxSetYmm.c $ */
+/** @file
+ * BS3Kit - Bs3ExtCtxSetYmm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3ExtCtxSetYmm
+BS3_CMN_DEF(bool, Bs3ExtCtxSetYmm,(PBS3EXTCTX pExtCtx, uint8_t iReg, PCRTUINT256U pValue, uint8_t cbValue))
+{
+ BS3_ASSERT(cbValue == 16 || cbValue == 32);
+ switch (pExtCtx->enmMethod)
+ {
+ case BS3EXTCTXMETHOD_FXSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x87.aXMM))
+ {
+ pExtCtx->Ctx.x87.aXMM[iReg].uXmm = pValue->DQWords.dqw0;
+ return true;
+ }
+ break;
+
+ case BS3EXTCTXMETHOD_XSAVE:
+ if (iReg < RT_ELEMENTS(pExtCtx->Ctx.x.x87.aXMM))
+ {
+ pExtCtx->Ctx.x87.aXMM[iReg].uXmm = pValue->DQWords.dqw0;
+ if (pExtCtx->fXcr0Nominal & XSAVE_C_YMM)
+ {
+ if (cbValue >= 32)
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].uXmm = pValue->DQWords.dqw1;
+ else
+ {
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[0] = 0;
+ pExtCtx->Ctx.x.u.YmmHi.aYmmHi[iReg].au64[1] = 0;
+ }
+ /** @todo zero high ZMM part. */
+ }
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetCpuVendor.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetCpuVendor.c
new file mode 100644
index 00000000..982108cb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetCpuVendor.c
@@ -0,0 +1,63 @@
+/* $Id: bs3-cmn-GetCpuVendor.c $ */
+/** @file
+ * BS3Kit - Bs3GetCpuVendor
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3GetCpuVendor
+BS3_CMN_DEF(BS3CPUVENDOR, Bs3GetCpuVendor,(void))
+{
+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
+ {
+ uint32_t uEbx, uEcx, uEdx;
+ ASMCpuIdExSlow(0, 0, 0, 0, NULL, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsIntelCpu(uEbx, uEcx, uEdx))
+ return BS3CPUVENDOR_INTEL;
+ if (RTX86IsAmdCpu(uEbx, uEcx, uEdx))
+ return BS3CPUVENDOR_AMD;
+ if (RTX86IsViaCentaurCpu(uEbx, uEcx, uEdx))
+ return BS3CPUVENDOR_VIA;
+ if (RTX86IsShanghaiCpu(uEbx, uEcx, uEdx))
+ return BS3CPUVENDOR_SHANGHAI;
+ if (RTX86IsHygonCpu(uEbx, uEcx, uEdx))
+ return BS3CPUVENDOR_HYGON;
+ return BS3CPUVENDOR_UNKNOWN;
+ }
+ return BS3CPUVENDOR_INTEL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeName.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeName.c
new file mode 100644
index 00000000..b8dcedab
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeName.c
@@ -0,0 +1,72 @@
+/* $Id: bs3-cmn-GetModeName.c $ */
+/** @file
+ * BS3Kit - Bs3GetModeName
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+
+#undef Bs3GetModeName
+BS3_CMN_DEF(const char BS3_FAR *, Bs3GetModeName,(uint8_t bMode))
+{
+ switch (bMode)
+ {
+ case BS3_MODE_RM: return g_szBs3ModeName_rm;
+ case BS3_MODE_PE16: return g_szBs3ModeName_pe16;
+ case BS3_MODE_PE16_32: return g_szBs3ModeName_pe16_32;
+ case BS3_MODE_PE16_V86: return g_szBs3ModeName_pe16_v86;
+ case BS3_MODE_PE32: return g_szBs3ModeName_pe32;
+ case BS3_MODE_PE32_16: return g_szBs3ModeName_pe32_16;
+ case BS3_MODE_PEV86: return g_szBs3ModeName_pev86;
+ case BS3_MODE_PP16: return g_szBs3ModeName_pp16;
+ case BS3_MODE_PP16_32: return g_szBs3ModeName_pp16_32;
+ case BS3_MODE_PP16_V86: return g_szBs3ModeName_pp16_v86;
+ case BS3_MODE_PP32: return g_szBs3ModeName_pp32;
+ case BS3_MODE_PP32_16: return g_szBs3ModeName_pp32_16;
+ case BS3_MODE_PPV86: return g_szBs3ModeName_ppv86;
+ case BS3_MODE_PAE16: return g_szBs3ModeName_pae16;
+ case BS3_MODE_PAE16_32: return g_szBs3ModeName_pae16_32;
+ case BS3_MODE_PAE16_V86: return g_szBs3ModeName_pae16_v86;
+ case BS3_MODE_PAE32: return g_szBs3ModeName_pae32;
+ case BS3_MODE_PAE32_16: return g_szBs3ModeName_pae32_16;
+ case BS3_MODE_PAEV86: return g_szBs3ModeName_paev86;
+ case BS3_MODE_LM16: return g_szBs3ModeName_lm16;
+ case BS3_MODE_LM32: return g_szBs3ModeName_lm32;
+ case BS3_MODE_LM64: return g_szBs3ModeName_lm64;
+ case BS3_MODE_INVALID: return "invalid";
+ default: return "unknow";
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeNameShortLower.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeNameShortLower.c
new file mode 100644
index 00000000..a7488aa8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-GetModeNameShortLower.c
@@ -0,0 +1,72 @@
+/* $Id: bs3-cmn-GetModeNameShortLower.c $ */
+/** @file
+ * BS3Kit - Bs3GetModeNameShortLower
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+
+#undef Bs3GetModeNameShortLower
+BS3_CMN_DEF(const char BS3_FAR *, Bs3GetModeNameShortLower,(uint8_t bMode))
+{
+ switch (bMode)
+ {
+ case BS3_MODE_RM: return g_szBs3ModeNameShortLower_rm;
+ case BS3_MODE_PE16: return g_szBs3ModeNameShortLower_pe16;
+ case BS3_MODE_PE16_32: return g_szBs3ModeNameShortLower_pe16_32;
+ case BS3_MODE_PE16_V86: return g_szBs3ModeNameShortLower_pe16_v86;
+ case BS3_MODE_PE32: return g_szBs3ModeNameShortLower_pe32;
+ case BS3_MODE_PE32_16: return g_szBs3ModeNameShortLower_pe32_16;
+ case BS3_MODE_PEV86: return g_szBs3ModeNameShortLower_pev86;
+ case BS3_MODE_PP16: return g_szBs3ModeNameShortLower_pp16;
+ case BS3_MODE_PP16_32: return g_szBs3ModeNameShortLower_pp16_32;
+ case BS3_MODE_PP16_V86: return g_szBs3ModeNameShortLower_pp16_v86;
+ case BS3_MODE_PP32: return g_szBs3ModeNameShortLower_pp32;
+ case BS3_MODE_PP32_16: return g_szBs3ModeNameShortLower_pp32_16;
+ case BS3_MODE_PPV86: return g_szBs3ModeNameShortLower_ppv86;
+ case BS3_MODE_PAE16: return g_szBs3ModeNameShortLower_pae16;
+ case BS3_MODE_PAE16_32: return g_szBs3ModeNameShortLower_pae16_32;
+ case BS3_MODE_PAE16_V86: return g_szBs3ModeNameShortLower_pae16_v86;
+ case BS3_MODE_PAE32: return g_szBs3ModeNameShortLower_pae32;
+ case BS3_MODE_PAE32_16: return g_szBs3ModeNameShortLower_pae32_16;
+ case BS3_MODE_PAEV86: return g_szBs3ModeNameShortLower_paev86;
+ case BS3_MODE_LM16: return g_szBs3ModeNameShortLower_lm16;
+ case BS3_MODE_LM32: return g_szBs3ModeNameShortLower_lm32;
+ case BS3_MODE_LM64: return g_szBs3ModeNameShortLower_lm64;
+ case BS3_MODE_INVALID: return "inv";
+ default: return "unk";
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdRead.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdRead.asm
new file mode 100644
index 00000000..de74bdef
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdRead.asm
@@ -0,0 +1,75 @@
+; $Id: bs3-cmn-KbdRead.asm $
+;; @file
+; BS3Kit - Bs3KbdRead.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Sends a read command to the keyboard controller and gets the result.
+;
+; The caller is responsible for making sure the keyboard controller is ready
+; for a command (call Bs3KbdWait if unsure).
+;
+; @returns The value read is returned (in al).
+; @param bCmd The read command.
+; @uses al (obviously)
+;
+; @cproto BS3_DECL(uint8_t) Bs3KbdRead_c16(uint8_t bCmd);
+;
+BS3_PROC_BEGIN_CMN Bs3KbdRead, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+
+ mov al, [xBP + xCB*2]
+ out 64h, al ; Write the command.
+
+.check_status:
+ in al, 64h
+ test al, 1 ; KBD_STAT_OBF
+ jz .check_status
+
+ in al, 60h ; Read the data.
+
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3KbdRead
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3KbdRead, 2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWait.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWait.asm
new file mode 100644
index 00000000..e63fcb99
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWait.asm
@@ -0,0 +1,64 @@
+; $Id: bs3-cmn-KbdWait.asm $
+;; @file
+; BS3Kit - Bs3KbdWait.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;;
+; Waits for the keyboard controller to become ready.
+;
+; @cproto BS3_DECL(void) Bs3KbdWait_c16(void);
+;
+BS3_PROC_BEGIN_CMN Bs3KbdWait, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+ push xAX
+
+.check_status:
+ in al, 64h
+ test al, 1 ; KBD_STAT_OBF
+ jnz .read_data_and_status
+ test al, 2 ; KBD_STAT_IBF
+ jnz .check_status
+
+ pop xAX
+ pop xBP
+ BS3_HYBRID_RET
+
+.read_data_and_status:
+ in al, 60h
+ jmp .check_status
+BS3_PROC_END_CMN Bs3KbdWait
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWrite.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWrite.asm
new file mode 100644
index 00000000..f2ae9525
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-KbdWrite.asm
@@ -0,0 +1,82 @@
+; $Id: bs3-cmn-KbdWrite.asm $
+;; @file
+; BS3Kit - Bs3KbdRead.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3KbdWait
+
+
+;;
+; Sends a write command to the keyboard controller and then sends the data.
+;
+; The caller is responsible for making sure the keyboard controller is ready
+; for a command (call Bs3KbdWait if unsure).
+;
+; @param bCmd The write command.
+; @param bData The data to write.
+; @uses Nothing.
+;
+; @todo Return status?
+;
+; @cproto BS3_DECL(void) Bs3KbdWait_c16(uint8_t bCmd, uint8_t bData);
+;
+BS3_PROC_BEGIN_CMN Bs3KbdWrite, BS3_PBC_NEAR
+ push xBP
+ mov xBP, xSP
+ push xAX
+BONLY64 sub rsp, 20h
+
+ mov al, [xBP + xCB*2]
+ out 64h, al ; Write the command.
+ call Bs3KbdWait
+
+ mov al, [xBP + xCB*3]
+ out 60h, al ; Write the data
+ call Bs3KbdWait
+
+BONLY64 add rsp, 20h
+ pop xAX
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3KbdWrite
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3KbdWrite, 4
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAlloc.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAlloc.c
new file mode 100644
index 00000000..851ddc26
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAlloc.c
@@ -0,0 +1,111 @@
+/* $Id: bs3-cmn-MemAlloc.c $ */
+/** @file
+ * BS3Kit - Bs3MemAlloc
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-memory.h"
+#include <iprt/asm.h>
+
+
+#undef Bs3MemAlloc
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemAlloc,(BS3MEMKIND enmKind, size_t cb))
+{
+ void BS3_FAR *pvRet;
+ uint8_t idxSlabList;
+
+#if ARCH_BITS == 16
+ /* Don't try allocate memory which address we cannot return. */
+ if ( enmKind != BS3MEMKIND_REAL
+ && BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+ enmKind = BS3MEMKIND_REAL;
+#endif
+
+ idxSlabList = bs3MemSizeToSlabListIndex(cb);
+ if (idxSlabList < BS3_MEM_SLAB_LIST_COUNT)
+ {
+ /*
+ * Try allocate a chunk from the list.
+ */
+ PBS3SLABHEAD pHead = enmKind == BS3MEMKIND_REAL
+ ? &g_aBs3LowSlabLists[idxSlabList]
+ : &g_aBs3UpperTiledSlabLists[idxSlabList];
+
+ BS3_ASSERT(g_aBs3LowSlabLists[idxSlabList].cbChunk >= cb);
+ pvRet = Bs3SlabListAlloc(pHead);
+ if (pvRet)
+ { /* likely */ }
+ else
+ {
+ /*
+ * Grow the list.
+ */
+ PBS3SLABCTL pNew = (PBS3SLABCTL)Bs3SlabAlloc( enmKind == BS3MEMKIND_REAL
+ ? &g_Bs3Mem4KLow.Core
+ : &g_Bs3Mem4KUpperTiled.Core);
+ BS3_ASSERT(((uintptr_t)pNew & 0xfff) == 0);
+ if (pNew)
+ {
+ uint16_t const cbHdr = g_cbBs3SlabCtlSizesforLists[idxSlabList];
+ BS3_XPTR_AUTO(void, pvNew);
+ BS3_XPTR_SET(void, pvNew, pNew);
+
+ Bs3SlabInit(pNew, cbHdr, BS3_XPTR_GET_FLAT(void, pvNew) + cbHdr, _4K - cbHdr, pHead->cbChunk);
+ Bs3SlabListAdd(pHead, pNew);
+
+ pvRet = Bs3SlabListAlloc(pHead);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Allocate one or more pages.
+ */
+ size_t const cbAligned = RT_ALIGN_Z(cb, _4K);
+ uint16_t const cPages = cbAligned >> 12 /* div _4K */;
+ PBS3SLABCTL pSlabCtl = enmKind == BS3MEMKIND_REAL
+ ? &g_Bs3Mem4KLow.Core : &g_Bs3Mem4KUpperTiled.Core;
+
+ pvRet = Bs3SlabAllocEx(pSlabCtl,
+ cPages,
+ cPages <= _64K / _4K ? BS3_SLAB_ALLOC_F_SAME_TILE : 0);
+ }
+ return pvRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAllocZ.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAllocZ.c
new file mode 100644
index 00000000..a7beb560
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemAllocZ.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-MemAllocZ.c $ */
+/** @file
+ * BS3Kit - Bs3MemAllocZ
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-memory.h"
+
+
+#undef Bs3MemAllocZ
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemAllocZ,(BS3MEMKIND enmKind, size_t cb))
+{
+ void BS3_FAR *pvRet = Bs3MemAlloc(enmKind, cb);
+ if (pvRet)
+ Bs3MemZero(pvRet, cb);
+ return pvRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemChr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemChr.asm
new file mode 100644
index 00000000..82015c7e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemChr.asm
@@ -0,0 +1,88 @@
+; $Id: bs3-cmn-MemChr.asm $
+;; @file
+; BS3Kit - Bs3MemChr.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(void BS3_FAR *, Bs3MemChr,(void BS3_FAR const *pvHaystack, uint8_t bNeedle, size_t cbHaystack));
+;
+BS3_PROC_BEGIN_CMN Bs3MemChr, BS3_PBC_HYBRID
+ push xBP
+ mov xBP, xSP
+ push xDI
+TONLY16 push es
+
+%if TMPL_BITS == 64
+
+ mov rdi, rcx ; rdi = pvHaystack
+ mov rcx, r8 ; rcx = cbHaystack
+ mov al, dl ; bNeedle
+ mov rcx, r8
+
+%elif TMPL_BITS == 16
+ mov di, [bp + 2 + cbCurRetAddr] ; pvHaystack.off
+ mov es, [bp + 2 + cbCurRetAddr + 2] ; pvHaystack.sel
+ mov al, [bp + 2 + cbCurRetAddr + 4] ; bNeedle
+ mov cx, [bp + 2 + cbCurRetAddr + 6] ; cbHaystack
+
+%elif TMPL_BITS == 32
+ mov edi, [ebp + 8] ; pvHaystack
+ mov al, byte [ebp + 4 + cbCurRetAddr + 4] ; bNeedle
+ mov ecx, [ebp + 4 + cbCurRetAddr + 8] ; cbHaystack
+%else
+ %error "TMPL_BITS!"
+%endif
+
+ cld
+ repne scasb
+ je .found
+
+ xor xAX, xAX
+TONLY16 xor dx, dx
+
+.return:
+TONLY16 pop es
+ pop xDI
+ pop xBP
+ BS3_HYBRID_RET
+
+.found:
+ lea xAX, [xDI - 1]
+TONLY16 mov dx, es
+ jmp .return
+
+BS3_PROC_END_CMN Bs3MemChr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCmp.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCmp.asm
new file mode 100644
index 00000000..1f032010
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCmp.asm
@@ -0,0 +1,99 @@
+; $Id: bs3-cmn-MemCmp.asm $
+;; @file
+; BS3Kit - Bs3MemCmp.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(int, Bs3MemCmp,(void const BS3_FAR *pv1, void const BS3_FAR *pv2, size_t cb));
+;
+BS3_PROC_BEGIN_CMN Bs3MemCmp, BS3_PBC_HYBRID
+TONLY16 CPU 8086
+ push xBP
+ mov xBP, xSP
+ push xDI
+ push xSI
+TNOT64 push es
+TONLY16 push ds
+ cld
+
+ ;
+ ; To save complexity and space, do straight forward byte compares.
+ ;
+%if TMPL_BITS == 16
+ mov di, [bp + 2 + cbCurRetAddr] ; pv1.off
+ mov es, [bp + 2 + cbCurRetAddr + 2] ; pv1.sel
+ mov si, [bp + 2 + cbCurRetAddr + 4] ; pv2.off
+ mov ds, [bp + 2 + cbCurRetAddr + 6] ; pv2.sel
+ mov cx, [bp + 2 + cbCurRetAddr + 8] ; cbDst
+ xor ax, ax
+ repe cmpsb
+ je .return
+
+ mov al, [es:di - 1]
+ xor dx, dx
+ mov dl, [esi - 1]
+ sub ax, dx
+
+%else
+ %if TMPL_BITS == 64
+ mov rdi, rcx ; rdi = pv1
+ mov rsi, rdx ; rdi = pv2
+ mov rcx, r8 ; rcx = cbDst
+ %else
+ mov ax, ds
+ mov es, ax ; paranoia
+ mov edi, [ebp + 4 + cbCurRetAddr] ; pv1
+ mov esi, [ebp + 4 + cbCurRetAddr + 4] ; pv2
+ mov ecx, [ebp + 4 + cbCurRetAddr + 8] ; cbDst
+ %endif
+ xor eax, eax
+ repe cmpsb
+ je .return
+
+ mov al, [xDI - 1]
+ movzx edx, byte [xSI - 1]
+ sub eax, edx
+%endif
+
+.return:
+TONLY16 pop ds
+TNOT64 pop es
+ pop xSI
+ pop xDI
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3MemCmp
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCpy.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCpy.c
new file mode 100644
index 00000000..9bbbe3c6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemCpy.c
@@ -0,0 +1,85 @@
+/* $Id: bs3-cmn-MemCpy.c $ */
+/** @file
+ * BS3Kit - Bs3MemCpy
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3MemCpy
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemCpy,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy))
+{
+#if 1
+ const size_t BS3_FAR *pBigSrc = (const size_t BS3_FAR *)pvSrc;
+ size_t BS3_FAR *pBigDst = (size_t *)pvDst;
+ size_t cBig = cbToCopy / sizeof(size_t);
+ while (cBig-- > 0)
+ *pBigDst++ = *pBigSrc++;
+
+ switch (cbToCopy % sizeof(size_t))
+ {
+#if TMPL_BITS >= 64
+ case 7: ((uint8_t BS3_FAR *)pBigDst)[6] = ((const uint8_t BS3_FAR *)pBigSrc)[6];
+ case 6: ((uint8_t BS3_FAR *)pBigDst)[5] = ((const uint8_t BS3_FAR *)pBigSrc)[5];
+ case 5: ((uint8_t BS3_FAR *)pBigDst)[4] = ((const uint8_t BS3_FAR *)pBigSrc)[4];
+ case 4: ((uint8_t BS3_FAR *)pBigDst)[3] = ((const uint8_t BS3_FAR *)pBigSrc)[3];
+#endif
+#if TMPL_BITS >= 32
+ case 3: ((uint8_t BS3_FAR *)pBigDst)[2] = ((const uint8_t BS3_FAR *)pBigSrc)[2];
+ case 2: ((uint8_t BS3_FAR *)pBigDst)[1] = ((const uint8_t BS3_FAR *)pBigSrc)[1];
+#endif
+ case 1: ((uint8_t BS3_FAR *)pBigDst)[0] = ((const uint8_t BS3_FAR *)pBigSrc)[0];
+ case 0:
+ break;
+ }
+
+#else
+ size_t cLargeRounds;
+ BS3CPTRUNION uSrc;
+ BS3PTRUNION uDst;
+ uSrc.pv = pvSrc;
+ uDst.pv = pvDst;
+
+ cLargeRounds = cbToCopy / sizeof(*uSrc.pcb);
+ while (cLargeRounds-- > 0)
+ *uDst.pcb++ = *uSrc.pcb++;
+
+ cbToCopy %= sizeof(*uSrc.pcb);
+ while (cbToCopy-- > 0)
+ *uDst.pb++ = *uSrc.pb++;
+
+#endif
+
+ return pvDst;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemFree.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemFree.c
new file mode 100644
index 00000000..f7592832
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemFree.c
@@ -0,0 +1,73 @@
+/* $Id: bs3-cmn-MemFree.c $ */
+/** @file
+ * BS3Kit - Bs3MemFree
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-memory.h"
+
+
+#undef Bs3MemFree
+BS3_CMN_DEF(void, Bs3MemFree,(void BS3_FAR *pv, size_t cb))
+{
+ if (pv != NULL)
+ {
+ uint16_t cChunks;
+ PBS3SLABCTL pCtl;
+ BS3_XPTR_AUTO(void, pvFlat);
+ BS3_XPTR_SET(void, pvFlat, pv);
+
+ if (BS3_XPTR_GET_FLAT(void, pvFlat) & 0xfffU)
+ {
+ /* Use an XPTR here in case we're in real mode and the caller has
+ messed around with the pointer. */
+ BS3_XPTR_AUTO(BS3SLABCTL, pTmp);
+ BS3_XPTR_SET_FLAT(BS3SLABCTL, pTmp, BS3_XPTR_GET_FLAT(void, pvFlat) & ~(uint32_t)0xfff);
+ pCtl = BS3_XPTR_GET(BS3SLABCTL, pTmp);
+ BS3_ASSERT(pCtl->cbChunk >= cb);
+ cChunks = 1;
+ }
+ else
+ {
+ pCtl = BS3_XPTR_GET_FLAT(void, pvFlat) < _1M ? &g_Bs3Mem4KLow.Core : &g_Bs3Mem4KUpperTiled.Core;
+ cChunks = RT_ALIGN_Z(cb, _4K) >> 12;
+ }
+ Bs3SlabFree(pCtl, BS3_XPTR_GET_FLAT(void, pvFlat), cChunks);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemGuardedTestPage.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemGuardedTestPage.c
new file mode 100644
index 00000000..7d5eb28c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemGuardedTestPage.c
@@ -0,0 +1,109 @@
+/* $Id: bs3-cmn-MemGuardedTestPage.c $ */
+/** @file
+ * BS3Kit - Bs3MemGuardedTestPageAlloc, Bs3MemGuardedTestPageFree
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "iprt/asm.h"
+
+
+#undef Bs3MemGuardedTestPageAllocEx
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemGuardedTestPageAllocEx,(BS3MEMKIND enmKind, uint64_t fPte))
+{
+ uint8_t BS3_FAR *pb = (uint8_t BS3_FAR *)Bs3MemAlloc(enmKind, X86_PAGE_4K_SIZE * 3);
+ if (pb)
+ {
+ int rc;
+
+ Bs3MemSet(pb, 0xcc, X86_PAGE_4K_SIZE);
+ Bs3MemSet(&pb[X86_PAGE_4K_SIZE], 0x00, X86_PAGE_4K_SIZE);
+ Bs3MemSet(&pb[X86_PAGE_4K_SIZE*2], 0xaa, X86_PAGE_4K_SIZE);
+
+ rc = Bs3PagingProtectPtr(pb, X86_PAGE_4K_SIZE, fPte, UINT64_MAX & ~fPte);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Bs3PagingProtectPtr(&pb[X86_PAGE_4K_SIZE*2], X86_PAGE_4K_SIZE, fPte, UINT64_MAX & ~fPte);
+ if (RT_SUCCESS(rc))
+ return pb + X86_PAGE_4K_SIZE;
+
+ Bs3TestPrintf("warning: Bs3MemGuardedTestPageAlloc - Tail protect error %d (mode %#x)\n", rc, g_bBs3CurrentMode);
+ Bs3PagingProtectPtr(pb, X86_PAGE_4K_SIZE, X86_PTE_P, 0);
+ }
+ else
+ Bs3TestPrintf("warning: Bs3MemGuardedTestPageAlloc - Head protect error %d (mode %#x)\n", rc, g_bBs3CurrentMode);
+ Bs3MemFree(pb, X86_PAGE_4K_SIZE * 3);
+ }
+ else
+ Bs3TestPrintf("warning: Bs3MemGuardedTestPageAlloc - out of memory (mode %#x)\n", g_bBs3CurrentMode);
+ return NULL;
+}
+
+
+#undef Bs3MemGuardedTestPageAlloc
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemGuardedTestPageAlloc,(BS3MEMKIND enmKind))
+{
+ return BS3_CMN_FAR_NM(Bs3MemGuardedTestPageAllocEx)(enmKind, 0);
+}
+
+
+#undef Bs3MemGuardedTestPageFree
+BS3_CMN_DEF(void, Bs3MemGuardedTestPageFree,(void BS3_FAR *pvGuardedPage))
+{
+ if (pvGuardedPage)
+ {
+ uint8_t BS3_FAR *pbGuardViolation;
+ uint8_t BS3_FAR *pb = (uint8_t BS3_FAR *)pvGuardedPage - X86_PAGE_4K_SIZE;
+ Bs3PagingProtectPtr(pb, X86_PAGE_4K_SIZE,
+ X86_PTE_P | X86_PTE_RW | X86_PTE_US | X86_PTE_A | X86_PTE_D, UINT64_MAX);
+ Bs3PagingProtectPtr(&pb[X86_PAGE_4K_SIZE*2], X86_PAGE_4K_SIZE,
+ X86_PTE_P | X86_PTE_RW | X86_PTE_US | X86_PTE_A | X86_PTE_D, UINT64_MAX);
+
+ pbGuardViolation = ASMMemFirstMismatchingU8(pb, X86_PAGE_4K_SIZE, 0xcc);
+ if (pbGuardViolation)
+ Bs3TestFailedF("Leading guard page touched: byte %#05x is %#04x instead of 0xcc\n",
+ (unsigned)(uintptr_t)(pbGuardViolation - pb), *pbGuardViolation);
+
+ pbGuardViolation = ASMMemFirstMismatchingU8(&pb[X86_PAGE_4K_SIZE*2], X86_PAGE_4K_SIZE, 0xaa);
+ if (pbGuardViolation)
+ Bs3TestFailedF("Trailing guard page touched: byte %#05x is %#04x instead of 0xaa\n",
+ (unsigned)(uintptr_t)(pbGuardViolation - &pb[X86_PAGE_4K_SIZE*2]), *pbGuardViolation);
+
+ Bs3MemFree(pb, X86_PAGE_4K_SIZE * 3);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemMove.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemMove.c
new file mode 100644
index 00000000..7ac92e4e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemMove.c
@@ -0,0 +1,88 @@
+/* $Id: bs3-cmn-MemMove.c $ */
+/** @file
+ * BS3Kit - Bs3MemMove
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3MemMove
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemMove,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy))
+{
+ size_t cLargeRounds;
+ BS3CVPTRUNION uSrc;
+ BS3PTRUNION uDst;
+ uSrc.pv = pvSrc;
+ uDst.pv = pvDst;
+
+ /* We don't care about segment wrapping here. */
+ if ((uintptr_t)pvDst > (uintptr_t)pvSrc + cbToCopy)
+ {
+ /* Reverse copy. */
+ uSrc.pb += cbToCopy;
+ uDst.pb += cbToCopy;
+
+ cLargeRounds = cbToCopy / sizeof(*uSrc.pcb);
+ while (cLargeRounds-- > 0)
+ {
+ size_t uTmp = *--uSrc.pcb;
+ *--uDst.pcb = uTmp;
+ }
+
+ cbToCopy %= sizeof(*uSrc.pcb);
+ while (cbToCopy-- > 0)
+ {
+ uint8_t b = *--uSrc.pb;
+ *--uDst.pb = b;
+ }
+ }
+ else
+ {
+ /* Forward copy. */
+ cLargeRounds = cbToCopy / sizeof(*uSrc.pcb);
+ while (cLargeRounds-- > 0)
+ {
+ size_t uTmp = *uSrc.pcb++;
+ *uDst.pcb++ = uTmp;
+ }
+
+ cbToCopy %= sizeof(*uSrc.pcb);
+ while (cbToCopy-- > 0)
+ {
+ uint8_t b = *uSrc.pb++;
+ *uDst.pb++ = b;
+ }
+ }
+ return pvDst;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPCpy.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPCpy.c
new file mode 100644
index 00000000..ac23e7f2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPCpy.c
@@ -0,0 +1,58 @@
+/* $Id: bs3-cmn-MemPCpy.c $ */
+/** @file
+ * BS3Kit - Bs3MemPCpy
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3MemPCpy
+BS3_CMN_DEF(void BS3_FAR *, Bs3MemPCpy,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy))
+{
+ size_t cLargeRounds;
+ BS3CPTRUNION uSrc;
+ BS3PTRUNION uDst;
+ uSrc.pv = pvSrc;
+ uDst.pv = pvDst;
+
+ cLargeRounds = cbToCopy / sizeof(*uSrc.pcb);
+ while (cLargeRounds-- > 0)
+ *uDst.pcb++ = *uSrc.pcb++;
+
+ cbToCopy %= sizeof(*uSrc.pcb);
+ while (cbToCopy-- > 0)
+ *uDst.pb++ = *uSrc.pb++;
+
+ return uDst.pv;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPrintInfo.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPrintInfo.c
new file mode 100644
index 00000000..a6f5b6f6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemPrintInfo.c
@@ -0,0 +1,95 @@
+/* $Id: bs3-cmn-MemPrintInfo.c $ */
+/** @file
+ * BS3Kit - Bs3MemPrintInfo
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-memory.h"
+#include <iprt/asm.h>
+
+
+/**
+ * Prints a slab control structure with allocation map.
+ *
+ * @param pCtl The slab control structure to print.
+ * @param pszPrefix The output prefix.
+ */
+static void Bs3MemPrintInfoSlabCtl(PBS3SLABCTL pCtl, const char BS3_FAR *pszPrefix)
+{
+ unsigned iChunk;
+ Bs3TestPrintf("%s / %#06x: %u of %u chunks free", pszPrefix, pCtl->cbChunk, pCtl->cFreeChunks, pCtl->cChunks);
+ for (iChunk = 0; iChunk < pCtl->cChunks; iChunk++)
+ {
+ if ((iChunk & 63) == 0)
+ Bs3TestPrintf("\n%s:", pszPrefix);
+ if (ASMBitTest(pCtl->bmAllocated, iChunk))
+ Bs3TestPrintf((iChunk & 7) != 0 ? "x" : " x");
+ else
+ Bs3TestPrintf((iChunk & 7) != 0 ? "-" : " -");
+ }
+ Bs3TestPrintf("\n");
+}
+
+
+
+/**
+ * Prints a summary of a slab allocation list (i.e. the heap).
+ *
+ * @param paLists Array of BS3_MEM_SLAB_LIST_COUNT lists.
+ * @param pszPrefix The output prefix.
+ */
+static void Bs3MemPrintInfoSlabList(PBS3SLABHEAD paLists, const char BS3_FAR *pszPrefix)
+{
+ unsigned iSlab;
+ for (iSlab = 0; iSlab < BS3_MEM_SLAB_LIST_COUNT; iSlab++)
+ if (paLists[iSlab].cSlabs)
+ Bs3TestPrintf("%s / %#06x: %u slabs, %RU32 of %RU32 chunks free\n",
+ pszPrefix, paLists[iSlab].cbChunk, paLists[iSlab].cSlabs,
+ paLists[iSlab].cFreeChunks, paLists[iSlab].cChunks);
+}
+
+
+#undef Bs3MemPrintInfo
+BS3_CMN_DEF(void, Bs3MemPrintInfo,(void))
+{
+ Bs3MemPrintInfoSlabList(g_aBs3LowSlabLists, "Lower");
+ Bs3MemPrintInfoSlabList(g_aBs3LowSlabLists, "Upper");
+ Bs3MemPrintInfoSlabCtl(&g_Bs3Mem4KLow.Core, "4KLow");
+ Bs3MemPrintInfoSlabCtl(&g_Bs3Mem4KUpperTiled.Core, "Tiled");
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemSet.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemSet.asm
new file mode 100644
index 00000000..513a63d0
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemSet.asm
@@ -0,0 +1,102 @@
+; $Id: bs3-cmn-MemSet.asm $
+;; @file
+; BS3Kit - Bs3MemSet.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(void, Bs3MemSet,(void BS3_FAR *pvDst, uint8_t bFiller, size_t cbDst));
+;
+BS3_PROC_BEGIN_CMN Bs3MemSet, BS3_PBC_HYBRID
+ push xBP
+ mov xBP, xSP
+ push xDI
+%ifdef RT_ARCH_AMD64
+
+ mov rdi, rcx ; rdi = pvDst
+ mov rcx, r8 ; rcx = cbDst
+ movzx edx, dl ; bFiller
+ mov rax, 0101010101010101h
+ mul rdx
+ mov rcx, r8
+ shr rcx, 3 ; calc qword count.
+ cld
+ rep stosq
+
+ mov rcx, r8 ; cbDst
+ and rcx, 7 ; calc trailing byte count.
+ rep stosb
+
+%elif ARCH_BITS == 16
+ push es
+
+ mov di, [bp + 2 + cbCurRetAddr] ; pvDst.off
+ mov es, [bp + 2 + cbCurRetAddr + 2] ; pvDst.sel
+ mov al, [bp + 2 + cbCurRetAddr + 4] ; bFiller
+ mov ah, al
+ mov cx, [bp + 2 + cbCurRetAddr + 6] ; cbDst
+ shr cx, 1 ; calc dword count.
+ rep stosw
+
+ mov cx, [bp + 2 + cbCurRetAddr + 6] ; cbDst
+ and cx, 1 ; calc tailing byte count.
+ rep stosb
+
+ pop es
+
+%elif ARCH_BITS == 32
+ mov edi, [ebp + 8] ; pvDst
+ mov al, byte [ebp + 4 + cbCurRetAddr + 4] ; bFiller
+ mov ah, al
+ mov dx, ax
+ shl eax, 16
+ mov ax, dx ; eax = RT_MAKE_U32_FROM_U8(bFiller, bFiller, bFiller, bFiller)
+ mov ecx, [ebp + 4 + cbCurRetAddr + 8] ; cbDst
+ shr cx, 2 ; calc dword count.
+ rep stosd
+
+ mov ecx, [ebp + 4 + cbCurRetAddr + 8] ; cbDst
+ and ecx, 3 ; calc tailing byte count.
+ rep stosb
+
+%else
+ %error "Unknown bitness."
+%endif
+
+ pop xDI
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3MemSet
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemZero.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemZero.asm
new file mode 100644
index 00000000..4e254020
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-MemZero.asm
@@ -0,0 +1,103 @@
+; $Id: bs3-cmn-MemZero.asm $
+;; @file
+; BS3Kit - Bs3MemZero.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;;
+; @cproto BS3_DECL(void) Bs3MemZero_c16(void BS3_FAR *pvDst, size_t cbDst);
+;
+BS3_PROC_BEGIN_CMN Bs3MemZero, BS3_PBC_HYBRID
+%ifdef RT_ARCH_AMD64
+ push rdi
+
+ mov rdi, rcx ; rdi = pvDst
+ mov rcx, rdx ; rcx = cbDst
+ shr rcx, 3 ; calc qword count.
+ xor eax, eax ; rax = 0 (filler qword)
+ cld
+ rep stosq
+
+ mov rcx, rdx ; cbDst
+ and rcx, 7 ; calc trailing byte count.
+ rep stosb
+
+ pop rdi
+ BS3_HYBRID_RET
+
+%elif ARCH_BITS == 16
+ push bp
+ mov bp, sp
+ push di
+ push es
+
+ mov di, [bp + 2 + cbCurRetAddr] ; pvDst.off
+ mov dx, [bp + 2 + cbCurRetAddr + 2] ; pvDst.sel
+ mov es, dx
+ mov cx, [bp + 2 + cbCurRetAddr + 4] ; cbDst
+ shr cx, 1 ; calc dword count.
+ xor ax, ax
+ rep stosw
+
+ mov cx, [bp + 2 + cbCurRetAddr + 4] ; cbDst
+ and cx, 1 ; calc tailing byte count.
+ rep stosb
+
+ pop es
+ pop di
+ pop bp
+ BS3_HYBRID_RET
+
+%elif ARCH_BITS == 32
+ push edi
+
+ mov edi, [esp + 8] ; pvDst
+ mov ecx, [esp + 8 + 4] ; cbDst
+ shr cx, 2 ; calc dword count.
+ xor eax, eax
+ rep stosd
+
+ mov ecx, [esp + 8 + 4] ; cbDst
+ and ecx, 3 ; calc tailing byte count.
+ rep stosb
+
+ pop edi
+ BS3_HYBRID_RET
+
+%else
+ %error "Unknown bitness."
+%endif
+BS3_PROC_END_CMN Bs3MemZero
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingAlias.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingAlias.c
new file mode 100644
index 00000000..a529e50c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingAlias.c
@@ -0,0 +1,193 @@
+/* $Id: bs3-cmn-PagingAlias.c $ */
+/** @file
+ * BS3Kit - Bs3PagingAlias, Bs3PagingUnalias
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+#include "iprt/asm-amd64-x86.h"
+
+
+#undef Bs3PagingAlias
+BS3_CMN_DEF(int, Bs3PagingAlias,(uint64_t uDst, uint64_t uPhysToAlias, uint32_t cbHowMuch, uint64_t fPte))
+{
+#if ARCH_BITS == 16
+ if (!BS3_MODE_IS_V86(g_bBs3CurrentMode))
+#endif
+ {
+ RTCCUINTXREG cr3 = ASMGetCR3();
+ uint32_t cPages;
+ int rc;
+
+ /*
+ * Validate and adjust the input a little.
+ */
+ if (uDst & X86_PAGE_OFFSET_MASK)
+ {
+ cbHowMuch += X86_PAGE_SIZE - (uDst & X86_PAGE_OFFSET_MASK);
+ uDst &= ~(uint64_t)X86_PAGE_OFFSET_MASK;
+ }
+ uPhysToAlias &= X86_PTE_PAE_PG_MASK;
+ fPte &= ~(X86_PTE_PAE_MBZ_MASK_NX | X86_PTE_PAE_PG_MASK);
+ cbHowMuch = RT_ALIGN_32(cbHowMuch, X86_PAGE_SIZE);
+ cPages = cbHowMuch >> X86_PAGE_SHIFT;
+ //Bs3TestPrintf("Bs3PagingAlias: adjusted: uDst=%RX64 uPhysToAlias=%RX64 cbHowMuch=%RX32 fPte=%Rx64 cPages=%RX32\n", uDst, uPhysToAlias, cbHowMuch, fPte, cPages);
+ if (BS3_MODE_IS_LEGACY_PAGING(g_bBs3CurrentMode))
+ {
+ X86PTE BS3_FAR *pPteLegacy;
+ uint32_t uDst32 = (uint32_t)uDst;
+ uint32_t uPhysToAlias32 = (uint32_t)uPhysToAlias;
+ if (uDst32 != uDst)
+ {
+ Bs3TestPrintf("warning: Bs3PagingAlias - uDst=%RX64 is out of range for legacy paging!\n", uDst);
+ return VERR_INVALID_PARAMETER;
+ }
+ if (uPhysToAlias32 != uPhysToAlias)
+ {
+ Bs3TestPrintf("warning: Bs3PagingAlias - uPhysToAlias=%RX64 is out of range for legacy paging!\n", uPhysToAlias);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Trigger page table splitting first.
+ */
+ while (cPages > 0)
+ {
+ pPteLegacy = bs3PagingGetLegacyPte(cr3, uDst32, false, &rc);
+ if (pPteLegacy)
+ {
+ uint32_t cLeftInPt = X86_PG_ENTRIES - ((uDst32 >> X86_PT_SHIFT) & X86_PT_MASK);
+ if (cPages <= cLeftInPt)
+ break;
+ uDst32 += cLeftInPt << X86_PAGE_SHIFT;
+ cPages -= cLeftInPt;
+ }
+ else
+ {
+ Bs3TestPrintf("warning: Bs3PagingAlias - bs3PagingGetLegacyPte failed: rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /*
+ * Make the changes.
+ */
+ cPages = cbHowMuch >> X86_PAGE_SHIFT;
+ uDst32 = (uint32_t)uDst;
+ while (cPages > 0)
+ {
+ uint32_t cLeftInPt = X86_PG_ENTRIES - ((uDst32 >> X86_PT_SHIFT) & X86_PT_MASK);
+ pPteLegacy = bs3PagingGetLegacyPte(cr3, uDst32, false, &rc);
+ while (cLeftInPt > 0 && cPages > 0)
+ {
+ pPteLegacy->u = uPhysToAlias32 | (uint32_t)fPte;
+ pPteLegacy++;
+ uDst32 += X86_PAGE_SIZE;
+ uPhysToAlias32 += X86_PAGE_SIZE;
+ cPages--;
+ cLeftInPt--;
+ }
+ }
+ }
+ else
+ {
+ X86PTEPAE BS3_FAR *pPtePae;
+ uint64_t const uDstSaved = uDst;
+
+ /*
+ * Trigger page table splitting first.
+ */
+ while (cPages > 0)
+ {
+ pPtePae = bs3PagingGetPaePte(cr3, g_bBs3CurrentMode, uDst, false, &rc);
+ if (pPtePae)
+ {
+ uint32_t cLeftInPt = X86_PG_PAE_ENTRIES - ((uDst >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
+ if (cPages <= cLeftInPt)
+ break;
+ cPages -= cLeftInPt;
+ uDst += cLeftInPt << X86_PAGE_SHIFT;
+ }
+ else
+ {
+ Bs3TestPrintf("warning: Bs3PagingAlias - bs3PagingGetLegacyPte failed: rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ /*
+ * Make the changes.
+ */
+ cPages = cbHowMuch >> X86_PAGE_SHIFT;
+ uDst = uDstSaved;
+ while (cPages > 0)
+ {
+ uint32_t cLeftInPt = X86_PG_PAE_ENTRIES - ((uDst >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
+ pPtePae = bs3PagingGetPaePte(cr3, g_bBs3CurrentMode, uDst, false, &rc);
+ while (cLeftInPt > 0 && cPages > 0)
+ {
+ pPtePae->u = uPhysToAlias | fPte;
+ pPtePae++;
+ uDst += X86_PAGE_SIZE;
+ uPhysToAlias += X86_PAGE_SIZE;
+ cPages--;
+ cLeftInPt--;
+ }
+ }
+ }
+
+ ASMReloadCR3();
+ }
+#if ARCH_BITS == 16
+ /*
+ * We can't do this stuff in v8086 mode, so switch to 16-bit prot mode and do it there.
+ */
+ else
+ return Bs3SwitchFromV86To16BitAndCallC((FPFNBS3FAR)Bs3PagingAlias_f16, sizeof(uint64_t)*3 + sizeof(uint32_t),
+ uDst, uPhysToAlias, cbHowMuch, fPte);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+#undef Bs3PagingUnalias
+BS3_CMN_DEF(int, Bs3PagingUnalias,(uint64_t uDst, uint32_t cbHowMuch))
+{
+ return BS3_CMN_NM(Bs3PagingAlias)(uDst, uDst, cbHowMuch, X86_PTE_P | X86_PTE_RW | X86_PTE_US | X86_PTE_A | X86_PTE_D);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingData.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingData.c
new file mode 100644
index 00000000..a6ab4f5b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingData.c
@@ -0,0 +1,59 @@
+/* $Id: bs3-cmn-PagingData.c $ */
+/** @file
+ * BS3Kit - Paging Data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+
+uint32_t g_PhysPagingRootPP = UINT32_MAX;
+uint32_t g_PhysPagingRootPAE = UINT32_MAX;
+uint32_t g_PhysPagingRootLM = UINT32_MAX;
+
+uint32_t g_uBs3PagingCanonicalTrapsAddr = UINT32_MAX;
+uint16_t g_cbBs3PagingCanonicalTraps = 0;
+uint16_t g_cbBs3PagingOneCanonicalTrap = 0;
+
+#endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForLM.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForLM.c
new file mode 100644
index 00000000..7a57dc1d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForLM.c
@@ -0,0 +1,115 @@
+/* $Id: bs3-cmn-PagingInitRootForLM.c $ */
+/** @file
+ * BS3Kit - Bs3PagingInitRootForLM
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+
+
+#undef Bs3PagingInitRootForLM
+BS3_CMN_DEF(int, Bs3PagingInitRootForLM,(void))
+{
+ X86PML4 BS3_FAR *pPml4;
+
+ BS3_ASSERT(g_PhysPagingRootLM == UINT32_MAX);
+
+ /*
+ * The default is an identity mapping of the first 4GB repeated for the
+ * whole 48-bit virtual address space. So, we need one level more than PAE.
+ */
+ pPml4 = (X86PML4 BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, _4K);
+ if (pPml4)
+ {
+ X86PDPT BS3_FAR *pPdPtr = (X86PDPT BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, _4K);
+ BS3_ASSERT((uintptr_t)pPdPtr != (uintptr_t)pPml4);
+ if (pPdPtr)
+ {
+ X86PDPAE BS3_FAR *paPgDirs = (X86PDPAE BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, _4K * 4U);
+ BS3_ASSERT((uintptr_t)paPgDirs != (uintptr_t)pPml4);
+ if (paPgDirs)
+ {
+ unsigned i;
+ BS3_XPTR_AUTO(X86PML4, XPtrPml4);
+ BS3_XPTR_AUTO(X86PDPT, XPtrPdPtr);
+ BS3_XPTR_AUTO(X86PDPAE, XPtrPgDirs);
+
+ /* Set up the 2048 2MB pages first. */
+ for (i = 0; i < RT_ELEMENTS(paPgDirs->a) * 4U; i++)
+ paPgDirs->a[i].u = ((uint32_t)i << X86_PD_PAE_SHIFT)
+ | X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_US | X86_PDE4M_PS | X86_PDE4M_A | X86_PDE4M_D;
+
+ /* Set up the page directory pointer table next (4GB replicated, remember). */
+ BS3_XPTR_SET(X86PDPAE, XPtrPgDirs, paPgDirs);
+ pPdPtr->a[0].u = BS3_XPTR_GET_FLAT(X86PDPAE, XPtrPgDirs)
+ | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US | X86_PDPE_A;
+ pPdPtr->a[1].u = pPdPtr->a[0].u + _4K;
+ pPdPtr->a[2].u = pPdPtr->a[1].u + _4K;
+ pPdPtr->a[3].u = pPdPtr->a[2].u + _4K;
+
+ for (i = 4; i < RT_ELEMENTS(pPdPtr->a); i += 4)
+ {
+ pPdPtr->a[i + 0].u = pPdPtr->a[0].u;
+ pPdPtr->a[i + 1].u = pPdPtr->a[1].u;
+ pPdPtr->a[i + 2].u = pPdPtr->a[2].u;
+ pPdPtr->a[i + 3].u = pPdPtr->a[3].u;
+ }
+
+ /* Set up the page map level 4 (all entries are the same). */
+ BS3_XPTR_SET(X86PDPT, XPtrPdPtr, pPdPtr);
+ pPml4->a[0].u = BS3_XPTR_GET_FLAT(X86PDPT, XPtrPdPtr)
+ | X86_PML4E_P | X86_PML4E_RW | X86_PML4E_US | X86_PML4E_A;
+ for (i = 1; i < RT_ELEMENTS(pPml4->a); i++)
+ pPml4->a[i].u = pPml4->a[0].u;
+
+ /* Set the global root pointer and we're done. */
+ BS3_XPTR_SET(X86PML4, XPtrPml4, pPml4);
+ g_PhysPagingRootLM = BS3_XPTR_GET_FLAT(X86PML4, XPtrPml4);
+ return VINF_SUCCESS;
+ }
+
+ BS3_ASSERT(false);
+ Bs3MemFree(pPdPtr, _4K);
+ }
+ BS3_ASSERT(false);
+ Bs3MemFree(pPml4, _4K);
+ }
+ BS3_ASSERT(false);
+ return VERR_NO_MEMORY;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPAE.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPAE.c
new file mode 100644
index 00000000..4e376109
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPAE.c
@@ -0,0 +1,102 @@
+/* $Id: bs3-cmn-PagingInitRootForPAE.c $ */
+/** @file
+ * BS3Kit - Bs3PagingInitRootForPAE
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+
+
+#undef Bs3PagingInitRootForPAE
+BS3_CMN_DEF(int, Bs3PagingInitRootForPAE,(void))
+{
+ X86PDPT BS3_FAR *pPdPtr;
+
+ BS3_ASSERT(g_PhysPagingRootPAE == UINT32_MAX);
+
+ /*
+ * By default we do a identity mapping of the entire address space
+ * using 2 GB pages. So, we need four page directories and one page
+ * directory pointer table with 4 entries. (We cannot share the PDPT with
+ * long mode because of reserved bit which will cause fatal trouble.)
+ *
+ * We assume that the availability of PAE means that PSE is available too.
+ */
+/** @todo testcase: loading invalid PDPTREs will tripple fault the CPU, won't it? We guru with invalid guest state. */
+ pPdPtr = (X86PDPT BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, sizeof(X86PDPE) * 4U);
+ if (pPdPtr)
+ {
+ X86PDPAE BS3_FAR *paPgDirs;
+ BS3_ASSERT(((uintptr_t)pPdPtr & 0x3f) == 0);
+
+ paPgDirs = (X86PDPAE BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, _4K * 4U);
+ if (paPgDirs)
+ {
+ unsigned i;
+ BS3_XPTR_AUTO(X86PDPT, XPtrPdPtr);
+ BS3_XPTR_AUTO(X86PDPAE, XPtrPgDirs);
+
+ /* Set up the 2048 2MB pages first. */
+ for (i = 0; i < RT_ELEMENTS(paPgDirs->a) * 4U; i++)
+ paPgDirs->a[i].u = ((uint32_t)i << X86_PD_PAE_SHIFT)
+ | X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_US | X86_PDE4M_PS | X86_PDE4M_A | X86_PDE4M_D;
+
+ /* Set up the four page directory pointer table entries. */
+ BS3_XPTR_SET(X86PDPAE, XPtrPgDirs, paPgDirs);
+ pPdPtr->a[0].u = BS3_XPTR_GET_FLAT(X86PDPAE, XPtrPgDirs) | X86_PDPE_P;
+ pPdPtr->a[1].u = pPdPtr->a[0].u + _4K;
+ pPdPtr->a[2].u = pPdPtr->a[1].u + _4K;
+ pPdPtr->a[3].u = pPdPtr->a[2].u + _4K;
+
+ /* Free up 8 consequtive entries for raw-mode hypervisor code. */
+ if (1) /** @todo detect raw-mode and only do this then. */
+ for (i = 0; i < 8; i++)
+ paPgDirs->a[i + (UINT32_C(0xc0000000) >> X86_PD_PAE_SHIFT)].b.u1Present = 0;
+
+ /* Set the global root pointer and we're done. */
+ BS3_XPTR_SET(X86PDPT, XPtrPdPtr, pPdPtr);
+ g_PhysPagingRootPAE = BS3_XPTR_GET_FLAT(X86PDPT, XPtrPdPtr);
+ return VINF_SUCCESS;
+ }
+ BS3_ASSERT(false);
+ Bs3MemFree(pPdPtr, _4K);
+ }
+ BS3_ASSERT(false);
+ return VERR_NO_MEMORY;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPP.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPP.c
new file mode 100644
index 00000000..1701edb1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingInitRootForPP.c
@@ -0,0 +1,163 @@
+/* $Id: bs3-cmn-PagingInitRootForPP.c $ */
+/** @file
+ * BS3Kit - Bs3PagingInitRootForPP
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+#include "bs3-cmn-memory.h" /* bad bird */
+#include <iprt/param.h>
+
+
+/**
+ * Creates page tables for a section of the page directory.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param pPgDir The page directory.
+ * @param iFirst The first PD entry.
+ * @param cEntries How many PD entries to create pages tables for.
+ */
+static int Bs3PagingInitPageTablesForPgDir(X86PD BS3_FAR *pPgDir, unsigned iFirst, unsigned cEntries)
+{
+ uint32_t uCurPhys = (uint32_t)iFirst << X86_PD_SHIFT;
+
+ while (cEntries--)
+ {
+ X86PT BS3_FAR *pPt = (X86PT BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, _4K);
+ if (pPt)
+ {
+ unsigned j = 0;
+ for (j = 0; j < RT_ELEMENTS(pPt->a); j++, uCurPhys += PAGE_SIZE)
+ {
+ pPt->a[j].u = uCurPhys;
+ pPt->a[j].u |= X86_PTE_P | X86_PTE_RW | X86_PTE_US | X86_PTE_A | X86_PTE_D;
+ }
+ pPgDir->a[iFirst].u = Bs3SelPtrToFlat(pPt);
+ pPgDir->a[iFirst].u |= X86_PDE_P | X86_PDE_RW | X86_PDE_US | X86_PDE_A;
+ iFirst++;
+ }
+ else
+ return VERR_NO_MEMORY;
+ }
+ return VINF_SUCCESS;
+}
+
+
+#undef Bs3PagingInitRootForPP
+BS3_CMN_DEF(int, Bs3PagingInitRootForPP,(void))
+{
+ X86PD BS3_FAR *pPgDir;
+
+ BS3_ASSERT(g_PhysPagingRootPP == UINT32_MAX);
+
+
+ /*
+ * By default we do a identity mapping of the entire address space
+ * using 4 GB pages. So, we only really need one page directory,
+ * that's all.
+ *
+ * ASSUMES page size extension available, i.e. pentium+.
+ */
+ pPgDir = (X86PD BS3_FAR *)Bs3MemAllocZ(BS3MEMKIND_TILED, _4K);
+ if (pPgDir)
+ {
+ BS3_XPTR_AUTO(X86PD, XptrPgDir);
+ unsigned i;
+ int rc = VINF_SUCCESS;
+
+ if (g_uBs3CpuDetected & BS3CPU_F_PSE)
+ {
+ for (i = 0; i < RT_ELEMENTS(pPgDir->a); i++)
+ {
+ pPgDir->a[i].u = (uint32_t)i << X86_PD_SHIFT;
+ pPgDir->a[i].u |= X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_US | X86_PDE4M_PS | X86_PDE4M_A | X86_PDE4M_D;
+ }
+
+ /* Free up 4 consequtive entries for raw-mode hypervisor code. */
+ if (1) /** @todo detect raw-mode and only do this then. */
+ for (i = 0; i < 4; i++)
+ pPgDir->a[i + (UINT32_C(0xc0000000) >> X86_PD_SHIFT)].b.u1Present = 0;
+ }
+ else
+ {
+ /*
+ * This requires 4MB of page tables if we map everything.
+ * So, we check how much memory we have available and make sure we
+ * don't use all of it for page tables.
+ */
+ unsigned cMax = RT_ELEMENTS(pPgDir->a);
+ uint32_t cFreePages = g_Bs3Mem4KUpperTiled.Core.cFreeChunks + g_Bs3Mem4KLow.Core.cFreeChunks;
+ if (cFreePages >= cMax + 128)
+ Bs3PagingInitPageTablesForPgDir(pPgDir, 0, cMax);
+ else
+ {
+ unsigned cTop;
+ if (cMax >= 256 /*1MB*/)
+ {
+ cMax = cFreePages - 128;
+ cTop = 32;
+ }
+ else if (cMax >= 128)
+ {
+ cMax = cFreePages - 48;
+ cTop = 16;
+ }
+ else
+ {
+ cMax = cFreePages - 16;
+ cTop = RT_MIN(16, cMax / 4);
+ }
+ Bs3TestPrintf("Bs3PagingInitRootForPP: Warning! insufficient memory for mapping all 4GB!\n"
+ " Will only map 0x00000000-%#010RX32 and %#010RX32-0xffffffff.\n",
+ (uint32_t)(cMax - cTop) << PAGE_SHIFT, UINT32_MAX - ((uint32_t)cTop << PAGE_SHIFT) + 1);
+ rc = Bs3PagingInitPageTablesForPgDir(pPgDir, 0, cMax - cTop);
+ if (RT_SUCCESS(rc))
+ rc = Bs3PagingInitPageTablesForPgDir(pPgDir, RT_ELEMENTS(pPgDir->a) - cTop, cTop);
+ }
+ }
+
+ BS3_XPTR_SET(X86PD, XptrPgDir, pPgDir);
+ g_PhysPagingRootPP = BS3_XPTR_GET_FLAT(X86PD, XptrPgDir);
+ return rc;
+ }
+
+ Bs3Printf("Bs3PagingInitRootForPP: No memory!\n");
+ BS3_ASSERT(false);
+ return VERR_NO_MEMORY;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingMapRamAbove4GForLM.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingMapRamAbove4GForLM.c
new file mode 100644
index 00000000..3ce4cbe8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingMapRamAbove4GForLM.c
@@ -0,0 +1,120 @@
+/* $Id: bs3-cmn-PagingMapRamAbove4GForLM.c $ */
+/** @file
+ * BS3Kit - Bs3PagingInitMapAbove4GForLM
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+
+#if ARCH_BITS == 16
+# error "This does not work in 16-bit mode, only 32-bit and 64-bit"
+#endif
+
+#undef Bs3PagingMapRamAbove4GForLM
+BS3_CMN_DEF(int, Bs3PagingMapRamAbove4GForLM,(uint64_t *puFailurePoint))
+{
+ X86PML4 * const pPml4 = (X86PML4 *)((uintptr_t)g_PhysPagingRootLM);
+ unsigned iPml4 = 0;
+ unsigned iPdpt = 4;
+ uint64_t uAddr = _4G;
+ X86PDPT * pPdpt;
+
+ if (puFailurePoint)
+ *puFailurePoint = 0;
+
+ /* Must call Bs3PagingInitRootForLM first! */
+ if (g_PhysPagingRootLM == UINT32_MAX)
+ return VERR_WRONG_ORDER;
+
+ /* Done already? */
+ if (pPml4->a[0].u != pPml4->a[4].u)
+ return VINF_ALREADY_INITIALIZED;
+
+ /*
+ * Map RAM pages up to g_uBs3EndOfRamAbove4G.
+ */
+ pPdpt = (X86PDPT *)(pPml4->a[0].u & X86_PML4E_PG_MASK);
+ while (uAddr < g_uBs3EndOfRamAbove4G)
+ {
+ X86PDPAE *pPd;
+ unsigned i;
+
+ /* Do we need a new PDPT? */
+ if (iPdpt >= RT_ELEMENTS(pPdpt->a))
+ {
+ if (iPml4 >= RT_ELEMENTS(pPml4->a) / 2)
+ {
+ if (puFailurePoint)
+ *puFailurePoint = uAddr;
+ return VERR_OUT_OF_RANGE;
+ }
+ pPdpt = (X86PDPT *)Bs3MemAllocZ(BS3MEMKIND_FLAT32, X86_PAGE_SIZE);
+ if (!pPdpt || ((uintptr_t)pPdpt & X86_PAGE_OFFSET_MASK))
+ {
+ if (puFailurePoint)
+ *puFailurePoint = uAddr;
+ return !pPdpt ? VERR_NO_MEMORY : VERR_UNSUPPORTED_ALIGNMENT;
+ }
+ pPml4->a[++iPml4].u = X86_PML4E_P | X86_PML4E_RW | X86_PML4E_US | X86_PML4E_A
+ | (uintptr_t)pPdpt;
+ iPdpt = 0;
+ }
+
+ /* Allocate a new page directory. */
+ pPd = (X86PDPAE *)Bs3MemAlloc(BS3MEMKIND_FLAT32, X86_PAGE_SIZE);
+ if (!pPd || ((uintptr_t)pPd & X86_PAGE_OFFSET_MASK))
+ {
+ if (puFailurePoint)
+ *puFailurePoint = uAddr;
+ return !pPd ? VERR_NO_MEMORY : VERR_UNSUPPORTED_ALIGNMENT;
+ }
+
+ /* Initialize it. */
+ for (i = 0; i < RT_ELEMENTS(pPd->a); i++)
+ {
+ pPd->a[i].u = uAddr | X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_US | X86_PDE4M_PS | X86_PDE4M_A | X86_PDE4M_D;
+ uAddr += _2M;
+ }
+
+ /* Insert it into the page directory pointer table. */
+ pPdpt->a[iPdpt++].u = (uintptr_t)pPd | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US | X86_PDPE_A;
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingProtect.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingProtect.c
new file mode 100644
index 00000000..347fd3fd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingProtect.c
@@ -0,0 +1,392 @@
+/* $Id: bs3-cmn-PagingProtect.c $ */
+/** @file
+ * BS3Kit - Bs3PagingProtect
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/param.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if 0
+# define BS3PAGING_DPRINTF1(a) Bs3TestPrintf a
+#else
+# define BS3PAGING_DPRINTF1(a) do { } while (0)
+#endif
+#if 0
+# define BS3PAGING_DPRINTF2(a) Bs3TestPrintf a
+#else
+# define BS3PAGING_DPRINTF2(a) do { } while (0)
+#endif
+
+
+static void *bs3PagingBuildPaeTable(uint64_t uTmpl, uint64_t cbIncrement, BS3MEMKIND enmKind, int *prc)
+{
+ uint64_t BS3_FAR *pau64 = (uint64_t BS3_FAR *)Bs3MemAlloc(enmKind, _4K);
+ if (pau64)
+ {
+ unsigned i;
+ for (i = 0; i < _4K / sizeof(uint64_t); i++, uTmpl += cbIncrement)
+ pau64[i] = uTmpl;
+ }
+ else
+ *prc = VERR_NO_MEMORY;
+ return pau64;
+}
+
+
+#undef bs3PagingGetLegacyPte
+BS3_CMN_DEF(X86PTE BS3_FAR *, bs3PagingGetLegacyPte,(RTCCUINTXREG cr3, uint32_t uFlat, bool fUseInvlPg, int *prc))
+{
+ X86PTE BS3_FAR *pPTE = NULL;
+#if TMPL_BITS == 16
+ uint32_t const uMaxAddr = BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode) ? _1M - 1 : BS3_SEL_TILED_AREA_SIZE - 1;
+#else
+ uint32_t const uMaxAddr = UINT32_MAX;
+#endif
+ BS3PAGING_DPRINTF2(("bs3PagingGetLegacyPte: cr3=%RX32 uFlat=%RX32 uMaxAddr=%RX32\n", (uint32_t)cr3, uFlat, uMaxAddr));
+
+ *prc = VERR_OUT_OF_RANGE;
+ if (cr3 <= uMaxAddr)
+ {
+ unsigned const iPde = (uFlat >> X86_PD_SHIFT) & X86_PD_MASK;
+ PX86PD const pPD = (PX86PD)Bs3XptrFlatToCurrent(cr3 & X86_CR3_PAGE_MASK);
+
+ BS3_ASSERT(pPD->a[iPde].b.u1Present);
+ if (pPD->a[iPde].b.u1Present)
+ {
+ unsigned const iPte = (uFlat >> X86_PT_SHIFT) & X86_PT_MASK;
+
+ BS3_ASSERT(pPD->a[iPde].b.u1Present);
+ BS3PAGING_DPRINTF2(("bs3PagingGetLegacyPte: pPD=%p iPde=%#x: %#RX32\n", pPD, iPde, pPD->a[iPde]));
+ if (pPD->a[iPde].b.u1Present)
+ {
+ if (!pPD->a[iPde].b.u1Size)
+ {
+ if (pPD->a[iPde].u <= uMaxAddr)
+ pPTE = &((X86PT BS3_FAR *)Bs3XptrFlatToCurrent(pPD->a[iPde].u & ~(uint32_t)PAGE_OFFSET_MASK))->a[iPte];
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetLegacyPte: out of range! iPde=%#x: %#x\n", iPde, pPD->a[iPde].u));
+ }
+ else
+ {
+ X86PT BS3_FAR *pPT;
+ uint32_t uPte = (pPD->a[iPde].u & ~(uint32_t)(X86_PDE4M_PS | X86_PDE4M_G | X86_PDE4M_PG_HIGH_MASK)) \
+ | X86_PTE_D;
+ if (pPD->a[iPde].b.u1Global)
+ uPte |= X86_PTE_G;
+ if (pPD->a[iPde].b.u1PAT)
+ uPte |= X86_PTE_PAT;
+
+ pPT = (X86PT BS3_FAR *)bs3PagingBuildPaeTable(RT_MAKE_U64(uPte, uPte | PAGE_SIZE),
+ RT_MAKE_U64(PAGE_SIZE*2, PAGE_SIZE*2),
+ uMaxAddr > _1M ? BS3MEMKIND_TILED : BS3MEMKIND_REAL, prc);
+
+ BS3PAGING_DPRINTF2(("bs3PagingGetLegacyPte: Built pPT=%p uPte=%RX32\n", pPT, uPte));
+ if (pPT)
+ {
+ ASMAtomicUoWriteU32(&pPD->a[iPde].u,
+ Bs3SelPtrToFlat(pPT)
+ | ( pPD->a[iPde].u
+ & ~(uint32_t)(X86_PTE_PG_MASK | X86_PDE4M_PS | X86_PDE4M_G | X86_PDE4M_D)));
+ BS3PAGING_DPRINTF2(("bs3PagingGetLegacyPte: iPde=%#x: %#RX32\n", iPde, pPD->a[iPde].u));
+ if (fUseInvlPg)
+ ASMInvalidatePage(uFlat);
+ pPTE = &pPT->a[iPte];
+ }
+ }
+ }
+ }
+ }
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetLegacyPte: out of range! cr3=%#x\n", cr3));
+ return pPTE;
+}
+
+
+/**
+ * Get the PTE for an address, given a PAE or long mode CR3.
+ *
+ * @returns Pointer to the PTE on success, NULL on failure.
+ * @param cr3 The CR3.
+ * @param bMode Indicates whether it's PAE or long mode.
+ * @param uFlat The address for which we want the PTE.
+ * @param fUseInvlPg Whether we can use invalidate page when
+ * replacing large pages.
+ * @param prc Updated only on failure.
+ */
+#undef bs3PagingGetPaePte
+BS3_CMN_DEF(X86PTEPAE BS3_FAR *, bs3PagingGetPaePte,(RTCCUINTXREG cr3, uint8_t bMode, uint64_t uFlat, bool fUseInvlPg, int *prc))
+{
+ X86PTEPAE BS3_FAR *pPTE = NULL;
+#if TMPL_BITS == 16
+ uint32_t const uMaxAddr = BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode) ? _1M - 1 : BS3_SEL_TILED_AREA_SIZE - 1;
+#else
+ uintptr_t const uMaxAddr = ~(uintptr_t)0;
+#endif
+
+ *prc = VERR_OUT_OF_RANGE;
+ if ((cr3 & X86_CR3_AMD64_PAGE_MASK) <= uMaxAddr)
+ {
+ X86PDPAE BS3_FAR *pPD;
+ if (BS3_MODE_IS_64BIT_SYS(bMode))
+ {
+ unsigned const iPml4e = (uFlat >> X86_PML4_SHIFT) & X86_PML4_MASK;
+ X86PML4 BS3_FAR *pPml4 = (X86PML4 BS3_FAR *)Bs3XptrFlatToCurrent(cr3 & X86_CR3_AMD64_PAGE_MASK);
+ BS3_ASSERT(pPml4->a[iPml4e].n.u1Present);
+ if ((pPml4->a[iPml4e].u & X86_PML4E_PG_MASK) <= uMaxAddr)
+ {
+ unsigned const iPdpte = (uFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_AMD64;
+ X86PDPT BS3_FAR *pPdpt = (X86PDPT BS3_FAR *)Bs3XptrFlatToCurrent(pPml4->a[iPml4e].u & X86_PML4E_PG_MASK);
+ BS3_ASSERT(pPdpt->a[iPdpte].n.u1Present);
+ if (!pPdpt->a[iPdpte].b.u1Size)
+ {
+ if ((pPdpt->a[iPdpte].u & X86_PDPE_PG_MASK) <= uMaxAddr)
+ pPD = (X86PDPAE BS3_FAR *)Bs3XptrFlatToCurrent(pPdpt->a[iPdpte].u & ~(uint64_t)PAGE_OFFSET_MASK);
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetPaePte: out of range! iPdpte=%#x: %RX64 max=%RX32\n",
+ iPdpte, pPdpt->a[iPdpte].u, (uint32_t)uMaxAddr));
+ }
+ else
+ {
+ /* Split 1GB page. */
+ pPD = (X86PDPAE BS3_FAR *)bs3PagingBuildPaeTable(pPdpt->a[iPdpte].u, _2M,
+ uMaxAddr > _1M ? BS3MEMKIND_TILED : BS3MEMKIND_REAL, prc);
+ if (pPD)
+ {
+ ASMAtomicUoWriteU64(&pPdpt->a[iPdpte].u,
+ Bs3SelPtrToFlat(pPD)
+ | ( pPdpt->a[iPdpte].u
+ & ~(uint64_t)(X86_PDPE_PG_MASK | X86_PDE4M_PS | X86_PDE4M_G | X86_PDE4M_D)));
+ if (fUseInvlPg)
+ ASMInvalidatePage(uFlat);
+ }
+ }
+ }
+ }
+ //else if (uFlat <= UINT32_MAX) - fixme!
+ else if (!(uFlat >> 32))
+ {
+ unsigned const iPdpte = ((uint32_t)uFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE;
+ X86PDPT BS3_FAR *pPdpt = (X86PDPT BS3_FAR *)Bs3XptrFlatToCurrent(cr3 & X86_CR3_PAE_PAGE_MASK);
+ BS3_ASSERT(pPdpt->a[iPdpte].n.u1Present);
+ if ((pPdpt->a[iPdpte].u & X86_PDPE_PG_MASK) <= uMaxAddr)
+ pPD = (X86PDPAE BS3_FAR *)Bs3XptrFlatToCurrent(pPdpt->a[iPdpte].u & X86_PDPE_PG_MASK);
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetPaePte: out of range! iPdpte=%#x: %RX64 max=%RX32\n",
+ iPdpte, pPdpt->a[iPdpte].u, (uint32_t)uMaxAddr));
+ }
+ else
+ {
+ pPD = NULL;
+ BS3PAGING_DPRINTF1(("bs3PagingGetPaePte: out of range! uFlat=%#RX64 max=%RX32\n", uFlat, (uint32_t)uMaxAddr));
+ }
+ if (pPD)
+ {
+ unsigned const iPte = (uFlat >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK;
+ unsigned const iPde = (uFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
+ if (!pPD->a[iPde].b.u1Size)
+ {
+ if ((pPD->a[iPde].u & X86_PDE_PAE_PG_MASK) <= uMaxAddr)
+ pPTE = &((X86PTPAE BS3_FAR *)Bs3XptrFlatToCurrent(pPD->a[iPde].u & ~(uint64_t)PAGE_OFFSET_MASK))->a[iPte];
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetPaePte: out of range! iPde=%#x: %RX64 max=%RX32\n",
+ iPde, pPD->a[iPde].u, (uint32_t)uMaxAddr));
+ }
+ else
+ {
+ /* Split 2MB page. */
+ X86PTPAE BS3_FAR *pPT;
+ uint64_t uTmpl = pPD->a[iPde].u & ~(uint64_t)(X86_PDE4M_G | X86_PDE4M_PS | X86_PDE4M_PAT);
+ if (!pPD->a[iPde].b.u1Global)
+ uTmpl |= X86_PTE_G;
+ if (!pPD->a[iPde].b.u1PAT)
+ uTmpl |= X86_PTE_PAT;
+
+ pPT = (X86PTPAE BS3_FAR *)bs3PagingBuildPaeTable(uTmpl, PAGE_SIZE,
+ uMaxAddr > _1M ? BS3MEMKIND_TILED : BS3MEMKIND_REAL, prc);
+ if (pPT)
+ {
+ ASMAtomicUoWriteU64(&pPD->a[iPde].u,
+ Bs3SelPtrToFlat(pPT)
+ | ( pPD->a[iPde].u
+ & ~(uint64_t)(X86_PTE_PAE_PG_MASK | X86_PDE4M_PS | X86_PDE4M_G | X86_PDE4M_D)));
+ if (fUseInvlPg)
+ ASMInvalidatePage(uFlat);
+ pPTE = &pPT->a[iPte];
+ }
+ }
+ }
+ }
+ else
+ BS3PAGING_DPRINTF1(("bs3PagingGetPaePte: out of range! cr3=%#RX32 uMaxAddr=%#RX32\n", (uint32_t)cr3, (uint32_t)uMaxAddr));
+ return pPTE;
+}
+
+
+#undef Bs3PagingProtect
+BS3_CMN_DEF(int, Bs3PagingProtect,(uint64_t uFlat, uint64_t cb, uint64_t fSet, uint64_t fClear))
+{
+#if ARCH_BITS == 16
+ if (!BS3_MODE_IS_V86(g_bBs3CurrentMode))
+#endif
+ {
+ RTCCUINTXREG const cr3 = ASMGetCR3();
+ RTCCUINTXREG const cr4 = g_uBs3CpuDetected & BS3CPU_F_CPUID ? ASMGetCR4() : 0;
+ bool const fLegacyPTs = !(cr4 & X86_CR4_PAE);
+ bool const fUseInvlPg = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486
+ && ( cb < UINT64_C(16)*PAGE_SIZE
+ || (cr4 & X86_CR4_PGE));
+ unsigned cEntries;
+ int rc;
+
+ /*
+ * Adjust the range parameters.
+ */
+ cb += uFlat & PAGE_OFFSET_MASK;
+ cb = RT_ALIGN_64(cb, PAGE_SIZE);
+ uFlat &= ~(uint64_t)PAGE_OFFSET_MASK;
+
+ fSet &= ~X86_PTE_PAE_PG_MASK;
+ fClear &= ~X86_PTE_PAE_PG_MASK;
+
+ BS3PAGING_DPRINTF1(("Bs3PagingProtect: uFlat=%RX64 cb=%RX64 fSet=%RX64 fClear=%RX64 %s %s\n", uFlat, cb, fSet, fClear,
+ fLegacyPTs ? "legacy" : "pae/amd64", fUseInvlPg ? "invlpg" : "reload-cr3"));
+ if (fLegacyPTs)
+ {
+ /*
+ * Legacy page tables.
+ */
+ while ((uint32_t)cb > 0)
+ {
+ PX86PTE pPte = BS3_CMN_FAR_NM(bs3PagingGetLegacyPte)(cr3, (uint32_t)uFlat, fUseInvlPg, &rc);
+ if (!pPte)
+ return rc;
+
+ cEntries = X86_PG_ENTRIES - ((uFlat >> X86_PT_SHIFT) & X86_PT_MASK);
+ while (cEntries-- > 0 && cb > 0)
+ {
+ pPte->u &= ~(uint32_t)fClear;
+ pPte->u |= (uint32_t)fSet;
+ if (fUseInvlPg)
+ ASMInvalidatePage(uFlat);
+
+ pPte++;
+ uFlat += PAGE_SIZE;
+ cb -= PAGE_SIZE;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Long mode or PAE page tables (at this level they are the same).
+ */
+ while (cb > 0)
+ {
+ PX86PTEPAE pPte = BS3_CMN_FAR_NM(bs3PagingGetPaePte)(cr3, g_bBs3CurrentMode, uFlat, fUseInvlPg, &rc);
+ if (!pPte)
+ return rc;
+
+ cEntries = X86_PG_ENTRIES - ((uFlat >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
+ while (cEntries-- > 0 && cb > 0)
+ {
+ pPte->u &= ~fClear;
+ pPte->u |= fSet;
+ if (fUseInvlPg)
+ ASMInvalidatePage(uFlat);
+
+ pPte++;
+ uFlat += PAGE_SIZE;
+ cb -= PAGE_SIZE;
+ }
+ }
+ }
+
+ /*
+ * Flush the TLB if we didn't use INVLPG above.
+ */
+ BS3PAGING_DPRINTF2(("Bs3PagingProtect: reloading cr3=%RX32\n", (uint32_t)cr3));
+ //if (!fUseInvlPg)
+ ASMSetCR3(cr3);
+ BS3PAGING_DPRINTF2(("Bs3PagingProtect: reloaded cr3=%RX32\n", (uint32_t)cr3));
+ }
+#if ARCH_BITS == 16
+ /*
+ * We can do this stuff in v8086 mode.
+ */
+ else
+ return Bs3SwitchFromV86To16BitAndCallC((FPFNBS3FAR)Bs3PagingProtect_f16, sizeof(uint64_t) * 4, uFlat, cb, fSet, fClear);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+#undef Bs3PagingProtectPtr
+BS3_CMN_DEF(int, Bs3PagingProtectPtr,(void *pv, size_t cb, uint64_t fSet, uint64_t fClear))
+{
+#if ARCH_BITS == 16
+ return BS3_CMN_NM(Bs3PagingProtect)(Bs3SelPtrToFlat(pv), cb, fSet, fClear);
+#else
+ return BS3_CMN_NM(Bs3PagingProtect)((uintptr_t)pv, cb, fSet, fClear);
+#endif
+}
+
+
+#undef Bs3PagingGetPte
+BS3_CMN_DEF(void BS3_FAR *, Bs3PagingGetPte,(uint64_t uFlat, int *prc))
+{
+ RTCCUINTXREG const cr3 = ASMGetCR3();
+ RTCCUINTXREG const cr4 = g_uBs3CpuDetected & BS3CPU_F_CPUID ? ASMGetCR4() : 0;
+ bool const fLegacyPTs = !(cr4 & X86_CR4_PAE);
+ bool const fUseInvlPg = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486;
+ int rc;
+ if (!prc)
+ prc = &rc;
+ if (!fLegacyPTs)
+ return BS3_CMN_FAR_NM(bs3PagingGetPaePte)(cr3, g_bBs3CurrentMode, uFlat, fUseInvlPg, prc);
+ if (uFlat < _4G)
+ return BS3_CMN_FAR_NM(bs3PagingGetLegacyPte)(cr3, (uint32_t)uFlat, fUseInvlPg, prc);
+ *prc = VERR_OUT_OF_RANGE;
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingQueryAddressInfo.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingQueryAddressInfo.c
new file mode 100644
index 00000000..b846b248
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingQueryAddressInfo.c
@@ -0,0 +1,159 @@
+/* $Id: bs3-cmn-PagingQueryAddressInfo.c $ */
+/** @file
+ * BS3Kit - Bs3PagingQueryAddressInfo
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+#include <VBox/err.h>
+
+
+#undef Bs3PagingQueryAddressInfo
+BS3_CMN_DEF(int, Bs3PagingQueryAddressInfo,(uint64_t uFlat, PBS3PAGINGINFO4ADDR pPgInfo))
+{
+ RTCCUINTXREG const cr3 = ASMGetCR3();
+ RTCCUINTXREG const cr4 = g_uBs3CpuDetected & BS3CPU_F_CPUID ? ASMGetCR4() : 0;
+ bool const fLegacyPTs = !(cr4 & X86_CR4_PAE);
+ int rc = VERR_OUT_OF_RANGE;
+
+
+ pPgInfo->fFlags = 0;
+ pPgInfo->u.apbEntries[0] = NULL;
+ pPgInfo->u.apbEntries[1] = NULL;
+ pPgInfo->u.apbEntries[2] = NULL;
+ pPgInfo->u.apbEntries[3] = NULL;
+
+ if (!fLegacyPTs)
+ {
+#if TMPL_BITS == 16
+ uint32_t const uMaxAddr = BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode) ? _1M - 1 : BS3_SEL_TILED_AREA_SIZE - 1;
+#else
+ uintptr_t const uMaxAddr = ~(uintptr_t)0;
+#endif
+ uint64_t const fEfer = g_uBs3CpuDetected & BS3CPU_F_LONG_MODE ? ASMRdMsr(MSR_K6_EFER) : 0;
+
+ pPgInfo->cEntries = fEfer & MSR_K6_EFER_LMA ? 4 : 3;
+ pPgInfo->cbEntry = sizeof(X86PTEPAE);
+ if ((cr3 & X86_CR3_AMD64_PAGE_MASK) <= uMaxAddr)
+ {
+ if ( (fEfer & MSR_K6_EFER_LMA)
+ && X86_IS_CANONICAL(uFlat))
+ {
+ /* 48-bit long mode paging. */
+ pPgInfo->u.Pae.pPml4e = (X86PML4E BS3_FAR *)Bs3XptrFlatToCurrent(cr3 & X86_CR3_AMD64_PAGE_MASK);
+ pPgInfo->u.Pae.pPml4e += (uFlat >> X86_PML4_SHIFT) & X86_PML4_MASK;
+ if (!pPgInfo->u.Pae.pPml4e->n.u1Present)
+ rc = VERR_PAGE_NOT_PRESENT;
+ else if ((pPgInfo->u.Pae.pPml4e->u & X86_PML4E_PG_MASK) <= uMaxAddr)
+ {
+ pPgInfo->u.Pae.pPdpe = (X86PDPE BS3_FAR *)Bs3XptrFlatToCurrent(pPgInfo->u.Pae.pPml4e->u & X86_PML4E_PG_MASK);
+ pPgInfo->u.Pae.pPdpe += (uFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_AMD64;
+ if (!pPgInfo->u.Pae.pPdpe->n.u1Present)
+ rc = VERR_PAGE_NOT_PRESENT;
+ else if (pPgInfo->u.Pae.pPdpe->b.u1Size)
+ rc = VINF_SUCCESS;
+ else
+ rc = VINF_TRY_AGAIN;
+ }
+ }
+ else if ( !(fEfer & MSR_K6_EFER_LMA)
+ && uFlat <= _4G)
+ {
+ /* 32-bit PAE paging. */
+ pPgInfo->u.Pae.pPdpe = (X86PDPE BS3_FAR *)Bs3XptrFlatToCurrent(cr3 & X86_CR3_PAE_PAGE_MASK);
+ pPgInfo->u.Pae.pPdpe += ((uint32_t)uFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE;
+ if (!pPgInfo->u.Pae.pPdpe->n.u1Present)
+ rc = VERR_PAGE_NOT_PRESENT;
+ else
+ rc = VINF_TRY_AGAIN;
+ }
+
+ /* Common code for the PD and PT levels. */
+ if ( rc == VINF_TRY_AGAIN
+ && (pPgInfo->u.Pae.pPdpe->u & X86_PDPE_PG_MASK) <= uMaxAddr)
+ {
+ rc = VERR_OUT_OF_RANGE;
+ pPgInfo->u.Pae.pPde = (X86PDEPAE BS3_FAR *)Bs3XptrFlatToCurrent(pPgInfo->u.Pae.pPdpe->u & X86_PDPE_PG_MASK);
+ pPgInfo->u.Pae.pPde += (uFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
+ if (!pPgInfo->u.Pae.pPde->n.u1Present)
+ rc = VERR_PAGE_NOT_PRESENT;
+ else if (pPgInfo->u.Pae.pPde->b.u1Size)
+ rc = VINF_SUCCESS;
+ else if ((pPgInfo->u.Pae.pPde->u & X86_PDE_PAE_PG_MASK) <= uMaxAddr)
+ {
+ pPgInfo->u.Pae.pPte = (X86PTEPAE BS3_FAR *)Bs3XptrFlatToCurrent(pPgInfo->u.Pae.pPde->u & X86_PDE_PAE_PG_MASK);
+ rc = VINF_SUCCESS;
+ }
+ }
+ else if (rc == VINF_TRY_AGAIN)
+ rc = VERR_OUT_OF_RANGE;
+ }
+ }
+ else
+ {
+#if TMPL_BITS == 16
+ uint32_t const uMaxAddr = BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode) ? _1M - 1 : BS3_SEL_TILED_AREA_SIZE - 1;
+#else
+ uint32_t const uMaxAddr = UINT32_MAX;
+#endif
+
+ pPgInfo->cEntries = 2;
+ pPgInfo->cbEntry = sizeof(X86PTE);
+ if ( uFlat < _4G
+ && cr3 <= uMaxAddr)
+ {
+ pPgInfo->u.Legacy.pPde = (X86PDE BS3_FAR *)Bs3XptrFlatToCurrent(cr3 & X86_CR3_PAGE_MASK);
+ pPgInfo->u.Legacy.pPde += ((uint32_t)uFlat >> X86_PD_SHIFT) & X86_PD_MASK;
+ if (!pPgInfo->u.Legacy.pPde->b.u1Present)
+ rc = VERR_PAGE_NOT_PRESENT;
+ else if (pPgInfo->u.Legacy.pPde->b.u1Size)
+ rc = VINF_SUCCESS;
+ else if (pPgInfo->u.Legacy.pPde->u <= uMaxAddr)
+ {
+ pPgInfo->u.Legacy.pPte = (X86PTE BS3_FAR *)Bs3XptrFlatToCurrent(pPgInfo->u.Legacy.pPde->u & X86_PDE_PG_MASK);
+ pPgInfo->u.Legacy.pPte += ((uint32_t)uFlat >> X86_PT_SHIFT) & X86_PT_MASK;
+ if (pPgInfo->u.Legacy.pPte->n.u1Present)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_PAGE_NOT_PRESENT;
+ }
+ }
+ }
+ return rc;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingSetupCanonicalTraps.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingSetupCanonicalTraps.c
new file mode 100644
index 00000000..3343819f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PagingSetupCanonicalTraps.c
@@ -0,0 +1,123 @@
+/* $Id: bs3-cmn-PagingSetupCanonicalTraps.c $ */
+/** @file
+ * BS3Kit - Bs3PagingSetupCanonicalTraps
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-paging.h"
+#include "iprt/asm-amd64-x86.h"
+
+
+#undef Bs3PagingSetupCanonicalTraps
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3PagingSetupCanonicalTraps,(void))
+{
+ if (g_uBs3CpuDetected & BS3CPU_F_LONG_MODE)
+ {
+#if ARCH_BITS == 16
+ if (!BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+#endif
+ {
+ uint8_t BS3_FAR *pb;
+ X86PTEPAE BS3_FAR *paLoPtes;
+ X86PTEPAE BS3_FAR *paHiPtes;
+ int rc;
+
+ /* Already initialized? Likely. */
+ if (g_cbBs3PagingCanonicalTraps != 0)
+ return Bs3XptrFlatToCurrent(g_uBs3PagingCanonicalTrapsAddr);
+
+ /* Initialize AMD64 page tables if necessary (unlikely). */
+ if (g_PhysPagingRootLM == UINT32_MAX)
+ {
+ rc = Bs3PagingInitRootForLM();
+ if (RT_FAILURE(rc))
+ return NULL;
+ }
+
+ /*
+ * Get the page table entries first to avoid having to unmap things.
+ */
+ paLoPtes = bs3PagingGetPaePte(g_PhysPagingRootLM, BS3_MODE_LM64, UINT64_C(0x00007fffffffe000), false, &rc);
+ paHiPtes = bs3PagingGetPaePte(g_PhysPagingRootLM, BS3_MODE_LM64, UINT64_C(0xffff800000000000), false, &rc);
+ if (!paHiPtes || !paLoPtes)
+ {
+ Bs3TestPrintf("warning: Bs3PagingSetupCanonicalTraps - failed to get PTEs!\n");
+ return NULL;
+ }
+
+ /*
+ * Allocate the buffer. Currently using 8KB on each side.
+ */
+ pb = (uint8_t BS3_FAR *)Bs3MemAlloc(BS3MEMKIND_TILED, X86_PAGE_SIZE * 4);
+ if (pb)
+ {
+ RTCCUINTXREG uFlat = Bs3SelPtrToFlat(pb);
+
+ /*
+ * Inject it into the page tables.
+ */
+ paLoPtes[0].u &= ~X86_PTE_PAE_PG_MASK;
+ paLoPtes[0].u |= uFlat + X86_PAGE_SIZE * 0;
+ paLoPtes[1].u &= ~X86_PTE_PAE_PG_MASK;
+ paLoPtes[1].u |= uFlat + X86_PAGE_SIZE * 1;
+
+ paHiPtes[0].u &= ~X86_PTE_PAE_PG_MASK;
+ paHiPtes[0].u |= uFlat + X86_PAGE_SIZE * 2;
+ paHiPtes[1].u &= ~X86_PTE_PAE_PG_MASK;
+ paHiPtes[1].u |= uFlat + X86_PAGE_SIZE * 3;
+ ASMReloadCR3();
+
+ /*
+ * Update globals and return successfully.
+ */
+ g_uBs3PagingCanonicalTrapsAddr = uFlat;
+ g_cbBs3PagingCanonicalTraps = X86_PAGE_SIZE * 4;
+ g_cbBs3PagingOneCanonicalTrap = X86_PAGE_SIZE * 2;
+ return pb;
+ }
+
+ Bs3TestPrintf("warning: Bs3PagingSetupCanonicalTraps - out of memory (mode %#x)\n", g_bBs3CurrentMode);
+ }
+#if ARCH_BITS == 16
+ else
+ Bs3TestPrintf("warning: Bs3PagingSetupCanonicalTraps was called in RM or V86 mode (%#x)!\n", g_bBs3CurrentMode);
+#endif
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Panic.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Panic.asm
new file mode 100644
index 00000000..e4d286c8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Panic.asm
@@ -0,0 +1,48 @@
+; $Id: bs3-cmn-Panic.asm $
+;; @file
+; BS3Kit - Bs3Panic, Common.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_PROC_BEGIN_CMN Bs3Panic, BS3_PBC_HYBRID_0_ARGS
+ push xBP
+ mov xBP, xSP
+ cli
+.panic_again:
+ hlt
+ jmp .panic_again
+BS3_PROC_END_CMN Bs3Panic
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PerCpuData.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PerCpuData.c
new file mode 100644
index 00000000..9ebe7096
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PerCpuData.c
@@ -0,0 +1,74 @@
+/* $Id: bs3-cmn-PerCpuData.c $ */
+/** @file
+ * BS3Kit - Per CPU Data.
+ *
+ * @remarks Not quite sure how to do per-cpu data yet, but this is stuff
+ * that eventually needs to be per CPU.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+
+/** Hint for 16-bit trap handlers regarding the high word of EIP. */
+uint32_t g_uBs3TrapEipHint = 0;
+
+/** Flat pointer to a BS3TRAPFRAME registered by Bs3TrapSetJmp.
+ * When this is non-zero, the setjmp is considered armed. */
+uint32_t g_pBs3TrapSetJmpFrame = 0;
+
+/** The current CPU mode. */
+uint8_t g_bBs3CurrentMode = BS3_MODE_RM;
+
+uint8_t g_bStupidUnalignedCompiler1 = 0xfe;
+
+/** Set to disable special V8086 \#GP and \#UD handling in Bs3TrapDefaultHandler.
+ * This is useful for getting */
+bool volatile g_fBs3TrapNoV86Assist = false;
+
+/** The context of the last Bs3TrapSetJmp call.
+ * This will have eax set to 1 and need only be restored when it triggers. */
+BS3REGCTX g_Bs3TrapSetJmpCtx;
+
+#endif /* ARCH_BITS == 16 */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicMaskAll.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicMaskAll.c
new file mode 100644
index 00000000..19946af6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicMaskAll.c
@@ -0,0 +1,51 @@
+/* $Id: bs3-cmn-PicMaskAll.c $ */
+/** @file
+ * BS3Kit - Masks all IRQs on the PIC.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3PicMaskAll
+BS3_CMN_DEF(void, Bs3PicMaskAll,(void))
+{
+ ASMOutU8(0xa1, 0xff);
+ ASMOutU8(0x21, 0xff);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicSetup.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicSetup.c
new file mode 100644
index 00000000..7db73469
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicSetup.c
@@ -0,0 +1,90 @@
+/* $Id: bs3-cmn-PicSetup.c $ */
+/** @file
+ * BS3Kit - PIC Setup.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+#include "bs3-cmn-pic.h"
+
+
+
+/**
+ * Configures the PIC, once only.
+ *
+ * Subsequent calls to this function will not do anything.
+ *
+ * The PIC will be programmed to use IDT/IVT vectors 0x70 thru 0x7f, auto
+ * end-of-interrupt, and all IRQs masked. The individual PIC users will have to
+ * use #Bs3PicUpdateMask unmask their IRQ once they've got all the handlers
+ * installed.
+ */
+#undef Bs3PicSetup
+BS3_CMN_DEF(void, Bs3PicSetup,(bool fForcedReInit))
+{
+ /*
+ * The first call configures the PIC to send interrupts to vectors 0x70 thru 0x7f,
+ * masking all of them. Things producing IRQs is responsible for configure their
+ * handlers and then(!) use Bs3PicUpdateMask to unmask the IRQ.
+ */
+ if (!g_fBs3PicConfigured || fForcedReInit)
+ {
+ g_fBs3PicConfigured = true;
+
+ /* Start init. */
+ ASMOutU8(BS3_PIC_PORT_MASTER, BS3_PIC_CMD_INIT | BS3_PIC_CMD_INIT_F_4STEP);
+ ASMOutU8(BS3_PIC_PORT_SLAVE, BS3_PIC_CMD_INIT | BS3_PIC_CMD_INIT_F_4STEP);
+
+ /* Set IRQ base. */
+ ASMOutU8(BS3_PIC_PORT_MASTER + 1, 0x70);
+ ASMOutU8(BS3_PIC_PORT_SLAVE + 1, 0x78);
+
+ /* Dunno. */
+ ASMOutU8(BS3_PIC_PORT_MASTER + 1, 4);
+ ASMOutU8(BS3_PIC_PORT_SLAVE + 1, 2);
+
+ /* Set IRQ base. */
+ ASMOutU8(BS3_PIC_PORT_MASTER + 1, BS3_PIC_I4_F_AUTO_EOI);
+ ASMOutU8(BS3_PIC_PORT_SLAVE + 1, BS3_PIC_I4_F_AUTO_EOI);
+
+ /* Mask everything. */
+ ASMOutU8(BS3_PIC_PORT_MASTER + 1, UINT8_MAX);
+ ASMOutU8(BS3_PIC_PORT_SLAVE + 1, UINT8_MAX);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicUpdateMask.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicUpdateMask.c
new file mode 100644
index 00000000..6485a236
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PicUpdateMask.c
@@ -0,0 +1,55 @@
+/* $Id: bs3-cmn-PicUpdateMask.c $ */
+/** @file
+ * BS3Kit - PIC Setup.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+#include "bs3-cmn-pic.h"
+
+
+#undef Bs3PicUpdateMask
+BS3_CMN_DEF(uint16_t, Bs3PicUpdateMask,(uint16_t fAndMask, uint16_t fOrMask))
+{
+ uint8_t bPic0Mask = (ASMInU8(BS3_PIC_PORT_MASTER + 1) & (uint8_t)fAndMask) | (uint8_t)fOrMask;
+ uint8_t bPic1Mask = (ASMInU8(BS3_PIC_PORT_SLAVE + 1) & (fAndMask >> 8)) | (fOrMask >> 8);
+ ASMOutU8(BS3_PIC_PORT_SLAVE + 1, bPic1Mask);
+ ASMOutU8(BS3_PIC_PORT_MASTER + 1, bPic0Mask);
+ return RT_MAKE_U16(bPic0Mask, bPic1Mask);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PitIrqHandler.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PitIrqHandler.c
new file mode 100644
index 00000000..b296bb11
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PitIrqHandler.c
@@ -0,0 +1,76 @@
+/* $Id: bs3-cmn-PitIrqHandler.c $ */
+/** @file
+ * BS3Kit - The PIT IRQ Handler and associated data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+/** Nano seconds (approx) since last the PIT timer was started. */
+uint64_t volatile g_cBs3PitNs = 0;
+/** Milliseconds seconds (very approx) since last the PIT timer was started. */
+uint64_t volatile g_cBs3PitMs = 0;
+/** Number of ticks since last the PIT timer was started. */
+uint32_t volatile g_cBs3PitTicks = 0;
+/** The current interval in nanon seconds. */
+uint32_t g_cBs3PitIntervalNs = 0;
+/** The current interval in milliseconds (approximately).
+ * This is 0 if not yet started (used for checking the state internally). */
+uint16_t g_cBs3PitIntervalMs = 0;
+/** The current PIT frequency (approximately). 0 if not yet started. */
+uint16_t volatile g_cBs3PitIntervalHz = 0;
+#endif
+
+
+BS3_DECL_NEAR_CALLBACK(void) BS3_CMN_NM(bs3PitIrqHandler)(PBS3TRAPFRAME pTrapFrame)
+{
+ if (g_cBs3PitIntervalHz)
+ {
+ g_cBs3PitMs += g_cBs3PitIntervalMs;
+ g_cBs3PitNs += g_cBs3PitIntervalNs;
+ g_cBs3PitTicks++;
+ }
+ NOREF(pTrapFrame);
+ ASMOutU8(0x20, 0x20); /** @todo function! */
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintChr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintChr.asm
new file mode 100644
index 00000000..90c7d6c1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintChr.asm
@@ -0,0 +1,115 @@
+; $Id: bs3-cmn-PrintChr.asm $
+;; @file
+; BS3Kit - Bs3PrintChr.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_CMN Bs3Syscall
+
+
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(void) Bs3PrintChr_c16(char ch);
+;
+BS3_PROC_BEGIN_CMN Bs3PrintChr, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xCX
+ push xBX
+
+%if TMPL_BITS == 16
+ ; If we're in real mode or v8086 mode, call the VGA BIOS directly.
+ mov bl, [g_bBs3CurrentMode]
+ cmp bl, BS3_MODE_RM
+ je .do_vga_bios_call
+ %if 0
+ test bl, BS3_MODE_CODE_V86
+ jz .do_system_call
+ %else
+ jmp .do_system_call
+ %endif
+
+.do_vga_bios_call:
+ mov al, [xBP + xCB*2] ; Load the char
+ cmp al, 0ah ; \n
+ je .newline
+ mov bx, 0ff00h
+ mov ah, 0eh
+ int 10h
+ jmp .return
+.newline:
+ mov ax, 0e0dh ; cmd + '\r'.
+ mov bx, 0ff00h
+ int 10h
+ mov ax, 0e0ah ; cmd + '\n'.
+ mov bx, 0ff00h
+ int 10h
+ jmp .return
+%endif
+
+.do_system_call:
+ mov cl, [xBP + xCB*2] ; Load the char
+ mov ax, BS3_SYSCALL_PRINT_CHR
+ call Bs3Syscall ; near! no BS3_CALL!
+
+.return:
+ pop xBX
+ pop xCX
+ pop xAX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ ret
+BS3_PROC_END_CMN Bs3PrintChr
+
+;
+; Generate 16-bit far stub.
+; Peformance critical, so don't penalize near calls.
+;
+BS3_CMN_FAR_STUB Bs3PrintChr, 2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStr.c
new file mode 100644
index 00000000..7ff63b60
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStr.c
@@ -0,0 +1,44 @@
+/* $Id: bs3-cmn-PrintStr.c $ */
+/** @file
+ * BS3Kit - Bs3PrintStr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3PrintStr
+BS3_CMN_DEF(void, Bs3PrintStr,(const char BS3_FAR *pszString))
+{
+ Bs3PrintStrN(pszString, Bs3StrLen(pszString));
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStrN.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStrN.asm
new file mode 100644
index 00000000..4a0fe223
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintStrN.asm
@@ -0,0 +1,204 @@
+; $Id: bs3-cmn-PrintStrN.asm $
+;; @file
+; BS3Kit - Bs3PrintStrN.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_CMN Bs3Syscall
+
+
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(void) Bs3PrintStrN_c16(const char BS3_FAR *pszString, size_t cchString);
+;
+; ASSUMES cchString < 64KB!
+;
+BS3_PROC_BEGIN_CMN Bs3PrintStrN, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xCX
+ push xBX
+ push xSI
+
+%if TMPL_BITS == 16
+ ; If we're in real mode or v8086 mode, call the VGA BIOS directly.
+ mov bl, [g_bBs3CurrentMode]
+ cmp bl, BS3_MODE_RM
+ je .do_bios_call
+ %if 0
+ test bl, BS3_MODE_CODE_V86
+ jz .do_system_call
+ %else
+ jmp .do_system_call
+ %endif
+
+ ;
+ ; We can do the work right here.
+ ;
+.do_bios_call:
+ push ds
+ lds si, [xBP + xCB + cbCurRetAddr] ; DS:SI -> string.
+ cld
+ mov cx, [xBP + xCB + cbCurRetAddr + sCB] ; Use CX for counting down.
+ call Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
+ pop ds
+ jmp .return
+%endif
+
+
+ ;
+ ; Need to do system call(s).
+ ; String goes into CX:xSI, count into DX.
+ ;
+ ; We must ensure the string is real-mode addressable first, if not we
+ ; must do it char-by-char.
+ ;
+.do_system_call:
+%if TMPL_BITS == 16
+ mov cx, [xBP + xCB + cbCurRetAddr + 2]
+%else
+ mov cx, ds
+%endif
+ mov xSI, [xBP + xCB + cbCurRetAddr]
+ mov dx, [xBP + xCB + cbCurRetAddr + sCB]
+%if TMPL_BITS == 16
+
+%else
+ cmp xSI, _1M
+ jae .char_by_char
+%endif
+ mov ax, BS3_SYSCALL_PRINT_STR
+ call Bs3Syscall ; near! no BS3_CALL!
+
+.return:
+ pop xSI
+ pop xBX
+ pop xCX
+ pop xAX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+
+ ;
+ ; Doesn't look like it's real-mode addressable. So, char-by-char.
+ ;
+.char_by_char:
+%if TMPL_BITS == 16
+ push es
+ mov es, cx
+%endif
+ cld
+ test dx, dx
+ jz .char_by_char_return
+.char_by_char_loop:
+ mov ax, BS3_SYSCALL_PRINT_CHR
+ mov cl, [BS3_ONLY_16BIT(es:) xSI]
+ call Bs3Syscall ; near! no BS3_CALL!
+ inc xSI
+ dec xDX
+ jnz .char_by_char_loop
+.char_by_char_return:
+%if TMPL_BITS == 16
+ pop es
+%endif
+ jmp .return
+
+BS3_PROC_END_CMN Bs3PrintStrN
+
+
+%if TMPL_BITS == 16
+;
+; This code is shared with the system handler.
+;
+; @param CX Number of byte sto print.
+; @param DS:SI The string to print
+; @uses AX, BX, CX, SI
+;
+BS3_PROC_BEGIN Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
+ CPU 8086
+ ; Check if CX is zero first.
+ test cx, cx
+ jz .bios_loop_done
+
+ ; The loop, processing the string char-by-char.
+.bios_loop:
+ mov bx, 0ff00h
+ lodsb ; al = next char
+ cmp al, 0ah ; \n
+ je .bios_loop_newline
+%ifdef BS3_STRICT
+ test al, al
+ jnz .not_zero
+ hlt
+.not_zero:
+%endif
+ mov ah, 0eh
+.bios_loop_int10h:
+ int 10h
+ loop .bios_loop
+.bios_loop_done:
+ ret
+
+.bios_loop_newline:
+ mov ax, 0e0dh ; cmd + '\r'.
+ int 10h
+ mov ax, 0e0ah ; cmd + '\n'.
+ mov bx, 0ff00h
+ jmp .bios_loop_int10h
+BS3_PROC_END Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
+
+
+;
+; Generate 16-bit far stub.
+; Peformance critical, so don't penalize near calls.
+;
+BS3_CMN_FAR_STUB Bs3PrintStrN, 6
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintU32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintU32.asm
new file mode 100644
index 00000000..90be607d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintU32.asm
@@ -0,0 +1,93 @@
+; $Id: bs3-cmn-PrintU32.asm $
+;; @file
+; BS3Kit - Bs3PrintU32, Common.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3PrintStr
+
+;;
+; Prints a 32-bit unsigned integer value.
+;
+; @param [xBP + xCB*2] 32-bit value to format and print.
+;
+BS3_PROC_BEGIN_CMN Bs3PrintU32, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ push sBX
+BONLY16 push ds
+
+ mov eax, [xBP + xCB + cbCurRetAddr]
+
+ ; Allocate a stack buffer and terminate it. ds:bx points ot the end.
+ sub xSP, 30h
+BONLY16 mov bx, ss
+BONLY16 mov ds, bx
+ mov xBX, xSP
+ add xBX, 2fh
+ mov byte [xBX], 0
+
+ mov ecx, 10 ; what to divide by
+.next:
+ xor edx, edx
+ div ecx ; edx:eax / ecx -> eax and rest in edx.
+ add dl, '0'
+ dec xBX
+ mov [BS3_ONLY_16BIT(ss:)xBX], dl
+ cmp eax, 0
+ jnz .next
+
+ ; Print the string.
+BONLY64 add rsp, 18h
+BONLY16 push ss
+ push xBX
+ BS3_CALL Bs3PrintStr, 1
+
+ add xSP, 30h + BS3_IF_16_32_64BIT(2, 0, 18h) + xCB
+BONLY16 pop ds
+ pop sBX
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3PrintU32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintX32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintX32.asm
new file mode 100644
index 00000000..6469ac08
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-PrintX32.asm
@@ -0,0 +1,97 @@
+; $Id: bs3-cmn-PrintX32.asm $
+;; @file
+; BS3Kit - Bs3PrintU32, Common.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3PrintStr
+
+;;
+; Prints a 32-bit unsigned integer value as hex.
+;
+; @param [xBP + xCB*2] 32-bit value to format and print.
+;
+BS3_PROC_BEGIN_CMN Bs3PrintX32, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sDX
+ push sCX
+ push sBX
+BONLY16 push ds
+
+ mov eax, [xBP + xCB + cbCurRetAddr]
+
+ ; Allocate a stack buffer and terminate it. ds:bx points ot the end.
+ sub xSP, 30h
+BONLY16 mov bx, ss
+BONLY16 mov ds, bx
+ mov xBX, xSP
+ add xBX, 2fh
+ mov byte [xBX], 0
+
+ mov ecx, 16 ; what to divide by
+.next:
+ xor edx, edx
+ div ecx ; edx:eax / ecx -> eax and rest in edx.
+ cmp dl, 10
+ jb .decimal
+ add dl, 'a' - '0' - 10
+.decimal:
+ add dl, '0'
+ dec xBX
+ mov [BS3_ONLY_16BIT(ss:)xBX], dl
+ cmp eax, 0
+ jnz .next
+
+ ; Print the string.
+BONLY64 add rsp, 18h
+BONLY16 push ss
+ push xBX
+ BS3_CALL Bs3PrintStr, 1
+
+ add xSP, 30h + BS3_IF_16_32_64BIT(2, 0, 18h) + xCB
+BONLY16 pop ds
+ pop sBX
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3PrintX32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Printf.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Printf.c
new file mode 100644
index 00000000..29fcd518
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Printf.c
@@ -0,0 +1,95 @@
+/* $Id: bs3-cmn-Printf.c $ */
+/** @file
+ * BS3Kit - Bs3Printf, Bs3PrintfV
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/ctype.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Output buffering for Bs3TestPrintfV. */
+typedef struct BS3PRINTBUF
+{
+ uint8_t cchBuf;
+ char achBuf[79];
+} BS3PRINTBUF;
+
+
+static BS3_DECL_CALLBACK(size_t) bs3PrintFmtOutput(char ch, void BS3_FAR *pvUser)
+{
+ BS3PRINTBUF BS3_FAR *pBuf = (BS3PRINTBUF BS3_FAR *)pvUser;
+ if (ch != '\0')
+ {
+ BS3_ASSERT(pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf));
+ pBuf->achBuf[pBuf->cchBuf++] = ch;
+
+ /* Whether to flush the buffer. We do line flushing here to avoid
+ dropping too much info when the formatter crashes on bad input. */
+ if ( pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf)
+ && ch != '\n')
+ return 1;
+ }
+ Bs3PrintStrN(&pBuf->achBuf[0], pBuf->cchBuf);
+ pBuf->cchBuf = 0;
+ return ch != '\0';
+}
+
+
+#undef Bs3PrintfV
+BS3_CMN_DEF(size_t, Bs3PrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va))
+{
+ BS3PRINTBUF Buf;
+ Buf.cchBuf = 0;
+ return Bs3StrFormatV(pszFormat, va, bs3PrintFmtOutput, &Buf);
+}
+
+
+#undef Bs3Printf
+BS3_CMN_DEF(size_t, Bs3Printf,(const char BS3_FAR *pszFormat, ...))
+{
+ size_t cchRet;
+ va_list va;
+ va_start(va, pszFormat);
+ cchRet = BS3_CMN_NM(Bs3PrintfV)(pszFormat, va);
+ va_end(va);
+ return cchRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertToRingX.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertToRingX.c
new file mode 100644
index 00000000..f55c0c78
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertToRingX.c
@@ -0,0 +1,182 @@
+/* $Id: bs3-cmn-RegCtxConvertToRingX.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxConvertToRingX
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/**
+ * Transforms a real mode segment into a protected mode selector.
+ *
+ * @returns Protected mode selector.
+ * @param uSeg The real mode segment.
+ * @param bRing The target ring.
+ */
+static uint16_t bs3RegCtxConvertRealSegToRingX(uint16_t uSeg, uint8_t bRing)
+{
+ uint16_t uSel;
+ if ( uSeg == 0
+ || uSeg == BS3_SEL_R0_SS16)
+ uSel = BS3_SEL_R0_SS16 + ((uint16_t)bRing << BS3_SEL_RING_SHIFT);
+ else if ( uSeg == (BS3_ADDR_BS3TEXT16 >> 4)
+ || uSeg == BS3_SEL_R0_CS16)
+ uSel = BS3_SEL_R0_CS16 + ((uint16_t)bRing << BS3_SEL_RING_SHIFT);
+ else if ( uSeg == (BS3_ADDR_BS3DATA16 >> 4)
+ || uSeg == BS3_SEL_R0_DS16)
+ uSel = BS3_SEL_R0_DS16 + ((uint16_t)bRing << BS3_SEL_RING_SHIFT);
+ else if (uSeg == (BS3_ADDR_BS3SYSTEM16 >> 4))
+ uSel = BS3_SEL_SYSTEM16;
+ else if (!(uSeg & 0xfff))
+ uSel = (uSeg >> (12 - X86_SEL_SHIFT)) + BS3_SEL_TILED;
+ else if (uSeg == BS3_SEL_R0_DS16)
+ uSel = (uSeg >> (12 - X86_SEL_SHIFT)) + BS3_SEL_TILED;
+ else
+ {
+ Bs3Printf("uSeg=%#x\n", uSeg);
+ BS3_ASSERT(0);
+ return 0;
+ }
+ uSel |= bRing;
+ return uSel;
+}
+
+
+/**
+ * Transforms a protected mode selector to a different ring.
+ *
+ * @returns Adjusted protected mode selector.
+ * @param uSel The current selector value.
+ * @param bRing The target ring.
+ * @param iReg Register index.
+ */
+static uint16_t bs3RegCtxConvertProtSelToRingX(uint16_t uSel, uint8_t bRing, uint8_t iReg)
+{
+ if ( uSel > X86_SEL_RPL
+ && !(uSel & X86_SEL_LDT) )
+ {
+ if (uSel >= BS3_SEL_R0_FIRST && uSel < BS3_SEL_R0_FIRST + (5 << BS3_SEL_RING_SHIFT))
+ {
+ /* Convert BS3_SEL_R*_XXX to the target ring. */
+ uSel &= BS3_SEL_RING_SUB_MASK;
+ uSel |= bRing;
+ uSel += BS3_SEL_R0_FIRST;
+ uSel += (uint16_t)bRing << BS3_SEL_RING_SHIFT;
+ }
+ else
+ {
+ /* Convert TEXT16 and DATA16 to BS3_SEL_R*_XXX. */
+ uint16_t const uSelRaw = uSel & X86_SEL_MASK_OFF_RPL;
+ if (uSelRaw == BS3_SEL_TEXT16)
+ uSel = (BS3_SEL_R0_CS16 | bRing) + ((uint16_t)bRing << BS3_SEL_RING_SHIFT);
+ else if (uSelRaw == BS3_SEL_DATA16)
+ uSel = (BS3_SEL_R0_DS16 | bRing) + ((uint16_t)bRing << BS3_SEL_RING_SHIFT);
+ /* CS and SS must have CPL == DPL. So, convert to standard selectors as we're
+ usually here because Bs3SwitchToRing0 was called to get out of a test situation. */
+ else if (iReg == X86_SREG_CS || iReg == X86_SREG_SS)
+ {
+ if ( Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u1Long
+ && BS3_MODE_IS_64BIT_SYS(g_bBs3CurrentMode) )
+ uSel = iReg == X86_SREG_CS ? BS3_SEL_R0_CS64 : BS3_SEL_R0_DS64;
+ else
+ {
+ uint32_t uFlat = Bs3SelFar32ToFlat32(0, uSel);
+ bool fDefBig = Bs3Gdt[uSel >> X86_SEL_SHIFT].Gen.u1DefBig;
+ if (!fDefBig && uFlat == BS3_ADDR_BS3TEXT16 && iReg == X86_SREG_CS)
+ uSel = BS3_SEL_R0_CS16;
+ else if (!fDefBig && uFlat == 0 && iReg == X86_SREG_SS)
+ uSel = BS3_SEL_R0_SS16;
+ else if (fDefBig && uFlat == 0)
+ uSel = iReg == X86_SREG_CS ? BS3_SEL_R0_CS32 : BS3_SEL_R0_SS32;
+ else
+ {
+ Bs3Printf("uSel=%#x iReg=%d\n", uSel, iReg);
+ BS3_ASSERT(0);
+ return uSel;
+ }
+ uSel |= bRing;
+ uSel += (uint16_t)bRing << BS3_SEL_RING_SHIFT;
+ }
+ }
+ /* Adjust the RPL on tiled and MMIO selectors. */
+ else if ( uSelRaw == BS3_SEL_VMMDEV_MMIO16
+ || uSelRaw >= BS3_SEL_TILED)
+ uSel = uSelRaw | bRing;
+ }
+ }
+ return uSel;
+}
+
+
+/**
+ * Transforms a register context to a different ring.
+ *
+ * @param pRegCtx The register context.
+ * @param bRing The target ring (0..3).
+ *
+ * @note Do _NOT_ call this for creating real mode or v8086 contexts, because
+ * it will always output a protected mode context!
+ */
+#undef Bs3RegCtxConvertToRingX
+BS3_CMN_DEF(void, Bs3RegCtxConvertToRingX,(PBS3REGCTX pRegCtx, uint8_t bRing))
+{
+ if ( (pRegCtx->rflags.u32 & X86_EFL_VM)
+ || pRegCtx->bMode == BS3_MODE_RM)
+ {
+ pRegCtx->rflags.u32 &= ~X86_EFL_VM;
+ pRegCtx->bMode &= ~BS3_MODE_CODE_MASK;
+ pRegCtx->bMode |= BS3_MODE_CODE_16;
+ pRegCtx->cs = bs3RegCtxConvertRealSegToRingX(pRegCtx->cs, bRing);
+ pRegCtx->ss = bs3RegCtxConvertRealSegToRingX(pRegCtx->ss, bRing);
+ pRegCtx->ds = bs3RegCtxConvertRealSegToRingX(pRegCtx->ds, bRing);
+ pRegCtx->es = bs3RegCtxConvertRealSegToRingX(pRegCtx->es, bRing);
+ pRegCtx->fs = bs3RegCtxConvertRealSegToRingX(pRegCtx->fs, bRing);
+ pRegCtx->gs = bs3RegCtxConvertRealSegToRingX(pRegCtx->gs, bRing);
+ }
+ else
+ {
+ pRegCtx->cs = bs3RegCtxConvertProtSelToRingX(pRegCtx->cs, bRing, X86_SREG_CS);
+ pRegCtx->ss = bs3RegCtxConvertProtSelToRingX(pRegCtx->ss, bRing, X86_SREG_SS);
+ pRegCtx->ds = bs3RegCtxConvertProtSelToRingX(pRegCtx->ds, bRing, X86_SREG_DS);
+ pRegCtx->es = bs3RegCtxConvertProtSelToRingX(pRegCtx->es, bRing, X86_SREG_ES);
+ pRegCtx->fs = bs3RegCtxConvertProtSelToRingX(pRegCtx->fs, bRing, X86_SREG_FS);
+ pRegCtx->gs = bs3RegCtxConvertProtSelToRingX(pRegCtx->gs, bRing, X86_SREG_GS);
+ }
+ pRegCtx->bCpl = bRing;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertV86ToRm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertV86ToRm.c
new file mode 100644
index 00000000..913ee555
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxConvertV86ToRm.c
@@ -0,0 +1,55 @@
+/* $Id: bs3-cmn-RegCtxConvertV86ToRm.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxConvertV86ToRm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxConvertV86ToRm
+BS3_CMN_DEF(void, Bs3RegCtxConvertV86ToRm,(PBS3REGCTX pRegCtx))
+{
+ BS3_ASSERT(BS3_MODE_IS_V86(pRegCtx->bMode));
+
+ pRegCtx->cr0.u32 &= ~(X86_CR0_PE | X86_CR0_PG);
+ pRegCtx->rflags.u32 &= ~X86_EFL_VM;
+ pRegCtx->fbFlags |= BS3REG_CTX_F_NO_TR_LDTR | BS3REG_CTX_F_NO_AMD64;
+ pRegCtx->bCpl = 0;
+ pRegCtx->bMode = BS3_MODE_RM;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxGetRspSsAsCurPtr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxGetRspSsAsCurPtr.c
new file mode 100644
index 00000000..5f36e2b1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxGetRspSsAsCurPtr.c
@@ -0,0 +1,71 @@
+/* $Id: bs3-cmn-RegCtxGetRspSsAsCurPtr.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxGetRspSsAsCurPtr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxGetRspSsAsCurPtr
+BS3_CMN_DEF(void BS3_FAR *, Bs3RegCtxGetRspSsAsCurPtr,(PBS3REGCTX pRegCtx))
+{
+ uint64_t uFlat;
+ if (BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ {
+#if ARCH_BITS == 16
+ if (BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+ return BS3_FP_MAKE(pRegCtx->ss, pRegCtx->rsp.u16);
+#endif
+ uFlat = ((uint32_t)pRegCtx->ss << 4) + pRegCtx->rsp.u16;
+ }
+ else if (!BS3_MODE_IS_64BIT_CODE(pRegCtx->bMode))
+ uFlat = Bs3SelFar32ToFlat32(pRegCtx->rsp.u32, pRegCtx->ss);
+ else
+ uFlat = pRegCtx->rsp.u64;
+
+#if ARCH_BITS == 16
+ if (uFlat >= (BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode) ? _1M : BS3_SEL_TILED_AREA_SIZE))
+ return NULL;
+ return BS3_FP_MAKE(Bs3Sel16HighFlatPtrToSelector((uint32_t)uFlat >> 16), (uint16_t)uFlat);
+#else
+ /* Typically no need to check limit in 32-bit mode, because 64-bit mode
+ just repeats the first 4GB for the rest of the address space. */
+ return (void *)(uintptr_t)uFlat;
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxPrint.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxPrint.c
new file mode 100644
index 00000000..0e5e73ba
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxPrint.c
@@ -0,0 +1,77 @@
+/* $Id: bs3-cmn-RegCtxPrint.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxPrint
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxPrint
+BS3_CMN_DEF(void, Bs3RegCtxPrint,(PCBS3REGCTX pRegCtx))
+{
+ if (!BS3_MODE_IS_64BIT_CODE(pRegCtx->bMode))
+ {
+ Bs3TestPrintf("eax=%08RX32 ebx=%08RX32 ecx=%08RX32 edx=%08RX32 esi=%08RX32 edi=%08RX32\n",
+ pRegCtx->rax.u32, pRegCtx->rbx.u32, pRegCtx->rcx.u32, pRegCtx->rdx.u32, pRegCtx->rsi.u32, pRegCtx->rdi.u32);
+ Bs3TestPrintf("eip=%08RX32 esp=%08RX32 ebp=%08RX32 efl=%08RX32 cr0=%08RX32 cr2=%08RX32\n",
+ pRegCtx->rip.u32, pRegCtx->rsp.u32, pRegCtx->rbp.u32, pRegCtx->rflags.u32,
+ pRegCtx->cr0.u32, pRegCtx->cr2.u32);
+ Bs3TestPrintf("cs=%04RX16 ds=%04RX16 es=%04RX16 fs=%04RX16 gs=%04RX16 ss=%04RX16 cr3=%08RX32 cr4=%08RX32\n",
+ pRegCtx->cs, pRegCtx->ds, pRegCtx->es, pRegCtx->fs, pRegCtx->gs, pRegCtx->ss,
+ pRegCtx->cr3.u32, pRegCtx->cr4.u32);
+ }
+ else
+ {
+ Bs3TestPrintf("rax=%016RX64 rbx=%016RX64 rcx=%016RX64 rdx=%016RX64\n",
+ pRegCtx->rax.u64, pRegCtx->rbx.u64, pRegCtx->rcx.u64, pRegCtx->rdx.u64);
+ Bs3TestPrintf("rsi=%016RX64 rdi=%016RX64 r8 =%016RX64 r9 =%016RX64\n",
+ pRegCtx->rsi.u64, pRegCtx->rdi.u64, pRegCtx->r8.u64, pRegCtx->r9.u64);
+ Bs3TestPrintf("r10=%016RX64 r11=%016RX64 r12=%016RX64 r13=%016RX64\n",
+ pRegCtx->r10.u64, pRegCtx->r11.u64, pRegCtx->r12.u64, pRegCtx->r13.u64);
+ Bs3TestPrintf("r14=%016RX64 r15=%016RX64 cr0=%08RX64 cr4=%08RX64 cr3=%08RX64\n",
+ pRegCtx->r14.u64, pRegCtx->r15.u64, pRegCtx->cr0.u64, pRegCtx->cr4.u64, pRegCtx->cr3.u64);
+ Bs3TestPrintf("rip=%016RX64 rsp=%016RX64 rbp=%016RX64 rfl=%08RX64\n",
+ pRegCtx->rip.u64, pRegCtx->rsp.u64, pRegCtx->rbp.u64, pRegCtx->rflags.u32);
+ Bs3TestPrintf("cs=%04RX16 ds=%04RX16 es=%04RX16 fs=%04RX16 gs=%04RX16 ss=%04RX16 cr2=%016RX64\n",
+ pRegCtx->cs, pRegCtx->ds, pRegCtx->es, pRegCtx->fs, pRegCtx->gs, pRegCtx->ss,
+ pRegCtx->cr3.u64, pRegCtx->cr2.u64);
+ }
+ Bs3TestPrintf("tr=%04RX16 ldtr=%04RX16 cpl=%d mode=%#x fbFlags=%#x\n",
+ pRegCtx->tr, pRegCtx->ldtr, pRegCtx->bCpl, pRegCtx->bMode, pRegCtx->fbFlags);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxRestore.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxRestore.asm
new file mode 100644
index 00000000..37d3a526
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxRestore.asm
@@ -0,0 +1,608 @@
+; $Id: bs3-cmn-RegCtxRestore.asm $
+;; @file
+; BS3Kit - Bs3RegCtxRestore.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_DATA16 g_fBs3TrapNoV86Assist
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3Syscall
+BS3_EXTERN_CMN Bs3Panic
+TMPL_BEGIN_TEXT
+
+
+;;
+; Restores the given register context.
+;
+; @param pRegCtx
+; @param fFlags
+; @uses All registers and may trash stack immediately before the resume point.
+;
+; @note Only respects the BS3_MODE_CODE_MASK part of pRegCtx->bMode.
+;
+%if TMPL_BITS == 16
+BS3_PROC_BEGIN_CMN Bs3RegCtxRestore_aborts, BS3_PBC_FAR ; special entry point for when watcom applies __aborts
+BS3_PROC_BEGIN_CMN Bs3RegCtxRestore_aborts, BS3_PBC_NEAR ; special entry point for when watcom applies __aborts
+ CPU 8086
+ xor xAX, xAX
+ push xAX ; fake return address.
+ push xAX
+ jmp _Bs3RegCtxRestore_f16
+%elif TMPL_BITS == 32
+BS3_PROC_BEGIN_CMN Bs3RegCtxRestore_aborts, BS3_PBC_NEAR ; special entry point for when watcom applies __aborts
+ push 0feedfaceh ; fake return address.
+%endif
+BS3_PROC_BEGIN_CMN Bs3RegCtxRestore, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; If we're not in ring-0, ask the kernel to restore it for us (quicker
+ ; and less problematic if we're in a funny context right now with weird
+ ; CS or SS values).
+ ;
+%if TMPL_BITS == 16
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .in_ring0
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .do_syscall_restore_ctx
+%endif
+ mov ax, ss
+ test al, 3
+ jz .in_ring0
+
+.do_syscall_restore_ctx:
+%if TMPL_BITS != 16
+.do_syscall_restore_ctx_restore_ds:
+ mov cx, ds
+ mov xSI, [xBP + xCB*2]
+ movzx edx, word [xBP + xCB*3]
+ mov eax, BS3_SYSCALL_RESTORE_CTX
+%else
+ mov si, [bp + xCB + cbCurRetAddr]
+ mov cx, [bp + xCB + cbCurRetAddr + 2]
+ mov dx, [bp + xCB + cbCurRetAddr + sCB]
+ mov ax, BS3_SYSCALL_RESTORE_CTX
+%endif
+ call Bs3Syscall
+ call Bs3Panic
+
+%if TMPL_BITS == 16
+.do_syscall_restore_ctx_restore_ds:
+ push es
+ pop ds
+ jmp .do_syscall_restore_ctx
+%endif
+
+ ;
+ ; Prologue. Loads ES with BS3KIT_GRPNM_DATA16/FLAT (for g_bBs3CurrentMode
+ ; and g_uBs3CpuDetected), DS:xBX with pRegCtx and fFlags into xCX.
+ ;
+.in_ring0:
+%if TMPL_BITS == 16
+ mov ax, BS3_SEL_DATA16
+ mov es, ax
+ lds bx, [bp + xCB + cbCurRetAddr]
+ mov cx, [bp + xCB + cbCurRetAddr + sCB]
+%elif TMPL_BITS == 32
+ mov ax, BS3_SEL_R0_DS32
+ mov ds, ax
+ mov xBX, [xBP + xCB*2]
+ movzx xCX, word [xBP + xCB*3]
+%else
+ mov ax, BS3_SEL_R0_DS64
+ mov ds, ax
+ mov xBX, [xBP + xCB*2]
+ movzx xCX, word [xBP + xCB*3]
+%endif
+
+
+%if TMPL_BITS != 64
+ ; Restoring a 64-bit context is best done from 64-bit code.
+ mov al, [xBX + BS3REGCTX.bMode]
+ test al, BS3_MODE_CODE_64
+ jnz .do_syscall_restore_ctx_restore_ds
+%endif
+
+ ; The remainder must be done with interrupts disabled.
+ cli
+
+ ;
+ ; Update g_bs3CurrentMode.
+ ;
+%if TMPL_BITS == 64
+ mov al, [xBX + BS3REGCTX.bMode]
+%endif
+ and al, BS3_MODE_CODE_MASK
+ mov ah, [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and ah, ~BS3_MODE_CODE_MASK
+ or al, ah
+ mov [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_bBs3CurrentMode)], al
+
+ ;
+ ; Set g_fBs3TrapNoV86Assist if BS3REGCTXRESTORE_F_NO_V86_ASSIST specified.
+ ;
+ test cl, BS3REGCTXRESTORE_F_NO_V86_ASSIST
+ jz .no_f_no_v86_assist
+ mov byte [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_fBs3TrapNoV86Assist)], 1
+.no_f_no_v86_assist:
+
+%if TMPL_BITS == 16
+ ;
+ ; Check what the CPU can do.
+ ;
+ cmp byte [es:BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
+ jae .restore_full
+
+ ; Do the 80286 specifics first.
+ cmp byte [es:BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jb .restore_16_bit_ancient
+ CPU 286
+
+ lmsw [bx + BS3REGCTX.cr0]
+ cmp byte [es:BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .restore_16_bit_ancient
+ lldt [bx + BS3REGCTX.ldtr]
+
+ ; TR - complicated because we need to clear the busy bit. ASSUMES GDT.
+ str ax
+ cmp ax, [bx + BS3REGCTX.tr]
+ je .skip_tr_286
+
+ mov di, word [xBX + BS3REGCTX.tr]
+ or di, di ; check for null.
+ jz .load_tr_286
+
+ push ds
+ push BS3_SEL_SYSTEM16
+ pop ds
+ add di, Bs3Gdt wrt BS3SYSTEM16
+ add di, X86DESCGENERIC_BIT_OFF_TYPE / 8
+ and byte [di], ~(X86_SEL_TYPE_SYS_TSS_BUSY_MASK << (X86DESCGENERIC_BIT_OFF_TYPE % 8))
+ pop ds
+
+.load_tr_286:
+ ltr [bx + BS3REGCTX.tr]
+.skip_tr_286:
+
+.restore_16_bit_ancient:
+ CPU 8086
+ ; Some general registers.
+ mov cx, [bx + BS3REGCTX.rcx]
+ mov dx, [bx + BS3REGCTX.rdx]
+
+ ; Do the return frame and final registers (keep short as we're not quite
+ ; NMI safe here if pRegCtx is on the stack).
+ cmp byte [es:BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ mov di, [bx + BS3REGCTX.rsp]
+ je .restore_16_bit_same_privilege
+ cmp byte [bx + BS3REGCTX.bCpl], 0
+ je .restore_16_bit_same_privilege
+
+ mov ax, [bx + BS3REGCTX.ss]
+ push ax
+ mov ax, [bx + BS3REGCTX.rsp]
+ push ax
+ mov ax, [bx + BS3REGCTX.rflags]
+ push ax
+ mov ax, [bx + BS3REGCTX.cs]
+ push ax
+ mov ax, [bx + BS3REGCTX.rip]
+ push ax
+ mov ax, [bx + BS3REGCTX.ds]
+ push ax
+
+ mov si, [bx + BS3REGCTX.rsi]
+ mov di, [bx + BS3REGCTX.rdi]
+ mov es, [bx + BS3REGCTX.es]
+ mov ax, [bx + BS3REGCTX.rax]
+ mov bp, [bx + BS3REGCTX.rbp] ; restore late for better stacks.
+ mov bx, [bx + BS3REGCTX.rbx]
+
+ pop ds
+ iret
+
+.restore_16_bit_same_privilege:
+ sub di, 2*5 ; iret frame + pop ds
+ mov si, di
+ mov es, [bx + BS3REGCTX.ss] ; ES is target stack segment.
+ cld
+
+ mov ax, [bx + BS3REGCTX.ds]
+ stosw
+ mov ax, [bx + BS3REGCTX.rbp] ; Restore esp as late as possible for better stacks.
+ stosw
+ mov ax, [bx + BS3REGCTX.rip]
+ stosw
+ mov ax, [bx + BS3REGCTX.cs]
+ stosw
+ mov ax, [bx + BS3REGCTX.rflags]
+ stosw
+
+ mov di, [bx + BS3REGCTX.rdi]
+ mov es, [bx + BS3REGCTX.es]
+ mov ax, [bx + BS3REGCTX.rax]
+ mov ss, [bx + BS3REGCTX.ss]
+ mov sp, si
+ mov si, [bx + BS3REGCTX.rsi]
+ mov bx, [bx + BS3REGCTX.rbx]
+
+ pop ds
+ pop bp
+ iret
+
+ CPU 386
+%endif
+
+.restore_full:
+ ;
+ ; 80386 or later.
+ ; For 32-bit and 16-bit versions, we always use 32-bit iret.
+ ;
+
+ ; Restore control registers if they've changed.
+ test cl, BS3REGCTXRESTORE_F_SKIP_CRX
+ jnz .skip_control_regs
+ test byte [xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3
+ jnz .skip_control_regs
+
+ test byte [xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4 ; (old 486s and 386s didn't have CR4)
+ jnz .skip_cr4
+%if TMPL_BITS != 64
+ test word [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_F_CPUID
+ jz .skip_cr4
+%endif
+ mov sAX, [xBX + BS3REGCTX.cr4]
+ mov sDX, cr4
+ cmp sAX, sDX
+ je .skip_cr4
+ mov cr4, sAX
+.skip_cr4:
+
+ mov sAX, [xBX + BS3REGCTX.cr0]
+ mov sDX, cr0
+ cmp sAX, sDX
+ je .skip_cr0
+ mov cr0, sAX
+.skip_cr0:
+
+ mov sAX, [xBX + BS3REGCTX.cr3]
+ mov sDX, cr3
+ cmp sAX, sDX
+ je .skip_cr3
+ mov cr3, sAX
+.skip_cr3:
+
+ mov sAX, [xBX + BS3REGCTX.cr2]
+ mov sDX, cr2
+ cmp sAX, sDX
+ je .skip_cr2
+ mov cr2, sAX
+.skip_cr2:
+
+ ;
+ ; Restore
+ ;
+%if TMPL_BITS != 64
+ ; We cannot restore ldtr and tr if we're in real-mode.
+ cmp byte [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .skip_control_regs
+%endif
+ test byte [xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_TR_LDTR
+ jnz .skip_control_regs
+
+ ; LDTR
+ sldt ax
+ cmp ax, [xBX + BS3REGCTX.ldtr]
+ je .skip_ldtr
+ lldt [xBX + BS3REGCTX.ldtr]
+.skip_ldtr:
+
+ ; TR - complicated because we need to clear the busy bit. ASSUMES GDT.
+ str ax
+ cmp ax, [xBX + BS3REGCTX.tr]
+ je .skip_tr
+
+ movzx edi, word [xBX + BS3REGCTX.tr]
+ or edi, edi ; check for null.
+ jz .load_tr
+
+%if TMPL_BITS == 16
+ push ds
+ push BS3_SEL_SYSTEM16
+ pop ds
+ add xDI, Bs3Gdt wrt BS3SYSTEM16
+%else
+ add xDI, Bs3Gdt wrt FLAT
+%endif
+ add xDI, X86DESCGENERIC_BIT_OFF_TYPE / 8
+ and byte [xDI], ~(X86_SEL_TYPE_SYS_TSS_BUSY_MASK << (X86DESCGENERIC_BIT_OFF_TYPE % 8))
+%if TMPL_BITS == 16
+ pop ds
+%endif
+.load_tr:
+ ltr [xBX + BS3REGCTX.tr]
+.skip_tr:
+
+.skip_control_regs:
+
+
+%if TMPL_BITS == 64
+ ;
+ ; 64-bit returns are simple because ss:rsp are always restored.
+ ;
+ ; A small complication here when returning to a 16-bit stack (only
+ ; applicable to 16-bit and 32-bit code), iret doesn't touch the high
+ ; ESP bits and we can easily later end up with trap handlers
+ ; accessing memory never intended as stack.
+ ;
+ mov rcx, qword [xBX + BS3REGCTX.rsp] ; (also 1st param for conv call below)
+ cmp rcx, 0ffffh
+ ja .iretq_maybe_annoying_16bit_stack
+ cmp rsp, 0ffffh
+ ja .iretq_maybe_annoying_16bit_stack
+.iretq_ok:
+
+ movzx eax, word [xBX + BS3REGCTX.ss]
+ push rax
+ push qword [xBX + BS3REGCTX.rsp]
+ push qword [xBX + BS3REGCTX.rflags]
+ movzx eax, word [xBX + BS3REGCTX.cs]
+ push rax
+ push qword [xBX + BS3REGCTX.rip]
+
+.iretq_restore_regs_and_iret:
+ mov es, [xBX + BS3REGCTX.es]
+ mov fs, [xBX + BS3REGCTX.fs]
+ mov gs, [xBX + BS3REGCTX.gs]
+ mov rax, [xBX + BS3REGCTX.rax]
+ mov rdx, [xBX + BS3REGCTX.rdx]
+ mov rcx, [xBX + BS3REGCTX.rcx]
+ mov rsi, [xBX + BS3REGCTX.rsi]
+ mov rdi, [xBX + BS3REGCTX.rdi]
+ mov r8, [xBX + BS3REGCTX.r8]
+ mov r9, [xBX + BS3REGCTX.r9]
+ mov r10, [xBX + BS3REGCTX.r10]
+ mov r11, [xBX + BS3REGCTX.r11]
+ mov r12, [xBX + BS3REGCTX.r12]
+ mov r13, [xBX + BS3REGCTX.r13]
+ mov r14, [xBX + BS3REGCTX.r14]
+ mov r15, [xBX + BS3REGCTX.r15]
+ mov rbp, [xBX + BS3REGCTX.rbp] ; restore late for better stacks.
+ mov ds, [xBX + BS3REGCTX.ds]
+ mov rbx, [xBX + BS3REGCTX.rbx]
+ iretq
+
+.iretq_maybe_annoying_16bit_stack:
+ movzx edx, word [xBX + BS3REGCTX.ss] ; (also 2nd param for conv call below)
+ lar eax, dx
+ jnz .iretq_ok
+ test eax, X86LAR_F_D | X86LAR_F_L
+ jnz .iretq_ok ; Returning to a big of long SS needs not extra work.
+
+ lar eax, word [xBX + BS3REGCTX.cs]
+ jnz .iretq_ok
+ test eax, X86LAR_F_L
+ jnz .iretq_ok ; It doesn't matter when returning to 64-bit code.
+
+ ; Convert ss:sp to a flat address.
+ BS3_EXTERN_CMN Bs3SelFar32ToFlat32NoClobber
+ call Bs3SelFar32ToFlat32NoClobber
+ mov rdi, rax
+
+ ; 2nd return frame (32-bit, same CPL).
+ mov eax, [xBX + BS3REGCTX.rflags]
+ mov [rdi - 4], eax
+ movzx eax, word [xBX + BS3REGCTX.cs]
+ mov [rdi - 8], eax
+ mov eax, [xBX + BS3REGCTX.rip]
+ mov [rdi - 12], eax
+ mov ecx, [xBX + BS3REGCTX.rsp]
+ sub cx, 12
+ mov [rdi - 16], ecx
+
+ ; 1st return frame.
+ movzx eax, word [xBX + BS3REGCTX.ss]
+ push rax ; new 16-bit SS
+ sub cx, 4
+ push rcx ; new esp
+ mov rax, [xBX + BS3REGCTX.rflags]
+ and rax, ~(X86_EFL_NT | X86_EFL_TF)
+ push rax ; rflags
+ AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ mov eax, BS3_SEL_R0_CS32
+ add ah, [xBX + BS3REGCTX.bCpl]
+ or al, [xBX + BS3REGCTX.bCpl]
+ push rax ; 32-bit CS
+ push .iretq_pop_real_esp_and_iret_again wrt FLAT
+ jmp .iretq_restore_regs_and_iret
+
+ BS3_SET_BITS 32
+.iretq_pop_real_esp_and_iret_again:
+ pop esp
+ iretd
+ BS3_SET_BITS 64
+
+%else
+ ;
+ ; 32-bit/16-bit is more complicated as we have three different iret frames.
+ ;
+ mov al, [BS3_ONLY_16BIT(es:) BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ cmp al, BS3_MODE_RM
+ je .iretd_same_cpl_rm
+
+ test dword [xBX + BS3REGCTX.rflags], X86_EFL_VM
+ jnz .restore_v8086
+
+ cmp byte [xBX + BS3REGCTX.bCpl], 0
+ je .iretd_same_cpl
+
+ ;
+ ; IRETD to different CPL. Frame includes ss:esp.
+ ;
+.iretd_different_cpl:
+ or eax, 0ffffffffh ; poison unused parts of segment pushes
+ mov ax, [xBX + BS3REGCTX.ss]
+ push eax
+ push dword [xBX + BS3REGCTX.rsp]
+ push dword [xBX + BS3REGCTX.rflags]
+ mov ax, [xBX + BS3REGCTX.cs]
+ push eax
+ push dword [xBX + BS3REGCTX.rip]
+ push dword [xBX + BS3REGCTX.rbp] ; Restore esp as late as possible for better stacks.
+ mov ax, [xBX + BS3REGCTX.ds]
+ push xAX
+
+ mov es, [xBX + BS3REGCTX.es]
+ mov fs, [xBX + BS3REGCTX.fs]
+ mov gs, [xBX + BS3REGCTX.gs]
+ mov eax, [xBX + BS3REGCTX.rax]
+ mov edx, [xBX + BS3REGCTX.rdx]
+ mov ecx, [xBX + BS3REGCTX.rcx]
+ mov esi, [xBX + BS3REGCTX.rsi]
+ %if TMPL_BITS == 16 ; if SS is 16-bit, we will not be able to restore the high word.
+;; @todo 16-bit stack will also mess us up in 32-bit code, so this needs fixing (see 64-bit above).
+ mov edi, [xBX + BS3REGCTX.rsp]
+ mov di, sp
+ mov esp, edi
+ %endif
+ mov edi, [xBX + BS3REGCTX.rdi]
+ mov ebx, [xBX + BS3REGCTX.rbx]
+
+ pop ds
+ pop ebp
+ iretd
+
+ ;
+ ; IRETD to same CPL (includes real mode).
+ ;
+.iretd_same_cpl_rm:
+ ; Use STOSD/ES:EDI to create the frame.
+ mov es, [xBX + BS3REGCTX.ss]
+ mov esi, [xBX + BS3REGCTX.rsp]
+ sub esi, 5*4
+ movzx edi, si
+ jmp .es_edi_is_pointing_to_return_frame_location
+
+.iretd_same_cpl:
+ ; Use STOSD/ES:EDI to create the frame.
+ mov es, [xBX + BS3REGCTX.ss]
+ mov edi, [xBX + BS3REGCTX.rsp]
+ sub edi, 5*4
+
+ ; Which part of the stack pointer is actually used depends on the SS.D/B bit.
+ lar eax, [xBX + BS3REGCTX.ss]
+ jnz .using_32_bit_stack_pointer
+ test eax, X86LAR_F_D
+ jnz .using_32_bit_stack_pointer
+.using_16_bit_stack_pointer:
+ mov esi, edi ; save rsp for later.
+ movzx edi, di
+ jmp .es_edi_is_pointing_to_return_frame_location
+.using_32_bit_stack_pointer:
+ mov esi, edi
+.es_edi_is_pointing_to_return_frame_location:
+ cld
+ mov ax, [xBX + BS3REGCTX.ds]
+ o32 stosd
+ mov eax, [xBX + BS3REGCTX.rbp] ; Restore esp as late as possible for better stacks.
+ o32 stosd
+ mov eax, [xBX + BS3REGCTX.rip]
+ o32 stosd
+ mov ax, [xBX + BS3REGCTX.cs]
+ o32 stosd
+ mov eax, [xBX + BS3REGCTX.rflags]
+ o32 stosd
+
+ mov es, [xBX + BS3REGCTX.es]
+ mov fs, [xBX + BS3REGCTX.fs]
+ mov gs, [xBX + BS3REGCTX.gs]
+ mov eax, [xBX + BS3REGCTX.rax]
+ mov edx, [xBX + BS3REGCTX.rdx]
+ mov ecx, [xBX + BS3REGCTX.rcx]
+ mov edi, [xBX + BS3REGCTX.rdi]
+ mov ebp, [xBX + BS3REGCTX.rbp] ; restore late for better stacks.
+
+ mov ss, [xBX + BS3REGCTX.ss]
+ mov esp, esi
+ mov esi, [xBX + BS3REGCTX.rsi]
+ mov ebx, [xBX + BS3REGCTX.rbx]
+
+ o32 pop ds
+ pop ebp
+ iretd
+
+ ;
+ ; IRETD to v8086 mode. Frame includes ss:esp and the 4 data segment registers.
+ ;
+.restore_v8086:
+ ; Create the return frame.
+ or eax, 0ffffffffh ; poison unused parts of segment pushes
+ mov eax, [xBX + BS3REGCTX.gs]
+ push eax
+ mov eax, [xBX + BS3REGCTX.fs]
+ push eax
+ mov eax, [xBX + BS3REGCTX.ds]
+ push eax
+ mov eax, [xBX + BS3REGCTX.es]
+ push eax
+ mov eax, [xBX + BS3REGCTX.ss]
+ push eax
+ push dword [xBX + BS3REGCTX.rsp]
+ push dword [xBX + BS3REGCTX.rflags]
+ mov ax, [xBX + BS3REGCTX.cs]
+ push eax
+ push dword [xBX + BS3REGCTX.rip]
+
+ ; Load registers.
+ mov eax, [xBX + BS3REGCTX.rax]
+ mov edx, [xBX + BS3REGCTX.rdx]
+ mov ecx, [xBX + BS3REGCTX.rcx]
+ mov esi, [xBX + BS3REGCTX.rsi]
+ mov edi, [xBX + BS3REGCTX.rdi]
+ mov ebp, [xBX + BS3REGCTX.rbp] ; restore late for better stacks.
+ mov ebx, [xBX + BS3REGCTX.rbx]
+
+ iretd
+%endif
+BS3_PROC_END_CMN Bs3RegCtxRestore
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSave.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSave.asm
new file mode 100644
index 00000000..4888c8e7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSave.asm
@@ -0,0 +1,271 @@
+; $Id: bs3-cmn-RegCtxSave.asm $
+;; @file
+; BS3Kit - Bs3RegCtxSave.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+TMPL_BEGIN_TEXT
+
+
+
+;;
+; Saves the current register context.
+;
+; @param pRegCtx
+; @uses None.
+;
+BS3_PROC_BEGIN_CMN Bs3RegCtxSave, BS3_PBC_HYBRID_SAFE
+TONLY16 CPU 8086
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ xPUSHF ; xBP - xCB*1: save the incoming flags exactly.
+ push xAX ; xBP - xCB*2: save incoming xAX
+ push xCX ; xBP - xCB*3: save incoming xCX
+ push xDI ; xBP - xCB*4: save incoming xDI
+BONLY16 push es ; xBP - xCB*5
+BONLY16 push ds ; xBP - xCB*6
+
+ ;
+ ; Clear the whole structure first.
+ ;
+ xor xAX, xAX
+ cld
+ AssertCompileSizeAlignment(BS3REGCTX, 4)
+%if TMPL_BITS == 16
+ les xDI, [xBP + xCB + cbCurRetAddr]
+ mov xCX, BS3REGCTX_size / 2
+ rep stosw
+%else
+ mov xDI, [xBP + xCB + cbCurRetAddr]
+ mov xCX, BS3REGCTX_size / 4
+ rep stosd
+%endif
+ mov xDI, [xBP + xCB + cbCurRetAddr]
+
+ ;
+ ; Save the current mode.
+ ;
+ mov cl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ mov [BS3_ONLY_16BIT(es:) xDI + BS3REGCTX.bMode], cl
+%if TMPL_BITS == 16
+
+ ;
+ ; In 16-bit mode we could be running on really ancient CPUs, so check
+ ; mode and detected CPU and proceed with care.
+ ;
+ cmp cl, BS3_MODE_PP16
+ jae .save_full
+
+ mov cl, [BS3_DATA16_WRT(g_uBs3CpuDetected)]
+ cmp cl, BS3CPU_80386
+ jae .save_full
+
+ ; load ES into DS so we can save some segment prefix bytes.
+ push es
+ pop ds
+
+ ; 16-bit GPRs not on the stack.
+ mov [xDI + BS3REGCTX.rdx], dx
+ mov [xDI + BS3REGCTX.rbx], bx
+ mov [xDI + BS3REGCTX.rsi], si
+
+ ; Join the common code.
+ cmp cl, BS3CPU_80286
+ jb .common_ancient
+ CPU 286
+ smsw [xDI + BS3REGCTX.cr0]
+
+ mov cl, [xDI + BS3REGCTX.bMode] ; assumed by jump destination
+ jmp .common_80286
+
+ CPU 386
+%endif
+
+
+.save_full:
+ ;
+ ; 80386 or later.
+ ;
+%if TMPL_BITS != 64
+ ; Check for CR4 here while we've got a working DS in all contexts.
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
+ jnz .save_full_have_cr4
+ or byte [BS3_ONLY_16BIT(es:) xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+.save_full_have_cr4:
+%endif
+%if TMPL_BITS == 16
+ ; Load es into ds so we can save ourselves some segment prefix bytes.
+ push es
+ pop ds
+%endif
+
+ ; GPRs first.
+ mov [xDI + BS3REGCTX.rdx], sDX
+ mov [xDI + BS3REGCTX.rbx], sBX
+ mov [xDI + BS3REGCTX.rsi], sSI
+%if TMPL_BITS == 64
+ mov [xDI + BS3REGCTX.r8], r8
+ mov [xDI + BS3REGCTX.r9], r9
+ mov [xDI + BS3REGCTX.r10], r10
+ mov [xDI + BS3REGCTX.r11], r11
+ mov [xDI + BS3REGCTX.r12], r12
+ mov [xDI + BS3REGCTX.r13], r13
+ mov [xDI + BS3REGCTX.r14], r14
+ mov [xDI + BS3REGCTX.r15], r15
+%else
+ or byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
+%endif
+%if TMPL_BITS == 16 ; Save high bits.
+ mov [xDI + BS3REGCTX.rax], eax
+ mov [xDI + BS3REGCTX.rcx], ecx
+ mov [xDI + BS3REGCTX.rdi], edi
+ mov [xDI + BS3REGCTX.rbp], ebp
+ mov [xDI + BS3REGCTX.rsp], esp
+ pushfd
+ pop dword [xDI + BS3REGCTX.rflags]
+%endif
+%if TMPL_BITS != 64
+ ; The VM flag is never on the stack, so derive it from the bMode we saved above.
+ test byte [xDI + BS3REGCTX.bMode], BS3_MODE_CODE_V86
+ jz .not_v8086
+ or byte [xDI + BS3REGCTX.rflags + 2], X86_EFL_VM >> 16
+ mov byte [xDI + BS3REGCTX.bCpl], 3
+.not_v8086:
+%endif
+
+ ; 386 segment registers.
+ mov [xDI + BS3REGCTX.fs], fs
+ mov [xDI + BS3REGCTX.gs], gs
+
+%if TMPL_BITS == 16 ; v8086 and real mode woes.
+ mov cl, [xDI + BS3REGCTX.bMode]
+ cmp cl, BS3_MODE_RM
+ je .common_full_control_regs
+ test cl, BS3_MODE_CODE_V86
+ jnz .common_full_no_control_regs
+%endif
+ mov ax, ss
+ test al, 3
+ jnz .common_full_no_control_regs
+
+ ; Control registers (ring-0 and real-mode only).
+.common_full_control_regs:
+ mov sAX, cr0
+ mov [xDI + BS3REGCTX.cr0], sAX
+ mov sAX, cr2
+ mov [xDI + BS3REGCTX.cr2], sAX
+ mov sAX, cr3
+ mov [xDI + BS3REGCTX.cr3], sAX
+%if TMPL_BITS != 64
+ test byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+ jnz .common_80286
+%endif
+ mov sAX, cr4
+ mov [xDI + BS3REGCTX.cr4], sAX
+ jmp .common_80286
+
+.common_full_no_control_regs:
+ or byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
+ smsw [xDI + BS3REGCTX.cr0]
+
+ ; 80286 control registers.
+.common_80286:
+TONLY16 CPU 286
+%if TMPL_BITS != 64
+ cmp cl, BS3_MODE_RM
+ je .no_str_sldt
+ test cl, BS3_MODE_CODE_V86
+ jnz .no_str_sldt
+%endif
+ str [xDI + BS3REGCTX.tr]
+ sldt [xDI + BS3REGCTX.ldtr]
+ jmp .common_ancient
+
+.no_str_sldt:
+ or byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_TR_LDTR
+
+ ; Common stuff - stuff on the stack, 286 segment registers.
+.common_ancient:
+TONLY16 CPU 8086
+ mov xAX, [xBP - xCB*1]
+ mov [xDI + BS3REGCTX.rflags], xAX
+ mov xAX, [xBP - xCB*2]
+ mov [xDI + BS3REGCTX.rax], xAX
+ mov xAX, [xBP - xCB*3]
+ mov [xDI + BS3REGCTX.rcx], xAX
+ mov xAX, [xBP - xCB*4]
+ mov [xDI + BS3REGCTX.rdi], xAX
+ mov xAX, [xBP]
+ mov [xDI + BS3REGCTX.rbp], xAX
+ mov xAX, [xBP + xCB]
+ mov [xDI + BS3REGCTX.rip], xAX
+ lea xAX, [xBP + xCB + cbCurRetAddr]
+ mov [xDI + BS3REGCTX.rsp], xAX
+
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + 2]
+ mov [xDI + BS3REGCTX.cs], ax
+ mov ax, [xBP - xCB*6]
+ mov [xDI + BS3REGCTX.ds], ax
+ mov ax, [xBP - xCB*5]
+ mov [xDI + BS3REGCTX.es], ax
+%else
+ mov [xDI + BS3REGCTX.cs], cs
+ mov [xDI + BS3REGCTX.ds], ds
+ mov [xDI + BS3REGCTX.es], es
+%endif
+ mov [xDI + BS3REGCTX.ss], ss
+
+ ;
+ ; Return.
+ ;
+.return:
+BONLY16 pop ds
+BONLY16 pop es
+ pop xDI
+ pop xCX
+ pop xAX
+ xPOPF
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegCtxSave
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveEx.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveEx.asm
new file mode 100644
index 00000000..94203fe3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveEx.asm
@@ -0,0 +1,460 @@
+; $Id: bs3-cmn-RegCtxSaveEx.asm $
+;; @file
+; BS3Kit - Bs3RegCtxSaveEx.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%if ARCH_BITS != 64
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3RegCtxSave
+BS3_EXTERN_CMN Bs3SwitchTo16Bit
+%if TMPL_BITS != 64
+BS3_EXTERN_CMN Bs3SwitchTo16BitV86
+%endif
+%if TMPL_BITS != 32
+BS3_EXTERN_CMN Bs3SwitchTo32Bit
+%endif
+%if TMPL_BITS != 64
+BS3_EXTERN_CMN Bs3SwitchTo64Bit
+%endif
+%if TMPL_BITS == 16
+BS3_EXTERN_CMN Bs3SelRealModeDataToProtFar16
+BS3_EXTERN_CMN Bs3SelProtFar16DataToRealMode
+BS3_EXTERN_CMN Bs3SelRealModeDataToFlat
+BS3_EXTERN_CMN Bs3SelProtFar16DataToFlat
+%else
+BS3_EXTERN_CMN Bs3SelFlatDataToProtFar16
+%endif
+%if TMPL_BITS == 32
+BS3_EXTERN_CMN Bs3SelFlatDataToRealMode
+%endif
+
+BS3_BEGIN_TEXT16
+%if TMPL_BITS != 16
+extern _Bs3RegCtxSave_c16
+extern _Bs3SwitchTo%[TMPL_BITS]Bit_c16
+%endif
+
+BS3_BEGIN_TEXT32
+%if TMPL_BITS != 32
+extern _Bs3RegCtxSave_c32
+extern _Bs3SwitchTo%[TMPL_BITS]Bit_c32
+%endif
+%if TMPL_BITS == 16
+extern _Bs3SwitchTo16BitV86_c32
+%endif
+
+BS3_BEGIN_TEXT64
+%if TMPL_BITS != 64
+extern _Bs3RegCtxSave_c64
+extern _Bs3SwitchTo%[TMPL_BITS]Bit_c64
+%endif
+
+TMPL_BEGIN_TEXT
+
+
+
+;;
+; Saves the current register context.
+;
+; @param pRegCtx
+; @param bBitMode (8)
+; @param cbExtraStack (16)
+; @uses xAX, xDX, xCX
+;
+BS3_PROC_BEGIN_CMN Bs3RegCtxSaveEx, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+TONLY16 CPU 8086
+ BS3_CALL_CONV_PROLOG 3
+ push xBP
+ mov xBP, xSP
+%if ARCH_BITS == 64
+ push rcx ; Save pRegCtx
+%endif
+
+ ;
+ ; Get the CPU bitcount part of the current mode.
+ ;
+ mov dl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and dl, BS3_MODE_CODE_MASK
+%if TMPL_BITS == 16
+ push dx ; bp - 2: previous CPU mode (16-bit)
+%endif
+
+ ;
+ ; Reserve extra stack space. Make sure we've got 20h here in case we
+ ; are saving a 64-bit context.
+ ;
+TONLY16 mov ax, [xBP + xCB + cbCurRetAddr + sCB + xCB]
+TNOT16 movzx eax, word [xBP + xCB + cbCurRetAddr + sCB + xCB]
+%ifdef BS3_STRICT
+ cmp xAX, 4096
+ jb .extra_stack_ok
+ call Bs3Panic
+.extra_stack_ok:
+%endif
+ cmp xAX, 20h
+ jae .at_least_20h_extra_stack
+ add xAX, 20h
+.at_least_20h_extra_stack:
+ sub xSP, xAX
+
+ ;
+ ; Are we just saving the mode we're already in?
+ ;
+ mov al, [xBP + xCB + cbCurRetAddr + sCB]
+ and al, BS3_MODE_CODE_MASK
+ cmp dl, al
+ jne .not_the_same_mode
+
+%if TMPL_BITS == 16
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+%elif TMPL_BITS == 32
+ push dword [xBP + xCB + cbCurRetAddr]
+%endif
+ call Bs3RegCtxSave ; 64-bit: rcx is untouched thus far.
+
+
+ ;
+ ; Return - no need to pop xAX and xDX as the last two
+ ; operations preserves all registers.
+ ;
+.return:
+ mov xSP, xBP
+ pop xBP
+ BS3_CALL_CONV_EPILOG 3
+ BS3_HYBRID_RET
+
+ ;
+ ; Turns out we have to do switch to a different bitcount before saving.
+ ;
+.not_the_same_mode:
+ cmp al, BS3_MODE_CODE_16
+ je .code_16
+
+TONLY16 CPU 386
+%if TMPL_BITS != 32
+ cmp al, BS3_MODE_CODE_32
+ je .code_32
+%endif
+%if TMPL_BITS != 64
+ cmp al, BS3_MODE_CODE_V86
+ je .code_v86
+ cmp al, BS3_MODE_CODE_64
+ jne .bad_input_mode
+ jmp .code_64
+%endif
+
+ ; Bad input (al=input, dl=current).
+.bad_input_mode:
+ call Bs3Panic
+
+
+ ;
+ ; Save a 16-bit context.
+ ;
+ ; Convert pRegCtx to 16:16 protected mode and make sure we're in the
+ ; 16-bit code segment.
+ ;
+.code_16:
+%if TMPL_BITS == 16
+ %ifdef BS3_STRICT
+ cmp dl, BS3_MODE_CODE_V86
+ jne .bad_input_mode
+ %endif
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelRealModeDataToProtFar16
+ add sp, 4h
+ push dx ; Parameter #0 for _Bs3RegCtxSave_c16
+ push ax
+%else
+ %if TMPL_BITS == 32
+ push dword [xBP + xCB + cbCurRetAddr]
+ %endif
+ call Bs3SelFlatDataToProtFar16 ; 64-bit: BS3_CALL not needed, ecx not touched thus far.
+ mov [xSP], eax ; Parameter #0 for _Bs3RegCtxSave_c16
+ jmp .code_16_safe_segment
+ BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+.code_16_safe_segment:
+%endif
+ call Bs3SwitchTo16Bit
+ BS3_SET_BITS 16
+
+ call _Bs3RegCtxSave_c16
+
+%if TMPL_BITS == 16
+ call _Bs3SwitchTo16BitV86_c16
+%else
+ call _Bs3SwitchTo%[TMPL_BITS]Bit_c16
+%endif
+ BS3_SET_BITS TMPL_BITS
+ jmp .supplement_and_return
+ TMPL_BEGIN_TEXT
+
+TONLY16 CPU 386
+
+
+%if TMPL_BITS != 64
+ ;
+ ; Save a v8086 context.
+ ;
+.code_v86:
+ %if TMPL_BITS == 16
+ %ifdef BS3_STRICT
+ cmp dl, BS3_MODE_CODE_16
+ jne .bad_input_mode
+ %endif
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelProtFar16DataToRealMode
+ add sp, 4h
+ push dx ; Parameter #0 for _Bs3RegCtxSave_c16
+ push ax
+ %else
+ push dword [xBP + xCB + cbCurRetAddr]
+ call Bs3SelFlatDataToRealMode
+ mov [xSP], eax ; Parameter #0 for _Bs3RegCtxSave_c16
+ jmp .code_v86_safe_segment
+ BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+.code_v86_safe_segment:
+ %endif
+ call Bs3SwitchTo16BitV86
+ BS3_SET_BITS 16
+
+ call _Bs3RegCtxSave_c16
+
+ call _Bs3SwitchTo%[TMPL_BITS]Bit_c16
+ BS3_SET_BITS TMPL_BITS
+ jmp .supplement_and_return
+TMPL_BEGIN_TEXT
+%endif
+
+
+%if TMPL_BITS != 32
+ ;
+ ; Save a 32-bit context.
+ ;
+.code_32:
+ %if TMPL_BITS == 16
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+ test dl, BS3_MODE_CODE_V86
+ jnz .code_32_from_v86
+ call Bs3SelProtFar16DataToFlat
+ jmp .code_32_flat_ptr
+.code_32_from_v86:
+ call Bs3SelRealModeDataToFlat
+.code_32_flat_ptr:
+ add sp, 4h
+ push dx ; Parameter #0 for _Bs3RegCtxSave_c32
+ push ax
+ %else
+ mov [rsp], ecx ; Parameter #0 for _Bs3RegCtxSave_c16
+ %endif
+ call Bs3SwitchTo32Bit
+ BS3_SET_BITS 32
+
+ call _Bs3RegCtxSave_c32
+
+ %if TMPL_BITS == 16
+ cmp byte [bp - 2], BS3_MODE_CODE_V86
+ je .code_32_back_to_v86
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS TMPL_BITS
+ jmp .supplement_and_return
+.code_32_back_to_v86:
+ BS3_SET_BITS 32
+ call _Bs3SwitchTo16BitV86_c32
+ BS3_SET_BITS TMPL_BITS
+ jmp .return
+ %else
+ call _Bs3SwitchTo64Bit_c32
+ BS3_SET_BITS TMPL_BITS
+ jmp .supplement_and_return
+ %endif
+%endif
+
+
+%if TMPL_BITS != 64
+ ;
+ ; Save a 64-bit context.
+ ;
+ CPU x86-64
+.code_64:
+ %if TMPL_BITS == 16
+ %ifdef BS3_STRICT
+ cmp dl, BS3_MODE_CODE_16
+ jne .bad_input_mode
+ %endif
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelProtFar16DataToFlat
+ add sp, 4h
+ mov cx, dx ; Parameter #0 for _Bs3RegCtxSave_c64
+ shl ecx, 16
+ mov cx, ax
+ %else
+ mov ecx, [xBP + xCB + cbCurRetAddr] ; Parameter #0 for _Bs3RegCtxSave_c64
+ %endif
+ call Bs3SwitchTo64Bit ; (preserves all 32-bit GPRs)
+ BS3_SET_BITS 64
+
+ call _Bs3RegCtxSave_c64 ; No BS3_CALL as rcx is already ready.
+
+ call _Bs3SwitchTo%[TMPL_BITS]Bit_c64
+ BS3_SET_BITS TMPL_BITS
+ jmp .return
+%endif
+
+
+ ;
+ ; Supplement the state out of the current context and then return.
+ ;
+.supplement_and_return:
+%if ARCH_BITS == 16
+ CPU 8086
+ ; Skip 286 and older. Also make 101% sure we not in real mode or v8086 mode.
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
+ jb .return ; Just skip if 286 or older.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .return
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ jne .return ; paranoia
+ CPU 386
+%endif
+
+ ; Load the context pointer into a suitable register.
+%if ARCH_BITS == 64
+ %define pRegCtx rcx
+ mov rcx, [xBP - xCB]
+%elif ARCH_BITS == 32
+ %define pRegCtx ecx
+ mov ecx, [xBP + xCB + cbCurRetAddr]
+%else
+ %define pRegCtx es:bx
+ push es
+ push bx
+ les bx, [xBP + xCB + cbCurRetAddr]
+%endif
+%if ARCH_BITS == 64
+ ; If we're in 64-bit mode we can capture and restore the high bits.
+ test byte [pRegCtx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
+ jz .supplemented_64bit_registers
+ mov [pRegCtx + BS3REGCTX.r8], r8
+ mov [pRegCtx + BS3REGCTX.r9], r9
+ mov [pRegCtx + BS3REGCTX.r10], r10
+ mov [pRegCtx + BS3REGCTX.r11], r11
+ mov [pRegCtx + BS3REGCTX.r12], r12
+ mov [pRegCtx + BS3REGCTX.r13], r13
+ mov [pRegCtx + BS3REGCTX.r14], r14
+ mov [pRegCtx + BS3REGCTX.r15], r15
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rax + 4], eax
+ mov rax, rbx
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rbx + 4], eax
+ mov rax, rcx
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rcx + 4], eax
+ mov rax, rdx
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rdx + 4], eax
+ mov rax, rsp
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rsp + 4], eax
+ mov rax, rbp
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rbp + 4], eax
+ mov rax, rsi
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rsi + 4], eax
+ mov rax, rdi
+ shr rax, 32
+ mov [pRegCtx + BS3REGCTX.rdi + 4], eax
+ and byte [pRegCtx + BS3REGCTX.fbFlags], ~BS3REG_CTX_F_NO_AMD64
+.supplemented_64bit_registers:
+%endif
+ ; The rest requires ring-0 (at least during restore).
+ mov ax, ss
+ test ax, 3
+ jnz .done_supplementing
+
+ ; Do control registers.
+ test byte [pRegCtx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR4
+ jz .supplemented_control_registers
+ mov sAX, cr0
+ mov [pRegCtx + BS3REGCTX.cr0], sAX
+ mov sAX, cr2
+ mov [pRegCtx + BS3REGCTX.cr2], sAX
+ mov sAX, cr3
+ mov [pRegCtx + BS3REGCTX.cr3], sAX
+ and byte [pRegCtx + BS3REGCTX.fbFlags], ~(BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR0_IS_MSW)
+
+%if ARCH_BITS != 64
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
+ jz .supplemented_control_registers
+%endif
+ mov sAX, cr4
+ mov [pRegCtx + BS3REGCTX.cr4], sAX
+ and byte [pRegCtx + BS3REGCTX.fbFlags], ~BS3REG_CTX_F_NO_CR4
+.supplemented_control_registers:
+
+ ; Supply tr and ldtr if necessary
+ test byte [pRegCtx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_TR_LDTR
+ jz .done_supplementing
+ str [pRegCtx + BS3REGCTX.tr]
+ sldt [pRegCtx + BS3REGCTX.ldtr]
+ and byte [pRegCtx + BS3REGCTX.fbFlags], ~BS3REG_CTX_F_NO_TR_LDTR
+
+.done_supplementing:
+TONLY16 pop bx
+TONLY16 pop es
+ jmp .return
+%undef pRegCtx
+BS3_PROC_END_CMN Bs3RegCtxSaveEx
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveForMode.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveForMode.c
new file mode 100644
index 00000000..517d35a2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSaveForMode.c
@@ -0,0 +1,65 @@
+/* $Id: bs3-cmn-RegCtxSaveForMode.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSaveForMode
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSaveForMode
+BS3_CMN_DEF(void, Bs3RegCtxSaveForMode,(PBS3REGCTX pRegCtx, uint8_t bMode, uint16_t cbExtraStack))
+{
+ if ( bMode != BS3_MODE_RM
+#if ARCH_BIT == 16
+ || g_bBs3CurrentMode == BS3_MODE_RM
+#endif
+ )
+ {
+ BS3_ASSERT((bMode & BS3_MODE_SYS_MASK) == (g_bBs3CurrentMode & BS3_MODE_SYS_MASK));
+ Bs3RegCtxSaveEx(pRegCtx, bMode, cbExtraStack);
+ }
+ else
+ {
+#if ARCH_BIT == 64
+ BS3_ASSERT(0); /* No V86 mode in LM! */
+#endif
+ Bs3RegCtxSaveEx(pRegCtx, (bMode & ~BS3_MODE_CODE_MASK) | BS3_MODE_CODE_V86, cbExtraStack);
+ Bs3RegCtxConvertV86ToRm(pRegCtx);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGpr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGpr.c
new file mode 100644
index 00000000..1411ee76
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGpr.c
@@ -0,0 +1,64 @@
+/* $Id: bs3-cmn-RegCtxSetGpr.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetGpr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetGpr
+BS3_CMN_DEF(bool, Bs3RegCtxSetGpr,(PBS3REGCTX pRegCtx, uint8_t iGpr, uint64_t uValue, uint8_t cb))
+{
+ if (iGpr < 16)
+ {
+ PBS3REG pGpr = &pRegCtx->rax + iGpr;
+ switch (cb)
+ {
+ case 1: pGpr->u8 = (uint8_t)uValue; break;
+ case 2: pGpr->u16 = (uint16_t)uValue; break;
+ case 4: pGpr->u32 = (uint32_t)uValue; break;
+ case 8: pGpr->u64 = uValue; break;
+ default:
+ BS3_ASSERT(false);
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromCurPtr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromCurPtr.c
new file mode 100644
index 00000000..b45f583b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromCurPtr.c
@@ -0,0 +1,60 @@
+/* $Id: bs3-cmn-RegCtxSetGrpSegFromCurPtr.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetGrpSegFromCurPtr, Bs3RegCtxSetGrpDsFromCurPtr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetGrpSegFromCurPtr
+BS3_CMN_DEF(void, Bs3RegCtxSetGrpSegFromCurPtr,(PBS3REGCTX pRegCtx, PBS3REG pGpr, PRTSEL pSel, void BS3_FAR *pvPtr))
+{
+#if ARCH_BITS == 16
+ Bs3RegCtxSetGrpSegFromFlat(pRegCtx, pGpr, pSel, Bs3SelPtrToFlat(pvPtr));
+#else
+ Bs3RegCtxSetGrpSegFromFlat(pRegCtx, pGpr, pSel, (uintptr_t)pvPtr);
+#endif
+}
+
+
+#undef Bs3RegCtxSetGrpDsFromCurPtr
+BS3_CMN_DEF(void, Bs3RegCtxSetGrpDsFromCurPtr,(PBS3REGCTX pRegCtx, PBS3REG pGpr, void BS3_FAR *pvPtr))
+{
+ BS3_CMN_FAR_NM(Bs3RegCtxSetGrpSegFromCurPtr)(pRegCtx, pGpr, &pRegCtx->ds, pvPtr);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromFlat.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromFlat.c
new file mode 100644
index 00000000..9c9f525d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetGrpSegFromFlat.c
@@ -0,0 +1,75 @@
+/* $Id: bs3-cmn-RegCtxSetGrpSegFromFlat.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetGrpSegFromFlat
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetGrpSegFromFlat
+BS3_CMN_DEF(void, Bs3RegCtxSetGrpSegFromFlat,(PBS3REGCTX pRegCtx, PBS3REG pGpr, PRTSEL pSel, RTCCUINTXREG uFlat))
+{
+ if (BS3_MODE_IS_16BIT_CODE(pRegCtx->bMode))
+ {
+ uint32_t uFar1616;
+ if (BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ uFar1616 = Bs3SelFlatDataToRealMode(uFlat);
+ else
+ uFar1616 = Bs3SelFlatDataToProtFar16(uFlat);
+ pGpr->u = uFar1616 & UINT16_MAX;
+ *pSel = uFar1616 >> 16;
+ }
+ else
+ {
+ pGpr->u = uFlat;
+ if (BS3_MODE_IS_32BIT_CODE(pRegCtx->bMode))
+ *pSel = BS3_SEL_R0_DS32;
+ else
+ *pSel = BS3_SEL_R0_DS64;
+ }
+
+ /* Adjust CS to the right ring, if not ring-0 or V86 context. */
+ if ( pRegCtx->bCpl != 0
+ && !BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ {
+ if (BS3_SEL_IS_IN_R0_RANGE(*pSel))
+ *pSel += (uint16_t)pRegCtx->bCpl << BS3_SEL_RING_SHIFT;
+ *pSel |= pRegCtx->bCpl;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromCurPtr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromCurPtr.c
new file mode 100644
index 00000000..ba0948f1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromCurPtr.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-RegCtxSetRipCsFromCurPtr.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetRipCsFromCurPtr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetRipCsFromCurPtr
+BS3_CMN_DEF(void, Bs3RegCtxSetRipCsFromCurPtr,(PBS3REGCTX pRegCtx, FPFNBS3FAR pfnCode))
+{
+#if ARCH_BITS == 16
+ Bs3RegCtxSetRipCsFromFlat(pRegCtx, Bs3SelPtrToFlat((void BS3_FAR *)pfnCode));
+#else
+ Bs3RegCtxSetRipCsFromFlat(pRegCtx, (uintptr_t)pfnCode);
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromFlat.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromFlat.c
new file mode 100644
index 00000000..833997a4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromFlat.c
@@ -0,0 +1,75 @@
+/* $Id: bs3-cmn-RegCtxSetRipCsFromFlat.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetRipCsFromFlat
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetRipCsFromFlat
+BS3_CMN_DEF(void, Bs3RegCtxSetRipCsFromFlat,(PBS3REGCTX pRegCtx, RTCCUINTXREG uFlatCode))
+{
+ if (BS3_MODE_IS_16BIT_CODE(pRegCtx->bMode))
+ {
+ uint32_t uFar1616;
+ if (BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ uFar1616 = Bs3SelFlatCodeToRealMode(uFlatCode);
+ else
+ uFar1616 = Bs3SelFlatCodeToProtFar16(uFlatCode);
+ pRegCtx->rip.u = uFar1616 & UINT16_MAX;
+ pRegCtx->cs = uFar1616 >> 16;
+ }
+ else
+ {
+ pRegCtx->rip.u = uFlatCode;
+ if (BS3_MODE_IS_32BIT_CODE(pRegCtx->bMode))
+ pRegCtx->cs = BS3_SEL_R0_CS32;
+ else
+ pRegCtx->cs = BS3_SEL_R0_CS64;
+ }
+
+ /* Adjust CS to the right ring, if not ring-0 or V86 context. */
+ if ( pRegCtx->bCpl != 0
+ && !BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode)
+ && BS3_SEL_IS_IN_R0_RANGE(pRegCtx->cs))
+ {
+ pRegCtx->cs += (uint16_t)pRegCtx->bCpl << BS3_SEL_RING_SHIFT;
+ pRegCtx->cs |= pRegCtx->bCpl;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromLnkPtr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromLnkPtr.c
new file mode 100644
index 00000000..fd05fbbc
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegCtxSetRipCsFromLnkPtr.c
@@ -0,0 +1,87 @@
+/* $Id: bs3-cmn-RegCtxSetRipCsFromLnkPtr.c $ */
+/** @file
+ * BS3Kit - Bs3RegCtxSetRipCsFromLnkPtr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3RegCtxSetRipCsFromLnkPtr
+BS3_CMN_DEF(void, Bs3RegCtxSetRipCsFromLnkPtr,(PBS3REGCTX pRegCtx, FPFNBS3FAR pfnCode))
+{
+ if (BS3_MODE_IS_16BIT_CODE(pRegCtx->bMode))
+ {
+#if ARCH_BITS == 16
+ pRegCtx->rip.u = BS3_FP_OFF(pfnCode);
+ if (BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ pRegCtx->cs = BS3_FP_SEG(pfnCode);
+ else
+ pRegCtx->cs = Bs3SelRealModeCodeToProtMode(BS3_FP_SEG(pfnCode));
+#else
+ uint32_t uFar1616;
+ if (BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode))
+ uFar1616 = Bs3SelFlatCodeToRealMode((uint32_t)(uintptr_t)pfnCode);
+ else
+ uFar1616 = Bs3SelFlatCodeToProtFar16((uint32_t)(uintptr_t)pfnCode);
+ pRegCtx->rip.u = uFar1616 & UINT16_MAX;
+ pRegCtx->cs = uFar1616 >> 16;
+#endif
+ }
+ else
+ {
+#if ARCH_BITS == 16
+ pRegCtx->rip.u = Bs3SelRealModeCodeToFlat(pfnCode);
+#else
+ pRegCtx->rip.u = (uintptr_t)pfnCode;
+#endif
+ if (BS3_MODE_IS_32BIT_CODE(pRegCtx->bMode))
+ pRegCtx->cs = BS3_SEL_R0_CS32;
+ else
+ pRegCtx->cs = BS3_SEL_R0_CS64;
+ }
+
+ /* Adjust CS to the right ring, if not ring-0 or V86 context. */
+ if ( pRegCtx->bCpl != 0
+ && !BS3_MODE_IS_RM_OR_V86(pRegCtx->bMode)
+ && BS3_SEL_IS_IN_R0_RANGE(pRegCtx->cs))
+ {
+ pRegCtx->cs += (uint16_t)pRegCtx->bCpl << BS3_SEL_RING_SHIFT;
+ pRegCtx->cs |= pRegCtx->bCpl;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr0.asm
new file mode 100644
index 00000000..3f91ed80
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr0.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetCr0.asm $
+;; @file
+; BS3Kit - Bs3RegGetCr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetCr0,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetCr0, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, cr0
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_CRX
+ mov dl, 0
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetCr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr2.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr2.asm
new file mode 100644
index 00000000..eb6be133
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr2.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetCr2.asm $
+;; @file
+; BS3Kit - Bs3RegGetCr2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetCr2,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetCr2, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, cr2
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_CRX
+ mov dl, 2
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetCr2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr3.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr3.asm
new file mode 100644
index 00000000..4eddbd96
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr3.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetCr3.asm $
+;; @file
+; BS3Kit - Bs3RegGetCr3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetCr3,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetCr3, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, cr3
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_CRX
+ mov dl, 3
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetCr3
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr4.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr4.asm
new file mode 100644
index 00000000..a5e60735
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetCr4.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetCr4.asm $
+;; @file
+; BS3Kit - Bs3RegGetCr4
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetCr4,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetCr4, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, cr4
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_CRX
+ mov dl, 4
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetCr4
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr0.asm
new file mode 100644
index 00000000..1c144c33
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr0.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr0.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr0,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr0, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr0
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 0
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr1.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr1.asm
new file mode 100644
index 00000000..057a763c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr1.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr1.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr1,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr1, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr1
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 1
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr1
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr2.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr2.asm
new file mode 100644
index 00000000..8f2be61e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr2.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr2.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr2,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr2, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr2
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 2
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr3.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr3.asm
new file mode 100644
index 00000000..5388857e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr3.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr3.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr3,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr3, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr3
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 3
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr3
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr6.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr6.asm
new file mode 100644
index 00000000..12a56485
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr6.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr6.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr6
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr6,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr6, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr6
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 6
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr6
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr7.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr7.asm
new file mode 100644
index 00000000..ae45c4a8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDr7.asm
@@ -0,0 +1,89 @@
+; $Id: bs3-cmn-RegGetDr7.asm $
+;; @file
+; BS3Kit - Bs3RegGetDr7
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDr7,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDr7, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sAX, dr7
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+ jmp .return
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, 7
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDr7
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDrX.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDrX.asm
new file mode 100644
index 00000000..26cc9774
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetDrX.asm
@@ -0,0 +1,135 @@
+; $Id: bs3-cmn-RegGetDrX.asm $
+;; @file
+; BS3Kit - Bs3RegGetDrX
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+;TONLY16 CPU 386
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(RTCCUINTXREG, Bs3RegGetDrX,(uint8_t iReg));
+;
+; @returns Register value.
+; @param iRegister The source register
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs (only return full register(s)).
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetDrX, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ ; Switch (iRegister)
+ mov al, [xBP + xCB + cbCurRetAddr]
+ cmp al, 6
+ jz .get_dr6
+ cmp al, 7
+ jz .get_dr7
+ cmp al, 0
+ jz .get_dr0
+ cmp al, 1
+ jz .get_dr1
+ cmp al, 2
+ jz .get_dr2
+ cmp al, 3
+ jz .get_dr3
+ cmp al, 4
+ jz .get_dr4
+ cmp al, 5
+ jz .get_dr5
+ call Bs3Panic
+
+.get_dr0:
+ mov sAX, dr0
+ jmp .return_fixup
+.get_dr1:
+ mov sAX, dr1
+ jmp .return_fixup
+.get_dr2:
+ mov sAX, dr2
+ jmp .return_fixup
+.get_dr3:
+ mov sAX, dr3
+ jmp .return_fixup
+.get_dr4:
+ mov sAX, dr4
+ jmp .return_fixup
+.get_dr5:
+ mov sAX, dr5
+ jmp .return_fixup
+.get_dr7:
+ mov sAX, dr7
+ jmp .return_fixup
+.get_dr6:
+ mov sAX, dr6
+ jmp .return_fixup
+
+.via_system_call:
+ mov xAX, BS3_SYSCALL_GET_DRX
+ mov dl, [xBP + xCB + cbCurRetAddr]
+ call Bs3Syscall
+ jmp .return
+
+.return_fixup:
+TONLY16 mov edx, eax
+TONLY16 shr edx, 16
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetDrX
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetLdtr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetLdtr.asm
new file mode 100644
index 00000000..5291d5c6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetLdtr.asm
@@ -0,0 +1,80 @@
+; $Id: bs3-cmn-RegGetLdtr.asm $
+;; @file
+; BS3Kit - Bs3RegGetLdtr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(uint16_t, Bs3RegGetLdtr,(void));
+;
+; @returns The LDTR value.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetLdtr, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+%endif
+ ; Load it.
+ sldt ax
+ jmp .return
+
+.via_system_call:
+ mov ax, BS3_SYSCALL_GET_LDTR
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetLdtr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetTr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetTr.asm
new file mode 100644
index 00000000..d1f6a2d9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetTr.asm
@@ -0,0 +1,80 @@
+; $Id: bs3-cmn-RegGetTr.asm $
+;; @file
+; BS3Kit - Bs3RegGetTr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(uint16_t, Bs3RegGetTr,(void));
+;
+; @returns The LDTR value.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetTr, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+%endif
+ ; Load it.
+ str ax
+ jmp .return
+
+.via_system_call:
+ mov ax, BS3_SYSCALL_GET_TR
+ call Bs3Syscall
+
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetTr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetXcr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetXcr0.asm
new file mode 100644
index 00000000..f717205d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegGetXcr0.asm
@@ -0,0 +1,77 @@
+; $Id: bs3-cmn-RegGetXcr0.asm $
+;; @file
+; BS3Kit - Bs3RegGetXcr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(uint64_t, Bs3RegGetXcr0,(void));
+;
+; @returns Register value.
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs, though 16-bit mode the upper 48-bits of RAX, RDX and RCX are cleared.
+;
+BS3_PROC_BEGIN_CMN Bs3RegGetXcr0, BS3_PBC_HYBRID_SAFE
+ push xBP
+ mov xBP, xSP
+TONLY64 push rdx
+
+ ; Read the value.
+TNOT16 push sCX
+ xor ecx, ecx
+ xgetbv
+TNOT16 pop sCX
+
+ ; Move the edx:eax value into the appropriate return register(s).
+%if TMPL_BITS == 16
+ ; value [dx cx bx ax]
+ ror eax, 16
+ mov bx, ax
+ mov cx, dx
+ shr eax, 16
+ shr edx, 16
+%elif TMPL_BITS == 64
+ mov eax, eax
+ shr rdx, 32
+ or rax, rdx
+%endif
+
+TONLY64 pop rdx
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegGetXcr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr0.asm
new file mode 100644
index 00000000..2c1f4bc1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr0.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetCr0.asm $
+;; @file
+; BS3Kit - Bs3RegSetCr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetCr0,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetCr0, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov cr0, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_CRX
+ mov dl, 0
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetCr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr2.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr2.asm
new file mode 100644
index 00000000..282d850a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr2.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetCr2.asm $
+;; @file
+; BS3Kit - Bs3RegSetCr2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetCr1,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetCr2, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov cr2, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 2
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetCr2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr3.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr3.asm
new file mode 100644
index 00000000..b95df2db
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr3.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetCr3.asm $
+;; @file
+; BS3Kit - Bs3RegSetCr3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetCr3,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetCr3, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov cr3, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 3
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetCr3
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr4.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr4.asm
new file mode 100644
index 00000000..f32cc9fc
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetCr4.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetCr4.asm $
+;; @file
+; BS3Kit - Bs3RegSetCr4
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetCr4,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetCr4, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov cr4, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_CRX
+ mov dl, 4
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetCr4
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr0.asm
new file mode 100644
index 00000000..704d6f31
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr0.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr0.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr0,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr0, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr0, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 0
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr1.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr1.asm
new file mode 100644
index 00000000..6b20e58c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr1.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr1.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr1,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr1, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr1, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 1
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr1
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr2.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr2.asm
new file mode 100644
index 00000000..e39ff7f2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr2.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr2.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr2,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr2, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr2, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 2
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr3.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr3.asm
new file mode 100644
index 00000000..66b98c0a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr3.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr3.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr3,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr3, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr3, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 3
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr3
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr6.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr6.asm
new file mode 100644
index 00000000..11e945dd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr6.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr6.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr6
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr6,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr6, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr6, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 6
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr6
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr7.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr7.asm
new file mode 100644
index 00000000..ea36ad7c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDr7.asm
@@ -0,0 +1,96 @@
+; $Id: bs3-cmn-RegSetDr7.asm $
+;; @file
+; BS3Kit - Bs3RegSetDr7
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDr7,(RTCCUINTXREG uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDr7, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push sSI
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov dr7, sSI
+ jmp .return
+
+.via_system_call:
+ push xDX
+ push xAX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr]
+ mov xAX, BS3_SYSCALL_SET_DRX
+ mov dl, 7
+ call Bs3Syscall
+ pop xAX
+ pop xDX
+
+.return:
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDr7
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDrX.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDrX.asm
new file mode 100644
index 00000000..cc36df17
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetDrX.asm
@@ -0,0 +1,142 @@
+; $Id: bs3-cmn-RegSetDrX.asm $
+;; @file
+; BS3Kit - Bs3RegSetDrX
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Panic
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+;TONLY16 CPU 386
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetDrX,(uint8_t iReg, RTCCUINTXREG uValue));
+;
+; @returns Register value.
+; @param iRegister The source register
+; @param uValue The new Value.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetDrX, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push sSI
+ push xDX
+
+ mov sSI, [xBP + xCB + cbCurRetAddr + xCB]
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov dx, ss
+ and dx, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ ; Switch (iRegister)
+ mov dl, [xBP + xCB + cbCurRetAddr]
+ cmp dl, 6
+ jz .set_dr6
+ cmp dl, 7
+ jz .set_dr7
+ cmp dl, 0
+ jz .set_dr0
+ cmp dl, 1
+ jz .set_dr1
+ cmp dl, 2
+ jz .set_dr2
+ cmp dl, 3
+ jz .set_dr3
+ cmp dl, 4
+ jz .set_dr4
+ cmp dl, 5
+ jz .set_dr5
+
+ call Bs3Panic
+
+.set_dr0:
+ mov dr0, sSI
+ jmp .return
+.set_dr1:
+ mov dr1, sSI
+ jmp .return
+.set_dr2:
+ mov dr2, sSI
+ jmp .return
+.set_dr3:
+ mov dr3, sSI
+ jmp .return
+.set_dr4:
+ mov dr4, sSI
+ jmp .return
+.set_dr5:
+ mov dr5, sSI
+ jmp .return
+.set_dr7:
+ mov dr7, sSI
+ jmp .return
+.set_dr6:
+ mov dr6, sSI
+ jmp .return
+
+.via_system_call:
+ mov dl, [xBP + xCB + cbCurRetAddr]
+ push xAX
+ mov xAX, BS3_SYSCALL_SET_DRX
+ call Bs3Syscall
+ pop xAX
+
+.return:
+ pop xDX
+ pop sSI
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetDrX
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetLdtr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetLdtr.asm
new file mode 100644
index 00000000..8ff229ec
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetLdtr.asm
@@ -0,0 +1,91 @@
+; $Id: bs3-cmn-RegSetLdtr.asm $
+;; @file
+; BS3Kit - Bs3RegSetLdtr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetLdtr,(uint16_t uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetLdtr, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xDX
+ push xAX
+
+ mov dx, [xBP + xCB + cbCurRetAddr]
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+ ; Load it.
+ lldt dx
+ jmp .return
+
+.via_system_call:
+ mov ax, BS3_SYSCALL_SET_LDTR
+ call Bs3Syscall
+
+.return:
+ pop xAX
+ pop xDX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetLdtr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetTr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetTr.asm
new file mode 100644
index 00000000..0d0ffa8e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetTr.asm
@@ -0,0 +1,108 @@
+; $Id: bs3-cmn-RegSetTr.asm $
+;; @file
+; BS3Kit - Bs3RegSetTr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetTr,(uint16_t uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetTr, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xDX
+ push xAX
+ push xDI
+
+ mov dx, [xBP + xCB + cbCurRetAddr]
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov ax, ss
+ and ax, X86_SEL_RPL
+ jnz .via_system_call
+
+ ; Before we can load it, we must mark it non-busy.
+%if TMPL_BITS == 16
+ push ds
+ mov ax, BS3_SEL_SYSTEM16
+ mov ds, ax
+ mov di, dx
+ add xDI, Bs3Gdt wrt BS3SYSTEM16
+%else
+ movzx edi, dx
+ add xDI, Bs3Gdt wrt FLAT
+%endif
+ add xDI, X86DESCGENERIC_BIT_OFF_TYPE / 8
+ and byte [xDI], ~(X86_SEL_TYPE_SYS_TSS_BUSY_MASK << (X86DESCGENERIC_BIT_OFF_TYPE % 8))
+%if TMPL_BITS == 16
+ pop ds
+%endif
+ ltr dx
+ jmp .return
+
+.via_system_call:
+ mov ax, BS3_SYSCALL_SET_TR
+ call Bs3Syscall
+
+.return:
+ pop xDI
+ pop xAX
+ pop xDX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetTr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetXcr0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetXcr0.asm
new file mode 100644
index 00000000..eba35364
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-RegSetXcr0.asm
@@ -0,0 +1,104 @@
+; $Id: bs3-cmn-RegSetXcr0.asm $
+;; @file
+; BS3Kit - Bs3RegSetXcr0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO_STUB(void, Bs3RegSetXcr0,(uint64_t uValue));
+;
+; @param uValue The value to set.
+
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3RegSetXcr0, BS3_PBC_HYBRID_SAFE
+ push xBP
+ mov xBP, xSP
+ push sSI
+ push sDX
+ push sAX
+
+ ; Load the value
+%if TMPL_BITS == 64
+ mov eax, ecx
+ mov rdx, rcx
+ shr rdx, 32
+%else
+ mov sAX, [xBP + xCB + cbCurRetAddr]
+ mov sDX, [xBP + xCB + cbCurRetAddr + 4]
+%endif
+
+%if TMPL_BITS == 16
+ ; If V8086 mode we have to go thru a syscall.
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ jnz .via_system_call
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_RM
+ je .direct_access
+%endif
+ ; If not in ring-0, we have to make a system call.
+ mov si, ss
+ and si, X86_SEL_RPL
+ jnz .via_system_call
+
+.direct_access:
+ push sCX
+ xor ecx, ecx
+ xsetbv
+ pop sCX
+ jmp .return
+
+.via_system_call:
+ xchg esi, eax
+ mov xAX, BS3_SYSCALL_SET_XCR0
+ call Bs3Syscall
+
+.return:
+ pop sAX
+ pop sDX
+ pop sSI
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3RegSetXcr0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32.c
new file mode 100644
index 00000000..af3bc079
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32.c
@@ -0,0 +1,47 @@
+/* $Id: bs3-cmn-SelFar32ToFlat32.c $ */
+/** @file
+ * BS3Kit - Bs3SelFar32ToFlat32
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelFar32ToFlat32
+BS3_CMN_DEF(uint32_t, Bs3SelFar32ToFlat32,(uint32_t off, uint16_t uSel))
+{
+ if (BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+ return ((uint32_t)uSel << 4) + off;
+ return Bs3SelProtFar32ToFlat32(off, uSel);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32NoClobber.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32NoClobber.asm
new file mode 100644
index 00000000..d2df7366
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFar32ToFlat32NoClobber.asm
@@ -0,0 +1,114 @@
+; $Id: bs3-cmn-SelFar32ToFlat32NoClobber.asm $
+;; @file
+; BS3Kit - Bs3SelFar32ToFlat32NoClobber.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN Bs3SelFar32ToFlat32
+TMPL_BEGIN_TEXT
+
+
+;;
+; Wrapper around the Bs3SelFar32ToFlat32 C function that doesn't
+; clobber any registers nor require 20h stack scratch area (64-bit).
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch space required in 64-bit mode.
+;
+BS3_PROC_BEGIN_CMN Bs3SelFar32ToFlat32NoClobber, BS3_PBC_NEAR ; Far stub generated by the makefile.
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 16
+ push bx
+ push cx
+ push es
+
+ push word [xBP + xCB + cbCurRetAddr + 4] ; uSel
+ push word [xBP + xCB + cbCurRetAddr + 2] ; high off
+ push word [xBP + xCB + cbCurRetAddr] ; low off
+ call Bs3SelFar32ToFlat32
+ add sp, 6
+
+ pop es
+ pop cx
+ pop bx
+%else
+ push xDX
+ push xCX
+ %if TMPL_BITS == 32
+ push es
+ push fs
+ push gs
+
+ push dword [xBP + xCB + cbCurRetAddr + 4] ; uSel
+ push dword [xBP + xCB + cbCurRetAddr] ; off
+ call Bs3SelFar32ToFlat32
+ add esp, 8
+
+ pop gs
+ pop fs
+ pop es
+ %else
+ push r8
+ push r9
+ push r10
+ push r11
+ sub rsp, 20h
+
+ call Bs3SelFar32ToFlat32 ; Just pass ECX and DX along as-is.
+
+ add rsp, 20h
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ %endif
+ pop xCX
+ pop xDX
+%endif
+
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelFar32ToFlat32NoClobber
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToProtFar16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToProtFar16.asm
new file mode 100644
index 00000000..e7734dcb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToProtFar16.asm
@@ -0,0 +1,128 @@
+; $Id: bs3-cmn-SelFlatCodeToProtFar16.asm $
+;; @file
+; BS3Kit - Bs3SelFlatCodeToProtFar16.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN Bs3SelFlatCodeToRealMode
+BS3_EXTERN_CMN Bs3SelRealModeCodeToProtMode
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO(uint32_t, Bs3SelRealModeCodeToProtMode,(uint32_t uFlatAddr), false);
+;
+; @uses Only return registers (ax:dx, eax, eax)
+;
+BS3_PROC_BEGIN_CMN Bs3SelFlatCodeToProtFar16, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; Call Bs3SelFlatCodeToRealMode and then Bs3SelRealModeCodeToProtMode.
+ ; This avoid some code duplication.
+ ;
+%if TMPL_BITS == 16
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelFlatCodeToRealMode
+ add sp, 4h
+
+ push ax ; save the offset as it will be the same.
+
+ push dx
+ call Bs3SelRealModeCodeToProtMode
+ add sp, 2h
+
+ mov dx, ax ; The protected mode selector.
+ pop ax ; The offset.
+
+%elif TMPL_BITS == 32
+ push dword [xBP + xCB + cbCurRetAddr]
+ call Bs3SelFlatCodeToRealMode
+ add esp, 4h
+
+ push eax ; save the result.
+
+ shr eax, 16
+ push eax
+ call Bs3SelRealModeCodeToProtMode
+ add esp, 4h
+
+ mov [esp + 2], ax ; Update the selector before popping the result.
+ pop eax
+
+%elif TMPL_BITS == 64
+ push xCX ; Preserve RCX to make the behaviour uniform.
+ sub xSP, 28h ; 20h bytes of calling convention scratch and 8 byte for saving the result.
+
+ mov ecx, [xBP + xCB + cbCurRetAddr] ; move straight to parameter 0 register.
+ call Bs3SelFlatCodeToRealMode
+
+ mov [xBP - xCB*2], eax ; Save the result.
+
+ shr eax, 16
+ mov ecx, eax ; Move straight to parameter 0 register.
+ call Bs3SelRealModeCodeToProtMode
+
+ shl eax, 16 ; Shift prot mode selector into result position.
+ mov ax, [xBP - xCB*2] ; The segment offset from the previous call.
+
+ add xSP, 28h
+ pop xCX
+%else
+ %error "TMPL_BITS=" TMPL_BITS "!"
+%endif
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelFlatCodeToProtFar16
+
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3SelFlatCodeToProtFar16, 4
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToRealMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToRealMode.asm
new file mode 100644
index 00000000..f88d9d46
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatCodeToRealMode.asm
@@ -0,0 +1,163 @@
+; $Id: bs3-cmn-SelFlatCodeToRealMode.asm $
+;; @file
+; BS3Kit - Bs3SelFlatCodeToRealMode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 Bs3RmText16_EndOfSegment
+BS3_EXTERN_DATA16 Bs3X0Text16_EndOfSegment
+BS3_EXTERN_DATA16 Bs3X1Text16_EndOfSegment
+
+
+;
+; Make sure we can get at all the segments.
+;
+BS3_BEGIN_TEXT16
+BS3_BEGIN_RMTEXT16
+BS3_BEGIN_X0TEXT16
+BS3_BEGIN_X1TEXT16
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO(uint32_t, Bs3SelRealModeCodeToProtMode,(uint32_t uFlatAddr), false);
+;
+; @uses Only return registers (ax:dx, eax, eax)
+;
+BS3_PROC_BEGIN_CMN Bs3SelFlatCodeToRealMode, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xCX
+ push xBX
+%if TMPL_BITS != 16
+ push xDX
+%endif
+
+ ;
+ ; Load the real mode frame number into DX so we can compare with the
+ ; segment frame numbers fixed up by the linker.
+ ;
+ ; Imagine: FlatAddr = 0x054321
+ ;
+ mov dx, [xBP + xCB + cbCurRetAddr + 1] ; dx = 0x0543
+ mov al, [xBP + xCB + cbCurRetAddr + 0] ; al = 0x21
+ mov cl,4
+ shl dx, 4 ; dx = 0x5430
+ shr al, 4 ; al = 0x02
+ or dl, al ; dx = 0x5432
+
+ mov ax, dx
+ sub ax, CGROUP16
+ cmp ax, 1000h
+ jb .bs3text16
+
+ mov ax, dx
+ sub ax, BS3GROUPRMTEXT16
+ mov bx, Bs3RmText16_EndOfSegment wrt BS3GROUPRMTEXT16
+ add bx, 15
+ shr bx, cl
+ cmp ax, bx
+ jb .bs3rmtext16
+
+ mov ax, dx
+ sub ax, BS3GROUPX0TEXT16
+ mov bx, Bs3X0Text16_EndOfSegment wrt BS3GROUPX0TEXT16
+ add bx, 15
+ shr bx, cl
+ cmp ax, bx
+ jb .bs3x0text16
+
+ mov ax, dx
+ sub ax, BS3GROUPX1TEXT16
+ mov bx, Bs3X1Text16_EndOfSegment wrt BS3GROUPX1TEXT16
+ add bx, 15
+ shr bx, cl
+ cmp ax, bx
+ jb .bs3x1text16
+
+ extern BS3_CMN_NM(Bs3Panic)
+ call BS3_CMN_NM(Bs3Panic)
+
+ ;
+ ; Load the real-mode frame into DX and calc the offset in AX.
+ ;
+.bs3x1text16:
+ mov dx, BS3GROUPX1TEXT16
+ jmp .calc_return
+.bs3x0text16:
+ mov dx, BS3GROUPX0TEXT16
+ jmp .calc_return
+.bs3rmtext16:
+ mov dx, BS3GROUPRMTEXT16
+ jmp .calc_return
+.bs3text16:
+ mov dx, CGROUP16
+.calc_return:
+ ; Convert the real-mode frame into the low 16-bit base (BX).
+ mov bx, dx
+ shl bx, cl
+ ; Subtract the 16-bit base from the flat address. (No need to consider
+ ; the top half on either side.)
+ mov ax, [xBP + xCB + cbCurRetAddr + 0]
+ sub ax, bx
+%if TMPL_BITS != 16
+ ; Got a single 32-bit return register here.
+ shl edx, 16
+ mov dx, ax
+ mov eax, edx
+ pop xDX
+%endif
+ pop xBX
+ pop xCX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelFlatCodeToRealMode
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3SelFlatCodeToRealMode, 4
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToProtFar16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToProtFar16.asm
new file mode 100644
index 00000000..6869247b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToProtFar16.asm
@@ -0,0 +1,142 @@
+; $Id: bs3-cmn-SelFlatDataToProtFar16.asm $
+;; @file
+; BS3Kit - Bs3SelFlatDataToProtFar16.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%ifdef BS3_STRICT
+BS3_EXTERN_CMN Bs3Panic
+%endif
+
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelFlatDataToProtFar16,(uint32_t uFlatAddr));
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelFlatDataToProtFar16, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; Check if we can use the protected mode stack or data selector.
+ ; The latter ensures the usability of this function for setting SS.
+ ;
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr]
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+ test dx, dx
+ jnz .not_stack
+ mov dx, BS3_SEL_R0_SS16
+%else
+TNOT64 mov eax, [xBP + xCB + cbCurRetAddr]
+TONLY64 mov eax, ecx
+ test eax, 0ffff0000h
+ jnz .not_stack
+ or eax, BS3_SEL_R0_SS16 << 16
+%endif
+ jmp .return
+
+.not_stack:
+%if TMPL_BITS == 16
+ sub ax, BS3_ADDR_BS3DATA16 & 0xffff
+ sbb dx, BS3_ADDR_BS3DATA16 >> 16
+ jnz .do_tiled
+ mov dx, BS3_SEL_R0_DS16
+%else
+ sub eax, BS3_ADDR_BS3DATA16
+ test eax, 0ffff0000h
+ jnz .do_tiled
+ or eax, BS3_SEL_R0_DS16 << 16
+%endif
+ jmp .return
+
+ ;
+ ; Just translate the address to tiled.
+ ;
+.do_tiled:
+%if TMPL_BITS == 16
+ ; Convert upper 16-bit to a tiled selector.
+ mov ax, cx ; save cx
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+ %ifdef BS3_STRICT
+ cmp dx, BS3_SEL_TILED_AREA_SIZE >> 16
+ jb .address_ok
+ call Bs3Panic
+.address_ok:
+ %endif
+ mov cl, X86_SEL_SHIFT
+ shl dx, cl
+ add dx, BS3_SEL_TILED
+ mov cx, ax ; restore cx
+
+ ; Load segment offset and return.
+ mov ax, [xBP + xCB + cbCurRetAddr]
+
+%else
+ ; Convert upper 16-bit to tiled selector.
+TNOT64 mov eax, [xBP + xCB + cbCurRetAddr]
+TONLY64 mov rax, rcx
+ %ifdef BS3_STRICT
+ cmp xAX, BS3_SEL_TILED_AREA_SIZE
+ jb .address_ok
+ call Bs3Panic
+.address_ok:
+ %endif
+ ror eax, 16
+ shl ax, X86_SEL_SHIFT
+ add ax, BS3_SEL_TILED
+ rol eax, 16
+%endif
+
+.return:
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelFlatDataToProtFar16
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToRealMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToRealMode.asm
new file mode 100644
index 00000000..cb142e80
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelFlatDataToRealMode.asm
@@ -0,0 +1,104 @@
+; $Id: bs3-cmn-SelFlatDataToRealMode.asm $
+;; @file
+; BS3Kit - Bs3SelFlatDataToRealMode
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%ifdef BS3_STRICT
+BS3_EXTERN_CMN Bs3Panic
+%endif
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelFlatDataToRealMode,(uint32_t uFlatAddr));
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelFlatDataToRealMode, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; Take the simplest approach possible (64KB tiled).
+ ;
+%if TMPL_BITS == 16
+ mov ax, cx ; save cx
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+ %ifdef BS3_STRICT
+ cmp dx, _1M >> 16
+ jb .address_ok
+ call Bs3Panic
+.address_ok:
+ %endif
+ mov cl, 12
+ shl dx, cl
+ mov ax, cx ; restore cx
+
+ mov ax, [xBP + xCB + cbCurRetAddr]
+
+%else
+ %if TMPL_BITS == 32
+ mov eax, [xBP + xCB + cbCurRetAddr]
+ %else
+ mov rax, rcx
+ %endif
+ %ifdef BS3_STRICT
+ cmp xAX, _1M
+ jb .address_ok
+ call Bs3Panic
+.address_ok:
+ %endif
+ ror eax, 16
+ shl ax, 12
+ rol eax, 16
+%endif
+
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelFlatDataToRealMode
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToCurPtr.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToCurPtr.c
new file mode 100644
index 00000000..add55863
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToCurPtr.c
@@ -0,0 +1,51 @@
+/* $Id: bs3-cmn-SelLnkPtrToCurPtr.c $ */
+/** @file
+ * BS3Kit - Bs3SelLnkPtrToCurPtr
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelLnkPtrToCurPtr
+BS3_CMN_DEF(void BS3_FAR *, Bs3SelLnkPtrToCurPtr,(void BS3_FAR *pvLnkPtr))
+{
+#if ARCH_BITS == 16
+ if (BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+ return pvLnkPtr;
+ return (void BS3_FAR *)Bs3SelRealModeDataToProtFar16((uint32_t)pvLnkPtr);
+#else
+ return pvLnkPtr;
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToFlat.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToFlat.c
new file mode 100644
index 00000000..7de7db3e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelLnkPtrToFlat.c
@@ -0,0 +1,49 @@
+/* $Id: bs3-cmn-SelLnkPtrToFlat.c $ */
+/** @file
+ * BS3Kit - Bs3SelLnkPtrToFlat
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelLnkPtrToFlat
+BS3_CMN_DEF(uint32_t, Bs3SelLnkPtrToFlat,(void BS3_FAR *pvLnkPtr))
+{
+#if ARCH_BITS == 16
+ return Bs3SelRealModeDataToFlat((uint32_t)pvLnkPtr);
+#else
+ return (uint32_t)(uintptr_t)pvLnkPtr;
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToFlat.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToFlat.asm
new file mode 100644
index 00000000..5aea8c16
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToFlat.asm
@@ -0,0 +1,100 @@
+; $Id: bs3-cmn-SelProtFar16DataToFlat.asm $
+;; @file
+; BS3Kit - Bs3SelProtFar16DataToFlat.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN Bs3SelFar32ToFlat32NoClobber
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelProtFar16DataToFlat,(uint32_t uFar1616));
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelProtFar16DataToFlat, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; Just call Bs3SelFar32ToFlat32NoClobber to do the job.
+ ;
+%if TMPL_BITS == 16
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ xor ax, ax
+ push ax
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelFar32ToFlat32NoClobber
+ add sp, 6
+%else
+ %if TMPL_BITS == 32
+ movzx eax, word [xBP + xCB + cbCurRetAddr + 2]
+ push eax
+ movzx eax, word [xBP + xCB + cbCurRetAddr]
+ push eax
+ call Bs3SelFar32ToFlat32NoClobber
+ add esp, 8
+ %else
+ push xDX
+ push xCX
+
+ mov edx, ecx ; arg #2: selector
+ shr edx, 16
+ movzx ecx, cx ; arg #1: offset
+ call Bs3SelFar32ToFlat32NoClobber
+
+ pop xDX
+ pop xCX
+ %endif
+%endif
+
+.return:
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelProtFar16DataToFlat
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToRealMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToRealMode.asm
new file mode 100644
index 00000000..b244adf8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar16DataToRealMode.asm
@@ -0,0 +1,157 @@
+; $Id: bs3-cmn-SelProtFar16DataToRealMode.asm $
+;; @file
+; BS3Kit - Bs3SelProtFar16DataToRealMode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16 ; For real mode segment value.
+BS3_BEGIN_SYSTEM16 ; Ditto.
+TMPL_BEGIN_TEXT
+BS3_EXTERN_CMN Bs3SelFar32ToFlat32NoClobber
+
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelProtFar16DataToRealMode,(uint32_t uFar1616));
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelProtFar16DataToRealMode, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; See if it's our default 16-bit ring-0 data, stack or system data segment.
+ ;
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr + 2]
+%elif TMPL_BITS == 32
+ movzx eax, word [xBP + xCB + cbCurRetAddr + 2]
+%else
+ mov eax, ecx
+ shr eax, 16
+%endif
+ cmp ax, BS3_SEL_R0_SS16
+ jne .not_stack
+ mov ax, 0
+
+.quick_return:
+%if TMPL_BITS == 16
+ mov dx, ax
+ mov ax, [xBP + xCB + cbCurRetAddr]
+%elif TMPL_BITS == 32
+ shl eax, 16
+ mov ax, word [xBP + xCB + cbCurRetAddr]
+%else
+ shl eax, 16
+ mov ax, cx
+%endif
+
+.return:
+ pop xBP
+ BS3_HYBRID_RET
+
+.not_stack:
+ cmp ax, BS3_SEL_R0_DS16
+ jne .not_dgroup
+ mov ax, BS3KIT_GRPNM_DATA16
+ jmp .quick_return
+
+.not_dgroup:
+ cmp ax, BS3_SEL_SYSTEM16
+ jne .not_system16
+ mov ax, BS3SYSTEM16
+ jmp .quick_return
+
+ ;
+ ; Call worker function to convert it to flat and the do tiled
+ ; calculation from that.
+ ;
+.not_system16:
+%if TMPL_BITS == 16
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ xor ax, ax
+ push ax
+ push word [xBP + xCB + cbCurRetAddr]
+ call Bs3SelFar32ToFlat32NoClobber
+ add sp, 6
+
+ ; Convert upper 16-bit of the flat address to a tiled selector.
+ push cx
+ mov cl, X86_SEL_SHIFT
+ shl dx, cl
+ add dx, BS3_SEL_TILED
+ pop cx
+%else
+ %if TMPL_BITS == 32
+ push eax
+ movzx eax, word [xBP + xCB + cbCurRetAddr]
+ push eax
+ call Bs3SelFar32ToFlat32NoClobber
+ add esp, 8
+ %else
+ push xDX
+ push xCX
+
+ mov edx, eax ; arg #2: selector
+ movzx ecx, cx ; arg #1: offset
+ call Bs3SelFar32ToFlat32NoClobber
+
+ pop xDX
+ pop xCX
+ %endif
+
+ ; Convert upper 16-bit to tiled selector.
+ rol eax, 16
+ shl ax, X86_SEL_SHIFT
+ add ax, BS3_SEL_TILED
+ ror eax, 16
+%endif
+ jmp .return
+BS3_PROC_END_CMN Bs3SelProtFar16DataToRealMode
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar32ToFlat32.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar32ToFlat32.c
new file mode 100644
index 00000000..7d31de27
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtFar32ToFlat32.c
@@ -0,0 +1,55 @@
+/* $Id: bs3-cmn-SelProtFar32ToFlat32.c $ */
+/** @file
+ * BS3Kit - Bs3SelProtFar32ToFlat32
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelProtFar32ToFlat32
+BS3_CMN_DEF(uint32_t, Bs3SelProtFar32ToFlat32,(uint32_t off, uint16_t uSel))
+{
+ uint32_t uRet;
+ PCX86DESC pEntry;
+ if (!(uSel & X86_SEL_LDT))
+ pEntry = &Bs3Gdt[uSel >> X86_SEL_SHIFT];
+ else
+ pEntry = &Bs3Ldt[uSel >> X86_SEL_SHIFT];
+ uRet = pEntry->Gen.u16BaseLow;
+ uRet |= (uint32_t)pEntry->Gen.u8BaseHigh1 << 16;
+ uRet |= (uint32_t)pEntry->Gen.u8BaseHigh2 << 24;
+ uRet += off;
+ return uRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtModeCodeToRealMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtModeCodeToRealMode.asm
new file mode 100644
index 00000000..6fd749bd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelProtModeCodeToRealMode.asm
@@ -0,0 +1,122 @@
+; $Id: bs3-cmn-SelProtModeCodeToRealMode.asm $
+;; @file
+; BS3Kit - Bs3SelProtModeCodeToRealMode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;
+; Make sure we can get at all the segments.
+;
+BS3_BEGIN_TEXT16
+BS3_BEGIN_RMTEXT16
+BS3_BEGIN_X0TEXT16
+BS3_BEGIN_X1TEXT16
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO(uint16_t, Bs3SelProtModeCodeToRealMode,(uint16_t uRealSel), false);
+; @uses ax (return register)
+;
+BS3_PROC_BEGIN_CMN Bs3SelProtModeCodeToRealMode, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+ ; Load it and mask off the RPL.
+ mov ax, [xBP + xCB + cbCurRetAddr]
+ and ax, X86_SEL_MASK_OFF_RPL
+
+ ; We're allowed to use the real-mode segment value.
+ cmp ax, CGROUP16
+ je .bs3text16
+
+ ; Check for typical code segments.
+ cmp ax, BS3_SEL_R0_CS16
+ je .bs3text16
+ cmp ax, BS3_SEL_RMTEXT16_CS
+ je .bs3rmtext16
+ cmp ax, BS3_SEL_X0TEXT16_CS
+ je .bs3x0text16
+ cmp ax, BS3_SEL_X1TEXT16_CS
+ je .bs3x1text16
+
+ ; Check for less common BS3_SEL_R*_CS16_* values.
+ cmp ax, BS3_SEL_R0_FIRST
+ jb .panic
+ cmp ax, BS3_SEL_R3_FIRST + (1 << BS3_SEL_RING_SHIFT)
+ jae .panic
+
+ ; Since the relevant bits now are the lower 8 ones, we skip the
+ ; AND AX, BS3_SEL_RING_SHIFT
+ ; ADD AX, BS3_SEL_R0_FIRST
+ ; bits and compare AL with lower 8-bit of the BS3_SEL_R0_CS16* values.
+AssertCompile(BS3_SEL_RING_SHIFT == 8)
+ cmp al, BS3_SEL_R0_CS16 & 0xff
+ je .bs3text16
+ cmp al, BS3_SEL_R0_CS16_EO & 0xff
+ je .bs3text16
+ cmp ax, BS3_SEL_R0_CS16_CNF & 0xff
+ je .bs3text16
+ cmp ax, BS3_SEL_R0_CS16_CNF_EO & 0xff
+ je .bs3text16
+.panic:
+ extern BS3_CMN_NM(Bs3Panic)
+ call BS3_CMN_NM(Bs3Panic)
+ jmp .return
+
+.bs3x1text16:
+ mov ax, BS3GROUPX1TEXT16
+ jmp .return
+.bs3x0text16:
+ mov ax, BS3GROUPX0TEXT16
+ jmp .return
+.bs3rmtext16:
+ mov ax, BS3GROUPRMTEXT16
+ jmp .return
+.bs3text16:
+ mov ax, CGROUP16
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelProtModeCodeToRealMode
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3SelProtModeCodeToRealMode, 2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeCodeToProtMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeCodeToProtMode.asm
new file mode 100644
index 00000000..c574901a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeCodeToProtMode.asm
@@ -0,0 +1,94 @@
+; $Id: bs3-cmn-SelRealModeCodeToProtMode.asm $
+;; @file
+; BS3Kit - Bs3SelRealModeCodeToProtMode.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+;
+; Make sure we can get at all the segments.
+;
+BS3_BEGIN_TEXT16
+BS3_BEGIN_RMTEXT16
+BS3_BEGIN_X0TEXT16
+BS3_BEGIN_X1TEXT16
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_CMN_PROTO(uint16_t, Bs3SelRealModeCodeToProtMode,(uint16_t uRealSel), false);
+; @uses ax (return register)
+;
+BS3_PROC_BEGIN_CMN Bs3SelRealModeCodeToProtMode, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+
+ mov ax, [xBP + xCB + cbCurRetAddr]
+ cmp ax, CGROUP16
+ je .bs3text16
+ cmp ax, BS3GROUPRMTEXT16
+ je .bs3rmtext16
+ cmp ax, BS3GROUPX0TEXT16
+ je .bs3x0text16
+ cmp ax, BS3GROUPX1TEXT16
+ je .bs3x1text16
+
+ extern BS3_CMN_NM(Bs3Panic)
+ call BS3_CMN_NM(Bs3Panic)
+ jmp .return
+
+.bs3x1text16:
+ mov ax, BS3_SEL_X1TEXT16_CS
+ jmp .return
+.bs3x0text16:
+ mov ax, BS3_SEL_X0TEXT16_CS
+ jmp .return
+.bs3rmtext16:
+ mov ax, BS3_SEL_RMTEXT16_CS
+ jmp .return
+.bs3text16:
+ mov ax, BS3_SEL_R0_CS16
+.return:
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelRealModeCodeToProtMode
+
+;
+; We may be using the near code in some critical code paths, so don't
+; penalize it.
+;
+BS3_CMN_FAR_STUB Bs3SelRealModeCodeToProtMode, 2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToFlat.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToFlat.asm
new file mode 100644
index 00000000..0f0f0408
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToFlat.asm
@@ -0,0 +1,101 @@
+; $Id: bs3-cmn-SelRealModeDataToFlat.asm $
+;; @file
+; BS3Kit - Bs3SelRealModeDataToFlat.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelRealModeDataToFlat,(uint32_t uFar1616));
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelRealModeCodeToFlat,(uint32_t uFar1616));
+;
+; @uses Only return registers (ax:dx, eax, eax);
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelRealModeCodeToFlat, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+BS3_PROC_BEGIN_CMN Bs3SelRealModeDataToFlat, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ; Calc flat address.
+%if TMPL_BITS == 16
+ push cx
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+ mov ax, dx
+ mov cl, 12
+ shr dx, cl
+ mov cl, 4
+ shl ax, cl
+ add ax, [xBP + xCB + cbCurRetAddr]
+ adc dx, 0
+ pop cx
+
+%elif TMPL_BITS == 32
+ movzx eax, word [xBP + xCB + cbCurRetAddr + 2]
+ shl eax, 4
+ add ax, [xBP + xCB + cbCurRetAddr]
+ jnc .return
+ add eax, 10000h
+
+%elif TMPL_BITS == 64
+ mov eax, ecx
+ shr eax, 16
+ shl eax, 4
+ add ax, cx
+ jnc .return
+ add eax, 10000h
+
+%else
+ %error "TMPL_BITS!"
+%endif
+
+.return:
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SelRealModeDataToFlat
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToProtFar16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToProtFar16.asm
new file mode 100644
index 00000000..b70a7894
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelRealModeDataToProtFar16.asm
@@ -0,0 +1,151 @@
+; $Id: bs3-cmn-SelRealModeDataToProtFar16.asm $
+;; @file
+; BS3Kit - Bs3SelRealModeDataToProtFar16.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16 ; For real mode segment value.
+BS3_BEGIN_SYSTEM16 ; Ditto.
+
+TMPL_BEGIN_TEXT
+%if TMPL_BITS == 16
+CPU 8086
+%endif
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelRealModeDataToProtFar16,(uint32_t uFar1616));
+;
+; @uses Only return registers (ax:dx, eax, eax)
+; @remarks No 20h scratch area requirements.
+;
+BS3_PROC_BEGIN_CMN Bs3SelRealModeDataToProtFar16, BS3_PBC_NEAR ; Far stub generated by the makefile/bs3kit.h.
+ push xBP
+ mov xBP, xSP
+
+ ;
+ ; See if it's our default 16-bit data, stack or system data segment.
+ ;
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr + 2]
+%elif TMPL_BITS == 32
+ movzx eax, word [xBP + xCB + cbCurRetAddr + 2]
+%else
+ mov eax, ecx
+ shr eax, 16
+%endif
+ cmp ax, 0
+ jnz .not_stack
+ mov ax, BS3_SEL_R0_SS16
+
+.quick_return:
+%if TMPL_BITS == 16
+ mov dx, ax
+ mov ax, [xBP + xCB + cbCurRetAddr]
+%elif TMPL_BITS == 32
+ shl eax, 16
+ mov ax, word [xBP + xCB + cbCurRetAddr]
+%else
+ shl eax, 16
+ mov ax, cx
+%endif
+
+.return:
+ pop xBP
+ BS3_HYBRID_RET
+
+.not_stack:
+ cmp ax, BS3KIT_GRPNM_DATA16
+ jne .not_dgroup
+ mov ax, BS3_SEL_R0_DS16
+ jmp .quick_return
+
+.not_dgroup:
+ cmp ax, BS3SYSTEM16
+ jne .not_system16
+ mov ax, BS3_SEL_SYSTEM16
+ jmp .quick_return
+
+ ;
+ ; Compute flat address and translate it to tiled.
+ ;
+.not_system16:
+%if TMPL_BITS == 16
+ push cx
+
+ ; Calc flat address.
+ mov dx, ax
+ mov cl, 12
+ shr dx, cl
+ mov cl, 4
+ shl ax, cl
+ add ax, [xBP + xCB + cbCurRetAddr]
+ adc dx, 0
+
+ ; Convert upper 16-bit to tiled selector.
+ mov cl, X86_SEL_SHIFT
+ shl dx, cl
+ add dx, BS3_SEL_TILED
+
+ pop cx
+%else
+ ; Calc flat address.
+ shl eax, 4
+ %if TMPL_BITS == 32
+ add ax, [xBP + xCB + cbCurRetAddr]
+ %else
+ add ax, cx
+ %endif
+ jnc .no_carry
+ add eax, 10000h
+.no_carry:
+
+ ; Convert upper 16-bit to tiled selector.
+ rol eax, 16
+ shl ax, X86_SEL_SHIFT
+ add ax, BS3_SEL_TILED
+ ror eax, 16
+%endif
+ jmp .return
+BS3_PROC_END_CMN Bs3SelRealModeDataToProtFar16
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitCode.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitCode.c
new file mode 100644
index 00000000..5c53c98d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitCode.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-SelSetup16BitCode.c $ */
+/** @file
+ * BS3Kit - Bs3SelSetup16BitCode
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+
+
+#undef Bs3SelSetup16BitCode
+BS3_CMN_DEF(void, Bs3SelSetup16BitCode,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr, uint8_t bDpl))
+{
+ pDesc->Gen.u16LimitLow = UINT16_C(0xffff);
+ pDesc->Gen.u16BaseLow = (uint16_t)uBaseAddr;
+ pDesc->Gen.u8BaseHigh1 = (uint8_t)(uBaseAddr >> 16);
+ pDesc->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
+ pDesc->Gen.u1DescType = 1; /* data/code */
+ pDesc->Gen.u2Dpl = bDpl & 3;
+ pDesc->Gen.u1Present = 1;
+ pDesc->Gen.u4LimitHigh = 0;
+ pDesc->Gen.u1Available = 0;
+ pDesc->Gen.u1Long = 0;
+ pDesc->Gen.u1DefBig = 0;
+ pDesc->Gen.u1Granularity = 0;
+ pDesc->Gen.u8BaseHigh2 = (uint8_t)(uBaseAddr >> 24);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitData.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitData.c
new file mode 100644
index 00000000..611f5a1e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup16BitData.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-SelSetup16BitData.c $ */
+/** @file
+ * BS3Kit - Bs3SelSetup16BitData
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+
+
+#undef Bs3SelSetup16BitData
+BS3_CMN_DEF(void, Bs3SelSetup16BitData,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr))
+{
+ pDesc->Gen.u16LimitLow = UINT16_C(0xffff);
+ pDesc->Gen.u16BaseLow = (uint16_t)uBaseAddr;
+ pDesc->Gen.u8BaseHigh1 = (uint8_t)(uBaseAddr >> 16);
+ pDesc->Gen.u4Type = X86_SEL_TYPE_RW_ACC;
+ pDesc->Gen.u1DescType = 1; /* data/code */
+ pDesc->Gen.u2Dpl = 3;
+ pDesc->Gen.u1Present = 1;
+ pDesc->Gen.u4LimitHigh = 0;
+ pDesc->Gen.u1Available = 0;
+ pDesc->Gen.u1Long = 0;
+ pDesc->Gen.u1DefBig = 0;
+ pDesc->Gen.u1Granularity = 0;
+ pDesc->Gen.u8BaseHigh2 = (uint8_t)(uBaseAddr >> 24);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup32BitCode.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup32BitCode.c
new file mode 100644
index 00000000..e3942d80
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetup32BitCode.c
@@ -0,0 +1,62 @@
+/* $Id: bs3-cmn-SelSetup32BitCode.c $ */
+/** @file
+ * BS3Kit - Bs3SelSetup32BitCode
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+
+
+#undef Bs3SelSetup32BitCode
+BS3_CMN_DEF(void, Bs3SelSetup32BitCode,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr, uint32_t uLimit, uint8_t bDpl))
+{
+ uint8_t const cLimitShift = uLimit <= UINT32_C(0xfffff) ? 0 : 12;
+ pDesc->Gen.u16LimitLow = (uint16_t)(uLimit >> cLimitShift);
+ pDesc->Gen.u16BaseLow = (uint16_t)uBaseAddr;
+ pDesc->Gen.u8BaseHigh1 = (uint8_t)(uBaseAddr >> 16);
+ pDesc->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
+ pDesc->Gen.u1DescType = 1; /* data/code */
+ pDesc->Gen.u2Dpl = bDpl & 3;
+ pDesc->Gen.u1Present = 1;
+ pDesc->Gen.u4LimitHigh = (unsigned)(uLimit >> (16 + cLimitShift));
+ pDesc->Gen.u1Available = 0;
+ pDesc->Gen.u1Long = 0;
+ pDesc->Gen.u1DefBig = 1;
+ pDesc->Gen.u1Granularity = cLimitShift != 0;
+ pDesc->Gen.u8BaseHigh2 = (uint8_t)(uBaseAddr >> 24);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate.c
new file mode 100644
index 00000000..99cc3581
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-SelSetupGate.c $ */
+/** @file
+ * BS3Kit - Bs3SelSetupGate
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelSetupGate
+BS3_CMN_DEF(void, Bs3SelSetupGate,(X86DESC BS3_FAR *pDesc, uint8_t bType, uint8_t bDpl,
+ uint16_t uSel, uint32_t off, uint8_t cParams))
+{
+ BS3_ASSERT(bDpl <= 3);
+ BS3_ASSERT(bType <= 15);
+ BS3_ASSERT(cParams <= 15);
+ pDesc->Gate.u16OffsetLow = (uint16_t)off;
+ pDesc->Gate.u16OffsetHigh = (uint16_t)(off >> 16);
+ pDesc->Gate.u16Sel = uSel;
+ pDesc->Gate.u5ParmCount = cParams;
+ pDesc->Gate.u4Type = bType;
+ pDesc->Gate.u2Dpl = bDpl;
+ pDesc->Gate.u3Reserved = 0;
+ pDesc->Gate.u1DescType = 0; /* system */
+ pDesc->Gate.u1Present = 1;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate64.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate64.c
new file mode 100644
index 00000000..b2c4927e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SelSetupGate64.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-SelSetupGate64.c $ */
+/** @file
+ * BS3Kit - Bs3SelSetupGate64
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SelSetupGate64
+BS3_CMN_DEF(void, Bs3SelSetupGate64,(X86DESC BS3_FAR *pDescPair, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint64_t off))
+{
+ BS3_ASSERT(bDpl <= 3);
+ BS3_ASSERT(bType <= 15);
+ pDescPair[0].Gate.u16OffsetLow = (uint16_t)off;
+ pDescPair[0].Gate.u16OffsetHigh = (uint16_t)(off >> 16);
+ pDescPair[0].Gate.u16Sel = uSel;
+ pDescPair[0].Gate.u5ParmCount = 0;
+ pDescPair[0].Gate.u4Type = bType;
+ pDescPair[0].Gate.u2Dpl = bDpl;
+ pDescPair[0].Gate.u3Reserved = 0;
+ pDescPair[0].Gate.u1DescType = 0; /* system */
+ pDescPair[0].Gate.u1Present = 1;
+ pDescPair[1].au32[0] = (uint32_t)(off >> 32);
+ pDescPair[1].au32[1] = 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Shutdown.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Shutdown.asm
new file mode 100644
index 00000000..fbe82287
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Shutdown.asm
@@ -0,0 +1,63 @@
+; $Id: bs3-cmn-Shutdown.asm $
+;; @file
+; BS3Kit - Bs3Shutdown
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/bios.mac"
+
+BS3_EXTERN_CMN Bs3Panic
+
+BS3_PROC_BEGIN_CMN Bs3Shutdown, BS3_PBC_HYBRID_0_ARGS
+ cli
+%ifdef TMPL_16BIT
+ mov ax, cs
+ mov ds, ax
+%endif
+ mov bl, 64
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT
+.retry:
+ mov ecx, 8
+ mov esi, .s_szShutdown
+ rep outsb
+ xchg ax, dx ; alternate between the new (VBox) and old (Bochs) ports.
+ dec bl
+ jnz .retry
+ ; Shutdown failed!
+ jmp Bs3Panic
+.s_szShutdown:
+ db 'Shutdown', 0
+BS3_PROC_END_CMN Bs3Shutdown
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAlloc.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAlloc.c
new file mode 100644
index 00000000..12e87e4f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAlloc.c
@@ -0,0 +1,64 @@
+/* $Id: bs3-cmn-SlabAlloc.c $ */
+/** @file
+ * BS3Kit - Bs3SlabAlloc
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm.h>
+
+
+#undef Bs3SlabAlloc
+BS3_CMN_DEF(void BS3_FAR *, Bs3SlabAlloc,(PBS3SLABCTL pSlabCtl))
+{
+ if (pSlabCtl->cFreeChunks)
+ {
+ int32_t iBit = ASMBitFirstClear(&pSlabCtl->bmAllocated, pSlabCtl->cChunks);
+ if (iBit >= 0)
+ {
+ BS3_XPTR_AUTO(void, pvRet);
+ ASMBitSet(&pSlabCtl->bmAllocated, iBit);
+ pSlabCtl->cFreeChunks -= 1;
+
+ BS3_XPTR_SET_FLAT(void, pvRet,
+ BS3_XPTR_GET_FLAT(uint8_t, pSlabCtl->pbStart) + ((uint32_t)iBit << pSlabCtl->cChunkShift));
+ return BS3_XPTR_GET(void, pvRet);
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAllocEx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAllocEx.c
new file mode 100644
index 00000000..a3354185
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabAllocEx.c
@@ -0,0 +1,111 @@
+/* $Id: bs3-cmn-SlabAllocEx.c $ */
+/** @file
+ * BS3Kit - Bs3SlabAllocEx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm.h>
+
+
+#undef Bs3SlabAllocEx
+BS3_CMN_DEF(void BS3_FAR *, Bs3SlabAllocEx,(PBS3SLABCTL pSlabCtl, uint16_t cChunks, uint16_t fFlags))
+{
+ BS3_ASSERT(cChunks > 0);
+ if (pSlabCtl->cFreeChunks >= cChunks)
+ {
+ int32_t iBit = ASMBitFirstClear(&pSlabCtl->bmAllocated, pSlabCtl->cChunks);
+ if (iBit >= 0)
+ {
+ BS3_ASSERT(!ASMBitTest(&pSlabCtl->bmAllocated, iBit));
+
+ while ((uint32_t)iBit + cChunks <= pSlabCtl->cChunks)
+ {
+ /* Check that we've got the requested number of free chunks here. */
+ uint16_t i;
+ for (i = 1; i < cChunks; i++)
+ if (ASMBitTest(&pSlabCtl->bmAllocated, iBit + i))
+ break;
+ if (i == cChunks)
+ {
+ /* Check if the chunks are all in the same tiled segment. */
+ BS3_XPTR_AUTO(void, pvRet);
+ BS3_XPTR_SET_FLAT(void, pvRet,
+ BS3_XPTR_GET_FLAT(uint8_t, pSlabCtl->pbStart) + ((uint32_t)iBit << pSlabCtl->cChunkShift));
+ if ( !(fFlags & BS3_SLAB_ALLOC_F_SAME_TILE)
+ || (BS3_XPTR_GET_FLAT(void, pvRet) >> 16)
+ == ((BS3_XPTR_GET_FLAT(void, pvRet) + ((uint32_t)cChunks << pSlabCtl->cChunkShift) - 1) >> 16) )
+ {
+ /* Complete the allocation. */
+ void *fpRet;
+ for (i = 0; i < cChunks; i++)
+ ASMBitSet(&pSlabCtl->bmAllocated, iBit + i);
+ pSlabCtl->cFreeChunks -= cChunks;
+ fpRet = BS3_XPTR_GET(void, pvRet);
+#if ARCH_BITS == 16
+ BS3_ASSERT(fpRet != NULL);
+#endif
+ return fpRet;
+ }
+
+ /*
+ * We're crossing a tiled segment boundrary.
+ * Skip to the start of the next segment and retry there.
+ * (We already know that the first chunk in the next tiled
+ * segment is free, otherwise we wouldn't have a crossing.)
+ */
+ BS3_ASSERT(((uint32_t)cChunks << pSlabCtl->cChunkShift) <= _64K);
+ i = BS3_XPTR_GET_FLAT_LOW(void, pvRet);
+ i = UINT16_C(0) - i;
+ i >>= pSlabCtl->cChunkShift;
+ iBit += i;
+ }
+ else
+ {
+ /*
+ * Continue searching.
+ */
+ iBit = ASMBitNextClear(&pSlabCtl->bmAllocated, pSlabCtl->cChunks, iBit + i);
+ if (iBit < 0)
+ break;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabFree.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabFree.c
new file mode 100644
index 00000000..730de635
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabFree.c
@@ -0,0 +1,69 @@
+/* $Id: bs3-cmn-SlabFree.c $ */
+/** @file
+ * BS3Kit - Bs3SlabFree
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm.h>
+
+
+#undef Bs3SlabFree
+BS3_CMN_DEF(uint16_t, Bs3SlabFree,(PBS3SLABCTL pSlabCtl, uint32_t uFlatChunkPtr, uint16_t cChunks))
+{
+ uint16_t cFreed = 0;
+ BS3_ASSERT(cChunks > 0);
+ if (cChunks > 0)
+ {
+ uint16_t iChunk = (uint16_t)((uFlatChunkPtr - BS3_XPTR_GET_FLAT(uint8_t, pSlabCtl->pbStart)) >> pSlabCtl->cChunkShift);
+ BS3_ASSERT(iChunk < pSlabCtl->cChunks);
+ BS3_ASSERT(iChunk + cChunks <= pSlabCtl->cChunks);
+
+ do
+ {
+ if (ASMBitTestAndClear(&pSlabCtl->bmAllocated, iChunk))
+ cFreed++;
+ else
+ BS3_ASSERT(0);
+ iChunk++;
+ } while (--cChunks > 0);
+
+ pSlabCtl->cFreeChunks += cFreed;
+ }
+ return cFreed;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabInit.c
new file mode 100644
index 00000000..4d50ee4c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabInit.c
@@ -0,0 +1,72 @@
+/* $Id: bs3-cmn-SlabInit.c $ */
+/** @file
+ * BS3Kit - Bs3SlabInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm.h>
+
+
+#undef Bs3SlabInit
+BS3_CMN_DEF(void, Bs3SlabInit,(PBS3SLABCTL pSlabCtl, size_t cbSlabCtl, uint32_t uFlatSlabPtr, uint32_t cbSlab, uint16_t cbChunk))
+{
+ uint16_t cBits;
+ BS3_ASSERT(RT_IS_POWER_OF_TWO(cbChunk));
+ BS3_ASSERT(cbSlab >= cbChunk * 4);
+ BS3_ASSERT(!(uFlatSlabPtr & (cbChunk - 1)));
+
+ BS3_XPTR_SET_FLAT(BS3SLABCTL, pSlabCtl->pNext, 0);
+ BS3_XPTR_SET_FLAT(BS3SLABCTL, pSlabCtl->pHead, 0);
+ BS3_XPTR_SET_FLAT(BS3SLABCTL, pSlabCtl->pbStart, uFlatSlabPtr);
+ pSlabCtl->cbChunk = cbChunk;
+ pSlabCtl->cChunkShift = ASMBitFirstSetU16(cbChunk) - 1;
+ pSlabCtl->cChunks = cbSlab >> pSlabCtl->cChunkShift;
+ pSlabCtl->cFreeChunks = pSlabCtl->cChunks;
+ cBits = RT_ALIGN_T(pSlabCtl->cChunks, 32, uint16_t);
+ BS3_ASSERT(cbSlabCtl >= RT_UOFFSETOF_DYN(BS3SLABCTL, bmAllocated[cBits >> 3]));
+ Bs3MemZero(&pSlabCtl->bmAllocated, cBits >> 3);
+
+ /* Mark excess bitmap padding bits as allocated. */
+ if (cBits != pSlabCtl->cChunks)
+ {
+ uint16_t iBit;
+ for (iBit = pSlabCtl->cChunks; iBit < cBits; iBit++)
+ ASMBitSet(pSlabCtl->bmAllocated, iBit);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAdd.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAdd.c
new file mode 100644
index 00000000..ba881526
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAdd.c
@@ -0,0 +1,53 @@
+/* $Id: bs3-cmn-SlabListAdd.c $ */
+/** @file
+ * BS3Kit - Bs3SlabListAdd
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SlabListAdd
+BS3_CMN_DEF(void, Bs3SlabListAdd,(PBS3SLABHEAD pHead, PBS3SLABCTL pSlabCtl))
+{
+ BS3_ASSERT(pHead->cbChunk == pSlabCtl->cbChunk);
+ BS3_ASSERT(BS3_XPTR_IS_NULL(BS3SLABHEAD, pSlabCtl->pNext));
+
+ BS3_XPTR_SET_FLAT(BS3SLABCTL, pSlabCtl->pNext, BS3_XPTR_GET_FLAT(BS3SLABCTL, pHead->pFirst));
+ BS3_XPTR_SET(BS3SLABCTL, pHead->pFirst, pSlabCtl);
+
+ pHead->cSlabs += 1;
+ pHead->cChunks += pSlabCtl->cChunks;
+ pHead->cFreeChunks += pSlabCtl->cFreeChunks;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAlloc.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAlloc.c
new file mode 100644
index 00000000..3d1596c2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAlloc.c
@@ -0,0 +1,67 @@
+/* $Id: bs3-cmn-SlabListAlloc.c $ */
+/** @file
+ * BS3Kit - Bs3SlabListAlloc
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SlabListAlloc
+BS3_CMN_DEF(void BS3_FAR *, Bs3SlabListAlloc,(PBS3SLABHEAD pHead))
+{
+ if (pHead->cFreeChunks)
+ {
+ PBS3SLABCTL pCur;
+ for (pCur = BS3_XPTR_GET(BS3SLABCTL, pHead->pFirst);
+ pCur != NULL;
+ pCur = BS3_XPTR_GET(BS3SLABCTL, pCur->pNext))
+ {
+ if (pCur->cFreeChunks)
+ {
+ void BS3_FAR *pvRet = Bs3SlabAlloc(pCur);
+ if (pvRet)
+ {
+ pHead->cFreeChunks -= 1;
+ return pvRet;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAllocEx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAllocEx.c
new file mode 100644
index 00000000..9f0eddd8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListAllocEx.c
@@ -0,0 +1,68 @@
+/* $Id: bs3-cmn-SlabListAllocEx.c $ */
+/** @file
+ * BS3Kit - Bs3SlabListAllocEx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SlabListAllocEx
+BS3_CMN_DEF(void BS3_FAR *, Bs3SlabListAllocEx,(PBS3SLABHEAD pHead, uint16_t cChunks, uint16_t fFlags))
+{
+ BS3_ASSERT(!(fFlags & ~BS3_SLAB_ALLOC_F_SAME_TILE));
+ if (pHead->cFreeChunks >= cChunks)
+ {
+ PBS3SLABCTL pCur;
+ for (pCur = BS3_XPTR_GET(BS3SLABCTL, pHead->pFirst);
+ pCur != NULL;
+ pCur = BS3_XPTR_GET(BS3SLABCTL, pCur->pNext))
+ {
+ if (pCur->cFreeChunks >= cChunks)
+ {
+ void BS3_FAR *pvRet = Bs3SlabAllocEx(pCur, cChunks, fFlags);
+ if (pvRet)
+ {
+ pHead->cFreeChunks -= cChunks;
+ return pvRet;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListFree.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListFree.c
new file mode 100644
index 00000000..16bcc23d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListFree.c
@@ -0,0 +1,64 @@
+/* $Id: bs3-cmn-SlabListFree.c $ */
+/** @file
+ * BS3Kit - Bs3SlabListFree
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SlabListFree
+BS3_CMN_DEF(void, Bs3SlabListFree,(PBS3SLABHEAD pHead, void BS3_FAR *pvChunks, uint16_t cChunks))
+{
+ BS3_ASSERT(cChunks > 0);
+ if (cChunks > 0)
+ {
+ PBS3SLABCTL pCur;
+ BS3_XPTR_AUTO(void, pvFlatChunk);
+ BS3_XPTR_SET(void, pvFlatChunk, pvChunks);
+
+ for (pCur = BS3_XPTR_GET(BS3SLABCTL, pHead->pFirst);
+ pCur != NULL;
+ pCur = BS3_XPTR_GET(BS3SLABCTL, pCur->pNext))
+ {
+ if ( ((BS3_XPTR_GET_FLAT(void, pvFlatChunk) - BS3_XPTR_GET_FLAT(uint8_t, pCur->pbStart)) >> pCur->cChunkShift)
+ < pCur->cChunks)
+ {
+ pHead->cFreeChunks += Bs3SlabFree(pCur, BS3_XPTR_GET_FLAT(void, pvFlatChunk), cChunks);
+ return;
+ }
+ }
+ BS3_ASSERT(0);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListInit.c
new file mode 100644
index 00000000..38c68b3f
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SlabListInit.c
@@ -0,0 +1,50 @@
+/* $Id: bs3-cmn-SlabListInit.c $ */
+/** @file
+ * BS3Kit - Bs3SlabListInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3SlabListInit
+BS3_CMN_DEF(void, Bs3SlabListInit,(PBS3SLABHEAD pHead, uint16_t cbChunk))
+{
+ BS3_ASSERT(RT_IS_POWER_OF_TWO(cbChunk));
+ BS3_XPTR_SET(struct BS3SLABCTL, pHead->pFirst, 0);
+ pHead->cbChunk = cbChunk;
+ pHead->cSlabs = 0;
+ pHead->cChunks = 0;
+ pHead->cFreeChunks = 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrCpy.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrCpy.c
new file mode 100644
index 00000000..3da9e78b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrCpy.c
@@ -0,0 +1,51 @@
+/* $Id: bs3-cmn-StrCpy.c $ */
+/** @file
+ * BS3Kit - Bs3StrCpy
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3StrCpy
+BS3_CMN_DEF(char BS3_FAR *, Bs3StrCpy,(char BS3_FAR *pszDst, const char BS3_FAR *pszSrc))
+{
+ char BS3_FAR *pszRet = pszDst;
+ char ch;
+ do
+ {
+ ch = *pszSrc++;
+ *pszDst++ = ch;
+ } while (ch != '\0');
+ return pszRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrFormatV.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrFormatV.c
new file mode 100644
index 00000000..89d14a1d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrFormatV.c
@@ -0,0 +1,788 @@
+/* $Id: bs3-cmn-StrFormatV.c $ */
+/** @file
+ * BS3Kit - Bs3StrFormatV
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/ctype.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define STR_F_CAPITAL 0x0001
+#define STR_F_LEFT 0x0002
+#define STR_F_ZEROPAD 0x0004
+#define STR_F_SPECIAL 0x0008
+#define STR_F_VALSIGNED 0x0010
+#define STR_F_PLUS 0x0020
+#define STR_F_BLANK 0x0040
+#define STR_F_WIDTH 0x0080
+#define STR_F_PRECISION 0x0100
+#define STR_F_THOUSAND_SEP 0x0200
+#define STR_F_NEGATIVE 0x0400 /**< Used to indicated '-' must be printed. */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Size of the temporary buffer. */
+#define BS3FMT_TMP_SIZE 64
+
+/**
+ * BS3kit string format state.
+ */
+typedef struct BS3FMTSTATE
+{
+ /** The output function. */
+ PFNBS3STRFORMATOUTPUT pfnOutput;
+ /** User argument for pfnOutput. */
+ void BS3_FAR *pvUser;
+
+ /** STR_F_XXX flags. */
+ unsigned fFlags;
+ /** The width when STR_F_WIDTH is specific. */
+ int cchWidth;
+ /** The width when STR_F_PRECISION is specific. */
+ int cchPrecision;
+ /** The number format base. */
+ unsigned uBase;
+ /** Temporary buffer. */
+ char szTmp[BS3FMT_TMP_SIZE];
+} BS3FMTSTATE;
+/** Pointer to a BS3Kit string formatter state. */
+typedef BS3FMTSTATE BS3_FAR *PBS3FMTSTATE;
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#if ARCH_BITS != 64
+static size_t bs3StrFormatU32(PBS3FMTSTATE pState, uint32_t uValue);
+#endif
+
+
+
+/**
+ * Formats a number string.
+ *
+ * @returns Number of chars printed.
+ * @param pState The string formatter state.
+ * @param pszNumber The formatted number string.
+ * @param cchNumber The length of the number.
+ */
+static size_t bs3StrFormatNumberString(PBS3FMTSTATE pState, char const BS3_FAR *pszNumber, size_t cchNumber)
+{
+ /*
+ * Calc the length of the core number with prefixes.
+ */
+ size_t cchActual = 0;
+ size_t cchRet = cchNumber;
+
+ /* Accunt for sign char. */
+ cchRet += !!(pState->fFlags & (STR_F_NEGATIVE | STR_F_PLUS | STR_F_BLANK));
+
+ /* Account for the hex prefix: '0x' or '0X' */
+ if (pState->fFlags & STR_F_SPECIAL)
+ {
+ cchRet += 2;
+ BS3_ASSERT(pState->uBase == 16);
+ }
+
+ /* Account for thousand separators (applied while printing). */
+ if (pState->fFlags & STR_F_THOUSAND_SEP)
+ cchRet += (cchNumber - 1) / (pState->uBase == 10 ? 3 : 8);
+
+ /*
+ * Do left blank padding.
+ */
+ if ((pState->fFlags & (STR_F_ZEROPAD | STR_F_LEFT | STR_F_WIDTH)) == STR_F_WIDTH)
+ while (cchRet < pState->cchWidth)
+ {
+ cchActual += pState->pfnOutput(' ', pState->pvUser);
+ cchRet++;
+ }
+
+ /*
+ * Sign indicator / space.
+ */
+ if (pState->fFlags & (STR_F_NEGATIVE | STR_F_PLUS | STR_F_BLANK))
+ {
+ char ch;
+ if (pState->fFlags & STR_F_NEGATIVE)
+ ch = '-';
+ else if (pState->fFlags & STR_F_PLUS)
+ ch = '+';
+ else
+ ch = ' ';
+ cchActual += pState->pfnOutput(ch, pState->pvUser);
+ }
+
+ /*
+ * Hex prefix.
+ */
+ if (pState->fFlags & STR_F_SPECIAL)
+ {
+ cchActual += pState->pfnOutput('0', pState->pvUser);
+ cchActual += pState->pfnOutput(!(pState->fFlags & STR_F_CAPITAL) ? 'x' : 'X', pState->pvUser);
+ }
+
+ /*
+ * Zero padding.
+ */
+ if (pState->fFlags & STR_F_ZEROPAD)
+ while (cchRet < pState->cchWidth)
+ {
+ cchActual += pState->pfnOutput('0', pState->pvUser);
+ cchRet++;
+ }
+
+ /*
+ * Output the number.
+ */
+ if ( !(pState->fFlags & STR_F_THOUSAND_SEP)
+ || cchNumber < 4)
+ while (cchNumber-- > 0)
+ cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
+ else
+ {
+ char const chSep = pState->uBase == 10 ? ' ' : '\'';
+ unsigned const cchEvery = pState->uBase == 10 ? 3 : 8;
+ unsigned cchLeft = --cchNumber % cchEvery;
+
+ cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
+ while (cchNumber-- > 0)
+ {
+ if (cchLeft == 0)
+ {
+ cchActual += pState->pfnOutput(chSep, pState->pvUser);
+ cchLeft = cchEvery;
+ }
+ cchLeft--;
+ cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
+ }
+ }
+
+ /*
+ * Do right blank padding.
+ */
+ if ((pState->fFlags & (STR_F_ZEROPAD | STR_F_LEFT | STR_F_WIDTH)) == (STR_F_WIDTH | STR_F_LEFT))
+ while (cchRet < pState->cchWidth)
+ {
+ cchActual += pState->pfnOutput(' ', pState->pvUser);
+ cchRet++;
+ }
+
+ return cchActual;
+}
+
+
+/**
+ * Format a 64-bit number.
+ *
+ * @returns Number of characters.
+ * @param pState The string formatter state.
+ * @param uValue The value.
+ */
+static size_t bs3StrFormatU64(PBS3FMTSTATE pState, uint64_t uValue)
+{
+#if ARCH_BITS != 64
+ /* Avoid 64-bit division by formatting 64-bit numbers as hex if they're higher than _4G. */
+ if (pState->uBase == 10)
+ {
+ if (!(uValue >> 32)) /* uValue <= UINT32_MAX does not work, trouble with 64-bit compile time math! */
+ return bs3StrFormatU32(pState, uValue);
+ pState->fFlags |= STR_F_SPECIAL;
+ pState->uBase = 16;
+ }
+#endif
+
+ {
+ const char BS3_FAR *pachDigits = !(pState->fFlags & STR_F_CAPITAL) ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
+ char BS3_FAR *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
+
+ *--psz = '\0';
+#if ARCH_BITS == 64
+ if (pState->uBase == 10)
+ {
+ do
+ {
+ *--psz = pachDigits[uValue % 10];
+ uValue /= 10;
+ } while (uValue > 0);
+ }
+ else
+#endif
+ {
+ BS3_ASSERT(pState->uBase == 16);
+ do
+ {
+ *--psz = pachDigits[uValue & 0xf];
+ uValue >>= 4;
+ } while (uValue > 0);
+ }
+ return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
+ }
+}
+
+
+/**
+ * Format a 32-bit number.
+ *
+ * @returns Number of characters.
+ * @param pState The string formatter state.
+ * @param uValue The value.
+ */
+static size_t bs3StrFormatU32(PBS3FMTSTATE pState, uint32_t uValue)
+{
+#if ARCH_BITS < 64
+ const char BS3_FAR *pachDigits = !(pState->fFlags & STR_F_CAPITAL) ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
+ char BS3_FAR *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
+
+ *--psz = '\0';
+ if (pState->uBase == 10)
+ {
+ do
+ {
+ *--psz = pachDigits[uValue % 10];
+ uValue /= 10;
+ } while (uValue > 0);
+ }
+ else
+ {
+ BS3_ASSERT(pState->uBase == 16);
+ do
+ {
+ *--psz = pachDigits[uValue & 0xf];
+ uValue >>= 4;
+ } while (uValue > 0);
+ }
+ return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
+
+#else
+ /* We've got native 64-bit division, save space. */
+ return bs3StrFormatU64(pState, uValue);
+#endif
+}
+
+
+#if ARCH_BITS == 16
+/**
+ * Format a 16-bit number.
+ *
+ * @returns Number of characters.
+ * @param pState The string formatter state.
+ * @param uValue The value.
+ */
+static size_t bs3StrFormatU16(PBS3FMTSTATE pState, uint16_t uValue)
+{
+ if (pState->uBase == 10)
+ {
+ const char BS3_FAR *pachDigits = !(pState->fFlags & STR_F_CAPITAL)
+ ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
+ char BS3_FAR *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
+
+ *--psz = '\0';
+ do
+ {
+ *--psz = pachDigits[uValue % 10];
+ uValue /= 10;
+ } while (uValue > 0);
+ return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
+ }
+
+ /*
+ * 32-bit shifting is reasonably cheap and inlined, so combine with 32-bit.
+ */
+ return bs3StrFormatU32(pState, uValue);
+}
+#endif
+
+
+static size_t bs3StrFormatS64(PBS3FMTSTATE pState, int32_t iValue)
+{
+ if (iValue < 0)
+ {
+ iValue = -iValue;
+ pState->fFlags |= STR_F_NEGATIVE;
+ }
+ return bs3StrFormatU64(pState, iValue);
+}
+
+
+static size_t bs3StrFormatS32(PBS3FMTSTATE pState, int32_t iValue)
+{
+ if (iValue < 0)
+ {
+ iValue = -iValue;
+ pState->fFlags |= STR_F_NEGATIVE;
+ }
+ return bs3StrFormatU32(pState, iValue);
+}
+
+
+#if ARCH_BITS == 16
+static size_t bs3StrFormatS16(PBS3FMTSTATE pState, int16_t iValue)
+{
+ if (iValue < 0)
+ {
+ iValue = -iValue;
+ pState->fFlags |= STR_F_NEGATIVE;
+ }
+ return bs3StrFormatU16(pState, iValue);
+}
+#endif
+
+
+#undef Bs3StrFormatV
+BS3_CMN_DEF(size_t, Bs3StrFormatV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va,
+ PFNBS3STRFORMATOUTPUT pfnOutput, void BS3_FAR *pvUser))
+{
+ BS3FMTSTATE State;
+ size_t cchRet = 0;
+ char ch;
+#if ARCH_BITS == 16
+ typedef int SIZE_CHECK_TYPE1[sizeof(va) == 4 && sizeof(va[0]) == 4];
+#endif
+
+ State.pfnOutput = pfnOutput;
+ State.pvUser = pvUser;
+
+ while ((ch = *pszFormat++) != '\0')
+ {
+ char chArgSize;
+
+ /*
+ * Deal with plain chars.
+ */
+ if (ch != '%')
+ {
+ cchRet += State.pfnOutput(ch, State.pvUser);
+ continue;
+ }
+
+ ch = *pszFormat++;
+ if (ch == '%')
+ {
+ cchRet += State.pfnOutput(ch, State.pvUser);
+ continue;
+ }
+
+ /*
+ * Flags.
+ */
+ State.fFlags = 0;
+ for (;;)
+ {
+ unsigned int fThis;
+ switch (ch)
+ {
+ default: fThis = 0; break;
+ case '#': fThis = STR_F_SPECIAL; break;
+ case '-': fThis = STR_F_LEFT; break;
+ case '+': fThis = STR_F_PLUS; break;
+ case ' ': fThis = STR_F_BLANK; break;
+ case '0': fThis = STR_F_ZEROPAD; break;
+ case '\'': fThis = STR_F_THOUSAND_SEP; break;
+ }
+ if (!fThis)
+ break;
+ State.fFlags |= fThis;
+ ch = *pszFormat++;
+ }
+
+ /*
+ * Width.
+ */
+ State.cchWidth = 0;
+ if (RT_C_IS_DIGIT(ch))
+ {
+ do
+ {
+ State.cchWidth *= 10;
+ State.cchWidth += ch - '0';
+ ch = *pszFormat++;
+ } while (RT_C_IS_DIGIT(ch));
+ State.fFlags |= STR_F_WIDTH;
+ }
+ else if (ch == '*')
+ {
+ State.cchWidth = va_arg(va, int);
+ if (State.cchWidth < 0)
+ {
+ State.cchWidth = -State.cchWidth;
+ State.fFlags |= STR_F_LEFT;
+ }
+ State.fFlags |= STR_F_WIDTH;
+ ch = *pszFormat++;
+ }
+
+ /*
+ * Precision
+ */
+ State.cchPrecision = 0;
+ if (ch == '.')
+ {
+ ch = *pszFormat++;
+ if (RT_C_IS_DIGIT(ch))
+ {
+ do
+ {
+ State.cchPrecision *= 10;
+ State.cchPrecision += ch - '0';
+ ch = *pszFormat++;
+ } while (RT_C_IS_DIGIT(ch));
+ State.fFlags |= STR_F_PRECISION;
+ }
+ else if (ch == '*')
+ {
+ State.cchPrecision = va_arg(va, int);
+ if (State.cchPrecision < 0)
+ State.cchPrecision = 0;
+ State.fFlags |= STR_F_PRECISION;
+ ch = *pszFormat++;
+ }
+ }
+
+ /*
+ * Argument size.
+ */
+ chArgSize = ch;
+ switch (ch)
+ {
+ default:
+ chArgSize = 0;
+ break;
+
+ case 'z':
+ case 'L':
+ case 'j':
+ case 't':
+ ch = *pszFormat++;
+ break;
+
+ case 'l':
+ ch = *pszFormat++;
+ if (ch == 'l')
+ {
+ chArgSize = 'L';
+ ch = *pszFormat++;
+ }
+ break;
+
+ case 'h':
+ ch = *pszFormat++;
+ if (ch == 'h')
+ {
+ chArgSize = 'H';
+ ch = *pszFormat++;
+ }
+ break;
+ }
+
+ /*
+ * The type.
+ */
+ switch (ch)
+ {
+ /*
+ * Char
+ */
+ case 'c':
+ {
+ char ch = va_arg(va, int /*char*/);
+ cchRet += State.pfnOutput(ch, State.pvUser);
+ break;
+ }
+
+ /*
+ * String.
+ */
+ case 's':
+ {
+ const char BS3_FAR *psz = va_arg(va, const char BS3_FAR *);
+ size_t cch;
+ if (psz != NULL)
+ cch = Bs3StrNLen(psz, State.fFlags & STR_F_PRECISION ? RT_ABS(State.cchPrecision) : ~(size_t)0);
+ else
+ {
+ psz = "<NULL>";
+ cch = 6;
+ }
+
+ if ((State.fFlags & (STR_F_LEFT | STR_F_WIDTH)) == STR_F_WIDTH)
+ while (--State.cchWidth >= cch)
+ cchRet += State.pfnOutput(' ', State.pvUser);
+
+ while (cch-- > 0)
+ cchRet += State.pfnOutput(*psz++, State.pvUser);
+
+ if ((State.fFlags & (STR_F_LEFT | STR_F_WIDTH)) == (STR_F_LEFT | STR_F_WIDTH))
+ while (--State.cchWidth >= cch)
+ cchRet += State.pfnOutput(' ', State.pvUser);
+ break;
+ }
+
+ /*
+ * Signed integers.
+ */
+ case 'i':
+ case 'd':
+ State.fFlags &= ~STR_F_SPECIAL;
+ State.fFlags |= STR_F_VALSIGNED;
+ State.uBase = 10;
+ switch (chArgSize)
+ {
+ case 0:
+ case 'h': /* signed short should be promoted to int or be the same as int */
+ case 'H': /* signed char should be promoted to int. */
+ {
+ signed int iValue = va_arg(va, signed int);
+#if ARCH_BITS == 16
+ cchRet += bs3StrFormatS16(&State, iValue);
+#else
+ cchRet += bs3StrFormatS32(&State, iValue);
+#endif
+ break;
+ }
+ case 'l':
+ {
+ signed long lValue = va_arg(va, signed long);
+ if (sizeof(lValue) == 4)
+ cchRet += bs3StrFormatS32(&State, lValue);
+ else
+ cchRet += bs3StrFormatS64(&State, lValue);
+ break;
+ }
+ case 'L':
+ {
+ unsigned long long ullValue = va_arg(va, unsigned long long);
+ cchRet += bs3StrFormatS64(&State, ullValue);
+ break;
+ }
+ }
+ break;
+
+ /*
+ * Unsigned integers.
+ */
+ case 'X':
+ State.fFlags |= STR_F_CAPITAL;
+ case 'x':
+ case 'u':
+ {
+ if (ch == 'u')
+ {
+ State.uBase = 10;
+ State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK | STR_F_SPECIAL);
+ }
+ else
+ {
+ State.uBase = 16;
+ State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
+ }
+ switch (chArgSize)
+ {
+ case 0:
+ case 'h': /* unsigned short should be promoted to int or be the same as int */
+ case 'H': /* unsigned char should be promoted to int. */
+ {
+ unsigned int uValue = va_arg(va, unsigned int);
+#if ARCH_BITS == 16
+ cchRet += bs3StrFormatU16(&State, uValue);
+#else
+ cchRet += bs3StrFormatU32(&State, uValue);
+#endif
+ break;
+ }
+ case 'l':
+ {
+ unsigned long ulValue = va_arg(va, unsigned long);
+ if (sizeof(ulValue) == 4)
+ cchRet += bs3StrFormatU32(&State, ulValue);
+ else
+ cchRet += bs3StrFormatU64(&State, ulValue);
+ break;
+ }
+ case 'L':
+ {
+ unsigned long long ullValue = va_arg(va, unsigned long long);
+ cchRet += bs3StrFormatU64(&State, ullValue);
+ break;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Our stuff.
+ */
+ case 'R':
+ {
+ ch = *pszFormat++;
+ switch (ch)
+ {
+ case 'I':
+ State.fFlags |= STR_F_VALSIGNED;
+ State.uBase &= ~STR_F_SPECIAL;
+ State.uBase = 10;
+ break;
+ case 'U':
+ State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK | STR_F_SPECIAL);
+ State.uBase = 10;
+ break;
+ case 'X':
+ State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
+ State.uBase = 16;
+ break;
+ case 'h':
+ ch = *pszFormat++;
+ if (ch == 'x')
+ {
+ /* Hex dumping. */
+ uint8_t const BS3_FAR *pbHex = va_arg(va, uint8_t const BS3_FAR *);
+ if (State.cchPrecision < 0)
+ State.cchPrecision = 16;
+ ch = *pszFormat++;
+ if (ch == 's' || ch == 'd')
+ {
+ /* %Rhxd is currently implemented as %Rhxs. */
+ while (State.cchPrecision-- > 0)
+ {
+ uint8_t b = *pbHex++;
+ State.pfnOutput(g_achBs3HexDigits[b >> 4], State.pvUser);
+ State.pfnOutput(g_achBs3HexDigits[b & 0x0f], State.pvUser);
+ if (State.cchPrecision)
+ State.pfnOutput(' ', State.pvUser);
+ }
+ }
+ }
+ State.uBase = 0;
+ break;
+ default:
+ State.uBase = 0;
+ break;
+ }
+ if (State.uBase)
+ {
+ ch = *pszFormat++;
+ switch (ch)
+ {
+#if ARCH_BITS != 16
+ case '3':
+ case '1': /* Will an unsigned 16-bit value always be promoted
+ to a 16-bit unsigned int. It certainly will be promoted to a 32-bit int. */
+ pszFormat++; /* Assumes (1)'6' or (3)'2' */
+#else
+ case '1':
+ pszFormat++; /* Assumes (1)'6' */
+#endif
+ case '8': /* An unsigned 8-bit value should be promoted to int, which is at least 16-bit. */
+ {
+ unsigned int uValue = va_arg(va, unsigned int);
+#if ARCH_BITS == 16
+ cchRet += bs3StrFormatU16(&State, uValue);
+#else
+ cchRet += bs3StrFormatU32(&State, uValue);
+#endif
+ break;
+ }
+#if ARCH_BITS == 16
+ case '3':
+ {
+ uint32_t uValue = va_arg(va, uint32_t);
+ pszFormat++;
+ cchRet += bs3StrFormatU32(&State, uValue);
+ break;
+ }
+#endif
+ case '6':
+ {
+ uint64_t uValue = va_arg(va, uint64_t);
+ pszFormat++;
+ cchRet += bs3StrFormatU64(&State, uValue);
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ /*
+ * Pointers.
+ */
+ case 'P':
+ State.fFlags |= STR_F_CAPITAL;
+ RT_FALL_THRU();
+ case 'p':
+ {
+ void BS3_FAR *pv = va_arg(va, void BS3_FAR *);
+ State.uBase = 16;
+ State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
+#if ARCH_BITS == 16
+ State.fFlags |= STR_F_ZEROPAD;
+ State.cchWidth = State.fFlags & STR_F_SPECIAL ? 6: 4;
+ cchRet += bs3StrFormatU16(&State, BS3_FP_SEG(pv));
+ cchRet += State.pfnOutput(':', State.pvUser);
+ cchRet += bs3StrFormatU16(&State, BS3_FP_OFF(pv));
+#elif ARCH_BITS == 32
+ State.fFlags |= STR_F_SPECIAL | STR_F_ZEROPAD;
+ State.cchWidth = 10;
+ cchRet += bs3StrFormatU32(&State, (uintptr_t)pv);
+#elif ARCH_BITS == 64
+ State.fFlags |= STR_F_SPECIAL | STR_F_ZEROPAD | STR_F_THOUSAND_SEP;
+ State.cchWidth = 19;
+ cchRet += bs3StrFormatU64(&State, (uintptr_t)pv);
+#else
+# error "Undefined or invalid ARCH_BITS."
+#endif
+ break;
+ }
+
+ }
+ }
+
+ /*
+ * Termination call.
+ */
+ cchRet += State.pfnOutput(0, State.pvUser);
+
+ return cchRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrLen.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrLen.c
new file mode 100644
index 00000000..b188bf7c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrLen.c
@@ -0,0 +1,47 @@
+/* $Id: bs3-cmn-StrLen.c $ */
+/** @file
+ * BS3Kit - Bs3StrLen
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3StrLen
+BS3_CMN_DEF(size_t, Bs3StrLen,(const char BS3_FAR *pszString))
+{
+ size_t cch = 0;
+ while (pszString[cch] != '\0')
+ cch++;
+ return cch;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrNLen.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrNLen.c
new file mode 100644
index 00000000..d52eedab
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrNLen.c
@@ -0,0 +1,47 @@
+/* $Id: bs3-cmn-StrNLen.c $ */
+/** @file
+ * BS3Kit - Bs3StrNLen
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+#undef Bs3StrNLen
+BS3_CMN_DEF(size_t, Bs3StrNLen,(const char BS3_FAR *pszString, size_t cchMax))
+{
+ size_t cch = 0;
+ while (cchMax-- > 0 && pszString[cch] != '\0')
+ cch++;
+ return cch;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrPrintf.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrPrintf.c
new file mode 100644
index 00000000..8c2d9511
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrPrintf.c
@@ -0,0 +1,105 @@
+/* $Id: bs3-cmn-StrPrintf.c $ */
+/** @file
+ * BS3Kit - Bs3StrPrintf, Bs3StrPrintfV
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/ctype.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct BS3STRPRINTFSTATE
+{
+ /** Current buffer position. */
+ char *pchBuf;
+ /** Number of bytes left in the buffer. */
+ size_t cchLeft;
+} BS3STRPRINTFSTATE;
+typedef BS3STRPRINTFSTATE BS3_FAR *PBS3STRPRINTFSTATE;
+
+
+
+static BS3_DECL_CALLBACK(size_t) bs3StrPrintfFmtOutput(char ch, void BS3_FAR *pvUser)
+{
+ PBS3STRPRINTFSTATE pState = (PBS3STRPRINTFSTATE)pvUser;
+ if (ch)
+ {
+ /* Put to the buffer if there is place for this char and a terminator. */
+ if (pState->cchLeft > 1)
+ {
+ pState->cchLeft--;
+ *pState->pchBuf++ = ch;
+ }
+
+ /* Always return 1. */
+ return 1;
+ }
+
+ /* Terminate the string. */
+ if (pState->cchLeft)
+ {
+ pState->cchLeft--;
+ *pState->pchBuf++ = '\0';
+ }
+ return 0;
+}
+
+
+#undef Bs3StrPrintfV
+BS3_CMN_DEF(size_t, Bs3StrPrintfV,(char BS3_FAR *pszBuf, size_t cchBuf, const char BS3_FAR *pszFormat, va_list BS3_FAR va))
+{
+ BS3STRPRINTFSTATE State;
+ State.pchBuf = pszBuf;
+ State.cchLeft = cchBuf;
+ return Bs3StrFormatV(pszFormat, va, bs3StrPrintfFmtOutput, &State);
+}
+
+
+#undef Bs3StrPrintf
+BS3_CMN_DEF(size_t, Bs3StrPrintf,(char BS3_FAR *pszBuf, size_t cchBuf, const char BS3_FAR *pszFormat, ...))
+{
+ size_t cchRet;
+ va_list va;
+ va_start(va, pszFormat);
+ cchRet = BS3_CMN_NM(Bs3StrPrintfV)(pszBuf, cchBuf, pszFormat, va);
+ va_end(va);
+ return cchRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm
new file mode 100644
index 00000000..e3b40821
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm
@@ -0,0 +1,78 @@
+; $Id: bs3-cmn-SwitchHlpConvFlatRetToRetfProtMode.asm $
+;; @file
+; BS3Kit - SwitchHlpConvFlatRetToRetfProtMode
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+%if TMPL_BITS != 16
+BS3_EXTERN_CMN Bs3SelFlatCodeToProtFar16
+
+;;
+; SwitchToXxx helper that converts a 32-bit or 64-bit flat return address
+; into a 16-bit protected mode far return.
+;
+;
+; The caller calls this routine before switching modes. The flat return
+; to be converted is immediately after our own return address on the stack.
+;
+; @uses Nothing.
+; @remarks No 16-bit version.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ push xAX
+ push xCX
+ sub xSP, 20h
+
+ mov xCX, [xSP + xCB*3 + 20h]
+ call Bs3SelFlatCodeToProtFar16 ; well behaved assembly function, only clobbers ecx
+ mov [xSP + xCB*3 + 20h + 4], eax
+
+ add xSP, 20h
+ pop xCX
+ pop xAX
+ ret 4
+ %else
+ xchg eax, [xSP + xCB]
+ push xAX
+ call Bs3SelFlatCodeToProtFar16 ; well behaved assembly function, only clobbers eax
+ add xSP, 4
+ xchg [xSP + xCB], eax
+ ret
+ %endif
+BS3_PROC_END_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+
+%endif ; 32 || 64
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm
new file mode 100644
index 00000000..223e95e6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm
@@ -0,0 +1,126 @@
+; $Id: bs3-cmn-SwitchHlpConvProtModeRetfPopBpDecBpAndReturn.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%if TMPL_BITS == 16
+BS3_EXTERN_CMN Bs3SelProtModeCodeToRealMode
+%else
+BS3_EXTERN_CMN Bs3SelFar32ToFlat32
+%endif
+
+
+;;
+; SwitchToXxx helper that converts a 16-bit protected mode far return
+; into something suitable for the current mode and performs the return.
+;
+; The caller jmps to this routine. The stack holds an incremented BP (odd is
+; far indicator) and a 16-bit far return address.
+;
+; @uses Nothing.
+; @remarks 16-bit ASSUMES we're returning to protected mode!!
+;
+%if TMPL_BITS == 16
+BS3_BEGIN_TEXT16_FARSTUBS
+%endif
+BS3_PROC_BEGIN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn, BS3_PBC_NEAR
+%if TMPL_BITS == 16
+ ; Convert the selector of the 16:16 protected mode return address to the
+ ; corresponding 16-bit real mode segment.
+ push ax
+
+ mov ax, [bp + 2 + 2]
+ push ax
+ call Bs3SelProtModeCodeToRealMode ; This doesn't trash any registers (except AX).
+ add sp, 2
+ mov [bp + 2 + 2], ax
+
+ pop ax
+
+ pop bp
+ dec bp
+ retf
+
+%elif TMPL_BITS == 32
+ push eax
+ push ecx
+ push edx
+
+ movzx eax, word [esp + 4*3 + 2] ; return offset
+ movzx edx, word [esp + 4*3 + 2 + 2] ; return selector
+ push eax
+ push edx
+ call Bs3SelFar32ToFlat32
+ add esp, 8
+ mov [esp + 4*3 + 2], eax
+
+ pop edx
+ pop ecx
+ pop eax
+ pop bp
+ dec bp
+ ret
+%else
+ push rax
+ push rcx
+ push rdx
+ push r8
+ push r9
+ push r10
+ push r11
+
+ movzx ecx, word [rsp + 8*7 + 2] ; return offset
+ movzx edx, word [rsp + 8*7 + 2 + 2] ; return selector
+ sub rsp, 20h
+ call Bs3SelFar32ToFlat32
+ add rsp, 20h
+ mov [rsp + 8*7 + 2], eax
+
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rdx
+ pop rcx
+ pop rax
+ mov bp, [rsp]
+ add rsp, 2h
+ dec bp
+ o32 ret
+%endif
+BS3_PROC_END_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm
new file mode 100644
index 00000000..04b9c912
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm
@@ -0,0 +1,109 @@
+; $Id: bs3-cmn-SwitchHlpConvRealModeRetfPopBpDecBpAndReturn.asm $
+;; @file
+; BS3Kit - SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3SelFar32ToFlat32
+
+;;
+; SwitchToXxx helper that converts a 16-bit real mode far return
+; into something suitable for the current mode and performs the return.
+;
+; The caller jmps to this routine. The stack holds an incremented BP (odd is
+; far indicator) and a 16-bit far return address.
+;
+; @uses Nothing.
+; @remarks 16-bit ASSUMES we're returning to protected mode!!
+;
+%if TMPL_BITS == 16
+BS3_BEGIN_TEXT16_FARSTUBS
+%endif
+BS3_PROC_BEGIN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn, BS3_PBC_NEAR
+%if TMPL_BITS == 16
+ ; Convert the selector of the 16:16 real mode return address to the
+ ; corresponding 16-bit protected mode selector.
+ push ax
+
+ mov ax, [bp + 2 + 2]
+ push ax
+ BS3_EXTERN_CMN Bs3SelRealModeCodeToProtMode
+ call Bs3SelRealModeCodeToProtMode ; This doesn't trash any registers (except AX).
+ add sp, 2
+ mov [bp + 2 + 2], ax
+
+ pop ax
+
+ pop bp
+ dec bp
+ retf
+
+%elif TMPL_BITS == 32
+ push xAX
+ push xDX
+
+ movzx eax, word [xSP + xCB*2 + 2 + 2] ; return segment
+ movzx edx, word [xSP + xCB*2 + 2] ; return offset
+ shl eax, 4
+ add eax, edx
+ mov [xSP + xCB*2 + 2], eax
+
+ pop xDX
+ pop xAX
+ pop bp
+ dec bp
+ ret
+%else
+ sub rsp, 2h
+
+ push xAX
+ push xDX
+
+ movzx eax, word [xSP + xCB*2 + 4 + 2] ; return segment
+ movzx edx, word [xSP + xCB*2 + 4] ; return offset
+ shl eax, 4
+ add eax, edx
+
+ mov bp, [xSP + xCB*2 + 2]
+ dec bp
+
+ mov [xSP + xCB*2], rax
+
+ pop xDX
+ pop xAX
+ ret
+%endif
+BS3_PROC_END_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16Bit.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16Bit.asm
new file mode 100644
index 00000000..1e1d8747
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16Bit.asm
@@ -0,0 +1,130 @@
+; $Id: bs3-cmn-SwitchTo16Bit.asm $
+;; @file
+; BS3Kit - Bs3SwitchTo16Bit
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%if TMPL_BITS == 16
+BS3_EXTERN_CMN Bs3Syscall
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchTo16Bit(void);
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchTo16Bit, BS3_PBC_NEAR
+%if TMPL_BITS == 16
+ push ax
+ push ds
+
+ ; Check g_bBs3CurrentMode whether we're in v8086 mode or not.
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ test al, BS3_MODE_CODE_V86
+ jz .ret_16bit
+
+ ; Switch to ring-0 if v8086 mode.
+ mov ax, BS3_SYSCALL_TO_RING0
+ call Bs3Syscall
+
+.ret_16bit:
+ pop ds
+ pop ax
+ ret
+
+%else
+ push xAX
+ push xBX
+ xPUSHF
+ cli
+
+ ; Calc new CS.
+ mov ax, cs
+ and xAX, 3
+ shl xAX, BS3_SEL_RING_SHIFT ; ring addend.
+ add xAX, BS3_SEL_R0_CS16
+
+ ; Construct a far return for switching to 16-bit code.
+ push xAX
+ push .sixteen_bit
+ xRETF
+
+BS3_BEGIN_TEXT16
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit
+
+ ; Load 16-bit segment registers.
+ add ax, BS3_SEL_R0_SS16 - BS3_SEL_R0_CS16
+ mov ss, ax
+
+ add ax, BS3_SEL_R0_DS16 - BS3_SEL_R0_SS16
+ mov ds, ax
+ mov es, ax
+
+ ; Thunk the stack if necessary.
+ mov ebx, esp
+ shr ebx, 16
+ jz .stack_ok
+int3 ; This is for later, just remove this int3 once needed.
+ test ax, X86_SEL_RPL
+ jnz .stack_rpl_must_be_0_for_custom_stacks
+ shl bx, X86_SEL_SHIFT
+ add bx, BS3_SEL_TILED
+ mov ss, bx
+ movzx esp, sp
+.stack_ok:
+
+ ; Update globals.
+ and byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], ~BS3_MODE_CODE_MASK
+ or byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_16
+
+ popfd
+TONLY64 pop ebx
+ pop ebx
+TONLY64 pop eax
+ pop eax
+TONLY64 add sp, 4
+ ret (TMPL_BITS - 16) / 8 ; Return and pop 2 or 6 bytes of "parameters" (unused return value)
+
+.stack_rpl_must_be_0_for_custom_stacks:
+ int3
+ jmp .stack_rpl_must_be_0_for_custom_stacks
+TMPL_BEGIN_TEXT
+%endif
+BS3_PROC_END_CMN Bs3SwitchTo16Bit
+
+;; @todo far 16-bit variant.
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16BitV86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16BitV86.asm
new file mode 100644
index 00000000..636e6184
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo16BitV86.asm
@@ -0,0 +1,133 @@
+; $Id: bs3-cmn-SwitchTo16BitV86.asm $
+;; @file
+; BS3Kit - Bs3SwitchTo16BitV86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+%if TMPL_BITS != 64
+
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+BS3_EXTERN_CMN Bs3SwitchToRing0
+BS3_EXTERN_CMN Bs3SelProtFar32ToFlat32
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchTo16BitV86(void);
+; @uses No general registers modified. Regment registers loaded with specific
+; values and the stack register converted to real mode (not ebp).
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchTo16BitV86, BS3_PBC_NEAR
+ ; Construct basic v8086 return frame.
+BONLY16 movzx esp, sp
+ push dword 0 ; +0x20: GS
+ push dword 0 ; +0x1c: FS
+ push dword BS3_SEL_DATA16 ; +0x18: ES
+ push dword BS3_SEL_DATA16 ; +0x14: DS
+ push dword 0 ; +0x10: SS - later
+ push dword 0 ; +0x0c: return ESP, later.
+ pushfd
+ or dword [esp], X86_EFL_VM | X86_EFL_IOPL ; +0x08: Set IOPL=3 and the VM flag (EFLAGS).
+ push dword BS3_SEL_TEXT16 ; +0x04
+ push word 0
+ push word [esp + 24h - 2] ; +0x00
+ ; Save registers and stuff.
+ push eax
+ push edx
+ push ecx
+ push ebx
+ %if TMPL_BITS == 16
+ push ds
+
+ ; Check g_bBs3CurrentMode whether we're in v8086 mode or not.
+ mov ax, seg g_bBs3CurrentMode
+ mov ds, ax
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ test al, BS3_MODE_CODE_V86
+ jz .not_v8086
+
+ pop ds
+ pop ebx
+ pop ecx
+ pop edx
+ pop eax
+ add xSP, 0x24
+ ret
+
+.not_v8086:
+ pop ax ; Drop the push ds so the stacks are identical. Keep DS = BS3KIT_GRPNM_DATA16 though.
+ %endif
+
+ ; Ensure that we're in ring-0.
+ mov ax, ss
+ test ax, 3
+ jz .is_ring0
+ call Bs3SwitchToRing0
+ %if TMPL_BITS == 16
+ mov ax, seg g_bBs3CurrentMode
+ mov ds, ax ; parnoia
+ %endif
+.is_ring0:
+
+ ; Update globals.
+ and byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], ~BS3_MODE_CODE_MASK
+ or byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+
+ ; Thunk return SS:ESP to real-mode address via 32-bit flat.
+ lea eax, [esp + 4*4 + 24h + xCB]
+ push ss
+ push eax
+ BS3_CALL Bs3SelProtFar32ToFlat32, 2
+ add esp, sCB + xCB
+ mov [esp + 4*4 + 0ch], ax ; high word is already zero
+ %if TMPL_BITS == 16
+ mov [esp + 4*4 + 10h], dx
+ %else
+ shr eax, 16
+ mov [esp + 4*4 + 10h], ax
+ %endif
+
+ ; Return to v8086 mode.
+ pop ebx
+ pop ecx
+ pop edx
+ pop eax
+ iretd
+BS3_PROC_END_CMN Bs3SwitchTo16BitV86
+
+;; @todo far 16-bit variant.
+
+%endif ; ! 64-bit
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo32Bit.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo32Bit.asm
new file mode 100644
index 00000000..5b3cf18b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo32Bit.asm
@@ -0,0 +1,162 @@
+; $Id: bs3-cmn-SwitchTo32Bit.asm $
+;; @file
+; BS3Kit - Bs3SwitchTo32Bit
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%if TMPL_BITS == 16
+BS3_EXTERN_CMN Bs3SelProtFar32ToFlat32
+BS3_EXTERN_CMN Bs3Syscall
+%endif
+%if TMPL_BITS != 32
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+TMPL_BEGIN_TEXT
+%endif
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchTo32Bit(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchTo32Bit, BS3_PBC_NEAR
+%if TMPL_BITS == 32
+ ret
+%else
+ %if TMPL_BITS == 16
+ push ax ; Reserve space for larger return value (adjusted in 32-bit code).
+ push eax
+ pushfd
+ push edx
+ %else
+ pushfq
+ mov [rsp + 4], eax
+ %endif
+ cli
+
+ %if TMPL_BITS == 16
+ ; Check for v8086 mode, we need to exit it to enter 32-bit mode.
+ mov ax, seg g_bBs3CurrentMode
+ mov ds, ax
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ test al, BS3_MODE_CODE_V86
+ jz .not_v8086
+
+ ; Calc flat stack into edx.
+ mov dx, ss
+ movzx edx, dx
+ shl edx, 4
+ add dx, sp
+ adc edx, 0 ; edx = flat stack address corresponding to ss:sp
+
+ ; Switch to 16-bit ring0 and go on to do the far jump to 32-bit code.
+ mov ax, BS3_SYSCALL_TO_RING0
+ call Bs3Syscall
+
+ mov xAX, BS3_SEL_R0_CS32
+ jmp .do_far_jump
+ %endif
+
+.not_v8086:
+ %if TMPL_BITS == 16
+ ; Calc flat stack into edx.
+ movzx eax, sp
+ push ecx
+ push ebx
+ push ss
+ push eax
+ call Bs3SelProtFar32ToFlat32
+ add sp, 6
+ shl edx, 16
+ mov dx, ax ; edx = flat stack address corresponding to ss:sp
+ pop ebx
+ pop ecx
+ %endif
+
+ ; Calc ring addend.
+ mov ax, cs
+ and xAX, 3
+ shl xAX, BS3_SEL_RING_SHIFT
+ add xAX, BS3_SEL_R0_CS32
+
+ ; Create far return for switching to 32-bit mode.
+.do_far_jump:
+ push sAX
+ %if TMPL_BITS == 16
+ push dword .thirty_two_bit wrt FLAT
+ o32 retf
+ %else
+ push .thirty_two_bit
+ o64 retf
+ %endif
+
+BS3_SET_BITS 32
+.thirty_two_bit:
+ ; Load 32-bit segment registers.
+ add eax, BS3_SEL_R0_SS32 - BS3_SEL_R0_CS32
+ mov ss, ax
+ %if TMPL_BITS == 16
+ mov esp, edx ; Load flat stack address.
+ %endif
+
+ add eax, BS3_SEL_R0_DS32 - BS3_SEL_R0_SS32
+ mov ds, ax
+ mov es, ax
+
+ ; Update globals.
+ and byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], ~BS3_MODE_CODE_MASK
+ or byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_32
+
+ %if TMPL_BITS == 16
+ ; Adjust the return address.
+ movzx eax, word [esp + 4*3 + 2]
+ add eax, BS3_ADDR_BS3TEXT16
+ mov [esp + 4*3], eax
+ %endif
+
+ ; Restore and return.
+ %if TMPL_BITS == 16
+ pop edx
+ %endif
+ popfd
+ pop eax
+TONLY64 ret 4
+TNOT64 ret
+%endif
+BS3_PROC_END_CMN Bs3SwitchTo32Bit
+
+;; @todo far 16-bit variant.
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo64Bit.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo64Bit.asm
new file mode 100644
index 00000000..dfa70f58
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchTo64Bit.asm
@@ -0,0 +1,120 @@
+; $Id: bs3-cmn-SwitchTo64Bit.asm $
+;; @file
+; BS3Kit - Bs3SwitchTo64Bit
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+TMPL_BEGIN_TEXT
+%endif
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchTo64Bit(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchTo64Bit, BS3_PBC_NEAR
+%if TMPL_BITS == 64
+ ret
+
+%else
+ %if TMPL_BITS == 16
+ sub sp, 6 ; Space for extended return value (corrected in 64-bit mode).
+ %else
+ push xPRE [xSP] ; Duplicate the return address.
+ and dword [xSP + xCB], 0 ; Clear the high dword or it.
+ %endif
+ push dword 0
+ push sAX
+ push dword 0
+ pushfd
+ cli
+
+ %if TMPL_BITS == 16
+ ; Check that this is LM16
+ mov ax, seg g_bBs3CurrentMode
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_LM16
+ je .ok_lm16
+ int3
+ .ok_lm16:
+ %endif
+
+ ; Calc ring addend.
+ mov ax, cs
+ and xAX, 3
+ shl xAX, BS3_SEL_RING_SHIFT
+ add xAX, BS3_SEL_R0_CS64
+
+ ; setup far return.
+ push sAX
+ %if TMPL_BITS == 16
+ push dword .sixty_four_bit wrt FLAT
+ o32 retf
+ %else
+ push .sixty_four_bit
+ retf
+ %endif
+
+BS3_SET_BITS 64
+.sixty_four_bit:
+
+ ; Load 64-bit segment registers (SS64==DS64).
+ add eax, BS3_SEL_R0_DS64 - BS3_SEL_R0_CS64
+ mov ss, ax
+ mov ds, ax
+ mov es, ax
+
+ ; Update globals.
+ and byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], ~BS3_MODE_CODE_MASK
+ or byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_64
+
+ %if TMPL_BITS == 16
+ movzx eax, word [rsp + 8*2+6]
+ add eax, BS3_ADDR_BS3TEXT16
+ mov [rsp + 8*2], rax
+ %endif
+
+ popfq
+ pop rax
+ ret
+%endif
+BS3_PROC_END_CMN Bs3SwitchTo64Bit
+
+
+;; @todo far 16-bit variant.
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing0.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing0.asm
new file mode 100644
index 00000000..fd3f1135
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing0.asm
@@ -0,0 +1,73 @@
+; $Id: bs3-cmn-SwitchToRing0.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRing0
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN_FAR Bs3SwitchToRingX
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchToRing0(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchToRing0, BS3_PBC_HYBRID_0_ARGS
+%if TMPL_BITS == 64
+ push rcx
+ sub rsp, 20h
+ mov ecx, 0
+ mov [rsp], rcx
+ call Bs3SwitchToRingX
+ add rsp, 20h
+ pop rcx
+%else
+ push 0
+TONLY16 push cs
+ call Bs3SwitchToRingX
+ add xSP, xCB
+%endif
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SwitchToRing0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing1.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing1.asm
new file mode 100644
index 00000000..834992c8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing1.asm
@@ -0,0 +1,73 @@
+; $Id: bs3-cmn-SwitchToRing1.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRing1
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN_FAR Bs3SwitchToRingX
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchToRing1(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchToRing1, BS3_PBC_HYBRID_0_ARGS
+%if TMPL_BITS == 64
+ push rcx
+ sub rsp, 20h
+ mov ecx, 1
+ mov [rsp], rcx
+ call Bs3SwitchToRingX
+ add rsp, 20h
+ pop rcx
+%else
+ push 1
+TONLY16 push cs
+ call Bs3SwitchToRingX
+ add xSP, xCB
+%endif
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SwitchToRing1
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing2.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing2.asm
new file mode 100644
index 00000000..a14202d6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing2.asm
@@ -0,0 +1,73 @@
+; $Id: bs3-cmn-SwitchToRing2.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRing2
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN_FAR Bs3SwitchToRingX
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchToRing2(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchToRing2, BS3_PBC_HYBRID_0_ARGS
+%if TMPL_BITS == 64
+ push rcx
+ sub rsp, 20h
+ mov ecx, 2
+ mov [rsp], rcx
+ call Bs3SwitchToRingX
+ add rsp, 20h
+ pop rcx
+%else
+ push 2
+TONLY16 push cs
+ call Bs3SwitchToRingX
+ add xSP, xCB
+%endif
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SwitchToRing2
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing3.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing3.asm
new file mode 100644
index 00000000..901be524
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRing3.asm
@@ -0,0 +1,73 @@
+; $Id: bs3-cmn-SwitchToRing3.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRing3
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN_FAR Bs3SwitchToRingX
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchToRing3(void);
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchToRing3, BS3_PBC_HYBRID_0_ARGS
+%if TMPL_BITS == 64
+ push rcx
+ sub rsp, 20h
+ mov ecx, 3
+ mov [rsp], rcx
+ call Bs3SwitchToRingX
+ add rsp, 20h
+ pop rcx
+%else
+ push 3
+TONLY16 push cs
+ call Bs3SwitchToRingX
+ add xSP, xCB
+%endif
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3SwitchToRing3
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRingX.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRingX.asm
new file mode 100644
index 00000000..9c48e0f8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-SwitchToRingX.asm
@@ -0,0 +1,103 @@
+; $Id: bs3-cmn-SwitchToRingX.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRingX
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_EXTERN_CMN Bs3Syscall
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(void) Bs3SwitchToRingX(uint8_t bRing);
+;
+; @param bRing The target ring (0..3).
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+; @uses No GPRs.
+;
+BS3_PROC_BEGIN_CMN Bs3SwitchToRingX, BS3_PBC_HYBRID_SAFE
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xAX
+
+%if TMPL_BITS == 16
+ ; Check the current mode.
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+
+ ; If real mode: Nothing we can do, but we'll bitch if the request isn't for ring-0.
+ cmp al, BS3_MODE_RM
+ je .return_real_mode
+
+ ; If V8086 mode: Always do syscall and add a lock prefix to make sure it gets to the VMM.
+ test al, BS3_MODE_CODE_V86
+ jnz .just_do_it
+%endif
+
+ ; In protected mode: Check the CPL we're currently at skip syscall if ring-0 already.
+ mov ax, cs
+ and al, 3
+ cmp al, byte [xBP + xCB + cbCurRetAddr]
+ je .return
+
+.just_do_it:
+ mov xAX, BS3_SYSCALL_TO_RING0
+ add al, [xBP + xCB + cbCurRetAddr]
+ call Bs3Syscall
+
+%ifndef BS3_STRICT
+.return_real_mode:
+%endif
+.return:
+ pop xAX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+
+%ifdef BS3_STRICT
+; In real mode, only ring-0 makes any sense.
+.return_real_mode:
+ cmp byte [xBP + xCB + cbCurRetAddr], 0
+ je .return
+ int3
+ jmp .return
+%endif
+BS3_PROC_END_CMN Bs3SwitchToRingX
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Syscall.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Syscall.asm
new file mode 100644
index 00000000..6fad4703
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Syscall.asm
@@ -0,0 +1,94 @@
+; $Id: bs3-cmn-Syscall.asm $
+;; @file
+; BS3Kit - Bs3Syscall.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+BS3_EXTERN_DATA16 g_uBs3TrapEipHint
+TMPL_BEGIN_TEXT
+
+
+;;
+; Worker for doing a syscall - Assembly only.
+;
+; This worker deals with the needing to use a different opcode
+; sequence in v8086 mode as well as the high EIP word hint for
+; the weird PE16_32, PP16_32 and PAE16_32 modes.
+;
+; @uses Whatever the syscall modified (xBX and XBP are always saved).
+;
+BS3_PROC_BEGIN_CMN Bs3Syscall, BS3_PBC_HYBRID_0_ARGS ; (all parameters are in registers)
+ push xBP
+ mov xBP, xSP
+ push xBX
+
+%if TMPL_BITS == 32
+ mov ebx, .return
+ xchg ebx, [BS3_DATA16_WRT(g_uBs3TrapEipHint)]
+%elif TMPL_BITS == 16
+ test byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], BS3_MODE_CODE_V86
+ mov bx, 0
+ xchg bx, [2 + BS3_DATA16_WRT(g_uBs3TrapEipHint)]
+ jz .normal
+
+ db 0xf0 ; Lock prefix for causing #UD in V8086 mode.
+%endif
+.normal:
+ int BS3_TRAP_SYSCALL
+
+.return:
+ ; Restore the EIP hint so the testcase code doesn't need to set it all the time.
+%if TMPL_BITS == 32
+ mov [BS3_DATA16_WRT(g_uBs3TrapEipHint)], ebx
+%elif TMPL_BITS == 16
+ mov [2 + BS3_DATA16_WRT(g_uBs3TrapEipHint)], bx
+%endif
+
+ pop xBX
+ pop xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3Syscall
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckExtCtx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckExtCtx.c
new file mode 100644
index 00000000..7c4b4a0e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckExtCtx.c
@@ -0,0 +1,287 @@
+/* $Id: bs3-cmn-TestCheckExtCtx.c $ */
+/** @file
+ * BS3Kit - Bs3TestCheckExtCtx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Field descriptors. */
+static struct
+{
+ uint8_t enmMethod;
+ uint8_t cb;
+ uint16_t off;
+ const char BS3_FAR *pszName;
+} const g_aFields[] =
+{
+#define BS3EXTCTX_FIELD_ENTRY(a_enmMethod, a_Member) \
+ { a_enmMethod, RT_SIZEOFMEMB(BS3EXTCTX, a_Member), RT_OFFSETOF(BS3EXTCTX, a_Member), #a_Member }
+ BS3EXTCTX_FIELD_ENTRY(BS3EXTCTXMETHOD_END, fXcr0Saved),
+
+#define BS3EXTCTX_FIELD_ENTRY_CTX(a_enmMethod, a_Member) \
+ { a_enmMethod, RT_SIZEOFMEMB(BS3EXTCTX, Ctx.a_Member), RT_OFFSETOF(BS3EXTCTX, Ctx.a_Member), #a_Member }
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FCW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.Dummy1),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FSW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.Dummy2),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FTW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.Dummy3),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FPUIP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.CS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FOP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FPUOO),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.FPUOS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_ANCIENT, Ancient.regs[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FCW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FSW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FTW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FOP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FPUIP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.CS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.Rsrvd1),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.FPUDP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.DS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.Rsrvd2),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.MXCSR),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.MXCSR_MASK),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aRegs[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[8]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[9]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[10]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[11]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[12]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[13]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[14]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_FXSAVE, x87.aXMM[15]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FCW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FSW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FTW),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FOP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FPUIP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.CS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.Rsrvd1),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.FPUDP),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.DS),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.Rsrvd2),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.MXCSR),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.MXCSR_MASK),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aRegs[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[8]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[9]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[10]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[11]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[12]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[13]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[14]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.x87.aXMM[15]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.Hdr.bmXState),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.Hdr.bmXComp),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[0]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[1]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[2]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[3]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[4]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[5]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[6]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[7]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[8]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[9]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[10]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[11]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[12]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[13]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[14]),
+ BS3EXTCTX_FIELD_ENTRY_CTX(BS3EXTCTXMETHOD_XSAVE, x.u.YmmHi.aYmmHi[15]),
+};
+
+
+#undef Bs3TestCheckExtCtx
+BS3_CMN_DEF(bool, Bs3TestCheckExtCtx,(PCBS3EXTCTX pActualExtCtx, PCBS3EXTCTX pExpectedExtCtx, uint16_t fFlags,
+ const char BS3_FAR *pszMode, uint16_t idTestStep))
+{
+ /*
+ * Make sure the context of a similar and valid before starting.
+ */
+ if (!pActualExtCtx || pActualExtCtx->u16Magic != BS3EXTCTX_MAGIC)
+ return Bs3TestFailedF("%u - %s: invalid actual context pointer: %p", idTestStep, pszMode, pActualExtCtx);
+ if (!pExpectedExtCtx || pExpectedExtCtx->u16Magic != BS3EXTCTX_MAGIC)
+ return Bs3TestFailedF("%u - %s: invalid expected context pointer: %p", idTestStep, pszMode, pExpectedExtCtx);
+ if ( pActualExtCtx->enmMethod != pExpectedExtCtx->enmMethod
+ || pActualExtCtx->enmMethod == BS3EXTCTXMETHOD_INVALID
+ || pActualExtCtx->enmMethod >= BS3EXTCTXMETHOD_END)
+ return Bs3TestFailedF("%u - %s: mismatching or/and invalid context methods: %d vs %d",
+ idTestStep, pszMode, pActualExtCtx->enmMethod, pExpectedExtCtx->enmMethod);
+ if (pActualExtCtx->cb != pExpectedExtCtx->cb)
+ return Bs3TestFailedF("%u - %s: mismatching context sizes: %#x vs %#x",
+ idTestStep, pszMode, pActualExtCtx->cb, pExpectedExtCtx->cb);
+
+ /*
+ * Try get the job done quickly with a memory compare.
+ */
+ if (Bs3MemCmp(pActualExtCtx, pExpectedExtCtx, pActualExtCtx->cb) == 0)
+ return true;
+
+ Bs3TestFailedF("%u - %s: context memory differs", idTestStep, pszMode); // debug
+ {
+ uint8_t const BS3_FAR *pb1 = (uint8_t const BS3_FAR *)pActualExtCtx;
+ uint8_t const BS3_FAR *pb2 = (uint8_t const BS3_FAR *)pExpectedExtCtx;
+ unsigned const cb = pActualExtCtx->cb;
+ unsigned off;
+ for (off = 0; off < cb; off++)
+ if (pb1[off] != pb2[off])
+ {
+ unsigned offStart = off++;
+ const char BS3_FAR *pszName = NULL;
+ unsigned cbDiff = 0;
+ unsigned idxField;
+ for (idxField = 0; idxField < RT_ELEMENTS(g_aFields); idxField++)
+ if ( offStart - g_aFields[idxField].off < g_aFields[idxField].cb
+ && ( g_aFields[idxField].enmMethod == BS3EXTCTXMETHOD_END
+ || g_aFields[idxField].enmMethod == pActualExtCtx->enmMethod))
+ {
+ pszName = g_aFields[idxField].pszName;
+ cbDiff = g_aFields[idxField].cb;
+ offStart = g_aFields[idxField].off;
+ off = offStart + cbDiff;
+ break;
+ }
+ if (!pszName)
+ {
+ while (off < cb && pb1[off] != pb2[off])
+ off++;
+ cbDiff = off - offStart;
+ pszName = "unknown";
+ }
+ switch (cbDiff)
+ {
+ case 1:
+ Bs3TestFailedF("%u - %s: Byte difference at %#x (%s): %#04x, expected %#04x",
+ idTestStep, pszMode, offStart, pszName, pb1[offStart], pb2[offStart]);
+ break;
+ case 2:
+ Bs3TestFailedF("%u - %s: Word difference at %#x (%s): %#06x, expected %#06x",
+ idTestStep, pszMode, offStart, pszName,
+ RT_MAKE_U16(pb1[offStart], pb1[offStart + 1]),
+ RT_MAKE_U16(pb2[offStart], pb2[offStart + 1]));
+ break;
+ case 4:
+ Bs3TestFailedF("%u - %s: DWord difference at %#x (%s): %#010RX32, expected %#010RX32",
+ idTestStep, pszMode, offStart, pszName,
+ RT_MAKE_U32_FROM_U8(pb1[offStart], pb1[offStart + 1], pb1[offStart + 2], pb1[offStart + 3]),
+ RT_MAKE_U32_FROM_U8(pb2[offStart], pb2[offStart + 1], pb2[offStart + 2], pb2[offStart + 3]));
+ break;
+ case 8:
+ Bs3TestFailedF("%u - %s: QWord difference at %#x (%s): %#018RX64, expected %#018RX64",
+ idTestStep, pszMode, offStart, pszName,
+ RT_MAKE_U64_FROM_U8(pb1[offStart], pb1[offStart + 1], pb1[offStart + 2], pb1[offStart + 3],
+ pb1[offStart + 4], pb1[offStart + 5], pb1[offStart + 6], pb1[offStart + 7]),
+ RT_MAKE_U64_FROM_U8(pb2[offStart], pb2[offStart + 1], pb2[offStart + 2], pb2[offStart + 3],
+ pb2[offStart + 4], pb2[offStart + 5], pb2[offStart + 6], pb2[offStart + 7]));
+ break;
+ case 16:
+ Bs3TestFailedF("%u - %s: DQword difference at %#x (%s): \n"
+ "got %#018RX64'%#018RX64\n"
+ "expected %#018RX64'%#018RX64",
+ idTestStep, pszMode, offStart, pszName,
+ RT_MAKE_U64_FROM_U8(pb1[offStart + 8], pb1[offStart + 9], pb1[offStart + 10], pb1[offStart + 11],
+ pb1[offStart + 12], pb1[offStart + 13], pb1[offStart + 14], pb1[offStart + 15]),
+ RT_MAKE_U64_FROM_U8(pb1[offStart], pb1[offStart + 1], pb1[offStart + 2], pb1[offStart + 3],
+ pb1[offStart + 4], pb1[offStart + 5], pb1[offStart + 6], pb1[offStart + 7]),
+
+ RT_MAKE_U64_FROM_U8(pb2[offStart + 8], pb2[offStart + 9], pb2[offStart + 10], pb2[offStart + 11],
+ pb2[offStart + 12], pb2[offStart + 13], pb2[offStart + 14], pb2[offStart + 15]),
+ RT_MAKE_U64_FROM_U8(pb2[offStart], pb2[offStart + 1], pb2[offStart + 2], pb2[offStart + 3],
+ pb2[offStart + 4], pb2[offStart + 5], pb2[offStart + 6], pb2[offStart + 7])
+ );
+ break;
+
+ default:
+ Bs3TestFailedF("%u - %s: %#x..%#x differs (%s)\n"
+ "got %.*Rhxs\n"
+ "expected %.*Rhxs",
+ idTestStep, pszMode, offStart, off - 1, pszName,
+ off - offStart, &pb1[offStart],
+ off - offStart, &pb2[offStart]);
+ break;
+ }
+ }
+ }
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckRegCtxEx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckRegCtxEx.c
new file mode 100644
index 00000000..d75b49f7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestCheckRegCtxEx.c
@@ -0,0 +1,107 @@
+/* $Id: bs3-cmn-TestCheckRegCtxEx.c $ */
+/** @file
+ * BS3Kit - TestCheckRegCtxEx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TestCheckRegCtxEx
+BS3_CMN_DEF(bool, Bs3TestCheckRegCtxEx,(PCBS3REGCTX pActualCtx, PCBS3REGCTX pExpectedCtx, uint16_t cbPcAdjust, int16_t cbSpAcjust,
+ uint32_t fExtraEfl, const char BS3_FAR *pszMode, uint16_t idTestStep))
+{
+ uint16_t const cErrorsBefore = Bs3TestSubErrorCount();
+ uint8_t const fbFlags = pActualCtx->fbFlags | pExpectedCtx->fbFlags;
+
+#define CHECK_MEMBER(a_szName, a_szFmt, a_Actual, a_Expected) \
+ do { \
+ if ((a_Actual) == (a_Expected)) { /* likely */ } \
+ else Bs3TestFailedF("%u - %s: " a_szName "=" a_szFmt " expected " a_szFmt, idTestStep, pszMode, (a_Actual), (a_Expected)); \
+ } while (0)
+
+ CHECK_MEMBER("rax", "%08RX64", pActualCtx->rax.u, pExpectedCtx->rax.u);
+ CHECK_MEMBER("rcx", "%08RX64", pActualCtx->rcx.u, pExpectedCtx->rcx.u);
+ CHECK_MEMBER("rdx", "%08RX64", pActualCtx->rdx.u, pExpectedCtx->rdx.u);
+ CHECK_MEMBER("rbx", "%08RX64", pActualCtx->rbx.u, pExpectedCtx->rbx.u);
+ CHECK_MEMBER("rsp", "%08RX64", pActualCtx->rsp.u, pExpectedCtx->rsp.u + cbSpAcjust);
+ CHECK_MEMBER("rbp", "%08RX64", pActualCtx->rbp.u, pExpectedCtx->rbp.u);
+ CHECK_MEMBER("rsi", "%08RX64", pActualCtx->rsi.u, pExpectedCtx->rsi.u);
+ CHECK_MEMBER("rdi", "%08RX64", pActualCtx->rdi.u, pExpectedCtx->rdi.u);
+ if (!(fbFlags & BS3REG_CTX_F_NO_AMD64))
+ {
+ CHECK_MEMBER("r8", "%08RX64", pActualCtx->r8.u, pExpectedCtx->r8.u);
+ CHECK_MEMBER("r9", "%08RX64", pActualCtx->r9.u, pExpectedCtx->r9.u);
+ CHECK_MEMBER("r10", "%08RX64", pActualCtx->r10.u, pExpectedCtx->r10.u);
+ CHECK_MEMBER("r11", "%08RX64", pActualCtx->r11.u, pExpectedCtx->r11.u);
+ CHECK_MEMBER("r12", "%08RX64", pActualCtx->r12.u, pExpectedCtx->r12.u);
+ CHECK_MEMBER("r13", "%08RX64", pActualCtx->r13.u, pExpectedCtx->r13.u);
+ CHECK_MEMBER("r14", "%08RX64", pActualCtx->r14.u, pExpectedCtx->r14.u);
+ CHECK_MEMBER("r15", "%08RX64", pActualCtx->r15.u, pExpectedCtx->r15.u);
+ }
+ CHECK_MEMBER("rflags", "%08RX64", pActualCtx->rflags.u, pExpectedCtx->rflags.u | fExtraEfl);
+ CHECK_MEMBER("rip", "%08RX64", pActualCtx->rip.u, pExpectedCtx->rip.u + cbPcAdjust);
+ CHECK_MEMBER("cs", "%04RX16", pActualCtx->cs, pExpectedCtx->cs);
+ CHECK_MEMBER("ds", "%04RX16", pActualCtx->ds, pExpectedCtx->ds);
+ CHECK_MEMBER("es", "%04RX16", pActualCtx->es, pExpectedCtx->es);
+ CHECK_MEMBER("fs", "%04RX16", pActualCtx->fs, pExpectedCtx->fs);
+ CHECK_MEMBER("gs", "%04RX16", pActualCtx->gs, pExpectedCtx->gs);
+
+ if (!(fbFlags & BS3REG_CTX_F_NO_TR_LDTR))
+ {
+ CHECK_MEMBER("tr", "%04RX16", pActualCtx->tr, pExpectedCtx->tr);
+ CHECK_MEMBER("ldtr", "%04RX16", pActualCtx->ldtr, pExpectedCtx->ldtr);
+ }
+ CHECK_MEMBER("bMode", "%#04x", pActualCtx->bMode, pExpectedCtx->bMode);
+ CHECK_MEMBER("bCpl", "%u", pActualCtx->bCpl, pExpectedCtx->bCpl);
+
+ if (!(fbFlags & BS3REG_CTX_F_NO_CR0_IS_MSW))
+ CHECK_MEMBER("cr0", "%08RX64", pActualCtx->cr0.u, pExpectedCtx->cr0.u);
+ else
+ CHECK_MEMBER("msw", "%08RX16", pActualCtx->cr0.u16, pExpectedCtx->cr0.u16);
+ if (!(fbFlags & BS3REG_CTX_F_NO_CR2_CR3))
+ {
+ CHECK_MEMBER("cr2", "%08RX64", pActualCtx->cr2.u, pExpectedCtx->cr2.u);
+ CHECK_MEMBER("cr3", "%08RX64", pActualCtx->cr3.u, pExpectedCtx->cr3.u);
+ }
+ if (!(fbFlags & BS3REG_CTX_F_NO_CR4))
+ CHECK_MEMBER("cr4", "%08RX64", pActualCtx->cr4.u, pExpectedCtx->cr4.u);
+#undef CHECK_MEMBER
+
+ return Bs3TestSubErrorCount() == cErrorsBefore;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestData.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestData.c
new file mode 100644
index 00000000..8ff06dec
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestData.c
@@ -0,0 +1,135 @@
+/* $Id: bs3-cmn-TestData.c $ */
+/** @file
+ * BS3Kit - Test Data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+
+/** Indicates whether the VMMDev is operational. */
+bool g_fbBs3VMMDevTesting = true;
+
+/** Alignment padding. */
+bool g_fTestDataPadding0 = true;
+
+/** The number of tests that have failed. */
+uint16_t g_cusBs3TestErrors = 0;
+
+/** The start error count of the current subtest. */
+uint16_t g_cusBs3SubTestAtErrors = 0;
+
+/** Whether we've reported the sub-test result or not. */
+bool g_fbBs3SubTestReported = true;
+/** Whether the sub-test has been skipped or not. */
+bool g_fbBs3SubTestSkipped = false;
+
+/** The number of sub tests. */
+uint16_t g_cusBs3SubTests = 0;
+
+/** The number of sub tests that failed. */
+uint16_t g_cusBs3SubTestsFailed = 0;
+
+/** VMMDEV_TESTING_UNIT_XXX -> string */
+char const g_aszBs3TestUnitNames[][12] =
+{
+ "inv",
+ "%",
+ "bytes",
+ "bytes/s",
+ "KB",
+ "KB/s",
+ "MB",
+ "MB/s",
+ "packets",
+ "packets/s",
+ "frames",
+ "frames/",
+ "occ",
+ "occ/s",
+ "rndtrp",
+ "calls",
+ "calls/s",
+ "s",
+ "ms",
+ "ns",
+ "ns/call",
+ "ns/frame",
+ "ns/occ",
+ "ns/packet",
+ "ns/rndtrp",
+ "ins",
+ "ins/s",
+ "", /* none */
+ "pp1k",
+ "pp10k",
+ "ppm",
+ "ppb",
+ "ticks",
+ "ticks/call",
+ "ticks/occ",
+ "pages",
+ "pages/s",
+ "ticks/page",
+ "ns/page",
+ "ps",
+ "ps/call",
+ "ps/frame",
+ "ps/occ",
+ "ps/packet",
+ "ps/rndtrp",
+ "ps/page",
+};
+
+
+/** The subtest name. */
+char g_szBs3SubTest[64];
+
+/** The current test step. */
+uint16_t g_usBs3TestStep;
+
+#endif /* ARCH_BITS == 16 */
+
+/** The test name. */
+const char BS3_FAR *BS3_CMN_NM(g_pszBs3Test) = NULL;
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestDoModesByOneHlp.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestDoModesByOneHlp.asm
new file mode 100644
index 00000000..46dd230a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestDoModesByOneHlp.asm
@@ -0,0 +1,253 @@
+; $Id: bs3-cmn-TestDoModesByOneHlp.asm $
+;; @file
+; BS3Kit - Bs3TestDoModesByOne Helpers for switching to the bit-count of the worker function.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent),,0
+ RTCCPTR_DEF 0
+
+
+;*********************************************************************************************************************************
+;* Exported Symbols *
+;*********************************************************************************************************************************
+%ifdef BS3_STRICT
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+
+%if TMPL_BITS == 16
+BS3_BEGIN_TEXT16
+extern _Bs3SelRealModeCodeToProtMode_c16
+%endif
+
+
+;;
+; @cproto FNBS3TESTDOMODE
+;
+; @param bMode The current mode
+; @uses What allowed by calling convention and possibly mode, caller deals with it.
+;
+
+%if TMPL_BITS == 16
+ ;
+ ; For 16-bit workers.
+ ;
+BS3_BEGIN_TEXT16
+
+BS3_SET_BITS 32
+BS3_PROC_BEGIN _Bs3TestCallDoerTo16_c32
+ push xBP
+ mov xBP, xSP
+
+ ; Load bMode into eax.
+ movzx eax, byte [xBP + xCB*2]
+ %ifdef BS3_STRICT
+ cmp al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ je .ok_mode
+ int3
+.ok_mode:
+ %endif
+ ; Switch to 16-bit.
+ extern _Bs3SwitchTo16Bit_c32
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS 16
+
+ push ax ; Worker bMode argument.
+
+ ; Assuming real mode far pointer, convert protected mode before calling it.
+ push word [2 + BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))]
+ call _Bs3SelRealModeCodeToProtMode_c16
+ add sp, 2
+
+ push cs ; return selector
+ push word .return ; return address
+
+ push ax ; call converted selector
+ push word [BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))] ; call offset
+ retf
+
+.return:
+ ; Switch back to 32-bit mode.
+ extern _Bs3SwitchTo32Bit_c16
+ call _Bs3SwitchTo32Bit_c16
+ BS3_SET_BITS 32
+
+ leave
+ ret
+BS3_PROC_END _Bs3TestCallDoerTo16_c32
+
+
+BS3_SET_BITS 64
+BS3_PROC_BEGIN _Bs3TestCallDoerTo16_c64
+ push xBP
+ mov xBP, xSP
+
+ ; Load bMode into eax.
+ movzx eax, cl
+ %ifdef BS3_STRICT
+ cmp al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ je .ok_mode
+ int3
+.ok_mode:
+ %endif
+ ; Switch to 16-bit.
+ extern _Bs3SwitchTo16Bit_c64
+ call _Bs3SwitchTo16Bit_c64
+ BS3_SET_BITS 16
+
+ push ax ; Worker bMode argument.
+
+ ; Assuming real mode far pointer, convert protected mode before calling it.
+ push word [2 + BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))]
+ call _Bs3SelRealModeCodeToProtMode_c16
+ add sp, 2
+
+ push cs ; return selector
+ push word .return ; return address
+ push ax ; call converted selector
+ push word [BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))] ; call offset
+ retf
+
+.return:
+ ; Switch back to 64-bit mode.
+ extern _Bs3SwitchTo64Bit_c16
+ call _Bs3SwitchTo64Bit_c16
+ BS3_SET_BITS 64
+
+ leave
+ ret
+BS3_PROC_END _Bs3TestCallDoerTo16_c64
+
+
+%elif TMPL_BITS == 32
+ ;
+ ; For 32-bit workers.
+ ;
+
+BS3_BEGIN_TEXT16
+BS3_SET_BITS 16
+BS3_PROC_BEGIN _Bs3TestCallDoerTo32_f16
+ push xBP
+ mov xBP, xSP
+
+ ; Load bMode into eax.
+ movzx eax, byte [xBP + xCB + sCB]
+ %ifdef BS3_STRICT
+ cmp al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ je .ok_mode
+ int3
+.ok_mode:
+ %endif
+ ; Switch to 32-bit.
+ extern _Bs3SwitchTo32Bit_c16
+ call _Bs3SwitchTo32Bit_c16
+ BS3_SET_BITS 32
+
+ push eax ; Worker bMode argument.
+
+ test al, BS3_MODE_CODE_V86
+ jnz .return_to_v86 ; Need to figure this while we still have the mode value.
+
+ call [BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))]
+
+ ; Switch back to 16-bit mode.
+ extern _Bs3SwitchTo16Bit_c32
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS 16
+.return:
+ leave
+ retf
+
+ BS3_SET_BITS 32
+.return_to_v86:
+ call [BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))]
+
+ ; Switch back to v8086 mode.
+ extern _Bs3SwitchTo16BitV86_c32
+ call _Bs3SwitchTo16BitV86_c32
+ BS3_SET_BITS 16
+ jmp .return
+BS3_PROC_END _Bs3TestCallDoerTo32_f16
+
+
+BS3_BEGIN_TEXT32
+BS3_SET_BITS 64
+BS3_PROC_BEGIN _Bs3TestCallDoerTo32_c64
+ push xBP
+ mov xBP, xSP
+
+ ; Load bMode into eax.
+ movzx eax, cl
+ %ifdef BS3_STRICT
+ cmp al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ je .ok_mode
+ int3
+.ok_mode:
+ %endif
+ ; Switch to 32-bit.
+ extern _Bs3SwitchTo32Bit_c64
+ call _Bs3SwitchTo32Bit_c64
+ BS3_SET_BITS 32
+
+ push eax ; Worker bMode argument.
+ call [BS3_DATA16_WRT(BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent))]
+
+ ; Switch back to 64-bit mode.
+ extern _Bs3SwitchTo64Bit_c32
+ call _Bs3SwitchTo64Bit_c32
+ BS3_SET_BITS 64
+
+ leave
+ ret
+BS3_PROC_END _Bs3TestCallDoerTo32_c64
+
+
+%elif TMPL_BITS == 64
+;
+; 64-bit workers makes no sense, so skip that.
+;
+%else
+ %error "TMPL_BITS!"
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestFailed.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestFailed.c
new file mode 100644
index 00000000..2fc104fa
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestFailed.c
@@ -0,0 +1,151 @@
+/* $Id: bs3-cmn-TestFailed.c $ */
+/** @file
+ * BS3Kit - Bs3TestFailed, Bs3TestFailedF, Bs3TestFailedV.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+/**
+ * @callback_method_impl{FNBS3STRFORMATOUTPUT,
+ * Used by Bs3TestFailedV and Bs3TestSkippedV.}
+ */
+BS3_DECL_CALLBACK(size_t) bs3TestFailedStrOutput(char ch, void BS3_FAR *pvUser)
+{
+ PBS3TESTFAILEDBUF pBuf = (PBS3TESTFAILEDBUF)pvUser;
+
+ /*
+ * VMMDev first. We postpone newline processing here so we can strip one
+ * trailing newline.
+ */
+ if (g_fbBs3VMMDevTesting)
+ {
+ if (pBuf->fNewLine && ch != '\0')
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, '\n');
+ pBuf->fNewLine = ch == '\n';
+ if (ch != '\n')
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, ch);
+ }
+
+ /*
+ * Console next.
+ */
+ if (ch != '\0')
+ {
+ BS3_ASSERT(pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf));
+ pBuf->achBuf[pBuf->cchBuf++] = ch;
+
+ /* Whether to flush the buffer. We do line flushing here to avoid
+ dropping too much info when the formatter crashes on bad input. */
+ if ( pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf)
+ && ch != '\n')
+ {
+ pBuf->fNewLine = false;
+ return 1;
+ }
+ pBuf->fNewLine = '\n';
+ }
+ /* Try fit missing newline into the buffer. */
+ else if (!pBuf->fNewLine && pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf))
+ {
+ pBuf->fNewLine = true;
+ pBuf->achBuf[pBuf->cchBuf++] = '\n';
+ }
+
+ BS3_ASSERT(pBuf->cchBuf <= RT_ELEMENTS(pBuf->achBuf));
+ Bs3PrintStrN(&pBuf->achBuf[0], pBuf->cchBuf);
+ pBuf->cchBuf = 0;
+
+ /* In case we failed to add trailing new line, print one separately. */
+ if (!pBuf->fNewLine)
+ Bs3PrintChr('\n');
+
+ return ch != '\0';
+}
+
+
+/**
+ * Equivalent to RTTestIFailedV.
+ */
+#undef Bs3TestFailedV
+BS3_CMN_DEF(bool, Bs3TestFailedV,(const char *pszFormat, va_list BS3_FAR va))
+{
+ BS3TESTFAILEDBUF Buf;
+
+ if (!++g_cusBs3TestErrors)
+ g_cusBs3TestErrors++;
+
+ if (g_fbBs3VMMDevTesting)
+#if ARCH_BITS == 16
+ ASMOutU16(VMMDEV_TESTING_IOPORT_CMD, (uint16_t)VMMDEV_TESTING_CMD_FAILED);
+#else
+ ASMOutU32(VMMDEV_TESTING_IOPORT_CMD, VMMDEV_TESTING_CMD_FAILED);
+#endif
+
+ Buf.fNewLine = false;
+ Buf.cchBuf = 0;
+ Bs3StrFormatV(pszFormat, va, bs3TestFailedStrOutput, &Buf);
+ return false;
+}
+
+
+/**
+ * Equivalent to RTTestIFailedF.
+ */
+#undef Bs3TestFailedF
+BS3_CMN_DEF(bool, Bs3TestFailedF,(const char *pszFormat, ...))
+{
+ va_list va;
+ va_start(va, pszFormat);
+ BS3_CMN_NM(Bs3TestFailedV)(pszFormat, va);
+ va_end(va);
+ return false;
+}
+
+
+/**
+ * Equivalent to RTTestIFailed.
+ */
+#undef Bs3TestFailed
+BS3_CMN_DEF(bool, Bs3TestFailed,(const char *pszMessage))
+{
+ return BS3_CMN_NM(Bs3TestFailedF)("%s", pszMessage);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestHostPrintf.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestHostPrintf.c
new file mode 100644
index 00000000..73622683
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestHostPrintf.c
@@ -0,0 +1,114 @@
+/* $Id: bs3-cmn-TestHostPrintf.c $ */
+/** @file
+ * BS3Kit - BS3TestPrintf, BS3TestPrintfV
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Output state for Bs3TestHostPrintfV. */
+typedef struct BS3TESTHOSTPRINTF
+{
+ bool fNewCmd;
+} BS3TESTHOSTPRINTF;
+
+
+/**
+ * @callback_method_impl{FNBS3STRFORMATOUTPUT, Prints to screen and VMMDev}
+ */
+static BS3_DECL_CALLBACK(size_t) bs3TestPrintfStrOutput(char ch, void BS3_FAR *pvUser)
+{
+ BS3TESTHOSTPRINTF BS3_FAR *pState = (BS3TESTHOSTPRINTF BS3_FAR *)pvUser;
+
+ /*
+ * VMMDev first. We do line by line processing to avoid running out of
+ * string buffer on the host side.
+ */
+ if (g_fbBs3VMMDevTesting)
+ {
+ if (ch != '\n' && !pState->fNewCmd)
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, ch);
+ else if (ch != '\0')
+ {
+ if (pState->fNewCmd)
+ {
+#if ARCH_BITS == 16
+ ASMOutU16(VMMDEV_TESTING_IOPORT_CMD, (uint16_t)VMMDEV_TESTING_CMD_PRINT);
+#else
+ ASMOutU32(VMMDEV_TESTING_IOPORT_CMD, VMMDEV_TESTING_CMD_PRINT);
+#endif
+ pState->fNewCmd = false;
+ }
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, ch);
+ if (ch == '\n')
+ {
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, '\0');
+ pState->fNewCmd = true;
+ }
+ }
+ }
+
+ return ch != '\0';
+}
+
+
+#undef Bs3TestHostPrintfV
+BS3_CMN_DEF(void, Bs3TestHostPrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va))
+{
+ BS3TESTHOSTPRINTF State;
+ State.fNewCmd = true;
+ Bs3StrFormatV(pszFormat, va, bs3TestPrintfStrOutput, &State);
+}
+
+
+
+#undef Bs3TestHostPrintf
+BS3_CMN_DEF(void, Bs3TestHostPrintf,(const char BS3_FAR *pszFormat, ...))
+{
+ va_list va;
+ va_start(va, pszFormat);
+ BS3_CMN_NM(Bs3TestHostPrintfV)(pszFormat, va);
+ va_end(va);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestInit.c
new file mode 100644
index 00000000..ad9b4123
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestInit.c
@@ -0,0 +1,77 @@
+/* $Id: bs3-cmn-TestInit.c $ */
+/** @file
+ * BS3Kit - Bs3TestInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+/**
+ * Equivalent to RTTestCreate + RTTestBanner.
+ *
+ * @param pszTest The test name.
+ */
+#undef Bs3TestInit
+BS3_CMN_DEF(void, Bs3TestInit,(const char BS3_FAR *pszTest))
+{
+ /*
+ * Initialize the globals.
+ */
+ BS3_CMN_NM(g_pszBs3Test) = pszTest;
+ g_szBs3SubTest[0] = '\0';
+ g_cusBs3TestErrors = 0;
+ g_cusBs3SubTestAtErrors = 0;
+ g_fbBs3SubTestReported = true;
+ g_fbBs3SubTestSkipped = false;
+ g_cusBs3SubTests = 0;
+ g_cusBs3SubTestsFailed = 0;
+ g_fbBs3VMMDevTesting = bs3TestIsVmmDevTestingPresent();
+
+ /*
+ * Print the name - RTTestBanner.
+ */
+ Bs3PrintStr(pszTest);
+ Bs3PrintStr(": TESTING...\n");
+
+ /*
+ * Report it to the VMMDev.
+ */
+ bs3TestSendCmdWithStr(VMMDEV_TESTING_CMD_INIT, pszTest);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestIsVmmDevTestingPresent.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestIsVmmDevTestingPresent.asm
new file mode 100644
index 00000000..08c089db
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestIsVmmDevTestingPresent.asm
@@ -0,0 +1,78 @@
+; $Id: bs3-cmn-TestIsVmmDevTestingPresent.asm $
+;; @file
+; BS3Kit - bs3TestIsVmmDevTestingPresent
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(bool) bs3TestIsVmmDevTestingPresent_c16(void);
+;
+BS3_PROC_BEGIN_CMN bs3TestIsVmmDevTestingPresent, BS3_PBC_HYBRID_0_ARGS
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xDX
+
+ ; Check the response from the NOP port.
+ mov dx, VMMDEV_TESTING_IOPORT_NOP
+ cmp byte [g_uBs3CpuDetected], BS3CPU_80386
+ jb .ancient_cpu
+ in eax, dx
+ cmp eax, VMMDEV_TESTING_NOP_RET
+.set_ax_and_return:
+ mov ax, 0
+ jne .return
+ mov ax, 1
+
+.return:
+ pop xDX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+
+.ancient_cpu:
+ in ax, dx
+ cmp ax, (VMMDEV_TESTING_NOP_RET & 0xffff)
+ jmp .set_ax_and_return
+BS3_PROC_END_CMN bs3TestIsVmmDevTestingPresent
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestNow.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestNow.asm
new file mode 100644
index 00000000..c5ab98fe
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestNow.asm
@@ -0,0 +1,113 @@
+; $Id: bs3-cmn-TestNow.asm $
+;; @file
+; BS3Kit - Bs3TestNow.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+BS3_EXTERN_DATA16 g_fbBs3VMMDevTesting
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(uint64_t) Bs3TestNow(void);
+;
+; @uses eflags, return register(s)
+;
+BS3_PROC_BEGIN_CMN Bs3TestNow, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 0
+ push xBP
+ mov xBP, xSP
+%if __BITS__ == 16
+BONLY16 push sAX
+%else
+ push xCX
+BONLY64 push xDX
+%endif
+
+ cmp byte [BS3_DATA16_WRT(g_fbBs3VMMDevTesting)], 0
+ je .no_vmmdev
+
+ ; Read the lower timestamp.
+ mov dx, VMMDEV_TESTING_IOPORT_TS_LOW
+ in eax, dx
+%if __BITS__ == 16
+ mov bx, ax ; Save the first word in BX (returned in DX).
+ shr eax, 16
+ mov cx, ax ; The second word is returned in CX.
+%else
+ mov ecx, eax
+%endif
+
+ ; Read the high timestamp (latached in above read).
+ mov dx, VMMDEV_TESTING_IOPORT_TS_HIGH
+ in eax, dx
+%if __BITS__ == 16
+ mov dx, bx ; The first word is returned in DX.
+ mov bx, ax ; The third word is returned in BX.
+ shr eax, 16 ; The fourth word is returned in AX.
+%elif __BITS__ == 32
+ mov edx, eax
+ mov eax, ecx
+%else
+ shl rax, 32
+ or rax, rcx
+%endif
+
+.return:
+%if __BITS__ == 16
+ mov [bp - sCB], ax ; Update the AX part of the saved EAX.
+ pop sAX
+%else
+ pop xCX
+BONLY64 pop xDX
+%endif
+ pop xBP
+ BS3_CALL_CONV_EPILOG 0
+ BS3_HYBRID_RET
+
+.no_vmmdev:
+ ; No fallback, just zero the result.
+%if __BITS__ == 16
+ xor ax, ax
+ xor bx, bx
+ xor cx, cx
+ xor dx, dx
+%else
+ xor eax, eax
+BONLY32 xor edx, edx
+%endif
+ jmp .return
+BS3_PROC_END_CMN Bs3TestNow
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestPrintf.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestPrintf.c
new file mode 100644
index 00000000..4b7df136
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestPrintf.c
@@ -0,0 +1,146 @@
+/* $Id: bs3-cmn-TestPrintf.c $ */
+/** @file
+ * BS3Kit - BS3TestPrintf, BS3TestPrintfV
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SMALL_BUFFER 1
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Output buffering for Bs3TestPrintfV. */
+typedef struct BS3TESTPRINTBUF
+{
+ bool fNewCmd;
+#if SMALL_BUFFER
+ uint8_t cchBuf;
+ char achBuf[78];
+#else
+ uint16_t cchBuf;
+ char achBuf[512];
+#endif
+} BS3TESTPRINTBUF;
+
+
+/**
+ * @callback_method_impl{FNBS3STRFORMATOUTPUT, Prints to screen and VMMDev}
+ */
+static BS3_DECL_CALLBACK(size_t) bs3TestPrintfStrOutput(char ch, void BS3_FAR *pvUser)
+{
+ BS3TESTPRINTBUF BS3_FAR *pBuf = (BS3TESTPRINTBUF BS3_FAR *)pvUser;
+
+ /*
+ * VMMDev first. We do line by line processing to avoid running out of
+ * string buffer on the host side.
+ */
+ if (g_fbBs3VMMDevTesting)
+ {
+ if (ch != '\n' && !pBuf->fNewCmd)
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, ch);
+ else if (ch != '\0')
+ {
+ if (pBuf->fNewCmd)
+ {
+#if ARCH_BITS == 16
+ ASMOutU16(VMMDEV_TESTING_IOPORT_CMD, (uint16_t)VMMDEV_TESTING_CMD_PRINT);
+#else
+ ASMOutU32(VMMDEV_TESTING_IOPORT_CMD, VMMDEV_TESTING_CMD_PRINT);
+#endif
+ pBuf->fNewCmd = false;
+ }
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, ch);
+ if (ch == '\n')
+ {
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, '\0');
+ pBuf->fNewCmd = true;
+ }
+ }
+ }
+
+ /*
+ * Console next.
+ */
+ if (ch != '\0')
+ {
+ BS3_ASSERT(pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf));
+ pBuf->achBuf[pBuf->cchBuf++] = ch;
+
+ /* Whether to flush the buffer. We do line flushing here to avoid
+ dropping too much info when the formatter crashes on bad input. */
+ if ( pBuf->cchBuf < RT_ELEMENTS(pBuf->achBuf)
+ && (!SMALL_BUFFER || ch != '\n') )
+ return 1;
+ }
+ BS3_ASSERT(pBuf->cchBuf <= RT_ELEMENTS(pBuf->achBuf));
+ Bs3PrintStrN(&pBuf->achBuf[0], pBuf->cchBuf);
+ pBuf->cchBuf = 0;
+ return ch != '\0';
+}
+
+
+
+#undef Bs3TestPrintfV
+BS3_CMN_DEF(void, Bs3TestPrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va))
+{
+ BS3TESTPRINTBUF Buf;
+ Buf.fNewCmd = true;
+ Buf.cchBuf = 0;
+ Bs3StrFormatV(pszFormat, va, bs3TestPrintfStrOutput, &Buf);
+}
+
+
+
+#undef Bs3TestPrintf
+BS3_CMN_DEF(void, Bs3TestPrintf,(const char BS3_FAR *pszFormat, ...))
+{
+ va_list va;
+ va_start(va, pszFormat);
+ BS3_CMN_NM(Bs3TestPrintfV)(pszFormat, va);
+ va_end(va);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU32.asm
new file mode 100644
index 00000000..953b1e6d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU32.asm
@@ -0,0 +1,94 @@
+; $Id: bs3-cmn-TestQueryCfgU32.asm $
+;; @file
+; BS3Kit - Bs3TestQueryCfgU8.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+BS3_EXTERN_DATA16 g_fbBs3VMMDevTesting
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3TestQueryCfgU32(uint16_t uCfg);
+;
+BS3_PROC_BEGIN_CMN Bs3TestQueryCfgU32, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+TNOT16 push xDX
+
+ cmp byte [BS3_DATA16_WRT(g_fbBs3VMMDevTesting)], 0
+ je .no_vmmdev
+
+ ; Issue the query command.
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+%if TMPL_BITS == 16
+ mov ax, VMMDEV_TESTING_CMD_QUERY_CFG - VMMDEV_TESTING_CMD_MAGIC_HI_WORD
+ out dx, ax
+%else
+ mov eax, VMMDEV_TESTING_CMD_QUERY_CFG
+ out dx, eax
+%endif
+
+ ; Write what we wish to query.
+ mov ax, [xBP + xCB + cbCurRetAddr]
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ out dx, ax
+
+ ; Read back the result.
+%if TMPL_BITS == 16
+ in ax, dx
+ push ax
+ in ax, dx
+ mov dx, ax
+ pop ax
+%else
+ in eax, dx
+%endif
+
+.return:
+TNOT16 pop xDX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+
+.no_vmmdev:
+ xor xAX, xAX
+%if TMPL_BITS == 16
+ xor xDX, xDX
+%endif
+ jmp .return
+BS3_PROC_END_CMN Bs3TestQueryCfgU32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU8.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU8.asm
new file mode 100644
index 00000000..ea4aab19
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestQueryCfgU8.asm
@@ -0,0 +1,86 @@
+; $Id: bs3-cmn-TestQueryCfgU8.asm $
+;; @file
+; BS3Kit - Bs3TestQueryCfgU8, Bs3TestQueryCfgBool.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+BS3_EXTERN_DATA16 g_fbBs3VMMDevTesting
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestQueryCfgU8(uint16_t uCfg);
+; @cproto BS3_DECL(bool) Bs3TestQueryCfgBool(uint16_t uCfg);
+;
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(Bs3TestQueryCfgBool), function, 3
+BS3_PROC_BEGIN_CMN Bs3TestQueryCfgU8, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xDX
+
+ xor al, al
+ cmp byte [BS3_DATA16_WRT(g_fbBs3VMMDevTesting)], 0
+ je .no_vmmdev
+
+ ; Issue the query command.
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+%if TMPL_BITS == 16
+ mov ax, VMMDEV_TESTING_CMD_QUERY_CFG - VMMDEV_TESTING_CMD_MAGIC_HI_WORD
+ out dx, ax
+%else
+ mov eax, VMMDEV_TESTING_CMD_QUERY_CFG
+ out dx, eax
+%endif
+
+ ; Write what we wish to query.
+ mov ax, [xBP + xCB + cbCurRetAddr]
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+ out dx, ax
+
+ ; Read back the result.
+ in al, dx
+
+.no_vmmdev:
+ pop xDX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3TestQueryCfgU8
+
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX BS3_CMN_NM_FAR(Bs3TestQueryCfgBool), function, 3
+ jmp BS3_CMN_NM_FAR(Bs3TestQueryCfgU8)
+%endif
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithStr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithStr.asm
new file mode 100644
index 00000000..4f61abc6
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithStr.asm
@@ -0,0 +1,90 @@
+; $Id: bs3-cmn-TestSendCmdWithStr.asm $
+;; @file
+; BS3Kit - bs3TestSendStrCmd.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+BS3_EXTERN_DATA16 g_fbBs3VMMDevTesting
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(void) bs3TestSendCmdWithStr_c16(uint32_t uCmd, const char BS3_FAR *pszString);
+;
+BS3_PROC_BEGIN_CMN bs3TestSendCmdWithStr, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xDX
+ push xSI
+BONLY16 push ds
+
+ cmp byte [BS3_DATA16_WRT(g_fbBs3VMMDevTesting)], 0
+ je .no_vmmdev
+
+ ; The command (uCmd).
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr] ; We ignore the top bits in 16-bit mode.
+ out dx, ax
+%else
+ mov eax, [xBP + xCB + cbCurRetAddr]
+ out dx, eax
+%endif
+
+ ; The string.
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+%if TMPL_BITS == 16
+ lds si, [xBP + xCB + cbCurRetAddr + sCB]
+%else
+ mov xSI, [xBP + xCB + cbCurRetAddr + sCB]
+%endif
+.next_char:
+ lodsb
+ out dx, al
+ test al, al
+ jnz .next_char
+
+.no_vmmdev:
+BONLY16 pop ds
+ pop xSI
+ pop xDX
+ pop xAX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN bs3TestSendCmdWithStr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithU32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithU32.asm
new file mode 100644
index 00000000..4062c1dc
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSendCmdWithU32.asm
@@ -0,0 +1,88 @@
+; $Id: bs3-cmn-TestSendCmdWithU32.asm $
+;; @file
+; BS3Kit - bs3TestSendCmdWithU32.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+%include "VBox/VMMDevTesting.mac"
+
+BS3_EXTERN_DATA16 g_fbBs3VMMDevTesting
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(void) bs3TestSendCmdWithU32_c16(uint32_t uCmd, uint32_t uValue);
+;
+BS3_PROC_BEGIN_CMN bs3TestSendCmdWithU32, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xDX
+ push xSI
+
+ cmp byte [BS3_DATA16_WRT(g_fbBs3VMMDevTesting)], 0
+ je .no_vmmdev
+
+ ; The command (uCmd) -
+ mov dx, VMMDEV_TESTING_IOPORT_CMD
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr] ; We ignore the top bits in 16-bit mode.
+ out dx, ax
+%else
+ mov eax, [xBP + xCB*2]
+ out dx, eax
+%endif
+
+
+ ; The value (uValue).
+ mov dx, VMMDEV_TESTING_IOPORT_DATA
+%if TMPL_BITS == 16
+ mov ax, [xBP + xCB + cbCurRetAddr + sCB]
+ out dx, ax
+ mov ax, [xBP + xCB + cbCurRetAddr + sCB + 2]
+ out dx, ax
+%else
+ mov eax, [xBP + xCB*2 + sCB]
+ out dx, eax
+%endif
+
+.no_vmmdev:
+ pop xSI
+ pop xDX
+ pop xAX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 2
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN bs3TestSendCmdWithU32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSkipped.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSkipped.c
new file mode 100644
index 00000000..4d7f0ec3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSkipped.c
@@ -0,0 +1,100 @@
+/* $Id: bs3-cmn-TestSkipped.c $ */
+/** @file
+ * BS3Kit - Bs3TestSkipped
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+/**
+ * Equivalent to RTTestSkippedV.
+ */
+#undef Bs3TestSkippedV
+BS3_CMN_DEF(void, Bs3TestSkippedV,(const char *pszFormat, va_list BS3_FAR va))
+{
+ if (g_cusBs3TestErrors == g_cusBs3SubTestAtErrors)
+ {
+ /* Just mark it as skipped and deal with it when the sub-test is done. */
+ g_fbBs3SubTestSkipped = true;
+
+ /* Tell VMMDev */
+ if (g_fbBs3VMMDevTesting)
+#if ARCH_BITS == 16
+ ASMOutU16(VMMDEV_TESTING_IOPORT_CMD, (uint16_t)VMMDEV_TESTING_CMD_SKIPPED);
+#else
+ ASMOutU32(VMMDEV_TESTING_IOPORT_CMD, VMMDEV_TESTING_CMD_SKIPPED);
+#endif
+
+ /* The reason why it was skipped is optional. */
+ if (pszFormat)
+ {
+ BS3TESTFAILEDBUF Buf;
+ Buf.fNewLine = false;
+ Buf.cchBuf = 0;
+ Bs3StrFormatV(pszFormat, va, bs3TestFailedStrOutput, &Buf);
+ }
+ else if (g_fbBs3VMMDevTesting)
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, 0);
+ }
+}
+
+
+/**
+ * Equivalent to RTTestSkipped.
+ */
+#undef Bs3TestSkippedF
+BS3_CMN_DEF(void, Bs3TestSkippedF,(const char *pszFormat, ...))
+{
+ va_list va;
+ va_start(va, pszFormat);
+ BS3_CMN_NM(Bs3TestSkippedV)(pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Equivalent to RTTestSkipped.
+ */
+#undef Bs3TestSkipped
+BS3_CMN_DEF(void, Bs3TestSkipped,(const char *pszWhy))
+{
+ BS3_CMN_NM(Bs3TestSkippedF)(pszWhy ? "%s" : NULL, pszWhy);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSub.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSub.c
new file mode 100644
index 00000000..0f76b5b3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSub.c
@@ -0,0 +1,105 @@
+/* $Id: bs3-cmn-TestSub.c $ */
+/** @file
+ * BS3Kit - Bs3TestSub, Bs3TestSubF, Bs3TestSubV.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+
+/**
+ * Equivalent to RTTestISubV.
+ */
+#undef Bs3TestSubV
+BS3_CMN_DEF(void, Bs3TestSubV,(const char *pszFormat, va_list BS3_FAR va))
+{
+ size_t cch;
+
+ /*
+ * Cleanup any previous sub-test.
+ */
+ bs3TestSubCleanup();
+
+ /*
+ * Format the sub-test name and update globals.
+ */
+ cch = Bs3StrPrintfV(g_szBs3SubTest, sizeof(g_szBs3SubTest), pszFormat, va);
+ g_cusBs3SubTestAtErrors = g_cusBs3TestErrors;
+ BS3_ASSERT(!g_fbBs3SubTestSkipped);
+ g_cusBs3SubTests++;
+
+ /*
+ * Tell VMMDev and output to the console.
+ */
+ bs3TestSendCmdWithStr(VMMDEV_TESTING_CMD_SUB_NEW, g_szBs3SubTest);
+
+ Bs3PrintStr(g_szBs3SubTest);
+ Bs3PrintChr(':');
+ do
+ Bs3PrintChr(' ');
+ while (cch++ < 48);
+ Bs3PrintStr(" TESTING\n");
+
+ /* The sub-test result is not yet reported. */
+ g_fbBs3SubTestReported = false;
+}
+
+
+/**
+ * Equivalent to RTTestIFailedF.
+ */
+#undef Bs3TestSubF
+BS3_CMN_DEF(void, Bs3TestSubF,(const char *pszFormat, ...))
+{
+ va_list va;
+ va_start(va, pszFormat);
+ BS3_CMN_NM(Bs3TestSubV)(pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Equivalent to RTTestISub.
+ */
+#undef Bs3TestSub
+BS3_CMN_DEF(void, Bs3TestSub,(const char *pszMessage))
+{
+ BS3_CMN_NM(Bs3TestSubF)("%s", pszMessage);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubDone.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubDone.c
new file mode 100644
index 00000000..e49d298d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubDone.c
@@ -0,0 +1,54 @@
+/* $Id: bs3-cmn-TestSubDone.c $ */
+/** @file
+ * BS3Kit - Bs3TestSubDone.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+
+/**
+ * Equivalent to RTTestISubDone.
+ */
+#undef Bs3TestSubDone
+BS3_CMN_DEF(void, Bs3TestSubDone,(void))
+{
+ bs3TestSubCleanup();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubErrorCount.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubErrorCount.c
new file mode 100644
index 00000000..27d54f75
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestSubErrorCount.c
@@ -0,0 +1,54 @@
+/* $Id: bs3-cmn-TestSubErrorCount.c $ */
+/** @file
+ * BS3Kit - Bs3TestSubErrorCount.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+
+/**
+ * Equivalent to RTTestSubErrorCount.
+ */
+#undef Bs3TestSubErrorCount
+BS3_CMN_DEF(uint16_t, Bs3TestSubErrorCount,(void))
+{
+ return g_cusBs3TestErrors - g_cusBs3SubTestAtErrors;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestTerm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestTerm.c
new file mode 100644
index 00000000..1c2519ec
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestTerm.c
@@ -0,0 +1,117 @@
+/* $Id: bs3-cmn-TestTerm.c $ */
+/** @file
+ * BS3Kit - Bs3TestTerm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+
+
+/**
+ * Equivalent to rtTestSubCleanup + rtTestSubTestReport.
+ */
+BS3_DECL(void) bs3TestSubCleanup(void)
+{
+ if (g_szBs3SubTest[0] != '\0')
+ {
+ if (!g_fbBs3SubTestReported)
+ {
+ size_t cch;
+ uint16_t cErrors = g_cusBs3TestErrors - g_cusBs3SubTestAtErrors;
+
+ /* Tell VMMDev. */
+ bs3TestSendCmdWithU32(VMMDEV_TESTING_CMD_SUB_DONE, cErrors);
+
+ /* Print result to the console. */
+ Bs3PrintStr(g_szBs3SubTest);
+ Bs3PrintChr(':');
+ cch = Bs3StrLen(g_szBs3SubTest);
+ do
+ Bs3PrintChr(' ');
+ while (cch++ < 49);
+
+ if (!cErrors)
+ Bs3PrintStr(!g_fbBs3SubTestSkipped ? "PASSED\n" : "SKIPPED\n");
+ else
+ {
+ g_cusBs3SubTestsFailed++;
+ Bs3Printf("FAILED (%u errors)\n", g_szBs3SubTest, cErrors);
+ }
+ }
+
+ /* Reset the sub-test. */
+ g_fbBs3SubTestReported = true;
+ g_fbBs3SubTestSkipped = false;
+ g_szBs3SubTest[0] = '\0';
+ }
+}
+
+
+/**
+ * Equivalent to RTTestSummaryAndDestroy.
+ */
+#undef Bs3TestTerm
+BS3_CMN_DEF(void, Bs3TestTerm,(void))
+{
+ /*
+ * Close any current sub-test.
+ */
+ bs3TestSubCleanup();
+
+ /*
+ * Report summary.
+ */
+ if (BS3_CMN_NM(g_pszBs3Test))
+ {
+ Bs3PrintStr(BS3_CMN_NM(g_pszBs3Test));
+ if (g_cusBs3TestErrors == 0)
+ Bs3Printf(": SUCCESS (%u tests)\n", g_cusBs3SubTests);
+ else
+ Bs3Printf(": FAILURE - %u (%u of %u tests)\n",
+ g_cusBs3TestErrors, g_cusBs3SubTestsFailed, g_cusBs3SubTests);
+ }
+
+ /*
+ * Tell VMMDev.
+ */
+ bs3TestSendCmdWithU32(VMMDEV_TESTING_CMD_TERM, g_cusBs3TestErrors);
+
+ BS3_CMN_NM(g_pszBs3Test) = NULL;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestValue.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestValue.c
new file mode 100644
index 00000000..ad98aaab
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TestValue.c
@@ -0,0 +1,86 @@
+/* $Id: bs3-cmn-TestValue.c $ */
+/** @file
+ * BS3Kit - Bs3TestValue
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3TestValue
+BS3_CMN_DEF(void, Bs3TestValue,(const char BS3_FAR *pszName, uint64_t u64Value, uint8_t bUnit))
+{
+ const char * const pszUnit = g_aszBs3TestUnitNames[bUnit];
+ Bs3Printf(" %-48s: %'16llu %s\n", pszName, u64Value, pszUnit);
+
+ /*
+ * Report it to the host.
+ */
+ if (g_fbBs3VMMDevTesting)
+ {
+#if ARCH_BITS == 16
+ ASMOutU16(VMMDEV_TESTING_IOPORT_CMD, (uint16_t)VMMDEV_TESTING_CMD_VALUE);
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, (uint16_t)u64Value);
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, (uint16_t)(u64Value >> 16));
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, (uint16_t)(u64Value >> 32));
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, (uint16_t)(u64Value >> 48));
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, (uint16_t)bUnit);
+ ASMOutU16(VMMDEV_TESTING_IOPORT_DATA, 0);
+# if 1
+ ASMOutStrU8(VMMDEV_TESTING_IOPORT_DATA, pszName, Bs3StrLen(pszName) + 1);
+# else
+ for (;;)
+ {
+ uint8_t const b = *pszName++;
+ ASMOutU8(VMMDEV_TESTING_IOPORT_DATA, b);
+ if (!b)
+ break;
+ }
+# endif
+#else
+ ASMOutU32(VMMDEV_TESTING_IOPORT_CMD, VMMDEV_TESTING_CMD_VALUE);
+ ASMOutU32(VMMDEV_TESTING_IOPORT_DATA, (uint32_t)u64Value);
+ ASMOutU32(VMMDEV_TESTING_IOPORT_DATA, (uint32_t)(u64Value >> 32));
+ ASMOutU32(VMMDEV_TESTING_IOPORT_DATA, (uint32_t)bUnit);
+ ASMOutStrU8(VMMDEV_TESTING_IOPORT_DATA, pszName, Bs3StrLen(pszName) + 1);
+#endif
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16Init.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16Init.c
new file mode 100644
index 00000000..be43260e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16Init.c
@@ -0,0 +1,129 @@
+/* $Id: bs3-cmn-Trap16Init.c $ */
+/** @file
+ * BS3Kit - Bs3Trap16Init
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* We ASSUME that BS3CLASS16CODE is 64KB aligned, so the low 16-bit of the
+ flat address matches. Also, these symbols are defined both with
+ and without underscore prefixes. */
+extern BS3_DECL(void) BS3_FAR_CODE Bs3Trap16DoubleFaultHandler80386(void);
+extern BS3_DECL(void) BS3_FAR_CODE Bs3Trap16DoubleFaultHandler80286(void);
+extern BS3_DECL(void) BS3_FAR_CODE Bs3Trap16GenericEntries(void);
+
+/* These two are ugly. Need data access for patching purposes. */
+extern uint8_t BS3_FAR_DATA bs3Trap16GenericTrapOrInt[];
+
+
+#undef Bs3Trap16InitEx
+BS3_CMN_DEF(void, Bs3Trap16InitEx,(bool f386Plus))
+{
+ X86TSS16 BS3_FAR *pTss;
+ unsigned iIdt;
+
+ /*
+ * If 386 or later, patch the trap handler code to not jump to the 80286
+ * code but continue with the next instruction (the 386+ code).
+ */
+ if (f386Plus)
+ {
+ uint8_t BS3_FAR_DATA *pbFunction = &bs3Trap16GenericTrapOrInt[0];
+#if ARCH_BITS == 16
+ if (g_bBs3CurrentMode != BS3_MODE_RM)
+ pbFunction = (uint8_t BS3_FAR_DATA *)BS3_FP_MAKE(BS3_SEL_TILED + 1, BS3_FP_OFF(pbFunction));
+#endif
+ pbFunction[1] = 0;
+ pbFunction[2] = 0;
+ }
+
+ /*
+ * IDT entries, except the system call gate.
+ */
+ for (iIdt = 0; iIdt < 256; iIdt++)
+ if (iIdt != BS3_TRAP_SYSCALL)
+ Bs3Trap16SetGate(iIdt, X86_SEL_TYPE_SYS_286_INT_GATE, 0 /*bDpl*/,
+ BS3_SEL_R0_CS16, (uint16_t)(uintptr_t)Bs3Trap16GenericEntries + iIdt * 8, 0 /*cParams*/);
+
+ /*
+ * Initialize the normal TSS so we can do ring transitions via the IDT.
+ */
+ pTss = &Bs3Tss16;
+ Bs3MemZero(pTss, sizeof(*pTss));
+ pTss->sp0 = BS3_ADDR_STACK_R0;
+ pTss->ss0 = BS3_SEL_R0_SS16;
+ pTss->sp1 = BS3_ADDR_STACK_R1;
+ pTss->ss1 = BS3_SEL_R1_SS16 | 1;
+ pTss->sp2 = BS3_ADDR_STACK_R2;
+ pTss->ss2 = BS3_SEL_R2_SS16 | 2;
+
+ /*
+ * Initialize the double fault TSS.
+ * cr3 is filled in by switcher code, when needed.
+ */
+ pTss = &Bs3Tss16DoubleFault;
+ Bs3MemZero(pTss, sizeof(*pTss));
+ pTss->sp0 = BS3_ADDR_STACK_R0;
+ pTss->ss0 = BS3_SEL_R0_SS16;
+ pTss->sp1 = BS3_ADDR_STACK_R1;
+ pTss->ss1 = BS3_SEL_R1_SS16 | 1;
+ pTss->sp2 = BS3_ADDR_STACK_R2;
+ pTss->ss2 = BS3_SEL_R2_SS16 | 2;
+ pTss->ip = (uint16_t)(uintptr_t)(f386Plus ? &Bs3Trap16DoubleFaultHandler80386 : &Bs3Trap16DoubleFaultHandler80286);
+ pTss->flags = X86_EFL_1;
+ pTss->sp = BS3_ADDR_STACK_R0_IST1;
+ pTss->es = BS3_SEL_R0_DS16;
+ pTss->ds = BS3_SEL_R0_DS16;
+ pTss->cs = BS3_SEL_R0_CS16;
+ pTss->ss = BS3_SEL_R0_SS16;
+ pTss->dx = f386Plus;
+
+ Bs3Trap16SetGate(X86_XCPT_DF, X86_SEL_TYPE_SYS_TASK_GATE, 0 /*bDpl*/, BS3_SEL_TSS16_DF, 0, 0 /*cParams*/);
+}
+
+
+#undef Bs3Trap16Init
+BS3_CMN_DEF(void, Bs3Trap16Init,(void))
+{
+ BS3_CMN_NM(Bs3Trap16InitEx)((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16SetGate.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16SetGate.c
new file mode 100644
index 00000000..1f70a2a5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap16SetGate.c
@@ -0,0 +1,62 @@
+/* $Id: bs3-cmn-Trap16SetGate.c $ */
+/** @file
+ * BS3Kit - Bs3Trap16SetGate
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3Trap16SetGate
+BS3_CMN_DEF(void, Bs3Trap16SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint16_t off, uint8_t cParams))
+{
+ X86DESC BS3_FAR *pIdte = &Bs3Idt16[iIdt];
+
+ BS3_ASSERT(bDpl <= 3);
+ BS3_ASSERT(bType <= 15);
+ BS3_ASSERT(cParams <= 15);
+ pIdte->Gate.u16OffsetLow = (uint16_t)off;
+ pIdte->Gate.u16OffsetHigh = 0;
+ pIdte->Gate.u16Sel = uSel;
+ pIdte->Gate.u5ParmCount = cParams;
+ pIdte->Gate.u4Type = bType;
+ pIdte->Gate.u2Dpl = bDpl;
+ pIdte->Gate.u3Reserved = 0;
+ pIdte->Gate.u1DescType = 0; /* system */
+ pIdte->Gate.u1Present = 1;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32Init.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32Init.c
new file mode 100644
index 00000000..3cf1ec82
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32Init.c
@@ -0,0 +1,101 @@
+/* $Id: bs3-cmn-Trap32Init.c $ */
+/** @file
+ * BS3Kit - Bs3Trap32Init
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+#define g_Bs3Trap32DoubleFaultHandlerFlatAddr BS3_DATA_NM(g_Bs3Trap32DoubleFaultHandlerFlatAddr)
+extern uint32_t g_Bs3Trap32DoubleFaultHandlerFlatAddr;
+
+
+#undef Bs3Trap32Init
+BS3_CMN_DEF(void, Bs3Trap32Init,(void))
+{
+ X86TSS32 BS3_FAR *pTss;
+ unsigned iIdt;
+
+ /*
+ * IDT entries, except the system call gate.
+ */
+ for (iIdt = 0; iIdt < BS3_TRAP_SYSCALL; iIdt++)
+ Bs3Trap32SetGate(iIdt, X86_SEL_TYPE_SYS_386_INT_GATE, 0 /*bDpl*/,
+ BS3_SEL_R0_CS32, g_Bs3Trap32GenericEntriesFlatAddr + iIdt * 10, 0 /*cParams*/);
+ for (iIdt = BS3_TRAP_SYSCALL + 1; iIdt < 256; iIdt++)
+ Bs3Trap32SetGate(iIdt, X86_SEL_TYPE_SYS_386_INT_GATE, 0 /*bDpl*/,
+ BS3_SEL_R0_CS32, g_Bs3Trap32GenericEntriesFlatAddr + iIdt * 10, 0 /*cParams*/);
+
+ /*
+ * Initialize the normal TSS so we can do ring transitions via the IDT.
+ */
+ pTss = &Bs3Tss32;
+ Bs3MemZero(pTss, sizeof(*pTss));
+ pTss->esp0 = BS3_ADDR_STACK_R0;
+ pTss->ss0 = BS3_SEL_R0_SS32;
+ pTss->esp1 = BS3_ADDR_STACK_R1;
+ pTss->ss1 = BS3_SEL_R1_SS32 | 1;
+ pTss->esp2 = BS3_ADDR_STACK_R2;
+ pTss->ss2 = BS3_SEL_R2_SS32 | 2;
+
+ /*
+ * Initialize the double fault TSS.
+ * cr3 is filled in by switcher code, when needed.
+ */
+ pTss = &Bs3Tss32DoubleFault;
+ Bs3MemZero(pTss, sizeof(*pTss));
+ pTss->esp0 = BS3_ADDR_STACK_R0;
+ pTss->ss0 = BS3_SEL_R0_SS32;
+ pTss->esp1 = BS3_ADDR_STACK_R1;
+ pTss->ss1 = BS3_SEL_R1_SS32 | 1;
+ pTss->esp2 = BS3_ADDR_STACK_R2;
+ pTss->ss2 = BS3_SEL_R2_SS32 | 2;
+ pTss->eip = g_Bs3Trap32DoubleFaultHandlerFlatAddr;
+ pTss->eflags = X86_EFL_1;
+ pTss->esp = BS3_ADDR_STACK_R0_IST1;
+ pTss->es = BS3_SEL_R0_DS32;
+ pTss->ds = BS3_SEL_R0_DS32;
+ pTss->cs = BS3_SEL_R0_CS32;
+ pTss->ss = BS3_SEL_R0_SS32;
+
+ Bs3Trap32SetGate(X86_XCPT_DF, X86_SEL_TYPE_SYS_TASK_GATE, 0 /*bDpl*/, BS3_SEL_TSS32_DF, 0, 0 /*cParams*/);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32SetGate.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32SetGate.c
new file mode 100644
index 00000000..2501ea17
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap32SetGate.c
@@ -0,0 +1,62 @@
+/* $Id: bs3-cmn-Trap32SetGate.c $ */
+/** @file
+ * BS3Kit - Bs3Trap32SetGate
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3Trap32SetGate
+BS3_CMN_DEF(void, Bs3Trap32SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint32_t off, uint8_t cParams))
+{
+ X86DESC BS3_FAR *pIdte = &Bs3Idt32[iIdt];
+
+ BS3_ASSERT(bDpl <= 3);
+ BS3_ASSERT(bType <= 15);
+ BS3_ASSERT(cParams <= 15);
+ pIdte->Gate.u16OffsetLow = (uint16_t)off;
+ pIdte->Gate.u16OffsetHigh = (uint16_t)(off >> 16);
+ pIdte->Gate.u16Sel = uSel;
+ pIdte->Gate.u5ParmCount = cParams;
+ pIdte->Gate.u4Type = bType;
+ pIdte->Gate.u2Dpl = bDpl;
+ pIdte->Gate.u3Reserved = 0;
+ pIdte->Gate.u1DescType = 0; /* system */
+ pIdte->Gate.u1Present = 1;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64Init.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64Init.c
new file mode 100644
index 00000000..98aad56d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64Init.c
@@ -0,0 +1,109 @@
+/* $Id: bs3-cmn-Trap64Init.c $ */
+/** @file
+ * BS3Kit - Bs3Trap64Init
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3Trap64Init
+BS3_CMN_DEF(void, Bs3Trap64Init,(void))
+{
+ Bs3Trap64InitEx(false);
+}
+
+#undef Bs3Trap64InitEx
+BS3_CMN_DEF(void, Bs3Trap64InitEx,(bool fMoreIstUsage))
+{
+ static const uint8_t s_aAssignments[] =
+ {
+ /* [X86_XCPT_DE] = */ 3,
+ /* [X86_XCPT_DB] = */ 2,
+ /* [X86_XCPT_NMI] = */ 0,
+ /* [X86_XCPT_BP] = */ 2,
+ /* [X86_XCPT_OF] = */ 3,
+ /* [X86_XCPT_BR] = */ 3,
+ /* [X86_XCPT_UD] = */ 4,
+ /* [X86_XCPT_NM] = */ 3,
+ /* [X86_XCPT_DF] = */ 1,
+ /* [0x09] = */ 0,
+ /* [X86_XCPT_TS] = */ 1,
+ /* [X86_XCPT_NP] = */ 5,
+ /* [X86_XCPT_SS] = */ 5,
+ /* [X86_XCPT_GP] = */ 6,
+ /* [X86_XCPT_PF] = */ 7,
+ /* [0x0f] = */ 0,
+ /* [X86_XCPT_MF] = */ 0,
+ /* [X86_XCPT_AC] = */ 3,
+ /* [X86_XCPT_MC] = */ 0,
+ /* [X86_XCPT_XF] = */ 0,
+ /* [X86_XCPT_VE] = */ 0,
+ /* [X86_XCPT_CP] = */ 6,
+ };
+ X86TSS64 BS3_FAR *pTss;
+ unsigned iIdt;
+
+ /*
+ * IDT entries, except the system call gate.
+ * The #DF entry get IST=1, all others IST=0.
+ */
+ for (iIdt = 0; iIdt < BS3_TRAP_SYSCALL; iIdt++)
+ Bs3Trap64SetGate(iIdt, AMD64_SEL_TYPE_SYS_INT_GATE, 0 /*bDpl*/,
+ BS3_SEL_R0_CS64, g_Bs3Trap64GenericEntriesFlatAddr + iIdt * 8,
+ !fMoreIstUsage ? iIdt == X86_XCPT_DF : iIdt < RT_ELEMENTS(s_aAssignments) ? s_aAssignments[iIdt] : 0);
+ for (iIdt = BS3_TRAP_SYSCALL + 1; iIdt < 256; iIdt++)
+ Bs3Trap64SetGate(iIdt, AMD64_SEL_TYPE_SYS_INT_GATE, 0 /*bDpl*/,
+ BS3_SEL_R0_CS64, g_Bs3Trap64GenericEntriesFlatAddr + iIdt * 8, 0);
+
+ /*
+ * Initialize the normal TSS so we can do ring transitions via the IDT.
+ */
+ pTss = &Bs3Tss64;
+ Bs3MemZero(pTss, sizeof(*pTss));
+ pTss->rsp0 = BS3_ADDR_STACK_R0;
+ pTss->rsp1 = BS3_ADDR_STACK_R1;
+ pTss->rsp2 = BS3_ADDR_STACK_R2;
+ pTss->ist1 = BS3_ADDR_STACK_R0_IST1;
+ pTss->ist2 = BS3_ADDR_STACK_R0_IST2;
+ pTss->ist3 = BS3_ADDR_STACK_R0_IST3;
+ pTss->ist4 = BS3_ADDR_STACK_R0_IST4;
+ pTss->ist5 = BS3_ADDR_STACK_R0_IST5;
+ pTss->ist6 = BS3_ADDR_STACK_R0_IST6;
+ pTss->ist7 = BS3_ADDR_STACK_R0_IST7;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64SetGate.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64SetGate.c
new file mode 100644
index 00000000..6ca6d960
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-Trap64SetGate.c
@@ -0,0 +1,65 @@
+/* $Id: bs3-cmn-Trap64SetGate.c $ */
+/** @file
+ * BS3Kit - Bs3Trap64SetGate
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3Trap64SetGate
+BS3_CMN_DEF(void, Bs3Trap64SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint64_t off, uint8_t bIst))
+{
+ X86DESC64 BS3_FAR *pIdte = &Bs3Idt64[iIdt];
+
+ BS3_ASSERT(bDpl <= 3);
+ BS3_ASSERT(bType <= 15);
+ BS3_ASSERT(bIst <= 7);
+ pIdte->Gate.u16OffsetLow = (uint16_t)off;
+ pIdte->Gate.u16OffsetHigh = (uint16_t)((uint32_t)off >> 16);
+ pIdte->Gate.u32OffsetTop = (uint32_t)(off >> 32);
+ pIdte->Gate.u16Sel = uSel;
+ pIdte->Gate.u3IST = bIst;
+ pIdte->Gate.u4Type = bType;
+ pIdte->Gate.u2Dpl = bDpl;
+ pIdte->Gate.u5Reserved = 0;
+ pIdte->Gate.u1DescType = 0; /* system */
+ pIdte->Gate.u1Present = 1;
+ pIdte->Gate.u32Reserved = 0;
+
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapDefaultHandler.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapDefaultHandler.c
new file mode 100644
index 00000000..76a5bdbd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapDefaultHandler.c
@@ -0,0 +1,331 @@
+/* $Id: bs3-cmn-TrapDefaultHandler.c $ */
+/** @file
+ * BS3Kit - Bs3TrapDefaultHandler
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#if TMPL_BITS != 64
+# include <VBox/VMMDevTesting.h>
+# include <iprt/asm-amd64-x86.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#define g_pBs3TrapSetJmpFrame BS3_DATA_NM(g_pBs3TrapSetJmpFrame)
+extern uint32_t g_pBs3TrapSetJmpFrame;
+
+#define g_Bs3TrapSetJmpCtx BS3_DATA_NM(g_Bs3TrapSetJmpCtx)
+extern BS3REGCTX g_Bs3TrapSetJmpCtx;
+
+
+#if TMPL_BITS != 64
+/**
+ * V86 syscall handler.
+ */
+static void bs3TrapDefaultHandlerV8086Syscall(PBS3TRAPFRAME pTrapFrame)
+{
+ /* Minimal syscall. */
+ uint16_t uSyscallNo = pTrapFrame->Ctx.rax.u16;
+ if (uSyscallNo == BS3_SYSCALL_PRINT_CHR)
+ Bs3PrintChr(pTrapFrame->Ctx.rcx.u8);
+ else if (uSyscallNo == BS3_SYSCALL_PRINT_STR)
+ Bs3PrintStrN(Bs3XptrFlatToCurrent(((uint32_t)pTrapFrame->Ctx.rcx.u16 << 4) + pTrapFrame->Ctx.rsi.u16),
+ pTrapFrame->Ctx.rdx.u16);
+ else if (uSyscallNo == BS3_SYSCALL_RESTORE_CTX)
+ Bs3RegCtxRestore(Bs3XptrFlatToCurrent(((uint32_t)pTrapFrame->Ctx.rcx.u16 << 4) + pTrapFrame->Ctx.rsi.u16),
+ pTrapFrame->Ctx.rdx.u16);
+ else if ( uSyscallNo == BS3_SYSCALL_TO_RING0
+ || uSyscallNo == BS3_SYSCALL_TO_RING1
+ || uSyscallNo == BS3_SYSCALL_TO_RING2
+ || uSyscallNo == BS3_SYSCALL_TO_RING3)
+ {
+ Bs3RegCtxConvertToRingX(&pTrapFrame->Ctx, pTrapFrame->Ctx.rax.u16 - BS3_SYSCALL_TO_RING0);
+ }
+ else if (uSyscallNo == BS3_SYSCALL_SET_DRX)
+ {
+ uint32_t uValue = pTrapFrame->Ctx.rsi.u32;
+ switch (pTrapFrame->Ctx.rdx.u8)
+ {
+ case 0: ASMSetDR0(uValue); break;
+ case 1: ASMSetDR1(uValue); break;
+ case 2: ASMSetDR2(uValue); break;
+ case 3: ASMSetDR3(uValue); break;
+ case 6: ASMSetDR6(uValue); break;
+ case 7: ASMSetDR7(uValue); break;
+ default: Bs3Panic();
+ }
+ }
+ else if (uSyscallNo == BS3_SYSCALL_GET_DRX)
+ {
+ uint32_t uValue;
+ switch (pTrapFrame->Ctx.rdx.u8)
+ {
+ case 0: uValue = ASMGetDR0(); break;
+ case 1: uValue = ASMGetDR1(); break;
+ case 2: uValue = ASMGetDR2(); break;
+ case 3: uValue = ASMGetDR3(); break;
+ case 6: uValue = ASMGetDR6(); break;
+ case 7: uValue = ASMGetDR7(); break;
+ default: uValue = 0; Bs3Panic();
+ }
+ pTrapFrame->Ctx.rax.u32 = uValue;
+ pTrapFrame->Ctx.rdx.u32 = uValue >> 16;
+ }
+ else if (uSyscallNo == BS3_SYSCALL_SET_CRX)
+ {
+ uint32_t uValue = pTrapFrame->Ctx.rsi.u32;
+ switch (pTrapFrame->Ctx.rdx.u8)
+ {
+ case 0: ASMSetCR0(uValue); pTrapFrame->Ctx.cr0.u32 = uValue; break;
+ case 2: ASMSetCR2(uValue); pTrapFrame->Ctx.cr2.u32 = uValue; break;
+ case 3: ASMSetCR3(uValue); pTrapFrame->Ctx.cr3.u32 = uValue; break;
+ case 4: ASMSetCR4(uValue); pTrapFrame->Ctx.cr4.u32 = uValue; break;
+ default: Bs3Panic();
+ }
+ }
+ else if (uSyscallNo == BS3_SYSCALL_GET_CRX)
+ {
+ uint32_t uValue;
+ switch (pTrapFrame->Ctx.rdx.u8)
+ {
+ case 0: uValue = ASMGetCR0(); break;
+ case 2: uValue = ASMGetCR2(); break;
+ case 3: uValue = ASMGetCR3(); break;
+ case 4: uValue = ASMGetCR4(); break;
+ default: uValue = 0; Bs3Panic();
+ }
+ pTrapFrame->Ctx.rax.u32 = uValue;
+ pTrapFrame->Ctx.rdx.u32 = uValue >> 16;
+ }
+ else if (uSyscallNo == BS3_SYSCALL_SET_TR)
+ {
+ Bs3RegSetTr(pTrapFrame->Ctx.rdx.u16);
+ pTrapFrame->Ctx.tr = pTrapFrame->Ctx.rdx.u16;
+ }
+ else if (uSyscallNo == BS3_SYSCALL_GET_TR)
+ pTrapFrame->Ctx.rax.u16 = ASMGetTR();
+ else if (uSyscallNo == BS3_SYSCALL_SET_LDTR)
+ {
+ Bs3RegSetLdtr(pTrapFrame->Ctx.rdx.u16);
+ pTrapFrame->Ctx.ldtr = pTrapFrame->Ctx.rdx.u16;
+ }
+ else if (uSyscallNo == BS3_SYSCALL_GET_LDTR)
+ pTrapFrame->Ctx.rax.u16 = ASMGetLDTR();
+ else if (uSyscallNo == BS3_SYSCALL_SET_XCR0)
+ ASMSetXcr0(RT_MAKE_U64(pTrapFrame->Ctx.rsi.u32, pTrapFrame->Ctx.rdx.u32));
+ else if (uSyscallNo == BS3_SYSCALL_GET_XCR0)
+ {
+ uint64_t const uValue = ASMGetXcr0();
+ pTrapFrame->Ctx.rax.u32 = (uint32_t)uValue;
+ pTrapFrame->Ctx.rdx.u32 = (uint32_t)(uValue >> 32);
+ }
+ else
+ Bs3Panic();
+}
+#endif
+
+#undef Bs3TrapDefaultHandler
+BS3_CMN_DEF(void, Bs3TrapDefaultHandler,(PBS3TRAPFRAME pTrapFrame))
+{
+#if TMPL_BITS != 64
+ /*
+ * v8086 VMM tasks.
+ */
+ //Bs3TestHostPrintf("Bs3____DefaultHandler: %02xh %04RX16:%08RX32 %08RX32 %04RX16:%08RX32 %d %d\n", pTrapFrame->bXcpt,
+ // pTrapFrame->Ctx.cs, pTrapFrame->Ctx.rip.u32, pTrapFrame->Ctx.rflags.u32, pTrapFrame->Ctx.ss,
+ // pTrapFrame->Ctx.rsp.u32, g_fBs3TrapNoV86Assist, 42);
+ if ((pTrapFrame->Ctx.rflags.u32 & X86_EFL_VM))
+ {
+ bool fHandled = true;
+ uint8_t cBitsOpcode = 16;
+ uint8_t bOpCode;
+ uint8_t const BS3_FAR *pbCodeStart;
+ uint8_t const BS3_FAR *pbCode;
+ uint16_t BS3_FAR *pusStack;
+
+ pusStack = (uint16_t BS3_FAR *)BS3_MAKE_PROT_R0PTR_FROM_REAL(pTrapFrame->Ctx.ss, pTrapFrame->Ctx.rsp.u16);
+ pbCode = (uint8_t const BS3_FAR *)BS3_MAKE_PROT_R0PTR_FROM_REAL(pTrapFrame->Ctx.cs, pTrapFrame->Ctx.rip.u16);
+ pbCodeStart = pbCode;
+
+ /*
+ * Deal with GPs in V8086 mode.
+ */
+ if ( pTrapFrame->bXcpt == X86_XCPT_GP
+ && !g_fBs3TrapNoV86Assist)
+ {
+ bOpCode = *pbCode++;
+ if (bOpCode == 0x66)
+ {
+ cBitsOpcode = 32;
+ bOpCode = *pbCode++;
+ }
+
+ /* INT xx: Real mode behaviour, but intercepting and implementing most of our syscall interface. */
+ if (bOpCode == 0xcd)
+ {
+ uint8_t bVector = *pbCode++;
+ if (bVector == BS3_TRAP_SYSCALL)
+ bs3TrapDefaultHandlerV8086Syscall(pTrapFrame);
+ else
+ {
+ /* Real mode behaviour. */
+ uint16_t BS3_FAR *pusIvte = (uint16_t BS3_FAR *)BS3_MAKE_PROT_R0PTR_FROM_REAL(0, 0);
+ pusIvte += (uint16_t)bVector *2;
+
+ pusStack[0] = pTrapFrame->Ctx.rflags.u16;
+ pusStack[1] = pTrapFrame->Ctx.cs;
+ pusStack[2] = pTrapFrame->Ctx.rip.u16 + (uint16_t)(pbCode - pbCodeStart);
+
+ pTrapFrame->Ctx.rip.u16 = pusIvte[0];
+ pTrapFrame->Ctx.cs = pusIvte[1];
+ pTrapFrame->Ctx.rflags.u16 &= ~X86_EFL_IF; /** @todo this isn't all, but it'll do for now, I hope. */
+ Bs3RegCtxRestore(&pTrapFrame->Ctx, 0/*fFlags*/); /* does not return. */
+ }
+ }
+ /* PUSHF: Real mode behaviour. */
+ else if (bOpCode == 0x9c)
+ {
+ if (cBitsOpcode == 32)
+ *pusStack++ = pTrapFrame->Ctx.rflags.au16[1] & ~(X86_EFL_VM | X86_EFL_RF);
+ *pusStack++ = pTrapFrame->Ctx.rflags.u16;
+ pTrapFrame->Ctx.rsp.u16 += cBitsOpcode / 8;
+ }
+ /* POPF: Real mode behaviour. */
+ else if (bOpCode == 0x9d)
+ {
+ if (cBitsOpcode == 32)
+ {
+ pTrapFrame->Ctx.rflags.u32 &= ~X86_EFL_POPF_BITS;
+ pTrapFrame->Ctx.rflags.u32 |= X86_EFL_POPF_BITS & *(uint32_t const *)pusStack;
+ }
+ else
+ {
+ pTrapFrame->Ctx.rflags.u32 &= ~(X86_EFL_POPF_BITS | UINT32_C(0xffff0000)) & ~X86_EFL_RF;
+ pTrapFrame->Ctx.rflags.u16 |= (uint16_t)X86_EFL_POPF_BITS & *pusStack;
+ }
+ pTrapFrame->Ctx.rsp.u16 -= cBitsOpcode / 8;
+ }
+ /* CLI: Real mode behaviour. */
+ else if (bOpCode == 0xfa)
+ pTrapFrame->Ctx.rflags.u16 &= ~X86_EFL_IF;
+ /* STI: Real mode behaviour. */
+ else if (bOpCode == 0xfb)
+ pTrapFrame->Ctx.rflags.u16 |= X86_EFL_IF;
+ /* OUT: byte I/O to VMMDev. */
+ else if ( bOpCode == 0xee
+ && ((unsigned)(pTrapFrame->Ctx.rdx.u16 - VMMDEV_TESTING_IOPORT_BASE) < (unsigned)VMMDEV_TESTING_IOPORT_COUNT))
+ ASMOutU8(pTrapFrame->Ctx.rdx.u16, pTrapFrame->Ctx.rax.u8);
+ /* OUT: [d]word I/O to VMMDev. */
+ else if ( bOpCode == 0xef
+ && ((unsigned)(pTrapFrame->Ctx.rdx.u16 - VMMDEV_TESTING_IOPORT_BASE) < (unsigned)VMMDEV_TESTING_IOPORT_COUNT))
+ {
+ if (cBitsOpcode != 32)
+ ASMOutU16(pTrapFrame->Ctx.rdx.u16, pTrapFrame->Ctx.rax.u16);
+ else
+ ASMOutU32(pTrapFrame->Ctx.rdx.u16, pTrapFrame->Ctx.rax.u32);
+ }
+ /* IN: byte I/O to VMMDev. */
+ else if ( bOpCode == 0xec
+ && ((unsigned)(pTrapFrame->Ctx.rdx.u16 - VMMDEV_TESTING_IOPORT_BASE) < (unsigned)VMMDEV_TESTING_IOPORT_COUNT))
+ pTrapFrame->Ctx.rax.u8 = ASMInU8(pTrapFrame->Ctx.rdx.u16);
+ /* IN: [d]word I/O to VMMDev. */
+ else if ( bOpCode == 0xed
+ && ((unsigned)(pTrapFrame->Ctx.rdx.u16 - VMMDEV_TESTING_IOPORT_BASE) < (unsigned)VMMDEV_TESTING_IOPORT_COUNT))
+ {
+ if (cBitsOpcode != 32)
+ pTrapFrame->Ctx.rax.u16 = ASMInU16(pTrapFrame->Ctx.rdx.u16);
+ else
+ pTrapFrame->Ctx.rax.u32 = ASMInU32(pTrapFrame->Ctx.rdx.u32);
+ }
+ /* Unexpected. */
+ else
+ fHandled = false;
+ }
+ /*
+ * Deal with lock prefixed int xxh syscall in v8086 mode.
+ */
+ else if ( pTrapFrame->bXcpt == X86_XCPT_UD
+ && pTrapFrame->Ctx.cs == BS3_SEL_TEXT16
+ && pTrapFrame->Ctx.rax.u16 <= BS3_SYSCALL_LAST
+ && pbCode[0] == 0xf0
+ && pbCode[1] == 0xcd
+ && pbCode[2] == BS3_TRAP_SYSCALL)
+ {
+ pbCode += 3;
+ bs3TrapDefaultHandlerV8086Syscall(pTrapFrame);
+ }
+ else
+ {
+ fHandled = false;
+ }
+ if (fHandled)
+ {
+ pTrapFrame->Ctx.rip.u16 += (uint16_t)(pbCode - pbCodeStart);
+# if 0
+ Bs3Printf("Calling Bs3RegCtxRestore\n");
+ Bs3RegCtxPrint(&pTrapFrame->Ctx);
+# endif
+ Bs3RegCtxRestore(&pTrapFrame->Ctx, 0 /*fFlags*/); /* does not return. */
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Any pending setjmp?
+ */
+ if (g_pBs3TrapSetJmpFrame != 0)
+ {
+ PBS3TRAPFRAME pSetJmpFrame = (PBS3TRAPFRAME)Bs3XptrFlatToCurrent(g_pBs3TrapSetJmpFrame);
+ //Bs3Printf("Calling longjmp: pSetJmpFrame=%p (%#lx)\n", pSetJmpFrame, g_pBs3TrapSetJmpFrame);
+ g_pBs3TrapSetJmpFrame = 0;
+ Bs3MemCpy(pSetJmpFrame, pTrapFrame, sizeof(*pSetJmpFrame));
+ //Bs3RegCtxPrint(&g_Bs3TrapSetJmpCtx);
+ Bs3RegCtxRestore(&g_Bs3TrapSetJmpCtx, 0 /*fFlags*/);
+ }
+
+ /*
+ * Fatal.
+ */
+ Bs3TestPrintf("*** GURU ***\n");
+ Bs3TrapPrintFrame(pTrapFrame);
+ Bs3Panic();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapHandlersData.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapHandlersData.asm
new file mode 100644
index 00000000..89bfa799
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapHandlersData.asm
@@ -0,0 +1,52 @@
+; $Id: bs3-cmn-TrapHandlersData.asm $
+;; @file
+; BS3Kit - Per bit-count trap data.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+
+BS3_BEGIN_DATA16
+ BS3_SET_BITS ARCH_BITS ; Override the 16-bit mode that BS3_BEGIN_DATA16 implies so we get a correct xCB value.
+
+;; Pointer C trap handlers.
+;; Note! The 16-bit ones are all near so we can share them between real, v86 and prot mode.
+;; Note! Must be in 16-bit data because of BS3TrapSetHandlerEx.
+BS3_GLOBAL_NAME_EX BS3_CMN_NM(g_apfnBs3TrapHandlers), , 256 * xCB
+ resb (256 * xCB)
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapPrintFrame.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapPrintFrame.c
new file mode 100644
index 00000000..ab3af490
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapPrintFrame.c
@@ -0,0 +1,89 @@
+/* $Id: bs3-cmn-TrapPrintFrame.c $ */
+/** @file
+ * BS3Kit - Bs3TrapPrintFrame
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapPrintFrame
+BS3_CMN_DEF(void, Bs3TrapPrintFrame,(PCBS3TRAPFRAME pTrapFrame))
+{
+#if 1
+ Bs3TestPrintf("Trap %#04x errcd=%#06RX64 at %04x:%016RX64 - test step %d (%#x)\n"
+ "Handler: ss:rsp=%04x:%08RX64 cs=%04x cbIret=%#x rflags=%#06RX64\n"
+ ,
+ pTrapFrame->bXcpt,
+ pTrapFrame->uErrCd,
+ pTrapFrame->Ctx.cs,
+ pTrapFrame->Ctx.rip.u64,
+ g_usBs3TestStep, g_usBs3TestStep,
+ pTrapFrame->uHandlerSs,
+ pTrapFrame->uHandlerRsp,
+ pTrapFrame->uHandlerCs,
+ pTrapFrame->cbIretFrame,
+ pTrapFrame->fHandlerRfl);
+ Bs3RegCtxPrint(&pTrapFrame->Ctx);
+#else
+ /* This is useful if having trouble returning from real mode. */
+ PCBS3REGCTX pRegCtx = &pTrapFrame->Ctx;
+ Bs3TestPrintf("Trap %#04x errcd=%#06RX64 at %04x:%016RX64 - test step %d (%#x)\n"
+ "eax=%08RX32 ebx=%08RX32 ecx=%08RX32 edx=%08RX32 esi=%08RX32 edi=%08RX32\n"
+ "eip=%08RX32 esp=%08RX32 ebp=%08RX32 efl=%08RX32 cr0=%08RX32 cr2=%08RX32\n"
+ "cs=%04RX16 ds=%04RX16 es=%04RX16 fs=%04RX16 gs=%04RX16 ss=%04RX16 cr3=%08RX32 cr4=%08RX32\n"
+ "tr=%04RX16 ldtr=%04RX16 cpl=%d mode=%#x fbFlags=%#x\n"
+ ,
+ pTrapFrame->bXcpt,
+ pTrapFrame->uErrCd,
+ pTrapFrame->Ctx.cs,
+ pTrapFrame->Ctx.rip.u64,
+ g_usBs3TestStep, g_usBs3TestStep
+ ,
+ pRegCtx->rax.u32, pRegCtx->rbx.u32, pRegCtx->rcx.u32, pRegCtx->rdx.u32, pRegCtx->rsi.u32, pRegCtx->rdi.u32
+ ,
+ pRegCtx->rip.u32, pRegCtx->rsp.u32, pRegCtx->rbp.u32, pRegCtx->rflags.u32,
+ pRegCtx->cr0.u32, pRegCtx->cr2.u32
+ ,
+ pRegCtx->cs, pRegCtx->ds, pRegCtx->es, pRegCtx->fs, pRegCtx->gs, pRegCtx->ss,
+ pRegCtx->cr3.u32, pRegCtx->cr4.u32
+ ,
+ pRegCtx->tr, pRegCtx->ldtr, pRegCtx->bCpl, pRegCtx->bMode, pRegCtx->fbFlags);
+
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapReInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapReInit.c
new file mode 100644
index 00000000..879de0fb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapReInit.c
@@ -0,0 +1,65 @@
+/* $Id: bs3-cmn-TrapReInit.c $ */
+/** @file
+ * BS3Kit - Bs3TrapReInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapReInit
+BS3_CMN_DEF(void, Bs3TrapReInit,(void))
+{
+ if (BS3_MODE_IS_RM_SYS(g_bBs3CurrentMode))
+ Bs3TrapRmV86Init();
+ else if (BS3_MODE_IS_16BIT_SYS(g_bBs3CurrentMode))
+ {
+ Bs3TrapRmV86Init();
+ Bs3Trap16Init();
+ }
+ else if (BS3_MODE_IS_32BIT_SYS(g_bBs3CurrentMode))
+ {
+ Bs3TrapRmV86Init();
+ Bs3Trap32Init();
+ }
+ else
+ {
+ BS3_ASSERT(BS3_MODE_IS_64BIT_SYS(g_bBs3CurrentMode));
+ Bs3Trap64Init();
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86Init.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86Init.c
new file mode 100644
index 00000000..38dc3a5a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86Init.c
@@ -0,0 +1,122 @@
+/* $Id: bs3-cmn-TrapRmV86Init.c $ */
+/** @file
+ * BS3Kit - Bs3TrapRmV86Init
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* We ASSUME that BS3CLASS16CODE is 64KB aligned, so the low 16-bit of the
+ flat address matches. Also, these symbols are defined both with
+ and without underscore prefixes. */
+extern BS3_DECL(void) BS3_FAR_CODE Bs3TrapRmV86GenericEntries(void);
+
+/* These two are ugly. Need data access for patching purposes. */
+extern uint8_t BS3_FAR_DATA bs3TrapRmV86GenericTrapOrInt[];
+
+/* bs3-cmn-TrapRmV86Data.c: */
+#define g_fBs3RmIvtCopied BS3_DATA_NM(g_fBs3RmIvtCopied)
+extern bool g_fBs3RmIvtCopied;
+
+
+#undef Bs3TrapRmV86InitEx
+BS3_CMN_DEF(void, Bs3TrapRmV86InitEx,(bool f386Plus))
+{
+ RTFAR16 BS3_FAR *paIvt = Bs3XptrFlatToCurrent(0);
+ unsigned iIvt;
+
+ /*
+ * Copy the real mode IVT the first time we are here.
+ */
+ if (!g_fBs3RmIvtCopied)
+ {
+ Bs3MemCpy(g_aBs3RmIvtOriginal, paIvt, sizeof(g_aBs3RmIvtOriginal));
+ g_fBs3RmIvtCopied = true;
+ }
+ /*
+ * The rest of the times, we copy back the original and modify it.
+ */
+ else
+ Bs3MemCpy(paIvt, g_aBs3RmIvtOriginal, sizeof(g_aBs3RmIvtOriginal));
+
+
+ /*
+ * If 386 or later, patch the trap handler code to not jump to the 80286
+ * code but continue with the next instruction (the 386+ code).
+ */
+ if (f386Plus)
+ {
+ uint8_t BS3_FAR_DATA *pbFunction = &bs3TrapRmV86GenericTrapOrInt[0];
+#if ARCH_BITS == 16
+ if (g_bBs3CurrentMode != BS3_MODE_RM)
+ pbFunction = (uint8_t BS3_FAR_DATA *)BS3_FP_MAKE(BS3_SEL_TILED + 1, BS3_FP_OFF(pbFunction));
+#endif
+ pbFunction[1] = 0;
+ pbFunction[2] = 0;
+ }
+
+ /*
+ * Since we want to play with V86 mode as well as 8086 and 186 CPUs, we
+ * cannot move the IVT from its default location. So, modify it in place.
+ *
+ * Note! We must keep INT 10h working, which is easy since the CPU does
+ * use it (well, it's been reserved for 30+ years).
+ * Turns out we must not hook INT 6Dh either then, as some real VGA
+ * BIOS installs their INT 10h handler there as well, and seemingly
+ * must be using it internally or something.
+ *
+ * We also keep 15h working for memory interfaces (see bs3-mode-BiosInt15*).
+ */
+ for (iIvt = 0; iIvt < 256; iIvt++)
+ if (iIvt != 0x10 && iIvt != 0x15 && iIvt != 0x6d && iIvt != BS3_TRAP_SYSCALL)
+ {
+ paIvt[iIvt].off = (uint16_t)(uintptr_t)Bs3TrapRmV86GenericEntries + iIvt * 8;
+ paIvt[iIvt].sel = BS3_SEL_TEXT16;
+ }
+}
+
+
+#undef Bs3TrapRmV86Init
+BS3_CMN_DEF(void, Bs3TrapRmV86Init,(void))
+{
+ BS3_CMN_NM(Bs3TrapRmV86InitEx)((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86SetGate.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86SetGate.c
new file mode 100644
index 00000000..a8198298
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapRmV86SetGate.c
@@ -0,0 +1,51 @@
+/* $Id: bs3-cmn-TrapRmV86SetGate.c $ */
+/** @file
+ * BS3Kit - Bs3TrapRmV86SetGate
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapRmV86SetGate
+BS3_CMN_DEF(void, Bs3TrapRmV86SetGate,(uint8_t iIvt, uint16_t uSeg, uint16_t off))
+{
+ RTFAR16 BS3_FAR *paIvt = Bs3XptrFlatToCurrent(0);
+ paIvt[iIvt].off = off;
+ paIvt[iIvt].sel = uSeg;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetDpl.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetDpl.c
new file mode 100644
index 00000000..9038ce7e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetDpl.c
@@ -0,0 +1,57 @@
+/* $Id: bs3-cmn-TrapSetDpl.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetDpl
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapSetDpl
+BS3_CMN_DEF(uint8_t, Bs3TrapSetDpl,(uint8_t iIdt, uint8_t bDpl))
+{
+ uint8_t bRet;
+ BS3_ASSERT(bDpl <= 3);
+
+ Bs3Idt16[iIdt].Gate.u2Dpl = bDpl;
+ Bs3Idt32[iIdt].Gate.u2Dpl = bDpl;
+ bRet = Bs3Idt64[iIdt].Gate.u2Dpl;
+ Bs3Idt64[iIdt].Gate.u2Dpl = bDpl;
+
+ return bRet;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandler.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandler.c
new file mode 100644
index 00000000..1f4956d0
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandler.c
@@ -0,0 +1,57 @@
+/* $Id: bs3-cmn-TrapSetHandler.c $ */
+/** @file
+ * BS3Kit - Bs3Trap32SetHandler
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern PFNBS3TRAPHANDLER BS3_DATA_NM(BS3_CMN_NM(g_apfnBs3TrapHandlers))[256];
+
+
+#undef Bs3TrapSetHandler
+BS3_CMN_DEF(PFNBS3TRAPHANDLER, Bs3TrapSetHandler,(uint8_t iIdt, PFNBS3TRAPHANDLER pfnHandler))
+{
+ PFNBS3TRAPHANDLER pfnOld = BS3_DATA_NM(BS3_CMN_NM(g_apfnBs3TrapHandlers))[iIdt];
+ BS3_DATA_NM(BS3_CMN_NM(g_apfnBs3TrapHandlers))[iIdt] = pfnHandler;
+ return pfnOld;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandlerEx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandlerEx.c
new file mode 100644
index 00000000..a80c380e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetHandlerEx.c
@@ -0,0 +1,71 @@
+/* $Id: bs3-cmn-TrapSetHandlerEx.c $ */
+/** @file
+ * BS3Kit - Bs3Trap32SetHandlerEx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern uint16_t BS3_DATA_NM(g_apfnBs3TrapHandlers_c16)[256];
+extern uint32_t BS3_DATA_NM(g_apfnBs3TrapHandlers_c32)[256];
+extern uint64_t BS3_DATA_NM(g_apfnBs3TrapHandlers_c64)[256];
+
+
+#undef Bs3TrapSetHandlerEx
+BS3_CMN_DEF(void, Bs3TrapSetHandlerEx,(uint8_t iIdt, PFNBS3TRAPHANDLER16 pfnHandler16,
+ PFNBS3TRAPHANDLER32 pfnHandler32, PFNBS3TRAPHANDLER64 pfnHandler64))
+{
+ RTCCUINTREG fFlags = ASMIntDisableFlags();
+#if ARCH_BITS == 16
+ /* Far real mode pointers as input. */
+ g_apfnBs3TrapHandlers_c16[iIdt] = (uint16_t)pfnHandler16;
+ g_apfnBs3TrapHandlers_c32[iIdt] = Bs3SelRealModeCodeToFlat((PFNBS3FARADDRCONV)pfnHandler32);
+ g_apfnBs3TrapHandlers_c64[iIdt] = Bs3SelRealModeCodeToFlat((PFNBS3FARADDRCONV)pfnHandler64);
+#else
+ /* Flat pointers as input. */
+ g_apfnBs3TrapHandlers_c16[iIdt] = (uint16_t)Bs3SelFlatCodeToProtFar16((uintptr_t)pfnHandler16);
+ g_apfnBs3TrapHandlers_c32[iIdt] = (uint32_t)(uintptr_t)pfnHandler32;
+ g_apfnBs3TrapHandlers_c64[iIdt] = (uintptr_t)pfnHandler64;
+#endif
+ ASMSetFlags(fFlags);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmp.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmp.asm
new file mode 100644
index 00000000..495f77d1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmp.asm
@@ -0,0 +1,143 @@
+; $Id: bs3-cmn-TrapSetJmp.asm $
+;; @file
+; BS3Kit - Bs3TrapSetJmp.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_CMN Bs3RegCtxSave
+%if TMPL_BITS == 16
+BS3_EXTERN_CMN_FAR Bs3SelFar32ToFlat32
+%endif
+BS3_EXTERN_DATA16 g_Bs3TrapSetJmpCtx
+BS3_EXTERN_DATA16 g_pBs3TrapSetJmpFrame
+TMPL_BEGIN_TEXT
+
+
+;;
+; Sets up a one-shot set-jmp-on-trap.
+;
+; @uses See, applicable C calling convention.
+;
+BS3_PROC_BEGIN_CMN Bs3TrapSetJmp, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ push xBX
+BONLY64 sub xSP, 20h
+
+ ;
+ ; Save the current register context.
+ ;
+BONLY16 push ds
+ BS3_LEA_MOV_WRT_RIP(xAX, BS3_DATA16_WRT(g_Bs3TrapSetJmpCtx))
+ push xAX
+ BS3_CALL Bs3RegCtxSave, 1
+ add xSP, sCB
+
+ ;
+ ; Adjust the return context a little.
+ ;
+ BS3_LEA_MOV_WRT_RIP(xBX, BS3_DATA16_WRT(g_Bs3TrapSetJmpCtx))
+ mov xAX, [xBP + xCB] ; The return address of this function
+ mov [xBX + BS3REGCTX.rip], xAX
+%if TMPL_BITS == 16
+ mov xAX, [xBP + xCB+2] ; The return address CS of this function.
+ mov [xBX + BS3REGCTX.cs], xAX
+%endif
+ mov xAX, [xBP]
+ mov [xBX + BS3REGCTX.rbp], xAX
+ lea xAX, [xBP + xCB + cbCurRetAddr]
+ mov [xBX + BS3REGCTX.rsp], xAX
+ mov xAX, [xBP - xCB]
+ mov [xBX + BS3REGCTX.rbx], xAX
+ xor xAX, xAX
+ mov [xBX + BS3REGCTX.rax], xAX ; the return value.
+
+ ;
+ ; Fill the trap frame return structure.
+ ;
+ push xDI
+%if TMPL_BITS == 16
+ push es
+ les di, [xBP + xCB + cbCurRetAddr]
+ mov cx, BS3TRAPFRAME_size / 2
+ mov ax, 0faceh
+ rep stosw
+ pop es
+%else
+ mov xDI, [xBP + xCB*2]
+ mov ecx, BS3TRAPFRAME_size / 4
+ mov xAX, 0feedfaceh
+ rep stosd
+%endif
+ pop xDI
+
+ ;
+ ; Save the (flat) pointer to the trap frame return structure.
+ ;
+%if TMPL_BITS == 16
+ xor ax, ax
+ push word [xBP + xCB + cbCurRetAddr + 2]
+ push ax
+ push word [xBP + xCB + cbCurRetAddr]
+ push cs
+ call Bs3SelFar32ToFlat32
+ add sp, 6h
+ mov [BS3_DATA16_WRT(g_pBs3TrapSetJmpFrame)], ax
+ mov [2 + BS3_DATA16_WRT(g_pBs3TrapSetJmpFrame)], dx
+%else
+ mov xAX, [xBP + xCB*2]
+ mov [BS3_DATA16_WRT(g_pBs3TrapSetJmpFrame)], eax
+%endif
+
+ ;
+ ; Return 'true'.
+ ;
+ mov xAX, 1
+BONLY64 add xSP, 20h
+ pop xBX
+ pop xBP
+ BS3_CALL_CONV_EPILOG 1
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3TrapSetJmp
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestore.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestore.c
new file mode 100644
index 00000000..74c03371
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestore.c
@@ -0,0 +1,56 @@
+/* $Id: bs3-cmn-TrapSetJmpAndRestore.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetJmpAndRestore
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapSetJmpAndRestore
+BS3_CMN_DEF(void, Bs3TrapSetJmpAndRestore,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame))
+{
+ if (Bs3TrapSetJmp(pTrapFrame))
+ {
+#if TMPL_BITS == 32
+ g_uBs3TrapEipHint = pCtxRestore->rip.u32;
+#endif
+ Bs3RegCtxRestore(pCtxRestore, BS3REGCTXRESTORE_F_NO_V86_ASSIST);
+ }
+ g_fBs3TrapNoV86Assist = false;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreInRm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreInRm.c
new file mode 100644
index 00000000..bd8175c9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreInRm.c
@@ -0,0 +1,133 @@
+/* $Id: bs3-cmn-TrapSetJmpAndRestoreInRm.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetJmpAndRestoreInRm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+/* assembly helpers */
+BS3_MODE_PROTO_NOSB(void, Bs3TrapSetJmpAndRestoreInRmAsm, (uint32_t, uint32_t));
+
+
+#undef Bs3TrapSetJmpAndRestoreInRm
+BS3_CMN_DEF(void, Bs3TrapSetJmpAndRestoreInRm,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame))
+{
+#if TMPL_BITS == 16
+ if (g_bBs3CurrentMode == BS3_MODE_RM)
+ Bs3TrapSetJmpAndRestore(pCtxRestore, pTrapFrame);
+ else
+#endif
+ {
+ uint32_t const pfRealModeCtxRestore = Bs3SelFlatDataToRealMode(Bs3SelPtrToFlat((PBS3REGCTX)pCtxRestore));
+ uint32_t const pfRealModeTrapFrame = Bs3SelFlatDataToRealMode(Bs3SelPtrToFlat(pTrapFrame));
+
+#if TMPL_BITS == 16
+ switch (g_bBs3CurrentMode & BS3_MODE_SYS_MASK)
+ {
+ case BS3_MODE_SYS_PE16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pe16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PE32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pe32_16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PP16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pp16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PP32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pp32_16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PAE16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pae16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PAE32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pae32_16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_LM:
+ Bs3TrapSetJmpAndRestoreInRmAsm_lm16(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ default:
+ BS3_ASSERT(0);
+ }
+
+#elif TMPL_BITS == 32
+ switch (g_bBs3CurrentMode & BS3_MODE_SYS_MASK)
+ {
+ case BS3_MODE_SYS_PE16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pe16_32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PE32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pe32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PP16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pp16_32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PP32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pp32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PAE16:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pae16_32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_PAE32:
+ Bs3TrapSetJmpAndRestoreInRmAsm_pae32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ case BS3_MODE_SYS_LM:
+ Bs3TrapSetJmpAndRestoreInRmAsm_lm32(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ default:
+ BS3_ASSERT(0);
+ }
+
+#elif TMPL_BITS == 64
+ switch (g_bBs3CurrentMode & BS3_MODE_SYS_MASK)
+ {
+ case BS3_MODE_SYS_LM:
+ Bs3TrapSetJmpAndRestoreInRmAsm_lm64(pfRealModeCtxRestore, pfRealModeTrapFrame);
+ break;
+ default:
+ BS3_ASSERT(0);
+ }
+#else
+# error Bogus TMPL_BITS
+#endif
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c
new file mode 100644
index 00000000..1b9dae0e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-cmn-TrapSetJmpAndRestoreWithExtCtx.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetJmpAndRestoreWithExtCtx
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapSetJmpAndRestoreWithExtCtx
+BS3_CMN_DEF(void, Bs3TrapSetJmpAndRestoreWithExtCtx,(PCBS3REGCTX pCtxRestore, PCBS3EXTCTX pExtCtxRestore,
+ PBS3TRAPFRAME pTrapFrame, PBS3EXTCTX pExtCtxTrap))
+{
+ /* ASSUMES compile emits no SSE instructions between the calls here
+ (only a potential issue in 64-bit mode). */
+ Bs3ExtCtxRestoreEx(pExtCtxRestore);
+ if (Bs3TrapSetJmp(pTrapFrame))
+ {
+#if TMPL_BITS == 32
+ g_uBs3TrapEipHint = pCtxRestore->rip.u32;
+#endif
+ Bs3RegCtxRestore(pCtxRestore, BS3REGCTXRESTORE_F_NO_V86_ASSIST);
+ }
+ g_fBs3TrapNoV86Assist = false;
+ Bs3ExtCtxSaveEx(pExtCtxTrap);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c
new file mode 100644
index 00000000..95d038a2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c
@@ -0,0 +1,66 @@
+/* $Id: bs3-cmn-TrapSetJmpAndRestoreWithExtCtxAndRm.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetJmpAndRestoreWithExtCtxAndRm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapSetJmpAndRestoreWithExtCtxAndRm
+BS3_CMN_DEF(void, Bs3TrapSetJmpAndRestoreWithExtCtxAndRm,(PCBS3REGCTX pCtxRestore, PCBS3EXTCTX pExtCtxRestore,
+ PBS3TRAPFRAME pTrapFrame, PBS3EXTCTX pExtCtxTrap))
+{
+ if ( pCtxRestore->bMode != BS3_MODE_RM
+#if ARCH_BITS == 16
+ || g_bBs3CurrentMode == BS3_MODE_RM
+#endif
+ )
+ {
+ BS3_ASSERT((pCtxRestore->bMode & BS3_MODE_SYS_MASK) == (g_bBs3CurrentMode & BS3_MODE_SYS_MASK));
+ Bs3TrapSetJmpAndRestoreWithExtCtx(pCtxRestore, pExtCtxRestore, pTrapFrame, pExtCtxTrap);
+ }
+ else
+ {
+ /* ASSUMES compile emits no SSE instructions between the calls here
+ (only a potential issue in 64-bit mode). */
+ Bs3ExtCtxRestoreEx(pExtCtxRestore);
+ Bs3TrapSetJmpAndRestoreInRm(pCtxRestore, pTrapFrame);
+ Bs3ExtCtxSaveEx(pExtCtxTrap);
+ }
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithRm.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithRm.c
new file mode 100644
index 00000000..c059690e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapSetJmpAndRestoreWithRm.c
@@ -0,0 +1,59 @@
+/* $Id: bs3-cmn-TrapSetJmpAndRestoreWithRm.c $ */
+/** @file
+ * BS3Kit - Bs3TrapSetJmpAndRestoreWithRm
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapSetJmpAndRestoreWithRm
+BS3_CMN_DEF(void, Bs3TrapSetJmpAndRestoreWithRm,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame))
+{
+ if ( pCtxRestore->bMode != BS3_MODE_RM
+#if ARCH_BITS == 16
+ || g_bBs3CurrentMode == BS3_MODE_RM
+#endif
+ )
+ {
+ BS3_ASSERT((pCtxRestore->bMode & BS3_MODE_SYS_MASK) == (g_bBs3CurrentMode & BS3_MODE_SYS_MASK));
+ Bs3TrapSetJmpAndRestore(pCtxRestore, pTrapFrame);
+ }
+ else
+ Bs3TrapSetJmpAndRestoreInRm(pCtxRestore, pTrapFrame);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapUnsetJmp.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapUnsetJmp.c
new file mode 100644
index 00000000..8d26c905
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-TrapUnsetJmp.c
@@ -0,0 +1,58 @@
+/* $Id: bs3-cmn-TrapUnsetJmp.c $ */
+/** @file
+ * BS3Kit - Bs3TrapUnsetJmp
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifndef DOXYGEN_RUNNING
+# define g_pBs3TrapSetJmpFrame BS3_DATA_NM(g_pBs3TrapSetJmpFrame)
+#endif
+extern uint32_t g_pBs3TrapSetJmpFrame;
+
+
+#undef Bs3TrapUnsetJmp
+BS3_CMN_DEF(void, Bs3TrapUnsetJmp,(void))
+{
+ g_pBs3TrapSetJmpFrame = 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt32Div.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt32Div.c
new file mode 100644
index 00000000..73080a80
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt32Div.c
@@ -0,0 +1,50 @@
+/* $Id: bs3-cmn-UInt32Div.c $ */
+/** @file
+ * BS3Kit - Unsigned 32-bit division (compiler support routine helper).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/uint32.h>
+
+
+#undef Bs3UInt32Div
+BS3_CMN_DEF(void, Bs3UInt32Div,(RTUINT32U uDividend, RTUINT32U uDivisor, RTUINT32U BS3_FAR *paQuotientReminder))
+{
+ RTUInt32DivRem(&paQuotientReminder[0], &paQuotientReminder[1], &uDividend, &uDivisor);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt64Div.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt64Div.c
new file mode 100644
index 00000000..6d705209
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UInt64Div.c
@@ -0,0 +1,50 @@
+/* $Id: bs3-cmn-UInt64Div.c $ */
+/** @file
+ * BS3Kit - Unsigned 64-bit division (compiler support routine helper).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <bs3kit.h>
+#include <iprt/uint64.h>
+
+
+#undef Bs3UInt64Div
+BS3_CMN_DEF(void, Bs3UInt64Div,(RTUINT64U uDividend, RTUINT64U uDivisor, RTUINT64U BS3_FAR *paQuotientReminder))
+{
+ RTUInt64DivRem(&paQuotientReminder[0], &paQuotientReminder[1], &uDividend, &uDivisor);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullGdtr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullGdtr.asm
new file mode 100644
index 00000000..235329cf
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullGdtr.asm
@@ -0,0 +1,195 @@
+; $Id: bs3-cmn-UtilSetFullGdtr.asm $
+;; @file
+; BS3Kit - Bs3UtilSetFullGdtr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(void, Bs3UtilSetFullGdtr,(uint16_t cbLimit, uint64_t uBase));
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses eax/rax; cbLimit on stack in 32-bit mode.
+;
+BS3_PROC_BEGIN_CMN Bs3UtilSetFullGdtr, BS3_PBC_HYBRID
+TONLY16 inc xBP
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 64
+ ;
+ ; It doesn't (currently) get any better than 64-bit mode.
+ ;
+ push rdx
+ mov rax, rcx
+ shl rax, 48
+ push rax
+ lgdt [rsp + 6]
+ add rsp, 10h
+
+
+%elif TMPL_BITS == 32
+ ;
+ ; Move the limit up two bytes so we can use it directly.
+ ;
+ shl dword [xBP + xCB + cbCurRetAddr], 16
+
+ ;
+ ; If the system is currently running in long mode, we have to switch to
+ ; it in order to do the job with the highest precision.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .do_64bit
+
+ ; 32-bit is the best we can do.
+.do_32bit:
+ lgdt [xBP + xCB + cbCurRetAddr + 2]
+ jmp .return
+
+ ; Must switch to long mode and do it there.
+.do_64bit:
+ jmp BS3_SEL_R0_CS64:.in_64bit wrt FLAT
+.in_64bit:
+ BS3_SET_BITS 64
+ lgdt [xSP + 4 + cbCurRetAddr + 2]
+ push BS3_SEL_R0_CS32
+ push .return wrt FLAT
+ o64 retf
+ BS3_SET_BITS 32
+
+
+%elif TMPL_BITS == 16
+ ;
+ ; All options are open here, we can be in any 16-bit mode,
+ ; including real mode.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ test al, BS3_MODE_CODE_V86
+ jnz .do_v8086
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .do_64bit
+ cmp al, BS3_MODE_SYS_RM
+ je .do_16bit
+ cmp byte [ BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
+ jae .do_32bit
+
+ ;
+ ; We're in real mode or in 16-bit protected mode on a 286.
+ ;
+.do_16bit: ;ba x 1 127f5
+ lgdt [xBP + xCB + cbCurRetAddr]
+ jmp .return
+
+ ;
+ ; We're in some kind of protected mode on a 386 or better.
+ ;
+.do_32bit:
+ jmp dword BS3_SEL_R0_CS32:.in_32bit wrt FLAT
+.in_32bit:
+ BS3_SET_BITS 32
+ lgdt [bp + 2 + cbCurRetAddr]
+ jmp BS3_SEL_R0_CS16:.return wrt CGROUP16
+ BS3_SET_BITS 16
+
+ ;
+ ; V8086 mode - need to switch to 32-bit kernel code to do stuff here.
+ ;
+.do_v8086:
+ BS3_EXTERN_CMN Bs3SwitchTo32Bit
+ call Bs3SwitchTo32Bit
+ BS3_SET_BITS 32
+
+ lgdt [xSP + 2 + cbCurRetAddr]
+
+ extern _Bs3SwitchTo16BitV86_c32
+ call _Bs3SwitchTo16BitV86_c32
+ BS3_SET_BITS 16
+ jmp .return
+
+ ;
+ ; System is in long mode, so we can switch to 64-bit mode and do the job there.
+ ;
+.do_64bit:
+ push edx ; save
+ push ss
+ push 0
+ push bp
+ BS3_EXTERN_CMN Bs3SelFar32ToFlat32NoClobber
+ call Bs3SelFar32ToFlat32NoClobber
+ add sp, 6
+ shl edx, 16
+ mov dx, ax
+ mov eax, edx ; eax = flattened ss:bp
+ pop edx ; restore
+ jmp dword BS3_SEL_R0_CS64:.in_64bit wrt FLAT
+.in_64bit:
+ BS3_SET_BITS 64
+ lgdt [rax + 2 + cbCurRetAddr]
+
+ push BS3_SEL_R0_CS16
+ push .return wrt CGROUP16
+ o64 retf
+ BS3_SET_BITS 16
+
+%else
+ %error "TMPL_BITS!"
+%endif
+
+.return:
+ pop xBP
+TONLY16 dec xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3UtilSetFullGdtr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullIdtr.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullIdtr.asm
new file mode 100644
index 00000000..d383e0ce
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-UtilSetFullIdtr.asm
@@ -0,0 +1,195 @@
+; $Id: bs3-cmn-UtilSetFullIdtr.asm $
+;; @file
+; BS3Kit - Bs3UtilSetFullIdtr
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%endif
+TMPL_BEGIN_TEXT
+
+
+
+;;
+; @cproto BS3_CMN_PROTO_NOSB(void, Bs3UtilSetFullIdtr,(uint16_t cbLimit, uint64_t uBase));
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+; @uses eax/rax; cbLimit on stack in 32-bit mode.
+;
+BS3_PROC_BEGIN_CMN Bs3UtilSetFullIdtr, BS3_PBC_HYBRID
+TONLY16 inc xBP
+ push xBP
+ mov xBP, xSP
+
+%if TMPL_BITS == 64
+ ;
+ ; It doesn't (currently) get any better than 64-bit mode.
+ ;
+ push rdx
+ mov rax, rcx
+ shl rax, 48
+ push rax
+ lidt [rsp + 6]
+ add rsp, 10h
+
+
+%elif TMPL_BITS == 32
+ ;
+ ; Move the limit up two bytes so we can use it directly.
+ ;
+ shl dword [xBP + xCB + cbCurRetAddr], 16
+
+ ;
+ ; If the system is currently running in long mode, we have to switch to
+ ; it in order to do the job with the highest precision.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .do_64bit
+
+ ; 32-bit is the best we can do.
+.do_32bit:
+ lidt [xBP + xCB + cbCurRetAddr + 2]
+ jmp .return
+
+ ; Must switch to long mode and do it there.
+.do_64bit:
+ jmp BS3_SEL_R0_CS64:.in_64bit wrt FLAT
+.in_64bit:
+ BS3_SET_BITS 64
+ lidt [xSP + 4 + cbCurRetAddr + 2]
+ push BS3_SEL_R0_CS32
+ push .return wrt FLAT
+ o64 retf
+ BS3_SET_BITS 32
+
+
+%elif TMPL_BITS == 16
+ ;
+ ; All options are open here, we can be in any 16-bit mode,
+ ; including real mode.
+ ;
+ mov al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ test al, BS3_MODE_CODE_V86
+ jnz .do_v8086
+ and al, BS3_MODE_SYS_MASK
+ cmp al, BS3_MODE_SYS_LM
+ je .do_64bit
+ cmp al, BS3_MODE_SYS_RM
+ je .do_16bit
+ cmp byte [ BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
+ jae .do_32bit
+
+ ;
+ ; We're in real mode or in 16-bit protected mode on a 286.
+ ;
+.do_16bit: ;ba x 1 127f5
+ lidt [xBP + xCB + cbCurRetAddr]
+ jmp .return
+
+ ;
+ ; We're in some kind of protected mode on a 386 or better.
+ ;
+.do_32bit:
+ jmp dword BS3_SEL_R0_CS32:.in_32bit wrt FLAT
+.in_32bit:
+ BS3_SET_BITS 32
+ lidt [bp + 2 + cbCurRetAddr]
+ jmp BS3_SEL_R0_CS16:.return wrt CGROUP16
+ BS3_SET_BITS 16
+
+ ;
+ ; V8086 mode - need to switch to 32-bit kernel code to do stuff here.
+ ;
+.do_v8086:
+ BS3_EXTERN_CMN Bs3SwitchTo32Bit
+ call Bs3SwitchTo32Bit
+ BS3_SET_BITS 32
+
+ lidt [xSP + 2 + cbCurRetAddr]
+
+ extern _Bs3SwitchTo16BitV86_c32
+ call _Bs3SwitchTo16BitV86_c32
+ BS3_SET_BITS 16
+ jmp .return
+
+ ;
+ ; System is in long mode, so we can switch to 64-bit mode and do the job there.
+ ;
+.do_64bit:
+ push edx ; save
+ push ss
+ push 0
+ push bp
+ BS3_EXTERN_CMN Bs3SelFar32ToFlat32NoClobber
+ call Bs3SelFar32ToFlat32NoClobber
+ add sp, 6
+ shl edx, 16
+ mov dx, ax
+ mov eax, edx ; eax = flattened ss:bp
+ pop edx ; restore
+ jmp dword BS3_SEL_R0_CS64:.in_64bit wrt FLAT
+.in_64bit:
+ BS3_SET_BITS 64
+ lidt [rax + 2 + cbCurRetAddr]
+
+ push BS3_SEL_R0_CS16
+ push .return wrt CGROUP16
+ o64 retf
+ BS3_SET_BITS 16
+
+%else
+ %error "TMPL_BITS!"
+%endif
+
+.return:
+ pop xBP
+TONLY16 dec xBP
+ BS3_HYBRID_RET
+BS3_PROC_END_CMN Bs3UtilSetFullIdtr
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-hexdigits.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-hexdigits.c
new file mode 100644
index 00000000..003394f8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-hexdigits.c
@@ -0,0 +1,42 @@
+/* $Id: bs3-cmn-hexdigits.c $ */
+/** @file
+ * BS3Kit - Hex digits constants.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit-template-header.h"
+
+
+char const g_achBs3HexDigits[16+1] = "0123456789abcdef";
+char const g_achBs3HexDigitsUpper[16+1] = "0123456789ABCDEF";
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-common.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-common.h
new file mode 100644
index 00000000..6a19cc9d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-common.h
@@ -0,0 +1,214 @@
+/* $Id: bs3-cmn-instantiate-common.h $ */
+/** @file
+ * BS3Kit - Common template instantiator body.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ * Instantiating common code (c16, c32, c64).
+ * This must be done first.
+ */
+
+/** @def BS3_INSTANTIATING_CMN
+ * @ingroup grp_bs3kit_tmpl
+ * Indicates that we're instantiating common code (c16, c32, c64).
+ */
+#define BS3_INSTANTIATING_CMN
+
+#ifdef BS3_CMN_INSTANTIATE_FILE1
+
+# define BS3_CMN_INSTANTIATE_FILE1_B <BS3_CMN_INSTANTIATE_FILE1>
+
+# if ARCH_BITS == 16 /* 16-bit - real mode. */
+# define TMPL_MODE BS3_MODE_RM
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_CMN_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+# endif
+
+# if ARCH_BITS == 32 /* 32-bit - paged protected mode. */
+# define TMPL_MODE BS3_MODE_PP32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_CMN_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+# endif
+
+# if ARCH_BITS == 64 /* 64-bit. */
+# define TMPL_MODE BS3_MODE_LM64
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_CMN_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+# endif
+
+#endif
+
+#undef BS3_INSTANTIATING_CMN
+
+
+/*
+ * Instantiating code for each individual mode (rm, pe16, pe16_32, ...).
+ */
+
+/** @def BS3_INSTANTIATING_MODE
+ * @ingroup grp_bs3kit_tmpl
+ * Indicates that we're instantiating mode specific code (rm, pe16, ...).
+ */
+#define BS3_INSTANTIATING_MODE
+
+#ifdef BS3_MODE_INSTANTIATE_FILE1
+
+# define BS3_MODE_INSTANTIATE_FILE1_B <BS3_MODE_INSTANTIATE_FILE1>
+
+# if ARCH_BITS == 16 /* 16-bit */
+
+# define TMPL_MODE BS3_MODE_RM
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PE16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PE16_V86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PE32_16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PEV86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PP16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PP16_V86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PP32_16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PPV86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAE16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAE16_V86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAE32_16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAEV86
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_LM16
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# endif
+
+# if ARCH_BITS == 32 /* 32-bit */
+
+# define TMPL_MODE BS3_MODE_PE16_32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PE32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PP16_32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PP32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAE16_32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_PAE32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# define TMPL_MODE BS3_MODE_LM32
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+
+# endif
+
+# if ARCH_BITS == 64 /* 64-bit. */
+# define TMPL_MODE BS3_MODE_LM64
+# include <bs3kit/bs3kit-template-header.h>
+# include BS3_MODE_INSTANTIATE_FILE1_B
+# include <bs3kit/bs3kit-template-footer.h>
+# endif
+
+#endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x0.c16 b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x0.c16
new file mode 100644
index 00000000..352b9be7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x0.c16
@@ -0,0 +1,39 @@
+/* $Id: bs3-cmn-instantiate-x0.c16 $ */
+/** @file
+ * BS3Kit - 16-bit common C template instantiator, using the BS3X0TEXT16 segment.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define BS3_USE_X0_TEXT_SEG 1
+#include "bs3-cmn-instantiate-common.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x1.c16 b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x1.c16
new file mode 100644
index 00000000..631a3673
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate-x1.c16
@@ -0,0 +1,39 @@
+/* $Id: bs3-cmn-instantiate-x1.c16 $ */
+/** @file
+ * BS3Kit - 16-bit common C template instantiator, using the BS3X1TEXT16 segment.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define BS3_USE_X1_TEXT_SEG 1
+#include "bs3-cmn-instantiate-common.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c16 b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c16
new file mode 100644
index 00000000..4c2bdee2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c16
@@ -0,0 +1,39 @@
+/* $Id: bs3-cmn-instantiate.c16 $ */
+/** @file
+ * BS3Kit - 16-bit common C template instantiator.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include "bs3-cmn-instantiate-common.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c32 b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c32
new file mode 100644
index 00000000..00988062
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c32
@@ -0,0 +1,39 @@
+/* $Id: bs3-cmn-instantiate.c32 $ */
+/** @file
+ * BS3Kit - 32-bit common C template instantiator.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include "bs3-cmn-instantiate-common.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c64 b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c64
new file mode 100644
index 00000000..efeb3f17
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-instantiate.c64
@@ -0,0 +1,39 @@
+/* $Id: bs3-cmn-instantiate.c64 $ */
+/** @file
+ * BS3Kit - 64-bit common C template instantiator.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include "bs3-cmn-instantiate-common.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-memory.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-memory.h
new file mode 100644
index 00000000..efcf459b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-memory.h
@@ -0,0 +1,109 @@
+/* $Id: bs3-cmn-memory.h $ */
+/** @file
+ * BS3Kit - Internal Memory Structures, Variables and Functions.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3_cmn_memory_h
+#define BS3KIT_INCLUDED_bs3_cmn_memory_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "bs3kit.h"
+#include <iprt/asm.h>
+
+RT_C_DECLS_BEGIN;
+
+
+typedef union BS3SLABCTLLOW
+{
+ BS3SLABCTL Core;
+ uint32_t au32Alloc[(sizeof(BS3SLABCTL) + (0xA0000 / _4K / 8) ) / 4];
+} BS3SLABCTLLOW;
+#ifndef DOXYGEN_RUNNING
+# define g_Bs3Mem4KLow BS3_DATA_NM(g_Bs3Mem4KLow)
+#endif
+extern BS3SLABCTLLOW g_Bs3Mem4KLow;
+
+
+typedef union BS3SLABCTLUPPERTILED
+{
+ BS3SLABCTL Core;
+ uint32_t au32Alloc[(sizeof(BS3SLABCTL) + ((BS3_SEL_TILED_AREA_SIZE - _1M) / _4K / 8) ) / 4];
+} BS3SLABCTLUPPERTILED;
+#ifndef DOXYGEN_RUNNING
+# define g_Bs3Mem4KUpperTiled BS3_DATA_NM(g_Bs3Mem4KUpperTiled)
+#endif
+extern BS3SLABCTLUPPERTILED g_Bs3Mem4KUpperTiled;
+
+
+/** The number of chunk sizes used by the slab list arrays
+ * (g_aBs3LowSlabLists, g_aBs3UpperTiledSlabLists, more?). */
+#define BS3_MEM_SLAB_LIST_COUNT 6
+
+#ifndef DOXYGEN_RUNNING
+# define g_aiBs3SlabListsByPowerOfTwo BS3_DATA_NM(g_aiBs3SlabListsByPowerOfTwo)
+# define g_acbBs3SlabLists BS3_DATA_NM(g_acbBs3SlabLists)
+# define g_aBs3LowSlabLists BS3_DATA_NM(g_aBs3LowSlabLists)
+# define g_aBs3UpperTiledSlabLists BS3_DATA_NM(g_aBs3UpperTiledSlabLists)
+# define g_cbBs3SlabCtlSizesforLists BS3_DATA_NM(g_cbBs3SlabCtlSizesforLists)
+#endif
+extern uint8_t const g_aiBs3SlabListsByPowerOfTwo[12];
+extern uint16_t const g_acbBs3SlabLists[BS3_MEM_SLAB_LIST_COUNT];
+extern BS3SLABHEAD g_aBs3LowSlabLists[BS3_MEM_SLAB_LIST_COUNT];
+extern BS3SLABHEAD g_aBs3UpperTiledSlabLists[BS3_MEM_SLAB_LIST_COUNT];
+extern uint16_t const g_cbBs3SlabCtlSizesforLists[BS3_MEM_SLAB_LIST_COUNT];
+
+
+/**
+ * Translates a allocation request size to a slab list index.
+ *
+ * @returns Slab list index if small request, UINT8_MAX if large.
+ * @param cbRequest The number of bytes requested.
+ */
+DECLINLINE(uint8_t) bs3MemSizeToSlabListIndex(size_t cbRequest)
+{
+ if (cbRequest <= g_acbBs3SlabLists[BS3_MEM_SLAB_LIST_COUNT - 1])
+ {
+ unsigned idx = cbRequest ? ASMBitLastSetU16((uint16_t)(cbRequest - 1)) : 0;
+ return g_aiBs3SlabListsByPowerOfTwo[idx];
+ }
+ return UINT8_MAX;
+}
+
+
+RT_C_DECLS_END;
+
+#endif /* !BS3KIT_INCLUDED_bs3_cmn_memory_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-paging.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-paging.h
new file mode 100644
index 00000000..7962b1cf
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-paging.h
@@ -0,0 +1,69 @@
+/* $Id: bs3-cmn-paging.h $ */
+/** @file
+ * BS3Kit - Internal Paging Structures, Variables and Functions.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3_cmn_paging_h
+#define BS3KIT_INCLUDED_bs3_cmn_paging_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "bs3kit.h"
+#include <iprt/asm.h>
+
+RT_C_DECLS_BEGIN
+
+/** Root directory for page protected mode.
+ * UINT32_MAX if not initialized. */
+extern uint32_t g_PhysPagingRootPP;
+/** Root directory pointer table for PAE mode.
+ * UINT32_MAX if not initialized. */
+extern uint32_t g_PhysPagingRootPAE;
+/** Root table (level 4) for long mode.
+ * UINT32_MAX if not initialized. */
+extern uint32_t g_PhysPagingRootLM;
+
+#undef bs3PagingGetLegacyPte
+BS3_CMN_PROTO_STUB(X86PTE BS3_FAR *, bs3PagingGetLegacyPte,(RTCCUINTXREG cr3, uint32_t uFlat, bool fUseInvlPg, int *prc));
+#undef bs3PagingGetPaePte
+BS3_CMN_PROTO_STUB(X86PTEPAE BS3_FAR *, bs3PagingGetPaePte,(RTCCUINTXREG cr3, uint8_t bMode, uint64_t uFlat,
+ bool fUseInvlPg, int *prc));
+
+RT_C_DECLS_END
+
+#include "bs3kit-mangling-code.h"
+
+#endif /* !BS3KIT_INCLUDED_bs3_cmn_paging_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic-data.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic-data.c
new file mode 100644
index 00000000..fbef9fbb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic-data.c
@@ -0,0 +1,52 @@
+/* $Id: bs3-cmn-pic-data.c $ */
+/** @file
+ * BS3Kit - PIC Data.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-pic.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if ARCH_BITS == 16
+/** Set by the first call to Bs3PicSetup. */
+bool g_fBs3PicConfigured = false;
+#endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic.h
new file mode 100644
index 00000000..f515a772
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pic.h
@@ -0,0 +1,70 @@
+/* $Id: bs3-cmn-pic.h $ */
+/** @file
+ * BS3Kit - Internal PIC Defines, Variables and Functions.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3_cmn_pic_h
+#define BS3KIT_INCLUDED_bs3_cmn_pic_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "bs3kit.h"
+
+
+/** The master PIC port (base). */
+#define BS3_PIC_PORT_MASTER UINT8_C(0x20)
+/** The slave PIC port (base). */
+#define BS3_PIC_PORT_SLAVE UINT8_C(0xa0)
+
+/** The init command. */
+#define BS3_PIC_CMD_INIT UINT8_C(0x10)
+/** 4th init step option for the init command. */
+#define BS3_PIC_CMD_INIT_F_4STEP UINT8_C(0x01)
+
+/** Auto end of interrupt flag for the 4th init step. */
+#define BS3_PIC_I4_F_AUTO_EOI UINT8_C(0x01)
+
+
+RT_C_DECLS_BEGIN
+
+extern bool g_fBs3PicConfigured;
+
+RT_C_DECLS_END
+
+
+#include "bs3kit-mangling-code.h"
+
+#endif /* !BS3KIT_INCLUDED_bs3_cmn_pic_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pit.c
new file mode 100644
index 00000000..9c0060a7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-pit.c
@@ -0,0 +1,170 @@
+/* $Id: bs3-cmn-pit.c $ */
+/** @file
+ * BS3Kit - PIT Setup and Disable code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define BS3_PIT_PORT_CMD 0x43
+#define BS3_PIT_PORT_CH0_DATA 0x40
+#define BS3_PIT_HZ UINT32_C(1193182)
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern FNBS3TRAPHANDLER16 bs3PitIrqHandler_c16;
+extern FNBS3TRAPHANDLER32 bs3PitIrqHandler_c32;
+extern FNBS3TRAPHANDLER64 bs3PitIrqHandler_c64;
+
+
+#undef Bs3PitSetupAndEnablePeriodTimer
+BS3_CMN_DEF(void, Bs3PitSetupAndEnablePeriodTimer,(uint16_t cHzDesired))
+{
+ RTCCUINTREG fSaved;
+ uint16_t cCount;
+ uint16_t cMsInterval;
+ uint32_t cNsInterval;
+
+ /*
+ * Disable the PIT and make sure we've configured the IRQ handlers.
+ */
+ Bs3PitDisable();
+ Bs3PicSetup(false /*fForcedReInit*/);
+ Bs3TrapSetHandlerEx(0x70, bs3PitIrqHandler_c16, bs3PitIrqHandler_c32, bs3PitIrqHandler_c64);
+
+ /*
+ * Reset the counters.
+ */
+ g_cBs3PitNs = 0;
+ g_cBs3PitMs = 0;
+ g_cBs3PitTicks = 0;
+
+ /*
+ * Calculate an interval.
+ */
+ if (cHzDesired <= 18)
+ {
+ cCount = 0; /* 1193182 / 65536 = 18.206512451171875 Hz */
+ cHzDesired = 18;
+ cNsInterval = UINT32_C(54925401); /* 65536 / 1193182 = 0.054925401154224586022920225078823 seconds */
+ cMsInterval = 55;
+ }
+ else
+ {
+ cCount = BS3_PIT_HZ / cHzDesired;
+ cHzDesired = BS3_PIT_HZ / cCount;
+ /* 1s/1193182 = 0.000 000 838 095 110 38550698887512550474278 */
+#if ARCH_BITS == 64
+ cNsInterval = cCount * UINT64_C(838095110) / 1000000;
+#elif ARCH_BITS == 32
+ cNsInterval = cCount * UINT32_C(8381) / 10;
+#else
+ cNsInterval = cCount * 838;
+#endif
+ if (cCount <= 1194)
+ cMsInterval = 1; /* Must not be zero! */
+ else
+ cMsInterval = cCount / 1194;
+ }
+
+
+ /*
+ * Do the reprogramming.
+ */
+ fSaved = ASMIntDisableFlags();
+ ASMOutU8(BS3_PIT_PORT_CMD,
+ (0 << 6) /* select: channel 0 */
+ | (3 << 4) /* access mode: lobyte/hibyte */
+ | (2 << 1) /* operation: Mode 2 */
+ | 0 /* binary mode */
+ );
+ ASMOutU8(BS3_PIT_PORT_CH0_DATA, (uint8_t)cCount);
+ ASMOutU8(BS3_PIT_PORT_CH0_DATA, (uint8_t)(cCount >> 8));
+
+ g_cBs3PitIntervalNs = cNsInterval;
+ g_cBs3PitIntervalHz = cHzDesired;
+ g_cBs3PitIntervalMs = cMsInterval;
+
+ Bs3PicUpdateMask(UINT16_C(0xfffe), 0);
+
+ ASMSetFlags(fSaved);
+}
+
+
+#undef Bs3PitDisable
+BS3_CMN_DEF(void, Bs3PitDisable,(void))
+{
+ if (g_cBs3PitIntervalHz != 0)
+ {
+ RTCCUINTREG fSaved = ASMIntDisableFlags();
+
+ /*
+ * Not entirely sure what's the best way to do this, but let's try reprogram
+ * it to a no-reload mode like 0 and set the count to 1.
+ */
+ g_cBs3PitIntervalMs = 0;
+ ASMOutU8(BS3_PIT_PORT_CMD,
+ (0 << 6) /* select: channel 0 */
+ | (1 << 4) /* access mode: lobyte */
+ | (0 << 1) /* operation: Mode 0 */
+ | 0 /* binary mode */
+ );
+ ASMOutU8(BS3_PIT_PORT_CH0_DATA, (uint8_t)1);
+
+ /*
+ * Then mask the PIT IRQ on the PIC.
+ */
+ Bs3PicUpdateMask(UINT16_C(0xffff), 1);
+
+ ASMSetFlags(fSaved);
+ }
+
+ /*
+ * Reset the interval values (leave the ticks and elapsed ns/ms values as-is).
+ */
+ g_cBs3PitIntervalNs = 0;
+ g_cBs3PitIntervalMs = 0;
+ g_cBs3PitIntervalHz = 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-test.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-test.h
new file mode 100644
index 00000000..135baebc
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-test.h
@@ -0,0 +1,179 @@
+/* $Id: bs3-cmn-test.h $ */
+/** @file
+ * BS3Kit - Bs3Test internal header.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3_cmn_test_h
+#define BS3KIT_INCLUDED_bs3_cmn_test_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "bs3kit.h"
+#include <VBox/VMMDevTesting.h>
+
+
+/** Indicates whether the VMMDev is operational. */
+#ifndef DOXYGEN_RUNNING
+# define g_fbBs3VMMDevTesting BS3_DATA_NM(g_fbBs3VMMDevTesting)
+#endif
+extern bool g_fbBs3VMMDevTesting;
+
+/** The number of tests that have failed. */
+#ifndef DOXYGEN_RUNNING
+# define g_cusBs3TestErrors BS3_DATA_NM(g_cusBs3TestErrors)
+#endif
+extern uint16_t g_cusBs3TestErrors;
+
+/** The start error count of the current subtest. */
+#ifndef DOXYGEN_RUNNING
+# define g_cusBs3SubTestAtErrors BS3_DATA_NM(g_cusBs3SubTestAtErrors)
+#endif
+extern uint16_t g_cusBs3SubTestAtErrors;
+
+/** Whether we've reported the sub-test result or not. */
+#ifndef DOXYGEN_RUNNING
+# define g_fbBs3SubTestReported BS3_DATA_NM(g_fbBs3SubTestReported)
+#endif
+extern bool g_fbBs3SubTestReported;
+/** Whether the sub-test has been skipped or not. */
+#ifndef DOXYGEN_RUNNING
+# define g_fbBs3SubTestSkipped BS3_DATA_NM(g_fbBs3SubTestSkipped)
+#endif
+extern bool g_fbBs3SubTestSkipped;
+
+/** The number of sub tests. */
+#ifndef DOXYGEN_RUNNING
+# define g_cusBs3SubTests BS3_DATA_NM(g_cusBs3SubTests)
+#endif
+extern uint16_t g_cusBs3SubTests;
+
+/** The number of sub tests that failed. */
+#ifndef DOXYGEN_RUNNING
+# define g_cusBs3SubTestsFailed BS3_DATA_NM(g_cusBs3SubTestsFailed)
+#endif
+extern uint16_t g_cusBs3SubTestsFailed;
+
+/** VMMDEV_TESTING_UNIT_XXX -> string */
+#ifndef DOXYGEN_RUNNING
+# define g_aszBs3TestUnitNames BS3_DATA_NM(g_aszBs3TestUnitNames)
+#endif
+extern char const g_aszBs3TestUnitNames[][12];
+
+/** The test name. */
+extern const char BS3_FAR *g_pszBs3Test_c16;
+extern const char *g_pszBs3Test_c32;
+extern const char *g_pszBs3Test_c64;
+
+/** The subtest name. */
+#ifndef DOXYGEN_RUNNING
+# define g_szBs3SubTest BS3_DATA_NM(g_szBs3SubTest)
+#endif
+extern char g_szBs3SubTest[64];
+
+
+/**
+ * Sends a command to VMMDev followed by a single string.
+ *
+ * If the VMMDev is not present or is not being used, this function will
+ * do nothing.
+ *
+ * @param uCmd The command.
+ * @param pszString The string.
+ */
+#ifndef DOXYGEN_RUNNING
+# define bs3TestSendCmdWithStr BS3_CMN_NM(bs3TestSendCmdWithStr)
+#endif
+BS3_DECL(void) bs3TestSendCmdWithStr(uint32_t uCmd, const char BS3_FAR *pszString);
+
+/**
+ * Sends a command to VMMDev followed by a 32-bit unsigned integer value.
+ *
+ * If the VMMDev is not present or is not being used, this function will
+ * do nothing.
+ *
+ * @param uCmd The command.
+ * @param uValue The value.
+ */
+#ifndef DOXYGEN_RUNNING
+# define bs3TestSendCmdWithU32 BS3_CMN_NM(bs3TestSendCmdWithU32)
+#endif
+BS3_DECL(void) bs3TestSendCmdWithU32(uint32_t uCmd, uint32_t uValue);
+
+/**
+ * Checks if the VMMDev is configured for testing.
+ *
+ * @returns true / false.
+ */
+#ifndef DOXYGEN_RUNNING
+# define bs3TestIsVmmDevTestingPresent BS3_CMN_NM(bs3TestIsVmmDevTestingPresent)
+#endif
+BS3_DECL(bool) bs3TestIsVmmDevTestingPresent(void);
+
+/**
+ * Similar to rtTestSubCleanup.
+ */
+#ifndef DOXYGEN_RUNNING
+# define bs3TestSubCleanup BS3_CMN_NM(bs3TestSubCleanup)
+#endif
+BS3_DECL(void) bs3TestSubCleanup(void);
+
+/**
+ * @callback_method_impl{FNBS3STRFORMATOUTPUT,
+ * Used by Bs3TestFailedV and Bs3TestSkippedV.
+ *
+ * The @a pvUser parameter must point a BS3TESTFAILEDBUF structure. }
+ */
+#ifndef DOXYGEN_RUNNING
+# define bs3TestFailedStrOutput BS3_CMN_NM(bs3TestFailedStrOutput)
+#endif
+BS3_DECL_CALLBACK(size_t) bs3TestFailedStrOutput(char ch, void BS3_FAR *pvUser);
+
+/**
+ * Output buffering for bs3TestFailedStrOutput.
+ */
+typedef struct BS3TESTFAILEDBUF
+{
+ /** Initialize to false. */
+ bool fNewLine;
+ /** Initialize to zero. */
+ uint8_t cchBuf;
+ /** Buffer, uninitialized. */
+ char achBuf[128];
+} BS3TESTFAILEDBUF;
+/** Pointer to a bs3TestFailedStrOutput buffer. */
+typedef BS3TESTFAILEDBUF BS3_FAR *PBS3TESTFAILEDBUF;
+
+#endif /* !BS3KIT_INCLUDED_bs3_cmn_test_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-common.mac b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-common.mac
new file mode 100644
index 00000000..1cc40765
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-common.mac
@@ -0,0 +1,281 @@
+; $Id: bs3-first-common.mac $
+;; @file
+; BS3Kit - First Object, common stuff.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%define BS3_BEGIN_TEXT16_WITHOUT_GROUP
+%define BS3_BEGIN_DATA16_WITHOUT_GROUP
+%define BS3_BEGIN_RMTEXT16_WITHOUT_GROUP
+%define BS3_BEGIN_X0TEXT16_WITHOUT_GROUP
+%define BS3_BEGIN_X1TEXT16_WITHOUT_GROUP
+
+%include "bs3kit.mac"
+
+
+;
+;
+; Define all the segments and their grouping, just to get that right once at
+; the start of everything.
+;
+;
+
+;
+; 16-bit text
+;
+%ifndef BS3_IS_DOS_EXE
+BS3_BEGIN_TEXT16
+%else
+section BEGTEXT align=2 CLASS=BS3CLASS16CODE PUBLIC USE16
+BS3_BEGIN_TEXT16
+section BEGTEXT
+%endif
+BS3_GLOBAL_DATA Bs3Text16_StartOfSegment, 0
+
+; Entry point with eye-catcher.
+GLOBALNAME start
+global __ImageBase ; for MS compiler - must be first!
+__ImageBase:
+global ___begtext ; for DOS EXEs (causes harmless duplicate symbol warning)
+___begtext:
+%ifndef BS3_IS_DOS_EXE
+ jmp .after_eye_catcher
+%else
+ int3
+ jmp __ImageBase
+%endif
+ db 10,13,'eye-catcher: BS3TEXT16',10,13
+BS3_BEGIN_TEXT16
+.after_eye_catcher:
+
+section _TEXT align=2 CLASS=BS3CLASS16CODE PUBLIC USE16
+section BS3TEXT16_NEARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+section BS3TEXT16_FARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+section BS3TEXT16_END align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+
+BS3_GLOBAL_DATA Bs3Text16_EndOfSegment, 0
+
+%ifndef BS3_IS_DOS_EXE
+GROUP CGROUP16 BS3TEXT16 _TEXT BS3TEXT16_NEARSTUBS BS3TEXT16_FARSTUBS BS3TEXT16_END
+%else
+GROUP CGROUP16 BEGTEXT BS3TEXT16 _TEXT BS3TEXT16_NEARSTUBS BS3TEXT16_FARSTUBS BS3TEXT16_END
+%endif
+
+
+;
+; 16-bit data
+;
+BS3_BEGIN_DATA16
+BS3_GLOBAL_DATA Bs3Data16_StartOfSegment, 0
+ db 10,13,'eye-catcher: BS3DATA16',10,13
+
+ALIGNDATA(16)
+BS3_GLOBAL_DATA Bs3Data16_Size, 4
+ dd BS3_DATA_NM(Bs3Data16_EndOfSegment) wrt BS3KIT_GRPNM_DATA16
+BS3_GLOBAL_DATA Bs3Data16Thru64Text32And64_TotalSize, 4
+ dd BS3_DATA_NM(Bs3Data64_EndOfSegment) wrt BS3KIT_GRPNM_DATA16
+BS3_GLOBAL_DATA Bs3TotalImageSize, 4
+ dd BS3_DATA_NM(Bs3Text64_EndOfSegment) wrt CGROUP16 ; ASSUMES TEXT64 is last.
+
+BS3_GLOBAL_DATA Bs3Text16_Size, 2
+ dw BS3_DATA_NM(Bs3Text16_EndOfSegment) wrt CGROUP16
+BS3_GLOBAL_DATA Bs3RmText16_Size, 2
+ dw BS3_DATA_NM(Bs3RmText16_EndOfSegment) wrt BS3GROUPRMTEXT16
+BS3_GLOBAL_DATA Bs3X0Text16_Size, 2
+ dw BS3_DATA_NM(Bs3X0Text16_EndOfSegment) wrt BS3GROUPX0TEXT16
+BS3_GLOBAL_DATA Bs3X1Text16_Size, 2
+ dw BS3_DATA_NM(Bs3X1Text16_EndOfSegment) wrt BS3GROUPX1TEXT16
+
+BS3_GLOBAL_DATA Bs3RmText16_FlatAddr, 4
+ dd BS3_DATA_NM(Bs3RmText16_StartOfSegment) wrt BS3FLAT
+BS3_GLOBAL_DATA Bs3X0Text16_FlatAddr, 4
+ dd BS3_DATA_NM(Bs3X0Text16_StartOfSegment) wrt BS3FLAT
+BS3_GLOBAL_DATA Bs3X1Text16_FlatAddr, 4
+ dd BS3_DATA_NM(Bs3X1Text16_StartOfSegment) wrt BS3FLAT
+
+section BS3DATA16CONST align=2 CLASS=BS3KIT_CLASS_DATA16 PUBLIC USE16
+section BS3DATA16CONST2 align=2 CLASS=BS3KIT_CLASS_DATA16 PUBLIC USE16
+section BS3DATA16_DATA align=2 CLASS=BS3KIT_CLASS_DATA16 PUBLIC USE16
+%ifdef BS3_IS_DOS_EXE
+section _NULL align=16 CLASS=BEGDATA PUBLIC USE16
+section _AFTERNULL align=2 CLASS=BEGDATA PUBLIC USE16
+%endif
+section CONST align=2 CLASS=DATA PUBLIC USE16
+section CONST2 align=2 CLASS=DATA PUBLIC USE16
+section _DATA align=2 CLASS=DATA PUBLIC USE16
+%ifdef BS3_IS_DOS_EXE
+section XIB align=1 CLASS=DATA PUBLIC USE16
+section XI align=1 CLASS=DATA PUBLIC USE16
+section XIE align=1 CLASS=DATA PUBLIC USE16
+section YIB align=1 CLASS=DATA PUBLIC USE16
+section YI align=1 CLASS=DATA PUBLIC USE16
+section YIE align=1 CLASS=DATA PUBLIC USE16
+%endif
+section STRINGS align=2 CLASS=DATA PUBLIC USE16
+section DATA align=2 CLASS=DATA PUBLIC USE16
+section _BSS align=2 CLASS=BS3KIT_CLASS_BSS16 PUBLIC USE16
+section BSS align=2 CLASS=BS3KIT_CLASS_BSS16 PUBLIC USE16
+%ifdef BS3_IS_DOS_EXE
+section STACK align=16 CLASS=STACK STACK USE16
+%endif
+section BS3DATA16_END align=2 CLASS=BS3KIT_CLASS_BSS16 PUBLIC USE16
+
+BS3_GLOBAL_DATA Bs3Data16_EndOfSegment, 0
+
+%ifndef BS3_IS_DOS_EXE
+GROUP BS3KIT_GRPNM_DATA16 BS3DATA16 BS3DATA16_DATA _DATA DATA BS3DATA16CONST CONST BS3DATA16CONST2 CONST2 STRINGS _BSS BSS BS3DATA16_END
+%else
+GROUP BS3KIT_GRPNM_DATA16 \
+ _NULL _AFTERNULL \
+ CONST BS3DATA16CONST CONST2 BS3DATA16CONST2 _DATA XIB XI XIE YIB YI YIE STRINGS DATA BS3DATA16 BS3DATA16_DATA \
+ _BSS BSS BS3DATA16_END \
+ STACK
+%endif
+
+;
+; 16-bit real-mode text
+;
+section BS3RMTEXT16_START align=16 CLASS=BS3CLASS16RMCODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3RmText16_StartOfSegment, 0
+ ;db 10,13,'eye-catcher: BS3RMTEXT16',10,13 - messes up switch in C code. Alt. is fConvertFixupp VBoxBs3ObjConverter.cpp.
+BS3_BEGIN_RMTEXT16
+section BS3RMTEXT16_END align=1 CLASS=BS3CLASS16RMCODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3RmText16_EndOfSegment, 0
+GROUP BS3GROUPRMTEXT16 BS3RMTEXT16_START BS3RMTEXT16 BS3RMTEXT16_END
+
+
+;
+; 16-bit extra text segment #0.
+;
+section BS3X0TEXT16_START align=16 CLASS=BS3CLASS16X0CODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3X0Text16_StartOfSegment, 0
+ ;db 10,13,'eye-catcher: BS3X0TEXT16',10,13 - messes up switch in C code. Alt. is fConvertFixupp VBoxBs3ObjConverter.cpp.
+BS3_BEGIN_X0TEXT16 4
+section BS3X0TEXT16_END align=16 CLASS=BS3CLASS16X0CODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3X0Text16_EndOfSegment, 0
+GROUP BS3GROUPX0TEXT16 BS3X0TEXT16_START BS3X0TEXT16 BS3X0TEXT16_END
+
+
+;
+; 16-bit extra text segment #1.
+;
+section BS3X1TEXT16_START align=16 CLASS=BS3CLASS16X1CODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3X1Text16_StartOfSegment, 0
+ ;db 10,13,'eye-catcher: BS3X1TEXT16',10,13 - messes up switch in C code. Alt. is fConvertFixupp VBoxBs3ObjConverter.cpp.
+BS3_BEGIN_X1TEXT16 4
+section BS3X1TEXT16_END align=16 CLASS=BS3CLASS16X1CODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3X1Text16_EndOfSegment, 0
+GROUP BS3GROUPX1TEXT16 BS3X1TEXT16_START BS3X1TEXT16 BS3X1TEXT16_END
+
+
+;
+; 32-bit text
+;
+BS3_BEGIN_TEXT32
+BS3_GLOBAL_DATA Bs3Text32_StartOfSegment, 0
+ db 10,13,'eye-catcher: BS3TEXT32',10,13
+section BS3TEXT32_END align=1 CLASS=BS3CLASS32CODE PUBLIC USE32 FLAT
+BS3_GLOBAL_DATA Bs3Text32_EndOfSegment, 0
+
+
+;
+; This is a hack to separate the 32-bit and 64-bit text segments when linking,
+; such that they don't share the same base frame because they're both assigned
+; to the AUTO group by the linker.
+;
+section BS3SEPARATE32AND64BITCODE align=16 CLASS=BS3CLASSSEPARATE32AND64BITCODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3Separate32And64BitCode_StartOfSegment, 0
+ db 10,13,'eye-catcher: 32-64 wedge',10,13
+section BS3SEPARATE32AND64BITCODE_END align=16 CLASS=BS3CLASSSEPARATE32AND64BITCODE PUBLIC USE16
+BS3_GLOBAL_DATA Bs3Separate32And64BitCode_EndOfSegment, 0
+GROUP BS3SEPARATE32AND64BITCODEGROUP BS3SEPARATE32AND64BITCODE BS3SEPARATE32AND64BITCODE_END
+
+
+;
+; 64-bit text
+;
+BS3_BEGIN_TEXT64
+BS3_GLOBAL_DATA Bs3Text64_StartOfSegment, 0
+ db 10,13,'eye-catcher: BS3TEXT64',10,13
+section BS3TEXT64_END align=1 CLASS=BS3CLASS64CODE PUBLIC USE32 FLAT
+BS3_GLOBAL_DATA Bs3Text64_EndOfSegment, 0
+
+
+;
+; FAR_DATA segment in DOS EXEs should be near the other FAR_DATA class segments.
+;
+%ifdef BS3_IS_DOS_EXE
+section FAR_DATA align=1 CLASS=FAR_DATA PUBLIC USE16
+%endif
+
+;
+; 32-bit data
+;
+BS3_BEGIN_DATA32
+BS3_GLOBAL_DATA Bs3Data32_StartOfSegment, 0
+ db 10,13,'eye-catcher: BS3DATA32',10,13
+section BS3DATA32CONST align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA32CONST2 align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA32_DATA align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA32_BSS align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA32_END align=16 CLASS=FAR_DATA PUBLIC USE32
+BS3_GLOBAL_DATA Bs3Data32_EndOfSegment, 0
+GROUP BS3DATA32_GROUP BS3DATA32 BS3DATA32_DATA BS3DATA32CONST BS3DATA32CONST2 BS3DATA32_BSS BS3DATA32_END
+
+;
+; 64-bit data
+;
+BS3_BEGIN_DATA64
+BS3_GLOBAL_DATA Bs3Data64_StartOfSegment, 0
+ db 10,13,'eye-catcher: BS3DATA64',10,13
+section BS3DATA64CONST align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA64_BSS align=16 CLASS=FAR_DATA PUBLIC USE32
+section BS3DATA64_END align=16 CLASS=FAR_DATA PUBLIC USE32
+BS3_GLOBAL_DATA Bs3Data64_EndOfSegment, 0
+GROUP BS3DATA64_GROUP BS3DATA64 BS3DATA64CONST BS3DATA64_BSS BS3DATA64_END
+
+
+;
+; 16-bit accessible system data.
+; No need to do anything here.
+;
+BS3_BEGIN_SYSTEM16
+
+
+;
+; Switch back to the 16-bit code segment and the startup code.
+;
+BS3_BEGIN_TEXT16
+BS3_GLOBAL_NAME_EX Bs3KitEntryPoint, function, 0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-dosexe.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-dosexe.asm
new file mode 100644
index 00000000..165cde49
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-dosexe.asm
@@ -0,0 +1,43 @@
+; $Id: bs3-first-dosexe.asm $
+;; @file
+; BS3Kit - First Object for DOS executables, defines segments only.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-lm64.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-lm64.asm
new file mode 100644
index 00000000..21227cf9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-lm64.asm
@@ -0,0 +1,91 @@
+; $Id: bs3-first-init-all-lm64.asm $
+;; @file
+; BS3Kit - First Object, calling 32-bit paged protected mode main() after full init.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
+extern NAME(Bs3InitAll_rm)
+extern NAME(Bs3SwitchToLM64_rm)
+extern NAME(Main_lm64)
+extern NAME(Bs3Shutdown_c64)
+extern BS3_DATA_NM(g_uBs3CpuDetected)
+extern NAME(Bs3PrintStrN_c16)
+extern NAME(Bs3Panic_c16)
+
+;; Entry point.
+ push word 0 ; zero return address.
+ push word 0 ; zero caller BP
+ mov bp, sp
+
+ ;
+ ; Init all while we're in real mode.
+ ;
+ mov ax, BS3_SEL_DATA16
+ mov es, ax
+ mov ds, ax
+ call NAME(Bs3InitAll_rm)
+
+ ;
+ ; Check that long mode is supported.
+ ;
+ test word [BS3_DATA_NM(g_uBs3CpuDetected)], BS3CPU_F_LONG_MODE
+ jnz .long_mode_supported
+ push .s_szLongModeError_End - .s_szLongModeError
+ push cs
+ push .s_szLongModeError wrt CGROUP16
+ call NAME(Bs3PrintStrN_c16)
+ call NAME(Bs3Panic_c16)
+.long_mode_supported:
+
+ ;
+ ; Switch to LM64 and call main.
+ ;
+ call _Bs3SwitchToLM64_rm
+ BITS 64
+ call NAME(Main_lm64)
+
+ ; Try shutdown if it returns.
+ call NAME(Bs3Shutdown_c64)
+
+.s_szLongModeError:
+ db 'BS3 Error! Long mode not supported!', 0ah, 0dh
+.s_szLongModeError_End:
+ db 00h
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pe32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pe32.asm
new file mode 100644
index 00000000..dd638056
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pe32.asm
@@ -0,0 +1,69 @@
+; $Id: bs3-first-init-all-pe32.asm $
+;; @file
+; BS3Kit - First Object, calling 32-bit protected mode main() after full init.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
+extern NAME(Bs3InitAll_rm)
+extern NAME(Bs3SwitchToPE32_rm)
+
+;; Entry point.
+ push word 0 ; zero return address.
+ push word 0 ; zero caller BP
+ mov bp, sp
+
+ ;
+ ; Init all while we're in real mode.
+ ;
+ mov ax, BS3_SEL_DATA16
+ mov es, ax
+ mov ds, ax
+ call NAME(Bs3InitAll_rm)
+
+ ;
+ ; Switch to 32-bit protected mode and call main.
+ ;
+ call NAME(Bs3SwitchToPE32_rm)
+ BS3_SET_BITS 32
+ call _Main_pe32
+extern _Main_pe32
+BS3_EXTERN_CMN Bs3Shutdown
+ call Bs3Shutdown
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pp32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pp32.asm
new file mode 100644
index 00000000..39b288ab
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-init-all-pp32.asm
@@ -0,0 +1,69 @@
+; $Id: bs3-first-init-all-pp32.asm $
+;; @file
+; BS3Kit - First Object, calling 32-bit paged protected mode main() after full init.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
+extern NAME(Bs3InitAll_rm)
+extern NAME(Bs3SwitchToPP32_rm)
+
+;; Entry point.
+ push word 0 ; zero return address.
+ push word 0 ; zero caller BP
+ mov bp, sp
+
+ ;
+ ; Init all while we're in real mode.
+ ;
+ mov ax, BS3_SEL_DATA16
+ mov es, ax
+ mov ds, ax
+ call NAME(Bs3InitAll_rm)
+
+ ;
+ ; Switch to 32-bit protected mode and call main.
+ ;
+ call NAME(Bs3SwitchToPP32_rm)
+ BS3_SET_BITS 32
+ call _Main_pp32
+extern _Main_pp32
+BS3_EXTERN_CMN Bs3Shutdown
+ call Bs3Shutdown
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-pe16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-pe16.asm
new file mode 100644
index 00000000..cad3787e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-pe16.asm
@@ -0,0 +1,105 @@
+; $Id: bs3-first-pe16.asm $
+;; @file
+; BS3Kit - First Object, calling 16-bit protected-mode main().
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_BEGIN_DATA16
+
+BS3_BEGIN_RMTEXT16
+extern _Bs3CpuDetect_rm_far
+extern _Bs3InitMemory_rm_far
+
+BS3_BEGIN_TEXT16
+BS3_EXTERN_CMN Bs3PicMaskAll
+BS3_EXTERN_CMN Bs3Trap16Init
+extern _Bs3SwitchToPE16_rm
+extern _Main_pe16
+BS3_EXTERN_CMN Bs3Shutdown
+
+
+BS3_BEGIN_TEXT16
+ ;
+ ; Zero return address and zero caller BP.
+ ;
+ xor ax, ax
+ push ax
+ push ax
+ mov bp, sp
+
+ ;
+ ; Load DS and ES with data selectors.
+ ;
+ mov ax, BS3KIT_GRPNM_DATA16
+ mov ds, ax
+ mov es, ax
+
+
+ ;
+ ; Make sure interrupts are disabled as we cannot (don't want to) service
+ ; BIOS interrupts once we switch mode.
+ ;
+ cli
+ call Bs3PicMaskAll
+
+ ;
+ ; Initialize 16-bit protected mode.
+ ;
+ call far _Bs3CpuDetect_rm_far
+ call far _Bs3InitMemory_rm_far
+ call Bs3Trap16Init
+
+ ;
+ ; Switch to PE16 and call main.
+ ;
+ call _Bs3SwitchToPE16_rm
+ call _Main_pe16
+
+ ; Try shutdown if it returns.
+ call Bs3Shutdown
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-rm.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-rm.asm
new file mode 100644
index 00000000..ab6b426e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-first-rm.asm
@@ -0,0 +1,59 @@
+; $Id: bs3-first-rm.asm $
+;; @file
+; BS3Kit - First Object, calling real-mode main().
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Segment defs, grouping and related variables.
+; Defines the entry point 'start' as well, leaving us in BS3TEXT16.
+;
+%include "bs3-first-common.mac"
+
+
+EXTERN Main_rm
+BS3_EXTERN_CMN Bs3Shutdown
+ push word 0 ; zero return address.
+ push word 0 ; zero caller BP
+ mov bp, sp
+
+ ;
+ ; Nothing to init here, just call main and shutdown if it returns.
+ ;
+ mov ax, BS3_SEL_DATA16
+ mov es, ax
+ mov ds, ax
+ call NAME(Main_rm)
+ call Bs3Shutdown
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-BiosInt15hE820.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-BiosInt15hE820.asm
new file mode 100644
index 00000000..6b3c66e4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-BiosInt15hE820.asm
@@ -0,0 +1,245 @@
+; $Id: bs3-mode-BiosInt15hE820.asm $
+;; @file
+; BS3Kit - Bs3BiosInt15hE820
+;
+
+;
+; Copyright (C) 2021-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+;; Signature: 'SMAP'
+%define INT15_E820_SIGNATURE 0534d4150h
+
+
+;*********************************************************************************************************************************
+;* External symbols *
+;*********************************************************************************************************************************
+TMPL_BEGIN_TEXT
+extern TMPL_NM(Bs3SwitchToRM)
+BS3_BEGIN_TEXT16
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+
+
+;;
+; Performs a int 15h function 0xe820 call.
+;
+; @cproto BS3_MODE_PROTO_STUB(bool, Bs3BiosInt15hE820,(INT15E820ENTRY BS3_FAR *pEntry, uint32_t BS3_FAR *pcbEntry,
+; uint32_t BS3_FAR *puContinuationValue));
+;
+; @returns Success indicator.
+; @param pEntry The return buffer.
+; @param pcbEntry Input: The size of the buffer (min 20 bytes);
+; Output: The size of the returned data.
+; @param puContinuationValue Where to get and return the continuation value (EBX)
+; Set to zero the for the first call. Returned as zero
+; after the last entry.
+;
+; @remarks ASSUMES we're in ring-0 when not in some kind of real mode.
+; @remarks ASSUMES we're on a 16-bit suitable stack.
+;
+; @uses rax
+;
+TMPL_BEGIN_TEXT
+BS3_PROC_BEGIN_MODE Bs3BiosInt15hE820, BS3_PBC_HYBRID
+ push xBP
+ mov xBP, xSP
+ sPUSHF
+ cli
+ push sBX
+ push sCX
+ push sDX
+ push sSI
+ push sDI
+%ifdef TMPL_16BIT
+ push ds
+ push es
+%endif
+ ; Load/Save parameters.
+%define a_pEntry [xBP + xCB + cbCurRetAddr + sCB*0]
+%define a_pcbEntry [xBP + xCB + cbCurRetAddr + sCB*1]
+%define a_puContinuationValue [xBP + xCB + cbCurRetAddr + sCB*2]
+%ifdef TMPL_64BIT
+ mov a_pEntry, rcx ; save pEntry
+ mov a_pcbEntry, rdx ; save pcbEntry
+ mov a_puContinuationValue, r8 ; save a_puContinuationValue
+ mov ebx, [r8] ; uContinuationValue for int15
+ mov ecx, [rdx] ; Buffer size for int15.
+%elifdef TMPL_16BIT
+ les bx, a_pcbEntry
+ mov ecx, [es:bx] ; Buffer size for int15.
+ les bx, a_puContinuationValue
+ mov ebx, [es:bx] ; Buffer size for int15.
+%else
+ mov ecx, a_pcbEntry
+ mov ecx, [ecx] ; Buffer size for int15.
+ mov ebx, a_puContinuationValue
+ mov ebx, [ebx] ; uContinuationValue for int15
+%endif
+ ;
+ ; Check that the cbEntry isn't too big or too small before doing
+ ; the stack allocation. (Our BIOS doesn't check if too small.)
+ ;
+ cmp ecx, 100h
+ jae .failed
+ cmp cl, 14h
+ jb .failed
+
+%if TMPL_MODE != BS3_MODE_RM
+ sub xSP, xCX ; allocate a temporary buffer on the stack.
+ and xSP, ~0fh
+%endif
+
+ ;
+ ; Switch to real mode, first we just ot the 16-bit text segment.
+ ; This preserve all 32-bit register values.
+ ;
+%if TMPL_MODE != BS3_MODE_RM
+ %ifndef TMPL_16BIT
+ jmp .to_text16
+BS3_BEGIN_TEXT16
+.to_text16:
+ BS3_SET_BITS TMPL_BITS
+ %endif
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+%endif
+
+ ;
+ ; Make the call.
+ ;
+%if TMPL_MODE == BS3_MODE_RM
+ les di, a_pEntry
+%else
+ push ss ; es:di -> ss:sp
+ pop es
+ mov di, sp
+%endif
+ mov edx, INT15_E820_SIGNATURE
+ mov eax, 0e820h ; BIOS function number
+ int 15h
+
+ ;
+ ; Switch back.
+ ;
+%if TMPL_MODE != BS3_MODE_RM
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+ BS3_SET_BITS TMPL_BITS
+ %ifndef TMPL_16BIT
+ jmp .from_text16
+TMPL_BEGIN_TEXT
+.from_text16:
+ %endif
+%endif
+ ;
+ ; Check that we didn't failed.
+ ;
+ jc .failed
+ cmp eax, INT15_E820_SIGNATURE
+ jc .failed
+ cmp ecx, 20
+ jb .failed
+
+ ;
+ ; Save the continuation value.
+ ;
+%ifdef TMPL_16BIT
+ mov eax, ebx
+ lds bx, a_puContinuationValue
+ mov [bx], eax
+%else
+ mov xAX, a_puContinuationValue
+ mov [xAX], ebx
+%endif
+
+ ;
+ ; Save the entry size.
+ ;
+%ifdef TMPL_16BIT
+ lds bx, a_pcbEntry
+%else
+ mov xBX, a_pcbEntry
+%endif
+ mov [xBX], ecx
+
+%if TMPL_MODE != BS3_MODE_RM
+ ;
+ ; Copy the returned stuff into the caller's buffer.
+ ;
+ mov xSI, xSP
+ %ifdef TMPL_16BIT
+ push ss
+ pop es
+ lds di, a_pEntry
+ %else
+ mov xDI, a_pEntry
+ %endif
+ cld
+ rep movsb
+%endif
+
+ ;
+ ; Return success
+ ;
+ mov al, 1
+
+.return:
+%ifdef TMPL_16BIT
+ lea xSP, [xBP - sCB * 6 - xCB*2]
+ pop es
+ pop ds
+%else
+ lea xSP, [xBP - sCB * 6]
+%endif
+ pop sDI
+ pop sSI
+ pop sDX
+ pop sCX
+ pop sBX
+ sPOPF
+ leave
+ BS3_HYBRID_RET
+
+ ;
+ ; Failed.
+ ;
+.failed:
+ xor al, al
+ jmp .return
+BS3_PROC_END_MODE Bs3BiosInt15hE820
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm
new file mode 100644
index 00000000..b54cba28
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm
@@ -0,0 +1,347 @@
+; $Id: bs3-mode-CpuDetect.asm $
+;; @file
+; BS3Kit - Bs3CpuDetect
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+
+
+;;
+; Rough CPU detection, mainly for detecting really old CPUs.
+;
+; A Bs3CpuDetectEx can be added if this is insufficient.
+;
+; @returns BS3CPU_xxx in xAX.
+; @cproto BS3_DECL(BS3CPU) Bs3CpuDetect(void);
+;
+; @uses xAX.
+;
+; @remarks ASSUMES we're in ring-0 when not in some kind of real mode.
+;
+; @note We put the real mode version of this code in the RMTEXT16 segment
+; to save space elsewhere. We generate a far call stub that goes
+; to the right segment.
+;
+%if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_RMTEXT16
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_FAR
+%else
+TMPL_BEGIN_TEXT
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_HYBRID
+%endif
+CPU 8086
+ push xBP
+ mov xBP, xSP
+ pushf ; xBP - xCB*1
+ push xCX ; xBP - xCB*2
+ push xDX ; xBP - xCB*3
+ push xBX ; xBP - xCB*4
+ sub xSP, 20h ; xBP - xCB*4 - 20h
+
+%ifndef TMPL_CMN_PAGING
+ %ifdef TMPL_RM
+ %if 1 ; this is simpler
+ ;
+ ; FLAGS bits 15:12 are always set on 8086, 8088, V20, V30, 80186, and
+ ; 80188. FLAGS bit 15 is always zero on 286+, whereas bit 14 is NT and
+ ; bits 13:12 are IOPL.
+ ;
+ test byte [xBP - xCB + 1], 80h ; Top byte of saved flags.
+ jz .286plus
+ %else
+ ;
+ ; When executing 'PUSH SP' the 8086, 8088, V20, V30, 80186, and 80188
+ ; should be pushing the updated SP value instead of the initial one.
+ ;
+ push xSP
+ pop xAX
+ cmp xAX, xSP
+ je .286plus
+ %endif
+
+ ;
+ ; Older than 286.
+ ;
+ ; Detect 8086/8088/V20/V30 vs. 80186/80188 by checking for pre 80186
+ ; shift behavior. the 80186/188 and later will mask the CL value according
+ ; to the width of the destination register, whereas 8086/88 and V20/30 will
+ ; perform the exact number of shifts specified.
+ ;
+ mov cl, 20h ; Shift count; 80186/88 and later will mask this by 0x1f (or 0xf)?
+ mov dx, 7fh
+ shl dx, cl
+ cmp dx, 7fh ; If no change, this is a 80186/88.
+ mov xAX, BS3CPU_80186
+ je .return
+
+ ;
+ ; Detect 8086/88 vs V20/30 by exploiting undocumented POP CS encoding
+ ; that was redefined on V20/30 to SET1.
+ ;
+ xor ax, ax ; clear
+ push cs
+ db 0fh ; 8086/88: pop cs V20/30: set1 bl,cl
+ db 14h, 3ch ; 8086/88: add al, 3ch
+ ; 8086/88: al = 3ch V20/30: al = 0, cs on stack, bl modified.
+ cmp al, 3ch
+ jne .is_v20_or_v30
+ mov xAX, BS3CPU_8086
+ jmp .return
+
+.is_v20_or_v30:
+ pop xCX ; unclaimed CS
+ mov xAX, BS3CPU_V20
+ jmp .return
+
+ %endif ; TMPL_RM
+
+CPU 286
+.286plus:
+ ;
+ ; The 4th bit of the machine status word / CR0 indicates the precense
+ ; of a 80387 or later co-processor (a 80287+80386 => ET=0). 486 and
+ ; later should be hardcoding this to 1, according to the documentation
+ ; (need to test on 486SX). The initial idea here then would be to
+ ; assume 386+ if ET=1.
+ ;
+ ; The second idea was to check whether any reserved bits are set,
+ ; because the 286 here has bits 4 thru 15 all set. Unfortunately, it
+ ; turned out the 386SX and AMD 486DX-40 also sets bits 4 thru 15 when
+ ; using SMSW. So, nothing conclusive to distinguish 386 from 286, but
+ ; we've probably got a safe 486+ detection here.
+ ;
+ ;; @todo check if LOADALL can set any of the reserved bits on a 286 or 386.
+ smsw ax
+ test ax, ~(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE)
+ jz .486plus
+
+ ;
+ ; The 286 stores 0xff in the high byte of the SIDT and SGDT base
+ ; address (since it only did 24-bit addressing and the top 8-bit was
+ ; reserved for the 386). ASSUMES low IDT (which is the case for BS3Kit).
+ ;
+ sidt [xBP - xCB*4 - 20h]
+ cmp byte [xBP - xCB*4 - 20h + 2 + 3], 0ffh
+ jne .386plus
+
+ %if 0
+ ;
+ ; Detect 80286 by checking whether the IOPL and NT bits of EFLAGS can be
+ ; modified or not. There are different accounts of these bits. Dr.Dobb's
+ ; (http://www.drdobbs.com/embedded-systems/processor-detection-schemes/184409011)
+ ; say they are undefined on 286es and will always be zero. Whereas Intel
+ ; iAPX 286 Programmer's Reference Manual (both order #210498-001 and
+ ; #210498-003) documents both IOPL and NT, but with comment 4 on page
+ ; C-43 stating that they cannot be POPFed in real mode and will both
+ ; remain 0. This is different from the 386+, where the NT flag isn't
+ ; privileged according to page 3-37 in #230985-003. Later Intel docs
+ ; (#235383-052US, page 4-192) documents real mode as taking both NT and
+ ; IOPL from what POPF reads off the stack - which is the behavior
+ ; observed a 386SX here.
+ ;
+ test al, X86_CR0_PE ; This flag test doesn't work in protected mode, ...
+ jnz .386plus ; ... so ASSUME 386plus if in PE for now.
+
+ pushf ; Save a copy of the original flags for restoring IF.
+ pushf
+ pop ax
+ xor ax, X86_EFL_IOPL | X86_EFL_NT ; Try modify IOPL and NT.
+ and ax, ~X86_EFL_IF ; Try clear IF.
+ push ax ; Load modified flags.
+ popf
+ pushf ; Get actual flags.
+ pop dx
+ popf ; Restore IF, IOPL and NT.
+ cmp ax, dx
+ je .386plus ; If any of the flags are set, we're on 386+.
+
+ ; While we could in theory be in v8086 mode at this point and be fooled
+ ; by a flaky POPF implementation, we assume this isn't the case in our
+ ; execution environment.
+ %endif
+.is_286:
+ mov ax, BS3CPU_80286
+ jmp .return
+%endif ; !TMPL_CMN_PAGING
+
+CPU 386
+.386plus:
+.486plus:
+ ;
+ ; Check for CPUID and AC. The former flag indicates CPUID support, the
+ ; latter was introduced with the 486.
+ ;
+ mov ebx, esp ; Save esp.
+ and esp, 0fffch ; Clear high word and don't trigger ACs.
+ pushfd
+ mov eax, [esp] ; eax = original EFLAGS.
+ xor dword [esp], X86_EFL_ID | X86_EFL_AC ; Flip the ID and AC flags.
+ popfd ; Load modified flags.
+ pushfd ; Save actual flags.
+ xchg eax, [esp] ; Switch, so the stack has the original flags.
+ xor eax, [esp] ; Calc changed flags.
+ popf ; Restore EFLAGS.
+ mov esp, ebx ; Restore possibly unaligned ESP.
+ test eax, X86_EFL_ID
+ jnz .have_cpuid ; If ID changed, we've got CPUID.
+ test eax, X86_EFL_AC
+ mov xAX, BS3CPU_80486
+ jnz .return ; If AC changed, we've got a 486 without CPUID (or similar).
+ mov xAX, BS3CPU_80386
+ jmp .return
+
+CPU 586
+.have_cpuid:
+ ;
+ ; Do a very simple minded check here using the (standard) family field.
+ ; While here, we also check for PAE.
+ ;
+ mov eax, 1
+ cpuid
+
+ ; Calc the extended family and model values before we mess up EAX.
+ mov cl, ah
+ and cl, 0fh
+ cmp cl, 0fh
+ jnz .not_extended_family
+ mov ecx, eax
+ shr ecx, 20
+ and cl, 7fh
+ add cl, 0fh
+.not_extended_family: ; cl = family
+ mov ch, al
+ shr ch, 4
+ cmp cl, 0fh
+ jae .extended_model
+ cmp cl, 06h ; actually only intel, but we'll let this slip for now.
+ jne .done_model
+.extended_model:
+ shr eax, 12
+ and al, 0f0h
+ or ch, al
+.done_model: ; ch = model
+
+ ; Start assembling return flags, checking for PSE + PAE.
+ mov eax, X86_CPUID_FEATURE_EDX_PSE | X86_CPUID_FEATURE_EDX_PAE
+ and eax, edx
+ mov ah, al
+ AssertCompile(X86_CPUID_FEATURE_EDX_PAE_BIT > BS3CPU_F_PAE_BIT - 8) ; 6 vs 10-8=2
+ and al, X86_CPUID_FEATURE_EDX_PAE
+ shr al, X86_CPUID_FEATURE_EDX_PAE_BIT - (BS3CPU_F_PAE_BIT - 8)
+ AssertCompile(X86_CPUID_FEATURE_EDX_PSE_BIT == BS3CPU_F_PSE_BIT - 8) ; 3 vs 11-8=3
+ and ah, X86_CPUID_FEATURE_EDX_PSE
+ or ah, al
+ or ah, (BS3CPU_F_CPUID >> 8)
+
+ ; Add the CPU type based on the family and model values.
+ cmp cl, 6
+ jne .not_family_06h
+ mov al, BS3CPU_PPro
+ cmp ch, 1
+ jbe .return
+ mov al, BS3CPU_PProOrNewer
+ jmp .NewerThanPPro
+
+.not_family_06h:
+ mov al, BS3CPU_PProOrNewer
+ ja .NewerThanPPro
+ cmp cl, 5
+ mov al, BS3CPU_Pentium
+ je .return
+ cmp cl, 4
+ mov al, BS3CPU_80486
+ je .return
+ cmp cl, 3
+ mov al, BS3CPU_80386
+ je .return
+
+.NewerThanPPro:
+
+ ; Check for extended leaves and long mode.
+ push xAX ; save PAE+PProOrNewer
+ mov eax, 0x80000000
+ cpuid
+ sub eax, 0x80000001 ; Minimum leaf 0x80000001
+ cmp eax, 0x00010000 ; At most 0x10000 leaves.
+ ja .no_ext_leaves
+
+ mov eax, 0x80000001
+ cpuid
+ pop xAX ; restore PAE+PProOrNewer
+ test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE
+ jz .no_long_mode
+ or ah, ((BS3CPU_F_CPUID_EXT_LEAVES | BS3CPU_F_LONG_MODE) >> 8)
+ jmp .no_check_for_nx
+.no_long_mode:
+ or ah, (BS3CPU_F_CPUID_EXT_LEAVES >> 8)
+.no_check_for_nx:
+ test edx, X86_CPUID_EXT_FEATURE_EDX_NX
+ jz .return
+ or ax, BS3CPU_F_NX
+ jmp .return
+
+.no_ext_leaves:
+ pop xAX ; restore PAE+PProOrNewer
+
+CPU 8086
+.return:
+ ;
+ ; Save the return value.
+ ;
+ mov [BS3_DATA16_WRT(g_uBs3CpuDetected)], ax
+
+ ;
+ ; Epilogue.
+ ;
+ add xSP, 20h
+ pop xBX
+ pop xDX
+ pop xCX
+ popf
+ pop xBP
+ BS3_HYBRID_RET
+
+BS3_PROC_END_MODE Bs3CpuDetect
+
+
+%if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_TEXT16_NEARSTUBS
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_NEAR
+ call far TMPL_FAR_NM(Bs3CpuDetect)
+ ret
+BS3_PROC_END_MODE Bs3CpuDetect
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-EnteredMode.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-EnteredMode.asm
new file mode 100644
index 00000000..4cc2c11e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-EnteredMode.asm
@@ -0,0 +1,279 @@
+; $Id: bs3-mode-EnteredMode.asm $
+;; @file
+; BS3Kit - Bs3EnteredMode
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+TMPL_BEGIN_TEXT
+
+;;
+; @cproto BS3_DECL(void) Bs3EnteredMode(void);
+;
+; @uses Nothing.
+;
+; @remarks ASSUMES we're in ring-0 when not in some kind of real mode.
+;
+BS3_PROC_BEGIN_MODE Bs3EnteredMode, BS3_PBC_NEAR ; won't need this outside the switchers, so always near.
+ push xBP
+ mov xBP, xSP
+ push xAX
+ push xCX
+ push xDX
+TONLY16 push xBX
+%if BS3_MODE_IS_64BIT_CODE(TMPL_MODE)
+ push r8
+ push r9
+%endif
+
+ ;
+ ; Load stack selector (not always necessary) and sometimes CS too.
+ ;
+%if BS3_MODE_IS_RM_SYS(TMPL_MODE)
+ xor ax, ax
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ extern v86_versions_of_Bs3EnteredMode_should_not_be_dragged_into_the_link
+ call v86_versions_of_Bs3EnteredMode_should_not_be_dragged_into_the_link
+%elif BS3_MODE_IS_16BIT_CODE(TMPL_MODE)
+ jmp BS3_SEL_R0_CS16:.reloaded_cs
+.reloaded_cs:
+ mov ax, BS3_SEL_R0_SS16
+%elif BS3_MODE_IS_32BIT_CODE(TMPL_MODE)
+ mov ax, BS3_SEL_R0_SS32
+%elif BS3_MODE_IS_64BIT_CODE(TMPL_MODE)
+ mov ax, BS3_SEL_R0_DS64
+%else
+ %error "TMPL_MODE"
+%endif
+ mov ss, ax
+
+ ;
+ ; Load selector appropriate for accessing BS3SYSTEM16 data.
+ ;
+%if BS3_MODE_IS_16BIT_CODE(TMPL_MODE)
+ mov ax, BS3_SEL_SYSTEM16
+%else
+ mov ax, RT_CONCAT(BS3_SEL_R0_DS,TMPL_BITS)
+%endif
+ mov ds, ax
+
+ ;
+ ; Load the appropritate IDT or IVT.
+ ; Always 64-bit in long mode, otherwise according to TMPL_BITS.
+ ;
+%if BS3_MODE_IS_RM_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Lidt_Ivt
+ TMPL_BEGIN_TEXT
+ lidt [Bs3Lidt_Ivt]
+
+%elif BS3_MODE_IS_16BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Lidt_Idt16
+ TMPL_BEGIN_TEXT
+ lidt [Bs3Lidt_Idt16 TMPL_WRT_SYSTEM16_OR_FLAT]
+
+%elif BS3_MODE_IS_32BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Lidt_Idt32
+ TMPL_BEGIN_TEXT
+ lidt [Bs3Lidt_Idt32 TMPL_WRT_SYSTEM16_OR_FLAT]
+
+%elif BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Lidt_Idt64
+ TMPL_BEGIN_TEXT
+ lidt [Bs3Lidt_Idt64 TMPL_WRT_SYSTEM16_OR_FLAT]
+%else
+ %error "TMPL_MODE"
+%endif
+
+%if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ;
+ ; Load the appropriate task selector.
+ ; Always 64-bit in long mode, otherwise according to TMPL_BITS.
+ ;
+ %if BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Gdte_Tss64
+ TMPL_BEGIN_TEXT
+ and byte [5 + Bs3Gdte_Tss64 TMPL_WRT_SYSTEM16_OR_FLAT], ~X86_SEL_TYPE_SYS_TSS_BUSY_MASK
+ mov ax, BS3_SEL_TSS64
+
+ %elif BS3_MODE_IS_16BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Gdte_Tss16
+ BS3_EXTERN_SYSTEM16 Bs3Gdte_Tss16DoubleFault
+ TMPL_BEGIN_TEXT
+ and byte [5 + Bs3Gdte_Tss16 TMPL_WRT_SYSTEM16_OR_FLAT], ~X86_SEL_TYPE_SYS_TSS_BUSY_MASK
+ and byte [5 + Bs3Gdte_Tss16DoubleFault TMPL_WRT_SYSTEM16_OR_FLAT], ~X86_SEL_TYPE_SYS_TSS_BUSY_MASK
+ mov ax, BS3_SEL_TSS16
+
+ %elif BS3_MODE_IS_32BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_SYSTEM16 Bs3Gdte_Tss32
+ BS3_EXTERN_SYSTEM16 Bs3Gdte_Tss32DoubleFault
+ BS3_EXTERN_SYSTEM16 Bs3Tss32
+ BS3_EXTERN_SYSTEM16 Bs3Tss32DoubleFault
+ TMPL_BEGIN_TEXT
+ and byte [5 + Bs3Gdte_Tss32 TMPL_WRT_SYSTEM16_OR_FLAT], ~X86_SEL_TYPE_SYS_TSS_BUSY_MASK
+ and byte [5 + Bs3Gdte_Tss32DoubleFault TMPL_WRT_SYSTEM16_OR_FLAT], ~X86_SEL_TYPE_SYS_TSS_BUSY_MASK
+ mov eax, cr3
+ mov [X86TSS32.cr3 + Bs3Tss32 TMPL_WRT_SYSTEM16_OR_FLAT], eax
+ mov [X86TSS32.cr3 + Bs3Tss32DoubleFault TMPL_WRT_SYSTEM16_OR_FLAT], eax
+ mov ax, BS3_SEL_TSS32
+ %else
+ %error "TMPL_BITS"
+ %endif
+ ltr ax
+%endif ; !TMPL_CMN_R86
+
+%if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ;
+ ; Load the LDT.
+ ;
+ mov ax, BS3_SEL_LDT
+ lldt ax
+%endif
+
+ ;
+ ; Load ds and es; clear fs and gs.
+ ;
+%if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ mov ax, BS3_SEL_DATA16
+%else
+ mov ax, RT_CONCAT(BS3_SEL_R0_DS,TMPL_BITS)
+%endif
+ mov ds, ax
+ mov es, ax
+
+%if TMPL_BITS == 16
+ ; For restoring after Bs3Trap* calls below.
+ push ax
+ push ax
+
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jbe .skip_fs_gs
+%endif
+ xor ax, ax
+ mov fs, ax
+ mov gs, ax
+.skip_fs_gs:
+
+ ;
+ ; Set global indicating CPU mode.
+ ;
+ mov byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], TMPL_MODE
+
+ ;
+ ; Install system call handler.
+ ; Always 64-bit in long mode, otherwise according to TMPL_BITS.
+ ;
+%if BS3_MODE_IS_RM_SYS(TMPL_MODE)
+ extern _Bs3TrapSystemCallHandler_rm
+ mov word [ss: BS3_TRAP_SYSCALL*4], _Bs3TrapSystemCallHandler_rm wrt CGROUP16
+ mov word [ss: BS3_TRAP_SYSCALL*4 + 2], CGROUP16
+
+%elif BS3_MODE_IS_16BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_CMN Bs3Trap16SetGate
+ extern TMPL_NM(Bs3TrapSystemCallHandler)
+ BS3_BEGIN_TEXT16
+ TMPL_BEGIN_TEXT
+ push 0 ; cParams
+ push TMPL_NM(Bs3TrapSystemCallHandler) wrt CGROUP16
+ push BS3_SEL_R0_CS16
+ push 3 ; DPL
+ push X86_SEL_TYPE_SYS_286_INT_GATE
+ push BS3_TRAP_SYSCALL
+ BS3_CALL Bs3Trap16SetGate,6
+ add xSP, xCB * 6
+
+%elif BS3_MODE_IS_32BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_CMN Bs3Trap32SetGate
+ extern TMPL_NM(Bs3TrapSystemCallHandler)
+ TMPL_BEGIN_TEXT
+ push 0 ; cParams
+ push dword TMPL_NM(Bs3TrapSystemCallHandler) wrt FLAT
+ push BS3_SEL_R0_CS32
+ push 3 ; DPL
+ push X86_SEL_TYPE_SYS_386_INT_GATE
+ push BS3_TRAP_SYSCALL
+ BS3_CALL Bs3Trap32SetGate,6
+ add xSP, xCB * 6
+
+%elif BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ BS3_EXTERN_CMN Bs3Trap64SetGate
+ extern _Bs3TrapSystemCallHandler_lm64
+ TMPL_BEGIN_TEXT
+ push 0 ; bIst
+ %if BS3_MODE_IS_64BIT_CODE(TMPL_MODE)
+ push _Bs3TrapSystemCallHandler_lm64 wrt FLAT
+ %else
+ push dword 0 ; upper offset
+ push dword _Bs3TrapSystemCallHandler_lm64 wrt FLAT
+ %endif
+ push BS3_SEL_R0_CS64
+ push 3 ; DPL
+ push AMD64_SEL_TYPE_SYS_INT_GATE
+ push BS3_TRAP_SYSCALL
+ BS3_CALL Bs3Trap64SetGate,6
+ add xSP, xCB * 5 + 8
+%else
+ %error "TMPL_BITS"
+%endif
+
+%if TMPL_BITS == 16
+ ; Restoring ds and es after the above calls.
+ pop es
+ pop ds
+%endif
+
+ ;
+ ; Epilogue.
+ ;
+%if TMPL_BITS == 64
+ pop r9
+ pop r8
+%endif
+TONLY16 pop xBX
+ pop xDX
+ pop xCX
+ pop xAX
+%ifdef BS3_STRICT
+ cmp xBP, xSP
+ je .return_stack_ok
+ int3
+.return_stack_ok:
+%endif
+ pop xBP
+ ret
+BS3_PROC_END_MODE Bs3EnteredMode
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-Name.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-Name.asm
new file mode 100644
index 00000000..0cad7a2b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-Name.asm
@@ -0,0 +1,44 @@
+; $Id: bs3-mode-Name.asm $
+;; @file
+; BS3Kit - g_szBs3ModeName_xxx
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_BEGIN_DATA16
+BS3_GLOBAL_NAME_EX RT_CONCAT3(g_szBs3ModeName, _, TMPL_MODE_LNAME), , %strlen(TMPL_MODE_STR)
+BS3_GLOBAL_NAME_EX RT_CONCAT3(_g_szBs3ModeName, _, TMPL_MODE_LNAME), , %strlen(TMPL_MODE_STR)
+ db TMPL_MODE_STR, 0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-NameShortLower.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-NameShortLower.asm
new file mode 100644
index 00000000..08d679c7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-NameShortLower.asm
@@ -0,0 +1,46 @@
+; $Id: bs3-mode-NameShortLower.asm $
+;; @file
+; BS3Kit - g_szBs3ModeName_xxx
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+BS3_BEGIN_DATA16
+%undef MY_MODE_NAME_STR
+%defstr MY_MODE_NAME_STR TMPL_MODE_LNAME
+BS3_GLOBAL_NAME_EX RT_CONCAT3(g_szBs3ModeNameShortLower, _, TMPL_MODE_LNAME), , %strlen(MY_MODE_NAME_STR)
+BS3_GLOBAL_NAME_EX RT_CONCAT3(_g_szBs3ModeNameShortLower, _, TMPL_MODE_LNAME), , %strlen(MY_MODE_NAME_STR)
+ db MY_MODE_NAME_STR, 0
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForLM64.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForLM64.asm
new file mode 100644
index 00000000..9fea5e53
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForLM64.asm
@@ -0,0 +1,141 @@
+; $Id: bs3-mode-PagingGetRootForLM64.asm $
+;; @file
+; BS3Kit - Bs3PagingGetRootForLM64
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%ifdef TMPL_RM
+extern TMPL_NM(Bs3SwitchToPE16)
+extern NAME(Bs3SwitchToRM_pe16)
+%elifdef TMPL_CMN_V86
+extern TMPL_NM(Bs3SwitchToRing0)
+extern TMPL_NM(Bs3SwitchTo16BitV86)
+%endif
+
+BS3_EXTERN_CMN Bs3PagingInitRootForLM
+
+BS3_EXTERN_DATA16 g_PhysPagingRootLM
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3PagingGetRootForLM64(void)
+;
+; @returns eax
+;
+; @uses ax
+;
+; @remarks returns value in EAX, not dx:ax!
+;
+BS3_PROC_BEGIN_MODE Bs3PagingGetRootForLM64, BS3_PBC_NEAR ; Internal function, no far variant necessary.
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootLM)]
+ cmp eax, 0ffffffffh
+ je .init_root
+%ifdef BS3_STRICT
+.return:
+ cmp eax, 1000h
+ jnb .cr3_ok_low
+ hlt
+.cr3_ok_low:
+ cmp eax, 16*_1M
+ jb .cr3_ok_high
+ hlt
+.cr3_ok_high:
+%endif
+ ret
+
+.init_root:
+ push xBP
+ mov xBP, xSP
+BONLY16 push es
+ push sDX
+ push sCX
+ push sBX
+%if TMPL_BITS == 64
+ push r8
+ push r9
+ push r10
+ push r11
+%endif
+
+%ifdef TMPL_RM
+ ;
+ ; We don't want to be restricted to real mode addressing, so
+ ; temporarily switch to 16-bit protected mode.
+ ;
+ call TMPL_NM(Bs3SwitchToPE16)
+ call Bs3PagingInitRootForLM
+ call NAME(Bs3SwitchToRM_pe16)
+%elifdef TMPL_CMN_V86
+ ;
+ ; V8086 mode uses real mode addressing too. Unlikly that we'll
+ ; ever end up here though.
+ ;
+ call TMPL_NM(Bs3SwitchToRing0)
+ call Bs3PagingInitRootForLM
+ call TMPL_NM(Bs3SwitchTo16BitV86)
+%else
+ ;
+ ; Not a problematic addressing mode.
+ ;
+BONLY64 sub rsp, 20h
+ BS3_CALL Bs3PagingInitRootForLM, 0
+BONLY64 add rsp, 20h
+%endif
+
+ ;
+ ; Load the value and return.
+ ;
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootLM)]
+
+%if TMPL_BITS == 64
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+%endif
+ pop sBX
+ pop sCX
+ pop sDX
+BONLY16 pop es
+ leave
+%ifdef BS3_STRICT
+ jmp .return
+%else
+ ret
+%endif
+BS3_PROC_END_MODE Bs3PagingGetRootForLM64
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE16.asm
new file mode 100644
index 00000000..db5c700d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE16.asm
@@ -0,0 +1,55 @@
+; $Id: bs3-mode-PagingGetRootForPAE16.asm $
+;; @file
+; BS3Kit - Bs3PagingGetRootForPAE16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+extern TMPL_NM(Bs3PagingGetRootForPAE32)
+
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3PagingGetRootForPAE16(void)
+;
+; @returns eax
+;
+; @uses ax
+;
+; @remarks returns value in EAX, not dx:ax!
+;
+BS3_PROC_BEGIN_MODE Bs3PagingGetRootForPAE16, BS3_PBC_NEAR ; Internal function, no far variant necessary.
+ jmp TMPL_NM(Bs3PagingGetRootForPAE32)
+BS3_PROC_END_MODE Bs3PagingGetRootForPAE16
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE32.asm
new file mode 100644
index 00000000..9f48d3bd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPAE32.asm
@@ -0,0 +1,127 @@
+; $Id: bs3-mode-PagingGetRootForPAE32.asm $
+;; @file
+; BS3Kit - Bs3PagingGetRootForPAE32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%ifdef TMPL_RM
+extern TMPL_NM(Bs3SwitchToPE16)
+extern NAME(Bs3SwitchToRM_pe16)
+%elifdef TMPL_CMN_V86
+extern TMPL_NM(Bs3SwitchToRing0)
+extern TMPL_NM(Bs3SwitchTo16BitV86)
+%endif
+
+BS3_EXTERN_CMN Bs3PagingInitRootForPAE
+
+BS3_EXTERN_DATA16 g_PhysPagingRootPAE
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3PagingGetRootForPAE32(void)
+;
+; @returns eax
+;
+; @uses ax
+;
+; @remarks returns value in EAX, not dx:ax!
+;
+BS3_PROC_BEGIN_MODE Bs3PagingGetRootForPAE32, BS3_PBC_NEAR ; Internal function, no far variant necessary.
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootPAE)]
+ cmp eax, 0ffffffffh
+ je .init_root
+ ret
+
+.init_root:
+ push xBP
+ mov xBP, xSP
+BONLY16 push es
+ push sDX
+ push sCX
+ push sBX
+%if TMPL_BITS == 64
+ push r8
+ push r9
+ push r10
+ push r11
+%endif
+
+%ifdef TMPL_RM
+ ;
+ ; We don't want to be restricted to real mode addressing, so
+ ; temporarily switch to 16-bit protected mode.
+ ;
+ call TMPL_NM(Bs3SwitchToPE16)
+ call Bs3PagingInitRootForPAE
+ call NAME(Bs3SwitchToRM_pe16)
+
+%elifdef TMPL_CMN_V86
+ ;
+ ; V8086 mode uses real mode addressing too. Unlikly that we'll
+ ; ever end up here though.
+ ;
+ call TMPL_NM(Bs3SwitchToRing0)
+ call Bs3PagingInitRootForPAE
+ call TMPL_NM(Bs3SwitchTo16BitV86)
+%else
+ ;
+ ; Not a problematic addressing mode.
+ ;
+BONLY64 sub rsp, 20h
+ BS3_CALL Bs3PagingInitRootForPAE, 0
+BONLY64 add rsp, 20h
+%endif
+
+ ;
+ ; Load the value and return.
+ ;
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootPAE)]
+
+%if TMPL_BITS == 64
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+%endif
+ pop sBX
+ pop sCX
+ pop sDX
+BONLY16 pop es
+ leave
+ ret
+BS3_PROC_END_MODE Bs3PagingGetRootForPAE32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP16.asm
new file mode 100644
index 00000000..88c22bd3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP16.asm
@@ -0,0 +1,55 @@
+; $Id: bs3-mode-PagingGetRootForPP16.asm $
+;; @file
+; BS3Kit - Bs3PagingGetRootForPP16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+extern TMPL_NM(Bs3PagingGetRootForPP32)
+
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3PagingGetRootForPP16(void)
+;
+; @returns eax
+;
+; @uses ax
+;
+; @remarks returns value in EAX, not dx:ax!
+;
+BS3_PROC_BEGIN_MODE Bs3PagingGetRootForPP16, BS3_PBC_NEAR ; Internal function, no far variant necessary.
+ jmp TMPL_NM(Bs3PagingGetRootForPP32)
+BS3_PROC_END_MODE Bs3PagingGetRootForPP16
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP32.asm
new file mode 100644
index 00000000..865fac3e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-PagingGetRootForPP32.asm
@@ -0,0 +1,142 @@
+; $Id: bs3-mode-PagingGetRootForPP32.asm $
+;; @file
+; BS3Kit - Bs3PagingGetRootForPP32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+%ifdef TMPL_RM
+extern TMPL_NM(Bs3SwitchToPE16)
+extern NAME(Bs3SwitchToRM_pe16)
+%elifdef TMPL_CMN_V86
+extern TMPL_NM(Bs3SwitchToRing0)
+extern TMPL_NM(Bs3SwitchTo16BitV86)
+%endif
+
+BS3_EXTERN_CMN Bs3PagingInitRootForPP
+
+BS3_EXTERN_DATA16 g_PhysPagingRootPP
+TMPL_BEGIN_TEXT
+
+
+;;
+; @cproto BS3_DECL(uint32_t) Bs3PagingGetRootForPP32(void)
+;
+; @returns eax
+;
+; @uses ax
+;
+; @remarks returns value in EAX, not dx:ax!
+;
+BS3_PROC_BEGIN_MODE Bs3PagingGetRootForPP32, BS3_PBC_NEAR ; Internal function, no far variant necessary.
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootPP)]
+ cmp eax, 0ffffffffh
+ je .init_root
+%ifdef BS3_STRICT
+.return:
+ cmp eax, 1000h
+ jnb .cr3_ok_low
+ hlt
+.cr3_ok_low:
+ cmp eax, 16*_1M
+ jb .cr3_ok_high
+ hlt
+.cr3_ok_high:
+%endif
+ ret
+
+.init_root:
+ push xBP
+ mov xBP, xSP
+BONLY16 push es
+ push sDX
+ push sCX
+ push sBX
+%if TMPL_BITS == 64
+ push r8
+ push r9
+ push r10
+ push r11
+%endif
+
+%ifdef TMPL_RM
+ ;
+ ; We don't want to be restricted to real mode addressing, so
+ ; temporarily switch to 16-bit protected mode.
+ ;
+ call TMPL_NM(Bs3SwitchToPE16)
+ call Bs3PagingInitRootForPP
+ call NAME(Bs3SwitchToRM_pe16)
+
+%elifdef TMPL_CMN_V86
+ ;
+ ; V8086 mode uses real mode addressing too. Unlikly that we'll
+ ; ever end up here though.
+ ;
+ call TMPL_NM(Bs3SwitchToRing0)
+ call Bs3PagingInitRootForPP
+ call TMPL_NM(Bs3SwitchTo16BitV86)
+%else
+ ;
+ ; Not a problematic addressing mode.
+ ;
+BONLY64 sub rsp, 20h
+ BS3_CALL Bs3PagingInitRootForPP, 0
+BONLY64 add rsp, 20h
+%endif
+
+ ;
+ ; Load the value and return.
+ ;
+ mov eax, [BS3_DATA16_WRT(g_PhysPagingRootPP)]
+
+%if TMPL_BITS == 64
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+%endif
+ pop sBX
+ pop sCX
+ pop sDX
+BONLY16 pop es
+ leave
+%ifdef BS3_STRICT
+ jmp .return
+%else
+ ret
+%endif
+BS3_PROC_END_MODE Bs3PagingGetRootForPP32
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchTo32BitAndCallC.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchTo32BitAndCallC.asm
new file mode 100644
index 00000000..23c18cd7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchTo32BitAndCallC.asm
@@ -0,0 +1,164 @@
+; $Id: bs3-mode-SwitchTo32BitAndCallC.asm $
+;; @file
+; BS3Kit - bs3SwitchTo32BitAndCallC
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+TMPL_BEGIN_TEXT
+
+%ifdef BS3_STRICT
+BS3_EXTERN_CMN Bs3Panic
+%endif
+
+%if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+BS3_EXTERN_CMN Bs3SelRealModeCodeToFlat
+%endif
+
+%if TMPL_MODE == BS3_MODE_RM
+extern NAME(Bs3SwitchToPE32_rm)
+extern NAME(Bs3SwitchToRM_pe32)
+%elif !BS3_MODE_IS_32BIT_CODE(TMPL_MODE)
+BS3_EXTERN_CMN Bs3SwitchTo32Bit
+ %if BS3_MODE_IS_16BIT_CODE_NO_V86(TMPL_MODE)
+extern _Bs3SwitchTo16Bit_c32
+ %elif BS3_MODE_IS_V86(TMPL_MODE)
+extern _Bs3SwitchTo16BitV86_c32
+ %elif !BS3_MODE_IS_32BIT_CODE(TMPL_MODE)
+extern _Bs3SwitchTo64_c32
+ %endif
+%endif
+
+
+
+;;
+; @cproto BS3_MODE_PROTO_STUB(int, Bs3SwitchTo32BitAndCallC,(PFNBS3FARADDRCONV fpfnCall, unsigned cbParams, ...));
+;
+BS3_PROC_BEGIN_MODE Bs3SwitchTo32BitAndCallC, BS3_PBC_HYBRID
+ BS3_CALL_CONV_PROLOG 4
+TONLY16 inc xBP
+ push xBP
+ mov xBP, xSP
+ push xSI
+
+ ;
+ ; Push the arguments first.
+ ;
+TONLY16 mov si, [xBP + xCB + cbCurRetAddr + sCB]
+TNOT16 mov esi, [xBP + xCB + cbCurRetAddr + sCB]
+%ifdef BS3_STRICT
+ test xSI, 3
+ jz .cbParams_ok
+ call Bs3Panic
+.cbParams_ok:
+ cmp byte [BS3_DATA16_WRT(g_bBs3CurrentMode)], TMPL_MODE
+ je .mode_ok
+ call Bs3Panic
+.mode_ok:
+%endif
+ add xSI, sCB - 1 ; round it up to nearest push size / dword.
+ and xSI, ~(sCB - 1)
+ jz .done_pushing ; skip if zero
+.push_more:
+ push xPRE [xBP + xCB + cbCurRetAddr + sCB + xCB + xSI - xCB]
+ sub xSI, xCB
+ jnz .push_more
+ mov xSI, xAX ; restore xSI
+.done_pushing:
+
+ ;
+ ; Load fpfnCall into eax.
+ ;
+%if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ push sPRE [xBP + xCB + cbCurRetAddr]
+ BS3_CALL Bs3SelRealModeCodeToFlat, 1
+ add xSP, sCB
+ rol eax, 16
+ mov ax, dx
+ rol eax, 16
+%else
+ mov eax, [xBP + xCB + cbCurRetAddr]
+%endif
+
+ ;
+ ; Switch to 32-bit mode, if this is real mode pick PE32.
+ ;
+%if TMPL_MODE == BS3_MODE_RM
+ call NAME(Bs3SwitchToPE32_rm)
+ BS3_SET_BITS 32
+%elif !BS3_MODE_IS_32BIT_CODE(TMPL_MODE)
+ call Bs3SwitchTo32Bit
+ BS3_SET_BITS 32
+%endif
+
+ ;
+ ; Make the call.
+ ;
+ call eax
+
+ ;
+ ; Return, preserving xAX.
+ ;
+%if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ mov edx, eax
+ shr edx, 16
+%endif
+%if TMPL_MODE == BS3_MODE_RM
+ call NAME(Bs3SwitchToRM_pe32)
+%elif BS3_MODE_IS_16BIT_CODE_NO_V86(TMPL_MODE)
+ call _Bs3SwitchTo16Bit_c32
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ call _Bs3SwitchTo16BitV86_c32
+%elif !BS3_MODE_IS_32BIT_CODE(TMPL_MODE)
+ call _Bs3SwitchTo64_c32
+%endif
+ BS3_SET_BITS TMPL_BITS
+
+ ; Epilog.
+ lea xSP, [xBP - xCB]
+ pop xSI
+ pop xBP
+TONLY16 dec xBP
+ BS3_CALL_CONV_EPILOG 4
+ BS3_HYBRID_RET
+BS3_PROC_END_MODE Bs3SwitchTo32BitAndCallC
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM16.asm
new file mode 100644
index 00000000..f8210fcc
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM16.asm
@@ -0,0 +1,136 @@
+; $Id: bs3-mode-SwitchToLM16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToLM16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit long mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToLM16(void);
+;
+; @uses Nothing (except possibly high 32-bit and/or upper 64-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was in 32-bit
+; or 64-bit mode. It doesn't not preserve the callers ring, but
+; instead changes to ring-0.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToLM16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM16, BS3_PBC_NEAR
+%ifdef TMPL_LM16
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ push ax
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ pop ax
+ ret
+
+%elifdef TMPL_CMN_LM
+ ;
+ ; Already in long mode, just switch to 16-bit.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchTo16Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo16Bit)
+
+%else
+ ;
+ ; Switch to LM32 and then switch to 64-bits (IDT & TSS are the same for
+ ; LM16, LM32 and LM64, unlike the rest).
+ ;
+ ; (The long mode switching code is going via 32-bit protected mode, so
+ ; Bs3SwitchToLM32 contains the actual code for switching to avoid
+ ; unnecessary 32-bit -> 64-bit -> 32-bit trips.)
+ ;
+ extern TMPL_NM(Bs3SwitchToLM32)
+ call TMPL_NM(Bs3SwitchToLM32)
+ BS3_SET_BITS 32
+
+ extern _Bs3SwitchTo16Bit_c32
+ %if TMPL_BITS == 16
+ sub esp, 2
+ shr dword [esp], 16
+ %elif TMPL_BITS == 64
+ pop dword [esp + 4]
+ %endif
+ jmp _Bs3SwitchTo16Bit_c32
+%endif
+BS3_PROC_END_MODE Bs3SwitchToLM16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToLM16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToLM16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToLM16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToLM16_Safe
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM32.asm
new file mode 100644
index 00000000..5ac49b74
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM32.asm
@@ -0,0 +1,203 @@
+; $Id: bs3-mode-SwitchToLM32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToLM32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit long mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToLM32(void);
+;
+; @uses Nothing (except possibly high 32-bit and/or upper 64-bit register parts).
+;
+; @remarks There are no IDT or TSS differences between LM16, LM32 and LM64 (unlike
+; PE16 & PE32, PP16 & PP32, and PAE16 & PAE32).
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was in 16-bit
+; or 64-bit mode. It doesn't not preserve the callers ring, but
+; instead changes to ring-0.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToLM32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM32, BS3_PBC_NEAR
+%ifdef TMPL_LM32
+ ret
+
+%elifdef TMPL_CMN_LM
+ ;
+ ; Already in long mode, just switch to 32-bit.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchTo32Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo32Bit)
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToLM32)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToLM32)
+
+%else
+ %if TMPL_BITS == 16
+ push word 0 ; save space for extending the return value.
+ %endif
+
+ ;
+ ; Switch to 32-bit protected mode (for identify mapped pages).
+ ;
+ extern TMPL_NM(Bs3SwitchToPE32)
+ call TMPL_NM(Bs3SwitchToPE32)
+ BS3_SET_BITS 32
+ %if TMPL_BITS == 16
+ jmp .thirty_two_bit_segment
+BS3_BEGIN_TEXT32
+BS3_GLOBAL_LOCAL_LABEL .thirty_two_bit_segment
+ %endif
+
+ push eax
+ push ecx
+ push edx
+ pushfd
+
+ ;
+ ; Make sure both PAE and PSE are enabled (requires pentium pro).
+ ;
+ mov eax, cr4
+ mov ecx, eax
+ or eax, X86_CR4_PAE | X86_CR4_PSE
+ cmp eax, ecx
+ je .cr4_is_fine
+ mov cr4, eax
+.cr4_is_fine:
+
+ ;
+ ; Get the page directory (returned in eax).
+ ; Will lazy init page tables.
+ ;
+ extern NAME(Bs3PagingGetRootForLM64_pe32)
+ call NAME(Bs3PagingGetRootForLM64_pe32)
+
+ cli
+ mov cr3, eax
+
+ ;
+ ; Enable long mode in EFER.
+ ;
+ mov ecx, MSR_K6_EFER
+ rdmsr
+ or eax, MSR_K6_EFER_LME
+ wrmsr
+
+ ;
+ ; Enable paging and thereby activating LM64.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT32
+ mov eax, cr0
+ or eax, X86_CR0_PG
+ mov cr0, eax
+ jmp .in_lm32
+.in_lm32:
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ extern NAME(Bs3EnteredMode_lm32)
+ call NAME(Bs3EnteredMode_lm32)
+
+ ;
+ ; Load full 64-bit GDT base address from 64-bit segment.
+ ;
+ jmp dword BS3_SEL_R0_CS64:.load_full_gdt_base wrt FLAT
+.load_full_gdt_base:
+ BS3_SET_BITS 64
+ lgdt [Bs3Lgdt_Gdt wrt FLAT]
+ push BS3_SEL_R0_CS32
+ push .back_to_32bit wrt FLAT
+ o64 retf
+.back_to_32bit:
+ BS3_SET_BITS 32
+
+ ;
+ ; Restore ecx, eax and flags (IF).
+ ;
+ %if TMPL_BITS == 16
+ movzx eax, word [esp + 16 + 2] ; Load return address.
+ add eax, BS3_ADDR_BS3TEXT16 ; Convert it to a flat address.
+ mov [esp + 16], eax ; Store it in the place right for 32-bit returns.
+ %endif
+ popfd
+ pop edx
+ pop ecx
+ pop eax
+ ret
+
+ %if TMPL_BITS != 32
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToLM32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToLM32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToLM32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM64.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM64.asm
new file mode 100644
index 00000000..f906044c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToLM64.asm
@@ -0,0 +1,114 @@
+; $Id: bs3-mode-SwitchToLM64.asm $
+;; @file
+; BS3Kit - Bs3SwitchToLM64
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 64-bit long mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToLM64(void);
+;
+; @uses Nothing (except possibly high 32-bit and/or upper 64-bit register parts).
+;
+; @remarks Obviously returns to 64-bit mode, even if the caller was in 16-bit
+; or 32-bit mode. It doesn't not preserve the callers ring, but
+; instead changes to ring-0.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToLM64_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM64, BS3_PBC_NEAR
+%ifdef TMPL_LM64
+ ret
+
+%elifdef TMPL_CMN_LM
+ ;
+ ; Already in long mode, just switch to 64-bit.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchTo64Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo64Bit)
+
+%else
+ ;
+ ; Switch to LM32 and then switch to 64-bits (IDT & TSS are the same for
+ ; LM16, LM32 and LM64, unlike the rest).
+ ;
+ ; (The long mode switching code is going via 32-bit protected mode, so
+ ; Bs3SwitchToLM32 contains the actual code for switching to avoid
+ ; unnecessary 32-bit -> 64-bit -> 32-bit trips.)
+ ;
+ %ifdef TMPL_16BIT
+ and esp, 0ffffh
+ push word [esp] ; copy return address.
+ and word [esp + 2], 0 ; clear upper return address
+ add dword [esp], BS3_ADDR_BS3TEXT16 ; Add base of return segment, completing 32-bit conversion.
+ %endif
+ extern TMPL_NM(Bs3SwitchToLM32)
+ call TMPL_NM(Bs3SwitchToLM32)
+ BS3_SET_BITS 32
+
+ extern _Bs3SwitchTo64Bit_c32
+ jmp _Bs3SwitchTo64Bit_c32
+%endif
+BS3_PROC_END_MODE Bs3SwitchToLM64
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToLM64, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToLM64)
+ BS3_SET_BITS 64
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c64
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c64
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c64
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c64
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToLM64
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16.asm
new file mode 100644
index 00000000..15ced7ab
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16.asm
@@ -0,0 +1,243 @@
+; $Id: bs3-mode-SwitchToPAE16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAE16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%ifndef TMPL_PAE16
+BS3_BEGIN_TEXT16
+extern NAME(Bs3EnteredMode_pae16)
+ %ifdef TMPL_PAE32
+ BS3_EXTERN_CMN Bs3SwitchTo16Bit
+ %endif
+TMPL_BEGIN_TEXT
+%endif
+
+
+;;
+; Switch to 16-bit paged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPAE16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAE16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16, BS3_PBC_NEAR
+%ifdef TMPL_PAE16
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ push ax
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ pop ax
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPAE16)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPAE16)
+
+%else
+ ;
+ ; Switch to 16-bit text segment and prepare for returning in 16-bit mode.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16 ; Adjust the return address.
+ add xSP, xCB - 2
+
+ ; Must be in 16-bit segment when calling Bs3SwitchToRM and Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ %ifdef TMPL_PAE32
+ ;
+ ; No need to go to real-mode here, we use the same CR3 and stuff.
+ ; Just switch to 32-bit mode and call the Bs3EnteredMode routine to
+ ; load the right descriptor tables.
+ ;
+ call Bs3SwitchTo16Bit
+ BS3_SET_BITS 16
+ call NAME(Bs3EnteredMode_pae16)
+ ret
+ %else
+
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push eax
+ push ecx
+ pushfd
+
+ ;
+ ; Get the page directory (returned in eax).
+ ; Will lazy init page tables (in 16-bit prot mode).
+ ;
+ extern NAME(Bs3PagingGetRootForPAE16_rm)
+ call NAME(Bs3PagingGetRootForPAE16_rm)
+
+ cli
+ mov cr3, eax
+
+ ;
+ ; Make sure PAE, PSE, and VME are enabled (former two require pentium pro, latter 486).
+ ;
+ mov eax, cr4
+ mov ecx, eax
+ or eax, X86_CR4_PAE | X86_CR4_PSE | X86_CR4_VME
+ cmp eax, ecx
+ je .cr4_is_fine
+ mov cr4, eax
+.cr4_is_fine:
+
+ ;
+ ; Load the GDT and enable PP16.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp BS3_SEL_R0_CS16:.reload_cs_and_stuff
+.reload_cs_and_stuff:
+
+ ;
+ ; Convert the (now) real mode stack to 16-bit.
+ ;
+ mov ax, .stack_fix_return
+ extern NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+ jmp NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+.stack_fix_return:
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ call NAME(Bs3EnteredMode_pae16)
+
+ ;
+ ; Load full 32-bit GDT base address from 32-bit segment.
+ ;
+ push ds
+ mov ax, BS3_SEL_SYSTEM16
+ mov ds, ax
+ jmp dword BS3_SEL_R0_CS32:.load_full_gdt_base wrt FLAT
+.load_full_gdt_base:
+ BS3_SET_BITS 32
+ lgdt [Bs3Lgdt_Gdt wrt BS3SYSTEM16]
+ jmp BS3_SEL_R0_CS16:.back_to_16bit
+.back_to_16bit:
+ BS3_SET_BITS 16
+ pop ds
+
+ popfd
+ pop ecx
+ pop eax
+ ret
+
+ %endif ; !TMPL_PP32
+ %if TMPL_BITS != 16
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAE16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAE16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPAE16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPAE16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPAE16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_32.asm
new file mode 100644
index 00000000..130a239a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_32.asm
@@ -0,0 +1,114 @@
+; $Id: bs3-mode-SwitchToPAE16_32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAE16_32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit code under 16-bit PAE paged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE16_32(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAE16_32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_32, BS3_PBC_NEAR
+%ifdef TMPL_PAE16_32
+ ret
+
+%else
+ ;
+ ; Make sure we're in the 16-bit segment and then call Bs3SwitchToPAE16.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ extern TMPL_NM(Bs3SwitchToPAE16)
+ call TMPL_NM(Bs3SwitchToPAE16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to 32-bit mode.
+ ;
+ extern _Bs3SwitchTo32Bit_c16
+ %if TMPL_BITS == 16
+ jmp _Bs3SwitchTo32Bit_c16
+ %else
+ call _Bs3SwitchTo32Bit_c16
+ BS3_SET_BITS 32
+ %if TMPL_BITS == 32
+ ret
+ %else
+ ret 4 ; Return and pop 4 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAE16_32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAE16_32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPAE16_32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_V86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_V86.asm
new file mode 100644
index 00000000..e20ab30a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE16_V86.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-mode-SwitchToPAE16_V86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAE16_V86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit PAE paged protected mode with 16-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPAE16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to v8086 16-bit mode, even if the caller was
+; in 16-bit, 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAE16_V86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_V86, BS3_PBC_NEAR
+%ifdef TMPL_PAE16_V86
+ ret
+
+%else
+ ;
+ ; Convert the return address and jump to the 16-bit code segment.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16
+ add xSP, (TMPL_BITS - 16) / 8
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to 16-bit PAE16 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPAE16)
+ call TMPL_NM(Bs3SwitchToPAE16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to v8086 mode (return address is already 16-bit).
+ ;
+ extern _Bs3SwitchTo16BitV86_c16
+ jmp _Bs3SwitchTo16BitV86_c16
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAE16_V86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_V86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAE16_V86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPAE16_V86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE16_V86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPAE16_V86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPAE16_V86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32.asm
new file mode 100644
index 00000000..768bc013
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32.asm
@@ -0,0 +1,202 @@
+; $Id: bs3-mode-SwitchToPAE32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAE32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to PAE paged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPAE32(void);
+;
+; @uses Nothing (except high 32-bit register parts), upper part of ESP is
+; cleared if caller is in 16-bit mode.
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode. It doesn't not preserve the callers
+; ring, but instead changes to ring-0.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAE32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE32, BS3_PBC_NEAR
+%ifdef TMPL_PAE32
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPAE32)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPAE32)
+
+%else
+ ;
+ ; Switch to real mode.
+ ;
+ %if TMPL_BITS != 32
+ %if TMPL_BITS > 32
+ shl xPRE [xSP], 32 ; Adjust the return address from 64-bit to 32-bit.
+ add rsp, xCB - 4
+ %else
+ push word 0 ; Reserve space to expand the return address.
+ %endif
+ %endif
+ %if TMPL_BITS != 16
+ ; Must be in 16-bit segment when calling Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push eax
+ push ecx
+ pushfd
+
+ ;
+ ; Get the page directory (returned in eax).
+ ; Will lazy init page tables (in 16-bit prot mode).
+ ;
+ extern NAME(Bs3PagingGetRootForPAE32_rm)
+ call NAME(Bs3PagingGetRootForPAE32_rm)
+
+ cli
+ mov cr3, eax
+
+ ;
+ ; Make sure PAE, PSE, and VME are enabled (former two require pentium pro, latter 486).
+ ;
+ mov eax, cr4
+ mov ecx, eax
+ or eax, X86_CR4_PAE | X86_CR4_PSE | X86_CR4_VME
+ cmp eax, ecx
+ je .cr4_is_fine
+ mov cr4, eax
+.cr4_is_fine:
+
+ ;
+ ; Load the GDT and enable PE32.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp BS3_SEL_R0_CS32:dword .thirty_two_bit wrt FLAT
+BS3_BEGIN_TEXT32
+BS3_GLOBAL_LOCAL_LABEL .thirty_two_bit
+
+ ;
+ ; Convert the (now) real mode stack pointer to 32-bit flat.
+ ;
+ xor eax, eax
+ mov ax, ss
+ shl eax, 4
+ and esp, 0ffffh
+ add esp, eax
+
+ mov ax, BS3_SEL_R0_SS32
+ mov ss, ax
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ extern NAME(Bs3EnteredMode_pae32)
+ call NAME(Bs3EnteredMode_pae32)
+
+ ; Load full 32-bit GDT base address.
+ lgdt [Bs3Lgdt_Gdt wrt FLAT]
+
+ ;
+ ; Restore ecx, eax and flags (IF).
+ ;
+ %if TMPL_BITS < 32
+ movzx eax, word [esp + 12 + 2] ; Load return address.
+ add eax, BS3_ADDR_BS3TEXT16 ; Convert it to a flat address.
+ mov [esp + 12], eax ; Store it in the place right for 32-bit returns.
+ %endif
+ popfd
+ pop ecx
+ pop eax
+ ret
+
+ %if TMPL_BITS != 32
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAE32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAE32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPAE32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32_16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32_16.asm
new file mode 100644
index 00000000..6a69d35e
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAE32_16.asm
@@ -0,0 +1,132 @@
+; $Id: bs3-mode-SwitchToPAE32_16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAE32_16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit code under 32-bit PAE paged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPAE32_16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAE32_16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE32_16, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PAE32_16
+ ret
+
+%elif TMPL_MODE == BS3_MODE_PAE32
+ extern BS3_CMN_NM(Bs3SwitchTo16Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo16Bit)
+
+%else
+ ;
+ ; Switch to PAE32.
+ ;
+ extern TMPL_NM(Bs3SwitchToPAE32)
+ call TMPL_NM(Bs3SwitchToPAE32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Make sure we're in the 16-bit segment and then do the switch to 16-bit.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ extern _Bs3SwitchTo16Bit_c32
+ %if TMPL_BITS == 32
+ jmp _Bs3SwitchTo16Bit_c32
+ %else
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS 16
+ %if TMPL_BITS == 16
+ ret
+ %else
+ ret 6 ; Return and pop 6 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAE32_16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE32_16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAE32_16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPAE32_16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAE32_16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPAE32_16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPAE32_16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAEV86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAEV86.asm
new file mode 100644
index 00000000..288d0823
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPAEV86.asm
@@ -0,0 +1,118 @@
+; $Id: bs3-mode-SwitchToPAEV86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPAEV86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit v8086 PAE paged protected mode with 32-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPAEV86(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit v8086 mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPAEV86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAEV86, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PAEV86
+ ret
+
+%else
+ ;
+ ; Switch to 32-bit PAE32 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPAE32)
+ call TMPL_NM(Bs3SwitchToPAE32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Switch to v8086 mode after adjusting the return address.
+ ;
+ %if TMPL_BITS == 16
+ push word [esp]
+ mov word [esp + 2], 0
+ %elif TMPL_BITS == 64
+ pop dword [esp + 4]
+ %endif
+ extern _Bs3SwitchTo16BitV86_c32
+ jmp _Bs3SwitchTo16BitV86_c32
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPAEV86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAEV86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPAEV86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPAEV86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPAEV86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPAEV86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPAEV86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16.asm
new file mode 100644
index 00000000..058642be
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16.asm
@@ -0,0 +1,198 @@
+; $Id: bs3-mode-SwitchToPE16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPE16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit unpaged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPE16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16, BS3_PBC_NEAR
+%ifdef TMPL_PE16
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ push ax
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ pop ax
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPE16)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPE16)
+
+%else
+ ;
+ ; Switch to 16-bit mode and prepare for returning in 16-bit mode.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16 ; Adjust the return address.
+ add xSP, xCB - 2
+
+ ; Must be in 16-bit segment when calling Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push ax
+ push cx
+ pushf
+ cli
+
+ ;
+ ; Load the GDT and enable PE16.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ smsw ax
+ or ax, X86_CR0_PE
+ lmsw ax
+
+ ;
+ ; Convert from real mode stack to protected mode stack.
+ ;
+ mov ax, .p16_stack
+ extern NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+ jmp NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+.p16_stack:
+
+ ;
+ ; Call routine for doing mode specific setups.
+ ;
+ extern NAME(Bs3EnteredMode_pe16)
+ call NAME(Bs3EnteredMode_pe16)
+
+ ;
+ ; Load full 32-bit GDT base address from 32-bit segment, if 386+ CPU.
+ ;
+ BS3_EXTERN_DATA16 g_uBs3CpuDetected
+ BS3_BEGIN_TEXT16
+ cmp byte [g_uBs3CpuDetected], BS3CPU_80386
+ jb .old_cpu_skip_32bit_lgdt
+ push ds
+ mov ax, BS3_SEL_SYSTEM16
+ mov ds, ax
+ jmp dword BS3_SEL_R0_CS32:.load_full_gdt_base wrt FLAT
+.load_full_gdt_base:
+ BS3_SET_BITS 32
+ lgdt [Bs3Lgdt_Gdt wrt BS3SYSTEM16]
+ jmp BS3_SEL_R0_CS16:.back_to_16bit
+.back_to_16bit:
+ BS3_SET_BITS 16
+ pop ds
+.old_cpu_skip_32bit_lgdt:
+
+ popf
+ pop cx
+ pop ax
+ ret
+
+ %if TMPL_BITS != 16
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPE16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPE16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPE16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPE16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPE16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_32.asm
new file mode 100644
index 00000000..587e6446
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_32.asm
@@ -0,0 +1,114 @@
+; $Id: bs3-mode-SwitchToPE16_32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPE16_32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit code under 16-bit unpaged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE16_32(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPE16_32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_32, BS3_PBC_NEAR
+%ifdef TMPL_PE16_32
+ ret
+
+%else
+ ;
+ ; Make sure we're in the 16-bit segment and then call Bs3SwitchToPE16.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ extern TMPL_NM(Bs3SwitchToPE16)
+ call TMPL_NM(Bs3SwitchToPE16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to 32-bit mode.
+ ;
+ extern _Bs3SwitchTo32Bit_c16
+ %if TMPL_BITS == 16
+ jmp _Bs3SwitchTo32Bit_c16
+ %else
+ call _Bs3SwitchTo32Bit_c16
+ BS3_SET_BITS 32
+ %if TMPL_BITS == 32
+ ret
+ %else
+ ret 4 ; Return and pop 4 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPE16_32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPE16_32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPE16_32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_V86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_V86.asm
new file mode 100644
index 00000000..84fb26d9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE16_V86.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-mode-SwitchToPE16_V86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPE16_V86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit unpaged protected mode with 16-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit v8086 mode, even if the caller was
+; in 16-bit, 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPE16_V86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_V86, BS3_PBC_NEAR
+%ifdef TMPL_PE16_V86
+ ret
+
+%else
+ ;
+ ; Convert the return address and jump to the 16-bit code segment.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16
+ add xSP, (TMPL_BITS - 16) / 8
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to 16-bit PE16 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPE16)
+ call TMPL_NM(Bs3SwitchToPE16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to v8086 mode (return address is already 16-bit).
+ ;
+ extern _Bs3SwitchTo16BitV86_c16
+ jmp _Bs3SwitchTo16BitV86_c16
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPE16_V86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_V86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPE16_V86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPE16_V86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE16_V86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPE16_V86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPE16_V86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32.asm
new file mode 100644
index 00000000..9030dd5b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32.asm
@@ -0,0 +1,179 @@
+; $Id: bs3-mode-SwitchToPE32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPE32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit unpaged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE32(void);
+;
+; @uses Nothing (except high 32-bit register parts), upper part of ESP is
+; cleared if caller is in 16-bit mode.
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPE32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE32, BS3_PBC_NEAR
+%ifdef TMPL_PE32
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPE32)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPE32)
+
+%else
+ ;
+ ; Switch to real mode.
+ ;
+ %if TMPL_BITS != 32
+ %if TMPL_BITS > 32
+ shl xPRE [xSP], 32 ; Adjust the return address from 64-bit to 32-bit.
+ add rsp, xCB - 4
+ %else
+ push word 0 ; Reserve space to expand the return address.
+ %endif
+ %endif
+ %if TMPL_BITS != 16
+ ; Must be in 16-bit segment when calling Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push eax
+ pushfd
+ cli
+
+ ;
+ ; Load the GDT and enable PE32.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ mov eax, cr0
+ or eax, X86_CR0_PE
+ mov cr0, eax
+ jmp BS3_SEL_R0_CS32:dword .thirty_two_bit wrt FLAT
+BS3_BEGIN_TEXT32
+BS3_GLOBAL_LOCAL_LABEL .thirty_two_bit
+
+ ;
+ ; Convert the (now) real mode stack pointer to 32-bit flat.
+ ;
+ xor eax, eax
+ mov ax, ss
+ shl eax, 4
+ and esp, 0ffffh
+ add esp, eax
+
+ mov ax, BS3_SEL_R0_SS32
+ mov ss, ax
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ extern NAME(Bs3EnteredMode_pe32)
+ call NAME(Bs3EnteredMode_pe32)
+
+ ; Load full 32-bit GDT base address.
+ lgdt [Bs3Lgdt_Gdt wrt FLAT]
+
+ ;
+ ; Restore eax and flags (IF).
+ ;
+ %if TMPL_BITS < 32
+ and esp, 0ffffh ; Make sure the high word is zero.
+ movzx eax, word [esp + 8 + 2] ; Load return address.
+ add eax, BS3_ADDR_BS3TEXT16 ; Convert it to a flat address.
+ mov [esp + 8], eax ; Store it in the place right for 32-bit returns.
+ %endif
+ popfd
+ pop eax
+ ret
+
+ %if TMPL_BITS != 32
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPE32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPE32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPE32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32_16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32_16.asm
new file mode 100644
index 00000000..4dc5b926
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPE32_16.asm
@@ -0,0 +1,132 @@
+; $Id: bs3-mode-SwitchToPE32_16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPE32_16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit code under 32-bit unpaged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE32_16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPE32_16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE32_16, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PE32_16
+ ret
+
+%elif TMPL_MODE == BS3_MODE_PE32
+ extern BS3_CMN_NM(Bs3SwitchTo16Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo16Bit)
+
+%else
+ ;
+ ; Switch to PE32.
+ ;
+ extern TMPL_NM(Bs3SwitchToPE32)
+ call TMPL_NM(Bs3SwitchToPE32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Make sure we're in the 16-bit segment and then do the switch to 16-bit.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ extern _Bs3SwitchTo16Bit_c32
+ %if TMPL_BITS == 32
+ jmp _Bs3SwitchTo16Bit_c32
+ %else
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS 16
+ %if TMPL_BITS == 16
+ ret
+ %else
+ ret 6 ; Return and pop 6 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPE32_16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE32_16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPE32_16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPE32_16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPE32_16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPE32_16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPE32_16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPEV86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPEV86.asm
new file mode 100644
index 00000000..7613f9ef
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPEV86.asm
@@ -0,0 +1,118 @@
+; $Id: bs3-mode-SwitchToPEV86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPEV86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit v8086 unpaged protected mode with 32-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPEV86(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit v8086 mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPEV86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPEV86, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PEV86
+ ret
+
+%else
+ ;
+ ; Switch to 32-bit PE32 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPE32)
+ call TMPL_NM(Bs3SwitchToPE32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Switch to v8086 mode after adjusting the return address.
+ ;
+ %if TMPL_BITS == 16
+ push word [esp]
+ mov word [esp + 2], 0
+ %elif TMPL_BITS == 64
+ pop dword [esp + 4]
+ %endif
+ extern _Bs3SwitchTo16BitV86_c32
+ jmp _Bs3SwitchTo16BitV86_c32
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPEV86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPEV86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPEV86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPEV86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPEV86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPEV86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPEV86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16.asm
new file mode 100644
index 00000000..3986939d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16.asm
@@ -0,0 +1,258 @@
+; $Id: bs3-mode-SwitchToPP16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%ifndef TMPL_PP16
+BS3_BEGIN_TEXT16
+extern NAME(Bs3EnteredMode_pp16)
+ %ifdef TMPL_PP32
+ BS3_EXTERN_CMN Bs3SwitchTo16Bit
+ %endif
+TMPL_BEGIN_TEXT
+%endif
+
+
+;;
+; Switch to 16-bit paged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPP16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPP16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16, BS3_PBC_NEAR
+%ifdef TMPL_PP16
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ push ax
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ pop ax
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPP16)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPP16)
+
+%else
+
+ ;
+ ; Switch to 16-bit text segment and prepare for returning in 16-bit mode.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16 ; Adjust the return address.
+ add xSP, xCB - 2
+
+ ; Must be in 16-bit segment when calling Bs3SwitchToRM and Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ %ifdef TMPL_PP32
+ ;
+ ; No need to go to real-mode here, we use the same CR3 and stuff.
+ ; Just switch to 32-bit mode and call the Bs3EnteredMode routine to
+ ; load the right descriptor tables.
+ ;
+ call Bs3SwitchTo16Bit
+ BS3_SET_BITS 16
+ call NAME(Bs3EnteredMode_pp16)
+ ret
+ %else
+
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push eax
+ push ecx
+ pushfd
+%ifdef BS3_STRICT
+ mov ax, ds
+ cmp ax, BS3_ADDR_BS3DATA16 >> 4
+ je .real_mode_ds_ok
+ hlt
+.real_mode_ds_ok:
+%endif
+
+ ;
+ ; Get the page directory (returned in eax).
+ ; Will lazy init page tables (in 16-bit prot mode).
+ ;
+ extern NAME(Bs3PagingGetRootForPP16_rm)
+ call NAME(Bs3PagingGetRootForPP16_rm)
+
+ cli
+ mov cr3, eax
+
+ ;
+ ; Make sure PAE is really off and that PSE is enabled when supported.
+ ;
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_BEGIN_TEXT16
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
+ jz .cr4_is_fine
+ mov eax, cr4
+ mov ecx, eax
+ and eax, ~(X86_CR4_PAE | X86_CR4_PSE)
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_PSE >> 8)
+ jz .no_pse
+ or eax, X86_CR4_PSE
+.no_pse:
+ cmp eax, ecx
+ je .cr4_is_fine
+ mov cr4, eax
+.cr4_is_fine:
+
+ ;
+ ; Load the GDT and enable PP16.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp BS3_SEL_R0_CS16:.reload_cs_and_stuff
+.reload_cs_and_stuff:
+
+ ;
+ ; Convert the (now) real mode stack to 16-bit.
+ ;
+ mov ax, .stack_fix_return
+ extern NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+ jmp NAME(Bs3ConvertRMStackToP16UsingCxReturnToAx_c16)
+.stack_fix_return:
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ call NAME(Bs3EnteredMode_pp16)
+
+ ;
+ ; Load full 32-bit GDT base address from 32-bit segment.
+ ;
+ push ds
+ mov ax, BS3_SEL_SYSTEM16
+ mov ds, ax
+ jmp dword BS3_SEL_R0_CS32:.load_full_gdt_base wrt FLAT
+.load_full_gdt_base:
+ BS3_SET_BITS 32
+ lgdt [Bs3Lgdt_Gdt wrt BS3SYSTEM16]
+ jmp BS3_SEL_R0_CS16:.back_to_16bit
+.back_to_16bit:
+ BS3_SET_BITS 16
+ pop ds
+
+ popfd
+ pop ecx
+ pop eax
+ ret
+
+ %endif ; !TMPL_PP32
+ %if TMPL_BITS != 16
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPP16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPP16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPP16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPP16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPP16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_32.asm
new file mode 100644
index 00000000..0dc97c3a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_32.asm
@@ -0,0 +1,114 @@
+; $Id: bs3-mode-SwitchToPP16_32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP16_32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit code under 16-bit paged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPP16_32(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPP16_32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_32, BS3_PBC_NEAR
+%ifdef TMPL_PP16_32
+ ret
+
+%else
+ ;
+ ; Make sure we're in the 16-bit segment and then call Bs3SwitchToPP16.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+ extern TMPL_NM(Bs3SwitchToPP16)
+ call TMPL_NM(Bs3SwitchToPP16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to 32-bit mode.
+ ;
+ extern _Bs3SwitchTo32Bit_c16
+ %if TMPL_BITS == 16
+ jmp _Bs3SwitchTo32Bit_c16
+ %else
+ call _Bs3SwitchTo32Bit_c16
+ BS3_SET_BITS 32
+ %if TMPL_BITS == 32
+ ret
+ %else
+ ret 4 ; Return and pop 4 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPP16_32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPP16_32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPP16_32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_V86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_V86.asm
new file mode 100644
index 00000000..0647fa90
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP16_V86.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-mode-SwitchToPP16_V86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP16_V86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit paged protected mode with 16-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPP16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to v8086 16-bit mode, even if the caller was
+; in 16-bit, 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPP16_V86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_V86, BS3_PBC_NEAR
+%ifdef TMPL_PP16_V86
+ ret
+
+%else
+ ;
+ ; Convert the return address and jump to the 16-bit code segment.
+ ;
+ %if TMPL_BITS != 16
+ shl xPRE [xSP], TMPL_BITS - 16
+ add xSP, (TMPL_BITS - 16) / 8
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to 16-bit PP16 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPP16)
+ call TMPL_NM(Bs3SwitchToPP16)
+ BS3_SET_BITS 16
+
+ ;
+ ; Switch to v8086 mode (return address is already 16-bit).
+ ;
+ extern _Bs3SwitchTo16BitV86_c16
+ jmp _Bs3SwitchTo16BitV86_c16
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPP16_V86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_V86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPP16_V86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPP16_V86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP16_V86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPP16_V86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPP16_V86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32.asm
new file mode 100644
index 00000000..455127f5
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32.asm
@@ -0,0 +1,209 @@
+; $Id: bs3-mode-SwitchToPP32.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP32
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 32-bit paged protected mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPE32(void);
+;
+; @uses Nothing (except high 32-bit register parts), upper part of ESP is
+; cleared if caller is in 16-bit mode.
+;
+; @remarks Obviously returns to 32-bit mode, even if the caller was
+; in 16-bit or 64-bit mode. It doesn't not preserve the callers
+; ring, but instead changes to ring-0.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPP32_Safe), function, 0
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP32, BS3_PBC_NEAR
+%ifdef TMPL_PP32
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPP32)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToPP32)
+
+%else
+ ;
+ ; Switch to real mode.
+ ;
+ %if TMPL_BITS != 32
+ %if TMPL_BITS > 32
+ shl xPRE [xSP], 32 ; Adjust the return address from 64-bit to 32-bit.
+ add rsp, xCB - 4
+ %else
+ push word 0 ; Reserve space to expand the return address.
+ %endif
+ %endif
+ %if TMPL_BITS != 16
+ ; Must be in 16-bit segment when calling Bs3SwitchTo16Bit.
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+ %endif
+
+ ;
+ ; Switch to real mode.
+ ;
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ push eax
+ push ecx
+ pushfd
+
+ ;
+ ; Make sure PAE is really off and that PSE is on when supported.
+ ;
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_BEGIN_TEXT16
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
+ jz .cr4_is_fine
+ mov eax, cr4
+ mov ecx, eax
+ and eax, ~(X86_CR4_PAE | X86_CR4_PSE)
+ test byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_PSE >> 8)
+ jz .no_pse
+ or eax, X86_CR4_PSE
+.no_pse:
+ cmp eax, ecx
+ je .cr4_is_fine
+ mov cr4, eax
+.cr4_is_fine:
+
+ ;
+ ; Get the page directory (returned in eax).
+ ; Will lazy init page tables (in 16-bit prot mode).
+ ;
+ extern NAME(Bs3PagingGetRootForPP32_rm)
+ call NAME(Bs3PagingGetRootForPP32_rm)
+
+ cli
+ mov cr3, eax
+
+ ;
+ ; Load the GDT and enable PE32.
+ ;
+BS3_EXTERN_SYSTEM16 Bs3LgdtDef_Gdt
+BS3_EXTERN_SYSTEM16 Bs3Lgdt_Gdt
+BS3_BEGIN_TEXT16
+ mov ax, BS3SYSTEM16
+ mov ds, ax
+ lgdt [Bs3LgdtDef_Gdt] ; Will only load 24-bit base!
+
+ mov eax, cr0
+ or eax, X86_CR0_PE | X86_CR0_PG
+ mov cr0, eax
+ jmp BS3_SEL_R0_CS32:dword .thirty_two_bit wrt FLAT
+BS3_BEGIN_TEXT32
+BS3_GLOBAL_LOCAL_LABEL .thirty_two_bit
+ ;
+ ; Convert the (now) real mode stack pointer to 32-bit flat.
+ ;
+ xor eax, eax
+ mov ax, ss
+ shl eax, 4
+ and esp, 0ffffh
+ add esp, eax
+
+ mov ax, BS3_SEL_R0_SS32
+ mov ss, ax
+
+ ;
+ ; Call rountine for doing mode specific setups.
+ ;
+ extern NAME(Bs3EnteredMode_pp32)
+ call NAME(Bs3EnteredMode_pp32)
+
+ ; Load full 32-bit GDT base address.
+ lgdt [Bs3Lgdt_Gdt wrt FLAT]
+
+ ;
+ ; Restore ecx, eax and flags (IF).
+ ;
+ %if TMPL_BITS < 32
+ movzx eax, word [esp + 12 + 2] ; Load return address.
+ add eax, BS3_ADDR_BS3TEXT16 ; Convert it to a flat address.
+ mov [esp + 12], eax ; Store it in the place right for 32-bit returns.
+ %endif
+ popfd
+ pop ecx
+ pop eax
+ ret
+
+ %if TMPL_BITS != 32
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPP32
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP32, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPP32)
+ BS3_SET_BITS 32
+
+ ; Jmp to common code for the tedious conversion.
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ extern _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn_c32
+ %else
+ extern _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ jmp _Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn_c32
+ %endif
+ BS3_SET_BITS 16
+BS3_PROC_END_MODE Bs3SwitchToPP32
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32_16.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32_16.asm
new file mode 100644
index 00000000..41f14beb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPP32_16.asm
@@ -0,0 +1,132 @@
+; $Id: bs3-mode-SwitchToPP32_16.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPP32_16
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit code under 32-bit paged protected mode sys/tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPP32_16(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPP32_16_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP32_16, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PP32_16
+ ret
+
+%elif TMPL_MODE == BS3_MODE_PP32
+ extern BS3_CMN_NM(Bs3SwitchTo16Bit)
+ jmp BS3_CMN_NM(Bs3SwitchTo16Bit)
+
+%else
+ ;
+ ; Switch to PP32.
+ ;
+ extern TMPL_NM(Bs3SwitchToPP32)
+ call TMPL_NM(Bs3SwitchToPP32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Make sure we're in the 16-bit segment and then do the switch to 16-bit.
+ ;
+ %if TMPL_BITS != 16
+ jmp .sixteen_bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+.sixteen_bit_segment:
+ %endif
+ extern _Bs3SwitchTo16Bit_c32
+ %if TMPL_BITS == 32
+ jmp _Bs3SwitchTo16Bit_c32
+ %else
+ call _Bs3SwitchTo16Bit_c32
+ BS3_SET_BITS 16
+ %if TMPL_BITS == 16
+ ret
+ %else
+ ret 6 ; Return and pop 6 bytes of "parameters" (unused return address).
+ %endif
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPP32_16
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP32_16, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPP32_16)
+
+ %if BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvRealModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPP32_16
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPP32_16_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPP32_16)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPP32_16_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPPV86.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPPV86.asm
new file mode 100644
index 00000000..9d492b9a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToPPV86.asm
@@ -0,0 +1,118 @@
+; $Id: bs3-mode-SwitchToPPV86.asm $
+;; @file
+; BS3Kit - Bs3SwitchToPPV86
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; Switch to 16-bit v8086 paged protected mode with 32-bit sys+tss from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToPPV86(void);
+;
+; @uses Nothing (except high 32-bit register parts).
+;
+; @remarks Obviously returns to 16-bit v8086 mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToPPV86_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToPPV86, BS3_PBC_NEAR
+%if TMPL_MODE == BS3_MODE_PPV86
+ ret
+
+%else
+ ;
+ ; Switch to 32-bit PP32 and from there to V8086.
+ ;
+ extern TMPL_NM(Bs3SwitchToPP32)
+ call TMPL_NM(Bs3SwitchToPP32)
+ BS3_SET_BITS 32
+
+ ;
+ ; Switch to v8086 mode after adjusting the return address.
+ ;
+ %if TMPL_BITS == 16
+ push word [esp]
+ mov word [esp + 2], 0
+ %elif TMPL_BITS == 64
+ pop dword [esp + 4]
+ %endif
+ extern _Bs3SwitchTo16BitV86_c32
+ jmp _Bs3SwitchTo16BitV86_c32
+%endif
+BS3_PROC_END_MODE Bs3SwitchToPPV86
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPPV86, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToPPV86)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToPPV86
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SwitchHlpConvFlatRetToRetfProtMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToPPV86_Safe, BS3_PBC_NEAR
+ call Bs3SwitchHlpConvFlatRetToRetfProtMode ; Special internal function. Uses nothing, but modifies the stack.
+ call TMPL_NM(Bs3SwitchToPPV86)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToPPV86_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToRM.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToRM.asm
new file mode 100644
index 00000000..3e67cb3a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-SwitchToRM.asm
@@ -0,0 +1,411 @@
+; $Id: bs3-mode-SwitchToRM.asm $
+;; @file
+; BS3Kit - Bs3SwitchToRM
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_SYSTEM16 Bs3Gdt
+%if TMPL_MODE == BS3_MODE_PE16
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+BS3_EXTERN_CMN Bs3KbdWrite
+BS3_EXTERN_CMN Bs3KbdWait
+%endif
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+%if TMPL_MODE == BS3_MODE_PE16
+BS3_BEGIN_DATA16
+;; Where to start restoring stack.
+g_ResumeSp: dw 0xfeed
+;; Where to start restoring stack.
+g_ResumeSs: dw 0xface
+%endif
+
+TMPL_BEGIN_TEXT
+
+
+;;
+; Switch to real mode from any other mode.
+;
+; @cproto BS3_DECL(void) Bs3SwitchToRM(void);
+;
+; @uses GPRs and EFLAGS are unchanged (except high 32-bit register (AMD64) parts).
+; CS is loaded with CGROUP16.
+; SS:[RE]SP is converted to real mode address.
+; DS and ES are loaded with BS3DATA16_GROUP.
+; FS and GS are loaded with zero if present.
+;
+; @remarks Obviously returns to 16-bit mode, even if the caller was
+; in 32-bit or 64-bit mode.
+;
+; @remarks Does not require 20h of parameter scratch space in 64-bit mode.
+;
+%if TMPL_BITS == 16
+BS3_GLOBAL_NAME_EX TMPL_NM(Bs3SwitchToRM_Safe), function , 0
+%endif
+BS3_PROC_BEGIN_MODE Bs3SwitchToRM, BS3_PBC_NEAR
+%ifdef TMPL_RM
+ push ax
+ mov ax, BS3_SEL_DATA16
+ mov ds, ax
+ mov es, ax
+ pop ax
+ ret
+
+%elif BS3_MODE_IS_V86(TMPL_MODE)
+ ;
+ ; V8086 - Switch to 16-bit ring-0 and call worker for that mode.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+ extern %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToRM)
+ jmp %[BS3_MODE_R0_NM_ %+ TMPL_MODE](Bs3SwitchToRM)
+
+%else
+ ;
+ ; Protected mode.
+ ; 80286 requirements for PE16 clutters the code a little.
+ ;
+ %if TMPL_MODE == BS3_MODE_PE16
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ ja .do_386_prologue
+ push ax
+ push bx
+ pushf
+ push word 1
+ jmp .done_prologue
+ %endif
+.do_386_prologue:
+ push sAX
+ push sBX
+ sPUSHF
+ %if TMPL_MODE == BS3_MODE_PE16
+ push word 0
+ %elif BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ push sDX
+ push sCX
+ %endif
+.done_prologue:
+
+ ;
+ ; Get to 16-bit ring-0 and disable interrupts.
+ ;
+ extern BS3_CMN_NM(Bs3SwitchToRing0)
+ call BS3_CMN_NM(Bs3SwitchToRing0)
+
+ cli
+
+ %if TMPL_MODE == BS3_MODE_PE16
+ ;
+ ; On 80286 we must reset the CPU to get back to real mode.
+ ;
+ CPU 286
+ pop ax
+ push ax
+ test ax, ax
+ jz .is_386_or_better
+
+ ; Save registers and flags, storing SS:SP in at a known global address.
+%ifdef BS3_STRICT
+ mov ax, 0feedh
+ mov bx, 0faceh
+%endif
+ push di
+ push si
+ push bp
+ push bx
+ push dx
+ push cx
+ push ax
+ pushf
+
+ ; Convert ss:sp to real mode address.
+ BS3_EXTERN_CMN Bs3SelProtFar32ToFlat32
+ mov ax, sp
+ push ss
+ push 0
+ push ax
+ call Bs3SelProtFar32ToFlat32
+ add sp, 6
+
+ mov [g_ResumeSp], ax
+ shl dx, 12
+ mov [g_ResumeSs], dx
+
+ ; Setup resume vector.
+ mov bx, BS3_SEL_R0_SS16
+ mov es, bx
+ mov word [es:467h], .resume
+ mov word [es:467h+2], BS3_SEL_TEXT16
+
+ mov al, 0fh | 80h
+ out 70h, al ; set register index
+ in al, 80h
+ mov al, 0ah ; shutdown action command - no EOI, no 287 reset.
+ out 71h, al ; set cmos[f] = al - invoke testResume as early as possible.
+ in al, 71h ; flush
+
+ %if 0 ; for testing in VM
+ CPU 386
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ mov eax, cr0
+ and ax, ~X86_CR0_PE
+ mov cr0, eax
+ jmp BS3_SEL_TEXT16:.resume
+ %endif
+
+ ; Port A reset. (FYI: tripple fault does not do the trick)
+ in al, 92h
+ or al, 1
+ out 92h, al
+ in al, 80h ; flush
+ mov cx, 0ffffh
+.reset_delay:
+ loop .reset_delay
+
+ ; Keyboard controller reset.
+ call Bs3KbdWait
+ push 0 ; zero data (whatever.
+ push 0fh ; KBD_CCMD_RESET
+ call Bs3KbdWrite
+.forever:
+ jmp .forever
+
+ ; This is the resume point. We should be in real mode now, at least in theory.
+.resume:
+ mov ax, BS3_SEL_DATA16
+ mov ds, ax
+ mov es, ax
+ mov ax, [g_ResumeSp]
+ mov ss, [g_ResumeSs]
+ mov sp, ax
+
+ popf
+ pop ax
+ pop cx
+ pop dx
+ pop bx
+ pop bp
+ pop si
+ pop di
+ %ifdef BS3_STRICT
+ cmp ax, 0feedh
+ jne .bad_286_rm_switch
+ cmp bx, 0faceh
+ jne .bad_286_rm_switch
+ %endif
+ jmp .enter_mode
+
+ %ifdef BS3_STRICT
+.bad_286_rm_switch:
+ mov ax, 0e00h + 'Q'
+ mov bx, 0ff00h
+ int 10h
+ jmp .bad_286_rm_switch
+ %endif
+
+ CPU 386
+ %elif TMPL_BITS != 16
+ ;
+ ; Must be in 16-bit segment when calling Bs3SwitchTo16Bit.
+ ;
+ jmp .sixteen_bit_segment wrt FLAT
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .sixteen_bit_segment
+
+ extern BS3_CMN_NM(Bs3SwitchTo16Bit)
+ call BS3_CMN_NM(Bs3SwitchTo16Bit)
+ BS3_SET_BITS 16
+ %endif
+ ;
+ ; Before exiting to real mode we must load sensible selectors into the
+ ; segment registers so the hidden parts (which doesn't get reloaded in
+ ; real mode) are real mode compatible.
+ ;
+ ; ASSUMES BS3_SEL_R0_SS16 and BS3_SEL_R0_CS16 are both maxed out and
+ ; has no funny bits set!
+ ;
+.is_386_or_better:
+;; @todo Testcase: Experiment leaving weird stuff in the hidden segment registers.
+ mov ax, BS3_SEL_R0_DS16
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ;
+ ; Exit to real mode.
+ ;
+ mov eax, cr0
+ and eax, X86_CR0_NO_PE_NO_PG
+ mov cr0, eax
+ jmp CGROUP16:.reload_cs
+.reload_cs:
+
+ ;
+ ; Convert the stack (now 16-bit prot) to real mode.
+ ;
+ mov ax, BS3_SEL_SYSTEM16
+ mov ds, ax
+ mov bx, ss
+ and bx, X86_SEL_MASK ; ASSUMES GDT stack selector
+ mov al, [bx + 4 + Bs3Gdt]
+ mov ah, [bx + 7 + Bs3Gdt]
+ add sp, [bx + 2 + Bs3Gdt] ; ASSUMES not expand down segment.
+ adc ax, 0
+ %ifdef BS3_STRICT
+ test ax, 0fff0h
+ jz .stack_conv_ok
+ int3
+.stack_conv_ok:
+ %endif
+ shl ax, 12
+ mov ss, ax
+ %if TMPL_BITS != 16
+ and esp, 0ffffh
+ %endif
+
+ %if BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ ;
+ ; Clear the long mode enable bit.
+ ;
+ mov ecx, MSR_K6_EFER
+ rdmsr
+ and eax, ~MSR_K6_EFER_LME
+ wrmsr
+ %endif
+
+ ;
+ ; Call routine for doing mode specific setups.
+ ;
+.enter_mode:
+ extern NAME(Bs3EnteredMode_rm)
+ call NAME(Bs3EnteredMode_rm)
+
+ %if TMPL_MODE == BS3_MODE_PE16
+ pop ax
+ test ax, ax
+ jz .do_386_epilogue
+ popf
+ pop bx
+ pop ax
+ ret
+ %endif
+.do_386_epilogue:
+ %if BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ pop ecx
+TONLY64 pop eax
+ pop edx
+TONLY64 pop eax
+ %endif
+ popfd
+TONLY64 pop eax
+ pop ebx
+TONLY64 pop eax
+ pop eax
+TONLY64 add sp, 4
+ retn (TMPL_BITS - 16) / 8
+
+ %if TMPL_BITS != 16
+TMPL_BEGIN_TEXT
+ %endif
+%endif
+BS3_PROC_END_MODE Bs3SwitchToRM
+
+
+%if TMPL_BITS == 16
+;;
+; Custom far stub.
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_MODE Bs3SwitchToRM, BS3_PBC_FAR
+ inc bp
+ push bp
+ mov bp, sp
+
+ ; Call the real thing.
+ call TMPL_NM(Bs3SwitchToRM)
+
+ %if !BS3_MODE_IS_RM_OR_V86(TMPL_MODE)
+ ; Jmp to common code for the tedious conversion.
+ BS3_EXTERN_CMN Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ jmp Bs3SwitchHlpConvProtModeRetfPopBpDecBpAndReturn
+ %else
+ pop bp
+ dec bp
+ retf
+ %endif
+BS3_PROC_END_MODE Bs3SwitchToRM
+
+%else
+;;
+; Safe far return to non-BS3TEXT16 code.
+BS3_EXTERN_CMN Bs3SelFlatCodeToRealMode
+BS3_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_PROC_BEGIN_MODE Bs3SwitchToRM_Safe, BS3_PBC_NEAR
+ %if TMPL_BITS == 64
+ push xAX
+ push xCX
+ sub xSP, 20h
+
+ mov xCX, [xSP + xCB*2 + 20h]
+ call Bs3SelFlatCodeToRealMode ; well behaved assembly function, only clobbers ecx
+ mov [xSP + xCB*2 + 20h + 4], eax
+
+ add xSP, 20h
+ pop xCX
+ pop xAX
+ add xSP, 4
+ %else
+ xchg eax, [xSP]
+ push xAX
+ call Bs3SelFlatCodeToRealMode ; well behaved assembly function, only clobbers eax
+ add xSP, 4
+ xchg [xSP], eax
+ %endif
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+ retf
+BS3_PROC_END_MODE Bs3SwitchToRM_Safe
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.c
new file mode 100644
index 00000000..a7adf446
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.c
@@ -0,0 +1,380 @@
+/* $Id: bs3-mode-TestDoModes.c $ */
+/** @file
+ * BS3Kit - Bs3TestDoModes
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if TMPL_MODE == BS3_MODE_RM
+# define BS3_USE_RM_TEXT_SEG 1 /* Real mode version in RMTEXT16 segment to save space. */
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#else
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#endif
+#include "bs3-mode-TestDoModes.h"
+
+
+
+/**
+ * Warns about CPU modes that must be skipped.
+ *
+ * It will try not warn about modes for which there are no tests.
+ *
+ * @param paEntries The mode test entries.
+ * @param cEntries The number of tests.
+ * @param bCpuType The CPU type byte (see #BS3CPU_TYPE_MASK).
+ * @param fHavePae Whether the CPU has PAE.
+ * @param fHaveLongMode Whether the CPU does long mode.
+ */
+static void bs3TestWarnAboutSkippedModes(PCBS3TESTMODEENTRY paEntries, unsigned cEntries,
+ uint8_t bCpuType, bool fHavePae, bool fHaveLongMode)
+{
+ bool fComplained286 = false;
+ bool fComplained386 = false;
+ bool fComplainedPAE = false;
+ bool fComplainedAMD64 = false;
+ unsigned i;
+
+ /*
+ * Complaint run.
+ */
+ for (i = 0; i < cEntries; i++)
+ {
+ if ( !fComplained286
+ && paEntries[i].pfnDoPE16)
+ {
+ if (bCpuType < BS3CPU_80286)
+ {
+ Bs3Printf("Only executing real-mode tests as no 80286+ CPU was detected.\n");
+ break;
+ }
+ fComplained286 = true;
+ }
+
+ if ( !fComplained386
+ && ( paEntries[i].pfnDoPE16_32
+ || paEntries[i].pfnDoPE16_V86
+ || paEntries[i].pfnDoPE32
+ || paEntries[i].pfnDoPE32_16
+ || paEntries[i].pfnDoPEV86
+ || paEntries[i].pfnDoPP16
+ || paEntries[i].pfnDoPP16_32
+ || paEntries[i].pfnDoPP16_V86
+ || paEntries[i].pfnDoPP32
+ || paEntries[i].pfnDoPP32_16
+ || paEntries[i].pfnDoPPV86) )
+ {
+ if (bCpuType < BS3CPU_80386)
+ {
+ Bs3Printf("80286 CPU: Only executing 16-bit protected and real mode tests.\n");
+ break;
+ }
+ fComplained386 = true;
+ }
+
+ if ( !fComplainedPAE
+ && ( paEntries[i].pfnDoPAE16
+ || paEntries[i].pfnDoPAE16_32
+ || paEntries[i].pfnDoPAE16_V86
+ || paEntries[i].pfnDoPAE32
+ || paEntries[i].pfnDoPAE32_16
+ || paEntries[i].pfnDoPAEV86) )
+ {
+ if (!fHavePae)
+ {
+ Bs3Printf("PAE and long mode tests will be skipped.\n");
+ break;
+ }
+ fComplainedPAE = true;
+ }
+
+ if ( !fComplainedAMD64
+ && ( paEntries[i].pfnDoLM16
+ || paEntries[i].pfnDoLM32
+ || paEntries[i].pfnDoLM64) )
+ {
+ if (!fHaveLongMode)
+ {
+ Bs3Printf("Long mode tests will be skipped.\n");
+ break;
+ }
+ fComplainedAMD64 = true;
+ }
+ }
+}
+
+#undef Bs3TestDoModes
+BS3_MODE_DEF(void, Bs3TestDoModes,(PCBS3TESTMODEENTRY paEntries, size_t cEntries))
+{
+ bool const fVerbose = true;
+ bool const fDoV86Modes = true;
+ bool const fDoWeirdV86Modes = true;
+ uint16_t const uCpuDetected = g_uBs3CpuDetected;
+ uint8_t const bCpuType = uCpuDetected & BS3CPU_TYPE_MASK;
+ bool const fHavePae = RT_BOOL(uCpuDetected & BS3CPU_F_PAE);
+ bool const fHaveLongMode = RT_BOOL(uCpuDetected & BS3CPU_F_LONG_MODE);
+ unsigned i;
+
+#if 1 /* debug. */
+ Bs3Printf("Bs3TestDoModes: uCpuDetected=%#x fHavePae=%d fHaveLongMode=%d\n", uCpuDetected, fHavePae, fHaveLongMode);
+#endif
+ bs3TestWarnAboutSkippedModes(paEntries, cEntries, bCpuType, fHavePae, fHaveLongMode);
+
+ /*
+ * The real run.
+ */
+ for (i = 0; i < cEntries; i++)
+ {
+ const char *pszFmtStr = "Error #%u (%#x) in %s!\n";
+ bool fSkipped = true;
+ uint8_t bErrNo;
+
+ if (paEntries[i].pszSubTest != NULL)
+ Bs3TestSub(paEntries[i].pszSubTest);
+
+#define PRE_DO_CALL(a_szModeName) do { if (fVerbose) Bs3TestPrintf("...%s\n", a_szModeName); } while (0)
+#define CHECK_RESULT(a_szModeName) \
+ do { \
+ if (bErrNo != BS3TESTDOMODE_SKIPPED) \
+ { \
+ /*Bs3Printf("bErrNo=%#x %s\n", bErrNo, a_szModeName);*/ \
+ fSkipped = false; \
+ if (bErrNo != 0) \
+ Bs3TestFailedF(pszFmtStr, bErrNo, bErrNo, a_szModeName); \
+ } \
+ } while (0)
+
+ if (paEntries[i].pfnDoRM)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_rm);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInRM)(CONV_TO_RM_FAR16(paEntries[i].pfnDoRM));
+ CHECK_RESULT(g_szBs3ModeName_rm);
+ }
+
+ if (bCpuType < BS3CPU_80286)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ /*
+ * Unpaged prot mode.
+ */
+ if (paEntries[i].pfnDoPE16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPE16));
+ CHECK_RESULT(g_szBs3ModeName_pe16);
+ }
+ if (bCpuType < BS3CPU_80386)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].pfnDoPE16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPE16_32), BS3_MODE_PE16_32);
+ CHECK_RESULT(g_szBs3ModeName_pe16_32);
+ }
+
+ if (paEntries[i].pfnDoPE16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPE16_V86));
+ CHECK_RESULT(g_szBs3ModeName_pe16_v86);
+ }
+
+ if (paEntries[i].pfnDoPE32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnDoPE32), BS3_MODE_PE32);
+ CHECK_RESULT(g_szBs3ModeName_pe32);
+ }
+
+ if (paEntries[i].pfnDoPE32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPE32_16));
+ CHECK_RESULT(g_szBs3ModeName_pe32_16);
+ }
+
+ if (paEntries[i].pfnDoPEV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pev86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPEV86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPEV86));
+ CHECK_RESULT(g_szBs3ModeName_pev86);
+ }
+
+ /*
+ * Paged protected mode.
+ */
+ if (paEntries[i].pfnDoPP16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPP16));
+ CHECK_RESULT(g_szBs3ModeName_pp16);
+ }
+
+ if (paEntries[i].pfnDoPP16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPP16_32), BS3_MODE_PP16_32);
+ CHECK_RESULT(g_szBs3ModeName_pp16_32);
+ }
+
+ if (paEntries[i].pfnDoPP16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPP16_V86));
+ CHECK_RESULT(g_szBs3ModeName_pp16_v86);
+ }
+
+ if (paEntries[i].pfnDoPP32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(paEntries[i].pfnDoPP32), BS3_MODE_PP32);
+ CHECK_RESULT(g_szBs3ModeName_pp32);
+ }
+
+ if (paEntries[i].pfnDoPP32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPP32_16));
+ CHECK_RESULT(g_szBs3ModeName_pp32_16);
+ }
+
+ if (paEntries[i].pfnDoPPV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_ppv86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPPV86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPPV86));
+ CHECK_RESULT(g_szBs3ModeName_ppv86);
+ }
+
+ /*
+ * Protected mode with PAE paging.
+ */
+ if (!fHavePae)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].pfnDoPAE16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPAE16));
+ CHECK_RESULT(g_szBs3ModeName_pae16);
+ }
+
+ if (paEntries[i].pfnDoPAE16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE16_32), BS3_MODE_PAE16_32);
+ CHECK_RESULT(g_szBs3ModeName_pae16_32);
+ }
+
+ if (paEntries[i].pfnDoPAE16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPAE16_V86));
+ CHECK_RESULT(g_szBs3ModeName_pae16_v86);
+ }
+
+ if (paEntries[i].pfnDoPAE32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE32), BS3_MODE_PAE32);
+ CHECK_RESULT(g_szBs3ModeName_pae32);
+ }
+
+ if (paEntries[i].pfnDoPAE32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPAE32_16));
+ CHECK_RESULT(g_szBs3ModeName_pae32_16);
+ }
+
+ if (paEntries[i].pfnDoPAEV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_paev86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAEV86)(CONV_TO_RM_FAR16(paEntries[i].pfnDoPAEV86));
+ CHECK_RESULT(g_szBs3ModeName_paev86);
+ }
+
+ /*
+ * Long mode.
+ */
+ if (!fHaveLongMode)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].pfnDoLM16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoLM16));
+ CHECK_RESULT(g_szBs3ModeName_lm16);
+ }
+
+ if (paEntries[i].pfnDoLM32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM32)(CONV_TO_FLAT(paEntries[i].pfnDoLM32));
+ CHECK_RESULT(g_szBs3ModeName_lm32);
+ }
+
+ if (paEntries[i].pfnDoLM64)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm64);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(paEntries[i].pfnDoLM64), BS3_MODE_LM64);
+ CHECK_RESULT(g_szBs3ModeName_lm64);
+ }
+
+ if (fSkipped)
+ Bs3TestSkipped("skipped\n");
+ }
+ Bs3TestSubDone();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.h
new file mode 100644
index 00000000..26c93009
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModes.h
@@ -0,0 +1,99 @@
+/* $Id: bs3-mode-TestDoModes.h $ */
+/** @file
+ * BS3Kit - Common header for the Bs3TestDoModes family.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3_mode_TestDoModes_h
+#define BS3KIT_INCLUDED_bs3_mode_TestDoModes_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "bs3kit.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def CONV_TO_FLAT
+ * Get flat address. In 16-bit the parameter is a real mode far address, while
+ * in 32-bit and 64-bit modes it is already flat.
+ */
+/** @def CONV_TO_PROT_FAR16
+ * Get a 32-bit value that makes a protected mode far 16:16 address.
+ */
+/** @def CONV_TO_RM_FAR16
+ * Get a 32-bit value that makes a real mode far 16:16 address. In 16-bit mode
+ * this is already what we've got, except must be converted to uint32_t.
+ */
+#if ARCH_BITS == 16
+# define CONV_TO_FLAT(a_fpfn) (((uint32_t)BS3_FP_SEG(a_fpfn) << 4) + BS3_FP_OFF(a_fpfn))
+# define CONV_TO_PROT_FAR16(a_fpfn) RT_MAKE_U32(BS3_FP_OFF(a_fpfn), Bs3SelRealModeCodeToProtMode(BS3_FP_SEG(a_fpfn)))
+# define CONV_TO_RM_FAR16(a_fpfn) RT_MAKE_U32(BS3_FP_OFF(a_fpfn), BS3_FP_SEG(a_fpfn))
+#else
+# define CONV_TO_FLAT(a_fpfn) ((uint32_t)(uintptr_t)(a_fpfn))
+# define CONV_TO_PROT_FAR16(a_fpfn) Bs3SelFlatCodeToProtFar16((uint32_t)(uintptr_t)(a_fpfn))
+# define CONV_TO_RM_FAR16(a_fpfn) Bs3SelFlatCodeToRealMode( (uint32_t)(uintptr_t)(a_fpfn))
+#endif
+
+
+/*********************************************************************************************************************************
+* Assembly Symbols *
+*********************************************************************************************************************************/
+/* These are in the same code segment as the main API, so no FAR necessary. */
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInRM)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPE16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPE16_32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPE16_V86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPE32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPE32_16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPEV86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPP16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPP16_32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPP16_V86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPP32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPP32_16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPPV86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAE16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAE16_32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAE16_V86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAE32)(uint32_t uFlatAddrCallback, uint8_t bMode);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAE32_16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInPAEV86)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInLM16)(uint32_t uCallbackFarPtr);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInLM32)(uint32_t uFlatAddrCallback);
+BS3_DECL_NEAR(uint8_t) TMPL_NM(Bs3TestCallDoerInLM64)(uint32_t uFlatAddrCallback, uint8_t bMode);
+
+#endif /* !BS3KIT_INCLUDED_bs3_mode_TestDoModes_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMax.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMax.c
new file mode 100644
index 00000000..3bb86b9d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMax.c
@@ -0,0 +1,380 @@
+/* $Id: bs3-mode-TestDoModesByMax.c $ */
+/** @file
+ * BS3Kit - Bs3TestDoModesByMax
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if TMPL_MODE == BS3_MODE_RM
+# define BS3_USE_RM_TEXT_SEG 1 /* Real mode version in RMTEXT16 segment to save space. */
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#else
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#endif
+#include "bs3-mode-TestDoModes.h"
+
+
+
+/**
+ * Warns about CPU modes that must be skipped.
+ *
+ * It will try not warn about modes for which there are no tests.
+ *
+ * @param paEntries The mode test entries.
+ * @param cEntries The number of tests.
+ * @param bCpuType The CPU type byte (see #BS3CPU_TYPE_MASK).
+ * @param fHavePae Whether the CPU has PAE.
+ * @param fHaveLongMode Whether the CPU does long mode.
+ */
+static void bs3TestWarnAboutSkippedModes(PCBS3TESTMODEBYMAXENTRY paEntries, unsigned cEntries,
+ uint8_t bCpuType, bool fHavePae, bool fHaveLongMode)
+{
+ bool fComplained286 = false;
+ bool fComplained386 = false;
+ bool fComplainedPAE = false;
+ bool fComplainedAMD64 = false;
+ unsigned i;
+
+ /*
+ * Complaint run.
+ */
+ for (i = 0; i < cEntries; i++)
+ {
+ if ( !fComplained286
+ && paEntries[i].pfnDoPE16)
+ {
+ if (bCpuType < BS3CPU_80286)
+ {
+ Bs3Printf("Only executing real-mode tests as no 80286+ CPU was detected.\n");
+ break;
+ }
+ fComplained286 = true;
+ }
+
+ if ( !fComplained386
+ && ( paEntries[i].fDoPE16_32
+ || paEntries[i].fDoPE16_V86
+ || paEntries[i].fDoPE32
+ || paEntries[i].fDoPE32_16
+ || paEntries[i].fDoPEV86
+ || paEntries[i].fDoPP16
+ || paEntries[i].fDoPP16_32
+ || paEntries[i].fDoPP16_V86
+ || paEntries[i].fDoPP32
+ || paEntries[i].fDoPP32_16
+ || paEntries[i].fDoPPV86) )
+ {
+ if (bCpuType < BS3CPU_80386)
+ {
+ Bs3Printf("80286 CPU: Only executing 16-bit protected and real mode tests.\n");
+ break;
+ }
+ fComplained386 = true;
+ }
+
+ if ( !fComplainedPAE
+ && ( paEntries[i].fDoPAE16
+ || paEntries[i].fDoPAE16_32
+ || paEntries[i].fDoPAE16_V86
+ || paEntries[i].fDoPAE32
+ || paEntries[i].fDoPAE32_16
+ || paEntries[i].fDoPAEV86) )
+ {
+ if (!fHavePae)
+ {
+ Bs3Printf("PAE and long mode tests will be skipped.\n");
+ break;
+ }
+ fComplainedPAE = true;
+ }
+
+ if ( !fComplainedAMD64
+ && ( paEntries[i].fDoLM16
+ || paEntries[i].fDoLM32
+ || paEntries[i].fDoLM64) )
+ {
+ if (!fHaveLongMode)
+ {
+ Bs3Printf("Long mode tests will be skipped.\n");
+ break;
+ }
+ fComplainedAMD64 = true;
+ }
+ }
+}
+
+#undef Bs3TestDoModesByMax
+BS3_MODE_DEF(void, Bs3TestDoModesByMax,(PCBS3TESTMODEBYMAXENTRY paEntries, size_t cEntries))
+{
+ bool const fVerbose = true;
+ bool const fDoV86Modes = true;
+ bool const fDoWeirdV86Modes = true;
+ uint16_t const uCpuDetected = g_uBs3CpuDetected;
+ uint8_t const bCpuType = uCpuDetected & BS3CPU_TYPE_MASK;
+ bool const fHavePae = RT_BOOL(uCpuDetected & BS3CPU_F_PAE);
+ bool const fHaveLongMode = RT_BOOL(uCpuDetected & BS3CPU_F_LONG_MODE);
+ unsigned i;
+
+#if 1 /* debug. */
+ Bs3Printf("Bs3TestDoModes: uCpuDetected=%#x fHavePae=%d fHaveLongMode=%d\n", uCpuDetected, fHavePae, fHaveLongMode);
+#endif
+ bs3TestWarnAboutSkippedModes(paEntries, cEntries, bCpuType, fHavePae, fHaveLongMode);
+
+ /*
+ * The real run.
+ */
+ for (i = 0; i < cEntries; i++)
+ {
+ const char *pszFmtStr = "Error #%u (%#x) in %s!\n";
+ bool fSkipped = true;
+ uint8_t bErrNo;
+
+ if (paEntries[i].pszSubTest != NULL)
+ Bs3TestSub(paEntries[i].pszSubTest);
+
+#define PRE_DO_CALL(a_szModeName) do { if (fVerbose) Bs3TestPrintf("...%s\n", a_szModeName); } while (0)
+#define CHECK_RESULT(a_szModeName) \
+ do { \
+ if (bErrNo != BS3TESTDOMODE_SKIPPED) \
+ { \
+ /*Bs3Printf("bErrNo=%#x %s\n", bErrNo, a_szModeName);*/ \
+ fSkipped = false; \
+ if (bErrNo != 0) \
+ Bs3TestFailedF(pszFmtStr, bErrNo, bErrNo, a_szModeName); \
+ } \
+ } while (0)
+
+ if (paEntries[i].fDoRM)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_rm);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInRM)(CONV_TO_RM_FAR16(paEntries[i].pfnDoRM));
+ CHECK_RESULT(g_szBs3ModeName_rm);
+ }
+
+ if (bCpuType < BS3CPU_80286)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ /*
+ * Unpaged prot mode.
+ */
+ if (paEntries[i].fDoPE16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16)(CONV_TO_PROT_FAR16(paEntries[i].pfnDoPE16));
+ CHECK_RESULT(g_szBs3ModeName_pe16);
+ }
+ if (bCpuType < BS3CPU_80386)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].fDoPE16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPE16_32), BS3_MODE_PE16_32);
+ CHECK_RESULT(g_szBs3ModeName_pe16_32);
+ }
+
+ if (paEntries[i].fDoPE16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPE16_32), BS3_MODE_PE16_V86);
+ CHECK_RESULT(g_szBs3ModeName_pe16_v86);
+ }
+
+ if (paEntries[i].fDoPE32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnDoPE32), BS3_MODE_PE32);
+ CHECK_RESULT(g_szBs3ModeName_pe32);
+ }
+
+ if (paEntries[i].fDoPE32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnDoPE32), BS3_MODE_PE32_16);
+ CHECK_RESULT(g_szBs3ModeName_pe32_16);
+ }
+
+ if (paEntries[i].fDoPEV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pev86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnDoPE32), BS3_MODE_PEV86);
+ CHECK_RESULT(g_szBs3ModeName_pev86);
+ }
+
+ /*
+ * Paged protected mode.
+ */
+ if (paEntries[i].fDoPP16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPP16_32), BS3_MODE_PP16);
+ CHECK_RESULT(g_szBs3ModeName_pp16);
+ }
+
+ if (paEntries[i].fDoPP16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPP16_32), BS3_MODE_PP16_32);
+ CHECK_RESULT(g_szBs3ModeName_pp16_32);
+ }
+
+ if (paEntries[i].fDoPP16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPP16_32), BS3_MODE_PP16_V86);
+ CHECK_RESULT(g_szBs3ModeName_pp16_v86);
+ }
+
+ if (paEntries[i].fDoPP32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(paEntries[i].pfnDoPP32), BS3_MODE_PP32);
+ CHECK_RESULT(g_szBs3ModeName_pp32);
+ }
+
+ if (paEntries[i].fDoPP32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(paEntries[i].pfnDoPP32), BS3_MODE_PP32_16);
+ CHECK_RESULT(g_szBs3ModeName_pp32_16);
+ }
+
+ if (paEntries[i].fDoPPV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_ppv86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(paEntries[i].pfnDoPP32), BS3_MODE_PPV86);
+ CHECK_RESULT(g_szBs3ModeName_ppv86);
+ }
+
+ /*
+ * Protected mode with PAE paging.
+ */
+ if (!fHavePae)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].fDoPAE16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE16_32), BS3_MODE_PAE16);
+ CHECK_RESULT(g_szBs3ModeName_pae16);
+ }
+
+ if (paEntries[i].fDoPAE16_32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE16_32), BS3_MODE_PAE16_32);
+ CHECK_RESULT(g_szBs3ModeName_pae16_32);
+ }
+
+ if (paEntries[i].fDoPAE16_V86 && fDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_v86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE16_32), BS3_MODE_PAE16_V86);
+ CHECK_RESULT(g_szBs3ModeName_pae16_v86);
+ }
+
+ if (paEntries[i].fDoPAE32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE32), BS3_MODE_PAE32);
+ CHECK_RESULT(g_szBs3ModeName_pae32);
+ }
+
+ if (paEntries[i].fDoPAE32_16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32_16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE32), BS3_MODE_PAE32_16);
+ CHECK_RESULT(g_szBs3ModeName_pae32_16);
+ }
+
+ if (paEntries[i].fDoPAEV86 && fDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_paev86);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(paEntries[i].pfnDoPAE32), BS3_MODE_PAEV86);
+ CHECK_RESULT(g_szBs3ModeName_paev86);
+ }
+
+ /*
+ * Long mode.
+ */
+ if (!fHaveLongMode)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (paEntries[i].fDoLM16)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm16);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(paEntries[i].pfnDoLM64), BS3_MODE_LM16);
+ CHECK_RESULT(g_szBs3ModeName_lm16);
+ }
+
+ if (paEntries[i].fDoLM32)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm32);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(paEntries[i].pfnDoLM64), BS3_MODE_LM32);
+ CHECK_RESULT(g_szBs3ModeName_lm32);
+ }
+
+ if (paEntries[i].fDoLM64)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm64);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(paEntries[i].pfnDoLM64), BS3_MODE_LM64);
+ CHECK_RESULT(g_szBs3ModeName_lm64);
+ }
+
+ if (fSkipped)
+ Bs3TestSkipped("skipped\n");
+ }
+ Bs3TestSubDone();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMaxStub.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMaxStub.asm
new file mode 100644
index 00000000..a2f35324
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByMaxStub.asm
@@ -0,0 +1,63 @@
+; $Id: bs3-mode-TestDoModesByMaxStub.asm $
+;; @file
+; BS3Kit - Bs3TestDoModesByMax near stub.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+;
+; Near stub for the API call (16-bit only).
+;
+%if TMPL_BITS == 16
+ %if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_RMTEXT16
+ %endif
+BS3_BEGIN_TEXT16_NEARSTUBS
+BS3_PROC_BEGIN_MODE Bs3TestDoModesByMax, BS3_PBC_NEAR
+ pop ax
+ push cs
+ push ax
+ %if TMPL_MODE == BS3_MODE_RM
+ extern TMPL_FAR_NM(Bs3TestDoModesByMax):wrt BS3GROUPRMTEXT16
+ jmp far TMPL_FAR_NM(Bs3TestDoModesByMax)
+ %else
+ extern TMPL_FAR_NM(Bs3TestDoModesByMax):wrt CGROUP16
+ jmp TMPL_NM(Bs3TestDoModesByMax)
+ %endif
+BS3_PROC_END_MODE Bs3TestDoModesByMax
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOne.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOne.c
new file mode 100644
index 00000000..7a057d5a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOne.c
@@ -0,0 +1,424 @@
+/* $Id: bs3-mode-TestDoModesByOne.c $ */
+/** @file
+ * BS3Kit - Bs3TestDoModesByOne
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if TMPL_MODE == BS3_MODE_RM
+# define BS3_USE_RM_TEXT_SEG 1 /* Real mode version in RMTEXT16 segment to save space. */
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#else
+# include "bs3kit-template-header.h"
+# include "bs3-cmn-test.h"
+#endif
+#include "bs3-mode-TestDoModes.h"
+
+
+/*********************************************************************************************************************************
+* Assembly Symbols *
+*********************************************************************************************************************************/
+/* Assembly helpers for switching to the work bitcount and calling it. */
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo16_f16(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo16_c32(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo16_c64(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo32_f16(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo32_c32(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo32_c64(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo64_f16(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo64_c32(uint8_t bMode);
+BS3_DECL_FAR(uint8_t) Bs3TestCallDoerTo64_c64(uint8_t bMode);
+
+
+/** The current worker function, picked up by our assembly helpers. */
+#ifndef DOXYGEN_RUNNING
+# define g_pfnBs3TestDoModesByOneCurrent BS3_CMN_NM(g_pfnBs3TestDoModesByOneCurrent)
+#endif
+extern PFNBS3TESTDOMODE g_pfnBs3TestDoModesByOneCurrent;
+
+#include <iprt/asm-amd64-x86.h>
+
+
+#undef Bs3TestDoModesByOne
+BS3_MODE_DEF(void, Bs3TestDoModesByOne,(PCBS3TESTMODEBYONEENTRY paEntries, size_t cEntries, uint32_t fFlags))
+{
+ bool const fVerbose = true;
+ bool const fDoV86Modes = true;
+ bool const fDoWeirdV86Modes = true;
+ uint16_t const uCpuDetected = g_uBs3CpuDetected;
+ uint8_t const bCpuType = uCpuDetected & BS3CPU_TYPE_MASK;
+ bool const fHavePae = RT_BOOL(uCpuDetected & BS3CPU_F_PAE);
+ bool const fHaveLongMode = RT_BOOL(uCpuDetected & BS3CPU_F_LONG_MODE);
+ unsigned i;
+
+#if 1 /* debug. */
+ Bs3Printf("Bs3TestDoModesByOne: uCpuDetected=%#x fHavePae=%d fHaveLongMode=%d\n", uCpuDetected, fHavePae, fHaveLongMode);
+#endif
+
+ /*
+ * Inform about modes we won't test (if any).
+ */
+ if (bCpuType < BS3CPU_80286)
+ Bs3Printf("Only executing real-mode tests as no 80286+ CPU was detected.\n");
+ else if (bCpuType < BS3CPU_80386)
+ Bs3Printf("80286 CPU: Only executing 16-bit protected and real mode tests.\n");
+ else if (!fHavePae)
+ Bs3Printf("PAE and long mode tests will be skipped.\n");
+ else if (!fHaveLongMode)
+ Bs3Printf("Long mode tests will be skipped.\n");
+#if ARCH_BITS != 16
+ Bs3Printf("Real-mode tests will be skipped.\n");
+#endif
+
+ /*
+ * The real run.
+ */
+ for (i = 0; i < cEntries; i++)
+ {
+ const char *pszFmtStr = "Error #%u (%#x) in %s!\n";
+ bool fSkipped = true;
+ bool const fOnlyPaging = RT_BOOL((paEntries[i].fFlags | fFlags) & BS3TESTMODEBYONEENTRY_F_ONLY_PAGING);
+ bool const fMinimal = RT_BOOL((paEntries[i].fFlags | fFlags) & BS3TESTMODEBYONEENTRY_F_MINIMAL);
+ bool const fCurDoV86Modes = fDoV86Modes && !fMinimal;
+ bool const fCurDoWeirdV86Modes = fDoWeirdV86Modes && fCurDoV86Modes;
+ uint8_t bErrNo;
+ Bs3TestSub(paEntries[i].pszSubTest);
+
+#define PRE_DO_CALL(a_szModeName) do { if (fVerbose) Bs3TestPrintf("...%s\n", a_szModeName); } while (0)
+#define CHECK_RESULT(a_szModeName) \
+ do { \
+ if (bErrNo != BS3TESTDOMODE_SKIPPED) \
+ { \
+ /*Bs3Printf("bErrNo=%#x %s\n", bErrNo, a_szModeName);*/ \
+ fSkipped = false; \
+ if (bErrNo != 0) \
+ Bs3TestFailedF(pszFmtStr, bErrNo, bErrNo, a_szModeName); \
+ } \
+ } while (0)
+
+ g_pfnBs3TestDoModesByOneCurrent = paEntries[i].pfnWorker;
+
+#if ARCH_BITS != 64
+
+# if ARCH_BITS == 16
+ if (!fOnlyPaging)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_rm);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInRM)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+ CHECK_RESULT(g_szBs3ModeName_rm);
+ }
+# else
+ if (!fOnlyPaging && (paEntries[i].fFlags | fFlags) & BS3TESTMODEBYONEENTRY_F_REAL_MODE_READY)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_rm);
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_RM);
+ CHECK_RESULT(g_szBs3ModeName_rm);
+ }
+# endif
+
+ if (bCpuType < BS3CPU_80286)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ /*
+ * Unpaged prot mode.
+ */
+ if (!fOnlyPaging && (!fMinimal || bCpuType < BS3CPU_80386))
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pe16);
+ }
+ if (bCpuType < BS3CPU_80386)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (!fOnlyPaging)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PE16_32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PE16_32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pe16_32);
+ }
+
+ if (fCurDoWeirdV86Modes && !fOnlyPaging)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe16_v86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE16_V86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pe16_v86);
+ }
+
+ if (!fOnlyPaging)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PE32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PE32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pe32);
+ }
+
+ if (!fOnlyPaging && !fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pe32_16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPE32_16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pe32_16);
+ }
+
+ if (fCurDoV86Modes && !fOnlyPaging)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pev86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPEV86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPEV86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pev86);
+ }
+
+ /*
+ * Paged protected mode.
+ */
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pp16);
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PP16_32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PP16_32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pp16_32);
+ }
+
+ if (fCurDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp16_v86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP16_V86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pp16_v86);
+ }
+
+ if (true)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PP32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PP32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pp32);
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pp32_16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPP32_16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pp32_16);
+ }
+
+ if (fCurDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_ppv86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPPV86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPPV86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_ppv86);
+ }
+
+
+ /*
+ * Protected mode with PAE paging.
+ */
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pae16);
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PAE16_32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PAE16_32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pae16_32);
+ }
+
+ if (fCurDoWeirdV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae16_v86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_V86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE16_V86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pae16_v86);
+ }
+
+ if (true)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32);
+# if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_PAE32);
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)), BS3_MODE_PAE32);
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pae32);
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_pae32_16);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32_16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAE32_16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_pae32_16);
+ }
+
+ if (fCurDoV86Modes)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_paev86);
+# if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAEV86)(CONV_TO_RM_FAR16(paEntries[i].pfnWorker));
+# else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInPAEV86)(CONV_TO_RM_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+# endif
+ CHECK_RESULT(g_szBs3ModeName_paev86);
+ }
+
+#endif /* ARCH_BITS != 64 */
+
+ /*
+ * Long mode.
+ */
+ if (!fHaveLongMode)
+ {
+ if (fSkipped)
+ Bs3TestSkipped(NULL);
+ continue;
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm16);
+#if ARCH_BITS == 16
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM16)(CONV_TO_PROT_FAR16(paEntries[i].pfnWorker));
+#else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM16)(CONV_TO_PROT_FAR16(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_f16)));
+#endif
+ CHECK_RESULT(g_szBs3ModeName_lm16);
+ }
+
+ if (!fMinimal)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm32);
+#if ARCH_BITS == 32
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM32)(CONV_TO_FLAT(paEntries[i].pfnWorker));
+#else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM32)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c32)));
+#endif
+ CHECK_RESULT(g_szBs3ModeName_lm32);
+ }
+
+ if (true)
+ {
+ PRE_DO_CALL(g_szBs3ModeName_lm64);
+#if ARCH_BITS == 64
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(paEntries[i].pfnWorker), BS3_MODE_LM64);
+#else
+ bErrNo = TMPL_NM(Bs3TestCallDoerInLM64)(CONV_TO_FLAT(RT_CONCAT3(Bs3TestCallDoerTo,ARCH_BITS,_c64)), BS3_MODE_LM64);
+#endif
+ CHECK_RESULT(g_szBs3ModeName_lm64);
+ }
+
+ if (fSkipped)
+ Bs3TestSkipped("skipped\n");
+ }
+ Bs3TestSubDone();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOneStub.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOneStub.asm
new file mode 100644
index 00000000..2f9c9916
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesByOneStub.asm
@@ -0,0 +1,63 @@
+; $Id: bs3-mode-TestDoModesByOneStub.asm $
+;; @file
+; BS3Kit - Bs3TestDoModesByOne near stub.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+;
+; Near stub for the API call (16-bit only).
+;
+%if TMPL_BITS == 16
+ %if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_RMTEXT16
+ %endif
+BS3_BEGIN_TEXT16_NEARSTUBS
+BS3_PROC_BEGIN_MODE Bs3TestDoModesByOne, BS3_PBC_NEAR
+ pop ax
+ push cs
+ push ax
+ %if TMPL_MODE == BS3_MODE_RM
+ extern TMPL_FAR_NM(Bs3TestDoModesByOne):wrt BS3GROUPRMTEXT16
+ jmp far TMPL_FAR_NM(Bs3TestDoModesByOne)
+ %else
+ extern TMPL_FAR_NM(Bs3TestDoModesByOne):wrt CGROUP16
+ jmp TMPL_NM(Bs3TestDoModesByOne)
+ %endif
+BS3_PROC_END_MODE Bs3TestDoModesByOne
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesHlp.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesHlp.asm
new file mode 100644
index 00000000..cb4b19f1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesHlp.asm
@@ -0,0 +1,1139 @@
+; $Id: bs3-mode-TestDoModesHlp.asm $
+;; @file
+; BS3Kit - Bs3TestDoModes helpers
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+;
+; We put most of this mess in the RMTEXT16 segment when in real mode.
+;
+%if TMPL_MODE == BS3_MODE_RM
+ %define MY_BEGIN_TEXT BS3_BEGIN_RMTEXT16
+ %define MY_BEGIN_TEXT16 BS3_BEGIN_RMTEXT16
+ %define MY_TEXT16_WRT(a_Label) a_Label wrt BS3GROUPRMTEXT16
+%else
+ %define MY_BEGIN_TEXT TMPL_BEGIN_TEXT
+ %define MY_BEGIN_TEXT16 BS3_BEGIN_TEXT16
+ %define MY_TEXT16_WRT(a_Label) BS3_TEXT16_WRT(a_Label)
+%endif
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+%if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_TEXT16_FARSTUBS
+extern TMPL_FAR_NM(Bs3SwitchToRM)
+extern TMPL_FAR_NM(Bs3SwitchToPE16)
+extern TMPL_FAR_NM(Bs3SwitchToPE16_32)
+extern TMPL_FAR_NM(Bs3SwitchToPE16_V86)
+extern TMPL_FAR_NM(Bs3SwitchToPE32)
+extern TMPL_FAR_NM(Bs3SwitchToPE32_16)
+extern TMPL_FAR_NM(Bs3SwitchToPEV86)
+extern TMPL_FAR_NM(Bs3SwitchToPP16)
+extern TMPL_FAR_NM(Bs3SwitchToPP16_32)
+extern TMPL_FAR_NM(Bs3SwitchToPP16_V86)
+extern TMPL_FAR_NM(Bs3SwitchToPP32)
+extern TMPL_FAR_NM(Bs3SwitchToPP32_16)
+extern TMPL_FAR_NM(Bs3SwitchToPPV86)
+extern TMPL_FAR_NM(Bs3SwitchToPAE16)
+extern TMPL_FAR_NM(Bs3SwitchToPAE16_32)
+extern TMPL_FAR_NM(Bs3SwitchToPAE16_V86)
+extern TMPL_FAR_NM(Bs3SwitchToPAE32)
+extern TMPL_FAR_NM(Bs3SwitchToPAE32_16)
+extern TMPL_FAR_NM(Bs3SwitchToPAEV86)
+extern TMPL_FAR_NM(Bs3SwitchToLM16)
+extern TMPL_FAR_NM(Bs3SwitchToLM32)
+extern TMPL_FAR_NM(Bs3SwitchToLM64)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe16_v86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe32_16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pev86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp16_v86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp32_16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_ppv86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae16_v86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae32_16_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_paev86_far)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_lm16_far)
+%else
+BS3_BEGIN_TEXT16
+extern TMPL_NM(Bs3SwitchToRM)
+extern TMPL_NM(Bs3SwitchToPE16)
+extern TMPL_NM(Bs3SwitchToPE16_32)
+extern TMPL_NM(Bs3SwitchToPE16_V86)
+extern TMPL_NM(Bs3SwitchToPE32)
+extern TMPL_NM(Bs3SwitchToPE32_16)
+extern TMPL_NM(Bs3SwitchToPEV86)
+extern TMPL_NM(Bs3SwitchToPP16)
+extern TMPL_NM(Bs3SwitchToPP16_32)
+extern TMPL_NM(Bs3SwitchToPP16_V86)
+extern TMPL_NM(Bs3SwitchToPP32)
+extern TMPL_NM(Bs3SwitchToPP32_16)
+extern TMPL_NM(Bs3SwitchToPPV86)
+extern TMPL_NM(Bs3SwitchToPAE16)
+extern TMPL_NM(Bs3SwitchToPAE16_32)
+extern TMPL_NM(Bs3SwitchToPAE16_V86)
+extern TMPL_NM(Bs3SwitchToPAE32)
+extern TMPL_NM(Bs3SwitchToPAE32_16)
+extern TMPL_NM(Bs3SwitchToPAEV86)
+extern TMPL_NM(Bs3SwitchToLM16)
+extern TMPL_NM(Bs3SwitchToLM32)
+extern TMPL_NM(Bs3SwitchToLM64)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16_v86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe32_16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pev86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16_v86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp32_16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_ppv86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16_v86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae32_16)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_paev86)
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm16)
+%endif
+BS3_BEGIN_TEXT16
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16_32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16_32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16_32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm32):wrt BS3FLAT
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm64):wrt BS3FLAT
+
+
+MY_BEGIN_TEXT16 ; need the group definition
+MY_BEGIN_TEXT
+
+;;
+; Shared prologue code.
+; @param xAX Where to jump to for the main event.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(bs3TestCallDoerPrologue), , 0
+ BS3_CALL_CONV_PROLOG 1
+ push xBP
+ mov xBP, xSP
+ xPUSHF
+
+ ; Save non-volatile registers so the DO function doesn't have to.
+ push xBX
+ push xCX
+ push xDX
+ push xSI
+ push xDI
+%if TMPL_BITS != 64
+ push ds
+ push es
+ push ss
+ %if TMPL_BITS != 16
+ push fs
+ push gs
+ %endif
+%endif
+%if TMPL_BITS == 64
+ push r8
+ push r9
+ push r10
+ push r11
+ push r12
+ push r13
+ push r14
+ push r15
+%endif
+
+ ; Jump to the main code.
+ jmp xAX
+
+;;
+; Shared epilogue code.
+; @param xAX Return code.
+;
+BS3_GLOBAL_NAME_EX TMPL_NM(bs3TestCallDoerEpilogue), , 0
+ ; Restore registers.
+%if TMPL_BITS == 16
+ sub bp, (1+5+3)*2
+ mov sp, bp
+%elif TMPL_BITS == 32
+ lea xSP, [xBP - (1+5+5)*4]
+%else
+ lea xSP, [xBP - (1+5+8)*8]
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+%endif
+%if TMPL_BITS != 64
+ %if TMPL_BITS != 16
+ pop gs
+ pop fs
+ %endif
+ pop ss
+ pop es
+ pop ds
+%endif
+ pop xDI
+ pop xSI
+ pop xDX
+ pop xCX
+ pop xBX
+ xPOPF
+ pop xBP
+ ret
+
+;
+; For checking that the mode switching macros doesn't screw up GPRs.
+; Note! Does not work on pre 286 hardware! So, for debugging only.
+;
+%if 0
+ %macro STRICT_SAVE_REGS 0
+ movzx esp, sp
+ sub esp, BS3REGCTX_size
+ mov [esp + BS3REGCTX.rax], eax
+ mov dword [esp + BS3REGCTX.rax+4], 0xdead0000
+ mov [esp + BS3REGCTX.rcx], ecx
+ mov dword [esp + BS3REGCTX.rcx+4], 0xdead0001
+ mov [esp + BS3REGCTX.rdx], edx
+ mov dword [esp + BS3REGCTX.rdx+4], 0xdead0002
+ mov [esp + BS3REGCTX.rbx], ebx
+ mov dword [esp + BS3REGCTX.rbx+4], 0xdead0003
+ mov [esp + BS3REGCTX.rbp], ebp
+ mov [esp + BS3REGCTX.rsp], esp
+ mov [esp + BS3REGCTX.rsi], esi
+ mov [esp + BS3REGCTX.rdi], edi
+ %endmacro
+
+ %macro STRICT_CHECK_REGS 0
+%%_esp: cmp [esp + BS3REGCTX.rsp], esp
+ jne %%_esp
+%%_eax: cmp [esp + BS3REGCTX.rax], eax
+ jne %%_eax
+%%_ecx: mov [esp + BS3REGCTX.rcx], ecx
+ jne %%_ecx
+%%_edx: cmp [esp + BS3REGCTX.rdx], edx
+ jne %%_edx
+%%_ebx: cmp [esp + BS3REGCTX.rbx], ebx
+ jne %%_ebx
+%%_ebp: cmp [esp + BS3REGCTX.rbp], ebp
+ jne %%_ebp
+%%_esi: cmp [esp + BS3REGCTX.rsi], esi
+ jne %%_esi
+%%_edi: cmp [esp + BS3REGCTX.rdi], edi
+ jne %%_edi
+ add esp, BS3REGCTX_size
+ %endmacro
+%else
+
+ %macro STRICT_SAVE_REGS 0
+ %endmacro
+ %macro STRICT_CHECK_REGS 0
+ %endmacro
+%endif
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Real mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInRM(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInRM, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToRM)
+%else
+ call TMPL_NM(Bs3SwitchToRM)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ mov cx, BS3_MODE_RM
+ push cx
+ push cs
+ mov cx, .return
+ push cx
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInRM
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Unpage protection mode.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPE16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPE16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPE16)
+%else
+ call TMPL_NM(Bs3SwitchToPE16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PE16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPE16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPE16_32(uint32_t FlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPE16_32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPE16_32)
+%else
+ call TMPL_NM(Bs3SwitchToPE16_32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx ; bMode
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16_32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPE16_32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPE16_V86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPE16_V86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPE16_V86)
+%else
+ call TMPL_NM(Bs3SwitchToPE16_V86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PE16_V86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe16_v86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe16_v86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPE16_V86
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPE32(uint32_t FlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPE32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPE32)
+%else
+ call TMPL_NM(Bs3SwitchToPE32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx ; bMode
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPE32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPE32_16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPE32_16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPE32_16)
+%else
+ call TMPL_NM(Bs3SwitchToPE32_16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PE32_16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pe32_16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pe32_16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPE32_16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPEV86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPEV86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPEV86)
+%else
+ call TMPL_NM(Bs3SwitchToPEV86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PEV86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pev86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pev86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPEV86
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Page protection mode.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPP16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPP16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPP16)
+%else
+ call TMPL_NM(Bs3SwitchToPP16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PP16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPP16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPP16_32(uint32_t uFlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPP16_32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPP16_32)
+%else
+ call TMPL_NM(Bs3SwitchToPP16_32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16_32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPP16_32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPP16_V86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPP16_V86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPP16_V86)
+%else
+ call TMPL_NM(Bs3SwitchToPP16_V86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PP16_V86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp16_v86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp16_v86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPP16_V86
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPP32(uint32_t uFlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPP32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPP32)
+%else
+ call TMPL_NM(Bs3SwitchToPP32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx ; bMode
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPP32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPP32_16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPP32_16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPP32_16)
+%else
+ call TMPL_NM(Bs3SwitchToPP32_16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PP32_16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pp32_16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pp32_16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPP32_16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPPV86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPPV86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPPV86)
+%else
+ call TMPL_NM(Bs3SwitchToPPV86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PPV86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_ppv86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_ppv86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPPV86
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; PAE paged protection mode.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAE16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAE16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAE16)
+%else
+ call TMPL_NM(Bs3SwitchToPAE16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PAE16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPAE16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAE16_32(uint32_t uFlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAE16_32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAE16_32)
+%else
+ call TMPL_NM(Bs3SwitchToPAE16_32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx ; bMode
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16_32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPAE16_32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAE16_V86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAE16_V86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAE16_V86)
+%else
+ call TMPL_NM(Bs3SwitchToPAE16_V86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PAE16_V86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae16_v86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae16_v86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPAE16_V86
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAE32(uint32_t uFlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAE32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAE32)
+%else
+ call TMPL_NM(Bs3SwitchToPAE32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ push edx ; bMode
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInPAE32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAE32_16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAE32_16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAE32_16)
+%else
+ call TMPL_NM(Bs3SwitchToPAE32_16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PAE32_16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_pae32_16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_pae32_16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPAE32_16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInPAEV86(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInPAEV86, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToPAEV86)
+%else
+ call TMPL_NM(Bs3SwitchToPAEV86)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_PAEV86
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_paev86_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_paev86)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInPAEV86
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Long mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInLM16(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInLM16, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+MY_BEGIN_TEXT16
+BS3_SET_BITS TMPL_BITS
+BS3_GLOBAL_LOCAL_LABEL .doit
+ mov ax, [xBP + xCB + cbCurRetAddr] ; Load far function pointer.
+ mov dx, [xBP + xCB + cbCurRetAddr + 2]
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToLM16)
+%else
+ call TMPL_NM(Bs3SwitchToLM16)
+%endif
+ BS3_SET_BITS 16
+ STRICT_CHECK_REGS
+
+ push BS3_MODE_LM16
+ push cs
+ push .return
+ push dx
+ push ax
+ retf
+.return:
+
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_lm16_far)
+%else
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm16)
+%endif
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+MY_BEGIN_TEXT
+BS3_PROC_END_MODE Bs3TestCallDoerInLM16
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInLM32(uint16_t offBs3Text16);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInLM32, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToLM32)
+%else
+ call TMPL_NM(Bs3SwitchToLM32)
+%endif
+ BS3_SET_BITS 32
+ STRICT_CHECK_REGS
+
+ and esp, ~03h
+ push BS3_MODE_LM32
+ call eax
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm32)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInLM32
+
+;;
+; @cproto BS3_DECL(uint8_t) Bs3TestCallDoerInLM64(uint32_t uFlatWorkerAddr, uint8_t bMode);
+; @uses rax
+BS3_PROC_BEGIN_MODE Bs3TestCallDoerInLM64, BS3_PBC_NEAR
+ BS3_LEA_MOV_WRT_RIP(xAX, MY_TEXT16_WRT(.doit))
+ jmp TMPL_NM(bs3TestCallDoerPrologue)
+.doit:
+ mov eax, [xBP + xCB + cbCurRetAddr] ; Load function pointer.
+ movzx edx, byte [xBP + xCB + cbCurRetAddr + sCB] ; bMode
+
+ ; Mode switch, make the call, switch back.
+ STRICT_SAVE_REGS
+%if TMPL_MODE == BS3_MODE_RM
+ call far TMPL_FAR_NM(Bs3SwitchToLM64)
+%else
+ call TMPL_NM(Bs3SwitchToLM64)
+%endif
+ BS3_SET_BITS 64
+ STRICT_CHECK_REGS
+
+ and rsp, ~0fh
+ sub rsp, 18h
+ push rdx ; bMode
+ BS3_CALL rax, 1
+
+ STRICT_SAVE_REGS
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_lm64)
+ BS3_SET_BITS TMPL_BITS
+ STRICT_CHECK_REGS
+ jmp TMPL_NM(bs3TestCallDoerEpilogue)
+BS3_PROC_END_MODE Bs3TestCallDoerInLM64
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesStub.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesStub.asm
new file mode 100644
index 00000000..8b8f05ee
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TestDoModesStub.asm
@@ -0,0 +1,63 @@
+; $Id: bs3-mode-TestDoModesStub.asm $
+;; @file
+; BS3Kit - Bs3TestDoModes near stub.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+;
+; Near stub for the API call (16-bit only).
+;
+%if TMPL_BITS == 16
+ %if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_RMTEXT16
+ %endif
+BS3_BEGIN_TEXT16_NEARSTUBS
+BS3_PROC_BEGIN_MODE Bs3TestDoModes, BS3_PBC_NEAR
+ pop ax
+ push cs
+ push ax
+ %if TMPL_MODE == BS3_MODE_RM
+ extern TMPL_FAR_NM(Bs3TestDoModes):wrt BS3GROUPRMTEXT16
+ jmp far TMPL_FAR_NM(Bs3TestDoModes)
+ %else
+ extern TMPL_FAR_NM(Bs3TestDoModes):wrt CGROUP16
+ jmp TMPL_NM(Bs3TestDoModes)
+ %endif
+BS3_PROC_END_MODE Bs3TestDoModes
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapInit.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapInit.c
new file mode 100644
index 00000000..56066d8d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapInit.c
@@ -0,0 +1,61 @@
+/* $Id: bs3-mode-TrapInit.c $ */
+/** @file
+ * BS3Kit - Bs3TrapInit
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit-template-header.h"
+
+
+#undef Bs3TrapInit
+BS3_MODE_DEF(void, Bs3TrapInit,(void))
+{
+#if BS3_MODE_IS_RM_SYS(TMPL_MODE)
+ Bs3TrapRmV86Init();
+#elif BS3_MODE_IS_16BIT_SYS(TMPL_MODE)
+ Bs3TrapRmV86Init();
+ Bs3Trap16Init();
+#elif BS3_MODE_IS_32BIT_SYS(TMPL_MODE)
+ Bs3TrapRmV86Init();
+ Bs3Trap32Init();
+#elif BS3_MODE_IS_64BIT_SYS(TMPL_MODE)
+ Bs3Trap64Init();
+#else
+# error "TMPL_MODE"
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm
new file mode 100644
index 00000000..52c0928a
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm
@@ -0,0 +1,174 @@
+; $Id: bs3-mode-TrapSetJmpAndRestoreInRmAsm.asm $
+;; @file
+; BS3Kit - Bs3TrapSetJmpAndRestoreInRm helper
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_BEGIN_TEXT16
+extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+extern _Bs3TrapSetJmpAndRestore_c16
+
+TMPL_BEGIN_TEXT
+extern TMPL_NM(Bs3SwitchToRM)
+
+
+;;
+; Shared prologue code.
+; @param xAX Where to jump to for the main event.
+;
+BS3_PROC_BEGIN_MODE Bs3TrapSetJmpAndRestoreInRmAsm, BS3_PBC_NEAR
+ BS3_CALL_CONV_PROLOG 2
+ push xBP
+ mov xBP, xSP
+ xPUSHF
+
+ ;
+ ; Save non-volatile registers so the DO function doesn't have to.
+ ;
+ push xBX
+ push xCX
+ push xDX
+ push xSI
+ push xDI
+%if TMPL_BITS != 64
+ push ds
+ push es
+ push ss
+ %if TMPL_BITS != 16
+ push fs
+ push gs
+ %endif
+%endif
+%if TMPL_BITS == 64
+ push r8
+ push r9
+ push r10
+ push r11
+ push r12
+ push r13
+ push r14
+ push r15
+%endif
+
+ ;
+ ; Load EAX and EDX with the two pointers.
+ ;
+ mov eax, [xBP + xCB + cbCurRetAddr]
+ mov edx, [xBP + xCB + cbCurRetAddr + sCB]
+
+ ;
+ ; Jump to 16-bit segment for the mode switching.
+ ;
+%if TMPL_BITS != 16
+ jmp .in_16bit_segment
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+.in_16bit_segment:
+%endif
+
+ ;
+ ; Switch to real-mode.
+ ;
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+
+ ;
+ ; Now we do the
+ ;
+ push edx
+ push eax
+ call _Bs3TrapSetJmpAndRestore_c16
+ add sp, 8h
+
+ ;
+ ; Switch back to the original mode.
+ ;
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_Safe_rm)
+ BS3_SET_BITS TMPL_BITS
+
+ ;
+ ; Jump back to the 32-bit or 64-bit segment.
+ ;
+%if TMPL_BITS != 16
+ jmp .in_text_segment
+TMPL_BEGIN_TEXT
+.in_text_segment:
+%endif
+
+ ;
+ ; Restore registers.
+ ;
+%if TMPL_BITS == 16
+ sub bp, (1+5+3)*2
+ mov sp, bp
+%elif TMPL_BITS == 32
+ lea xSP, [xBP - (1+5+5)*4]
+%else
+ lea xSP, [xBP - (1+5+8)*8]
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+%endif
+%if TMPL_BITS != 64
+ %if TMPL_BITS != 16
+ pop gs
+ pop fs
+ %endif
+ pop ss
+ pop es
+ pop ds
+%endif
+ pop xDI
+ pop xSI
+ pop xDX
+ pop xCX
+ pop xBX
+ xPOPF
+ pop xBP
+ ret
+BS3_PROC_END_MODE Bs3TrapSetJmpAndRestoreInRmAsm
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSystemCallHandler.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSystemCallHandler.asm
new file mode 100644
index 00000000..313774c0
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-TrapSystemCallHandler.asm
@@ -0,0 +1,891 @@
+; $Id: bs3-mode-TrapSystemCallHandler.asm $
+;; @file
+; BS3Kit - System call trap handler.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit-template-header.mac"
+
+
+;*********************************************************************************************************************************
+;* External Symbols *
+;*********************************************************************************************************************************
+BS3_EXTERN_DATA16 g_bBs3CurrentMode
+%if TMPL_BITS != 64
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+%endif
+%if TMPL_BITS == 16
+BS3_EXTERN_DATA16 g_uBs3TrapEipHint
+%endif
+TMPL_BEGIN_TEXT
+
+BS3_EXTERN_CMN Bs3SelProtFar32ToFlat32
+BS3_EXTERN_CMN Bs3RegCtxConvertToRingX
+BS3_EXTERN_CMN Bs3RegCtxRestore
+BS3_EXTERN_CMN Bs3Panic
+
+BS3_BEGIN_TEXT16
+extern Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
+TMPL_BEGIN_TEXT
+
+
+;;
+; System call handler.
+;
+; This is an assembly trap handler that is called in response to a system call
+; request from 'user' code. The only fixed parameter is [ER]AX which contains
+; the system call number. Other registers are assigned on a per system call
+; basis, ditto for which registers are preserved and which are used to return
+; stuff. Generally, though, we preserve all registers not used as return
+; values or otherwise implicitly transformed by the call.
+;
+; Note! The 16-bit versions of this code must be careful with using extended
+; registers as we wish this code to work on real 80286 (maybe even 8086)
+; CPUs too!
+;
+BS3_PROC_BEGIN_MODE Bs3TrapSystemCallHandler, BS3_PBC_NEAR ; Near because we'll probably only ever need this from CGROUP16.
+ ;
+ ; This prologue is kind of complicated because of 80286 and older CPUs
+ ; as well as different requirements for 64-bit and the other modes.
+ ;
+%define VAR_CALLER_BP [xBP]
+%if TMPL_BITS != 64
+ %define VAR_CALLER_DS [xBP - xCB]
+%endif
+%define VAR_CALLER_BX [xBP - sCB*1 - xCB] ; Note! the upper word is not clean on pre-386 (16-bit mode).
+%define VAR_CALLER_AX [xBP - sCB*2 - xCB]
+%define VAR_CALLER_CX [xBP - sCB*3 - xCB]
+%define VAR_CALLER_DX [xBP - sCB*4 - xCB]
+%define VAR_CALLER_SI [xBP - sCB*5 - xCB]
+%define VAR_CALLER_SI_HI [xBP - sCB*5 - xCB + 2]
+%define VAR_CALLER_DI [xBP - sCB*6 - xCB]
+%define VAR_CALLER_DI_HI [xBP - sCB*6 - xCB + 2]
+%if TMPL_BITS == 16
+ %define VAR_CALLER_EBP [xBP - sCB*7 - xCB]
+ %define VAR_CALLER_ESP [xBP - sCB*8 - xCB]
+ %define VAR_CALLER_EFLAGS [xBP - sCB*9 - xCB]
+ %define VAR_CALLER_MODE [xBP - sCB*9 - xCB*2]
+ %define BP_TOP_STACK_EXPR xBP - sCB*9 - xCB*2
+%else
+ %define VAR_CALLER_MODE [xBP - sCB*6 - xCB*2]
+ %define BP_TOP_STACK_EXPR xBP - sCB*6 - xCB*2
+%endif
+ push xBP
+ mov xBP, xSP
+%if TMPL_BITS == 64
+ push 0
+ mov [rsp+2], es
+ mov [rsp], ds
+%else
+ push ds
+ %ifdef TMPL_CMN_R86
+ push BS3_SEL_DATA16
+ %else
+ push RT_CONCAT(BS3_SEL_R0_DS,TMPL_BITS)
+ %endif
+ pop ds ; DS = BS3KIT_GRPNM_DATA16 or FLAT and we can safely access data
+ %if TMPL_BITS == 16 && (TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16)
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jbe .prologue_pre_80386
+ %endif
+%endif
+ push sBX
+ push sAX
+ push sCX
+ push sDX
+ push sSI
+ push sDI
+%if TMPL_BITS == 16
+ push ebp
+ push esp
+ pushfd
+ %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
+ jmp .prologue_end
+
+.prologue_pre_80386:
+ push bx ; dummy
+ push bx
+ xor bx, bx
+ push bx ; dummy
+ push ax
+ push bx ; dummy
+ push cx
+ push bx ; dummy
+ push dx
+ push bx ; dummy
+ push si
+ push bx ; dummy
+ push di
+ sub sp, 0ch ; dummy
+ %endif
+%endif
+.prologue_end:
+
+ ;
+ ; VAR_CALLER_MODE: Save the current mode (important for v8086 with 16-bit kernel).
+ ;
+ xor xBX, xBX
+ mov bl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
+ push xBX
+
+ ;
+ ; Dispatch the system call.
+ ;
+ cmp ax, BS3_SYSCALL_LAST
+ ja .invalid_syscall
+%if TMPL_BITS == 16
+ mov bx, ax
+ shl bx, 1
+ jmp word [cs:.aoffSyscallHandlers + bx]
+%else
+ movzx ebx, ax
+ mov ebx, [.aoffSyscallHandlers + ebx * 4]
+ jmp xBX
+%endif
+.aoffSyscallHandlers:
+%ifdef TMPL_16BIT
+ dw .invalid_syscall wrt CGROUP16
+ dw .print_chr wrt CGROUP16
+ dw .print_str wrt CGROUP16
+ dw .to_ringX wrt CGROUP16
+ dw .to_ringX wrt CGROUP16
+ dw .to_ringX wrt CGROUP16
+ dw .to_ringX wrt CGROUP16
+ dw .restore_ctx wrt CGROUP16
+%else
+ dd .invalid_syscall wrt FLAT
+ dd .print_chr wrt FLAT
+ dd .print_str wrt FLAT
+ dd .to_ringX wrt FLAT
+ dd .to_ringX wrt FLAT
+ dd .to_ringX wrt FLAT
+ dd .to_ringX wrt FLAT
+ dd .restore_ctx wrt FLAT
+%endif
+
+ ;
+ ; Invalid system call.
+ ;
+.invalid_syscall:
+ int3
+ jmp .return
+
+ ;
+ ; Print char in the CL register.
+ ;
+ ; We use the vga bios teletype interrupt to do the writing, so we must
+ ; be in some kind of real mode for this to work. 16-bit code segment
+ ; requried for the mode switching code.
+ ;
+BS3_BEGIN_TEXT16
+ BS3_SET_BITS TMPL_BITS
+.print_chr:
+%if TMPL_BITS != 64
+ push es
+ mov di, ss ; Must save and restore SS for supporting 16/32 and 32/16 caller/kernel ring-0 combinations.
+%endif
+%ifndef TMPL_CMN_R86
+ ; Switch to real mode (20h param scratch area not required).
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+%endif
+
+ ; Print the character, turning '\n' into '\r\n'.
+ cmp cl, 0ah ; \n
+ je .print_chr_newline
+ mov ah, 0eh
+ mov al, cl
+ mov bx, 0ff00h
+ int 10h
+ jmp .print_chr_done
+
+.print_chr_newline:
+ mov ax, 0e0dh ; cmd + \r
+ mov bx, 0ff00h
+ int 10h
+ mov ax, 0e0ah ; cmd + \n
+ mov bx, 0ff00h
+ int 10h
+
+.print_chr_done:
+%ifndef TMPL_CMN_R86
+ ; Switch back (20h param scratch area not required).
+ extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
+ BS3_SET_BITS TMPL_BITS
+%endif
+%if TMPL_BITS != 64
+ mov ss, di
+ pop es
+%endif
+ jmp .return
+TMPL_BEGIN_TEXT
+
+
+ ;
+ ; Prints DX chars from the string pointed to by CX:xSI to the screen.
+ ;
+ ; We use the vga bios teletype interrupt to do the writing, so we must
+ ; be in some kind of real mode for this to work. The string must be
+ ; accessible from real mode too.
+ ;
+.print_str:
+%if TMPL_BITS != 64
+ push es
+ push ss ; Must save and restore SS for supporting 16/32 and 32/16 caller/kernel ring-0 combinations.
+%endif
+ ; Convert the incoming pointer to real mode (assuming caller checked
+ ; that real mode can access it).
+ call .convert_ptr_arg_to_real_mode_ax_si
+ mov cx, VAR_CALLER_DX
+
+ ; Switch to real mode (no 20h scratch required)
+%ifndef TMPL_CMN_R86
+ %if TMPL_BITS != 16
+ jmp .print_str_to_16bit
+BS3_BEGIN_TEXT16
+.print_str_to_16bit:
+ BS3_SET_BITS TMPL_BITS
+ %endif
+ extern TMPL_NM(Bs3SwitchToRM)
+ call TMPL_NM(Bs3SwitchToRM)
+ BS3_SET_BITS 16
+%endif
+ ; Call code in Bs3PrintStrN to do the work.
+ mov ds, ax
+ call Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
+
+ ; Switch back (20h param scratch area not required).
+%ifndef TMPL_CMN_R86
+ extern RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
+ call RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
+ %if TMPL_BITS != 16
+ BS3_SET_BITS TMPL_BITS
+ jmp .print_str_end
+TMPL_BEGIN_TEXT
+ %endif
+.print_str_end:
+%endif
+%if TMPL_BITS != 64
+ pop ss
+ pop es
+%endif
+ jmp .return
+
+
+ ;
+ ; Switch the caller to ring-0, ring-1, ring-2 or ring-3.
+ ;
+ ; This implement this by saving the entire register context, calling
+ ; a transformation function (C) and restoring the modified register
+ ; context using a generic worker.
+ ;
+.to_ringX:
+ sub xSP, BS3REGCTX_size
+ mov xBX, xSP ; xBP = BS3REGCTX pointer.
+ call .save_context
+
+%if TMPL_BITS == 32
+ ; Convert xBP to flat pointer in 32-bit
+ push ss
+ push xBX
+ call Bs3SelProtFar32ToFlat32
+ add sSP, 8
+ mov xBX, xAX
+%endif
+ push xBX ; Save pointer for the final restore call.
+
+ ; Convert the register context from whatever it is to ring-0.
+BONLY64 sub rsp, 10h
+ mov ax, VAR_CALLER_AX
+ sub ax, BS3_SYSCALL_TO_RING0
+ push xAX
+BONLY16 push ss
+ push xBX
+ BS3_CALL Bs3RegCtxConvertToRingX, 2
+ add xSP, sCB + xCB BS3_ONLY_64BIT(+ 10h)
+
+ ; Restore the register context (does not return).
+ pop xBX ; restore saved pointer.
+BONLY64 sub rsp, 18h
+BONLY16 push ss
+ push xBX
+ BS3_CALL Bs3RegCtxRestore, 1
+ jmp Bs3Panic
+
+
+ ;
+ ; Restore context pointed to by cx:xSI.
+ ;
+.restore_ctx:
+ call .convert_ptr_arg_to_cx_xSI
+BONLY64 sub rsp, 10h
+ mov xDX, VAR_CALLER_DX
+ push xDX
+BONLY16 push cx
+ push xSI
+ BS3_CALL Bs3RegCtxRestore, 2
+ jmp Bs3Panic
+
+ ;
+ ; Return.
+ ;
+.return:
+ pop xBX ; saved mode
+ mov [BS3_DATA16_WRT(g_bBs3CurrentMode)], bl
+%if TMPL_BITS == 16
+ and bl, BS3_MODE_CODE_MASK
+ cmp bl, BS3_MODE_CODE_V86
+ je .return_to_v8086_from_16bit_krnl
+ cmp bl, BS3_MODE_CODE_32
+ je .return_to_32bit_from_16bit_krnl
+ %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jbe .return_pre_80386
+ %endif
+
+ popfd
+ pop esp
+ pop ebp
+%endif
+ pop sDI
+ pop sSI
+ pop sDX
+ pop sCX
+ pop sAX
+ pop sBX
+%if TMPL_BITS != 64
+ pop ds
+ leave
+ iret
+%else
+ mov es, [rsp+2]
+ mov ds, [rsp]
+ leave ; skips ds
+ iretq
+%endif
+
+%if TMPL_BITS == 16
+ %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
+ ; Variant of the above for 80286 and older.
+.return_pre_80386:
+ add sp, 0ch
+ pop di
+ pop bx ; dummy
+ pop si
+ pop bx ; dummy
+ pop dx
+ pop bx ; dummy
+ pop cx
+ pop bx ; dummy
+ pop ax
+ pop bx ; dummy
+ pop bx ; pushed twice
+ pop bx
+ pop ds
+ pop bp
+ iret
+ %endif
+
+.return_to_v8086_from_16bit_krnl:
+ int3
+ jmp .return_to_v8086_from_16bit_krnl
+
+ ;
+ ; Returning to 32-bit code may require us to expand and seed the eip
+ ; and esp addresses in the iret frame since these are truncated when
+ ; using a 16-bit interrupt handler.
+ ;
+ ; Incoming stack: New stack diff cpl:
+ ; bp + 0ah: [ss]
+ ; bp + 08h: [sp] bx + 38h: [ss] New stack same cpl:
+ ; bp + 06h: flags
+ ; bp + 04h: cs bx + 34h: [esp] bx + 30h: eflags
+ ; bp + 02h: ip
+ ; -------------- bx + 30h: eflags bx + 2ch: cs
+ ; bp + 00h: bp
+ ; bp - 02h: ds bx + 2ch: cs bx + 28h: eip
+ ; -------------
+ ; bp - 06h: ebx bx + 28h: eip bx + 26h: bp
+ ; -------------- bx + 24h: ds
+ ; bp - 0ah: eax bx + 26h: bp
+ ; bx + 24h: ds bx + 20h: ebx
+ ; bp - 0eh: ecx
+ ; bx + 20h: ebx bx + 1ch: eax
+ ; bp - 12h: edx
+ ; bx + 1ch: eax bx + 18h: ecx
+ ; bp - 16h: esi
+ ; bx + 18h: ecx bx + 14h: edx
+ ; bp - 1ah: edi
+ ; bx + 14h: edx bx + 10h: esi
+ ; bp - 1eh: esp
+ ; bx + 10h: esi bx + 0ch: edi
+ ; bp - 22h: ebp
+ ; bx + 0ch: edi bx + 08h: esp
+ ; bp - 26h: eflags
+ ; bx + 08h: esp bx + 04h: ebp
+ ;
+ ; bx + 04h: ebp bx + 00h: eflags
+ ;
+ ; bx + 00h: eflags
+ ;
+ ;
+ ; If we're returning to the same CPL, we're still using the stack of
+ ; the 32-bit caller. The high ESP word does not need restoring.
+ ;
+ ; If we're returning to a lower CPL, there on a 16-bit ring-0 stack,
+ ; however, the high ESP word is still that of the caller.
+ ;
+.return_to_32bit_from_16bit_krnl:
+ mov ax, cs
+ and al, 3
+ mov ah, 3
+ and ah, [xBP + xCB*2]
+ ; The iret frame doubles in size, so allocate more stack.
+ cmp al, ah
+ je .return_to_32bit_from_16bit_krnl_same_cpl_sub_sp
+ sub sp, 2*2
+.return_to_32bit_from_16bit_krnl_same_cpl_sub_sp:
+ sub sp, 3*2
+ mov bx, sp
+ ; Copy the saved registers.
+ xor di, di
+.return_to_32bit_from_16bit_krnl_copy_loop:
+ mov ecx, [bp + di - 26h]
+ mov [ss:bx + di], ecx
+ add di, 4
+ cmp di, 28h
+ jb .return_to_32bit_from_16bit_krnl_copy_loop
+ ; Convert the 16-bit iret frame to a 32-bit iret frame.
+ mov ecx, [BS3_DATA16_WRT(g_uBs3TrapEipHint)]
+ mov cx, [bp + 02h] ; ip
+ mov [ss:bx + 28h], ecx
+ mov ecx, 0f00d0000h
+ mov cx, [bp + 04h] ; cs
+ mov [ss:bx + 2ch], ecx
+ mov ecx, [ss:bx] ; caller eflags
+ mov cx, [bp + 06h] ; flags
+ mov [ss:bx + 30h], ecx
+ cmp al, ah
+ jz .return_to_32bit_from_16bit_krnl_do_return
+ mov ecx, [ss:bx + 08h] ; caller esp
+ mov cx, [bp + 08h] ; sp
+ mov [ss:bx + 34h], ecx
+ mov ecx, 0f00d0000h
+ mov cx, [bp + 0ah] ; ss
+ mov [ss:bx + 38h], ecx
+.return_to_32bit_from_16bit_krnl_do_return:
+ popfd
+ pop ecx ; esp - only the high bits!
+ mov cx, sp
+ mov esp, ecx
+ pop ebp
+ lea bp, [bx + 26h]
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop eax
+ pop ebx
+ pop ds
+ leave
+ iretd
+
+%endif ; 16-bit
+
+
+ ;
+ ; Internal function. ss:xBX = Pointer to register frame (BS3REGCTX).
+ ; @uses xAX
+ ;
+.save_context:
+%if TMPL_BITS == 16
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
+ jae .save_context_full
+
+ ;
+ ; 80286 or earlier.
+ ;
+
+ ; Clear the state area first.
+ push di
+ xor di, di
+.save_context_16_clear_loop:
+ mov word [ss:bx + di], 0
+ mov word [ss:bx + di + 2], 0
+ mov word [ss:bx + di + 4], 0
+ mov word [ss:bx + di + 6], 0
+ add di, 8
+ cmp di, BS3REGCTX_size
+ jb .save_context_16_clear_loop
+ pop di
+
+ ; Do the 8086/80186/80286 state saving.
+ mov ax, VAR_CALLER_AX
+ mov [ss:bx + BS3REGCTX.rax], ax
+ mov cx, VAR_CALLER_CX
+ mov [ss:bx + BS3REGCTX.rcx], ax
+ mov ax, VAR_CALLER_DX
+ mov [ss:bx + BS3REGCTX.rdx], ax
+ mov ax, VAR_CALLER_BX
+ mov [ss:bx + BS3REGCTX.rbx], ax
+ mov [ss:bx + BS3REGCTX.rsi], si
+ mov [ss:bx + BS3REGCTX.rdi], di
+ mov ax, VAR_CALLER_BP
+ mov [ss:bx + BS3REGCTX.rbp], ax
+ mov ax, VAR_CALLER_DS
+ mov [ss:bx + BS3REGCTX.ds], ax
+ mov [ss:bx + BS3REGCTX.es], es
+ mov ax, [xBP + xCB]
+ mov [ss:bx + BS3REGCTX.rip], ax
+ mov ax, [xBP + xCB*2]
+ mov [ss:bx + BS3REGCTX.cs], ax
+ and al, X86_SEL_RPL
+ mov [ss:bx + BS3REGCTX.bCpl], al
+ cmp al, 0
+ je .save_context_16_same
+ mov ax, [xBP + xCB*4]
+ mov [ss:bx + BS3REGCTX.rsp], ax
+ mov ax, [xBP + xCB*5]
+ mov [ss:bx + BS3REGCTX.ss], ax
+ jmp .save_context_16_done_stack
+.save_context_16_same:
+ mov ax, bp
+ add ax, xCB * (1 + 3)
+ mov [ss:bx + BS3REGCTX.rsp], ax
+ mov ax, ss
+ mov [ss:bx + BS3REGCTX.ss], ax
+.save_context_16_done_stack:
+ mov ax, [xBP + xCB*3]
+ mov [ss:bx + BS3REGCTX.rflags], ax
+ mov al, VAR_CALLER_MODE
+ mov [ss:bx + BS3REGCTX.bMode], al
+ cmp byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
+ jne .save_context_16_return
+ smsw [ss:bx + BS3REGCTX.cr0]
+ str [ss:bx + BS3REGCTX.tr]
+ sldt [ss:bx + BS3REGCTX.ldtr]
+.save_context_16_return:
+ or byte [ss:bx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64 | BS3REG_CTX_F_NO_CR4
+ ret
+%endif ; TMPL_BITS == 16
+
+ ;
+ ; 80386 or later.
+ ;
+.save_context_full:
+
+ ; Clear the state area.
+ push xDI
+ xor xDI, xDI
+ AssertCompileSizeAlignment(BS3REGCTX, 16)
+.save_context_full_clear_loop:
+%if TMPL_BITS != 64
+ mov dword [ss:xBX + xDI], 0
+ mov dword [ss:xBX + xDI + 4], 0
+ add xDI, 8
+%else
+ mov qword [xBX + xDI], 0
+ mov qword [xBX + xDI + 8], 0
+ add xDI, 10h
+%endif
+ cmp xDI, BS3REGCTX_size
+ jb .save_context_full_clear_loop
+ pop xDI
+
+ ; Do the 386+ state saving.
+%if TMPL_BITS == 16 ; save the high word of registered pushed on the stack.
+ mov ecx, VAR_CALLER_AX
+ mov [ss:bx + BS3REGCTX.rax], ecx
+ mov ecx, VAR_CALLER_CX
+ mov [ss:bx + BS3REGCTX.rcx], ecx
+ mov ecx, VAR_CALLER_DX
+ mov [ss:bx + BS3REGCTX.rdx], ecx
+ mov ecx, VAR_CALLER_BX
+ mov [ss:bx + BS3REGCTX.rbx], ecx
+ mov ecx, VAR_CALLER_EBP
+ mov [ss:bx + BS3REGCTX.rbp], ecx
+ mov ecx, VAR_CALLER_ESP
+ mov [ss:bx + BS3REGCTX.rsp], ecx
+ mov ecx, VAR_CALLER_SI
+ mov [ss:bx + BS3REGCTX.rsi], ecx
+ mov ecx, VAR_CALLER_DI
+ mov [ss:bx + BS3REGCTX.rdi], ecx
+ mov ecx, VAR_CALLER_EFLAGS
+ mov [ss:bx + BS3REGCTX.rflags], ecx
+
+ ; Seed high EIP word if 32-bit CS.
+ lar ecx, [bp + 4]
+ jnz .save_context_full_done_16bit_high_word
+ test ecx, X86LAR_F_D
+ jz .save_context_full_done_16bit_high_word
+ mov ecx, [BS3_DATA16_WRT(g_uBs3TrapEipHint)]
+ mov [ss:bx + BS3REGCTX.rip], ecx
+.save_context_full_done_16bit_high_word:
+%endif ; 16-bit
+ mov xAX, VAR_CALLER_AX
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rax], xAX
+ mov xCX, VAR_CALLER_CX
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rcx], xCX
+ mov xAX, VAR_CALLER_DX
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rdx], xAX
+ mov xAX, VAR_CALLER_BX
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rbx], xAX
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsi], sSI
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rdi], sDI
+ mov xAX, VAR_CALLER_BP
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rbp], xAX
+%if TMPL_BITS != 64
+ mov ax, VAR_CALLER_DS
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ax
+%else
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ds
+%endif
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.es], es
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fs], fs
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.gs], gs
+ mov xAX, [xBP + xCB]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rip], xAX
+ mov ax, [xBP + xCB*2]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cs], ax
+%if TMPL_MODE != BS3_MODE_RM
+ and al, X86_SEL_RPL
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], al
+ cmp al, 0
+ je .save_context_full_same
+ mov xAX, [xBP + xCB*4]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
+ mov ax, [xBP + xCB*5]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
+ jmp .save_context_full_done_stack
+%else
+ mov byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], 0
+%endif
+.save_context_full_same:
+ mov xAX, xBP
+ add xAX, xCB * (1 + 3)
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
+ mov ax, ss
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
+.save_context_full_done_stack:
+ mov xAX, [xBP + xCB*3]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], xAX
+
+ mov al, VAR_CALLER_MODE
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bMode], al
+%if TMPL_BITS == 64
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r8], r8
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r9], r9
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r10], r10
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r11], r11
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r12], r12
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r13], r13
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r14], r14
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r15], r15
+%endif
+ str [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.tr]
+ sldt [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ldtr]
+ mov sAX, cr0
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr0], sAX
+ mov sAX, cr2
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr2], sAX
+ mov sAX, cr3
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr3], sAX
+%if TMPL_BITS != 64
+ test byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
+ jnz .have_cr4
+ or byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
+ jmp .done_cr4
+.have_cr4:
+%endif
+ mov sAX, cr4
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr4], sAX
+%if TMPL_BITS != 64
+.done_cr4:
+ or byte [ss:xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
+
+ ; Deal with extended v8086 frame.
+ %if TMPL_BITS == 32
+ test dword [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], X86_EFL_VM
+ jz .save_context_full_return
+ %else
+ test byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
+ jz .save_context_full_return
+ mov dword [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], X86_EFL_VM
+ %endif
+ mov xAX, [xBP + xCB*4]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
+ mov ax, [xBP + xCB*5]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
+ mov ax, [xBP + xCB*6]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.es], ax
+ mov ax, [xBP + xCB*7]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ax
+ mov ax, [xBP + xCB*8]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fs], ax
+ mov ax, [xBP + xCB*9]
+ mov [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.gs], ax
+ mov byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], 3
+ jmp .save_context_full_return
+
+%endif ; !64-bit
+
+.save_context_full_return:
+ ret
+
+%if TMPL_BITS == 16
+ CPU 286
+%endif
+
+ ;
+ ; Internal function for converting a syscall pointer parameter (cx:xSI)
+ ; to a pointer we can use here in this context.
+ ;
+ ; Returns the result in cx:xSI.
+ ; @uses xAX, xCX, xDX
+ ;
+.convert_ptr_arg_to_cx_xSI:
+ call .convert_ptr_arg_to_flat
+%if TMPL_BITS == 16
+ ; Convert to tiled address.
+ mov si, ax ; offset.
+ shl dx, X86_SEL_SHIFT
+ add dx, BS3_SEL_TILED
+ mov cx, dx
+%else
+ ; Just supply a flat selector.
+ mov xSI, xAX
+ mov cx, ds
+%endif
+ ret
+
+ ;
+ ; Internal function for converting a syscall pointer parameter (caller CX:xSI)
+ ; to a real mode pointer.
+ ;
+ ; Returns the result in AX:SI.
+ ; @uses xAX, xCX, xDX
+ ;
+.convert_ptr_arg_to_real_mode_ax_si:
+ call .convert_ptr_arg_to_flat
+ mov si, ax
+%if TMPL_BITS == 16
+ mov ax, dx
+%else
+ shr eax, 16
+%endif
+ shl ax, 12
+ ret
+
+ ;
+ ; Internal function for the above that wraps the Bs3SelProtFar32ToFlat32 call.
+ ;
+ ; @returns eax (32-bit, 64-bit), dx+ax (16-bit).
+ ; @uses eax, ecx, edx
+ ;
+.convert_ptr_arg_to_flat:
+%if TMPL_BITS == 16
+ ; Convert to (32-bit) flat address first.
+ test byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
+ jz .convert_ptr_arg_to_flat_prot_16
+
+ mov ax, VAR_CALLER_CX
+ mov dx, ax
+ shl ax, 4
+ shr dx, 12
+ add ax, VAR_CALLER_SI
+ adc dx, 0
+ ret
+
+.convert_ptr_arg_to_flat_prot_16:
+ push es
+ push bx
+ push word VAR_CALLER_CX ; selector
+ xor ax, ax
+ test byte VAR_CALLER_MODE, BS3_MODE_CODE_16
+ jnz .caller_is_16_bit
+ mov ax, VAR_CALLER_SI_HI
+.caller_is_16_bit:
+ push ax ; offset high
+ push word VAR_CALLER_SI ; offset low
+ call Bs3SelProtFar32ToFlat32
+ add sp, 2*3
+ pop bx
+ pop es
+ ret
+
+%else ; 32 or 64 bit
+ test byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
+ jz .convert_ptr_arg_to_cx_xSI_prot
+
+ ; Convert real mode address to flat address and return it.
+ movzx eax, word VAR_CALLER_CX
+ shl eax, 4
+ movzx edx, word VAR_CALLER_SI
+ add eax, edx
+ ret
+
+ ; Convert to (32-bit) flat address.
+.convert_ptr_arg_to_cx_xSI_prot:
+ %if TMPL_BITS == 64
+ push r11
+ push r10
+ push r9
+ push r8
+ sub rsp, 10h
+ %endif
+ movzx ecx, word VAR_CALLER_CX
+ push xCX
+ mov eax, VAR_CALLER_SI
+ test byte VAR_CALLER_MODE, BS3_MODE_CODE_16
+ jz .no_masking_offset
+ and eax, 0ffffh
+.no_masking_offset:
+ push xAX
+ BS3_CALL Bs3SelProtFar32ToFlat32,2
+ add xSP, xCB*2 BS3_ONLY_64BIT(+ 10h)
+ %if TMPL_BITS == 64
+ pop r8
+ pop r9
+ pop r10
+ pop r11
+ %endif
+%endif
+ ret
+
+BS3_PROC_END_MODE Bs3TrapSystemCallHandler
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitAll.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitAll.c
new file mode 100644
index 00000000..3d22fb5c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitAll.c
@@ -0,0 +1,100 @@
+/* $Id: bs3-rm-InitAll.c $ */
+/** @file
+ * BS3Kit - Initialize all components, real mode.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+//#define BS3_USE_RM_TEXT_SEG 1
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-test.h"
+#include <iprt/asm-amd64-x86.h>
+
+BS3_MODE_PROTO_NOSB(void, Bs3EnteredMode,(void));
+
+
+BS3_DECL(void) Bs3InitAll_rm(void)
+{
+ uint8_t volatile BS3_FAR *pcTicksFlpyOff;
+
+ /*
+ * Detect CPU first as the memory init code will otherwise use 386
+ * instrunctions and cause trouble on older CPUs.
+ */
+ Bs3CpuDetect_rm_far();
+ Bs3InitMemory_rm_far();
+ Bs3InitGdt_rm_far();
+
+ /*
+ * Before we disable all interrupts, try convince the BIOS to stop the
+ * floppy motor, as it is kind of disturbing when the floppy light remains
+ * on for the whole testcase execution.
+ */
+ ASMIntDisable(); /* (probably already disabled, but no guarantees) */
+ pcTicksFlpyOff = (uint8_t volatile BS3_FAR *)BS3_FP_MAKE(0x40, 0x40);
+ if (*pcTicksFlpyOff)
+ {
+ uint32_t volatile BS3_FAR *pcTicks = (uint32_t volatile BS3_FAR *)BS3_FP_MAKE(0x40, 0x6c);
+ uint32_t cInitialTicks;
+
+ *pcTicksFlpyOff = 1; /* speed up the countdown, don't want to wait for two seconds here. */
+ cInitialTicks = *pcTicks;
+ ASMIntEnable();
+
+ while (*pcTicks == cInitialTicks)
+ ASMHalt();
+ }
+ ASMIntDisable();
+ Bs3PicSetup(false /*fForcedReInit*/);
+
+ /*
+ * Initialize IDTs and such.
+ */
+ if (g_uBs3CpuDetected & BS3CPU_F_LONG_MODE)
+ Bs3Trap64Init();
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386)
+ Bs3Trap32Init();
+ if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80286)
+ Bs3Trap16Init();
+ Bs3TrapRmV86Init();
+
+ /*
+ * Perform a real-mode enter to make some final environment adjustments
+ * (like installing our syscall).
+ */
+ Bs3EnteredMode_rm();
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitGdt.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitGdt.c
new file mode 100644
index 00000000..9187875b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitGdt.c
@@ -0,0 +1,71 @@
+/* $Id: bs3-rm-InitGdt.c $ */
+/** @file
+ * BS3Kit - Bs3InitGdt
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define BS3_USE_RM_TEXT_SEG 1
+#include "bs3kit-template-header.h"
+#include <iprt/asm.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+
+BS3_DECL_FAR(void) Bs3InitGdt_rm_far(void)
+{
+#if 0 /* This totaly messes us up when going back to raw-mode for BIOS work. */
+ Bs3Gdte_R0_CS16.Gen.u16LimitLow = Bs3Text16_Size - 1;
+ Bs3Gdte_R1_CS16.Gen.u16LimitLow = Bs3Text16_Size - 1;
+ Bs3Gdte_R2_CS16.Gen.u16LimitLow = Bs3Text16_Size - 1;
+ Bs3Gdte_R3_CS16.Gen.u16LimitLow = Bs3Text16_Size - 1;
+#endif
+ Bs3Gdte_RMTEXT16_CS.Gen.u16LimitLow = Bs3RmText16_Size - 1;
+ Bs3Gdte_X0TEXT16_CS.Gen.u16LimitLow = Bs3X0Text16_Size - 1;
+ Bs3Gdte_X1TEXT16_CS.Gen.u16LimitLow = Bs3X1Text16_Size - 1;
+
+ Bs3Gdte_RMTEXT16_CS.Gen.u16BaseLow = (uint16_t)Bs3RmText16_FlatAddr;
+ Bs3Gdte_X0TEXT16_CS.Gen.u16BaseLow = (uint16_t)Bs3X0Text16_FlatAddr;
+ Bs3Gdte_X1TEXT16_CS.Gen.u16BaseLow = (uint16_t)Bs3X1Text16_FlatAddr;
+
+ Bs3Gdte_RMTEXT16_CS.Gen.u8BaseHigh1 = (uint8_t)(Bs3RmText16_FlatAddr >> 16);
+ Bs3Gdte_X0TEXT16_CS.Gen.u8BaseHigh1 = (uint8_t)(Bs3X0Text16_FlatAddr >> 16);
+ Bs3Gdte_X1TEXT16_CS.Gen.u8BaseHigh1 = (uint8_t)(Bs3X1Text16_FlatAddr >> 16);
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitMemory.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitMemory.c
new file mode 100644
index 00000000..7e3d4bd2
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-rm-InitMemory.c
@@ -0,0 +1,317 @@
+/* $Id: bs3-rm-InitMemory.c $ */
+/** @file
+ * BS3Kit - Bs3InitMemory
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define BS3_USE_RM_TEXT_SEG 1
+#define BS3_BIOS_INLINE_RM
+#include "bs3kit-template-header.h"
+#include "bs3-cmn-memory.h"
+#include <iprt/asm.h>
+#include <VBox/VMMDevTesting.h>
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Slab control structure for the 4K management of low memory (< 1MB). */
+BS3SLABCTLLOW g_Bs3Mem4KLow;
+/** Slab control structure for the 4K management of tiled upper memory,
+ * between 1 MB and 16MB. */
+BS3SLABCTLUPPERTILED g_Bs3Mem4KUpperTiled;
+
+
+/** Translates a power of two request size to an slab list index. */
+uint8_t const g_aiBs3SlabListsByPowerOfTwo[12] =
+{
+ /* 2^0 = 1 */ 0,
+ /* 2^1 = 2 */ 0,
+ /* 2^2 = 4 */ 0,
+ /* 2^3 = 8 */ 0,
+ /* 2^4 = 16 */ 0,
+ /* 2^5 = 32 */ 1,
+ /* 2^6 = 64 */ 2,
+ /* 2^7 = 128 */ 3,
+ /* 2^8 = 256 */ 4,
+ /* 2^9 = 512 */ 5,
+ /* 2^10 = 1024 */ -1
+ /* 2^11 = 2048 */ -1
+};
+
+/** The slab list chunk sizes. */
+uint16_t const g_acbBs3SlabLists[BS3_MEM_SLAB_LIST_COUNT] =
+{
+ 16,
+ 32,
+ 64,
+ 128,
+ 256,
+ 512,
+};
+
+/** Low memory slab lists, sizes given by g_acbBs3SlabLists. */
+BS3SLABHEAD g_aBs3LowSlabLists[BS3_MEM_SLAB_LIST_COUNT];
+/** Upper tiled memory slab lists, sizes given by g_acbBs3SlabLists. */
+BS3SLABHEAD g_aBs3UpperTiledSlabLists[BS3_MEM_SLAB_LIST_COUNT];
+
+/** Slab control structure sizes for the slab lists.
+ * This is to help the allocator when growing a list. */
+uint16_t const g_cbBs3SlabCtlSizesforLists[BS3_MEM_SLAB_LIST_COUNT] =
+{
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 16 / 8 /*=32*/), 16),
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 32 / 8 /*=16*/), 32),
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 64 / 8 /*=8*/), 64),
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 128 / 8 /*=4*/), 128),
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 256 / 8 /*=2*/), 256),
+ RT_ALIGN(sizeof(BS3SLABCTL) - 4 + (4096 / 512 / 8 /*=1*/), 512),
+};
+
+
+/** The end RAM address below 4GB (approximately). */
+uint32_t g_uBs3EndOfRamBelow4G = 0;
+/** The end RAM address above 4GB, zero if no memory above 4GB. */
+uint64_t g_uBs3EndOfRamAbove4G = 0;
+
+
+/**
+ * Adds a range of memory to the tiled slabs.
+ *
+ * @param uRange Start of range.
+ * @param cbRange Size of range.
+ */
+static void bs3InitMemoryAddRange32(uint32_t uRange, uint32_t cbRange)
+{
+ uint32_t uRangeEnd = uRange + cbRange;
+ if (uRangeEnd < uRange)
+ uRangeEnd = UINT32_MAX;
+
+ /* Raise the end-of-ram-below-4GB marker? */
+ if (uRangeEnd > g_uBs3EndOfRamBelow4G)
+ g_uBs3EndOfRamBelow4G = uRangeEnd;
+
+ /* Applicable to tiled memory? */
+ if ( uRange < BS3_SEL_TILED_AREA_SIZE
+ && ( uRange >= _1M
+ || uRangeEnd >= _1M))
+ {
+ uint16_t cPages;
+
+ /* Adjust the start of the range such that it's at or above 1MB and page aligned. */
+ if (uRange < _1M)
+ {
+ cbRange -= _1M - uRange;
+ uRange = _1M;
+ }
+ else if (uRange & (_4K - 1U))
+ {
+ cbRange -= uRange & (_4K - 1U);
+ uRange = RT_ALIGN_32(uRange, _4K);
+ }
+
+ /* Adjust the end/size of the range such that it's page aligned and not beyond the tiled area. */
+ if (uRangeEnd > BS3_SEL_TILED_AREA_SIZE)
+ {
+ cbRange -= uRangeEnd - BS3_SEL_TILED_AREA_SIZE;
+ uRangeEnd = BS3_SEL_TILED_AREA_SIZE;
+ }
+ else if (uRangeEnd & (_4K - 1U))
+ {
+ cbRange -= uRangeEnd & (_4K - 1U);
+ uRangeEnd &= ~(uint32_t)(_4K - 1U);
+ }
+
+ /* If there is still something, enable it.
+ (We're a bit paranoid here don't trust the BIOS to only report a page once.) */
+ cPages = cbRange >> 12; /*div 4K*/
+ if (cPages)
+ {
+ unsigned i;
+ uRange -= _1M;
+ i = uRange >> 12; /*div _4K*/
+ while (cPages-- > 0)
+ {
+ uint16_t uLineToLong = ASMBitTestAndClear(g_Bs3Mem4KUpperTiled.Core.bmAllocated, i);
+ g_Bs3Mem4KUpperTiled.Core.cFreeChunks += uLineToLong;
+ i++;
+ }
+ }
+ }
+}
+
+
+BS3_DECL(void) BS3_FAR_CODE Bs3InitMemory_rm_far(void)
+{
+ INT15E820ENTRY Entry = { 0, 0, 0, 0 };
+ uint32_t cbEntry = sizeof(Entry);
+ uint32_t uCont = 0;
+ uint16_t i;
+ uint16_t cPages;
+ uint32_t u32;
+ uint32_t BS3_FAR *pu32Mmio;
+
+ /*
+ * Enable the A20 gate.
+ */
+ Bs3A20Enable();
+
+ /*
+ * Low memory (4K chunks).
+ * - 0x00000 to 0x004ff - Interrupt Vector table, BIOS data area.
+ * - 0x01000 to 0x0ffff - Stacks.
+ * - 0x10000 to 0x1yyyy - BS3TEXT16
+ * - 0x20000 to 0x26fff - BS3SYSTEM16
+ * - 0x29000 to 0xzzzzz - BS3DATA16, BS3TEXT32, BS3TEXT64, BS3DATA32, BS3DATA64 (in that order).
+ * - 0xzzzzZ to 0x9fdff - Free conventional memory.
+ * - 0x9fc00 to 0x9ffff - Extended BIOS data area (exact start may vary).
+ * - 0xa0000 to 0xbffff - VGA MMIO
+ * - 0xc0000 to 0xc7fff - VGA BIOS
+ * - 0xc8000 to 0xeffff - ROMs, tables, unusable.
+ * - 0xf0000 to 0xfffff - PC BIOS.
+ */
+ Bs3SlabInit(&g_Bs3Mem4KLow.Core, sizeof(g_Bs3Mem4KLow), 0 /*uFlatSlabPtr*/, 0xA0000 /* 640 KB*/, _4K);
+
+ /* Mark the stacks and whole image as allocated. */
+ cPages = (Bs3TotalImageSize + _4K - 1U) >> 12;
+ ASMBitSetRange(g_Bs3Mem4KLow.Core.bmAllocated, 0, 0x10 + cPages);
+
+ /* Mark any unused pages between BS3TEXT16 and BS3SYSTEM16 as free. */
+ cPages = (Bs3Text16_Size + (uint32_t)_4K - 1U) >> 12;
+ ASMBitClearRange(g_Bs3Mem4KLow.Core.bmAllocated, 0x10U + cPages, 0x20U);
+
+ /* In case the system has less than 640KB of memory, check the BDA variable for it. */
+ cPages = *(uint16_t BS3_FAR *)BS3_FP_MAKE(0x0000, 0x0413); /* KB of low memory */
+ if (cPages < 640)
+ {
+ cPages = 640 - cPages;
+ cPages = RT_ALIGN(cPages, 4);
+ cPages >>= 2;
+ ASMBitSetRange(g_Bs3Mem4KLow.Core.bmAllocated, 0xA0 - cPages, 0xA0);
+ }
+ else
+ ASMBitSet(g_Bs3Mem4KLow.Core.bmAllocated, 0x9F);
+
+ /* Recalc free pages. */
+ cPages = 0;
+ i = g_Bs3Mem4KLow.Core.cChunks;
+ while (i-- > 0)
+ cPages += !ASMBitTest(g_Bs3Mem4KLow.Core.bmAllocated, i);
+ g_Bs3Mem4KLow.Core.cFreeChunks = cPages;
+
+ /*
+ * First 16 MB of memory above 1MB. We start out by marking it all allocated.
+ */
+ Bs3SlabInit(&g_Bs3Mem4KUpperTiled.Core, sizeof(g_Bs3Mem4KUpperTiled), _1M, BS3_SEL_TILED_AREA_SIZE - _1M, _4K);
+
+ ASMBitSetRange(g_Bs3Mem4KUpperTiled.Core.bmAllocated, 0, g_Bs3Mem4KUpperTiled.Core.cChunks);
+ g_Bs3Mem4KUpperTiled.Core.cFreeChunks = 0;
+
+ /* Ask the BIOS about where there's memory, and make pages in between 1MB
+ and BS3_SEL_TILED_AREA_SIZE present. This means we're only interested
+ in entries describing usable memory, ASSUMING of course no overlaps. */
+ if ( (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80386
+ && Bs3BiosInt15hE820_rm_far(&Entry, &cbEntry, &uCont))
+ {
+ unsigned i = 0;
+ do
+ {
+ if (Entry.uType == INT15E820_TYPE_USABLE)
+ {
+ if (!(Entry.uBaseAddr >> 32))
+ /* Convert from 64-bit to 32-bit value and record it. */
+ bs3InitMemoryAddRange32((uint32_t)Entry.uBaseAddr,
+ (Entry.cbRange >> 32) ? UINT32_C(0xfffff000) : (uint32_t)Entry.cbRange);
+ else
+ {
+ uint64_t uEnd = Entry.uBaseAddr + Entry.cbRange;
+ if (uEnd > g_uBs3EndOfRamAbove4G)
+ g_uBs3EndOfRamAbove4G = uEnd;
+ }
+ }
+
+ /* next */
+ Entry.uType = 0;
+ cbEntry = sizeof(Entry);
+ i++;
+ } while ( uCont != 0
+ && i < 2048
+ && Bs3BiosInt15hE820_rm_far(&Entry, &cbEntry, &uCont));
+ }
+ /* Try the 286+ API for getting memory above 1MB and (usually) below 16MB. */
+ else if ( (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80286
+ && (u32 = Bs3BiosInt15h88()) != UINT32_MAX
+ && u32 > 0)
+ bs3InitMemoryAddRange32(_1M, u32 * _1K);
+
+ /*
+ * Check if we've got the VMMDev MMIO testing memory mapped above 1MB.
+ */
+ pu32Mmio = (uint32_t BS3_FAR *)BS3_FP_MAKE(VMMDEV_TESTING_MMIO_RM_SEL,
+ VMMDEV_TESTING_MMIO_RM_OFF2(VMMDEV_TESTING_MMIO_OFF_NOP));
+ if (*pu32Mmio == VMMDEV_TESTING_NOP_RET)
+ {
+ Bs3Printf("Memory: Found VMMDev MMIO testing region\n");
+ if (!ASMBitTestAndSet(g_Bs3Mem4KUpperTiled.Core.bmAllocated, 1))
+ g_Bs3Mem4KUpperTiled.Core.cFreeChunks--;
+
+ }
+
+ /*
+ * Initialize the slab lists.
+ */
+ for (i = 0; i < BS3_MEM_SLAB_LIST_COUNT; i++)
+ {
+ Bs3SlabListInit(&g_aBs3LowSlabLists[i], g_acbBs3SlabLists[i]);
+ Bs3SlabListInit(&g_aBs3UpperTiledSlabLists[i], g_acbBs3SlabLists[i]);
+ }
+
+#if 0
+ /*
+ * For debugging.
+ */
+ Bs3Printf("Memory-low: %u/%u chunks bmAllocated[]=", g_Bs3Mem4KLow.Core.cFreeChunks, g_Bs3Mem4KLow.Core.cChunks);
+ for (i = 0; i < 20; i++)
+ Bs3Printf("%02x ", g_Bs3Mem4KLow.Core.bmAllocated[i]);
+ Bs3Printf("\n");
+ Bs3Printf("Memory-upt: %u/%u chunks bmAllocated[]=", g_Bs3Mem4KUpperTiled.Core.cFreeChunks, g_Bs3Mem4KUpperTiled.Core.cChunks);
+ for (i = 0; i < 32; i++)
+ Bs3Printf("%02x ", g_Bs3Mem4KUpperTiled.Core.bmAllocated[i]);
+ Bs3Printf("...\n");
+#endif
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-shutdown.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-shutdown.c
new file mode 100644
index 00000000..58b64ca3
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-shutdown.c
@@ -0,0 +1,87 @@
+/* $Id: bs3-shutdown.c $ */
+/** @file
+ * BS3Kit - Shutdown VM from PE16 - proof of concept (BS3Kit).
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "bs3kit.h"
+#include <iprt/assert.h>
+#include <iprt/asm-amd64-x86.h>
+
+AssertCompileSize(uint16_t, 2);
+AssertCompileSize(uint32_t, 4);
+AssertCompileSize(uint64_t, 8);
+
+extern uint16_t ASMGetMsw();
+#pragma aux ASMGetMsw = \
+ ".286" \
+ "smsw ax" \
+ value [ax] \
+ modify exact;
+
+extern void ASMSetMsw(uint16_t uMsw);
+#pragma aux ASMSetMsw = \
+ ".286p" \
+ "lmsw ax" \
+ parm [ax] \
+ modify exact;
+
+/* Just a sample. */
+BS3_DECL(void) Main_pe16(void)
+{
+ uint16_t uMsw = ASMGetMsw();
+ Bs3Printf("msw=%#x cr0=%RX32 g_uBs3CpuDetected=%#x\n", uMsw, ASMGetCR0(), g_uBs3CpuDetected);
+ Bs3Printf("cr2=%RX32 cr3=%RX32\n", ASMGetCR2(), ASMGetCR3());
+ ASMSetMsw(X86_CR0_PE);
+ Bs3Printf("lmsw(PE) => msw=%#x cr0=%RX32\n", ASMGetMsw(), ASMGetCR0());
+ ASMSetMsw(UINT16_MAX);
+ Bs3Printf("lmsw(0xffff) => msw=%#x cr0=%RX32\n", ASMGetMsw(), ASMGetCR0());
+ ASMSetCR0(X86_CR0_PE);
+ Bs3Printf("ASMSetCR0(X86_CR0_PE) => msw=%#x cr0=%RX32\n", ASMGetMsw(), ASMGetCR0());
+ ASMSetCR0(UINT32_C(0x7fffffff));
+ Bs3Printf("ASMSetCR0(0x7fffffff) => msw=%#x cr0=%RX32\n", ASMGetMsw(), ASMGetCR0());
+
+ Bs3TestInit("bs3-shutdown");
+ Bs3TestPrintf("detected cpu: %#x\n", g_uBs3CpuDetected);
+#if 1
+ ASMHalt();
+#else
+ Bs3Shutdown();
+#endif
+ return;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-system-data.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-system-data.asm
new file mode 100644
index 00000000..6be79dc9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-system-data.asm
@@ -0,0 +1,1056 @@
+; $Id: bs3-system-data.asm $
+;; @file
+; BS3Kit - GDT
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit.mac"
+
+%define BS3_SYSTEM16_BASE_16_23 ((BS3_ADDR_BS3SYSTEM16 >> 16) & 0xff)
+%define BS3_SYSTEM16_BASE_LOW(a_DataSym) ((BS3_DATA_NM(a_DataSym) - StartSystem16) & 0xffff)
+
+;;
+; The GDT (X86DESCGENERIC).
+;
+BS3_BEGIN_SYSTEM16
+StartSystem16:
+ db 10, 13, 'eye-catcher: SYSTEM16.......', 10, 13 ; 32 bytes long
+BS3_GLOBAL_DATA Bs3Gdt, 4000h - 20h
+
+;; Macro for checking GDT offsets as we go along.
+;; @param %1 The expected current offset.
+%macro BS3GdtAssertOffset 1
+ %ifndef KBUILD_GENERATING_MAKEFILE_DEPENDENCIES
+ %if ($ - BS3_DATA_NM(Bs3Gdt)) != %1
+ %assign offActual ($ - BS3_DATA_NM(Bs3Gdt))
+ %error "BS3GdtAssertOffset: Bad offset: " %+ offActual %+ ", expected " %+ %1
+ %endif
+ %endif
+%endmacro
+
+ dw 00000h, 00000h, 00000h, 00000h ; null selector
+BS3GdtAssertOffset 8
+
+ ;
+ ; 008h..0f8h - System selectors and other stuff
+ ;
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 008h - currently unused
+
+BS3_GLOBAL_DATA Bs3Gdte_Ldt, 16 ; Entry 010h
+ dw BS3_DATA_NM(Bs3LdtEnd) - BS3_DATA_NM(Bs3Ldt) - 1
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Ldt)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_LDT | 0x80
+ dw 00000h
+ dw 00000h, 00000h, 00000h, 00000h ; zero for 64-bit mode.
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss16, 8 ; Entry 020h
+ dw 0002bh ; 16-bit TSS.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss16)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_286_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss16DoubleFault, 8 ; Entry 028h
+ dw 0002bh ; 16-bit TSS, double fault.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss16DoubleFault)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_286_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss16Spare0, 8 ; Entry 030h
+ dw 0002bh ; 16-bit TSS, spare 0.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss16Spare0)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_286_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss16Spare1, 8 ; Entry 038h
+ dw 0002bh ; 16-bit TSS, spare 0.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss16Spare1)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_286_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32, 8 ; Entry 040h
+ dw 00067h ; 32-bit TSS.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32DoubleFault, 8 ; Entry 048h
+ dw 00067h ; 32-bit TSS, double fault.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32DoubleFault)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32Spare0, 8 ; Entry 050h
+ dw 00067h ; 32-bit TSS, spare 0.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32Spare0)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32Spare1, 8 ; Entry 058h
+ dw 00067h ; 32-bit TSS, spare 1.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32Spare1)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32IobpIntRedirBm, 8 ; Entry 060h
+ ; 32-bit TSS, with I/O permission & interrupt redirection bitmaps.
+ dw BS3_DATA_NM(Bs3SharedIobpEnd) - BS3_DATA_NM(Bs3Tss32WithIopb) - 1
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32WithIopb)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss32IntRedirBm, 8 ; Entry 068h
+ ; 32-bit TSS, with interrupt redirection bitmap (IOBP stripped by limit).
+ dw BS3_DATA_NM(Bs3SharedIobp) - BS3_DATA_NM(Bs3Tss32WithIopb) - 1
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss32WithIopb)
+ db BS3_SYSTEM16_BASE_16_23
+ db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80
+ dw 0
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss64, 8 ; Entry 070h
+ dw 00067h ; 64-bit TSS.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss64)
+ db BS3_SYSTEM16_BASE_16_23
+ db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80
+ dw 0
+ dw 00000h, 00000h, 00000h, 00000h
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss64Spare0, 8 ; Entry 080h
+ dw 00067h ; 64-bit TSS, spare 0.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss64Spare0)
+ db BS3_SYSTEM16_BASE_16_23
+ db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80
+ dw 0
+ dw 00000h, 00000h, 00000h, 00000h
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss64Spare1, 8 ; Entry 090h
+ dw 00067h ; 64-bit TSS, spare 1.
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss64Spare1)
+ db BS3_SYSTEM16_BASE_16_23
+ db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80
+ dw 0
+ dw 00000h, 00000h, 00000h, 00000h
+
+BS3_GLOBAL_DATA Bs3Gdte_Tss64Iobp, 8 ; Entry 0a0h
+ ; 64-bit TSS, with I/O permission bitmap
+ dw BS3_DATA_NM(Bs3SharedIobp) - BS3_DATA_NM(Bs3Tss64WithIopb) - 1
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Tss64WithIopb)
+ db BS3_SYSTEM16_BASE_16_23
+ db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80
+ dw 0
+ dw 00000h, 00000h, 00000h, 00000h
+
+BS3GdtAssertOffset 0b0h
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0b0h - currently unused
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0b8h - currently unused
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0c0h - currently unused
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0c8h - currently unused
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0d0h - currently unused
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 0d8h - currently unused
+
+ ; Misc selectors.
+BS3_GLOBAL_DATA Bs3Gdte_RMTEXT16_CS, 8 ; Entry 0e0h
+ dw 0fffeh, 00000h ; 16-bit conforming code (read+exec) segment, accessed. Will be finalized at startup.
+ dw 09f00h, 00000h
+BS3_GLOBAL_DATA Bs3Gdte_X0TEXT16_CS, 8 ; Entry 0e8h
+ dw 0fffeh, 00000h ; 16-bit conforming code (read+exec) segment, accessed. Will be finalized at startup.
+ dw 09f00h, 00000h
+BS3_GLOBAL_DATA Bs3Gdte_X1TEXT16_CS, 8 ; Entry 0f0h
+ dw 0fffeh, 00000h ; 16-bit conforming code (read+exec) segment, accessed. Will be finalized at startup.
+ dw 09f00h, 00000h
+BS3_GLOBAL_DATA Bs3Gdte_R0_MMIO16, 8 ; Entry 0f8h
+ dw 0ffffh, 0f000h, 0930dh, 00000h ; 16-bit VMMDev MMIO segment with base 0df000h.
+BS3GdtAssertOffset 0100h
+
+
+;;
+; Macro that defines the selectors for ring-%1.
+;
+%macro BS3_GDT_RING_X_SELECTORS 1
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _First, 80h
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS16, 8 ; Entry 100h
+ dw 0ffffh, (0xffff & BS3_ADDR_BS3TEXT16) ; 16-bit code segment with base 010000h.
+ dw 09b01h | (%1 << 0dh) | (0xff & (BS3_ADDR_BS3TEXT16 >> 16)), 00000h | (0xff00 & (BS3_ADDR_BS3TEXT16 >> 16))
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _DS16, 8 ; Entry 108h
+ dw 0ffffh, (0xffff & BS3_ADDR_BS3DATA16) ; 16-bit data segment with base 029000h.
+ dw 09300h | (%1 << 0dh) | (0xff & (BS3_ADDR_BS3DATA16 >> 16)), 00000h | (0xff00 & (BS3_ADDR_BS3DATA16 >> 16))
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _SS16, 8 ; Entry 110h
+ dw 0ffffh, 00000h ; 16-bit stack segment with base 0.
+ dw 09300h | (%1 << 0dh), 00000h
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS32, 8 ; Entry 118h
+ dw 0ffffh, 00000h ; 32-bit flat code segment.
+ dw 09b00h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _DS32, 8 ; Entry 120h
+ dw 0ffffh, 00000h ; 32-bit flat data segment.
+ dw 09300h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _SS32, 8 ; Entry 128h
+ dw 0ffffh, 00000h ; 32-bit flat stack segment.
+ dw 09300h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS64, 8 ; Entry 130h
+ dw 0ffffh, 00000h ; 64-bit code segment.
+ dw 09a00h | (%1 << 0dh), 000afh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _DS64, 8 ; Entry 138h (also SS64)
+ dw 0ffffh, 00000h ; 64-bit stack and data segment.
+ dw 09300h | (%1 << 0dh), 000afh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS16_EO, 8 ; Entry 140h
+ dw 0ffffh, (0xffff & BS3_ADDR_BS3TEXT16) ; 16-bit code segment with base 01000h, not accessed, execute only, short limit.
+ dw 09800h | (%1 << 0dh) | (0xff & (BS3_ADDR_BS3TEXT16 >> 16)), 00000h | (0xff00 & (BS3_ADDR_BS3TEXT16 >> 16))
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS16_CNF, 8 ; Entry 148h
+ dw 0ffffh, (0xffff & BS3_ADDR_BS3TEXT16) ; 16-bit code segment with base 01000h, not accessed, execute only, short limit.
+ dw 09e00h | (%1 << 0dh) | (0xff & (BS3_ADDR_BS3TEXT16 >> 16)), 00000h | (0xff00 & (BS3_ADDR_BS3TEXT16 >> 16))
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS16_CND_EO, 8 ; Entry 150h
+ dw 0fffeh, 00000h ; 16-bit conforming code segment with base 0, not accessed, execute only, short limit.
+ dw 09c00h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS32_EO, 8 ; Entry 158h
+ dw 0ffffh, 00000h ; 32-bit flat code segment, not accessed, execute only.
+ dw 09800h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS32_CNF, 8 ; Entry 160h
+ dw 0ffffh, 00000h ; 32-bit flat conforming code segment, not accessed.
+ dw 09e00h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS32_CNF_EO, 8 ; Entry 168h
+ dw 0ffffh, 00000h ; 32-bit flat conforming code segment, not accessed, execute only.
+ dw 09c00h | (%1 << 0dh), 000cfh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS64_EO, 8 ; Entry 170h
+ dw 0ffffh, 00000h ; 64-bit code segment, not accessed, execute only.
+ dw 09800h | (%1 << 0dh), 000afh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS64_CNF, 8 ; Entry 178h
+ dw 0ffffh, 00000h ; 64-bit conforming code segment, not accessed.
+ dw 09e00h | (%1 << 0dh), 000afh
+
+BS3_GLOBAL_DATA Bs3Gdte_R %+ %1 %+ _CS64_CNF_EO, 8 ; Entry 180h
+ dw 0ffffh, 00000h ; 64-bit conforming code segment, execute only, not accessed.
+ dw 09c00h | (%1 << 0dh), 000afh
+
+;; @todo expand down segments.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 188h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 190h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 198h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1a0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1a8h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1b0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1b8h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1c0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1c8h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1d0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1d8h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1e0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1e8h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1f0h - unused.
+ dw 00000h, 00000h, 00000h, 00000h ; Entry 1f8h - unused.
+%endmacro
+
+ ;
+ ; 100h..1f8h - Ring-0 selectors.
+ ;
+ BS3_GDT_RING_X_SELECTORS 0
+
+ ;
+ ; 200h..2f8h - Ring-1 selectors.
+ ;
+ BS3_GDT_RING_X_SELECTORS 1
+
+ ;
+ ; 300h..3f8h - Ring-2 selectors.
+ ;
+ BS3_GDT_RING_X_SELECTORS 2
+
+ ;
+ ; 400h..4f8h - Ring-3 selectors.
+ ;
+ BS3_GDT_RING_X_SELECTORS 3
+
+ ;
+ ; 500..5f8h - Named spare GDT entries.
+ ;
+BS3GdtAssertOffset 0500h
+BS3_GLOBAL_DATA Bs3GdteSpare00, 8 ; Entry 500h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare01, 8 ; Entry 508h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare02, 8 ; Entry 510h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare03, 8 ; Entry 518h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare04, 8 ; Entry 520h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare05, 8 ; Entry 528h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare06, 8 ; Entry 530h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare07, 8 ; Entry 538h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare08, 8 ; Entry 540h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare09, 8 ; Entry 548h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0a, 8 ; Entry 550h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0b, 8 ; Entry 558h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0c, 8 ; Entry 560h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0d, 8 ; Entry 568h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0e, 8 ; Entry 570h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare0f, 8 ; Entry 578h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare10, 8 ; Entry 580h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare11, 8 ; Entry 588h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare12, 8 ; Entry 590h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare13, 8 ; Entry 598h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare14, 8 ; Entry 5a0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare15, 8 ; Entry 5a8h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare16, 8 ; Entry 5b0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare17, 8 ; Entry 5b8h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare18, 8 ; Entry 5c0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare19, 8 ; Entry 5c8h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1a, 8 ; Entry 5d0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1b, 8 ; Entry 5d8h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1c, 8 ; Entry 5e0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1d, 8 ; Entry 5e8h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1e, 8 ; Entry 5f0h
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteSpare1f, 8 ; Entry 5f8h
+ dq 0
+
+ ;
+ ; 600..df8h - 16-bit DPL=3 data segments covering the first 16MB of memory.
+ ;
+BS3_GLOBAL_DATA Bs3GdteTiled, 8 ; Entry 600h
+%assign u8HighBase 0
+%rep 256
+ dw 0ffffh, 00000h, 0f300h | u8HighBase, 00000h
+%assign u8HighBase u8HighBase + 1
+%endrep
+ ;
+ ; e00..ff8h - Free GDTEs.
+ ;
+BS3GdtAssertOffset 0e00h
+BS3_GLOBAL_DATA Bs3GdteFreePart1, 200h
+ times 200h db 0
+
+ ;
+ ; 1000h - the real mode segment number for BS3TEXT16. DPL=0, BASE=0x10000h, conforming, exec, read.
+ ;
+BS3GdtAssertOffset 01000h
+BS3_GLOBAL_DATA Bs3Gdte_CODE16, 8h
+ dw 0ffffh, 00000h, 09f01h, 00000h
+
+ ;
+ ; 1008..17f8h - Free GDTEs.
+ ;
+BS3GdtAssertOffset 01008h
+BS3_GLOBAL_DATA Bs3GdteFreePart2, 07f8h
+ times 07f8h db 0
+
+ ;
+ ; 1800..1ff8h - 16-bit DPL=0 data/stack segments covering the first 16MB of memory.
+ ;
+BS3GdtAssertOffset 01800h
+BS3_GLOBAL_DATA Bs3GdteTiledR0, 8 ; Entry 1800h
+%assign u8HighBase 0
+%rep 256
+ dw 0ffffh, 00000h, 09300h | u8HighBase, 00000h
+%assign u8HighBase u8HighBase + 1
+%endrep
+
+ ;
+ ; 2000h - the real mode segment number for BS3SYSTEM. DPL=3. BASE=0x20000h
+ ;
+BS3GdtAssertOffset 02000h
+BS3_GLOBAL_DATA Bs3Gdte_SYSTEM16, 8h
+ dw 0ffffh, 00000h, 0f302h, 00000h
+
+ ;
+ ; 2008..28f8h - Free GDTEs.
+ ;
+BS3_GLOBAL_DATA Bs3GdteFreePart3, 08f8h
+ times 08f8h db 0
+
+ ;
+ ; 2900h - the real mode segment number for BS3KIT_GRPNM_DATA16. DPL=3. BASE=0x29000h
+ ;
+BS3GdtAssertOffset 02900h
+BS3_GLOBAL_DATA Bs3Gdte_DATA16, 8h
+ dw 0ffffh, 09000h, 0f302h, 00000h
+
+ ;
+ ; 2908..2f98h - Free GDTEs.
+ ;
+BS3GdtAssertOffset 02908h
+BS3_GLOBAL_DATA Bs3GdteFreePart4, 698h
+ times 698h db 0
+
+ ;
+ ; 2be0..2fe0h - 8 spare entries preceeding the test page which we're free
+ ; to mess with page table protection.
+ ;
+BS3GdtAssertOffset 02fa0h
+BS3_GLOBAL_DATA Bs3GdtePreTestPage08, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage07, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage06, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage05, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage04, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage03, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage02, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdtePreTestPage01, 8
+ dq 0
+
+ ;
+ ; 2fe0..3fd8h - 16 Test entries at the start of the page where we're free
+ ; to mess with page table protection.
+ ;
+BS3GdtAssertOffset 02fe0h
+AssertCompile(($ - $$) == 0x3000)
+BS3_GLOBAL_DATA Bs3GdteTestPage, 0
+BS3_GLOBAL_DATA Bs3GdteTestPage00, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage01, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage02, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage03, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage04, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage05, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage06, 8
+ dq 0
+BS3_GLOBAL_DATA Bs3GdteTestPage07, 8
+ dq 0
+BS3GdtAssertOffset 3020h
+ times 0fb8h db 0
+BS3GdtAssertOffset 3fd8h
+BS3_GLOBAL_DATA Bs3GdtEnd, 0
+ db 10, 13, 'GDTE', 10, 13 ; alignment padding (next address on 16 byte boundrary).
+BS3GdtAssertOffset 4000h - 20h ; We're at a page boundrary here! Only GDT and eyecatchers on page starting at 3000h!
+AssertCompile(($ - $$) == 0x4000)
+
+
+
+;;
+; The 16-bit TSS.
+;
+BS3_GLOBAL_DATA Bs3Tss16, X86TSS16_size
+istruc X86TSS16
+ at X86TSS16.selPrev, dw 0
+ at X86TSS16.sp0, dw BS3_ADDR_STACK_R0
+ at X86TSS16.ss0, dw BS3_SEL_R0_SS16
+ at X86TSS16.sp1, dw BS3_ADDR_STACK_R1
+ at X86TSS16.ss1, dw BS3_SEL_R1_SS16
+ at X86TSS16.sp2, dw BS3_ADDR_STACK_R2
+ at X86TSS16.ss2, dw BS3_SEL_R2_SS16
+ at X86TSS16.ip, dw 0
+ at X86TSS16.flags, dw 0
+ at X86TSS16.ax, dw 0
+ at X86TSS16.cx, dw 0
+ at X86TSS16.dx, dw 0
+ at X86TSS16.bx, dw 0
+ at X86TSS16.sp, dw 0
+ at X86TSS16.bp, dw 0
+ at X86TSS16.si, dw 0
+ at X86TSS16.di, dw 0
+ at X86TSS16.es, dw 0
+ at X86TSS16.cs, dw 0
+ at X86TSS16.ss, dw 0
+ at X86TSS16.ds, dw 0
+ at X86TSS16.selLdt, dw 0
+iend
+
+;;
+; 16-bit TSS for (trying to) handle double faults.
+BS3_GLOBAL_DATA Bs3Tss16DoubleFault, X86TSS16_size
+istruc X86TSS16
+ at X86TSS16.selPrev, dw 0
+ at X86TSS16.sp0, dw BS3_ADDR_STACK_R0
+ at X86TSS16.ss0, dw BS3_SEL_R0_SS16
+ at X86TSS16.sp1, dw BS3_ADDR_STACK_R1
+ at X86TSS16.ss1, dw BS3_SEL_R1_SS16
+ at X86TSS16.sp2, dw BS3_ADDR_STACK_R2
+ at X86TSS16.ss2, dw BS3_SEL_R2_SS16
+ at X86TSS16.ip, dw 0 ; Will be filled in by routine setting up 16-bit mode w/ traps++.
+ at X86TSS16.flags, dw X86_EFL_1
+ at X86TSS16.ax, dw 0
+ at X86TSS16.cx, dw 0
+ at X86TSS16.dx, dw 0
+ at X86TSS16.bx, dw 0
+ at X86TSS16.sp, dw BS3_ADDR_STACK_R0_IST1
+ at X86TSS16.bp, dw 0
+ at X86TSS16.si, dw 0
+ at X86TSS16.di, dw 0
+ at X86TSS16.es, dw BS3_SEL_R0_DS16
+ at X86TSS16.cs, dw BS3_SEL_R0_CS16
+ at X86TSS16.ss, dw BS3_SEL_R0_SS16
+ at X86TSS16.ds, dw BS3_SEL_R0_DS16
+ at X86TSS16.selLdt, dw 0
+iend
+
+;;
+; A spare 16-bit TSS for testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss16Spare0, X86TSS16_size
+istruc X86TSS16
+ at X86TSS16.selPrev, dw 0
+ at X86TSS16.sp0, dw BS3_ADDR_STACK_R0
+ at X86TSS16.ss0, dw BS3_SEL_R0_SS16
+ at X86TSS16.sp1, dw BS3_ADDR_STACK_R1
+ at X86TSS16.ss1, dw BS3_SEL_R1_SS16
+ at X86TSS16.sp2, dw BS3_ADDR_STACK_R2
+ at X86TSS16.ss2, dw BS3_SEL_R2_SS16
+ at X86TSS16.ip, dw 0 ; Will be filled in by routine setting up 16-bit mode w/ traps++.
+ at X86TSS16.flags, dw X86_EFL_1
+ at X86TSS16.ax, dw 0
+ at X86TSS16.cx, dw 0
+ at X86TSS16.dx, dw 0
+ at X86TSS16.bx, dw 0
+ at X86TSS16.sp, dw BS3_ADDR_STACK_R0_IST2
+ at X86TSS16.bp, dw 0
+ at X86TSS16.si, dw 0
+ at X86TSS16.di, dw 0
+ at X86TSS16.es, dw BS3_SEL_R0_DS16
+ at X86TSS16.cs, dw BS3_SEL_R0_CS16
+ at X86TSS16.ss, dw BS3_SEL_R0_SS16
+ at X86TSS16.ds, dw BS3_SEL_R0_DS16
+ at X86TSS16.selLdt, dw 0
+iend
+
+;;
+; A spare 16-bit TSS for testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss16Spare1, X86TSS16_size
+istruc X86TSS16
+ at X86TSS16.selPrev, dw 0
+ at X86TSS16.sp0, dw BS3_ADDR_STACK_R0
+ at X86TSS16.ss0, dw BS3_SEL_R0_SS16
+ at X86TSS16.sp1, dw BS3_ADDR_STACK_R1
+ at X86TSS16.ss1, dw BS3_SEL_R1_SS16
+ at X86TSS16.sp2, dw BS3_ADDR_STACK_R2
+ at X86TSS16.ss2, dw BS3_SEL_R2_SS16
+ at X86TSS16.ip, dw 0 ; Will be filled in by routine setting up 16-bit mode w/ traps++.
+ at X86TSS16.flags, dw X86_EFL_1
+ at X86TSS16.ax, dw 0
+ at X86TSS16.cx, dw 0
+ at X86TSS16.dx, dw 0
+ at X86TSS16.bx, dw 0
+ at X86TSS16.sp, dw BS3_ADDR_STACK_R0_IST4
+ at X86TSS16.bp, dw 0
+ at X86TSS16.si, dw 0
+ at X86TSS16.di, dw 0
+ at X86TSS16.es, dw BS3_SEL_R0_DS16
+ at X86TSS16.cs, dw BS3_SEL_R0_CS16
+ at X86TSS16.ss, dw BS3_SEL_R0_SS16
+ at X86TSS16.ds, dw BS3_SEL_R0_DS16
+ at X86TSS16.selLdt, dw 0
+iend
+
+
+;;
+; The 32-bit TSS.
+;
+BS3_GLOBAL_DATA Bs3Tss32, X86TSS32_size
+istruc X86TSS32
+ at X86TSS32.selPrev, dw 0
+ at X86TSS32.padding1, dw 0
+ at X86TSS32.esp0, dd BS3_ADDR_STACK_R0
+ at X86TSS32.ss0, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss0, dw 1
+ at X86TSS32.esp1, dd 1
+ at X86TSS32.ss1, dw BS3_SEL_R1_SS32
+ at X86TSS32.padding_ss1, dw 1
+ at X86TSS32.esp2, dd 1
+ at X86TSS32.ss2, dw BS3_SEL_R2_SS32
+ at X86TSS32.padding_ss2, dw 1
+ at X86TSS32.cr3, dd 0
+ at X86TSS32.eip, dd 0
+ at X86TSS32.eflags, dd X86_EFL_1
+ at X86TSS32.eax, dd 0
+ at X86TSS32.ecx, dd 0
+ at X86TSS32.edx, dd 0
+ at X86TSS32.ebx, dd 0
+ at X86TSS32.esp, dd 0
+ at X86TSS32.ebp, dd 0
+ at X86TSS32.esi, dd 0
+ at X86TSS32.edi, dd 0
+ at X86TSS32.es, dw 0
+ at X86TSS32.padding_es, dw 0
+ at X86TSS32.cs, dw 0
+ at X86TSS32.padding_cs, dw 0
+ at X86TSS32.ss, dw 0
+ at X86TSS32.padding_ss, dw 0
+ at X86TSS32.ds, dw 0
+ at X86TSS32.padding_ds, dw 0
+ at X86TSS32.fs, dw 0
+ at X86TSS32.padding_fs, dw 0
+ at X86TSS32.gs, dw 0
+ at X86TSS32.padding_gs, dw 0
+ at X86TSS32.selLdt, dw 0
+ at X86TSS32.padding_ldt, dw 0
+ at X86TSS32.fDebugTrap, dw 0
+ at X86TSS32.offIoBitmap, dw (BS3_DATA_NM(Bs3SharedIobp) - BS3_DATA_NM(Bs3Tss32WithIopb))
+iend
+
+;;
+; The 32-bit TSS for handling double faults.
+BS3_GLOBAL_DATA Bs3Tss32DoubleFault, X86TSS32_size
+istruc X86TSS32
+ at X86TSS32.selPrev, dw 0
+ at X86TSS32.padding1, dw 0
+ at X86TSS32.esp0, dd BS3_ADDR_STACK_R0
+ at X86TSS32.ss0, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss0, dw 1
+ at X86TSS32.esp1, dd 1
+ at X86TSS32.ss1, dw BS3_SEL_R1_SS32
+ at X86TSS32.padding_ss1, dw 1
+ at X86TSS32.esp2, dd 1
+ at X86TSS32.ss2, dw BS3_SEL_R2_SS32
+ at X86TSS32.padding_ss2, dw 1
+ at X86TSS32.cr3, dd 0 ; Will be filled in by routine setting up paged 32-bit mode w/ traps++.
+ at X86TSS32.eip, dd 0 ; Will be filled in by routine setting up 32-bit mode w/ traps++.
+ at X86TSS32.eflags, dd X86_EFL_1
+ at X86TSS32.eax, dd 0
+ at X86TSS32.ecx, dd 0
+ at X86TSS32.edx, dd 0
+ at X86TSS32.ebx, dd 0
+ at X86TSS32.esp, dd BS3_ADDR_STACK_R0_IST1
+ at X86TSS32.ebp, dd 0
+ at X86TSS32.esi, dd 0
+ at X86TSS32.edi, dd 0
+ at X86TSS32.es, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_es, dw 0
+ at X86TSS32.cs, dw BS3_SEL_R0_CS32
+ at X86TSS32.padding_cs, dw 0
+ at X86TSS32.ss, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss, dw 0
+ at X86TSS32.ds, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_ds, dw 0
+ at X86TSS32.fs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_fs, dw 0
+ at X86TSS32.gs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_gs, dw 0
+ at X86TSS32.selLdt, dw 0
+ at X86TSS32.padding_ldt, dw 0
+ at X86TSS32.fDebugTrap, dw 0
+ at X86TSS32.offIoBitmap, dw 0
+iend
+
+;;
+; A spare 32-bit TSS testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss32Spare0, X86TSS32_size
+istruc X86TSS32
+ at X86TSS32.selPrev, dw 0
+ at X86TSS32.padding1, dw 0
+ at X86TSS32.esp0, dd BS3_ADDR_STACK_R0
+ at X86TSS32.ss0, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss0, dw 1
+ at X86TSS32.esp1, dd 1
+ at X86TSS32.ss1, dw BS3_SEL_R1_SS32
+ at X86TSS32.padding_ss1, dw 1
+ at X86TSS32.esp2, dd 1
+ at X86TSS32.ss2, dw BS3_SEL_R2_SS32
+ at X86TSS32.padding_ss2, dw 1
+ at X86TSS32.cr3, dd 0 ; Will be filled in by routine setting up paged 32-bit mode w/ traps++.
+ at X86TSS32.eip, dd 0 ; Will be filled in by routine setting up 32-bit mode w/ traps++.
+ at X86TSS32.eflags, dd X86_EFL_1
+ at X86TSS32.eax, dd 0
+ at X86TSS32.ecx, dd 0
+ at X86TSS32.edx, dd 0
+ at X86TSS32.ebx, dd 0
+ at X86TSS32.esp, dd BS3_ADDR_STACK_R0_IST2
+ at X86TSS32.ebp, dd 0
+ at X86TSS32.esi, dd 0
+ at X86TSS32.edi, dd 0
+ at X86TSS32.es, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_es, dw 0
+ at X86TSS32.cs, dw BS3_SEL_R0_CS32
+ at X86TSS32.padding_cs, dw 0
+ at X86TSS32.ss, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss, dw 0
+ at X86TSS32.ds, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_ds, dw 0
+ at X86TSS32.fs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_fs, dw 0
+ at X86TSS32.gs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_gs, dw 0
+ at X86TSS32.selLdt, dw 0
+ at X86TSS32.padding_ldt, dw 0
+ at X86TSS32.fDebugTrap, dw 0
+ at X86TSS32.offIoBitmap, dw 0
+iend
+
+;;
+; A spare 32-bit TSS testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss32Spare1, X86TSS32_size
+istruc X86TSS32
+ at X86TSS32.selPrev, dw 0
+ at X86TSS32.padding1, dw 0
+ at X86TSS32.esp0, dd BS3_ADDR_STACK_R0
+ at X86TSS32.ss0, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss0, dw 1
+ at X86TSS32.esp1, dd 1
+ at X86TSS32.ss1, dw BS3_SEL_R1_SS32
+ at X86TSS32.padding_ss1, dw 1
+ at X86TSS32.esp2, dd 1
+ at X86TSS32.ss2, dw BS3_SEL_R2_SS32
+ at X86TSS32.padding_ss2, dw 1
+ at X86TSS32.cr3, dd 0 ; Will be filled in by routine setting up paged 32-bit mode w/ traps++.
+ at X86TSS32.eip, dd 0 ; Will be filled in by routine setting up 32-bit mode w/ traps++.
+ at X86TSS32.eflags, dd X86_EFL_1
+ at X86TSS32.eax, dd 0
+ at X86TSS32.ecx, dd 0
+ at X86TSS32.edx, dd 0
+ at X86TSS32.ebx, dd 0
+ at X86TSS32.esp, dd BS3_ADDR_STACK_R0_IST4
+ at X86TSS32.ebp, dd 0
+ at X86TSS32.esi, dd 0
+ at X86TSS32.edi, dd 0
+ at X86TSS32.es, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_es, dw 0
+ at X86TSS32.cs, dw BS3_SEL_R0_CS32
+ at X86TSS32.padding_cs, dw 0
+ at X86TSS32.ss, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss, dw 0
+ at X86TSS32.ds, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_ds, dw 0
+ at X86TSS32.fs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_fs, dw 0
+ at X86TSS32.gs, dw BS3_SEL_R0_DS32
+ at X86TSS32.padding_gs, dw 0
+ at X86TSS32.selLdt, dw 0
+ at X86TSS32.padding_ldt, dw 0
+ at X86TSS32.fDebugTrap, dw 0
+ at X86TSS32.offIoBitmap, dw 0
+iend
+
+
+
+;;
+; 64-bit TSS
+BS3_GLOBAL_DATA Bs3Tss64, X86TSS64_size
+istruc X86TSS64
+ at X86TSS64.u32Reserved, dd 0
+ at X86TSS64.rsp0, dq BS3_ADDR_STACK_R0
+ at X86TSS64.rsp1, dq BS3_ADDR_STACK_R1
+ at X86TSS64.rsp2, dq BS3_ADDR_STACK_R2
+ at X86TSS64.u32Reserved2, dd 0
+ at X86TSS64.ist1, dq BS3_ADDR_STACK_R0_IST1
+ at X86TSS64.ist2, dq BS3_ADDR_STACK_R0_IST2
+ at X86TSS64.ist3, dq BS3_ADDR_STACK_R0_IST3
+ at X86TSS64.ist4, dq BS3_ADDR_STACK_R0_IST4
+ at X86TSS64.ist5, dq BS3_ADDR_STACK_R0_IST5
+ at X86TSS64.ist6, dq BS3_ADDR_STACK_R0_IST6
+ at X86TSS64.ist7, dq BS3_ADDR_STACK_R0_IST7
+ at X86TSS64.u16Reserved, dw 0
+ at X86TSS64.offIoBitmap, dw 0
+iend
+
+;;
+; A spare TSS for testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss64Spare0, X86TSS64_size
+istruc X86TSS64
+ at X86TSS64.u32Reserved, dd 0
+ at X86TSS64.rsp0, dq BS3_ADDR_STACK_R0
+ at X86TSS64.rsp1, dq BS3_ADDR_STACK_R1
+ at X86TSS64.rsp2, dq BS3_ADDR_STACK_R2
+ at X86TSS64.u32Reserved2, dd 0
+ at X86TSS64.ist1, dq BS3_ADDR_STACK_R0_IST1
+ at X86TSS64.ist2, dq BS3_ADDR_STACK_R0_IST2
+ at X86TSS64.ist3, dq BS3_ADDR_STACK_R0_IST3
+ at X86TSS64.ist4, dq BS3_ADDR_STACK_R0_IST4
+ at X86TSS64.ist5, dq BS3_ADDR_STACK_R0_IST5
+ at X86TSS64.ist6, dq BS3_ADDR_STACK_R0_IST6
+ at X86TSS64.ist7, dq BS3_ADDR_STACK_R0_IST7
+ at X86TSS64.u16Reserved, dw 0
+ at X86TSS64.offIoBitmap, dw 0
+iend
+
+;;
+; A spare TSS for testcases to play around with.
+BS3_GLOBAL_DATA Bs3Tss64Spare1, X86TSS64_size
+istruc X86TSS64
+ at X86TSS64.u32Reserved, dd 0
+ at X86TSS64.rsp0, dq BS3_ADDR_STACK_R0
+ at X86TSS64.rsp1, dq BS3_ADDR_STACK_R1
+ at X86TSS64.rsp2, dq BS3_ADDR_STACK_R2
+ at X86TSS64.u32Reserved2, dd 0
+ at X86TSS64.ist1, dq BS3_ADDR_STACK_R0_IST1
+ at X86TSS64.ist2, dq BS3_ADDR_STACK_R0_IST2
+ at X86TSS64.ist3, dq BS3_ADDR_STACK_R0_IST3
+ at X86TSS64.ist4, dq BS3_ADDR_STACK_R0_IST4
+ at X86TSS64.ist5, dq BS3_ADDR_STACK_R0_IST5
+ at X86TSS64.ist6, dq BS3_ADDR_STACK_R0_IST6
+ at X86TSS64.ist7, dq BS3_ADDR_STACK_R0_IST7
+ at X86TSS64.u16Reserved, dw 0
+ at X86TSS64.offIoBitmap, dw 0
+iend
+
+
+
+;;
+; 64-bit TSS sharing an I/O permission bitmap (Bs3SharedIobp) with a 32-bit TSS.
+;
+BS3_GLOBAL_DATA Bs3Tss64WithIopb, X86TSS64_size
+istruc X86TSS64
+ at X86TSS64.u32Reserved, dd 0
+ at X86TSS64.rsp0, dq BS3_ADDR_STACK_R0
+ at X86TSS64.rsp1, dq BS3_ADDR_STACK_R1
+ at X86TSS64.rsp2, dq BS3_ADDR_STACK_R2
+ at X86TSS64.u32Reserved2, dd 0
+ at X86TSS64.ist1, dq BS3_ADDR_STACK_R0_IST1
+ at X86TSS64.ist2, dq BS3_ADDR_STACK_R0_IST2
+ at X86TSS64.ist3, dq BS3_ADDR_STACK_R0_IST3
+ at X86TSS64.ist4, dq BS3_ADDR_STACK_R0_IST4
+ at X86TSS64.ist5, dq BS3_ADDR_STACK_R0_IST5
+ at X86TSS64.ist6, dq BS3_ADDR_STACK_R0_IST6
+ at X86TSS64.ist7, dq BS3_ADDR_STACK_R0_IST7
+ at X86TSS64.u16Reserved, dw 0
+ at X86TSS64.offIoBitmap, dw (BS3_DATA_NM(Bs3SharedIobp) - BS3_DATA_NM(Bs3Tss64WithIopb))
+iend
+
+;;
+; 32-bit TSS sharing an I/O permission bitmap (Bs3SharedIobp) with a 64-bit TSS,
+; and sporting an interrupt redirection bitmap (Bs3SharedIntRedirBm).
+BS3_GLOBAL_DATA Bs3Tss32WithIopb, X86TSS32_size
+istruc X86TSS32
+ at X86TSS32.selPrev, dw 0
+ at X86TSS32.padding1, dw 0
+ at X86TSS32.esp0, dd BS3_ADDR_STACK_R0
+ at X86TSS32.ss0, dw BS3_SEL_R0_SS32
+ at X86TSS32.padding_ss0, dw 1
+ at X86TSS32.esp1, dd 1
+ at X86TSS32.ss1, dw BS3_SEL_R1_SS32
+ at X86TSS32.padding_ss1, dw 1
+ at X86TSS32.esp2, dd 1
+ at X86TSS32.ss2, dw BS3_SEL_R2_SS32
+ at X86TSS32.padding_ss2, dw 1
+ at X86TSS32.cr3, dd 0 ; Will be filled in by routine setting up paged 32-bit mode w/ traps++.
+ at X86TSS32.eip, dd 0 ; Will be filled in by routine setting up 32-bit mode w/ traps++.
+ at X86TSS32.eflags, dd X86_EFL_1
+ at X86TSS32.eax, dd 0
+ at X86TSS32.ecx, dd 0
+ at X86TSS32.edx, dd 0
+ at X86TSS32.ebx, dd 0
+ at X86TSS32.esp, dd 0
+ at X86TSS32.ebp, dd 0
+ at X86TSS32.esi, dd 0
+ at X86TSS32.edi, dd 0
+ at X86TSS32.es, dw 0
+ at X86TSS32.padding_es, dw 0
+ at X86TSS32.cs, dw 0
+ at X86TSS32.padding_cs, dw 0
+ at X86TSS32.ss, dw 0
+ at X86TSS32.padding_ss, dw 0
+ at X86TSS32.ds, dw 0
+ at X86TSS32.padding_ds, dw 0
+ at X86TSS32.fs, dw 0
+ at X86TSS32.padding_fs, dw 0
+ at X86TSS32.gs, dw 0
+ at X86TSS32.padding_gs, dw 0
+ at X86TSS32.selLdt, dw 0
+ at X86TSS32.padding_ldt, dw 0
+ at X86TSS32.fDebugTrap, dw 0
+ at X86TSS32.offIoBitmap, dw (BS3_DATA_NM(Bs3SharedIobp) - BS3_DATA_NM(Bs3Tss32WithIopb))
+iend
+
+;
+; We insert 6 bytes before the interrupt redirection bitmap just to make sure
+; we've all got the same idea about where it starts (i.e. 32 bytes before IOBP).
+;
+ times 6 db 0ffh
+
+;;
+; Interrupt redirection bitmap (used by 32-bit TSS).
+BS3_GLOBAL_DATA Bs3SharedIntRedirBm, 32
+ times 32 db 00h
+
+;;
+; Shared I/O permission bitmap used both by Bs3Tss64WithIopb and Bs3Tss32WithIopb.
+BS3_GLOBAL_DATA Bs3SharedIobp, 8192+2
+ times 8192+2 db 0ffh
+BS3_GLOBAL_DATA Bs3SharedIobpEnd, 0
+
+
+align 128
+
+;;
+; 16-bit IDT.
+; This requires manual setup by code fielding traps, so we'll just reserve the
+; memory here.
+;
+BS3_GLOBAL_DATA Bs3Idt16, 256*8
+ times 256 dq 0
+
+;;
+; 32-bit IDT.
+; This requires manual setup by code fielding traps, so we'll just reserve the
+; memory here.
+;
+BS3_GLOBAL_DATA Bs3Idt32, 256*8
+ times 256 dq 0
+
+;;
+; 64-bit IDT.
+; This requires manual setup by code fielding traps, so we'll just reserve the
+; memory here.
+;
+BS3_GLOBAL_DATA Bs3Idt64, 256*16
+ times 256 dq 0, 0
+
+
+ times 6 db 0 ; Pad the first LIDT correctly.
+
+;;
+; LIDT structure for the 16-bit IDT (8-byte aligned on offset).
+BS3_GLOBAL_DATA Bs3Lidt_Idt16, 2+8
+ dw 256*8 - 1 ; limit
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Idt16) ; low offset
+ dw (BS3_ADDR_BS3SYSTEM16 >> 16) ; high offset
+ dd 0 ; top32 offset
+
+ times 4 db 0 ; padding the start of the next
+
+;;
+; LIDT structure for the 32-bit IDT (8-byte aligned on offset).
+BS3_GLOBAL_DATA Bs3Lidt_Idt32, 2+8
+ dw 256*8 - 1 ; limit
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Idt32) ; low offset
+ dw (BS3_ADDR_BS3SYSTEM16 >> 16) ; high offset
+ dd 0 ; top32 offset
+
+ times 4 db 0 ; padding the start of the next
+
+;;
+; LIDT structure for the 64-bit IDT (8-byte aligned on offset).
+BS3_GLOBAL_DATA Bs3Lidt_Idt64, 2+8
+ dw 256*16 - 1 ; limit
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Idt64) ; low offset
+ dw (BS3_ADDR_BS3SYSTEM16 >> 16) ; high offset
+ dd 0 ; top32 offset
+
+ times 4 db 0 ; padding the start of the next
+
+;;
+; LIDT structure for the real mode IVT at address 0x00000000 (8-byte aligned on offset).
+BS3_GLOBAL_DATA Bs3Lidt_Ivt, 2+8
+ dw 0ffffh ; limit
+ dw 0 ; low offset
+ dw 0 ; high offset
+ dd 0 ; top32 offset
+
+ times 4 db 0 ; padding the start of the next
+
+;;
+; LGDT structure for the current GDT (8-byte aligned on offset).
+BS3_GLOBAL_DATA Bs3Lgdt_Gdt, 2+8
+ dw BS3_DATA_NM(Bs3GdtEnd) - BS3_DATA_NM(Bs3Gdt) - 1 ; limit
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Gdt) ; low offset
+ dw (BS3_ADDR_BS3SYSTEM16 >> 16) ; high offset
+ dd 0 ; top32 offset
+
+;;
+; LGDT structure for the default GDT (8-byte aligned on offset).
+; This must not be modified, whereas Bs3Lgdt_Gdt can be modified by the user.
+BS3_GLOBAL_DATA Bs3LgdtDef_Gdt, 2+8
+ dw BS3_DATA_NM(Bs3GdtEnd) - BS3_DATA_NM(Bs3Gdt) - 1 ; limit
+ dw BS3_SYSTEM16_BASE_LOW(Bs3Gdt) ; low offset
+ dw (BS3_ADDR_BS3SYSTEM16 >> 16) ; high offset
+ dd 0 ; top32 offset
+
+
+
+align 16
+;;
+; LDT filling up the rest of the segment.
+;
+; Currently this starts at 0x84e0, which leaves us with 0xb20 bytes. We'll use
+; the last 32 of those for an eye catcher.
+;
+BS3_GLOBAL_DATA Bs3Ldt, 0b20h - 32
+ times (0b20h - 32) db 0
+BS3_GLOBAL_DATA Bs3LdtEnd, 0
+ db 10, 13, 'eye-catcher: SYSTEM16 END', 10, 13, 0, 0, 0 ; 32 bytes long
+
+;
+; Check the segment size.
+;
+%ifndef KBUILD_GENERATING_MAKEFILE_DEPENDENCIES
+ %if ($ - $$) != 09000h
+ %assign offActual ($ - $$)
+ %error "Bad BS3SYSTEM16 segment size: " %+ offActual %+ ", expected 0x9000 (36864)"
+ %endif
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I4D.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I4D.asm
new file mode 100644
index 00000000..2fd75d90
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I4D.asm
@@ -0,0 +1,80 @@
+; $Id: bs3-wc16-I4D.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 32-bit signed integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 32-bit signed integer division.
+;
+; @returns DX:AX quotient, CX:BX remainder.
+; @param DX:AX Dividend.
+; @param CX:BX Divisor
+;
+; @uses Nothing.
+;
+global $_?I4D
+$_?I4D:
+;; @todo no idea if we're getting the negative division stuff right here according to what watcom expectes...
+extern TODO_NEGATIVE_SIGNED_DIVISION
+ ; Move dividend into EDX:EAX
+ shl eax, 10h
+ mov ax, dx
+ sar dx, 0fh
+ movsx edx, dx
+
+ ; Move divisor into ebx.
+ shl ebx, 10h
+ mov bx, cx
+
+ ; Do it!
+ idiv ebx
+
+ ; Reminder in to CX:BX
+ mov bx, dx
+ shr edx, 10h
+ mov cx, dx
+
+ ; Quotient into DX:AX
+ mov edx, eax
+ shr edx, 10h
+
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DQ.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DQ.asm
new file mode 100644
index 00000000..0087647c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DQ.asm
@@ -0,0 +1,121 @@
+; $Id: bs3-wc16-I8DQ.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit unsigned integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3Int64Div
+
+
+;;
+; 64-bit unsigned integer division, SS variant.
+;
+; @returns ax:bx:cx:dx quotient. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [ss:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?I8DQ
+$_?I8DQ:
+ push es
+ push ss
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ push cs
+%endif
+ call $_?I8DQE
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
+;;
+; 64-bit unsigned integer division, ES variant.
+;
+; @returns ax:bx:cx:dx quotient. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [es:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?I8DQE
+$_?I8DQE:
+ push ds
+ push es
+
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame of sorts, allocating 16 bytes for the result buffer.
+ push bp
+ sub sp, 10h
+ mov bp, sp
+
+ ; Pointer to the return buffer.
+ push ss
+ push bp
+ add bp, 10h ; Correct bp.
+
+ ; The divisor.
+ push dword [es:si + 4]
+ push dword [es:si]
+
+ ; The dividend.
+ push ax
+ push bx
+ push cx
+ push dx
+
+ call Bs3Int64Div
+
+ ; Load the quotient.
+ mov ax, [bp - 10h + 8 + 6]
+ mov bx, [bp - 10h + 8 + 4]
+ mov cx, [bp - 10h + 8 + 2]
+ mov dx, [bp - 10h + 8]
+
+ leave
+ pop es
+ pop ds
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DR.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DR.asm
new file mode 100644
index 00000000..b90581d8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8DR.asm
@@ -0,0 +1,121 @@
+; $Id: bs3-wc16-I8DR.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit unsigned integer modulo.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3Int64Div
+
+
+;;
+; 64-bit unsigned integer modulo, SS variant.
+;
+; @returns ax:bx:cx:dx reminder. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [ss:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?I8DR
+$_?I8DR:
+ push es
+ push ss
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ push cs
+%endif
+ call $_?I8DRE
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
+;;
+; 64-bit unsigned integer modulo, ES variant.
+;
+; @returns ax:bx:cx:dx reminder.
+; @param ax:bx:cx:dx Dividend.
+; @param [es:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?I8DRE
+$_?I8DRE:
+ push ds
+ push es
+
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame of sorts, allocating 16 bytes for the result buffer.
+ push bp
+ sub sp, 10h
+ mov bp, sp
+
+ ; Pointer to the return buffer.
+ push ss
+ push bp
+ add bp, 10h ; Correct bp.
+
+ ; The divisor.
+ push dword [es:si + 4]
+ push dword [es:si]
+
+ ; The dividend.
+ push ax
+ push bx
+ push cx
+ push dx
+
+ call Bs3Int64Div
+
+ ; Load the reminder.
+ mov ax, [bp - 10h + 8 + 6]
+ mov bx, [bp - 10h + 8 + 4]
+ mov cx, [bp - 10h + 8 + 2]
+ mov dx, [bp - 10h + 8]
+
+ leave
+ pop es
+ pop ds
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8RS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8RS.asm
new file mode 100644
index 00000000..f90201dd
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-I8RS.asm
@@ -0,0 +1,78 @@
+; $Id: bs3-wc16-I8RS.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit signed integer right shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit signed integer left shift.
+;
+; @returns AX:BX:CX:DX (AX is the most significant, DX the least)
+; @param AX:BX:CX:DX Value to shift.
+; @param SI Shift count.
+;
+global $_?I8RS
+$_?I8RS:
+ push si
+
+ ;
+ ; The 16-bit watcom code differs from the 32-bit one in the way it
+ ; handles the shift count. All 16-bit bits are used in the 16-bit
+ ; code, we do the same as the 32-bit one as we don't want to wast
+ ; time in the below loop.
+ ;
+ ; Using 8086 comatible approach here as it's less hazzle to write
+ ; and smaller.
+ ;
+ and si, 3fh
+ jz .return
+
+.next_shift:
+ sar ax, 1
+ rcr bx, 1
+ rcr cx, 1
+ rcr dx, 1
+ dec si
+ jnz .next_shift
+
+.return:
+ pop si
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U4D.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U4D.asm
new file mode 100644
index 00000000..989ff869
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U4D.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-wc16-U4D.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 32-bit unsigned integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+
+;;
+; 32-bit unsigned integer division.
+;
+; @returns DX:AX quotient, CX:BX remainder.
+; @param DX:AX Dividend.
+; @param CX:BX Divisor
+;
+; @uses Nothing.
+;
+%ifdef BS3KIT_WITH_REAL_WATCOM_INTRINSIC_NAMES
+global __U4D
+__U4D:
+%endif
+global $_?U4D
+$_?U4D:
+%if TMPL_BITS >= 32
+ ; Move dividend into EDX:EAX
+ shl eax, 10h
+ mov ax, dx
+ xor edx, edx
+
+ ; Move divisor into ebx.
+ shl ebx, 10h
+ mov bx, cx
+
+ ; Do it!
+ div ebx
+
+ ; Reminder in to CX:BX
+ mov bx, dx
+ shr edx, 10h
+ mov cx, dx
+
+ ; Quotient into DX:AX
+ mov edx, eax
+ shr edx, 10h
+%else
+ push ds
+ push es
+
+ ;
+ ; Convert to a C __cdecl call - too lazy to do this in assembly.
+ ;
+
+ ; Set up a frame of sorts, allocating 8 bytes for the result buffer.
+ push bp
+ sub sp, 08h
+ mov bp, sp
+
+ ; Pointer to the return buffer.
+ push ss
+ push bp
+ add bp, 08h ; Correct bp.
+
+ ; The divisor.
+ push cx
+ push bx
+
+ ; The dividend.
+ push dx
+ push ax
+
+ BS3_EXTERN_CMN Bs3UInt32Div
+ call Bs3UInt32Div
+
+ ; Load the reminder.
+ mov cx, [bp - 08h + 6]
+ mov bx, [bp - 08h + 4]
+ ; Load the quotient.
+ mov dx, [bp - 08h + 2]
+ mov ax, [bp - 08h]
+
+ mov sp, bp
+ pop bp
+ pop es
+ pop ds
+
+%endif
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DQ.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DQ.asm
new file mode 100644
index 00000000..c6fbf0e7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DQ.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-wc16-U8DQ.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit unsigned integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3UInt64Div
+
+
+;;
+; 64-bit unsigned integer division, SS variant.
+;
+; @returns ax:bx:cx:dx quotient. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [ss:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?U8DQ
+$_?U8DQ:
+ push es
+ push ss
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ push cs
+%endif
+ call $_?U8DQE
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
+;;
+; 64-bit unsigned integer division, ES variant.
+;
+; @returns ax:bx:cx:dx quotient. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [es:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?U8DQE
+$_?U8DQE:
+ push ds
+ push es
+
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame of sorts, allocating 16 bytes for the result buffer.
+ push bp
+ sub sp, 10h
+ mov bp, sp
+
+ ; Pointer to the return buffer.
+ push ss
+ push bp
+ add bp, 10h ; Correct bp.
+
+ ; The divisor.
+ push word [es:si + 6]
+ push word [es:si + 4]
+ push word [es:si + 2]
+ push word [es:si]
+
+ ; The dividend.
+ push ax
+ push bx
+ push cx
+ push dx
+
+ call Bs3UInt64Div
+
+ ; Load the quotient.
+ mov ax, [bp - 10h + 6]
+ mov bx, [bp - 10h + 4]
+ mov cx, [bp - 10h + 2]
+ mov dx, [bp - 10h]
+
+ mov sp, bp
+ pop bp
+ pop es
+ pop ds
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DR.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DR.asm
new file mode 100644
index 00000000..18381119
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8DR.asm
@@ -0,0 +1,124 @@
+; $Id: bs3-wc16-U8DR.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit unsigned integer modulo.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3UInt64Div
+
+
+;;
+; 64-bit unsigned integer modulo, SS variant.
+;
+; @returns ax:bx:cx:dx reminder. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [ss:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?U8DR
+$_?U8DR:
+ push es
+ push ss
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ push cs
+%endif
+ call $_?U8DRE
+ pop es
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
+;;
+; 64-bit unsigned integer modulo, ES variant.
+;
+; @returns ax:bx:cx:dx reminder. (AX is the most significant, DX the least)
+; @param ax:bx:cx:dx Dividend.
+; @param [es:si] Divisor
+;
+; @uses Nothing.
+;
+global $_?U8DRE
+$_?U8DRE:
+ push ds
+ push es
+
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame of sorts, allocating 16 bytes for the result buffer.
+ push bp
+ sub sp, 10h
+ mov bp, sp
+
+ ; Pointer to the return buffer.
+ push ss
+ push bp
+ add bp, 10h ; Correct bp.
+
+ ; The divisor.
+ push word [es:si + 6]
+ push word [es:si + 4]
+ push word [es:si + 2]
+ push word [es:si]
+
+ ; The dividend.
+ push ax
+ push bx
+ push cx
+ push dx
+
+ call Bs3UInt64Div
+
+ ; Load the reminder.
+ mov ax, [bp - 10h + 8 + 6]
+ mov bx, [bp - 10h + 8 + 4]
+ mov cx, [bp - 10h + 8 + 2]
+ mov dx, [bp - 10h + 8]
+
+ mov sp, bp
+ pop bp
+ pop es
+ pop ds
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8LS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8LS.asm
new file mode 100644
index 00000000..879e4f9c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8LS.asm
@@ -0,0 +1,80 @@
+; $Id: bs3-wc16-U8LS.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit integer left shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit integer left shift.
+;
+; @returns AX:BX:CX:DX (AX is the most significant, DX the least)
+; @param AX:BX:CX:DX Value to shift.
+; @param SI Shift count.
+;
+global $_?U8LS
+$_?U8LS:
+global $_?I8LS
+$_?I8LS:
+ push si
+
+ ;
+ ; The 16-bit watcom code differs from the 32-bit one in the way it
+ ; handles the shift count. All 16-bit bits are used in the 16-bit
+ ; code, we do the same as the 32-bit one as we don't want to wast
+ ; time in the below loop.
+ ;
+ ; Using 8086 compatible approach here as it's less hazzle to write
+ ; and smaller.
+ ;
+ and si, 3fh
+
+ jz .return
+.next_shift:
+ shl dx, 1
+ rcl cx, 1
+ rcl bx, 1
+ rcl ax, 1
+ dec si
+ jnz .next_shift
+
+.return:
+ pop si
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8RS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8RS.asm
new file mode 100644
index 00000000..4da920d7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc16-U8RS.asm
@@ -0,0 +1,82 @@
+; $Id: bs3-wc16-U8RS.asm $
+;; @file
+; BS3Kit - 16-bit Watcom C/C++, 64-bit unsigned integer right shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit unsigned integer left shift.
+;
+; @returns AX:BX:CX:DX (AX is the most significant, DX the least)
+; @param AX:BX:CX:DX Value to shift.
+; @param SI Shift count.
+;
+%ifdef BS3KIT_WITH_REAL_WATCOM_INTRINSIC_NAMES
+global __U8RS
+__U8RS:
+%endif
+global $_?U8RS
+$_?U8RS:
+ push si
+
+ ;
+ ; The 16-bit watcom code differs from the 32-bit one in the way it
+ ; handles the shift count. All 16-bit bits are used in the 16-bit
+ ; code, we do the same as the 32-bit one as we don't want to wast
+ ; time in the below loop.
+ ;
+ ; Using 8086 comatible approach here as it's less hazzle to write
+ ; and smaller.
+ ;
+ and si, 3fh
+ jz .return
+
+.next_shift:
+ shr ax, 1
+ rcr bx, 1
+ rcr cx, 1
+ rcr dx, 1
+ dec si
+ jnz .next_shift
+
+.return:
+ pop si
+%ifdef ASM_MODEL_FAR_CODE
+ retf
+%else
+ ret
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8D.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8D.asm
new file mode 100644
index 00000000..2c608ffb
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8D.asm
@@ -0,0 +1,81 @@
+; $Id: bs3-wc32-I8D.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit signed integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3Int64Div
+
+
+;;
+; 64-bit signed integer division.
+;
+; @returns EDX:EAX Quotient, ECX:EBX Remainder.
+; @param EDX:EAX Dividend.
+; @param ECX:EBX Divisor
+;
+global $??I8D
+$??I8D:
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame, allocating 16 bytes for the result buffer.
+ push ebp
+ mov ebp, esp
+ sub esp, 10h
+
+ ; Pointer to the return buffer.
+ push esp
+
+ ; The dividend.
+ push ecx
+ push ebx
+
+ ; The dividend.
+ push edx
+ push eax
+
+ call Bs3Int64Div
+
+ ; Load the result.
+ mov ecx, [ebp - 10h + 12]
+ mov ebx, [ebp - 10h + 8]
+ mov edx, [ebp - 10h + 4]
+ mov eax, [ebp - 10h]
+
+ leave
+ ret
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8RS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8RS.asm
new file mode 100644
index 00000000..4840a7f7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-I8RS.asm
@@ -0,0 +1,70 @@
+; $Id: bs3-wc32-I8RS.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit signed integer right shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit signed integer left shift.
+;
+; @returns EDX:EAX
+; @param EDX:EAX Value to shift.
+; @param BL Shift count (it's specified as ECX:EBX, but we only use BL).
+;
+global $??I8RS
+$??I8RS:
+ push ecx ; We're allowed to trash ECX, but why bother.
+
+ mov cl, bl
+ and cl, 3fh
+ test cl, 20h
+ jnz .big_shift
+
+ ; Shifting less than 32.
+ shrd eax, edx, cl
+ sar edx, cl
+
+.return:
+ pop ecx
+ ret
+
+.big_shift:
+ ; Shifting 32 or more.
+ mov eax, edx
+ sar eax, cl ; Only uses lower 5 bits.
+ sar edx, 1fh ; Sign extend it.
+ jmp .return
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8D.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8D.asm
new file mode 100644
index 00000000..5f14c32b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8D.asm
@@ -0,0 +1,81 @@
+; $Id: bs3-wc32-U8D.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit unsigned integer division.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_CMN Bs3UInt64Div
+
+
+;;
+; 64-bit unsigned integer division.
+;
+; @returns EDX:EAX Quotient, ECX:EBX Remainder.
+; @param EDX:EAX Dividend.
+; @param ECX:EBX Divisor
+;
+global $??U8D
+$??U8D:
+ ;
+ ; Convert to a C __cdecl call - not doing this in assembly.
+ ;
+
+ ; Set up a frame, allocating 16 bytes for the result buffer.
+ push ebp
+ mov ebp, esp
+ sub esp, 10h
+
+ ; Pointer to the return buffer.
+ push esp
+
+ ; The divisor.
+ push ecx
+ push ebx
+
+ ; The dividend.
+ push edx
+ push eax
+
+ call Bs3UInt64Div
+
+ ; Load the result.
+ mov ecx, [ebp - 10h + 12]
+ mov ebx, [ebp - 10h + 8]
+ mov edx, [ebp - 10h + 4]
+ mov eax, [ebp - 10h]
+
+ leave
+ ret
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8LS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8LS.asm
new file mode 100644
index 00000000..ffd30a48
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8LS.asm
@@ -0,0 +1,72 @@
+; $Id: bs3-wc32-U8LS.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit integer left shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit integer left shift.
+;
+; @returns EDX:EAX
+; @param EDX:EAX Value to shift.
+; @param BL Shift count (it's specified as ECX:EBX, but we only use BL).
+;
+global $??U8LS
+$??U8LS:
+global $??I8LS
+$??I8LS:
+ push ecx ; We're allowed to trash ECX, but why bother.
+
+ mov cl, bl
+ and cl, 3fh
+ test cl, 20h
+ jnz .big_shift
+
+ ; Shifting less than 32.
+ shld edx, eax, cl
+ shl eax, cl
+
+.return:
+ pop ecx
+ ret
+
+.big_shift:
+ ; Shifting 32 or more.
+ mov edx, eax
+ shl edx, cl ; Only uses lower 5 bits.
+ xor eax, eax
+ jmp .return
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8M.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8M.asm
new file mode 100644
index 00000000..57a1e583
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8M.asm
@@ -0,0 +1,93 @@
+; $Id: bs3-wc32-U8M.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit integer multiplication.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit unsigned & signed integer multiplication.
+;
+; @returns EDX:EAX as the product.
+; @param EDX:EAX Factor 1 - edx=F1H, eax=F1L.
+; @param ECX:EBX Factor 2 - ecx=F2H, ebx=F2L.
+;
+global $??I8M
+$??I8M:
+global $??U8M
+$??U8M:
+ ;
+ ; If both the high dwords are zero, we can get away with
+ ; a simple 32-bit multiplication.
+ ;
+ test ecx, ecx
+ jnz .big
+ test edx, edx
+ jnz .big
+ mul ebx
+ ret
+
+.big:
+ ;
+ ; Imagine we use 4294967296-base (2^32), so each factor has two
+ ; digits H and L, thus we have: F1H:F1L * F2H:F1L which we can
+ ; multipy like we learned in primary school. Since the result
+ ; is limited to 64-bit, we can skip F1H*F2H and discard the
+ ; high 32-bit in F1L*F2H and F1H*F2L.
+ ; result = ((F1L*F2H) << 32)
+ ; + ((F1H*F2L) << 32)
+ ; + (F1L*F2L);
+ ;
+ push ecx ; Preserve ECX just to be nice.
+ push eax ; Stash F1L for later.
+ push edx ; Stash F1H for later.
+
+ ; ECX = F1L*F2H
+ mul ecx
+ mov ecx, eax
+
+ ; ECX += F1H * F2L
+ pop eax
+ mul ebx
+ add ecx, eax
+
+ ; EDX:EAX = F1L * F2L
+ pop eax
+ mul ebx
+ add edx, ecx
+
+ pop ecx
+ ret
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8RS.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8RS.asm
new file mode 100644
index 00000000..82b20169
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-wc32-U8RS.asm
@@ -0,0 +1,70 @@
+; $Id: bs3-wc32-U8RS.asm $
+;; @file
+; BS3Kit - 32-bit Watcom C/C++, 64-bit unsigned integer right shift.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit-template-header.mac"
+
+
+;;
+; 64-bit unsigned integer right shift.
+;
+; @returns EDX:EAX
+; @param EDX:EAX Value to shift.
+; @param BL Shift count (it's specified as ECX:EBX, but we only use BL).
+;
+global $??U8RS
+$??U8RS:
+ push ecx ; We're allowed to trash ECX, but why bother.
+
+ mov cl, bl
+ and cl, 3fh
+ test cl, 20h
+ jnz .big_shift
+
+ ; Shifting less than 32.
+ shrd eax, edx, cl
+ shr edx, cl
+
+.return:
+ pop ecx
+ ret
+
+.big_shift:
+ ; Shifting 32 or more.
+ mov eax, edx
+ shr eax, cl ; Only uses lower 5 bits.
+ xor edx, edx
+ jmp .return
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3cpudt.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3cpudt.c
new file mode 100644
index 00000000..b4210da7
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3cpudt.c
@@ -0,0 +1,71 @@
+/* $Id: bs3cpudt.c $ */
+/** @file
+ * BS3Kit - Tests Bs3CpuDetect_rm.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "bs3kit.h"
+#include <stdio.h>
+#include <stdint.h>
+
+
+unsigned StoreMsw(void);
+#pragma aux StoreMsw = \
+ ".286" \
+ "smsw ax" \
+ value [ax];
+
+void LoadMsw(unsigned);
+#pragma aux LoadMsw = \
+ ".286p" \
+ "lmsw ax" \
+ parm [ax];
+
+int main()
+{
+ uint16_t volatile usCpu = Bs3CpuDetect_rm();
+ printf("usCpu=%#x\n", usCpu);
+ if ((usCpu & BS3CPU_TYPE_MASK) >= BS3CPU_80286)
+ {
+ printf("(42=%d) msw=%#x (42=%d)\n", 42, StoreMsw(), 42);
+ LoadMsw(0);
+ printf("lmsw 0 => msw=%#x (42=%d)\n", StoreMsw(), 42);
+ }
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-autostubs.kmk b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-autostubs.kmk
new file mode 100644
index 00000000..2445e182
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-autostubs.kmk
@@ -0,0 +1,194 @@
+# $Id: bs3kit-autostubs.kmk $
+## @file
+# BS3Kit - Automatic near/far stubs - generated by the bs3kit-autostubs.kmk makefile rule.
+#
+
+#
+# Copyright (C) 2007-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelFlatDataToProtFar16,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelFlatDataToRealMode,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelLnkPtrToFlat,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelProtFar16DataToFlat,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelProtFar16DataToRealMode,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelRealModeCodeToFlat,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelRealModeDataToFlat,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelRealModeDataToProtFar16,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3ExtCtxRestoreEx,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3ExtCtxRestore,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3ExtCtxSaveEx,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3ExtCtxSave,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelLnkPtrToCurPtr,4)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3SelFar32ToFlat32NoClobber,6)
+$(call BS3KIT_FN_GEN_CMN_FARSTUB,bs3kit-common-16,Bs3RegCtxSaveEx,8)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetAbridgedFtw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetMm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetMxCsrMask)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetMxCsr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetXmm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetYmm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetGpr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestCheckExtCtx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestCheckRegCtxEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestFailed)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestFailedF)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestFailedV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestQueryCfgBool)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3GetCpuVendor)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrCpy)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3GetModeNameShortLower)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3GetModeName)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingAlias)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingInitRootForLM)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingInitRootForPAE)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingInitRootForPP)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingMapRamAbove4GForLM)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingProtectPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingProtect)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingQueryAddressInfo)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingUnalias)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SwitchFromV86To16BitAndCallC)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxAlloc)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxCopy)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxInit)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetHandler)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetXmm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetYmm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Printf)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PrintfV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrFormatV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrLen)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrNLen)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrPrintf)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3StrPrintfV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetAbridgedFtw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetFcw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetFsw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetSize)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PicUpdateMask)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabFree)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSubErrorCount)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetMxCsrMask)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetMxCsr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelFar32ToFlat32)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelProtFar32ToFlat32)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestQueryCfgU32)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxGetMm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestNow)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestQueryCfgU8)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetDpl)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxFree)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetFcw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3ExtCtxSetFsw)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemAlloc)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemAllocZ)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemCpy)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemGuardedTestPageAlloc)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemGuardedTestPageAllocEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemMove)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemPCpy)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingGetPte)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PagingSetupCanonicalTraps)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxGetRspSsAsCurPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabAllocEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabAlloc)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabListAllocEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabListAlloc)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemFree)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemGuardedTestPageFree)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3MemPrintInfo)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PicMaskAll)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PicSetup)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PitDisable)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PitSetupAndEnablePeriodTimer)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3PrintStr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxConvertToRingX)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxConvertV86ToRm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxPrint)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSaveForMode)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetGrpDsFromCurPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetGrpSegFromCurPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetGrpSegFromFlat)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetRipCsFromCurPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetRipCsFromFlat)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3RegCtxSetRipCsFromLnkPtr)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelSetup16BitCode)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelSetup16BitData)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelSetup32BitCode)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelSetupGate64)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SelSetupGate)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabInit)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabListAdd)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabListFree)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3SlabListInit)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestHostPrintf)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestHostPrintfV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestInit)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestPrintf)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestPrintfV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSkipped)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSkippedF)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSkippedV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSub)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSubDone)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSubF)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestSubV)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestTerm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TestValue)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap16InitEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap16Init)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap16SetGate)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap32Init)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap32SetGate)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap64InitEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap64Init)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3Trap64SetGate)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapDefaultHandler)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapPrintFrame)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapReInit)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapRmV86InitEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapRmV86Init)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapRmV86SetGate)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetHandlerEx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetJmpAndRestoreInRm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetJmpAndRestore)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetJmpAndRestoreWithExtCtxAndRm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetJmpAndRestoreWithExtCtx)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapSetJmpAndRestoreWithRm)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3TrapUnsetJmp)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3UInt32Div)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,Bs3UInt64Div)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,bs3PagingGetLegacyPte)
+$(call BS3KIT_FN_GEN_CMN_NEARSTUB,bs3kit-common-16,bs3PagingGetPaePte)
+$(call BS3KIT_FN_GEN_MODE_NEARSTUB,bs3kit-common-16,Bs3BiosInt15hE820)
+$(call BS3KIT_FN_GEN_MODE_NEARSTUB,bs3kit-common-16,Bs3SwitchTo32BitAndCallC)
+$(call BS3KIT_FN_GEN_MODE_NEARSTUB,bs3kit-common-16,Bs3BiosInt15h88)
+$(call BS3KIT_FN_GEN_MODE_NEARSTUB,bs3kit-common-16,Bs3TrapInit)
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-docs.c b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-docs.c
new file mode 100644
index 00000000..ef798d35
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-docs.c
@@ -0,0 +1,170 @@
+/* $Id: bs3kit-docs.c $ */
+/** @file
+ * BS3Kit - Documentation.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/** @page pg_bs3kit BS3Kit - Boot Sector Kit \#3
+ *
+ * The BS3Kit is a framework for bare metal floppy/usb image tests.
+ *
+ * The 3rd iteration of the framework includes support for 16-bit and 32-bit
+ * C/C++ code, with provisions for 64-bit C code to possibly be added later.
+ * The C code have to do without a runtime library, otherwhat what we can share
+ * possibly with IPRT.
+ *
+ * This iteration also adds a real linker into the picture, which is an
+ * improvment over early when all had to done in a single assembler run with
+ * lots of includes and macros controlling what we needed. The functions are no
+ * in separate files and compiled/assembled into libraries, so the linker will
+ * only include exactly what is needed. The current linker is the OpenWatcom
+ * one, wlink, that we're already using when building the BIOSes. If it wasn't
+ * for the segment/selector fixups in 16-bit code (mostly), maybe we could
+ * convince the ELF linker from GNU binutils to do the job too (with help from
+ * the ).
+ *
+ *
+ * @sa grp_bs3kit, grp_bs3kit_tmpl, grp_bs3kit_cmn, grp_bs3kit_mode,
+ * grp_bs3kit_system
+ *
+ * @section sec_calling_convention Calling convention
+ *
+ * Because we're not mixing with C code, we will use __cdecl for 16-bit and
+ * 32-bit code, where as 64-bit code will use the microsoft calling AMD64
+ * convention. To avoid unnecessary %ifdef'ing in assembly code, we will use a
+ * macro to load the RCX, RDX, R8 and R9 registers off the stack in 64-bit
+ * assembly code.
+ *
+ * Register treatment in 16-bit __cdecl, 32-bit __cdecl and 64-bit msabi:
+ *
+ * | Register | 16-bit | 32-bit | 64-bit | ASM template |
+ * | ------------ | ----------- | ---------- | --------------- | ------------ |
+ * | EAX, RAX | volatile | volatile | volatile | volatile |
+ * | EBX, RBX | volatile | preserved | preserved | both |
+ * | ECX, RCX | volatile | volatile | volatile, arg 0 | volatile |
+ * | EDX, RDX | volatile | volatile | volatile, arg 1 | volatile |
+ * | ESP, RSP | preserved | preserved | preserved | preserved |
+ * | EBP, RBP | preserved | preserved | preserved | preserved |
+ * | EDI, RDI | preserved | preserved | preserved | preserved |
+ * | ESI, RSI | preserved | preserved | preserved | preserved |
+ * | R8 | volatile | volatile | volatile, arg 2 | volatile |
+ * | R9 | volatile | volatile | volatile, arg 3 | volatile |
+ * | R10 | volatile | volatile | volatile | volatile |
+ * | R11 | volatile | volatile | volatile | volatile |
+ * | R12 | volatile | volatile | preserved | preserved(*) |
+ * | R13 | volatile | volatile | preserved | preserved(*) |
+ * | R14 | volatile | volatile | preserved | preserved(*) |
+ * | R15 | volatile | volatile | preserved | preserved(*) |
+ * | RFLAGS.DF | =0 | =0 | =0 | =0 |
+ * | CS | preserved | preserved | preserved | preserved |
+ * | DS | preserved! | preserved? | preserved | both |
+ * | ES | volatile | volatile | preserved | volatile |
+ * | FS | preserved | preserved | preserved | preserved |
+ * | GS | preserved | volatile | preserved | both |
+ * | SS | preserved | preserved | preserved | preserved |
+ *
+ * The 'both' here means that we preserve it wrt to our caller, while at the
+ * same time assuming anything we call will clobber it.
+ *
+ * The 'preserved(*)' marking of R12-R15 indicates that they'll be preserved in
+ * 64-bit mode, but may be changed in certain cases when running 32-bit or
+ * 16-bit code. This is especially true if switching CPU mode, e.g. from 32-bit
+ * protected mode to 32-bit long mode.
+ *
+ * Return values are returned in the xAX register, but with the following
+ * caveats for values larger than ARCH_BITS:
+ * - 16-bit code:
+ * - 32-bit values are returned in AX:DX, where AX holds bits 15:0 and
+ * DX bits 31:16.
+ * - 64-bit values are returned in DX:CX:BX:AX, where DX holds bits
+ * 15:0, CX bits 31:16, BX bits 47:32, and AX bits 63:48.
+ * - 32-bit code:
+ * - 64-bit values are returned in EAX:EDX, where eax holds the least
+ * significant bits.
+ *
+ * The DS segment register is pegged to BS3DATA16_GROUP in 16-bit code so that
+ * we don't need to reload it all the time. This allows us to modify it in
+ * ring-0 and mode switching code without ending up in any serious RPL or DPL
+ * trouble. In 32-bit and 64-bit mode the DS register is a flat, unlimited,
+ * writable selector.
+ *
+ * In 16-bit and 32-bit code we do not assume anything about ES, FS, and GS.
+ *
+ *
+ * For an in depth coverage of x86 and AMD64 calling convensions, see
+ * http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/function-calling-conventions.html
+ *
+ *
+ *
+ * @section sec_modes Execution Modes
+ *
+ * BS3Kit defines a number of execution modes in order to be able to test the
+ * full CPU capabilities (that VirtualBox care about anyways). It currently
+ * omits system management mode, hardware virtualization modes, and security
+ * modes as those aren't supported by VirtualBox or are difficult to handle.
+ *
+ * The modes are categorized into normal and weird ones.
+ *
+ * The normal ones are:
+ * + RM - Real mode.
+ * + PE16 - Protected mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PE32 - Protected mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ * + PEV86 - Protected mode running v8086 code, 32-bit TSS and 32-bit handlers.
+ * + PP16 - 386 paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PP32 - 386 paged mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ * + PPV86 - 386 paged mode running v8086 code, 32-bit TSS and 32-bit handlers.
+ * + PAE16 - PAE paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PAE32 - PAE paged mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ * + PAEV86 - PAE paged mode running v8086 code, 32-bit TSS and 32-bit handlers.
+ * + LM16 - AMD64 long mode running 16-bit code, 64-bit TSS and 64-bit handlers.
+ * + LM32 - AMD64 long mode running 32-bit code, 64-bit TSS and 64-bit handlers.
+ * + LM64 - AMD64 long mode running 64-bit code, 64-bit TSS and 64-bit handlers.
+ *
+ * The weird ones:
+ * + PE16_32 - Protected mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PE16_V86 - Protected mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PE32_16 - Protected mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ * + PP16_32 - 386 paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PP16_V86 - 386 paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PP32_16 - 386 paged mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ * + PAE16_32 - PAE paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PAE16_V86 - PAE paged mode running 16-bit code, 16-bit TSS and 16-bit handlers.
+ * + PAE32_16 - PAE paged mode running 32-bit code, 32-bit TSS and 32-bit handlers.
+ *
+ * Actually, the PE32_16, PP32_16 and PAE32_16 modes aren't all that weird and fits in
+ * right next to LM16 and LM32, but this is the way it ended up. :-)
+ *
+ */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-define.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-define.h
new file mode 100644
index 00000000..96946e25
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-define.h
@@ -0,0 +1,256 @@
+/* $Id: bs3kit-mangling-code-define.h $ */
+/** @file
+ * BS3Kit - Function needing mangling - generated by the bs3kit-mangling-code-define.h makefile rule.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define Bs3A20Disable BS3_CMN_MANGLER(Bs3A20Disable)
+#define Bs3A20DisableViaKbd BS3_CMN_MANGLER(Bs3A20DisableViaKbd)
+#define Bs3A20DisableViaPortA BS3_CMN_MANGLER(Bs3A20DisableViaPortA)
+#define Bs3A20Enable BS3_CMN_MANGLER(Bs3A20Enable)
+#define Bs3A20EnableViaKbd BS3_CMN_MANGLER(Bs3A20EnableViaKbd)
+#define Bs3A20EnableViaPortA BS3_CMN_MANGLER(Bs3A20EnableViaPortA)
+#define Bs3ExtCtxAlloc BS3_CMN_MANGLER(Bs3ExtCtxAlloc)
+#define Bs3ExtCtxCopy BS3_CMN_MANGLER(Bs3ExtCtxCopy)
+#define Bs3ExtCtxFree BS3_CMN_MANGLER(Bs3ExtCtxFree)
+#define Bs3ExtCtxGetAbridgedFtw BS3_CMN_MANGLER(Bs3ExtCtxGetAbridgedFtw)
+#define Bs3ExtCtxGetFcw BS3_CMN_MANGLER(Bs3ExtCtxGetFcw)
+#define Bs3ExtCtxGetFsw BS3_CMN_MANGLER(Bs3ExtCtxGetFsw)
+#define Bs3ExtCtxGetMm BS3_CMN_MANGLER(Bs3ExtCtxGetMm)
+#define Bs3ExtCtxGetMxCsr BS3_CMN_MANGLER(Bs3ExtCtxGetMxCsr)
+#define Bs3ExtCtxGetMxCsrMask BS3_CMN_MANGLER(Bs3ExtCtxGetMxCsrMask)
+#define Bs3ExtCtxGetSize BS3_CMN_MANGLER(Bs3ExtCtxGetSize)
+#define Bs3ExtCtxGetXmm BS3_CMN_MANGLER(Bs3ExtCtxGetXmm)
+#define Bs3ExtCtxGetYmm BS3_CMN_MANGLER(Bs3ExtCtxGetYmm)
+#define Bs3ExtCtxInit BS3_CMN_MANGLER(Bs3ExtCtxInit)
+#define Bs3ExtCtxRestore BS3_CMN_MANGLER(Bs3ExtCtxRestore)
+#define Bs3ExtCtxRestoreEx BS3_CMN_MANGLER(Bs3ExtCtxRestoreEx)
+#define Bs3ExtCtxSave BS3_CMN_MANGLER(Bs3ExtCtxSave)
+#define Bs3ExtCtxSaveEx BS3_CMN_MANGLER(Bs3ExtCtxSaveEx)
+#define Bs3ExtCtxSetAbridgedFtw BS3_CMN_MANGLER(Bs3ExtCtxSetAbridgedFtw)
+#define Bs3ExtCtxSetFcw BS3_CMN_MANGLER(Bs3ExtCtxSetFcw)
+#define Bs3ExtCtxSetFsw BS3_CMN_MANGLER(Bs3ExtCtxSetFsw)
+#define Bs3ExtCtxSetMm BS3_CMN_MANGLER(Bs3ExtCtxSetMm)
+#define Bs3ExtCtxSetMxCsr BS3_CMN_MANGLER(Bs3ExtCtxSetMxCsr)
+#define Bs3ExtCtxSetMxCsrMask BS3_CMN_MANGLER(Bs3ExtCtxSetMxCsrMask)
+#define Bs3ExtCtxSetXmm BS3_CMN_MANGLER(Bs3ExtCtxSetXmm)
+#define Bs3ExtCtxSetYmm BS3_CMN_MANGLER(Bs3ExtCtxSetYmm)
+#define Bs3GetCpuVendor BS3_CMN_MANGLER(Bs3GetCpuVendor)
+#define Bs3GetModeName BS3_CMN_MANGLER(Bs3GetModeName)
+#define Bs3GetModeNameShortLower BS3_CMN_MANGLER(Bs3GetModeNameShortLower)
+#define Bs3KbdRead BS3_CMN_MANGLER(Bs3KbdRead)
+#define Bs3KbdWait BS3_CMN_MANGLER(Bs3KbdWait)
+#define Bs3KbdWrite BS3_CMN_MANGLER(Bs3KbdWrite)
+#define Bs3MemAlloc BS3_CMN_MANGLER(Bs3MemAlloc)
+#define Bs3MemAllocZ BS3_CMN_MANGLER(Bs3MemAllocZ)
+#define Bs3MemChr BS3_CMN_MANGLER(Bs3MemChr)
+#define Bs3MemCmp BS3_CMN_MANGLER(Bs3MemCmp)
+#define Bs3MemCpy BS3_CMN_MANGLER(Bs3MemCpy)
+#define Bs3MemFree BS3_CMN_MANGLER(Bs3MemFree)
+#define Bs3MemGuardedTestPageAlloc BS3_CMN_MANGLER(Bs3MemGuardedTestPageAlloc)
+#define Bs3MemGuardedTestPageAllocEx BS3_CMN_MANGLER(Bs3MemGuardedTestPageAllocEx)
+#define Bs3MemGuardedTestPageFree BS3_CMN_MANGLER(Bs3MemGuardedTestPageFree)
+#define Bs3MemMove BS3_CMN_MANGLER(Bs3MemMove)
+#define Bs3MemPCpy BS3_CMN_MANGLER(Bs3MemPCpy)
+#define Bs3MemPrintInfo BS3_CMN_MANGLER(Bs3MemPrintInfo)
+#define Bs3MemSet BS3_CMN_MANGLER(Bs3MemSet)
+#define Bs3MemZero BS3_CMN_MANGLER(Bs3MemZero)
+#define Bs3PagingAlias BS3_CMN_MANGLER(Bs3PagingAlias)
+#define bs3PagingGetLegacyPte BS3_CMN_MANGLER(bs3PagingGetLegacyPte)
+#define bs3PagingGetPaePte BS3_CMN_MANGLER(bs3PagingGetPaePte)
+#define Bs3PagingGetPte BS3_CMN_MANGLER(Bs3PagingGetPte)
+#define Bs3PagingInitRootForLM BS3_CMN_MANGLER(Bs3PagingInitRootForLM)
+#define Bs3PagingInitRootForPAE BS3_CMN_MANGLER(Bs3PagingInitRootForPAE)
+#define Bs3PagingInitRootForPP BS3_CMN_MANGLER(Bs3PagingInitRootForPP)
+#define Bs3PagingMapRamAbove4GForLM BS3_CMN_MANGLER(Bs3PagingMapRamAbove4GForLM)
+#define Bs3PagingProtect BS3_CMN_MANGLER(Bs3PagingProtect)
+#define Bs3PagingProtectPtr BS3_CMN_MANGLER(Bs3PagingProtectPtr)
+#define Bs3PagingQueryAddressInfo BS3_CMN_MANGLER(Bs3PagingQueryAddressInfo)
+#define Bs3PagingSetupCanonicalTraps BS3_CMN_MANGLER(Bs3PagingSetupCanonicalTraps)
+#define Bs3PagingUnalias BS3_CMN_MANGLER(Bs3PagingUnalias)
+#define Bs3Panic BS3_CMN_MANGLER(Bs3Panic)
+#define Bs3PicMaskAll BS3_CMN_MANGLER(Bs3PicMaskAll)
+#define Bs3PicSetup BS3_CMN_MANGLER(Bs3PicSetup)
+#define Bs3PicUpdateMask BS3_CMN_MANGLER(Bs3PicUpdateMask)
+#define Bs3PitDisable BS3_CMN_MANGLER(Bs3PitDisable)
+#define Bs3PitSetupAndEnablePeriodTimer BS3_CMN_MANGLER(Bs3PitSetupAndEnablePeriodTimer)
+#define Bs3PrintChr BS3_CMN_MANGLER(Bs3PrintChr)
+#define Bs3Printf BS3_CMN_MANGLER(Bs3Printf)
+#define Bs3PrintfV BS3_CMN_MANGLER(Bs3PrintfV)
+#define Bs3PrintStr BS3_CMN_MANGLER(Bs3PrintStr)
+#define Bs3PrintStrN BS3_CMN_MANGLER(Bs3PrintStrN)
+#define Bs3PrintU32 BS3_CMN_MANGLER(Bs3PrintU32)
+#define Bs3PrintX32 BS3_CMN_MANGLER(Bs3PrintX32)
+#define Bs3RegCtxConvertToRingX BS3_CMN_MANGLER(Bs3RegCtxConvertToRingX)
+#define Bs3RegCtxConvertV86ToRm BS3_CMN_MANGLER(Bs3RegCtxConvertV86ToRm)
+#define Bs3RegCtxGetRspSsAsCurPtr BS3_CMN_MANGLER(Bs3RegCtxGetRspSsAsCurPtr)
+#define Bs3RegCtxPrint BS3_CMN_MANGLER(Bs3RegCtxPrint)
+#define Bs3RegCtxRestore BS3_CMN_MANGLER(Bs3RegCtxRestore)
+#define Bs3RegCtxSave BS3_CMN_MANGLER(Bs3RegCtxSave)
+#define Bs3RegCtxSaveEx BS3_CMN_MANGLER(Bs3RegCtxSaveEx)
+#define Bs3RegCtxSaveForMode BS3_CMN_MANGLER(Bs3RegCtxSaveForMode)
+#define Bs3RegCtxSetGpr BS3_CMN_MANGLER(Bs3RegCtxSetGpr)
+#define Bs3RegCtxSetGrpDsFromCurPtr BS3_CMN_MANGLER(Bs3RegCtxSetGrpDsFromCurPtr)
+#define Bs3RegCtxSetGrpSegFromCurPtr BS3_CMN_MANGLER(Bs3RegCtxSetGrpSegFromCurPtr)
+#define Bs3RegCtxSetGrpSegFromFlat BS3_CMN_MANGLER(Bs3RegCtxSetGrpSegFromFlat)
+#define Bs3RegCtxSetRipCsFromCurPtr BS3_CMN_MANGLER(Bs3RegCtxSetRipCsFromCurPtr)
+#define Bs3RegCtxSetRipCsFromFlat BS3_CMN_MANGLER(Bs3RegCtxSetRipCsFromFlat)
+#define Bs3RegCtxSetRipCsFromLnkPtr BS3_CMN_MANGLER(Bs3RegCtxSetRipCsFromLnkPtr)
+#define Bs3RegGetCr0 BS3_CMN_MANGLER(Bs3RegGetCr0)
+#define Bs3RegGetCr2 BS3_CMN_MANGLER(Bs3RegGetCr2)
+#define Bs3RegGetCr3 BS3_CMN_MANGLER(Bs3RegGetCr3)
+#define Bs3RegGetCr4 BS3_CMN_MANGLER(Bs3RegGetCr4)
+#define Bs3RegGetDr0 BS3_CMN_MANGLER(Bs3RegGetDr0)
+#define Bs3RegGetDr1 BS3_CMN_MANGLER(Bs3RegGetDr1)
+#define Bs3RegGetDr2 BS3_CMN_MANGLER(Bs3RegGetDr2)
+#define Bs3RegGetDr3 BS3_CMN_MANGLER(Bs3RegGetDr3)
+#define Bs3RegGetDr6 BS3_CMN_MANGLER(Bs3RegGetDr6)
+#define Bs3RegGetDr7 BS3_CMN_MANGLER(Bs3RegGetDr7)
+#define Bs3RegGetDrX BS3_CMN_MANGLER(Bs3RegGetDrX)
+#define Bs3RegGetLdtr BS3_CMN_MANGLER(Bs3RegGetLdtr)
+#define Bs3RegGetTr BS3_CMN_MANGLER(Bs3RegGetTr)
+#define Bs3RegGetXcr0 BS3_CMN_MANGLER(Bs3RegGetXcr0)
+#define Bs3RegSetCr0 BS3_CMN_MANGLER(Bs3RegSetCr0)
+#define Bs3RegSetCr2 BS3_CMN_MANGLER(Bs3RegSetCr2)
+#define Bs3RegSetCr3 BS3_CMN_MANGLER(Bs3RegSetCr3)
+#define Bs3RegSetCr4 BS3_CMN_MANGLER(Bs3RegSetCr4)
+#define Bs3RegSetDr0 BS3_CMN_MANGLER(Bs3RegSetDr0)
+#define Bs3RegSetDr1 BS3_CMN_MANGLER(Bs3RegSetDr1)
+#define Bs3RegSetDr2 BS3_CMN_MANGLER(Bs3RegSetDr2)
+#define Bs3RegSetDr3 BS3_CMN_MANGLER(Bs3RegSetDr3)
+#define Bs3RegSetDr6 BS3_CMN_MANGLER(Bs3RegSetDr6)
+#define Bs3RegSetDr7 BS3_CMN_MANGLER(Bs3RegSetDr7)
+#define Bs3RegSetDrX BS3_CMN_MANGLER(Bs3RegSetDrX)
+#define Bs3RegSetLdtr BS3_CMN_MANGLER(Bs3RegSetLdtr)
+#define Bs3RegSetTr BS3_CMN_MANGLER(Bs3RegSetTr)
+#define Bs3RegSetXcr0 BS3_CMN_MANGLER(Bs3RegSetXcr0)
+#define Bs3SelFar32ToFlat32 BS3_CMN_MANGLER(Bs3SelFar32ToFlat32)
+#define Bs3SelFar32ToFlat32NoClobber BS3_CMN_MANGLER(Bs3SelFar32ToFlat32NoClobber)
+#define Bs3SelFlatCodeToProtFar16 BS3_CMN_MANGLER(Bs3SelFlatCodeToProtFar16)
+#define Bs3SelFlatCodeToRealMode BS3_CMN_MANGLER(Bs3SelFlatCodeToRealMode)
+#define Bs3SelFlatDataToProtFar16 BS3_CMN_MANGLER(Bs3SelFlatDataToProtFar16)
+#define Bs3SelFlatDataToRealMode BS3_CMN_MANGLER(Bs3SelFlatDataToRealMode)
+#define Bs3SelLnkPtrToCurPtr BS3_CMN_MANGLER(Bs3SelLnkPtrToCurPtr)
+#define Bs3SelLnkPtrToFlat BS3_CMN_MANGLER(Bs3SelLnkPtrToFlat)
+#define Bs3SelProtFar16DataToFlat BS3_CMN_MANGLER(Bs3SelProtFar16DataToFlat)
+#define Bs3SelProtFar16DataToRealMode BS3_CMN_MANGLER(Bs3SelProtFar16DataToRealMode)
+#define Bs3SelProtFar32ToFlat32 BS3_CMN_MANGLER(Bs3SelProtFar32ToFlat32)
+#define Bs3SelProtModeCodeToRealMode BS3_CMN_MANGLER(Bs3SelProtModeCodeToRealMode)
+#define Bs3SelRealModeCodeToFlat BS3_CMN_MANGLER(Bs3SelRealModeCodeToFlat)
+#define Bs3SelRealModeCodeToProtMode BS3_CMN_MANGLER(Bs3SelRealModeCodeToProtMode)
+#define Bs3SelRealModeDataToFlat BS3_CMN_MANGLER(Bs3SelRealModeDataToFlat)
+#define Bs3SelRealModeDataToProtFar16 BS3_CMN_MANGLER(Bs3SelRealModeDataToProtFar16)
+#define Bs3SelSetup16BitCode BS3_CMN_MANGLER(Bs3SelSetup16BitCode)
+#define Bs3SelSetup16BitData BS3_CMN_MANGLER(Bs3SelSetup16BitData)
+#define Bs3SelSetup32BitCode BS3_CMN_MANGLER(Bs3SelSetup32BitCode)
+#define Bs3SelSetupGate64 BS3_CMN_MANGLER(Bs3SelSetupGate64)
+#define Bs3SelSetupGate BS3_CMN_MANGLER(Bs3SelSetupGate)
+#define Bs3Shutdown BS3_CMN_MANGLER(Bs3Shutdown)
+#define Bs3SlabAlloc BS3_CMN_MANGLER(Bs3SlabAlloc)
+#define Bs3SlabAllocEx BS3_CMN_MANGLER(Bs3SlabAllocEx)
+#define Bs3SlabFree BS3_CMN_MANGLER(Bs3SlabFree)
+#define Bs3SlabInit BS3_CMN_MANGLER(Bs3SlabInit)
+#define Bs3SlabListAdd BS3_CMN_MANGLER(Bs3SlabListAdd)
+#define Bs3SlabListAlloc BS3_CMN_MANGLER(Bs3SlabListAlloc)
+#define Bs3SlabListAllocEx BS3_CMN_MANGLER(Bs3SlabListAllocEx)
+#define Bs3SlabListFree BS3_CMN_MANGLER(Bs3SlabListFree)
+#define Bs3SlabListInit BS3_CMN_MANGLER(Bs3SlabListInit)
+#define Bs3StrCpy BS3_CMN_MANGLER(Bs3StrCpy)
+#define Bs3StrFormatV BS3_CMN_MANGLER(Bs3StrFormatV)
+#define Bs3StrLen BS3_CMN_MANGLER(Bs3StrLen)
+#define Bs3StrNLen BS3_CMN_MANGLER(Bs3StrNLen)
+#define Bs3StrPrintf BS3_CMN_MANGLER(Bs3StrPrintf)
+#define Bs3StrPrintfV BS3_CMN_MANGLER(Bs3StrPrintfV)
+#define Bs3SwitchFromV86To16BitAndCallC BS3_CMN_MANGLER(Bs3SwitchFromV86To16BitAndCallC)
+#define Bs3TestCheckExtCtx BS3_CMN_MANGLER(Bs3TestCheckExtCtx)
+#define Bs3TestCheckRegCtxEx BS3_CMN_MANGLER(Bs3TestCheckRegCtxEx)
+#define Bs3TestFailed BS3_CMN_MANGLER(Bs3TestFailed)
+#define Bs3TestFailedF BS3_CMN_MANGLER(Bs3TestFailedF)
+#define Bs3TestFailedV BS3_CMN_MANGLER(Bs3TestFailedV)
+#define Bs3TestHostPrintf BS3_CMN_MANGLER(Bs3TestHostPrintf)
+#define Bs3TestHostPrintfV BS3_CMN_MANGLER(Bs3TestHostPrintfV)
+#define Bs3TestInit BS3_CMN_MANGLER(Bs3TestInit)
+#define Bs3TestNow BS3_CMN_MANGLER(Bs3TestNow)
+#define Bs3TestPrintf BS3_CMN_MANGLER(Bs3TestPrintf)
+#define Bs3TestPrintfV BS3_CMN_MANGLER(Bs3TestPrintfV)
+#define Bs3TestQueryCfgBool BS3_CMN_MANGLER(Bs3TestQueryCfgBool)
+#define Bs3TestQueryCfgU32 BS3_CMN_MANGLER(Bs3TestQueryCfgU32)
+#define Bs3TestQueryCfgU8 BS3_CMN_MANGLER(Bs3TestQueryCfgU8)
+#define Bs3TestSkipped BS3_CMN_MANGLER(Bs3TestSkipped)
+#define Bs3TestSkippedF BS3_CMN_MANGLER(Bs3TestSkippedF)
+#define Bs3TestSkippedV BS3_CMN_MANGLER(Bs3TestSkippedV)
+#define Bs3TestSub BS3_CMN_MANGLER(Bs3TestSub)
+#define Bs3TestSubDone BS3_CMN_MANGLER(Bs3TestSubDone)
+#define Bs3TestSubErrorCount BS3_CMN_MANGLER(Bs3TestSubErrorCount)
+#define Bs3TestSubF BS3_CMN_MANGLER(Bs3TestSubF)
+#define Bs3TestSubV BS3_CMN_MANGLER(Bs3TestSubV)
+#define Bs3TestTerm BS3_CMN_MANGLER(Bs3TestTerm)
+#define Bs3TestValue BS3_CMN_MANGLER(Bs3TestValue)
+#define Bs3Trap16Init BS3_CMN_MANGLER(Bs3Trap16Init)
+#define Bs3Trap16InitEx BS3_CMN_MANGLER(Bs3Trap16InitEx)
+#define Bs3Trap16SetGate BS3_CMN_MANGLER(Bs3Trap16SetGate)
+#define Bs3Trap32Init BS3_CMN_MANGLER(Bs3Trap32Init)
+#define Bs3Trap32SetGate BS3_CMN_MANGLER(Bs3Trap32SetGate)
+#define Bs3Trap64Init BS3_CMN_MANGLER(Bs3Trap64Init)
+#define Bs3Trap64InitEx BS3_CMN_MANGLER(Bs3Trap64InitEx)
+#define Bs3Trap64SetGate BS3_CMN_MANGLER(Bs3Trap64SetGate)
+#define Bs3TrapDefaultHandler BS3_CMN_MANGLER(Bs3TrapDefaultHandler)
+#define Bs3TrapPrintFrame BS3_CMN_MANGLER(Bs3TrapPrintFrame)
+#define Bs3TrapReInit BS3_CMN_MANGLER(Bs3TrapReInit)
+#define Bs3TrapRmV86Init BS3_CMN_MANGLER(Bs3TrapRmV86Init)
+#define Bs3TrapRmV86InitEx BS3_CMN_MANGLER(Bs3TrapRmV86InitEx)
+#define Bs3TrapRmV86SetGate BS3_CMN_MANGLER(Bs3TrapRmV86SetGate)
+#define Bs3TrapSetDpl BS3_CMN_MANGLER(Bs3TrapSetDpl)
+#define Bs3TrapSetHandler BS3_CMN_MANGLER(Bs3TrapSetHandler)
+#define Bs3TrapSetHandlerEx BS3_CMN_MANGLER(Bs3TrapSetHandlerEx)
+#define Bs3TrapSetJmpAndRestore BS3_CMN_MANGLER(Bs3TrapSetJmpAndRestore)
+#define Bs3TrapSetJmpAndRestoreInRm BS3_CMN_MANGLER(Bs3TrapSetJmpAndRestoreInRm)
+#define Bs3TrapSetJmpAndRestoreWithExtCtxAndRm BS3_CMN_MANGLER(Bs3TrapSetJmpAndRestoreWithExtCtxAndRm)
+#define Bs3TrapSetJmpAndRestoreWithExtCtx BS3_CMN_MANGLER(Bs3TrapSetJmpAndRestoreWithExtCtx)
+#define Bs3TrapSetJmpAndRestoreWithRm BS3_CMN_MANGLER(Bs3TrapSetJmpAndRestoreWithRm)
+#define Bs3TrapSetJmp BS3_CMN_MANGLER(Bs3TrapSetJmp)
+#define Bs3TrapUnsetJmp BS3_CMN_MANGLER(Bs3TrapUnsetJmp)
+#define Bs3UInt32Div BS3_CMN_MANGLER(Bs3UInt32Div)
+#define Bs3UInt64Div BS3_CMN_MANGLER(Bs3UInt64Div)
+#define Bs3UtilSetFullGdtr BS3_CMN_MANGLER(Bs3UtilSetFullGdtr)
+#define Bs3UtilSetFullIdtr BS3_CMN_MANGLER(Bs3UtilSetFullIdtr)
+#ifndef BS3_CMN_ONLY
+# define Bs3BiosInt15h88 BS3_MODE_MANGLER(Bs3BiosInt15h88)
+# define Bs3BiosInt15hE820 BS3_MODE_MANGLER(Bs3BiosInt15hE820)
+# define Bs3CpuDetect BS3_MODE_MANGLER(Bs3CpuDetect)
+# define Bs3SwitchTo32BitAndCallC BS3_MODE_MANGLER(Bs3SwitchTo32BitAndCallC)
+# define Bs3TestDoModes BS3_MODE_MANGLER(Bs3TestDoModes)
+# define Bs3TestDoModesByMax BS3_MODE_MANGLER(Bs3TestDoModesByMax)
+# define Bs3TestDoModesByOne BS3_MODE_MANGLER(Bs3TestDoModesByOne)
+# define Bs3TrapInit BS3_MODE_MANGLER(Bs3TrapInit)
+#endif /* !BS3_CMN_ONLY */
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-undef.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-undef.h
new file mode 100644
index 00000000..1347d303
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code-undef.h
@@ -0,0 +1,256 @@
+/* $Id: bs3kit-mangling-code-undef.h $ */
+/** @file
+ * BS3Kit - Undefining function mangling - automatically generated by the bs3kit-mangling-code-undef.h makefile rule.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#undef Bs3A20Disable
+#undef Bs3A20DisableViaKbd
+#undef Bs3A20DisableViaPortA
+#undef Bs3A20Enable
+#undef Bs3A20EnableViaKbd
+#undef Bs3A20EnableViaPortA
+#undef Bs3ExtCtxAlloc
+#undef Bs3ExtCtxCopy
+#undef Bs3ExtCtxFree
+#undef Bs3ExtCtxGetAbridgedFtw
+#undef Bs3ExtCtxGetFcw
+#undef Bs3ExtCtxGetFsw
+#undef Bs3ExtCtxGetMm
+#undef Bs3ExtCtxGetMxCsr
+#undef Bs3ExtCtxGetMxCsrMask
+#undef Bs3ExtCtxGetSize
+#undef Bs3ExtCtxGetXmm
+#undef Bs3ExtCtxGetYmm
+#undef Bs3ExtCtxInit
+#undef Bs3ExtCtxRestore
+#undef Bs3ExtCtxRestoreEx
+#undef Bs3ExtCtxSave
+#undef Bs3ExtCtxSaveEx
+#undef Bs3ExtCtxSetAbridgedFtw
+#undef Bs3ExtCtxSetFcw
+#undef Bs3ExtCtxSetFsw
+#undef Bs3ExtCtxSetMm
+#undef Bs3ExtCtxSetMxCsr
+#undef Bs3ExtCtxSetMxCsrMask
+#undef Bs3ExtCtxSetXmm
+#undef Bs3ExtCtxSetYmm
+#undef Bs3GetCpuVendor
+#undef Bs3GetModeName
+#undef Bs3GetModeNameShortLower
+#undef Bs3KbdRead
+#undef Bs3KbdWait
+#undef Bs3KbdWrite
+#undef Bs3MemAlloc
+#undef Bs3MemAllocZ
+#undef Bs3MemChr
+#undef Bs3MemCmp
+#undef Bs3MemCpy
+#undef Bs3MemFree
+#undef Bs3MemGuardedTestPageAlloc
+#undef Bs3MemGuardedTestPageAllocEx
+#undef Bs3MemGuardedTestPageFree
+#undef Bs3MemMove
+#undef Bs3MemPCpy
+#undef Bs3MemPrintInfo
+#undef Bs3MemSet
+#undef Bs3MemZero
+#undef Bs3PagingAlias
+#undef bs3PagingGetLegacyPte
+#undef bs3PagingGetPaePte
+#undef Bs3PagingGetPte
+#undef Bs3PagingInitRootForLM
+#undef Bs3PagingInitRootForPAE
+#undef Bs3PagingInitRootForPP
+#undef Bs3PagingMapRamAbove4GForLM
+#undef Bs3PagingProtect
+#undef Bs3PagingProtectPtr
+#undef Bs3PagingQueryAddressInfo
+#undef Bs3PagingSetupCanonicalTraps
+#undef Bs3PagingUnalias
+#undef Bs3Panic
+#undef Bs3PicMaskAll
+#undef Bs3PicSetup
+#undef Bs3PicUpdateMask
+#undef Bs3PitDisable
+#undef Bs3PitSetupAndEnablePeriodTimer
+#undef Bs3PrintChr
+#undef Bs3Printf
+#undef Bs3PrintfV
+#undef Bs3PrintStr
+#undef Bs3PrintStrN
+#undef Bs3PrintU32
+#undef Bs3PrintX32
+#undef Bs3RegCtxConvertToRingX
+#undef Bs3RegCtxConvertV86ToRm
+#undef Bs3RegCtxGetRspSsAsCurPtr
+#undef Bs3RegCtxPrint
+#undef Bs3RegCtxRestore
+#undef Bs3RegCtxSave
+#undef Bs3RegCtxSaveEx
+#undef Bs3RegCtxSaveForMode
+#undef Bs3RegCtxSetGpr
+#undef Bs3RegCtxSetGrpDsFromCurPtr
+#undef Bs3RegCtxSetGrpSegFromCurPtr
+#undef Bs3RegCtxSetGrpSegFromFlat
+#undef Bs3RegCtxSetRipCsFromCurPtr
+#undef Bs3RegCtxSetRipCsFromFlat
+#undef Bs3RegCtxSetRipCsFromLnkPtr
+#undef Bs3RegGetCr0
+#undef Bs3RegGetCr2
+#undef Bs3RegGetCr3
+#undef Bs3RegGetCr4
+#undef Bs3RegGetDr0
+#undef Bs3RegGetDr1
+#undef Bs3RegGetDr2
+#undef Bs3RegGetDr3
+#undef Bs3RegGetDr6
+#undef Bs3RegGetDr7
+#undef Bs3RegGetDrX
+#undef Bs3RegGetLdtr
+#undef Bs3RegGetTr
+#undef Bs3RegGetXcr0
+#undef Bs3RegSetCr0
+#undef Bs3RegSetCr2
+#undef Bs3RegSetCr3
+#undef Bs3RegSetCr4
+#undef Bs3RegSetDr0
+#undef Bs3RegSetDr1
+#undef Bs3RegSetDr2
+#undef Bs3RegSetDr3
+#undef Bs3RegSetDr6
+#undef Bs3RegSetDr7
+#undef Bs3RegSetDrX
+#undef Bs3RegSetLdtr
+#undef Bs3RegSetTr
+#undef Bs3RegSetXcr0
+#undef Bs3SelFar32ToFlat32
+#undef Bs3SelFar32ToFlat32NoClobber
+#undef Bs3SelFlatCodeToProtFar16
+#undef Bs3SelFlatCodeToRealMode
+#undef Bs3SelFlatDataToProtFar16
+#undef Bs3SelFlatDataToRealMode
+#undef Bs3SelLnkPtrToCurPtr
+#undef Bs3SelLnkPtrToFlat
+#undef Bs3SelProtFar16DataToFlat
+#undef Bs3SelProtFar16DataToRealMode
+#undef Bs3SelProtFar32ToFlat32
+#undef Bs3SelProtModeCodeToRealMode
+#undef Bs3SelRealModeCodeToFlat
+#undef Bs3SelRealModeCodeToProtMode
+#undef Bs3SelRealModeDataToFlat
+#undef Bs3SelRealModeDataToProtFar16
+#undef Bs3SelSetup16BitCode
+#undef Bs3SelSetup16BitData
+#undef Bs3SelSetup32BitCode
+#undef Bs3SelSetupGate64
+#undef Bs3SelSetupGate
+#undef Bs3Shutdown
+#undef Bs3SlabAlloc
+#undef Bs3SlabAllocEx
+#undef Bs3SlabFree
+#undef Bs3SlabInit
+#undef Bs3SlabListAdd
+#undef Bs3SlabListAlloc
+#undef Bs3SlabListAllocEx
+#undef Bs3SlabListFree
+#undef Bs3SlabListInit
+#undef Bs3StrCpy
+#undef Bs3StrFormatV
+#undef Bs3StrLen
+#undef Bs3StrNLen
+#undef Bs3StrPrintf
+#undef Bs3StrPrintfV
+#undef Bs3SwitchFromV86To16BitAndCallC
+#undef Bs3TestCheckExtCtx
+#undef Bs3TestCheckRegCtxEx
+#undef Bs3TestFailed
+#undef Bs3TestFailedF
+#undef Bs3TestFailedV
+#undef Bs3TestHostPrintf
+#undef Bs3TestHostPrintfV
+#undef Bs3TestInit
+#undef Bs3TestNow
+#undef Bs3TestPrintf
+#undef Bs3TestPrintfV
+#undef Bs3TestQueryCfgBool
+#undef Bs3TestQueryCfgU32
+#undef Bs3TestQueryCfgU8
+#undef Bs3TestSkipped
+#undef Bs3TestSkippedF
+#undef Bs3TestSkippedV
+#undef Bs3TestSub
+#undef Bs3TestSubDone
+#undef Bs3TestSubErrorCount
+#undef Bs3TestSubF
+#undef Bs3TestSubV
+#undef Bs3TestTerm
+#undef Bs3TestValue
+#undef Bs3Trap16Init
+#undef Bs3Trap16InitEx
+#undef Bs3Trap16SetGate
+#undef Bs3Trap32Init
+#undef Bs3Trap32SetGate
+#undef Bs3Trap64Init
+#undef Bs3Trap64InitEx
+#undef Bs3Trap64SetGate
+#undef Bs3TrapDefaultHandler
+#undef Bs3TrapPrintFrame
+#undef Bs3TrapReInit
+#undef Bs3TrapRmV86Init
+#undef Bs3TrapRmV86InitEx
+#undef Bs3TrapRmV86SetGate
+#undef Bs3TrapSetDpl
+#undef Bs3TrapSetHandler
+#undef Bs3TrapSetHandlerEx
+#undef Bs3TrapSetJmpAndRestore
+#undef Bs3TrapSetJmpAndRestoreInRm
+#undef Bs3TrapSetJmpAndRestoreWithExtCtxAndRm
+#undef Bs3TrapSetJmpAndRestoreWithExtCtx
+#undef Bs3TrapSetJmpAndRestoreWithRm
+#undef Bs3TrapSetJmp
+#undef Bs3TrapUnsetJmp
+#undef Bs3UInt32Div
+#undef Bs3UInt64Div
+#undef Bs3UtilSetFullGdtr
+#undef Bs3UtilSetFullIdtr
+#ifndef BS3_CMN_ONLY
+# undef Bs3BiosInt15h88
+# undef Bs3BiosInt15hE820
+# undef Bs3CpuDetect
+# undef Bs3SwitchTo32BitAndCallC
+# undef Bs3TestDoModes
+# undef Bs3TestDoModesByMax
+# undef Bs3TestDoModesByOne
+# undef Bs3TrapInit
+#endif /* !BS3_CMN_ONLY */
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code.h
new file mode 100644
index 00000000..9650c44b
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-code.h
@@ -0,0 +1,52 @@
+/* $Id: bs3kit-mangling-code.h $ */
+/** @file
+ * BS3Kit - Symbol mangling, code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ * Do function mangling. This can be redone at compile time (templates).
+ */
+#undef BS3_CMN_MANGLER
+#undef BS3_MODE_MANGLER
+#if ARCH_BITS != 16 || !defined(BS3_USE_ALT_16BIT_TEXT_SEG)
+# define BS3_CMN_MANGLER(a_Function) BS3_CMN_NM(a_Function)
+# define BS3_MODE_MANGLER(a_Function) TMPL_NM(a_Function)
+#else
+# define BS3_CMN_MANGLER(a_Function) BS3_CMN_FAR_NM(a_Function)
+# define BS3_MODE_MANGLER(a_Function) TMPL_FAR_NM(a_Function)
+#endif
+#include "bs3kit-mangling-code-undef.h"
+#include "bs3kit-mangling-code-define.h"
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-data.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-data.h
new file mode 100644
index 00000000..e6a30afe
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-mangling-data.h
@@ -0,0 +1,295 @@
+/* $Id: bs3kit-mangling-data.h $ */
+/** @file
+ * BS3Kit - Symbol mangling.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ * First part is only applied once. It concerns itself with data symbols.
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3kit_mangling_data_h
+#define BS3KIT_INCLUDED_bs3kit_mangling_data_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#if 0 /* the object converter deals with this now */
+#if ARCH_BITS == 64
+
+# define Bs3Gdt BS3_DATA_NM(Bs3Gdt)
+# define Bs3Gdt_Ldt BS3_DATA_NM(Bs3Gdt_Ldt)
+# define Bs3Gdte_Tss16 BS3_DATA_NM(Bs3Gdte_Tss16)
+# define Bs3Gdte_Tss16DoubleFault BS3_DATA_NM(Bs3Gdte_Tss16DoubleFault)
+# define Bs3Gdte_Tss16Spare0 BS3_DATA_NM(Bs3Gdte_Tss16Spare0)
+# define Bs3Gdte_Tss16Spare1 BS3_DATA_NM(Bs3Gdte_Tss16Spare1)
+# define Bs3Gdte_Tss32 BS3_DATA_NM(Bs3Gdte_Tss32)
+# define Bs3Gdte_Tss32DoubleFault BS3_DATA_NM(Bs3Gdte_Tss32DoubleFault)
+# define Bs3Gdte_Tss32Spare0 BS3_DATA_NM(Bs3Gdte_Tss32Spare0)
+# define Bs3Gdte_Tss32Spare1 BS3_DATA_NM(Bs3Gdte_Tss32Spare1)
+# define Bs3Gdte_Tss32IobpIntRedirBm BS3_DATA_NM(Bs3Gdte_Tss32IobpIntRedirBm)
+# define Bs3Gdte_Tss32IntRedirBm BS3_DATA_NM(Bs3Gdte_Tss32IntRedirBm)
+# define Bs3Gdte_Tss64 BS3_DATA_NM(Bs3Gdte_Tss64)
+# define Bs3Gdte_Tss64Spare0 BS3_DATA_NM(Bs3Gdte_Tss64Spare0)
+# define Bs3Gdte_Tss64Spare1 BS3_DATA_NM(Bs3Gdte_Tss64Spare1)
+# define Bs3Gdte_Tss64Iobp BS3_DATA_NM(Bs3Gdte_Tss64Iobp)
+# define Bs3Gdte_RMTEXT16_CS BS3_DATA_NM(Bs3Gdte_RMTEXT16_CS)
+# define Bs3Gdte_X0TEXT16_CS BS3_DATA_NM(Bs3Gdte_X0TEXT16_CS)
+# define Bs3Gdte_X1TEXT16_CS BS3_DATA_NM(Bs3Gdte_X1TEXT16_CS)
+# define Bs3Gdte_R0_MMIO16 BS3_DATA_NM(Bs3Gdte_R0_MMIO16)
+
+# define Bs3Gdte_R0_First BS3_DATA_NM(Bs3Gdte_R0_First)
+# define Bs3Gdte_R0_CS16 BS3_DATA_NM(Bs3Gdte_R0_CS16)
+# define Bs3Gdte_R0_DS16 BS3_DATA_NM(Bs3Gdte_R0_DS16)
+# define Bs3Gdte_R0_SS16 BS3_DATA_NM(Bs3Gdte_R0_SS16)
+# define Bs3Gdte_R0_CS32 BS3_DATA_NM(Bs3Gdte_R0_CS32)
+# define Bs3Gdte_R0_DS32 BS3_DATA_NM(Bs3Gdte_R0_DS32)
+# define Bs3Gdte_R0_SS32 BS3_DATA_NM(Bs3Gdte_R0_SS32)
+# define Bs3Gdte_R0_CS64 BS3_DATA_NM(Bs3Gdte_R0_CS64)
+# define Bs3Gdte_R0_DS64 BS3_DATA_NM(Bs3Gdte_R0_DS64)
+# define Bs3Gdte_R0_CS16_EO BS3_DATA_NM(Bs3Gdte_R0_CS16_EO)
+# define Bs3Gdte_R0_CS16_CNF BS3_DATA_NM(Bs3Gdte_R0_CS16_CNF)
+# define Bs3Gdte_R0_CS16_CND_EO BS3_DATA_NM(Bs3Gdte_R0_CS16_CND_EO)
+# define Bs3Gdte_R0_CS32_EO BS3_DATA_NM(Bs3Gdte_R0_CS32_EO)
+# define Bs3Gdte_R0_CS32_CNF BS3_DATA_NM(Bs3Gdte_R0_CS32_CNF)
+# define Bs3Gdte_R0_CS32_CNF_EO BS3_DATA_NM(Bs3Gdte_R0_CS32_CNF_EO)
+# define Bs3Gdte_R0_CS64_EO BS3_DATA_NM(Bs3Gdte_R0_CS64_EO)
+# define Bs3Gdte_R0_CS64_CNF BS3_DATA_NM(Bs3Gdte_R0_CS64_CNF)
+# define Bs3Gdte_R0_CS64_CNF_EO BS3_DATA_NM(Bs3Gdte_R0_CS64_CNF_EO)
+
+# define Bs3Gdte_R1_First BS3_DATA_NM(Bs3Gdte_R1_First)
+# define Bs3Gdte_R1_CS16 BS3_DATA_NM(Bs3Gdte_R1_CS16)
+# define Bs3Gdte_R1_DS16 BS3_DATA_NM(Bs3Gdte_R1_DS16)
+# define Bs3Gdte_R1_SS16 BS3_DATA_NM(Bs3Gdte_R1_SS16)
+# define Bs3Gdte_R1_CS32 BS3_DATA_NM(Bs3Gdte_R1_CS32)
+# define Bs3Gdte_R1_DS32 BS3_DATA_NM(Bs3Gdte_R1_DS32)
+# define Bs3Gdte_R1_SS32 BS3_DATA_NM(Bs3Gdte_R1_SS32)
+# define Bs3Gdte_R1_CS64 BS3_DATA_NM(Bs3Gdte_R1_CS64)
+# define Bs3Gdte_R1_DS64 BS3_DATA_NM(Bs3Gdte_R1_DS64)
+# define Bs3Gdte_R1_CS16_EO BS3_DATA_NM(Bs3Gdte_R1_CS16_EO)
+# define Bs3Gdte_R1_CS16_CNF BS3_DATA_NM(Bs3Gdte_R1_CS16_CNF)
+# define Bs3Gdte_R1_CS16_CND_EO BS3_DATA_NM(Bs3Gdte_R1_CS16_CND_EO)
+# define Bs3Gdte_R1_CS32_EO BS3_DATA_NM(Bs3Gdte_R1_CS32_EO)
+# define Bs3Gdte_R1_CS32_CNF BS3_DATA_NM(Bs3Gdte_R1_CS32_CNF)
+# define Bs3Gdte_R1_CS32_CNF_EO BS3_DATA_NM(Bs3Gdte_R1_CS32_CNF_EO)
+# define Bs3Gdte_R1_CS64_EO BS3_DATA_NM(Bs3Gdte_R1_CS64_EO)
+# define Bs3Gdte_R1_CS64_CNF BS3_DATA_NM(Bs3Gdte_R1_CS64_CNF)
+# define Bs3Gdte_R1_CS64_CNF_EO BS3_DATA_NM(Bs3Gdte_R1_CS64_CNF_EO)
+
+# define Bs3Gdte_R2_First BS3_DATA_NM(Bs3Gdte_R2_First)
+# define Bs3Gdte_R2_CS16 BS3_DATA_NM(Bs3Gdte_R2_CS16)
+# define Bs3Gdte_R2_DS16 BS3_DATA_NM(Bs3Gdte_R2_DS16)
+# define Bs3Gdte_R2_SS16 BS3_DATA_NM(Bs3Gdte_R2_SS16)
+# define Bs3Gdte_R2_CS32 BS3_DATA_NM(Bs3Gdte_R2_CS32)
+# define Bs3Gdte_R2_DS32 BS3_DATA_NM(Bs3Gdte_R2_DS32)
+# define Bs3Gdte_R2_SS32 BS3_DATA_NM(Bs3Gdte_R2_SS32)
+# define Bs3Gdte_R2_CS64 BS3_DATA_NM(Bs3Gdte_R2_CS64)
+# define Bs3Gdte_R2_DS64 BS3_DATA_NM(Bs3Gdte_R2_DS64)
+# define Bs3Gdte_R2_CS16_EO BS3_DATA_NM(Bs3Gdte_R2_CS16_EO)
+# define Bs3Gdte_R2_CS16_CNF BS3_DATA_NM(Bs3Gdte_R2_CS16_CNF)
+# define Bs3Gdte_R2_CS16_CND_EO BS3_DATA_NM(Bs3Gdte_R2_CS16_CND_EO)
+# define Bs3Gdte_R2_CS32_EO BS3_DATA_NM(Bs3Gdte_R2_CS32_EO)
+# define Bs3Gdte_R2_CS32_CNF BS3_DATA_NM(Bs3Gdte_R2_CS32_CNF)
+# define Bs3Gdte_R2_CS32_CNF_EO BS3_DATA_NM(Bs3Gdte_R2_CS32_CNF_EO)
+# define Bs3Gdte_R2_CS64_EO BS3_DATA_NM(Bs3Gdte_R2_CS64_EO)
+# define Bs3Gdte_R2_CS64_CNF BS3_DATA_NM(Bs3Gdte_R2_CS64_CNF)
+# define Bs3Gdte_R2_CS64_CNF_EO BS3_DATA_NM(Bs3Gdte_R2_CS64_CNF_EO)
+
+# define Bs3Gdte_R3_First BS3_DATA_NM(Bs3Gdte_R3_First)
+# define Bs3Gdte_R3_CS16 BS3_DATA_NM(Bs3Gdte_R3_CS16)
+# define Bs3Gdte_R3_DS16 BS3_DATA_NM(Bs3Gdte_R3_DS16)
+# define Bs3Gdte_R3_SS16 BS3_DATA_NM(Bs3Gdte_R3_SS16)
+# define Bs3Gdte_R3_CS32 BS3_DATA_NM(Bs3Gdte_R3_CS32)
+# define Bs3Gdte_R3_DS32 BS3_DATA_NM(Bs3Gdte_R3_DS32)
+# define Bs3Gdte_R3_SS32 BS3_DATA_NM(Bs3Gdte_R3_SS32)
+# define Bs3Gdte_R3_CS64 BS3_DATA_NM(Bs3Gdte_R3_CS64)
+# define Bs3Gdte_R3_DS64 BS3_DATA_NM(Bs3Gdte_R3_DS64)
+# define Bs3Gdte_R3_CS16_EO BS3_DATA_NM(Bs3Gdte_R3_CS16_EO)
+# define Bs3Gdte_R3_CS16_CNF BS3_DATA_NM(Bs3Gdte_R3_CS16_CNF)
+# define Bs3Gdte_R3_CS16_CND_EO BS3_DATA_NM(Bs3Gdte_R3_CS16_CND_EO)
+# define Bs3Gdte_R3_CS32_EO BS3_DATA_NM(Bs3Gdte_R3_CS32_EO)
+# define Bs3Gdte_R3_CS32_CNF BS3_DATA_NM(Bs3Gdte_R3_CS32_CNF)
+# define Bs3Gdte_R3_CS32_CNF_EO BS3_DATA_NM(Bs3Gdte_R3_CS32_CNF_EO)
+# define Bs3Gdte_R3_CS64_EO BS3_DATA_NM(Bs3Gdte_R3_CS64_EO)
+# define Bs3Gdte_R3_CS64_CNF BS3_DATA_NM(Bs3Gdte_R3_CS64_CNF)
+# define Bs3Gdte_R3_CS64_CNF_EO BS3_DATA_NM(Bs3Gdte_R3_CS64_CNF_EO)
+
+# define Bs3GdteSpare00 BS3_DATA_NM(Bs3GdteSpare00)
+# define Bs3GdteSpare01 BS3_DATA_NM(Bs3GdteSpare01)
+# define Bs3GdteSpare02 BS3_DATA_NM(Bs3GdteSpare02)
+# define Bs3GdteSpare03 BS3_DATA_NM(Bs3GdteSpare03)
+# define Bs3GdteSpare04 BS3_DATA_NM(Bs3GdteSpare04)
+# define Bs3GdteSpare05 BS3_DATA_NM(Bs3GdteSpare05)
+# define Bs3GdteSpare06 BS3_DATA_NM(Bs3GdteSpare06)
+# define Bs3GdteSpare07 BS3_DATA_NM(Bs3GdteSpare07)
+# define Bs3GdteSpare08 BS3_DATA_NM(Bs3GdteSpare08)
+# define Bs3GdteSpare09 BS3_DATA_NM(Bs3GdteSpare09)
+# define Bs3GdteSpare0a BS3_DATA_NM(Bs3GdteSpare0a)
+# define Bs3GdteSpare0b BS3_DATA_NM(Bs3GdteSpare0b)
+# define Bs3GdteSpare0c BS3_DATA_NM(Bs3GdteSpare0c)
+# define Bs3GdteSpare0d BS3_DATA_NM(Bs3GdteSpare0d)
+# define Bs3GdteSpare0e BS3_DATA_NM(Bs3GdteSpare0e)
+# define Bs3GdteSpare0f BS3_DATA_NM(Bs3GdteSpare0f)
+# define Bs3GdteSpare10 BS3_DATA_NM(Bs3GdteSpare10)
+# define Bs3GdteSpare11 BS3_DATA_NM(Bs3GdteSpare11)
+# define Bs3GdteSpare12 BS3_DATA_NM(Bs3GdteSpare12)
+# define Bs3GdteSpare13 BS3_DATA_NM(Bs3GdteSpare13)
+# define Bs3GdteSpare14 BS3_DATA_NM(Bs3GdteSpare14)
+# define Bs3GdteSpare15 BS3_DATA_NM(Bs3GdteSpare15)
+# define Bs3GdteSpare16 BS3_DATA_NM(Bs3GdteSpare16)
+# define Bs3GdteSpare17 BS3_DATA_NM(Bs3GdteSpare17)
+# define Bs3GdteSpare18 BS3_DATA_NM(Bs3GdteSpare18)
+# define Bs3GdteSpare19 BS3_DATA_NM(Bs3GdteSpare19)
+# define Bs3GdteSpare1a BS3_DATA_NM(Bs3GdteSpare1a)
+# define Bs3GdteSpare1b BS3_DATA_NM(Bs3GdteSpare1b)
+# define Bs3GdteSpare1c BS3_DATA_NM(Bs3GdteSpare1c)
+# define Bs3GdteSpare1d BS3_DATA_NM(Bs3GdteSpare1d)
+# define Bs3GdteSpare1e BS3_DATA_NM(Bs3GdteSpare1e)
+# define Bs3GdteSpare1f BS3_DATA_NM(Bs3GdteSpare1f)
+
+# define Bs3GdteTiled BS3_DATA_NM(Bs3GdteTiled)
+# define Bs3GdteFreePart1 BS3_DATA_NM(Bs3GdteFreePart1)
+# define Bs3Gdte_CODE16 BS3_DATA_NM(Bs3Gdte_CODE16)
+# define Bs3GdteFreePart2 BS3_DATA_NM(Bs3GdteFreePart2)
+# define Bs3Gdte_SYSTEM16 BS3_DATA_NM(Bs3Gdte_SYSTEM16)
+# define Bs3GdteFreePart3 BS3_DATA_NM(Bs3GdteFreePart3)
+# define Bs3Gdte_DATA16 BS3_DATA_NM(Bs3Gdte_DATA16)
+
+# define Bs3GdteFreePart4 BS3_DATA_NM(Bs3GdteFreePart4)
+# define Bs3GdtePreTestPage08 BS3_DATA_NM(Bs3GdtePreTestPage08)
+# define Bs3GdtePreTestPage07 BS3_DATA_NM(Bs3GdtePreTestPage07)
+# define Bs3GdtePreTestPage06 BS3_DATA_NM(Bs3GdtePreTestPage06)
+# define Bs3GdtePreTestPage05 BS3_DATA_NM(Bs3GdtePreTestPage05)
+# define Bs3GdtePreTestPage04 BS3_DATA_NM(Bs3GdtePreTestPage04)
+# define Bs3GdtePreTestPage03 BS3_DATA_NM(Bs3GdtePreTestPage03)
+# define Bs3GdtePreTestPage02 BS3_DATA_NM(Bs3GdtePreTestPage02)
+# define Bs3GdtePreTestPage01 BS3_DATA_NM(Bs3GdtePreTestPage01)
+# define Bs3GdteTestPage BS3_DATA_NM(Bs3GdteTestPage)
+# define Bs3GdteTestPage00 BS3_DATA_NM(Bs3GdteTestPage00)
+# define Bs3GdteTestPage01 BS3_DATA_NM(Bs3GdteTestPage01)
+# define Bs3GdteTestPage02 BS3_DATA_NM(Bs3GdteTestPage02)
+# define Bs3GdteTestPage03 BS3_DATA_NM(Bs3GdteTestPage03)
+# define Bs3GdteTestPage04 BS3_DATA_NM(Bs3GdteTestPage04)
+# define Bs3GdteTestPage05 BS3_DATA_NM(Bs3GdteTestPage05)
+# define Bs3GdteTestPage06 BS3_DATA_NM(Bs3GdteTestPage06)
+# define Bs3GdteTestPage07 BS3_DATA_NM(Bs3GdteTestPage07)
+
+# define Bs3GdtEnd BS3_DATA_NM(Bs3GdtEnd)
+
+# define Bs3Tss16 BS3_DATA_NM(Bs3Tss16)
+# define Bs3Tss16DoubleFault BS3_DATA_NM(Bs3Tss16DoubleFault)
+# define Bs3Tss16Spare0 BS3_DATA_NM(Bs3Tss16Spare0)
+# define Bs3Tss16Spare1 BS3_DATA_NM(Bs3Tss16Spare1)
+# define Bs3Tss32 BS3_DATA_NM(Bs3Tss32)
+# define Bs3Tss32DoubleFault BS3_DATA_NM(Bs3Tss32DoubleFault)
+# define Bs3Tss32Spare0 BS3_DATA_NM(Bs3Tss32Spare0)
+# define Bs3Tss32Spare1 BS3_DATA_NM(Bs3Tss32Spare1)
+# define Bs3Tss64 BS3_DATA_NM(Bs3Tss64)
+# define Bs3Tss64Spare0 BS3_DATA_NM(Bs3Tss64Spare0)
+# define Bs3Tss64Spare1 BS3_DATA_NM(Bs3Tss64Spare1)
+# define Bs3Tss64WithIopb BS3_DATA_NM(Bs3Tss64WithIopb)
+# define Bs3Tss32WithIopb BS3_DATA_NM(Bs3Tss32WithIopb)
+# define Bs3SharedIntRedirBm BS3_DATA_NM(Bs3SharedIntRedirBm)
+# define Bs3SharedIobp BS3_DATA_NM(Bs3SharedIobp)
+# define Bs3SharedIobpEnd BS3_DATA_NM(Bs3SharedIobpEnd)
+# define Bs3Idt16 BS3_DATA_NM(Bs3Idt16)
+# define Bs3Idt32 BS3_DATA_NM(Bs3Idt32)
+# define Bs3Idt64 BS3_DATA_NM(Bs3Idt64)
+# define Bs3Lidt_Idt16 BS3_DATA_NM(Bs3Lidt_Idt16)
+# define Bs3Lidt_Idt32 BS3_DATA_NM(Bs3Lidt_Idt32)
+# define Bs3Lidt_Idt64 BS3_DATA_NM(Bs3Lidt_Idt64)
+# define Bs3Lidt_Ivt BS3_DATA_NM(Bs3Lidt_Ivt)
+# define Bs3Lgdt_Gdt BS3_DATA_NM(Bs3Lgdt_Gdt)
+# define Bs3Ldt BS3_DATA_NM(Bs3Ldt)
+# define Bs3LdtEnd BS3_DATA_NM(Bs3LdtEnd)
+
+# define Bs3Text16_StartOfSegment BS3_DATA_NM(Bs3Text16_StartOfSegment)
+# define Bs3Text16_EndOfSegment BS3_DATA_NM(Bs3Text16_EndOfSegment)
+# define Bs3Text16_Size BS3_DATA_NM(Bs3Text16_Size)
+
+# define Bs3System16_StartOfSegment BS3_DATA_NM(Bs3System16_StartOfSegment)
+# define Bs3System16_EndOfSegment BS3_DATA_NM(Bs3System16_EndOfSegment)
+
+# define Bs3Data16_StartOfSegment BS3_DATA_NM(Bs3Data16_StartOfSegment)
+# define Bs3Data16_EndOfSegment BS3_DATA_NM(Bs3Data16_EndOfSegment)
+
+# define Bs3RmText16_StartOfSegment BS3_DATA_NM(Bs3RmText16_StartOfSegment)
+# define Bs3RmText16_EndOfSegment BS3_DATA_NM(Bs3RmText16_EndOfSegment)
+# define Bs3RmText16_Size BS3_DATA_NM(Bs3RmText16_Size)
+# define Bs3RmText16_FlatAddr BS3_DATA_NM(Bs3RmText16_FlatAddr)
+
+# define Bs3X0Text16_StartOfSegment BS3_DATA_NM(Bs3X0Text16_StartOfSegment)
+# define Bs3X0Text16_EndOfSegment BS3_DATA_NM(Bs3X0Text16_EndOfSegment)
+# define Bs3X0Text16_Size BS3_DATA_NM(Bs3X0Text16_Size)
+# define Bs3X0Text16_FlatAddr BS3_DATA_NM(Bs3X0Text16_FlatAddr)
+
+# define Bs3X1Text16_StartOfSegment BS3_DATA_NM(Bs3X1Text16_StartOfSegment)
+# define Bs3X1Text16_EndOfSegment BS3_DATA_NM(Bs3X1Text16_EndOfSegment)
+# define Bs3X1Text16_Size BS3_DATA_NM(Bs3X1Text16_Size)
+# define Bs3X1Text16_FlatAddr BS3_DATA_NM(Bs3X1Text16_FlatAddr)
+
+# define Bs3Text32_StartOfSegment BS3_DATA_NM(Bs3Text32_StartOfSegment)
+# define Bs3Text32_EndOfSegment BS3_DATA_NM(Bs3Text32_EndOfSegment)
+
+# define Bs3Data32_StartOfSegment BS3_DATA_NM(Bs3Data32_StartOfSegment)
+# define Bs3Data32_EndOfSegment BS3_DATA_NM(Bs3Data32_EndOfSegment)
+
+# define Bs3Text64_StartOfSegment BS3_DATA_NM(Bs3Text64_StartOfSegment)
+# define Bs3Text64_EndOfSegment BS3_DATA_NM(Bs3Text64_EndOfSegment)
+
+# define Bs3Data64_StartOfSegment BS3_DATA_NM(Bs3Data64_StartOfSegment)
+# define Bs3Data64_EndOfSegment BS3_DATA_NM(Bs3Data64_EndOfSegment)
+
+# define Bs3Data16Thru64Text32And64_TotalSize BS3_DATA_NM(Bs3Data16Thru64Text32And64_TotalSize)
+# define Bs3TotalImageSize BS3_DATA_NM(Bs3TotalImageSize)
+
+# define g_achBs3HexDigits BS3_DATA_NM(g_achBs3HexDigits)
+# define g_achBs3HexDigitsUpper BS3_DATA_NM(g_achBs3HexDigitsUpper)
+# define g_bBs3CurrentMode BS3_DATA_NM(g_bBs3CurrentMode)
+# define g_uBs3TrapEipHint BS3_DATA_NM(g_uBs3TrapEipHint)
+# define g_aBs3RmIvtOriginal BS3_DATA_NM(g_aBs3RmIvtOriginal)
+
+# define g_usBs3TestStep BS3_DATA_NM(g_usBs3TestStep)
+# define g_usBs3TestStep BS3_DATA_NM(g_usBs3TestStep)
+# define g_Bs3Trap16GenericEntriesFlatAddr BS3_DATA_NM(g_Bs3Trap16GenericEntriesFlatAddr)
+# define g_Bs3Trap32GenericEntriesFlatAddr BS3_DATA_NM(g_Bs3Trap32GenericEntriesFlatAddr)
+# define g_Bs3Trap64GenericEntriesFlatAddr BS3_DATA_NM(g_Bs3Trap64GenericEntriesFlatAddr)
+
+# define g_uBs3CpuDetected BS3_DATA_NM(g_uBs3CpuDetected)
+
+#endif /* ARCH_BITS == 64 */
+#endif /* not needed */
+
+#endif /* !BS3KIT_INCLUDED_bs3kit_mangling_data_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.h
new file mode 100644
index 00000000..b80ee399
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.h
@@ -0,0 +1,93 @@
+/* $Id: bs3kit-template-footer.h $ */
+/** @file
+ * BS3Kit footer for multi-mode code templates.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*
+ * Undefine macros defined by the header.
+ * This is a subset of what bs3kit-template-footer.mac does.
+ */
+#undef TMPL_RM
+#undef TMPL_PE16
+#undef TMPL_PE16_32
+#undef TMPL_PE16_V86
+#undef TMPL_PE32
+#undef TMPL_PE32_16
+#undef TMPL_PEV86
+#undef TMPL_PP16
+#undef TMPL_PP16_32
+#undef TMPL_PP16_V86
+#undef TMPL_PP32
+#undef TMPL_PP32_16
+#undef TMPL_PPV86
+#undef TMPL_PAE16
+#undef TMPL_PAE16_32
+#undef TMPL_PAE16_V86
+#undef TMPL_PAE32
+#undef TMPL_PAE32_16
+#undef TMPL_PAEV86
+#undef TMPL_LM16
+#undef TMPL_LM32
+#undef TMPL_LM64
+
+#undef TMPL_CMN_PE
+#undef TMPL_SYS_PE16
+#undef TMPL_SYS_PE32
+#undef TMPL_CMN_PP
+#undef TMPL_SYS_PP16
+#undef TMPL_SYS_PP32
+#undef TMPL_CMN_PAE
+#undef TMPL_SYS_PAE16
+#undef TMPL_SYS_PAE32
+#undef TMPL_CMN_LM
+#undef TMPL_CMN_V86
+#undef TMPL_CMN_R86
+#undef TMPL_CMN_PAGING
+#undef TMPL_CMN_WEIRD
+#undef TMPL_CMN_WEIRD_V86
+
+#undef TMPL_CMN_R86
+
+#undef TMPL_NM
+#undef TMPL_FAR_NM
+#undef TMPL_MODE
+#undef TMPL_MODE_STR
+#undef TMPL_MODE_LNAME
+#undef TMPL_MODE_UNAME
+#undef TMPL_16BIT
+#undef TMPL_32BIT
+#undef TMPL_64BIT
+#undef TMPL_BITS
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.mac b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.mac
new file mode 100644
index 00000000..42325ac8
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-footer.mac
@@ -0,0 +1,142 @@
+; $Id: bs3kit-template-footer.mac $
+;; @file
+; BS3Kit footer for multi-mode code templates.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;
+; Undefine macros defined by the header.
+;
+; Note! The following is useful for verifying that all macros are included here:
+;
+; for i in `grep "%define" bootsector2-template-header.mac \
+; | sed -e 's/^ *%define *//' -e 's/^\([^() ]*\).*$/\1/' \
+; | sort -u`
+; do
+; if ! grep -wF "%undef $i" bootsector2-template-footer.mac; then
+; echo $i
+; fi
+; done
+;
+%undef TMPL_RM
+%undef TMPL_PE16
+%undef TMPL_PE16_32
+%undef TMPL_PE16_V86
+%undef TMPL_PE32
+%undef TMPL_PE32_16
+%undef TMPL_PEV86
+%undef TMPL_PP16
+%undef TMPL_PP16_32
+%undef TMPL_PP16_V86
+%undef TMPL_PP32
+%undef TMPL_PP32_16
+%undef TMPL_PPV86
+%undef TMPL_PAE16
+%undef TMPL_PAE16_32
+%undef TMPL_PAE16_V86
+%undef TMPL_PAE32
+%undef TMPL_PAE32_16
+%undef TMPL_PAEV86
+%undef TMPL_LM16
+%undef TMPL_LM32
+%undef TMPL_LM64
+
+%undef TMPL_CMN_PE
+%undef TMPL_SYS_PE16
+%undef TMPL_SYS_PE32
+%undef TMPL_CMN_PP
+%undef TMPL_SYS_PP16
+%undef TMPL_SYS_PP32
+%undef TMPL_CMN_PAE
+%undef TMPL_SYS_PAE16
+%undef TMPL_SYS_PAE32
+%undef TMPL_CMN_LM
+%undef TMPL_CMN_V86
+%undef TMPL_CMN_R86
+%undef TMPL_CMN_PAGING
+%undef TMPL_CMN_WEIRD
+%undef TMPL_CMN_WEIRD_V86
+
+%undef TMPL_CMN_R86
+
+%undef TMPL_NM
+%undef TMPL_NM_U
+%undef TMPL_FAR_NM
+%undef BS3_CMN_NM
+%undef TMPL_UNDESCORE
+%undef TMPL_MODE_UNAME
+%undef TMPL_MODE_LNAME
+%undef TMPL_MODE_STR
+%undef TMPL_16BIT
+%undef TMPL_32BIT
+%undef TMPL_64BIT
+%undef TMPL_BITS
+%undef TMPL_PTR_DEF
+%undef TMPL_HAVE_BIOS
+%undef TMPL_BEGINCODE
+
+%undef xCB
+%undef xDEF
+%undef xRES
+%undef xPRE
+%undef xSP
+%undef xBP
+%undef xAX
+%undef xBX
+%undef xCX
+%undef xDX
+%undef xDI
+%undef xSI
+%undef xWrtRIP
+
+%undef sCB
+%undef sDEF
+%undef sRES
+%undef sPRE
+%undef sSP
+%undef sBP
+%undef sAX
+%undef sBX
+%undef sCX
+%undef sDX
+%undef sDI
+%undef sSI
+
+%unmacro TONLY16 1+
+%unmacro TONLY32 1+
+%unmacro TONLY64 1+
+%unmacro TNOT16 1+
+%unmacro TNOT32 1+
+%unmacro TNOT64 1+
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.h
new file mode 100644
index 00000000..fa326ab0
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.h
@@ -0,0 +1,532 @@
+/* $Id: bs3kit-template-header.h $ */
+/** @file
+ * BS3Kit header for multi-mode code templates.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include "bs3kit.h"
+
+/** @defgroup grp_bs3kit_tmpl Multi-Mode Code Templates
+ * @ingroup grp_bs3kit
+ *
+ * Multi-mode code templates avoid duplicating code for each of the CPU modes.
+ * Instead the code is compiled multiple times, either via multiple inclusions
+ * into a source files with different mode selectors defined or by multiple
+ * compiler invocations.
+ *
+ * In C/C++ code we're restricted to the compiler target bit count, whereas in
+ * assembly we can do everything in assembler run (with some 64-bit
+ * restrictions, that is).
+ *
+ * Before \#defining the next mode selector and including
+ * bs3kit-template-header.h again, include bs3kit-template-footer.h to undefine
+ * all the previous mode selectors and the macros defined by the header.
+ *
+ * @{
+ */
+
+#ifdef DOXYGEN_RUNNING
+/** @name Template mode selectors.
+ *
+ * Exactly one of these are defined by the file including the
+ * bs3kit-template-header.h header file. When building the code libraries, the
+ * kBuild target defines this.
+ *
+ * @{ */
+# define TMPL_RM /**< real mode. */
+
+# define TMPL_PE16 /**< 16-bit protected mode kernel+tss, running 16-bit code, unpaged. */
+# define TMPL_PE16_32 /**< 16-bit protected mode kernel+tss, running 32-bit code, unpaged. */
+# define TMPL_PE16_V86 /**< 16-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged. */
+# define TMPL_PE32 /**< 32-bit protected mode kernel+tss, running 32-bit code, unpaged. */
+# define TMPL_PE32_16 /**< 32-bit protected mode kernel+tss, running 16-bit code, unpaged. */
+# define TMPL_PEV86 /**< 32-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged. */
+
+# define TMPL_PP16 /**< 16-bit protected mode kernel+tss, running 16-bit code, paged. */
+# define TMPL_PP16_32 /**< 16-bit protected mode kernel+tss, running 32-bit code, paged. */
+# define TMPL_PP16_V86 /**< 16-bit protected mode kernel+tss, running virtual 8086 mode code, paged. */
+# define TMPL_PP32 /**< 32-bit protected mode kernel+tss, running 32-bit code, paged. */
+# define TMPL_PP32_16 /**< 32-bit protected mode kernel+tss, running 16-bit code, paged. */
+# define TMPL_PPV86 /**< 32-bit protected mode kernel+tss, running virtual 8086 mode code, paged. */
+
+# define TMPL_PAE16 /**< 16-bit protected mode kernel+tss, running 16-bit code, PAE paging. */
+# define TMPL_PAE16_32 /**< 16-bit protected mode kernel+tss, running 32-bit code, PAE paging. */
+# define TMPL_PAE16_V86 /**< 16-bit protected mode kernel+tss, running virtual 8086 mode code, PAE paging. */
+# define TMPL_PAE32 /**< 32-bit protected mode kernel+tss, running 32-bit code, PAE paging. */
+# define TMPL_PAE32_16 /**< 32-bit protected mode kernel+tss, running 16-bit code, PAE paging. */
+# define TMPL_PAEV86 /**< 32-bit protected mode kernel+tss, running virtual 8086 mode code, PAE paging. */
+
+# define TMPL_LM16 /**< 16-bit long mode (paged), kernel+tss always 64-bit. */
+# define TMPL_LM32 /**< 32-bit long mode (paged), kernel+tss always 64-bit. */
+# define TMPL_LM64 /**< 64-bit long mode (paged), kernel+tss always 64-bit. */
+/** @} */
+
+/** @name Derived Indicators
+ * @{ */
+# define TMPL_CMN_PE /**< TMPL_PE16 | TMPL_PE16_32 | TMPL_PE16_V86 | TMPL_PE32 | TMPL_PE32_16 | TMPL_PEV86 */
+# define TMPL_SYS_PE16 /**< TMPL_PE16 | TMPL_PE16_32 | TMPL_PE16_V86 */
+# define TMPL_SYS_PE32 /**< TMPL_PE32 | TMPL_PE32_16 | TMPL_PEV86 */
+# define TMPL_CMN_PP /**< TMPL_PP16 | TMPL_PP16_32 | TMPL_PP16_V86 | TMPL_PP32 | TMPL_PP32_16 | TMPL_PPV86 */
+# define TMPL_SYS_PP16 /**< TMPL_PP16 | TMPL_PP16_32 | TMPL_PP16_V86 */
+# define TMPL_SYS_PP32 /**< TMPL_PP32 | TMPL_PP32_16 | TMPL_PPV86 */
+# define TMPL_CMN_PAE /**< TMPL_PAE16 | TMPL_PAE16_32 | TMPL_PAE16_V86 | TMPL_PAE32 | TMPL_PAE32_16 | TMPL_PAEV86 */
+# define TMPL_SYS_PAE16 /**< TMPL_PAE16 | TMPL_PAE16_32 | TMPL_PAE16_V86 */
+# define TMPL_SYS_PAE32 /**< TMPL_PAE32 | TMPL_PAE32_16 | TMPL_PAEV86 */
+# define TMPL_CMN_LM /**< TMPL_LM16 | TMPL_LM32 | TMPL_LM64 */
+# define TMPL_CMN_V86 /**< TMPL_PEV86 | TMPL_PE16_V86 | TMPL_PPV86 | TMPL_PP16_V86 | TMPL_PAEV86 | TMPL_PAE16_V86 */
+# define TMPL_CMN_R86 /**< TMPL_CMN_V86 | TMPL_RM */
+# define TMPL_CMN_PAGING /**< TMPL_CMN_PP | TMPL_CMN_PAE | TMPL_CMN_LM */
+# define TMPL_CMN_WEIRD /**< TMPL_PE16_32 | TMPL_PE32_16 | TMPL_PP16_32 | TMPL_PP32_16 | TMPL_PAE16_32 | TMPL_PAE32_16 | TMPL_CMN_WEIRD_V86 */
+# define TMPL_CMN_WEIRD_V86 /**< TMPL_PE16_V86 | TMPL_PP16_V86 | TMPL_PAE16_V86 */
+/** @} */
+
+/** @def TMPL_NM
+ * Name mangling macro for the current mode.
+ *
+ * Example: TMPL_NM(PrintChr)
+ *
+ * @param Name The function or variable name to mangle.
+ * @sa #TMPL_FAR_NM, #BS3_CMN_NM, #BS3_CMN_FAR_NM
+ */
+# define TMPL_NM(Name) RT_CONCAT(Name,_mode)
+
+/** @def TMPL_FAR_NM
+ * Name mangling macro for the current mode into a far function name.
+ *
+ * In 32-bit and 64-bit code this does not differ from #TMPL_NM.
+ *
+ * Example: TMPL_FAR_NM(PrintChr)
+ *
+ * @param Name The function or variable name to mangle.
+ * @sa #TMPL_NM, #BS3_CMN_FAR_NM, #BS3_CMN_NM
+ */
+# define TMPL_FAR_NM(Name) RT_CONCAT3(Name,_mode,_far)
+
+/** @def TMPL_MODE_STR
+ * Short mode description. */
+# define TMPL_MODE_STR
+
+/** @def TMPL_HAVE_BIOS
+ * Indicates that we have direct access to the BIOS (only in real mode). */
+# define TMPL_HAVE_BIOS
+
+
+/** @name For ASM compatability
+ * @{ */
+/** @def TMPL_16BIT
+ * For ASM compatibility - please use ARCH_BITS == 16. */
+# define TMPL_16BIT
+/** @def TMPL_32BIT
+ * For ASM compatibility - please use ARCH_BITS == 32. */
+# define TMPL_32BIT
+/** @def TMPL_64BIT
+ * For ASM compatibility - please use ARCH_BITS == 64. */
+# define TMPL_64BIT
+
+/** @def TMPL_BITS
+ * For ASM compatibility - please use ARCH_BITS instead. */
+# define TMPL_BITS ARCH_BITS
+/** @} */
+
+#else /* !DOXYGEN_RUNNING */
+
+//#undef BS3_CMN_NM
+//#undef BS3_CMN_FAR_NM
+
+
+/*
+ * Convert TMPL_XXX to TMPL_MODE.
+ */
+#ifndef TMPL_MODE
+# ifdef TMPL_RM
+# define TMPL_MODE BS3_MODE_RM
+# elif defined(TMPL_PE16)
+# define TMPL_MODE BS3_MODE_PE16
+# elif defined(TMPL_PE16_32)
+# define TMPL_MODE BS3_MODE_PE16_32
+# elif defined(TMPL_PE16_V86)
+# define TMPL_MODE BS3_MODE_PE16_V86
+# elif defined(TMPL_PE32)
+# define TMPL_MODE BS3_MODE_PE32
+# elif defined(TMPL_PE32_16)
+# define TMPL_MODE BS3_MODE_PE32_16
+# elif defined(TMPL_PEV86)
+# define TMPL_MODE BS3_MODE_PEV86
+# elif defined(TMPL_PP16)
+# define TMPL_MODE BS3_MODE_PP16
+# elif defined(TMPL_PP16_32)
+# define TMPL_MODE BS3_MODE_PP16_32
+# elif defined(TMPL_PP16_V86)
+# define TMPL_MODE BS3_MODE_PP16_V86
+# elif defined(TMPL_PP32)
+# define TMPL_MODE BS3_MODE_PP32
+# elif defined(TMPL_PP32_16)
+# define TMPL_MODE BS3_MODE_PP32_16
+# elif defined(TMPL_PPV86)
+# define TMPL_MODE BS3_MODE_PPV86
+# elif defined(TMPL_PAE16)
+# define TMPL_MODE BS3_MODE_PAE16
+# elif defined(TMPL_PAE16_32)
+# define TMPL_MODE BS3_MODE_PAE16_32
+# elif defined(TMPL_PAE16_V86)
+# define TMPL_MODE BS3_MODE_PAE16_V86
+# elif defined(TMPL_PAE32)
+# define TMPL_MODE BS3_MODE_PAE32
+# elif defined(TMPL_PAE32_16)
+# define TMPL_MODE BS3_MODE_PAE32_16
+# elif defined(TMPL_PAEV86)
+# define TMPL_MODE BS3_MODE_PAEV86
+# elif defined(TMPL_LM16)
+# define TMPL_MODE BS3_MODE_LM16
+# elif defined(TMPL_LM32)
+# define TMPL_MODE BS3_MODE_LM32
+# elif defined(TMPL_LM64)
+# define TMPL_MODE BS3_MODE_LM64
+# else
+# error "Unable to to figure out the template mode."
+# endif
+#endif
+
+
+/*
+ * Check the code bitness and set derived defines.
+ */
+#if (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_16
+# if ARCH_BITS != 16
+# error "BS3_MODE_CODE_16 requires ARCH_BITS to be 16."
+# endif
+# define TMPL_16BIT
+# define TMPL_BITS 16
+# define TMPL_UNDERSCORE _
+//# define BS3_CMN_NM(Name) RT_CONCAT(Name,_c16)
+//# define BS3_CMN_FAR_NM(Name) RT_CONCAT(Name,_f16)
+
+
+#elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_32
+# if ARCH_BITS != 32
+# error "BS3_MODE_CODE_32 requires ARCH_BITS to be 32."
+# endif
+# define TMPL_32BIT
+# define TMPL_BITS 32
+# define TMPL_UNDERSCORE _
+//# define BS3_CMN_NM(Name) RT_CONCAT(Name,_c32)
+//# define BS3_CMN_FAR_NM(a_Name) RT_CONCAT(Name,_c32)
+
+#elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86
+# if ARCH_BITS != 16
+# error "BS3_MODE_CODE_V86 requires ARCH_BITS to be 16."
+# endif
+# define TMPL_16BIT
+# define TMPL_BITS 16
+# define TMPL_UNDERSCORE _
+//# define BS3_CMN_NM(Name) RT_CONCAT(Name,_c16)
+//# define BS3_CMN_FAR_NM(Name) RT_CONCAT(Name,_f16)
+# define TMPL_CMN_R86
+# define TMPL_CMN_V86
+
+#elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_64
+# if ARCH_BITS != 64
+# error "BS3_MODE_CODE_64 requires ARCH_BITS to be 64."
+# endif
+# define TMPL_64BIT
+# define TMPL_BITS 64
+# define TMPL_UNDERSCORE
+//# define BS3_CMN_NM(Name) RT_CONCAT(Name,_c64)
+//# define BS3_CMN_FAR_NM(a_Name) RT_CONCAT(Name,_c64)
+
+#else
+# error "Invalid TMPL_MODE value!"
+#endif
+
+
+/*
+ * Check the system specific mask and set derived values.
+ */
+#if (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_RM
+# define TMPL_HAVE_BIOS
+# define TMPL_CMN_R86
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE16
+# define TMPL_SYS_PE16
+# define TMPL_CMN_PE
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE32
+# define TMPL_SYS_PE32
+# define TMPL_CMN_PE
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP16
+# define TMPL_SYS_PP16
+# define TMPL_CMN_PP
+# define TMPL_CMN_PAGING
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP32
+# define TMPL_SYS_PP32
+# define TMPL_CMN_PP
+# define TMPL_CMN_PAGING
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE16
+# define TMPL_SYS_PAE16
+# define TMPL_CMN_PAE
+# define TMPL_CMN_PAGING
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE32
+# define TMPL_SYS_PAE32
+# define TMPL_CMN_PAE
+# define TMPL_CMN_PAGING
+
+#elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_LM
+# define TMPL_SYS_LM
+# define TMPL_CMN_LM
+# define TMPL_CMN_PAGING
+
+#else
+# error "Invalid TMPL_MODE value!"
+#endif
+
+
+/*
+ * Mode specific stuff.
+ */
+#if TMPL_MODE == BS3_MODE_RM
+# define TMPL_RM 1
+# define TMPL_MODE_STR "real mode"
+# define TMPL_NM(Name) RT_CONCAT(Name,_rm)
+# define TMPL_MODE_LNAME rm
+# define TMPL_MODE_UNAME RM
+
+
+#elif TMPL_MODE == BS3_MODE_PE16
+# define TMPL_PE16 1
+# define TMPL_MODE_STR "16-bit prot, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pe16)
+# define TMPL_MODE_LNAME pe16
+# define TMPL_MODE_UNAME PE16
+
+#elif TMPL_MODE == BS3_MODE_PE16_32
+# define TMPL_PE16_32 1
+# define TMPL_MODE_STR "16-bit prot, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pe16_32)
+# define TMPL_MODE_LNAME pe16_32
+# define TMPL_MODE_UNAME PE16_32
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PE16_V86
+# define TMPL_PE16_V86 1
+# define TMPL_MODE_STR "16-bit prot, v8086"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pe16_v86)
+# define TMPL_MODE_LNAME pe16_v86
+# define TMPL_MODE_UNAME PE16_v86
+# define TMPL_CMN_WEIRD
+# define TMPL_CMN_WEIRD_V86
+
+
+#elif TMPL_MODE == BS3_MODE_PE32
+# define TMPL_PE32 1
+# define TMPL_MODE_STR "32-bit prot, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pe32)
+# define TMPL_MODE_LNAME pe32
+# define TMPL_MODE_UNAME PE32
+
+#elif TMPL_MODE == BS3_MODE_PE32_16
+# define TMPL_PE32_16 1
+# define TMPL_MODE_STR "32-bit prot, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pe32_16)
+# define TMPL_MODE_LNAME pe32_16
+# define TMPL_MODE_UNAME PE32_16
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PEV86
+# define TMPL_PEV86 1
+# define TMPL_MODE_STR "32-bit prot, v8086"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pev86)
+# define TMPL_MODE_LNAME pev86
+# define TMPL_MODE_UNAME PEV86
+
+
+#elif TMPL_MODE == BS3_MODE_PP16
+# define TMPL_PP16 1
+# define TMPL_MODE_STR "16-bit paged, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pp16)
+# define TMPL_MODE_LNAME pp16
+# define TMPL_MODE_UNAME PP16
+
+#elif TMPL_MODE == BS3_MODE_PP16_32
+# define TMPL_PP16_32 1
+# define TMPL_MODE_STR "16-bit paged, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pp16_32)
+# define TMPL_MODE_LNAME pp16_32
+# define TMPL_MODE_UNAME PP16_32
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PP16_V86
+# define TMPL_PP16_V86 1
+# define TMPL_MODE_STR "16-bit paged, v8086"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pp16_v86)
+# define TMPL_MODE_LNAME pp16_v86
+# define TMPL_MODE_UNAME PP16_v86
+# define TMPL_CMN_WEIRD
+# define TMPL_CMN_WEIRD_V86
+
+
+#elif TMPL_MODE == BS3_MODE_PP32
+# define TMPL_PP32 1
+# define TMPL_MODE_STR "32-bit paged, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pp32)
+# define TMPL_MODE_LNAME pp32
+# define TMPL_MODE_UNAME PP32
+
+#elif TMPL_MODE == BS3_MODE_PP32_16
+# define TMPL_PP32_16 1
+# define TMPL_MODE_STR "32-bit paged, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pp32_16)
+# define TMPL_MODE_LNAME pp32_16
+# define TMPL_MODE_UNAME PP32_16
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PPV86
+# define TMPL_PPV86 1
+# define TMPL_MODE_STR "32-bit paged, v8086"
+# define TMPL_NM(Name) RT_CONCAT(Name,_ppv86)
+# define TMPL_MODE_LNAME ppv86
+# define TMPL_MODE_UNAME PPV86
+
+
+#elif TMPL_MODE == BS3_MODE_PAE16
+# define TMPL_PAE16 1
+# define TMPL_MODE_STR "16-bit pae, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pae16)
+# define TMPL_MODE_LNAME pae16
+# define TMPL_MODE_UNAME PAE16
+
+#elif TMPL_MODE == BS3_MODE_PAE16_32
+# define TMPL_PAE16_32 1
+# define TMPL_MODE_STR "16-bit pae, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pae16_32)
+# define TMPL_MODE_LNAME pae16_32
+# define TMPL_MODE_UNAME PAE16_32
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PAE16_V86
+# define TMPL_PAE16_V86 1
+# define TMPL_MODE_STR "16-bit pae, v8086"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pae16_v86)
+# define TMPL_MODE_LNAME pae16_v86
+# define TMPL_MODE_UNAME PAE16_v86
+# define TMPL_CMN_WEIRD
+# define TMPL_CMN_WEIRD_V86
+
+
+#elif TMPL_MODE == BS3_MODE_PAE32
+# define TMPL_PAE32 1
+# define TMPL_MODE_STR "32-bit pae, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pae32)
+# define TMPL_MODE_LNAME pae32
+# define TMPL_MODE_UNAME PAE32
+
+#elif TMPL_MODE == BS3_MODE_PAE32_16
+# define TMPL_PAE32_16 1
+# define TMPL_MODE_STR "32-bit pae, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_pae32_16)
+# define TMPL_MODE_LNAME pae32_16
+# define TMPL_MODE_UNAME PAE32_16
+# define TMPL_CMN_WEIRD
+
+#elif TMPL_MODE == BS3_MODE_PAEV86
+# define TMPL_PAEV86 1
+# define TMPL_MODE_STR "32-bit pae, v8086 pae"
+# define TMPL_NM(Name) RT_CONCAT(Name,_paev86)
+# define TMPL_MODE_LNAME paev86
+# define TMPL_MODE_UNAME PAEV86
+
+
+#elif TMPL_MODE == BS3_MODE_LM16
+# define TMPL_LM16 1
+# define TMPL_MODE_STR "long, 16-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_lm16)
+# define TMPL_MODE_LNAME lm16
+# define TMPL_MODE_UNAME LM16
+
+#elif TMPL_MODE == BS3_MODE_LM32
+# define TMPL_LM32 1
+# define TMPL_MODE_STR "long, 32-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_lm32)
+# define TMPL_MODE_LNAME lm32
+# define TMPL_MODE_UNAME LM32
+
+#elif TMPL_MODE == BS3_MODE_LM64
+# define TMPL_LM64 1
+# define TMPL_MODE_STR "long, 64-bit"
+# define TMPL_NM(Name) RT_CONCAT(Name,_lm64)
+# define TMPL_MODE_LNAME lm64
+# define TMPL_MODE_UNAME LM64
+
+#else
+# error "Invalid TMPL_MODE value!!"
+#endif
+
+
+#if TMPL_MODE & (BS3_MODE_CODE_16 | BS3_MODE_CODE_V86)
+# define TMPL_FAR_NM(Name) RT_CONCAT3(TMPL_NM(Name),_f,ar) /* _far and far may be #defined already. */
+#else
+# define TMPL_FAR_NM(Name) TMPL_NM(Name)
+#endif
+
+
+/** @def BS3_MODE_DEF
+ * Macro for defining a mode specific function.
+ *
+ * This makes 16-bit mode functions far, while 32-bit and 64-bit are near.
+ * You need to update the make file to generate near->far wrappers in most
+ * cases.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ *
+ * @sa BS3_MODE_PROTO
+ */
+#if ARCH_BITS == 16
+# define BS3_MODE_DEF(a_RetType, a_Name, a_Params) BS3_DECL_FAR(a_RetType) TMPL_FAR_NM(a_Name) a_Params
+#else
+# define BS3_MODE_DEF(a_RetType, a_Name, a_Params) BS3_DECL_NEAR(a_RetType) TMPL_NM(a_Name) a_Params
+#endif
+
+
+
+#ifndef TMPL_MODE_STR
+# error "internal error"
+#endif
+
+#endif /* !DOXYGEN_RUNNING */
+/** @} */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.mac b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.mac
new file mode 100644
index 00000000..ae9acdd9
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit-template-header.mac
@@ -0,0 +1,532 @@
+; $Id: bs3kit-template-header.mac $
+;; @file
+; BS3Kit header for multi-mode code templates.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "bs3kit.mac"
+
+;
+; Check and expand the mode defines.
+; One of the following must be defined:
+; - TMPL_RM - real mode.
+; - TMPL_PE16 - 16-bit protected mode, unpaged.
+; - TMPL_PE32 - 32-bit protected mode, unpaged.
+; - TMPL_PEV86 - virtual 8086 mode under protected mode, unpaged.
+; - TMPL_PP16 - 16-bit protected mode, paged.
+; - TMPL_PP32 - 32-bit protected mode, paged.
+; - TMPL_PPV86 - virtual 8086 mode under protected mode, paged.
+; - TMPL_PAE16 - 16-bit protected mode with PAE (paged).
+; - TMPL_PAE32 - 16-bit protected mode with PAE (paged).
+; - TMPL_PAEV86- virtual 8086 mode under protected mode with PAE (paged).
+; - TMPL_LM16 - 16-bit long mode (paged).
+; - TMPL_LM32 - 32-bit long mode (paged).
+; - TMPL_LM64 - 64-bit long mode (paged).
+;
+; Derived indicators:
+; - TMPL_CMN_PE = TMPL_PE16 | TMPL_PE32 | TMPL_PEV86
+; - TMPL_CMN_PP = TMPL_PP16 | TMPL_PP32 | TMPL_PPV86
+; - TMPL_CMN_PAE = TMPL_PAE16 | TMPL_PAE32 | TMPL_PAEV86
+; - TMPL_CMN_LM = TMPL_LM16 | TMPL_LM32 | TMPL_LM64
+; - TMPL_CMN_V86 = TMPL_PEV86 | TMPL_PPV86 | TMPL_PAEV86
+; - TMPL_CMN_R86 = TMPL_CMN_V86 | TMPL_RM
+; - TMPL_CMN_PAGING = TMPL_CMN_PP | TMPL_CMN_PAE | TMPL_CMN_LM
+;
+
+
+;
+; Convert TMPL_XXX to TMPL_MODE.
+;
+%ifndef TMPL_MODE
+ %ifdef TMPL_RM
+ %define TMPL_MODE BS3_MODE_RM
+ %elifdef TMPL_PE16
+ %define TMPL_MODE BS3_MODE_PE16
+ %elifdef TMPL_PE16_32
+ %define TMPL_MODE BS3_MODE_PE16_32
+ %elifdef TMPL_PE16_V86
+ %define TMPL_MODE BS3_MODE_PE16_V86
+ %elifdef TMPL_PE32
+ %define TMPL_MODE BS3_MODE_PE32
+ %elifdef TMPL_PE32_16
+ %define TMPL_MODE BS3_MODE_PE32_16
+ %elifdef TMPL_PEV86
+ %define TMPL_MODE BS3_MODE_PEV86
+ %elifdef TMPL_PP16
+ %define TMPL_MODE BS3_MODE_PP16
+ %elifdef TMPL_PP16_32
+ %define TMPL_MODE BS3_MODE_PP16_32
+ %elifdef TMPL_PP16_V86
+ %define TMPL_MODE BS3_MODE_PP16_V86
+ %elifdef TMPL_PP32
+ %define TMPL_MODE BS3_MODE_PP32
+ %elifdef TMPL_PP32_16
+ %define TMPL_MODE BS3_MODE_PP32_16
+ %elifdef TMPL_PPV86
+ %define TMPL_MODE BS3_MODE_PPV86
+ %elifdef TMPL_PAE16
+ %define TMPL_MODE BS3_MODE_PAE16
+ %elifdef TMPL_PAE16_32
+ %define TMPL_MODE BS3_MODE_PAE16_32
+ %elifdef TMPL_PAE16_V86
+ %define TMPL_MODE BS3_MODE_PAE16_V86
+ %elifdef TMPL_PAE32
+ %define TMPL_MODE BS3_MODE_PAE32
+ %elifdef TMPL_PAE32_16
+ %define TMPL_MODE BS3_MODE_PAE32_16
+ %elifdef TMPL_PAEV86
+ %define TMPL_MODE BS3_MODE_PAEV86
+ %elifdef TMPL_LM16
+ %define TMPL_MODE BS3_MODE_LM16
+ %elifdef TMPL_LM32
+ %define TMPL_MODE BS3_MODE_LM32
+ %elifdef TMPL_LM64
+ %define TMPL_MODE BS3_MODE_LM64
+ %else
+ %error "Unable to to figure out the template mode."
+ %endif
+%endif
+
+;
+; Check the code bitness and set TMPL_XXBITS, TMPL_BITS, BS3_CMN_NM
+;
+%if (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_16
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_PTR_DEF dw
+ %define TMPL_UNDERSCORE _
+ %define BS3_CMN_NM(Name) _ %+ Name %+ _c16
+
+%elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_32
+ %define TMPL_32BIT
+ %define TMPL_BITS 32
+ %define TMPL_PTR_DEF dd
+ %define TMPL_UNDERSCORE _
+ %define BS3_CMN_NM(Name) _ %+ Name %+ _c32
+
+%elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86
+ %define TMPL_16BIT
+ %define TMPL_BITS 16
+ %define TMPL_UNDERSCORE _
+ %define BS3_CMN_NM(Name) _ %+ Name %+ _c16
+ %define TMPL_CMN_R86
+ %define TMPL_CMN_V86
+
+%elif (TMPL_MODE & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_64
+ %define TMPL_64BIT
+ %define TMPL_BITS 64
+ %define TMPL_PTR_DEF dq
+ %define TMPL_UNDERSCORE _
+ %define BS3_CMN_NM(Name) _ %+ Name %+ _c64
+
+%else
+ %error "Invalid TMPL_MODE value!"
+%endif
+
+;
+; Check the system specific mask and set derived values.
+;
+%if (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_RM
+ %define TMPL_HAVE_BIOS
+ %define TMPL_CMN_R86
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE16
+ %define TMPL_SYS_PE16
+ %define TMPL_CMN_PE
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE32
+ %define TMPL_SYS_PE32
+ %define TMPL_CMN_PE
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP16
+ %define TMPL_SYS_PP16
+ %define TMPL_CMN_PP
+ %define TMPL_CMN_PAGING
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP32
+ %define TMPL_SYS_PP32
+ %define TMPL_CMN_PP
+ %define TMPL_CMN_PAGING
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE16
+ %define TMPL_SYS_PAE16
+ %define TMPL_CMN_PAE
+ %define TMPL_CMN_PAGING
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE32
+ %define TMPL_SYS_PAE32
+ %define TMPL_CMN_PAE
+ %define TMPL_CMN_PAGING
+
+%elif (TMPL_MODE & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_LM
+ %define TMPL_SYS_LM
+ %define TMPL_CMN_LM
+ %define TMPL_CMN_PAGING
+
+%else
+ %error "Invalid TMPL_MODE value!"
+%endif
+
+
+;
+; Mode specific stuff.
+;
+%if TMPL_MODE == BS3_MODE_RM
+ %define TMPL_RM
+ %define TMPL_MODE_STR "real mode"
+ %define TMPL_NM(Name) _ %+ Name %+ _rm
+ %define TMPL_MODE_LNAME rm
+ %define TMPL_MODE_UNAME RM
+
+
+%elif TMPL_MODE == BS3_MODE_PE16
+ %define TMPL_PE16
+ %define TMPL_MODE_STR "16-bit prot, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pe16
+ %define TMPL_MODE_LNAME pe16
+ %define TMPL_MODE_UNAME PE16
+
+%elif TMPL_MODE == BS3_MODE_PE16_32
+ %define TMPL_PE16_32
+ %define TMPL_MODE_STR "16-bit prot, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pe16_32
+ %define TMPL_MODE_LNAME pe16_32
+ %define TMPL_MODE_UNAME PE16_32
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PE16_V86
+ %define TMPL_PE16_V86
+ %define TMPL_MODE_STR "16-bit prot, v8086"
+ %define TMPL_NM(Name) _ %+ Name %+ _pe16_v86
+ %define TMPL_MODE_LNAME pe16_v86
+ %define TMPL_MODE_UNAME PE16_v86
+ %define TMPL_CMN_WEIRD
+ %define TMPL_CMN_WEIRD_V86
+
+
+%elif TMPL_MODE == BS3_MODE_PE32
+ %define TMPL_PE32
+ %define TMPL_MODE_STR "32-bit prot, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pe32
+ %define TMPL_MODE_LNAME pe32
+ %define TMPL_MODE_UNAME PE32
+
+%elif TMPL_MODE == BS3_MODE_PE32_16
+ %define TMPL_PE32_16
+ %define TMPL_MODE_STR "32-bit prot, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pe32_16
+ %define TMPL_MODE_LNAME pe32_16
+ %define TMPL_MODE_UNAME PE32_16
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PEV86
+ %define TMPL_PEV86
+ %define TMPL_MODE_STR "32-bit prot, v8086"
+ %define TMPL_NM(Name) _ %+ Name %+ _pev86
+ %define TMPL_MODE_LNAME pev86
+ %define TMPL_MODE_UNAME PEV86
+
+
+%elif TMPL_MODE == BS3_MODE_PP16
+ %define TMPL_PP16
+ %define TMPL_MODE_STR "16-bit paged, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pp16
+ %define TMPL_MODE_LNAME pp16
+ %define TMPL_MODE_UNAME PP16
+
+%elif TMPL_MODE == BS3_MODE_PP16_32
+ %define TMPL_PP16_32
+ %define TMPL_MODE_STR "16-bit paged, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pp16_32
+ %define TMPL_MODE_LNAME pp16_32
+ %define TMPL_MODE_UNAME PP16_32
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PP16_V86
+ %define TMPL_PP16_V86
+ %define TMPL_MODE_STR "16-bit paged, v8086"
+ %define TMPL_NM(Name) _ %+ Name %+ _pp16_v86
+ %define TMPL_MODE_LNAME pp16_v86
+ %define TMPL_MODE_UNAME PP16_v86
+ %define TMPL_CMN_WEIRD
+ %define TMPL_CMN_WEIRD_V86
+
+
+%elif TMPL_MODE == BS3_MODE_PP32
+ %define TMPL_PP32
+ %define TMPL_MODE_STR "32-bit paged, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pp32
+ %define TMPL_MODE_LNAME pp32
+ %define TMPL_MODE_UNAME PP32
+
+%elif TMPL_MODE == BS3_MODE_PP32_16
+ %define TMPL_PP32_16
+ %define TMPL_MODE_STR "32-bit paged, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pp32_16
+ %define TMPL_MODE_LNAME pp32_16
+ %define TMPL_MODE_UNAME PP32_16
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PPV86
+ %define TMPL_PPV86
+ %define TMPL_MODE_STR "32-bit paged, v8086"
+ %define TMPL_NM(Name) _ %+ Name %+ _ppv86
+ %define TMPL_MODE_LNAME ppv86
+ %define TMPL_MODE_UNAME PPV86
+
+
+%elif TMPL_MODE == BS3_MODE_PAE16
+ %define TMPL_PAE16
+ %define TMPL_MODE_STR "16-bit pae, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pae16
+ %define TMPL_MODE_LNAME pae16
+ %define TMPL_MODE_UNAME PAE16
+
+%elif TMPL_MODE == BS3_MODE_PAE16_32
+ %define TMPL_PAE16_32
+ %define TMPL_MODE_STR "16-bit pae, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pae16_32
+ %define TMPL_MODE_LNAME pae16_32
+ %define TMPL_MODE_UNAME PAE16_32
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PAE16_V86
+ %define TMPL_PAE16_V86
+ %define TMPL_MODE_STR "16-bit pae, v8086"
+ %define TMPL_NM(Name) _ %+ Name %+ _pae16_v86
+ %define TMPL_MODE_LNAME pae16_v86
+ %define TMPL_MODE_UNAME PAE16_v86
+ %define TMPL_CMN_WEIRD
+ %define TMPL_CMN_WEIRD_V86
+
+
+%elif TMPL_MODE == BS3_MODE_PAE32
+ %define TMPL_PAE32
+ %define TMPL_MODE_STR "32-bit pae, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pae32
+ %define TMPL_MODE_LNAME pae32
+ %define TMPL_MODE_UNAME PAE32
+
+%elif TMPL_MODE == BS3_MODE_PAE32_16
+ %define TMPL_PAE32_16
+ %define TMPL_MODE_STR "32-bit pae, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _pae32_16
+ %define TMPL_MODE_LNAME pae32_16
+ %define TMPL_MODE_UNAME PAE32_16
+ %define TMPL_CMN_WEIRD
+
+%elif TMPL_MODE == BS3_MODE_PAEV86
+ %define TMPL_PAEV86
+ %define TMPL_MODE_STR "32-bit pae, v8086 pae"
+ %define TMPL_NM(Name) _ %+ Name %+ _paev86
+ %define TMPL_MODE_LNAME paev86
+ %define TMPL_MODE_UNAME PAEV86
+
+
+%elif TMPL_MODE == BS3_MODE_LM16
+ %define TMPL_LM16
+ %define TMPL_MODE_STR "long, 16-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _lm16
+ %define TMPL_MODE_LNAME lm16
+ %define TMPL_MODE_UNAME LM16
+
+%elif TMPL_MODE == BS3_MODE_LM32
+ %define TMPL_LM32
+ %define TMPL_MODE_STR "long, 32-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _lm32
+ %define TMPL_MODE_LNAME lm32
+ %define TMPL_MODE_UNAME LM32
+
+%elif TMPL_MODE == BS3_MODE_LM64
+ %define TMPL_LM64
+ %define TMPL_MODE_STR "long, 64-bit"
+ %define TMPL_NM(Name) _ %+ Name %+ _lm64
+ %define TMPL_MODE_LNAME lm64
+ %define TMPL_MODE_UNAME LM64
+
+%else
+ %error "Invalid TMPL_MODE value!!"
+%endif
+
+%ifnidn TMPL_UNDERSCORE,_; RT_CONCAT3 doesn't work with TMPL_UNDERSCORE being empty. duh.
+ %ifidn RT_CONCAT(TestName_,TMPL_MODE_LNAME),TMPL_NM(TestName)
+ %else
+ %error internal error: RT_CONCAT(TestName_,TMPL_MODE_LNAME) vs TMPL_NM(TestName)
+ %endif
+%else
+ %ifidn RT_CONCAT3(TMPL_UNDERSCORE,TestName_,TMPL_MODE_LNAME),TMPL_NM(TestName)
+ %else
+ %error internal error: RT_CONCAT3(TMPL_UNDERSCORE,TestName_,TMPL_MODE_LNAME) vs TMPL_NM(TestName)
+ %endif
+%endif
+
+; TMPL_NM version with uppercased suffix and no underscore separating them.
+%define TMPL_NM_U(Name) TMPL_UNDERSCORE %+ Name %+ TMPL_MODE_UNAME
+
+; TMPL_FAR_NM
+%if TMPL_MODE & (BS3_MODE_CODE_16 | BS3_MODE_CODE_V86)
+ %define TMPL_FAR_NM(Name) TMPL_NM(Name) %+ _far
+%else
+ %define TMPL_FAR_NM(Name) TMPL_NM(Name)
+%endif
+
+
+;; @def TMPL_WRT_FLAT
+; WRT flat when not in 16-bit modes.
+;
+%ifdef TMPL_16BIT
+ %define TMPL_WRT_FLAT
+%else
+ %define TMPL_WRT_FLAT wrt FLAT
+%endif
+
+;; @def TMPL_WRT_DATA16_OR_FLAT
+; WRT DATA16 in 16-bit mode, WRT FLAT in 32- and 64-bit modes.
+; This is important when accessing global variables.
+;
+%ifdef TMPL_16BIT
+ %define TMPL_WRT_DATA16_OR_FLAT wrt BS3KIT_GRPNM_DATA16
+%else
+ %define TMPL_WRT_DATA16_OR_FLAT wrt FLAT
+%endif
+
+;; @def TMPL_DATA16_WRT
+; WRT DATA16 in 16-bit mode, WRT FLAT in 32- and 64-bit modes.
+; This is important when accessing global variables.
+;
+%if TMPL_BITS == 16
+ %define TMPL_DATA16_WRT(a_Var) a_Var wrt BS3KIT_GRPNM_DATA16
+%elif TMPL_BITS == 32
+ %define TMPL_DATA16_WRT(a_Var) a_Var wrt FLAT
+%elif TMPL_BITS == 64
+ %define TMPL_DATA16_WRT(a_Var) rel a_Var wrt FLAT
+%else
+ %error TMPL_BITS
+%endif
+
+;; @def TMPL_WRT_SYSTEM16_OR_FLAT
+; WRT BS3SYSTEM16 in 16-bit mode, WRT FLAT in 32- and 64-bit modes.
+; This is important when accessing global variables in the BS3SYSTEM16 segment.
+%ifdef TMPL_16BIT
+ %define TMPL_WRT_SYSTEM16_OR_FLAT wrt BS3SYSTEM16
+%else
+ %define TMPL_WRT_SYSTEM16_OR_FLAT wrt FLAT
+%endif
+
+;; @def TONLY16
+; Version of BONLY16 that follows the code template.
+; Like BONLY16 this normally goes in column 1.
+%if TMPL_BITS == 16
+ %macro TONLY16 1+
+ %1
+ %endmacro
+%else
+ %macro TONLY16 1+
+ %endmacro
+%endif
+
+;; @def TONLY32
+; Version of BONLY32 that follows the code template.
+; Like BONLY32 this normally goes in column 1.
+%if TMPL_BITS == 32
+ %macro TONLY32 1+
+ %1
+ %endmacro
+%else
+ %macro TONLY32 1+
+ %endmacro
+%endif
+
+;; @def TONLY64
+; Version of BONLY64 that follows the code template.
+; Like BONLY64 this normally goes in column 1.
+%if TMPL_BITS == 64
+ %macro TONLY64 1+
+ %1
+ %endmacro
+%else
+ %macro TONLY64 1+
+ %endmacro
+%endif
+
+;; @def TNOT16
+; Version of BNOT16 that follows the code template.
+; Like BNOT16 this normally goes in column 1.
+%if TMPL_BITS == 16
+ %macro TNOT16 1+
+ %endmacro
+%else
+ %macro TNOT16 1+
+ %1
+ %endmacro
+%endif
+
+;; @def TNOT32
+; Version of BNOT32 that follows the code template.
+; Like BNOT32 this normally goes in column 1.
+%if TMPL_BITS == 32
+ %macro TNOT32 1+
+ %endmacro
+%else
+ %macro TNOT32 1+
+ %1
+ %endmacro
+%endif
+
+;; @def TNOT64
+; Version of BNOT64 that follows the code template.
+; Like BNOT64 this normally goes in column 1.
+%if TMPL_BITS == 64
+ %macro TNOT64 1+
+ %endmacro
+%else
+ %macro TNOT64 1+
+ %1
+ %endmacro
+%endif
+
+
+;
+; Default code segment (changes BITS too).
+;
+%ifdef TMPL_64BIT
+ %define TMPL_BEGIN_TEXT BS3_BEGIN_TEXT64
+%elifdef TMPL_32BIT
+ %define TMPL_BEGIN_TEXT BS3_BEGIN_TEXT32
+%elifdef TMPL_16BIT
+ %define TMPL_BEGIN_TEXT BS3_BEGIN_TEXT16
+%else
+ %error "Missing TMPL_xxBIT!"
+%endif
+TMPL_BEGIN_TEXT
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.h b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.h
new file mode 100644
index 00000000..3aa214e4
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.h
@@ -0,0 +1,4534 @@
+/* $Id: bs3kit.h $ */
+/** @file
+ * BS3Kit - structures, symbols, macros and stuff.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef BS3KIT_INCLUDED_bs3kit_h
+#define BS3KIT_INCLUDED_bs3kit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef DOXYGEN_RUNNING
+# undef IN_RING0
+# define IN_RING0
+#endif
+
+#define RT_NO_STRICT /* Don't drag in IPRT assertion code in inline code we may use (asm.h). */
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#ifndef DOXYGEN_RUNNING
+# undef IN_RING0
+#endif
+
+/*
+ * Make asm.h and friend compatible with our 64-bit assembly config (ASM_CALL64_MSC).
+ */
+#if defined(__GNUC__) && ARCH_BITS == 64
+# undef DECLASM
+# ifdef __cplusplus
+# define DECLASM(type) extern "C" type BS3_CALL
+# else
+# define DECLASM(type) type BS3_CALL
+# endif
+#endif
+
+
+/*
+ * Work around ms_abi trouble in the gcc camp (gcc bugzilla #50818).
+ * ASSUMES all va_lists are in functions with
+ */
+#if defined(__GNUC__) && ARCH_BITS == 64
+# undef va_list
+# undef va_start
+# undef va_end
+# undef va_copy
+# define va_list __builtin_ms_va_list
+# define va_start(a_Va, a_Arg) __builtin_ms_va_start(a_Va, a_Arg)
+# define va_end(a_Va) __builtin_ms_va_end(a_Va)
+# define va_copy(a_DstVa, a_SrcVa) __builtin_ms_va_copy(a_DstVa, a_SrcVa)
+#endif
+
+
+/** @def BS3_USE_ALT_16BIT_TEXT_SEG
+ * @ingroup grp_bs3kit
+ * Combines the BS3_USE_RM_TEXT_SEG, BS3_USE_X0_TEXT_SEG, and
+ * BS3_USE_X1_TEXT_SEG indicators into a single one.
+ */
+#if defined(BS3_USE_RM_TEXT_SEG) || defined(BS3_USE_X0_TEXT_SEG) || defined(BS3_USE_X1_TEXT_SEG) || defined(DOXYGEN_RUNNING)
+# define BS3_USE_ALT_16BIT_TEXT_SEG
+#else
+# undef BS3_USE_ALT_16BIT_TEXT_SEG
+#endif
+
+/** @def BS3_USE_X0_TEXT_SEG
+ * @ingroup grp_bs3kit
+ * Emit 16-bit code to the BS3X0TEXT16 segment - ignored for 32-bit and 64-bit.
+ *
+ * Calling directly into the BS3X0TEXT16 segment is only possible in real-mode
+ * and v8086 mode. In protected mode the real far pointer have to be converted
+ * to a protected mode pointer that uses BS3_SEL_X0TEXT16_CS, Bs3TestDoModes and
+ * associates does this automatically.
+ */
+#ifdef DOXYGEN_RUNNING
+# define BS3_USE_X0_TEXT_SEG
+#endif
+
+/** @def BS3_USE_X1_TEXT_SEG
+ * @ingroup grp_bs3kit
+ * Emit 16-bit code to the BS3X1TEXT16 segment - ignored for 32-bit and 64-bit.
+ *
+ * Calling directly into the BS3X1TEXT16 segment is only possible in real-mode
+ * and v8086 mode. In protected mode the real far pointer have to be converted
+ * to a protected mode pointer that uses BS3_SEL_X1TEXT16_CS, Bs3TestDoModes and
+ * associates does this automatically.
+ */
+#ifdef DOXYGEN_RUNNING
+# define BS3_USE_X1_TEXT_SEG
+#endif
+
+/** @def BS3_USE_RM_TEXT_SEG
+ * @ingroup grp_bs3kit
+ * Emit 16-bit code to the BS3RMTEXT16 segment - ignored for 32-bit and 64-bit.
+ *
+ * This segment is normally used for real-mode only code, though
+ * BS3_SEL_RMTEXT16_CS can be used to call it from protected mode. Unlike the
+ * BS3X0TEXT16 and BS3X1TEXT16 segments which are empty by default, this segment
+ * is used by common BS3Kit code.
+ */
+#ifdef DOXYGEN_RUNNING
+# define BS3_USE_X0_TEXT_SEG
+#endif
+
+/** @def BS3_MODEL_FAR_CODE
+ * @ingroup grp_bs3kit
+ * Default compiler model indicates far code.
+ */
+#ifdef DOXYGEN_RUNNING
+# define BS3_MODEL_FAR_CODE
+#elif !defined(BS3_MODEL_FAR_CODE) && (defined(__LARGE__) || defined(__MEDIUM__) || defined(__HUGE__)) && ARCH_BITS == 16
+# define BS3_MODEL_FAR_CODE
+#endif
+
+
+/*
+ * We normally don't want the noreturn / aborts attributes as they mess up stack traces.
+ *
+ * Note! pragma aux <fnname> aborts can only be used with functions
+ * implemented in C and functions that does not have parameters.
+ */
+#define BS3_KIT_WITH_NO_RETURN
+#ifndef BS3_KIT_WITH_NO_RETURN
+# undef DECL_NO_RETURN
+# define DECL_NO_RETURN(type) type
+#endif
+
+
+/*
+ * We may want to reuse some IPRT code in the common name space, so we
+ * redefine the RT_MANGLER to work like BS3_CMN_NM. (We cannot use
+ * BS3_CMN_NM yet, as we need to include IPRT headers with function
+ * declarations before we can define it. Thus the duplciate effort.)
+ */
+#if ARCH_BITS == 16
+# undef RTCALL
+# if defined(BS3_USE_ALT_16BIT_TEXT_SEG)
+# define RTCALL __cdecl __far
+# define RT_MANGLER(a_Name) RT_CONCAT(a_Name,_f16)
+# else
+# define RTCALL __cdecl __near
+# define RT_MANGLER(a_Name) RT_CONCAT(a_Name,_c16)
+# endif
+#else
+# define RT_MANGLER(a_Name) RT_CONCAT3(a_Name,_c,ARCH_BITS)
+#endif
+#include <iprt/mangling.h>
+#include <iprt/x86.h>
+#include <iprt/err.h>
+
+/*
+ * Include data symbol mangling (function mangling/mapping must be done
+ * after the protypes).
+ */
+#include "bs3kit-mangling-data.h"
+
+
+
+RT_C_DECLS_BEGIN
+
+/** @defgroup grp_bs3kit BS3Kit - Boot Sector Kit \#3
+ *
+ * The BS3Kit is a framework for bare metal floppy/usb image tests,
+ * see the @ref pg_bs3kit "doc page" for more.
+ *
+ * @{ */
+
+/** @name Execution modes.
+ * @{ */
+#define BS3_MODE_INVALID UINT8_C(0x00)
+#define BS3_MODE_RM UINT8_C(0x01) /**< real mode. */
+#define BS3_MODE_PE16 UINT8_C(0x11) /**< 16-bit protected mode kernel+tss, running 16-bit code, unpaged. */
+#define BS3_MODE_PE16_32 UINT8_C(0x12) /**< 16-bit protected mode kernel+tss, running 32-bit code, unpaged. */
+#define BS3_MODE_PE16_V86 UINT8_C(0x18) /**< 16-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged. */
+#define BS3_MODE_PE32 UINT8_C(0x22) /**< 32-bit protected mode kernel+tss, running 32-bit code, unpaged. */
+#define BS3_MODE_PE32_16 UINT8_C(0x21) /**< 32-bit protected mode kernel+tss, running 16-bit code, unpaged. */
+#define BS3_MODE_PEV86 UINT8_C(0x28) /**< 32-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged. */
+#define BS3_MODE_PP16 UINT8_C(0x31) /**< 16-bit protected mode kernel+tss, running 16-bit code, paged. */
+#define BS3_MODE_PP16_32 UINT8_C(0x32) /**< 16-bit protected mode kernel+tss, running 32-bit code, paged. */
+#define BS3_MODE_PP16_V86 UINT8_C(0x38) /**< 16-bit protected mode kernel+tss, running virtual 8086 mode code, paged. */
+#define BS3_MODE_PP32 UINT8_C(0x42) /**< 32-bit protected mode kernel+tss, running 32-bit code, paged. */
+#define BS3_MODE_PP32_16 UINT8_C(0x41) /**< 32-bit protected mode kernel+tss, running 16-bit code, paged. */
+#define BS3_MODE_PPV86 UINT8_C(0x48) /**< 32-bit protected mode kernel+tss, running virtual 8086 mode code, paged. */
+#define BS3_MODE_PAE16 UINT8_C(0x51) /**< 16-bit protected mode kernel+tss, running 16-bit code, PAE paging. */
+#define BS3_MODE_PAE16_32 UINT8_C(0x52) /**< 16-bit protected mode kernel+tss, running 32-bit code, PAE paging. */
+#define BS3_MODE_PAE16_V86 UINT8_C(0x58) /**< 16-bit protected mode kernel+tss, running virtual 8086 mode, PAE paging. */
+#define BS3_MODE_PAE32 UINT8_C(0x62) /**< 32-bit protected mode kernel+tss, running 32-bit code, PAE paging. */
+#define BS3_MODE_PAE32_16 UINT8_C(0x61) /**< 32-bit protected mode kernel+tss, running 16-bit code, PAE paging. */
+#define BS3_MODE_PAEV86 UINT8_C(0x68) /**< 32-bit protected mode kernel+tss, running virtual 8086 mode, PAE paging. */
+#define BS3_MODE_LM16 UINT8_C(0x71) /**< 16-bit long mode (paged), kernel+tss always 64-bit. */
+#define BS3_MODE_LM32 UINT8_C(0x72) /**< 32-bit long mode (paged), kernel+tss always 64-bit. */
+#define BS3_MODE_LM64 UINT8_C(0x74) /**< 64-bit long mode (paged), kernel+tss always 64-bit. */
+
+#define BS3_MODE_CODE_MASK UINT8_C(0x0f) /**< Running code mask. */
+#define BS3_MODE_CODE_16 UINT8_C(0x01) /**< Running 16-bit code. */
+#define BS3_MODE_CODE_32 UINT8_C(0x02) /**< Running 32-bit code. */
+#define BS3_MODE_CODE_64 UINT8_C(0x04) /**< Running 64-bit code. */
+#define BS3_MODE_CODE_V86 UINT8_C(0x08) /**< Running 16-bit virtual 8086 code. */
+
+#define BS3_MODE_SYS_MASK UINT8_C(0xf0) /**< kernel+tss mask. */
+#define BS3_MODE_SYS_RM UINT8_C(0x00) /**< Real mode kernel+tss. */
+#define BS3_MODE_SYS_PE16 UINT8_C(0x10) /**< 16-bit protected mode kernel+tss. */
+#define BS3_MODE_SYS_PE32 UINT8_C(0x20) /**< 32-bit protected mode kernel+tss. */
+#define BS3_MODE_SYS_PP16 UINT8_C(0x30) /**< 16-bit paged protected mode kernel+tss. */
+#define BS3_MODE_SYS_PP32 UINT8_C(0x40) /**< 32-bit paged protected mode kernel+tss. */
+#define BS3_MODE_SYS_PAE16 UINT8_C(0x50) /**< 16-bit PAE paged protected mode kernel+tss. */
+#define BS3_MODE_SYS_PAE32 UINT8_C(0x60) /**< 32-bit PAE paged protected mode kernel+tss. */
+#define BS3_MODE_SYS_LM UINT8_C(0x70) /**< 64-bit (paged) long mode protected mode kernel+tss. */
+
+/** Whether the mode has paging enabled. */
+#define BS3_MODE_IS_PAGED(a_fMode) ((a_fMode) >= BS3_MODE_PP16)
+/** Whether the mode has legacy paging enabled (legacy as opposed to PAE or
+ * long mode). */
+#define BS3_MODE_IS_LEGACY_PAGING(a_fMode) ((a_fMode) >= BS3_MODE_PP16 && (a_fMode) < BS3_MODE_PAE16)
+
+/** Whether the mode is running v8086 code. */
+#define BS3_MODE_IS_V86(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86)
+/** Whether the we're executing in real mode or v8086 mode. */
+#define BS3_MODE_IS_RM_OR_V86(a_fMode) ((a_fMode) == BS3_MODE_RM || BS3_MODE_IS_V86(a_fMode))
+/** Whether the mode is running 16-bit code, except v8086. */
+#define BS3_MODE_IS_16BIT_CODE_NO_V86(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_16)
+/** Whether the mode is running 16-bit code (includes v8086). */
+#define BS3_MODE_IS_16BIT_CODE(a_fMode) (BS3_MODE_IS_16BIT_CODE_NO_V86(a_fMode) || BS3_MODE_IS_V86(a_fMode))
+/** Whether the mode is running 32-bit code. */
+#define BS3_MODE_IS_32BIT_CODE(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_32)
+/** Whether the mode is running 64-bit code. */
+#define BS3_MODE_IS_64BIT_CODE(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_64)
+
+/** Whether the system is in real mode. */
+#define BS3_MODE_IS_RM_SYS(a_fMode) (((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_RM)
+/** Whether the system is some 16-bit mode that isn't real mode. */
+#define BS3_MODE_IS_16BIT_SYS_NO_RM(a_fMode) ( ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE16 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP16 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE16)
+/** Whether the system is some 16-bit mode (includes real mode). */
+#define BS3_MODE_IS_16BIT_SYS(a_fMode) (BS3_MODE_IS_16BIT_SYS_NO_RM(a_fMode) || BS3_MODE_IS_RM_SYS(a_fMode))
+/** Whether the system is some 32-bit mode. */
+#define BS3_MODE_IS_32BIT_SYS(a_fMode) ( ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE32 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP32 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE32)
+/** Whether the system is long mode. */
+#define BS3_MODE_IS_64BIT_SYS(a_fMode) (((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_LM)
+
+/** Whether the system is in protected mode (with or without paging).
+ * @note Long mode is not included. */
+#define BS3_MODE_IS_PM_SYS(a_fMode) ((a_fMode) >= BS3_MODE_SYS_PE16 && (a_fMode) < BS3_MODE_SYS_LM)
+
+/** @todo testcase: How would long-mode handle a 16-bit TSS loaded prior to the switch? (mainly stack switching wise) Hopefully, it will tripple fault, right? */
+/** @} */
+
+
+/** @name BS3_ADDR_XXX - Static Memory Allocation
+ * @{ */
+/** The flat load address for the code after the bootsector. */
+#define BS3_ADDR_LOAD 0x10000
+/** Where we save the boot registers during init.
+ * Located right before the code. */
+#define BS3_ADDR_REG_SAVE (BS3_ADDR_LOAD - sizeof(BS3REGCTX) - 8)
+/** Where the stack starts (initial RSP value).
+ * Located 16 bytes (assumed by boot sector) before the saved registers.
+ * SS.BASE=0. The size is a little short of 32KB */
+#define BS3_ADDR_STACK (BS3_ADDR_REG_SAVE - 16)
+/** The ring-0 stack (8KB) for ring transitions. */
+#define BS3_ADDR_STACK_R0 0x06000
+/** The ring-1 stack (8KB) for ring transitions. */
+#define BS3_ADDR_STACK_R1 0x04000
+/** The ring-2 stack (8KB) for ring transitions. */
+#define BS3_ADDR_STACK_R2 0x02000
+/** IST1 ring-0 stack for long mode (4KB), used for double faults elsewhere. */
+#define BS3_ADDR_STACK_R0_IST1 0x09000
+/** IST2 ring-0 stack for long mode (3KB), used for spare 0 stack elsewhere. */
+#define BS3_ADDR_STACK_R0_IST2 0x08000
+/** IST3 ring-0 stack for long mode (1KB). */
+#define BS3_ADDR_STACK_R0_IST3 0x07400
+/** IST4 ring-0 stack for long mode (1KB), used for spare 1 stack elsewhere. */
+#define BS3_ADDR_STACK_R0_IST4 0x07000
+/** IST5 ring-0 stack for long mode (1KB). */
+#define BS3_ADDR_STACK_R0_IST5 0x06c00
+/** IST6 ring-0 stack for long mode (1KB). */
+#define BS3_ADDR_STACK_R0_IST6 0x06800
+/** IST7 ring-0 stack for long mode (1KB). */
+#define BS3_ADDR_STACK_R0_IST7 0x06400
+
+/** The base address of the BS3TEXT16 segment (same as BS3_LOAD_ADDR).
+ * @sa BS3_SEL_TEXT16 */
+#define BS3_ADDR_BS3TEXT16 0x10000
+/** The base address of the BS3SYSTEM16 segment.
+ * @sa BS3_SEL_SYSTEM16 */
+#define BS3_ADDR_BS3SYSTEM16 0x20000
+/** The base address of the BS3DATA16/BS3KIT_GRPNM_DATA16 segment.
+ * @sa BS3_SEL_DATA16 */
+#define BS3_ADDR_BS3DATA16 0x29000
+/** @} */
+
+/** @name BS3_SEL_XXX - GDT selector assignments.
+ *
+ * The real mode segment numbers for BS16TEXT, BS16DATA and BS16SYSTEM are
+ * present in the GDT, this allows the 16-bit C/C++ and assembly code to
+ * continue using the real mode segment values in ring-0 protected mode.
+ *
+ * The three segments have fixed locations:
+ * | segment | flat address | real mode segment |
+ * | ----------- | ------------ | ----------------- |
+ * | BS3TEXT16 | 0x00010000 | 1000h |
+ * | BS3SYSTEM16 | 0x00020000 | 2000h |
+ * | BS3DATA16 | 0x00029000 | 2900h |
+ *
+ * This means that we've got a lot of GDT space to play around with.
+ *
+ * @{ */
+#define BS3_SEL_LDT 0x0010 /**< The LDT selector for Bs3Ldt. */
+#define BS3_SEL_TSS16 0x0020 /**< The 16-bit TSS selector. */
+#define BS3_SEL_TSS16_DF 0x0028 /**< The 16-bit TSS selector for double faults. */
+#define BS3_SEL_TSS16_SPARE0 0x0030 /**< The 16-bit TSS selector for testing. */
+#define BS3_SEL_TSS16_SPARE1 0x0038 /**< The 16-bit TSS selector for testing. */
+#define BS3_SEL_TSS32 0x0040 /**< The 32-bit TSS selector. */
+#define BS3_SEL_TSS32_DF 0x0048 /**< The 32-bit TSS selector for double faults. */
+#define BS3_SEL_TSS32_SPARE0 0x0050 /**< The 32-bit TSS selector for testing. */
+#define BS3_SEL_TSS32_SPARE1 0x0058 /**< The 32-bit TSS selector for testing. */
+#define BS3_SEL_TSS32_IOBP_IRB 0x0060 /**< The 32-bit TSS selector with I/O permission and interrupt redirection bitmaps. */
+#define BS3_SEL_TSS32_IRB 0x0068 /**< The 32-bit TSS selector with only interrupt redirection bitmap (IOPB stripped by limit). */
+#define BS3_SEL_TSS64 0x0070 /**< The 64-bit TSS selector. */
+#define BS3_SEL_TSS64_SPARE0 0x0080 /**< The 64-bit TSS selector. */
+#define BS3_SEL_TSS64_SPARE1 0x0090 /**< The 64-bit TSS selector. */
+#define BS3_SEL_TSS64_IOBP 0x00a0 /**< The 64-bit TSS selector. */
+
+#define BS3_SEL_RMTEXT16_CS 0x00e0 /**< Conforming code selector for accessing the BS3RMTEXT16 segment. Runtime config. */
+#define BS3_SEL_X0TEXT16_CS 0x00e8 /**< Conforming code selector for accessing the BS3X0TEXT16 segment. Runtime config. */
+#define BS3_SEL_X1TEXT16_CS 0x00f0 /**< Conforming code selector for accessing the BS3X1TEXT16 segment. Runtime config. */
+#define BS3_SEL_VMMDEV_MMIO16 0x00f8 /**< Selector for accessing the VMMDev MMIO segment at 00df000h from 16-bit code. */
+
+/** Checks if @a uSel is in the BS3_SEL_RX_XXX range. */
+#define BS3_SEL_IS_IN_RING_RANGE(uSel) ( (unsigned)(uSel - BS3_SEL_R0_FIRST) < (unsigned)(4 << BS3_SEL_RING_SHIFT) )
+#define BS3_SEL_RING_SHIFT 8 /**< For the formula: BS3_SEL_R0_XXX + ((cs & 3) << BS3_SEL_RING_SHIFT) */
+#define BS3_SEL_RING_SUB_MASK 0x00f8 /**< Mask for getting the sub-selector. For use with BS3_SEL_R*_FIRST. */
+
+/** Checks if @a uSel is in the BS3_SEL_R0_XXX range. */
+#define BS3_SEL_IS_IN_R0_RANGE(uSel) ( (unsigned)(uSel - BS3_SEL_R0_FIRST) < (unsigned)(1 << BS3_SEL_RING_SHIFT) )
+#define BS3_SEL_R0_FIRST 0x0100 /**< The first selector in the ring-0 block. */
+#define BS3_SEL_R0_CS16 0x0100 /**< ring-0: 16-bit code selector, base 0x10000. */
+#define BS3_SEL_R0_DS16 0x0108 /**< ring-0: 16-bit data selector, base 0x23000. */
+#define BS3_SEL_R0_SS16 0x0110 /**< ring-0: 16-bit stack selector, base 0x00000. */
+#define BS3_SEL_R0_CS32 0x0118 /**< ring-0: 32-bit flat code selector. */
+#define BS3_SEL_R0_DS32 0x0120 /**< ring-0: 32-bit flat data selector. */
+#define BS3_SEL_R0_SS32 0x0128 /**< ring-0: 32-bit flat stack selector. */
+#define BS3_SEL_R0_CS64 0x0130 /**< ring-0: 64-bit flat code selector. */
+#define BS3_SEL_R0_DS64 0x0138 /**< ring-0: 64-bit flat data & stack selector. */
+#define BS3_SEL_R0_CS16_EO 0x0140 /**< ring-0: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R0_CS16_CNF 0x0148 /**< ring-0: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R0_CS16_CNF_EO 0x0150 /**< ring-0: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R0_CS32_EO 0x0158 /**< ring-0: 32-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R0_CS32_CNF 0x0160 /**< ring-0: 32-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R0_CS32_CNF_EO 0x0168 /**< ring-0: 32-bit execute-only conforming code selector, not accessed, flat. */
+#define BS3_SEL_R0_CS64_EO 0x0170 /**< ring-0: 64-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R0_CS64_CNF 0x0178 /**< ring-0: 64-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R0_CS64_CNF_EO 0x0180 /**< ring-0: 64-bit execute-only conforming code selector, not accessed, flat. */
+
+#define BS3_SEL_R1_FIRST 0x0200 /**< The first selector in the ring-1 block. */
+#define BS3_SEL_R1_CS16 0x0200 /**< ring-1: 16-bit code selector, base 0x10000. */
+#define BS3_SEL_R1_DS16 0x0208 /**< ring-1: 16-bit data selector, base 0x23000. */
+#define BS3_SEL_R1_SS16 0x0210 /**< ring-1: 16-bit stack selector, base 0x00000. */
+#define BS3_SEL_R1_CS32 0x0218 /**< ring-1: 32-bit flat code selector. */
+#define BS3_SEL_R1_DS32 0x0220 /**< ring-1: 32-bit flat data selector. */
+#define BS3_SEL_R1_SS32 0x0228 /**< ring-1: 32-bit flat stack selector. */
+#define BS3_SEL_R1_CS64 0x0230 /**< ring-1: 64-bit flat code selector. */
+#define BS3_SEL_R1_DS64 0x0238 /**< ring-1: 64-bit flat data & stack selector. */
+#define BS3_SEL_R1_CS16_EO 0x0240 /**< ring-1: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R1_CS16_CNF 0x0248 /**< ring-1: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R1_CS16_CNF_EO 0x0250 /**< ring-1: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R1_CS32_EO 0x0258 /**< ring-1: 32-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R1_CS32_CNF 0x0260 /**< ring-1: 32-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R1_CS32_CNF_EO 0x0268 /**< ring-1: 32-bit execute-only conforming code selector, not accessed, flat. */
+#define BS3_SEL_R1_CS64_EO 0x0270 /**< ring-1: 64-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R1_CS64_CNF 0x0278 /**< ring-1: 64-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R1_CS64_CNF_EO 0x0280 /**< ring-1: 64-bit execute-only conforming code selector, not accessed, flat. */
+
+#define BS3_SEL_R2_FIRST 0x0300 /**< The first selector in the ring-2 block. */
+#define BS3_SEL_R2_CS16 0x0300 /**< ring-2: 16-bit code selector, base 0x10000. */
+#define BS3_SEL_R2_DS16 0x0308 /**< ring-2: 16-bit data selector, base 0x23000. */
+#define BS3_SEL_R2_SS16 0x0310 /**< ring-2: 16-bit stack selector, base 0x00000. */
+#define BS3_SEL_R2_CS32 0x0318 /**< ring-2: 32-bit flat code selector. */
+#define BS3_SEL_R2_DS32 0x0320 /**< ring-2: 32-bit flat data selector. */
+#define BS3_SEL_R2_SS32 0x0328 /**< ring-2: 32-bit flat stack selector. */
+#define BS3_SEL_R2_CS64 0x0330 /**< ring-2: 64-bit flat code selector. */
+#define BS3_SEL_R2_DS64 0x0338 /**< ring-2: 64-bit flat data & stack selector. */
+#define BS3_SEL_R2_CS16_EO 0x0340 /**< ring-2: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R2_CS16_CNF 0x0348 /**< ring-2: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R2_CS16_CNF_EO 0x0350 /**< ring-2: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R2_CS32_EO 0x0358 /**< ring-2: 32-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R2_CS32_CNF 0x0360 /**< ring-2: 32-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R2_CS32_CNF_EO 0x0368 /**< ring-2: 32-bit execute-only conforming code selector, not accessed, flat. */
+#define BS3_SEL_R2_CS64_EO 0x0370 /**< ring-2: 64-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R2_CS64_CNF 0x0378 /**< ring-2: 64-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R2_CS64_CNF_EO 0x0380 /**< ring-2: 64-bit execute-only conforming code selector, not accessed, flat. */
+
+#define BS3_SEL_R3_FIRST 0x0400 /**< The first selector in the ring-3 block. */
+#define BS3_SEL_R3_CS16 0x0400 /**< ring-3: 16-bit code selector, base 0x10000. */
+#define BS3_SEL_R3_DS16 0x0408 /**< ring-3: 16-bit data selector, base 0x23000. */
+#define BS3_SEL_R3_SS16 0x0410 /**< ring-3: 16-bit stack selector, base 0x00000. */
+#define BS3_SEL_R3_CS32 0x0418 /**< ring-3: 32-bit flat code selector. */
+#define BS3_SEL_R3_DS32 0x0420 /**< ring-3: 32-bit flat data selector. */
+#define BS3_SEL_R3_SS32 0x0428 /**< ring-3: 32-bit flat stack selector. */
+#define BS3_SEL_R3_CS64 0x0430 /**< ring-3: 64-bit flat code selector. */
+#define BS3_SEL_R3_DS64 0x0438 /**< ring-3: 64-bit flat data & stack selector. */
+#define BS3_SEL_R3_CS16_EO 0x0440 /**< ring-3: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R3_CS16_CNF 0x0448 /**< ring-3: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R3_CS16_CNF_EO 0x0450 /**< ring-3: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base. */
+#define BS3_SEL_R3_CS32_EO 0x0458 /**< ring-3: 32-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R3_CS32_CNF 0x0460 /**< ring-3: 32-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R3_CS32_CNF_EO 0x0468 /**< ring-3: 32-bit execute-only conforming code selector, not accessed, flat. */
+#define BS3_SEL_R3_CS64_EO 0x0470 /**< ring-3: 64-bit execute-only code selector, not accessed, flat. */
+#define BS3_SEL_R3_CS64_CNF 0x0478 /**< ring-3: 64-bit conforming code selector, not accessed, flat. */
+#define BS3_SEL_R3_CS64_CNF_EO 0x0480 /**< ring-3: 64-bit execute-only conforming code selector, not accessed, flat. */
+
+#define BS3_SEL_R3_LAST 0x04f8 /**< ring-3: Last of the BS3_SEL_RX_XXX range. */
+
+#define BS3_SEL_SPARE_FIRST 0x0500 /**< The first selector in the spare block */
+#define BS3_SEL_SPARE_00 0x0500 /**< Spare selector number 00h. */
+#define BS3_SEL_SPARE_01 0x0508 /**< Spare selector number 01h. */
+#define BS3_SEL_SPARE_02 0x0510 /**< Spare selector number 02h. */
+#define BS3_SEL_SPARE_03 0x0518 /**< Spare selector number 03h. */
+#define BS3_SEL_SPARE_04 0x0520 /**< Spare selector number 04h. */
+#define BS3_SEL_SPARE_05 0x0528 /**< Spare selector number 05h. */
+#define BS3_SEL_SPARE_06 0x0530 /**< Spare selector number 06h. */
+#define BS3_SEL_SPARE_07 0x0538 /**< Spare selector number 07h. */
+#define BS3_SEL_SPARE_08 0x0540 /**< Spare selector number 08h. */
+#define BS3_SEL_SPARE_09 0x0548 /**< Spare selector number 09h. */
+#define BS3_SEL_SPARE_0a 0x0550 /**< Spare selector number 0ah. */
+#define BS3_SEL_SPARE_0b 0x0558 /**< Spare selector number 0bh. */
+#define BS3_SEL_SPARE_0c 0x0560 /**< Spare selector number 0ch. */
+#define BS3_SEL_SPARE_0d 0x0568 /**< Spare selector number 0dh. */
+#define BS3_SEL_SPARE_0e 0x0570 /**< Spare selector number 0eh. */
+#define BS3_SEL_SPARE_0f 0x0578 /**< Spare selector number 0fh. */
+#define BS3_SEL_SPARE_10 0x0580 /**< Spare selector number 10h. */
+#define BS3_SEL_SPARE_11 0x0588 /**< Spare selector number 11h. */
+#define BS3_SEL_SPARE_12 0x0590 /**< Spare selector number 12h. */
+#define BS3_SEL_SPARE_13 0x0598 /**< Spare selector number 13h. */
+#define BS3_SEL_SPARE_14 0x05a0 /**< Spare selector number 14h. */
+#define BS3_SEL_SPARE_15 0x05a8 /**< Spare selector number 15h. */
+#define BS3_SEL_SPARE_16 0x05b0 /**< Spare selector number 16h. */
+#define BS3_SEL_SPARE_17 0x05b8 /**< Spare selector number 17h. */
+#define BS3_SEL_SPARE_18 0x05c0 /**< Spare selector number 18h. */
+#define BS3_SEL_SPARE_19 0x05c8 /**< Spare selector number 19h. */
+#define BS3_SEL_SPARE_1a 0x05d0 /**< Spare selector number 1ah. */
+#define BS3_SEL_SPARE_1b 0x05d8 /**< Spare selector number 1bh. */
+#define BS3_SEL_SPARE_1c 0x05e0 /**< Spare selector number 1ch. */
+#define BS3_SEL_SPARE_1d 0x05e8 /**< Spare selector number 1dh. */
+#define BS3_SEL_SPARE_1e 0x05f0 /**< Spare selector number 1eh. */
+#define BS3_SEL_SPARE_1f 0x05f8 /**< Spare selector number 1fh. */
+
+#define BS3_SEL_TILED 0x0600 /**< 16-bit data tiling: First - base=0x00000000, limit=64KB, DPL=3. */
+#define BS3_SEL_TILED_LAST 0x0df8 /**< 16-bit data tiling: Last - base=0x00ff0000, limit=64KB, DPL=3. */
+#define BS3_SEL_TILED_AREA_SIZE 0x001000000 /**< 16-bit data tiling: Size of addressable area, in bytes. (16 MB) */
+
+#define BS3_SEL_FREE_PART1 0x0e00 /**< Free selector space - part \#1. */
+#define BS3_SEL_FREE_PART1_LAST 0x0ff8 /**< Free selector space - part \#1, last entry. */
+
+#define BS3_SEL_TEXT16 0x1000 /**< The BS3TEXT16 selector. */
+
+#define BS3_SEL_FREE_PART2 0x1008 /**< Free selector space - part \#2. */
+#define BS3_SEL_FREE_PART2_LAST 0x17f8 /**< Free selector space - part \#2, last entry. */
+
+#define BS3_SEL_TILED_R0 0x1800 /**< 16-bit data/stack tiling: First - base=0x00000000, limit=64KB, DPL=0. */
+#define BS3_SEL_TILED_R0_LAST 0x1ff8 /**< 16-bit data/stack tiling: Last - base=0x00ff0000, limit=64KB, DPL=0. */
+
+#define BS3_SEL_SYSTEM16 0x2000 /**< The BS3SYSTEM16 selector. */
+
+#define BS3_SEL_FREE_PART3 0x2008 /**< Free selector space - part \#3. */
+#define BS3_SEL_FREE_PART3_LAST 0x28f8 /**< Free selector space - part \#3, last entry. */
+
+#define BS3_SEL_DATA16 0x2900 /**< The BS3DATA16/BS3KIT_GRPNM_DATA16 selector. */
+
+#define BS3_SEL_FREE_PART4 0x2908 /**< Free selector space - part \#4. */
+#define BS3_SEL_FREE_PART4_LAST 0x2f98 /**< Free selector space - part \#4, last entry. */
+
+#define BS3_SEL_PRE_TEST_PAGE_08 0x2fa0 /**< Selector located 8 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_07 0x2fa8 /**< Selector located 7 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_06 0x2fb0 /**< Selector located 6 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_05 0x2fb8 /**< Selector located 5 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_04 0x2fc0 /**< Selector located 4 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_03 0x2fc8 /**< Selector located 3 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_02 0x2fd0 /**< Selector located 2 selectors before the test page. */
+#define BS3_SEL_PRE_TEST_PAGE_01 0x2fd8 /**< Selector located 1 selector before the test page. */
+#define BS3_SEL_TEST_PAGE 0x2fe0 /**< Start of the test page intended for playing around with paging and GDT. */
+#define BS3_SEL_TEST_PAGE_00 0x2fe0 /**< Test page selector number 00h (convenience). */
+#define BS3_SEL_TEST_PAGE_01 0x2fe8 /**< Test page selector number 01h (convenience). */
+#define BS3_SEL_TEST_PAGE_02 0x2ff0 /**< Test page selector number 02h (convenience). */
+#define BS3_SEL_TEST_PAGE_03 0x2ff8 /**< Test page selector number 03h (convenience). */
+#define BS3_SEL_TEST_PAGE_04 0x3000 /**< Test page selector number 04h (convenience). */
+#define BS3_SEL_TEST_PAGE_05 0x3008 /**< Test page selector number 05h (convenience). */
+#define BS3_SEL_TEST_PAGE_06 0x3010 /**< Test page selector number 06h (convenience). */
+#define BS3_SEL_TEST_PAGE_07 0x3018 /**< Test page selector number 07h (convenience). */
+#define BS3_SEL_TEST_PAGE_LAST 0x3fd0 /**< The last selector in the spare page. */
+
+#define BS3_SEL_GDT_LIMIT 0x3fd8 /**< The GDT limit. */
+/** @} */
+
+/** @name BS3_SEL_IS_XXX - Predicates for standard selectors.
+ *
+ * Standard selectors are in the range BS3_SEL_R0_FIRST thru BS3_SEL_R3_LAST.
+ *
+ * @{ */
+#define BS3_SEL_IS_CS16(a_uSel) (((a_uSel) & 0xf8) == 0x00)
+#define BS3_SEL_IS_CS32(a_uSel) (((a_uSel) & 0xf8) == 0x18)
+#define BS3_SEL_IS_CS64(a_uSel) (((a_uSel) & 0xf8) == 0x30)
+
+#define BS3_SEL_IS_ANY_CS16(a_uSel) ( ((a_uSel) & 0xf8) == 0x00 \
+ || ((a_uSel) & 0xf8) == 0x40 \
+ || ((a_uSel) & 0xf8) == 0x48 \
+ || ((a_uSel) & 0xf8) == 0x50 )
+#define BS3_SEL_IS_ANY_CS32(a_uSel) ( ((a_uSel) & 0xf8) == 0x18 \
+ || ((a_uSel) & 0xf8) == 0x58 \
+ || ((a_uSel) & 0xf8) == 0x60 \
+ || ((a_uSel) & 0xf8) == 0x68 )
+#define BS3_SEL_IS_ANY_CS64(a_uSel) ( ((a_uSel) & 0xf8) == 0x18 \
+ || ((a_uSel) & 0xf8) == 0x58 \
+ || ((a_uSel) & 0xf8) == 0x60 \
+ || ((a_uSel) & 0xf8) == 0x68 )
+
+#define BS3_SEL_IS_DS16(a_uSel) (((a_uSel) & 0xf8) == 0x08)
+#define BS3_SEL_IS_DS32(a_uSel) (((a_uSel) & 0xf8) == 0x20)
+#define BS3_SEL_IS_DS64(a_uSel) (((a_uSel) & 0xf8) == 0x38)
+
+#define BS3_SEL_IS_SS16(a_uSel) (((a_uSel) & 0xf8) == 0x10)
+#define BS3_SEL_IS_SS32(a_uSel) (((a_uSel) & 0xf8) == 0x28)
+/** @} */
+
+
+/** @def BS3_FAR
+ * For indicating far pointers in 16-bit code.
+ * Does nothing in 32-bit and 64-bit code. */
+/** @def BS3_NEAR
+ * For indicating near pointers in 16-bit code.
+ * Does nothing in 32-bit and 64-bit code. */
+/** @def BS3_FAR_CODE
+ * For indicating far 16-bit functions.
+ * Does nothing in 32-bit and 64-bit code. */
+/** @def BS3_NEAR_CODE
+ * For indicating near 16-bit functions.
+ * Does nothing in 32-bit and 64-bit code. */
+/** @def BS3_FAR_DATA
+ * For indicating far 16-bit external data, i.e. in a segment other than DATA16.
+ * Does nothing in 32-bit and 64-bit code. */
+#ifdef M_I86
+# define BS3_FAR __far
+# define BS3_NEAR __near
+# define BS3_FAR_CODE __far
+# define BS3_NEAR_CODE __near
+# define BS3_FAR_DATA __far
+#else
+# define BS3_FAR
+# define BS3_NEAR
+# define BS3_FAR_CODE
+# define BS3_NEAR_CODE
+# define BS3_FAR_DATA
+#endif
+
+#if ARCH_BITS == 16 || defined(DOXYGEN_RUNNING)
+/** @def BS3_FP_SEG
+ * Get the selector (segment) part of a far pointer.
+ *
+ * @returns selector.
+ * @param a_pv Far pointer.
+ */
+# define BS3_FP_SEG(a_pv) ((uint16_t)(__segment)(void BS3_FAR *)(a_pv))
+/** @def BS3_FP_OFF
+ * Get the segment offset part of a far pointer.
+ *
+ * For sake of convenience, this works like a uintptr_t cast in 32-bit and
+ * 64-bit code.
+ *
+ * @returns offset.
+ * @param a_pv Far pointer.
+ */
+# define BS3_FP_OFF(a_pv) ((uint16_t)(void __near *)(a_pv))
+/** @def BS3_FP_MAKE
+ * Create a far pointer.
+ *
+ * @returns Far pointer.
+ * @param a_uSeg The selector/segment.
+ * @param a_off The offset into the segment.
+ */
+# define BS3_FP_MAKE(a_uSeg, a_off) (((__segment)(a_uSeg)) :> ((void __near *)(a_off)))
+#else
+# define BS3_FP_OFF(a_pv) ((uintptr_t)(a_pv))
+#endif
+
+/** @def BS3_MAKE_PROT_R0PTR_FROM_FLAT
+ * Creates a protected mode pointer from a flat address.
+ *
+ * For sake of convenience, this macro also works in 32-bit and 64-bit mode,
+ * only there it doesn't return a far pointer but a flat point.
+ *
+ * @returns far void pointer if 16-bit code, near/flat void pointer in 32-bit
+ * and 64-bit.
+ * @param a_uFlat Flat address in the first 16MB. */
+#if ARCH_BITS == 16
+# define BS3_MAKE_PROT_R0PTR_FROM_FLAT(a_uFlat) \
+ BS3_FP_MAKE(((uint16_t)(a_uFlat >> 16) << 3) + BS3_SEL_TILED, (uint16_t)(a_uFlat))
+#else
+# define BS3_MAKE_PROT_R0PTR_FROM_FLAT(a_uFlat) ((void *)(uintptr_t)(a_uFlat))
+#endif
+
+/** @def BS3_MAKE_PROT_R0PTR_FROM_REAL
+ * Creates a protected mode pointer from a far real mode address.
+ *
+ * For sake of convenience, this macro also works in 32-bit and 64-bit mode,
+ * only there it doesn't return a far pointer but a flat point.
+ *
+ * @returns far void pointer if 16-bit code, near/flat void pointer in 32-bit
+ * and 64-bit.
+ * @param a_uSeg The selector/segment.
+ * @param a_off The offset into the segment.
+ */
+#if ARCH_BITS == 16
+# define BS3_MAKE_PROT_R0PTR_FROM_REAL(a_uSeg, a_off) BS3_MAKE_PROT_R0PTR_FROM_FLAT(((uint32_t)(a_uSeg) << 4) + (uint16_t)(a_off))
+#else
+# define BS3_MAKE_PROT_R0PTR_FROM_REAL(a_uSeg, a_off) ( (void *)(uintptr_t)(((uint32_t)(a_uSeg) << 4) + (uint16_t)(a_off)) )
+#endif
+
+
+/** @def BS3_CALL
+ * The calling convension used by BS3 functions. */
+#if ARCH_BITS != 64
+# define BS3_CALL __cdecl
+#elif !defined(_MSC_VER)
+# define BS3_CALL __attribute__((__ms_abi__))
+#else
+# define BS3_CALL
+#endif
+
+/** @def IN_BS3KIT
+ * Indicates that we're in the same link job as the BS3Kit code. */
+#ifdef DOXYGEN_RUNNING
+# define IN_BS3KIT
+#endif
+
+/** @def BS3_DECL
+ * Declares a BS3Kit function with default far/near.
+ *
+ * Until we outgrow BS3TEXT16, we use all near functions in 16-bit.
+ *
+ * @param a_Type The return type. */
+#if ARCH_BITS != 16 || !defined(BS3_USE_ALT_16BIT_TEXT_SEG)
+# define BS3_DECL(a_Type) BS3_DECL_NEAR(a_Type)
+#else
+# define BS3_DECL(a_Type) BS3_DECL_FAR(a_Type)
+#endif
+
+/** @def BS3_DECL_NEAR
+ * Declares a BS3Kit function, always near everywhere.
+ *
+ * Until we outgrow BS3TEXT16, we use all near functions in 16-bit.
+ *
+ * @param a_Type The return type. */
+#ifdef IN_BS3KIT
+# define BS3_DECL_NEAR(a_Type) DECLEXPORT(a_Type) BS3_NEAR_CODE BS3_CALL
+#else
+# define BS3_DECL_NEAR(a_Type) DECLIMPORT(a_Type) BS3_NEAR_CODE BS3_CALL
+#endif
+
+/** @def BS3_DECL_FAR
+ * Declares a BS3Kit function, far 16-bit, otherwise near.
+ *
+ * Until we outgrow BS3TEXT16, we use all near functions in 16-bit.
+ *
+ * @param a_Type The return type. */
+#ifdef IN_BS3KIT
+# define BS3_DECL_FAR(a_Type) DECLEXPORT(a_Type) BS3_FAR_CODE BS3_CALL
+#else
+# define BS3_DECL_FAR(a_Type) DECLIMPORT(a_Type) BS3_FAR_CODE BS3_CALL
+#endif
+
+/** @def BS3_DECL_CALLBACK
+ * Declares a BS3Kit callback function (typically static).
+ *
+ * @param a_Type The return type. */
+#ifdef IN_BS3KIT
+# define BS3_DECL_CALLBACK(a_Type) a_Type BS3_FAR_CODE BS3_CALL
+#else
+# define BS3_DECL_CALLBACK(a_Type) a_Type BS3_FAR_CODE BS3_CALL
+#endif
+
+/** @def BS3_DECL_NEAR_CALLBACK
+ * Declares a near BS3Kit callback function (typically static).
+ *
+ * 16-bit users must be in CGROUP16!
+ *
+ * @param a_Type The return type. */
+#ifdef IN_BS3KIT
+# define BS3_DECL_NEAR_CALLBACK(a_Type) a_Type BS3_NEAR_CODE BS3_CALL
+#else
+# define BS3_DECL_NEAR_CALLBACK(a_Type) a_Type BS3_NEAR_CODE BS3_CALL
+#endif
+
+/**
+ * Constructs a common name.
+ *
+ * Example: BS3_CMN_NM(Bs3Shutdown)
+ *
+ * @param a_Name The name of the function or global variable.
+ */
+#define BS3_CMN_NM(a_Name) RT_CONCAT3(a_Name,_c,ARCH_BITS)
+
+/**
+ * Constructs a common function name, far in 16-bit code.
+ *
+ * Example: BS3_CMN_FAR_NM(Bs3Shutdown)
+ *
+ * @param a_Name The name of the function.
+ */
+#if ARCH_BITS == 16
+# define BS3_CMN_FAR_NM(a_Name) RT_CONCAT(a_Name,_f16)
+#else
+# define BS3_CMN_FAR_NM(a_Name) RT_CONCAT3(a_Name,_c,ARCH_BITS)
+#endif
+
+/**
+ * Constructs a common function name, far or near as defined by the source.
+ *
+ * Which to use in 16-bit mode is defined by BS3_USE_ALT_16BIT_TEXT_SEG. In
+ * 32-bit and 64-bit mode there are no far symbols, only near ones.
+ *
+ * Example: BS3_CMN_FN_NM(Bs3Shutdown)
+ *
+ * @param a_Name The name of the function.
+ */
+#if ARCH_BITS != 16 || !defined(BS3_USE_ALT_16BIT_TEXT_SEG)
+# define BS3_CMN_FN_NM(a_Name) BS3_CMN_NM(a_Name)
+#else
+# define BS3_CMN_FN_NM(a_Name) BS3_CMN_FAR_NM(a_Name)
+#endif
+
+
+/**
+ * Constructs a data name.
+ *
+ * This glosses over the underscore prefix usage of our 16-bit, 32-bit and
+ * 64-bit compilers.
+ *
+ * Example: @code{.c}
+ * \#define Bs3Gdt BS3_DATA_NM(Bs3Gdt)
+ * extern X86DESC BS3_FAR_DATA Bs3Gdt
+ * @endcode
+ *
+ * @param a_Name The name of the global variable.
+ * @remarks Mainly used in bs3kit-mangling.h, internal headers and templates.
+ */
+//converter does this now//#if ARCH_BITS == 64
+//converter does this now//# define BS3_DATA_NM(a_Name) RT_CONCAT(_,a_Name)
+//converter does this now//#else
+# define BS3_DATA_NM(a_Name) a_Name
+//converter does this now//#endif
+
+/**
+ * Template for creating a pointer union type.
+ * @param a_BaseName The base type name.
+ * @param a_Modifiers The type modifier.
+ */
+#define BS3_PTR_UNION_TEMPLATE(a_BaseName, a_Modifiers) \
+ typedef union a_BaseName \
+ { \
+ /** Pointer into the void. */ \
+ a_Modifiers void BS3_FAR *pv; \
+ /** As a signed integer. */ \
+ intptr_t i; \
+ /** As an unsigned integer. */ \
+ uintptr_t u; \
+ /** Pointer to char value. */ \
+ a_Modifiers char BS3_FAR *pch; \
+ /** Pointer to char value. */ \
+ a_Modifiers unsigned char BS3_FAR *puch; \
+ /** Pointer to a int value. */ \
+ a_Modifiers int BS3_FAR *pi; \
+ /** Pointer to a unsigned int value. */ \
+ a_Modifiers unsigned int BS3_FAR *pu; \
+ /** Pointer to a long value. */ \
+ a_Modifiers long BS3_FAR *pl; \
+ /** Pointer to a long value. */ \
+ a_Modifiers unsigned long BS3_FAR *pul; \
+ /** Pointer to a memory size value. */ \
+ a_Modifiers size_t BS3_FAR *pcb; \
+ /** Pointer to a byte value. */ \
+ a_Modifiers uint8_t BS3_FAR *pb; \
+ /** Pointer to a 8-bit unsigned value. */ \
+ a_Modifiers uint8_t BS3_FAR *pu8; \
+ /** Pointer to a 16-bit unsigned value. */ \
+ a_Modifiers uint16_t BS3_FAR *pu16; \
+ /** Pointer to a 32-bit unsigned value. */ \
+ a_Modifiers uint32_t BS3_FAR *pu32; \
+ /** Pointer to a 64-bit unsigned value. */ \
+ a_Modifiers uint64_t BS3_FAR *pu64; \
+ /** Pointer to a UTF-16 character. */ \
+ a_Modifiers RTUTF16 BS3_FAR *pwc; \
+ /** Pointer to a UUID character. */ \
+ a_Modifiers RTUUID BS3_FAR *pUuid; \
+ } a_BaseName; \
+ /** Pointer to a pointer union. */ \
+ typedef a_BaseName *RT_CONCAT(P,a_BaseName)
+BS3_PTR_UNION_TEMPLATE(BS3PTRUNION, RT_NOTHING);
+BS3_PTR_UNION_TEMPLATE(BS3CPTRUNION, const);
+BS3_PTR_UNION_TEMPLATE(BS3VPTRUNION, volatile);
+BS3_PTR_UNION_TEMPLATE(BS3CVPTRUNION, const volatile);
+
+/** Generic far function type. */
+typedef BS3_DECL_FAR(void) FNBS3FAR(void);
+/** Generic far function pointer type. */
+typedef FNBS3FAR *FPFNBS3FAR;
+
+/** Generic near function type. */
+typedef BS3_DECL_NEAR(void) FNBS3NEAR(void);
+/** Generic near function pointer type. */
+typedef FNBS3NEAR *PFNBS3NEAR;
+
+/** Generic far 16:16 function pointer type for address conversion functions. */
+#if ARCH_BITS == 16
+typedef FPFNBS3FAR PFNBS3FARADDRCONV;
+#else
+typedef uint32_t PFNBS3FARADDRCONV;
+#endif
+
+/** The system call vector. */
+#define BS3_TRAP_SYSCALL UINT8_C(0x20)
+
+/** @name System call numbers (ax).
+ * Paramenters are generally passed in registers specific to each system call,
+ * however cx:xSI is used for passing a pointer parameter.
+ * @{ */
+/** Print char (cl). */
+#define BS3_SYSCALL_PRINT_CHR UINT16_C(0x0001)
+/** Print string (pointer in cx:xSI, length in dx). */
+#define BS3_SYSCALL_PRINT_STR UINT16_C(0x0002)
+/** Switch to ring-0. */
+#define BS3_SYSCALL_TO_RING0 UINT16_C(0x0003)
+/** Switch to ring-1. */
+#define BS3_SYSCALL_TO_RING1 UINT16_C(0x0004)
+/** Switch to ring-2. */
+#define BS3_SYSCALL_TO_RING2 UINT16_C(0x0005)
+/** Switch to ring-3. */
+#define BS3_SYSCALL_TO_RING3 UINT16_C(0x0006)
+/** Restore context (pointer in cx:xSI, flags in dx). */
+#define BS3_SYSCALL_RESTORE_CTX UINT16_C(0x0007)
+/** Set DRx register (value in ESI, register number in dl). */
+#define BS3_SYSCALL_SET_DRX UINT16_C(0x0008)
+/** Get DRx register (register number in dl, value returned in ax:dx). */
+#define BS3_SYSCALL_GET_DRX UINT16_C(0x0009)
+/** Set CRx register (value in ESI, register number in dl). */
+#define BS3_SYSCALL_SET_CRX UINT16_C(0x000a)
+/** Get CRx register (register number in dl, value returned in ax:dx). */
+#define BS3_SYSCALL_GET_CRX UINT16_C(0x000b)
+/** Set the task register (value in ESI). */
+#define BS3_SYSCALL_SET_TR UINT16_C(0x000c)
+/** Get the task register (value returned in ax). */
+#define BS3_SYSCALL_GET_TR UINT16_C(0x000d)
+/** Set the LDT register (value in ESI). */
+#define BS3_SYSCALL_SET_LDTR UINT16_C(0x000e)
+/** Get the LDT register (value returned in ax). */
+#define BS3_SYSCALL_GET_LDTR UINT16_C(0x000f)
+/** Set XCR0 register (value in edx:esi). */
+#define BS3_SYSCALL_SET_XCR0 UINT16_C(0x0010)
+/** Get XCR0 register (value returned in edx:eax). */
+#define BS3_SYSCALL_GET_XCR0 UINT16_C(0x0011)
+/** The last system call value. */
+#define BS3_SYSCALL_LAST BS3_SYSCALL_GET_XCR0
+/** @} */
+
+
+
+/** @defgroup grp_bs3kit_system System Structures
+ * @{ */
+/** The GDT, indexed by BS3_SEL_XXX shifted by 3. */
+extern X86DESC BS3_FAR_DATA Bs3Gdt[(BS3_SEL_GDT_LIMIT + 1) / 8];
+
+extern X86DESC64 BS3_FAR_DATA Bs3Gdt_Ldt; /**< @see BS3_SEL_LDT */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss16; /**< @see BS3_SEL_TSS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss16DoubleFault; /**< @see BS3_SEL_TSS16_DF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss16Spare0; /**< @see BS3_SEL_TSS16_SPARE0 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss16Spare1; /**< @see BS3_SEL_TSS16_SPARE1 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32; /**< @see BS3_SEL_TSS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32DoubleFault; /**< @see BS3_SEL_TSS32_DF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32Spare0; /**< @see BS3_SEL_TSS32_SPARE0 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32Spare1; /**< @see BS3_SEL_TSS32_SPARE1 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32IobpIntRedirBm; /**< @see BS3_SEL_TSS32_IOBP_IRB */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss32IntRedirBm; /**< @see BS3_SEL_TSS32_IRB */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss64; /**< @see BS3_SEL_TSS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss64Spare0; /**< @see BS3_SEL_TSS64_SPARE0 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss64Spare1; /**< @see BS3_SEL_TSS64_SPARE1 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_Tss64Iobp; /**< @see BS3_SEL_TSS64_IOBP */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_RMTEXT16_CS; /**< @see BS3_SEL_RMTEXT16_CS */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_X0TEXT16_CS; /**< @see BS3_SEL_X0TEXT16_CS */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_X1TEXT16_CS; /**< @see BS3_SEL_X1TEXT16_CS */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_MMIO16; /**< @see BS3_SEL_VMMDEV_MMIO16 */
+
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_First; /**< @see BS3_SEL_R0_FIRST */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS16; /**< @see BS3_SEL_R0_CS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_DS16; /**< @see BS3_SEL_R0_DS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_SS16; /**< @see BS3_SEL_R0_SS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS32; /**< @see BS3_SEL_R0_CS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_DS32; /**< @see BS3_SEL_R0_DS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_SS32; /**< @see BS3_SEL_R0_SS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS64; /**< @see BS3_SEL_R0_CS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_DS64; /**< @see BS3_SEL_R0_DS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS16_EO; /**< @see BS3_SEL_R0_CS16_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS16_CNF; /**< @see BS3_SEL_R0_CS16_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS16_CND_EO; /**< @see BS3_SEL_R0_CS16_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS32_EO; /**< @see BS3_SEL_R0_CS32_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS32_CNF; /**< @see BS3_SEL_R0_CS32_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS32_CNF_EO; /**< @see BS3_SEL_R0_CS32_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS64_EO; /**< @see BS3_SEL_R0_CS64_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS64_CNF; /**< @see BS3_SEL_R0_CS64_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R0_CS64_CNF_EO; /**< @see BS3_SEL_R0_CS64_CNF_EO */
+
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_First; /**< @see BS3_SEL_R1_FIRST */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS16; /**< @see BS3_SEL_R1_CS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_DS16; /**< @see BS3_SEL_R1_DS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_SS16; /**< @see BS3_SEL_R1_SS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS32; /**< @see BS3_SEL_R1_CS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_DS32; /**< @see BS3_SEL_R1_DS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_SS32; /**< @see BS3_SEL_R1_SS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS64; /**< @see BS3_SEL_R1_CS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_DS64; /**< @see BS3_SEL_R1_DS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS16_EO; /**< @see BS3_SEL_R1_CS16_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS16_CNF; /**< @see BS3_SEL_R1_CS16_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS16_CND_EO; /**< @see BS3_SEL_R1_CS16_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS32_EO; /**< @see BS3_SEL_R1_CS32_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS32_CNF; /**< @see BS3_SEL_R1_CS32_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS32_CNF_EO; /**< @see BS3_SEL_R1_CS32_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS64_EO; /**< @see BS3_SEL_R1_CS64_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS64_CNF; /**< @see BS3_SEL_R1_CS64_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R1_CS64_CNF_EO; /**< @see BS3_SEL_R1_CS64_CNF_EO */
+
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_First; /**< @see BS3_SEL_R2_FIRST */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS16; /**< @see BS3_SEL_R2_CS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_DS16; /**< @see BS3_SEL_R2_DS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_SS16; /**< @see BS3_SEL_R2_SS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS32; /**< @see BS3_SEL_R2_CS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_DS32; /**< @see BS3_SEL_R2_DS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_SS32; /**< @see BS3_SEL_R2_SS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS64; /**< @see BS3_SEL_R2_CS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_DS64; /**< @see BS3_SEL_R2_DS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS16_EO; /**< @see BS3_SEL_R2_CS16_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS16_CNF; /**< @see BS3_SEL_R2_CS16_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS16_CND_EO; /**< @see BS3_SEL_R2_CS16_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS32_EO; /**< @see BS3_SEL_R2_CS32_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS32_CNF; /**< @see BS3_SEL_R2_CS32_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS32_CNF_EO; /**< @see BS3_SEL_R2_CS32_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS64_EO; /**< @see BS3_SEL_R2_CS64_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS64_CNF; /**< @see BS3_SEL_R2_CS64_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R2_CS64_CNF_EO; /**< @see BS3_SEL_R2_CS64_CNF_EO */
+
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_First; /**< @see BS3_SEL_R3_FIRST */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS16; /**< @see BS3_SEL_R3_CS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_DS16; /**< @see BS3_SEL_R3_DS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_SS16; /**< @see BS3_SEL_R3_SS16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS32; /**< @see BS3_SEL_R3_CS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_DS32; /**< @see BS3_SEL_R3_DS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_SS32; /**< @see BS3_SEL_R3_SS32 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS64; /**< @see BS3_SEL_R3_CS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_DS64; /**< @see BS3_SEL_R3_DS64 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS16_EO; /**< @see BS3_SEL_R3_CS16_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS16_CNF; /**< @see BS3_SEL_R3_CS16_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS16_CND_EO; /**< @see BS3_SEL_R3_CS16_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS32_EO; /**< @see BS3_SEL_R3_CS32_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS32_CNF; /**< @see BS3_SEL_R3_CS32_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS32_CNF_EO; /**< @see BS3_SEL_R3_CS32_CNF_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS64_EO; /**< @see BS3_SEL_R3_CS64_EO */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS64_CNF; /**< @see BS3_SEL_R3_CS64_CNF */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_R3_CS64_CNF_EO; /**< @see BS3_SEL_R3_CS64_CNF_EO */
+
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare00; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_00 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare01; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_01 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare02; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_02 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare03; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_03 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare04; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_04 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare05; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_05 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare06; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_06 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare07; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_07 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare08; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_08 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare09; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_09 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0a; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0a */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0b; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0b */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0c; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0c */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0d; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0d */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0e; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0e */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare0f; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_0f */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare10; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_10 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare11; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_11 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare12; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_12 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare13; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_13 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare14; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_14 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare15; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_15 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare16; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_16 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare17; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_17 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare18; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_18 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare19; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_19 */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1a; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1a */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1b; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1b */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1c; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1c */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1d; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1d */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1e; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1e */
+extern X86DESC BS3_FAR_DATA Bs3GdteSpare1f; /**< GDT entry for playing with in testcases. @see BS3_SEL_SPARE_1f */
+
+/** GDTs setting up the tiled 16-bit access to the first 16 MBs of memory.
+ * @see BS3_SEL_TILED, BS3_SEL_TILED_LAST, BS3_SEL_TILED_AREA_SIZE */
+extern X86DESC BS3_FAR_DATA Bs3GdteTiled[256];
+/** Free GDTes, part \#1. */
+extern X86DESC BS3_FAR_DATA Bs3GdteFreePart1[64];
+/** The BS3TEXT16/BS3CLASS16CODE GDT entry. @see BS3_SEL_TEXT16 */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_CODE16;
+/** Free GDTes, part \#2. */
+extern X86DESC BS3_FAR_DATA Bs3GdteFreePart2[511];
+/** The BS3SYSTEM16 GDT entry. */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_SYSTEM16;
+/** Free GDTes, part \#3. */
+extern X86DESC BS3_FAR_DATA Bs3GdteFreePart3[223];
+/** The BS3DATA16/BS3KIT_GRPNM_DATA16 GDT entry. */
+extern X86DESC BS3_FAR_DATA Bs3Gdte_DATA16;
+
+/** Free GDTes, part \#4. */
+extern X86DESC BS3_FAR_DATA Bs3GdteFreePart4[211];
+
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage08; /**< GDT entry 8 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_08 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage07; /**< GDT entry 7 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_07 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage06; /**< GDT entry 6 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_06 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage05; /**< GDT entry 5 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_05 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage04; /**< GDT entry 4 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_04 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage03; /**< GDT entry 3 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_03 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage02; /**< GDT entry 2 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_02 */
+extern X86DESC BS3_FAR_DATA Bs3GdtePreTestPage01; /**< GDT entry 1 selectors prior to the test page, testcase resource. @see BS3_SEL_PRE_TEST_PAGE_01 */
+/** Array of GDT entries starting on a page boundrary and filling (almost) the
+ * whole page. This is for playing with paging and GDT usage.
+ * @see BS3_SEL_TEST_PAGE */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage[2043];
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage00; /**< GDT entry 0 on the test page (convenience). @see BS3_SEL_TEST_PAGE_00 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage01; /**< GDT entry 1 on the test page (convenience). @see BS3_SEL_TEST_PAGE_01 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage02; /**< GDT entry 2 on the test page (convenience). @see BS3_SEL_TEST_PAGE_02 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage03; /**< GDT entry 3 on the test page (convenience). @see BS3_SEL_TEST_PAGE_03 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage04; /**< GDT entry 4 on the test page (convenience). @see BS3_SEL_TEST_PAGE_04 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage05; /**< GDT entry 5 on the test page (convenience). @see BS3_SEL_TEST_PAGE_05 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage06; /**< GDT entry 6 on the test page (convenience). @see BS3_SEL_TEST_PAGE_06 */
+extern X86DESC BS3_FAR_DATA Bs3GdteTestPage07; /**< GDT entry 7 on the test page (convenience). @see BS3_SEL_TEST_PAGE_07 */
+
+/** The end of the GDT (exclusive - contains eye-catcher string). */
+extern X86DESC BS3_FAR_DATA Bs3GdtEnd;
+
+/** The default 16-bit TSS. */
+extern X86TSS16 BS3_FAR_DATA Bs3Tss16;
+extern X86TSS16 BS3_FAR_DATA Bs3Tss16DoubleFault;
+extern X86TSS16 BS3_FAR_DATA Bs3Tss16Spare0;
+extern X86TSS16 BS3_FAR_DATA Bs3Tss16Spare1;
+/** The default 32-bit TSS. */
+extern X86TSS32 BS3_FAR_DATA Bs3Tss32;
+extern X86TSS32 BS3_FAR_DATA Bs3Tss32DoubleFault;
+extern X86TSS32 BS3_FAR_DATA Bs3Tss32Spare0;
+extern X86TSS32 BS3_FAR_DATA Bs3Tss32Spare1;
+/** The default 64-bit TSS. */
+extern X86TSS64 BS3_FAR_DATA Bs3Tss64;
+extern X86TSS64 BS3_FAR_DATA Bs3Tss64Spare0;
+extern X86TSS64 BS3_FAR_DATA Bs3Tss64Spare1;
+extern X86TSS64 BS3_FAR_DATA Bs3Tss64WithIopb;
+extern X86TSS32 BS3_FAR_DATA Bs3Tss32WithIopb;
+/** Interrupt redirection bitmap used by Bs3Tss32WithIopb. */
+extern uint8_t BS3_FAR_DATA Bs3SharedIntRedirBm[32];
+/** I/O permission bitmap used by Bs3Tss32WithIopb and Bs3Tss64WithIopb. */
+extern uint8_t BS3_FAR_DATA Bs3SharedIobp[8192+2];
+/** End of the I/O permission bitmap (exclusive). */
+extern uint8_t BS3_FAR_DATA Bs3SharedIobpEnd;
+/** 16-bit IDT. */
+extern X86DESC BS3_FAR_DATA Bs3Idt16[256];
+/** 32-bit IDT. */
+extern X86DESC BS3_FAR_DATA Bs3Idt32[256];
+/** 64-bit IDT. */
+extern X86DESC64 BS3_FAR_DATA Bs3Idt64[256];
+/** Structure for the LIDT instruction for loading the 16-bit IDT. */
+extern X86XDTR64 BS3_FAR_DATA Bs3Lidt_Idt16;
+/** Structure for the LIDT instruction for loading the 32-bit IDT. */
+extern X86XDTR64 BS3_FAR_DATA Bs3Lidt_Idt32;
+/** Structure for the LIDT instruction for loading the 64-bit IDT. */
+extern X86XDTR64 BS3_FAR_DATA Bs3Lidt_Idt64;
+/** Structure for the LIDT instruction for loading the real mode interrupt
+ * vector table. */
+extern X86XDTR64 BS3_FAR_DATA Bs3Lidt_Ivt;
+/** Structure for the LGDT instruction for loading the current GDT. */
+extern X86XDTR64 BS3_FAR_DATA Bs3Lgdt_Gdt;
+/** Structure for the LGDT instruction for loading the default GDT. */
+extern X86XDTR64 BS3_FAR_DATA Bs3LgdtDef_Gdt;
+/** The LDT (all entries are empty, fill in for testing). */
+extern X86DESC BS3_FAR_DATA Bs3Ldt[116];
+/** The end of the LDT (exclusive). */
+extern X86DESC BS3_FAR_DATA Bs3LdtEnd;
+
+/** @} */
+
+
+/** @name Segment start and end markers, sizes.
+ * @{ */
+/** Start of the BS3TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text16_StartOfSegment;
+/** End of the BS3TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text16_EndOfSegment;
+/** The size of the BS3TEXT16 segment. */
+extern uint16_t BS3_FAR_DATA Bs3Text16_Size;
+
+/** Start of the BS3SYSTEM16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3System16_StartOfSegment;
+/** End of the BS3SYSTEM16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3System16_EndOfSegment;
+
+/** Start of the BS3DATA16/BS3KIT_GRPNM_DATA16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data16_StartOfSegment;
+/** End of the BS3DATA16/BS3KIT_GRPNM_DATA16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data16_EndOfSegment;
+
+/** Start of the BS3RMTEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3RmText16_StartOfSegment;
+/** End of the BS3RMTEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3RmText16_EndOfSegment;
+/** The size of the BS3RMTEXT16 segment. */
+extern uint16_t BS3_FAR_DATA Bs3RmText16_Size;
+/** The flat start address of the BS3X0TEXT16 segment. */
+extern uint32_t BS3_FAR_DATA Bs3RmText16_FlatAddr;
+
+/** Start of the BS3X0TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3X0Text16_StartOfSegment;
+/** End of the BS3X0TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3X0Text16_EndOfSegment;
+/** The size of the BS3X0TEXT16 segment. */
+extern uint16_t BS3_FAR_DATA Bs3X0Text16_Size;
+/** The flat start address of the BS3X0TEXT16 segment. */
+extern uint32_t BS3_FAR_DATA Bs3X0Text16_FlatAddr;
+
+/** Start of the BS3X1TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3X1Text16_StartOfSegment;
+/** End of the BS3X1TEXT16 segment. */
+extern uint8_t BS3_FAR_DATA Bs3X1Text16_EndOfSegment;
+/** The size of the BS3X1TEXT16 segment. */
+extern uint16_t BS3_FAR_DATA Bs3X1Text16_Size;
+/** The flat start address of the BS3X1TEXT16 segment. */
+extern uint32_t BS3_FAR_DATA Bs3X1Text16_FlatAddr;
+
+/** Start of the BS3TEXT32 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text32_StartOfSegment;
+/** Start of the BS3TEXT32 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text32_EndOfSegment;
+
+/** Start of the BS3DATA32 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data32_StartOfSegment;
+/** Start of the BS3DATA32 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data32_EndOfSegment;
+
+/** Start of the BS3TEXT64 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text64_StartOfSegment;
+/** Start of the BS3TEXT64 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Text64_EndOfSegment;
+
+/** Start of the BS3DATA64 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data64_StartOfSegment;
+/** Start of the BS3DATA64 segment. */
+extern uint8_t BS3_FAR_DATA Bs3Data64_EndOfSegment;
+
+/** The size of the Data16, Text32, Text64, Data32 and Data64 blob. */
+extern uint32_t BS3_FAR_DATA Bs3Data16Thru64Text32And64_TotalSize;
+/** The total image size (from Text16 thu Data64). */
+extern uint32_t BS3_FAR_DATA Bs3TotalImageSize;
+/** @} */
+
+
+/** Lower case hex digits. */
+extern char const g_achBs3HexDigits[16+1];
+/** Upper case hex digits. */
+extern char const g_achBs3HexDigitsUpper[16+1];
+
+
+/** The current mode (BS3_MODE_XXX) of CPU \#0. */
+extern uint8_t g_bBs3CurrentMode;
+
+/** Hint for 16-bit trap handlers regarding the high word of EIP. */
+extern uint32_t g_uBs3TrapEipHint;
+
+/** Set to disable special V8086 \#GP and \#UD handling in Bs3TrapDefaultHandler.
+ * This is useful for getting */
+extern bool volatile g_fBs3TrapNoV86Assist;
+
+/** Copy of the original real-mode interrupt vector table. */
+extern RTFAR16 g_aBs3RmIvtOriginal[256];
+
+
+#ifdef __WATCOMC__
+/**
+ * Executes the SMSW instruction and returns the value.
+ *
+ * @returns Machine status word.
+ */
+uint16_t Bs3AsmSmsw(void);
+# pragma aux Bs3AsmSmsw = \
+ ".286" \
+ "smsw ax" \
+ value [ax] modify exact [ax] nomemory;
+#endif
+
+
+/** @defgroup bs3kit_cross_ptr Cross Context Pointer Type
+ *
+ * The cross context pointer type is
+ *
+ * @{ */
+
+/**
+ * Cross context pointer base type.
+ */
+typedef union BS3XPTR
+{
+ /** The flat pointer. */
+ uint32_t uFlat;
+ /** 16-bit view. */
+ struct
+ {
+ uint16_t uLow;
+ uint16_t uHigh;
+ } u;
+#if ARCH_BITS == 16
+ /** 16-bit near pointer. */
+ void __near *pvNear;
+#elif ARCH_BITS == 32
+ /** 32-bit pointer. */
+ void *pvRaw;
+#endif
+} BS3XPTR;
+AssertCompileSize(BS3XPTR, 4);
+
+
+/** @def BS3_XPTR_DEF_INTERNAL
+ * Internal worker.
+ *
+ * @param a_Scope RT_NOTHING if structure or global, static or extern
+ * otherwise.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ * @internal
+ */
+#if ARCH_BITS == 16
+# define BS3_XPTR_DEF_INTERNAL(a_Scope, a_Type, a_Name) \
+ a_Scope union \
+ { \
+ BS3XPTR XPtr; \
+ a_Type __near *pNearTyped; \
+ } a_Name
+#elif ARCH_BITS == 32
+# define BS3_XPTR_DEF_INTERNAL(a_Scope, a_Type, a_Name) \
+ a_Scope union \
+ { \
+ BS3XPTR XPtr; \
+ a_Type *pTyped; \
+ } a_Name
+#elif ARCH_BITS == 64
+# define BS3_XPTR_DEF_INTERNAL(a_Scope, a_Type, a_Name) \
+ a_Scope union \
+ { \
+ BS3XPTR XPtr; \
+ } a_Name
+#else
+# error "ARCH_BITS"
+#endif
+
+/** @def BS3_XPTR_MEMBER
+ * Defines a pointer member that can be shared by all CPU modes.
+ *
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#define BS3_XPTR_MEMBER(a_Type, a_Name) BS3_XPTR_DEF_INTERNAL(RT_NOTHING, a_Type, a_Name)
+
+/** @def BS3_XPTR_AUTO
+ * Defines a pointer static variable for working with an XPTR.
+ *
+ * This is typically used to convert flat pointers into context specific
+ * pointers.
+ *
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#define BS3_XPTR_AUTO(a_Type, a_Name) BS3_XPTR_DEF_INTERNAL(RT_NOTHING, a_Type, a_Name)
+
+/** @def BS3_XPTR_SET_FLAT
+ * Sets a cross context pointer.
+ *
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ * @param a_uFlatPtr The flat pointer value to assign. If the x-pointer is
+ * used in real mode, this must be less than 1MB.
+ * Otherwise the limit is 16MB (due to selector tiling).
+ */
+#define BS3_XPTR_SET_FLAT(a_Type, a_Name, a_uFlatPtr) \
+ do { a_Name.XPtr.uFlat = (a_uFlatPtr); } while (0)
+
+/** @def BS3_XPTR_GET_FLAT
+ * Gets the flat address of a cross context pointer.
+ *
+ * @returns 32-bit flat pointer.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#define BS3_XPTR_GET_FLAT(a_Type, a_Name) (a_Name.XPtr.uFlat)
+
+/** @def BS3_XPTR_GET_FLAT_LOW
+ * Gets the low 16 bits of the flat address.
+ *
+ * @returns Low 16 bits of the flat pointer.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#define BS3_XPTR_GET_FLAT_LOW(a_Type, a_Name) (a_Name.XPtr.u.uLow)
+
+
+#if ARCH_BITS == 16
+
+/**
+ * Gets the current ring number.
+ * @returns Ring number.
+ */
+DECLINLINE(uint16_t) Bs3Sel16GetCurRing(void);
+# pragma aux Bs3Sel16GetCurRing = \
+ "mov ax, ss" \
+ "and ax, 3" \
+ value [ax] modify exact [ax] nomemory;
+
+/**
+ * Converts the high word of a flat pointer into a 16-bit selector.
+ *
+ * This makes use of the tiled area. It also handles real mode.
+ *
+ * @returns Segment selector value.
+ * @param uHigh The high part of flat pointer.
+ * @sa BS3_XPTR_GET, BS3_XPTR_SET
+ */
+DECLINLINE(__segment) Bs3Sel16HighFlatPtrToSelector(uint16_t uHigh)
+{
+ if (!BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode))
+ return (__segment)(((uHigh << 3) + BS3_SEL_TILED) | Bs3Sel16GetCurRing());
+ return (__segment)(uHigh << 12);
+}
+
+#endif /* ARCH_BITS == 16 */
+
+/** @def BS3_XPTR_GET
+ * Gets the current context pointer value.
+ *
+ * @returns Usable pointer.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#if ARCH_BITS == 16
+# define BS3_XPTR_GET(a_Type, a_Name) \
+ ((a_Type BS3_FAR *)BS3_FP_MAKE(Bs3Sel16HighFlatPtrToSelector((a_Name).XPtr.u.uHigh), (a_Name).pNearTyped))
+#elif ARCH_BITS == 32
+# define BS3_XPTR_GET(a_Type, a_Name) ((a_Name).pTyped)
+#elif ARCH_BITS == 64
+# define BS3_XPTR_GET(a_Type, a_Name) ((a_Type *)(uintptr_t)(a_Name).XPtr.uFlat)
+#else
+# error "ARCH_BITS"
+#endif
+
+/** @def BS3_XPTR_SET
+ * Gets the current context pointer value.
+ *
+ * @returns Usable pointer.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ * @param a_pValue The new pointer value, current context pointer.
+ */
+#if ARCH_BITS == 16
+# define BS3_XPTR_SET(a_Type, a_Name, a_pValue) \
+ do { \
+ a_Type BS3_FAR *pTypeCheck = (a_pValue); \
+ if (BS3_MODE_IS_RM_OR_V86(g_bBs3CurrentMode)) \
+ (a_Name).XPtr.uFlat = BS3_FP_OFF(pTypeCheck) + ((uint32_t)BS3_FP_SEG(pTypeCheck) << 4); \
+ else \
+ { \
+ (a_Name).XPtr.u.uLow = BS3_FP_OFF(pTypeCheck); \
+ (a_Name).XPtr.u.uHigh = ((BS3_FP_SEG(pTypeCheck) & UINT16_C(0xfff8)) - BS3_SEL_TILED) >> 3; \
+ } \
+ } while (0)
+#elif ARCH_BITS == 32
+# define BS3_XPTR_SET(a_Type, a_Name, a_pValue) \
+ do { (a_Name).pTyped = (a_pValue); } while (0)
+#elif ARCH_BITS == 64
+# define BS3_XPTR_SET(a_Type, a_Name, a_pValue) \
+ do { \
+ a_Type *pTypeCheck = (a_pValue); \
+ (a_Name).XPtr.uFlat = (uint32_t)(uintptr_t)pTypeCheck; \
+ } while (0)
+#else
+# error "ARCH_BITS"
+#endif
+
+
+/** @def BS3_XPTR_IS_NULL
+ * Checks if the cross context pointer is NULL.
+ *
+ * @returns true if NULL, false if not.
+ * @param a_Type The type we're pointing to.
+ * @param a_Name The member or variable name.
+ */
+#define BS3_XPTR_IS_NULL(a_Type, a_Name) ((a_Name).XPtr.uFlat == 0)
+
+/**
+ * Gets a working pointer from a flat address.
+ *
+ * @returns Current context pointer.
+ * @param uFlatPtr The flat address to convert (32-bit or 64-bit).
+ */
+DECLINLINE(void BS3_FAR *) Bs3XptrFlatToCurrent(RTCCUINTXREG uFlatPtr)
+{
+ BS3_XPTR_AUTO(void, pTmp);
+ BS3_XPTR_SET_FLAT(void, pTmp, uFlatPtr);
+ return BS3_XPTR_GET(void, pTmp);
+}
+
+/** @} */
+
+
+
+/** @defgroup grp_bs3kit_cmn Common Functions and Data
+ *
+ * The common functions comes in three variations: 16-bit, 32-bit and 64-bit.
+ * Templated code uses the #BS3_CMN_NM macro to mangle the name according to the
+ * desired
+ *
+ * @{
+ */
+
+/** @def BS3_CMN_PROTO_INT
+ * Internal macro for prototyping all the variations of a common function.
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_CMN_PROTO_STUB, BS3_CMN_PROTO_NOSB
+ */
+#if ARCH_BITS == 16
+# ifndef BS3_USE_ALT_16BIT_TEXT_SEG
+# define BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params) \
+ BS3_DECL_NEAR(a_RetType) BS3_CMN_NM(a_Name) a_Params; \
+ BS3_DECL_FAR(a_RetType) BS3_CMN_FAR_NM(a_Name) a_Params
+# else
+# define BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params) \
+ BS3_DECL_FAR(a_RetType) BS3_CMN_FAR_NM(a_Name) a_Params
+# endif
+#else
+# define BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params) \
+ BS3_DECL_NEAR(a_RetType) BS3_CMN_NM(a_Name) a_Params
+#endif
+
+/** @def BS3_CMN_PROTO_STUB
+ * Macro for prototyping all the variations of a common function with automatic
+ * near -> far stub.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_CMN_PROTO_NOSB
+ */
+#define BS3_CMN_PROTO_STUB(a_RetType, a_Name, a_Params) BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params)
+
+/** @def BS3_CMN_PROTO_NOSB
+ * Macro for prototyping all the variations of a common function without any
+ * near > far stub.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_CMN_PROTO_STUB
+ */
+#define BS3_CMN_PROTO_NOSB(a_RetType, a_Name, a_Params) BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params)
+
+/** @def BS3_CMN_PROTO_FARSTUB
+ * Macro for prototyping all the variations of a common function with automatic
+ * far -> near stub.
+ *
+ * @param a_cbParam16 The size of the 16-bit parameter list in bytes.
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_CMN_PROTO_STUB
+ */
+#define BS3_CMN_PROTO_FARSTUB(a_cbParam16, a_RetType, a_Name, a_Params) BS3_CMN_PROTO_INT(a_RetType, a_Name, a_Params)
+
+
+/** @def BS3_CMN_DEF
+ * Macro for defining a common function.
+ *
+ * This makes 16-bit common function far, while 32-bit and 64-bit are near.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ */
+#if ARCH_BITS == 16
+# define BS3_CMN_DEF(a_RetType, a_Name, a_Params) \
+ BS3_DECL_FAR(a_RetType) BS3_CMN_FAR_NM(a_Name) a_Params
+#else
+# define BS3_CMN_DEF(a_RetType, a_Name, a_Params) \
+ BS3_DECL_NEAR(a_RetType) BS3_CMN_NM(a_Name) a_Params
+#endif
+
+/** @def BS3_ASSERT
+ * Assert that an expression is true.
+ *
+ * Calls Bs3Panic if false and it's a strict build. Does nothing in
+ * non-strict builds. */
+#ifdef BS3_STRICT
+# define BS3_ASSERT(a_Expr) do { if (!!(a_Expr)) { /* likely */ } else { Bs3Panic(); } } while (0) /**< @todo later */
+#else
+# define BS3_ASSERT(a_Expr) do { } while (0)
+#endif
+
+/**
+ * Panic, never return.
+ *
+ * The current implementation will only halt the CPU.
+ */
+BS3_CMN_PROTO_NOSB(DECL_NO_RETURN(void), Bs3Panic,(void));
+#if !defined(BS3_KIT_WITH_NO_RETURN) && defined(__WATCOMC__)
+# pragma aux Bs3Panic_c16 __aborts
+# pragma aux Bs3Panic_f16 __aborts
+# pragma aux Bs3Panic_c32 __aborts
+#endif
+
+
+/**
+ * Translate a mode into a string.
+ *
+ * @returns Pointer to read-only mode name string.
+ * @param bMode The mode value (BS3_MODE_XXX).
+ */
+BS3_CMN_PROTO_STUB(const char BS3_FAR *, Bs3GetModeName,(uint8_t bMode));
+
+/**
+ * Translate a mode into a short lower case string.
+ *
+ * @returns Pointer to read-only short mode name string.
+ * @param bMode The mode value (BS3_MODE_XXX).
+ */
+BS3_CMN_PROTO_STUB(const char BS3_FAR *, Bs3GetModeNameShortLower,(uint8_t bMode));
+
+/** CPU vendors. */
+typedef enum BS3CPUVENDOR
+{
+ BS3CPUVENDOR_INVALID = 0,
+ BS3CPUVENDOR_INTEL,
+ BS3CPUVENDOR_AMD,
+ BS3CPUVENDOR_VIA,
+ BS3CPUVENDOR_CYRIX,
+ BS3CPUVENDOR_SHANGHAI,
+ BS3CPUVENDOR_HYGON,
+ BS3CPUVENDOR_UNKNOWN,
+ BS3CPUVENDOR_END
+} BS3CPUVENDOR;
+
+/**
+ * Tries to detect the CPU vendor.
+ *
+ * @returns CPU vendor.
+ */
+BS3_CMN_PROTO_STUB(BS3CPUVENDOR, Bs3GetCpuVendor,(void));
+
+/**
+ * Shutdown the system, never returns.
+ *
+ * This currently only works for VMs. When running on real systems it will
+ * just halt the CPU.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3Shutdown,(void));
+
+/**
+ * Prints a 32-bit unsigned value as decimal to the screen.
+ *
+ * @param uValue The 32-bit value.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3PrintU32,(uint32_t uValue));
+
+/**
+ * Prints a 32-bit unsigned value as hex to the screen.
+ *
+ * @param uValue The 32-bit value.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3PrintX32,(uint32_t uValue));
+
+/**
+ * Formats and prints a string to the screen.
+ *
+ * See #Bs3StrFormatV for supported format types.
+ *
+ * @param pszFormat The format string.
+ * @param ... Format arguments.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3Printf,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Formats and prints a string to the screen, va_list version.
+ *
+ * See #Bs3StrFormatV for supported format types.
+ *
+ * @param pszFormat The format string.
+ * @param va Format arguments.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3PrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Prints a string to the screen.
+ *
+ * @param pszString The string to print.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3PrintStr,(const char BS3_FAR *pszString));
+
+/**
+ * Prints a string to the screen.
+ *
+ * @param pszString The string to print. Any terminator charss will be printed.
+ * @param cchString The exact number of characters to print.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3PrintStrN,(const char BS3_FAR *pszString, size_t cchString));
+
+/**
+ * Prints a char to the screen.
+ *
+ * @param ch The character to print.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3PrintChr,(char ch));
+
+
+/**
+ * An output function for #Bs3StrFormatV.
+ *
+ * @returns Number of characters written.
+ * @param ch The character to write. Zero in the final call.
+ * @param pvUser User argument supplied to #Bs3StrFormatV.
+ */
+typedef BS3_DECL_CALLBACK(size_t) FNBS3STRFORMATOUTPUT(char ch, void BS3_FAR *pvUser);
+/** Pointer to an output function for #Bs3StrFormatV. */
+typedef FNBS3STRFORMATOUTPUT *PFNBS3STRFORMATOUTPUT;
+
+/**
+ * Formats a string, sending the output to @a pfnOutput.
+ *
+ * Supported types:
+ * - %RI8, %RI16, %RI32, %RI64
+ * - %RU8, %RU16, %RU32, %RU64
+ * - %RX8, %RX16, %RX32, %RX64
+ * - %i, %d
+ * - %u
+ * - %x
+ * - %c
+ * - %p (far pointer)
+ * - %s (far pointer)
+ *
+ * @returns Sum of @a pfnOutput return values.
+ * @param pszFormat The format string.
+ * @param va Format arguments.
+ * @param pfnOutput The output function.
+ * @param pvUser The user argument for the output function.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3StrFormatV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va,
+ PFNBS3STRFORMATOUTPUT pfnOutput, void BS3_FAR *pvUser));
+
+/**
+ * Formats a string into a buffer.
+ *
+ * See #Bs3StrFormatV for supported format types.
+ *
+ * @returns The length of the formatted string (excluding terminator).
+ * This will be higher or equal to @c cbBuf in case of an overflow.
+ * @param pszBuf The output buffer.
+ * @param cbBuf The size of the output buffer.
+ * @param pszFormat The format string.
+ * @param va Format arguments.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3StrPrintfV,(char BS3_FAR *pszBuf, size_t cbBuf, const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Formats a string into a buffer.
+ *
+ * See #Bs3StrFormatV for supported format types.
+ *
+ * @returns The length of the formatted string (excluding terminator).
+ * This will be higher or equal to @c cbBuf in case of an overflow.
+ * @param pszBuf The output buffer.
+ * @param cbBuf The size of the output buffer.
+ * @param pszFormat The format string.
+ * @param ... Format arguments.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3StrPrintf,(char BS3_FAR *pszBuf, size_t cbBuf, const char BS3_FAR *pszFormat, ...));
+
+
+/**
+ * Finds the length of a zero terminated string.
+ *
+ * @returns String length in chars/bytes.
+ * @param pszString The string to examine.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3StrLen,(const char BS3_FAR *pszString));
+
+/**
+ * Finds the length of a zero terminated string, but with a max length.
+ *
+ * @returns String length in chars/bytes, or @a cchMax if no zero-terminator
+ * was found before we reached the limit.
+ * @param pszString The string to examine.
+ * @param cchMax The max length to examine.
+ */
+BS3_CMN_PROTO_STUB(size_t, Bs3StrNLen,(const char BS3_FAR *pszString, size_t cchMax));
+
+/**
+ * CRT style unsafe strcpy.
+ *
+ * @returns pszDst.
+ * @param pszDst The destination buffer. Must be large enough to
+ * hold the source string.
+ * @param pszSrc The source string.
+ */
+BS3_CMN_PROTO_STUB(char BS3_FAR *, Bs3StrCpy,(char BS3_FAR *pszDst, const char BS3_FAR *pszSrc));
+
+/**
+ * CRT style memcpy.
+ *
+ * @returns pvDst
+ * @param pvDst The destination buffer.
+ * @param pvSrc The source buffer.
+ * @param cbToCopy The number of bytes to copy.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemCpy,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy));
+
+/**
+ * GNU (?) style mempcpy.
+ *
+ * @returns pvDst + cbCopy
+ * @param pvDst The destination buffer.
+ * @param pvSrc The source buffer.
+ * @param cbToCopy The number of bytes to copy.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemPCpy,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy));
+
+/**
+ * CRT style memmove (overlapping buffers is fine).
+ *
+ * @returns pvDst
+ * @param pvDst The destination buffer.
+ * @param pvSrc The source buffer.
+ * @param cbToCopy The number of bytes to copy.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemMove,(void BS3_FAR *pvDst, const void BS3_FAR *pvSrc, size_t cbToCopy));
+
+/**
+ * BSD style bzero.
+ *
+ * @param pvDst The buffer to be zeroed.
+ * @param cbDst The number of bytes to zero.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3MemZero,(void BS3_FAR *pvDst, size_t cbDst));
+
+/**
+ * CRT style memset.
+ *
+ * @param pvDst The buffer to be fill.
+ * @param bFiller The filler byte.
+ * @param cbDst The number of bytes to fill.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3MemSet,(void BS3_FAR *pvDst, uint8_t bFiller, size_t cbDst));
+
+/**
+ * CRT style memchr.
+ *
+ * @param pvHaystack The memory to scan for @a bNeedle.
+ * @param bNeedle The byte to search for.
+ * @param cbHaystack The amount of memory to search.
+ */
+BS3_CMN_PROTO_NOSB(void BS3_FAR *, Bs3MemChr,(void const BS3_FAR *pvHaystack, uint8_t bNeedle, size_t cbHaystack));
+
+/**
+ * CRT style memcmp.
+ *
+ * @returns 0 if equal. Negative if the left side is 'smaller' than the right
+ * side, and positive in the other case.
+ * @param pv1 The left hand memory.
+ * @param pv2 The right hand memory.
+ * @param cb The number of bytes to compare.
+ */
+BS3_CMN_PROTO_NOSB(int, Bs3MemCmp,(void const BS3_FAR *pv1, void const BS3_FAR *pv2, size_t cb));
+
+BS3_CMN_PROTO_STUB(void, Bs3UInt64Div,(RTUINT64U uDividend, RTUINT64U uDivisor, RTUINT64U BS3_FAR *paQuotientReminder));
+BS3_CMN_PROTO_STUB(void, Bs3UInt32Div,(RTUINT32U uDividend, RTUINT32U uDivisor, RTUINT32U BS3_FAR *paQuotientReminder));
+
+
+/**
+ * Converts a protected mode 32-bit far pointer to a 32-bit flat address.
+ *
+ * @returns 32-bit flat address.
+ * @param off The segment offset.
+ * @param uSel The protected mode segment selector.
+ */
+BS3_CMN_PROTO_STUB(uint32_t, Bs3SelProtFar32ToFlat32,(uint32_t off, uint16_t uSel));
+
+/**
+ * Converts a current mode 32-bit far pointer to a 32-bit flat address.
+ *
+ * @returns 32-bit flat address.
+ * @param off The segment offset.
+ * @param uSel The current mode segment selector.
+ */
+BS3_CMN_PROTO_STUB(uint32_t, Bs3SelFar32ToFlat32,(uint32_t off, uint16_t uSel));
+
+/**
+ * Wrapper around Bs3SelFar32ToFlat32 that makes it easier to use in tight
+ * assembly spots.
+ *
+ * @returns 32-bit flat address.
+ * @param off The segment offset.
+ * @param uSel The current mode segment selector.
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(6, uint32_t, Bs3SelFar32ToFlat32NoClobber,(uint32_t off, uint16_t uSel));
+
+/**
+ * Converts a real mode code segment to a protected mode code segment selector.
+ *
+ * @returns protected mode segment selector.
+ * @param uRealSeg Real mode code segment.
+ * @remarks All register are preserved, except return and parameter.
+ */
+BS3_CMN_PROTO_NOSB(uint16_t, Bs3SelRealModeCodeToProtMode,(uint16_t uRealSeg));
+
+/**
+ * Converts a real mode code segment to a protected mode code segment selector.
+ *
+ * @returns protected mode segment selector.
+ * @param uProtSel Real mode code segment.
+ * @remarks All register are preserved, except return and parameter.
+ */
+BS3_CMN_PROTO_NOSB(uint16_t, Bs3SelProtModeCodeToRealMode,(uint16_t uProtSel));
+
+/**
+ * Converts a flat code address to a real mode segment and offset.
+ *
+ * @returns Far real mode address (high 16-bit is segment, low is offset).
+ * @param uFlatAddr Flat code address.
+ * @remarks All register are preserved, except return and parameter.
+ */
+BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelFlatCodeToRealMode,(uint32_t uFlatAddr));
+
+/**
+ * Converts a flat code address to a protected mode 16-bit far pointer (ring-0).
+ *
+ * @returns Far 16-bit protected mode address (high 16-bit is segment selector,
+ * low is segment offset).
+ * @param uFlatAddr Flat code address.
+ * @remarks All register are preserved, except return and parameter.
+ */
+BS3_CMN_PROTO_NOSB(uint32_t, Bs3SelFlatCodeToProtFar16,(uint32_t uFlatAddr));
+
+/**
+ * Converts a far 16:16 real mode (code) address to a flat address.
+ *
+ * @returns 32-bit flat address.
+ * @param uFar1616 Far real mode address (high 16-bit is segment, low
+ * is offset).
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ * @remarks Exactly the same as Bs3SelRealModeDataToFlat, except for param.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelRealModeCodeToFlat,(PFNBS3FARADDRCONV uFar1616));
+
+/**
+ * Converts a flat data address to a real mode segment and offset.
+ *
+ * @returns Far real mode address (high 16-bit is segment, low is offset)
+ * @param uFlatAddr Flat code address.
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelFlatDataToRealMode,(uint32_t uFlatAddr));
+
+/**
+ * Converts a flat data address to a real mode segment and offset.
+ *
+ * @returns Far 16-bit protected mode address (high 16-bit is segment selector,
+ * low is segment offset).
+ * @param uFlatAddr Flat code address.
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelFlatDataToProtFar16,(uint32_t uFlatAddr));
+
+/**
+ * Converts a far 16:16 data address to a real mode segment and offset.
+ *
+ * @returns Far real mode address (high 16-bit is segment, low is offset)
+ * @param uFar1616 Far 16-bit protected mode address (high 16-bit is
+ * segment selector, low is segment offset).
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelProtFar16DataToRealMode,(uint32_t uFar1616));
+
+/**
+ * Converts a far 16:16 real mode address to a 16-bit protected mode address.
+ *
+ * @returns Far real mode address (high 16-bit is segment, low is offset)
+ * @param uFar1616 Far real mode address (high 16-bit is segment, low
+ * is offset).
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelRealModeDataToProtFar16,(uint32_t uFar1616));
+
+/**
+ * Converts a far 16:16 data address to a flat 32-bit address.
+ *
+ * @returns 32-bit flat address.
+ * @param uFar1616 Far 16-bit protected mode address (high 16-bit is
+ * segment selector, low is segment offset).
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelProtFar16DataToFlat,(uint32_t uFar1616));
+
+/**
+ * Converts a far 16:16 real mode address to a flat address.
+ *
+ * @returns 32-bit flat address.
+ * @param uFar1616 Far real mode address (high 16-bit is segment, low
+ * is offset).
+ * @remarks All register are preserved, except return.
+ * @remarks No 20h scratch space required in 64-bit mode.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelRealModeDataToFlat,(uint32_t uFar1616));
+
+/**
+ * Converts a link-time pointer to a current context pointer.
+ *
+ * @returns Converted pointer.
+ * @param pvLnkPtr The pointer the linker produced.
+ */
+BS3_CMN_PROTO_FARSTUB(4, void BS3_FAR *, Bs3SelLnkPtrToCurPtr,(void BS3_FAR *pvLnkPtr));
+
+/**
+ * Converts a link-time pointer to a flat address.
+ *
+ * @returns 32-bit flag address.
+ * @param pvLnkPtr The pointer the linker produced.
+ */
+BS3_CMN_PROTO_FARSTUB(4, uint32_t, Bs3SelLnkPtrToFlat,(void BS3_FAR *pvLnkPtr));
+
+/**
+ * Gets a flat address from a working poitner.
+ *
+ * @returns flat address (32-bit or 64-bit).
+ * @param pv Current context pointer.
+ */
+DECLINLINE(RTCCUINTXREG) Bs3SelPtrToFlat(void BS3_FAR *pv)
+{
+#if ARCH_BITS == 16
+ return BS3_CMN_FN_NM(Bs3SelFar32ToFlat32)(BS3_FP_OFF(pv), BS3_FP_SEG(pv));
+#else
+ return (uintptr_t)pv;
+#endif
+}
+
+/**
+ * Sets up a 16-bit read-write data selector with ring-3 access and 64KB limit.
+ *
+ * @param pDesc Pointer to the descriptor table entry.
+ * @param uBaseAddr The base address of the descriptor.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SelSetup16BitData,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr));
+
+/**
+ * Sets up a 16-bit execute-read selector with a 64KB limit.
+ *
+ * @param pDesc Pointer to the descriptor table entry.
+ * @param uBaseAddr The base address of the descriptor.
+ * @param bDpl The descriptor privilege level.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SelSetup16BitCode,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr, uint8_t bDpl));
+
+/**
+ * Sets up a 32-bit execute-read selector with a user specified limit.
+ *
+ * @param pDesc Pointer to the descriptor table entry.
+ * @param uBaseAddr The base address of the descriptor.
+ * @param uLimit The limit. (This is included here and not in the 16-bit
+ * functions because we're more likely to want to set it
+ * than for 16-bit selectors.)
+ * @param bDpl The descriptor privilege level.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SelSetup32BitCode,(X86DESC BS3_FAR *pDesc, uint32_t uBaseAddr, uint32_t uLimit, uint8_t bDpl));
+
+/**
+ * Sets up a 16-bit or 32-bit gate descriptor.
+ *
+ * This can be used both for GDT/LDT and IDT.
+ *
+ * @param pDesc Pointer to the descriptor table entry.
+ * @param bType The gate type.
+ * @param bDpl The gate DPL.
+ * @param uSel The gate selector value.
+ * @param off The gate IP/EIP value.
+ * @param cParams Number of parameters to copy if call-gate.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SelSetupGate,(X86DESC BS3_FAR *pDesc, uint8_t bType, uint8_t bDpl,
+ uint16_t uSel, uint32_t off, uint8_t cParams));
+
+/**
+ * Sets up a 64-bit gate descriptor.
+ *
+ * This can be used both for GDT/LDT and IDT.
+ *
+ * @param pDescPair Pointer to the _two_ descriptor table entries.
+ * @param bType The gate type.
+ * @param bDpl The gate DPL.
+ * @param uSel The gate selector value.
+ * @param off The gate IP/EIP value.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SelSetupGate64,(X86DESC BS3_FAR *pDescPair, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint64_t off));
+
+
+/**
+ * Slab control structure list head.
+ *
+ * The slabs on the list must all have the same chunk size.
+ */
+typedef struct BS3SLABHEAD
+{
+ /** Pointer to the first slab. */
+ BS3_XPTR_MEMBER(struct BS3SLABCTL, pFirst);
+ /** The allocation chunk size. */
+ uint16_t cbChunk;
+ /** Number of slabs in the list. */
+ uint16_t cSlabs;
+ /** Number of chunks in the list. */
+ uint32_t cChunks;
+ /** Number of free chunks. */
+ uint32_t cFreeChunks;
+} BS3SLABHEAD;
+AssertCompileSize(BS3SLABHEAD, 16);
+/** Pointer to a slab list head. */
+typedef BS3SLABHEAD BS3_FAR *PBS3SLABHEAD;
+
+/**
+ * Allocation slab control structure.
+ *
+ * This may live at the start of the slab for 4KB slabs, while in a separate
+ * static location for the larger ones.
+ */
+typedef struct BS3SLABCTL
+{
+ /** Pointer to the next slab control structure in this list. */
+ BS3_XPTR_MEMBER(struct BS3SLABCTL, pNext);
+ /** Pointer to the slab list head. */
+ BS3_XPTR_MEMBER(BS3SLABHEAD, pHead);
+ /** The base address of the slab. */
+ BS3_XPTR_MEMBER(uint8_t, pbStart);
+ /** Number of chunks in this slab. */
+ uint16_t cChunks;
+ /** Number of currently free chunks. */
+ uint16_t cFreeChunks;
+ /** The chunk size. */
+ uint16_t cbChunk;
+ /** The shift count corresponding to cbChunk.
+ * This is for turning a chunk number into a byte offset and vice versa. */
+ uint16_t cChunkShift;
+ /** Bitmap where set bits indicates allocated blocks (variable size,
+ * multiple of 4). */
+ uint8_t bmAllocated[4];
+} BS3SLABCTL;
+/** Pointer to a bs3kit slab control structure. */
+typedef BS3SLABCTL BS3_FAR *PBS3SLABCTL;
+
+/** The chunks must all be in the same 16-bit segment tile. */
+#define BS3_SLAB_ALLOC_F_SAME_TILE UINT16_C(0x0001)
+
+/**
+ * Initializes a slab.
+ *
+ * @param pSlabCtl The slab control structure to initialize.
+ * @param cbSlabCtl The size of the slab control structure.
+ * @param uFlatSlabPtr The base address of the slab.
+ * @param cbSlab The size of the slab.
+ * @param cbChunk The chunk size.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SlabInit,(PBS3SLABCTL pSlabCtl, size_t cbSlabCtl, uint32_t uFlatSlabPtr,
+ uint32_t cbSlab, uint16_t cbChunk));
+
+/**
+ * Allocates one chunk from a slab.
+ *
+ * @returns Pointer to a chunk on success, NULL if we're out of chunks.
+ * @param pSlabCtl The slab control structure to allocate from.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3SlabAlloc,(PBS3SLABCTL pSlabCtl));
+
+/**
+ * Allocates one or more chunks rom a slab.
+ *
+ * @returns Pointer to the request number of chunks on success, NULL if we're
+ * out of chunks.
+ * @param pSlabCtl The slab control structure to allocate from.
+ * @param cChunks The number of contiguous chunks we want.
+ * @param fFlags Flags, see BS3_SLAB_ALLOC_F_XXX
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3SlabAllocEx,(PBS3SLABCTL pSlabCtl, uint16_t cChunks, uint16_t fFlags));
+
+/**
+ * Frees one or more chunks from a slab.
+ *
+ * @returns Number of chunks actually freed. When correctly used, this will
+ * match the @a cChunks parameter, of course.
+ * @param pSlabCtl The slab control structure to free from.
+ * @param uFlatChunkPtr The flat address of the chunks to free.
+ * @param cChunks The number of contiguous chunks to free.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3SlabFree,(PBS3SLABCTL pSlabCtl, uint32_t uFlatChunkPtr, uint16_t cChunks));
+
+
+/**
+ * Initializes the given slab list head.
+ *
+ * @param pHead The slab list head.
+ * @param cbChunk The chunk size.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SlabListInit,(PBS3SLABHEAD pHead, uint16_t cbChunk));
+
+/**
+ * Adds an initialized slab control structure to the list.
+ *
+ * @param pHead The slab list head to add it to.
+ * @param pSlabCtl The slab control structure to add.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SlabListAdd,(PBS3SLABHEAD pHead, PBS3SLABCTL pSlabCtl));
+
+/**
+ * Allocates one chunk.
+ *
+ * @returns Pointer to a chunk on success, NULL if we're out of chunks.
+ * @param pHead The slab list to allocate from.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3SlabListAlloc,(PBS3SLABHEAD pHead));
+
+/**
+ * Allocates one or more chunks.
+ *
+ * @returns Pointer to the request number of chunks on success, NULL if we're
+ * out of chunks.
+ * @param pHead The slab list to allocate from.
+ * @param cChunks The number of contiguous chunks we want.
+ * @param fFlags Flags, see BS3_SLAB_ALLOC_F_XXX
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3SlabListAllocEx,(PBS3SLABHEAD pHead, uint16_t cChunks, uint16_t fFlags));
+
+/**
+ * Frees one or more chunks from a slab list.
+ *
+ * @param pHead The slab list to allocate from.
+ * @param pvChunks Pointer to the first chunk to free.
+ * @param cChunks The number of contiguous chunks to free.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3SlabListFree,(PBS3SLABHEAD pHead, void BS3_FAR *pvChunks, uint16_t cChunks));
+
+/**
+ * Allocation addressing constraints.
+ */
+typedef enum BS3MEMKIND
+{
+ /** Invalid zero type. */
+ BS3MEMKIND_INVALID = 0,
+ /** Real mode addressable memory. */
+ BS3MEMKIND_REAL,
+ /** Memory addressable using the 16-bit protected mode tiling. */
+ BS3MEMKIND_TILED,
+ /** Memory addressable using 32-bit flat addressing. */
+ BS3MEMKIND_FLAT32,
+ /** Memory addressable using 64-bit flat addressing. */
+ BS3MEMKIND_FLAT64,
+ /** End of valid types. */
+ BS3MEMKIND_END,
+} BS3MEMKIND;
+
+/**
+ * Allocates low memory.
+ *
+ * @returns Pointer to a chunk on success, NULL if we're out of chunks.
+ * @param enmKind The kind of addressing constraints imposed on the
+ * allocation.
+ * @param cb How much to allocate.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemAlloc,(BS3MEMKIND enmKind, size_t cb));
+
+/**
+ * Allocates zero'ed memory.
+ *
+ * @param enmKind The kind of addressing constraints imposed on the
+ * allocation.
+ * @param cb How much to allocate.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemAllocZ,(BS3MEMKIND enmKind, size_t cb));
+
+/**
+ * Frees memory.
+ *
+ * @returns Pointer to a chunk on success, NULL if we're out of chunks.
+ * @param pv The memory to free (returned by #Bs3MemAlloc).
+ * @param cb The size of the allocation.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3MemFree,(void BS3_FAR *pv, size_t cb));
+
+/**
+ * Allocates a page with non-present pages on each side.
+ *
+ * @returns Pointer to the usable page. NULL on failure. Use
+ * Bs3MemGuardedTestPageFree to free the allocation.
+ * @param enmKind The kind of addressing constraints imposed on the
+ * allocation.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemGuardedTestPageAlloc,(BS3MEMKIND enmKind));
+
+/**
+ * Allocates a page with pages on each side to the @a fPte specification.
+ *
+ * @returns Pointer to the usable page. NULL on failure. Use
+ * Bs3MemGuardedTestPageFree to free the allocation.
+ * @param enmKind The kind of addressing constraints imposed on the
+ * allocation.
+ * @param fPte The page table entry specification for the guard pages.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3MemGuardedTestPageAllocEx,(BS3MEMKIND enmKind, uint64_t fPte));
+
+/**
+ * Frees guarded page allocated by Bs3MemGuardedTestPageAlloc or
+ * Bs3MemGuardedTestPageAllocEx.
+ *
+ * @param pvGuardedPage Pointer returned by Bs3MemGuardedTestPageAlloc or
+ * Bs3MemGuardedTestPageAllocEx. NULL is ignored.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3MemGuardedTestPageFree,(void BS3_FAR *pvGuardedPage));
+
+/**
+ * Print all heap info.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3MemPrintInfo, (void));
+
+/** The end RAM address below 4GB (approximately). */
+extern uint32_t g_uBs3EndOfRamBelow4G;
+/** The end RAM address above 4GB, zero if no memory above 4GB. */
+extern uint64_t g_uBs3EndOfRamAbove4G;
+
+
+/**
+ * Enables the A20 gate.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20Enable,(void));
+
+/**
+ * Enables the A20 gate via the keyboard controller
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20EnableViaKbd,(void));
+
+/**
+ * Enables the A20 gate via the PS/2 control port A.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20EnableViaPortA,(void));
+
+/**
+ * Disables the A20 gate.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20Disable,(void));
+
+/**
+ * Disables the A20 gate via the keyboard controller
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20DisableViaKbd,(void));
+
+/**
+ * Disables the A20 gate via the PS/2 control port A.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3A20DisableViaPortA,(void));
+
+
+/**
+ * Initializes root page tables for page protected mode (PP16, PP32).
+ *
+ * @returns IPRT status code.
+ * @remarks Must not be called in real-mode!
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingInitRootForPP,(void));
+
+/**
+ * Initializes root page tables for PAE page protected mode (PAE16, PAE32).
+ *
+ * @returns IPRT status code.
+ * @remarks The default long mode page tables depends on the PAE ones.
+ * @remarks Must not be called in real-mode!
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingInitRootForPAE,(void));
+
+/**
+ * Initializes root page tables for long mode (LM16, LM32, LM64).
+ *
+ * @returns IPRT status code.
+ * @remarks The default long mode page tables depends on the PAE ones.
+ * @remarks Must not be called in real-mode!
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingInitRootForLM,(void));
+
+/**
+ * Maps all RAM above 4GB into the long mode page tables.
+ *
+ * This requires Bs3PagingInitRootForLM to have been called first.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_WRONG_ORDER if Bs3PagingInitRootForLM wasn't called.
+ * @retval VINF_ALREADY_INITIALIZED if already called or someone mapped
+ * something else above 4GiB already.
+ * @retval VERR_OUT_OF_RANGE if too much RAM (more than 2^47 bytes).
+ * @retval VERR_NO_MEMORY if no more memory for paging structures.
+ * @retval VERR_UNSUPPORTED_ALIGNMENT if the bs3kit allocator malfunctioned and
+ * didn't give us page aligned memory as it should.
+ *
+ * @param puFailurePoint Where to return the address where we encountered
+ * a failure. Optional.
+ *
+ * @remarks Must be called in 32-bit or 64-bit mode as paging structures will be
+ * allocated using BS3MEMKIND_FLAT32, as there might not be sufficient
+ * BS3MEMKIND_TILED memory around. (Also, too it's simply too much of
+ * a bother to deal with 16-bit for something that's long-mode only.)
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingMapRamAbove4GForLM,(uint64_t *puFailurePoint));
+
+/**
+ * Modifies the page table protection of an address range.
+ *
+ * This only works on the lowest level of the page tables in the current mode.
+ *
+ * Since we generally use the largest pages available when setting up the
+ * initial page tables, this function will usually have to allocate and create
+ * more tables. This may fail if we're low on memory.
+ *
+ * @returns IPRT status code.
+ * @param uFlat The flat address of the first page in the range (rounded
+ * down nearest page boundrary).
+ * @param cb The range size from @a pv (rounded up to nearest page boundrary).
+ * @param fSet Mask of zero or more X86_PTE_XXX values to set for the range.
+ * @param fClear Mask of zero or more X86_PTE_XXX values to clear for the range.
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingProtect,(uint64_t uFlat, uint64_t cb, uint64_t fSet, uint64_t fClear));
+
+/**
+ * Modifies the page table protection of an address range.
+ *
+ * This only works on the lowest level of the page tables in the current mode.
+ *
+ * Since we generally use the largest pages available when setting up the
+ * initial page tables, this function will usually have to allocate and create
+ * more tables. This may fail if we're low on memory.
+ *
+ * @returns IPRT status code.
+ * @param pv The address of the first page in the range (rounded
+ * down nearest page boundrary).
+ * @param cb The range size from @a pv (rounded up to nearest page boundrary).
+ * @param fSet Mask of zero or more X86_PTE_XXX values to set for the range.
+ * @param fClear Mask of zero or more X86_PTE_XXX values to clear for the range.
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingProtectPtr,(void BS3_FAR *pv, size_t cb, uint64_t fSet, uint64_t fClear));
+
+/**
+ * Aliases (maps) one or more contiguous physical pages to a virtual range.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INVALID_PARAMETER if we're in legacy paging mode and @a uDst or
+ * @a uPhysToAlias are not compatible with legacy paging.
+ * @retval VERR_OUT_OF_RANGE if we cannot traverse the page tables in this mode
+ * (typically real mode or v86, maybe 16-bit PE).
+ * @retval VERR_NO_MEMORY if we cannot allocate page tables for splitting up
+ * the necessary large pages. No aliasing was performed.
+ *
+ * @param uDst The virtual address to map it at. Rounded down
+ * to the nearest page (@a cbHowMuch is adjusted
+ * up).
+ * @param uPhysToAlias The physical address of the first page in the
+ * (contiguous) range to map. Chopped down to
+ * nearest page boundrary (@a cbHowMuch is not
+ * adjusted).
+ * @param cbHowMuch How much to map. Rounded up to nearest page.
+ * @param fPte The PTE flags.
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingAlias,(uint64_t uDst, uint64_t uPhysToAlias, uint32_t cbHowMuch, uint64_t fPte));
+
+/**
+ * Unaliases memory, i.e. restores the 1:1 mapping.
+ *
+ * @returns VBox status code. Cannot fail if @a uDst and @a cbHowMuch specify
+ * the range of a successful Bs3PagingAlias call, however it may run
+ * out of memory if it's breaking new ground.
+ *
+ * @param uDst The virtual address to restore to 1:1 mapping.
+ * Rounded down to the nearest page (@a cbHowMuch
+ * is adjusted up).
+ * @param cbHowMuch How much to restore. Rounded up to nearest page.
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingUnalias,(uint64_t uDst, uint32_t cbHowMuch));
+
+/**
+ * Get the pointer to the PTE for the given address.
+ *
+ * @returns Pointer to the PTE.
+ * @param uFlat The flat address of the page which PTE we want.
+ * @param prc Where to return additional error info. Optional.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3PagingGetPte,(uint64_t uFlat, int *prc));
+
+/**
+ * Paging information for an address.
+ */
+typedef struct BS3PAGINGINFO4ADDR
+{
+ /** The depth of the system's paging mode.
+ * This is always 2 for legacy, 3 for PAE and 4 for long mode. */
+ uint8_t cEntries;
+ /** The size of the page structures (the entires). */
+ uint8_t cbEntry;
+ /** Flags defined for future fun, currently zero. */
+ uint16_t fFlags;
+ /** Union display different view on the entry pointers. */
+ union
+ {
+ /** Pointer to the page structure entries, starting with the PTE as 0.
+ * If large pages are involved, the first entry will be NULL (first two if 1GB
+ * page). Same if the address is invalid on a higher level. */
+ uint8_t BS3_FAR *apbEntries[4];
+ /** Alternative view for legacy mode. */
+ struct
+ {
+ X86PTE BS3_FAR *pPte;
+ X86PDE BS3_FAR *pPde;
+ void *pvUnused2;
+ void *pvUnused3;
+ } Legacy;
+ /** Alternative view for PAE and Long mode. */
+ struct
+ {
+ X86PTEPAE BS3_FAR *pPte;
+ X86PDEPAE BS3_FAR *pPde;
+ X86PDPE BS3_FAR *pPdpe;
+ X86PML4E BS3_FAR *pPml4e;
+ } Pae;
+ } u;
+} BS3PAGINGINFO4ADDR;
+/** Pointer to paging information for and address. */
+typedef BS3PAGINGINFO4ADDR BS3_FAR *PBS3PAGINGINFO4ADDR;
+
+/**
+ * Queries paging information about the given virtual address.
+ *
+ * @returns VBox status code.
+ * @param uFlat The flat address to query information about.
+ * @param pPgInfo Where to return the information.
+ */
+BS3_CMN_PROTO_STUB(int, Bs3PagingQueryAddressInfo,(uint64_t uFlat, PBS3PAGINGINFO4ADDR pPgInfo));
+
+
+/** The physical / flat address of the buffer backing the canonical traps.
+ * This buffer is spread equally on each side of the 64-bit non-canonical
+ * address divide. Non-64-bit code can use this to setup trick shots and
+ * inspect their results. */
+extern uint32_t g_uBs3PagingCanonicalTrapsAddr;
+/** The size of the buffer at g_uPagingCanonicalTraps (both sides). */
+extern uint16_t g_cbBs3PagingCanonicalTraps;
+/** The size of one trap buffer (low or high).
+ * This is g_cbBs3PagingCanonicalTraps divided by two. */
+extern uint16_t g_cbBs3PagingOneCanonicalTrap;
+
+/**
+ * Sets up the 64-bit canonical address space trap buffers, if neceessary.
+ *
+ * @returns Pointer to the buffers (i.e. the first page of the low one) on
+ * success. NULL on failure.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3PagingSetupCanonicalTraps,(void));
+
+/**
+ * Waits for the keyboard controller to become ready.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3KbdWait,(void));
+
+/**
+ * Sends a read command to the keyboard controller and gets the result.
+ *
+ * The caller is responsible for making sure the keyboard controller is ready
+ * for a command (call #Bs3KbdWait if unsure).
+ *
+ * @returns The value read is returned (in al).
+ * @param bCmd The read command.
+ */
+BS3_CMN_PROTO_NOSB(uint8_t, Bs3KbdRead,(uint8_t bCmd));
+
+/**
+ * Sends a write command to the keyboard controller and then sends the data.
+ *
+ * The caller is responsible for making sure the keyboard controller is ready
+ * for a command (call #Bs3KbdWait if unsure).
+ *
+ * @param bCmd The write command.
+ * @param bData The data to write.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3KbdWrite,(uint8_t bCmd, uint8_t bData));
+
+
+/**
+ * Configures the PIC, once only.
+ *
+ * Subsequent calls to this function will not do anything.
+ *
+ * The PIC will be programmed to use IDT/IVT vectors 0x70 thru 0x7f, auto
+ * end-of-interrupt, and all IRQs masked. The individual PIC users will have to
+ * use #Bs3PicUpdateMask unmask their IRQ once they've got all the handlers
+ * installed.
+ *
+ * @param fForcedReInit Force a reinitialization.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3PicSetup,(bool fForcedReInit));
+
+/**
+ * Updates the PIC masks.
+ *
+ * @returns The new mask - master in low, slave in high byte.
+ * @param fAndMask Things to keep as-is. Master in low, slave in high byte.
+ * @param fOrMask Things to start masking. Ditto wrt bytes.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3PicUpdateMask,(uint16_t fAndMask, uint16_t fOrMask));
+
+/**
+ * Disables all IRQs on the PIC.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3PicMaskAll,(void));
+
+
+/**
+ * Sets up the PIT for periodic callback.
+ *
+ * @param cHzDesired The desired Hz. Zero means max interval length
+ * (18.2Hz). Plase check the various PIT globals for
+ * the actual interval length.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3PitSetupAndEnablePeriodTimer,(uint16_t cHzDesired));
+
+/**
+ * Disables the PIT if active.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3PitDisable,(void));
+
+/** Nanoseconds (approx) since last the PIT timer was started. */
+extern uint64_t volatile g_cBs3PitNs;
+/** Milliseconds seconds (very approx) since last the PIT timer was started. */
+extern uint64_t volatile g_cBs3PitMs;
+/** Number of ticks since last the PIT timer was started. */
+extern uint32_t volatile g_cBs3PitTicks;
+/** The current interval in nanoseconds.
+ * This is 0 if not yet started (cleared by Bs3PitDisable). */
+extern uint32_t g_cBs3PitIntervalNs;
+/** The current interval in milliseconds (approximately).
+ * This is 0 if not yet started (cleared by Bs3PitDisable). */
+extern uint16_t g_cBs3PitIntervalMs;
+/** The current PIT frequency (approximately).
+ * 0 if not yet started (cleared by Bs3PitDisable; used for checking the
+ * state internally). */
+extern uint16_t volatile g_cBs3PitIntervalHz;
+
+
+/**
+ * Call 16-bit prot mode function from v8086 mode.
+ *
+ * This switches from v8086 mode to 16-bit protected mode (code) and executed
+ * @a fpfnCall with @a cbParams bytes of parameters pushed on the stack.
+ * Afterwards it switches back to v8086 mode and returns a 16-bit status code.
+ *
+ * @returns 16-bit status code if the function returned anything.
+ * @param fpfnCall Far real mode pointer to the function to call.
+ * @param cbParams The size of the parameter list, in bytes.
+ * @param ... The parameters.
+ * @sa Bs3SwitchTo32BitAndCallC
+ */
+BS3_CMN_PROTO_STUB(int, Bs3SwitchFromV86To16BitAndCallC,(FPFNBS3FAR fpfnCall, unsigned cbParams, ...));
+
+
+/**
+ * BS3 integer register.
+ */
+typedef union BS3REG
+{
+ /** 8-bit unsigned integer. */
+ uint8_t u8;
+ /** 16-bit unsigned integer. */
+ uint16_t u16;
+ /** 32-bit unsigned integer. */
+ uint32_t u32;
+ /** 64-bit unsigned integer. */
+ uint64_t u64;
+ /** Full unsigned integer. */
+ uint64_t u;
+ /** High/low byte view. */
+ struct
+ {
+ uint8_t bLo;
+ uint8_t bHi;
+ } b;
+ /** 8-bit view. */
+ uint8_t au8[8];
+ /** 16-bit view. */
+ uint16_t au16[4];
+ /** 32-bit view. */
+ uint32_t au32[2];
+ /** Unsigned integer, depending on compiler context.
+ * This generally follows ARCH_BITS. */
+ RTCCUINTREG uCcReg;
+ /** Extended unsigned integer, depending on compiler context.
+ * This is 32-bit in 16-bit and 32-bit compiler contexts, and 64-bit in
+ * 64-bit. */
+ RTCCUINTXREG uCcXReg;
+} BS3REG;
+/** Pointer to an integer register. */
+typedef BS3REG BS3_FAR *PBS3REG;
+/** Pointer to a const integer register. */
+typedef BS3REG const BS3_FAR *PCBS3REG;
+
+/**
+ * Register context (without FPU).
+ */
+typedef struct BS3REGCTX
+{
+ BS3REG rax; /**< 0x00 */
+ BS3REG rcx; /**< 0x08 */
+ BS3REG rdx; /**< 0x10 */
+ BS3REG rbx; /**< 0x18 */
+ BS3REG rsp; /**< 0x20 */
+ BS3REG rbp; /**< 0x28 */
+ BS3REG rsi; /**< 0x30 */
+ BS3REG rdi; /**< 0x38 */
+ BS3REG r8; /**< 0x40 */
+ BS3REG r9; /**< 0x48 */
+ BS3REG r10; /**< 0x50 */
+ BS3REG r11; /**< 0x58 */
+ BS3REG r12; /**< 0x60 */
+ BS3REG r13; /**< 0x68 */
+ BS3REG r14; /**< 0x70 */
+ BS3REG r15; /**< 0x78 */
+ BS3REG rflags; /**< 0x80 */
+ BS3REG rip; /**< 0x88 */
+ uint16_t cs; /**< 0x90 */
+ uint16_t ds; /**< 0x92 */
+ uint16_t es; /**< 0x94 */
+ uint16_t fs; /**< 0x96 */
+ uint16_t gs; /**< 0x98 */
+ uint16_t ss; /**< 0x9a */
+ uint16_t tr; /**< 0x9c */
+ uint16_t ldtr; /**< 0x9e */
+ uint8_t bMode; /**< 0xa0: BS3_MODE_XXX. */
+ uint8_t bCpl; /**< 0xa1: 0-3, 0 is used for real mode. */
+ uint8_t fbFlags; /**< 0xa2: BS3REG_CTX_F_XXX */
+ uint8_t abPadding[5]; /**< 0xa3 */
+ BS3REG cr0; /**< 0xa8 */
+ BS3REG cr2; /**< 0xb0 */
+ BS3REG cr3; /**< 0xb8 */
+ BS3REG cr4; /**< 0xc0 */
+ uint64_t uUnused; /**< 0xc8 */
+} BS3REGCTX;
+AssertCompileSize(BS3REGCTX, 0xd0);
+/** Pointer to a register context. */
+typedef BS3REGCTX BS3_FAR *PBS3REGCTX;
+/** Pointer to a const register context. */
+typedef BS3REGCTX const BS3_FAR *PCBS3REGCTX;
+
+/** @name BS3REG_CTX_F_XXX - BS3REGCTX::fbFlags masks.
+ * @{ */
+/** The CR0 is MSW (only low 16-bit). */
+#define BS3REG_CTX_F_NO_CR0_IS_MSW UINT8_C(0x01)
+/** No CR2 and CR3 values. Not in CPL 0 or CPU too old for CR2 & CR3. */
+#define BS3REG_CTX_F_NO_CR2_CR3 UINT8_C(0x02)
+/** No CR4 value. The CPU is too old for CR4. */
+#define BS3REG_CTX_F_NO_CR4 UINT8_C(0x04)
+/** No TR and LDTR values. Context gathered in real mode or v8086 mode. */
+#define BS3REG_CTX_F_NO_TR_LDTR UINT8_C(0x08)
+/** The context doesn't have valid values for AMD64 GPR extensions. */
+#define BS3REG_CTX_F_NO_AMD64 UINT8_C(0x10)
+/** @} */
+
+/**
+ * Saves the current register context.
+ *
+ * @param pRegCtx Where to store the register context.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3RegCtxSave,(PBS3REGCTX pRegCtx));
+
+/**
+ * Switch to the specified CPU bitcount, reserve additional stack and save the
+ * CPU context.
+ *
+ * This is for writing more flexible test drivers that can test more than the
+ * CPU bitcount (16-bit, 32-bit, 64-bit, and virtual 8086) of the driver itself.
+ * For instance a 32-bit driver can do V86 and 16-bit testing, thus saving space
+ * by avoiding duplicate 16-bit driver code.
+ *
+ * @param pRegCtx Where to store the register context.
+ * @param bBitMode Bit mode to switch to, BS3_MODE_CODE_XXX. Only
+ * BS3_MODE_CODE_MASK is used, other bits are ignored
+ * to make it possible to pass a full mode value.
+ * @param cbExtraStack Number of bytes of additional stack to allocate.
+ */
+BS3_CMN_PROTO_FARSTUB(8, void, Bs3RegCtxSaveEx,(PBS3REGCTX pRegCtx, uint8_t bBitMode, uint16_t cbExtraStack));
+
+/**
+ * This is Bs3RegCtxSaveEx with automatic Bs3RegCtxConvertV86ToRm thrown in.
+ *
+ * This is for simplifying writing 32-bit test drivers that covers real-mode as
+ * well as virtual 8086, 16-bit, 32-bit, and 64-bit modes.
+ *
+ * @param pRegCtx Where to store the register context.
+ * @param bMode The mode to get a context for. If this isn't
+ * BS3_MODE_RM, the BS3_MODE_SYS_MASK has to match the
+ * one of the current mode.
+ * @param cbExtraStack Number of bytes of additional stack to allocate.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSaveForMode,(PBS3REGCTX pRegCtx, uint8_t bMode, uint16_t cbExtraStack));
+
+/**
+ * Transforms a register context to a different ring.
+ *
+ * @param pRegCtx The register context.
+ * @param bRing The target ring (0..3).
+ *
+ * @note Do _NOT_ call this for creating real mode or v8086 contexts, because
+ * it will always output a protected mode context!
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxConvertToRingX,(PBS3REGCTX pRegCtx, uint8_t bRing));
+
+/**
+ * Transforms a V8086 register context to a real mode one.
+ *
+ * @param pRegCtx The register context.
+ *
+ * @note Will assert if called on a non-V8086 context.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxConvertV86ToRm,(PBS3REGCTX pRegCtx));
+
+/**
+ * Restores a register context.
+ *
+ * @param pRegCtx The register context to be restored and resumed.
+ * @param fFlags BS3REGCTXRESTORE_F_XXX.
+ *
+ * @remarks Will switch to ring-0.
+ * @remarks Does not return.
+ */
+BS3_CMN_PROTO_NOSB(DECL_NO_RETURN(void), Bs3RegCtxRestore,(PCBS3REGCTX pRegCtx, uint16_t fFlags));
+#if !defined(BS3_KIT_WITH_NO_RETURN) && defined(__WATCOMC__)
+# pragma aux Bs3RegCtxRestore_c16 "_Bs3RegCtxRestore_aborts_c16" __aborts
+# pragma aux Bs3RegCtxRestore_f16 "_Bs3RegCtxRestore_aborts_f16" __aborts
+# pragma aux Bs3RegCtxRestore_c32 "_Bs3RegCtxRestore_aborts_c32" __aborts
+#endif
+
+/** @name Flags for Bs3RegCtxRestore
+ * @{ */
+/** Skip restoring the CRx registers. */
+#define BS3REGCTXRESTORE_F_SKIP_CRX UINT16_C(0x0001)
+/** Sets g_fBs3TrapNoV86Assist. */
+#define BS3REGCTXRESTORE_F_NO_V86_ASSIST UINT16_C(0x0002)
+/** @} */
+
+/**
+ * Prints the register context.
+ *
+ * @param pRegCtx The register context to be printed.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxPrint,(PCBS3REGCTX pRegCtx));
+
+/**
+ * Sets a GPR and segment register to point at the same location as @a uFlat.
+ *
+ * @param pRegCtx The register context.
+ * @param pGpr The general purpose register to set (points within
+ * @a pRegCtx).
+ * @param pSel The selector register (points within @a pRegCtx).
+ * @param uFlat Flat location address.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetGrpSegFromFlat,(PBS3REGCTX pRegCtx, PBS3REG pGpr, PRTSEL pSel, RTCCUINTXREG uFlat));
+
+/**
+ * Sets a GPR and segment register to point at the same location as @a ovPtr.
+ *
+ * @param pRegCtx The register context.
+ * @param pGpr The general purpose register to set (points within
+ * @a pRegCtx).
+ * @param pSel The selector register (points within @a pRegCtx).
+ * @param pvPtr Current context pointer.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetGrpSegFromCurPtr,(PBS3REGCTX pRegCtx, PBS3REG pGpr, PRTSEL pSel, void BS3_FAR *pvPtr));
+
+/**
+ * Sets a GPR and DS to point at the same location as @a pvPtr.
+ *
+ * @param pRegCtx The register context.
+ * @param pGpr The general purpose register to set (points within
+ * @a pRegCtx).
+ * @param pvPtr Current context pointer.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetGrpDsFromCurPtr,(PBS3REGCTX pRegCtx, PBS3REG pGpr, void BS3_FAR *pvPtr));
+
+/**
+ * Sets CS:RIP to point at the same piece of code as @a uFlatCode.
+ *
+ * @param pRegCtx The register context.
+ * @param uFlatCode Flat code pointer
+ * @sa Bs3RegCtxSetRipCsFromLnkPtr, Bs3RegCtxSetRipCsFromCurPtr
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetRipCsFromFlat,(PBS3REGCTX pRegCtx, RTCCUINTXREG uFlatCode));
+
+/**
+ * Sets CS:RIP to point at the same piece of code as @a pfnCode.
+ *
+ * The 16-bit edition of this function expects a far 16:16 address as written by
+ * the linker (i.e. real mode).
+ *
+ * @param pRegCtx The register context.
+ * @param pfnCode Pointer to the code. In 32-bit and 64-bit mode this is a
+ * flat address, while in 16-bit it's a far 16:16 address
+ * as fixed up by the linker (real mode selector). This
+ * address is converted to match the mode of the context.
+ * @sa Bs3RegCtxSetRipCsFromCurPtr, Bs3RegCtxSetRipCsFromFlat
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetRipCsFromLnkPtr,(PBS3REGCTX pRegCtx, FPFNBS3FAR pfnCode));
+
+/**
+ * Sets CS:RIP to point at the same piece of code as @a pfnCode.
+ *
+ * @param pRegCtx The register context.
+ * @param pfnCode Pointer to the code. Current mode pointer.
+ * @sa Bs3RegCtxSetRipCsFromLnkPtr, Bs3RegCtxSetRipCsFromFlat
+ */
+BS3_CMN_PROTO_STUB(void, Bs3RegCtxSetRipCsFromCurPtr,(PBS3REGCTX pRegCtx, FPFNBS3FAR pfnCode));
+
+/**
+ * Sets a GPR by number.
+ *
+ * @return true if @a iGpr is valid, false if not.
+ * @param pRegCtx The register context.
+ * @param iGpr The GPR number.
+ * @param uValue The new value.
+ * @param cbValue The size of the value: 1, 2, 4 or 8.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3RegCtxSetGpr,(PBS3REGCTX pRegCtx, uint8_t iGpr, uint64_t uValue, uint8_t cb));
+
+/**
+ * Gets the stack pointer as a current context pointer.
+ *
+ * @return Pointer to the top of the stack. NULL on failure.
+ * @param pRegCtx The register context.
+ */
+BS3_CMN_PROTO_STUB(void BS3_FAR *, Bs3RegCtxGetRspSsAsCurPtr,(PBS3REGCTX pRegCtx));
+
+
+/**
+ * The method to be used to save and restore the extended context.
+ */
+typedef enum BS3EXTCTXMETHOD
+{
+ BS3EXTCTXMETHOD_INVALID = 0,
+ BS3EXTCTXMETHOD_ANCIENT, /**< Ancient fnsave/frstor format. */
+ BS3EXTCTXMETHOD_FXSAVE, /**< fxsave/fxrstor format. */
+ BS3EXTCTXMETHOD_XSAVE, /**< xsave/xrstor format. */
+ BS3EXTCTXMETHOD_END,
+} BS3EXTCTXMETHOD;
+
+
+/**
+ * Extended CPU context (FPU, SSE, AVX, ++).
+ *
+ * @remarks Also in bs3kit.inc
+ */
+typedef struct BS3EXTCTX
+{
+ /** Dummy/magic value. */
+ uint16_t u16Magic;
+ /** The size of the structure. */
+ uint16_t cb;
+ /** The method used to save and restore the context (BS3EXTCTXMETHOD). */
+ uint8_t enmMethod;
+ uint8_t abPadding0[3];
+ /** Nominal XSAVE_C_XXX. */
+ uint64_t fXcr0Nominal;
+ /** The saved XCR0 mask (restored after xrstor). */
+ uint64_t fXcr0Saved;
+
+ /** Explicit alignment padding. */
+ uint8_t abPadding[64 - 2 - 2 - 1 - 3 - 8 - 8];
+
+ /** The context, variable size (see above).
+ * This must be aligned on a 64 byte boundrary. */
+ union
+ {
+ /** fnsave/frstor. */
+ X86FPUSTATE Ancient;
+ /** fxsave/fxrstor */
+ X86FXSTATE x87;
+ /** xsave/xrstor */
+ X86XSAVEAREA x;
+ /** Byte array view. */
+ uint8_t ab[sizeof(X86XSAVEAREA)];
+ } Ctx;
+} BS3EXTCTX;
+AssertCompileMemberAlignment(BS3EXTCTX, Ctx, 64);
+/** Pointer to an extended CPU context. */
+typedef BS3EXTCTX BS3_FAR *PBS3EXTCTX;
+/** Pointer to a const extended CPU context. */
+typedef BS3EXTCTX const BS3_FAR *PCBS3EXTCTX;
+
+/** Magic value for BS3EXTCTX. */
+#define BS3EXTCTX_MAGIC UINT16_C(0x1980)
+
+/**
+ * Allocates and initializes the extended CPU context structure.
+ *
+ * @returns The new extended CPU context structure.
+ * @param enmKind The kind of allocation to make.
+ */
+BS3_CMN_PROTO_STUB(PBS3EXTCTX, Bs3ExtCtxAlloc,(BS3MEMKIND enmKind));
+
+/**
+ * Frees an extended CPU context structure.
+ *
+ * @param pExtCtx The extended CPU context (returned by
+ * Bs3ExtCtxAlloc).
+ */
+BS3_CMN_PROTO_STUB(void, Bs3ExtCtxFree,(PBS3EXTCTX pExtCtx));
+
+/**
+ * Get the size required for a BS3EXTCTX structure.
+ *
+ * @returns size in bytes of the whole structure.
+ * @param pfFlags Where to return flags for Bs3ExtCtxInit.
+ * @note Use Bs3ExtCtxAlloc when possible.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3ExtCtxGetSize,(uint64_t *pfFlags));
+
+/**
+ * Initializes the extended CPU context structure.
+ * @returns pExtCtx
+ * @param pExtCtx The extended CPU context.
+ * @param cbExtCtx The size of the @a pExtCtx allocation.
+ * @param fFlags XSAVE_C_XXX flags.
+ */
+BS3_CMN_PROTO_STUB(PBS3EXTCTX, Bs3ExtCtxInit,(PBS3EXTCTX pExtCtx, uint16_t cbExtCtx, uint64_t fFlags));
+
+/**
+ * Saves the extended CPU state to the given structure.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @remarks All GPRs preserved.
+ */
+BS3_CMN_PROTO_FARSTUB(4, void, Bs3ExtCtxSave,(PBS3EXTCTX pExtCtx));
+
+/**
+ * Saves the extended CPU state to the given structure, when in long mode this
+ * is done from 64-bit mode to capture YMM8 thru YMM15.
+ *
+ * This is for testing 64-bit code from a 32-bit test driver.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @note Only safe to call from ring-0 at present.
+ * @remarks All GPRs preserved.
+ * @sa Bs3ExtCtxRestoreEx
+ */
+BS3_CMN_PROTO_FARSTUB(4, void, Bs3ExtCtxSaveEx,(PBS3EXTCTX pExtCtx));
+
+/**
+ * Restores the extended CPU state from the given structure.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @remarks All GPRs preserved.
+ */
+BS3_CMN_PROTO_FARSTUB(4, void, Bs3ExtCtxRestore,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Restores the extended CPU state from the given structure and in long mode
+ * switch to 64-bit mode to do this so YMM8-YMM15 are also loaded.
+ *
+ * This is for testing 64-bit code from a 32-bit test driver.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @note Only safe to call from ring-0 at present.
+ * @remarks All GPRs preserved.
+ * @sa Bs3ExtCtxSaveEx
+ */
+BS3_CMN_PROTO_FARSTUB(4, void, Bs3ExtCtxRestoreEx,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Copies the state from one context to another.
+ *
+ * @returns pDst
+ * @param pDst The destination extended CPU context.
+ * @param pSrc The source extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(PBS3EXTCTX, Bs3ExtCtxCopy,(PBS3EXTCTX pDst, PCBS3EXTCTX pSrc));
+
+/**
+ * Gets the FCW register value from @a pExtCtx.
+ *
+ * @returns FCW value.
+ * @param pExtCtx The extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3ExtCtxGetFcw,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Sets the FCW register value in @a pExtCtx.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @param uValue The new FCW value.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3ExtCtxSetFcw,(PBS3EXTCTX pExtCtx, uint16_t uValue));
+
+/**
+ * Gets the FSW register value from @a pExtCtx.
+ *
+ * @returns FSW value.
+ * @param pExtCtx The extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3ExtCtxGetFsw,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Sets the FSW register value in @a pExtCtx.
+ *
+ * @param pExtCtx The extended CPU context.
+ * @param uValue The new FSW value.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3ExtCtxSetFsw,(PBS3EXTCTX pExtCtx, uint16_t uValue));
+
+/**
+ * Gets the abridged FTW register value from @a pExtCtx.
+ *
+ * @returns FTW value.
+ * @param pExtCtx The extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3ExtCtxGetAbridgedFtw,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Sets the abridged FTW register value in @a pExtCtx.
+ *
+ * Currently this requires that the state stores teh abridged FTW, no conversion
+ * to the two-bit variant will be attempted.
+ *
+ * @returns true if set successfully, false if not.
+ * @param pExtCtx The extended CPU context.
+ * @param uValue The new FTW value.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetAbridgedFtw,(PBS3EXTCTX pExtCtx, uint16_t uValue));
+
+/**
+ * Gets the MXCSR register value from @a pExtCtx.
+ *
+ * @returns MXCSR value, 0 if not part of context.
+ * @param pExtCtx The extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(uint32_t, Bs3ExtCtxGetMxCsr,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Sets the MXCSR register value in @a pExtCtx.
+ *
+ * @returns true if set, false if not supported by the format.
+ * @param pExtCtx The extended CPU context.
+ * @param uValue The new MXCSR value.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetMxCsr,(PBS3EXTCTX pExtCtx, uint32_t uValue));
+
+/**
+ * Gets the MXCSR MASK value from @a pExtCtx.
+ *
+ * @returns MXCSR MASK value, 0 if not part of context.
+ * @param pExtCtx The extended CPU context.
+ */
+BS3_CMN_PROTO_STUB(uint32_t, Bs3ExtCtxGetMxCsrMask,(PCBS3EXTCTX pExtCtx));
+
+/**
+ * Sets the MXCSR MASK value in @a pExtCtx.
+ *
+ * @returns true if set, false if not supported by the format.
+ * @param pExtCtx The extended CPU context.
+ * @param uValue The new MXCSR MASK value.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetMxCsrMask,(PBS3EXTCTX pExtCtx, uint32_t uValue));
+
+/**
+ * Gets the value of MM register number @a iReg from @a pExtCtx.
+ *
+ * @returns The MM register value.
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to get (0 thru 7).
+ */
+BS3_CMN_PROTO_STUB(uint64_t, Bs3ExtCtxGetMm,(PCBS3EXTCTX pExtCtx, uint8_t iReg));
+
+/** What to do about the 16-bit above the MM QWORD. */
+typedef enum BS3EXTCTXTOPMM
+{
+ /** Invalid zero value. */
+ BS3EXTCTXTOPMM_INVALID = 0,
+ /** Set to 0FFFFh like real CPUs typically does when updating an MM register. */
+ BS3EXTCTXTOPMM_SET,
+ /** Set to zero. */
+ BS3EXTCTXTOPMM_ZERO,
+ /** Don't change the value, leaving it as-is. */
+ BS3EXTCTXTOPMM_AS_IS,
+ /** End of valid values. */
+ BS3EXTCTXTOPMM_END
+} BS3EXTCTXTOPMM;
+
+/**
+ * Sets the value of YMM register number @a iReg in @a pExtCtx to @a pValue.
+ *
+ * @returns True if set, false if not.
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to set.
+ * @param uValue The new register value.
+ * @param enmTop What to do about the 16-bit value above the MM
+ * QWord.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetMm,(PBS3EXTCTX pExtCtx, uint8_t iReg, uint64_t uValue, BS3EXTCTXTOPMM enmTop));
+
+/**
+ * Gets the value of XMM register number @a iReg from @a pExtCtx.
+ *
+ * @returns pValue
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to get.
+ * @param pValue Where to return the value. Zeroed if the state
+ * doesn't support SSE or if @a iReg is invalid.
+ */
+BS3_CMN_PROTO_STUB(PRTUINT128U, Bs3ExtCtxGetXmm,(PCBS3EXTCTX pExtCtx, uint8_t iReg, PRTUINT128U pValue));
+
+/**
+ * Sets the value of XMM register number @a iReg in @a pExtCtx to @a pValue.
+ *
+ * @returns True if set, false if not set (not supported by state format or
+ * invalid iReg).
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to set.
+ * @param pValue The new register value.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetXmm,(PBS3EXTCTX pExtCtx, uint8_t iReg, PCRTUINT128U pValue));
+
+/**
+ * Gets the value of YMM register number @a iReg from @a pExtCtx.
+ *
+ * @returns pValue
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to get.
+ * @param pValue Where to return the value. Parts not in the
+ * extended state are zeroed. For absent or invalid
+ * @a iReg values this is set to zero.
+ */
+BS3_CMN_PROTO_STUB(PRTUINT256U, Bs3ExtCtxGetYmm,(PCBS3EXTCTX pExtCtx, uint8_t iReg, PRTUINT256U pValue));
+
+/**
+ * Sets the value of YMM register number @a iReg in @a pExtCtx to @a pValue.
+ *
+ * @returns true if set (even if only partially). False if not set (not
+ * supported by state format, unsupported/invalid iReg).
+ * @param pExtCtx The extended CPU context.
+ * @param iReg The register to set.
+ * @param pValue The new register value.
+ * @param cbValue Number of bytes to take from @a pValue, either 16 or
+ * 32. If 16, the high part will be zeroed when present
+ * in the state.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3ExtCtxSetYmm,(PBS3EXTCTX pExtCtx, uint8_t iReg, PCRTUINT256U pValue, uint8_t cbValue));
+
+
+/** @name Debug register accessors for V8086 mode (works everwhere).
+ * @{ */
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr0,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr1,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr2,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr3,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr6,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDr7,(void));
+
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr0,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr1,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr2,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr3,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr6,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDr7,(RTCCUINTXREG uValue));
+
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetDrX,(uint8_t iReg));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetDrX,(uint8_t iReg, RTCCUINTXREG uValue));
+/** @} */
+
+
+/** @name Control register accessors for V8086 mode (works everwhere).
+ * @{ */
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetCr0,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetCr2,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetCr3,(void));
+BS3_CMN_PROTO_NOSB(RTCCUINTXREG, Bs3RegGetCr4,(void));
+BS3_CMN_PROTO_NOSB(uint16_t, Bs3RegGetTr,(void));
+BS3_CMN_PROTO_NOSB(uint16_t, Bs3RegGetLdtr,(void));
+BS3_CMN_PROTO_NOSB(uint64_t, Bs3RegGetXcr0,(void));
+
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetCr0,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetCr2,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetCr3,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetCr4,(RTCCUINTXREG uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetTr,(uint16_t uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetLdtr,(uint16_t uValue));
+BS3_CMN_PROTO_NOSB(void, Bs3RegSetXcr0,(uint64_t uValue));
+/** @} */
+
+
+/**
+ * Trap frame.
+ */
+typedef struct BS3TRAPFRAME
+{
+ /** 0x00: Exception/interrupt number. */
+ uint8_t bXcpt;
+ /** 0x01: The size of the IRET frame. */
+ uint8_t cbIretFrame;
+ /** 0x02: The handler CS. */
+ uint16_t uHandlerCs;
+ /** 0x04: The handler SS. */
+ uint16_t uHandlerSs;
+ /** 0x06: Explicit alignment. */
+ uint16_t usAlignment;
+ /** 0x08: The handler RSP (pointer to the iret frame, skipping ErrCd). */
+ uint64_t uHandlerRsp;
+ /** 0x10: The handler RFLAGS value. */
+ uint64_t fHandlerRfl;
+ /** 0x18: The error code (if applicable). */
+ uint64_t uErrCd;
+ /** 0x20: The register context. */
+ BS3REGCTX Ctx;
+} BS3TRAPFRAME;
+AssertCompileSize(BS3TRAPFRAME, 0x20 + 0xd0);
+/** Pointer to a trap frame. */
+typedef BS3TRAPFRAME BS3_FAR *PBS3TRAPFRAME;
+/** Pointer to a const trap frame. */
+typedef BS3TRAPFRAME const BS3_FAR *PCBS3TRAPFRAME;
+
+
+/**
+ * Re-initializes the trap handling for the current mode.
+ *
+ * Useful after a test that messes with the IDT/IVT.
+ *
+ * @sa Bs3TrapInit
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapReInit,(void));
+
+/**
+ * Initializes real mode and v8086 trap handling.
+ *
+ * @remarks Does not install RM/V86 trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapRmV86Init,(void));
+
+/**
+ * Initializes real mode and v8086 trap handling, extended version.
+ *
+ * @param f386Plus Set if the CPU is 80386 or later and
+ * extended registers should be saved. Once initialized
+ * with this parameter set to @a true, the effect cannot be
+ * reversed.
+ *
+ * @remarks Does not install RM/V86 trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapRmV86InitEx,(bool f386Plus));
+
+/**
+ * Initializes 16-bit (protected mode) trap handling.
+ *
+ * @remarks Does not install 16-bit trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap16Init,(void));
+
+/**
+ * Initializes 16-bit (protected mode) trap handling, extended version.
+ *
+ * @param f386Plus Set if the CPU is 80386 or later and
+ * extended registers should be saved. Once initialized
+ * with this parameter set to @a true, the effect cannot be
+ * reversed.
+ *
+ * @remarks Does not install 16-bit trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap16InitEx,(bool f386Plus));
+
+/**
+ * Initializes 32-bit trap handling.
+ *
+ * @remarks Does not install 32-bit trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap32Init,(void));
+
+/**
+ * Initializes 64-bit trap handling
+ *
+ * @remarks Does not install 64-bit trap handling, just initializes the
+ * structures.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap64Init,(void));
+
+/**
+ * Initializes 64-bit trap handling, extended version.
+ *
+ * @remarks Does not install 64-bit trap handling, just initializes the
+ * structures.
+ * @param fMoreIstUsage Use the interrupt stacks for more CPU exceptions.
+ * Default (false) is to only IST1 for the double fault
+ * handler and the rest uses IST0.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap64InitEx,(bool fMoreIstUsage));
+
+/**
+ * Modifies the real-mode / V86 IVT entry specified by @a iIvt.
+ *
+ * @param iIvt The index of the IDT entry to set.
+ * @param uSeg The handler real-mode segment.
+ * @param off The handler offset.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapRmV86SetGate,(uint8_t iIvt, uint16_t uSeg, uint16_t off));
+
+/**
+ * Modifies the 16-bit IDT entry (protected mode) specified by @a iIdt.
+ *
+ * @param iIdt The index of the IDT entry to set.
+ * @param bType The gate type (X86_SEL_TYPE_SYS_XXX).
+ * @param bDpl The DPL.
+ * @param uSel The handler selector.
+ * @param off The handler offset (if applicable).
+ * @param cParams The parameter count (for call gates).
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap16SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl,
+ uint16_t uSel, uint16_t off, uint8_t cParams));
+
+/** The address of Bs3Trap16GenericEntries.
+ * Bs3Trap16GenericEntries is an array of interrupt/trap/whatever entry
+ * points, 8 bytes each, that will create a register frame and call the generic
+ * C compatible trap handlers. */
+extern uint32_t g_Bs3Trap16GenericEntriesFlatAddr;
+
+/**
+ * Modifies the 32-bit IDT entry specified by @a iIdt.
+ *
+ * @param iIdt The index of the IDT entry to set.
+ * @param bType The gate type (X86_SEL_TYPE_SYS_XXX).
+ * @param bDpl The DPL.
+ * @param uSel The handler selector.
+ * @param off The handler offset (if applicable).
+ * @param cParams The parameter count (for call gates).
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap32SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl,
+ uint16_t uSel, uint32_t off, uint8_t cParams));
+
+/** The address of Bs3Trap32GenericEntries.
+ * Bs3Trap32GenericEntries is an array of interrupt/trap/whatever entry
+ * points, 10 bytes each, that will create a register frame and call the generic
+ * C compatible trap handlers. */
+extern uint32_t g_Bs3Trap32GenericEntriesFlatAddr;
+
+/**
+ * Modifies the 64-bit IDT entry specified by @a iIdt.
+ *
+ * @param iIdt The index of the IDT entry to set.
+ * @param bType The gate type (X86_SEL_TYPE_SYS_XXX).
+ * @param bDpl The DPL.
+ * @param uSel The handler selector.
+ * @param off The handler offset (if applicable).
+ * @param bIst The interrupt stack to use.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3Trap64SetGate,(uint8_t iIdt, uint8_t bType, uint8_t bDpl, uint16_t uSel, uint64_t off, uint8_t bIst));
+
+/** The address of Bs3Trap64GenericEntries.
+ * Bs3Trap64GenericEntries is an array of interrupt/trap/whatever entry
+ * points, 8 bytes each, that will create a register frame and call the generic
+ * C compatible trap handlers. */
+extern uint32_t g_Bs3Trap64GenericEntriesFlatAddr;
+
+/**
+ * Adjusts the DPL the IDT entry specified by @a iIdt.
+ *
+ * The change is applied to the 16-bit, 32-bit and 64-bit IDTs.
+ *
+ * @returns Old DPL (from 64-bit IDT).
+ * @param iIdt The index of the IDT and IVT entry to set.
+ * @param bDpl The DPL.
+ */
+BS3_CMN_PROTO_STUB(uint8_t, Bs3TrapSetDpl,(uint8_t iIdt, uint8_t bDpl));
+
+/**
+ * C-style trap handler.
+ *
+ * The caller will resume the context in @a pTrapFrame upon return.
+ *
+ * @param pTrapFrame The trap frame. Registers can be modified.
+ * @note The 16-bit versions must be in CGROUP16!
+ */
+typedef BS3_DECL_NEAR_CALLBACK(void) FNBS3TRAPHANDLER(PBS3TRAPFRAME pTrapFrame);
+/** Pointer to a trap handler (current template context). */
+typedef FNBS3TRAPHANDLER *PFNBS3TRAPHANDLER;
+
+#if ARCH_BITS == 16
+/** @copydoc FNBS3TRAPHANDLER */
+typedef FNBS3FAR FNBS3TRAPHANDLER32;
+/** @copydoc FNBS3TRAPHANDLER */
+typedef FNBS3FAR FNBS3TRAPHANDLER64;
+#else
+/** @copydoc FNBS3TRAPHANDLER */
+typedef FNBS3TRAPHANDLER FNBS3TRAPHANDLER32;
+/** @copydoc FNBS3TRAPHANDLER */
+typedef FNBS3TRAPHANDLER FNBS3TRAPHANDLER64;
+#endif
+/** @copydoc PFNBS3TRAPHANDLER */
+typedef FNBS3TRAPHANDLER32 *PFNBS3TRAPHANDLER32;
+/** @copydoc PFNBS3TRAPHANDLER */
+typedef FNBS3TRAPHANDLER64 *PFNBS3TRAPHANDLER64;
+
+
+/**
+ * C-style trap handler, near 16-bit (CGROUP16).
+ *
+ * The caller will resume the context in @a pTrapFrame upon return.
+ *
+ * @param pTrapFrame The trap frame. Registers can be modified.
+ */
+typedef BS3_DECL_NEAR_CALLBACK(void) FNBS3TRAPHANDLER16(PBS3TRAPFRAME pTrapFrame);
+/** Pointer to a trap handler (current template context). */
+typedef FNBS3TRAPHANDLER16 *PFNBS3TRAPHANDLER16;
+
+/**
+ * C-style trap handler, near 16-bit (CGROUP16).
+ *
+ * The caller will resume the context in @a pTrapFrame upon return.
+ *
+ * @param pTrapFrame The trap frame. Registers can be modified.
+ */
+typedef BS3_DECL_CALLBACK(void) FNBS3TRAPHANDLER3264(PBS3TRAPFRAME pTrapFrame);
+/** Pointer to a trap handler (current template context). */
+typedef FNBS3TRAPHANDLER3264 *FPFNBS3TRAPHANDLER3264;
+
+
+/**
+ * Sets a trap handler (C/C++/assembly) for the current bitcount.
+ *
+ * @returns Previous handler.
+ * @param iIdt The index of the IDT entry to set.
+ * @param pfnHandler Pointer to the handler.
+ * @sa Bs3TrapSetHandlerEx
+ */
+BS3_CMN_PROTO_STUB(PFNBS3TRAPHANDLER, Bs3TrapSetHandler,(uint8_t iIdt, PFNBS3TRAPHANDLER pfnHandler));
+
+/**
+ * Sets a trap handler (C/C++/assembly) for all the bitcounts.
+ *
+ * @param iIdt The index of the IDT and IVT entry to set.
+ * @param pfnHandler16 Pointer to the 16-bit handler. (Assumes linker addresses.)
+ * @param pfnHandler32 Pointer to the 32-bit handler. (Assumes linker addresses.)
+ * @param pfnHandler64 Pointer to the 64-bit handler. (Assumes linker addresses.)
+ * @sa Bs3TrapSetHandler
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetHandlerEx,(uint8_t iIdt, PFNBS3TRAPHANDLER16 pfnHandler16,
+ PFNBS3TRAPHANDLER32 pfnHandler32, PFNBS3TRAPHANDLER64 pfnHandler64));
+
+/**
+ * Default C/C++ trap handler.
+ *
+ * This will check trap record and panic if no match was found.
+ *
+ * @param pTrapFrame Trap frame of the trap to handle.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapDefaultHandler,(PBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Prints the trap frame (to screen).
+ * @param pTrapFrame Trap frame to print.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapPrintFrame,(PCBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Sets up a long jump from a trap handler.
+ *
+ * The long jump will only be performed once, but will catch any kind of trap,
+ * fault, interrupt or irq.
+ *
+ * @retval true on the initial call.
+ * @retval false on trap return.
+ * @param pTrapFrame Where to store the trap information when
+ * returning @c false.
+ * @sa #Bs3TrapUnsetJmp
+ */
+BS3_CMN_PROTO_NOSB(DECL_RETURNS_TWICE(bool),Bs3TrapSetJmp,(PBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Combination of #Bs3TrapSetJmp and #Bs3RegCtxRestore.
+ *
+ * @param pCtxRestore The context to restore.
+ * @param pTrapFrame Where to store the trap information.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetJmpAndRestore,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Variation of Bs3TrapSetJmpAndRestore that includes
+ * #Bs3TrapSetJmpAndRestoreInRm and calls is if pCtxRestore is a real mode
+ * context and we're not in real mode.
+ *
+ * This is useful for 32-bit test drivers running via #Bs3TestDoModesByOne using
+ * BS3TESTMODEBYONEENTRY_F_REAL_MODE_READY to allow them to test real-mode too.
+ *
+ * @param pCtxRestore The context to restore.
+ * @param pTrapFrame Where to store the trap information.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetJmpAndRestoreWithRm,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Combination of #Bs3ExtCtxRestoreEx, #Bs3TrapSetJmp, #Bs3RegCtxRestore and
+ * #Bs3ExtCtxSaveEx.
+ *
+ * @param pCtxRestore The context to restore.
+ * @param pExtCtxRestore The extended context to restore.
+ * @param pTrapFrame Where to store the trap information.
+ * @param pExtCtxTrap Where to store the extended context after the trap.
+ * Note, the saving isn't done from the trap handler,
+ * but after #Bs3TrapSetJmp returns zero (i.e. for the
+ * 2nd time).
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetJmpAndRestoreWithExtCtx,(PCBS3REGCTX pCtxRestore, PCBS3EXTCTX pExtCtxRestore,
+ PBS3TRAPFRAME pTrapFrame, PBS3EXTCTX pExtCtxTrap));
+
+/**
+ * Variation of Bs3TrapSetJmpAndRestoreWithExtCtx that includes
+ * #Bs3TrapSetJmpAndRestoreInRm and calls is if pCtxRestore is a real mode
+ * context and we're not in real mode.
+ *
+ * This is useful for 32-bit test drivers running via #Bs3TestDoModesByOne using
+ * BS3TESTMODEBYONEENTRY_F_REAL_MODE_READY to allow them to test real-mode too.
+ *
+ * @param pCtxRestore The context to restore.
+ * @param pExtCtxRestore The extended context to restore.
+ * @param pTrapFrame Where to store the trap information.
+ * @param pExtCtxTrap Where to store the extended context after the trap.
+ * Note, the saving isn't done from the trap handler,
+ * but after #Bs3TrapSetJmp returns zero (i.e. for the
+ * 2nd time).
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetJmpAndRestoreWithExtCtxAndRm,(PCBS3REGCTX pCtxRestore, PCBS3EXTCTX pExtCtxRestore,
+ PBS3TRAPFRAME pTrapFrame, PBS3EXTCTX pExtCtxTrap));
+
+/**
+ * Combination of Bs3SwitchToRM, #Bs3TrapSetJmp and #Bs3RegCtxRestore.
+ *
+ * @param pCtxRestore The context to restore. Must be real-mode
+ * addressable.
+ * @param pTrapFrame Where to store the trap information. Must be
+ * real-mode addressable.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapSetJmpAndRestoreInRm,(PCBS3REGCTX pCtxRestore, PBS3TRAPFRAME pTrapFrame));
+
+/**
+ * Disables a previous #Bs3TrapSetJmp call.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TrapUnsetJmp,(void));
+
+
+/**
+ * The current test step.
+ */
+extern uint16_t g_usBs3TestStep;
+
+/**
+ * Equivalent to RTTestCreate + RTTestBanner.
+ *
+ * @param pszTest The test name.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestInit,(const char BS3_FAR *pszTest));
+
+
+/**
+ * Equivalent to RTTestSummaryAndDestroy.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestTerm,(void));
+
+/**
+ * Equivalent to RTTestISub.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSub,(const char BS3_FAR *pszSubTest));
+
+/**
+ * Equivalent to RTTestIFailedF.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSubF,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Equivalent to RTTestISubV.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSubV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Equivalent to RTTestISubDone.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSubDone,(void));
+
+/**
+ * Equivalent to RTTestIValue.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestValue,(const char BS3_FAR *pszName, uint64_t u64Value, uint8_t bUnit));
+
+/**
+ * Equivalent to RTTestSubErrorCount.
+ */
+BS3_CMN_PROTO_STUB(uint16_t, Bs3TestSubErrorCount,(void));
+
+/**
+ * Get nanosecond host timestamp.
+ *
+ * This only works when testing is enabled and will not work in VMs configured
+ * with a 286, 186 or 8086/8088 CPU profile.
+ */
+BS3_CMN_PROTO_STUB(uint64_t, Bs3TestNow,(void));
+
+
+/**
+ * Queries an unsigned 8-bit configuration value.
+ *
+ * @returns Value.
+ * @param uCfg A VMMDEV_TESTING_CFG_XXX value.
+ */
+BS3_CMN_PROTO_STUB(uint8_t, Bs3TestQueryCfgU8,(uint16_t uCfg));
+
+/**
+ * Queries an unsigned 8-bit configuration value.
+ *
+ * @returns Value.
+ * @param uCfg A VMMDEV_TESTING_CFG_XXX value.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestQueryCfgBool,(uint16_t uCfg));
+
+/**
+ * Queries an unsigned 32-bit configuration value.
+ *
+ * @returns Value.
+ * @param uCfg A VMMDEV_TESTING_CFG_XXX value.
+ */
+BS3_CMN_PROTO_STUB(uint32_t, Bs3TestQueryCfgU32,(uint16_t uCfg));
+
+/**
+ * Equivalent to RTTestIPrintf with RTTESTLVL_ALWAYS.
+ *
+ * @param pszFormat What to print, format string. Explicit newline char.
+ * @param ... String format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestPrintf,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Equivalent to RTTestIPrintfV with RTTESTLVL_ALWAYS.
+ *
+ * @param pszFormat What to print, format string. Explicit newline char.
+ * @param va String format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestPrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Same as Bs3TestPrintf, except no guest screen echo.
+ *
+ * @param pszFormat What to print, format string. Explicit newline char.
+ * @param ... String format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestHostPrintf,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Same as Bs3TestPrintfV, except no guest screen echo.
+ *
+ * @param pszFormat What to print, format string. Explicit newline char.
+ * @param va String format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestHostPrintfV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Equivalent to RTTestIFailed.
+ * @returns false.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestFailed,(const char BS3_FAR *pszMessage));
+
+/**
+ * Equivalent to RTTestIFailedF.
+ * @returns false.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestFailedF,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Equivalent to RTTestIFailedV.
+ * @returns false.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestFailedV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Equivalent to RTTestISkipped.
+ *
+ * @param pszWhy Optional reason why it's being skipped.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSkipped,(const char BS3_FAR *pszWhy));
+
+/**
+ * Equivalent to RTTestISkippedF.
+ *
+ * @param pszFormat Optional reason why it's being skipped.
+ * @param ... Format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSkippedF,(const char BS3_FAR *pszFormat, ...));
+
+/**
+ * Equivalent to RTTestISkippedV.
+ *
+ * @param pszFormat Optional reason why it's being skipped.
+ * @param va Format arguments.
+ */
+BS3_CMN_PROTO_STUB(void, Bs3TestSkippedV,(const char BS3_FAR *pszFormat, va_list BS3_FAR va));
+
+/**
+ * Compares two register contexts, with PC and SP adjustments.
+ *
+ * Differences will be reported as test failures.
+ *
+ * @returns true if equal, false if not.
+ * @param pActualCtx The actual register context.
+ * @param pExpectedCtx Expected register context.
+ * @param cbPcAdjust Program counter adjustment (applied to @a pExpectedCtx).
+ * @param cbSpAdjust Stack pointer adjustment (applied to @a pExpectedCtx).
+ * @param fExtraEfl Extra EFLAGS to OR into @a pExepctedCtx.
+ * @param pszMode CPU mode or some other helpful text.
+ * @param idTestStep Test step identifier.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestCheckRegCtxEx,(PCBS3REGCTX pActualCtx, PCBS3REGCTX pExpectedCtx, uint16_t cbPcAdjust,
+ int16_t cbSpAdjust, uint32_t fExtraEfl,
+ const char BS3_FAR *pszMode, uint16_t idTestStep));
+
+/**
+ * Compares two extended register contexts.
+ *
+ * Differences will be reported as test failures.
+ *
+ * @returns true if equal, false if not.
+ * @param pActualExtCtx The actual register context.
+ * @param pExpectedExtCtx Expected register context.
+ * @param fFlags Reserved, pass 0.
+ * @param pszMode CPU mode or some other helpful text.
+ * @param idTestStep Test step identifier.
+ */
+BS3_CMN_PROTO_STUB(bool, Bs3TestCheckExtCtx,(PCBS3EXTCTX pActualExtCtx, PCBS3EXTCTX pExpectedExtCtx, uint16_t fFlags,
+ const char BS3_FAR *pszMode, uint16_t idTestStep));
+
+/**
+ * Performs the testing for the given mode.
+ *
+ * This is called with the CPU already switch to that mode.
+ *
+ * @returns 0 on success or directly Bs3TestFailed calls, non-zero to indicate
+ * where the test when wrong. Special value BS3TESTDOMODE_SKIPPED
+ * should be returned to indicate that the test has been skipped.
+ * @param bMode The current CPU mode.
+ */
+typedef BS3_DECL_CALLBACK(uint8_t) FNBS3TESTDOMODE(uint8_t bMode);
+/** Pointer (far) to a test (for 32-bit and 64-bit code, will be flatten). */
+typedef FNBS3TESTDOMODE *PFNBS3TESTDOMODE;
+
+/** Special FNBS3TESTDOMODE return code for indicating a skipped mode test. */
+#define BS3TESTDOMODE_SKIPPED UINT8_MAX
+
+/**
+ * Mode sub-test entry.
+ *
+ * This can only be passed around to functions with the same bit count, as it
+ * contains function pointers. In 16-bit mode, the 16-bit pointers are near and
+ * implies BS3TEXT16, whereas the 32-bit and 64-bit pointers are far real mode
+ * addresses that will be converted to flat address prior to calling them.
+ * Similarly, in 32-bit and 64-bit the addresses are all flat and the 16-bit
+ * ones will be converted to BS3TEXT16 based addresses prior to calling.
+ */
+typedef struct BS3TESTMODEENTRY
+{
+ /** The sub-test name to be passed to Bs3TestSub if not NULL. */
+ const char * BS3_FAR pszSubTest;
+
+ PFNBS3TESTDOMODE pfnDoRM;
+
+ PFNBS3TESTDOMODE pfnDoPE16;
+ PFNBS3TESTDOMODE pfnDoPE16_32;
+ PFNBS3TESTDOMODE pfnDoPE16_V86;
+ PFNBS3TESTDOMODE pfnDoPE32;
+ PFNBS3TESTDOMODE pfnDoPE32_16;
+ PFNBS3TESTDOMODE pfnDoPEV86;
+
+ PFNBS3TESTDOMODE pfnDoPP16;
+ PFNBS3TESTDOMODE pfnDoPP16_32;
+ PFNBS3TESTDOMODE pfnDoPP16_V86;
+ PFNBS3TESTDOMODE pfnDoPP32;
+ PFNBS3TESTDOMODE pfnDoPP32_16;
+ PFNBS3TESTDOMODE pfnDoPPV86;
+
+ PFNBS3TESTDOMODE pfnDoPAE16;
+ PFNBS3TESTDOMODE pfnDoPAE16_32;
+ PFNBS3TESTDOMODE pfnDoPAE16_V86;
+ PFNBS3TESTDOMODE pfnDoPAE32;
+ PFNBS3TESTDOMODE pfnDoPAE32_16;
+ PFNBS3TESTDOMODE pfnDoPAEV86;
+
+ PFNBS3TESTDOMODE pfnDoLM16;
+ PFNBS3TESTDOMODE pfnDoLM32;
+ PFNBS3TESTDOMODE pfnDoLM64;
+
+} BS3TESTMODEENTRY;
+/** Pointer to a mode sub-test entry. */
+typedef BS3TESTMODEENTRY const *PCBS3TESTMODEENTRY;
+
+/** @def BS3TESTMODEENTRY_CMN
+ * Produces a BS3TESTMODEENTRY initializer for common (c16,c32,c64) test
+ * functions. */
+#define BS3TESTMODEENTRY_CMN(a_szTest, a_BaseNm) \
+ { /*pszSubTest =*/ a_szTest, \
+ /*RM*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PE16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PE16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PE16_V86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PE32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PE32_16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PEV86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PP16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PP16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PP16_V86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PP32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PP32_16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PPV86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PAE16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PAE16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PAE16_V86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PAE32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PAE32_16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PAEV86*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*LM16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*LM32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*LM64*/ RT_CONCAT(a_BaseNm, _c64), \
+ }
+
+/** @def BS3TESTMODE_PROTOTYPES_CMN
+ * A set of standard protypes to go with #BS3TESTMODEENTRY_CMN. */
+#define BS3TESTMODE_PROTOTYPES_CMN(a_BaseNm) \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c16); \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c32); \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c64)
+
+/** @def BS3TESTMODEENTRY_CMN_64
+ * Produces a BS3TESTMODEENTRY initializer for common 64-bit test functions. */
+#define BS3TESTMODEENTRY_CMN_64(a_szTest, a_BaseNm) \
+ { /*pszSubTest =*/ a_szTest, \
+ /*RM*/ NULL, \
+ /*PE16*/ NULL, \
+ /*PE16_32*/ NULL, \
+ /*PE16_V86*/ NULL, \
+ /*PE32*/ NULL, \
+ /*PE32_16*/ NULL, \
+ /*PEV86*/ NULL, \
+ /*PP16*/ NULL, \
+ /*PP16_32*/ NULL, \
+ /*PP16_V86*/ NULL, \
+ /*PP32*/ NULL, \
+ /*PP32_16*/ NULL, \
+ /*PPV86*/ NULL, \
+ /*PAE16*/ NULL, \
+ /*PAE16_32*/ NULL, \
+ /*PAE16_V86*/ NULL, \
+ /*PAE32*/ NULL, \
+ /*PAE32_16*/ NULL, \
+ /*PAEV86*/ NULL, \
+ /*LM16*/ NULL, \
+ /*LM32*/ NULL, \
+ /*LM64*/ RT_CONCAT(a_BaseNm, _c64), \
+ }
+
+/** @def BS3TESTMODE_PROTOTYPES_CMN
+ * Standard protype to go with #BS3TESTMODEENTRY_CMN_64. */
+#define BS3TESTMODE_PROTOTYPES_CMN_64(a_BaseNm) \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c64)
+
+/** @def BS3TESTMODEENTRY_MODE
+ * Produces a BS3TESTMODEENTRY initializer for a full set of mode test
+ * functions. */
+#define BS3TESTMODEENTRY_MODE(a_szTest, a_BaseNm) \
+ { /*pszSubTest =*/ a_szTest, \
+ /*RM*/ RT_CONCAT(a_BaseNm, _rm), \
+ /*PE16*/ RT_CONCAT(a_BaseNm, _pe16), \
+ /*PE16_32*/ RT_CONCAT(a_BaseNm, _pe16_32), \
+ /*PE16_V86*/ RT_CONCAT(a_BaseNm, _pe16_v86), \
+ /*PE32*/ RT_CONCAT(a_BaseNm, _pe32), \
+ /*PE32_16*/ RT_CONCAT(a_BaseNm, _pe32_16), \
+ /*PEV86*/ RT_CONCAT(a_BaseNm, _pev86), \
+ /*PP16*/ RT_CONCAT(a_BaseNm, _pp16), \
+ /*PP16_32*/ RT_CONCAT(a_BaseNm, _pp16_32), \
+ /*PP16_V86*/ RT_CONCAT(a_BaseNm, _pp16_v86), \
+ /*PP32*/ RT_CONCAT(a_BaseNm, _pp32), \
+ /*PP32_16*/ RT_CONCAT(a_BaseNm, _pp32_16), \
+ /*PPV86*/ RT_CONCAT(a_BaseNm, _ppv86), \
+ /*PAE16*/ RT_CONCAT(a_BaseNm, _pae16), \
+ /*PAE16_32*/ RT_CONCAT(a_BaseNm, _pae16_32), \
+ /*PAE16_V86*/ RT_CONCAT(a_BaseNm, _pae16_v86), \
+ /*PAE32*/ RT_CONCAT(a_BaseNm, _pae32), \
+ /*PAE32_16*/ RT_CONCAT(a_BaseNm, _pae32_16), \
+ /*PAEV86*/ RT_CONCAT(a_BaseNm, _paev86), \
+ /*LM16*/ RT_CONCAT(a_BaseNm, _lm16), \
+ /*LM32*/ RT_CONCAT(a_BaseNm, _lm32), \
+ /*LM64*/ RT_CONCAT(a_BaseNm, _lm64), \
+ }
+
+/** @def BS3TESTMODE_PROTOTYPES_MODE
+ * A set of standard protypes to go with #BS3TESTMODEENTRY_MODE. */
+#define BS3TESTMODE_PROTOTYPES_MODE(a_BaseNm) \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _rm); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe16_v86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe32_16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pev86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp16_v86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp32_16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _ppv86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae16_v86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae32_16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _paev86); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _lm16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _lm32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _lm64)
+
+
+/**
+ * Mode sub-test entry, max bit-count driven
+ *
+ * This is an alternative to BS3TESTMODEENTRY where a few workers (test drivers)
+ * does all the work, using faster 32-bit and 64-bit code where possible. This
+ * avoids executing workers in V8086 mode. It allows for modifying and checking
+ * 64-bit register content when testing LM16 and LM32.
+ *
+ * The 16-bit workers are only used for real mode and 16-bit protected mode.
+ * So, the 16-bit version of the code template can be stripped of anything
+ * related to paging and/or v8086, saving code space.
+ */
+typedef struct BS3TESTMODEBYMAXENTRY
+{
+ /** The sub-test name to be passed to Bs3TestSub if not NULL. */
+ const char * BS3_FAR pszSubTest;
+
+ PFNBS3TESTDOMODE pfnDoRM;
+ PFNBS3TESTDOMODE pfnDoPE16;
+ PFNBS3TESTDOMODE pfnDoPE16_32;
+ PFNBS3TESTDOMODE pfnDoPE32;
+ PFNBS3TESTDOMODE pfnDoPP16_32;
+ PFNBS3TESTDOMODE pfnDoPP32;
+ PFNBS3TESTDOMODE pfnDoPAE16_32;
+ PFNBS3TESTDOMODE pfnDoPAE32;
+ PFNBS3TESTDOMODE pfnDoLM64;
+
+ bool fDoRM : 1;
+
+ bool fDoPE16 : 1;
+ bool fDoPE16_32 : 1;
+ bool fDoPE16_V86 : 1;
+ bool fDoPE32 : 1;
+ bool fDoPE32_16 : 1;
+ bool fDoPEV86 : 1;
+
+ bool fDoPP16 : 1;
+ bool fDoPP16_32 : 1;
+ bool fDoPP16_V86 : 1;
+ bool fDoPP32 : 1;
+ bool fDoPP32_16 : 1;
+ bool fDoPPV86 : 1;
+
+ bool fDoPAE16 : 1;
+ bool fDoPAE16_32 : 1;
+ bool fDoPAE16_V86 : 1;
+ bool fDoPAE32 : 1;
+ bool fDoPAE32_16 : 1;
+ bool fDoPAEV86 : 1;
+
+ bool fDoLM16 : 1;
+ bool fDoLM32 : 1;
+ bool fDoLM64 : 1;
+
+} BS3TESTMODEBYMAXENTRY;
+/** Pointer to a mode-by-max sub-test entry. */
+typedef BS3TESTMODEBYMAXENTRY const *PCBS3TESTMODEBYMAXENTRY;
+
+/** @def BS3TESTMODEBYMAXENTRY_CMN
+ * Produces a BS3TESTMODEBYMAXENTRY initializer for common (c16,c32,c64) test
+ * functions. */
+#define BS3TESTMODEBYMAXENTRY_CMN(a_szTest, a_BaseNm) \
+ { /*pszSubTest =*/ a_szTest, \
+ /*RM*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PE16*/ RT_CONCAT(a_BaseNm, _c16), \
+ /*PE16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PE32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PP16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PP32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PAE16_32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*PAE32*/ RT_CONCAT(a_BaseNm, _c32), \
+ /*LM64*/ RT_CONCAT(a_BaseNm, _c64), \
+ /*fDoRM*/ true, \
+ /*fDoPE16*/ true, \
+ /*fDoPE16_32*/ true, \
+ /*fDoPE16_V86*/ true, \
+ /*fDoPE32*/ true, \
+ /*fDoPE32_16*/ true, \
+ /*fDoPEV86*/ true, \
+ /*fDoPP16*/ true, \
+ /*fDoPP16_32*/ true, \
+ /*fDoPP16_V86*/ true, \
+ /*fDoPP32*/ true, \
+ /*fDoPP32_16*/ true, \
+ /*fDoPPV86*/ true, \
+ /*fDoPAE16*/ true, \
+ /*fDoPAE16_32*/ true, \
+ /*fDoPAE16_V86*/ true, \
+ /*fDoPAE32*/ true, \
+ /*fDoPAE32_16*/ true, \
+ /*fDoPAEV86*/ true, \
+ /*fDoLM16*/ true, \
+ /*fDoLM32*/ true, \
+ /*fDoLM64*/ true, \
+ }
+
+/** @def BS3TESTMODEBYMAX_PROTOTYPES_CMN
+ * A set of standard protypes to go with #BS3TESTMODEBYMAXENTRY_CMN. */
+#define BS3TESTMODEBYMAX_PROTOTYPES_CMN(a_BaseNm) \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c16); \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c32); \
+ FNBS3TESTDOMODE /*BS3_FAR_CODE*/ RT_CONCAT(a_BaseNm, _c64)
+
+
+/** @def BS3TESTMODEBYMAXENTRY_MODE
+ * Produces a BS3TESTMODEBYMAXENTRY initializer for a full set of mode test
+ * functions. */
+#define BS3TESTMODEBYMAXENTRY_MODE(a_szTest, a_BaseNm) \
+ { /*pszSubTest =*/ a_szTest, \
+ /*RM*/ RT_CONCAT(a_BaseNm, _rm), \
+ /*PE16*/ RT_CONCAT(a_BaseNm, _pe16), \
+ /*PE16_32*/ RT_CONCAT(a_BaseNm, _pe16_32), \
+ /*PE32*/ RT_CONCAT(a_BaseNm, _pe32), \
+ /*PP16_32*/ RT_CONCAT(a_BaseNm, _pp16_32), \
+ /*PP32*/ RT_CONCAT(a_BaseNm, _pp32), \
+ /*PAE16_32*/ RT_CONCAT(a_BaseNm, _pae16_32), \
+ /*PAE32*/ RT_CONCAT(a_BaseNm, _pae32), \
+ /*LM64*/ RT_CONCAT(a_BaseNm, _lm64), \
+ /*fDoRM*/ true, \
+ /*fDoPE16*/ true, \
+ /*fDoPE16_32*/ true, \
+ /*fDoPE16_V86*/ true, \
+ /*fDoPE32*/ true, \
+ /*fDoPE32_16*/ true, \
+ /*fDoPEV86*/ true, \
+ /*fDoPP16*/ true, \
+ /*fDoPP16_32*/ true, \
+ /*fDoPP16_V86*/ true, \
+ /*fDoPP32*/ true, \
+ /*fDoPP32_16*/ true, \
+ /*fDoPPV86*/ true, \
+ /*fDoPAE16*/ true, \
+ /*fDoPAE16_32*/ true, \
+ /*fDoPAE16_V86*/ true, \
+ /*fDoPAE32*/ true, \
+ /*fDoPAE32_16*/ true, \
+ /*fDoPAEV86*/ true, \
+ /*fDoLM16*/ true, \
+ /*fDoLM32*/ true, \
+ /*fDoLM64*/ true, \
+ }
+
+/** @def BS3TESTMODEBYMAX_PROTOTYPES_MODE
+ * A set of standard protypes to go with #BS3TESTMODEBYMAXENTRY_MODE. */
+#define BS3TESTMODEBYMAX_PROTOTYPES_MODE(a_BaseNm) \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _rm); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe16); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pe32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pp32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae16_32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _pae32); \
+ FNBS3TESTDOMODE RT_CONCAT(a_BaseNm, _lm64)
+
+
+/**
+ * One worker drives all modes.
+ *
+ * This is an alternative to BS3TESTMODEENTRY where one worker, typically
+ * 16-bit, does all the test driver work. It's called repeatedly from all
+ * the modes being tested.
+ */
+typedef struct BS3TESTMODEBYONEENTRY
+{
+ const char * BS3_FAR pszSubTest;
+ PFNBS3TESTDOMODE pfnWorker;
+ /** BS3TESTMODEBYONEENTRY_F_XXX. */
+ uint32_t fFlags;
+} BS3TESTMODEBYONEENTRY;
+/** Pointer to a mode-by-one sub-test entry. */
+typedef BS3TESTMODEBYONEENTRY const *PCBS3TESTMODEBYONEENTRY;
+
+/** @name BS3TESTMODEBYONEENTRY_F_XXX - flags.
+ * @{ */
+/** Only test modes that has paging enabled. */
+#define BS3TESTMODEBYONEENTRY_F_ONLY_PAGING RT_BIT_32(0)
+/** Minimal mode selection. */
+#define BS3TESTMODEBYONEENTRY_F_MINIMAL RT_BIT_32(1)
+/** The 32-bit worker is ready to handle real-mode by mode switching. */
+#define BS3TESTMODEBYONEENTRY_F_REAL_MODE_READY RT_BIT_32(2)
+/** @} */
+
+
+/**
+ * Sets the full GDTR register.
+ *
+ * @param cbLimit The limit.
+ * @param uBase The base address - 24, 32 or 64 bit depending on the
+ * CPU mode.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3UtilSetFullGdtr,(uint16_t cbLimit, uint64_t uBase));
+
+/**
+ * Sets the full IDTR register.
+ *
+ * @param cbLimit The limit.
+ * @param uBase The base address - 24, 32 or 64 bit depending on the
+ * CPU mode.
+ */
+BS3_CMN_PROTO_NOSB(void, Bs3UtilSetFullIdtr,(uint16_t cbLimit, uint64_t uBase));
+
+
+/** @} */
+
+
+/**
+ * Initializes all of boot sector kit \#3.
+ */
+BS3_DECL(void) Bs3InitAll_rm(void);
+
+/**
+ * Initializes the REAL and TILED memory pools.
+ *
+ * For proper operation on OLDer CPUs, call #Bs3CpuDetect_mmm first.
+ */
+BS3_DECL_FAR(void) Bs3InitMemory_rm_far(void);
+
+/**
+ * Initializes the X0TEXT16 and X1TEXT16 GDT entries.
+ */
+BS3_DECL_FAR(void) Bs3InitGdt_rm_far(void);
+
+
+
+/** @defgroup grp_bs3kit_mode Mode Specific Functions and Data
+ *
+ * The mode specific functions come in bit count variations and CPU mode
+ * variations. The bs3kit-template-header.h/mac defines the BS3_NM macro to
+ * mangle a function or variable name according to the target CPU mode. In
+ * non-templated code, it's common to spell the name out in full.
+ *
+ * @{
+ */
+
+
+/** @def BS3_MODE_PROTO_INT
+ * Internal macro for emitting prototypes for mode functions.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_MODE_PROTO_STUB, BS3_MODE_PROTO_NOSB
+ */
+#define BS3_MODE_PROTO_INT(a_RetType, a_Name, a_Params) \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_rm) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pe16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pe16_32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pe16_v86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pe32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pe32_16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pev86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pp16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pp16_32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pp16_v86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pp32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pp32_16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_ppv86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pae16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pae16_32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pae16_v86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pae32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_pae32_16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_paev86) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_lm16) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_lm32) a_Params; \
+ BS3_DECL_NEAR(a_RetType) RT_CONCAT(a_Name,_lm64) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_rm_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pe16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pe16_v86_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pe32_16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pev86_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pp16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pp16_v86_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pp32_16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_ppv86_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pae16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pae16_v86_far)a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_pae32_16_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_paev86_far) a_Params; \
+ BS3_DECL_FAR(a_RetType) RT_CONCAT(a_Name,_lm16_far) a_Params
+
+/** @def BS3_MODE_PROTO_STUB
+ * Macro for prototyping all the variations of a mod function with automatic
+ * near -> far stub.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_MODE_PROTO_STUB, BS3_MODE_PROTO_NOSB
+ */
+#define BS3_MODE_PROTO_STUB(a_RetType, a_Name, a_Params) BS3_MODE_PROTO_INT(a_RetType, a_Name, a_Params)
+
+/** @def BS3_MODE_PROTO_STUB
+ * Macro for prototyping all the variations of a mod function without any
+ * near -> far stub.
+ *
+ * @param a_RetType The return type.
+ * @param a_Name The function basename.
+ * @param a_Params The parameter list (in parentheses).
+ * @sa BS3_MODE_PROTO_STUB, BS3_MODE_PROTO_NOSB
+ */
+#define BS3_MODE_PROTO_NOSB(a_RetType, a_Name, a_Params) BS3_MODE_PROTO_INT(a_RetType, a_Name, a_Params)
+
+
+/**
+ * Macro for reducing typing.
+ *
+ * Doxygen knows how to expand this, well, kind of.
+ *
+ * @remarks Variables instantiated in assembly code should define two labels,
+ * with and without leading underscore. Variables instantiated from
+ * C/C++ code doesn't need to as the object file convert does this for
+ * 64-bit object files.
+ */
+#define BS3_MODE_EXPAND_EXTERN_DATA16(a_VarType, a_VarName, a_Suffix) \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_rm) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pe16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pe16_32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pe16_v86) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pe32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pe32_16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pev86) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pp16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pp16_32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pp16_v86) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pp32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pp32_16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_ppv86) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pae16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pae16_32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pae16_v86)a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pae32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_pae32_16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_paev86) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_lm16) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_lm32) a_Suffix; \
+ extern a_VarType BS3_FAR_DATA RT_CONCAT(a_VarName,_lm64) a_Suffix
+
+
+/** The TMPL_MODE_STR value for each mode.
+ * These are all in DATA16 so they can be accessed from any code. */
+BS3_MODE_EXPAND_EXTERN_DATA16(const char, g_szBs3ModeName, []);
+/** The TMPL_MODE_LNAME value for each mode.
+ * These are all in DATA16 so they can be accessed from any code. */
+BS3_MODE_EXPAND_EXTERN_DATA16(const char, g_szBs3ModeNameShortLower, []);
+
+
+/**
+ * Basic CPU detection.
+ *
+ * This sets the #g_uBs3CpuDetected global variable to the return value.
+ *
+ * @returns BS3CPU_XXX value with the BS3CPU_F_CPUID flag set depending on
+ * capabilities.
+ */
+BS3_MODE_PROTO_NOSB(uint8_t, Bs3CpuDetect,(void));
+
+/** @name BS3CPU_XXX - CPU detected by BS3CpuDetect_c16() and friends.
+ * @{ */
+#define BS3CPU_8086 UINT16_C(0x0001) /**< Both 8086 and 8088. */
+#define BS3CPU_V20 UINT16_C(0x0002) /**< Both NEC V20, V30 and relatives. */
+#define BS3CPU_80186 UINT16_C(0x0003) /**< Both 80186 and 80188. */
+#define BS3CPU_80286 UINT16_C(0x0004)
+#define BS3CPU_80386 UINT16_C(0x0005)
+#define BS3CPU_80486 UINT16_C(0x0006)
+#define BS3CPU_Pentium UINT16_C(0x0007)
+#define BS3CPU_PPro UINT16_C(0x0008)
+#define BS3CPU_PProOrNewer UINT16_C(0x0009)
+/** CPU type mask. This is a full byte so it's possible to use byte access
+ * without and AND'ing to get the type value. */
+#define BS3CPU_TYPE_MASK UINT16_C(0x00ff)
+/** Flag indicating that the CPUID instruction is supported by the CPU. */
+#define BS3CPU_F_CPUID UINT16_C(0x0100)
+/** Flag indicating that extend CPUID leaves are available (at least two). */
+#define BS3CPU_F_CPUID_EXT_LEAVES UINT16_C(0x0200)
+/** Flag indicating that the CPU supports PAE. */
+#define BS3CPU_F_PAE UINT16_C(0x0400)
+/** Flag indicating that the CPU supports the page size extension (4MB pages). */
+#define BS3CPU_F_PSE UINT16_C(0x0800)
+/** Flag indicating that the CPU supports long mode. */
+#define BS3CPU_F_LONG_MODE UINT16_C(0x1000)
+/** Flag indicating that the CPU supports NX. */
+#define BS3CPU_F_NX UINT16_C(0x2000)
+/** @} */
+
+/** The return value of #Bs3CpuDetect_mmm. (Initial value is BS3CPU_TYPE_MASK.) */
+extern uint16_t g_uBs3CpuDetected;
+
+/**
+ * Call 32-bit prot mode C function.
+ *
+ * This switches to 32-bit mode and calls the 32-bit @a fpfnCall C code with @a
+ * cbParams on the stack, then returns in the original mode. When called in
+ * real mode, this will switch to PE32.
+ *
+ * @returns 32-bit status code if the function returned anything.
+ * @param fpfnCall Address of the 32-bit C function to call. When
+ * called from 16-bit code, this is a far real mode
+ * function pointer, i.e. as fixed up by the linker.
+ * In 32-bit and 64-bit code, this is a flat address.
+ * @param cbParams The size of the parameter list, in bytes.
+ * @param ... The parameters.
+ * @sa Bs3SwitchFromV86To16BitAndCallC
+ *
+ * @remarks WARNING! This probably doesn't work in 64-bit mode yet.
+ * Only tested for 16-bit real mode.
+ */
+BS3_MODE_PROTO_STUB(int32_t, Bs3SwitchTo32BitAndCallC,(FPFNBS3FAR fpfnCall, unsigned cbParams, ...));
+
+/**
+ * Initializes trap handling for the current system.
+ *
+ * Calls the appropriate Bs3Trap16Init, Bs3Trap32Init or Bs3Trap64Init function.
+ */
+BS3_MODE_PROTO_STUB(void, Bs3TrapInit,(void));
+
+/**
+ * Executes the array of tests in every possibly mode.
+ *
+ * @param paEntries The mode sub-test entries.
+ * @param cEntries The number of sub-test entries.
+ */
+BS3_MODE_PROTO_NOSB(void, Bs3TestDoModes,(PCBS3TESTMODEENTRY paEntries, size_t cEntries));
+
+/**
+ * Executes the array of tests in every possibly mode, unified driver.
+ *
+ * This requires much less code space than Bs3TestDoModes as there is only one
+ * instace of each sub-test driver code, instead of 3 (cmn) or 22 (per-mode)
+ * copies.
+ *
+ * @param paEntries The mode sub-test-by-one entries.
+ * @param cEntries The number of sub-test-by-one entries.
+ * @param fFlags BS3TESTMODEBYONEENTRY_F_XXX.
+ */
+BS3_MODE_PROTO_NOSB(void, Bs3TestDoModesByOne,(PCBS3TESTMODEBYONEENTRY paEntries, size_t cEntries, uint32_t fFlags));
+
+/**
+ * Executes the array of tests in every possibly mode, using the max bit-count
+ * worker for each.
+ *
+ * @param paEntries The mode sub-test entries.
+ * @param cEntries The number of sub-test entries.
+ */
+BS3_MODE_PROTO_NOSB(void, Bs3TestDoModesByMax,(PCBS3TESTMODEBYMAXENTRY paEntries, size_t cEntries));
+
+/** @} */
+
+
+/** @defgroup grp_bs3kit_bios_int15 BIOS - int 15h
+ * @{ */
+
+/** An INT15E820 data entry. */
+typedef struct INT15E820ENTRY
+{
+ uint64_t uBaseAddr;
+ uint64_t cbRange;
+ /** Memory type this entry describes, see INT15E820_TYPE_XXX. */
+ uint32_t uType;
+ /** Optional. */
+ uint32_t fAcpi3;
+} INT15E820ENTRY;
+AssertCompileSize(INT15E820ENTRY,24);
+
+
+/** @name INT15E820_TYPE_XXX - Memory types returned by int 15h function 0xe820.
+ * @{ */
+#define INT15E820_TYPE_USABLE 1 /**< Usable RAM. */
+#define INT15E820_TYPE_RESERVED 2 /**< Reserved by the system, unusable. */
+#define INT15E820_TYPE_ACPI_RECLAIMABLE 3 /**< ACPI reclaimable memory, whatever that means. */
+#define INT15E820_TYPE_ACPI_NVS 4 /**< ACPI non-volatile storage? */
+#define INT15E820_TYPE_BAD 5 /**< Bad memory, unusable. */
+/** @} */
+
+
+/**
+ * Performs an int 15h function 0xe820 call.
+ *
+ * @returns Success indicator.
+ * @param pEntry The return buffer.
+ * @param pcbEntry Input: The size of the buffer (min 20 bytes);
+ * Output: The size of the returned data.
+ * @param puContinuationValue Where to get and return the continuation value (EBX)
+ * Set to zero the for the first call. Returned as zero
+ * after the last entry.
+ */
+BS3_MODE_PROTO_STUB(bool, Bs3BiosInt15hE820,(INT15E820ENTRY BS3_FAR *pEntry, uint32_t BS3_FAR *pcbEntry,
+ uint32_t BS3_FAR *puContinuationValue));
+
+/**
+ * Performs an int 15h function 0x88 call.
+ *
+ * @returns UINT32_MAX on failure, number of KBs above 1MB otherwise.
+ */
+#if ARCH_BITS != 16 || !defined(BS3_BIOS_INLINE_RM)
+BS3_MODE_PROTO_STUB(uint32_t, Bs3BiosInt15h88,(void));
+#else
+BS3_DECL(uint32_t) Bs3BiosInt15h88(void);
+# pragma aux Bs3BiosInt15h88 = \
+ ".286" \
+ "clc" \
+ "mov ax, 08800h" \
+ "int 15h" \
+ "jc failed" \
+ "xor dx, dx" \
+ "jmp done" \
+ "failed:" \
+ "xor ax, ax" \
+ "dec ax" \
+ "mov dx, ax" \
+ "done:" \
+ value [ax dx] \
+ modify exact [ax bx cx dx es];
+#endif
+
+/** @} */
+
+
+/** @} */
+
+RT_C_DECLS_END
+
+
+/*
+ * Include default function symbol mangling.
+ */
+#include "bs3kit-mangling-code.h"
+
+/*
+ * Change 16-bit text segment if requested.
+ */
+#if defined(BS3_USE_ALT_16BIT_TEXT_SEG) && ARCH_BITS == 16 && !defined(BS3_DONT_CHANGE_TEXT_SEG)
+# if (defined(BS3_USE_RM_TEXT_SEG) + defined(BS3_USE_X0_TEXT_SEG) + defined(BS3_USE_X1_TEXT_SEG)) != 1
+# error "Cannot set more than one alternative 16-bit text segment!"
+# elif defined(BS3_USE_RM_TEXT_SEG)
+# pragma code_seg("BS3RMTEXT16", "BS3CLASS16RMCODE")
+# elif defined(BS3_USE_X0_TEXT_SEG)
+# pragma code_seg("BS3X0TEXT16", "BS3CLASS16X0CODE")
+# elif defined(BS3_USE_X1_TEXT_SEG)
+# pragma code_seg("BS3X1TEXT16", "BS3CLASS16X1CODE")
+# else
+# error "Huh? Which alternative text segment did you want again?"
+# endif
+#endif
+
+#endif /* !BS3KIT_INCLUDED_bs3kit_h */
+
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.mac b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.mac
new file mode 100644
index 00000000..0bd451b1
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3kit.mac
@@ -0,0 +1,1767 @@
+; $Id: bs3kit.mac $
+;; @file
+; BS3Kit - structures, symbols, macros and stuff.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%ifndef ___bs3kit_mac___
+%define ___bs3kit_mac___
+
+;
+; Before we can include anything, we need to override NAME and switch section.
+; If we don't do the latter we end up with an unused 'text' section.
+;
+
+; Drop the asmdefs-first.mac header for native bs3kit files.
+%undef RT_ASMDEFS_INC_FIRST_FILE
+
+;;
+; Macro for setting register aliases according to the bit count given by %1.
+;
+%macro BS3_SET_REG_ALIASES 1
+ ;
+ ; Register aliases.
+ ;
+ %if %1 == 64
+ %define xCB 8
+ %define xDEF dq
+ %define xRES resq
+ %define xPRE qword
+ %define xSP rsp
+ %define xBP rbp
+ %define xAX rax
+ %define xBX rbx
+ %define xCX rcx
+ %define xDX rdx
+ %define xDI rdi
+ %define xSI rsi
+ %define xWrtRIP wrt rip
+ %define xPUSHF pushfq
+ %define xPOPF popfq
+ %define xRETF o64 retf
+ %elif %1 == 32
+ %define xCB 4
+ %define xDEF dd
+ %define xRES resd
+ %define xPRE dword
+ %define xSP esp
+ %define xBP ebp
+ %define xAX eax
+ %define xBX ebx
+ %define xCX ecx
+ %define xDX edx
+ %define xDI edi
+ %define xSI esi
+ %define xWrtRIP
+ %define xPUSHF pushfd
+ %define xPOPF popfd
+ %define xRETF retf
+ %elif %1 == 16
+ %define xCB 2
+ %define xDEF dw
+ %define xRES resw
+ %define xPRE word
+ %define xSP sp
+ %define xBP bp
+ %define xAX ax
+ %define xBX bx
+ %define xCX cx
+ %define xDX dx
+ %define xDI di
+ %define xSI si
+ %define xWrtRIP
+ %define xPUSHF pushf
+ %define xPOPF popf
+ %define xRETF retf
+ %else
+ %error "Invalid BS3_SET_REG_ALIASES argument:" %1
+ %endif
+
+
+ ;
+ ; Register names corresponding to the max size for pop/push <reg>.
+ ;
+ ; 16-bit can push both 32-bit and 16-bit registers. This 's' prefixed variant
+ ; is used when 16-bit should use the 32-bit register.
+ ;
+ %if %1 == 64
+ %define sCB 8
+ %define sDEF dq
+ %define sRES resq
+ %define sPRE qword
+ %define sSP rsp
+ %define sBP rbp
+ %define sAX rax
+ %define sBX rbx
+ %define sCX rcx
+ %define sDX rdx
+ %define sDI rdi
+ %define sSI rsi
+ %define sPUSHF pushfq
+ %define sPOPF popfq
+ %else
+ %define sCB 4
+ %define sDEF dd
+ %define sRES resd
+ %define sPRE dword
+ %define sSP esp
+ %define sBP ebp
+ %define sAX eax
+ %define sBX ebx
+ %define sCX ecx
+ %define sDX edx
+ %define sDI edi
+ %define sSI esi
+ %define sPUSHF pushfd
+ %define sPOPF popfd
+ %endif
+%endmacro
+
+;;
+; Redefines macros that follows __BITS__.
+%macro BS3_SET_BITS_MACROS 1
+ ;; Emulate the __BITS__ macro in NASM 2.0+. Follows BS3_SET_BITS.
+ %ifdef __YASM__
+ %undef __BITS__
+ %define __BITS__ %1
+ %endif
+
+ ;; Mostly internal macro. Follows BS3_SET_BITS.
+ %undef BS3_NAME_UNDERSCORE
+ %define BS3_NAME_UNDERSCORE _
+
+ ;; For segment overrides and stuff. Follows BS3_SET_BITS.
+ %undef BS3_ONLY_16BIT
+ %if %1 == 16
+ %define BS3_ONLY_16BIT(a_Expr) a_Expr
+ %else
+ %define BS3_ONLY_16BIT(a_Expr)
+ %endif
+
+ ;; For odd 64-bit stuff. Follows BS3_SET_BITS.
+ %undef BS3_ONLY_64BIT
+ %if %1 == 64
+ %define BS3_ONLY_64BIT(a_Expr) a_Expr
+ %else
+ %define BS3_ONLY_64BIT(a_Expr)
+ %endif
+
+ ;; For segment overrides and stuff. Follows BS3_SET_BITS.
+ %undef BS3_NOT_64BIT
+ %if %1 == 64
+ %define BS3_NOT_64BIT(a_Expr)
+ %else
+ %define BS3_NOT_64BIT(a_Expr) a_Expr
+ %endif
+
+ ;; For stack cleanups and similar where each bit mode is different. Follows BS3_SET_BITS.
+ %undef BS3_IF_16_32_64BIT
+ %if %1 == 16
+ %define BS3_IF_16_32_64BIT(a_16BitExpr, a_32BitExpr, a_64BitExpr) a_16BitExpr
+ %elif %1 == 32
+ %define BS3_IF_16_32_64BIT(a_16BitExpr, a_32BitExpr, a_64BitExpr) a_32BitExpr
+ %else
+ %define BS3_IF_16_32_64BIT(a_16BitExpr, a_32BitExpr, a_64BitExpr) a_64BitExpr
+ %endif
+
+ ;; For RIP relative addressing in 64-bit mode and absolute addressing in
+ ; other modes. Follows BS3_SET_BITS.
+ %undef BS3_WRT_RIP
+ %if %1 == 64
+ %define BS3_WRT_RIP(a_Sym) rel a_Sym
+ %else
+ %define BS3_WRT_RIP(a_Sym) a_Sym
+ %endif
+
+ %undef BS3_LEA_MOV_WRT_RIP
+ %if %1 == 64
+ %define BS3_LEA_MOV_WRT_RIP(a_DstReg, a_Sym) lea a_DstReg, [BS3_WRT_RIP(a_Sym)]
+ %else
+ %define BS3_LEA_MOV_WRT_RIP(a_DstReg, a_Sym) mov a_DstReg, a_Sym
+ %endif
+
+ ;; @def BS3_DATA16_WRT
+ ; For accessing BS3DATA16 correctly.
+ ; @param a_Var The BS3DATA16 variable.
+ %undef BS3_DATA16_WRT
+ %if %1 == 16
+ %define BS3_DATA16_WRT(a_Var) a_Var wrt BS3KIT_GRPNM_DATA16
+ %elif %1 == 32
+ %define BS3_DATA16_WRT(a_Var) a_Var wrt FLAT
+ %else
+ %define BS3_DATA16_WRT(a_Var) BS3_WRT_RIP(a_Var) wrt FLAT
+ %endif
+
+ ;; @def BS3_TEXT16_WRT
+ ; For accessing BS3DATA16 correctly.
+ ; @param a_Label The BS3TEXT16 label.
+ %undef BS3_TEXT16_WRT
+ %if %1 == 16
+ %define BS3_TEXT16_WRT(a_Label) a_Label wrt CGROUP16
+ %elif %1 == 32
+ %define BS3_TEXT16_WRT(a_Label) a_Label wrt FLAT
+ %else
+ %define BS3_TEXT16_WRT(a_Label) BS3_WRT_RIP(a_Label) wrt FLAT
+ %endif
+
+ %undef BS3_IF_16BIT_OTHERWISE
+ %if %1 == 16
+ %define BS3_IF_16BIT_OTHERWISE(a_16BitExpr, a_OtherwiseExpr) a_16BitExpr
+ %else
+ %define BS3_IF_16BIT_OTHERWISE(a_16BitExpr, a_OtherwiseExpr) a_OtherwiseExpr
+ %endif
+
+ %undef BS3_IF_32BIT_OTHERWISE
+ %if %1 == 32
+ %define BS3_IF_32BIT_OTHERWISE(a_32BitExpr, a_OtherwiseExpr) a_32BitExpr
+ %else
+ %define BS3_IF_32BIT_OTHERWISE(a_32BitExpr, a_OtherwiseExpr) a_OtherwiseExpr
+ %endif
+
+ %undef BS3_IF_64BIT_OTHERWISE
+ %if %1 == 64
+ %define BS3_IF_64BIT_OTHERWISE(a_64BitExpr, a_OtherwiseExpr) a_64BitExpr
+ %else
+ %define BS3_IF_64BIT_OTHERWISE(a_64BitExpr, a_OtherwiseExpr) a_OtherwiseExpr
+ %endif
+
+ ;;
+ ; Same as BS3_CMN_NM except in 16-bit mode, it will generate the far name.
+ ; (16-bit code generally have both near and far callable symbols, so we won't
+ ; be restricted to 64KB test code.)
+ %if %1 == 16
+ %define BS3_CMN_NM_FAR(a_Name) BS3_NAME_UNDERSCORE %+ a_Name %+ _f %+ __BITS__
+ %else
+ %define BS3_CMN_NM_FAR(a_Name) BS3_CMN_NM(a_Name)
+ %endif
+
+%endmacro
+
+; Default to register aliases for ARCH_BITS.
+BS3_SET_REG_ALIASES ARCH_BITS
+
+; Define macros for ARCH_BITS.
+BS3_SET_BITS_MACROS ARCH_BITS
+
+
+;; Wrapper around BITS.
+; Updates __BITS__ (built-in variable in nasm, we work it for yasm) as well
+; a number of convenient macros and register aliases.
+;
+; @param %1 The CPU bit count: 16, 32 or 64
+; @remarks ARCH_BITS is not modified and will remain what it was on the
+; assembler command line.
+%macro BS3_SET_BITS 1
+ BITS %1
+ BS3_SET_BITS_MACROS %1
+ BS3_SET_REG_ALIASES %1
+%endmacro
+
+;;
+; For instruction that should only be emitted in 16-bit mode. Follows BS3_SET_BITS.
+; BONLY16 normally goes in column 1.
+%macro BONLY16 1+
+ %if __BITS__ == 16
+ %1
+ %endif
+%endmacro
+
+;;
+; For instruction that should only be emitted in 32-bit mode. Follows BS3_SET_BITS.
+; BONLY32 normally goes in column 1.
+%macro BONLY32 1+
+ %if __BITS__ == 32
+ %1
+ %endif
+%endmacro
+
+;;
+; For instruction that should only be emitted in 64-bit mode. Follows BS3_SET_BITS.
+; BONLY64 normally goes in column 1.
+%macro BONLY64 1+
+ %if __BITS__ == 64
+ %1
+ %endif
+%endmacro
+
+
+
+;; @name Segment definitions.
+;; @{
+
+%ifndef ASM_FORMAT_BIN
+; !!HACK ALERT!!
+;
+; To make FLAT actually be flat, i.e. have a base of 0 rather than the same as
+; the target (?) segment, we tweak it a little bit here. We associate a segment
+; with it so that we can get at it in the class/segment ordering directives
+; we pass to the linker. The segment does not contain any data or anything, it
+; is just an empty one which we assign the address of zero.
+;
+; Look for 'clname BS3FLAT segaddr=0x0000' and 'segment BS3FLAT segaddr=0x0000'
+; in the makefile.
+;
+; !!HACK ALERT!!
+segment BS3FLAT use32 class=BS3FLAT
+GROUP FLAT BS3FLAT
+%endif
+
+
+;;
+; Changes to the BS3TEXT16 segment, defining it if necessary.
+; @param %1 The bitcount to invoke BS3_SET_BITS with, default is 16.
+%macro BS3_BEGIN_TEXT16 0-1 16
+ %ifndef BS3_BEGIN_TEXT16_NOT_FIRST
+ %define BS3_BEGIN_TEXT16_NOT_FIRST
+ section BS3TEXT16 align=2 CLASS=BS3CLASS16CODE PUBLIC USE16
+ %ifndef BS3_BEGIN_TEXT16_WITHOUT_GROUP ; bs3-first-common.mac trick.
+ %ifndef BS3_BEGIN_TEXT16_NEARSTUBS_NOT_FIRST
+ %define BS3_BEGIN_TEXT16_NEARSTUBS_NOT_FIRST
+ section BS3TEXT16_NEARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+ %endif
+ %ifndef BS3_BEGIN_TEXT16_FARSTUBS_NOT_FIRST
+ %define BS3_BEGIN_TEXT16_FARSTUBS_NOT_FIRST
+ section BS3TEXT16_FARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+ %endif
+ GROUP CGROUP16 BS3TEXT16 BS3TEXT16_NEARSTUBS BS3TEXT16_FARSTUBS
+ section BS3TEXT16
+ %endif
+ %else
+ section BS3TEXT16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_TEXT16
+ BS3_SET_BITS %1
+%endmacro
+
+%macro BS3_BEGIN_TEXT16_NEARSTUBS 0
+ %ifndef BS3_BEGIN_TEXT16_NEARSTUBS_NOT_FIRST
+ %define BS3_BEGIN_TEXT16_NEARSTUBS_NOT_FIRST
+ section BS3TEXT16_NEARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+ %else
+ section BS3TEXT16_NEARSTUBS
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_TEXT16_NEARSTUBS
+ BS3_SET_BITS 16
+%endmacro
+
+%macro BS3_BEGIN_TEXT16_FARSTUBS 0
+ %ifndef BS3_BEGIN_TEXT16_FARSTUBS_NOT_FIRST
+ %define BS3_BEGIN_TEXT16_FARSTUBS_NOT_FIRST
+ section BS3TEXT16_FARSTUBS align=1 CLASS=BS3CLASS16CODE PUBLIC USE16
+ %else
+ section BS3TEXT16_FARSTUBS
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_TEXT16_FARSTUBS
+ BS3_SET_BITS 16
+%endmacro
+
+%macro BS3_BEGIN_RMTEXT16 0-1 2
+ %ifndef BS3_BEGIN_RMTEXT16_NOT_FIRST
+ %define BS3_BEGIN_RMTEXT16_NOT_FIRST
+ section BS3RMTEXT16 align=%1 CLASS=BS3CLASS16RMCODE PUBLIC USE16
+ %ifndef BS3_BEGIN_RMTEXT16_WITHOUT_GROUP ; bs3-first-common.mac trick.
+ GROUP BS3GROUPRMTEXT16 BS3RMTEXT16
+ %endif
+ %else
+ section BS3RMTEXT16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_RMTEXT16
+ BS3_SET_BITS 16
+%endmacro
+
+%macro BS3_BEGIN_X0TEXT16 0-1 2
+ %ifndef BS3_BEGIN_X0TEXT16_NOT_FIRST
+ %define BS3_BEGIN_X0TEXT16_NOT_FIRST
+ section BS3X0TEXT16 align=%1 CLASS=BS3CLASS16X0CODE PUBLIC USE16
+ %ifndef BS3_BEGIN_X0TEXT16_WITHOUT_GROUP ; bs3-first-common.mac trick.
+ GROUP BS3GROUPX0TEXT16 BS3X0TEXT16
+ %endif
+ %else
+ section BS3X0TEXT16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_X0TEXT16
+ BS3_SET_BITS 16
+%endmacro
+
+%macro BS3_BEGIN_X1TEXT16 0-1 2
+ %ifndef BS3_BEGIN_X1TEXT16_NOT_FIRST
+ %define BS3_BEGIN_X1TEXT16_NOT_FIRST
+ section BS3X1TEXT16 align=%1 CLASS=BS3CLASS16X1CODE PUBLIC USE16
+ %ifndef BS3_BEGIN_X1TEXT16_WITHOUT_GROUP ; bs3-first-common.mac trick.
+ GROUP BS3GROUPX1TEXT16 BS3X1TEXT16
+ %endif
+ %else
+ section BS3X1TEXT16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_X1TEXT16
+ BS3_SET_BITS 16
+%endmacro
+
+
+%macro BS3_BEGIN_DATA16 0-1 2
+ %ifndef BS3_BEGIN_DATA16_NOT_FIRST
+ %define BS3_BEGIN_DATA16_NOT_FIRST
+ section BS3DATA16 align=%1 CLASS=BS3KIT_CLASS_DATA16 PUBLIC USE16
+ %ifndef BS3_BEGIN_DATA16_WITHOUT_GROUP ; bs3-first-common.mac trick.
+ GROUP BS3KIT_GRPNM_DATA16 BS3DATA16
+ %endif
+ %else
+ section BS3DATA16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_DATA16
+ BS3_SET_BITS 16
+%endmacro
+
+%macro BS3_BEGIN_TEXT32 0-1 2
+ %ifndef BS3_BEGIN_TEXT32_NOT_FIRST
+ %define BS3_BEGIN_TEXT32_NOT_FIRST
+ section BS3TEXT32 align=%1 CLASS=BS3CLASS32CODE PUBLIC USE32 FLAT
+ %else
+ section BS3TEXT32
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_TEXT32
+ BS3_SET_BITS 32
+%endmacro
+
+%macro BS3_BEGIN_DATA32 0-1 16
+ %ifndef BS3_BEGIN_DATA32_NOT_FIRST
+ %define BS3_BEGIN_DATA32_NOT_FIRST
+ section BS3DATA32 align=%1 CLASS=FAR_DATA PUBLIC USE32 ;FLAT - compiler doesn't make data flat.
+ %else
+ section BS3DATA32
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_DATA32
+ BS3_SET_BITS 32
+%endmacro
+
+%macro BS3_BEGIN_TEXT64 0-1 2
+ %ifndef BS3_BEGIN_TEXT64_NOT_FIRST
+ %define BS3_BEGIN_TEXT64_NOT_FIRST
+ section BS3TEXT64 align=%1 CLASS=BS3CLASS64CODE PUBLIC USE32 FLAT
+ %else
+ section BS3TEXT64
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_TEXT64
+ BS3_SET_BITS 64
+%endmacro
+
+%macro BS3_BEGIN_DATA64 0-1 16
+ %ifndef BS3_BEGIN_DATA64_NOT_FIRST
+ %define BS3_BEGIN_DATA64_NOT_FIRST
+ section BS3DATA64 align=%1 CLASS=FAR_DATA PUBLIC USE32 ;FLAT (see DATA32)
+ %else
+ section BS3DATA64
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_DATA64
+ BS3_SET_BITS 64
+%endmacro
+
+;; The system data segment containing the GDT, TSSes and IDTs.
+%macro BS3_BEGIN_SYSTEM16 0-1 16
+ %ifndef BS3_BEGIN_SYSTEM16_NOT_FIRST
+ %define BS3_BEGIN_SYSTEM16_NOT_FIRST
+ section BS3SYSTEM16 align=%1 CLASS=BS3SYSTEM16 PUBLIC USE16
+ %else
+ section BS3SYSTEM16
+ %endif
+ %undef BS3_CUR_SEG_BEGIN_MACRO
+ %xdefine BS3_CUR_SEG_BEGIN_MACRO BS3_BEGIN_SYSTEM16
+ BS3_SET_BITS 16
+%endmacro
+
+;; Default text section.
+%macro BS3_BEGIN_DEFAULT_TEXT 0
+ %if ARCH_BITS == 16
+ BS3_BEGIN_TEXT16
+ %elif ARCH_BITS == 32
+ BS3_BEGIN_TEXT32
+ %elif ARCH_BITS == 64
+ BS3_BEGIN_TEXT64
+ %else
+ %error "ARCH_BITS must be defined as either 16, 32, or 64!"
+ INVALID_ARCH_BITS
+ %endif
+%endmacro
+
+;; @}
+
+
+;
+; Now, ditch the default 'text' section and define our own NAME macro.
+;
+%ifndef ASM_FORMAT_BIN
+ BS3_BEGIN_DEFAULT_TEXT
+ BS3_BEGIN_DEFAULT_TEXT ; stupid nasm automagically repeats the segment attributes.
+%endif
+
+;; When using watcom + OMF, we're using __cdecl by default, which
+; get an underscore added in front.
+%define NAME(name) _ %+ NAME_OVERLOAD(name)
+
+
+;
+; Include the standard headers from iprt.
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;;
+; Extern macro which mangles the name using NAME().
+%macro EXTERN 1
+ extern NAME(%1)
+%endmacro
+
+;;
+; Mangles a common name according to the current cpu bit count.
+; @remarks Requires the use of the BS3_SET_BITS macro instead of the BITS directive.
+%define BS3_CMN_NM(a_Name) BS3_NAME_UNDERSCORE %+ a_Name %+ _c %+ __BITS__
+
+;;
+; Extern macro which mangles the common name correctly, redefining the unmangled
+; name to the mangled one for ease of use.
+;
+; @param %1 The unmangled common name.
+;
+; @remarks Must enter the segment in which this name is defined.
+;
+%macro BS3_EXTERN_CMN 1
+ extern BS3_CMN_NM(%1)
+ %undef %1
+ %define %1 BS3_CMN_NM(%1)
+%endmacro
+
+;;
+; Same as BS3_EXTERN_CMN except it picks the far variant in 16-bit code.
+;
+; @param %1 The unmangled common name.
+;
+; @remarks Must enter the segment in which this name is defined.
+;
+%macro BS3_EXTERN_CMN_FAR 1
+ extern BS3_CMN_NM_FAR(%1)
+ %undef %1
+ %define %1 BS3_CMN_NM_FAR(%1)
+%endmacro
+
+;; @def BS3_EXTERN_TMPL
+; Mangles the given name into a template specific one. For ease of use, the
+; name is redefined to the mangled one, just like BS3_EXTERN_CMN does.
+; @note Segment does not change.
+%macro BS3_EXTERN_TMPL 1
+ extern TMPL_NM(%1)
+ %undef %1
+ %define %1 TMPL_NM(%1)
+%endmacro
+
+
+;;
+; Mangles a 16-bit and 32-bit accessible data name.
+; @remarks Requires the use of the BS3_SET_BITS macro instead of the BITS directive.
+%define BS3_DATA_NM(a_Name) _ %+ a_Name
+
+;;
+; Extern macro which mangles a DATA16 symbol correctly, redefining the
+; unmangled name to the mangled one for ease of use.
+;
+; @param %1 The unmangled common name.
+;
+; @remarks Will change to the DATA16 segment, use must switch back afterwards!
+;
+%macro BS3_EXTERN_DATA16 1
+ BS3_BEGIN_DATA16
+ extern _ %+ %1
+ %undef %1
+ %define %1 _ %+ %1
+%endmacro
+
+;;
+; Extern macro which mangles a BS3SYSTEM16 symbol correctly, redefining the
+; unmangled name to the mangled one for ease of use.
+;
+; @param %1 The unmangled common name.
+;
+; @remarks Will change to the SYSTEM16 segment, use must switch back afterwards!
+;
+%macro BS3_EXTERN_SYSTEM16 1
+ BS3_BEGIN_SYSTEM16
+ extern _ %+ %1
+ %undef %1
+ %define %1 _ %+ %1
+%endmacro
+
+
+;;
+; Global name with ELF attributes and size.
+;
+; This differs from GLOBALNAME_EX in that it expects a mangled symbol name,
+; and allows for nasm style symbol size expressions.
+;
+; @param %1 The mangled name.
+; @param %2 Symbol attributes.
+; @param %3 The size expression.
+;
+%macro BS3_GLOBAL_NAME_EX 3
+global %1
+%1:
+%undef BS3_LAST_LABEL
+%xdefine BS3_LAST_LABEL %1
+%endmacro
+
+;;
+; Global local label.
+;
+; This should be used when switching segments and jumping to it via a local lable.
+; It makes the lable visible to the debugger and map file.
+;
+%macro BS3_GLOBAL_LOCAL_LABEL 1
+global RT_CONCAT(BS3_LAST_LABEL,%1)
+%1:
+%endmacro
+
+;;
+; Global data unmangled label.
+;
+; @param %1 The unmangled name.
+; @param %2 The size (0 is fine).
+;
+%macro BS3_GLOBAL_DATA 2
+BS3_GLOBAL_NAME_EX BS3_DATA_NM(%1), , %2
+%endmacro
+
+;;
+; Starts a procedure.
+;
+; This differs from BEGINPROC in that it expects a mangled symbol name and
+; does the NASM symbol size stuff.
+;
+; @param %1 The mangled name.
+;
+%macro BS3_PROC_BEGIN 1
+BS3_GLOBAL_NAME_EX %1, function, (%1 %+ _EndProc - %1)
+%endmacro
+
+;;
+; Ends a procedure.
+;
+; Counter part to BS3_PROC_BEGIN.
+;
+; @param %1 The mangled name.
+;
+%macro BS3_PROC_END 1
+BS3_GLOBAL_NAME_EX %1 %+ _EndProc, function hidden, (%1 %+ _EndProc - %1)
+ int3 ; handy and avoids overlapping labels.
+%endmacro
+
+
+;; @name BS3_PBC_XXX - For use as the 2nd parameter to BS3_PROC_BEGIN_CMN and BS3_PROC_BEGIN_MODE.
+;; @{
+%define BS3_PBC_NEAR 1 ;;< Only near.
+%define BS3_PBC_FAR 2 ;;< Only far.
+%define BS3_PBC_HYBRID 3 ;;< Hybrid near/far procedure, trashing AX. Use BS3_HYBRID_RET to return.
+%define BS3_PBC_HYBRID_SAFE 4 ;;< Hybrid near/far procedure, no trashing but slower. Use BS3_HYBRID_RET to return.
+%define BS3_PBC_HYBRID_0_ARGS 5 ;;< Hybrid near/far procedure, no parameters so separate far stub, no trashing, fast near calls.
+;; @}
+
+;; Internal begin procedure macro.
+;
+; @param 1 The near name.
+; @param 2 The far name
+; @param 3 BS3_PBC_XXX.
+%macro BS3_PROC_BEGIN_INT 3
+ ;%warning "BS3_PROC_BEGIN_INT:" 1=%1 2=%2 3=%3
+ %undef BS3_CUR_PROC_FLAGS
+ %if __BITS__ == 16
+ %if %3 == BS3_PBC_NEAR
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_NEAR
+ %xdefine cbCurRetAddr 2
+ BS3_PROC_BEGIN %1
+
+ %elif %3 == BS3_PBC_FAR
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_FAR
+ %xdefine cbCurRetAddr 4
+ BS3_PROC_BEGIN %2
+
+ %elif %3 == BS3_PBC_HYBRID
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_HYBRID
+ %xdefine cbCurRetAddr 4
+ BS3_GLOBAL_NAME_EX %1, function, 3
+ pop ax
+ push cs
+ push ax
+ BS3_PROC_BEGIN %2
+
+ %elif %3 == BS3_PBC_HYBRID_SAFE
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_HYBRID_SAFE
+ %xdefine cbCurRetAddr 4
+ BS3_GLOBAL_NAME_EX %1, function, 3
+ extern Bs3CreateHybridFarRet_c16
+ call Bs3CreateHybridFarRet_c16
+ BS3_PROC_BEGIN %2
+
+ %elif %3 == BS3_PBC_HYBRID_0_ARGS
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_NEAR
+ %xdefine cbCurRetAddr 2
+ %xdefine TMP_BEGIN_PREV_SEG BS3_CUR_SEG_BEGIN_MACRO
+
+ BS3_BEGIN_TEXT16_FARSTUBS
+ BS3_PROC_BEGIN %2
+ call %1
+ retf
+ BS3_PROC_END %2
+
+ TMP_BEGIN_PREV_SEG
+ BS3_PROC_BEGIN %1
+ %undef TMP_BEGIN_PREV_SEG
+
+ %else
+ %error BS3_PROC_BEGIN_CMN parameter 2 value %3 is not recognized.
+
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_NEAR
+ %xdefine cbCurRetAddr 4
+ BS3_PROC_BEGIN %1
+ %endif
+ %else
+ %xdefine BS3_CUR_PROC_FLAGS BS3_PBC_NEAR
+ %xdefine cbCurRetAddr xCB
+ BS3_PROC_BEGIN %1
+ %endif
+%endmacro
+
+;; Internal end procedure macro
+;
+; @param 1 The near name.
+; @param 2 The far name
+;
+%macro BS3_PROC_END_INT 2
+ %if __BITS__ == 16
+ %if BS3_CUR_PROC_FLAGS == BS3_PBC_NEAR
+ BS3_PROC_END %1
+ %else
+ BS3_PROC_END %2
+ %endif
+ %else
+ BS3_PROC_END %1
+ %endif
+ %undef BS3_CUR_PROC_FLAGS
+ %undef cbCurRetAddr
+%endmacro
+
+
+;; Convenience macro for defining common procedures.
+; This will emit both near and far 16-bit symbols according to parameter %2 (BS3_PBC_XXX).
+%macro BS3_PROC_BEGIN_CMN 2
+ BS3_PROC_BEGIN_INT BS3_CMN_NM(%1), BS3_CMN_NM_FAR(%1), %2
+%endmacro
+
+;; Convenience macro for defining common procedures.
+%macro BS3_PROC_END_CMN 1
+ BS3_PROC_END_INT BS3_CMN_NM(%1), BS3_CMN_NM_FAR(%1)
+%endmacro
+
+;;
+; Generate a safe 16-bit far stub for function %1, shuffling %2 bytes of parameters.
+;
+; This does absolutely nothing in 32-bit and 64-bit mode.
+;
+; @param 1 The function basename.
+; @param 2 The number of bytes of parameters on the stack, must be a multiple of 2.
+; @remarks Changes the segment to TEXT16.
+;
+%macro BS3_CMN_FAR_STUB 2
+ %if %2 <= 1 || (%2 & 1)
+ %error Invalid parameter frame size passed to BS3_CMN_FAR_STUB: %2
+ %endif
+ %if __BITS__ == 16
+BS3_BEGIN_TEXT16_FARSTUBS
+BS3_PROC_BEGIN_CMN %1, BS3_PBC_FAR
+ CPU 8086
+ inc bp ; Odd bp is far call indicator.
+ push bp
+ mov bp, sp
+ %assign offParam %2
+ %rep %2/2
+ push word [bp + xCB + cbCurRetAddr + offParam - 2]
+ %assign offParam offParam - 2
+ %endrep
+ call BS3_CMN_NM(%1)
+ add sp, %2
+ pop bp
+ dec bp
+ retf
+BS3_PROC_END_CMN %1
+BS3_BEGIN_TEXT16
+ %endif
+%endmacro
+
+
+;; Convenience macro for defining mode specific procedures.
+%macro BS3_PROC_BEGIN_MODE 2
+ ;%warning "BS3_PROC_BEGIN_MODE: 1=" %1 "2=" %2
+ BS3_PROC_BEGIN_INT TMPL_NM(%1), TMPL_FAR_NM(%1), %2
+%endmacro
+
+;; Convenience macro for defining mode specific procedures.
+%macro BS3_PROC_END_MODE 1
+ BS3_PROC_END_INT TMPL_NM(%1), TMPL_FAR_NM(%1)
+%endmacro
+
+;; Does a far return in 16-bit code, near return in 32-bit and 64-bit.
+; This is for use with BS3_PBC_XXX
+%macro BS3_HYBRID_RET 0-1
+ %if __BITS__ == 16
+ %if %0 > 0
+ %if BS3_CUR_PROC_FLAGS == BS3_PBC_NEAR || BS3_CUR_PROC_FLAGS == BS3_PBC_HYBRID_0_ARGS
+ ret %1
+ %else
+ retf %1
+ %endif
+ %else
+ %if BS3_CUR_PROC_FLAGS == BS3_PBC_NEAR || BS3_CUR_PROC_FLAGS == BS3_PBC_HYBRID_0_ARGS
+ ret
+ %else
+ retf
+ %endif
+ %endif
+ %else
+ %if BS3_CUR_PROC_FLAGS != BS3_PBC_NEAR
+ %error Expected BS3_CUR_PROC_FLAGS to be BS3_PBC_NEAR in non-16-bit code.
+ %endif
+ %if %0 > 0
+ ret %1
+ %else
+ ret
+ %endif
+ %endif
+%endmacro
+
+
+;;
+; Prologue hacks for 64-bit code.
+;
+; This saves the four register parameters onto the stack so we can pretend
+; the calling convention is stack based. The 64-bit calling convension is
+; the microsoft one, so this is straight forward.
+;
+; Pairs with BS3_CALL_CONV_EPILOG.
+;
+; @param %1 The number of parameters.
+;
+; @remarks Must be invoked before any stack changing instructions are emitted.
+;
+%macro BS3_CALL_CONV_PROLOG 1
+ %undef BS3_CALL_CONV_PROLOG_PARAMS
+ %define BS3_CALL_CONV_PROLOG_PARAMS %1
+ %if __BITS__ == 64
+ %if %1 >= 1
+ mov [rsp + 008h], rcx
+ %elifdef BS3_STRICT
+ and qword [rsp + 008h], 1
+ %endif
+ %if %1 >= 2
+ mov [rsp + 010h], rdx
+ %elifdef BS3_STRICT
+ and qword [rsp + 010h], 2
+ %endif
+ %if %1 >= 3
+ mov [rsp + 018h], r8
+ %elifdef BS3_STRICT
+ and qword [rsp + 018h], 3
+ %endif
+ %if %1 >= 4
+ mov [rsp + 020h], r9
+ %elifdef BS3_STRICT
+ and qword [rsp + 020h], 4
+ %endif
+ %endif
+%endmacro
+
+;;
+; Epilogue hacks for 64-bit code.
+;
+; Counter part to BS3_CALL_CONV_PROLOG.
+;
+; @param %1 The number of parameters.
+;
+; @remarks Must be invoked right before the return instruction as it uses RSP.
+;
+%macro BS3_CALL_CONV_EPILOG 1
+ %if BS3_CALL_CONV_PROLOG_PARAMS != %1
+ %error "BS3_CALL_CONV_EPILOG argument differs from BS3_CALL_CONV_PROLOG."
+ %endif
+ %if __BITS__ == 64
+ %ifdef BS3_STRICT
+ mov dword [rsp + 008h], 31h
+ mov dword [rsp + 010h], 32h
+ mov dword [rsp + 018h], 33h
+ mov dword [rsp + 020h], 34h
+ %endif
+ %endif
+%endmacro
+
+;;
+; Wrapper for the call instruction that hides calling convension differences.
+;
+; This always calls %1.
+; In 64-bit code, it will load up to 4 parameters into register.
+;
+; @param %1 The function to call (mangled).
+; @param %2 The number of parameters.
+;
+%macro BS3_CALL 2
+ %if __BITS__ == 64
+ %if %2 >= 1
+ mov rcx, [rsp]
+ %ifdef BS3_STRICT
+ and qword [rsp], 11h
+ %endif
+ %endif
+ %if %2 >= 2
+ mov rdx, [rsp + 008h]
+ %ifdef BS3_STRICT
+ and qword [rsp + 008h], 12h
+ %endif
+ %endif
+ %if %2 >= 3
+ mov r8, [rsp + 010h]
+ %ifdef BS3_STRICT
+ and qword [rsp + 010h], 13h
+ %endif
+ %endif
+ %if %2 >= 4
+ mov r9, [rsp + 018h]
+ %ifdef BS3_STRICT
+ and qword [rsp + 018h], 14h
+ %endif
+ %endif
+ %endif
+ call %1
+%endmacro
+
+
+;; @name Execution Modes
+; @{
+%define BS3_MODE_INVALID 000h
+%define BS3_MODE_RM 001h ;;< real mode.
+%define BS3_MODE_PE16 011h ;;< 16-bit protected mode kernel+tss, running 16-bit code, unpaged.
+%define BS3_MODE_PE16_32 012h ;;< 16-bit protected mode kernel+tss, running 32-bit code, unpaged.
+%define BS3_MODE_PE16_V86 018h ;;< 16-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged.
+%define BS3_MODE_PE32 022h ;;< 32-bit protected mode kernel+tss, running 32-bit code, unpaged.
+%define BS3_MODE_PE32_16 021h ;;< 32-bit protected mode kernel+tss, running 16-bit code, unpaged.
+%define BS3_MODE_PEV86 028h ;;< 32-bit protected mode kernel+tss, running virtual 8086 mode code, unpaged.
+%define BS3_MODE_PP16 031h ;;< 16-bit protected mode kernel+tss, running 16-bit code, paged.
+%define BS3_MODE_PP16_32 032h ;;< 16-bit protected mode kernel+tss, running 32-bit code, paged.
+%define BS3_MODE_PP16_V86 038h ;;< 16-bit protected mode kernel+tss, running virtual 8086 mode code, paged.
+%define BS3_MODE_PP32 042h ;;< 32-bit protected mode kernel+tss, running 32-bit code, paged.
+%define BS3_MODE_PP32_16 041h ;;< 32-bit protected mode kernel+tss, running 16-bit code, paged.
+%define BS3_MODE_PPV86 048h ;;< 32-bit protected mode kernel+tss, running virtual 8086 mode code, paged.
+%define BS3_MODE_PAE16 051h ;;< 16-bit protected mode kernel+tss, running 16-bit code, PAE paging.
+%define BS3_MODE_PAE16_32 052h ;;< 16-bit protected mode kernel+tss, running 32-bit code, PAE paging.
+%define BS3_MODE_PAE16_V86 058h ;;< 16-bit protected mode kernel+tss, running virtual 8086 mode, PAE paging.
+%define BS3_MODE_PAE32 062h ;;< 32-bit protected mode kernel+tss, running 32-bit code, PAE paging.
+%define BS3_MODE_PAE32_16 061h ;;< 32-bit protected mode kernel+tss, running 16-bit code, PAE paging.
+%define BS3_MODE_PAEV86 068h ;;< 32-bit protected mode kernel+tss, running virtual 8086 mode, PAE paging.
+%define BS3_MODE_LM16 071h ;;< 16-bit long mode (paged), kernel+tss always 64-bit.
+%define BS3_MODE_LM32 072h ;;< 32-bit long mode (paged), kernel+tss always 64-bit.
+%define BS3_MODE_LM64 074h ;;< 64-bit long mode (paged), kernel+tss always 64-bit.
+
+%define BS3_MODE_CODE_MASK 00fh ;;< Running code mask.
+%define BS3_MODE_CODE_16 001h ;;< Running 16-bit code.
+%define BS3_MODE_CODE_32 002h ;;< Running 32-bit code.
+%define BS3_MODE_CODE_64 004h ;;< Running 64-bit code.
+%define BS3_MODE_CODE_V86 008h ;;< Running 16-bit virtual 8086 code.
+
+%define BS3_MODE_SYS_MASK 0f0h ;;< kernel+tss mask.
+%define BS3_MODE_SYS_RM 000h ;;< Real mode kernel+tss.
+%define BS3_MODE_SYS_PE16 010h ;;< 16-bit protected mode kernel+tss.
+%define BS3_MODE_SYS_PE32 020h ;;< 32-bit protected mode kernel+tss.
+%define BS3_MODE_SYS_PP16 030h ;;< 16-bit paged protected mode kernel+tss.
+%define BS3_MODE_SYS_PP32 040h ;;< 32-bit paged protected mode kernel+tss.
+%define BS3_MODE_SYS_PAE16 050h ;;< 16-bit PAE paged protected mode kernel+tss.
+%define BS3_MODE_SYS_PAE32 060h ;;< 32-bit PAE paged protected mode kernel+tss.
+%define BS3_MODE_SYS_LM 070h ;;< 64-bit (paged) long mode protected mode kernel+tss.
+
+;; Whether the mode has paging enabled.
+%define BS3_MODE_IS_PAGED(a_fMode) ((a_fMode) >= BS3_MODE_PP16)
+
+;; Whether the mode is running v8086 code.
+%define BS3_MODE_IS_V86(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86)
+;; Whether the we're executing in real mode or v8086 mode.
+%define BS3_MODE_IS_RM_OR_V86(a_fMode) ((a_fMode) == BS3_MODE_RM || BS3_MODE_IS_V86(a_fMode))
+;; Whether the mode is running 16-bit code, except v8086.
+%define BS3_MODE_IS_16BIT_CODE_NO_V86(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_16)
+;; Whether the mode is running 16-bit code (includes v8086).
+%define BS3_MODE_IS_16BIT_CODE(a_fMode) (BS3_MODE_IS_16BIT_CODE_NO_V86(a_fMode) || BS3_MODE_IS_V86(a_fMode))
+;; Whether the mode is running 32-bit code.
+%define BS3_MODE_IS_32BIT_CODE(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_32)
+;; Whether the mode is running 64-bit code.
+%define BS3_MODE_IS_64BIT_CODE(a_fMode) (((a_fMode) & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_64)
+
+;; Whether the system is in real mode.
+%define BS3_MODE_IS_RM_SYS(a_fMode) (((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_RM)
+;; Whether the system is some 16-bit mode that isn't real mode.
+%define BS3_MODE_IS_16BIT_SYS_NO_RM(a_fMode) ( ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE16 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP16 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE16)
+;; Whether the system is some 16-bit mode (includes real mode).
+%define BS3_MODE_IS_16BIT_SYS(a_fMode) (BS3_MODE_IS_16BIT_SYS_NO_RM(a_fMode) || BS3_MODE_IS_RM_SYS(a_fMode))
+;; Whether the system is some 32-bit mode.
+%define BS3_MODE_IS_32BIT_SYS(a_fMode) ( ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PE32 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PP32 \
+ || ((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_PAE32)
+;; Whether the system is long mode.
+%define BS3_MODE_IS_64BIT_SYS(a_fMode) (((a_fMode) & BS3_MODE_SYS_MASK) == BS3_MODE_SYS_LM)
+
+;; @}
+
+;; @name For mode specfic lookups:
+;; %[BS3_MODE_NM %+ BS3_MODE_PE32](SomeBaseName)
+;; %[BS3_MODE_LNAME_ %+ TMPL_MODE]
+;; @{
+%define BS3_MODE_NM_001h(a_Name) _ %+ a_Name %+ _rm
+%define BS3_MODE_NM_011h(a_Name) _ %+ a_Name %+ _pe16
+%define BS3_MODE_NM_012h(a_Name) _ %+ a_Name %+ _pe16_32
+%define BS3_MODE_NM_018h(a_Name) _ %+ a_Name %+ _pe16_v86
+%define BS3_MODE_NM_022h(a_Name) _ %+ a_Name %+ _pe32
+%define BS3_MODE_NM_021h(a_Name) _ %+ a_Name %+ _pe32_16
+%define BS3_MODE_NM_028h(a_Name) _ %+ a_Name %+ _pev86
+%define BS3_MODE_NM_031h(a_Name) _ %+ a_Name %+ _pp16
+%define BS3_MODE_NM_032h(a_Name) _ %+ a_Name %+ _pp16_32
+%define BS3_MODE_NM_038h(a_Name) _ %+ a_Name %+ _pp16_v86
+%define BS3_MODE_NM_042h(a_Name) _ %+ a_Name %+ _pp32
+%define BS3_MODE_NM_041h(a_Name) _ %+ a_Name %+ _pp32_16
+%define BS3_MODE_NM_048h(a_Name) _ %+ a_Name %+ _ppv86
+%define BS3_MODE_NM_051h(a_Name) _ %+ a_Name %+ _pae16
+%define BS3_MODE_NM_052h(a_Name) _ %+ a_Name %+ _pae16_32
+%define BS3_MODE_NM_058h(a_Name) _ %+ a_Name %+ _pae16_v86
+%define BS3_MODE_NM_062h(a_Name) _ %+ a_Name %+ _pae32
+%define BS3_MODE_NM_061h(a_Name) _ %+ a_Name %+ _pae32_16
+%define BS3_MODE_NM_068h(a_Name) _ %+ a_Name %+ _paev86
+%define BS3_MODE_NM_071h(a_Name) _ %+ a_Name %+ _lm16
+%define BS3_MODE_NM_072h(a_Name) _ %+ a_Name %+ _lm32
+%define BS3_MODE_NM_074h(a_Name) _ %+ a_Name %+ _lm64
+
+%define BS3_MODE_LNAME_001h rm
+%define BS3_MODE_LNAME_011h pe16
+%define BS3_MODE_LNAME_012h pe16_32
+%define BS3_MODE_LNAME_018h pe16_v86
+%define BS3_MODE_LNAME_022h pe32
+%define BS3_MODE_LNAME_021h pe32_16
+%define BS3_MODE_LNAME_028h pev86
+%define BS3_MODE_LNAME_031h pp16
+%define BS3_MODE_LNAME_032h pp16_32
+%define BS3_MODE_LNAME_038h pp16_v86
+%define BS3_MODE_LNAME_042h pp32
+%define BS3_MODE_LNAME_041h pp32_16
+%define BS3_MODE_LNAME_048h ppv86
+%define BS3_MODE_LNAME_051h pae16
+%define BS3_MODE_LNAME_052h pae16_32
+%define BS3_MODE_LNAME_058h pae16_v86
+%define BS3_MODE_LNAME_062h pae32
+%define BS3_MODE_LNAME_061h pae32_16
+%define BS3_MODE_LNAME_068h paev86
+%define BS3_MODE_LNAME_071h lm16
+%define BS3_MODE_LNAME_072h lm32
+%define BS3_MODE_LNAME_074h lm64
+
+%define BS3_MODE_UNAME_001h RM
+%define BS3_MODE_UNAME_011h PE16
+%define BS3_MODE_UNAME_012h PE16_32
+%define BS3_MODE_UNAME_018h PE16_V86
+%define BS3_MODE_UNAME_022h PE32
+%define BS3_MODE_UNAME_021h PE32_16
+%define BS3_MODE_UNAME_028h PEV86
+%define BS3_MODE_UNAME_031h PP16
+%define BS3_MODE_UNAME_032h PP16_32
+%define BS3_MODE_UNAME_038h PP16_V86
+%define BS3_MODE_UNAME_042h PP32
+%define BS3_MODE_UNAME_041h PP32_16
+%define BS3_MODE_UNAME_048h PPV86
+%define BS3_MODE_UNAME_051h PAE16
+%define BS3_MODE_UNAME_052h PAE16_32
+%define BS3_MODE_UNAME_058h PAE16_V86
+%define BS3_MODE_UNAME_062h PAE32
+%define BS3_MODE_UNAME_061h PAE32_16
+%define BS3_MODE_UNAME_068h PAEV86
+%define BS3_MODE_UNAME_071h LM16
+%define BS3_MODE_UNAME_072h LM32
+%define BS3_MODE_UNAME_074h LM64
+
+%define BS3_MODE_UNDERSCORE_001h _
+%define BS3_MODE_UNDERSCORE_011h _
+%define BS3_MODE_UNDERSCORE_012h _
+%define BS3_MODE_UNDERSCORE_018h _
+%define BS3_MODE_UNDERSCORE_022h _
+%define BS3_MODE_UNDERSCORE_021h _
+%define BS3_MODE_UNDERSCORE_028h _
+%define BS3_MODE_UNDERSCORE_031h _
+%define BS3_MODE_UNDERSCORE_032h _
+%define BS3_MODE_UNDERSCORE_038h _
+%define BS3_MODE_UNDERSCORE_042h _
+%define BS3_MODE_UNDERSCORE_041h _
+%define BS3_MODE_UNDERSCORE_048h _
+%define BS3_MODE_UNDERSCORE_051h _
+%define BS3_MODE_UNDERSCORE_052h _
+%define BS3_MODE_UNDERSCORE_058h _
+%define BS3_MODE_UNDERSCORE_062h _
+%define BS3_MODE_UNDERSCORE_061h _
+%define BS3_MODE_UNDERSCORE_068h _
+%define BS3_MODE_UNDERSCORE_071h _
+%define BS3_MODE_UNDERSCORE_072h _
+%define BS3_MODE_UNDERSCORE_074h _
+
+%define BS3_MODE_CNAME_001h c16
+%define BS3_MODE_CNAME_011h c16
+%define BS3_MODE_CNAME_012h c32
+%define BS3_MODE_CNAME_018h c16
+%define BS3_MODE_CNAME_022h c32
+%define BS3_MODE_CNAME_021h c16
+%define BS3_MODE_CNAME_028h c16
+%define BS3_MODE_CNAME_031h c16
+%define BS3_MODE_CNAME_032h c32
+%define BS3_MODE_CNAME_038h c16
+%define BS3_MODE_CNAME_042h c32
+%define BS3_MODE_CNAME_041h c16
+%define BS3_MODE_CNAME_048h c16
+%define BS3_MODE_CNAME_051h c16
+%define BS3_MODE_CNAME_052h c32
+%define BS3_MODE_CNAME_058h c16
+%define BS3_MODE_CNAME_062h c32
+%define BS3_MODE_CNAME_061h c16
+%define BS3_MODE_CNAME_068h c16
+%define BS3_MODE_CNAME_071h c16
+%define BS3_MODE_CNAME_072h c32
+%define BS3_MODE_CNAME_074h c64
+;; @}
+
+;; @name For getting the ring-0 mode for v86 modes: %[BS3_MODE_R0_NM_001h %+ TMPL_MODE](Bs3SwitchToRM)
+;; @{
+%define BS3_MODE_R0_NM_001h(a_Name) _ %+ a_Name %+ _rm
+%define BS3_MODE_R0_NM_011h(a_Name) _ %+ a_Name %+ _pe16
+%define BS3_MODE_R0_NM_012h(a_Name) _ %+ a_Name %+ _pe16_32
+%define BS3_MODE_R0_NM_018h(a_Name) _ %+ a_Name %+ _pe16
+%define BS3_MODE_R0_NM_022h(a_Name) _ %+ a_Name %+ _pe32
+%define BS3_MODE_R0_NM_021h(a_Name) _ %+ a_Name %+ _pe32_16
+%define BS3_MODE_R0_NM_028h(a_Name) _ %+ a_Name %+ _pe32_16
+%define BS3_MODE_R0_NM_031h(a_Name) _ %+ a_Name %+ _pp16
+%define BS3_MODE_R0_NM_032h(a_Name) _ %+ a_Name %+ _pp16_32
+%define BS3_MODE_R0_NM_038h(a_Name) _ %+ a_Name %+ _pp16
+%define BS3_MODE_R0_NM_042h(a_Name) _ %+ a_Name %+ _pp32
+%define BS3_MODE_R0_NM_041h(a_Name) _ %+ a_Name %+ _pp32_16
+%define BS3_MODE_R0_NM_048h(a_Name) _ %+ a_Name %+ _pp32_16
+%define BS3_MODE_R0_NM_051h(a_Name) _ %+ a_Name %+ _pae16
+%define BS3_MODE_R0_NM_052h(a_Name) _ %+ a_Name %+ _pae16_32
+%define BS3_MODE_R0_NM_058h(a_Name) _ %+ a_Name %+ _pae16
+%define BS3_MODE_R0_NM_062h(a_Name) _ %+ a_Name %+ _pae32
+%define BS3_MODE_R0_NM_061h(a_Name) _ %+ a_Name %+ _pae32_16
+%define BS3_MODE_R0_NM_068h(a_Name) _ %+ a_Name %+ _pae32_16
+%define BS3_MODE_R0_NM_071h(a_Name) _ %+ a_Name %+ _lm16
+%define BS3_MODE_R0_NM_072h(a_Name) _ %+ a_Name %+ _lm32
+%define BS3_MODE_R0_NM_074h(a_Name) _ %+ a_Name %+ _lm64
+;; @}
+
+
+;;
+; Includes the file %1 with TMPL_MODE set to all possible value.
+; @param 1 Double quoted include file name.
+%macro BS3_INSTANTIATE_TEMPLATE_WITH_WEIRD_ONES 1
+ %define BS3_INSTANTIATING_MODE
+ %define BS3_INSTANTIATING_ALL_MODES
+
+ %define TMPL_MODE BS3_MODE_RM
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PE16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PE16_32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PE16_V86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PE32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PE32_16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PEV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PP16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PP16_32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PP16_V86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PP32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PP32_16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PPV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PAE16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PAE16_32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PAE16_V86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PAE32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PAE32_16
+ %include %1
+ %define TMPL_MODE BS3_MODE_PAEV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_LM16
+ %include %1
+ %define TMPL_MODE BS3_MODE_LM32
+ %include %1
+ %define TMPL_MODE BS3_MODE_LM64
+ %include %1
+
+ %undef BS3_INSTANTIATING_MODE
+ %undef BS3_INSTANTIATING_ALL_MODES
+%endmacro
+
+
+;;
+; Includes the file %1 with TMPL_MODE set to all but the "weird" value.
+; @param 1 Double quoted include file name.
+%macro BS3_INSTANTIATE_TEMPLATE_ESSENTIALS 1
+ %define BS3_INSTANTIATING_MODE
+ %define BS3_INSTANTIATING_ESSENTIAL_MODES
+
+ %define TMPL_MODE BS3_MODE_RM
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PE16
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PE32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PEV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PP16
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PP32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PPV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PAE16
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_PAE32
+ %include %1
+ %define TMPL_MODE BS3_MODE_PAEV86
+ %include %1
+
+ %define TMPL_MODE BS3_MODE_LM16
+ %include %1
+ %define TMPL_MODE BS3_MODE_LM32
+ %include %1
+ %define TMPL_MODE BS3_MODE_LM64
+ %include %1
+
+ %undef BS3_INSTANTIATING_MODE
+ %undef BS3_INSTANTIATING_ESSENTIAL_MODES
+%endmacro
+
+;;
+; Includes the file %1 with TMPL_MODE set to a 16-bit, a 32-bit and a 64-bit value.
+; @param 1 Double quoted include file name.
+%macro BS3_INSTANTIATE_COMMON_TEMPLATE 1
+ %define BS3_INSTANTIATING_CMN
+
+ %define TMPL_MODE BS3_MODE_RM
+ %include %1
+ %define TMPL_MODE BS3_MODE_PE32
+ %include %1
+ %define TMPL_MODE BS3_MODE_LM64
+ %include %1
+
+ %undef BS3_INSTANTIATING_CMN
+%endmacro
+
+
+;; @name Static Memory Allocation
+; @{
+;; The flat load address for the code after the bootsector.
+%define BS3_ADDR_LOAD 010000h
+;; Where we save the boot registers during init.
+; Located right before the code.
+%define BS3_ADDR_REG_SAVE (BS3_ADDR_LOAD - BS3REGCTX_size - 8)
+;; Where the stack starts (initial RSP value).
+; Located 16 bytes (assumed by boot sector) before the saved registers. SS.BASE=0.
+%define BS3_ADDR_STACK (BS3_ADDR_REG_SAVE - 16)
+;; The ring-0 stack (8KB) for ring transitions.
+%define BS3_ADDR_STACK_R0 006000h
+;; The ring-1 stack (8KB) for ring transitions.
+%define BS3_ADDR_STACK_R1 004000h
+;; The ring-2 stack (8KB) for ring transitions.
+%define BS3_ADDR_STACK_R2 002000h
+;; IST1 ring-0 stack for long mode (4KB), used for double faults elsewhere.
+%define BS3_ADDR_STACK_R0_IST1 009000h
+;; IST2 ring-0 stack for long mode (3KB), used for spare 0 stack elsewhere.
+%define BS3_ADDR_STACK_R0_IST2 008000h
+;; IST3 ring-0 stack for long mode (1KB).
+%define BS3_ADDR_STACK_R0_IST3 007400h
+;; IST4 ring-0 stack for long mode (1KB), used for spare 1 stack elsewhere.
+%define BS3_ADDR_STACK_R0_IST4 007000h
+;; IST5 ring-0 stack for long mode (1KB).
+%define BS3_ADDR_STACK_R0_IST5 006c00h
+;; IST6 ring-0 stack for long mode (1KB).
+%define BS3_ADDR_STACK_R0_IST6 006800h
+;; IST7 ring-0 stack for long mode (1KB).
+%define BS3_ADDR_STACK_R0_IST7 006400h
+
+;; The base address of the BS3TEXT16 segment (same as BS3_LOAD_ADDR).
+;; @sa BS3_SEL_TEXT16
+%define BS3_ADDR_BS3TEXT16 010000h
+;; The base address of the BS3SYSTEM16 segment.
+;; @sa BS3_SEL_SYSTEM16
+%define BS3_ADDR_BS3SYSTEM16 020000h
+;; The base address of the BS3DATA16/BS3KIT_GRPNM_DATA16 segment.
+;; @sa BS3_SEL_DATA16
+%define BS3_ADDR_BS3DATA16 029000h
+;; @}
+
+
+;;
+; BS3 register context. Used by traps and such.
+;
+struc BS3REGCTX
+ .rax resq 1 ; BS3REG rax; /**< 0x00 */
+ .rcx resq 1 ; BS3REG rcx; /**< 0x08 */
+ .rdx resq 1 ; BS3REG rdx; /**< 0x10 */
+ .rbx resq 1 ; BS3REG rbx; /**< 0x18 */
+ .rsp resq 1 ; BS3REG rsp; /**< 0x20 */
+ .rbp resq 1 ; BS3REG rbp; /**< 0x28 */
+ .rsi resq 1 ; BS3REG rsi; /**< 0x30 */
+ .rdi resq 1 ; BS3REG rdi; /**< 0x38 */
+ .r8 resq 1 ; BS3REG r8; /**< 0x40 */
+ .r9 resq 1 ; BS3REG r9; /**< 0x48 */
+ .r10 resq 1 ; BS3REG r10; /**< 0x50 */
+ .r11 resq 1 ; BS3REG r11; /**< 0x58 */
+ .r12 resq 1 ; BS3REG r12; /**< 0x60 */
+ .r13 resq 1 ; BS3REG r13; /**< 0x68 */
+ .r14 resq 1 ; BS3REG r14; /**< 0x70 */
+ .r15 resq 1 ; BS3REG r15; /**< 0x78 */
+ .rflags resq 1 ; BS3REG rflags; /**< 0x80 */
+ .rip resq 1 ; BS3REG rip; /**< 0x88 */
+ .cs resw 1 ; uint16_t cs; /**< 0x90 */
+ .ds resw 1 ; uint16_t ds; /**< 0x92 */
+ .es resw 1 ; uint16_t es; /**< 0x94 */
+ .fs resw 1 ; uint16_t fs; /**< 0x96 */
+ .gs resw 1 ; uint16_t gs; /**< 0x98 */
+ .ss resw 1 ; uint16_t ss; /**< 0x9a */
+ .tr resw 1 ; uint16_t tr; /**< 0x9c */
+ .ldtr resw 1 ; uint16_t ldtr; /**< 0x9e */
+ .bMode resb 1 ; uint8_t bMode; /**< 0xa0: BS3_MODE_XXX. */
+ .bCpl resb 1 ; uint8_t bCpl; /**< 0xa1: 0-3, 0 is used for real mode. */
+ .fbFlags resb 1 ; uint8_t fbFlags; /**< 0xa2: BS3REG_CTX_F_XXX */
+ .abPadding resb 5 ; uint8_t abPadding[5]; /**< 0xa4 */
+ .cr0 resq 1 ; BS3REG cr0; /**< 0xa8 */
+ .cr2 resq 1 ; BS3REG cr2; /**< 0xb0 */
+ .cr3 resq 1 ; BS3REG cr3; /**< 0xb8 */
+ .cr4 resq 1 ; BS3REG cr4; /**< 0xc0 */
+ .uUnused resq 1 ; BS3REG uUnused; /**< 0xc8 */
+endstruc
+AssertCompileSize(BS3REGCTX, 0xd0)
+
+;; @name BS3REG_CTX_F_XXX - BS3REGCTX::fbFlags masks.
+; @{
+;; The CR0 is MSW (only low 16-bit). */
+%define BS3REG_CTX_F_NO_CR0_IS_MSW 0x01
+;; No CR2 and CR3 values. Not in CPL 0 or CPU too old for CR2 & CR3.
+%define BS3REG_CTX_F_NO_CR2_CR3 0x02
+;; No CR4 value. The CPU is too old for CR4.
+%define BS3REG_CTX_F_NO_CR4 0x04
+;; No TR and LDTR values. Context gathered in real mode or v8086 mode.
+%define BS3REG_CTX_F_NO_TR_LDTR 0x08
+;; The context doesn't have valid values for AMD64 GPR extensions.
+%define BS3REG_CTX_F_NO_AMD64 0x10
+;; @}
+
+
+;; @name Flags for Bs3RegCtxRestore
+; @{
+;; Skip restoring the CRx registers.
+%define BS3REGCTXRESTORE_F_SKIP_CRX 1
+;; Sets g_fBs3TrapNoV86Assist.
+%define BS3REGCTXRESTORE_F_NO_V86_ASSIST 2
+;; @}
+
+
+;;
+; BS3 extended register context (FPU, SSE, AVX, ++)
+;
+struc BS3EXTCTX
+ .u16Magic resw 1 ; uint16_t u16Magic;
+ .cb resw 1 ; uint16_t cb;
+ .enmMethod resb 1 ; uint8_t enmMethod;
+ alignb 8
+ .fXcr0Nominal resq 1 ; uint64_t fXcr0Nominal;
+ .fXcr0Saved resq 1 ; uint64_t fXcr0Saved;
+ alignb 64
+ .Ctx resb 512
+endstruc
+%define BS3EXTCTXMETHOD_ANCIENT 1
+%define BS3EXTCTXMETHOD_FXSAVE 2
+%define BS3EXTCTXMETHOD_XSAVE 3
+
+;;
+; BS3 Trap Frame.
+;
+struc BS3TRAPFRAME
+ .bXcpt resb 1
+ .cbIretFrame resb 1
+ .uHandlerCs resw 1
+ .uHandlerSs resw 1
+ .usAlignment resw 1
+ .uHandlerRsp resq 1
+ .fHandlerRfl resq 1
+ .uErrCd resq 1
+ .Ctx resb BS3REGCTX_size
+endstruc
+AssertCompileSize(BS3TRAPFRAME, 0x20 + 0xd0)
+
+;;
+; Trap record.
+;
+struc BS3TRAPREC
+ ;; The trap location relative to the base address given at
+ ; registration time.
+ .offWhere resd 1
+ ;; What to add to .offWhere to calculate the resume address.
+ .offResumeAddend resb 1
+ ;; The trap number.
+ .u8TrapNo resb 1
+ ;; The error code if the trap takes one.
+ .u16ErrCd resw 1
+endstruc
+
+;; The size shift.
+%define BS3TRAPREC_SIZE_SHIFT 3
+
+
+;; The system call vector.
+%define BS3_TRAP_SYSCALL 20h
+
+;; @name System call numbers (ax)
+;; @note Pointers are always passed in cx:xDI.
+;; @{
+;; Print char (cl).
+%define BS3_SYSCALL_PRINT_CHR 0001h
+;; Print string (pointer in cx:xDI, length in xDX).
+%define BS3_SYSCALL_PRINT_STR 0002h
+;; Switch to ring-0.
+%define BS3_SYSCALL_TO_RING0 0003h
+;; Switch to ring-1.
+%define BS3_SYSCALL_TO_RING1 0004h
+;; Switch to ring-2.
+%define BS3_SYSCALL_TO_RING2 0005h
+;; Switch to ring-3.
+%define BS3_SYSCALL_TO_RING3 0006h
+;; Restore context (pointer in cx:xDI, flags in dx).
+%define BS3_SYSCALL_RESTORE_CTX 0007h
+;; Set DRx register (value in ESI, register number in dl).
+%define BS3_SYSCALL_SET_DRX 0008h
+;; GET DRx register (register number in dl, value returned in ax:dx).
+%define BS3_SYSCALL_GET_DRX 0009h
+;; Set CRx register (value in ESI, register number in dl).
+%define BS3_SYSCALL_SET_CRX 000ah
+;; Get CRx register (register number in dl, value returned in ax:dx).
+%define BS3_SYSCALL_GET_CRX 000bh
+;; Set the task register (value in dx). */
+%define BS3_SYSCALL_SET_TR 000ch
+;; Get the task register (value returned in ax).
+%define BS3_SYSCALL_GET_TR 000dh
+;; Set the LDT register (value in dx).
+%define BS3_SYSCALL_SET_LDTR 000eh
+;; Get the LDT register (value returned in ax).
+%define BS3_SYSCALL_GET_LDTR 000fh
+;; Set XCR0 register (value in edx:esi).
+%define BS3_SYSCALL_SET_XCR0 0010h
+;; Get XCR0 register (value returned in edx:eax).
+%define BS3_SYSCALL_GET_XCR0 0011h
+;; The last system call value.
+%define BS3_SYSCALL_LAST BS3_SYSCALL_GET_XCR0
+;; @}
+
+
+
+;; @name BS3_SEL_XXX - GDT selectors
+;; @{
+
+%define BS3_SEL_LDT 0010h ;;< The LDT selector (requires setting up).
+%define BS3_SEL_TSS16 0020h ;;< The 16-bit TSS selector.
+%define BS3_SEL_TSS16_DF 0028h ;;< The 16-bit TSS selector for double faults.
+%define BS3_SEL_TSS16_SPARE0 0030h ;;< The 16-bit TSS selector for testing.
+%define BS3_SEL_TSS16_SPARE1 0038h ;;< The 16-bit TSS selector for testing.
+%define BS3_SEL_TSS32 0040h ;;< The 32-bit TSS selector.
+%define BS3_SEL_TSS32_DF 0048h ;;< The 32-bit TSS selector for double faults.
+%define BS3_SEL_TSS32_SPARE0 0050h ;;< The 32-bit TSS selector for testing.
+%define BS3_SEL_TSS32_SPARE1 0058h ;;< The 32-bit TSS selector for testing.
+%define BS3_SEL_TSS32_IOBP_IRB 0060h ;;< The 32-bit TSS selector with I/O permission and interrupt redirection bitmaps.
+%define BS3_SEL_TSS32_IRB 0068h ;;< The 32-bit TSS selector with only interrupt redirection bitmap (IOPB stripped by limit).
+%define BS3_SEL_TSS64 0070h ;;< The 64-bit TSS selector.
+%define BS3_SEL_TSS64_SPARE0 0080h ;;< The 64-bit TSS selector.
+%define BS3_SEL_TSS64_SPARE1 0090h ;;< The 64-bit TSS selector.
+%define BS3_SEL_TSS64_IOBP 00a0h ;;< The 64-bit TSS selector.
+
+%define BS3_SEL_RMTEXT16_CS 00e0h ;;< Conforming code selector for accessing the BS3RMTEXT16 segment. Runtime config.
+%define BS3_SEL_X0TEXT16_CS 00e8h ;;< Conforming code selector for accessing the BS3X0TEXT16 segment. Runtime config.
+%define BS3_SEL_X1TEXT16_CS 00f0h ;;< Conforming code selector for accessing the BS3X1TEXT16 segment. Runtime config.
+%define BS3_SEL_VMMDEV_MMIO16 00f8h ;;< Selector for accessing the VMMDev MMIO segment at 0100000h from 16-bit code.
+
+%define BS3_SEL_RING_SHIFT 8 ;;< For the formula: BS3_SEL_R0_XXX + ((cs & 3) << BS3_SEL_RING_SHIFT)
+
+%define BS3_SEL_R0_FIRST 0100h ;;< The first selector in the ring-0 block.
+%define BS3_SEL_R0_CS16 0100h ;;< ring-0: 16-bit code selector, base 0x10000.
+%define BS3_SEL_R0_DS16 0108h ;;< ring-0: 16-bit data selector, base 0x23000.
+%define BS3_SEL_R0_SS16 0110h ;;< ring-0: 16-bit stack selector, base 0x00000.
+%define BS3_SEL_R0_CS32 0118h ;;< ring-0: 32-bit flat code selector.
+%define BS3_SEL_R0_DS32 0120h ;;< ring-0: 32-bit flat data selector.
+%define BS3_SEL_R0_SS32 0128h ;;< ring-0: 32-bit flat stack selector.
+%define BS3_SEL_R0_CS64 0130h ;;< ring-0: 64-bit flat code selector.
+%define BS3_SEL_R0_DS64 0138h ;;< ring-0: 64-bit flat data & stack selector.
+%define BS3_SEL_R0_CS16_EO 0140h ;;< ring-0: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R0_CS16_CNF 0148h ;;< ring-0: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R0_CS16_CNF_EO 0150h ;;< ring-0: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R0_CS32_EO 0158h ;;< ring-0: 32-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R0_CS32_CNF 0160h ;;< ring-0: 32-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R0_CS32_CNF_EO 0168h ;;< ring-0: 32-bit execute-only conforming code selector, not accessed, flat.
+%define BS3_SEL_R0_CS64_EO 0170h ;;< ring-0: 64-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R0_CS64_CNF 0178h ;;< ring-0: 64-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R0_CS64_CNF_EO 0180h ;;< ring-0: 64-bit execute-only conforming code selector, not accessed, flat.
+
+%define BS3_SEL_R1_FIRST 0200h ;;< The first selector in the ring-1 block.
+%define BS3_SEL_R1_CS16 0200h ;;< ring-1: 16-bit code selector, base 0x10000.
+%define BS3_SEL_R1_DS16 0208h ;;< ring-1: 16-bit data selector, base 0x23000.
+%define BS3_SEL_R1_SS16 0210h ;;< ring-1: 16-bit stack selector, base 0x00000.
+%define BS3_SEL_R1_CS32 0218h ;;< ring-1: 32-bit flat code selector.
+%define BS3_SEL_R1_DS32 0220h ;;< ring-1: 32-bit flat data selector.
+%define BS3_SEL_R1_SS32 0228h ;;< ring-1: 32-bit flat stack selector.
+%define BS3_SEL_R1_CS64 0230h ;;< ring-1: 64-bit flat code selector.
+%define BS3_SEL_R1_DS64 0238h ;;< ring-1: 64-bit flat data & stack selector.
+%define BS3_SEL_R1_CS16_EO 0240h ;;< ring-1: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R1_CS16_CNF 0248h ;;< ring-1: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R1_CS16_CNF_EO 0250h ;;< ring-1: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R1_CS32_EO 0258h ;;< ring-1: 32-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R1_CS32_CNF 0260h ;;< ring-1: 32-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R1_CS32_CNF_EO 0268h ;;< ring-1: 32-bit execute-only conforming code selector, not accessed, flat.
+%define BS3_SEL_R1_CS64_EO 0270h ;;< ring-1: 64-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R1_CS64_CNF 0278h ;;< ring-1: 64-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R1_CS64_CNF_EO 0280h ;;< ring-1: 64-bit execute-only conforming code selector, not accessed, flat.
+
+%define BS3_SEL_R2_FIRST 0300h ;;< The first selector in the ring-2 block.
+%define BS3_SEL_R2_CS16 0300h ;;< ring-2: 16-bit code selector, base 0x10000.
+%define BS3_SEL_R2_DS16 0308h ;;< ring-2: 16-bit data selector, base 0x23000.
+%define BS3_SEL_R2_SS16 0310h ;;< ring-2: 16-bit stack selector, base 0x00000.
+%define BS3_SEL_R2_CS32 0318h ;;< ring-2: 32-bit flat code selector.
+%define BS3_SEL_R2_DS32 0320h ;;< ring-2: 32-bit flat data selector.
+%define BS3_SEL_R2_SS32 0328h ;;< ring-2: 32-bit flat stack selector.
+%define BS3_SEL_R2_CS64 0330h ;;< ring-2: 64-bit flat code selector.
+%define BS3_SEL_R2_DS64 0338h ;;< ring-2: 64-bit flat data & stack selector.
+%define BS3_SEL_R2_CS16_EO 0340h ;;< ring-2: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R2_CS16_CNF 0348h ;;< ring-2: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R2_CS16_CNF_EO 0350h ;;< ring-2: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R2_CS32_EO 0358h ;;< ring-2: 32-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R2_CS32_CNF 0360h ;;< ring-2: 32-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R2_CS32_CNF_EO 0368h ;;< ring-2: 32-bit execute-only conforming code selector, not accessed, flat.
+%define BS3_SEL_R2_CS64_EO 0370h ;;< ring-2: 64-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R2_CS64_CNF 0378h ;;< ring-2: 64-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R2_CS64_CNF_EO 0380h ;;< ring-2: 64-bit execute-only conforming code selector, not accessed, flat.
+
+%define BS3_SEL_R3_FIRST 0400h ;;< The first selector in the ring-3 block.
+%define BS3_SEL_R3_CS16 0400h ;;< ring-3: 16-bit code selector, base 0x10000.
+%define BS3_SEL_R3_DS16 0408h ;;< ring-3: 16-bit data selector, base 0x23000.
+%define BS3_SEL_R3_SS16 0410h ;;< ring-3: 16-bit stack selector, base 0x00000.
+%define BS3_SEL_R3_CS32 0418h ;;< ring-3: 32-bit flat code selector.
+%define BS3_SEL_R3_DS32 0420h ;;< ring-3: 32-bit flat data selector.
+%define BS3_SEL_R3_SS32 0428h ;;< ring-3: 32-bit flat stack selector.
+%define BS3_SEL_R3_CS64 0430h ;;< ring-3: 64-bit flat code selector.
+%define BS3_SEL_R3_DS64 0438h ;;< ring-3: 64-bit flat data & stack selector.
+%define BS3_SEL_R3_CS16_EO 0440h ;;< ring-3: 16-bit execute-only code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R3_CS16_CNF 0448h ;;< ring-3: 16-bit conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R3_CS16_CNF_EO 0450h ;;< ring-3: 16-bit execute-only conforming code selector, not accessed, 0xfffe limit, CS16 base.
+%define BS3_SEL_R3_CS32_EO 0458h ;;< ring-3: 32-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R3_CS32_CNF 0460h ;;< ring-3: 32-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R3_CS32_CNF_EO 0468h ;;< ring-3: 32-bit execute-only conforming code selector, not accessed, flat.
+%define BS3_SEL_R3_CS64_EO 0470h ;;< ring-3: 64-bit execute-only code selector, not accessed, flat.
+%define BS3_SEL_R3_CS64_CNF 0478h ;;< ring-3: 64-bit conforming code selector, not accessed, flat.
+%define BS3_SEL_R3_CS64_CNF_EO 0480h ;;< ring-3: 64-bit execute-only conforming code selector, not accessed, flat.
+
+%define BS3_SEL_SPARE_FIRST 0500h ;;< The first selector in the spare block
+%define BS3_SEL_SPARE_00 0500h ;;< Spare selector number 00h.
+%define BS3_SEL_SPARE_01 0508h ;;< Spare selector number 01h.
+%define BS3_SEL_SPARE_02 0510h ;;< Spare selector number 02h.
+%define BS3_SEL_SPARE_03 0518h ;;< Spare selector number 03h.
+%define BS3_SEL_SPARE_04 0520h ;;< Spare selector number 04h.
+%define BS3_SEL_SPARE_05 0528h ;;< Spare selector number 05h.
+%define BS3_SEL_SPARE_06 0530h ;;< Spare selector number 06h.
+%define BS3_SEL_SPARE_07 0538h ;;< Spare selector number 07h.
+%define BS3_SEL_SPARE_08 0540h ;;< Spare selector number 08h.
+%define BS3_SEL_SPARE_09 0548h ;;< Spare selector number 09h.
+%define BS3_SEL_SPARE_0a 0550h ;;< Spare selector number 0ah.
+%define BS3_SEL_SPARE_0b 0558h ;;< Spare selector number 0bh.
+%define BS3_SEL_SPARE_0c 0560h ;;< Spare selector number 0ch.
+%define BS3_SEL_SPARE_0d 0568h ;;< Spare selector number 0dh.
+%define BS3_SEL_SPARE_0e 0570h ;;< Spare selector number 0eh.
+%define BS3_SEL_SPARE_0f 0578h ;;< Spare selector number 0fh.
+%define BS3_SEL_SPARE_10 0580h ;;< Spare selector number 10h.
+%define BS3_SEL_SPARE_11 0588h ;;< Spare selector number 11h.
+%define BS3_SEL_SPARE_12 0590h ;;< Spare selector number 12h.
+%define BS3_SEL_SPARE_13 0598h ;;< Spare selector number 13h.
+%define BS3_SEL_SPARE_14 05a0h ;;< Spare selector number 14h.
+%define BS3_SEL_SPARE_15 05a8h ;;< Spare selector number 15h.
+%define BS3_SEL_SPARE_16 05b0h ;;< Spare selector number 16h.
+%define BS3_SEL_SPARE_17 05b8h ;;< Spare selector number 17h.
+%define BS3_SEL_SPARE_18 05c0h ;;< Spare selector number 18h.
+%define BS3_SEL_SPARE_19 05c8h ;;< Spare selector number 19h.
+%define BS3_SEL_SPARE_1a 05d0h ;;< Spare selector number 1ah.
+%define BS3_SEL_SPARE_1b 05d8h ;;< Spare selector number 1bh.
+%define BS3_SEL_SPARE_1c 05e0h ;;< Spare selector number 1ch.
+%define BS3_SEL_SPARE_1d 05e8h ;;< Spare selector number 1dh.
+%define BS3_SEL_SPARE_1e 05f0h ;;< Spare selector number 1eh.
+%define BS3_SEL_SPARE_1f 05f8h ;;< Spare selector number 1fh.
+
+%define BS3_SEL_TILED 0600h ;;< 16-bit data tiling: First - base=0x00000000, limit=64KB, DPL=3.
+%define BS3_SEL_TILED_LAST 0df8h ;;< 16-bit data tiling: Last - base=0x00ff0000, limit=64KB, DPL=3.
+%define BS3_SEL_TILED_AREA_SIZE 001000000h ;;< 16-bit data tiling: Size of addressable area, in bytes. (16 MB)
+
+%define BS3_SEL_FREE_PART1 0e00h ;;< Free selector space - part \%1.
+%define BS3_SEL_FREE_PART1_LAST 0ff8h ;;< Free selector space - part \%1, last entry.
+
+%define BS3_SEL_TEXT16 1000h ;;< The BS3TEXT16 selector.
+
+%define BS3_SEL_FREE_PART2 1008h ;;< Free selector space - part \#2.
+%define BS3_SEL_FREE_PART2_LAST 17f8h ;;< Free selector space - part \#2, last entry.
+
+%define BS3_SEL_TILED_R0 1800h ;;< 16-bit data/stack tiling: First - base=0x00000000, limit=64KB, DPL=0.
+%define BS3_SEL_TILED_R0_LAST 1ff8h ;;< 16-bit data/stack tiling: Last - base=0x00ff0000, limit=64KB, DPL=0.
+
+%define BS3_SEL_SYSTEM16 2000h ;;< The BS3SYSTEM16 selector.
+
+%define BS3_SEL_FREE_PART3 2008h ;;< Free selector space - part \%3.
+%define BS3_SEL_FREE_PART3_LAST 28f8h ;;< Free selector space - part \%3, last entry.
+
+%define BS3_SEL_DATA16 2900h ;;< The BS3DATA16/BS3KIT_GRPNM_DATA16 selector.
+
+%define BS3_SEL_FREE_PART4 2908h ;;< Free selector space - part \#4.
+%define BS3_SEL_FREE_PART4_LAST 2f98h ;;< Free selector space - part \#4, last entry.
+
+%define BS3_SEL_PRE_TEST_PAGE_08 2fa0h ;;< Selector located 8 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_07 2fa8h ;;< Selector located 7 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_06 2fb0h ;;< Selector located 6 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_05 2fb8h ;;< Selector located 5 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_04 2fc0h ;;< Selector located 4 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_03 2fc8h ;;< Selector located 3 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_02 2fd0h ;;< Selector located 2 selectors before the test page.
+%define BS3_SEL_PRE_TEST_PAGE_01 2fd8h ;;< Selector located 1 selector before the test page.
+%define BS3_SEL_TEST_PAGE 2fe0h ;;< Start of the test page intended for playing around with paging and GDT.
+%define BS3_SEL_TEST_PAGE_00 2fe0h ;;< Test page selector number 00h (convenience).
+%define BS3_SEL_TEST_PAGE_01 2fe8h ;;< Test page selector number 01h (convenience).
+%define BS3_SEL_TEST_PAGE_02 2ff0h ;;< Test page selector number 02h (convenience).
+%define BS3_SEL_TEST_PAGE_03 2ff8h ;;< Test page selector number 03h (convenience).
+%define BS3_SEL_TEST_PAGE_04 3000h ;;< Test page selector number 04h (convenience).
+%define BS3_SEL_TEST_PAGE_05 3008h ;;< Test page selector number 05h (convenience).
+%define BS3_SEL_TEST_PAGE_06 3010h ;;< Test page selector number 06h (convenience).
+%define BS3_SEL_TEST_PAGE_07 3018h ;;< Test page selector number 07h (convenience).
+%define BS3_SEL_TEST_PAGE_LAST 3fd0h ;;< The last selector in the spare page.
+
+%define BS3_SEL_GDT_LIMIT 3fd8h ;;< The GDT limit.
+
+;; @}
+
+
+;
+; Sanity checks.
+;
+%if BS3_ADDR_BS3TEXT16 != BS3_ADDR_LOAD
+ %error "BS3_ADDR_BS3TEXT16 and BS3_ADDR_LOAD are out of sync"
+%endif
+%if (BS3_ADDR_BS3TEXT16 / 16) != BS3_SEL_TEXT16
+ %error "BS3_ADDR_BS3TEXT16 and BS3_SEL_TEXT16 are out of sync"
+%endif
+%if (BS3_ADDR_BS3DATA16 / 16) != BS3_SEL_DATA16
+ %error "BS3_ADDR_BS3DATA16 and BS3_SEL_DATA16 are out of sync"
+%endif
+%if (BS3_ADDR_BS3SYSTEM16 / 16) != BS3_SEL_SYSTEM16
+ %error "BS3_ADDR_BS3SYSTEM16 and BS3_SEL_SYSTEM16 are out of sync"
+%endif
+
+
+;; @name BS3CPU_XXX - Bs3CpuDetect_mmm return value and g_bBs3CpuDetected.
+;; @{
+%define BS3CPU_8086 0x0001
+%define BS3CPU_V20 0x0002
+%define BS3CPU_80186 0x0003
+%define BS3CPU_80286 0x0004
+%define BS3CPU_80386 0x0005
+%define BS3CPU_80486 0x0006
+%define BS3CPU_Pentium 0x0007
+%define BS3CPU_PPro 0x0008
+%define BS3CPU_PProOrNewer 0x0009
+%define BS3CPU_TYPE_MASK 0x00ff
+%define BS3CPU_F_CPUID 0x0100
+%define BS3CPU_F_CPUID_EXT_LEAVES 0x0200
+%define BS3CPU_F_PAE 0x0400
+%define BS3CPU_F_PAE_BIT 10
+%define BS3CPU_F_PSE 0x0800
+%define BS3CPU_F_PSE_BIT 11
+%define BS3CPU_F_LONG_MODE 0x1000
+%define BS3CPU_F_LONG_MODE_BIT 12
+%define BS3CPU_F_NX 0x2000
+%define BS3CPU_F_NX_BIT 13
+;; @}
+
+%endif
+
diff --git a/src/VBox/ValidationKit/bootsectors/todo.txt b/src/VBox/ValidationKit/bootsectors/todo.txt
new file mode 100644
index 00000000..0d8d407d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/todo.txt
@@ -0,0 +1,10 @@
+$Id: todo.txt $
+
+Tripple fault variations:
+ - VT-x + NP: #PF w/ bad 32-bit IDT (set u1DescType=1). Injection causes #GP(73) loop. (r63775 cpu-pf-1)
+ - HWACCM (?): Enable PAE with bad PDPE for the next instr/jmp. Loops in at least one setup.
+
+Special General Protection Faults:
+ - Bad IDT entry. For instance X86DESCGATE::u1DescType = 1 (!system) of the #PF
+ entry and trigger a page fault. VT-x then tries to raise #GP(0x73).
+
diff --git a/src/VBox/ValidationKit/common/Makefile.kmk b/src/VBox/ValidationKit/common/Makefile.kmk
new file mode 100644
index 00000000..2abf7e48
--- /dev/null
+++ b/src/VBox/ValidationKit/common/Makefile.kmk
@@ -0,0 +1,77 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Common Python Code.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Install targets for the two zip files.
+#
+
+INSTALLS += testboxscript-common
+testboxscript-common_TEMPLATE = VBoxValidationKitR3
+testboxscript-common_INST = $(INST_TESTBOXSCRIPT)common/
+testboxscript-common_SOURCES = \
+ __init__.py \
+ utils.py \
+ netutils.py \
+ pathutils.py \
+ webutils.py \
+ constants/__init__.py=>constants/__init__.py \
+ constants/tbreq.py=>constants/tbreq.py \
+ constants/tbresp.py=>constants/tbresp.py \
+ constants/result.py=>constants/result.py \
+ constants/rtexitcode.py=>constants/rtexitcode.py \
+ constants/valueunit.py=>constants/valueunit.py
+
+INSTALLS += validationkit-common
+validationkit-common_TEMPLATE = VBoxValidationKitR3
+validationkit-common_INST = $(INST_VALIDATIONKIT)common/
+validationkit-common_SOURCES = $(testboxscript-common_SOURCES)
+
+
+#
+# Generate pylint and pychecker targets.
+#
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += \
+ $(wildcard \
+ $(PATH_SUB_CURRENT)/*.py \
+ $(PATH_SUB_CURRENT)/*/*.py \
+ )
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/common/__init__.py b/src/VBox/ValidationKit/common/__init__.py
new file mode 100755
index 00000000..fb19ad78
--- /dev/null
+++ b/src/VBox/ValidationKit/common/__init__.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Common code between testmanager, testbox and testdriver.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+from common import constants;
+from common import utils;
+from common import netutils;
+from common import pathutils;
+from common import webutils;
+
diff --git a/src/VBox/ValidationKit/common/constants/Makefile.kup b/src/VBox/ValidationKit/common/constants/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/Makefile.kup
diff --git a/src/VBox/ValidationKit/common/constants/__init__.py b/src/VBox/ValidationKit/common/constants/__init__.py
new file mode 100755
index 00000000..8d83001d
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/__init__.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Constants.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+from common.constants import tbreq;
+from common.constants import tbresp;
+from common.constants import result;
+from common.constants import rtexitcode;
+from common.constants import valueunit;
+
diff --git a/src/VBox/ValidationKit/common/constants/result.py b/src/VBox/ValidationKit/common/constants/result.py
new file mode 100644
index 00000000..d3f32e38
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/result.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# $Id: result.py $
+
+"""
+Test statuses.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+PASSED = 'PASSED';
+SKIPPED = 'SKIPPED';
+ABORTED = 'ABORTED';
+BAD_TESTBOX = 'BAD_TESTBOX';
+FAILED = 'FAILED';
+TIMED_OUT = 'TIMED_OUT';
+REBOOTED = 'REBOOTED';
+
+## List of valid result valies.
+g_kasValidResults = [ PASSED, SKIPPED, ABORTED, BAD_TESTBOX, FAILED, TIMED_OUT, REBOOTED, ];
diff --git a/src/VBox/ValidationKit/common/constants/rtexitcode.py b/src/VBox/ValidationKit/common/constants/rtexitcode.py
new file mode 100644
index 00000000..7c4023cf
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/rtexitcode.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+# $Id: rtexitcode.py $
+
+"""
+RTEXITCODE from iprt/types.h.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+## Success.
+RTEXITCODE_SUCCESS = 0;
+SUCCESS = RTEXITCODE_SUCCESS;
+## General failure.
+RTEXITCODE_FAILURE = 1;
+FAILURE = RTEXITCODE_FAILURE;
+## Invalid arguments.
+RTEXITCODE_SYNTAX = 2;
+SYNTAX = RTEXITCODE_SYNTAX;
+## Initialization failure.
+RTEXITCODE_INIT = 3;
+INIT = RTEXITCODE_INIT;
+## Test skipped.
+RTEXITCODE_SKIPPED = 4;
+SKIPPED = RTEXITCODE_SKIPPED;
+## Bad-testbox.
+RTEXITCODE_BAD_TESTBOX = 32;
+## Bad-testbox.
+BAD_TESTBOX = RTEXITCODE_BAD_TESTBOX;
+
diff --git a/src/VBox/ValidationKit/common/constants/tbreq.py b/src/VBox/ValidationKit/common/constants/tbreq.py
new file mode 100644
index 00000000..be066f09
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/tbreq.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+# $Id: tbreq.py $
+
+"""
+Test Manager Requests from the TestBox Script.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+## @name Test Manager actions
+# @{
+## TestBox sign-on.
+SIGNON = 'SIGNON'
+## TestBox request for a command while busy (EXEC).
+REQUEST_COMMAND_BUSY = 'REQUEST_COMMAND_BUSY'
+## TestBox request for a command while idling.
+REQUEST_COMMAND_IDLE = 'REQUEST_COMMAND_IDLE'
+## TestBox ACKs a command.
+COMMAND_ACK = 'COMMAND_ACK'
+## TestBox NACKs a command.
+COMMAND_NACK = 'COMMAND_NACK'
+## TestBox NACKs an unsupported command.
+COMMAND_NOTSUP = 'COMMAND_NOTSUP'
+## TestBox adds to the main log.
+LOG_MAIN = 'LOG_MAIN'
+## TestBox uploads a file to the current test result.
+UPLOAD = 'UPLOAD'
+## TestBox reports completion of an EXEC command.
+EXEC_COMPLETED = 'EXEC_COMPLETED'
+## Push more "XML" results to the server.
+XML_RESULTS = 'XML_RESULTS';
+## @}
+
+
+## @name Parameters for all actions.
+# @{
+ALL_PARAM_ACTION = 'ACTION'
+ALL_PARAM_TESTBOX_ID = 'TESTBOX_ID' ##< Not supplied by SIGNON.
+ALL_PARAM_TESTBOX_UUID = 'TESTBOX_UUID'
+## @}
+
+## @name SIGNON parameters.
+# @{
+SIGNON_PARAM_OS = 'OS';
+SIGNON_PARAM_OS_VERSION = 'OS_VERSION';
+SIGNON_PARAM_CPU_VENDOR = 'CPU_VENDOR';
+SIGNON_PARAM_CPU_ARCH = 'CPU_ARCH';
+SIGNON_PARAM_CPU_NAME = 'CPU_NAME';
+SIGNON_PARAM_CPU_REVISION = 'CPU_REVISION';
+SIGNON_PARAM_CPU_COUNT = 'CPU_COUNT';
+SIGNON_PARAM_HAS_HW_VIRT = 'HAS_HW_VIRT';
+SIGNON_PARAM_HAS_NESTED_PAGING = 'HAS_NESTED_PAGING';
+SIGNON_PARAM_HAS_64_BIT_GUEST = 'HAS_64_BIT_GUST';
+SIGNON_PARAM_HAS_IOMMU = 'HAS_IOMMU';
+SIGNON_PARAM_WITH_RAW_MODE = 'WITH_RAW_MODE';
+SIGNON_PARAM_MEM_SIZE = 'MEM_SIZE';
+SIGNON_PARAM_SCRATCH_SIZE = 'SCRATCH_SIZE';
+SIGNON_PARAM_REPORT = 'REPORT';
+SIGNON_PARAM_SCRIPT_REV = 'SCRIPT_REV';
+SIGNON_PARAM_PYTHON_VERSION = 'PYTHON_VERSION';
+## @}
+
+## @name Parameters for actions reporting results.
+# @{
+RESULT_PARAM_TEST_SET_ID = 'TEST_SET_ID'
+## @}
+
+## @name EXEC_COMPLETED parameters.
+# @{
+EXEC_COMPLETED_PARAM_RESULT = 'EXEC_RESULT'
+## @}
+
+## @name COMMAND_ACK, COMMAND_NACK and COMMAND_NOTSUP parameters.
+# @{
+## The name of the command that's being
+COMMAND_ACK_PARAM_CMD_NAME = 'CMD_NAME'
+## @}
+
+## @name LOG_MAIN parameters.
+## The log body.
+LOG_PARAM_BODY = 'LOG_BODY'
+## @}
+
+## @name UPLOAD_FILE parameters.
+## The file name.
+UPLOAD_PARAM_NAME = 'UPLOAD_NAME';
+## The MIME type of the file.
+UPLOAD_PARAM_MIME = 'UPLOAD_MIME';
+## The kind of file.
+UPLOAD_PARAM_KIND = 'UPLOAD_KIND';
+## The file description.
+UPLOAD_PARAM_DESC = 'UPLOAD_DESC';
+## @}
+
+## @name XML_RESULT parameters.
+## The "XML" body.
+XML_RESULT_PARAM_BODY = 'XML_BODY'
+## @}
+
diff --git a/src/VBox/ValidationKit/common/constants/tbresp.py b/src/VBox/ValidationKit/common/constants/tbresp.py
new file mode 100644
index 00000000..cfc101df
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/tbresp.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# $Id: tbresp.py $
+
+"""
+Test Manager Responses to the TestBox Script.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+## All test manager actions responses to the testbox include a RESULT field.
+ALL_PARAM_RESULT = 'RESULT'
+
+## @name Statuses (returned in ALL_PARAM_RESULT).
+## Acknowledgement.
+STATUS_ACK = 'ACK'
+## Negative acknowledgement.
+STATUS_NACK = 'NACK'
+## The testbox is dead, i.e. it no longer exists with the test manager.
+# @note Not used by the SIGNON action, but all the rest uses it.
+STATUS_DEAD = 'DEAD'
+## @}
+
+## @name Command names (returned in ALL_PARAM_RESULT).
+# @{
+CMD_IDLE = 'IDLE'
+CMD_WAIT = 'WAIT'
+CMD_EXEC = 'EXEC'
+CMD_ABORT = 'ABORT'
+CMD_REBOOT = 'REBOOT'
+CMD_UPGRADE = 'UPGRADE'
+CMD_UPGRADE_AND_REBOOT = 'UPGRADE_AND_REBOOT'
+CMD_SPECIAL = 'SPECIAL'
+## @ }
+
+## @name SIGNON parameter names.
+# @{
+## The TestBox ID.
+SIGNON_PARAM_ID = 'TESTBOX_ID'
+## The TestBox name.
+SIGNON_PARAM_NAME = 'TESTBOX_NAME'
+## @}
+
+
+## @name EXEC parameter names
+# @{
+## The test set id, used for reporting results.
+EXEC_PARAM_RESULT_ID = 'TEST_SET_ID'
+## The file to download/copy and unpack into TESTBOX_SCRIPT.
+EXEC_PARAM_SCRIPT_ZIPS = 'SCRIPT_ZIPS'
+## The testcase invocation command line (bourne shell style).
+EXEC_PARAM_SCRIPT_CMD_LINE = 'SCRIPT_CMD_LINE'
+## The testcase timeout in seconds.
+EXEC_PARAM_TIMEOUT = 'TIMEOUT'
+## @}
+
+## @name UPGRADE and @name UPGRADE_AND_REBOOT parameter names.
+# @{
+## A URL for downloading new version of Test Box Script archive
+UPGRADE_PARAM_URL = 'DOWNLOAD_URL'
+## @}
+
diff --git a/src/VBox/ValidationKit/common/constants/valueunit.py b/src/VBox/ValidationKit/common/constants/valueunit.py
new file mode 100644
index 00000000..11e83f78
--- /dev/null
+++ b/src/VBox/ValidationKit/common/constants/valueunit.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+# $Id: valueunit.py $
+
+"""
+Test Value Unit Definititions.
+
+This must correspond 1:1 with include/iprt/test.h and
+include/VBox/VMMDevTesting.h.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+
+## @name Unit constants.
+## Used everywhere.
+## @note Using upper case here so we can copy, past and chop from the other
+# headers.
+## @{
+PCT = 0x01;
+BYTES = 0x02;
+BYTES_PER_SEC = 0x03;
+KILOBYTES = 0x04;
+KILOBYTES_PER_SEC = 0x05;
+MEGABYTES = 0x06;
+MEGABYTES_PER_SEC = 0x07;
+PACKETS = 0x08;
+PACKETS_PER_SEC = 0x09;
+FRAMES = 0x0a;
+FRAMES_PER_SEC = 0x0b;
+OCCURRENCES = 0x0c;
+OCCURRENCES_PER_SEC = 0x0d;
+CALLS = 0x0e;
+CALLS_PER_SEC = 0x0f;
+ROUND_TRIP = 0x10;
+SECS = 0x11;
+MS = 0x12;
+NS = 0x13;
+NS_PER_CALL = 0x14;
+NS_PER_FRAME = 0x15;
+NS_PER_OCCURRENCE = 0x16;
+NS_PER_PACKET = 0x17;
+NS_PER_ROUND_TRIP = 0x18;
+INSTRS = 0x19;
+INSTRS_PER_SEC = 0x1a;
+NONE = 0x1b;
+PP1K = 0x1c;
+PP10K = 0x1d;
+PPM = 0x1e;
+PPB = 0x1f;
+TICKS = 0x20;
+TICKS_PER_CALL = 0x21;
+TICKS_PER_OCCURENCE = 0x22;
+PAGES = 0x23;
+PAGES_PER_SEC = 0x24;
+TICKS_PER_PAGE = 0x25;
+NS_PER_PAGE = 0x26;
+PS = 0x27;
+PS_PER_CALL = 0x28;
+PS_PER_FRAME = 0x29;
+PS_PER_OCCURRENCE = 0x2a;
+PS_PER_PACKET = 0x2b;
+PS_PER_ROUND_TRIP = 0x2c;
+PS_PER_PAGE = 0x2d;
+END = 0x2e;
+## @}
+
+
+## Translate constant to string.
+g_asNames = \
+[
+ 'invalid', # 0
+ '%',
+ 'bytes',
+ 'bytes/s',
+ 'KiB',
+ 'KiB/s',
+ 'MiB',
+ 'MiB/s',
+ 'packets',
+ 'packets/s',
+ 'frames',
+ 'frames/s',
+ 'occurrences',
+ 'occurrences/s',
+ 'calls',
+ 'calls/s',
+ 'roundtrips',
+ 's',
+ 'ms',
+ 'ns',
+ 'ns/call',
+ 'ns/frame',
+ 'ns/occurrences',
+ 'ns/packet',
+ 'ns/roundtrips',
+ 'ins',
+ 'ins/s',
+ '', # none
+ 'pp1k',
+ 'pp10k',
+ 'ppm',
+ 'ppb',
+ 'ticks',
+ 'ticks/call',
+ 'ticks/occ',
+ 'pages',
+ 'pages/s',
+ 'ticks/page',
+ 'ns/page',
+ 'ps',
+ 'ps/call',
+ 'ps/frame',
+ 'ps/occurrences',
+ 'ps/packet',
+ 'ps/roundtrips',
+ 'ps/page',
+];
+assert g_asNames[PP1K] == 'pp1k';
+assert g_asNames[NS_PER_PAGE] == 'ns/page';
+assert g_asNames[PS_PER_PAGE] == 'ps/page';
+
+
+## Translation table for XML -> number.
+g_kdNameToConst = \
+{
+ 'KB': KILOBYTES,
+ 'KB/s': KILOBYTES_PER_SEC,
+ 'MB': MEGABYTES,
+ 'MB/s': MEGABYTES_PER_SEC,
+ 'occurrences': OCCURRENCES,
+ 'occurrences/s': OCCURRENCES_PER_SEC,
+
+};
+for i in range(1, len(g_asNames)):
+ g_kdNameToConst[g_asNames[i]] = i;
+
diff --git a/src/VBox/ValidationKit/common/netutils.py b/src/VBox/ValidationKit/common/netutils.py
new file mode 100755
index 00000000..dc9cc8de
--- /dev/null
+++ b/src/VBox/ValidationKit/common/netutils.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# $Id: netutils.py $
+# pylint: disable=too-many-lines
+
+"""
+Common Network Utility Functions.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import socket;
+
+
+def getPrimaryHostIpByUdp(sPeerIp = '255.255.255.255'):
+ """
+ Worker for getPrimaryHostIp.
+
+ The method is opening a UDP socket targetting a random port on a
+ limited (local LAN) broadcast address. We then use getsockname() to
+ obtain our own IP address, which should then be the primary IP.
+
+ Unfortunately, this doesn't always work reliably on Solaris. When for
+ instance our host only is configured, which interface we end up on seems
+ to be totally random.
+ """
+
+ try: oSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);
+ except: oSocket = None;
+ if oSocket is not None:
+ try:
+ oSocket.connect((sPeerIp, 1984));
+ sHostIp = oSocket.getsockname()[0];
+ except:
+ sHostIp = None;
+ oSocket.close();
+ if sHostIp is not None:
+ return sHostIp;
+ return '127.0.0.1';
+
+
+def getPrimaryHostIpByHostname():
+ """
+ Worker for getPrimaryHostIp.
+
+ Attempts to resolve the hostname.
+ """
+ try:
+ return socket.gethostbyname(getHostnameFqdn());
+ except:
+ return '127.0.0.1';
+
+
+def getPrimaryHostIp():
+ """
+ Tries to figure out the primary (the one with default route), local
+ IPv4 address.
+
+ Returns the IP address on success and otherwise '127.0.0.1'.
+ """
+
+ #
+ # This isn't quite as easy as one would think. Doing a UDP connect to
+ # 255.255.255.255 turns out to be problematic on solaris with more than one
+ # network interface (IP is random selected it seems), as well as linux
+ # where we've seen 127.0.1.1 being returned on some hosts.
+ #
+ # So a modified algorithm first try a known public IP address, ASSUMING
+ # that the primary interface is the one that gets us onto the internet.
+ # If that fails, due to routing or whatever, we try 255.255.255.255 and
+ # then finally hostname resolution.
+ #
+ sHostIp = getPrimaryHostIpByUdp('8.8.8.8');
+ if sHostIp.startswith('127.'):
+ sHostIp = getPrimaryHostIpByUdp('255.255.255.255');
+ if sHostIp.startswith('127.'):
+ sHostIp = getPrimaryHostIpByHostname();
+ return sHostIp;
+
+
+def getHostnameFqdn():
+ """
+ Wrapper around getfqdn.
+
+ Returns the fully qualified hostname, None if not found.
+ """
+
+ try:
+ sHostname = socket.getfqdn();
+ except:
+ return None;
+
+ if '.' in sHostname or sHostname.startswith('localhost'):
+ return sHostname;
+
+ #
+ # Somewhat misconfigured system, needs expensive approach to guessing FQDN.
+ # Get address information on the hostname and do a reverse lookup from that.
+ #
+ try:
+ aAddressInfo = socket.getaddrinfo(sHostname, None);
+ except:
+ return sHostname;
+
+ for aAI in aAddressInfo:
+ try: sName, _ = socket.getnameinfo(aAI[4], 0);
+ except: continue;
+ if '.' in sName and not set(sName).issubset(set('0123456789.')) and not sName.startswith('localhost'):
+ return sName;
+
+ return sHostname;
+
diff --git a/src/VBox/ValidationKit/common/pathutils.py b/src/VBox/ValidationKit/common/pathutils.py
new file mode 100644
index 00000000..6787f1aa
--- /dev/null
+++ b/src/VBox/ValidationKit/common/pathutils.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# $Id: pathutils.py $
+# pylint: disable=too-many-lines
+
+"""
+Path Utility Functions.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import unittest;
+
+
+## @name Path data keyed by fDosStyle bool value.
+## @{
+g_dsPathSlash = { False: '/', True: '\\', };
+g_dasPathSlashes = { False: ('/',), True: ('\\', '/', ), };
+g_dasPathSeparators = { False: ('/',), True: ('\\', '/', ':', ), };
+## @}
+
+
+def joinEx(fDosStyle, sBase, *asAppend):
+ """
+ Mimicking os.path.join, but where target system isn't the host.
+ The code is very simple at present.
+ """
+ # Get the first non-None element and use it as base.
+ i = 0;
+ sRet = sBase;
+ while sRet is None and i < len(asAppend):
+ sRet = asAppend[i];
+ i += 1;
+
+ while i < len(asAppend):
+ sAppend = asAppend[i];
+
+ # Skip None elements.
+ if sAppend is not None:
+ # Strip leading slashes from sAppend:
+ offSkip = 0;
+ while offSkip < len(sAppend) and sAppend[offSkip] in g_dasPathSlashes[fDosStyle]:
+ offSkip += 1;
+
+ # Add separator if needed before appending the new bit:
+ if not sRet or sRet[-1] not in g_dasPathSeparators[fDosStyle]:
+ sRet += g_dsPathSlash[fDosStyle] + sAppend[offSkip:];
+ else:
+ sRet += sAppend[offSkip:];
+
+ i += 1;
+
+ return sRet;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring,undefined-variable
+class JoinExTestCase(unittest.TestCase):
+ def testJoinEx(self):
+ self.assertEqual(joinEx(True, None), None);
+ self.assertEqual(joinEx(False, None), None);
+ self.assertEqual(joinEx(True, ''), '');
+ self.assertEqual(joinEx(False, ''), '');
+ self.assertEqual(joinEx(True, '',''), '\\');
+ self.assertEqual(joinEx(False, '',''), '/');
+ self.assertEqual(joinEx(True, 'C:','dos'), 'C:dos');
+ self.assertEqual(joinEx(True, 'C:/','dos'), 'C:/dos');
+ self.assertEqual(joinEx(True, 'C:\\','dos'), 'C:\\dos');
+ self.assertEqual(joinEx(True, 'C:\\dos','edlin.com'), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(True, 'C:\\dos\\','edlin.com'), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(True, 'C:\\dos/','edlin.com'), 'C:\\dos/edlin.com');
+ self.assertEqual(joinEx(True, 'C:\\dos//','edlin.com'), 'C:\\dos//edlin.com');
+ self.assertEqual(joinEx(True, 'C:\\dos','\\/edlin.com'), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(True, 'C:\\dos', None, 'edlin.com'), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(True, None, 'C:\\dos', None, 'edlin.com'), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(True, None, None, 'C:\\dos', None, 'edlin.com', None), 'C:\\dos\\edlin.com');
+ self.assertEqual(joinEx(False, '/', 'bin', 'ls'), '/bin/ls');
+ self.assertEqual(joinEx(False, '/', '/bin', 'ls'), '/bin/ls');
+ self.assertEqual(joinEx(False, '/', '/bin/', 'ls'), '/bin/ls');
+ self.assertEqual(joinEx(False, '/', '/bin//', 'ls'), '/bin//ls');
+ self.assertEqual(joinEx(False, '/', None, 'bin', None, 'ls', None), '/bin/ls');
+ self.assertEqual(joinEx(False, None, '/', None, 'bin', None, 'ls', None), '/bin/ls');
+
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/common/utils.py b/src/VBox/ValidationKit/common/utils.py
new file mode 100755
index 00000000..e8a50604
--- /dev/null
+++ b/src/VBox/ValidationKit/common/utils.py
@@ -0,0 +1,2571 @@
+# -*- coding: utf-8 -*-
+# $Id: utils.py $
+# pylint: disable=too-many-lines
+
+"""
+Common Utility Functions.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import datetime;
+import errno;
+import os;
+import platform;
+import re;
+import stat;
+import subprocess;
+import sys;
+import time;
+import traceback;
+import unittest;
+
+if sys.platform == 'win32':
+ import ctypes;
+ import msvcrt; # pylint: disable=import-error
+ import win32api; # pylint: disable=import-error
+ import win32con; # pylint: disable=import-error
+ import win32console; # pylint: disable=import-error
+ import win32file; # pylint: disable=import-error
+ import win32process; # pylint: disable=import-error
+ import winerror; # pylint: disable=import-error
+ import pywintypes; # pylint: disable=import-error
+else:
+ import signal;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ unicode = str; # pylint: disable=redefined-builtin,invalid-name
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+#
+# Strings.
+#
+
+def toUnicode(sString, encoding = None, errors = 'strict'):
+ """
+ A little like the python 2 unicode() function.
+ """
+ if sys.version_info[0] >= 3:
+ if isinstance(sString, bytes):
+ return str(sString, encoding if encoding else 'utf-8', errors);
+ else:
+ if not isinstance(sString, unicode):
+ return unicode(sString, encoding if encoding else 'utf-8', errors);
+ return sString;
+
+
+
+#
+# Output.
+#
+
+def printOut(sString):
+ """
+ Outputs a string to standard output, dealing with python 2.x encoding stupidity.
+ """
+ sStreamEncoding = sys.stdout.encoding;
+ if sStreamEncoding is None: # Files, pipes and such on 2.x. (pylint is confused here)
+ sStreamEncoding = 'US-ASCII'; # pylint: disable=redefined-variable-type
+ if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
+ print(sString);
+ else:
+ print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding));
+
+def printErr(sString):
+ """
+ Outputs a string to standard error, dealing with python 2.x encoding stupidity.
+ """
+ sStreamEncoding = sys.stderr.encoding;
+ if sStreamEncoding is None: # Files, pipes and such on 2.x. (pylint is confused here)
+ sStreamEncoding = 'US-ASCII'; # pylint: disable=redefined-variable-type
+ if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
+ print(sString, file = sys.stderr);
+ else:
+ print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding), file = sys.stderr);
+
+
+#
+# Host OS and CPU.
+#
+
+def getHostOs():
+ """
+ Gets the host OS name (short).
+
+ See the KBUILD_OSES variable in kBuild/header.kmk for possible return values.
+ """
+ sPlatform = platform.system();
+ if sPlatform in ('Linux', 'Darwin', 'Solaris', 'FreeBSD', 'NetBSD', 'OpenBSD'):
+ sPlatform = sPlatform.lower();
+ elif sPlatform == 'Windows':
+ sPlatform = 'win';
+ elif sPlatform == 'SunOS':
+ sPlatform = 'solaris';
+ else:
+ raise Exception('Unsupported platform "%s"' % (sPlatform,));
+ return sPlatform;
+
+g_sHostArch = None;
+
+def getHostArch():
+ """
+ Gets the host CPU architecture.
+
+ See the KBUILD_ARCHES variable in kBuild/header.kmk for possible return values.
+ """
+ global g_sHostArch;
+ if g_sHostArch is None:
+ sArch = platform.machine();
+ if sArch in ('i386', 'i486', 'i586', 'i686', 'i786', 'i886', 'x86'):
+ sArch = 'x86';
+ elif sArch in ('AMD64', 'amd64', 'x86_64'):
+ sArch = 'amd64';
+ elif sArch == 'i86pc': # SunOS
+ if platform.architecture()[0] == '64bit':
+ sArch = 'amd64';
+ else:
+ try:
+ sArch = str(processOutputChecked(['/usr/bin/isainfo', '-n',]));
+ except:
+ pass;
+ sArch = sArch.strip();
+ if sArch != 'amd64':
+ sArch = 'x86';
+ else:
+ raise Exception('Unsupported architecture/machine "%s"' % (sArch,));
+ g_sHostArch = sArch;
+ return g_sHostArch;
+
+
+def getHostOsDotArch():
+ """
+ Gets the 'os.arch' for the host.
+ """
+ return '%s.%s' % (getHostOs(), getHostArch());
+
+
+def isValidOs(sOs):
+ """
+ Validates the OS name.
+ """
+ if sOs in ('darwin', 'dos', 'dragonfly', 'freebsd', 'haiku', 'l4', 'linux', 'netbsd', 'nt', 'openbsd', \
+ 'os2', 'solaris', 'win', 'os-agnostic'):
+ return True;
+ return False;
+
+
+def isValidArch(sArch):
+ """
+ Validates the CPU architecture name.
+ """
+ if sArch in ('x86', 'amd64', 'sparc32', 'sparc64', 's390', 's390x', 'ppc32', 'ppc64', \
+ 'mips32', 'mips64', 'ia64', 'hppa32', 'hppa64', 'arm', 'alpha'):
+ return True;
+ return False;
+
+def isValidOsDotArch(sOsDotArch):
+ """
+ Validates the 'os.arch' string.
+ """
+
+ asParts = sOsDotArch.split('.');
+ if asParts.length() != 2:
+ return False;
+ return isValidOs(asParts[0]) \
+ and isValidArch(asParts[1]);
+
+def getHostOsVersion():
+ """
+ Returns the host OS version. This is platform.release with additional
+ distro indicator on linux.
+ """
+ sVersion = platform.release();
+ sOs = getHostOs();
+ if sOs == 'linux':
+ sDist = '';
+ try:
+ # try /etc/lsb-release first to distinguish between Debian and Ubuntu
+ with open('/etc/lsb-release') as oFile: # pylint: disable=unspecified-encoding
+ for sLine in oFile:
+ oMatch = re.search(r'(?:DISTRIB_DESCRIPTION\s*=)\s*"*(.*)"', sLine);
+ if oMatch is not None:
+ sDist = oMatch.group(1).strip();
+ except:
+ pass;
+ if sDist:
+ sVersion += ' / ' + sDist;
+ else:
+ asFiles = \
+ [
+ [ '/etc/debian_version', 'Debian v'],
+ [ '/etc/gentoo-release', '' ],
+ [ '/etc/oracle-release', '' ],
+ [ '/etc/redhat-release', '' ],
+ [ '/etc/SuSE-release', '' ],
+ ];
+ for sFile, sPrefix in asFiles:
+ if os.path.isfile(sFile):
+ try:
+ with open(sFile) as oFile: # pylint: disable=unspecified-encoding
+ sLine = oFile.readline();
+ except:
+ continue;
+ sLine = sLine.strip()
+ if sLine:
+ sVersion += ' / ' + sPrefix + sLine;
+ break;
+
+ elif sOs == 'solaris':
+ sVersion = platform.version();
+ if os.path.isfile('/etc/release'):
+ try:
+ with open('/etc/release') as oFile: # pylint: disable=unspecified-encoding
+ sLast = oFile.readlines()[-1];
+ sLast = sLast.strip();
+ if sLast:
+ sVersion += ' (' + sLast + ')';
+ except:
+ pass;
+
+ elif sOs == 'darwin':
+ def getMacVersionName(sVersion):
+ """
+ Figures out the Mac OS X/macOS code name from the numeric version.
+ """
+ aOsVersion = sVersion.split('.') # example: ('10','15','7')
+ codenames = {"4": "Tiger",
+ "5": "Leopard",
+ "6": "Snow Leopard",
+ "7": "Lion",
+ "8": "Mountain Lion",
+ "9": "Mavericks",
+ "10": "Yosemite",
+ "11": "El Capitan",
+ "12": "Sierra",
+ "13": "High Sierra",
+ "14": "Mojave",
+ "15": "Catalina",
+ "16": "Wrong version",
+ }
+ codenames_afterCatalina = {"11": "Big Sur",
+ "12": "Monterey",
+ "13": "Ventura",
+ "14": "Unknown 14",
+ "15": "Unknown 15"}
+
+ if aOsVersion[0] == '10':
+ sResult = codenames[aOsVersion[1]]
+ else:
+ sResult = codenames_afterCatalina[aOsVersion[0]]
+ return sResult
+
+ sOsxVersion = platform.mac_ver()[0]
+ sVersion += ' / OS X ' + sOsxVersion + ' (' + getMacVersionName(sOsxVersion) + ')'
+
+ elif sOs == 'win':
+ class OSVersionInfoEx(ctypes.Structure):
+ """ OSVERSIONEX """
+ kaFields = [
+ ('dwOSVersionInfoSize', ctypes.c_ulong),
+ ('dwMajorVersion', ctypes.c_ulong),
+ ('dwMinorVersion', ctypes.c_ulong),
+ ('dwBuildNumber', ctypes.c_ulong),
+ ('dwPlatformId', ctypes.c_ulong),
+ ('szCSDVersion', ctypes.c_wchar*128),
+ ('wServicePackMajor', ctypes.c_ushort),
+ ('wServicePackMinor', ctypes.c_ushort),
+ ('wSuiteMask', ctypes.c_ushort),
+ ('wProductType', ctypes.c_byte),
+ ('wReserved', ctypes.c_byte)]
+ _fields_ = kaFields # pylint: disable=invalid-name
+
+ def __init__(self):
+ super(OSVersionInfoEx, self).__init__()
+ self.dwOSVersionInfoSize = ctypes.sizeof(self)
+
+ oOsVersion = OSVersionInfoEx()
+ rc = ctypes.windll.Ntdll.RtlGetVersion(ctypes.byref(oOsVersion))
+ if rc == 0:
+ # Python platform.release() is not reliable for newer server releases
+ if oOsVersion.wProductType != 1:
+ if oOsVersion.dwMajorVersion == 10 and oOsVersion.dwMinorVersion == 0:
+ sVersion = '2016Server';
+ elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 3:
+ sVersion = '2012ServerR2';
+ elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 2:
+ sVersion = '2012Server';
+ elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 1:
+ sVersion = '2008ServerR2';
+ elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 0:
+ sVersion = '2008Server';
+ elif oOsVersion.dwMajorVersion == 5 and oOsVersion.dwMinorVersion == 2:
+ sVersion = '2003Server';
+ sVersion += ' build ' + str(oOsVersion.dwBuildNumber)
+ if oOsVersion.wServicePackMajor:
+ sVersion += ' SP' + str(oOsVersion.wServicePackMajor)
+ if oOsVersion.wServicePackMinor:
+ sVersion += '.' + str(oOsVersion.wServicePackMinor)
+
+ return sVersion;
+
+def getPresentCpuCount():
+ """
+ Gets the number of CPUs present in the system.
+
+ This differs from multiprocessor.cpu_count() and os.cpu_count() on windows in
+ that we return the active count rather than the maximum count. If we don't,
+ we will end up thinking testboxmem1 has 512 CPU threads, which it doesn't and
+ never will have.
+
+ @todo This is probably not exactly what we get on non-windows...
+ """
+
+ if getHostOs() == 'win':
+ fnGetActiveProcessorCount = getattr(ctypes.windll.kernel32, 'GetActiveProcessorCount', None);
+ if fnGetActiveProcessorCount:
+ cCpus = fnGetActiveProcessorCount(ctypes.c_ushort(0xffff));
+ if cCpus > 0:
+ return cCpus;
+
+ import multiprocessing
+ return multiprocessing.cpu_count();
+
+
+#
+# File system.
+#
+
+def openNoInherit(sFile, sMode = 'r'):
+ """
+ Wrapper around open() that tries it's best to make sure the file isn't
+ inherited by child processes.
+
+ This is a best effort thing at the moment as it doesn't synchronizes with
+ child process spawning in any way. Thus it can be subject to races in
+ multithreaded programs.
+ """
+
+ # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
+ uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
+ if uPythonVer >= ((3 << 16) | 4):
+ oFile = open(sFile, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+ else:
+ try:
+ from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=import-error
+ except:
+ # On windows, we can use the 'N' flag introduced in Visual C++ 7.0 or 7.1 with python 2.x.
+ if getHostOs() == 'win':
+ if uPythonVer < (3 << 16):
+ offComma = sMode.find(',');
+ if offComma < 0:
+ return open(sFile, sMode + 'N'); # pylint: disable=consider-using-with,unspecified-encoding
+ return open(sFile, # pylint: disable=consider-using-with,unspecified-encoding,bad-open-mode
+ sMode[:offComma] + 'N' + sMode[offComma:]);
+
+ # Just in case.
+ return open(sFile, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+
+ oFile = open(sFile, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+ #try:
+ fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
+ #except:
+ # pass;
+ return oFile;
+
+def openNoDenyDeleteNoInherit(sFile, sMode = 'r'):
+ """
+ Wrapper around open() that tries it's best to make sure the file isn't
+ inherited by child processes.
+
+ This is a best effort thing at the moment as it doesn't synchronizes with
+ child process spawning in any way. Thus it can be subject to races in
+ multithreaded programs.
+ """
+
+ if getHostOs() == 'win':
+ # Need to use CreateFile directly to open the file so we can feed it FILE_SHARE_DELETE.
+ # pylint: disable=no-member,c-extension-no-member
+ fAccess = 0;
+ fDisposition = win32file.OPEN_EXISTING;
+ if 'r' in sMode or '+' in sMode:
+ fAccess |= win32file.GENERIC_READ;
+ if 'a' in sMode:
+ fAccess |= win32file.GENERIC_WRITE;
+ fDisposition = win32file.OPEN_ALWAYS;
+ elif 'w' in sMode:
+ fAccess = win32file.GENERIC_WRITE;
+ if '+' in sMode:
+ fDisposition = win32file.OPEN_ALWAYS;
+ fAccess |= win32file.GENERIC_READ;
+ else:
+ fDisposition = win32file.CREATE_ALWAYS;
+ if not fAccess:
+ fAccess |= win32file.GENERIC_READ;
+ fSharing = (win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE
+ | win32file.FILE_SHARE_DELETE);
+ hFile = win32file.CreateFile(sFile, fAccess, fSharing, None, fDisposition, 0, None);
+ if 'a' in sMode:
+ win32file.SetFilePointer(hFile, 0, win32file.FILE_END);
+
+ # Turn the NT handle into a CRT file descriptor.
+ hDetachedFile = hFile.Detach();
+ if fAccess == win32file.GENERIC_READ:
+ fOpen = os.O_RDONLY;
+ elif fAccess == win32file.GENERIC_WRITE:
+ fOpen = os.O_WRONLY;
+ else:
+ fOpen = os.O_RDWR;
+ # pulint: enable=no-member,c-extension-no-member
+ if 'a' in sMode:
+ fOpen |= os.O_APPEND;
+ if 'b' in sMode or 't' in sMode:
+ fOpen |= os.O_TEXT; # pylint: disable=no-member
+ fdFile = msvcrt.open_osfhandle(hDetachedFile, fOpen);
+
+ # Tell python to use this handle.
+ oFile = os.fdopen(fdFile, sMode);
+ else:
+ oFile = open(sFile, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+
+ # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
+ uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
+ if uPythonVer < ((3 << 16) | 4):
+ try:
+ from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=import-error
+ except:
+ pass;
+ else:
+ fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
+ return oFile;
+
+def noxcptReadLink(sPath, sXcptRet, sEncoding = 'utf-8'):
+ """
+ No exceptions os.readlink wrapper.
+ """
+ try:
+ sRet = os.readlink(sPath); # pylint: disable=no-member
+ except:
+ return sXcptRet;
+ if hasattr(sRet, 'decode'):
+ sRet = sRet.decode(sEncoding, 'ignore');
+ return sRet;
+
+def readFile(sFile, sMode = 'rb'):
+ """
+ Reads the entire file.
+ """
+ with open(sFile, sMode) as oFile: # pylint: disable=unspecified-encoding
+ sRet = oFile.read();
+ return sRet;
+
+def noxcptReadFile(sFile, sXcptRet, sMode = 'rb', sEncoding = 'utf-8'):
+ """
+ No exceptions common.readFile wrapper.
+ """
+ try:
+ sRet = readFile(sFile, sMode);
+ except:
+ sRet = sXcptRet;
+ if sEncoding is not None and hasattr(sRet, 'decode'):
+ sRet = sRet.decode(sEncoding, 'ignore');
+ return sRet;
+
+def noxcptRmDir(sDir, oXcptRet = False):
+ """
+ No exceptions os.rmdir wrapper.
+ """
+ oRet = True;
+ try:
+ os.rmdir(sDir);
+ except:
+ oRet = oXcptRet;
+ return oRet;
+
+def noxcptDeleteFile(sFile, oXcptRet = False):
+ """
+ No exceptions os.remove wrapper.
+ """
+ oRet = True;
+ try:
+ os.remove(sFile);
+ except:
+ oRet = oXcptRet;
+ return oRet;
+
+
+def dirEnumerateTree(sDir, fnCallback, fIgnoreExceptions = True):
+ # type: (string, (string, stat) -> bool) -> bool
+ """
+ Recursively walks a directory tree, calling fnCallback for each.
+
+ fnCallback takes a full path and stat object (can be None). It
+ returns a boolean value, False stops walking and returns immediately.
+
+ Returns True or False depending on fnCallback.
+ Returns None fIgnoreExceptions is True and an exception was raised by listdir.
+ """
+ def __worker(sCurDir):
+ """ Worker for """
+ try:
+ asNames = os.listdir(sCurDir);
+ except:
+ if not fIgnoreExceptions:
+ raise;
+ return None;
+ rc = True;
+ for sName in asNames:
+ if sName not in [ '.', '..' ]:
+ sFullName = os.path.join(sCurDir, sName);
+ try: oStat = os.lstat(sFullName);
+ except: oStat = None;
+ if fnCallback(sFullName, oStat) is False:
+ return False;
+ if oStat is not None and stat.S_ISDIR(oStat.st_mode):
+ rc = __worker(sFullName);
+ if rc is False:
+ break;
+ return rc;
+
+ # Ensure unicode path here so listdir also returns unicode on windows.
+ ## @todo figure out unicode stuff on non-windows.
+ if sys.platform == 'win32':
+ sDir = unicode(sDir);
+ return __worker(sDir);
+
+
+
+def formatFileMode(uMode):
+ # type: (int) -> string
+ """
+ Format a st_mode value 'ls -la' fasion.
+ Returns string.
+ """
+ if stat.S_ISDIR(uMode): sMode = 'd';
+ elif stat.S_ISREG(uMode): sMode = '-';
+ elif stat.S_ISLNK(uMode): sMode = 'l';
+ elif stat.S_ISFIFO(uMode): sMode = 'p';
+ elif stat.S_ISCHR(uMode): sMode = 'c';
+ elif stat.S_ISBLK(uMode): sMode = 'b';
+ elif stat.S_ISSOCK(uMode): sMode = 's';
+ else: sMode = '?';
+ ## @todo sticky bits.
+ sMode += 'r' if uMode & stat.S_IRUSR else '-';
+ sMode += 'w' if uMode & stat.S_IWUSR else '-';
+ sMode += 'x' if uMode & stat.S_IXUSR else '-';
+ sMode += 'r' if uMode & stat.S_IRGRP else '-';
+ sMode += 'w' if uMode & stat.S_IWGRP else '-';
+ sMode += 'x' if uMode & stat.S_IXGRP else '-';
+ sMode += 'r' if uMode & stat.S_IROTH else '-';
+ sMode += 'w' if uMode & stat.S_IWOTH else '-';
+ sMode += 'x' if uMode & stat.S_IXOTH else '-';
+ sMode += ' ';
+ return sMode;
+
+
+def formatFileStat(oStat):
+ # type: (stat) -> string
+ """
+ Format a stat result 'ls -la' fasion (numeric IDs).
+ Returns string.
+ """
+ return '%s %3s %4s %4s %10s %s' \
+ % (formatFileMode(oStat.st_mode), oStat.st_nlink, oStat.st_uid, oStat.st_gid, oStat.st_size,
+ time.strftime('%Y-%m-%d %H:%M', time.localtime(oStat.st_mtime)), );
+
+## Good buffer for file operations.
+g_cbGoodBufferSize = 256*1024;
+
+## The original shutil.copyfileobj.
+g_fnOriginalShCopyFileObj = None;
+
+def __myshutilcopyfileobj(fsrc, fdst, length = g_cbGoodBufferSize):
+ """ shutil.copyfileobj with different length default value (16384 is slow with python 2.7 on windows). """
+ return g_fnOriginalShCopyFileObj(fsrc, fdst, length);
+
+def __installShUtilHacks(shutil):
+ """ Installs the shutil buffer size hacks. """
+ global g_fnOriginalShCopyFileObj;
+ if g_fnOriginalShCopyFileObj is None:
+ g_fnOriginalShCopyFileObj = shutil.copyfileobj;
+ shutil.copyfileobj = __myshutilcopyfileobj;
+ return True;
+
+
+def copyFileSimple(sFileSrc, sFileDst):
+ """
+ Wrapper around shutil.copyfile that simply copies the data of a regular file.
+ Raises exception on failure.
+ Return True for show.
+ """
+ import shutil;
+ __installShUtilHacks(shutil);
+ return shutil.copyfile(sFileSrc, sFileDst);
+
+
+def getDiskUsage(sPath):
+ """
+ Get free space of a partition that corresponds to specified sPath in MB.
+
+ Returns partition free space value in MB.
+ """
+ if platform.system() == 'Windows':
+ oCTypeFreeSpace = ctypes.c_ulonglong(0);
+ ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(sPath), None, None,
+ ctypes.pointer(oCTypeFreeSpace));
+ cbFreeSpace = oCTypeFreeSpace.value;
+ else:
+ oStats = os.statvfs(sPath); # pylint: disable=no-member
+ cbFreeSpace = long(oStats.f_frsize) * oStats.f_bfree;
+
+ # Convert to MB
+ cMbFreeSpace = long(cbFreeSpace) / (1024 * 1024);
+
+ return cMbFreeSpace;
+
+
+
+#
+# SubProcess.
+#
+
+def _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs):
+ """
+ If the "executable" is a python script, insert the python interpreter at
+ the head of the argument list so that it will work on systems which doesn't
+ support hash-bang scripts.
+ """
+
+ asArgs = dKeywordArgs.get('args');
+ if asArgs is None:
+ asArgs = aPositionalArgs[0];
+
+ if asArgs[0].endswith('.py'):
+ if sys.executable:
+ asArgs.insert(0, sys.executable);
+ else:
+ asArgs.insert(0, 'python');
+
+ # paranoia...
+ if dKeywordArgs.get('args') is not None:
+ dKeywordArgs['args'] = asArgs;
+ else:
+ aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
+ return None;
+
+def processPopenSafe(*aPositionalArgs, **dKeywordArgs):
+ """
+ Wrapper for subprocess.Popen that's Ctrl-C safe on windows.
+ """
+ if getHostOs() == 'win':
+ if dKeywordArgs.get('creationflags', 0) == 0:
+ dKeywordArgs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP;
+ return subprocess.Popen(*aPositionalArgs, **dKeywordArgs); # pylint: disable=consider-using-with
+
+def processStart(*aPositionalArgs, **dKeywordArgs):
+ """
+ Wrapper around subprocess.Popen to deal with its absence in older
+ python versions.
+ Returns process object on success which can be worked on.
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ return processPopenSafe(*aPositionalArgs, **dKeywordArgs);
+
+def processCall(*aPositionalArgs, **dKeywordArgs):
+ """
+ Wrapper around subprocess.call to deal with its absence in older
+ python versions.
+ Returns process exit code (see subprocess.poll).
+ """
+ assert dKeywordArgs.get('stdout') is None;
+ assert dKeywordArgs.get('stderr') is None;
+ oProcess = processStart(*aPositionalArgs, **dKeywordArgs);
+ return oProcess.wait();
+
+def processOutputChecked(*aPositionalArgs, **dKeywordArgs):
+ """
+ Wrapper around subprocess.check_output to deal with its absense in older
+ python versions.
+
+ Extra keywords for specifying now output is to be decoded:
+ sEncoding='utf-8
+ fIgnoreEncoding=True/False
+ """
+ sEncoding = dKeywordArgs.get('sEncoding');
+ if sEncoding is not None: del dKeywordArgs['sEncoding'];
+ else: sEncoding = 'utf-8';
+
+ fIgnoreEncoding = dKeywordArgs.get('fIgnoreEncoding');
+ if fIgnoreEncoding is not None: del dKeywordArgs['fIgnoreEncoding'];
+ else: fIgnoreEncoding = True;
+
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ oProcess = processPopenSafe(stdout=subprocess.PIPE, *aPositionalArgs, **dKeywordArgs);
+
+ sOutput, _ = oProcess.communicate();
+ iExitCode = oProcess.poll();
+
+ if iExitCode != 0:
+ asArgs = dKeywordArgs.get('args');
+ if asArgs is None:
+ asArgs = aPositionalArgs[0];
+ print(sOutput);
+ raise subprocess.CalledProcessError(iExitCode, asArgs);
+
+ if hasattr(sOutput, 'decode'):
+ sOutput = sOutput.decode(sEncoding, 'ignore' if fIgnoreEncoding else 'strict');
+ return sOutput;
+
+def processOutputUnchecked(*aPositionalArgs, **dKeywordArgs):
+ """
+ Similar to processOutputChecked, but returns status code and both stdout
+ and stderr results.
+
+ Extra keywords for specifying now output is to be decoded:
+ sEncoding='utf-8
+ fIgnoreEncoding=True/False
+ """
+ sEncoding = dKeywordArgs.get('sEncoding');
+ if sEncoding is not None: del dKeywordArgs['sEncoding'];
+ else: sEncoding = 'utf-8';
+
+ fIgnoreEncoding = dKeywordArgs.get('fIgnoreEncoding');
+ if fIgnoreEncoding is not None: del dKeywordArgs['fIgnoreEncoding'];
+ else: fIgnoreEncoding = True;
+
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ oProcess = processPopenSafe(stdout = subprocess.PIPE, stderr = subprocess.PIPE, *aPositionalArgs, **dKeywordArgs);
+
+ sOutput, sError = oProcess.communicate();
+ iExitCode = oProcess.poll();
+
+ if hasattr(sOutput, 'decode'):
+ sOutput = sOutput.decode(sEncoding, 'ignore' if fIgnoreEncoding else 'strict');
+ if hasattr(sError, 'decode'):
+ sError = sError.decode(sEncoding, 'ignore' if fIgnoreEncoding else 'strict');
+ return (iExitCode, sOutput, sError);
+
+g_fOldSudo = None;
+def _sudoFixArguments(aPositionalArgs, dKeywordArgs, fInitialEnv = True):
+ """
+ Adds 'sudo' (or similar) to the args parameter, whereever it is.
+ """
+
+ # Are we root?
+ fIsRoot = True;
+ try:
+ fIsRoot = os.getuid() == 0; # pylint: disable=no-member
+ except:
+ pass;
+
+ # If not, prepend sudo (non-interactive, simulate initial login).
+ if fIsRoot is not True:
+ asArgs = dKeywordArgs.get('args');
+ if asArgs is None:
+ asArgs = aPositionalArgs[0];
+
+ # Detect old sudo.
+ global g_fOldSudo;
+ if g_fOldSudo is None:
+ try:
+ sVersion = str(processOutputChecked(['sudo', '-V']));
+ except:
+ sVersion = '1.7.0';
+ sVersion = sVersion.strip().split('\n', 1)[0];
+ sVersion = sVersion.replace('Sudo version', '').strip();
+ g_fOldSudo = len(sVersion) >= 4 \
+ and sVersion[0] == '1' \
+ and sVersion[1] == '.' \
+ and sVersion[2] <= '6' \
+ and sVersion[3] == '.';
+
+ asArgs.insert(0, 'sudo');
+ if not g_fOldSudo:
+ asArgs.insert(1, '-n');
+ if fInitialEnv and not g_fOldSudo:
+ asArgs.insert(1, '-i');
+
+ # paranoia...
+ if dKeywordArgs.get('args') is not None:
+ dKeywordArgs['args'] = asArgs;
+ else:
+ aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
+ return None;
+
+
+def sudoProcessStart(*aPositionalArgs, **dKeywordArgs):
+ """
+ sudo (or similar) + subprocess.Popen,
+ returning the process object on success.
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ _sudoFixArguments(aPositionalArgs, dKeywordArgs);
+ return processStart(*aPositionalArgs, **dKeywordArgs);
+
+def sudoProcessCall(*aPositionalArgs, **dKeywordArgs):
+ """
+ sudo (or similar) + subprocess.call
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ _sudoFixArguments(aPositionalArgs, dKeywordArgs);
+ return processCall(*aPositionalArgs, **dKeywordArgs);
+
+def sudoProcessOutputChecked(*aPositionalArgs, **dKeywordArgs):
+ """
+ sudo (or similar) + subprocess.check_output.
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ _sudoFixArguments(aPositionalArgs, dKeywordArgs);
+ return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
+
+def sudoProcessOutputCheckedNoI(*aPositionalArgs, **dKeywordArgs):
+ """
+ sudo (or similar) + subprocess.check_output, except '-i' isn't used.
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ _sudoFixArguments(aPositionalArgs, dKeywordArgs, False);
+ return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
+
+def sudoProcessPopen(*aPositionalArgs, **dKeywordArgs):
+ """
+ sudo (or similar) + processPopenSafe.
+ """
+ _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
+ _sudoFixArguments(aPositionalArgs, dKeywordArgs);
+ return processPopenSafe(*aPositionalArgs, **dKeywordArgs);
+
+
+def whichProgram(sName, sPath = None):
+ """
+ Works similar to the 'which' utility on unix.
+
+ Returns path to the given program if found.
+ Returns None if not found.
+ """
+ sHost = getHostOs();
+ sSep = ';' if sHost in [ 'win', 'os2' ] else ':';
+
+ if sPath is None:
+ if sHost == 'win':
+ sPath = os.environ.get('Path', None);
+ else:
+ sPath = os.environ.get('PATH', None);
+ if sPath is None:
+ return None;
+
+ for sDir in sPath.split(sSep):
+ if sDir.strip() != '':
+ sTest = os.path.abspath(os.path.join(sDir, sName));
+ else:
+ sTest = os.path.abspath(sName);
+ if os.path.exists(sTest):
+ return sTest;
+
+ return None;
+
+#
+# Generic process stuff.
+#
+
+def processInterrupt(uPid):
+ """
+ Sends a SIGINT or equivalent to interrupt the specified process.
+ Returns True on success, False on failure.
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ if sys.platform == 'win32':
+ try:
+ win32console.GenerateConsoleCtrlEvent(win32con.CTRL_BREAK_EVENT, # pylint: disable=no-member,c-extension-no-member
+ uPid);
+ fRc = True;
+ except:
+ fRc = False;
+ else:
+ try:
+ os.kill(uPid, signal.SIGINT);
+ fRc = True;
+ except:
+ fRc = False;
+ return fRc;
+
+def sendUserSignal1(uPid):
+ """
+ Sends a SIGUSR1 or equivalent to nudge the process into shutting down
+ (VBoxSVC) or something.
+ Returns True on success, False on failure or if not supported (win).
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ if sys.platform == 'win32':
+ fRc = False;
+ else:
+ try:
+ os.kill(uPid, signal.SIGUSR1); # pylint: disable=no-member
+ fRc = True;
+ except:
+ fRc = False;
+ return fRc;
+
+def processTerminate(uPid):
+ """
+ Terminates the process in a nice manner (SIGTERM or equivalent).
+ Returns True on success, False on failure.
+ """
+ fRc = False;
+ if sys.platform == 'win32':
+ try:
+ hProcess = win32api.OpenProcess(win32con.PROCESS_TERMINATE, # pylint: disable=no-member,c-extension-no-member
+ False, uPid);
+ except:
+ pass;
+ else:
+ try:
+ win32process.TerminateProcess(hProcess, # pylint: disable=no-member,c-extension-no-member
+ 0x40010004); # DBG_TERMINATE_PROCESS
+ fRc = True;
+ except:
+ pass;
+ hProcess.Close(); #win32api.CloseHandle(hProcess)
+ else:
+ try:
+ os.kill(uPid, signal.SIGTERM);
+ fRc = True;
+ except:
+ pass;
+ return fRc;
+
+def processKill(uPid):
+ """
+ Terminates the process with extreme prejudice (SIGKILL).
+ Returns True on success, False on failure.
+ """
+ if sys.platform == 'win32':
+ fRc = processTerminate(uPid);
+ else:
+ try:
+ os.kill(uPid, signal.SIGKILL); # pylint: disable=no-member
+ fRc = True;
+ except:
+ fRc = False;
+ return fRc;
+
+def processKillWithNameCheck(uPid, sName):
+ """
+ Like processKill(), but checks if the process name matches before killing
+ it. This is intended for killing using potentially stale pid values.
+
+ Returns True on success, False on failure.
+ """
+
+ if processCheckPidAndName(uPid, sName) is not True:
+ return False;
+ return processKill(uPid);
+
+
+def processExists(uPid):
+ """
+ Checks if the specified process exits.
+ This will only work if we can signal/open the process.
+
+ Returns True if it positively exists, False otherwise.
+ """
+ sHostOs = getHostOs();
+ if sHostOs == 'win':
+ fRc = False;
+ # We try open the process for waiting since this is generally only forbidden in a very few cases.
+ try:
+ hProcess = win32api.OpenProcess(win32con.SYNCHRONIZE, # pylint: disable=no-member,c-extension-no-member
+ False, uPid);
+ except pywintypes.error as oXcpt: # pylint: disable=no-member
+ if oXcpt.winerror == winerror.ERROR_ACCESS_DENIED:
+ fRc = True;
+ except:
+ pass;
+ else:
+ hProcess.Close();
+ fRc = True;
+ else:
+ fRc = False;
+ try:
+ os.kill(uPid, 0);
+ fRc = True;
+ except OSError as oXcpt:
+ if oXcpt.errno == errno.EPERM:
+ fRc = True;
+ except:
+ pass;
+ return fRc;
+
+def processCheckPidAndName(uPid, sName):
+ """
+ Checks if a process PID and NAME matches.
+ """
+ fRc = processExists(uPid);
+ if fRc is not True:
+ return False;
+
+ if sys.platform == 'win32':
+ try:
+ from win32com.client import GetObject; # pylint: disable=import-error
+ oWmi = GetObject('winmgmts:');
+ aoProcesses = oWmi.InstancesOf('Win32_Process');
+ for oProcess in aoProcesses:
+ if long(oProcess.Properties_("ProcessId").Value) == uPid:
+ sCurName = oProcess.Properties_("Name").Value;
+ #reporter.log2('uPid=%s sName=%s sCurName=%s' % (uPid, sName, sCurName));
+ sName = sName.lower();
+ sCurName = sCurName.lower();
+ if os.path.basename(sName) == sName:
+ sCurName = os.path.basename(sCurName);
+
+ if sCurName == sName \
+ or sCurName + '.exe' == sName \
+ or sCurName == sName + '.exe':
+ fRc = True;
+ break;
+ except:
+ #reporter.logXcpt('uPid=%s sName=%s' % (uPid, sName));
+ pass;
+ else:
+ if sys.platform in ('linux2', 'linux', 'linux3', 'linux4', 'linux5', 'linux6'):
+ asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
+ elif sys.platform in ('sunos5',):
+ asPsCmd = ['/usr/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
+ elif sys.platform in ('darwin',):
+ asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'ucomm='];
+ else:
+ asPsCmd = None;
+
+ if asPsCmd is not None:
+ try:
+ oPs = subprocess.Popen(asPsCmd, stdout=subprocess.PIPE); # pylint: disable=consider-using-with
+ sCurName = oPs.communicate()[0];
+ iExitCode = oPs.wait();
+ except:
+ #reporter.logXcpt();
+ return False;
+
+ # ps fails with non-zero exit code if the pid wasn't found.
+ if iExitCode != 0:
+ return False;
+ if sCurName is None:
+ return False;
+ sCurName = sCurName.strip();
+ if not sCurName:
+ return False;
+
+ if os.path.basename(sName) == sName:
+ sCurName = os.path.basename(sCurName);
+ elif os.path.basename(sCurName) == sCurName:
+ sName = os.path.basename(sName);
+
+ if sCurName != sName:
+ return False;
+
+ fRc = True;
+ return fRc;
+
+def processGetInfo(uPid, fSudo = False):
+ """
+ Tries to acquire state information of the given process.
+
+ Returns a string with the information on success or None on failure or
+ if the host is not supported.
+
+ Note that the format of the information is host system dependent and will
+ likely differ much between different hosts.
+ """
+ fRc = processExists(uPid);
+ if fRc is not True:
+ return None;
+
+ sHostOs = getHostOs();
+ if sHostOs in [ 'linux',]:
+ sGdb = '/usr/bin/gdb';
+ if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
+ if not os.path.isfile(sGdb): sGdb = 'gdb';
+ aasCmd = [
+ [ sGdb, '-batch',
+ '-ex', 'set pagination off',
+ '-ex', 'thread apply all bt',
+ '-ex', 'info proc mapping',
+ '-ex', 'info sharedlibrary',
+ '-p', '%u' % (uPid,), ],
+ ];
+ elif sHostOs == 'darwin':
+ # LLDB doesn't work in batch mode when attaching to a process, at least
+ # with macOS Sierra (10.12). GDB might not be installed. Use the sample
+ # tool instead with a 1 second duration and 1000ms sampling interval to
+ # get one stack trace. For the process mappings use vmmap.
+ aasCmd = [
+ [ '/usr/bin/sample', '-mayDie', '%u' % (uPid,), '1', '1000', ],
+ [ '/usr/bin/vmmap', '%u' % (uPid,), ],
+ ];
+ elif sHostOs == 'solaris':
+ aasCmd = [
+ [ '/usr/bin/pstack', '%u' % (uPid,), ],
+ [ '/usr/bin/pmap', '%u' % (uPid,), ],
+ ];
+ else:
+ aasCmd = [];
+
+ sInfo = '';
+ for asCmd in aasCmd:
+ try:
+ if fSudo:
+ sThisInfo = sudoProcessOutputChecked(asCmd);
+ else:
+ sThisInfo = processOutputChecked(asCmd);
+ if sThisInfo is not None:
+ sInfo += sThisInfo;
+ except:
+ pass;
+ if not sInfo:
+ sInfo = None;
+
+ return sInfo;
+
+
+class ProcessInfo(object):
+ """Process info."""
+ def __init__(self, iPid):
+ self.iPid = iPid;
+ self.iParentPid = None;
+ self.sImage = None;
+ self.sName = None;
+ self.asArgs = None;
+ self.sCwd = None;
+ self.iGid = None;
+ self.iUid = None;
+ self.iProcGroup = None;
+ self.iSessionId = None;
+
+ def loadAll(self):
+ """Load all the info."""
+ sOs = getHostOs();
+ if sOs == 'linux':
+ sProc = '/proc/%s/' % (self.iPid,);
+ if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'exe', None);
+ if self.sImage is None:
+ self.sImage = noxcptReadFile(sProc + 'comm', None);
+ if self.sImage: self.sImage = self.sImage.strip();
+ if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'cwd', None);
+ if self.asArgs is None: self.asArgs = noxcptReadFile(sProc + 'cmdline', '').split('\x00');
+ #elif sOs == 'solaris': - doesn't work for root processes, suid proces, and other stuff.
+ # sProc = '/proc/%s/' % (self.iPid,);
+ # if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'path/a.out', None);
+ # if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'path/cwd', None);
+ else:
+ pass;
+ if self.sName is None and self.sImage is not None:
+ self.sName = self.sImage;
+
+ def windowsGrabProcessInfo(self, oProcess):
+ """Windows specific loadAll."""
+ try: self.sName = oProcess.Properties_("Name").Value;
+ except: pass;
+ try: self.sImage = oProcess.Properties_("ExecutablePath").Value;
+ except: pass;
+ try: self.asArgs = [oProcess.Properties_("CommandLine").Value]; ## @todo split it.
+ except: pass;
+ try: self.iParentPid = oProcess.Properties_("ParentProcessId").Value;
+ except: pass;
+ try: self.iSessionId = oProcess.Properties_("SessionId").Value;
+ except: pass;
+ if self.sName is None and self.sImage is not None:
+ self.sName = self.sImage;
+
+ def getBaseImageName(self):
+ """
+ Gets the base image name if available, use the process name if not available.
+ Returns image/process base name or None.
+ """
+ sRet = self.sImage if self.sName is None else self.sName;
+ if sRet is None:
+ self.loadAll();
+ sRet = self.sImage if self.sName is None else self.sName;
+ if sRet is None:
+ if not self.asArgs:
+ return None;
+ sRet = self.asArgs[0];
+ if not sRet:
+ return None;
+ return os.path.basename(sRet);
+
+ def getBaseImageNameNoExeSuff(self):
+ """
+ Same as getBaseImageName, except any '.exe' or similar suffix is stripped.
+ """
+ sRet = self.getBaseImageName();
+ if sRet is not None and len(sRet) > 4 and sRet[-4] == '.':
+ if (sRet[-4:]).lower() in [ '.exe', '.com', '.msc', '.vbs', '.cmd', '.bat' ]:
+ sRet = sRet[:-4];
+ return sRet;
+
+
+def processListAll():
+ """
+ Return a list of ProcessInfo objects for all the processes in the system
+ that the current user can see.
+ """
+ asProcesses = [];
+
+ sOs = getHostOs();
+ if sOs == 'win':
+ from win32com.client import GetObject; # pylint: disable=import-error
+ oWmi = GetObject('winmgmts:');
+ aoProcesses = oWmi.InstancesOf('Win32_Process');
+ for oProcess in aoProcesses:
+ try:
+ iPid = int(oProcess.Properties_("ProcessId").Value);
+ except:
+ continue;
+ oMyInfo = ProcessInfo(iPid);
+ oMyInfo.windowsGrabProcessInfo(oProcess);
+ asProcesses.append(oMyInfo);
+ return asProcesses;
+
+ if sOs in [ 'linux', ]: # Not solaris, ps gets more info than /proc/.
+ try:
+ asDirs = os.listdir('/proc');
+ except:
+ asDirs = [];
+ for sDir in asDirs:
+ if sDir.isdigit():
+ asProcesses.append(ProcessInfo(int(sDir),));
+ return asProcesses;
+
+ #
+ # The other OSes parses the output from the 'ps' utility.
+ #
+ asPsCmd = [
+ '/bin/ps', # 0
+ '-A', # 1
+ '-o', 'pid=', # 2,3
+ '-o', 'ppid=', # 4,5
+ '-o', 'pgid=', # 6,7
+ '-o', 'sid=', # 8,9
+ '-o', 'uid=', # 10,11
+ '-o', 'gid=', # 12,13
+ '-o', 'comm=' # 14,15
+ ];
+
+ if sOs == 'darwin':
+ assert asPsCmd[9] == 'sid=';
+ asPsCmd[9] = 'sess=';
+ elif sOs == 'solaris':
+ asPsCmd[0] = '/usr/bin/ps';
+
+ try:
+ sRaw = processOutputChecked(asPsCmd);
+ except:
+ return asProcesses;
+
+ for sLine in sRaw.split('\n'):
+ sLine = sLine.lstrip();
+ if len(sLine) < 7 or not sLine[0].isdigit():
+ continue;
+
+ iField = 0;
+ off = 0;
+ aoFields = [None, None, None, None, None, None, None];
+ while iField < 7:
+ # Eat whitespace.
+ while off < len(sLine) and (sLine[off] == ' ' or sLine[off] == '\t'):
+ off += 1;
+
+ # Final field / EOL.
+ if iField == 6:
+ aoFields[6] = sLine[off:];
+ break;
+ if off >= len(sLine):
+ break;
+
+ # Generic field parsing.
+ offStart = off;
+ off += 1;
+ while off < len(sLine) and sLine[off] != ' ' and sLine[off] != '\t':
+ off += 1;
+ try:
+ if iField != 3:
+ aoFields[iField] = int(sLine[offStart:off]);
+ else:
+ aoFields[iField] = long(sLine[offStart:off], 16); # sess is a hex address.
+ except:
+ pass;
+ iField += 1;
+
+ if aoFields[0] is not None:
+ oMyInfo = ProcessInfo(aoFields[0]);
+ oMyInfo.iParentPid = aoFields[1];
+ oMyInfo.iProcGroup = aoFields[2];
+ oMyInfo.iSessionId = aoFields[3];
+ oMyInfo.iUid = aoFields[4];
+ oMyInfo.iGid = aoFields[5];
+ oMyInfo.sName = aoFields[6];
+ asProcesses.append(oMyInfo);
+
+ return asProcesses;
+
+
+def processCollectCrashInfo(uPid, fnLog, fnCrashFile):
+ """
+ Looks for information regarding the demise of the given process.
+ """
+ sOs = getHostOs();
+ if sOs == 'darwin':
+ #
+ # On darwin we look for crash and diagnostic reports.
+ #
+ asLogDirs = [
+ u'/Library/Logs/DiagnosticReports/',
+ u'/Library/Logs/CrashReporter/',
+ u'~/Library/Logs/DiagnosticReports/',
+ u'~/Library/Logs/CrashReporter/',
+ ];
+ for sDir in asLogDirs:
+ sDir = os.path.expanduser(sDir);
+ if not os.path.isdir(sDir):
+ continue;
+ try:
+ asDirEntries = os.listdir(sDir);
+ except:
+ continue;
+ for sEntry in asDirEntries:
+ # Only interested in .crash files.
+ _, sSuff = os.path.splitext(sEntry);
+ if sSuff != '.crash':
+ continue;
+
+ # The pid can be found at the end of the first line.
+ sFull = os.path.join(sDir, sEntry);
+ try:
+ with open(sFull, 'r') as oFile: # pylint: disable=unspecified-encoding
+ sFirstLine = oFile.readline();
+ except:
+ continue;
+ if len(sFirstLine) <= 4 or sFirstLine[-2] != ']':
+ continue;
+ offPid = len(sFirstLine) - 3;
+ while offPid > 1 and sFirstLine[offPid - 1].isdigit():
+ offPid -= 1;
+ try: uReportPid = int(sFirstLine[offPid:-2]);
+ except: continue;
+
+ # Does the pid we found match?
+ if uReportPid == uPid:
+ fnLog('Found crash report for %u: %s' % (uPid, sFull,));
+ fnCrashFile(sFull, False);
+ elif sOs == 'win':
+ #
+ # Getting WER reports would be great, however we have trouble match the
+ # PID to those as they seems not to mention it in the brief reports.
+ # Instead we'll just look for crash dumps in C:\CrashDumps (our custom
+ # location - see the windows readme for the testbox script) and what
+ # the MSDN article lists for now.
+ #
+ # It's been observed on Windows server 2012 that the dump files takes
+ # the form: <processimage>.<decimal-pid>.dmp
+ #
+ asDmpDirs = [
+ u'%SystemDrive%/CrashDumps/', # Testboxes.
+ u'%LOCALAPPDATA%/CrashDumps/', # MSDN example.
+ u'%WINDIR%/ServiceProfiles/LocalServices/', # Local and network service.
+ u'%WINDIR%/ServiceProfiles/NetworkSerices/',
+ u'%WINDIR%/ServiceProfiles/',
+ u'%WINDIR%/System32/Config/SystemProfile/', # System services.
+ ];
+ sMatchSuffix = '.%u.dmp' % (uPid,);
+
+ for sDir in asDmpDirs:
+ sDir = os.path.expandvars(sDir);
+ if not os.path.isdir(sDir):
+ continue;
+ try:
+ asDirEntries = os.listdir(sDir);
+ except:
+ continue;
+ for sEntry in asDirEntries:
+ if sEntry.endswith(sMatchSuffix):
+ sFull = os.path.join(sDir, sEntry);
+ fnLog('Found crash dump for %u: %s' % (uPid, sFull,));
+ fnCrashFile(sFull, True);
+ elif sOs == 'solaris':
+ asDmpDirs = [];
+ try:
+ sScratchPath = os.environ.get('TESTBOX_PATH_SCRATCH', None);
+ asDmpDirs.extend([ sScratchPath ]);
+ except:
+ pass;
+ # Some other useful locations as fallback.
+ asDmpDirs.extend([
+ u'/var/cores/',
+ u'/var/core/',
+ ]);
+ #
+ # Solaris by default creates a core file in the directory of the crashing process with the name 'core'.
+ #
+ # As we need to distinguish the core files correlating to their PIDs and have a persistent storage location,
+ # the host needs to be tweaked via:
+ #
+ # ```coreadm -g /path/to/cores/core.%f.%p```
+ #
+ sMatchSuffix = '.%u.core' % (uPid,);
+ for sDir in asDmpDirs:
+ sDir = os.path.expandvars(sDir);
+ if not os.path.isdir(sDir):
+ continue;
+ try:
+ asDirEntries = os.listdir(sDir);
+ except:
+ continue;
+ for sEntry in asDirEntries:
+ fnLog('Entry: %s' % (os.path.join(sDir, sEntry)));
+ if sEntry.endswith(sMatchSuffix):
+ sFull = os.path.join(sDir, sEntry);
+ fnLog('Found crash dump for %u: %s' % (uPid, sFull,));
+ fnCrashFile(sFull, True);
+ else:
+ pass; ## TODO
+ return None;
+
+
+#
+# Time.
+#
+
+#
+# The following test case shows how time.time() only have ~ms resolution
+# on Windows (tested W10) and why it therefore makes sense to try use
+# performance counters.
+#
+# Note! We cannot use time.clock() as the timestamp must be portable across
+# processes. See timeout testcase problem on win hosts (no logs).
+# Also, time.clock() was axed in python 3.8 (https://bugs.python.org/issue31803).
+#
+#import sys;
+#import time;
+#from common import utils;
+#
+#atSeries = [];
+#for i in xrange(1,160):
+# if i == 159: time.sleep(10);
+# atSeries.append((utils.timestampNano(), long(time.clock() * 1000000000), long(time.time() * 1000000000)));
+#
+#tPrev = atSeries[0]
+#for tCur in atSeries:
+# print 't1=%+22u, %u' % (tCur[0], tCur[0] - tPrev[0]);
+# print 't2=%+22u, %u' % (tCur[1], tCur[1] - tPrev[1]);
+# print 't3=%+22u, %u' % (tCur[2], tCur[2] - tPrev[2]);
+# print '';
+# tPrev = tCur
+#
+#print 't1=%u' % (atSeries[-1][0] - atSeries[0][0]);
+#print 't2=%u' % (atSeries[-1][1] - atSeries[0][1]);
+#print 't3=%u' % (atSeries[-1][2] - atSeries[0][2]);
+
+g_fWinUseWinPerfCounter = sys.platform == 'win32';
+g_fpWinPerfCounterFreq = None;
+g_oFuncwinQueryPerformanceCounter = None;
+
+def _winInitPerfCounter():
+ """ Initializes the use of performance counters. """
+ global g_fWinUseWinPerfCounter, g_fpWinPerfCounterFreq, g_oFuncwinQueryPerformanceCounter
+
+ uFrequency = ctypes.c_ulonglong(0);
+ if ctypes.windll.kernel32.QueryPerformanceFrequency(ctypes.byref(uFrequency)):
+ if uFrequency.value >= 1000:
+ #print 'uFrequency = %s' % (uFrequency,);
+ #print 'type(uFrequency) = %s' % (type(uFrequency),);
+ g_fpWinPerfCounterFreq = float(uFrequency.value);
+
+ # Check that querying the counter works too.
+ global g_oFuncwinQueryPerformanceCounter
+ g_oFuncwinQueryPerformanceCounter = ctypes.windll.kernel32.QueryPerformanceCounter;
+ uCurValue = ctypes.c_ulonglong(0);
+ if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
+ if uCurValue.value > 0:
+ return True;
+ g_fWinUseWinPerfCounter = False;
+ return False;
+
+def _winFloatTime():
+ """ Gets floating point time on windows. """
+ if g_fpWinPerfCounterFreq is not None or _winInitPerfCounter():
+ uCurValue = ctypes.c_ulonglong(0);
+ if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
+ return float(uCurValue.value) / g_fpWinPerfCounterFreq;
+ return time.time();
+
+def timestampNano():
+ """
+ Gets a nanosecond timestamp.
+ """
+ if g_fWinUseWinPerfCounter is True:
+ return long(_winFloatTime() * 1000000000);
+ return long(time.time() * 1000000000);
+
+def timestampMilli():
+ """
+ Gets a millisecond timestamp.
+ """
+ if g_fWinUseWinPerfCounter is True:
+ return long(_winFloatTime() * 1000);
+ return long(time.time() * 1000);
+
+def timestampSecond():
+ """
+ Gets a second timestamp.
+ """
+ if g_fWinUseWinPerfCounter is True:
+ return long(_winFloatTime());
+ return long(time.time());
+
+def secondsSinceUnixEpoch():
+ """
+ Returns unix time, floating point second count since 1970-01-01T00:00:00Z
+ """
+ ## ASSUMES This returns unix epoch time on all systems we care about...
+ return time.time();
+
+def getTimePrefix():
+ """
+ Returns a timestamp prefix, typically used for logging. UTC.
+ """
+ try:
+ oNow = datetime.datetime.utcnow();
+ sTs = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
+ except:
+ sTs = 'getTimePrefix-exception';
+ return sTs;
+
+def getTimePrefixAndIsoTimestamp():
+ """
+ Returns current UTC as log prefix and iso timestamp.
+ """
+ try:
+ oNow = datetime.datetime.utcnow();
+ sTsPrf = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
+ sTsIso = formatIsoTimestamp(oNow);
+ except:
+ sTsPrf = sTsIso = 'getTimePrefix-exception';
+ return (sTsPrf, sTsIso);
+
+class UtcTzInfo(datetime.tzinfo):
+ """UTC TZ Info Class"""
+ def utcoffset(self, _):
+ return datetime.timedelta(0);
+ def tzname(self, _):
+ return "UTC";
+ def dst(self, _):
+ return datetime.timedelta(0);
+
+class GenTzInfo(datetime.tzinfo):
+ """Generic TZ Info Class"""
+ def __init__(self, offInMin):
+ datetime.tzinfo.__init__(self);
+ self.offInMin = offInMin;
+ def utcoffset(self, _):
+ return datetime.timedelta(minutes = self.offInMin);
+ def tzname(self, _):
+ if self.offInMin >= 0:
+ return "+%02d%02d" % (self.offInMin // 60, self.offInMin % 60);
+ return "-%02d%02d" % (-self.offInMin // 60, -self.offInMin % 60);
+ def dst(self, _):
+ return datetime.timedelta(0);
+
+def formatIsoTimestamp(oNow):
+ """Formats the datetime object as an ISO timestamp."""
+ assert oNow.tzinfo is None or isinstance(oNow.tzinfo, UtcTzInfo);
+ sTs = '%s.%09uZ' % (oNow.strftime('%Y-%m-%dT%H:%M:%S'), oNow.microsecond * 1000);
+ return sTs;
+
+def getIsoTimestamp():
+ """Returns the current UTC timestamp as a string."""
+ return formatIsoTimestamp(datetime.datetime.utcnow());
+
+def formatShortIsoTimestamp(oNow):
+ """Formats the datetime object as an ISO timestamp, but w/o microseconds."""
+ assert oNow.tzinfo is None or isinstance(oNow.tzinfo, UtcTzInfo);
+ return oNow.strftime('%Y-%m-%dT%H:%M:%SZ');
+
+def getShortIsoTimestamp():
+ """Returns the current UTC timestamp as a string, but w/o microseconds."""
+ return formatShortIsoTimestamp(datetime.datetime.utcnow());
+
+def convertDateTimeToZulu(oDateTime):
+ """ Converts oDateTime to zulu time if it has timezone info. """
+ if oDateTime.tzinfo is not None:
+ oDateTime = oDateTime.astimezone(UtcTzInfo());
+ else:
+ oDateTime = oDateTime.replace(tzinfo = UtcTzInfo());
+ return oDateTime;
+
+def parseIsoTimestamp(sTs):
+ """
+ Parses a typical ISO timestamp, returing a datetime object, reasonably
+ forgiving, but will throw weird indexing/conversion errors if the input
+ is malformed.
+ """
+ # YYYY-MM-DD
+ iYear = int(sTs[0:4]);
+ assert(sTs[4] == '-');
+ iMonth = int(sTs[5:7]);
+ assert(sTs[7] == '-');
+ iDay = int(sTs[8:10]);
+
+ # Skip separator
+ sTime = sTs[10:];
+ while sTime[0] in 'Tt \t\n\r':
+ sTime = sTime[1:];
+
+ # HH:MM[:SS]
+ iHour = int(sTime[0:2]);
+ assert(sTime[2] == ':');
+ iMin = int(sTime[3:5]);
+ if sTime[5] == ':':
+ iSec = int(sTime[6:8]);
+
+ # Fraction?
+ offTime = 8;
+ iMicroseconds = 0;
+ if offTime < len(sTime) and sTime[offTime] in '.,':
+ offTime += 1;
+ cchFraction = 0;
+ while offTime + cchFraction < len(sTime) and sTime[offTime + cchFraction] in '0123456789':
+ cchFraction += 1;
+ if cchFraction > 0:
+ iMicroseconds = int(sTime[offTime : (offTime + cchFraction)]);
+ offTime += cchFraction;
+ while cchFraction < 6:
+ iMicroseconds *= 10;
+ cchFraction += 1;
+ while cchFraction > 6:
+ iMicroseconds = iMicroseconds // 10;
+ cchFraction -= 1;
+
+ else:
+ iSec = 0;
+ iMicroseconds = 0;
+ offTime = 5;
+
+ # Naive?
+ if offTime >= len(sTime):
+ return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds);
+
+ # Zulu?
+ if offTime >= len(sTime) or sTime[offTime] in 'Zz':
+ return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds, tzinfo = UtcTzInfo());
+
+ # Some kind of offset afterwards, and strptime is useless. sigh.
+ if sTime[offTime] in '+-':
+ chSign = sTime[offTime];
+ offTime += 1;
+ cMinTz = int(sTime[offTime : (offTime + 2)]) * 60;
+ offTime += 2;
+ if offTime < len(sTime) and sTime[offTime] in ':':
+ offTime += 1;
+ if offTime + 2 <= len(sTime):
+ cMinTz += int(sTime[offTime : (offTime + 2)]);
+ offTime += 2;
+ assert offTime == len(sTime);
+ if chSign == '-':
+ cMinTz = -cMinTz;
+ return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds, tzinfo = GenTzInfo(cMinTz));
+ assert False, sTs;
+ return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds);
+
+def normalizeIsoTimestampToZulu(sTs):
+ """
+ Takes a iso timestamp string and normalizes it (basically parseIsoTimestamp
+ + convertDateTimeToZulu + formatIsoTimestamp).
+ Returns ISO tiemstamp string.
+ """
+ return formatIsoTimestamp(convertDateTimeToZulu(parseIsoTimestamp(sTs)));
+
+def getLocalHourOfWeek():
+ """ Local hour of week (0 based). """
+ oNow = datetime.datetime.now();
+ return (oNow.isoweekday() - 1) * 24 + oNow.hour;
+
+
+def formatIntervalSeconds(cSeconds):
+ """ Format a seconds interval into a nice 01h 00m 22s string """
+ # Two simple special cases.
+ if cSeconds < 60:
+ return '%ss' % (cSeconds,);
+ if cSeconds < 3600:
+ cMins = cSeconds // 60;
+ cSecs = cSeconds % 60;
+ if cSecs == 0:
+ return '%sm' % (cMins,);
+ return '%sm %ss' % (cMins, cSecs,);
+
+ # Generic and a bit slower.
+ cDays = cSeconds // 86400;
+ cSeconds %= 86400;
+ cHours = cSeconds // 3600;
+ cSeconds %= 3600;
+ cMins = cSeconds // 60;
+ cSecs = cSeconds % 60;
+ sRet = '';
+ if cDays > 0:
+ sRet = '%sd ' % (cDays,);
+ if cHours > 0:
+ sRet += '%sh ' % (cHours,);
+ if cMins > 0:
+ sRet += '%sm ' % (cMins,);
+ if cSecs > 0:
+ sRet += '%ss ' % (cSecs,);
+ assert sRet; assert sRet[-1] == ' ';
+ return sRet[:-1];
+
+def formatIntervalSeconds2(oSeconds):
+ """
+ Flexible input version of formatIntervalSeconds for use in WUI forms where
+ data is usually already string form.
+ """
+ if isinstance(oSeconds, (int, long)):
+ return formatIntervalSeconds(oSeconds);
+ if not isString(oSeconds):
+ try:
+ lSeconds = long(oSeconds);
+ except:
+ pass;
+ else:
+ if lSeconds >= 0:
+ return formatIntervalSeconds2(lSeconds);
+ return oSeconds;
+
+def parseIntervalSeconds(sString):
+ """
+ Reverse of formatIntervalSeconds.
+
+ Returns (cSeconds, sError), where sError is None on success.
+ """
+
+ # We might given non-strings, just return them without any fuss.
+ if not isString(sString):
+ if isinstance(sString, (int, long)) or sString is None:
+ return (sString, None);
+ ## @todo time/date objects?
+ return (int(sString), None);
+
+ # Strip it and make sure it's not empty.
+ sString = sString.strip();
+ if not sString:
+ return (0, 'Empty interval string.');
+
+ #
+ # Split up the input into a list of 'valueN, unitN, ...'.
+ #
+ # Don't want to spend too much time trying to make re.split do exactly what
+ # I need here, so please forgive the extra pass I'm making here.
+ #
+ asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
+ asParts = [];
+ for sPart in asRawParts:
+ sPart = sPart.strip();
+ if sPart:
+ asParts.append(sPart);
+ if not asParts:
+ return (0, 'Empty interval string or something?');
+
+ #
+ # Process them one or two at the time.
+ #
+ cSeconds = 0;
+ asErrors = [];
+ i = 0;
+ while i < len(asParts):
+ sNumber = asParts[i];
+ i += 1;
+ if sNumber.isdigit():
+ iNumber = int(sNumber);
+
+ sUnit = 's';
+ if i < len(asParts) and not asParts[i].isdigit():
+ sUnit = asParts[i];
+ i += 1;
+
+ sUnitLower = sUnit.lower();
+ if sUnitLower in [ 's', 'se', 'sec', 'second', 'seconds' ]:
+ pass;
+ elif sUnitLower in [ 'm', 'mi', 'min', 'minute', 'minutes' ]:
+ iNumber *= 60;
+ elif sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
+ iNumber *= 3600;
+ elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
+ iNumber *= 86400;
+ elif sUnitLower in [ 'w', 'week', 'weeks' ]:
+ iNumber *= 7 * 86400;
+ else:
+ asErrors.append('Unknown unit "%s".' % (sUnit,));
+ cSeconds += iNumber;
+ else:
+ asErrors.append('Bad number "%s".' % (sNumber,));
+ return (cSeconds, None if not asErrors else ' '.join(asErrors));
+
+def formatIntervalHours(cHours):
+ """ Format a hours interval into a nice 1w 2d 1h string. """
+ # Simple special cases.
+ if cHours < 24:
+ return '%sh' % (cHours,);
+
+ # Generic and a bit slower.
+ cWeeks = cHours / (7 * 24);
+ cHours %= 7 * 24;
+ cDays = cHours / 24;
+ cHours %= 24;
+ sRet = '';
+ if cWeeks > 0:
+ sRet = '%sw ' % (cWeeks,);
+ if cDays > 0:
+ sRet = '%sd ' % (cDays,);
+ if cHours > 0:
+ sRet += '%sh ' % (cHours,);
+ assert sRet; assert sRet[-1] == ' ';
+ return sRet[:-1];
+
+def parseIntervalHours(sString):
+ """
+ Reverse of formatIntervalHours.
+
+ Returns (cHours, sError), where sError is None on success.
+ """
+
+ # We might given non-strings, just return them without any fuss.
+ if not isString(sString):
+ if isinstance(sString, (int, long)) or sString is None:
+ return (sString, None);
+ ## @todo time/date objects?
+ return (int(sString), None);
+
+ # Strip it and make sure it's not empty.
+ sString = sString.strip();
+ if not sString:
+ return (0, 'Empty interval string.');
+
+ #
+ # Split up the input into a list of 'valueN, unitN, ...'.
+ #
+ # Don't want to spend too much time trying to make re.split do exactly what
+ # I need here, so please forgive the extra pass I'm making here.
+ #
+ asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
+ asParts = [];
+ for sPart in asRawParts:
+ sPart = sPart.strip();
+ if sPart:
+ asParts.append(sPart);
+ if not asParts:
+ return (0, 'Empty interval string or something?');
+
+ #
+ # Process them one or two at the time.
+ #
+ cHours = 0;
+ asErrors = [];
+ i = 0;
+ while i < len(asParts):
+ sNumber = asParts[i];
+ i += 1;
+ if sNumber.isdigit():
+ iNumber = int(sNumber);
+
+ sUnit = 'h';
+ if i < len(asParts) and not asParts[i].isdigit():
+ sUnit = asParts[i];
+ i += 1;
+
+ sUnitLower = sUnit.lower();
+ if sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
+ pass;
+ elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
+ iNumber *= 24;
+ elif sUnitLower in [ 'w', 'week', 'weeks' ]:
+ iNumber *= 7 * 24;
+ else:
+ asErrors.append('Unknown unit "%s".' % (sUnit,));
+ cHours += iNumber;
+ else:
+ asErrors.append('Bad number "%s".' % (sNumber,));
+ return (cHours, None if not asErrors else ' '.join(asErrors));
+
+
+#
+# Introspection.
+#
+
+def getCallerName(oFrame=None, iFrame=2):
+ """
+ Returns the name of the caller's caller.
+ """
+ if oFrame is None:
+ try:
+ raise Exception();
+ except:
+ oFrame = sys.exc_info()[2].tb_frame.f_back;
+ while iFrame > 1:
+ if oFrame is not None:
+ oFrame = oFrame.f_back;
+ iFrame = iFrame - 1;
+ if oFrame is not None:
+ sName = '%s:%u' % (oFrame.f_code.co_name, oFrame.f_lineno);
+ return sName;
+ return "unknown";
+
+
+def getXcptInfo(cFrames = 1):
+ """
+ Gets text detailing the exception. (Good for logging.)
+ Returns list of info strings.
+ """
+
+ #
+ # Try get exception info.
+ #
+ try:
+ oType, oValue, oTraceback = sys.exc_info();
+ except:
+ oType = oValue = oTraceback = None;
+ if oType is not None:
+
+ #
+ # Try format the info
+ #
+ asRet = [];
+ try:
+ try:
+ asRet = asRet + traceback.format_exception_only(oType, oValue);
+ asTraceBack = traceback.format_tb(oTraceback);
+ if cFrames is not None and cFrames <= 1:
+ asRet.append(asTraceBack[-1]);
+ else:
+ asRet.append('Traceback:')
+ for iFrame in range(min(cFrames, len(asTraceBack))):
+ asRet.append(asTraceBack[-iFrame - 1]);
+ asRet.append('Stack:')
+ asRet = asRet + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
+ except:
+ asRet.append('internal-error: Hit exception #2! %s' % (traceback.format_exc(),));
+
+ if not asRet:
+ asRet.append('No exception info...');
+ except:
+ asRet.append('internal-error: Hit exception! %s' % (traceback.format_exc(),));
+ else:
+ asRet = ['Couldn\'t find exception traceback.'];
+ return asRet;
+
+
+def getObjectTypeName(oObject):
+ """
+ Get the type name of the given object.
+ """
+ if oObject is None:
+ return 'None';
+
+ # Get the type object.
+ try:
+ oType = type(oObject);
+ except:
+ return 'type-throws-exception';
+
+ # Python 2.x only: Handle old-style object wrappers.
+ if sys.version_info[0] < 3:
+ try:
+ from types import InstanceType; # pylint: disable=no-name-in-module
+ if oType == InstanceType:
+ oType = oObject.__class__;
+ except:
+ pass;
+
+ # Get the name.
+ try:
+ return oType.__name__;
+ except:
+ return '__type__-throws-exception';
+
+
+def chmodPlusX(sFile):
+ """
+ Makes the specified file or directory executable.
+ Returns success indicator, no exceptions.
+
+ Note! Symbolic links are followed and the target will be changed.
+ """
+ try:
+ oStat = os.stat(sFile);
+ except:
+ return False;
+ try:
+ os.chmod(sFile, oStat.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH);
+ except:
+ return False;
+ return True;
+
+
+#
+# TestSuite stuff.
+#
+
+def isRunningFromCheckout(cScriptDepth = 1):
+ """
+ Checks if we're running from the SVN checkout or not.
+ """
+
+ try:
+ sFile = __file__;
+ cScriptDepth = 1;
+ except:
+ sFile = sys.argv[0];
+
+ sDir = os.path.abspath(sFile);
+ while cScriptDepth >= 0:
+ sDir = os.path.dirname(sDir);
+ if os.path.exists(os.path.join(sDir, 'Makefile.kmk')) \
+ or os.path.exists(os.path.join(sDir, 'Makefile.kup')):
+ return True;
+ cScriptDepth -= 1;
+
+ return False;
+
+
+#
+# Bourne shell argument fun.
+#
+
+
+def argsSplit(sCmdLine):
+ """
+ Given a bourne shell command line invocation, split it up into arguments
+ assuming IFS is space.
+ Returns None on syntax error.
+ """
+ ## @todo bourne shell argument parsing!
+ return sCmdLine.split(' ');
+
+def argsGetFirst(sCmdLine):
+ """
+ Given a bourne shell command line invocation, get return the first argument
+ assuming IFS is space.
+ Returns None on invalid syntax, otherwise the parsed and unescaped argv[0] string.
+ """
+ asArgs = argsSplit(sCmdLine);
+ if not asArgs:
+ return None;
+
+ return asArgs[0];
+
+#
+# String helpers.
+#
+
+def stricmp(sFirst, sSecond):
+ """
+ Compares to strings in an case insensitive fashion.
+
+ Python doesn't seem to have any way of doing the correctly, so this is just
+ an approximation using lower.
+ """
+ if sFirst == sSecond:
+ return 0;
+ sLower1 = sFirst.lower();
+ sLower2 = sSecond.lower();
+ if sLower1 == sLower2:
+ return 0;
+ if sLower1 < sLower2:
+ return -1;
+ return 1;
+
+
+def versionCompare(sVer1, sVer2):
+ """
+ Compares to version strings in a fashion similar to RTStrVersionCompare.
+ """
+
+ ## @todo implement me!!
+
+ if sVer1 == sVer2:
+ return 0;
+ if sVer1 < sVer2:
+ return -1;
+ return 1;
+
+
+def formatNumber(lNum, sThousandSep = ' '):
+ """
+ Formats a decimal number with pretty separators.
+ """
+ sNum = str(lNum);
+ sRet = sNum[-3:];
+ off = len(sNum) - 3;
+ while off > 0:
+ off -= 3;
+ sRet = sNum[(off if off >= 0 else 0):(off + 3)] + sThousandSep + sRet;
+ return sRet;
+
+
+def formatNumberNbsp(lNum):
+ """
+ Formats a decimal number with pretty separators.
+ """
+ sRet = formatNumber(lNum);
+ return unicode(sRet).replace(' ', u'\u00a0');
+
+
+def isString(oString):
+ """
+ Checks if the object is a string object, hiding difference between python 2 and 3.
+
+ Returns True if it's a string of some kind.
+ Returns False if not.
+ """
+ if sys.version_info[0] >= 3:
+ return isinstance(oString, str);
+ return isinstance(oString, basestring); # pylint: disable=undefined-variable
+
+
+def hasNonAsciiCharacters(sText):
+ """
+ Returns True is specified string has non-ASCII characters, False if ASCII only.
+ """
+ if isString(sText):
+ for ch in sText:
+ if ord(ch) >= 128:
+ return True;
+ else:
+ # Probably byte array or some such thing.
+ for ch in sText:
+ if ch >= 128 or ch < 0:
+ return True;
+ return False;
+
+
+#
+# Unpacking.
+#
+
+def unpackZipFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
+ # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
+ """
+ Worker for unpackFile that deals with ZIP files, same function signature.
+ """
+ import zipfile
+ if fnError is None:
+ fnError = fnLog;
+
+ fnLog('Unzipping "%s" to "%s"...' % (sArchive, sDstDir));
+
+ # Open it.
+ try: oZipFile = zipfile.ZipFile(sArchive, 'r'); # pylint: disable=consider-using-with
+ except Exception as oXcpt:
+ fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
+ return None;
+
+ # Extract all members.
+ asMembers = [];
+ try:
+ for sMember in oZipFile.namelist():
+ if fnFilter is None or fnFilter(sMember) is not False:
+ if sMember.endswith('/'):
+ os.makedirs(os.path.join(sDstDir, sMember.replace('/', os.path.sep)), 0x1fd); # octal: 0775 (python 3/2)
+ else:
+ oZipFile.extract(sMember, sDstDir);
+ asMembers.append(os.path.join(sDstDir, sMember.replace('/', os.path.sep)));
+ except Exception as oXcpt:
+ fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
+ asMembers = None;
+
+ # close it.
+ try: oZipFile.close();
+ except Exception as oXcpt:
+ fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
+ asMembers = None;
+
+ return asMembers;
+
+
+## Set if we've replaced tarfile.copyfileobj with __mytarfilecopyfileobj already.
+g_fTarCopyFileObjOverriddend = False;
+
+def __mytarfilecopyfileobj(src, dst, length = None, exception = OSError, bufsize = None):
+ """ tarfile.copyfileobj with different buffer size (16384 is slow on windows). """
+ _ = bufsize;
+ if length is None:
+ __myshutilcopyfileobj(src, dst, g_cbGoodBufferSize);
+ elif length > 0:
+ cFull, cbRemainder = divmod(length, g_cbGoodBufferSize);
+ for _ in xrange(cFull):
+ abBuffer = src.read(g_cbGoodBufferSize);
+ dst.write(abBuffer);
+ if len(abBuffer) != g_cbGoodBufferSize:
+ raise exception('unexpected end of source file');
+ if cbRemainder > 0:
+ abBuffer = src.read(cbRemainder);
+ dst.write(abBuffer);
+ if len(abBuffer) != cbRemainder:
+ raise exception('unexpected end of source file');
+
+
+def unpackTarFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
+ # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
+ """
+ Worker for unpackFile that deals with tarballs, same function signature.
+ """
+ import shutil;
+ import tarfile;
+ if fnError is None:
+ fnError = fnLog;
+
+ fnLog('Untarring "%s" to "%s"...' % (sArchive, sDstDir));
+
+ #
+ # Default buffer sizes of 16384 bytes is causing too many syscalls on Windows.
+ # 60%+ speedup for python 2.7 and 50%+ speedup for python 3.5, both on windows with PDBs.
+ # 20%+ speedup for python 2.7 and 15%+ speedup for python 3.5, both on windows skipping PDBs.
+ #
+ if True is True: # pylint: disable=comparison-with-itself
+ __installShUtilHacks(shutil);
+ global g_fTarCopyFileObjOverriddend;
+ if g_fTarCopyFileObjOverriddend is False:
+ g_fTarCopyFileObjOverriddend = True;
+ #if sys.hexversion < 0x03060000:
+ tarfile.copyfileobj = __mytarfilecopyfileobj;
+
+ #
+ # Open it.
+ #
+ # Note! We not using 'r:*' because we cannot allow seeking compressed files!
+ # That's how we got a 13 min unpack time for VBoxAll on windows (hardlinked pdb).
+ #
+ try:
+ if sys.hexversion >= 0x03060000:
+ oTarFile = tarfile.open(sArchive, 'r|*', # pylint: disable=consider-using-with
+ bufsize = g_cbGoodBufferSize, copybufsize = g_cbGoodBufferSize);
+ else:
+ oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize); # pylint: disable=consider-using-with
+ except Exception as oXcpt:
+ fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
+ return None;
+
+ # Extract all members.
+ asMembers = [];
+ try:
+ for oTarInfo in oTarFile:
+ try:
+ if fnFilter is None or fnFilter(oTarInfo.name) is not False:
+ if oTarInfo.islnk():
+ # Links are trouble, especially on Windows. We must avoid the falling that will end up seeking
+ # in the compressed tar stream. So, fall back on shutil.copy2 instead.
+ sLinkFile = os.path.join(sDstDir, oTarInfo.name.rstrip('/').replace('/', os.path.sep));
+ sLinkTarget = os.path.join(sDstDir, oTarInfo.linkname.rstrip('/').replace('/', os.path.sep));
+ sParentDir = os.path.dirname(sLinkFile);
+ try: os.unlink(sLinkFile);
+ except: pass;
+ if sParentDir and not os.path.exists(sParentDir):
+ os.makedirs(sParentDir);
+ try: os.link(sLinkTarget, sLinkFile);
+ except: shutil.copy2(sLinkTarget, sLinkFile);
+ else:
+ if oTarInfo.isdir():
+ # Just make sure the user (we) got full access to dirs. Don't bother getting it 100% right.
+ oTarInfo.mode |= 0x1c0; # (octal: 0700)
+ oTarFile.extract(oTarInfo, sDstDir);
+ asMembers.append(os.path.join(sDstDir, oTarInfo.name.replace('/', os.path.sep)));
+ except Exception as oXcpt:
+ fnError('Error unpacking "%s" member "%s" into "%s": %s' % (sArchive, oTarInfo.name, sDstDir, oXcpt));
+ for sAttr in [ 'name', 'linkname', 'type', 'mode', 'size', 'mtime', 'uid', 'uname', 'gid', 'gname' ]:
+ fnError('Info: %8s=%s' % (sAttr, getattr(oTarInfo, sAttr),));
+ for sFn in [ 'isdir', 'isfile', 'islnk', 'issym' ]:
+ fnError('Info: %8s=%s' % (sFn, getattr(oTarInfo, sFn)(),));
+ asMembers = None;
+ break;
+ except Exception as oXcpt:
+ fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
+ asMembers = None;
+
+ #
+ # Finally, close it.
+ #
+ try: oTarFile.close();
+ except Exception as oXcpt:
+ fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
+ asMembers = None;
+
+ return asMembers;
+
+
+def unpackFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
+ # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
+ """
+ Unpacks the given file if it has a know archive extension, otherwise do
+ nothing.
+
+ fnLog & fnError both take a string parameter.
+
+ fnFilter takes a member name (string) and returns True if it's included
+ and False if excluded.
+
+ Returns list of the extracted files (full path) on success.
+ Returns empty list if not a supported archive format.
+ Returns None on failure. Raises no exceptions.
+ """
+ sBaseNameLower = os.path.basename(sArchive).lower();
+
+ #
+ # Zip file?
+ #
+ if sBaseNameLower.endswith('.zip'):
+ return unpackZipFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
+
+ #
+ # Tarball?
+ #
+ if sBaseNameLower.endswith('.tar') \
+ or sBaseNameLower.endswith('.tar.gz') \
+ or sBaseNameLower.endswith('.tgz') \
+ or sBaseNameLower.endswith('.tar.bz2'):
+ return unpackTarFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
+
+ #
+ # Cannot classify it from the name, so just return that to the caller.
+ #
+ fnLog('Not unpacking "%s".' % (sArchive,));
+ return [];
+
+
+#
+# Misc.
+#
+def areBytesEqual(oLeft, oRight):
+ """
+ Compares two byte arrays, strings or whatnot.
+
+ returns true / false accordingly.
+ """
+
+ # If both are None, consider them equal (bogus?):
+ if oLeft is None and oRight is None:
+ return True;
+
+ # If just one is None, they can't match:
+ if oLeft is None or oRight is None:
+ return False;
+
+ # If both have the same type, use the compare operator of the class:
+ if type(oLeft) is type(oRight):
+ #print('same type: %s' % (oLeft == oRight,));
+ return oLeft == oRight;
+
+ # On the offchance that they're both strings, but of different types.
+ if isString(oLeft) and isString(oRight):
+ #print('string compare: %s' % (oLeft == oRight,));
+ return oLeft == oRight;
+
+ #
+ # See if byte/buffer stuff that can be compared directory. If not convert
+ # strings to bytes.
+ #
+ # Note! For 2.x, we must convert both sides to the buffer type or the
+ # comparison may fail despite it working okay in test cases.
+ #
+ if sys.version_info[0] >= 3:
+ if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
+ return oLeft == oRight;
+
+ if isString(oLeft):
+ try: oLeft = bytes(oLeft, 'utf-8');
+ except: pass;
+ if isString(oRight):
+ try: oRight = bytes(oRight, 'utf-8');
+ except: pass;
+ else:
+ if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
+ if isinstance(oLeft, bytearray):
+ oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
+ else:
+ oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
+ #print('buf/byte #1 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
+ return oLeft == oRight;
+
+ if isString(oLeft):
+ try: oLeft = bytearray(oLeft, 'utf-8'); # pylint: disable=redefined-variable-type
+ except: pass;
+ if isString(oRight):
+ try: oRight = bytearray(oRight, 'utf-8'); # pylint: disable=redefined-variable-type
+ except: pass;
+
+ # Check if we now have the same type for both:
+ if type(oLeft) is type(oRight):
+ #print('same type now: %s' % (oLeft == oRight,));
+ return oLeft == oRight;
+
+ # Check if we now have buffer/memoryview vs bytes/bytesarray again.
+ if sys.version_info[0] >= 3:
+ if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
+ return oLeft == oRight;
+ else:
+ if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
+ if isinstance(oLeft, bytearray):
+ oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
+ else:
+ oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
+ #print('buf/byte #2 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
+ return oLeft == oRight;
+
+ # Do item by item comparison:
+ if len(oLeft) != len(oRight):
+ #print('different length: %s vs %s' % (len(oLeft), len(oRight)));
+ return False;
+ i = len(oLeft);
+ while i > 0:
+ i = i - 1;
+
+ iElmLeft = oLeft[i];
+ if not isinstance(iElmLeft, int) and not isinstance(iElmLeft, long):
+ iElmLeft = ord(iElmLeft);
+
+ iElmRight = oRight[i];
+ if not isinstance(iElmRight, int) and not isinstance(iElmRight, long):
+ iElmRight = ord(iElmRight);
+
+ if iElmLeft != iElmRight:
+ #print('element %d differs: %x %x' % (i, iElmLeft, iElmRight,));
+ return False;
+ return True;
+
+
+def calcCrc32OfFile(sFile):
+ """
+ Simple helper for calculating the CRC32 of a file.
+
+ Throws stuff if the file cannot be opened or read successfully.
+ """
+ import zlib;
+
+ uCrc32 = 0;
+ with open(sFile, 'rb') as oFile: # pylint: disable=unspecified-encoding
+ while True:
+ oBuf = oFile.read(1024 * 1024);
+ if not oBuf:
+ break
+ uCrc32 = zlib.crc32(oBuf, uCrc32);
+
+ return uCrc32 % 2**32;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+# pylint: disable=undefined-variable
+class BuildCategoryDataTestCase(unittest.TestCase):
+ def testIntervalSeconds(self):
+ self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(3600)), (3600, None));
+ self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(1209438593)), (1209438593, None));
+ self.assertEqual(parseIntervalSeconds('123'), (123, None));
+ self.assertEqual(parseIntervalSeconds(123), (123, None));
+ self.assertEqual(parseIntervalSeconds(99999999999), (99999999999, None));
+ self.assertEqual(parseIntervalSeconds(''), (0, 'Empty interval string.'));
+ self.assertEqual(parseIntervalSeconds('1X2'), (3, 'Unknown unit "X".'));
+ self.assertEqual(parseIntervalSeconds('1 Y3'), (4, 'Unknown unit "Y".'));
+ self.assertEqual(parseIntervalSeconds('1 Z 4'), (5, 'Unknown unit "Z".'));
+ self.assertEqual(parseIntervalSeconds('1 hour 2m 5second'), (3725, None));
+ self.assertEqual(parseIntervalSeconds('1 hour,2m ; 5second'), (3725, None));
+
+ def testZuluNormalization(self):
+ self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:34:25.000000000Z'), '2011-01-02T03:34:25.000000000Z');
+ self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:04:25-0030'), '2011-01-02T03:34:25.000000000Z');
+ self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:04:25+0030'), '2011-01-02T02:34:25.000000000Z');
+ self.assertEqual(normalizeIsoTimestampToZulu('2020-03-20T20:47:39,832312863+01:00'), '2020-03-20T19:47:39.832312000Z');
+ self.assertEqual(normalizeIsoTimestampToZulu('2020-03-20T20:47:39,832312863-02:00'), '2020-03-20T22:47:39.832312000Z');
+
+ def testHasNonAsciiChars(self):
+ self.assertEqual(hasNonAsciiCharacters(''), False);
+ self.assertEqual(hasNonAsciiCharacters('asdfgebASDFKJ@#$)(!@#UNASDFKHB*&$%&)@#(!)@(#!(#$&*#$&%*Y@#$IQWN---00;'), False);
+ self.assertEqual(hasNonAsciiCharacters('\x80 '), True);
+ self.assertEqual(hasNonAsciiCharacters('\x79 '), False);
+ self.assertEqual(hasNonAsciiCharacters(u'12039889y!@#$%^&*()0-0asjdkfhoiuyweasdfASDFnvV'), False);
+ self.assertEqual(hasNonAsciiCharacters(u'\u0079'), False);
+ self.assertEqual(hasNonAsciiCharacters(u'\u0080'), True);
+ self.assertEqual(hasNonAsciiCharacters(u'\u0081 \u0100'), True);
+ self.assertEqual(hasNonAsciiCharacters(b'\x20\x20\x20'), False);
+ self.assertEqual(hasNonAsciiCharacters(b'\x20\x81\x20'), True);
+
+ def testAreBytesEqual(self):
+ self.assertEqual(areBytesEqual(None, None), True);
+ self.assertEqual(areBytesEqual(None, ''), False);
+ self.assertEqual(areBytesEqual('', ''), True);
+ self.assertEqual(areBytesEqual('1', '1'), True);
+ self.assertEqual(areBytesEqual('12345', '1234'), False);
+ self.assertEqual(areBytesEqual('1234', '1234'), True);
+ self.assertEqual(areBytesEqual('1234', b'1234'), True);
+ self.assertEqual(areBytesEqual(b'1234', b'1234'), True);
+ self.assertEqual(areBytesEqual(b'1234', '1234'), True);
+ self.assertEqual(areBytesEqual(b'1234', bytearray([0x31,0x32,0x33,0x34])), True);
+ self.assertEqual(areBytesEqual('1234', bytearray([0x31,0x32,0x33,0x34])), True);
+ self.assertEqual(areBytesEqual(u'1234', bytearray([0x31,0x32,0x33,0x34])), True);
+ self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x33,0x34])), True);
+ self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), '1224'), False);
+ self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x32,0x34])), False);
+ if sys.version_info[0] >= 3:
+ pass;
+ else:
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
+ bytearray([0x31,0x32,0x33,0x34])), True);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
+ bytearray([0x99,0x32,0x32,0x34])), False);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
+ buffer(bytearray([0x31,0x32,0x33,0x34,0x34]), 0, 4)), True);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
+ buffer(bytearray([0x99,0x32,0x33,0x34,0x34]), 0, 4)), False);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), b'1234'), True);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), '1234'), True);
+ self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), u'1234'), True);
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
diff --git a/src/VBox/ValidationKit/common/webutils.py b/src/VBox/ValidationKit/common/webutils.py
new file mode 100755
index 00000000..ebe40a4d
--- /dev/null
+++ b/src/VBox/ValidationKit/common/webutils.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+# $Id: webutils.py $
+
+"""
+Common Web Utility Functions.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import os;
+import sys;
+import unittest;
+
+# Python 3 hacks:
+if sys.version_info[0] < 3:
+ from urllib2 import quote as urllib_quote; # pylint: disable=import-error,no-name-in-module
+ from urllib import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
+ from urllib2 import ProxyHandler as urllib_ProxyHandler; # pylint: disable=import-error,no-name-in-module
+ from urllib2 import build_opener as urllib_build_opener; # pylint: disable=import-error,no-name-in-module
+else:
+ from urllib.parse import quote as urllib_quote; # pylint: disable=import-error,no-name-in-module
+ from urllib.parse import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
+ from urllib.request import ProxyHandler as urllib_ProxyHandler; # pylint: disable=import-error,no-name-in-module
+ from urllib.request import build_opener as urllib_build_opener; # pylint: disable=import-error,no-name-in-module
+
+# Validation Kit imports.
+from common import utils;
+
+
+def escapeElem(sText):
+ """
+ Escapes special character to HTML-safe sequences.
+ """
+ sText = sText.replace('&', '&amp;')
+ sText = sText.replace('<', '&lt;')
+ return sText.replace('>', '&gt;')
+
+def escapeAttr(sText):
+ """
+ Escapes special character to HTML-safe sequences.
+ """
+ sText = sText.replace('&', '&amp;')
+ sText = sText.replace('<', '&lt;')
+ sText = sText.replace('>', '&gt;')
+ return sText.replace('"', '&quot;')
+
+def escapeElemToStr(oObject):
+ """
+ Stringifies the object and hands it to escapeElem.
+ """
+ if utils.isString(oObject):
+ return escapeElem(oObject);
+ return escapeElem(str(oObject));
+
+def escapeAttrToStr(oObject):
+ """
+ Stringifies the object and hands it to escapeAttr. May return unicode string.
+ """
+ if utils.isString(oObject):
+ return escapeAttr(oObject);
+ return escapeAttr(str(oObject));
+
+def escapeAttrJavaScriptStringDQ(sText):
+ """ Escapes a javascript string that is to be emitted between double quotes. """
+ if '"' not in sText:
+ chMin = min(sText);
+ if ord(chMin) >= 0x20:
+ return sText;
+
+ sRet = '';
+ for ch in sText:
+ if ch == '"':
+ sRet += '\\"';
+ elif ord(ch) >= 0x20:
+ sRet += ch;
+ elif ch == '\n':
+ sRet += '\\n';
+ elif ch == '\r':
+ sRet += '\\r';
+ elif ch == '\t':
+ sRet += '\\t';
+ else:
+ sRet += '\\x%02x' % (ch,);
+ return sRet;
+
+def quoteUrl(sText):
+ """
+ See urllib.quote().
+ """
+ return urllib_quote(sText);
+
+def encodeUrlParams(dParams):
+ """
+ See urllib.urlencode().
+ """
+ return urllib_urlencode(dParams, doseq=True)
+
+def hasSchema(sUrl):
+ """
+ Checks if the URL has a schema (e.g. http://) or is file/server relative.
+ Returns True if schema is present, False if not.
+ """
+ iColon = sUrl.find(':');
+ if iColon > 0:
+ sSchema = sUrl[0:iColon];
+ if len(sSchema) >= 2 and len(sSchema) < 16 and sSchema.islower() and sSchema.isalpha():
+ return True;
+ return False;
+
+def getFilename(sUrl):
+ """
+ Extracts the filename from the URL.
+ """
+ ## @TODO This isn't entirely correct. Use the urlparser instead!
+ sFilename = os.path.basename(sUrl.replace('/', os.path.sep));
+ return sFilename;
+
+
+def downloadFile(sUrlFile, sDstFile, sLocalPrefix, fnLog, fnError = None, fNoProxies=True):
+ """
+ Downloads the given file if an URL is given, otherwise assume it's
+ something on the build share and copy it from there.
+
+ Raises no exceptions, returns log + success indicator instead.
+
+ Note! This method may use proxies configured on the system and the
+ http_proxy, ftp_proxy, no_proxy environment variables.
+
+ """
+ if fnError is None:
+ fnError = fnLog;
+
+ if sUrlFile.startswith('http://') \
+ or sUrlFile.startswith('https://') \
+ or sUrlFile.startswith('ftp://'):
+ # Download the file.
+ fnLog('Downloading "%s" to "%s"...' % (sUrlFile, sDstFile));
+ try:
+ ## @todo We get 404.html content instead of exceptions here, which is confusing and should be addressed.
+ if not fNoProxies:
+ oOpener = urllib_build_opener();
+ else:
+ oOpener = urllib_build_opener(urllib_ProxyHandler(proxies = {} ));
+ oSrc = oOpener.open(sUrlFile);
+ oDst = utils.openNoInherit(sDstFile, 'wb');
+ oDst.write(oSrc.read());
+ oDst.close();
+ oSrc.close();
+ except Exception as oXcpt:
+ fnError('Error downloading "%s" to "%s": %s' % (sUrlFile, sDstFile, oXcpt));
+ return False;
+ else:
+ # Assumes file from the build share.
+ if sUrlFile.startswith('file:///'):
+ sSrcPath = sUrlFile[7:];
+ elif sUrlFile.startswith('file://'):
+ sSrcPath = sUrlFile[6:];
+ elif os.path.isabs(sUrlFile):
+ sSrcPath = sUrlFile;
+ else:
+ sSrcPath = os.path.join(sLocalPrefix, sUrlFile);
+ fnLog('Copying "%s" to "%s"...' % (sSrcPath, sDstFile));
+ try:
+ utils.copyFileSimple(sSrcPath, sDstFile);
+ except Exception as oXcpt:
+ fnError('Error copying "%s" to "%s": %s' % (sSrcPath, sDstFile, oXcpt));
+ return False;
+
+ return True;
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class CommonUtilsTestCase(unittest.TestCase):
+ def testHasSchema(self):
+ self.assertTrue(hasSchema('http://www.oracle.com/'));
+ self.assertTrue(hasSchema('https://virtualbox.com/'));
+ self.assertFalse(hasSchema('://virtualbox.com/'));
+ self.assertFalse(hasSchema('/usr/bin'));
+ self.assertFalse(hasSchema('usr/bin'));
+ self.assertFalse(hasSchema('bin'));
+ self.assertFalse(hasSchema('C:\\WINNT'));
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html
new file mode 100644
index 00000000..f5501717
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.html
@@ -0,0 +1,1354 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+<title>AutomaticTestingRevamp.txt</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: AutomaticTestingRevamp.html $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document">
+
+
+<div class="section" id="revamp-of-automatic-virtualbox-testing">
+<h1>Revamp of Automatic VirtualBox Testing</h1>
+<div class="section" id="introduction">
+<h2>Introduction</h2>
+<p>This is the design document for a revamped automatic testing framework.
+The revamp aims at replacing the current tinderbox based testing by a new
+system that is written from scratch.</p>
+<p>The old system is not easy to work with and was never meant to be used for
+managing tests, after all it just a simple a build manager tailored for
+contiguous building. Modifying the existing tinderbox system to do what
+we want would require fundamental changes that would render it useless as
+a build manager, it would therefore end up as a fork. The amount of work
+required would probably be about the same as writing a new system from
+scratch. Other considerations, such as the license of the tinderbox
+system (MPL) and language it is realized in (Perl), are also in favor of
+doing it from scratch.</p>
+<p>The language envisioned for the new automatic testing framework is Python. This
+is for several reasons:</p>
+<blockquote>
+<ul class="simple">
+<li>The VirtualBox API has Python bindings.</li>
+<li>Python is used quite a bit inside Sun (dunno about Oracle).</li>
+<li>Works relatively well with Apache for the server side bits.</li>
+<li>It is more difficult to produce write-only code in Python (alias the
+we-don't-like-perl argument).</li>
+<li>You don't need to compile stuff.</li>
+</ul>
+</blockquote>
+<p>Note that the author of this document has no special training as a test
+engineer and may therefore be using the wrong terms here and there. The
+primary focus is to express what we need to do in order to improve
+testing.</p>
+<p>This document is written in reStructuredText (rst) which just happens to
+be used by Python, the primary language for this revamp. For more
+information on reStructuredText: <a class="reference external" href="http://docutils.sourceforge.net/rst.html">http://docutils.sourceforge.net/rst.html</a></p>
+</div>
+</div>
+<div class="section" id="definitions-glossary">
+<h1>Definitions / Glossary</h1>
+<dl class="docutils">
+<dt>sub-test driver</dt>
+<dd>A set of test cases that can be used by more than one test driver. Could
+also be called a test unit, in the pascal sense of unit, if it wasn't so
+easily confused with 'unit test'.</dd>
+<dt>test</dt>
+<dd>This is somewhat ambiguous and this document try avoid using it where
+possible. When used it normally refers to doing testing by executing one or
+more testcases.</dd>
+<dt>test case</dt>
+<dd>A set of inputs, test programs and expected results. It validates system
+requirements and generates a pass or failed status. A basic unit of testing.
+Note that we use the term in a rather broad sense.</dd>
+<dt>test driver</dt>
+<dd>A program/script used to execute a test. Also known as a test harness.
+Generally abbreviated 'td'. It can have sub-test drivers.</dd>
+<dt>test manager</dt>
+<dd>Software managing the automatic testing. This is a web application that runs
+on a dedicated server (tindertux).</dd>
+<dt>test set</dt>
+<dd>The output of testing activity. Logs, results, ++. Our usage of this should
+probably be renamed to 'test run'.</dd>
+<dt>test group</dt>
+<dd>A collection of related test cases.</dd>
+<dt>testbox</dt>
+<dd>A computer that does testing.</dd>
+<dt>testbox script</dt>
+<dd>Script executing orders from the test manager on a testbox. Started
+automatically upon bootup.</dd>
+<dt>testing</dt>
+<dd>todo</dd>
+<dt>TODO: Check that we've got all this right and make them more exact</dt>
+<dd>where possible.</dd>
+</dl>
+<p>See also <a class="reference external" href="http://encyclopedia2.thefreedictionary.com/testing%20types">http://encyclopedia2.thefreedictionary.com/testing%20types</a>
+and <a class="reference external" href="http://www.aptest.com/glossary.html">http://www.aptest.com/glossary.html</a> .</p>
+</div>
+<div class="section" id="objectives">
+<h1>Objectives</h1>
+<blockquote>
+<ul class="simple">
+<li>A scalable test manager (&gt;200 testboxes).</li>
+<li>Optimize the web user interface (WUI) for typical workflows and analysis.</li>
+<li>Efficient and flexibile test configuration.</li>
+<li>Import test result from other test systems (logo testing, VDI, ++).</li>
+<li>Easy to add lots of new testscripts.</li>
+<li>Run tests locally without a manager.</li>
+<li>Revamp a bit at the time.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="the-testbox-side">
+<h1>The Testbox Side</h1>
+<p>Each testbox has a unique name corresponding to its DNS zone entry. When booted
+a testbox script is started automatically. This script will query the test
+manager for orders and execute them. The core order downloads and executes a
+test driver with parameters (configuration) from the server. The test driver
+does all the necessary work for executing the test. In a typical VirtualBox
+test this means picking a build, installing it, configuring VMs, running the
+test VMs, collecting the results, submitting them to the server, and finally
+cleaning up afterwards.</p>
+<p>The testbox environment which the test drivers are executed in will have a
+number of environment variables for determining location of the source images
+and other test data, scratch space, test set id, server URL, and so on and so
+forth.</p>
+<p>On startup, the testbox script will look for crash dumps and similar on
+systems where this is possible. If any sign of a crash is found, it will
+put any dumps and reports in the upload directory and inform the test
+manager before reporting for duty. In order to generate the proper file
+names and report the crash in the right test set as well as prevent
+reporting crashes unrelated to automatic testing, the testbox script will
+keep information (test set id, ++) in a separate scratch directory
+(${TESTBOX_PATH_SCRATCH}/../testbox) and make sure it is synced to the
+disk (both files and directories).</p>
+<p>After checking for crashes, the testbox script will clean up any previous test
+which might be around. This involves first invoking the test script in cleanup
+mode and the wiping the scratch space.</p>
+<p>When reporting for duty the script will submit information about the host: OS
+name, OS version, OS bitness, CPU vendor, total number of cores, VT-x support,
+AMD-V support, amount of memory, amount of scratch space, and anything else that
+can be found useful for scheduling tests or filtering test configurations.</p>
+<div class="section" id="testbox-script-orders">
+<h2>Testbox Script Orders</h2>
+<p>The orders are kept in a queue on the server and the testbox script will fetch
+them one by one. Orders that cannot be executed at the moment will be masked in
+the query from the testbox.</p>
+<dl class="docutils">
+<dt>Execute Test Driver</dt>
+<dd>Downloads and executes the a specified test driver with the given
+configuration (arguments). Only one test driver can be executed at a time.
+The server can specify more than one ZIP file to be downloaded and unpacked
+before executing the test driver. The testbox script may cache these zip
+files using http time stamping.</dd>
+<dt>Abort Test Driver</dt>
+<dd>Aborts the current test driver. This will drop a hint to the driver and give
+it 60 seconds to shut down the normal way. If that fails, the testbox script
+will kill the driver processes (SIGKILL or equivalent), invoke the
+testdriver in cleanup mode, and finally wipe the scratch area. Should either
+of the last two steps fail in some way, the testbox will be rebooted.</dd>
+<dt>Idle</dt>
+<dd>Ask again in X seconds, where X is specified by the server.</dd>
+<dt>Reboot</dt>
+<dd>Reboot the testbox. If a test driver is current running, an attempt at
+aborting it (Abort Test Driver) will be made first.</dd>
+<dt>Update</dt>
+<dd>Updates the testbox script. The order includes a server relative path to the
+new testbox script. This can only be executed when no test driver is
+currently being executed.</dd>
+</dl>
+</div>
+<div class="section" id="testbox-environment-variables">
+<h2>Testbox Environment: Variables</h2>
+<dl class="docutils">
+<dt>COMSPEC</dt>
+<dd>This will be set to C:WindowsSystem32cmd.exe on Windows.</dd>
+<dt>PATH</dt>
+<dd>This will contain the kBuild binary directory for the host platform.</dd>
+<dt>SHELL</dt>
+<dd>This will be set to point to kmk_ash(.exe) on all platforms.</dd>
+<dt>TESTBOX_NAME</dt>
+<dd>The testbox name.
+This is not required by the local reporter.</dd>
+<dt>TESTBOX_PATH_BUILDS</dt>
+<dd>The absolute path to where the build repository can be found. This should be
+a read only mount when possible.</dd>
+<dt>TESTBOX_PATH_RESOURCES</dt>
+<dd>The absolute path to where static test resources like ISOs and VDIs can be
+found. The test drivers knows the layout of this. This should be a read only
+mount when possible.</dd>
+<dt>TESTBOX_PATH_SCRATCH</dt>
+<dd>The absolute path to the scratch space. This is the current directory when
+starting the test driver. It will be wiped automatically after executing the
+test.
+(Envisioned as ${TESTBOX_PATH_SCRIPTS}/../scratch and that
+${TESTBOX_PATH_SCRATCH}/ will be automatically wiped by the testbox script.)</dd>
+<dt>TESTBOX_PATH_SCRIPTS</dt>
+<dd>The absolute path to the test driver and the other files that was unzipped
+together with it. This is also where the test-driver-abort file will be put.
+(Envisioned as ${TESTBOX_PATH_SCRATCH}/../driver, see above.)</dd>
+<dt>TESTBOX_PATH_UPLOAD</dt>
+<dd>The absolute path to the upload directory for the testbox. This is for
+putting VOBs, PNGs, core dumps, crash dumps, and such on. The files should be
+bzipped or zipped if they aren't compress already. The names should contain
+the testbox and test set ID.</dd>
+<dt>TESTBOX_REPORTER</dt>
+<dd>The name of the test reporter back end. If not present, it will default to
+the local reporter.</dd>
+<dt>TESTBOX_TEST_SET_ID</dt>
+<dd>The test set ID if we're running.
+This is not required by the local reporter.</dd>
+<dt>TESTBOX_MANAGER_URL</dt>
+<dd>The URL to the test manager.
+This is not required by the local reporter.</dd>
+<dt>TESTBOX_XYZ</dt>
+<dd>There will probably be some more of these.</dd>
+</dl>
+</div>
+<div class="section" id="testbox-environment-core-utilities">
+<h2>Testbox Environment: Core Utilities</h2>
+<p>The testbox will not provide the typical unix /bin and /usr/bin utilities. In
+other words, cygwin will not be used on Windows!</p>
+<p>The testbox will provide the unixy utilities that ships with kBuild and possibly
+some additional ones from tools/<em>.</em>/bin in the VirtualBox tree (wget, unzip,
+zip, and so on). The test drivers will avoid invoking any of these utilities
+directly and instead rely on generic utility methods in the test driver
+framework. That way we can more easily reimplement the functionality of the
+core utilities and drop the dependency on them. It also allows us to quickly
+work around platform specific oddities and bugs.</p>
+</div>
+<div class="section" id="test-drivers">
+<h2>Test Drivers</h2>
+<p>The test drivers are programs that will do the actual testing. In addition to
+run under the testbox script, they can be executed in the VirtualBox development
+environment. This is important for bug analysis and for simplifying local
+testing by the developers before committing changes. It also means the test
+drivers can be developed locally in the VirtualBox development environment.</p>
+<p>The main difference between executing a driver under the testbox script and
+running it manually is that there is no test manager in the latter case. The
+test result reporter will not talk to the server, but report things to a local
+log file and/or standard out/err. When invoked manually, all the necessary
+arguments will need to be specified by hand of course - it should be possible
+to extract them from a test set as well.</p>
+<p>For the early implementation stages, an implementation of the reporter interface
+that talks to the tinderbox base test manager will be needed. This will be
+dropped later on when a new test manager is ready.</p>
+<p>As hinted at in other sections, there will be a common framework
+(libraries/packages/classes) for taking care of the tedious bits that every
+test driver needs to do. Sharing code is essential to easing test driver
+development as well as reducing their complexity. The framework will contain:</p>
+<blockquote>
+<ul class="simple">
+<li>A generic way of submitting output. This will be a generic interface with
+multiple implementation, the TESTBOX_REPORTER environment variable
+will decide which of them to use. The interface will have very specific
+methods to allow the reporter to do a best possible job in reporting the
+results to the test manager.</li>
+<li><dl class="first docutils">
+<dt>Helpers for typical tasks, like:</dt>
+<dd><ul class="first last">
+<li>Copying files.</li>
+<li>Deleting files, directory trees and scratch space.</li>
+<li>Unzipping files.</li>
+<li>Creating ISOs</li>
+<li>And such things.</li>
+</ul>
+</dd>
+</dl>
+</li>
+<li>Helpers for installing and uninstalling VirtualBox.</li>
+<li>Helpers for defining VMs. (The VBox API where available.)</li>
+<li>Helpers for controlling VMs. (The VBox API where available.)</li>
+</ul>
+</blockquote>
+<p>The VirtualBox bits will be separate from the more generic ones, simply because
+this is cleaner it will allow us to reuse the system for testing other products.</p>
+<p>The framework will be packaged in a zip file other than the test driver so we
+don't waste time and space downloading the same common code.</p>
+<p>The test driver will poll for the file
+${TESTBOX_PATH_SCRIPTS}/test-driver-abort and abort all testing when it sees it.</p>
+<p>The test driver can be invoked in three modes: execute, help and cleanup. The
+default is execute mode, the help shows an configuration summary and the cleanup
+is for cleaning up after a reboot or aborted run. The latter is done by the
+testbox script on startup and after abort - the driver is expected to clean up
+by itself after a normal run.</p>
+</div>
+</div>
+<div class="section" id="the-server-side">
+<h1>The Server Side</h1>
+<p>The server side will be implemented using a webserver (apache), a database
+(postgres) and cgi scripts (Python). In addition a cron job (Python) running
+once a minute will generate static html for frequently used pages and maybe
+execute some other tasks for driving the testing forwards. The order queries
+from the testbox script is the primary driving force in the system. The total
+makes up the test manager.</p>
+<p>The test manager can be split up into three rough parts:</p>
+<blockquote>
+<ul class="simple">
+<li>Configuration (of tests, testgroups and testboxes).</li>
+<li>Execution (of tests, collecting and organizing the output).</li>
+<li>Analysis (of test output, mostly about presentation).</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="test-manager-requirements">
+<h1>Test Manager: Requirements</h1>
+<p>List of requirements:</p>
+<blockquote>
+<ul class="simple">
+<li>Two level testing - L1 quick smoke tests and L2 longer tests performed on
+builds passing L1. (Klaus (IIRC) meant this could be realized using
+test dependency.)</li>
+<li>Black listing builds (by revision or similar) known to be bad.</li>
+<li>Distinguish between build types so we can do a portion of the testing with
+strict builds.</li>
+<li>Easy to re-configure build source for testing different branch or for
+testing a release candidate. (Directory based is fine.)</li>
+<li>Useful to be able to partition testboxes (run specific builds on some
+boxes, let an engineer have a few boxes for a while).</li>
+<li>Interaction with ILOM/...: reset systems.</li>
+<li>Be able to suspend testing on selected testboxes when doing maintenance
+(where automatically resuming testing on reboot is undesired) or similar
+activity.</li>
+<li>Abort testing on selected testboxes.</li>
+<li>Scheduling of tests requiring more than one testbox.</li>
+<li>Scheduling of tests that cannot be executing concurrently on several
+machines because of some global resource like an iSCSI target.</li>
+<li>Jump the scheduling queue. Scheduling of specified test the next time a
+testbox is available (optionally specifying which testbox to schedule it
+on).</li>
+<li><dl class="first docutils">
+<dt>Configure tests with variable configuration to get better coverage. Two modes:</dt>
+<dd><ul class="first last">
+<li>TM generates the permutations based on one or more sets of test script arguments.</li>
+<li>Each configuration permutation is specified manually.</li>
+</ul>
+</dd>
+</dl>
+</li>
+<li>Test specification needs to be flexible (select tests, disable test, test
+scheduling (run certain tests nightly), ... ).</li>
+<li>Test scheduling by hour+weekday and by priority.</li>
+<li>Test dependencies (test A depends on test B being successful).</li>
+<li>Historize all configuration data, in particular test configs (permutations
+included) and testboxes.</li>
+<li>Test sets has at a minimum a build reference, a testbox reference and a
+primary log associated with it.</li>
+<li><dl class="first docutils">
+<dt>Test sets stores further result as a recursive collection of:</dt>
+<dd><ul class="first last">
+<li>hierarchical subtest name (slash sep)</li>
+<li>test parameters / config</li>
+<li>bool fail/succ</li>
+<li>attributes (typed?)</li>
+<li>test time</li>
+<li>e.g. throughput</li>
+<li>subresults</li>
+<li>log</li>
+<li>screenshots, video,...</li>
+</ul>
+</dd>
+</dl>
+</li>
+<li>The test sets database structure needs to designed such that data mining
+can be done in an efficient manner.</li>
+<li>Presentation/analysis: graphs!, categorize bugs, columns reorganizing
+grouped by test (hierarchical), overviews, result for last day.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="test-manager-configuration">
+<h1>Test Manager: Configuration</h1>
+<div class="section" id="testboxes">
+<h2>Testboxes</h2>
+<p>Configuration of testboxes doesn't involve much work normally. A testbox
+is added manually to the test manager by entering the DNS entry and/or IP
+address (the test manager resolves the missing one when necessary) as well as
+the system UUID (when obtainable - should be displayed by the testbox script
+installer). Queries from unregistered testboxes will be declined as a kind of
+security measure, the incident should be logged in the webserver log if
+possible. In later dealings with the client the System UUID will be the key
+identifier. It's permittable for the IP address to change when the testbox
+isn't online, but not while testing (just imagine live migration tests and
+network tests). Ideally, the testboxes should not change IP address.</p>
+<p>The testbox edit function must allow changing the name and system UUID.</p>
+<p>One further idea for the testbox configuration is indicating what they are
+capable of to filter out tests and test configurations that won't work on that
+testbox. To examplify this take the ACP2 installation test. If the test
+manager does not make sure the testbox have VT-x or AMD-v capabilities, the test
+is surely going to fail. Other testbox capabilities would be total number of
+CPU cores, memory size, scratch space. These testbox capabilities should be
+collected automatically on bootup by the testbox script together with OS name,
+OS version and OS bitness.</p>
+<p>A final thought, instead of outright declining all requests from new testboxes,
+we could record the unregistered testboxes with ip, UUID, name, os info and
+capabilities but mark them as inactive. The test operator can then activate
+them on an activation page or edit the testbox or something.</p>
+</div>
+<div class="section" id="testcases">
+<h2>Testcases</h2>
+<p>We use the term testcase for a test.</p>
+</div>
+<div class="section" id="testgroups">
+<h2>Testgroups</h2>
+<p>Testcases are organized into groups. A testcase can be member of more than one
+group. The testcase gets a priority assigned to it in connection with the
+group membership.</p>
+<p>Testgroups are picked up by a testbox partition (aka scheduling group) and a
+prioirty, scheduling time restriction and dependencies on other test groups are
+associated with the assignment. A testgroup can be used by several testbox
+partitions.</p>
+<p>(This used to be called 'testsuites' but was renamed to avoid confusion with
+the VBox Test Suite.)</p>
+</div>
+<div class="section" id="scheduling">
+<h2>Scheduling</h2>
+<p>The initial scheduler will be modelled after what we're doing already on in the
+tinderbox driven testing. It's best described as a best effort continuous
+integration scheduler. Meaning, it will always use the latest build suitable
+for a testcase. It will schedule on a testcase level, using the combined
+priority of the testcase in the test group and the test group with the testbox
+partition, trying to spread the test case argument variation out accordingly
+over the whole scheduilng queue. Which argument variation to start with, is
+not undefined (random would be best).</p>
+<p>Later, we may add other schedulers as needed.</p>
+</div>
+</div>
+<div class="section" id="the-test-manager-database">
+<h1>The Test Manager Database</h1>
+<p>First a general warning:</p>
+<blockquote>
+The guys working on this design are not database experts, web
+programming experts or similar, rather we are low level guys
+who's main job is x86 &amp; AMD64 virtualization. So, please don't
+be too hard on us. :-)</blockquote>
+<p>A logical table layout can be found in TestManagerDatabaseMap.png (created by
+Oracle SQL Data Modeler, stored in TestManagerDatabase.dmd). The physical
+database layout can be found in TestManagerDatabaseInit.pgsql postgreSQL
+script. The script is commented.</p>
+<div class="section" id="data-history">
+<h2>Data History</h2>
+<p>We need to somehow track configuration changes over time. We also need to
+be able to query the exact configuration a test set was run with so we can
+understand and make better use of the results.</p>
+<p>There are different techniques for archiving this, one is tuple-versioning
+( <a class="reference external" href="http://en.wikipedia.org/wiki/Tuple-versioning">http://en.wikipedia.org/wiki/Tuple-versioning</a> ), another is log trigger
+( <a class="reference external" href="http://en.wikipedia.org/wiki/Log_trigger">http://en.wikipedia.org/wiki/Log_trigger</a> ). We use tuple-versioning in
+this database, with 'effective' as start date field name and 'expire' as
+the end (exclusive).</p>
+<p>Tuple-versioning has a shortcoming wrt to keys, both primary and foreign.
+The primary key of a table employing tuple-versioning is really
+'id' + 'valid_period', where the latter is expressed using two fields
+([effective...expire-1]). Only, how do you tell the database engine that
+it should not allow overlapping valid_periods? Useful suggestions are
+welcomed. :-)</p>
+<p>Foreign key references to a table using tuple-versioning is running into
+trouble because of the time axis and that to our knowledge foreign keys
+must reference exactly one row in the other table. When time is involved
+what we wish to tell the database is that at any given time, there actually
+is exactly one row we want to match in the other table, only we've no idea
+how to express this. So, many foreign keys are not expressed in SQL of this
+database.</p>
+<p>In some cases, we extend the tuple-versioning with a generation ID so that
+normal foreign key referencing can be used. We only use this for recording
+(references in testset) and scheduling (schedqueue), as using it more widely
+would force updates (gen_id changes) to propagate into all related tables.</p>
+<dl class="docutils">
+<dt>See also:</dt>
+<dd><ul class="first last simple">
+<li><a class="reference external" href="http://en.wikipedia.org/wiki/Slowly_changing_dimension">http://en.wikipedia.org/wiki/Slowly_changing_dimension</a></li>
+<li><a class="reference external" href="http://en.wikipedia.org/wiki/Change_data_capture">http://en.wikipedia.org/wiki/Change_data_capture</a></li>
+<li><a class="reference external" href="http://en.wikipedia.org/wiki/Temporal_database">http://en.wikipedia.org/wiki/Temporal_database</a></li>
+</ul>
+</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="test-manager-execution">
+<h1>Test Manager: Execution</h1>
+</div>
+<div class="section" id="test-manager-scenarios">
+<h1>Test Manager: Scenarios</h1>
+<div class="section" id="testbox-signs-on-at-bootup">
+<h2>#1 - Testbox Signs On (At Bootup)</h2>
+<dl class="docutils">
+<dt>The testbox supplies a number of inputs when reporting for duty:</dt>
+<dd><ul class="first last simple">
+<li>IP address.</li>
+<li>System UUID.</li>
+<li>OS name.</li>
+<li>OS version.</li>
+<li>CPU architecture.</li>
+<li>CPU count (= threads).</li>
+<li>CPU VT-x/AMD-V capability.</li>
+<li>CPU nested paging capability.</li>
+<li>Chipset I/O MMU capability.</li>
+<li>Memory size.</li>
+<li>Scratch size space (for testing).</li>
+<li>Testbox Script revision.</li>
+</ul>
+</dd>
+<dt>Results:</dt>
+<dd><ul class="first last simple">
+<li>ACK or NACK.</li>
+<li>Testbox ID and name on ACK.</li>
+</ul>
+</dd>
+</dl>
+<p>After receiving a ACK the testbox will ask for work to do, i.e. continue with
+scenario #2. In the NACK case, it will sleep for 60 seconds and try again.</p>
+<p>Actions:</p>
+<ol class="arabic">
+<li><p class="first">Validate the testbox by looking the UUID up in the TestBoxes table.
+If not found, NACK the request. SQL:</p>
+<pre class="literal-block">
+SELECT idTestBox, sName
+FROM TestBoxes
+WHERE uuidSystem = :sUuid
+ AND tsExpire = 'infinity'::timestamp;
+</pre>
+</li>
+<li><p class="first">Check if any of the information by testbox script has changed. The two
+sizes are normalized first, memory size rounded to nearest 4 MB and scratch
+space is rounded down to nearest 64 MB. If anything changed, insert a new
+row in the testbox table and historize the current one, i.e. set
+OLD.tsExpire to NEW.tsEffective and get a new value for NEW.idGenTestBox.</p>
+</li>
+<li><dl class="first docutils">
+<dt>Check with TestBoxStatuses:</dt>
+<dd><ol class="first last loweralpha simple">
+<li>If there is an row for the testbox in it already clean up change it
+to 'idle' state and deal with any open testset like described in
+scenario #9.</li>
+<li>If there is no row, add one with 'idle' state.</li>
+</ol>
+</dd>
+</dl>
+</li>
+<li><p class="first">ACK the request and pass back the idTestBox.</p>
+</li>
+</ol>
+<dl class="docutils">
+<dt>Note! Testbox.enabled is not checked here, that is only relevant when it asks</dt>
+<dd>for a new task (scenario #2 and #5).</dd>
+<dt>Note! Should the testbox script detect changes in any of the inputs, it should</dt>
+<dd>redo the sign in.</dd>
+<dt>Note! In scenario #8, the box will not sign on until it has done the reboot and</dt>
+<dd>cleanup reporting!</dd>
+</dl>
+</div>
+<div class="section" id="testbox-asks-for-work-to-do">
+<h2>#2 - Testbox Asks For Work To Do</h2>
+<dl class="docutils">
+<dt>Inputs:</dt>
+<dd><ul class="first last simple">
+<li>The testbox is supplying its IP indirectly.</li>
+<li>The testbox should supply its UUID and ID directly.</li>
+</ul>
+</dd>
+<dt>Results:</dt>
+<dd><ul class="first last simple">
+<li>IDLE, WAIT, EXEC, REBOOT, UPGRADE, UPGRADE-AND-REBOOT, SPECIAL or DEAD.</li>
+</ul>
+</dd>
+</dl>
+<p>Actions:</p>
+<ol class="arabic">
+<li><p class="first">Validate the ID and IP by selecting the currently valid testbox row:</p>
+<pre class="literal-block">
+SELECT idGenTestBox, fEnabled, idSchedGroup, enmPendingCmd
+FROM TestBoxes
+WHERE id = :id
+ AND uuidSystem = :sUuid
+ AND ip = :ip
+ AND tsExpire = 'infinity'::timestamp;
+</pre>
+<p>If NOT found return DEAD to the testbox client (it will go back to sign on
+mode and retry every 60 seconds or so - see scenario #1).</p>
+<dl class="docutils">
+<dt>Note! The WUI will do all necessary clean-ups when deleting a testbox, so</dt>
+<dd><p class="first last">contrary to the initial plans, we don't need to do anything more for
+the DEAD status.</p>
+</dd>
+</dl>
+</li>
+<li><p class="first">Check with TestBoxStatuses (maybe joined with query from 1).</p>
+<p>If enmState is 'gang-gathering': Goto scenario #6 on timeout or pending
+'abort' or 'reboot' command. Otherwise, tell the testbox to WAIT [done].</p>
+<p>If enmState is 'gang-testing': The gang has been gathered and execution
+has been triggered. Goto 5.</p>
+<p>If enmState is not 'idle', change it to 'idle'.</p>
+<p>If idTestSet is not NULL, CALL scenario #9 to it up.</p>
+<p>If there is a pending abort command, remove it.</p>
+<p>If there is a pending command and the old state doesn't indicate that it was
+being executed, GOTO scenario #3.</p>
+<dl class="docutils">
+<dt>Note! There should be a TestBoxStatuses row after executing scenario #1,</dt>
+<dd><p class="first last">however should none be found for some funky reason, returning DEAD
+will fix the problem (see above)</p>
+</dd>
+</dl>
+</li>
+<li><p class="first">If the testbox was marked as disabled, respond with an IDLE command to the
+testbox [done]. (Note! Must do this after TestBoxStatuses maintenance from
+point 2, or abandoned tests won't be cleaned up after a testbox is disabled.)</p>
+</li>
+<li><p class="first">Consider testcases in the scheduling queue, pick the first one which the
+testbox can execute. There is a concurrency issue here, so we put and
+exclusive lock on the SchedQueues table while considering its content.</p>
+<p>The cursor we open looks something like this:</p>
+<pre class="literal-block">
+SELECT idItem, idGenTestCaseArgs,
+ idTestSetGangLeader, cMissingGangMembers
+FROM SchedQueues
+WHERE idSchedGroup = :idSchedGroup
+ AND ( bmHourlySchedule is NULL
+ OR get_bit(bmHourlySchedule, :iHourOfWeek) = 1 ) --&lt; does this work?
+ORDER BY ASC idItem;
+</pre>
+</li>
+</ol>
+<blockquote>
+<p>If there no rows are returned (this can happen because no testgroups are
+associated with this scheduling group, the scheduling group is disabled,
+or because the queue is being regenerated), we will tell the testbox to
+IDLE [done].</p>
+<dl class="docutils">
+<dt>For each returned row we will:</dt>
+<dd><ol class="first last loweralpha">
+<li><p class="first">Check testcase/group dependencies.</p>
+</li>
+<li><p class="first">Select a build (and default testsuite) satisfying the dependencies.</p>
+</li>
+<li><p class="first">Check the testcase requirements with that build in mind.</p>
+</li>
+<li><p class="first">If idTestSetGangLeader is NULL, try allocate the necessary resources.</p>
+</li>
+<li><p class="first">If it didn't check out, fetch the next row and redo from (a).</p>
+</li>
+<li><p class="first">Tentatively create a new test set row.</p>
+</li>
+<li><dl class="first docutils">
+<dt>If not gang scheduling:</dt>
+<dd><ul class="first last simple">
+<li>Next state: 'testing'</li>
+</ul>
+</dd>
+<dt>ElIf we're the last gang participant:</dt>
+<dd><ul class="first last simple">
+<li>Set idTestSetGangLeader to NULL.</li>
+<li>Set cMissingGangMembers to 0.</li>
+<li>Next state: 'gang-testing'</li>
+</ul>
+</dd>
+<dt>ElIf we're the first gang member:</dt>
+<dd><ul class="first last simple">
+<li>Set cMissingGangMembers to TestCaseArgs.cGangMembers - 1.</li>
+<li>Set idTestSetGangLeader to our idTestSet.</li>
+<li>Next state: 'gang-gathering'</li>
+</ul>
+</dd>
+<dt>Else:</dt>
+<dd><ul class="first last simple">
+<li>Decrement cMissingGangMembers.</li>
+<li>Next state: 'gang-gathering'</li>
+</ul>
+</dd>
+<dt>If we're not gang scheduling OR cMissingGangMembers is 0:</dt>
+<dd><p class="first last">Move the scheduler queue entry to the end of the queue.</p>
+</dd>
+</dl>
+<p>Update our TestBoxStatuses row with the new state and test set.
+COMMIT;</p>
+</li>
+</ol>
+</dd>
+</dl>
+</blockquote>
+<ol class="arabic" start="5">
+<li><dl class="first docutils">
+<dt>If state is 'testing' or 'gang-testing':</dt>
+<dd><p class="first">EXEC reponse.</p>
+<p class="last">The EXEC response for a gang scheduled testcase includes a number of
+extra arguments so that the script knows the position of the testbox
+it is running on and of the other members. This means the that the
+TestSet.iGangMemberNo is passed using --gang-member-no and the IP
+addresses of the all gang members using --gang-ipv4-&lt;memb-no&gt; &lt;ip&gt;.</p>
+</dd>
+<dt>Else (state is 'gang-gathering'):</dt>
+<dd><p class="first last">WAIT</p>
+</dd>
+</dl>
+</li>
+</ol>
+</div>
+<div class="section" id="pending-command-when-testbox-asks-for-work">
+<h2>#3 - Pending Command When Testbox Asks For Work</h2>
+<p>This is a subfunction of scenario #2 and #5.</p>
+<p>As seen in scenario #2, the testbox will send 'abort' commands to /dev/null
+when it finds one when not executing a test. This includes when it reports
+that the test has completed (no need to abort a completed test, wasting lot
+of effort when standing at the finish line).</p>
+<p>The other commands, though, are passed back to the testbox. The testbox
+script will respond with an ACK or NACK as it sees fit. If NACKed, the
+pending command will be removed (pending_cmd set to none) and that's it.
+If ACKed, the state of the testbox will change to that appropriate for the
+command and the pending_cmd set to none. Should the testbox script fail to
+respond, the command will be repeated the next time it asks for work.</p>
+</div>
+<div class="section" id="testbox-uploads-results-during-test">
+<h2>#4 - Testbox Uploads Results During Test</h2>
+<p>TODO</p>
+</div>
+<div class="section" id="testbox-completes-test-and-asks-for-work">
+<h2>#5 - Testbox Completes Test and Asks For Work</h2>
+<p>This is very similar to scenario #2</p>
+<p>TODO</p>
+</div>
+<div class="section" id="gang-gathering-timeout">
+<h2>#6 - Gang Gathering Timeout</h2>
+<p>This is a subfunction of scenario #2.</p>
+<p>When gathering a gang of testboxes for a testcase, we do not want to wait
+forever and have testboxes doing nothing for hours while waiting for partners.
+So, the gathering has a reasonable timeout (imagine something like 20-30 mins).</p>
+<p>Also, we need some way of dealing with 'abort' and 'reboot' commands being
+issued while waiting. The easy way out is pretend it's a time out.</p>
+<p>When changing the status to 'gang-timeout' we have to be careful. First of all,
+we need to exclusively lock the SchedQueues and TestBoxStatuses (in that order)
+and re-query our status. If it changed redo the checks in scenario #2 point 2.</p>
+<p>If we still want to timeout/abort, change the state from 'gang-gathering' to
+'gang-gathering-timedout' on all the gang members that has gathered so far.
+Then reset the scheduling queue record and move it to the end of the queue.</p>
+<p>When acting on 'gang-timeout' the TM will fail the testset in a manner similar
+to scenario #9. No need to repeat that.</p>
+</div>
+<div class="section" id="gang-cleanup">
+<h2>#7 - Gang Cleanup</h2>
+<p>When a testbox completes a gang scheduled test, we will have to serialize
+resource cleanup (both globally and on testboxes) as they stop. More details
+can be found in the documentation of 'gang-cleanup'.</p>
+<p>So, the transition from 'gang-testing' is always to 'gang-cleanup'. When we
+can safely leave 'gang-cleanup' is decided by the query:</p>
+<pre class="literal-block">
+SELECT COUNT(*)
+FROM TestBoxStatuses,
+ TestSets
+WHERE TestSets.idTestSetGangLeader = :idTestSetGangLeader
+ AND TestSets.idTestBox = TestBoxStatuses.idTestBox
+ AND TestBoxStatuses.enmState = 'gang-running'::TestBoxState_T;
+</pre>
+<p>As long as there are testboxes still running, we stay in the 'gang-cleanup'
+state. Once there are none, we continue closing the testset and such.</p>
+</div>
+<div class="section" id="testbox-reports-a-crash-during-test-execution">
+<h2>#8 - Testbox Reports A Crash During Test Execution</h2>
+<p>TODO</p>
+</div>
+<div class="section" id="cleaning-up-abandoned-testcase">
+<h2>#9 - Cleaning Up Abandoned Testcase</h2>
+<p>This is a subfunction of scenario #1 and #2. The actions taken are the same in
+both situations. The precondition for taking this path is that the row in the
+testboxstatus table is referring to a testset (i.e. testset_id is not NULL).</p>
+<p>Actions:</p>
+<ol class="arabic simple">
+<li><dl class="first docutils">
+<dt>If the testset is incomplete, we need to completed:</dt>
+<dd><ol class="first last loweralpha">
+<li>Add a message to the root TestResults row, creating one if necessary,
+that explains that the test was abandoned. This is done
+by inserting/finding the string into/in TestResultStrTab and adding
+a row to TestResultMsgs with idStrMsg set to that string id and
+enmLevel set to 'failure'.</li>
+<li>Mark the testset as failed.</li>
+</ol>
+</dd>
+</dl>
+</li>
+<li>Free any global resources referenced by the test set. This is done by
+deleting all rows in GlobalResourceStatuses matching the testbox id.</li>
+<li>Set the idTestSet to NULL in the TestBoxStatuses row.</li>
+</ol>
+</div>
+<div class="section" id="cleaning-up-a-disabled-dead-testbox">
+<h2>#10 - Cleaning Up a Disabled/Dead TestBox</h2>
+<p>The UI needs to be able to clean up the remains of a testbox which for some
+reason is out of action. Normal cleaning up of abandoned testcases requires
+that the testbox signs on or asks for work, but if the testbox is dead or
+in some way indisposed, it won't be doing any of that. So, the testbox
+sheriff needs to have a way of cleaning up after it.</p>
+<p>It's basically a manual scenario #9 but with some safe guards, like checking
+that the box hasn't been active for the last 1-2 mins (max idle/wait time * 2).</p>
+<dl class="docutils">
+<dt>Note! When disabling a box that still executing the testbox script, this</dt>
+<dd>cleanup isn't necessary as it will happen automatically. Also, it's
+probably desirable that the testbox finishes what ever it is doing first
+before going dormant.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="test-manager-analysis">
+<h1>Test Manager: Analysis</h1>
+<p>One of the testbox sheriff's tasks is to try figure out the reason why something
+failed. The test manager will provide facilities for doing so from very early
+in it's implementation.</p>
+<p>We need to work out some useful status reports for the early implementation.
+Later there will be more advanced analysis tools, where for instance we can
+create graphs from selected test result values or test execution times.</p>
+</div>
+<div class="section" id="implementation-plan">
+<h1>Implementation Plan</h1>
+<p>This has changed for various reasons. The current plan is to implement the
+infrastructure (TM &amp; testbox script) first and do a small deployment with the
+2-5 test drivers in the Testsuite as basis. Once the bugs are worked out, we
+will convert the rest of the tests and start adding new ones.</p>
+<p>We just need to finally get this done, no point in doing it piecemeal by now!</p>
+<div class="section" id="test-manager-implementation-sub-tasks">
+<h2>Test Manager Implementation Sub-Tasks</h2>
+<p>The implementation of the test manager and adjusting/completing of the testbox
+script and the test drivers are tasks which can be done by more than one
+person. Splitting up the TM implementation into smaller tasks should allow
+parallel development of different tasks and get us working code sooner.</p>
+</div>
+<div class="section" id="milestone-1">
+<h2>Milestone #1</h2>
+<p>The goal is to getting the fundamental testmanager engine implemented, debugged
+and working. With the exception of testboxes, the configuration will be done
+via SQL inserts.</p>
+<p>Tasks in somewhat prioritized order:</p>
+<blockquote>
+<ul class="simple">
+<li>Kick off test manager. It will live in testmanager/. Salvage as much as
+possible from att/testserv. Create basic source and file layout.</li>
+<li>Adjust the testbox script, part one. There currently is a testbox script
+in att/testbox, this shall be moved up into testboxscript/. The script
+needs to be adjusted according to the specification layed down earlier
+in this document. Installers or installation scripts for all relevant
+host OSes are required. Left for part two is result reporting beyond the
+primary log. This task must be 100% feature complete, on all host OSes,
+there is no room for FIXME, XXX or &#64;todo here.</li>
+<li>Implement the schedule queue generator.</li>
+<li>Implement the testbox dispatcher in TM. Support all the testbox script
+responses implemented above, including upgrading the testbox script.</li>
+<li>Implement simple testbox management page.</li>
+<li>Implement some basic activity and result reports so that we can see
+what's going on.</li>
+<li>Create a testmanager / testbox test setup. This lives in selftest/.<ol class="arabic">
+<li>Set up something that runs, no fiddly bits. Debug till it works.</li>
+<li>Create a setup that tests testgroup dependencies, i.e. real tests
+depending on smoke tests.</li>
+<li>Create a setup that exercises testcase dependency.</li>
+<li>Create a setup that exercises global resource allocation.</li>
+<li>Create a setup that exercises gang scheduling.</li>
+</ol>
+</li>
+<li>Check that all features work.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="milestone-2">
+<h2>Milestone #2</h2>
+<p>The goal is getting to VBox testing.</p>
+<p>Tasks in somewhat prioritized order:</p>
+<blockquote>
+<ul class="simple">
+<li>Implement full result reporting in the testbox script and testbox driver.
+A testbox script specific reporter needs to be implemented for the
+testdriver framework. The testbox script needs to forward the results to
+the test manager, or alternatively the testdriver report can talk
+directly to the TM.</li>
+<li>Implement the test manager side of the test result reporting.</li>
+<li>Extend the selftest with some setup that report all kinds of test
+results.</li>
+<li>Implement script/whatever feeding builds to the test manager from the
+tinderboxes.</li>
+<li>The toplevel test driver is a VBox thing that must be derived from the
+base TestDriver class or maybe the VBox one. It should move from
+toptestdriver to testdriver and be renamed to vboxtltd or smth.</li>
+<li>Create a vbox testdriver that boots the t-xppro VM once and that's it.</li>
+<li>Create a selftest setup which tests booting t-xppro taking builds from
+the tinderbox.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="milestone-3">
+<h2>Milestone #3</h2>
+<p>The goal for this milestone is configuration and converting current testcases,
+the result will be the a minimal test deployment (4-5 new testboxes).</p>
+<p>Tasks in somewhat prioritized order:</p>
+<blockquote>
+<ul class="simple">
+<li>Implement testcase configuration.</li>
+<li>Implement testgroup configuration.</li>
+<li>Implement build source configuration.</li>
+<li>Implement scheduling group configuration.</li>
+<li>Implement global resource configuration.</li>
+<li>Re-visit the testbox configuration.</li>
+<li>Black listing of builds.</li>
+<li>Implement simple failure analysis and reporting.</li>
+<li>Implement the initial smoke tests modelled on the current smoke tests.</li>
+<li>Implement installation tests for Windows guests.</li>
+<li>Implement installation tests for Linux guests.</li>
+<li>Implement installation tests for Solaris guest.</li>
+<li>Implement installation tests for OS/2 guest.</li>
+<li>Set up a small test deployment.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="further-work">
+<h2>Further work</h2>
+<p>After milestone #3 has been reached and issues found by the other team members
+have been addressed, we will probably go for full deployment.</p>
+<p>Beyond this point we will need to improve reporting and analysis. There may be
+configuration aspects needing reporting as well.</p>
+<p>Once deployed, a golden rule will be that all new features shall have test
+coverage. Preferably, implemented by someone else and prior to the feature
+implementation.</p>
+</div>
+</div>
+<div class="section" id="discussion-logs">
+<h1>Discussion Logs</h1>
+<div class="section" id="various-discussions-with-michal-and-or-klaus">
+<h2>2009-07-21,22,23 Various Discussions with Michal and/or Klaus</h2>
+<ul class="simple">
+<li>Scheduling of tests requiring more than one testbox.</li>
+<li>Scheduling of tests that cannot be executing concurrently on several machines
+because of some global resource like an iSCSI target.</li>
+<li>Manually create the test config permutations instead of having the test
+manager create all possible ones and wasting time.</li>
+<li>Distinguish between built types so we can run smoke tests on strick builds as
+well as release ones.</li>
+</ul>
+</div>
+<div class="section" id="brief-discussion-with-michal">
+<h2>2009-07-20 Brief Discussion with Michal</h2>
+<ul class="simple">
+<li>Installer for the testbox script to make bringing up a new testbox even
+smoother.</li>
+</ul>
+</div>
+<div class="section" id="raw-input">
+<h2>2009-07-16 Raw Input</h2>
+<ul class="simple">
+<li><dl class="first docutils">
+<dt>test set. recursive collection of:</dt>
+<dd><ul class="first last">
+<li>hierachical subtest name (slash sep)</li>
+<li>test parameters / config</li>
+<li>bool fail/succ</li>
+<li>attributes (typed?)</li>
+<li>test time</li>
+<li>e.g. throughput</li>
+<li>subresults</li>
+<li>log</li>
+<li>screenshots,....</li>
+</ul>
+</dd>
+</dl>
+</li>
+<li>client package (zip) dl from server (maybe client caching)</li>
+<li><dl class="first docutils">
+<dt>thoughts on bits to do at once.</dt>
+<dd><ul class="first last">
+<li>We <em>really</em> need the basic bits ASAP.</li>
+<li>client -&gt; support for test driver</li>
+<li>server -&gt; controls configs</li>
+<li>cleanup on both sides</li>
+</ul>
+</dd>
+</dl>
+</li>
+</ul>
+</div>
+<div class="section" id="raw-input-1">
+<h2>2009-07-15 Raw Input</h2>
+<ul class="simple">
+<li>testing should start automatically</li>
+<li>switching to branch too tedious</li>
+<li>useful to be able to partition testboxes (run specific builds on some boxes, let an engineer have a few boxes for a while).</li>
+<li>test specification needs to be more flexible (select tests, disable test, test scheduling (run certain tests nightly), ... )</li>
+<li>testcase dependencies (blacklisting builds, run smoketests on box A before long tests on box B, ...)</li>
+<li>more testing flexibility, more test than just install/moke. For instance unit tests, benchmarks, ...</li>
+<li>presentation/analysis: graphs!, categorize bugs, columns reorganizing grouped by test (hierarchical), overviews, result for last day.</li>
+<li>testcase specificion, variables (e.g. I/O-APIC, SMP, HWVIRT, SATA...) as sub-tests</li>
+<li>interation with ILOM/...: reset systems</li>
+<li>Changes needs LDAP authentication</li>
+<li>historize all configuration w/ name</li>
+<li>ability to run testcase locally (provided the VDI/ISO/whatever extra requirements can be met).</li>
+</ul>
+<hr class="docutils" />
+<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label">[1]</td><td>no such footnote</td></tr>
+</tbody>
+</table>
+<hr class="docutils" />
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: AutomaticTestingRevamp.html $</td>
+</tr>
+<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2023 Oracle Corporation.</td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt
new file mode 100644
index 00000000..17d920ba
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/AutomaticTestingRevamp.txt
@@ -0,0 +1,1061 @@
+
+Revamp of Automatic VirtualBox Testing
+======================================
+
+
+Introduction
+------------
+
+This is the design document for a revamped automatic testing framework.
+The revamp aims at replacing the current tinderbox based testing by a new
+system that is written from scratch.
+
+The old system is not easy to work with and was never meant to be used for
+managing tests, after all it just a simple a build manager tailored for
+contiguous building. Modifying the existing tinderbox system to do what
+we want would require fundamental changes that would render it useless as
+a build manager, it would therefore end up as a fork. The amount of work
+required would probably be about the same as writing a new system from
+scratch. Other considerations, such as the license of the tinderbox
+system (MPL) and language it is realized in (Perl), are also in favor of
+doing it from scratch.
+
+The language envisioned for the new automatic testing framework is Python. This
+is for several reasons:
+
+ - The VirtualBox API has Python bindings.
+ - Python is used quite a bit inside Sun (dunno about Oracle).
+ - Works relatively well with Apache for the server side bits.
+ - It is more difficult to produce write-only code in Python (alias the
+ we-don't-like-perl argument).
+ - You don't need to compile stuff.
+
+Note that the author of this document has no special training as a test
+engineer and may therefore be using the wrong terms here and there. The
+primary focus is to express what we need to do in order to improve
+testing.
+
+This document is written in reStructuredText (rst) which just happens to
+be used by Python, the primary language for this revamp. For more
+information on reStructuredText: http://docutils.sourceforge.net/rst.html
+
+
+Definitions / Glossary
+======================
+
+sub-test driver
+ A set of test cases that can be used by more than one test driver. Could
+ also be called a test unit, in the pascal sense of unit, if it wasn't so
+ easily confused with 'unit test'.
+
+test
+ This is somewhat ambiguous and this document try avoid using it where
+ possible. When used it normally refers to doing testing by executing one or
+ more testcases.
+
+test case
+ A set of inputs, test programs and expected results. It validates system
+ requirements and generates a pass or failed status. A basic unit of testing.
+ Note that we use the term in a rather broad sense.
+
+test driver
+ A program/script used to execute a test. Also known as a test harness.
+ Generally abbreviated 'td'. It can have sub-test drivers.
+
+test manager
+ Software managing the automatic testing. This is a web application that runs
+ on a dedicated server (tindertux).
+
+test set
+ The output of testing activity. Logs, results, ++. Our usage of this should
+ probably be renamed to 'test run'.
+
+test group
+ A collection of related test cases.
+
+testbox
+ A computer that does testing.
+
+testbox script
+ Script executing orders from the test manager on a testbox. Started
+ automatically upon bootup.
+
+testing
+ todo
+
+TODO: Check that we've got all this right and make them more exact
+ where possible.
+
+See also http://encyclopedia2.thefreedictionary.com/testing%20types
+and http://www.aptest.com/glossary.html .
+
+
+
+Objectives
+==========
+
+ - A scalable test manager (>200 testboxes).
+ - Optimize the web user interface (WUI) for typical workflows and analysis.
+ - Efficient and flexibile test configuration.
+ - Import test result from other test systems (logo testing, VDI, ++).
+ - Easy to add lots of new testscripts.
+ - Run tests locally without a manager.
+ - Revamp a bit at the time.
+
+
+
+The Testbox Side
+================
+
+Each testbox has a unique name corresponding to its DNS zone entry. When booted
+a testbox script is started automatically. This script will query the test
+manager for orders and execute them. The core order downloads and executes a
+test driver with parameters (configuration) from the server. The test driver
+does all the necessary work for executing the test. In a typical VirtualBox
+test this means picking a build, installing it, configuring VMs, running the
+test VMs, collecting the results, submitting them to the server, and finally
+cleaning up afterwards.
+
+The testbox environment which the test drivers are executed in will have a
+number of environment variables for determining location of the source images
+and other test data, scratch space, test set id, server URL, and so on and so
+forth.
+
+On startup, the testbox script will look for crash dumps and similar on
+systems where this is possible. If any sign of a crash is found, it will
+put any dumps and reports in the upload directory and inform the test
+manager before reporting for duty. In order to generate the proper file
+names and report the crash in the right test set as well as prevent
+reporting crashes unrelated to automatic testing, the testbox script will
+keep information (test set id, ++) in a separate scratch directory
+(${TESTBOX_PATH_SCRATCH}/../testbox) and make sure it is synced to the
+disk (both files and directories).
+
+After checking for crashes, the testbox script will clean up any previous test
+which might be around. This involves first invoking the test script in cleanup
+mode and the wiping the scratch space.
+
+When reporting for duty the script will submit information about the host: OS
+name, OS version, OS bitness, CPU vendor, total number of cores, VT-x support,
+AMD-V support, amount of memory, amount of scratch space, and anything else that
+can be found useful for scheduling tests or filtering test configurations.
+
+
+
+Testbox Script Orders
+---------------------
+
+The orders are kept in a queue on the server and the testbox script will fetch
+them one by one. Orders that cannot be executed at the moment will be masked in
+the query from the testbox.
+
+Execute Test Driver
+ Downloads and executes the a specified test driver with the given
+ configuration (arguments). Only one test driver can be executed at a time.
+ The server can specify more than one ZIP file to be downloaded and unpacked
+ before executing the test driver. The testbox script may cache these zip
+ files using http time stamping.
+
+Abort Test Driver
+ Aborts the current test driver. This will drop a hint to the driver and give
+ it 60 seconds to shut down the normal way. If that fails, the testbox script
+ will kill the driver processes (SIGKILL or equivalent), invoke the
+ testdriver in cleanup mode, and finally wipe the scratch area. Should either
+ of the last two steps fail in some way, the testbox will be rebooted.
+
+Idle
+ Ask again in X seconds, where X is specified by the server.
+
+Reboot
+ Reboot the testbox. If a test driver is current running, an attempt at
+ aborting it (Abort Test Driver) will be made first.
+
+Update
+ Updates the testbox script. The order includes a server relative path to the
+ new testbox script. This can only be executed when no test driver is
+ currently being executed.
+
+
+Testbox Environment: Variables
+------------------------------
+
+COMSPEC
+ This will be set to C:\Windows\System32\cmd.exe on Windows.
+
+PATH
+ This will contain the kBuild binary directory for the host platform.
+
+SHELL
+ This will be set to point to kmk_ash(.exe) on all platforms.
+
+TESTBOX_NAME
+ The testbox name.
+ This is not required by the local reporter.
+
+TESTBOX_PATH_BUILDS
+ The absolute path to where the build repository can be found. This should be
+ a read only mount when possible.
+
+TESTBOX_PATH_RESOURCES
+ The absolute path to where static test resources like ISOs and VDIs can be
+ found. The test drivers knows the layout of this. This should be a read only
+ mount when possible.
+
+TESTBOX_PATH_SCRATCH
+ The absolute path to the scratch space. This is the current directory when
+ starting the test driver. It will be wiped automatically after executing the
+ test.
+ (Envisioned as ${TESTBOX_PATH_SCRIPTS}/../scratch and that
+ ${TESTBOX_PATH_SCRATCH}/ will be automatically wiped by the testbox script.)
+
+TESTBOX_PATH_SCRIPTS
+ The absolute path to the test driver and the other files that was unzipped
+ together with it. This is also where the test-driver-abort file will be put.
+ (Envisioned as ${TESTBOX_PATH_SCRATCH}/../driver, see above.)
+
+TESTBOX_PATH_UPLOAD
+ The absolute path to the upload directory for the testbox. This is for
+ putting VOBs, PNGs, core dumps, crash dumps, and such on. The files should be
+ bzipped or zipped if they aren't compress already. The names should contain
+ the testbox and test set ID.
+
+TESTBOX_REPORTER
+ The name of the test reporter back end. If not present, it will default to
+ the local reporter.
+
+TESTBOX_TEST_SET_ID
+ The test set ID if we're running.
+ This is not required by the local reporter.
+
+TESTBOX_MANAGER_URL
+ The URL to the test manager.
+ This is not required by the local reporter.
+
+TESTBOX_XYZ
+ There will probably be some more of these.
+
+
+Testbox Environment: Core Utilities
+-----------------------------------
+
+The testbox will not provide the typical unix /bin and /usr/bin utilities. In
+other words, cygwin will not be used on Windows!
+
+The testbox will provide the unixy utilities that ships with kBuild and possibly
+some additional ones from tools/*.*/bin in the VirtualBox tree (wget, unzip,
+zip, and so on). The test drivers will avoid invoking any of these utilities
+directly and instead rely on generic utility methods in the test driver
+framework. That way we can more easily reimplement the functionality of the
+core utilities and drop the dependency on them. It also allows us to quickly
+work around platform specific oddities and bugs.
+
+
+Test Drivers
+------------
+
+The test drivers are programs that will do the actual testing. In addition to
+run under the testbox script, they can be executed in the VirtualBox development
+environment. This is important for bug analysis and for simplifying local
+testing by the developers before committing changes. It also means the test
+drivers can be developed locally in the VirtualBox development environment.
+
+The main difference between executing a driver under the testbox script and
+running it manually is that there is no test manager in the latter case. The
+test result reporter will not talk to the server, but report things to a local
+log file and/or standard out/err. When invoked manually, all the necessary
+arguments will need to be specified by hand of course - it should be possible
+to extract them from a test set as well.
+
+For the early implementation stages, an implementation of the reporter interface
+that talks to the tinderbox base test manager will be needed. This will be
+dropped later on when a new test manager is ready.
+
+As hinted at in other sections, there will be a common framework
+(libraries/packages/classes) for taking care of the tedious bits that every
+test driver needs to do. Sharing code is essential to easing test driver
+development as well as reducing their complexity. The framework will contain:
+
+ - A generic way of submitting output. This will be a generic interface with
+ multiple implementation, the TESTBOX_REPORTER environment variable
+ will decide which of them to use. The interface will have very specific
+ methods to allow the reporter to do a best possible job in reporting the
+ results to the test manager.
+
+ - Helpers for typical tasks, like:
+ - Copying files.
+ - Deleting files, directory trees and scratch space.
+ - Unzipping files.
+ - Creating ISOs
+ - And such things.
+
+ - Helpers for installing and uninstalling VirtualBox.
+
+ - Helpers for defining VMs. (The VBox API where available.)
+
+ - Helpers for controlling VMs. (The VBox API where available.)
+
+The VirtualBox bits will be separate from the more generic ones, simply because
+this is cleaner it will allow us to reuse the system for testing other products.
+
+The framework will be packaged in a zip file other than the test driver so we
+don't waste time and space downloading the same common code.
+
+The test driver will poll for the file
+${TESTBOX_PATH_SCRIPTS}/test-driver-abort and abort all testing when it sees it.
+
+The test driver can be invoked in three modes: execute, help and cleanup. The
+default is execute mode, the help shows an configuration summary and the cleanup
+is for cleaning up after a reboot or aborted run. The latter is done by the
+testbox script on startup and after abort - the driver is expected to clean up
+by itself after a normal run.
+
+
+
+The Server Side
+===============
+
+The server side will be implemented using a webserver (apache), a database
+(postgres) and cgi scripts (Python). In addition a cron job (Python) running
+once a minute will generate static html for frequently used pages and maybe
+execute some other tasks for driving the testing forwards. The order queries
+from the testbox script is the primary driving force in the system. The total
+makes up the test manager.
+
+The test manager can be split up into three rough parts:
+
+ - Configuration (of tests, testgroups and testboxes).
+ - Execution (of tests, collecting and organizing the output).
+ - Analysis (of test output, mostly about presentation).
+
+
+Test Manager: Requirements
+==========================
+
+List of requirements:
+
+ - Two level testing - L1 quick smoke tests and L2 longer tests performed on
+ builds passing L1. (Klaus (IIRC) meant this could be realized using
+ test dependency.)
+ - Black listing builds (by revision or similar) known to be bad.
+ - Distinguish between build types so we can do a portion of the testing with
+ strict builds.
+ - Easy to re-configure build source for testing different branch or for
+ testing a release candidate. (Directory based is fine.)
+ - Useful to be able to partition testboxes (run specific builds on some
+ boxes, let an engineer have a few boxes for a while).
+ - Interaction with ILOM/...: reset systems.
+ - Be able to suspend testing on selected testboxes when doing maintenance
+ (where automatically resuming testing on reboot is undesired) or similar
+ activity.
+ - Abort testing on selected testboxes.
+ - Scheduling of tests requiring more than one testbox.
+ - Scheduling of tests that cannot be executing concurrently on several
+ machines because of some global resource like an iSCSI target.
+ - Jump the scheduling queue. Scheduling of specified test the next time a
+ testbox is available (optionally specifying which testbox to schedule it
+ on).
+ - Configure tests with variable configuration to get better coverage. Two modes:
+ - TM generates the permutations based on one or more sets of test script arguments.
+ - Each configuration permutation is specified manually.
+ - Test specification needs to be flexible (select tests, disable test, test
+ scheduling (run certain tests nightly), ... ).
+ - Test scheduling by hour+weekday and by priority.
+ - Test dependencies (test A depends on test B being successful).
+ - Historize all configuration data, in particular test configs (permutations
+ included) and testboxes.
+ - Test sets has at a minimum a build reference, a testbox reference and a
+ primary log associated with it.
+ - Test sets stores further result as a recursive collection of:
+ - hierarchical subtest name (slash sep)
+ - test parameters / config
+ - bool fail/succ
+ - attributes (typed?)
+ - test time
+ - e.g. throughput
+ - subresults
+ - log
+ - screenshots, video,...
+ - The test sets database structure needs to designed such that data mining
+ can be done in an efficient manner.
+ - Presentation/analysis: graphs!, categorize bugs, columns reorganizing
+ grouped by test (hierarchical), overviews, result for last day.
+
+
+
+Test Manager: Configuration
+===========================
+
+
+Testboxes
+---------
+
+Configuration of testboxes doesn't involve much work normally. A testbox
+is added manually to the test manager by entering the DNS entry and/or IP
+address (the test manager resolves the missing one when necessary) as well as
+the system UUID (when obtainable - should be displayed by the testbox script
+installer). Queries from unregistered testboxes will be declined as a kind of
+security measure, the incident should be logged in the webserver log if
+possible. In later dealings with the client the System UUID will be the key
+identifier. It's permittable for the IP address to change when the testbox
+isn't online, but not while testing (just imagine live migration tests and
+network tests). Ideally, the testboxes should not change IP address.
+
+The testbox edit function must allow changing the name and system UUID.
+
+One further idea for the testbox configuration is indicating what they are
+capable of to filter out tests and test configurations that won't work on that
+testbox. To examplify this take the ACP2 installation test. If the test
+manager does not make sure the testbox have VT-x or AMD-v capabilities, the test
+is surely going to fail. Other testbox capabilities would be total number of
+CPU cores, memory size, scratch space. These testbox capabilities should be
+collected automatically on bootup by the testbox script together with OS name,
+OS version and OS bitness.
+
+A final thought, instead of outright declining all requests from new testboxes,
+we could record the unregistered testboxes with ip, UUID, name, os info and
+capabilities but mark them as inactive. The test operator can then activate
+them on an activation page or edit the testbox or something.
+
+
+Testcases
+---------
+
+We use the term testcase for a test.
+
+
+Testgroups
+----------
+
+Testcases are organized into groups. A testcase can be member of more than one
+group. The testcase gets a priority assigned to it in connection with the
+group membership.
+
+Testgroups are picked up by a testbox partition (aka scheduling group) and a
+prioirty, scheduling time restriction and dependencies on other test groups are
+associated with the assignment. A testgroup can be used by several testbox
+partitions.
+
+(This used to be called 'testsuites' but was renamed to avoid confusion with
+the VBox Test Suite.)
+
+
+Scheduling
+----------
+
+The initial scheduler will be modelled after what we're doing already on in the
+tinderbox driven testing. It's best described as a best effort continuous
+integration scheduler. Meaning, it will always use the latest build suitable
+for a testcase. It will schedule on a testcase level, using the combined
+priority of the testcase in the test group and the test group with the testbox
+partition, trying to spread the test case argument variation out accordingly
+over the whole scheduilng queue. Which argument variation to start with, is
+not undefined (random would be best).
+
+Later, we may add other schedulers as needed.
+
+
+
+The Test Manager Database
+=========================
+
+First a general warning:
+
+ The guys working on this design are not database experts, web
+ programming experts or similar, rather we are low level guys
+ who's main job is x86 & AMD64 virtualization. So, please don't
+ be too hard on us. :-)
+
+
+A logical table layout can be found in TestManagerDatabaseMap.png (created by
+Oracle SQL Data Modeler, stored in TestManagerDatabase.dmd). The physical
+database layout can be found in TestManagerDatabaseInit.pgsql postgreSQL
+script. The script is commented.
+
+
+Data History
+------------
+
+We need to somehow track configuration changes over time. We also need to
+be able to query the exact configuration a test set was run with so we can
+understand and make better use of the results.
+
+There are different techniques for archiving this, one is tuple-versioning
+( http://en.wikipedia.org/wiki/Tuple-versioning ), another is log trigger
+( http://en.wikipedia.org/wiki/Log_trigger ). We use tuple-versioning in
+this database, with 'effective' as start date field name and 'expire' as
+the end (exclusive).
+
+Tuple-versioning has a shortcoming wrt to keys, both primary and foreign.
+The primary key of a table employing tuple-versioning is really
+'id' + 'valid_period', where the latter is expressed using two fields
+([effective...expire-1]). Only, how do you tell the database engine that
+it should not allow overlapping valid_periods? Useful suggestions are
+welcomed. :-)
+
+Foreign key references to a table using tuple-versioning is running into
+trouble because of the time axis and that to our knowledge foreign keys
+must reference exactly one row in the other table. When time is involved
+what we wish to tell the database is that at any given time, there actually
+is exactly one row we want to match in the other table, only we've no idea
+how to express this. So, many foreign keys are not expressed in SQL of this
+database.
+
+In some cases, we extend the tuple-versioning with a generation ID so that
+normal foreign key referencing can be used. We only use this for recording
+(references in testset) and scheduling (schedqueue), as using it more widely
+would force updates (gen_id changes) to propagate into all related tables.
+
+See also:
+ - http://en.wikipedia.org/wiki/Slowly_changing_dimension
+ - http://en.wikipedia.org/wiki/Change_data_capture
+ - http://en.wikipedia.org/wiki/Temporal_database
+
+
+
+Test Manager: Execution
+=======================
+
+
+
+Test Manager: Scenarios
+=======================
+
+
+
+#1 - Testbox Signs On (At Bootup)
+---------------------------------
+
+The testbox supplies a number of inputs when reporting for duty:
+ - IP address.
+ - System UUID.
+ - OS name.
+ - OS version.
+ - CPU architecture.
+ - CPU count (= threads).
+ - CPU VT-x/AMD-V capability.
+ - CPU nested paging capability.
+ - Chipset I/O MMU capability.
+ - Memory size.
+ - Scratch size space (for testing).
+ - Testbox Script revision.
+
+Results:
+ - ACK or NACK.
+ - Testbox ID and name on ACK.
+
+After receiving a ACK the testbox will ask for work to do, i.e. continue with
+scenario #2. In the NACK case, it will sleep for 60 seconds and try again.
+
+
+Actions:
+
+1. Validate the testbox by looking the UUID up in the TestBoxes table.
+ If not found, NACK the request. SQL::
+
+ SELECT idTestBox, sName
+ FROM TestBoxes
+ WHERE uuidSystem = :sUuid
+ AND tsExpire = 'infinity'::timestamp;
+
+2. Check if any of the information by testbox script has changed. The two
+ sizes are normalized first, memory size rounded to nearest 4 MB and scratch
+ space is rounded down to nearest 64 MB. If anything changed, insert a new
+ row in the testbox table and historize the current one, i.e. set
+ OLD.tsExpire to NEW.tsEffective and get a new value for NEW.idGenTestBox.
+
+3. Check with TestBoxStatuses:
+ a) If there is an row for the testbox in it already clean up change it
+ to 'idle' state and deal with any open testset like described in
+ scenario #9.
+ b) If there is no row, add one with 'idle' state.
+
+4. ACK the request and pass back the idTestBox.
+
+
+Note! Testbox.enabled is not checked here, that is only relevant when it asks
+ for a new task (scenario #2 and #5).
+
+Note! Should the testbox script detect changes in any of the inputs, it should
+ redo the sign in.
+
+Note! In scenario #8, the box will not sign on until it has done the reboot and
+ cleanup reporting!
+
+
+#2 - Testbox Asks For Work To Do
+---------------------------------
+
+
+Inputs:
+ - The testbox is supplying its IP indirectly.
+ - The testbox should supply its UUID and ID directly.
+
+Results:
+ - IDLE, WAIT, EXEC, REBOOT, UPGRADE, UPGRADE-AND-REBOOT, SPECIAL or DEAD.
+
+Actions:
+
+1. Validate the ID and IP by selecting the currently valid testbox row::
+
+ SELECT idGenTestBox, fEnabled, idSchedGroup, enmPendingCmd
+ FROM TestBoxes
+ WHERE id = :id
+ AND uuidSystem = :sUuid
+ AND ip = :ip
+ AND tsExpire = 'infinity'::timestamp;
+
+ If NOT found return DEAD to the testbox client (it will go back to sign on
+ mode and retry every 60 seconds or so - see scenario #1).
+
+ Note! The WUI will do all necessary clean-ups when deleting a testbox, so
+ contrary to the initial plans, we don't need to do anything more for
+ the DEAD status.
+
+2. Check with TestBoxStatuses (maybe joined with query from 1).
+
+ If enmState is 'gang-gathering': Goto scenario #6 on timeout or pending
+ 'abort' or 'reboot' command. Otherwise, tell the testbox to WAIT [done].
+
+ If enmState is 'gang-testing': The gang has been gathered and execution
+ has been triggered. Goto 5.
+
+ If enmState is not 'idle', change it to 'idle'.
+
+ If idTestSet is not NULL, CALL scenario #9 to it up.
+
+ If there is a pending abort command, remove it.
+
+ If there is a pending command and the old state doesn't indicate that it was
+ being executed, GOTO scenario #3.
+
+ Note! There should be a TestBoxStatuses row after executing scenario #1,
+ however should none be found for some funky reason, returning DEAD
+ will fix the problem (see above)
+
+3. If the testbox was marked as disabled, respond with an IDLE command to the
+ testbox [done]. (Note! Must do this after TestBoxStatuses maintenance from
+ point 2, or abandoned tests won't be cleaned up after a testbox is disabled.)
+
+4. Consider testcases in the scheduling queue, pick the first one which the
+ testbox can execute. There is a concurrency issue here, so we put and
+ exclusive lock on the SchedQueues table while considering its content.
+
+ The cursor we open looks something like this::
+
+ SELECT idItem, idGenTestCaseArgs,
+ idTestSetGangLeader, cMissingGangMembers
+ FROM SchedQueues
+ WHERE idSchedGroup = :idSchedGroup
+ AND ( bmHourlySchedule is NULL
+ OR get_bit(bmHourlySchedule, :iHourOfWeek) = 1 ) --< does this work?
+ ORDER BY ASC idItem;
+
+ If there no rows are returned (this can happen because no testgroups are
+ associated with this scheduling group, the scheduling group is disabled,
+ or because the queue is being regenerated), we will tell the testbox to
+ IDLE [done].
+
+ For each returned row we will:
+ a) Check testcase/group dependencies.
+ b) Select a build (and default testsuite) satisfying the dependencies.
+ c) Check the testcase requirements with that build in mind.
+ d) If idTestSetGangLeader is NULL, try allocate the necessary resources.
+ e) If it didn't check out, fetch the next row and redo from (a).
+ f) Tentatively create a new test set row.
+ g) If not gang scheduling:
+ - Next state: 'testing'
+ ElIf we're the last gang participant:
+ - Set idTestSetGangLeader to NULL.
+ - Set cMissingGangMembers to 0.
+ - Next state: 'gang-testing'
+ ElIf we're the first gang member:
+ - Set cMissingGangMembers to TestCaseArgs.cGangMembers - 1.
+ - Set idTestSetGangLeader to our idTestSet.
+ - Next state: 'gang-gathering'
+ Else:
+ - Decrement cMissingGangMembers.
+ - Next state: 'gang-gathering'
+
+ If we're not gang scheduling OR cMissingGangMembers is 0:
+ Move the scheduler queue entry to the end of the queue.
+
+ Update our TestBoxStatuses row with the new state and test set.
+ COMMIT;
+
+5. If state is 'testing' or 'gang-testing':
+ EXEC reponse.
+
+ The EXEC response for a gang scheduled testcase includes a number of
+ extra arguments so that the script knows the position of the testbox
+ it is running on and of the other members. This means the that the
+ TestSet.iGangMemberNo is passed using --gang-member-no and the IP
+ addresses of the all gang members using --gang-ipv4-<memb-no> <ip>.
+ Else (state is 'gang-gathering'):
+ WAIT
+
+
+
+#3 - Pending Command When Testbox Asks For Work
+-----------------------------------------------
+
+This is a subfunction of scenario #2 and #5.
+
+As seen in scenario #2, the testbox will send 'abort' commands to /dev/null
+when it finds one when not executing a test. This includes when it reports
+that the test has completed (no need to abort a completed test, wasting lot
+of effort when standing at the finish line).
+
+The other commands, though, are passed back to the testbox. The testbox
+script will respond with an ACK or NACK as it sees fit. If NACKed, the
+pending command will be removed (pending_cmd set to none) and that's it.
+If ACKed, the state of the testbox will change to that appropriate for the
+command and the pending_cmd set to none. Should the testbox script fail to
+respond, the command will be repeated the next time it asks for work.
+
+
+
+#4 - Testbox Uploads Results During Test
+----------------------------------------
+
+
+TODO
+
+
+#5 - Testbox Completes Test and Asks For Work
+---------------------------------------------
+
+This is very similar to scenario #2
+
+TODO
+
+
+#6 - Gang Gathering Timeout
+---------------------------
+
+This is a subfunction of scenario #2.
+
+When gathering a gang of testboxes for a testcase, we do not want to wait
+forever and have testboxes doing nothing for hours while waiting for partners.
+So, the gathering has a reasonable timeout (imagine something like 20-30 mins).
+
+Also, we need some way of dealing with 'abort' and 'reboot' commands being
+issued while waiting. The easy way out is pretend it's a time out.
+
+When changing the status to 'gang-timeout' we have to be careful. First of all,
+we need to exclusively lock the SchedQueues and TestBoxStatuses (in that order)
+and re-query our status. If it changed redo the checks in scenario #2 point 2.
+
+If we still want to timeout/abort, change the state from 'gang-gathering' to
+'gang-gathering-timedout' on all the gang members that has gathered so far.
+Then reset the scheduling queue record and move it to the end of the queue.
+
+
+When acting on 'gang-timeout' the TM will fail the testset in a manner similar
+to scenario #9. No need to repeat that.
+
+
+
+#7 - Gang Cleanup
+-----------------
+
+When a testbox completes a gang scheduled test, we will have to serialize
+resource cleanup (both globally and on testboxes) as they stop. More details
+can be found in the documentation of 'gang-cleanup'.
+
+So, the transition from 'gang-testing' is always to 'gang-cleanup'. When we
+can safely leave 'gang-cleanup' is decided by the query::
+
+ SELECT COUNT(*)
+ FROM TestBoxStatuses,
+ TestSets
+ WHERE TestSets.idTestSetGangLeader = :idTestSetGangLeader
+ AND TestSets.idTestBox = TestBoxStatuses.idTestBox
+ AND TestBoxStatuses.enmState = 'gang-running'::TestBoxState_T;
+
+As long as there are testboxes still running, we stay in the 'gang-cleanup'
+state. Once there are none, we continue closing the testset and such.
+
+
+
+#8 - Testbox Reports A Crash During Test Execution
+--------------------------------------------------
+
+TODO
+
+
+#9 - Cleaning Up Abandoned Testcase
+-----------------------------------
+
+This is a subfunction of scenario #1 and #2. The actions taken are the same in
+both situations. The precondition for taking this path is that the row in the
+testboxstatus table is referring to a testset (i.e. testset_id is not NULL).
+
+
+Actions:
+
+1. If the testset is incomplete, we need to completed:
+ a) Add a message to the root TestResults row, creating one if necessary,
+ that explains that the test was abandoned. This is done
+ by inserting/finding the string into/in TestResultStrTab and adding
+ a row to TestResultMsgs with idStrMsg set to that string id and
+ enmLevel set to 'failure'.
+ b) Mark the testset as failed.
+
+2. Free any global resources referenced by the test set. This is done by
+ deleting all rows in GlobalResourceStatuses matching the testbox id.
+
+3. Set the idTestSet to NULL in the TestBoxStatuses row.
+
+
+
+#10 - Cleaning Up a Disabled/Dead TestBox
+-----------------------------------------
+
+The UI needs to be able to clean up the remains of a testbox which for some
+reason is out of action. Normal cleaning up of abandoned testcases requires
+that the testbox signs on or asks for work, but if the testbox is dead or
+in some way indisposed, it won't be doing any of that. So, the testbox
+sheriff needs to have a way of cleaning up after it.
+
+It's basically a manual scenario #9 but with some safe guards, like checking
+that the box hasn't been active for the last 1-2 mins (max idle/wait time * 2).
+
+
+Note! When disabling a box that still executing the testbox script, this
+ cleanup isn't necessary as it will happen automatically. Also, it's
+ probably desirable that the testbox finishes what ever it is doing first
+ before going dormant.
+
+
+
+Test Manager: Analysis
+=======================
+
+One of the testbox sheriff's tasks is to try figure out the reason why something
+failed. The test manager will provide facilities for doing so from very early
+in it's implementation.
+
+
+We need to work out some useful status reports for the early implementation.
+Later there will be more advanced analysis tools, where for instance we can
+create graphs from selected test result values or test execution times.
+
+
+
+Implementation Plan
+===================
+
+This has changed for various reasons. The current plan is to implement the
+infrastructure (TM & testbox script) first and do a small deployment with the
+2-5 test drivers in the Testsuite as basis. Once the bugs are worked out, we
+will convert the rest of the tests and start adding new ones.
+
+We just need to finally get this done, no point in doing it piecemeal by now!
+
+
+Test Manager Implementation Sub-Tasks
+-------------------------------------
+
+The implementation of the test manager and adjusting/completing of the testbox
+script and the test drivers are tasks which can be done by more than one
+person. Splitting up the TM implementation into smaller tasks should allow
+parallel development of different tasks and get us working code sooner.
+
+
+Milestone #1
+------------
+
+The goal is to getting the fundamental testmanager engine implemented, debugged
+and working. With the exception of testboxes, the configuration will be done
+via SQL inserts.
+
+Tasks in somewhat prioritized order:
+
+ - Kick off test manager. It will live in testmanager/. Salvage as much as
+ possible from att/testserv. Create basic source and file layout.
+
+ - Adjust the testbox script, part one. There currently is a testbox script
+ in att/testbox, this shall be moved up into testboxscript/. The script
+ needs to be adjusted according to the specification layed down earlier
+ in this document. Installers or installation scripts for all relevant
+ host OSes are required. Left for part two is result reporting beyond the
+ primary log. This task must be 100% feature complete, on all host OSes,
+ there is no room for FIXME, XXX or @todo here.
+
+ - Implement the schedule queue generator.
+
+ - Implement the testbox dispatcher in TM. Support all the testbox script
+ responses implemented above, including upgrading the testbox script.
+
+ - Implement simple testbox management page.
+
+ - Implement some basic activity and result reports so that we can see
+ what's going on.
+
+ - Create a testmanager / testbox test setup. This lives in selftest/.
+
+ 1. Set up something that runs, no fiddly bits. Debug till it works.
+ 2. Create a setup that tests testgroup dependencies, i.e. real tests
+ depending on smoke tests.
+ 3. Create a setup that exercises testcase dependency.
+ 4. Create a setup that exercises global resource allocation.
+ 5. Create a setup that exercises gang scheduling.
+
+ - Check that all features work.
+
+
+Milestone #2
+------------
+
+The goal is getting to VBox testing.
+
+Tasks in somewhat prioritized order:
+
+ - Implement full result reporting in the testbox script and testbox driver.
+ A testbox script specific reporter needs to be implemented for the
+ testdriver framework. The testbox script needs to forward the results to
+ the test manager, or alternatively the testdriver report can talk
+ directly to the TM.
+
+ - Implement the test manager side of the test result reporting.
+
+ - Extend the selftest with some setup that report all kinds of test
+ results.
+
+ - Implement script/whatever feeding builds to the test manager from the
+ tinderboxes.
+
+ - The toplevel test driver is a VBox thing that must be derived from the
+ base TestDriver class or maybe the VBox one. It should move from
+ toptestdriver to testdriver and be renamed to vboxtltd or smth.
+
+ - Create a vbox testdriver that boots the t-xppro VM once and that's it.
+
+ - Create a selftest setup which tests booting t-xppro taking builds from
+ the tinderbox.
+
+
+Milestone #3
+------------
+
+The goal for this milestone is configuration and converting current testcases,
+the result will be the a minimal test deployment (4-5 new testboxes).
+
+Tasks in somewhat prioritized order:
+
+ - Implement testcase configuration.
+
+ - Implement testgroup configuration.
+
+ - Implement build source configuration.
+
+ - Implement scheduling group configuration.
+
+ - Implement global resource configuration.
+
+ - Re-visit the testbox configuration.
+
+ - Black listing of builds.
+
+ - Implement simple failure analysis and reporting.
+
+ - Implement the initial smoke tests modelled on the current smoke tests.
+
+ - Implement installation tests for Windows guests.
+
+ - Implement installation tests for Linux guests.
+
+ - Implement installation tests for Solaris guest.
+
+ - Implement installation tests for OS/2 guest.
+
+ - Set up a small test deployment.
+
+
+Further work
+------------
+
+After milestone #3 has been reached and issues found by the other team members
+have been addressed, we will probably go for full deployment.
+
+Beyond this point we will need to improve reporting and analysis. There may be
+configuration aspects needing reporting as well.
+
+Once deployed, a golden rule will be that all new features shall have test
+coverage. Preferably, implemented by someone else and prior to the feature
+implementation.
+
+
+
+
+Discussion Logs
+===============
+
+2009-07-21,22,23 Various Discussions with Michal and/or Klaus
+-------------------------------------------------------------
+
+- Scheduling of tests requiring more than one testbox.
+- Scheduling of tests that cannot be executing concurrently on several machines
+ because of some global resource like an iSCSI target.
+- Manually create the test config permutations instead of having the test
+ manager create all possible ones and wasting time.
+- Distinguish between built types so we can run smoke tests on strick builds as
+ well as release ones.
+
+
+2009-07-20 Brief Discussion with Michal
+----------------------------------------
+
+- Installer for the testbox script to make bringing up a new testbox even
+ smoother.
+
+
+2009-07-16 Raw Input
+--------------------
+
+- test set. recursive collection of:
+ - hierachical subtest name (slash sep)
+ - test parameters / config
+ - bool fail/succ
+ - attributes (typed?)
+ - test time
+ - e.g. throughput
+ - subresults
+ - log
+ - screenshots,....
+
+- client package (zip) dl from server (maybe client caching)
+
+
+- thoughts on bits to do at once.
+ - We *really* need the basic bits ASAP.
+ - client -> support for test driver
+ - server -> controls configs
+ - cleanup on both sides
+
+
+2009-07-15 Raw Input
+--------------------
+
+- testing should start automatically
+- switching to branch too tedious
+- useful to be able to partition testboxes (run specific builds on some boxes, let an engineer have a few boxes for a while).
+- test specification needs to be more flexible (select tests, disable test, test scheduling (run certain tests nightly), ... )
+- testcase dependencies (blacklisting builds, run smoketests on box A before long tests on box B, ...)
+- more testing flexibility, more test than just install/moke. For instance unit tests, benchmarks, ...
+- presentation/analysis: graphs!, categorize bugs, columns reorganizing grouped by test (hierarchical), overviews, result for last day.
+- testcase specificion, variables (e.g. I/O-APIC, SMP, HWVIRT, SATA...) as sub-tests
+- interation with ILOM/...: reset systems
+- Changes needs LDAP authentication
+- historize all configuration w/ name
+- ability to run testcase locally (provided the VDI/ISO/whatever extra requirements can be met).
+
+
+-----
+
+.. [1] no such footnote
+
+-----
+
+:Status: $Id: AutomaticTestingRevamp.txt $
+:Copyright: Copyright (C) 2010-2023 Oracle Corporation.
diff --git a/src/VBox/ValidationKit/docs/Makefile.kmk b/src/VBox/ValidationKit/docs/Makefile.kmk
new file mode 100644
index 00000000..e92b5123
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/Makefile.kmk
@@ -0,0 +1,69 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Makefile for generating .html from .txt.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+DEPTH = ../../../..
+include $(KBUILD_PATH)/header.kmk
+
+# Figure out where rst2html.py is.
+ifndef VBOX_RST2HTML
+ VBOX_RST2HTML := $(firstword $(which $(foreach pyver, 3.2 3.1 3.0 2.8 2.7 2.6 2.5 2.4 ,rst2html-$(pyver).py) ) )
+ ifeq ($(VBOX_RST2HTML),)
+ if $(KBUILD_HOST) == "win" && $(VBOX_BLD_PYTHON) != "" && $(dir $(VBOX_BLD_PYTHON)) != "./"
+ VBOX_RST2HTML := $(dir $(VBOX_BLD_PYTHON))Scripts/rst2html.py
+ else
+ VBOX_RST2HTML := rst2html.py
+ endif
+ endif
+ if1of ($(KBUILD_HOST), win)
+ VBOX_RST2HTML := $(VBOX_BLD_PYTHON) $(VBOX_RST2HTML)
+ endif
+endif
+
+GENERATED_FILES = \
+ AutomaticTestingRevamp.html \
+ VBoxValidationKitReadMe.html \
+ VBoxAudioValidationKitReadMe.html \
+ TestBoxImaging.html
+
+all: $(GENERATED_FILES)
+
+$(foreach html,$(GENERATED_FILES) \
+,$(eval $(html): $(basename $(html)).txt ; $$(REDIRECT) -E LC_ALL=C -- $$(VBOX_RST2HTML) --no-generator $$< $$@))
+
+$(foreach html,$(GENERATED_FILES), $(eval $(basename $(html)).o:: $(html))) # editor compile aliases
+
+clean:
+ kmk_builtin_rm -f -- $(GENERATED_FILES)
diff --git a/src/VBox/ValidationKit/docs/TestBoxImaging.html b/src/VBox/ValidationKit/docs/TestBoxImaging.html
new file mode 100644
index 00000000..8675c137
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/TestBoxImaging.html
@@ -0,0 +1,758 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+<title>TestBoxImaging.txt</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: TestBoxImaging.html $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document">
+
+
+<div class="section" id="testbox-imaging-backup-restore">
+<h1>Testbox Imaging (Backup / Restore)</h1>
+<div class="section" id="introduction">
+<h2>Introduction</h2>
+<p>This document is explores deploying a very simple drive imaging solution to help
+avoid needing to manually reinstall testboxes when a disk goes bust or the OS
+install seems to be corrupted.</p>
+</div>
+</div>
+<div class="section" id="definitions-glossary">
+<h1>Definitions / Glossary</h1>
+<p>See AutomaticTestingRevamp.txt.</p>
+</div>
+<div class="section" id="objectives">
+<h1>Objectives</h1>
+<blockquote>
+<ul class="simple">
+<li>Off site, no admin interaction (no need for ILOM or similar).</li>
+<li>OS independent.</li>
+<li>Space and bandwidth efficient.</li>
+<li>As automatic as possible.</li>
+<li>Logging.</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="overview-of-the-solution">
+<h1>Overview of the Solution</h1>
+<p>Here is a brief summary:</p>
+<blockquote>
+<ul class="simple">
+<li>Always boot testboxes via PXE using PXELINUX.</li>
+<li>Default configuration is local boot (hard disk / SSD)</li>
+<li>Restore/backup action triggered by machine specific PXE config.</li>
+<li>Boots special debian maintenance install off NFS.</li>
+<li>A maintenance service (systemd style) does the work.</li>
+<li>The service reads action from TFTP location and performs it.</li>
+<li>When done the service removes the TFTP machine specific config
+and reboots the system.</li>
+</ul>
+</blockquote>
+<dl class="docutils">
+<dt>Maintenance actions are:</dt>
+<dd><ul class="first last simple">
+<li>backup</li>
+<li>backup-again</li>
+<li>restore</li>
+<li>refresh-info</li>
+<li>rescue</li>
+</ul>
+</dd>
+</dl>
+<p>Possible modifier that indicates a subset of disk on testboxes with other OSes
+installed. Support for partition level backup/restore is not explored here.</p>
+<div class="section" id="how-to-use">
+<h2>How to use</h2>
+<p>To perform one of the above maintenance actions on a testbox, run the
+<tt class="docutils literal"><span class="pre">testbox-pxe-conf.sh</span></tt> script:</p>
+<pre class="literal-block">
+/mnt/testbox-tftp/pxeclient.cfg/testbox-pxe-conf.sh 10.165.98.220 rescue
+</pre>
+<p>Then trigger a reboot. The box will then boot the NFS rooted debian image and
+execute the maintenance action. On success, it will remove the testbox hex-IP
+config file and reboot again.</p>
+</div>
+</div>
+<div class="section" id="storage-server">
+<h1>Storage Server</h1>
+<p>The storage server will have three areas used here. Using NFS for all three
+avoids extra work getting CIFS sharing right too (NFS is already a pain).</p>
+<blockquote>
+<ol class="arabic simple">
+<li>/export/testbox-tftp - TFTP config area. Read-write.</li>
+<li>/export/testbox-backup - Images and logs. Read-write.</li>
+<li>/export/testbox-nfsroot - Custom debian. Read-only, no root squash.</li>
+</ol>
+</blockquote>
+</div>
+<div class="section" id="tftp-export-testbox-tftp">
+<h1>TFTP (/export/testbox-tftp)</h1>
+<p>The testbox-tftp share needs to be writable, root squashing is okay.</p>
+<p>We need files from both PXELINUX and SYSLINUX to make this work now. On a
+debian system, the <tt class="docutils literal">pxelinux</tt> and <tt class="docutils literal">syslinux</tt> packages needs to be
+installed. We actually do this further down when setting up the nfsroot, so
+it's possible to get them from there by postponing this step a little. On
+debian 8.6.0 the PXELINUX files are found in <tt class="docutils literal">/usr/lib/PXELINUX</tt> and the
+SYSLINUX ones in <tt class="docutils literal">/usr/lib/syslinux</tt>.</p>
+<p>The initial PXE image as well as associated modules comes in three variants,
+BIOS, 32-bit EFI and 64-bit EFI. We'll only need the BIOS one for now.
+Perform the following copy operations:</p>
+<pre class="literal-block">
+cp /usr/lib/PXELINUX/pxelinux.0 /mnt/testbox-tftp/
+cp /usr/lib/syslinux/modules/*/ldlinux.* /mnt/testbox-tftp/
+cp -R /usr/lib/syslinux/modules/bios /mnt/testbox-tftp/
+cp -R /usr/lib/syslinux/modules/efi32 /mnt/testbox-tftp/
+cp -R /usr/lib/syslinux/modules/efi64 /mnt/testbox-tftp/
+</pre>
+<p>For simplicity, all the testboxes boot using good old fashioned BIOS, no EFI.
+However, it doesn't really hurt to be prepared.</p>
+<p>The PXELINUX related files goes in the root of the testbox-tftp share. (As
+mentioned further down, these can be installed on a debian system by running
+<tt class="docutils literal"><span class="pre">apt-get</span> install pxelinux syslinux</tt>.) We need the <tt class="docutils literal">*pxelinux.0</tt> files
+typically found in <tt class="docutils literal">/usr/lib/PXELINUX/</tt> on debian systems (recent ones
+anyway). It is possible we may need one ore more fo the modules <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a> that
+ships with PXELINUX/SYSLINUX, so do copy <tt class="docutils literal">/usr/lib/syslinux/modules</tt> to
+<tt class="docutils literal"><span class="pre">testbox-tftp/modules</span></tt> as well.</p>
+<p>The directory layout related to the configuration files is dictated by the
+PXELINUX configuration file searching algorithm <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[2]</a>. Create a subdirectory
+<tt class="docutils literal">pxelinux.cfg/</tt> under <tt class="docutils literal"><span class="pre">testbox-tftp</span></tt> and create the world readable file
+<tt class="docutils literal">default</tt> with the following content:</p>
+<pre class="literal-block">
+PATH bios
+DEFAULT local-boot
+LABEL local-boot
+LOCALBOOT
+</pre>
+<p>This will make the default behavior to boot the local disk system.</p>
+<p>Copy the <tt class="docutils literal"><span class="pre">testbox-pxe-conf.sh</span></tt> script file found in the same directory as
+this document to <tt class="docutils literal"><span class="pre">/mnt/testbox-tftp/pxelinux.cfg/</span></tt>. Edit the copy to correct
+the IP addresses near the top, as well as any linux, TFTP and PXE details near
+the bottom of the file. This script will generate the PXE configuration file
+when performing maintenance on a testbox.</p>
+</div>
+<div class="section" id="images-and-logs-export-testbox-backup">
+<h1>Images and logs (/export/testbox-backup)</h1>
+<p>The testbox-backup share needs to be writable, root squashing is okay.</p>
+<p>In the root there must be a file <tt class="docutils literal"><span class="pre">testbox-backup</span></tt> so we can easily tell
+whether we've actually mounted the share or are just staring at an empty mount
+point directory.</p>
+<p>The <tt class="docutils literal"><span class="pre">testbox-maintenance.sh</span></tt> script maintains a global log in the root
+directory that's called <tt class="docutils literal">maintenance.log</tt>. Errors will be logged there as
+well as a ping and the action.</p>
+<p>We use a directory layout based on dotted decimal IP addresses here, so for a
+server with the IP 10.40.41.42 all its file will be under <tt class="docutils literal">10.40.41.42/</tt>:</p>
+<dl class="docutils">
+<dt><tt class="docutils literal">&lt;hostname&gt;</tt></dt>
+<dd>The name of the testbox (empty file). Help finding a testbox by name.</dd>
+<dt><tt class="docutils literal"><span class="pre">testbox-info.txt</span></tt></dt>
+<dd>Information about the testbox. Starting off with the name, decimal IP,
+PXELINUX style hexadecimal IP, and more.</dd>
+<dt><tt class="docutils literal">maintenance.log</tt></dt>
+<dd>Maintenance log file recording what the maintenance service does.</dd>
+<dt><tt class="docutils literal"><span class="pre">disk-devices.lst</span></tt></dt>
+<dd>Optional list of disk devices to consider backuping up or restoring. This is
+intended for testboxes with additional disks that are used for other purposes
+and should touched.</dd>
+<dt><tt class="docutils literal">sda.raw.gz</tt></dt>
+<dd>The gzipped raw copy of the sda device of the testbox.</dd>
+<dt><tt class="docutils literal"><span class="pre">sd[bcdefgh].raw.gz</span></tt></dt>
+<dd>The gzipped raw copy sdb, sdc, sde, sdf, sdg, sdh, etc if any of them exists
+and are disks/SSDs.</dd>
+<dt>Note! If it turns out we can be certain to get a valid host name, we might just</dt>
+<dd>switch to use the hostname as the directory name instead of the IP.</dd>
+</dl>
+</div>
+<div class="section" id="debian-nfs-root-export-testbox-nfsroot">
+<h1>Debian NFS root (/export/testbox-nfsroot)</h1>
+<p>The testbox-nfsroot share should be read-only and must <strong>not</strong> have root
+squashing enabled. Also, make sure setting the set-uid-bit is allowed by the
+server, or <tt class="docutils literal">su` and ``sudo</tt> won't work</p>
+<p>There are several ways of creating a debian nfsroot, but since we've got a
+tool like VirtualBox around we've just installed it in a VM, prepared it,
+and copied it onto the NFS server share.</p>
+<p>As of writing debian 8.6.0 is current, so a minimal 64-bit install of it was
+done in a VM. After installation the following modifications was done:</p>
+<blockquote>
+<ul>
+<li><p class="first"><tt class="docutils literal"><span class="pre">apt-get</span> install pxelinux syslinux <span class="pre">initramfs-tools</span> zip gddrescue sudo joe</tt>
+and optionally <tt class="docutils literal"><span class="pre">apt-get</span> install smbclient <span class="pre">cifs-utils</span></tt>.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">/etc/default/grub</tt> was modified to set <tt class="docutils literal">GRUB_CMDLINE_LINUX_DEFAULT</tt> to
+<tt class="docutils literal">&quot;&quot;</tt> instead of <tt class="docutils literal">&quot;quiet&quot;</tt>. This allows us to see messages during boot
+and perhaps spot why something doesn't work on a testbox. Regenerate the
+grub configuration file by running <tt class="docutils literal"><span class="pre">update-grub</span></tt> afterwards.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">/etc/sudoers</tt> was modified to allow the <tt class="docutils literal">vbox</tt> user use sudo without
+requring any password.</p>
+</li>
+<li><p class="first">Create the directory <tt class="docutils literal">/etc/systemd/system/getty&#64;tty1.service.d</tt> and create
+the file <tt class="docutils literal">noclear.conf</tt> in it with the following content:</p>
+<pre class="literal-block">
+[Service]
+TTYVTDisallocate=no
+</pre>
+<p>This stops getty from clearing VT1 and let us see the tail of the boot up
+messages, which includes messages from the testbox-maintenance service.</p>
+</li>
+<li><p class="first">Mount the testbox-nfsroot under <tt class="docutils literal">/mnt/</tt> with write privileges. (The write
+privileges are temporary - don't forget to remove them later on.):</p>
+<pre class="literal-block">
+mount -t nfs myserver.com:/export/testbox-nfsroot
+</pre>
+<p>Note! Adding <tt class="docutils literal"><span class="pre">-o</span> nfsvers=3</tt> may help with some NTFv4 servers.</p>
+</li>
+<li><p class="first">Copy the debian root and dev file system onto nfsroot. If you have ssh
+access to the NFS server, the quickest way to do it is to use <tt class="docutils literal">tar</tt>:</p>
+<pre class="literal-block">
+tar -cz --one-file-system -f /mnt/testbox-maintenance-nfsroot.tar.gz . dev/
+</pre>
+<p>An alternative is <tt class="docutils literal">cp <span class="pre">-ax</span> . /mnt/. &amp;&amp;&nbsp; cp <span class="pre">-ax</span> dev/. /mnt/dev/.</tt> but this
+is quite a bit slower, obviously.</p>
+</li>
+<li><p class="first">Edit <tt class="docutils literal">/etc/ssh/sshd_config</tt> setting <tt class="docutils literal">PermitRootLogin</tt> to <tt class="docutils literal">yes</tt> so we can ssh
+in as root later on.</p>
+</li>
+<li><p class="first">chroot into the nfsroot: <tt class="docutils literal">chroot /mnt/</tt></p>
+<blockquote>
+<ul>
+<li><p class="first"><tt class="docutils literal">mount <span class="pre">-o</span> proc proc /proc</tt></p>
+</li>
+<li><p class="first"><tt class="docutils literal">mount <span class="pre">-o</span> sysfs sysfs /sys</tt></p>
+</li>
+<li><p class="first"><tt class="docutils literal">mkdir <span class="pre">/mnt/testbox-tftp</span> <span class="pre">/mnt/testbox-backup</span></tt></p>
+</li>
+<li><p class="first">Recreate <tt class="docutils literal">/etc/fstab</tt> with:</p>
+<pre class="literal-block">
+proc /proc proc defaults 0 0
+/dev/nfs / nfs defaults 1 1
+10.42.1.1:/export/testbox-tftp /mnt/testbox-tftp nfs tcp,nfsvers=3,noauto 2 2
+10.42.1.1:/export/testbox-backup /mnt/testbox-backup nfs tcp,nfsvers=3,noauto 3 3
+</pre>
+<p>We use NFS version 3 as that works better for our NFS server and client,
+remove if not necessary. The <tt class="docutils literal">noauto</tt> option is to work around mount
+trouble during early bootup on some of our boxes.</p>
+</li>
+<li><p class="first">Do <tt class="docutils literal">mount <span class="pre">/mnt/testbox-tftp</span> &amp;&amp; mount <span class="pre">/mnt/testbox-backup</span></tt> to mount the
+two shares. This may be a good time to execute the instructions in the
+sections above relating to these two shares.</p>
+</li>
+<li><p class="first">Edit <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/initramfs.conf</span></tt> and change the <tt class="docutils literal">MODULES</tt>
+value from <tt class="docutils literal">most</tt> to <tt class="docutils literal">netboot</tt>.</p>
+</li>
+<li><p class="first">Append <tt class="docutils literal">aufs</tt> to <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/modules</span></tt>. The advanced
+multi-layered unification filesystem (aufs) enables us to use a
+read-only NFS root. <a class="footnote-reference" href="#footnote-3" id="footnote-reference-3">[3]</a> <a class="footnote-reference" href="#footnote-4" id="footnote-reference-4">[4]</a> <a class="footnote-reference" href="#footnote-5" id="footnote-reference-5">[5]</a></p>
+</li>
+<li><p class="first">Create <tt class="docutils literal"><span class="pre">/etc/initramfs-tools/scripts/init-bottom/00_aufs_init</span></tt> as
+an executable file with the following content:</p>
+<pre class="literal-block">
+#!/bin/sh
+# Don't run during update-initramfs:
+case &quot;$1&quot; in
+ prereqs)
+ exit 0;
+ ;;
+esac
+
+modprobe aufs
+mkdir -p /ro /rw /aufs
+mount -t tmpfs tmpfs /rw -o noatime,mode=0755
+mount --move $rootmnt /ro
+mount -t aufs aufs /aufs -o noatime,dirs=/rw:/ro=ro
+mkdir -p /aufs/rw /aufs/ro
+mount --move /ro /aufs/ro
+mount --move /rw /aufs/rw
+mount --move /aufs /root
+exit 0
+</pre>
+</li>
+<li><p class="first">Update the init ramdisk: <tt class="docutils literal"><span class="pre">update-initramfs</span> <span class="pre">-u</span> <span class="pre">-k</span> all</tt></p>
+<dl class="docutils">
+<dt>Note! It may be necessary to do <tt class="docutils literal">mount <span class="pre">-t</span> tmpfs tmpfs /var/tmp</tt> to help</dt>
+<dd><p class="first last">this operation succeed.</p>
+</dd>
+</dl>
+</li>
+<li><p class="first">Copy <tt class="docutils literal">/boot</tt> to <tt class="docutils literal"><span class="pre">/mnt/testbox-tftp/maintenance-boot/</span></tt>.</p>
+</li>
+<li><p class="first">Copy the <tt class="docutils literal"><span class="pre">testbox-maintenance.sh</span></tt> file found in the same directory as this
+document to <tt class="docutils literal">/root/scripts/</tt> (need to create the dir) and make it
+executable.</p>
+</li>
+<li><p class="first">Create the systemd service file for the maintenance service as
+<tt class="docutils literal"><span class="pre">/etc/systemd/system/testbox-maintenance.service</span></tt> with the content:</p>
+<pre class="literal-block">
+[Unit]
+Description=Testbox Maintenance
+After=network.target
+Before=getty&#64;tty1.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=True
+ExecStart=/root/scripts/testbox-maintenance.sh
+ExecStartPre=/bin/echo -e \033%G
+ExecReload=/bin/kill -HUP $MAINPID
+WorkingDirectory=/tmp
+Environment=TERM=xterm
+StandardOutput=journal+console
+
+[Install]
+WantedBy=multi-user.target
+</pre>
+</li>
+<li><p class="first">Enable our service: <tt class="docutils literal">systemctl enable <span class="pre">/etc/systemd/system/testbox-maintenance.service</span></tt></p>
+</li>
+<li><p class="first">xxxx ... more ???</p>
+</li>
+<li><p class="first">Before leaving the chroot, do <tt class="docutils literal">mount /proc /sys <span class="pre">/mnt/testbox-*</span></tt>.</p>
+</li>
+</ul>
+</blockquote>
+</li>
+<li><p class="first">Testing the setup from a VM is kind of useful (if the nfs server can be
+convinced to accept root nfs mounts from non-privileged clinet ports):</p>
+<blockquote>
+<ul>
+<li><p class="first">Create a VM using the 64-bit debian profile. Let's call it &quot;pxe-vm&quot;.</p>
+</li>
+<li><p class="first">Mount the TFTP share somewhere, like M: or /mnt/testbox-tftp.</p>
+</li>
+<li><p class="first">Reconfigure the NAT DHCP and TFTP bits:</p>
+<pre class="literal-block">
+VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/AboveDriver NAT
+VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Action mergeconfig
+VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/TFTPPrefix M:/
+VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/BootFile pxelinux.0
+</pre>
+</li>
+<li><p class="first">Create the file <tt class="docutils literal"><span class="pre">testbox-tftp/pxelinux.cfg/0A00020F</span></tt> containing:</p>
+<pre class="literal-block">
+PATH bios
+DEFAULT maintenance
+LABEL maintenance
+ MENU LABEL Maintenance (NFS)
+ KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64
+ APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 ro ip=dhcp aufs=tmpfs \
+ boot=nfs root=/dev/nfs nfsroot=10.42.1.1:/export/testbox-nfsroot
+LABEL local-boot
+LOCALBOOT
+</pre>
+</li>
+</ul>
+</blockquote>
+</li>
+</ul>
+</blockquote>
+</div>
+<div class="section" id="troubleshooting">
+<h1>Troubleshooting</h1>
+<dl class="docutils">
+<dt><tt class="docutils literal"><span class="pre">PXE-E11</span></tt> or something like <tt class="docutils literal">No ARP reply</tt></dt>
+<dd>You probably got the TFTP and DHCP on different machines. Try move the TFTP
+to the same machine as the DHCP, then the PXE stack won't have to do any
+additional ARP resolving. Google results suggest that a congested network
+could use the ARP reply to get lost. Our suspicion is that it might also be
+related to the PXE stack shipping with the NIC.</dd>
+</dl>
+<hr class="docutils" />
+<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>See <a class="reference external" href="http://www.syslinux.org/wiki/index.php?title=Category:Modules">http://www.syslinux.org/wiki/index.php?title=Category:Modules</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[2]</a></td><td>See <a class="reference external" href="http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration">http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="footnote-3" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-3">[3]</a></td><td>See <a class="reference external" href="https://en.wikipedia.org/wiki/Aufs">https://en.wikipedia.org/wiki/Aufs</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="footnote-4" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-4">[4]</a></td><td>See <a class="reference external" href="http://shitwefoundout.com/wiki/Diskless_ubuntu">http://shitwefoundout.com/wiki/Diskless_ubuntu</a></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="footnote-5" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-5">[5]</a></td><td>See <a class="reference external" href="http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/">http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/</a></td></tr>
+</tbody>
+</table>
+<hr class="docutils" />
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: TestBoxImaging.html $</td>
+</tr>
+<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2023 Oracle Corporation.</td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+</body>
+</html>
diff --git a/src/VBox/ValidationKit/docs/TestBoxImaging.txt b/src/VBox/ValidationKit/docs/TestBoxImaging.txt
new file mode 100644
index 00000000..9468d944
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/TestBoxImaging.txt
@@ -0,0 +1,368 @@
+
+Testbox Imaging (Backup / Restore)
+==================================
+
+
+Introduction
+------------
+
+This document is explores deploying a very simple drive imaging solution to help
+avoid needing to manually reinstall testboxes when a disk goes bust or the OS
+install seems to be corrupted.
+
+
+Definitions / Glossary
+======================
+
+See AutomaticTestingRevamp.txt.
+
+
+Objectives
+==========
+
+ - Off site, no admin interaction (no need for ILOM or similar).
+ - OS independent.
+ - Space and bandwidth efficient.
+ - As automatic as possible.
+ - Logging.
+
+
+Overview of the Solution
+========================
+
+Here is a brief summary:
+
+ - Always boot testboxes via PXE using PXELINUX.
+ - Default configuration is local boot (hard disk / SSD)
+ - Restore/backup action triggered by machine specific PXE config.
+ - Boots special debian maintenance install off NFS.
+ - A maintenance service (systemd style) does the work.
+ - The service reads action from TFTP location and performs it.
+ - When done the service removes the TFTP machine specific config
+ and reboots the system.
+
+Maintenance actions are:
+ - backup
+ - backup-again
+ - restore
+ - refresh-info
+ - rescue
+
+Possible modifier that indicates a subset of disk on testboxes with other OSes
+installed. Support for partition level backup/restore is not explored here.
+
+
+How to use
+----------
+
+To perform one of the above maintenance actions on a testbox, run the
+``testbox-pxe-conf.sh`` script::
+
+ /mnt/testbox-tftp/pxeclient.cfg/testbox-pxe-conf.sh 10.165.98.220 rescue
+
+Then trigger a reboot. The box will then boot the NFS rooted debian image and
+execute the maintenance action. On success, it will remove the testbox hex-IP
+config file and reboot again.
+
+
+Storage Server
+==============
+
+The storage server will have three areas used here. Using NFS for all three
+avoids extra work getting CIFS sharing right too (NFS is already a pain).
+
+ 1. /export/testbox-tftp - TFTP config area. Read-write.
+ 2. /export/testbox-backup - Images and logs. Read-write.
+ 3. /export/testbox-nfsroot - Custom debian. Read-only, no root squash.
+
+
+TFTP (/export/testbox-tftp)
+============================
+
+The testbox-tftp share needs to be writable, root squashing is okay.
+
+We need files from both PXELINUX and SYSLINUX to make this work now. On a
+debian system, the ``pxelinux`` and ``syslinux`` packages needs to be
+installed. We actually do this further down when setting up the nfsroot, so
+it's possible to get them from there by postponing this step a little. On
+debian 8.6.0 the PXELINUX files are found in ``/usr/lib/PXELINUX`` and the
+SYSLINUX ones in ``/usr/lib/syslinux``.
+
+The initial PXE image as well as associated modules comes in three variants,
+BIOS, 32-bit EFI and 64-bit EFI. We'll only need the BIOS one for now.
+Perform the following copy operations::
+
+ cp /usr/lib/PXELINUX/pxelinux.0 /mnt/testbox-tftp/
+ cp /usr/lib/syslinux/modules/*/ldlinux.* /mnt/testbox-tftp/
+ cp -R /usr/lib/syslinux/modules/bios /mnt/testbox-tftp/
+ cp -R /usr/lib/syslinux/modules/efi32 /mnt/testbox-tftp/
+ cp -R /usr/lib/syslinux/modules/efi64 /mnt/testbox-tftp/
+
+
+For simplicity, all the testboxes boot using good old fashioned BIOS, no EFI.
+However, it doesn't really hurt to be prepared.
+
+The PXELINUX related files goes in the root of the testbox-tftp share. (As
+mentioned further down, these can be installed on a debian system by running
+``apt-get install pxelinux syslinux``.) We need the ``*pxelinux.0`` files
+typically found in ``/usr/lib/PXELINUX/`` on debian systems (recent ones
+anyway). It is possible we may need one ore more fo the modules [1]_ that
+ships with PXELINUX/SYSLINUX, so do copy ``/usr/lib/syslinux/modules`` to
+``testbox-tftp/modules`` as well.
+
+
+The directory layout related to the configuration files is dictated by the
+PXELINUX configuration file searching algorithm [2]_. Create a subdirectory
+``pxelinux.cfg/`` under ``testbox-tftp`` and create the world readable file
+``default`` with the following content::
+
+ PATH bios
+ DEFAULT local-boot
+ LABEL local-boot
+ LOCALBOOT
+
+This will make the default behavior to boot the local disk system.
+
+Copy the ``testbox-pxe-conf.sh`` script file found in the same directory as
+this document to ``/mnt/testbox-tftp/pxelinux.cfg/``. Edit the copy to correct
+the IP addresses near the top, as well as any linux, TFTP and PXE details near
+the bottom of the file. This script will generate the PXE configuration file
+when performing maintenance on a testbox.
+
+
+Images and logs (/export/testbox-backup)
+=========================================
+
+The testbox-backup share needs to be writable, root squashing is okay.
+
+In the root there must be a file ``testbox-backup`` so we can easily tell
+whether we've actually mounted the share or are just staring at an empty mount
+point directory.
+
+The ``testbox-maintenance.sh`` script maintains a global log in the root
+directory that's called ``maintenance.log``. Errors will be logged there as
+well as a ping and the action.
+
+We use a directory layout based on dotted decimal IP addresses here, so for a
+server with the IP 10.40.41.42 all its file will be under ``10.40.41.42/``:
+
+``<hostname>``
+ The name of the testbox (empty file). Help finding a testbox by name.
+
+``testbox-info.txt``
+ Information about the testbox. Starting off with the name, decimal IP,
+ PXELINUX style hexadecimal IP, and more.
+
+``maintenance.log``
+ Maintenance log file recording what the maintenance service does.
+
+``disk-devices.lst``
+ Optional list of disk devices to consider backuping up or restoring. This is
+ intended for testboxes with additional disks that are used for other purposes
+ and should touched.
+
+``sda.raw.gz``
+ The gzipped raw copy of the sda device of the testbox.
+
+``sd[bcdefgh].raw.gz``
+ The gzipped raw copy sdb, sdc, sde, sdf, sdg, sdh, etc if any of them exists
+ and are disks/SSDs.
+
+
+Note! If it turns out we can be certain to get a valid host name, we might just
+ switch to use the hostname as the directory name instead of the IP.
+
+
+Debian NFS root (/export/testbox-nfsroot)
+==========================================
+
+The testbox-nfsroot share should be read-only and must **not** have root
+squashing enabled. Also, make sure setting the set-uid-bit is allowed by the
+server, or ``su` and ``sudo`` won't work
+
+There are several ways of creating a debian nfsroot, but since we've got a
+tool like VirtualBox around we've just installed it in a VM, prepared it,
+and copied it onto the NFS server share.
+
+As of writing debian 8.6.0 is current, so a minimal 64-bit install of it was
+done in a VM. After installation the following modifications was done:
+
+ - ``apt-get install pxelinux syslinux initramfs-tools zip gddrescue sudo joe``
+ and optionally ``apt-get install smbclient cifs-utils``.
+
+ - ``/etc/default/grub`` was modified to set ``GRUB_CMDLINE_LINUX_DEFAULT`` to
+ ``""`` instead of ``"quiet"``. This allows us to see messages during boot
+ and perhaps spot why something doesn't work on a testbox. Regenerate the
+ grub configuration file by running ``update-grub`` afterwards.
+
+ - ``/etc/sudoers`` was modified to allow the ``vbox`` user use sudo without
+ requring any password.
+
+ - Create the directory ``/etc/systemd/system/getty@tty1.service.d`` and create
+ the file ``noclear.conf`` in it with the following content::
+
+ [Service]
+ TTYVTDisallocate=no
+
+ This stops getty from clearing VT1 and let us see the tail of the boot up
+ messages, which includes messages from the testbox-maintenance service.
+
+ - Mount the testbox-nfsroot under ``/mnt/`` with write privileges. (The write
+ privileges are temporary - don't forget to remove them later on.)::
+
+ mount -t nfs myserver.com:/export/testbox-nfsroot
+
+ Note! Adding ``-o nfsvers=3`` may help with some NTFv4 servers.
+
+ - Copy the debian root and dev file system onto nfsroot. If you have ssh
+ access to the NFS server, the quickest way to do it is to use ``tar``::
+
+ tar -cz --one-file-system -f /mnt/testbox-maintenance-nfsroot.tar.gz . dev/
+
+ An alternative is ``cp -ax . /mnt/. && cp -ax dev/. /mnt/dev/.`` but this
+ is quite a bit slower, obviously.
+
+ - Edit ``/etc/ssh/sshd_config`` setting ``PermitRootLogin`` to ``yes`` so we can ssh
+ in as root later on.
+
+ - chroot into the nfsroot: ``chroot /mnt/``
+
+ - ``mount -o proc proc /proc``
+
+ - ``mount -o sysfs sysfs /sys``
+
+ - ``mkdir /mnt/testbox-tftp /mnt/testbox-backup``
+
+ - Recreate ``/etc/fstab`` with::
+
+ proc /proc proc defaults 0 0
+ /dev/nfs / nfs defaults 1 1
+ 10.42.1.1:/export/testbox-tftp /mnt/testbox-tftp nfs tcp,nfsvers=3,noauto 2 2
+ 10.42.1.1:/export/testbox-backup /mnt/testbox-backup nfs tcp,nfsvers=3,noauto 3 3
+
+ We use NFS version 3 as that works better for our NFS server and client,
+ remove if not necessary. The ``noauto`` option is to work around mount
+ trouble during early bootup on some of our boxes.
+
+ - Do ``mount /mnt/testbox-tftp && mount /mnt/testbox-backup`` to mount the
+ two shares. This may be a good time to execute the instructions in the
+ sections above relating to these two shares.
+
+ - Edit ``/etc/initramfs-tools/initramfs.conf`` and change the ``MODULES``
+ value from ``most`` to ``netboot``.
+
+ - Append ``aufs`` to ``/etc/initramfs-tools/modules``. The advanced
+ multi-layered unification filesystem (aufs) enables us to use a
+ read-only NFS root. [3]_ [4]_ [5]_
+
+ - Create ``/etc/initramfs-tools/scripts/init-bottom/00_aufs_init`` as
+ an executable file with the following content::
+
+ #!/bin/sh
+ # Don't run during update-initramfs:
+ case "$1" in
+ prereqs)
+ exit 0;
+ ;;
+ esac
+
+ modprobe aufs
+ mkdir -p /ro /rw /aufs
+ mount -t tmpfs tmpfs /rw -o noatime,mode=0755
+ mount --move $rootmnt /ro
+ mount -t aufs aufs /aufs -o noatime,dirs=/rw:/ro=ro
+ mkdir -p /aufs/rw /aufs/ro
+ mount --move /ro /aufs/ro
+ mount --move /rw /aufs/rw
+ mount --move /aufs /root
+ exit 0
+
+ - Update the init ramdisk: ``update-initramfs -u -k all``
+
+ Note! It may be necessary to do ``mount -t tmpfs tmpfs /var/tmp`` to help
+ this operation succeed.
+
+ - Copy ``/boot`` to ``/mnt/testbox-tftp/maintenance-boot/``.
+
+ - Copy the ``testbox-maintenance.sh`` file found in the same directory as this
+ document to ``/root/scripts/`` (need to create the dir) and make it
+ executable.
+
+ - Create the systemd service file for the maintenance service as
+ ``/etc/systemd/system/testbox-maintenance.service`` with the content::
+
+ [Unit]
+ Description=Testbox Maintenance
+ After=network.target
+ Before=getty@tty1.service
+
+ [Service]
+ Type=oneshot
+ RemainAfterExit=True
+ ExecStart=/root/scripts/testbox-maintenance.sh
+ ExecStartPre=/bin/echo -e \033%G
+ ExecReload=/bin/kill -HUP $MAINPID
+ WorkingDirectory=/tmp
+ Environment=TERM=xterm
+ StandardOutput=journal+console
+
+ [Install]
+ WantedBy=multi-user.target
+
+ - Enable our service: ``systemctl enable /etc/systemd/system/testbox-maintenance.service``
+
+ - xxxx ... more ???
+
+ - Before leaving the chroot, do ``mount /proc /sys /mnt/testbox-*``.
+
+
+ - Testing the setup from a VM is kind of useful (if the nfs server can be
+ convinced to accept root nfs mounts from non-privileged clinet ports):
+
+ - Create a VM using the 64-bit debian profile. Let's call it "pxe-vm".
+ - Mount the TFTP share somewhere, like M: or /mnt/testbox-tftp.
+ - Reconfigure the NAT DHCP and TFTP bits::
+
+ VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/AboveDriver NAT
+ VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Action mergeconfig
+ VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/TFTPPrefix M:/
+ VBoxManage setextradata pxe-vm VBoxInternal/PDM/DriverTransformations/pxe/Config/BootFile pxelinux.0
+
+ - Create the file ``testbox-tftp/pxelinux.cfg/0A00020F`` containing::
+
+ PATH bios
+ DEFAULT maintenance
+ LABEL maintenance
+ MENU LABEL Maintenance (NFS)
+ KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64
+ APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 ro ip=dhcp aufs=tmpfs \
+ boot=nfs root=/dev/nfs nfsroot=10.42.1.1:/export/testbox-nfsroot
+ LABEL local-boot
+ LOCALBOOT
+
+
+Troubleshooting
+===============
+
+``PXE-E11`` or something like ``No ARP reply``
+ You probably got the TFTP and DHCP on different machines. Try move the TFTP
+ to the same machine as the DHCP, then the PXE stack won't have to do any
+ additional ARP resolving. Google results suggest that a congested network
+ could use the ARP reply to get lost. Our suspicion is that it might also be
+ related to the PXE stack shipping with the NIC.
+
+
+
+-----
+
+.. [1] See http://www.syslinux.org/wiki/index.php?title=Category:Modules
+.. [2] See http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration
+.. [3] See https://en.wikipedia.org/wiki/Aufs
+.. [4] See http://shitwefoundout.com/wiki/Diskless_ubuntu
+.. [5] See http://debianaddict.com/2012/06/19/diskless-debian-linux-booting-via-dhcppxenfstftp/
+
+
+-----
+
+:Status: $Id: TestBoxImaging.txt $
+:Copyright: Copyright (C) 2010-2023 Oracle Corporation.
diff --git a/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.html b/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.html
new file mode 100644
index 00000000..fe4c2f6d
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.html
@@ -0,0 +1,601 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+<title>Audio Testing of VirtualBox</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: VBoxAudioValidationKitReadMe.html $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="audio-testing-of-virtualbox">
+<h1 class="title">Audio Testing of VirtualBox</h1>
+
+<div class="section" id="overview-goal">
+<h1>Overview / Goal</h1>
+<p>The goal is to create a flexible testing framework to test the
+VirtualBox audio stack.</p>
+<p>It should be runnable with an easy-to-use setup so that also regular users
+can perform tests on request, without having to install or set up additional
+dependencies.</p>
+<p>That framework must be runnable on all host/guest combinations together with all
+audio drivers (&quot;backends&quot;) and device emulations being offered. This makes it a
+rather big testing matrix which therefore has to be processed in an automated
+fashion.</p>
+<p>Additionally it should be flexible enough to add more (custom) tests later on.</p>
+</div>
+<div class="section" id="operation">
+<h1>Operation</h1>
+<p>The framework consists of several components which try to make use as much of
+the existing audio stack code as possible. This allows the following
+operation modes:</p>
+<dl class="docutils">
+<dt>Standalone</dt>
+<dd>Playing back / recording audio data (test tones / .WAV files) in a
+standalone scenario, i.e. no VirtualBox / VMs required). This mode is using
+VirtualBox' audio (mixing) stack and available backend drivers without the
+need of VirtualBox being installed.</dd>
+<dt>Manual</dt>
+<dd>Performing single / multiple tests manually on a local machine.
+Requires a running and set up test VM.</dd>
+<dt>Automated</dt>
+<dd>Performs single / multiple tests via the Validation Kit audio test
+driver and can be triggered via the Validation Kit Test Manager.</dd>
+<dt>(Re-)validation of previously ran tests</dt>
+<dd>This takes two test sets and runs the validation / analysis on them.</dd>
+<dt>Self testing mode</dt>
+<dd>Performs standalone self tests to verify / debug the involved components.</dd>
+</dl>
+</div>
+<div class="section" id="components-and-terminology">
+<h1>Components and Terminology</h1>
+<p>The following components are in charge for performing the audio tests
+(depends on the operation mode, see above):</p>
+<ul>
+<li><p class="first">VBoxAudioTest (also known as VKAT, &quot;Validation Kit Audio Test&quot;):
+A binary which can perform the standalone audio tests mentioned above, as well
+as acting as the guest and host service(s) when performing manual or automated
+tests. It also includes the analysis / verification of audio test sets.
+VKAT also is included in host installations and Guest Additions since
+VirtualBox 7.0 to give customers and end users the opportunity to test and
+verify the audio stack.</p>
+<dl class="docutils">
+<dt>Additional features include:</dt>
+<dd><ul class="first last simple">
+<li>Automatic probing of audio backends (&quot;--probe-backends&quot;)</li>
+<li>Manual playback of test tones (&quot;play -t&quot;)</li>
+<li>Manual playback of .WAV files (&quot;play &lt;WAV-File&gt;&quot;)</li>
+<li>Manual recording to .WAV files (&quot;recording &lt;WAV-File&gt;&quot;)</li>
+<li>Manual device enumeration (sub command &quot;enum&quot;)</li>
+<li>Manual (re-)verification of test sets (sub command &quot;verify&quot;)</li>
+<li>Self-contained self tests (sub command &quot;selftest&quot;)</li>
+</ul>
+</dd>
+</dl>
+<p>See the syntax help (&quot;--help&quot;) for more.</p>
+</li>
+<li><p class="first">ATS (&quot;Audio Testing Service&quot;): Component which is being used by 1 and the
+Validation Kit audio driver (backend) to communicate across guest and host
+boundaries. Currently using a TCP/IP transport layer. Also works with VMs
+which are configured with NAT networking (&quot;reverse connection&quot;).</p>
+</li>
+<li><p class="first">Validation Kit audio test driver (tdAudioTest.py): Used for integrating and
+invoking VKAT for manual and automated tests via the Validation Kit framework
+(Test Manager). Optional. The test driver can be found at <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[1]</a>.</p>
+</li>
+<li><p class="first">Validation Kit audio driver (backend): A dedicated audio backend which
+communicates with VKAT running on the same host to perform the actual audio
+tests on a VirtualBox installation. This makes it possible to test the full
+audio stack on a running VM without any additional / external tools.</p>
+<p>On guest playback, data will be recorded, on guest recording, data will be
+injected from the host into the audio stack.</p>
+</li>
+<li><dl class="first docutils">
+<dt>Test sets contain</dt>
+<dd><ul class="first last simple">
+<li>a test manifest with all information required (vkat_manifest.ini)</li>
+<li>the generated / captured audio data (as raw PCM)</li>
+</ul>
+</dd>
+</dl>
+<p>and are either packed as .tar.gz archives or consist of a dedicated directory
+per test set.</p>
+<p>There always must be at least two test sets - one from the host side and one
+from the guest side - to perform a verification.</p>
+<p>Each test set contains a test tag so that matching test sets can be
+identified.</p>
+</li>
+</ul>
+<p>The above components are also included in VirtualBox release builds and can be
+optionally enabled (disabled by default).</p>
+<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[1]</a></td><td>src/VBox/ValidationKit/tests/audio/tdAudioTest.py</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="setup-instructions">
+<h1>Setup instructions</h1>
+<ul>
+<li><p class="first">VM needs to be configured to have audio emulation and audio testing enabled
+(via extra-data, set &quot;VBoxInternal2/Audio/Debug/Enabled&quot; to &quot;true&quot;).</p>
+</li>
+<li><p class="first">Audio input / output for the VM needs to be enabled (depending on the test).</p>
+</li>
+<li><p class="first">Start VBoxAudioTest on the guest, for example:</p>
+<p>VBoxAudioTest test --mode guest --tcp-connect-address 10.0.2.2</p>
+<dl class="docutils">
+<dt>Note: VBoxAudioTest is included with the Guest Additions starting at</dt>
+<dd><p class="first last">VirtualBox 7.0.</p>
+</dd>
+<dt>Note: Depending on the VM's networking configuration there might be further</dt>
+<dd><p class="first last">steps necessary in order to be able to reach the host from the guest.
+See the VirtualBox manual for more information.</p>
+</dd>
+</dl>
+</li>
+</ul>
+</div>
+<div class="section" id="performing-a-manual-test">
+<h1>Performing a manual test</h1>
+<ul>
+<li><p class="first">Follow &quot;Setup instructions&quot;.</p>
+</li>
+<li><p class="first">Start VBoxAudioTest on the host with selected test(s), for example:</p>
+<p>VBoxAudioTest test --mode host</p>
+<blockquote>
+<dl class="docutils">
+<dt>Note: VBoxAudioTest is included with the VirtualBox 7.0 host installers and</dt>
+<dd><p class="first last">will be installed by default.</p>
+</dd>
+</dl>
+</blockquote>
+</li>
+<li><p class="first">By default the test verification will be done automatically after running the
+tests.</p>
+</li>
+</ul>
+</div>
+<div class="section" id="advanced-performing-manual-verification">
+<h1>Advanced: Performing manual verification</h1>
+<p>VBoxAudioTest can manually be used with the &quot;verify&quot; sub command in order to
+(re-)verify previously generated test sets. It then will return different exit
+codes based on the verification result.</p>
+</div>
+<div class="section" id="advanced-performing-an-automated-test">
+<h1>Advanced: Performing an automated test</h1>
+<ul class="simple">
+<li>TxS (Test E[x]ecution Service) has to be up and running (part of the
+Validation Kit) on the guest.</li>
+<li>Invoke the tdAudioTest.py test driver, either manually or fully automated
+via Test Manager.</li>
+</ul>
+</div>
+<div class="section" id="internals-workflow-for-a-single-test">
+<h1>Internals: Workflow for a single test</h1>
+<p>When a single test is being executed on a running VM, the following (simplified)
+workflow applies:</p>
+<ul class="simple">
+<li>VKAT on the host connects to VKAT running on the guest (via ATS, also can be a
+remote machine in theory).</li>
+<li>VKAT on the host connects to Validation Kit audio driver on the host
+(via ATS, also can be a remote machine in theory).</li>
+<li><dl class="first docutils">
+<dt>For example, when doing playback tests, VKAT on the host ...</dt>
+<dd><ul class="first last">
+<li><dl class="first docutils">
+<dt>... tells the Validation Kit audio driver to start recording</dt>
+<dd>guest playback.</dd>
+</dl>
+</li>
+<li>... tells the VKAT on the guest to start playing back audio data.</li>
+<li><dl class="first docutils">
+<dt>... gathers all test data (generated from/by the guest and recorded from</dt>
+<dd>the host) as separate test sets.</dd>
+</dl>
+</li>
+<li>... starts verification / analysis of the test sets.</li>
+</ul>
+</dd>
+</dl>
+</li>
+</ul>
+</div>
+<div class="section" id="current-status-limitations">
+<h1>Current status / limitations</h1>
+<ul class="simple">
+<li><dl class="first docutils">
+<dt>The following test types are currently implemented:</dt>
+<dd><ul class="first last">
+<li>Test tone (sine wave) playback from the guest</li>
+<li>Test tone (sine wave) recording by the guest (injected from the host)</li>
+</ul>
+</dd>
+</dl>
+</li>
+<li>Only the HDA device emulation has been verified so far.</li>
+<li>Only the ALSA audio stack on Debian 10 has been verified so far.
+Note: This is different from PulseAudio using the ALSA plugin!</li>
+</ul>
+</div>
+<div class="section" id="troubleshooting">
+<h1>Troubleshooting</h1>
+<ul class="simple">
+<li>Make sure that audio device emulation is enabled and can be used within the
+guest. Also, audio input / output has to be enabled, depending on the tests.</li>
+<li>Make sure that the guest's VBoxAudioTest's instance can reach the host via
+the selected transport layer (TCP/IP by default).</li>
+<li>Increase the hosts audio logging level
+(via extra-data, set &quot;VBoxInternal2/Audio/Debug/Level&quot; to &quot;5&quot;).</li>
+<li>Increase VBoxAudioTest's verbosity level (add &quot;-v&quot;, can be specified
+multiple times).</li>
+<li>Check if the VBox release log contains any warnings / errors with the
+&quot;ValKit:&quot; prefix.</li>
+</ul>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: VBoxAudioValidationKitReadMe.html $</td>
+</tr>
+<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2021-2023 Oracle Corporation.</td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+</body>
+</html>
diff --git a/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.txt b/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.txt
new file mode 100644
index 00000000..2797f0a9
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/VBoxAudioValidationKitReadMe.txt
@@ -0,0 +1,207 @@
+Audio Testing of VirtualBox
+===========================
+
+
+Overview / Goal
+---------------
+
+The goal is to create a flexible testing framework to test the
+VirtualBox audio stack.
+
+It should be runnable with an easy-to-use setup so that also regular users
+can perform tests on request, without having to install or set up additional
+dependencies.
+
+That framework must be runnable on all host/guest combinations together with all
+audio drivers ("backends") and device emulations being offered. This makes it a
+rather big testing matrix which therefore has to be processed in an automated
+fashion.
+
+Additionally it should be flexible enough to add more (custom) tests later on.
+
+
+Operation
+---------
+
+The framework consists of several components which try to make use as much of
+the existing audio stack code as possible. This allows the following
+operation modes:
+
+Standalone
+ Playing back / recording audio data (test tones / .WAV files) in a
+ standalone scenario, i.e. no VirtualBox / VMs required). This mode is using
+ VirtualBox' audio (mixing) stack and available backend drivers without the
+ need of VirtualBox being installed.
+
+Manual
+ Performing single / multiple tests manually on a local machine.
+ Requires a running and set up test VM.
+
+Automated
+ Performs single / multiple tests via the Validation Kit audio test
+ driver and can be triggered via the Validation Kit Test Manager.
+
+(Re-)validation of previously ran tests
+ This takes two test sets and runs the validation / analysis on them.
+
+Self testing mode
+ Performs standalone self tests to verify / debug the involved components.
+
+
+Components and Terminology
+--------------------------
+
+The following components are in charge for performing the audio tests
+(depends on the operation mode, see above):
+
+- VBoxAudioTest (also known as VKAT, "Validation Kit Audio Test"):
+ A binary which can perform the standalone audio tests mentioned above, as well
+ as acting as the guest and host service(s) when performing manual or automated
+ tests. It also includes the analysis / verification of audio test sets.
+ VKAT also is included in host installations and Guest Additions since
+ VirtualBox 7.0 to give customers and end users the opportunity to test and
+ verify the audio stack.
+
+ Additional features include:
+ * Automatic probing of audio backends ("--probe-backends")
+ * Manual playback of test tones ("play -t")
+ * Manual playback of .WAV files ("play <WAV-File>")
+ * Manual recording to .WAV files ("recording <WAV-File>")
+ * Manual device enumeration (sub command "enum")
+ * Manual (re-)verification of test sets (sub command "verify")
+ * Self-contained self tests (sub command "selftest")
+
+ See the syntax help ("--help") for more.
+
+- ATS ("Audio Testing Service"): Component which is being used by 1 and the
+ Validation Kit audio driver (backend) to communicate across guest and host
+ boundaries. Currently using a TCP/IP transport layer. Also works with VMs
+ which are configured with NAT networking ("reverse connection").
+
+- Validation Kit audio test driver (tdAudioTest.py): Used for integrating and
+ invoking VKAT for manual and automated tests via the Validation Kit framework
+ (Test Manager). Optional. The test driver can be found at [1]_.
+
+- Validation Kit audio driver (backend): A dedicated audio backend which
+ communicates with VKAT running on the same host to perform the actual audio
+ tests on a VirtualBox installation. This makes it possible to test the full
+ audio stack on a running VM without any additional / external tools.
+
+ On guest playback, data will be recorded, on guest recording, data will be
+ injected from the host into the audio stack.
+
+- Test sets contain
+ - a test manifest with all information required (vkat_manifest.ini)
+ - the generated / captured audio data (as raw PCM)
+
+ and are either packed as .tar.gz archives or consist of a dedicated directory
+ per test set.
+
+ There always must be at least two test sets - one from the host side and one
+ from the guest side - to perform a verification.
+
+ Each test set contains a test tag so that matching test sets can be
+ identified.
+
+The above components are also included in VirtualBox release builds and can be
+optionally enabled (disabled by default).
+
+.. [1] src/VBox/ValidationKit/tests/audio/tdAudioTest.py
+
+
+Setup instructions
+------------------
+
+- VM needs to be configured to have audio emulation and audio testing enabled
+ (via extra-data, set "VBoxInternal2/Audio/Debug/Enabled" to "true").
+- Audio input / output for the VM needs to be enabled (depending on the test).
+- Start VBoxAudioTest on the guest, for example:
+
+ VBoxAudioTest test --mode guest --tcp-connect-address 10.0.2.2
+
+ Note: VBoxAudioTest is included with the Guest Additions starting at
+ VirtualBox 7.0.
+ Note: Depending on the VM's networking configuration there might be further
+ steps necessary in order to be able to reach the host from the guest.
+ See the VirtualBox manual for more information.
+
+
+Performing a manual test
+------------------------
+
+- Follow "Setup instructions".
+- Start VBoxAudioTest on the host with selected test(s), for example:
+
+ VBoxAudioTest test --mode host
+
+ Note: VBoxAudioTest is included with the VirtualBox 7.0 host installers and
+ will be installed by default.
+
+- By default the test verification will be done automatically after running the
+ tests.
+
+
+Advanced: Performing manual verification
+----------------------------------------
+
+VBoxAudioTest can manually be used with the "verify" sub command in order to
+(re-)verify previously generated test sets. It then will return different exit
+codes based on the verification result.
+
+
+Advanced: Performing an automated test
+--------------------------------------
+
+- TxS (Test E[x]ecution Service) has to be up and running (part of the
+ Validation Kit) on the guest.
+- Invoke the tdAudioTest.py test driver, either manually or fully automated
+ via Test Manager.
+
+
+Internals: Workflow for a single test
+-------------------------------------
+
+When a single test is being executed on a running VM, the following (simplified)
+workflow applies:
+
+- VKAT on the host connects to VKAT running on the guest (via ATS, also can be a
+ remote machine in theory).
+- VKAT on the host connects to Validation Kit audio driver on the host
+ (via ATS, also can be a remote machine in theory).
+- For example, when doing playback tests, VKAT on the host ...
+ * ... tells the Validation Kit audio driver to start recording
+ guest playback.
+ * ... tells the VKAT on the guest to start playing back audio data.
+ * ... gathers all test data (generated from/by the guest and recorded from
+ the host) as separate test sets.
+ * ... starts verification / analysis of the test sets.
+
+
+Current status / limitations
+----------------------------
+
+- The following test types are currently implemented:
+ * Test tone (sine wave) playback from the guest
+ * Test tone (sine wave) recording by the guest (injected from the host)
+- Only the HDA device emulation has been verified so far.
+- Only the ALSA audio stack on Debian 10 has been verified so far.
+ Note: This is different from PulseAudio using the ALSA plugin!
+
+
+Troubleshooting
+---------------
+
+- Make sure that audio device emulation is enabled and can be used within the
+ guest. Also, audio input / output has to be enabled, depending on the tests.
+- Make sure that the guest's VBoxAudioTest's instance can reach the host via
+ the selected transport layer (TCP/IP by default).
+- Increase the hosts audio logging level
+ (via extra-data, set "VBoxInternal2/Audio/Debug/Level" to "5").
+- Increase VBoxAudioTest's verbosity level (add "-v", can be specified
+ multiple times).
+- Check if the VBox release log contains any warnings / errors with the
+ "ValKit:" prefix.
+
+
+:Status: $Id: VBoxAudioValidationKitReadMe.txt $
+:Copyright: Copyright (C) 2021-2023 Oracle Corporation.
diff --git a/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html
new file mode 100644
index 00000000..45b4acbe
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.html
@@ -0,0 +1,467 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.19: https://docutils.sourceforge.io/" />
+<title>The VirtualBox Validation Kit</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: VBoxValidationKitReadMe.html $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="the-virtualbox-validation-kit">
+<h1 class="title">The VirtualBox Validation Kit</h1>
+
+<div class="section" id="introduction">
+<h1>Introduction</h1>
+<p>The VirtualBox Validation Kit is our new public tool for doing automated
+testing of VirtualBox. We are continually working on adding new features
+and guest operating systems to our battery of tests.</p>
+<p>We warmly welcome contributions, new ideas for good tests and fixes.</p>
+</div>
+<div class="section" id="directory-layout">
+<h1>Directory Layout</h1>
+<dl class="docutils">
+<dt>./docs/</dt>
+<dd><p class="first">The documentation for the test suite mostly lives here, the exception being
+readme.txt files that are better off living near what they concern.</p>
+<p class="last">For a definition of terms used here, see the Definitions / Glossary section
+of ./docs/AutomaticTestingRevamp.txt / ./docs/AutomaticTestingRevamp.html.</p>
+</dd>
+<dt>./testdriver/</dt>
+<dd><p class="first">Python module implementing the base test drivers and supporting stuff.
+The base test driver implementation is found in ./testdriver/base.py while
+the VBox centric specialization is in ./testdriver/vbox.py. Various VBox
+API wrappers that makes things easier to use and glosses over a lot of API
+version differences that live in ./testdriver/vboxwrappers.py.</p>
+<p>Test VM collections are often managed thru ./testdriver/vboxtestvms.py, but
+doesn't necessarily have to be, it's up to the individual test driver.</p>
+<p>For logging, reporting result, uploading useful files and such we have a
+reporter singleton sub-package, ./testdriver/reporter.py. It implements
+both local (for local testing) and remote (for testboxes + test manager)
+reporting.</p>
+<p class="last">There is also a VBoxTXS client implementation in txsclient.py and a stacked
+test driver for installing VBox (vboxinstaller.py). Most test drivers will
+use the TXS client indirectly thru vbox.py methods. The installer driver
+is a special trick for the testbox+testmanager setup.</p>
+</dd>
+<dt>./tests/</dt>
+<dd>The python scripts driving the tests. These are organized by what they
+test and are all derived from the base classes in ./testdriver (mostly from
+vbox.py of course). Most tests use one or more VMs from a standard set of
+preconfigured VMs defined by ./testdriver/vboxtestvms.py (mentioned above),
+though the installation tests used prepared ISOs and floppy images.</dd>
+<dt>./vms/</dt>
+<dd>Text documents describing the preconfigured test VMs defined by
+./testdrive/vboxtestvms.py. This will also contain description of how to
+prepare installation ISOs when we get around to it (soon).</dd>
+<dt>./utils/</dt>
+<dd><p class="first">Test utilities and lower level test programs, compiled from C, C++ and
+Assembly mostly. Generally available for both host and guest, i.e. in the
+zip and on the VBoxValidationKit.iso respectively.</p>
+<p>The Test eXecution Service (VBoxTXS) found in ./utils/TestExecServ is one
+of the more important utilities. It implements a remote execution service
+for running programs/tests inside VMs and on other test boxes. See
+./utils/TestExecServ/vboxtxs-readme.txt for more details.</p>
+<p class="last">A simple network bandwidth and latency test program can be found in
+./utils/network/NetPerf.cpp.</p>
+</dd>
+<dt>./bootsectors/</dt>
+<dd><p class="first">Boot sector test environment. This allows creating floppy images in
+assembly that tests specific CPU or device behavior. Most tests can be
+put on a USB stick, floppy or similar and booted up on real hardware for
+comparison. All floppy images can be used for manual testing by developers
+and most will be used by test drivers (./tests/<em>/td</em>.py) sooner or later.</p>
+<p class="last">The boot sector environment is heavily bound to yasm and it's ability to
+link binary images for single assembly input units. There is a &quot;library&quot;
+of standard initialization code and runtime code, which include switch to
+all (well V8086 mode is still missing, but we'll get that done eventually)
+processor modes and paging modes. The image specific code is split into
+init/driver code and test template, the latter can be instantiated for each
+process execution+paging mode.</p>
+</dd>
+<dt>./common/</dt>
+<dd>Python package containing common python code.</dd>
+<dt>./testboxscript/</dt>
+<dd>The testbox script. This is installed on testboxes used for automatic
+testing with the testmanager.</dd>
+<dt>./testmanager/</dt>
+<dd>The VirtualBox Test Manager (server side code). This is written in Python
+and currently uses postgresql as database backend for no particular reason
+other than that it was already installed on the server the test manager was
+going to run on. It's relatively generic, though there are of course
+things in there that are of more use when testing VirtualBox than other
+things. A more detailed account (though perhaps a little dated) of the
+test manager can be found in ./docs/AutomaticTestingRevamp.txt and
+./docs/AutomaticTestingRevamp.html.</dd>
+<dt>./testanalysis/</dt>
+<dd>A start a local test result analysis, comparing network test output. We'll
+probably be picking this up again later.</dd>
+<dt>./snippets/</dt>
+<dd>Various code snippets that may be turned into real tests at some point.</dd>
+</dl>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">Status:</th><td class="field-body">$Id: VBoxValidationKitReadMe.html $</td>
+</tr>
+<tr class="field"><th class="field-name">Copyright:</th><td class="field-body">Copyright (C) 2010-2023 Oracle Corporation.</td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+</body>
+</html>
diff --git a/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt
new file mode 100644
index 00000000..d1c7de60
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/VBoxValidationKitReadMe.txt
@@ -0,0 +1,113 @@
+
+The VirtualBox Validation Kit
+=============================
+
+
+Introduction
+------------
+
+The VirtualBox Validation Kit is our new public tool for doing automated
+testing of VirtualBox. We are continually working on adding new features
+and guest operating systems to our battery of tests.
+
+We warmly welcome contributions, new ideas for good tests and fixes.
+
+
+Directory Layout
+----------------
+
+./docs/
+ The documentation for the test suite mostly lives here, the exception being
+ readme.txt files that are better off living near what they concern.
+
+ For a definition of terms used here, see the Definitions / Glossary section
+ of ./docs/AutomaticTestingRevamp.txt / ./docs/AutomaticTestingRevamp.html.
+
+./testdriver/
+ Python module implementing the base test drivers and supporting stuff.
+ The base test driver implementation is found in ./testdriver/base.py while
+ the VBox centric specialization is in ./testdriver/vbox.py. Various VBox
+ API wrappers that makes things easier to use and glosses over a lot of API
+ version differences that live in ./testdriver/vboxwrappers.py.
+
+ Test VM collections are often managed thru ./testdriver/vboxtestvms.py, but
+ doesn't necessarily have to be, it's up to the individual test driver.
+
+ For logging, reporting result, uploading useful files and such we have a
+ reporter singleton sub-package, ./testdriver/reporter.py. It implements
+ both local (for local testing) and remote (for testboxes + test manager)
+ reporting.
+
+ There is also a VBoxTXS client implementation in txsclient.py and a stacked
+ test driver for installing VBox (vboxinstaller.py). Most test drivers will
+ use the TXS client indirectly thru vbox.py methods. The installer driver
+ is a special trick for the testbox+testmanager setup.
+
+./tests/
+ The python scripts driving the tests. These are organized by what they
+ test and are all derived from the base classes in ./testdriver (mostly from
+ vbox.py of course). Most tests use one or more VMs from a standard set of
+ preconfigured VMs defined by ./testdriver/vboxtestvms.py (mentioned above),
+ though the installation tests used prepared ISOs and floppy images.
+
+./vms/
+ Text documents describing the preconfigured test VMs defined by
+ ./testdrive/vboxtestvms.py. This will also contain description of how to
+ prepare installation ISOs when we get around to it (soon).
+
+./utils/
+ Test utilities and lower level test programs, compiled from C, C++ and
+ Assembly mostly. Generally available for both host and guest, i.e. in the
+ zip and on the VBoxValidationKit.iso respectively.
+
+ The Test eXecution Service (VBoxTXS) found in ./utils/TestExecServ is one
+ of the more important utilities. It implements a remote execution service
+ for running programs/tests inside VMs and on other test boxes. See
+ ./utils/TestExecServ/vboxtxs-readme.txt for more details.
+
+ A simple network bandwidth and latency test program can be found in
+ ./utils/network/NetPerf.cpp.
+
+./bootsectors/
+ Boot sector test environment. This allows creating floppy images in
+ assembly that tests specific CPU or device behavior. Most tests can be
+ put on a USB stick, floppy or similar and booted up on real hardware for
+ comparison. All floppy images can be used for manual testing by developers
+ and most will be used by test drivers (./tests/*/td*.py) sooner or later.
+
+ The boot sector environment is heavily bound to yasm and it's ability to
+ link binary images for single assembly input units. There is a "library"
+ of standard initialization code and runtime code, which include switch to
+ all (well V8086 mode is still missing, but we'll get that done eventually)
+ processor modes and paging modes. The image specific code is split into
+ init/driver code and test template, the latter can be instantiated for each
+ process execution+paging mode.
+
+./common/
+ Python package containing common python code.
+
+./testboxscript/
+ The testbox script. This is installed on testboxes used for automatic
+ testing with the testmanager.
+
+./testmanager/
+ The VirtualBox Test Manager (server side code). This is written in Python
+ and currently uses postgresql as database backend for no particular reason
+ other than that it was already installed on the server the test manager was
+ going to run on. It's relatively generic, though there are of course
+ things in there that are of more use when testing VirtualBox than other
+ things. A more detailed account (though perhaps a little dated) of the
+ test manager can be found in ./docs/AutomaticTestingRevamp.txt and
+ ./docs/AutomaticTestingRevamp.html.
+
+./testanalysis/
+ A start a local test result analysis, comparing network test output. We'll
+ probably be picking this up again later.
+
+./snippets/
+ Various code snippets that may be turned into real tests at some point.
+
+
+
+:Status: $Id: VBoxValidationKitReadMe.txt $
+:Copyright: Copyright (C) 2010-2023 Oracle Corporation.
diff --git a/src/VBox/ValidationKit/docs/WindbgPython.txt b/src/VBox/ValidationKit/docs/WindbgPython.txt
new file mode 100644
index 00000000..198ec917
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/WindbgPython.txt
@@ -0,0 +1,10 @@
+$Id: WindbgPython.txt $
+
+Just a couple of useful windbg commands:
+
+Show python filenames + frame line number (not statement) up the call stack:
+!for_each_frame ".block { dt python27!_frame qwo(!f) f_lineno; da qwo(qwo(qwo(!f)+0x20) + 50) + 20 } "
+
+Same, alternative version:
+!for_each_frame .if ( $spat("${@#FunctionName}","*PyEval_EvalFrameEx*") ) { .printf "python frame: line %d\npython frame: filename %ma\n", @@c++(f->f_lineno), qwo(qwo(qwo(!f)+0x20) + 50) + 20 }
+
diff --git a/src/VBox/ValidationKit/docs/testbox-maintenance.sh b/src/VBox/ValidationKit/docs/testbox-maintenance.sh
new file mode 100755
index 00000000..cf2d329d
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/testbox-maintenance.sh
@@ -0,0 +1,409 @@
+#!/bin/bash
+# $Id: testbox-maintenance.sh $
+## @file
+# VirtualBox Validation Kit - testbox maintenance service
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+#
+# Global Variables (config first).
+#
+MY_REBOOT_WHEN_DONE="yes"
+#MY_REBOOT_WHEN_DONE="" # enable this for debugging the script
+
+MY_TFTP_ROOT="/mnt/testbox-tftp"
+MY_BACKUP_ROOT="/mnt/testbox-backup"
+MY_BACKUP_MNT_TEST_FILE="/mnt/testbox-backup/testbox-backup"
+MY_GLOBAL_LOG_FILE="${MY_BACKUP_ROOT}/maintenance.log"
+MY_DD_BLOCK_SIZE=256K
+
+MY_IP=""
+MY_BACKUP_DIR=""
+MY_LOG_FILE=""
+MY_PXELINUX_CFG_FILE=""
+
+
+##
+# Info message.
+#
+InfoMsg()
+{
+ echo $*;
+ if test -n "${MY_LOG_FILE}"; then
+ echo "`date -uIsec`: ${MY_IP}: info:" $* >> ${MY_LOG_FILE};
+ fi
+}
+
+
+##
+# Error message and reboot+exit. First argument is exit code.
+#
+ErrorMsgExit()
+{
+ MY_RET=$1
+ shift
+ echo "testbox-maintenance.sh: error:" $* >&2;
+ # Append to the testbox log.
+ if test -n "${MY_LOG_FILE}"; then
+ echo "`date -uIsec`: ${MY_IP}: error:" $* >> "${MY_LOG_FILE}";
+ fi
+ # Append to the global log.
+ if test -f "${MY_BACKUP_MNT_TEST_FILE}"; then
+ echo "`date -uIsec`: ${MY_IP}: error:" $* >> "${MY_GLOBAL_LOG_FILE}";
+ fi
+
+ #
+ # On error we normally wait 5min before rebooting to avoid repeating the
+ # same error too many time before the admin finds out. We choose NOT to
+ # remove the PXE config file here because (a) the admin might otherwise
+ # not notice something went wrong, (b) the system could easily be in a
+ # weird unbootable state, (c) the problem might be temporary.
+ #
+ # While debugging, we just exit here.
+ #
+ if test -n "${MY_REBOOT_WHEN_DONE}"; then
+ sleep 5m
+ echo "testbox-maintenance.sh: rebooting (after error)" >&2;
+ reboot
+ fi
+ exit ${MY_RET}
+}
+
+#
+# Try figure out the IP address of the box and the hostname from it again.
+#
+MY_IP=` hostname -I | cut -f1 -d' ' | head -1 `
+if test -z "${MY_IP}" -o `echo "${MY_IP}" | wc -w` -ne "1" -o "${MY_IP}" = "127.0.0.1"; then
+ ErrorMsgExit 10 "Failed to get a good IP! (MY_IP=${MY_IP})"
+fi
+MY_HOSTNAME=`getent hosts "${MY_IP}" | sed -s 's/[[:space:]][[:space:]]*/ /g' | cut -d' ' -f2 `
+if test -z "${MY_HOSTNAME}"; then
+ MY_HOSTNAME="unknown";
+fi
+
+# Derive the backup dir and log file name from it.
+if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then
+ mount "${MY_BACKUP_ROOT}"
+ if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then
+ echo "Retrying mounting '${MY_BACKUP_ROOT}' in 15 seconds..." >&2
+ sleep 15
+ mount "${MY_BACKUP_ROOT}"
+ fi
+ if test ! -f "${MY_BACKUP_MNT_TEST_FILE}"; then
+ ErrorMsgExit 11 "Backup directory is not mounted."
+ fi
+fi
+MY_BACKUP_DIR="${MY_BACKUP_ROOT}/${MY_IP}"
+MY_LOG_FILE="${MY_BACKUP_DIR}/maintenance.log"
+mkdir -p "${MY_BACKUP_DIR}"
+echo "================ `date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} starts a new session ================" >> "${MY_LOG_FILE}"
+echo "`date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} says hi." >> "${MY_GLOBAL_LOG_FILE}"
+InfoMsg "MY_IP=${MY_IP}<eol>"
+
+#
+# Redirect stderr+stdout thru tee and to a log file on the server.
+#
+MY_OUTPUT_LOG_FILE="${MY_BACKUP_DIR}/maintenance-output.log"
+echo "" >> "${MY_OUTPUT_LOG_FILE}"
+echo "================ `date -uIsec`: ${MY_IP}: ${MY_HOSTNAME} starts a new session ================" >> "${MY_OUTPUT_LOG_FILE}"
+exec &> >(tee -a "${MY_OUTPUT_LOG_FILE}")
+
+#
+# Convert the IP address to PXELINUX hex format, then check that we've got
+# a config file on the TFTP share that we later can remove. We consider it a
+# fatal failure if we don't because we've probably got the wrong IP and we'll
+# be stuck doing the same stuff over and over again.
+#
+MY_TMP=`echo "${MY_IP}" | sed -e 's/\./ /g' `
+MY_IP_HEX=`printf "%02X%02X%02X%02X" ${MY_TMP}`
+InfoMsg "MY_IP_HEX=${MY_IP_HEX}<eol>"
+
+if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then
+ mount "${MY_TFTP_ROOT}"
+ if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then
+ echo "Retrying mounting '${MY_TFTP_ROOT}' in 15 seconds..." >&2
+ sleep 15
+ mount "${MY_BACKUP_ROOT}"
+ fi
+ if test ! -f "${MY_TFTP_ROOT}/pxelinux.0"; then
+ ErrorMsgExit 12 "TFTP share mounted or mixxing pxelinux.0 in the root."
+ fi
+fi
+
+MY_PXELINUX_CFG_FILE="${MY_TFTP_ROOT}/pxelinux.cfg/${MY_IP_HEX}"
+if test ! -f "${MY_PXELINUX_CFG_FILE}"; then
+ ErrorMsgExit 13 "No pxelinux.cfg file found (${MY_PXELINUX_CFG_FILE}) - wrong IP?"
+fi
+
+#
+# Dig the action out of from the kernel command line.
+#
+if test -n "${MY_REBOOT_WHEN_DONE}"; then
+ InfoMsg "/proc/cmdline: `cat /proc/cmdline`"
+ set `cat /proc/cmdline`
+else
+ InfoMsg "Using script command line: $*"
+fi
+MY_ACTION=not-found
+while test $# -ge 1; do
+ case "$1" in
+ testbox-action-*)
+ MY_ACTION="$1"
+ ;;
+ esac
+ shift
+done
+if test "${MY_ACTION}" = "not-found"; then
+ ErrorMsgExit 14 "No action given. Expected testbox-action-backup, testbox-action-backup-again, testbox-action-restore," \
+ "testbox-action-refresh-info, or testbox-action-rescue on the kernel command line.";
+fi
+
+# Validate and shorten the action.
+case "${MY_ACTION}" in
+ testbox-action-backup)
+ MY_ACTION="backup";
+ ;;
+ testbox-action-backup-again)
+ MY_ACTION="backup-again";
+ ;;
+ testbox-action-restore)
+ MY_ACTION="restore";
+ ;;
+ testbox-action-refresh-info)
+ MY_ACTION="refresh-info";
+ ;;
+ testbox-action-rescue)
+ MY_ACTION="rescue";
+ ;;
+ *) ErrorMsgExit 15 "Invalid action '${MY_ACTION}'";
+ ;;
+esac
+
+# Log the action in both logs.
+echo "`date -uIsec`: ${MY_IP}: info: Executing '${MY_ACTION}'." >> "${MY_GLOBAL_LOG_FILE}";
+
+#
+# Generate missing info for this testbox if backing up.
+#
+MY_INFO_FILE="${MY_BACKUP_DIR}/testbox-info.txt"
+if test '!' -f "${MY_INFO_FILE}" \
+ -o "${MY_ACTION}" = "backup" \
+ -o "${MY_ACTION}" = "backup-again" \
+ -o "${MY_ACTION}" = "refresh-info" ;
+then
+ echo "IP: ${MY_IP}" > ${MY_INFO_FILE};
+ echo "HEX-IP: ${MY_IP_HEX}" >> ${MY_INFO_FILE};
+ echo "Hostname: ${MY_HOSTNAME}" >> ${MY_INFO_FILE};
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE};
+ echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE};
+ echo "**** cat /proc/cpuinfo ****" >> ${MY_INFO_FILE};
+ cat /proc/cpuinfo >> ${MY_INFO_FILE};
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** lspci -vvv ****" >> ${MY_INFO_FILE};
+ echo "**** lspci -vvv ****" >> ${MY_INFO_FILE};
+ echo "**** lspci -vvv ****" >> ${MY_INFO_FILE};
+ lspci -vvv >> ${MY_INFO_FILE} 2>&1;
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** biosdecode ****" >> ${MY_INFO_FILE};
+ echo "**** biosdecode ****" >> ${MY_INFO_FILE};
+ echo "**** biosdecode ****" >> ${MY_INFO_FILE};
+ biosdecode >> ${MY_INFO_FILE} 2>&1;
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** dmidecode ****" >> ${MY_INFO_FILE};
+ echo "**** dmidecode ****" >> ${MY_INFO_FILE};
+ echo "**** dmidecode ****" >> ${MY_INFO_FILE};
+ dmidecode >> ${MY_INFO_FILE} 2>&1;
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** fdisk -l ****" >> ${MY_INFO_FILE};
+ echo "**** fdisk -l ****" >> ${MY_INFO_FILE};
+ echo "**** fdisk -l ****" >> ${MY_INFO_FILE};
+ fdisk -l >> ${MY_INFO_FILE} 2>&1;
+ echo "" >> ${MY_INFO_FILE};
+ echo "**** dmesg ****" >> ${MY_INFO_FILE};
+ echo "**** dmesg ****" >> ${MY_INFO_FILE};
+ echo "**** dmesg ****" >> ${MY_INFO_FILE};
+ dmesg >> ${MY_INFO_FILE} 2>&1;
+
+ #
+ # Get the raw ACPI tables and whatnot since we can. Use zip as tar will
+ # zero pad virtual files due to wrong misleading size returned by stat (4K).
+ #
+ # Note! /sys/firmware/dmi/entries/15-0/system_event_log/raw_event_log has been
+ # see causing fatal I/O errors, so skip all raw_event_log files.
+ #
+ zip -qr9 "${MY_BACKUP_DIR}/testbox-info.zip" \
+ /proc/cpuinfo \
+ /sys/firmware/ \
+ -x "*/raw_event_log"
+fi
+
+if test '!' -f "${MY_BACKUP_DIR}/${MY_HOSTNAME}" -a "${MY_HOSTNAME}" != "unknown"; then
+ echo "${MY_HOSTNAME}" > "${MY_BACKUP_DIR}/${MY_HOSTNAME}"
+fi
+
+if test '!' -f "${MY_BACKUP_DIR}/${MY_IP_HEX}"; then
+ echo "${MY_IP}" > "${MY_BACKUP_DIR}/${MY_IP_HEX}"
+fi
+
+#
+# Assemble a list of block devices using /sys/block/* and some filtering.
+#
+if test -f "${MY_BACKUP_DIR}/disk-devices.lst"; then
+ MY_BLOCK_DEVS=`cat ${MY_BACKUP_DIR}/disk-devices.lst \
+ | sed -e 's/[[:space:]][::space::]]*/ /g' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' `;
+ if test -z "${MY_BLOCK_DEVS}"; then
+ ErrorMsgExit 17 "No block devices found via sys/block."
+ fi
+ InfoMsg "disk-device.lst: MY_BLOCK_DEVS=${MY_BLOCK_DEVS}";
+else
+ MY_BLOCK_DEVS="";
+ for MY_DEV in `ls /sys/block`; do
+ case "${MY_DEV}" in
+ [sh]d*)
+ MY_BLOCK_DEVS="${MY_BLOCK_DEVS} ${MY_DEV}"
+ ;;
+ *) InfoMsg "Ignoring /sys/block/${MY_DEV}";
+ ;;
+ esac
+ done
+ if test -z "${MY_BLOCK_DEVS}"; then
+ ErrorMsgExit 17 "No block devices found via /sys/block."
+ fi
+ InfoMsg "/sys/block: MY_BLOCK_DEVS=${MY_BLOCK_DEVS}";
+fi
+
+#
+# Take action
+#
+case "${MY_ACTION}" in
+ #
+ # Create a backup. The 'backup' action refuses to overwrite an
+ # existing backup, but is otherwise identical to 'backup-again'.
+ #
+ backup|backup-again)
+ for MY_DEV in ${MY_BLOCK_DEVS}; do
+ MY_DST="${MY_BACKUP_DIR}/${MY_DEV}.gz"
+ if test -f "${MY_DST}"; then
+ if test "${MY_ACTION}" != 'backup-again'; then
+ ErrorMsgExit 18 "${MY_DST} already exists"
+ fi
+ InfoMsg "${MY_DST} already exists"
+ fi
+ done
+
+ # Do the backing up.
+ for MY_DEV in ${MY_BLOCK_DEVS}; do
+ MY_SRC="/dev/${MY_DEV}"
+ MY_DST="${MY_BACKUP_DIR}/${MY_DEV}.gz"
+ if test -f "${MY_DST}"; then
+ mv -f "${MY_DST}" "${MY_DST}.old";
+ fi
+ if test -b "${MY_SRC}"; then
+ InfoMsg "Backing up ${MY_SRC} to ${MY_DST}...";
+ dd if="${MY_SRC}" bs=${MY_DD_BLOCK_SIZE} | gzip -c > "${MY_DST}";
+ MY_RCS=("${PIPESTATUS[@]}");
+ if test "${MY_RCS[0]}" -eq 0 -a "${MY_RCS[1]}" -eq 0; then
+ InfoMsg "Successfully backed up ${MY_SRC} to ${MY_DST}";
+ else
+ rm -f "${MY_DST}";
+ ErrorMsgExit 19 "There was a problem backing up ${MY_SRC} to ${MY_DST}: dd => ${MY_RCS[0]}; gzip => ${MY_RCS[1]}";
+ fi
+ else
+ InfoMsg "Skipping ${MY_SRC} as it either doesn't exist or isn't a block device";
+ fi
+ done
+ ;;
+
+ #
+ # Restore existing.
+ #
+ restore)
+ for MY_DEV in ${MY_BLOCK_DEVS}; do
+ MY_SRC="${MY_BACKUP_DIR}/${MY_DEV}.gz"
+ MY_DST="/dev/${MY_DEV}"
+ if test -b "${MY_DST}"; then
+ if test -f "${MY_SRC}"; then
+ InfoMsg "Restoring ${MY_SRC} onto ${MY_DST}...";
+ gunzip -c "${MY_SRC}" | dd of="${MY_DST}" bs=${MY_DD_BLOCK_SIZE} iflag=fullblock;
+ MY_RCS=("${PIPESTATUS[@]}");
+ if test ${MY_RCS[0]} -eq 0 -a ${MY_RCS[1]} -eq 0; then
+ InfoMsg "Successfully restored ${MY_SRC} onto ${MY_DST}";
+ else
+ ErrorMsgExit 20 "There was a problem restoring ${MY_SRC} onto ${MY_DST}: dd => ${MY_RCS[1]}; gunzip => ${MY_RCS[0]}";
+ fi
+ else
+ InfoMsg "Skipping ${MY_DST} because ${MY_SRC} does not exist.";
+ fi
+ else
+ InfoMsg "Skipping ${MY_DST} as it either doesn't exist or isn't a block device.";
+ fi
+ done
+ ;;
+
+ #
+ # Nothing else to do for refresh-info.
+ #
+ refresh-info)
+ ;;
+
+ #
+ # For the rescue action, we just quit without removing the PXE config or
+ # rebooting the box. The admin will do that once the system has been rescued.
+ #
+ rescue)
+ InfoMsg "rescue: exiting. Admin must remove PXE config and reboot manually when done."
+ exit 0;
+ ;;
+
+ *) ErrorMsgExit 98 "Huh? MY_ACTION='${MY_ACTION}'"
+ ;;
+esac
+
+#
+# If we get here, remove the PXE config and reboot immediately.
+#
+InfoMsg "'${MY_ACTION}' - done";
+if test -n "${MY_REBOOT_WHEN_DONE}"; then
+ sync
+ if rm -f "${MY_PXELINUX_CFG_FILE}"; then
+ InfoMsg "removed ${MY_PXELINUX_CFG_FILE}";
+ else
+ ErrorMsgExit 99 "failed to remove ${MY_PXELINUX_CFG_FILE}";
+ fi
+ sync
+ InfoMsg "rebooting";
+ reboot
+fi
+exit 0
diff --git a/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh b/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh
new file mode 100755
index 00000000..d1f28b05
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/testbox-pxe-conf.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+# $Id: testbox-pxe-conf.sh $
+## @file
+# VirtualBox Validation Kit - testbox pxe config emitter.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+#
+# Global Variables (config first).
+#
+MY_NFS_SERVER_IP="10.165.98.101"
+MY_GATEWAY_IP="10.165.98.1"
+MY_NETMASK="255.255.254.0"
+MY_ETH_DEV="eth0"
+MY_AUTO_CFG="none"
+
+# options
+MY_PXELINUX_CFG_DIR="/mnt/testbox-tftp/pxelinux.cfg"
+MY_ACTION=""
+MY_IP=""
+MY_IP_HEX=""
+
+#
+# Parse arguments.
+#
+while test "$#" -ge 1; do
+ MY_ARG=$1
+ shift
+ case "${MY_ARG}" in
+ -c|--cfg-dir)
+ MY_PXELINUX_CFG_DIR="$1";
+ shift;
+ if test -z "${MY_PXELINUX_CFG_DIR}"; then
+ echo "syntax error: Empty pxeclient.cfg path." >&2;
+ exit 2;
+ fi
+ ;;
+
+ -h|--help)
+ echo "usage: testbox-pxe-conf.sh: [-c /mnt/testbox-tftp/pxelinux.cfg] <ip> <action>";
+ echo "Actions: backup, backup-again, restore, refresh-info, rescue";
+ exit 0;
+ ;;
+ -*)
+ echo "syntax error: Invalid option: ${MY_ARG}" >&2;
+ exit 2;
+ ;;
+
+ *) if test -z "$MY_ARG"; then
+ echo "syntax error: Empty argument" >&2;
+ exit 2;
+ fi
+ if test -z "${MY_IP}"; then
+ # Split up the IP if possible, if not do gethostbyname on the argument.
+ MY_TMP=`echo "${MY_ARG}" | sed -e 's/\./ /g'`
+ if test `echo "${MY_TMP}" | wc -w` -ne 4 \
+ || ! printf "%02X%02X%02X%02X" ${MY_TMP} > /dev/null 2>&1; then
+ MY_TMP2=`getent hosts "${MY_ARG}" | head -1 | cut -d' ' -f1`;
+ MY_TMP=`echo "${MY_TMP2}" | sed -e 's/\./ /g'`
+ if test `echo "${MY_TMP}" | wc -w` -eq 4 \
+ && printf "%02X%02X%02X%02X" ${MY_TMP} > /dev/null 2>&1; then
+ echo "info: resolved '${MY_ARG}' as '${MY_TMP2}'";
+ MY_ARG="${MY_TMP2}";
+ else
+ echo "syntax error: Invalid IP: ${MY_ARG}" >&2;
+ exit 2;
+ fi
+ fi
+ MY_IP_HEX=`printf "%02X%02X%02X%02X" ${MY_TMP}`;
+ MY_IP="${MY_ARG}";
+ else
+ if test -z "${MY_ACTION}"; then
+ case "${MY_ARG}" in
+ backup|backup-again|restore|refresh-info|rescue)
+ MY_ACTION="${MY_ARG}";
+ ;;
+ *)
+ echo "syntax error: Invalid action: ${MY_ARG}" >&2;
+ exit 2;
+ ;;
+ esac
+ else
+ echo "syntax error: Too many arguments" >&2;
+ exit 2;
+ fi
+ fi
+ ;;
+ esac
+done
+
+if test -z "${MY_ACTION}"; then
+ echo "syntax error: Insufficient arguments" >&2;
+ exit 2;
+fi
+if test ! -d "${MY_PXELINUX_CFG_DIR}"; then
+ echo "error: pxeclient.cfg path does not point to a directory: ${MY_PXELINUX_CFG_DIR}" >&2;
+ exit 1;
+fi
+if test ! -f "${MY_PXELINUX_CFG_DIR}/default"; then
+ echo "error: pxeclient.cfg path does contain a 'default' file: ${MY_PXELINUX_CFG_DIR}" >&2;
+ exit 1;
+fi
+
+
+#
+# Produce the file.
+# Using echo here so we can split up the APPEND line more easily.
+#
+MY_CFG_FILE="${MY_PXELINUX_CFG_DIR}/${MY_IP_HEX}"
+set +e
+echo "PATH bios" > "${MY_CFG_FILE}";
+echo "DEFAULT maintenance" >> "${MY_CFG_FILE}";
+echo "LABEL maintenance" >> "${MY_CFG_FILE}";
+echo " MENU LABEL Maintenance (NFS)" >> "${MY_CFG_FILE}";
+echo " KERNEL maintenance-boot/vmlinuz-3.16.0-4-amd64" >> "${MY_CFG_FILE}";
+echo -n " APPEND initrd=maintenance-boot/initrd.img-3.16.0-4-amd64 testbox-action-${MY_ACTION}" >> "${MY_CFG_FILE}";
+echo -n " ro aufs=tmpfs boot=nfs root=/dev/nfs" >> "${MY_CFG_FILE}";
+echo -n " nfsroot=${MY_NFS_SERVER_IP}:/export/testbox-nfsroot,ro,tcp" >> "${MY_CFG_FILE}";
+echo -n " nfsvers=3 nfsrootdebug" >> "${MY_CFG_FILE}";
+if test "${MY_AUTO_CFG}" = "none"; then
+ # Note! Only 6 arguments to ip! Userland ipconfig utility barfs if autoconf and dns options are given.
+ echo -n " ip=${MY_IP}:${MY_NFS_SERVER_IP}:${MY_GATEWAY_IP}:${MY_NETMASK}:maintenance:${MY_ETH_DEV}" >> "${MY_CFG_FILE}";
+else
+ echo -n " ip=${MY_AUTO_CFG}" >> "${MY_CFG_FILE}";
+fi
+echo "" >> "${MY_CFG_FILE}";
+echo "LABEL local-boot" >> "${MY_CFG_FILE}";
+echo "LOCALBOOT" >> "${MY_CFG_FILE}";
+echo "Successfully generated '${MY_CFG_FILE}'."
+exit 0;
+
diff --git a/src/VBox/ValidationKit/docs/valkit.txt b/src/VBox/ValidationKit/docs/valkit.txt
new file mode 100644
index 00000000..9e94eff5
--- /dev/null
+++ b/src/VBox/ValidationKit/docs/valkit.txt
@@ -0,0 +1 @@
+The VirtualBox ValidationKit ISO.
diff --git a/src/VBox/ValidationKit/jshintrc.js b/src/VBox/ValidationKit/jshintrc.js
new file mode 100644
index 00000000..96ec492e
--- /dev/null
+++ b/src/VBox/ValidationKit/jshintrc.js
@@ -0,0 +1,40 @@
+/* $Id: jshintrc.js $ */
+/** @file
+ * JSHint configuration file.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+{
+ "laxbreak": true
+}
+
diff --git a/src/VBox/ValidationKit/readme.txt b/src/VBox/ValidationKit/readme.txt
new file mode 100644
index 00000000..ac747f2f
--- /dev/null
+++ b/src/VBox/ValidationKit/readme.txt
@@ -0,0 +1,3 @@
+
+See docs/VBoxValidationKitReadMe.txt or docs/VBoxValidationKitReadMe.html.
+
diff --git a/src/VBox/ValidationKit/snippets/alloc-1.c b/src/VBox/ValidationKit/snippets/alloc-1.c
new file mode 100644
index 00000000..ded4e715
--- /dev/null
+++ b/src/VBox/ValidationKit/snippets/alloc-1.c
@@ -0,0 +1,110 @@
+/* $Id: alloc-1.c $ */
+/** @file
+ * Allocate lots of memory, portable ANSI C code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+
+
+int main(int argc, char **argv)
+{
+ unsigned uPct;
+ unsigned long cbDone;
+ unsigned long cMBs = 1024;
+ unsigned long cb;
+
+ /*
+ * Some quick and dirty argument parsing.
+ */
+ if (argc == 2)
+ cMBs = strtoul(argv[1], 0, 0);
+ if (!cMBs || argc > 2)
+ {
+ printf("usage: alloc-1 [MBs]\n");
+ return 1;
+ }
+ cb = cMBs * 1024 * 1024;
+ if (cb / (1024 * 1024) != cMBs)
+ cb = ~(unsigned long)0 / (1024 * 1024) * (1024 * 1024);
+ printf("alloc-1: allocating %lu MB (%lu bytes)\n", cb/1024/1024, cb);
+
+ /*
+ * The allocation loop.
+ */
+ printf("alloc-1: 0%%");
+ fflush(stdout);
+ cbDone = 0;
+ uPct = 0;
+ while (cbDone < cb)
+ {
+ unsigned uPctNow;
+ unsigned long cbThis = cb > 10*1024*1024 ? 10*1024*1024 : cb;
+ char *pb = malloc(cbThis);
+ if (!pb)
+ {
+ printf("\nalloc-1: calloc failed, cbDone=%lu MB (%lu bytes)\n",
+ cbDone/1024/1024, cbDone);
+ return 1;
+ }
+ cbDone += cbThis;
+
+ /* touch the memory. */
+ while (cbThis >= 0x1000)
+ {
+ *pb = (char)cbThis;
+ pb += 0x1000;
+ cbThis -= 0x1000;
+ }
+
+ /* progress */
+ uPctNow = 100.0 * cbDone / cb;
+ if (uPctNow != uPct && !(uPctNow & 1))
+ {
+ if (!(uPctNow % 10))
+ printf("%u%%", uPctNow);
+ else
+ printf(".");
+ fflush(stdout);
+ }
+ uPct = uPctNow;
+ }
+
+ printf("\nalloc-1: done\n");
+ return 0;
+}
diff --git a/src/VBox/ValidationKit/snippets/time-1.c b/src/VBox/ValidationKit/snippets/time-1.c
new file mode 100644
index 00000000..e70961dd
--- /dev/null
+++ b/src/VBox/ValidationKit/snippets/time-1.c
@@ -0,0 +1,123 @@
+/* $Id: time-1.c $ */
+/** @file
+ * Query the time and check that it always goes forward, POSIX only.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+
+
+
+int main()
+{
+ unsigned cErrors = 0;
+#ifdef USE_CLOCK_MONOTONIC
+ struct timespec aTs[2];
+ struct timespec *pCur = &aTs[0];
+ struct timespec *pPrev = &aTs[1];
+ struct timespec *pTmp;
+#else
+ struct timeval aTv[2];
+ struct timeval *pCur = &aTv[0];
+ struct timeval *pPrev = &aTv[1];
+ struct timeval *pTmp;
+#endif
+
+#ifdef USE_CLOCK_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, pPrev);
+#else
+ gettimeofday(pPrev, NULL);
+#endif
+ for (;;)
+ {
+#ifdef USE_CLOCK_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, pCur);
+#else
+ gettimeofday(pCur, NULL);
+#endif
+
+ if ( pCur->tv_sec == pPrev->tv_sec
+#ifdef USE_CLOCK_MONOTONIC
+ && pCur->tv_nsec < pPrev->tv_nsec
+#else
+ && pCur->tv_usec < pPrev->tv_usec
+#endif
+ )
+ {
+#ifdef USE_CLOCK_MONOTONIC
+ printf("tv_nsec in the past: %ld.%09u < %ld.%09u - %u nsec\n",
+ (long)pCur->tv_sec, (unsigned)pCur->tv_nsec,
+ (long)pPrev->tv_sec, (unsigned)pPrev->tv_nsec,
+ (unsigned)pPrev->tv_nsec - (unsigned)pCur->tv_nsec);
+#else
+ printf("tv_usec in the past: %ld.%06u < %ld.%06u - %u usec\n",
+ (long)pCur->tv_sec, (unsigned)pCur->tv_usec,
+ (long)pPrev->tv_sec, (unsigned)pPrev->tv_usec,
+ (unsigned)pPrev->tv_usec - (unsigned)pCur->tv_usec);
+#endif
+ cErrors++;
+ if (cErrors > 1000)
+ break;
+ }
+ else if (pCur->tv_sec < pPrev->tv_sec)
+ {
+#ifdef USE_CLOCK_MONOTONIC
+ printf("tv_sec in the past: %ld.%09u < %ld.%09u\n",
+ (long)pCur->tv_sec, (unsigned)pCur->tv_nsec,
+ (long)pPrev->tv_sec, (unsigned)pPrev->tv_nsec);
+#else
+ printf("tv_sec in the past: %ld.%06u < %ld.%06u\n",
+ (long)pCur->tv_sec, (unsigned)pCur->tv_usec,
+ (long)pPrev->tv_sec, (unsigned)pPrev->tv_usec);
+#endif
+ cErrors++;
+ if (cErrors > 1000)
+ break;
+ }
+ else
+ {
+ /* swap */
+ pTmp = pPrev;
+ pPrev = pCur;
+ pCur = pTmp;
+ }
+ }
+
+ return 1;
+}
diff --git a/src/VBox/ValidationKit/testboxscript/Makefile.kmk b/src/VBox/ValidationKit/testboxscript/Makefile.kmk
new file mode 100644
index 00000000..c2d7b22b
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/Makefile.kmk
@@ -0,0 +1,97 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - TestBox Script.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# The TestBox script.
+#
+INSTALLS += testboxscript
+testboxscript_TEMPLATE = VBoxValidationKitR3
+testboxscript_INST = $(INST_TESTBOXSCRIPT)testboxscript/
+testboxscript_EXEC_SOURCES = \
+ testboxscript.py \
+ $(testboxscript_0_OUTDIR)/testboxscript_real.py \
+ setup.sh
+$(call VBOX_EDIT_VERSION_RULE_FN,testboxscript,testboxscript_real.py)
+
+testboxscript_SOURCES = \
+ testboxcommand.py \
+ testboxcommons.py \
+ testboxconnection.py \
+ testboxtasks.py \
+ testboxupgrade.py
+
+testboxscript_SOURCES.darwin = \
+ darwin/setup-routines.sh=>darwin/setup-routines.sh
+
+testboxscript_EXEC_SOURCES.linux = \
+ linux/testboxscript-service.sh=>linux/testboxscript-service.sh
+testboxscript_SOURCES.linux = \
+ ../../Installer/linux/routines.sh=>linux/setup-installer-routines.sh \
+ linux/setup-routines.sh=>linux/setup-routines.sh
+
+testboxscript_SOURCES.solaris = \
+ solaris/setup-routines.sh=>solaris/setup-routines.sh
+
+testboxscript_SOURCES.win = \
+ win/autoexec-testbox.cmd=>win/autoexec-testbox.cmd \
+ win/readme.txt=>win/readme.txt \
+ $(if $(VBOX_OSE),,win/fix_stale_refs.py=>win/fix_stale_refs.py)
+
+
+#
+# Helper program, mostly for obtaining system information.
+#
+PROGRAMS += TestBoxHelper
+TestBoxHelper_TEMPLATE = VBoxValidationKitR3
+TestBoxHelper_INST = $(INST_TESTBOXSCRIPT)$(KBUILD_TARGET)/$(KBUILD_TARGET_ARCH)/
+TestBoxHelper_SOURCES = TestBoxHelper.cpp
+TestBoxHelper_LIBS.win = $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/wbemuuid.lib
+TestBoxHelper_LDFLAGS.darwin = -framework CoreFoundation
+TestBoxHelper_VBOX_IMPORT_CHECKER.win.x86 = $(NO_SUCH_VARIABLE)
+
+
+#
+# Generate pylint & pychecker targets.
+#
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testboxscript/TestBoxHelper.cpp b/src/VBox/ValidationKit/testboxscript/TestBoxHelper.cpp
new file mode 100644
index 00000000..97264ebc
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/TestBoxHelper.cpp
@@ -0,0 +1,780 @@
+/* $Id: TestBoxHelper.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - Testbox C Helper Utility.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/buildconfig.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/mp.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/x86.h>
+# include <iprt/asm-amd64-x86.h>
+#endif
+
+#ifdef RT_OS_DARWIN
+# include <sys/types.h>
+# include <sys/sysctl.h>
+#endif
+
+
+
+/**
+ * Does one free space wipe, using the given filename.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE on failure (fully
+ * bitched).
+ * @param pszFilename The filename to use for wiping free space. Will be
+ * replaced and afterwards deleted.
+ * @param pvFiller The filler block buffer.
+ * @param cbFiller The size of the filler block buffer.
+ * @param cbMinLeftOpt When to stop wiping.
+ */
+static RTEXITCODE doOneFreeSpaceWipe(const char *pszFilename, void const *pvFiller, size_t cbFiller, uint64_t cbMinLeftOpt)
+{
+ /*
+ * Open the file.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ RTFILE hFile = NIL_RTFILE;
+ int rc = RTFileOpen(&hFile, pszFilename,
+ RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE_REPLACE | (0775 << RTFILE_O_CREATE_MODE_SHIFT));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Query the amount of available free space. Figure out which API we should use.
+ */
+ RTFOFF cbTotal = 0;
+ RTFOFF cbFree = 0;
+ rc = RTFileQueryFsSizes(hFile, &cbTotal, &cbFree, NULL, NULL);
+ bool const fFileHandleApiSupported = rc != VERR_NOT_SUPPORTED && rc != VERR_NOT_IMPLEMENTED;
+ if (!fFileHandleApiSupported)
+ rc = RTFsQuerySizes(pszFilename, &cbTotal, &cbFree, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("%s: %'9RTfoff MiB out of %'9RTfoff are free\n", pszFilename, cbFree / _1M, cbTotal / _1M);
+
+ /*
+ * Start filling up the free space, down to the last 32MB.
+ */
+ uint64_t const nsStart = RTTimeNanoTS(); /* for speed calcs */
+ uint64_t nsStat = nsStart; /* for speed calcs */
+ uint64_t cbStatWritten = 0; /* for speed calcs */
+ RTFOFF const cbMinLeft = RT_MAX(cbMinLeftOpt, cbFiller * 2);
+ RTFOFF cbLeftToWrite = cbFree - cbMinLeft;
+ uint64_t cbWritten = 0;
+ uint32_t iLoop = 0;
+ while (cbLeftToWrite >= (RTFOFF)cbFiller)
+ {
+ rc = RTFileWrite(hFile, pvFiller, cbFiller, NULL);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_DISK_FULL)
+ RTPrintf("%s: Disk full after writing %'9RU64 MiB\n", pszFilename, cbWritten / _1M);
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Write error after %'RU64 bytes: %Rrc\n",
+ pszFilename, cbWritten, rc);
+ break;
+ }
+
+ /* Flush every now and then as we approach a completely full disk. */
+ if (cbLeftToWrite <= _1G && (iLoop & (cbLeftToWrite > _128M ? 15 : 3)) == 0)
+ RTFileFlush(hFile);
+
+ /*
+ * Advance and maybe recheck the amount of free space.
+ */
+ cbWritten += cbFiller;
+ cbLeftToWrite -= (ssize_t)cbFiller;
+ iLoop++;
+ if ((iLoop & (16 - 1)) == 0 || cbLeftToWrite < _256M)
+ {
+ RTFOFF cbFreeUpdated;
+ if (fFileHandleApiSupported)
+ rc = RTFileQueryFsSizes(hFile, NULL, &cbFreeUpdated, NULL, NULL);
+ else
+ rc = RTFsQuerySizes(pszFilename, NULL, &cbFreeUpdated, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ cbFree = cbFreeUpdated;
+ cbLeftToWrite = cbFree - cbMinLeft;
+ }
+ else
+ {
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to query free space after %'RU64 bytes: %Rrc\n",
+ pszFilename, cbWritten, rc);
+ break;
+ }
+ if ((iLoop & (512 - 1)) == 0)
+ {
+ uint64_t const nsNow = RTTimeNanoTS();
+ uint64_t cNsInterval = nsNow - nsStat;
+ uint64_t cbInterval = cbWritten - cbStatWritten;
+ uint64_t cbIntervalPerSec = !cbInterval ? 0
+ : (uint64_t)((double)cbInterval / ((double)cNsInterval / (double)RT_NS_1SEC));
+
+ RTPrintf("%s: %'9RTfoff MiB out of %'9RTfoff are free after writing %'9RU64 MiB (%'5RU64 MiB/s)\n",
+ pszFilename, cbFree / _1M, cbTotal / _1M, cbWritten / _1M, cbIntervalPerSec / _1M);
+ nsStat = nsNow;
+ cbStatWritten = cbWritten;
+ }
+ }
+ }
+
+ /*
+ * Now flush the file and then reduce the size a little before closing
+ * it so the system won't entirely run out of space. The flush should
+ * ensure the data has actually hit the disk.
+ */
+ rc = RTFileFlush(hFile);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Flush failed at %'RU64 bytes: %Rrc\n", pszFilename, cbWritten, rc);
+
+ uint64_t cbReduced = cbWritten > _512M ? cbWritten - _512M : cbWritten / 2;
+ rc = RTFileSetSize(hFile, cbReduced);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to reduce file size from %'RU64 to %'RU64 bytes: %Rrc\n",
+ pszFilename, cbWritten, cbReduced, rc);
+
+ /* Issue a summary statements. */
+ uint64_t cNsElapsed = RTTimeNanoTS() - nsStart;
+ uint64_t cbPerSec = cbWritten ? (uint64_t)((double)cbWritten / ((double)cNsElapsed / (double)RT_NS_1SEC)) : 0;
+ RTPrintf("%s: Wrote %'RU64 MiB in %'RU64 s, avg %'RU64 MiB/s.\n",
+ pszFilename, cbWritten / _1M, cNsElapsed / RT_NS_1SEC, cbPerSec / _1M);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Initial free space query failed: %Rrc \n", pszFilename, rc);
+
+ RTFileClose(hFile);
+
+ /*
+ * Delete the file.
+ */
+ rc = RTFileDelete(pszFilename);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Delete failed: %Rrc !!\n", pszFilename, rc);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Open failed: %Rrc\n", pszFilename, rc);
+ return rcExit;
+}
+
+
+/**
+ * Wipes free space on one or more volumes by creating large files.
+ */
+static RTEXITCODE handlerWipeFreeSpace(int argc, char **argv)
+{
+ /*
+ * Parse arguments.
+ */
+ const char *apszDefFiles[2] = { "./wipefree.spc", NULL };
+ bool fAll = false;
+ uint32_t u32Filler = UINT32_C(0xf6f6f6f6);
+ uint64_t cbMinLeftOpt = _32M;
+
+ static RTGETOPTDEF const s_aOptions[] =
+ {
+ { "--all", 'a', RTGETOPT_REQ_NOTHING },
+ { "--filler", 'f', RTGETOPT_REQ_UINT32 },
+ { "--min-free", 'm', RTGETOPT_REQ_UINT64 },
+ };
+ RTGETOPTSTATE State;
+ RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ RTGETOPTUNION ValueUnion;
+ int chOpt;
+ while ( (chOpt = RTGetOpt(&State, &ValueUnion)) != 0
+ && chOpt != VINF_GETOPT_NOT_OPTION)
+ {
+ switch (chOpt)
+ {
+ case 'a':
+ fAll = true;
+ break;
+ case 'f':
+ u32Filler = ValueUnion.u32;
+ break;
+ case 'm':
+ cbMinLeftOpt = ValueUnion.u64;
+ break;
+ case 'h':
+ RTPrintf("usage: wipefrespace [options] [filename1 [..]]\n"
+ "\n"
+ "Options:\n"
+ " -a, --all\n"
+ " Try do the free space wiping on all seemingly relevant file systems.\n"
+ " Changes the meaning of the filenames "
+ " This is not yet implemented\n"
+ " -p, --filler <32-bit value>\n"
+ " What to fill the blocks we write with.\n"
+ " Default: 0xf6f6f6f6\n"
+ " -m, --min-free <64-bit byte count>\n"
+ " Specifies when to stop in terms of free disk space (in bytes).\n"
+ " Default: 32MB\n"
+ "\n"
+ "Zero or more names of files to do the free space wiping thru can be given.\n"
+ "When --all is NOT used, each of the files are used to do free space wiping on\n"
+ "the volume they will live on. However, when --all is in effect the files are\n"
+ "appended to the volume mountpoints and only the first that can be created will\n"
+ "be used. Files (used ones) will be removed when done.\n"
+ "\n"
+ "If no filename is given, the default is: %s\n"
+ , apszDefFiles[0]);
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTGetOptPrintError(chOpt, &ValueUnion);
+ }
+ }
+
+ char **papszFiles;
+ if (chOpt == 0)
+ papszFiles = (char **)apszDefFiles;
+ else
+ papszFiles = RTGetOptNonOptionArrayPtr(&State);
+
+ /*
+ * Allocate and prep a memory which we'll write over and over again.
+ */
+ uint32_t cbFiller = _2M;
+ uint32_t *pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
+ while (!pu32Filler)
+ {
+ cbFiller <<= 1;
+ if (cbFiller >= _4K)
+ pu32Filler = (uint32_t *)RTMemPageAlloc(cbFiller);
+ else
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTMemPageAlloc failed for sizes between 4KB and 2MB!\n");
+ }
+ for (uint32_t i = 0; i < cbFiller / sizeof(pu32Filler[0]); i++)
+ pu32Filler[i] = u32Filler;
+
+ /*
+ * Do the requested work.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ if (!fAll)
+ {
+ for (uint32_t iFile = 0; papszFiles[iFile] != NULL; iFile++)
+ {
+ RTEXITCODE rcExit2 = doOneFreeSpaceWipe(papszFiles[iFile], pu32Filler, cbFiller, cbMinLeftOpt);
+ if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ }
+ }
+ else
+ {
+ /*
+ * Reject --all for now.
+ */
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "The --all option is not yet implemented!\n");
+ }
+
+ RTMemPageFree(pu32Filler, cbFiller);
+ return rcExit;
+}
+
+
+/**
+ * Generates a kind of report of the hardware, software and whatever else we
+ * think might be useful to know about the testbox.
+ */
+static RTEXITCODE handlerReport(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ /*
+ * For now, a simple CPUID dump. Need to figure out how to share code
+ * like this with other bits, putting it in IPRT.
+ */
+ RTPrintf("CPUID Dump\n"
+ "Leaf eax ebx ecx edx\n"
+ "---------------------------------------------\n");
+ static uint32_t const s_auRanges[] =
+ {
+ UINT32_C(0x00000000),
+ UINT32_C(0x80000000),
+ UINT32_C(0x80860000),
+ UINT32_C(0xc0000000),
+ UINT32_C(0x40000000),
+ };
+ for (uint32_t iRange = 0; iRange < RT_ELEMENTS(s_auRanges); iRange++)
+ {
+ uint32_t const uFirst = s_auRanges[iRange];
+
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ ASMCpuIdExSlow(uFirst, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEax >= uFirst && uEax < uFirst + 100)
+ {
+ uint32_t const cLeafs = RT_MIN(uEax - uFirst + 1, 32);
+ for (uint32_t iLeaf = 0; iLeaf < cLeafs; iLeaf++)
+ {
+ uint32_t uLeaf = uFirst + iLeaf;
+ ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
+
+ /* Clear APIC IDs to avoid submitting new reports all the time. */
+ if (uLeaf == 1)
+ uEbx &= UINT32_C(0x00ffffff);
+ if (uLeaf == 0xb)
+ uEdx = 0;
+ if (uLeaf == 0x8000001e)
+ uEax = 0;
+
+ /* Clear some other node/cpu/core/thread ids. */
+ if (uLeaf == 0x8000001e)
+ {
+ uEbx &= UINT32_C(0xffffff00);
+ uEcx &= UINT32_C(0xffffff00);
+ }
+
+ RTPrintf("%08x: %08x %08x %08x %08x\n", uLeaf, uEax, uEbx, uEcx, uEdx);
+ }
+ }
+ }
+ RTPrintf("\n");
+
+ /*
+ * DMI info.
+ */
+ RTPrintf("DMI Info\n"
+ "--------\n");
+ static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] =
+ {
+ { "Product Name", RTSYSDMISTR_PRODUCT_NAME },
+ { "Product version", RTSYSDMISTR_PRODUCT_VERSION },
+ { "Product UUID", RTSYSDMISTR_PRODUCT_UUID },
+ { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL },
+ { "System Manufacturer", RTSYSDMISTR_MANUFACTURER },
+ };
+ for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++)
+ {
+ char szTmp[4096];
+ RT_ZERO(szTmp);
+ int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1);
+ if (RT_SUCCESS(rc))
+ RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp));
+ else
+ RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc);
+ }
+ RTPrintf("\n");
+
+#else
+#endif
+
+ /*
+ * Dump the environment.
+ */
+ RTPrintf("Environment\n"
+ "-----------\n");
+ RTENV hEnv;
+ int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cVars = RTEnvCountEx(hEnv);
+ for (uint32_t iVar = 0; iVar < cVars; iVar++)
+ {
+ char szVar[1024];
+ char szValue[16384];
+ rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue));
+
+ /* zap the value of variables that are subject to change. */
+ if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
+ && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV")
+ || !strcmp(szVar, "TESTBOX_ID")
+ || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE")
+ || !strcmp(szVar, "TESTBOX_TIMEOUT")
+ || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS")
+ || !strcmp(szVar, "TESTBOX_TEST_SET_ID")
+ )
+ )
+ strcpy(szValue, "<volatile>");
+
+ if (RT_SUCCESS(rc))
+ RTPrintf("%25s=%s\n", szVar, szValue);
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue);
+ else
+ RTPrintf("rc=%Rrc\n", rc);
+ }
+ RTEnvDestroy(hEnv);
+ }
+
+ /** @todo enumerate volumes and whatnot. */
+
+ int cch = RTPrintf("\n");
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/** Print the total memory size in bytes. */
+static RTEXITCODE handlerMemSize(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+
+ uint64_t cb;
+ int rc = RTSystemQueryTotalRam(&cb);
+ if (RT_SUCCESS(rc))
+ {
+ int cch = RTPrintf("%llu\n", cb);
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+ RTPrintf("%Rrc\n", rc);
+ return RTEXITCODE_FAILURE;
+}
+
+typedef enum { HWVIRTTYPE_NONE, HWVIRTTYPE_VTX, HWVIRTTYPE_AMDV } HWVIRTTYPE;
+static HWVIRTTYPE isHwVirtSupported(void)
+{
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t uEax, uEbx, uEcx, uEdx;
+
+ /* VT-x */
+ ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidStdRange(uEax))
+ {
+ ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEcx & X86_CPUID_FEATURE_ECX_VMX)
+ return HWVIRTTYPE_VTX;
+ }
+
+ /* AMD-V */
+ ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidExtRange(uEax))
+ {
+ ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
+ return HWVIRTTYPE_AMDV;
+ }
+#endif
+
+ return HWVIRTTYPE_NONE;
+}
+
+/** Print the 'true' if VT-x or AMD-v is supported, 'false' it not. */
+static RTEXITCODE handlerCpuHwVirt(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+ int cch = RTPrintf(isHwVirtSupported() != HWVIRTTYPE_NONE ? "true\n" : "false\n");
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/** Print the 'true' if nested paging is supported, 'false' if not and
+ * 'dunno' if we cannot tell. */
+static RTEXITCODE handlerCpuNestedPaging(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+ HWVIRTTYPE enmHwVirt = isHwVirtSupported();
+ int fSupported = -1;
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ if (enmHwVirt == HWVIRTTYPE_AMDV)
+ {
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidExtRange(uEax) && uEax >= 0x8000000a)
+ {
+ ASMCpuId(0x8000000a, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEdx & RT_BIT(0) /* AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING */)
+ fSupported = 1;
+ else
+ fSupported = 0;
+ }
+ }
+# if defined(RT_OS_LINUX)
+ else if (enmHwVirt == HWVIRTTYPE_VTX)
+ {
+ /*
+ * For Intel there is no generic way to query EPT support but on
+ * Linux we can resort to checking for the EPT flag in /proc/cpuinfo
+ */
+ RTFILE hFileCpu;
+ int rc = RTFileOpen(&hFileCpu, "/proc/cpuinfo", RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read enough to fit the first CPU entry in, we only check the first
+ * CPU as all the others should have the same features.
+ */
+ char szBuf[_4K];
+ size_t cbRead = 0;
+
+ RT_ZERO(szBuf); /* Ensure proper termination. */
+ rc = RTFileRead(hFileCpu, &szBuf[0], sizeof(szBuf) - 1, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ /* Look for the start of the flags section. */
+ char *pszStrFlags = RTStrStr(&szBuf[0], "flags");
+ if (pszStrFlags)
+ {
+ /* Look for the end as indicated by new line. */
+ char *pszEnd = pszStrFlags;
+ while ( *pszEnd != '\0'
+ && *pszEnd != '\n')
+ pszEnd++;
+ *pszEnd = '\0'; /* Cut off everything after the flags section. */
+
+ /*
+ * Search for the ept flag indicating support and the absence meaning
+ * not supported.
+ */
+ if (RTStrStr(pszStrFlags, "ept"))
+ fSupported = 1;
+ else
+ fSupported = 0;
+ }
+ }
+ RTFileClose(hFileCpu);
+ }
+ }
+# elif defined(RT_OS_DARWIN)
+ else if (enmHwVirt == HWVIRTTYPE_VTX)
+ {
+ /*
+ * The kern.hv_support parameter indicates support for the hypervisor API in the
+ * kernel, which in turn is documented require nested paging and unrestricted
+ * guest mode. So, if it's there and set we've got nested paging. Howeber, if
+ * it's there and clear we have not definite answer as it might be due to lack
+ * of unrestricted guest mode support.
+ */
+ int32_t fHvSupport = 0;
+ size_t cbOld = sizeof(fHvSupport);
+ if (sysctlbyname("kern.hv_support", &fHvSupport, &cbOld, NULL, 0) == 0)
+ {
+ if (fHvSupport != 0)
+ fSupported = true;
+ }
+ }
+# endif
+#endif
+
+ int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/** Print the 'true' if long mode guests are supported, 'false' if not and
+ * 'dunno' if we cannot tell. */
+static RTEXITCODE handlerCpuLongMode(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+ HWVIRTTYPE enmHwVirt = isHwVirtSupported();
+ int fSupported = 0;
+
+ if (enmHwVirt != HWVIRTTYPE_NONE)
+ {
+#if defined(RT_ARCH_AMD64)
+ fSupported = 1; /* We're running long mode, so it must be supported. */
+
+#elif defined(RT_ARCH_X86)
+# ifdef RT_OS_DARWIN
+ /* On darwin, we just ask the kernel via sysctl. Rules are a bit different here. */
+ int f64bitCapable = 0;
+ size_t cbParameter = sizeof(f64bitCapable);
+ int rc = sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, 0);
+ if (rc != -1)
+ fSupported = f64bitCapable != 0;
+ else
+# endif
+ {
+ /* PAE and HwVirt are required */
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ ASMCpuId(0x00000000, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidStdRange(uEax))
+ {
+ ASMCpuId(0x00000001, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEdx & X86_CPUID_FEATURE_EDX_PAE)
+ {
+ /* AMD will usually advertise long mode in 32-bit mode. Intel OTOH,
+ won't necessarily do so. */
+ ASMCpuId(0x80000000, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidExtRange(uEax))
+ {
+ ASMCpuId(0x80000001, &uEax, &uEbx, &uEcx, &uEdx);
+ if (uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
+ fSupported = 1;
+ else if (enmHwVirt != HWVIRTTYPE_AMDV)
+ fSupported = -1;
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ int cch = RTPrintf(fSupported == 1 ? "true\n" : fSupported == 0 ? "false\n" : "dunno\n");
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/** Print the CPU 'revision', if available. */
+static RTEXITCODE handlerCpuRevision(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
+ if (RTX86IsValidStdRange(uEax) && uEax >= 1)
+ {
+ uint32_t uEax1 = ASMCpuId_EAX(1);
+ uint32_t uVersion = (RTX86GetCpuFamily(uEax1) << 24)
+ | (RTX86GetCpuModel(uEax1, RTX86IsIntelCpu(uEbx, uEcx, uEdx)) << 8)
+ | RTX86GetCpuStepping(uEax1);
+ int cch = RTPrintf("%#x\n", uVersion);
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+#endif
+ return RTEXITCODE_FAILURE;
+}
+
+
+/** Print the CPU name, if available. */
+static RTEXITCODE handlerCpuName(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+
+ char szTmp[1024];
+ int rc = RTMpGetDescription(NIL_RTCPUID, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(rc))
+ {
+ int cch = RTPrintf("%s\n", RTStrStrip(szTmp));
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+ return RTEXITCODE_FAILURE;
+}
+
+
+/** Print the CPU vendor name, 'GenuineIntel' and such. */
+static RTEXITCODE handlerCpuVendor(int argc, char **argv)
+{
+ NOREF(argc); NOREF(argv);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t uEax, uEbx, uEcx, uEdx;
+ ASMCpuId(0, &uEax, &uEbx, &uEcx, &uEdx);
+ int cch = RTPrintf("%.04s%.04s%.04s\n", &uEbx, &uEdx, &uEcx);
+#else
+ int cch = RTPrintf("%s\n", RTBldCfgTargetArch());
+#endif
+ return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * The first argument is a command. Figure out which and call its handler.
+ */
+ static const struct
+ {
+ const char *pszCommand;
+ RTEXITCODE (*pfnHandler)(int argc, char **argv);
+ bool fNoArgs;
+ } s_aHandlers[] =
+ {
+ { "cpuvendor", handlerCpuVendor, true },
+ { "cpuname", handlerCpuName, true },
+ { "cpurevision", handlerCpuRevision, true },
+ { "cpuhwvirt", handlerCpuHwVirt, true },
+ { "nestedpaging", handlerCpuNestedPaging, true },
+ { "longmode", handlerCpuLongMode, true },
+ { "memsize", handlerMemSize, true },
+ { "report", handlerReport, true },
+ { "wipefreespace", handlerWipeFreeSpace, false }
+ };
+
+ if (argc < 2)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "expected command as the first argument");
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
+ {
+ if (!strcmp(argv[1], s_aHandlers[i].pszCommand))
+ {
+ if ( s_aHandlers[i].fNoArgs
+ && argc != 2)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "the command '%s' does not take any arguments", argv[1]);
+ return s_aHandlers[i].pfnHandler(argc - 1, argv + 1);
+ }
+ }
+
+ /*
+ * Help or version query?
+ */
+ for (int i = 1; i < argc; i++)
+ if ( !strcmp(argv[i], "--help")
+ || !strcmp(argv[i], "-h")
+ || !strcmp(argv[i], "-?")
+ || !strcmp(argv[i], "help") )
+ {
+ RTPrintf("usage: %s <cmd> [cmd specific args]\n"
+ "\n"
+ "commands:\n", argv[0]);
+ for (unsigned j = 0; j < RT_ELEMENTS(s_aHandlers); j++)
+ RTPrintf(" %s\n", s_aHandlers[j].pszCommand);
+ return RTEXITCODE_FAILURE;
+ }
+ else if ( !strcmp(argv[i], "--version")
+ || !strcmp(argv[i], "-V") )
+ {
+ RTPrintf("%sr%u", RTBldCfgVersion(), RTBldCfgRevision());
+ return argc == 2 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Syntax error.
+ */
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unknown command '%s'", argv[1]);
+}
+
diff --git a/src/VBox/ValidationKit/testboxscript/darwin/setup-routines.sh b/src/VBox/ValidationKit/testboxscript/darwin/setup-routines.sh
new file mode 100644
index 00000000..ce6d802e
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/darwin/setup-routines.sh
@@ -0,0 +1,190 @@
+# $Id: setup-routines.sh $
+## @file
+# VirtualBox Validation Kit - TestBoxScript Service Setup on Mac OS X (darwin).
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+MY_CONFIG_FILE=/Library/LaunchDaemons/org.virtualbox.testboxscript.plist
+
+##
+# Loads config values from the current installation.
+#
+os_load_config() {
+ if [ -r "${MY_CONFIG_FILE}" ]; then
+ # User.
+ MY_TMP=`/usr/bin/tr '\n' ' ' < "${MY_CONFIG_FILE}" \
+ | /usr/bin/sed \
+ -e 's/ */ /g' \
+ -e 's|\(</[[:alnum:]]*>\)<|\1 <|g' \
+ -e 's|^.*<key>UserName</key> *<string>\([^<>]*\)</string>.*$|\1|'`;
+ if [ -n "${MY_TMP}" ]; then
+ TESTBOXSCRIPT_USER="${MY_TMP}";
+ fi
+
+ # Arguments.
+ XMLARGS=`/usr/bin/tr '\n' ' ' < "${MY_CONFIG_FILE}" \
+ | /usr/bin/sed \
+ -e 's/ */ /g' \
+ -e 's|\(</[[:alnum:]]*>\)<|\1 <|g' \
+ -e 's|^.*ProgramArguments</key> *<array> *\(.*\)</array>.*$|\1|'`;
+ eval common_testboxscript_args_to_config `echo "${XMLARGS}" | sed -e "s/<string>/'/g" -e "s/<\/string>/'/g" `;
+ fi
+}
+
+##
+# Adds an argument ($1) to MY_ARGV (XML plist format).
+#
+os_add_args() {
+ while [ $# -gt 0 ];
+ do
+ case "$1" in
+ *\<* | *\>* | *\&*)
+ MY_TMP='`echo "$1" | sed -e 's/&/&amp;/g' -e 's/</&lt;/g' -e 's/>/&gt;/g'`';
+ MY_ARGV="${MY_ARGV} <string>${MY_TMP}</string>";
+ ;;
+ *)
+ MY_ARGV="${MY_ARGV} <string>$1</string>";
+ ;;
+ esac
+ shift;
+ done
+ MY_ARGV="${MY_ARGV}"'
+ ';
+ return 0;
+}
+
+os_install_service() {
+ # Calc the command line.
+ MY_ARGV=""
+ common_compile_testboxscript_command_line
+
+
+ # Note! It's not possible to use screen 4.0.3 with the launchd due to buggy
+ # "setsid off" handling (and possible other things).
+ cat > "${MY_CONFIG_FILE}" <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key> <string>org.virtualbox.testboxscript</string>
+ <key>UserName</key> <string>${TESTBOXSCRIPT_USER}</string>
+ <key>WorkingDirectory</key> <string>${TESTBOXSCRIPT_DIR}</string>
+ <key>Enabled</key> <true/>
+ <key>RunAtLoad</key> <true/>
+ <key>KeepAlive</key> <true/>
+ <key>StandardInPath</key> <string>/dev/null</string>
+ <key>StandardOutPath</key> <string>/dev/null</string>
+ <key>StandardErrorPath</key> <string>/dev/null</string>
+ <key>ProgramArguments</key>
+ <array>
+ ${MY_ARGV}</array>
+</dict>
+</plist>
+EOF
+
+ return 0;
+}
+
+os_enable_service() {
+ launchctl load -w "${MY_CONFIG_FILE}"
+ return 0;
+}
+
+os_disable_service() {
+ if [ -r "${MY_CONFIG_FILE}" ]; then
+ launchctl unload "${MY_CONFIG_FILE}"
+ fi
+ return 0;
+}
+
+os_add_user() {
+ NEWUID=$(expr `dscl . -readall /Users UniqueID | sed -ne 's/UniqueID: *\([0123456789]*\) *$/\1/p' | sort -n | tail -1 ` + 1)
+ if [ -z "$NEWUID" -o "${NEWUID}" -lt 502 ]; then
+ NEWUID=502;
+ fi
+
+ dscl . -create "/Users/${TESTBOXSCRIPT_USER}" UserShell /bin/bash
+ dscl . -create "/Users/${TESTBOXSCRIPT_USER}" RealName "VBox Test User"
+ dscl . -create "/Users/${TESTBOXSCRIPT_USER}" UniqueID ${NEWUID}
+ dscl . -create "/Users/${TESTBOXSCRIPT_USER}" PrimaryGroupID 80
+ dscl . -create "/Users/${TESTBOXSCRIPT_USER}" NFSHomeDirectory "/Users/vbox"
+ dscl . -passwd "/Users/${TESTBOXSCRIPT_USER}" "password"
+ mkdir -p "/Users/${TESTBOXSCRIPT_USER}"
+}
+
+os_final_message() {
+ cat <<EOF
+
+Additional things to do:"
+ 1. Change the 'Energy Saver' options to never turn off the computer:
+ $ systemsetup -setcomputersleep Never -setdisplaysleep 5 -setharddisksleep 15
+ 2. Check 'Restart automatically if the computer freezes' if available in
+ the 'Energy Saver' settings.
+ $ systemsetup -setrestartfreeze on
+ 3. In the 'Sharing' panel enable (VBox/Oracle):
+ a) 'Remote Login' so ssh works.
+ $ systemsetup -setremotelogin on
+ b) 'Remote Management, tick all the checkboxes in the sheet dialog.
+ Open the 'Computer Settings' and check 'Show Remote Management
+ status in menu bar', 'Anyone may request permission to control
+ screen' and 'VNC viewers may control screen with password'. Set the
+ VNC password to 'password'.
+ 4. Make sure the proxy is configured correctly for your network by going to
+ the 'Network' panel, open 'Advanced...'. For Oracle this means 'TCP/IP'
+ should be configured by 'DHCP' (IPv4) and 'automatically' (IPv6), and
+ the 'Proxies' tab should have 'Automatic Proxy Configuration' checked
+ with the URL containing 'http://wpad.oracle.com/wpad.dat'. (Make sure
+ to hit OK to close the dialog.)
+ 5. Configure NTP to the nearest local time source. For VBox/Oracle this
+ means wei01-time.de.oracle.com:
+ $ systemsetup -setnetworktimeserver wei01-time.de.oracle.com
+ 6. Configure the vbox (pw:password) account for automatic login.
+ 7. For configure the kernel to keep symbols you might need to:
+ a) For 10.11 (El Capitan) and later boot to the recovery partition and
+ either enabling loading of unsigned kexts:
+ $ csrutil enable --without kext
+ or disable SIP all together:
+ $ csrutil disable
+ b) For 10.15 (Catalina) and later you also need to disable
+ the reboot requirement (also from recovery partition):
+ $ spctl kext-consent disable
+ c) If you are running 10.10 (Yosemite) there is a boot-args option for
+ allowing the loading of unsigned kexts. Run the following and reboot:
+ $ sudo nvram boot-args="kext-dev-mode=1"
+ And then run the following:
+ $ sudo nvram boot-args="keepsyms=1"
+
+Enjoy!
+EOF
+}
+
diff --git a/src/VBox/ValidationKit/testboxscript/linux/setup-routines.sh b/src/VBox/ValidationKit/testboxscript/linux/setup-routines.sh
new file mode 100755
index 00000000..d9404c89
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/linux/setup-routines.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+# $Id: setup-routines.sh $
+## @file
+# VirtualBox Validation Kit - TestBoxScript Service Setup.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+# Load the routines we share with the linux installer.
+if test ! -r "${DIR}/linux/setup-installer-routines.sh" -a -r "${DIR}/../../Installer/linux/routines.sh"; then
+ . "${DIR}/../../Installer/linux/routines.sh"
+else
+ . "${DIR}/linux/setup-installer-routines.sh"
+fi
+
+
+os_load_config() {
+ if [ -d /etc/conf.d/ ]; then
+ MY_CONFIG_FILE="/etc/conf.d/testboxscript"
+ elif [ -d /etc/default/ ]; then
+ MY_CONFIG_FILE="/etc/default/testboxscript"
+ else
+ echo "Port me!"
+ exit 1;
+ fi
+ if [ -r "${MY_CONFIG_FILE}" ]; then
+ . "${MY_CONFIG_FILE}"
+ fi
+}
+
+os_install_service() {
+ #
+ # Install the runlevel script.
+ #
+ install_init_script "${TESTBOXSCRIPT_DIR}/testboxscript/linux/testboxscript-service.sh" "testboxscript-service"
+ set +e
+ delrunlevel "testboxscript-service" > /dev/null 2>&1
+ addrunlevel "testboxscript-service" 90 10
+ set -e
+
+ #
+ # Install the configuration file.
+ #
+ echo "# Generated by $0." > "${MY_CONFIG_FILE}"
+ for var in ${TESTBOXSCRIPT_CFG_NAMES};
+ do
+ varcfg=TESTBOXSCRIPT_${var}
+ vardef=TESTBOXSCRIPT_DEFAULT_${var}
+ if [ "${!varcfg}" = "${!vardef}" ]; then
+ echo "# using default value: ${varcfg}=${!varcfg}" >> "${MY_CONFIG_FILE}"
+ else
+ echo "${varcfg}=${!varcfg}" >> "${MY_CONFIG_FILE}"
+ fi
+ done
+
+ # Work around a bug with arrays in old bash versions.
+ if [ ${#TESTBOXSCRIPT_ENVVARS[@]} -ne 0 ]; then
+ set | sed -n -e '/^TESTBOXSCRIPT_ENVVARS=/p' >> "${MY_CONFIG_FILE}"
+ fi
+ return 0;
+}
+
+os_enable_service() {
+ start_init_script testboxscript-service
+ return 0;
+}
+
+os_disable_service() {
+ stop_init_script testboxscript-service 2>&1 || true # Ignore
+ return 0;
+}
+
+os_add_user() {
+ ADD_GROUPS=""
+ if ! grep -q wheel /etc/group; then
+ ADD_GROUPS="-G wheel"
+ fi
+ set -e
+ useradd -m -U -p password -s /bin/bash ${ADD_GROUPS} "${TESTBOXSCRIPT_USER}"
+ set +e
+ return 0;
+}
+
+check_for_cifs() {
+ test -x /sbin/mount.cifs -o -x /usr/sbin/mount.cifs
+ grep -wq cifs /proc/filesystems || modprobe cifs;
+ # Note! If modprobe doesn't work above, /sbin and /usr/sbin are probably missing from the search PATH.
+ return 0;
+}
+
+##
+# Test if core dumps are enabled. See https://wiki.ubuntu.com/Apport!
+#
+test_coredumps() {
+ if test "`lsb_release -is`" = "Ubuntu"; then
+ if grep -q "apport" /proc/sys/kernel/core_pattern; then
+ if grep -q "#.*problem_types" /etc/apport/crashdb.conf; then
+ echo "It looks like core dumps are properly configured, good!"
+ else
+ echo "Warning: Core dumps will be not always generated!"
+ fi
+ else
+ echo "Warning: Apport not installed! This package is required for core dump handling!"
+ fi
+ fi
+}
+
+##
+# Test if unattended updates are disabled. See
+# http://ask.xmodulo.com/disable-automatic-updates-ubuntu.html
+test_unattended_updates_disabled() {
+ if grep "APT::Periodic::Unattended-Upgrade.*1" /etc/apt/apt.conf.d/* 2>/dev/null; then
+ echo "Unattended updates enabled?"
+ return 1
+ fi
+ if grep "APT::Periodic::Update-Package-List.*1" /etc/apt/apt.conf.d/* 2>/dev/null; then
+ echo "Unattended package updates enabled?"
+ return 1
+ fi
+}
+
+os_final_message() {
+ cat <<EOF
+
+Additional things to do:"
+ 1. Check if the proxy settings are appropriate for reaching the test
+ manager host. Python does not support domain matches starting with ".".
+
+ For Debian and Ubuntu: check /etc/environment.
+ For EL: check /etc/profile and/or the files in /etc/profile.d/.
+
+ 2. If the system should be doing RAM disk based testing, add the following
+ (or something similar, adapted to the system) to /etc/fstab:
+
+ tmpfs /var/tmp/testbox-1000 tmpfs defaults,size=16G 0 0
+
+After making such adjustments, it's the easiest solution to reboot the testbox.
+
+Enjoy!
+EOF
+}
+
diff --git a/src/VBox/ValidationKit/testboxscript/linux/testboxscript-service.sh b/src/VBox/ValidationKit/testboxscript/linux/testboxscript-service.sh
new file mode 100755
index 00000000..66892817
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/linux/testboxscript-service.sh
@@ -0,0 +1,519 @@
+#!/bin/sh
+## @file
+# VirtualBox Validation Kit - TestBoxScript service init script.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# chkconfig: 35 35 65
+# description: TestBoxScript service
+#
+### BEGIN INIT INFO
+# Provides: testboxscript-service
+# Required-Start: $network
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Description: TestBoxScript service
+### END INIT INFO
+
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+
+#
+# Load config and set up defaults.
+#
+service_name="testboxscript"
+
+[ -r /etc/default/${service_name} ] && . /etc/default/${service_name}
+[ -r /etc/conf.d/${service_name} ] && . /etc/conf.d/${service_name}
+
+if [ -z "${TESTBOXSCRIPT_DIR}" ]; then
+ TESTBOXSCRIPT_DIR="/opt/testboxscript"
+fi
+if [ -z "${TESTBOXSCRIPT_USER}" ]; then
+ TESTBOXSCRIPT_USER="vbox"
+fi
+binary="${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py"
+binary_real="${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript_real.py"
+
+
+
+#
+# Detect and abstract distro
+#
+[ -f /etc/debian_release -a -f /lib/lsb/init-functions ] || NOLSB=yes
+
+system=unknown
+if [ -f /etc/redhat-release ]; then
+ system=redhat
+ PIDFILE="/var/run/${service_name}-service.pid"
+elif [ -f /etc/SuSE-release ]; then
+ system=suse
+ PIDFILE="/var/lock/subsys/${service_name}-service"
+elif [ -f /etc/debian_version ]; then
+ system=debian
+ PIDFILE="/var/run/${service_name}-service"
+elif [ -f /etc/gentoo-release ]; then
+ system=gentoo
+ PIDFILE="/var/run/${service_name}-service"
+elif [ -f /etc/arch-release ]; then
+ system=arch
+ PIDFILE="/var/run/${service_name}-service"
+elif [ -f /etc/slackware-version ]; then
+ system=slackware
+ PIDFILE="/var/run/${service_name}-service"
+elif [ -f /etc/lfs-release ]; then
+ system=lfs
+ PIDFILE="/var/run/${service_name}-service.pid"
+else
+ system=other
+ if [ -d /var/run -a -w /var/run ]; then
+ PIDFILE="/var/run/${service_name}-service"
+ fi
+fi
+
+
+#
+# Generic implementation.
+#
+
+## Query daemon status.
+# $1 = daemon-user; $2 = binary name
+# returns 0 if running, 1 if started but no longer running, 3 if not started.
+# When 0 is return the pid variable contains a list of relevant pids.
+my_query_status() {
+ a_USER="$1";
+ a_BINARY="$2";
+ pid="";
+ if [ -f "${PIDFILE}" -a -s "${PIDFILE}" ]; then
+ MY_LINE="";
+ read MY_LINE < "${PIDFILE}";
+ for MY_PID in `echo $MY_LINE | sed -e 's/[^0123456789 ]/ /g'`;
+ do
+ if [ "`stat -c '%U' /proc/$MY_PID 2> /dev/null `" = "$a_USER" ]; then
+ pid="${pid} ${MY_PID}";
+ fi
+ done
+ if [ -n "${pid}" ]; then
+ RETVAL=0;
+ else
+ RETVAL=1;
+ fi
+ else
+ RETVAL=3
+ fi
+ return $RETVAL;
+}
+
+## Starts detached daeamon in screen or tmux.
+# $1 = daemon-user; $2+ = daemon and its arguments
+my_start_daemon() {
+ a_USER="$1"
+ shift
+ if touch "${PIDFILE}" && chown "${a_USER}" -- "${PIDFILE}"; then
+ ARGS=""
+ while [ $# -gt 0 ];
+ do
+ ARGS="$ARGS '$1'";
+ shift
+ done
+ ARGS="$ARGS --pidfile '$PIDFILE'";
+ if type screen > /dev/null; then
+ su - "${a_USER}" -c "screen -S ${service_name} -d -m ${ARGS}";
+ elif type tmux > /dev/null; then
+ su - "${a_USER}" -c "tmux new-session -AdD -s ${service_name} ${ARGS}";
+ else
+ echo "Need screen or tmux, please install!"
+ exit 1
+ fi
+ RETVAL=$?;
+ if [ $RETVAL -eq 0 ]; then
+ sleep 0.6;
+ if [ ! -s "$PIDFILE" ]; then sleep 1; fi
+ if [ ! -s "$PIDFILE" ]; then sleep 2; fi
+ if [ ! -s "$PIDFILE" ]; then sleep 3; fi
+ if [ -s "$PIDFILE" ]; then
+ RETVAL=0;
+ else
+ RETVAL=1;
+ fi
+ else
+ fail_msg "su failed with exit code $RETVAL";
+ fi
+ else
+ fail_msg "Failed to create pid file and change it's ownership to ${a_USER}."
+ RETVAL=1;
+ fi
+ return $RETVAL;
+}
+
+## Stops the daemon.
+# $1 = daemon-user; $2 = binary name
+my_stop_daemon() {
+ a_USER="$1";
+ a_BINARY="$2";
+ my_query_status "$a_USER" "$a_BINARY"
+ RETVAL=$?
+ if [ $RETVAL -eq 0 -a -n "$pid" ]; then
+ kill $pid;
+ fi
+ sleep 0.6
+ if my_query_status "$a_USER" "$a_BINARY"; then sleep 1; fi
+ if my_query_status "$a_USER" "$a_BINARY"; then sleep 2; fi
+ if my_query_status "$a_USER" "$a_BINARY"; then sleep 3; fi
+ if ! my_query_status "$a_USER" "$a_BINARY"; then
+ rm -f -- "${PIDFILE}"
+ return 0;
+ fi
+ return 1;
+}
+
+if [ -z "$NOLSB" ]; then
+ . /lib/lsb/init-functions
+ fail_msg() {
+ echo ""
+ log_failure_msg "$1"
+ }
+ succ_msg() {
+ log_success_msg " done."
+ }
+ begin_msg() {
+ log_daemon_msg "$@"
+ }
+else
+ fail_msg() {
+ echo " ...fail!"
+ echo "$@"
+ }
+ succ_msg() {
+ echo " ...done."
+ }
+ begin_msg() {
+ echo -n "$1"
+ }
+fi
+
+#
+# System specific overrides.
+#
+
+if [ "$system" = "redhat" ]; then
+ . /etc/init.d/functions
+ if [ -n "$NOLSB" ]; then
+ fail_msg() {
+ echo_failure
+ echo
+ }
+ succ_msg() {
+ echo_success
+ echo
+ }
+ begin_msg() {
+ echo -n "$1"
+ }
+ fi
+fi
+
+if [ "$system" = "suse" ]; then
+ . /etc/rc.status
+ if [ -n "$NOLSB" ]; then
+ fail_msg() {
+ rc_failed 1
+ rc_status -v
+ }
+ succ_msg() {
+ rc_reset
+ rc_status -v
+ }
+ begin_msg() {
+ echo -n "$1"
+ }
+ fi
+fi
+
+if [ "$system" = "debian" ]; then
+ # Share my_start_daemon and my_stop_daemon with gentoo
+ if [ -n "$NOLSB" ]; then
+ fail_msg() {
+ echo " ...fail!"
+ }
+ succ_msg() {
+ echo " ...done."
+ }
+ begin_msg() {
+ echo -n "$1"
+ }
+ fi
+fi
+
+if [ "$system" = "gentoo" ]; then
+ if [ -f /sbin/functions.sh ]; then
+ . /sbin/functions.sh
+ elif [ -f /etc/init.d/functions.sh ]; then
+ . /etc/init.d/functions.sh
+ fi
+ # Share my_start_daemon and my_stop_daemon with debian.
+ if [ -n "$NOLSB" ]; then
+ if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+ fi
+ fi
+fi
+
+if [ "$system" = "debian" -o "$system" = "gentoo" ]; then
+ #my_start_daemon() {
+ # usr="$1"
+ # shift
+ # bin="$1"
+ # shift
+ # echo usr=$usr
+ # start-stop-daemon --start --background --pidfile "${PIDFILE}" --make-pidfile --chuid "${usr}" --user "${usr}" \
+ # --exec $bin -- $@
+ #}
+ my_stop_daemon() {
+ a_USER="$1"
+ a_BINARY="$2"
+ start-stop-daemon --stop --user "${a_USER}" --pidfile "${PIDFILE}"
+ RETVAL=$?
+ rm -f "${PIDFILE}"
+ return $RETVAL
+ }
+fi
+
+if [ "$system" = "arch" ]; then
+ USECOLOR=yes
+ . /etc/rc.d/functions
+ if [ -n "$NOLSB" ]; then
+ fail_msg() {
+ stat_fail
+ }
+ succ_msg() {
+ stat_done
+ }
+ begin_msg() {
+ stat_busy "$1"
+ }
+ fi
+fi
+
+if [ "$system" = "lfs" ]; then
+ . /etc/rc.d/init.d/functions
+ if [ -n "$NOLSB" ]; then
+ fail_msg() {
+ echo_failure
+ }
+ succ_msg() {
+ echo_ok
+ }
+ begin_msg() {
+ echo $1
+ }
+ fi
+fi
+
+#
+# Implement the actions.
+#
+check_single_user() {
+ if [ -n "$2" ]; then
+ fail_msg "TESTBOXSCRIPT_USER must not contain multiple users!"
+ exit 1
+ fi
+}
+
+#
+# Open ports at the firewall:
+# 6000..6100 / TCP for VRDP
+# 5000..5032 / TCP for netperf
+# 5000..5032 / UDP for netperf
+#
+set_iptables() {
+ if [ -x /sbin/iptables ]; then
+ I="/sbin/iptables -j ACCEPT -A INPUT -m state --state NEW"
+ if ! /sbin/iptables -L INPUT | grep -q "testsuite vrdp"; then
+ $I -m tcp -p tcp --dport 6000:6100 -m comment --comment "testsuite vrdp"
+ fi
+ if ! /sbin/iptables -L INPUT | grep -q "testsuite perftcp"; then
+ $I -m tcp -p tcp --dport 5000:5032 -m comment --comment "testsuite perftcp"
+ fi
+ if ! /sbin/iptables -L INPUT | grep -q "testsuite perfudp"; then
+ $I -m udp -p udp --dport 5000:5032 -m comment --comment "testsuite perfudp"
+ fi
+ fi
+}
+
+
+start() {
+ if [ ! -f "${PIDFILE}" ]; then
+ begin_msg "Starting TestBoxScript";
+
+ #
+ # Verify config and installation.
+ #
+ if [ ! -d "$TESTBOXSCRIPT_DIR" -o ! -r "$binary" -o ! -r "$binary_real" ]; then
+ fail_msg "Cannot find TestBoxScript installation under '$TESTBOXSCRIPT_DIR'!"
+ exit 0;
+ fi
+ ## @todo check ownership (for upgrade purposes)
+ check_single_user $TESTBOXSCRIPT_USER
+
+ #
+ # Open some ports in the firewall
+ # Allows to access VMs remotely by VRDP, netperf
+ #
+ set_iptables
+
+ #
+ # Set execute bits to make installation (unzip) easier.
+ #
+ chmod a+x > /dev/null 2>&1 \
+ "${binary}" \
+ "${binary_real}" \
+ "${TESTBOXSCRIPT_DIR}/linux/amd64/TestBoxHelper" \
+ "${TESTBOXSCRIPT_DIR}/linux/x86/TestBoxHelper"
+
+ #
+ # Start the daemon as the specified user.
+ #
+ PARAMS=""
+ if [ "${TESTBOXSCRIPT_HWVIRT}" = "yes" ]; then PARAMS="${PARAMS} --hwvirt"; fi
+ if [ "${TESTBOXSCRIPT_HWVIRT}" = "no" ]; then PARAMS="${PARAMS} --no-hwvirt"; fi
+ if [ "${TESTBOXSCRIPT_NESTED_PAGING}" = "yes" ]; then PARAMS="${PARAMS} --nested-paging"; fi
+ if [ "${TESTBOXSCRIPT_NESTED_PAGING}" = "no" ]; then PARAMS="${PARAMS} --no-nested-paging"; fi
+ if [ "${TESTBOXSCRIPT_IOMMU}" = "yes" ]; then PARAMS="${PARAMS} --io-mmu"; fi
+ if [ "${TESTBOXSCRIPT_IOMMU}" = "no" ]; then PARAMS="${PARAMS} --no-io-mmu"; fi
+ if [ "${TESTBOXSCRIPT_SPB}" = "yes" ]; then PARAMS="${PARAMS} --spb"; fi
+ if [ -n "${TESTBOXSCRIPT_SYSTEM_UUID}" ]; then PARAMS="${PARAMS} --system-uuid '${TESTBOXSCRIPT_SYSTEM_UUID}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TEST_MANAGER}" ]; then PARAMS="${PARAMS} --test-manager '${TESTBOXSCRIPT_TEST_MANAGER}'"; fi
+ if [ -n "${TESTBOXSCRIPT_SCRATCH_ROOT}" ]; then PARAMS="${PARAMS} --scratch-root '${TESTBOXSCRIPT_SCRATCH_ROOT}'"; fi
+
+ if [ -n "${TESTBOXSCRIPT_BUILDS_PATH}" ]; then PARAMS="${PARAMS} --builds-path '${TESTBOXSCRIPT_BUILDS_PATH}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_TYPE}" ]; then PARAMS="${PARAMS} --builds-server-type '${TESTBOXSCRIPT_BUILDS_TYPE}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_NAME}" ]; then PARAMS="${PARAMS} --builds-server-name '${TESTBOXSCRIPT_BUILDS_NAME}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_SHARE}" ]; then PARAMS="${PARAMS} --builds-server-share '${TESTBOXSCRIPT_BUILDS_SHARE}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_USER}" ]; then PARAMS="${PARAMS} --builds-server-user '${TESTBOXSCRIPT_BUILDS_USER}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_PASSWD}" ]; then PARAMS="${PARAMS} --builds-server-passwd '${TESTBOXSCRIPT_BUILDS_PASSWD}'"; fi
+ if [ -n "${TESTBOXSCRIPT_BUILDS_MOUNTOPT}" ]; then PARAMS="${PARAMS} --builds-server-mountopt '${TESTBOXSCRIPT_BUILDS_MOUNTOPT}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_PATH}" ]; then PARAMS="${PARAMS} --testrsrc-path '${TESTBOXSCRIPT_TESTRSRC_PATH}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_TYPE}" ]; then PARAMS="${PARAMS} --testrsrc-server-type '${TESTBOXSCRIPT_TESTRSRC_TYPE}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_NAME}" ]; then PARAMS="${PARAMS} --testrsrc-server-name '${TESTBOXSCRIPT_TESTRSRC_NAME}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_SHARE}" ]; then PARAMS="${PARAMS} --testrsrc-server-share '${TESTBOXSCRIPT_TESTRSRC_SHARE}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_USER}" ]; then PARAMS="${PARAMS} --testrsrc-server-user '${TESTBOXSCRIPT_TESTRSRC_USER}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_PASSWD}" ]; then PARAMS="${PARAMS} --testrsrc-server-passwd '${TESTBOXSCRIPT_TESTRSRC_PASSWD}'"; fi
+ if [ -n "${TESTBOXSCRIPT_TESTRSRC_MOUNTOPT}" ]; then PARAMS="${PARAMS} --testrsrc-server-mountopt '${TESTBOXSCRIPT_TESTRSRC_MOUNTOPT}'"; fi
+
+ if [ -n "${TESTBOXSCRIPT_PYTHON}" ]; then
+ my_start_daemon "${TESTBOXSCRIPT_USER}" "${TESTBOXSCRIPT_PYTHON}" "${binary}" ${PARAMS}
+ else
+ my_start_daemon "${TESTBOXSCRIPT_USER}" "${binary}" ${PARAMS}
+ fi
+ RETVAL=$?
+
+ if [ $RETVAL -eq 0 ]; then
+ succ_msg
+ else
+ fail_msg
+ fi
+ else
+ succ_msg "Already running."
+ RETVAL=0
+ fi
+ return $RETVAL
+}
+
+stop() {
+ if [ -f "${PIDFILE}" ]; then
+ begin_msg "Stopping TestBoxScript";
+ my_stop_daemon "${TESTBOXSCRIPT_USER}" "${binary}"
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]; then
+ succ_msg
+ else
+ fail_msg
+ fi
+ else
+ RETVAL=0
+ fi
+ return $RETVAL
+}
+
+restart() {
+ stop && sleep 1 && start
+}
+
+status() {
+ echo -n "Checking for TestBoxScript"
+ my_query_status "${TESTBOXSCRIPT_USER}" "${binary}"
+ RETVAL=$?
+ if [ ${RETVAL} -eq 0 ]; then
+ echo " ...running"
+ elif [ ${RETVAL} -eq 3 ]; then
+ echo " ...stopped"
+ elif [ ${RETVAL} -eq 1 ]; then
+ echo " ...started but not running"
+ else
+ echo " ...unknown status '${RETVAL}'"
+ fi
+}
+
+
+#
+# main().
+#
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ force-reload)
+ restart
+ ;;
+ status)
+ status
+ ;;
+ setup)
+ ;;
+ cleanup)
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
+
diff --git a/src/VBox/ValidationKit/testboxscript/setup.sh b/src/VBox/ValidationKit/testboxscript/setup.sh
new file mode 100755
index 00000000..dd51f74a
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/setup.sh
@@ -0,0 +1,714 @@
+#!/usr/bin/env bash
+# $Id: setup.sh $
+## @file
+# VirtualBox Validation Kit - TestBoxScript Service Setup on Unixy platforms.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+#
+# !WARNING! Running the whole script in exit-on-failure mode.
+#
+# Note! Looking at the ash sources, it seems flags will be saved and restored
+# when calling functions. That's comforting.
+#
+set -e
+#set -x # debug only, disable!
+
+##
+# Get the host OS name, returning it in RETVAL.
+#
+get_host_os() {
+ RETVAL=`uname`
+ case "$RETVAL" in
+ Darwin|darwin)
+ RETVAL=darwin
+ ;;
+
+ DragonFly)
+ RETVAL=dragonfly
+ ;;
+
+ freebsd|FreeBSD|FREEBSD)
+ RETVAL=freebsd
+ ;;
+
+ Haiku)
+ RETVAL=haiku
+ ;;
+
+ linux|Linux|GNU/Linux|LINUX)
+ RETVAL=linux
+ ;;
+
+ netbsd|NetBSD|NETBSD)
+ RETVAL=netbsd
+ ;;
+
+ openbsd|OpenBSD|OPENBSD)
+ RETVAL=openbsd
+ ;;
+
+ os2|OS/2|OS2)
+ RETVAL=os2
+ ;;
+
+ SunOS)
+ RETVAL=solaris
+ ;;
+
+ WindowsNT|CYGWIN_NT-*)
+ RETVAL=win
+ ;;
+
+ *)
+ echo "$0: unknown os $RETVAL" 1>&2
+ exit 1
+ ;;
+ esac
+ return 0;
+}
+
+##
+# Get the host OS/CPU arch, returning it in RETVAL.
+#
+get_host_arch() {
+ if [ "${HOST_OS}" = "solaris" ]; then
+ RETVAL=`isainfo | cut -f 1 -d ' '`
+ else
+ RETVAL=`uname -m`
+ fi
+ case "${RETVAL}" in
+ amd64|AMD64|x86_64|k8|k8l|k9|k10)
+ RETVAL='amd64'
+ ;;
+ x86|i86pc|ia32|i[3456789]86|BePC)
+ RETVAL='x86'
+ ;;
+ sparc32|sparc|sparcv8|sparcv7|sparcv8e)
+ RETVAL='sparc32'
+ ;;
+ sparc64|sparcv9)
+ RETVAL='sparc64'
+ ;;
+ s390)
+ RETVAL='s390'
+ ;;
+ s390x)
+ RETVAL='s390x'
+ ;;
+ ppc32|ppc|powerpc)
+ RETVAL='ppc32'
+ ;;
+ ppc64|powerpc64)
+ RETVAL='ppc64'
+ ;;
+ mips32|mips)
+ RETVAL='mips32'
+ ;;
+ mips64)
+ RETVAL='mips64'
+ ;;
+ ia64)
+ RETVAL='ia64'
+ ;;
+ hppa32|parisc32|parisc)
+ RETVAL='hppa32'
+ ;;
+ hppa64|parisc64)
+ RETVAL='hppa64'
+ ;;
+ arm|arm64|armv4l|armv5tel|armv5tejl)
+ RETVAL='arm'
+ ;;
+ arm64|aarch64)
+ RETVAL='arm64'
+ ;;
+ alpha)
+ RETVAL='alpha'
+ ;;
+
+ *)
+ echo "$0: unknown cpu/arch - $RETVAL" 1>&$2
+ exit 1
+ ;;
+ esac
+ return 0;
+}
+
+
+##
+# Loads config values from the current installation.
+#
+os_load_config() {
+ echo "os_load_config is not implemented" 2>&1
+ exit 1
+}
+
+##
+# Installs, configures and starts the service.
+#
+os_install_service() {
+ echo "os_install_service is not implemented" 2>&1
+ exit 1
+}
+
+##
+# Enables (starts) the service.
+os_enable_service() {
+ echo "os_enable_service is not implemented" 2>&1
+ return 0;
+}
+
+##
+# Disables (stops) the service.
+os_disable_service() {
+ echo "os_disable_service is not implemented" 2>&1
+ return 0;
+}
+
+##
+# Adds the testbox user
+#
+os_add_user() {
+ echo "os_add_user is not implemented" 2>&1
+ exit 1
+}
+
+##
+# Prints a final message after successful script execution.
+# This can contain additional instructions which needs to be carried out
+# manually or similar.
+os_final_message() {
+ return 0;
+}
+
+##
+# Checks the installation, verifying that files are there and scripts work fine.
+#
+check_testboxscript_install() {
+
+ # Presence
+ test -r "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py"
+ test -r "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript_real.py"
+ test -r "${TESTBOXSCRIPT_DIR}/testboxscript/linux/testboxscript-service.sh" -o "${HOST_OS}" != "linux"
+ test -r "${TESTBOXSCRIPT_DIR}/${HOST_OS}/${HOST_ARCH}/TestBoxHelper"
+
+ # Zip file may be missing the x bits, so set them.
+ chmod a+x \
+ "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py" \
+ "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript_real.py" \
+ "${TESTBOXSCRIPT_DIR}/${HOST_OS}/${HOST_ARCH}/TestBoxHelper" \
+ "${TESTBOXSCRIPT_DIR}/testboxscript/linux/testboxscript-service.sh"
+
+
+ # Check that the scripts work.
+ set +e
+ "${TESTBOXSCRIPT_PYTHON}" "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py" --version > /dev/null
+ if [ $? -ne 2 ]; then
+ echo "$0: error: testboxscript.py didn't respons correctly to the --version option."
+ exit 1;
+ fi
+
+ "${TESTBOXSCRIPT_PYTHON}" "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript_real.py" --version > /dev/null
+ if [ $? -ne 2 ]; then
+ echo "$0: error: testboxscript.py didn't respons correctly to the --version option."
+ exit 1;
+ fi
+ set -e
+
+ return 0;
+}
+
+##
+# Check that sudo is installed.
+#
+check_for_sudo() {
+ which sudo
+ test -f "${MY_ETC_SUDOERS}"
+}
+
+##
+# Check that sudo is installed.
+#
+check_for_cifs() {
+ return 0;
+}
+
+##
+# Checks if the testboxscript_user exists.
+does_testboxscript_user_exist() {
+ id "${TESTBOXSCRIPT_USER}" > /dev/null 2>&1
+ return $?;
+}
+
+##
+# hushes up the root login.
+maybe_hush_up_root_login() {
+ # This is a solaris hook.
+ return 0;
+}
+
+##
+# Adds the testbox user and make sure it has unrestricted sudo access.
+maybe_add_testboxscript_user() {
+ if ! does_testboxscript_user_exist; then
+ os_add_user "${TESTBOXSCRIPT_USER}"
+ fi
+
+ SUDOERS_LINE="${TESTBOXSCRIPT_USER} ALL=(ALL) NOPASSWD: ALL"
+ if ! ${MY_FGREP} -q "${SUDOERS_LINE}" ${MY_ETC_SUDOERS}; then
+ echo "# begin tinderboxscript setup.sh" >> ${MY_ETC_SUDOERS}
+ echo "${SUDOERS_LINE}" >> ${MY_ETC_SUDOERS}
+ echo "# end tinderboxscript setup.sh" >> ${MY_ETC_SUDOERS}
+ fi
+
+ maybe_hush_up_root_login;
+}
+
+
+##
+# Test the user.
+#
+test_user() {
+ su - "${TESTBOXSCRIPT_USER}" -c "true"
+
+ # sudo 1.7.0 adds the -n option.
+ MY_TMP="`sudo -V 2>&1 | head -1 | sed -e 's/^.*version 1\.[6543210]\..*$/old/'`"
+ if [ "${MY_TMP}" != "old" ]; then
+ echo "Warning: If sudo starts complaining about not having a tty,"
+ echo " disable the requiretty option in /etc/sudoers."
+ su - "${TESTBOXSCRIPT_USER}" -c "sudo -n -i true"
+ else
+ echo "Warning: You've got an old sudo installed. If it starts"
+ echo " complaining about not having a tty, disable the"
+ echo " requiretty option in /etc/sudoers."
+ su - "${TESTBOXSCRIPT_USER}" -c "sudo true"
+ fi
+}
+
+##
+# Test if core dumps are enabled. See https://wiki.ubuntu.com/Apport!
+#
+test_coredumps() {
+ # This is a linux hook.
+ return 0;
+}
+
+##
+# Test if unattended updates are disabled. See
+# http://ask.xmodulo.com/disable-automatic-updates-ubuntu.html
+test_unattended_updates_disabled() {
+ # This is a linux hook.
+ return 0;
+}
+
+##
+# Grants the user write access to the testboxscript files so it can perform
+# upgrades.
+#
+grant_user_testboxscript_write_access() {
+ chown -R "${TESTBOXSCRIPT_USER}" "${TESTBOXSCRIPT_DIR}"
+}
+
+##
+# Check the proxy setup.
+#
+check_proxy_config() {
+ if [ -n "${http_proxy}" -o -n "${ftp_proxy}" ]; then
+ if [ -z "${no_proxy}" ]; then
+ echo "Error: Env.vars. http_proxy/ftp_proxy without no_proxy is going to break upgrade among other things."
+ exit 1
+ fi
+ fi
+}
+
+##
+# Parses the testboxscript.py invocation, setting TESTBOXSCRIPT_xxx config
+# variables accordingly. Both darwin and solaris uses this.
+common_testboxscript_args_to_config()
+{
+ MY_ARG=0
+ while [ $# -gt 0 ];
+ do
+ case "$1" in
+ # boolean
+ "--hwvirt") TESTBOXSCRIPT_HWVIRT="yes";;
+ "--no-hwvirt") TESTBOXSCRIPT_HWVIRT="no";;
+ "--nested-paging") TESTBOXSCRIPT_NESTED_PAGING="yes";;
+ "--no-nested-paging") TESTBOXSCRIPT_NESTED_PAGING="no";;
+ "--io-mmu") TESTBOXSCRIPT_IOMMU="yes";;
+ "--no-io-mmu") TESTBOXSCRIPT_IOMMU="no";;
+ # optios taking values.
+ "--system-uuid") TESTBOXSCRIPT_SYSTEM_UUID="$2"; shift;;
+ "--scratch-root") TESTBOXSCRIPT_SCRATCH_ROOT="$2"; shift;;
+ "--test-manager") TESTBOXSCRIPT_TEST_MANAGER="$2"; shift;;
+ "--builds-path") TESTBOXSCRIPT_BUILDS_PATH="$2"; shift;;
+ "--builds-server-type") TESTBOXSCRIPT_BUILDS_TYPE="$2"; shift;;
+ "--builds-server-name") TESTBOXSCRIPT_BUILDS_NAME="$2"; shift;;
+ "--builds-server-share") TESTBOXSCRIPT_BUILDS_SHARE="$2"; shift;;
+ "--builds-server-user") TESTBOXSCRIPT_BUILDS_USER="$2"; shift;;
+ "--builds-server-passwd") TESTBOXSCRIPT_BUILDS_PASSWD="$2"; shift;;
+ "--builds-server-mountopt") TESTBOXSCRIPT_BUILDS_MOUNTOPT="$2"; shift;;
+ "--testrsrc-path") TESTBOXSCRIPT_TESTRSRC_PATH="$2"; shift;;
+ "--testrsrc-server-type") TESTBOXSCRIPT_TESTRSRC_TYPE="$2"; shift;;
+ "--testrsrc-server-name") TESTBOXSCRIPT_TESTRSRC_NAME="$2"; shift;;
+ "--testrsrc-server-share") TESTBOXSCRIPT_TESTRSRC_SHARE="$2"; shift;;
+ "--testrsrc-server-user") TESTBOXSCRIPT_TESTRSRC_USER="$2"; shift;;
+ "--testrsrc-server-passwd") TESTBOXSCRIPT_TESTRSRC_PASSWD="$2"; shift;;
+ "--testrsrc-server-mountopt") TESTBOXSCRIPT_TESTRSRC_MOUNTOPT="$2"; shift;;
+ "--spb") ;;
+ "--putenv")
+ MY_FOUND=no
+ MY_VAR=`echo $2 | sed -e 's/=.*$//' `
+ for i in ${!TESTBOXSCRIPT_ENVVARS[@]};
+ do
+ MY_CURVAR=`echo "${TESTBOXSCRIPT_ENVVARS[i]}" | sed -e 's/=.*$//' `
+ if [ -n "${MY_CURVAR}" -a "${MY_CURVAR}" = "${MY_VAR}" ]; then
+ TESTBOXSCRIPT_ENVVARS[$i]="$2"
+ MY_FOUND=yes
+ fi
+ done
+ if [ "${MY_FOUND}" = "no" ]; then
+ TESTBOXSCRIPT_ENVVARS=( "${TESTBOXSCRIPT_ENVVARS[@]}" "$2" );
+ fi
+ shift;;
+ --*)
+ echo "error: Unknown option '$1' in existing config"
+ exit 1
+ ;;
+
+ # Non-option bits.
+ *.py) ;; # ignored, should be the script.
+
+ *) if [ ${MY_ARG} -ne 0 ]; then
+ echo "error: unknown non-option '$1' in existing config"
+ exit 1
+ fi
+ TESTBOXSCRIPT_PYTHON="$1"
+ ;;
+ esac
+ shift
+ MY_ARG=$((${MY_ARG} + 1))
+ done
+}
+
+##
+# Used by common_compile_testboxscript_command_line, please override.
+#
+os_add_args() {
+ echo "os_add_args is not implemented" 2>&1
+ exit 1
+}
+
+##
+# Compiles the testboxscript.py command line given the current
+# configuration and defaults.
+#
+# This is used by solaris and darwin.
+#
+# The os_add_args function will be called several with one or two arguments
+# each time. The caller must override it.
+#
+common_compile_testboxscript_command_line() {
+ if [ -n "${TESTBOXSCRIPT_PYTHON}" ]; then
+ os_add_args "${TESTBOXSCRIPT_PYTHON}"
+ fi
+ os_add_args "${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py"
+
+ for var in ${TESTBOXSCRIPT_CFG_NAMES};
+ do
+ varcfg=TESTBOXSCRIPT_${var}
+ vardef=TESTBOXSCRIPT_DEFAULT_${var}
+ if [ "${!varcfg}" != "${!vardef}" -a "${var}" != "PYTHON" ]; then # PYTHON handled above.
+ my_opt=TESTBOXSCRIPT_OPT_${var}
+ if [ -n "${!my_opt}" ]; then
+ if [ "${!my_opt}" == "--spb" ]; then
+ os_add_args "${!my_opt}"
+ elif [ "${!my_opt}" != "--skip" ]; then
+ os_add_args "${!my_opt}" "${!varcfg}"
+ fi
+ else
+ my_opt_yes=${my_opt}_YES
+ my_opt_no=${my_opt}_NO
+ if [ -n "${!my_opt_yes}" -a -n "${!my_opt_no}" ]; then
+ if [ "${!varcfg}" = "yes" ]; then
+ os_add_args "${!my_opt_yes}";
+ else
+ if [ "${!varcfg}" != "no" ]; then
+ echo "internal option misconfig: var=${var} not a yes/no value: ${!varcfg}";
+ exit 1;
+ fi
+ os_add_args "${!my_opt_yes}";
+ fi
+ else
+ echo "internal option misconfig: var=${var} my_opt_yes=${my_opt_yes}=${!my_opt_yes} my_opt_no=${my_opt_no}=${!my_opt_no}"
+ exit 1;
+ fi
+ fi
+ fi
+ done
+
+ i=0
+ while [ "${i}" -lt "${#TESTBOXSCRIPT_ENVVARS[@]}" ];
+ do
+ os_add_args "--putenv" "${TESTBOXSCRIPT_ENVVARS[${i}]}"
+ i=$((${i} + 1))
+ done
+}
+
+
+#
+#
+# main()
+#
+#
+
+
+#
+# Get our bearings and include the host specific code.
+#
+MY_ETC_SUDOERS="/etc/sudoers"
+MY_FGREP=fgrep
+DIR=`dirname "$0"`
+DIR=`cd "${DIR}"; /bin/pwd`
+
+get_host_os
+HOST_OS=${RETVAL}
+get_host_arch
+HOST_ARCH=${RETVAL}
+
+. "${DIR}/${HOST_OS}/setup-routines.sh"
+
+
+#
+# Config.
+#
+TESTBOXSCRIPT_CFG_NAMES="DIR PYTHON USER HWVIRT IOMMU NESTED_PAGING SYSTEM_UUID PATH_TESTRSRC TEST_MANAGER SCRATCH_ROOT"
+TESTBOXSCRIPT_CFG_NAMES="${TESTBOXSCRIPT_CFG_NAMES} BUILDS_PATH BUILDS_TYPE BUILDS_NAME BUILDS_SHARE BUILDS_USER"
+TESTBOXSCRIPT_CFG_NAMES="${TESTBOXSCRIPT_CFG_NAMES} BUILDS_PASSWD BUILDS_MOUNTOPT TESTRSRC_PATH TESTRSRC_TYPE TESTRSRC_NAME"
+TESTBOXSCRIPT_CFG_NAMES="${TESTBOXSCRIPT_CFG_NAMES} TESTRSRC_SHARE TESTRSRC_USER TESTRSRC_PASSWD TESTRSRC_MOUNTOPT SPB"
+
+# testboxscript.py option to config mappings.
+TESTBOXSCRIPT_OPT_DIR="--skip"
+TESTBOXSCRIPT_OPT_PYTHON="--skip"
+TESTBOXSCRIPT_OPT_USER="--skip"
+TESTBOXSCRIPT_OPT_HWVIRT_YES="--hwvirt"
+TESTBOXSCRIPT_OPT_HWVIRT_NO="--no-hwvirt"
+TESTBOXSCRIPT_OPT_NESTED_PAGING_YES="--nested-paging"
+TESTBOXSCRIPT_OPT_NESTED_PAGING_NO="--no-nested-paging"
+TESTBOXSCRIPT_OPT_IOMMU_YES="--io-mmu"
+TESTBOXSCRIPT_OPT_IOMMU_NO="--no-io-mmu"
+TESTBOXSCRIPT_OPT_SPB="--spb"
+TESTBOXSCRIPT_OPT_SYSTEM_UUID="--system-uuid"
+TESTBOXSCRIPT_OPT_TEST_MANAGER="--test-manager"
+TESTBOXSCRIPT_OPT_SCRATCH_ROOT="--scratch-root"
+TESTBOXSCRIPT_OPT_BUILDS_PATH="--builds-path"
+TESTBOXSCRIPT_OPT_BUILDS_TYPE="--builds-server-type"
+TESTBOXSCRIPT_OPT_BUILDS_NAME="--builds-server-name"
+TESTBOXSCRIPT_OPT_BUILDS_SHARE="--builds-server-share"
+TESTBOXSCRIPT_OPT_BUILDS_USER="--builds-server-user"
+TESTBOXSCRIPT_OPT_BUILDS_PASSWD="--builds-server-passwd"
+TESTBOXSCRIPT_OPT_BUILDS_MOUNTOPT="--builds-server-mountopt"
+TESTBOXSCRIPT_OPT_PATH_TESTRSRC="--testrsrc-path"
+TESTBOXSCRIPT_OPT_TESTRSRC_TYPE="--testrsrc-server-type"
+TESTBOXSCRIPT_OPT_TESTRSRC_NAME="--testrsrc-server-name"
+TESTBOXSCRIPT_OPT_TESTRSRC_SHARE="--testrsrc-server-share"
+TESTBOXSCRIPT_OPT_TESTRSRC_USER="--testrsrc-server-user"
+TESTBOXSCRIPT_OPT_TESTRSRC_PASSWD="--testrsrc-server-passwd"
+TESTBOXSCRIPT_OPT_TESTRSRC_MOUNTOPT="--testrsrc-server-mountopt"
+
+# Defaults:
+TESTBOXSCRIPT_DEFAULT_DIR="there-is-no-default-for-this-value"
+TESTBOXSCRIPT_DEFAULT_PYTHON=""
+TESTBOXSCRIPT_DEFAULT_USER="vbox"
+TESTBOXSCRIPT_DEFAULT_HWVIRT=""
+TESTBOXSCRIPT_DEFAULT_IOMMU=""
+TESTBOXSCRIPT_DEFAULT_NESTED_PAGING=""
+TESTBOXSCRIPT_DEFAULT_SPB=""
+TESTBOXSCRIPT_DEFAULT_SYSTEM_UUID=""
+TESTBOXSCRIPT_DEFAULT_PATH_TESTRSRC=""
+TESTBOXSCRIPT_DEFAULT_TEST_MANAGER=""
+TESTBOXSCRIPT_DEFAULT_SCRATCH_ROOT=""
+TESTBOXSCRIPT_DEFAULT_BUILDS_PATH=""
+TESTBOXSCRIPT_DEFAULT_BUILDS_TYPE="cifs"
+TESTBOXSCRIPT_DEFAULT_BUILDS_NAME="vboxstor.de.oracle.com"
+TESTBOXSCRIPT_DEFAULT_BUILDS_SHARE="builds"
+TESTBOXSCRIPT_DEFAULT_BUILDS_USER="guestr"
+TESTBOXSCRIPT_DEFAULT_BUILDS_PASSWD="guestr"
+TESTBOXSCRIPT_DEFAULT_BUILDS_MOUNTOPT=""
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_PATH=""
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_TYPE="cifs"
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_NAME="teststor.de.oracle.com"
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_SHARE="testrsrc"
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_USER="guestr"
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_PASSWD="guestr"
+TESTBOXSCRIPT_DEFAULT_TESTRSRC_MOUNTOPT=""
+
+# Set config values to defaults.
+for var in ${TESTBOXSCRIPT_CFG_NAMES}
+do
+ defvar=TESTBOXSCRIPT_DEFAULT_${var}
+ eval TESTBOXSCRIPT_${var}="${!defvar}"
+done
+declare -a TESTBOXSCRIPT_ENVVARS
+
+# Load old config values (platform specific).
+os_load_config
+
+
+#
+# Config tweaks.
+#
+
+# The USER must be a non-empty value for the successful execution of this script.
+if [ -z "${TESTBOXSCRIPT_USER}" ]; then
+ TESTBOXSCRIPT_USER=${TESTBOXSCRIPT_DEFAULT_USER};
+fi;
+
+# The DIR must be according to the setup.sh location.
+TESTBOXSCRIPT_DIR=`dirname "${DIR}"`
+
+# Storage server replacement trick.
+if [ "${TESTBOXSCRIPT_BUILDS_NAME}" = "solserv.de.oracle.com" ]; then
+ TESTBOXSCRIPT_BUILDS_NAME=${TESTBOXSCRIPT_DEFAULT_BUILDS_NAME}
+fi
+if [ "${TESTBOXSCRIPT_TESTRSRC_NAME}" = "solserv.de.oracle.com" ]; then
+ TESTBOXSCRIPT_TESTRSRC_NAME=${TESTBOXSCRIPT_DEFAULT_TESTRSRC_NAME}
+fi
+
+
+#
+# Parse arguments.
+#
+while test $# -gt 0;
+do
+ case "$1" in
+ -h|--help)
+ echo "TestBox Script setup utility."
+ echo "";
+ echo "Usage: setup.sh [options]";
+ echo "";
+ echo "Options:";
+ echo " Later...";
+ exit 0;
+ ;;
+ -V|--version)
+ echo '$Revision: 155244 $'
+ exit 0;
+ ;;
+
+ --python) TESTBOXSCRIPT_PYTHON="$2"; shift;;
+ --test-manager) TESTBOXSCRIPT_TEST_MANAGER="$2"; shift;;
+ --scratch-root) TESTBOXSCRIPT_SCRATCH_ROOT="$2"; shift;;
+ --system-uuid) TESTBOXSCRIPT_SYSTEM_UUID="$2"; shift;;
+ --hwvirt) TESTBOXSCRIPT_HWVIRT="yes";;
+ --no-hwvirt) TESTBOXSCRIPT_HWVIRT="no";;
+ --nested-paging) TESTBOXSCRIPT_NESTED_PAGING="yes";;
+ --no-nested-paging) TESTBOXSCRIPT_NESTED_PAGING="no";;
+ --io-mmu) TESTBOXSCRIPT_IOMMU="yes";;
+ --no-io-mmu) TESTBOXSCRIPT_IOMMU="no";;
+ --builds-path) TESTBOXSCRIPT_BUILDS_PATH="$2"; shift;;
+ --builds-server-type) TESTBOXSCRIPT_BUILDS_TYPE="$2"; shift;;
+ --builds-server-name) TESTBOXSCRIPT_BUILDS_NAME="$2"; shift;;
+ --builds-server-share) TESTBOXSCRIPT_BUILDS_SHARE="$2"; shift;;
+ --builds-server-user) TESTBOXSCRIPT_BUILDS_USER="$2"; shift;;
+ --builds-server-passwd) TESTBOXSCRIPT_BUILDS_PASSWD="$2"; shift;;
+ --builds-server-mountopt) TESTBOXSCRIPT_BUILDS_MOUNTOPT="$2"; shift;;
+ --testrsrc-path) TESTBOXSCRIPT_TESTRSRC_PATH="$2"; shift;;
+ --testrsrc-server-type) TESTBOXSCRIPT_TESTRSRC_TYPE="$2"; shift;;
+ --testrsrc-server-name) TESTBOXSCRIPT_TESTRSRC_NAME="$2"; shift;;
+ --testrsrc-server-share) TESTBOXSCRIPT_TESTRSRC_SHARE="$2"; shift;;
+ --testrsrc-server-user) TESTBOXSCRIPT_TESTRSRC_USER="$2"; shift;;
+ --testrsrc-server-passwd) TESTBOXSCRIPT_TESTRSRC_PASSWD="$2"; shift;;
+ --testrsrc-server-mountopt) TESTBOXSCRIPT_TESTRSRC_MOUNTOPT="$2"; shift;;
+ --spb) TESTBOXSCRIPT_SPB="yes";;
+ *)
+ echo 'Syntax error: Unknown option:' "$1" >&2;
+ exit 1;
+ ;;
+ esac
+ shift;
+done
+
+
+#
+# Find usable python if not already specified.
+#
+if [ -z "${TESTBOXSCRIPT_PYTHON}" ]; then
+ set +e
+ MY_PYTHON_VER_TEST="\
+import sys;\
+x = sys.version_info[0] == 3 or (sys.version_info[0] == 2 and (sys.version_info[1] >= 6 or (sys.version_info[1] == 5 and sys.version_info[2] >= 1)));\
+sys.exit(not x);\
+";
+ for python in python2.7 python2.6 python2.5 python;
+ do
+ python=`which ${python} 2> /dev/null`
+ if [ -n "${python}" -a -x "${python}" ]; then
+ if ${python} -c "${MY_PYTHON_VER_TEST}"; then
+ TESTBOXSCRIPT_PYTHON="${python}";
+ break;
+ fi
+ fi
+ done
+ set -e
+ test -n "${TESTBOXSCRIPT_PYTHON}";
+fi
+
+
+#
+# Do the job
+#
+set -e
+check_testboxscript_install;
+check_for_sudo;
+check_for_cifs;
+check_proxy_config;
+
+maybe_add_testboxscript_user;
+test_user;
+test_coredumps;
+test_unattended_updates_disabled;
+
+grant_user_testboxscript_write_access;
+
+os_disable_service;
+os_install_service;
+os_enable_service;
+
+#
+# That's all folks.
+#
+echo "done"
+os_final_message;
diff --git a/src/VBox/ValidationKit/testboxscript/solaris/setup-routines.sh b/src/VBox/ValidationKit/testboxscript/solaris/setup-routines.sh
new file mode 100644
index 00000000..1560f687
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/solaris/setup-routines.sh
@@ -0,0 +1,360 @@
+# $Id: setup-routines.sh $
+## @file
+# VirtualBox Validation Kit - TestBoxScript Service Setup on Solaris.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+#
+# Detect solaris version.
+#
+MY_SOLARIS_VER=`uname -r`
+case "${MY_SOLARIS_VER}" in
+ 5.10) MY_SOLARIS_VER=10;;
+ 5.11) MY_SOLARIS_VER=11;;
+ 5.12) MY_SOLARIS_VER=12;;
+ *)
+ echo "Your solaris version (${MY_SOLARIS_VER}) is not supported." >&2
+ exit 1;;
+esac
+
+#
+# Overriding setup.sh bits.
+#
+MY_FGREP="/usr/xpg4/bin/fgrep" # The other one does grok -q.
+if [ ! -f "${MY_ETC_SUDOERS}" ]; then # sudo isn't standard on S10.
+ if [ -f "/opt/csw/etc/sudoers" ]; then
+ MY_ETC_SUDOERS=/opt/csw/etc/sudoers
+ fi
+ if [ -f "/etc/opt/csw/sudoers" ]; then
+ MY_ETC_SUDOERS=/etc/opt/csw/sudoers
+ fi
+fi
+
+#
+# Solaris variables.
+#
+MY_SVC_FMRI="svc:/system/virtualbox/testboxscript"
+MY_SVCCFG="/usr/sbin/svccfg"
+MY_SVCADM="/usr/sbin/svcadm"
+MY_CHGRP="/usr/bin/chgrp"
+MY_TR="/usr/bin/tr"
+MY_TAB=`printf "\t"`
+
+if test "${MY_SOLARIS_VER}" -lt 11; then
+ # solaris 10 service import
+ MY_SVC="/tmp/testboxscript.xml"
+else
+ # use propper manifest directory
+ # /lib/svc/manifest/system for solaris 11 and higher for testboxscript.xml file
+
+ # Since sol 11.4 the solaris testboxscript service
+ # generates Warnings in /var/svc/log/system-manifest-import:default.log
+ # -------- Warning!!
+ # Configuring services...
+ # * Warning!! Importing Zone access service ...FAILED.
+
+ MY_SVC="/lib/svc/manifest/system/testboxscript.xml"
+fi
+if test "${MY_SOLARIS_VER}" -lt 11; then ## No gsed on S10?? ARG!
+ MY_SED="/usr/xpg4/bin/sed"
+else
+ MY_SED="/usr/bin/gsed"
+fi
+if test "${MY_SOLARIS_VER}" -lt 11; then
+ MY_SCREEN="/opt/csw/bin/screen"
+else
+ MY_SCREEN="screen"
+fi
+
+
+check_for_cifs() {
+ if [ ! -f /usr/kernel/fs/amd64/smbfs -a ! -f /usr/kernel/fs/smbfs -a "${MY_SOLARIS_VER}" -ge 11 ]; then
+ echo "error: smbfs client not installed?" >&2
+ echo "Please install smbfs client support:" >&2
+ echo " pkg install system/file-system/smb" >&2
+ echo " svcadm enable svc:/system/idmap" >&2
+ echo " svcadm enable svc:/network/smb/client" >&2
+ echo " svcs svc:/system/idmap" >&2
+ return 1;
+ fi
+ return 0;
+}
+
+##
+# Loads config values from the current installation.
+#
+os_load_config() {
+ #
+ # Adjust defaults.
+ #
+ # - Use NFS instead of CIFS because S10 doesn't have smbfs and S11 has
+ # problems getting the password.
+ # - Pass the PATH along so we'll find sudo and other stuff later.
+ #
+ TESTBOXSCRIPT_BUILDS_TYPE="nfs"
+ TESTBOXSCRIPT_TESTRSRC_TYPE="nfs"
+ TESTBOXSCRIPT_DEFAULT_BUILDS_TYPE="nfs"
+ TESTBOXSCRIPT_DEFAULT_TESTRSRC_TYPE="nfs"
+ TESTBOXSCRIPT_ENVVARS[${#TESTBOXSCRIPT_ENVVARS[@]}]="PATH=${PATH}";
+
+ # Load old current.
+ if "${MY_SVCCFG}" "export" "${MY_SVC_FMRI}" > /dev/null 2>&1; then
+ # User. ASSUMES single quoted attribs.
+ MY_TMP=`"${MY_SVCCFG}" "export" "${MY_SVC_FMRI}" \
+ | ${MY_TR} '\n' ' ' \
+ `;
+ MY_TMP=`echo "${MY_TMP} " \
+ | ${MY_SED} \
+ -e 's/> */> /g' \
+ -e 's/ *\/>/ \/>/g' \
+ -e 's/^.*<method_credential \([^>]*\) \/>.*$/\1/' \
+ -e "s/^.*user='\([^']*\)'.*\$/\1/" \
+ `;
+ if [ -n "${MY_TMP}" ]; then
+ TESTBOXSCRIPT_USER="${MY_TMP}";
+ fi
+
+ # Arguments. ASSUMES sub-elements. ASSUMES single quoted attribs.
+ XMLARGS=`"${MY_SVCCFG}" "export" "${MY_SVC_FMRI}" \
+ | ${MY_TR} '\n' ' ' \
+ `;
+ case "${XMLARGS}" in
+ *exec_method*)
+ XMLARGS=`echo "${XMLARGS} " \
+ | ${MY_SED} \
+ -e 's/> */> /g' \
+ -e 's/ *\/>/ \/>/g' \
+ -e "s/^.*<exec_method \([^>]*\)name='start'\([^>]*\)>.*\$/\1 \2/" \
+ -e "s/^.*exec='\([^']*\)'.*\$/\1/" \
+ -e 's/&quot;/"/g' \
+ -e 's/&lt;/</g' \
+ -e 's/&gt;/>/g' \
+ -e 's/&amp;/&/g' \
+ | ${MY_SED} \
+ -e 's/^.*testboxscript -d -m *//' \
+ `;
+ eval common_testboxscript_args_to_config ${XMLARGS}
+ ;;
+ *)
+ echo "error: ${MY_SVCCFG}" "export" "${MY_SVC_FMRI} contains no exec_method element." >&2
+ echo " Please delete the service manually and restart setup.sh" >&2
+ exit 2
+ ;;
+ esac
+ fi
+}
+
+##
+# Adds one or more arguments to MY_ARGV after checking them for conformity.
+#
+os_add_args() {
+ while [ $# -gt 0 ];
+ do
+ case "$1" in
+ *\ *)
+ echo "error: Space in option value is not allowed ($1)" >&2
+ exit 1;
+ ;;
+ *${MY_TAB}*)
+ echo "error: Tab in option value is not allowed ($1)" >&2
+ exit 1;
+ ;;
+ *\&*)
+ echo "error: Ampersand in option value is not allowed ($1)" >&2
+ exit 1;
+ ;;
+ *\<*)
+ echo "error: Greater-than in option value is not allowed ($1)" >&2
+ exit 1;
+ ;;
+ *\>*)
+ echo "error: Less-than in option value is not allowed ($1)" >&2
+ exit 1;
+ ;;
+ *)
+ MY_ARGV="${MY_ARGV} $1";
+ ;;
+ esac
+ shift;
+ done
+ return 0;
+}
+
+##
+# Installs, configures and starts the service.
+#
+os_install_service() {
+ # Only NFS for S10.
+ if [ "${MY_SOLARIS_VER}" -lt 11 ]; then
+ if [ "${TESTBOXSCRIPT_BUILDS_TYPE}" != "nfs" -o "${TESTBOXSCRIPT_TESTRSRC_TYPE}" != "nfs" ]; then
+ echo "On solaris 10 both share types must be 'nfs', cifs (smbfs) is not supported." >&2
+ return 1;
+ fi
+ fi
+
+ # Calc the command line.
+ MY_ARGV=""
+ common_compile_testboxscript_command_line
+
+ # Create the service xml config file.
+ cat > "${MY_SVC}" <<EOF
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<service_bundle type='manifest' name='export'>
+ <service name='system/virtualbox/testboxscript' type='service' version='1'>
+ <create_default_instance enabled='false' />
+ <single_instance/>
+
+ <!-- Wait for the network to start up -->
+ <dependency name='milestone-network' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/network:default' />
+ </dependency>
+
+ <!-- We wish to be started as late as possible... so go crazy with deps. -->
+ <dependency name='milestone-devices' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+ <dependency name='multi-user' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user:default' />
+ </dependency>
+ <dependency name='multi-user-server' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user-server:default' />
+ </dependency>
+ <dependency name='filesystem-local' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+ <dependency name='filesystem-autofs' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/autofs:default' />
+ </dependency>
+EOF
+ if [ "`uname -r`" = "5.10" ]; then # Seems to be gone in S11?
+ cat >> "${MY_SVC}" <<EOF
+ <dependency name='filesystem-volfs' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/volfs:default' />
+ </dependency>
+EOF
+ fi
+ cat >> "${MY_SVC}" <<EOF
+ <!-- start + stop methods -->
+ <exec_method type='method' name='start' exec='${MY_SCREEN} -S testboxscript -d -m ${MY_ARGV}'
+ timeout_seconds='30'>
+ <method_context working_directory='${TESTBOXSCRIPT_DIR}'>
+ <method_credential user='${TESTBOXSCRIPT_USER}' />
+ <method_environment>
+ <envvar name='PATH' value='${PATH}' />
+ </method_environment>
+ </method_context>
+ </exec_method>
+
+ <exec_method type='method' name='stop' exec=':kill' timeout_seconds='60' />
+
+ <property_group name='startd' type='framework'>
+ <!-- sub-process core dumps/signals should not restart session -->
+ <propval name='ignore_error' type='astring' value='core,signal' />
+ </property_group>
+
+ <!-- Description -->
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox TestBox Script</loctext>
+ </common_name>
+ </template>
+ </service>
+</service_bundle>
+EOF
+
+ if test "${MY_SOLARIS_VER}" -lt 11; then
+ # Install the service, replacing old stuff.
+ if "${MY_SVCCFG}" "export" "${MY_SVC_FMRI}" > /dev/null 2>&1; then
+ "${MY_SVCCFG}" "delete" "${MY_SVC_FMRI}"
+ fi
+ "${MY_SVCCFG}" "import" "${MY_SVC}"
+
+ # only for solaris version less than 11
+ rm -f "${MY_SVC}"
+ else
+ "${MY_CHGRP}" "sys" "${MY_SVC}"
+ "${MY_SVCADM}" "restart" "manifest-import"
+
+ # Do not remove the xml file in Solaris versions 11 and higher.
+ # The service will be removed automatically, if the command
+ # svcadm restart manifest-import
+ # will be executed
+
+ fi
+ return 0;
+}
+
+os_enable_service() {
+ "${MY_SVCADM}" "enable" "${MY_SVC_FMRI}"
+ return 0;
+}
+
+os_disable_service() {
+ if "${MY_SVCCFG}" "export" "${MY_SVC_FMRI}" > /dev/null 2>&1; then
+ "${MY_SVCADM}" "disable" "${MY_SVC_FMRI}"
+ sleep 1
+ fi
+ return 0;
+}
+
+os_add_user() {
+ useradd -m -s /usr/bin/bash -G staff "${TESTBOXSCRIPT_USER}"
+ passwd "${TESTBOXSCRIPT_USER}" # This sucker prompts, seemingly no way around that.
+ return 0;
+}
+
+
+maybe_hush_up_root_login() {
+ # We don't want /etc/profile to display /etc/motd, quotas and mail status
+ # every time we do sudo -i... It may screw up serious if we parse the
+ # output of the command we sudid.
+ > ~root/.hushlogin
+ return 0;
+}
+
+os_final_message() {
+ cat <<EOF
+
+Additional things to do:"
+ 1. Configure NTP:
+ a) echo "server wei01-time.de.oracle.com" > /etc/inet/ntp.conf
+ echo "driftfile /var/ntp/ntp.drift" >> /etc/inet/ntp.conf
+ b) Enable the service: svcadm enable ntp
+ c) Sync once in case of big diff: ntpdate wei01-time.de.oracle.com
+ d) Check that it works: ntpq -p
+
+Enjoy!
+EOF
+}
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxcommand.py b/src/VBox/ValidationKit/testboxscript/testboxcommand.py
new file mode 100755
index 00000000..8554ef8e
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxcommand.py
@@ -0,0 +1,362 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxcommand.py $
+
+"""
+TestBox Script - Command Processor.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import os;
+import sys;
+import threading;
+
+# Validation Kit imports.
+from common import constants;
+from common import utils, webutils;
+import testboxcommons;
+from testboxcommons import TestBoxException;
+from testboxscript import TBS_EXITCODE_NEED_UPGRADE;
+from testboxupgrade import upgradeFromZip;
+from testboxtasks import TestBoxExecTask, TestBoxCleanupTask, TestBoxTestDriverTask;
+
+# Figure where we are.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksTestScriptDir = os.path.dirname(os.path.abspath(__file__));
+
+
+
+class TestBoxCommand(object):
+ """
+ Implementation of Test Box command.
+ """
+
+ ## The time to wait on the current task to abort.
+ kcSecStopTimeout = 360
+ ## The time to wait on the current task to abort before rebooting.
+ kcSecStopBeforeRebootTimeout = 360
+
+ def __init__(self, oTestBoxScript):
+ """
+ Class instance init
+ """
+ self._oTestBoxScript = oTestBoxScript;
+ self._oCurTaskLock = threading.RLock();
+ self._oCurTask = None;
+
+ # List of available commands and their handlers
+ self._dfnCommands = \
+ {
+ constants.tbresp.CMD_IDLE: self._cmdIdle,
+ constants.tbresp.CMD_WAIT: self._cmdWait,
+ constants.tbresp.CMD_EXEC: self._cmdExec,
+ constants.tbresp.CMD_ABORT: self._cmdAbort,
+ constants.tbresp.CMD_REBOOT: self._cmdReboot,
+ constants.tbresp.CMD_UPGRADE: self._cmdUpgrade,
+ constants.tbresp.CMD_UPGRADE_AND_REBOOT: self._cmdUpgradeAndReboot,
+ constants.tbresp.CMD_SPECIAL: self._cmdSpecial,
+ }
+
+ def _cmdIdle(self, oResponse, oConnection):
+ """
+ Idle response, no ACK.
+ """
+ oResponse.checkParameterCount(1);
+
+ # The dispatch loop will delay for us, so nothing to do here.
+ _ = oConnection; # Leave the connection open.
+ return True;
+
+ def _cmdWait(self, oResponse, oConnection):
+ """
+ Gang scheduling wait response, no ACK.
+ """
+ oResponse.checkParameterCount(1);
+
+ # The dispatch loop will delay for us, so nothing to do here.
+ _ = oConnection; # Leave the connection open.
+ return True;
+
+ def _cmdExec(self, oResponse, oConnection):
+ """
+ Execute incoming command
+ """
+
+ # Check if required parameters given and make a little sense.
+ idResult = oResponse.getIntChecked( constants.tbresp.EXEC_PARAM_RESULT_ID, 1);
+ sScriptZips = oResponse.getStringChecked(constants.tbresp.EXEC_PARAM_SCRIPT_ZIPS);
+ sScriptCmdLine = oResponse.getStringChecked(constants.tbresp.EXEC_PARAM_SCRIPT_CMD_LINE);
+ cSecTimeout = oResponse.getIntChecked( constants.tbresp.EXEC_PARAM_TIMEOUT, 30);
+ oResponse.checkParameterCount(5);
+
+ sScriptFile = utils.argsGetFirst(sScriptCmdLine);
+ if sScriptFile is None:
+ raise TestBoxException('Bad script command line: "%s"' % (sScriptCmdLine,));
+ if len(os.path.basename(sScriptFile)) < len('t.py'):
+ raise TestBoxException('Script file name too short: "%s"' % (sScriptFile,));
+ if len(sScriptZips) < len('x.zip'):
+ raise TestBoxException('Script zip name too short: "%s"' % (sScriptFile,));
+
+ # One task at the time.
+ if self.isRunning():
+ raise TestBoxException('Already running other command');
+
+ # Don't bother running the task without the shares mounted.
+ self._oTestBoxScript.mountShares(); # Raises exception on failure.
+
+ # Kick off the task and ACK the command.
+ with self._oCurTaskLock:
+ self._oCurTask = TestBoxExecTask(self._oTestBoxScript, idResult = idResult, sScriptZips = sScriptZips,
+ sScriptCmdLine = sScriptCmdLine, cSecTimeout = cSecTimeout);
+ oConnection.sendAckAndClose(constants.tbresp.CMD_EXEC);
+ return True;
+
+ def _cmdAbort(self, oResponse, oConnection):
+ """
+ Abort background task
+ """
+ oResponse.checkParameterCount(1);
+ oConnection.sendAck(constants.tbresp.CMD_ABORT);
+
+ oCurTask = self._getCurTask();
+ if oCurTask is not None:
+ oCurTask.terminate();
+ oCurTask.flushLogOnConnection(oConnection);
+ oConnection.close();
+ oCurTask.wait(self.kcSecStopTimeout);
+
+ return True;
+
+ def doReboot(self):
+ """
+ Worker common to _cmdReboot and _doUpgrade that performs a system reboot.
+ """
+ # !! Not more exceptions beyond this point !!
+ testboxcommons.log('Rebooting');
+
+ # Stop anything that might be executing at this point.
+ oCurTask = self._getCurTask();
+ if oCurTask is not None:
+ oCurTask.terminate();
+ oCurTask.wait(self.kcSecStopBeforeRebootTimeout);
+
+ # Invoke shutdown command line utility.
+ sOs = utils.getHostOs();
+ asCmd2 = None;
+ if sOs == 'win':
+ asCmd = ['shutdown', '/r', '/t', '0', '/c', '"ValidationKit triggered reboot"', '/d', '4:1'];
+ elif sOs == 'os2':
+ asCmd = ['setboot', '/B'];
+ elif sOs in ('solaris',):
+ asCmd = ['/usr/sbin/reboot', '-p'];
+ asCmd2 = ['/usr/sbin/reboot']; # Hack! S10 doesn't have -p, but don't know how to reliably detect S10.
+ else:
+ asCmd = ['/sbin/shutdown', '-r', 'now'];
+ try:
+ utils.sudoProcessOutputChecked(asCmd);
+ except Exception as oXcpt:
+ if asCmd2 is not None:
+ try:
+ utils.sudoProcessOutputChecked(asCmd2);
+ except Exception as oXcpt:
+ testboxcommons.log('Error executing reboot command "%s" as well as "%s": %s' % (asCmd, asCmd2, oXcpt));
+ return False;
+ testboxcommons.log('Error executing reboot command "%s": %s' % (asCmd, oXcpt));
+ return False;
+
+ # Quit the script.
+ while True:
+ sys.exit(32);
+ return True;
+
+ def _cmdReboot(self, oResponse, oConnection):
+ """
+ Reboot Test Box
+ """
+ oResponse.checkParameterCount(1);
+ oConnection.sendAckAndClose(constants.tbresp.CMD_REBOOT);
+ return self.doReboot();
+
+ def _doUpgrade(self, oResponse, oConnection, fReboot):
+ """
+ Common worker for _cmdUpgrade and _cmdUpgradeAndReboot.
+ Will sys.exit on success!
+ """
+
+ #
+ # The server specifies a ZIP archive with the new scripts. It's ASSUMED
+ # that the zip is of selected files at g_ksValidationKitDir in SVN. It's
+ # further ASSUMED that we're executing from
+ #
+ sZipUrl = oResponse.getStringChecked(constants.tbresp.UPGRADE_PARAM_URL)
+ oResponse.checkParameterCount(2);
+
+ if utils.isRunningFromCheckout():
+ raise TestBoxException('Cannot upgrade when running from the tree!');
+ oConnection.sendAckAndClose(constants.tbresp.CMD_UPGRADE_AND_REBOOT if fReboot else constants.tbresp.CMD_UPGRADE);
+
+ testboxcommons.log('Upgrading...');
+
+ #
+ # Download the file and install it.
+ #
+ sDstFile = os.path.join(g_ksTestScriptDir, 'VBoxTestBoxScript.zip');
+ if os.path.exists(sDstFile):
+ os.unlink(sDstFile);
+ fRc = webutils.downloadFile(sZipUrl, sDstFile, self._oTestBoxScript.getPathBuilds(), testboxcommons.log);
+ if fRc is not True:
+ return False;
+
+ if upgradeFromZip(sDstFile) is not True:
+ return False;
+
+ #
+ # Restart the system or the script (we have a parent script which
+ # respawns us when we quit).
+ #
+ if fReboot:
+ self.doReboot();
+ sys.exit(TBS_EXITCODE_NEED_UPGRADE);
+ return False; # shuts up pylint (it will probably complain later when it learns DECL_NO_RETURN).
+
+ def _cmdUpgrade(self, oResponse, oConnection):
+ """
+ Upgrade Test Box Script
+ """
+ return self._doUpgrade(oResponse, oConnection, False);
+
+ def _cmdUpgradeAndReboot(self, oResponse, oConnection):
+ """
+ Upgrade Test Box Script
+ """
+ return self._doUpgrade(oResponse, oConnection, True);
+
+ def _cmdSpecial(self, oResponse, oConnection):
+ """
+ Reserved for future fun.
+ """
+ oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NOTSUP, constants.tbresp.CMD_SPECIAL);
+ testboxcommons.log('Special command %s not supported...' % (oResponse,));
+ return False;
+
+
+ def handleCommand(self, oResponse, oConnection):
+ """
+ Handles a command from the test manager.
+
+ Some commands will close the connection, others (generally the simple
+ ones) wont, leaving the caller the option to use it for log flushing.
+
+ Returns success indicator.
+ Raises no exception.
+ """
+ try:
+ sCmdName = oResponse.getStringChecked(constants.tbresp.ALL_PARAM_RESULT);
+ except:
+ oConnection.close();
+ return False;
+
+ # Do we know the command?
+ fRc = False;
+ if sCmdName in self._dfnCommands:
+ testboxcommons.log(sCmdName);
+ try:
+ # Execute the handler.
+ fRc = self._dfnCommands[sCmdName](oResponse, oConnection)
+ except Exception as oXcpt:
+ # NACK the command if an exception is raised during parameter validation.
+ testboxcommons.log1Xcpt('Exception executing "%s": %s' % (sCmdName, oXcpt));
+ if oConnection.isConnected():
+ try:
+ oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NACK, sCmdName);
+ except Exception as oXcpt2:
+ testboxcommons.log('Failed to NACK "%s": %s' % (sCmdName, oXcpt2));
+ elif sCmdName in [constants.tbresp.STATUS_DEAD, constants.tbresp.STATUS_NACK]:
+ testboxcommons.log('Received status instead of command: %s' % (sCmdName, ));
+ else:
+ # NOTSUP the unknown command.
+ testboxcommons.log('Received unknown command: %s' % (sCmdName, ));
+ try:
+ oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NOTSUP, sCmdName);
+ except Exception as oXcpt:
+ testboxcommons.log('Failed to NOTSUP "%s": %s' % (sCmdName, oXcpt));
+ return fRc;
+
+ def resumeIncompleteCommand(self):
+ """
+ Resumes an incomplete command at startup.
+
+ The EXEC commands saves essential state information in the scratch area
+ so we can resume them in case the testbox panics or is rebooted.
+ Current "resume" means doing cleanups, but we may need to implement
+ test scenarios involving rebooting the testbox later.
+
+ Returns (idTestBox, sTestBoxName, True) if a command was resumed,
+ otherwise (-1, '', False). Raises no exceptions.
+ """
+
+ try:
+ oTask = TestBoxCleanupTask(self._oTestBoxScript);
+ except:
+ return (-1, '', False);
+
+ with self._oCurTaskLock:
+ self._oCurTask = oTask;
+
+ return (oTask.idTestBox, oTask.sTestBoxName, True);
+
+ def isRunning(self):
+ """
+ Check if we're running a task or not.
+ """
+ oCurTask = self._getCurTask();
+ return oCurTask is not None and oCurTask.isRunning();
+
+ def flushLogOnConnection(self, oGivenConnection):
+ """
+ Flushes the log of any running task with a log buffer.
+ """
+ oCurTask = self._getCurTask();
+ if oCurTask is not None and isinstance(oCurTask, TestBoxTestDriverTask):
+ return oCurTask.flushLogOnConnection(oGivenConnection);
+ return None;
+
+ def _getCurTask(self):
+ """ Gets the current task in a paranoidly safe manny. """
+ with self._oCurTaskLock:
+ oCurTask = self._oCurTask;
+ return oCurTask;
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxcommons.py b/src/VBox/ValidationKit/testboxscript/testboxcommons.py
new file mode 100755
index 00000000..18a1c40a
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxcommons.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxcommons.py $
+
+"""
+TestBox Script - Common Functions and Classes.
+
+This module contains constants and functions that are useful for all
+the files in this (testbox) directory.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import sys
+import traceback
+
+# Validation Kit imports.
+from common import utils;
+
+#
+# Exceptions.
+#
+
+class TestBoxException(Exception):
+ """
+ Custom exception class
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+#
+# Logging.
+#
+
+def log(sMessage, sCaller = None, sTsPrf = None):
+ """
+ Print out a message and flush stdout
+ """
+ if sTsPrf is None: sTsPrf = utils.getTimePrefix();
+ print('[%s] %s' % (sTsPrf, sMessage,));
+ sys.stdout.flush();
+ _ = sCaller;
+
+def log2(sMessage, sCaller = None, sTsPrf = None):
+ """
+ Debug logging, will later be disabled by default.
+ """
+ if True is True: # pylint: disable=comparison-with-itself
+ if sTsPrf is None: sTsPrf = utils.getTimePrefix();
+ print('[%s] %s' % (sTsPrf, sMessage,));
+ sys.stdout.flush()
+ _ = sCaller;
+
+def _logXcptWorker(fnLogger, sPrefix = '', sText = None, cFrames = 1, fnLogger1 = log):
+ """
+ Log an exception, optionally with a preceeding message and more than one
+ call frame.
+ """
+ ## @todo skip all this if iLevel is too high!
+
+ # Try get exception info.
+ sTsPrf = utils.getTimePrefix();
+ try:
+ oType, oValue, oTraceback = sys.exc_info();
+ except:
+ oType = oValue = oTraceback = None;
+ if oType is not None:
+
+ # Try format the info
+ try:
+ rc = 0;
+ sCaller = utils.getCallerName(oTraceback.tb_frame);
+ if sText is not None:
+ rc = fnLogger('%s%s' % (sPrefix, sText), sCaller, sTsPrf);
+ asInfo = [];
+ try:
+ asInfo = asInfo + traceback.format_exception_only(oType, oValue);
+ if cFrames is not None and cFrames <= 1:
+ asInfo = asInfo + traceback.format_tb(oTraceback, 1);
+ else:
+ asInfo.append('Traceback:')
+ asInfo = asInfo + traceback.format_tb(oTraceback, cFrames);
+ asInfo.append('Stack:')
+ asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
+ except:
+ fnLogger1('internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
+
+ if asInfo:
+ # Do the logging.
+ for sItem in asInfo:
+ asLines = sItem.splitlines();
+ for sLine in asLines:
+ rc = fnLogger('%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
+
+ else:
+ fnLogger('No exception info...', sCaller, sTsPrf);
+ rc = -3;
+ except:
+ fnLogger1('internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
+ rc = -2;
+ else:
+ fnLogger1('internal-error: No exception! %s' % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
+ rc = -1;
+
+ return rc;
+
+
+def log1Xcpt(sText = None, cFrames = 1):
+ """Logs an exception."""
+ return _logXcptWorker(log, '', sText, cFrames);
+
+def log2Xcpt(sText = None, cFrames = 1):
+ """Debug logging of an exception."""
+ return _logXcptWorker(log2, '', sText, cFrames);
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxconnection.py b/src/VBox/ValidationKit/testboxscript/testboxconnection.py
new file mode 100755
index 00000000..ecbcbf1b
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxconnection.py
@@ -0,0 +1,312 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxconnection.py $
+
+"""
+TestBox Script - HTTP Connection Handling.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import sys;
+if sys.version_info[0] >= 3:
+ import http.client as httplib; # pylint: disable=import-error,no-name-in-module
+ import urllib.parse as urlparse; # pylint: disable=import-error,no-name-in-module
+ from urllib.parse import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
+else:
+ import httplib; # pylint: disable=import-error,no-name-in-module
+ import urlparse; # pylint: disable=import-error,no-name-in-module
+ from urllib import urlencode as urllib_urlencode; # pylint: disable=import-error,no-name-in-module
+
+# Validation Kit imports.
+from common import constants
+from common import utils
+import testboxcommons
+
+
+
+class TestBoxResponse(object):
+ """
+ Response object return by TestBoxConnection.request().
+ """
+ def __init__(self, oResponse):
+ """
+ Convert the HTTPResponse to a dictionary, raising TestBoxException on
+ malformed response.
+ """
+ if oResponse is not None:
+ # Read the whole response (so we can log it).
+ sBody = oResponse.read();
+ sBody = sBody.decode('utf-8');
+
+ # Check the content type.
+ sContentType = oResponse.getheader('Content-Type');
+ if sContentType is None or sContentType != 'application/x-www-form-urlencoded; charset=utf-8':
+ testboxcommons.log('SERVER RESPONSE: Content-Type: %s' % (sContentType,));
+ testboxcommons.log('SERVER RESPONSE: %s' % (sBody.rstrip(),))
+ raise testboxcommons.TestBoxException('Invalid server response type: "%s"' % (sContentType,));
+
+ # Parse the body (this should be the exact reverse of what
+ # TestBoxConnection.postRequestRaw).
+ ##testboxcommons.log2('SERVER RESPONSE: "%s"' % (sBody,))
+ self._dResponse = urlparse.parse_qs(sBody, strict_parsing=True);
+
+ # Convert the dictionary from 'field:values' to 'field:value'. Fail
+ # if a field has more than one value (i.e. given more than once).
+ for sField in self._dResponse:
+ if len(self._dResponse[sField]) != 1:
+ raise testboxcommons.TestBoxException('The field "%s" appears more than once in the server response' \
+ % (sField,));
+ self._dResponse[sField] = self._dResponse[sField][0]
+ else:
+ # Special case, dummy response object.
+ self._dResponse = {};
+ # Done.
+
+ def getStringChecked(self, sField):
+ """
+ Check if specified field is present in server response and returns it as string.
+ If not present, a fitting exception will be raised.
+ """
+ if not sField in self._dResponse:
+ raise testboxcommons.TestBoxException('Required data (' + str(sField) + ') was not found in server response');
+ return str(self._dResponse[sField]).strip();
+
+ def getIntChecked(self, sField, iMin = None, iMax = None):
+ """
+ Check if specified field is present in server response and returns it as integer.
+ If not present, a fitting exception will be raised.
+
+ The iMin and iMax values are inclusive.
+ """
+ if not sField in self._dResponse:
+ raise testboxcommons.TestBoxException('Required data (' + str(sField) + ') was not found in server response')
+ try:
+ iValue = int(self._dResponse[sField]);
+ except:
+ raise testboxcommons.TestBoxException('Malformed integer field %s: "%s"' % (sField, self._dResponse[sField]));
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise testboxcommons.TestBoxException('Value (%d) of field %s is out of range [%s..%s]' \
+ % (iValue, sField, iMin, iMax));
+ return iValue;
+
+ def checkParameterCount(self, cExpected):
+ """
+ Checks the parameter count, raise TestBoxException if it doesn't meet
+ the expectations.
+ """
+ if len(self._dResponse) != cExpected:
+ raise testboxcommons.TestBoxException('Expected %d parameters, server sent %d' % (cExpected, len(self._dResponse)));
+ return True;
+
+ def toString(self):
+ """
+ Convers the response to a string (for debugging purposes).
+ """
+ return str(self._dResponse);
+
+
+class TestBoxConnection(object):
+ """
+ Wrapper around HTTPConnection.
+ """
+
+ def __init__(self, sTestManagerUrl, sTestBoxId, sTestBoxUuid, fLongTimeout = False):
+ """
+ Constructor.
+ """
+ self._oConn = None;
+ self._oParsedUrl = urlparse.urlparse(sTestManagerUrl);
+ self._sTestBoxId = sTestBoxId;
+ self._sTestBoxUuid = sTestBoxUuid;
+
+ #
+ # Connect to it - may raise exception on failure.
+ # When connecting we're using a 15 second timeout, we increase it later.
+ #
+ if self._oParsedUrl.scheme == 'https': # pylint: disable=no-member
+ fnCtor = httplib.HTTPSConnection;
+ else:
+ fnCtor = httplib.HTTPConnection;
+ if sys.version_info[0] >= 3 \
+ or (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
+
+ self._oConn = fnCtor(self._oParsedUrl.hostname, timeout=15);
+ else:
+ self._oConn = fnCtor(self._oParsedUrl.hostname);
+
+ if self._oConn.sock is None:
+ self._oConn.connect();
+
+ #
+ # Increase the timeout for the non-connect operations.
+ #
+ try:
+ self._oConn.sock.settimeout(5*60 if fLongTimeout else 1 * 60);
+ except:
+ pass;
+
+ ##testboxcommons.log2('hostname=%s timeout=%u' % (self._oParsedUrl.hostname, self._oConn.sock.gettimeout()));
+
+ def __del__(self):
+ """ Makes sure the connection is really closed on destruction """
+ self.close()
+
+ def close(self):
+ """ Closes the connection """
+ if self._oConn is not None:
+ self._oConn.close();
+ self._oConn = None;
+
+ def postRequestRaw(self, sAction, dParams):
+ """
+ Posts a request to the test manager and gets the response. The dParams
+ argument is a dictionary of unencoded key-value pairs (will be
+ modified).
+ Raises exception on failure.
+ """
+ dHeader = \
+ {
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
+ 'User-Agent': 'TestBoxScript/%s.0 (%s, %s)' % (__version__, utils.getHostOs(), utils.getHostArch()),
+ 'Accept': 'text/plain,application/x-www-form-urlencoded',
+ 'Accept-Encoding': 'identity',
+ 'Cache-Control': 'max-age=0',
+ 'Connection': 'keep-alive',
+ };
+ sServerPath = '/%s/testboxdisp.py' % (self._oParsedUrl.path.strip('/'),); # pylint: disable=no-member
+ dParams[constants.tbreq.ALL_PARAM_ACTION] = sAction;
+ sBody = urllib_urlencode(dParams);
+ ##testboxcommons.log2('sServerPath=%s' % (sServerPath,));
+ try:
+ self._oConn.request('POST', sServerPath, sBody, dHeader);
+ oResponse = self._oConn.getresponse();
+ oResponse2 = TestBoxResponse(oResponse);
+ except:
+ testboxcommons.log2Xcpt();
+ raise
+ return oResponse2;
+
+ def postRequest(self, sAction, dParams = None):
+ """
+ Posts a request to the test manager, prepending the testbox ID and
+ UUID to the arguments, and gets the response. The dParams argument is a
+ is a dictionary of unencoded key-value pairs (will be modified).
+ Raises exception on failure.
+ """
+ if dParams is None:
+ dParams = {};
+ dParams[constants.tbreq.ALL_PARAM_TESTBOX_ID] = self._sTestBoxId;
+ dParams[constants.tbreq.ALL_PARAM_TESTBOX_UUID] = self._sTestBoxUuid;
+ return self.postRequestRaw(sAction, dParams);
+
+ def sendReply(self, sReplyAction, sCmdName):
+ """
+ Sends a reply to a test manager command.
+ Raises exception on failure.
+ """
+ return self.postRequest(sReplyAction, { constants.tbreq.COMMAND_ACK_PARAM_CMD_NAME: sCmdName });
+
+ def sendReplyAndClose(self, sReplyAction, sCmdName):
+ """
+ Sends a reply to a test manager command and closes the connection.
+ Raises exception on failure.
+ """
+ self.sendReply(sReplyAction, sCmdName);
+ self.close();
+ return True;
+
+ def sendAckAndClose(self, sCmdName):
+ """
+ Acks a command and closes the connection to the test manager.
+ Raises exception on failure.
+ """
+ return self.sendReplyAndClose(constants.tbreq.COMMAND_ACK, sCmdName);
+
+ def sendAck(self, sCmdName):
+ """
+ Acks a command.
+ Raises exception on failure.
+ """
+ return self.sendReply(constants.tbreq.COMMAND_ACK, sCmdName);
+
+ @staticmethod
+ def sendSignOn(sTestManagerUrl, dParams):
+ """
+ Sends a sign-on request to the server, returns the response (TestBoxResponse).
+ No exceptions will be raised.
+ """
+ oConnection = None;
+ try:
+ oConnection = TestBoxConnection(sTestManagerUrl, None, None);
+ return oConnection.postRequestRaw(constants.tbreq.SIGNON, dParams);
+ except:
+ testboxcommons.log2Xcpt();
+ if oConnection is not None: # Be kind to apache.
+ try: oConnection.close();
+ except: pass;
+
+ return TestBoxResponse(None);
+
+ @staticmethod
+ def requestCommandWithConnection(sTestManagerUrl, sTestBoxId, sTestBoxUuid, fBusy):
+ """
+ Queries the test manager for a command and returns its respons + an open
+ connection for acking/nack the command (and maybe more).
+
+ No exceptions will be raised. On failure (None, None) will be returned.
+ """
+ oConnection = None;
+ try:
+ oConnection = TestBoxConnection(sTestManagerUrl, sTestBoxId, sTestBoxUuid, fLongTimeout = not fBusy);
+ if fBusy:
+ oResponse = oConnection.postRequest(constants.tbreq.REQUEST_COMMAND_BUSY);
+ else:
+ oResponse = oConnection.postRequest(constants.tbreq.REQUEST_COMMAND_IDLE);
+ return (oResponse, oConnection);
+ except:
+ testboxcommons.log2Xcpt();
+ if oConnection is not None: # Be kind to apache.
+ try: oConnection.close();
+ except: pass;
+ return (None, None);
+
+ def isConnected(self):
+ """
+ Checks if we are still connected.
+ """
+ return self._oConn is not None;
diff --git a/src/VBox/ValidationKit/testboxscript/testboxscript.py b/src/VBox/ValidationKit/testboxscript/testboxscript.py
new file mode 100755
index 00000000..407ec2f7
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxscript.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: testboxscript.py $
+
+"""
+TestBox Script Wrapper.
+
+This script aimes at respawning the Test Box Script when it terminates
+abnormally or due to an UPGRADE request.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+import platform;
+import subprocess;
+import sys;
+import os;
+import time;
+
+
+## @name Test Box script exit statuses (see also RTEXITCODE)
+# @remarks These will _never_ change
+# @{
+TBS_EXITCODE_FAILURE = 1; # RTEXITCODE_FAILURE
+TBS_EXITCODE_SYNTAX = 2; # RTEXITCODE_SYNTAX
+TBS_EXITCODE_NEED_UPGRADE = 9;
+## @}
+
+
+class TestBoxScriptWrapper(object): # pylint: disable=too-few-public-methods
+ """
+ Wrapper class
+ """
+
+ TESTBOX_SCRIPT_FILENAME = 'testboxscript_real.py'
+
+ def __init__(self):
+ """
+ Init
+ """
+ self.oTask = None
+
+ def __del__(self):
+ """
+ Cleanup
+ """
+ if self.oTask is not None:
+ print('Wait for child task...');
+ self.oTask.terminate()
+ self.oTask.wait()
+ print('done. Exiting');
+ self.oTask = None;
+
+ def run(self):
+ """
+ Start spawning the real TestBox script.
+ """
+
+ # Figure out where we live first.
+ try:
+ __file__
+ except:
+ __file__ = sys.argv[0];
+ sTestBoxScriptDir = os.path.dirname(os.path.abspath(__file__));
+
+ # Construct the argument list for the real script (same dir).
+ sRealScript = os.path.join(sTestBoxScriptDir, TestBoxScriptWrapper.TESTBOX_SCRIPT_FILENAME);
+ asArgs = sys.argv[1:];
+ asArgs.insert(0, sRealScript);
+ if sys.executable:
+ asArgs.insert(0, sys.executable);
+
+ # Look for --pidfile <name> and write a pid file.
+ sPidFile = None;
+ for i, _ in enumerate(asArgs):
+ if asArgs[i] == '--pidfile' and i + 1 < len(asArgs):
+ sPidFile = asArgs[i + 1];
+ break;
+ if asArgs[i] == '--':
+ break;
+ if sPidFile:
+ with open(sPidFile, 'w') as oPidFile:
+ oPidFile.write(str(os.getpid()));
+
+ # Execute the testbox script almost forever in a relaxed loop.
+ rcExit = TBS_EXITCODE_FAILURE;
+ while True:
+ fCreationFlags = 0;
+ if platform.system() == 'Windows':
+ fCreationFlags = getattr(subprocess, 'CREATE_NEW_PROCESS_GROUP', 0x00000200); # for Ctrl-C isolation (python 2.7)
+ self.oTask = subprocess.Popen(asArgs, shell = False, # pylint: disable=consider-using-with
+ creationflags = fCreationFlags);
+ rcExit = self.oTask.wait();
+ self.oTask = None;
+ if rcExit == TBS_EXITCODE_SYNTAX:
+ break;
+
+ # Relax.
+ time.sleep(1);
+ return rcExit;
+
+if __name__ == '__main__':
+ sys.exit(TestBoxScriptWrapper().run());
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxscript_real.py b/src/VBox/ValidationKit/testboxscript/testboxscript_real.py
new file mode 100755
index 00000000..7a2581ae
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxscript_real.py
@@ -0,0 +1,1073 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: testboxscript_real.py $
+
+"""
+TestBox Script - main().
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import math
+import os
+from optparse import OptionParser # pylint: disable=deprecated-module
+import platform
+import random
+import shutil
+import sys
+import tempfile
+import time
+import uuid
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksTestScriptDir = os.path.dirname(os.path.abspath(__file__));
+g_ksValidationKitDir = os.path.dirname(g_ksTestScriptDir);
+sys.path.extend([g_ksTestScriptDir, g_ksValidationKitDir]);
+
+# Validation Kit imports.
+from common import constants;
+from common import utils;
+import testboxcommons;
+from testboxcommons import TestBoxException;
+from testboxcommand import TestBoxCommand;
+from testboxconnection import TestBoxConnection;
+from testboxscript import TBS_EXITCODE_SYNTAX, TBS_EXITCODE_FAILURE;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestBoxScriptException(Exception):
+ """ For raising exceptions during TestBoxScript.__init__. """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TestBoxScript(object):
+ """
+ Implementation of the test box script.
+ Communicate with test manager and perform offered actions.
+ """
+
+ ## @name Class Constants.
+ # @{
+
+ # Scratch space round value (MB).
+ kcMbScratchSpaceRounding = 64
+ # Memory size round value (MB).
+ kcMbMemoryRounding = 4
+ # A NULL UUID in string form.
+ ksNullUuid = '00000000-0000-0000-0000-000000000000';
+ # The minimum dispatch loop delay.
+ kcSecMinDelay = 12;
+ # The maximum dispatch loop delay (inclusive).
+ kcSecMaxDelay = 24;
+ # The minimum sign-on delay.
+ kcSecMinSignOnDelay = 30;
+ # The maximum sign-on delay (inclusive).
+ kcSecMaxSignOnDelay = 60;
+
+ # Keys for config params
+ VALUE = 'value'
+ FN = 'fn' # pylint: disable=invalid-name
+
+ ## @}
+
+
+ def __init__(self, oOptions):
+ """
+ Initialize internals
+ """
+ self._oOptions = oOptions;
+ self._sTestBoxHelper = None;
+
+ # Signed-on state
+ self._cSignOnAttempts = 0;
+ self._fSignedOn = False;
+ self._fNeedReSignOn = False;
+ self._fFirstSignOn = True;
+ self._idTestBox = None;
+ self._sTestBoxName = '';
+ self._sTestBoxUuid = self.ksNullUuid; # convenience, assigned below.
+
+ # Command processor.
+ self._oCommand = TestBoxCommand(self);
+
+ #
+ # Scratch dir setup. Use /var/tmp instead of /tmp because we may need
+ # many many GBs for some test scenarios and /tmp can be backed by swap
+ # or be a fast+small disk of some kind, while /var/tmp is normally
+ # larger, if slower. /var/tmp is generally not cleaned up on reboot,
+ # /tmp often is, this would break host panic / triple-fault detection.
+ #
+ if self._oOptions.sScratchRoot is None:
+ if utils.getHostOs() in ('win', 'os2', 'haiku', 'dos'):
+ # We need *lots* of space, so avoid /tmp as it may be a memory
+ # file system backed by the swap file, or worse.
+ self._oOptions.sScratchRoot = tempfile.gettempdir();
+ else:
+ self._oOptions.sScratchRoot = '/var/tmp';
+ sSubDir = 'testbox';
+ try:
+ sSubDir = '%s-%u' % (sSubDir, os.getuid()); # pylint: disable=no-member
+ except:
+ pass;
+ self._oOptions.sScratchRoot = os.path.join(self._oOptions.sScratchRoot, sSubDir);
+
+ self._sScratchSpill = os.path.join(self._oOptions.sScratchRoot, 'scratch');
+ self._sScratchScripts = os.path.join(self._oOptions.sScratchRoot, 'scripts');
+ self._sScratchState = os.path.join(self._oOptions.sScratchRoot, 'state'); # persistant storage.
+
+ for sDir in [self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState]:
+ if not os.path.isdir(sDir):
+ os.makedirs(sDir, 0o700);
+
+ # We count consecutive reinitScratch failures and will reboot the
+ # testbox after a while in the hope that it will correct the issue.
+ self._cReinitScratchErrors = 0;
+
+ #
+ # Mount builds and test resources if requested.
+ #
+ self.mountShares();
+
+ #
+ # Sign-on parameters: Packed into list of records of format:
+ # { <Parameter ID>: { <Current value>, <Check function> } }
+ #
+ self._ddSignOnParams = \
+ {
+ constants.tbreq.ALL_PARAM_TESTBOX_UUID: { self.VALUE: self._getHostSystemUuid(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_OS: { self.VALUE: utils.getHostOs(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_OS_VERSION: { self.VALUE: utils.getHostOsVersion(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_CPU_ARCH: { self.VALUE: utils.getHostArch(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_CPU_VENDOR: { self.VALUE: self._getHostCpuVendor(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_CPU_NAME: { self.VALUE: self._getHostCpuName(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_CPU_REVISION: { self.VALUE: self._getHostCpuRevision(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT: { self.VALUE: self._hasHostHwVirt(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING:{ self.VALUE: self._hasHostNestedPaging(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_HAS_64_BIT_GUEST: { self.VALUE: self._can64BitGuest(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_HAS_IOMMU: { self.VALUE: self._hasHostIoMmu(), self.FN: None },
+ #constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE: { self.VALUE: self._withRawModeSupport(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_SCRIPT_REV: { self.VALUE: self._getScriptRev(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_REPORT: { self.VALUE: self._getHostReport(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_PYTHON_VERSION: { self.VALUE: self._getPythonHexVersion(), self.FN: None },
+ constants.tbreq.SIGNON_PARAM_CPU_COUNT: { self.VALUE: None, self.FN: utils.getPresentCpuCount },
+ constants.tbreq.SIGNON_PARAM_MEM_SIZE: { self.VALUE: None, self.FN: self._getHostMemSize },
+ constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE: { self.VALUE: None, self.FN: self._getFreeScratchSpace },
+ }
+ for sItem in self._ddSignOnParams: # pylint: disable=consider-using-dict-items
+ if self._ddSignOnParams[sItem][self.FN] is not None:
+ self._ddSignOnParams[sItem][self.VALUE] = self._ddSignOnParams[sItem][self.FN]()
+
+ testboxcommons.log('Starting Test Box script (%s)' % (self._getScriptRev(),));
+ testboxcommons.log('Test Manager URL: %s' % self._oOptions.sTestManagerUrl,)
+ testboxcommons.log('Scratch root path: %s' % self._oOptions.sScratchRoot,)
+ for sItem in self._ddSignOnParams: # pylint: disable=consider-using-dict-items
+ testboxcommons.log('Sign-On value %18s: %s' % (sItem, self._ddSignOnParams[sItem][self.VALUE]));
+
+ #
+ # The System UUID is the primary identification of the machine, so
+ # refuse to cooperate if it's NULL.
+ #
+ self._sTestBoxUuid = self.getSignOnParam(constants.tbreq.ALL_PARAM_TESTBOX_UUID);
+ if self._sTestBoxUuid == self.ksNullUuid:
+ raise TestBoxScriptException('Couldn\'t determine the System UUID, please use --system-uuid to specify it.');
+
+ #
+ # Export environment variables, clearing any we don't know yet.
+ #
+ for sEnvVar in self._oOptions.asEnvVars:
+ iEqual = sEnvVar.find('=');
+ if iEqual == -1: # No '=', remove it.
+ if sEnvVar in os.environ:
+ del os.environ[sEnvVar];
+ elif iEqual > 0: # Set it.
+ os.environ[sEnvVar[:iEqual]] = sEnvVar[iEqual+1:];
+ else: # Starts with '=', bad user.
+ raise TestBoxScriptException('Invalid -E argument: "%s"' % (sEnvVar,));
+
+ os.environ['TESTBOX_PATH_BUILDS'] = self._oOptions.sBuildsPath;
+ os.environ['TESTBOX_PATH_RESOURCES'] = self._oOptions.sTestRsrcPath;
+ os.environ['TESTBOX_PATH_SCRATCH'] = self._sScratchSpill;
+ os.environ['TESTBOX_PATH_SCRIPTS'] = self._sScratchScripts;
+ os.environ['TESTBOX_PATH_UPLOAD'] = self._sScratchSpill; ## @todo drop the UPLOAD dir?
+ os.environ['TESTBOX_HAS_HW_VIRT'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT);
+ os.environ['TESTBOX_HAS_NESTED_PAGING'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING);
+ os.environ['TESTBOX_HAS_IOMMU'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_IOMMU);
+ os.environ['TESTBOX_SCRIPT_REV'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_SCRIPT_REV);
+ os.environ['TESTBOX_CPU_COUNT'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_CPU_COUNT);
+ os.environ['TESTBOX_MEM_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_MEM_SIZE);
+ os.environ['TESTBOX_SCRATCH_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE);
+ #TODO: os.environ['TESTBOX_WITH_RAW_MODE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE);
+ os.environ['TESTBOX_WITH_RAW_MODE'] = str(self._withRawModeSupport());
+ os.environ['TESTBOX_MANAGER_URL'] = self._oOptions.sTestManagerUrl;
+ os.environ['TESTBOX_UUID'] = self._sTestBoxUuid;
+ os.environ['TESTBOX_REPORTER'] = 'remote';
+ os.environ['TESTBOX_NAME'] = '';
+ os.environ['TESTBOX_ID'] = '';
+ os.environ['TESTBOX_TEST_SET_ID'] = '';
+ os.environ['TESTBOX_TIMEOUT'] = '0';
+ os.environ['TESTBOX_TIMEOUT_ABS'] = '0';
+
+ if utils.getHostOs() == 'win':
+ os.environ['COMSPEC'] = os.path.join(os.environ['SystemRoot'], 'System32', 'cmd.exe');
+ # Currently omitting any kBuild tools.
+
+ def mountShares(self):
+ """
+ Mounts the shares.
+ Raises exception on failure.
+ """
+ self._mountShare(self._oOptions.sBuildsPath, self._oOptions.sBuildsServerType, self._oOptions.sBuildsServerName,
+ self._oOptions.sBuildsServerShare,
+ self._oOptions.sBuildsServerUser, self._oOptions.sBuildsServerPasswd,
+ self._oOptions.sBuildsServerMountOpt, 'builds');
+ self._mountShare(self._oOptions.sTestRsrcPath, self._oOptions.sTestRsrcServerType, self._oOptions.sTestRsrcServerName,
+ self._oOptions.sTestRsrcServerShare,
+ self._oOptions.sTestRsrcServerUser, self._oOptions.sTestRsrcServerPasswd,
+ self._oOptions.sTestRsrcServerMountOpt, 'testrsrc');
+ return True;
+
+ def _mountShare(self, sMountPoint, sType, sServer, sShare, sUser, sPassword, sMountOpt, sWhat):
+ """
+ Mounts the specified share if needed.
+ Raises exception on failure.
+ """
+ # Only mount if the type is specified.
+ if sType is None:
+ return True;
+
+ # Test if already mounted.
+ sTestFile = os.path.join(sMountPoint + os.path.sep, os.path.basename(sShare) + '-new.txt');
+ if os.path.isfile(sTestFile):
+ return True;
+
+ #
+ # Platform specific mount code.
+ #
+ sHostOs = utils.getHostOs()
+ if sHostOs in ('darwin', 'freebsd'):
+ if sMountOpt != '':
+ sMountOpt = ',' + sMountOpt
+ utils.sudoProcessCall(['/sbin/umount', sMountPoint]);
+ utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]);
+ utils.sudoProcessCall(['/usr/sbin/chown', str(os.getuid()), sMountPoint]); # pylint: disable=no-member
+ if sType == 'cifs':
+ # Note! no smb://server/share stuff here, 10.6.8 didn't like it.
+ utils.processOutputChecked(['/sbin/mount_smbfs',
+ '-o',
+ 'automounted,nostreams,soft,noowners,noatime,rdonly' + sMountOpt,
+ '-f', '0555', '-d', '0555',
+ '//%s:%s@%s/%s' % (sUser, sPassword, sServer, sShare),
+ sMountPoint]);
+ else:
+ raise TestBoxScriptException('Unsupported server type %s.' % (sType,));
+
+ elif sHostOs == 'linux':
+ if sMountOpt != '':
+ sMountOpt = ',' + sMountOpt
+ utils.sudoProcessCall(['/bin/umount', sMountPoint]);
+ utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]);
+ if sType == 'cifs':
+ utils.sudoProcessOutputChecked(['/bin/mount', '-t', 'cifs',
+ '-o',
+ 'user=' + sUser
+ + ',password=' + sPassword
+ + ',sec=ntlmv2'
+ + ',uid=' + str(os.getuid()) # pylint: disable=no-member
+ + ',gid=' + str(os.getgid()) # pylint: disable=no-member
+ + ',nounix,file_mode=0555,dir_mode=0555,soft,ro'
+ + sMountOpt,
+ '//%s/%s' % (sServer, sShare),
+ sMountPoint]);
+ elif sType == 'nfs':
+ utils.sudoProcessOutputChecked(['/bin/mount', '-t', 'nfs',
+ '-o', 'soft,ro' + sMountOpt,
+ '%s:%s' % (sServer, sShare if sShare.find('/') >= 0 else ('/export/' + sShare)),
+ sMountPoint]);
+
+ else:
+ raise TestBoxScriptException('Unsupported server type %s.' % (sType,));
+
+ elif sHostOs == 'solaris':
+ if sMountOpt != '':
+ sMountOpt = ',' + sMountOpt
+ utils.sudoProcessCall(['/sbin/umount', sMountPoint]);
+ utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]);
+ if sType == 'cifs':
+ ## @todo This stuff doesn't work on wei01-x4600b.de.oracle.com running 11.1. FIXME!
+ oPasswdFile = tempfile.TemporaryFile(); # pylint: disable=consider-using-with
+ oPasswdFile.write(sPassword + '\n');
+ oPasswdFile.flush();
+ utils.sudoProcessOutputChecked(['/sbin/mount', '-F', 'smbfs',
+ '-o',
+ 'user=' + sUser
+ + ',uid=' + str(os.getuid()) # pylint: disable=no-member
+ + ',gid=' + str(os.getgid()) # pylint: disable=no-member
+ + ',fileperms=0555,dirperms=0555,noxattr,ro'
+ + sMountOpt,
+ '//%s/%s' % (sServer, sShare),
+ sMountPoint],
+ stdin = oPasswdFile);
+ oPasswdFile.close();
+ elif sType == 'nfs':
+ utils.sudoProcessOutputChecked(['/sbin/mount', '-F', 'nfs',
+ '-o', 'noxattr,ro' + sMountOpt,
+ '%s:%s' % (sServer, sShare if sShare.find('/') >= 0 else ('/export/' + sShare)),
+ sMountPoint]);
+
+ else:
+ raise TestBoxScriptException('Unsupported server type %s.' % (sType,));
+
+
+ elif sHostOs == 'win':
+ if sType != 'cifs':
+ raise TestBoxScriptException('Only CIFS mounts are supported on Windows.');
+ utils.processCall(['net', 'use', sMountPoint, '/d']);
+ utils.processOutputChecked(['net', 'use', sMountPoint,
+ '\\\\' + sServer + '\\' + sShare,
+ sPassword,
+ '/USER:' + sUser,]);
+ else:
+ raise TestBoxScriptException('Unsupported host %s' % (sHostOs,));
+
+ #
+ # Re-test.
+ #
+ if not os.path.isfile(sTestFile):
+ raise TestBoxException('Failed to mount %s (%s[%s]) at %s: %s not found'
+ % (sWhat, sServer, sShare, sMountPoint, sTestFile));
+
+ return True;
+
+ ## @name Signon property releated methods.
+ # @{
+
+ def _getHelperOutput(self, sCmd):
+ """
+ Invokes TestBoxHelper to obtain information hard to access from python.
+ """
+ if self._sTestBoxHelper is None:
+ if not utils.isRunningFromCheckout():
+ # See VBoxTestBoxScript.zip for layout.
+ self._sTestBoxHelper = os.path.join(g_ksValidationKitDir, utils.getHostOs(), utils.getHostArch(), \
+ 'TestBoxHelper');
+ else: # Only for in-tree testing, so don't bother be too accurate right now.
+ sType = os.environ.get('KBUILD_TYPE', 'debug');
+ self._sTestBoxHelper = os.path.join(g_ksValidationKitDir, os.pardir, os.pardir, os.pardir, 'out', \
+ utils.getHostOsDotArch(), sType, 'testboxscript', \
+ utils.getHostOs(), utils.getHostArch(), \
+ 'TestBoxHelper');
+ if utils.getHostOs() in ['win', 'os2']:
+ self._sTestBoxHelper += '.exe';
+
+ return utils.processOutputChecked([self._sTestBoxHelper, sCmd]).strip();
+
+ def _getHelperOutputTristate(self, sCmd, fDunnoValue):
+ """
+ Invokes TestBoxHelper to obtain information hard to access from python.
+ """
+ sValue = self._getHelperOutput(sCmd);
+ sValue = sValue.lower();
+ if sValue == 'true':
+ return True;
+ if sValue == 'false':
+ return False;
+ if sValue not in ('dunno', 'none',):
+ raise TestBoxException('Unexpected response "%s" to helper command "%s"' % (sValue, sCmd));
+ return fDunnoValue;
+
+
+ @staticmethod
+ def _isUuidGood(sUuid):
+ """
+ Checks if the UUID looks good.
+
+ There are systems with really bad UUIDs, for instance
+ "03000200-0400-0500-0006-000700080009".
+ """
+ if sUuid == TestBoxScript.ksNullUuid:
+ return False;
+ sUuid = sUuid.lower();
+ for sDigit in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']:
+ if sUuid.count(sDigit) > 16:
+ return False;
+ return True;
+
+ def _getHostSystemUuid(self):
+ """
+ Get the system UUID string from the System, return null-uuid if
+ unable to get retrieve it.
+ """
+ if self._oOptions.sSystemUuid is not None:
+ return self._oOptions.sSystemUuid;
+
+ sUuid = self.ksNullUuid;
+
+ #
+ # Try get at the firmware UUID.
+ #
+ if utils.getHostOs() == 'linux':
+ # NOTE: This requires to have kernel option enabled:
+ # Firmware Drivers -> Export DMI identification via sysfs to userspace
+ if os.path.exists('/sys/devices/virtual/dmi/id/product_uuid'):
+ try:
+ sVar = utils.sudoProcessOutputChecked(['cat', '/sys/devices/virtual/dmi/id/product_uuid']);
+ sUuid = str(uuid.UUID(sVar.strip()));
+ except:
+ pass;
+ ## @todo consider dmidecoder? What about EFI systems?
+
+ elif utils.getHostOs() == 'win':
+ # Windows: WMI
+ try:
+ import win32com.client; # pylint: disable=import-error
+ oWmi = win32com.client.Dispatch('WbemScripting.SWbemLocator');
+ oWebm = oWmi.ConnectServer('.', 'root\\cimv2');
+ for oItem in oWebm.ExecQuery('SELECT * FROM Win32_ComputerSystemProduct'):
+ if oItem.UUID is not None:
+ sUuid = str(uuid.UUID(oItem.UUID));
+ except:
+ pass;
+
+ elif utils.getHostOs() == 'darwin':
+ try:
+ sVar = utils.processOutputChecked(['/bin/sh', '-c',
+ '/usr/sbin/ioreg -k IOPlatformUUID' \
+ + '| /usr/bin/grep IOPlatformUUID' \
+ + '| /usr/bin/head -1']);
+ sVar = sVar.strip()[-(len(self.ksNullUuid) + 1):-1];
+ sUuid = str(uuid.UUID(sVar));
+ except:
+ pass;
+
+ elif utils.getHostOs() == 'solaris':
+ # Solaris: The smbios util.
+ try:
+ sVar = utils.processOutputChecked(['/bin/sh', '-c',
+ '/usr/sbin/smbios ' \
+ + '| /usr/xpg4/bin/sed -ne \'s/^.*UUID: *//p\'' \
+ + '| /usr/bin/head -1']);
+ sUuid = str(uuid.UUID(sVar.strip()));
+ except:
+ pass;
+
+ if self._isUuidGood(sUuid):
+ return sUuid;
+
+ #
+ # Try add the MAC address.
+ # uuid.getnode may provide it, or it may return a random number...
+ #
+ lMacAddr = uuid.getnode();
+ sNode = '%012x' % (lMacAddr,)
+ if lMacAddr == uuid.getnode() and lMacAddr != 0 and len(sNode) == 12:
+ return sUuid[:-12] + sNode;
+
+ return sUuid;
+
+ def _getHostCpuVendor(self):
+ """
+ Get the CPUID vendor string on intel HW.
+ """
+ return self._getHelperOutput('cpuvendor');
+
+ def _getHostCpuName(self):
+ """
+ Get the CPU name/description string.
+ """
+ return self._getHelperOutput('cpuname');
+
+ def _getHostCpuRevision(self):
+ """
+ Get the CPU revision (family/model/stepping) value.
+ """
+ return self._getHelperOutput('cpurevision');
+
+ def _hasHostHwVirt(self):
+ """
+ Check if the host supports AMD-V or VT-x
+ """
+ if self._oOptions.fHasHwVirt is None:
+ self._oOptions.fHasHwVirt = self._getHelperOutput('cpuhwvirt');
+ return self._oOptions.fHasHwVirt;
+
+ def _hasHostNestedPaging(self):
+ """
+ Check if the host supports nested paging.
+ """
+ if not self._hasHostHwVirt():
+ return False;
+ if self._oOptions.fHasNestedPaging is None:
+ self._oOptions.fHasNestedPaging = self._getHelperOutputTristate('nestedpaging', False);
+ return self._oOptions.fHasNestedPaging;
+
+ def _can64BitGuest(self):
+ """
+ Check if the we (VBox) can run 64-bit guests.
+ """
+ if not self._hasHostHwVirt():
+ return False;
+ if self._oOptions.fCan64BitGuest is None:
+ self._oOptions.fCan64BitGuest = self._getHelperOutputTristate('longmode', True);
+ return self._oOptions.fCan64BitGuest;
+
+ def _hasHostIoMmu(self):
+ """
+ Check if the host has an I/O MMU of the VT-d kind.
+ """
+ if not self._hasHostHwVirt():
+ return False;
+ if self._oOptions.fHasIoMmu is None:
+ ## @todo Any way to figure this one out on any host OS?
+ self._oOptions.fHasIoMmu = False;
+ return self._oOptions.fHasIoMmu;
+
+ def _withRawModeSupport(self):
+ """
+ Check if the testbox is configured with raw-mode support or not.
+ """
+ if self._oOptions.fWithRawMode is None:
+ self._oOptions.fWithRawMode = True;
+ return self._oOptions.fWithRawMode;
+
+ def _getHostReport(self):
+ """
+ Generate a report about the host hardware and software.
+ """
+ return self._getHelperOutput('report');
+
+
+ def _getHostMemSize(self):
+ """
+ Gets the amount of physical memory on the host (and accessible to the
+ OS, i.e. don't report stuff over 4GB if Windows doesn't wanna use it).
+ Unit: MiB.
+ """
+ cMbMemory = long(self._getHelperOutput('memsize').strip()) / (1024 * 1024);
+
+ # Round it.
+ cMbMemory = long(math.floor(cMbMemory / self.kcMbMemoryRounding)) * self.kcMbMemoryRounding;
+ return cMbMemory;
+
+ def _getFreeScratchSpace(self):
+ """
+ Get free space on the volume where scratch directory is located and
+ return it in bytes rounded down to nearest 64MB
+ (currently works on Linux only)
+ Unit: MiB.
+ """
+ if platform.system() == 'Windows':
+ import ctypes
+ cTypeMbFreeSpace = ctypes.c_ulonglong(0)
+ ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(self._oOptions.sScratchRoot), None, None,
+ ctypes.pointer(cTypeMbFreeSpace))
+ cMbFreeSpace = cTypeMbFreeSpace.value
+ else:
+ stats = os.statvfs(self._oOptions.sScratchRoot); # pylint: disable=no-member
+ cMbFreeSpace = stats.f_frsize * stats.f_bfree
+
+ # Convert to MB
+ cMbFreeSpace = long(cMbFreeSpace) /(1024 * 1024)
+
+ # Round free space size
+ cMbFreeSpace = long(math.floor(cMbFreeSpace / self.kcMbScratchSpaceRounding)) * self.kcMbScratchSpaceRounding;
+ return cMbFreeSpace;
+
+ def _getScriptRev(self):
+ """
+ The script (subversion) revision number.
+ """
+ sRev = '@VBOX_SVN_REV@';
+ sRev = sRev.strip(); # just in case...
+ try:
+ _ = int(sRev);
+ except:
+ return __version__[11:-1].strip();
+ return sRev;
+
+ def _getPythonHexVersion(self):
+ """
+ The python hex version number.
+ """
+ uHexVersion = getattr(sys, 'hexversion', None);
+ if uHexVersion is None:
+ uHexVersion = (sys.version_info[0] << 24) | (sys.version_info[1] << 16) | (sys.version_info[2] << 8);
+ if sys.version_info[3] == 'final':
+ uHexVersion |= 0xf0;
+ return uHexVersion;
+
+ # @}
+
+ def openTestManagerConnection(self):
+ """
+ Opens up a connection to the test manager.
+
+ Raises exception on failure.
+ """
+ return TestBoxConnection(self._oOptions.sTestManagerUrl, self._idTestBox, self._sTestBoxUuid);
+
+ def getSignOnParam(self, sName):
+ """
+ Returns a sign-on parameter value as string.
+ Raises exception if the name is incorrect.
+ """
+ return str(self._ddSignOnParams[sName][self.VALUE]);
+
+ def getPathState(self):
+ """
+ Get the path to the state dir in the scratch area.
+ """
+ return self._sScratchState;
+
+ def getPathScripts(self):
+ """
+ Get the path to the scripts dir (TESTBOX_PATH_SCRIPTS) in the scratch area.
+ """
+ return self._sScratchScripts;
+
+ def getPathSpill(self):
+ """
+ Get the path to the spill dir (TESTBOX_PATH_SCRATCH) in the scratch area.
+ """
+ return self._sScratchSpill;
+
+ def getPathBuilds(self):
+ """
+ Get the path to the builds.
+ """
+ return self._oOptions.sBuildsPath;
+
+ def getTestBoxId(self):
+ """
+ Get the TestBox ID for state saving purposes.
+ """
+ return self._idTestBox;
+
+ def getTestBoxName(self):
+ """
+ Get the TestBox name for state saving purposes.
+ """
+ return self._sTestBoxName;
+
+ def _reinitScratch(self, fnLog, fUseTheForce):
+ """
+ Wipes the scratch directories and re-initializes them.
+
+ No exceptions raise, returns success indicator instead.
+ """
+ if fUseTheForce is None:
+ fUseTheForce = self._fFirstSignOn;
+
+ class ErrorCallback(object): # pylint: disable=too-few-public-methods
+ """
+ Callbacks + state for the cleanup.
+ """
+ def __init__(self):
+ self.fRc = True;
+ def onErrorCallback(self, sFnName, sPath, aXcptInfo):
+ """ Logs error during shutil.rmtree operation. """
+ fnLog('Error removing "%s": fn=%s %s' % (sPath, sFnName, aXcptInfo[1]));
+ self.fRc = False;
+ oRc = ErrorCallback();
+
+ #
+ # Cleanup.
+ #
+ for sName in os.listdir(self._oOptions.sScratchRoot):
+ sFullName = os.path.join(self._oOptions.sScratchRoot, sName);
+ try:
+ if os.path.isdir(sFullName):
+ shutil.rmtree(sFullName, False, oRc.onErrorCallback);
+ else:
+ os.remove(sFullName);
+ if os.path.exists(sFullName):
+ raise Exception('Still exists after deletion, weird.');
+ except Exception as oXcpt:
+ if fUseTheForce is True \
+ and utils.getHostOs() not in ['win', 'os2'] \
+ and len(sFullName) >= 8 \
+ and sFullName[0] == '/' \
+ and sFullName[1] != '/' \
+ and sFullName.find('/../') < 0:
+ fnLog('Problems deleting "%s" (%s) using the force...' % (sFullName, oXcpt));
+ try:
+ if os.path.isdir(sFullName):
+ iRc = utils.sudoProcessCall(['/bin/rm', '-Rf', sFullName])
+ else:
+ iRc = utils.sudoProcessCall(['/bin/rm', '-f', sFullName])
+ if iRc != 0:
+ raise Exception('exit code %s' % iRc);
+ if os.path.exists(sFullName):
+ raise Exception('Still exists after forced deletion, weird^2.');
+ except:
+ fnLog('Error sudo deleting "%s": %s' % (sFullName, oXcpt));
+ oRc.fRc = False;
+ else:
+ fnLog('Error deleting "%s": %s' % (sFullName, oXcpt));
+ oRc.fRc = False;
+
+ # Display files left behind.
+ def dirEnumCallback(sName, oStat):
+ """ callback for dirEnumerateTree """
+ fnLog(u'%s %s' % (utils.formatFileStat(oStat) if oStat is not None else '????????????', sName));
+ utils.dirEnumerateTree(self._oOptions.sScratchRoot, dirEnumCallback);
+
+ #
+ # Re-create the directories.
+ #
+ for sDir in [self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState]:
+ if not os.path.isdir(sDir):
+ try:
+ os.makedirs(sDir, 0o700);
+ except Exception as oXcpt:
+ fnLog('Error creating "%s": %s' % (sDir, oXcpt));
+ oRc.fRc = False;
+
+ if oRc.fRc is True:
+ self._cReinitScratchErrors = 0;
+ else:
+ self._cReinitScratchErrors += 1;
+ return oRc.fRc;
+
+ def reinitScratch(self, fnLog = testboxcommons.log, fUseTheForce = None, cRetries = 0, cMsDelay = 5000):
+ """
+ Wipes the scratch directories and re-initializes them.
+
+ Will retry according to the cRetries and cMsDelay parameters. Windows
+ forces us to apply this hack as it ships with services asynchronously
+ scanning files after they execute, thus racing us cleaning up after a
+ test. On testboxwin3 we had frequent trouble with aelupsvc.dll keeping
+ vts_rm.exe kind of open, somehow preventing us from removing the
+ directory containing it, despite not issuing any errors deleting the
+ file itself. The service is called "Application Experience", which
+ feels like a weird joke here.
+
+ No exceptions raise, returns success indicator instead.
+ """
+ fRc = self._reinitScratch(fnLog, fUseTheForce)
+ while fRc is False and cRetries > 0:
+ time.sleep(cMsDelay / 1000.0);
+ fnLog('reinitScratch: Retrying...');
+ fRc = self._reinitScratch(fnLog, fUseTheForce)
+ cRetries -= 1;
+ return fRc;
+
+
+ def _doSignOn(self):
+ """
+ Worker for _maybeSignOn that does the actual signing on.
+ """
+ assert not self._oCommand.isRunning();
+
+ # Reset the siged-on state.
+ testboxcommons.log('Signing-on...')
+ self._fSignedOn = False
+ self._idTestBox = None
+ self._cSignOnAttempts += 1;
+
+ # Assemble SIGN-ON request parameters and send the request.
+ dParams = {};
+ for sParam in self._ddSignOnParams: # pylint: disable=consider-using-dict-items
+ dParams[sParam] = self._ddSignOnParams[sParam][self.VALUE];
+ oResponse = TestBoxConnection.sendSignOn(self._oOptions.sTestManagerUrl, dParams);
+
+ # Check response.
+ try:
+ sResult = oResponse.getStringChecked(constants.tbresp.ALL_PARAM_RESULT);
+ if sResult != constants.tbresp.STATUS_ACK:
+ raise TestBoxException('Result is %s' % (sResult,));
+ oResponse.checkParameterCount(3);
+ idTestBox = oResponse.getIntChecked(constants.tbresp.SIGNON_PARAM_ID, 1, 0x7ffffffe);
+ sTestBoxName = oResponse.getStringChecked(constants.tbresp.SIGNON_PARAM_NAME);
+ except TestBoxException as err:
+ testboxcommons.log('Failed to sign-on: %s' % (str(err),))
+ testboxcommons.log('Server response: %s' % (oResponse.toString(),));
+ return False;
+
+ # Successfully signed on, update the state.
+ self._fSignedOn = True;
+ self._fNeedReSignOn = False;
+ self._cSignOnAttempts = 0;
+ self._idTestBox = idTestBox;
+ self._sTestBoxName = sTestBoxName;
+
+ # Update the environment.
+ os.environ['TESTBOX_ID'] = str(self._idTestBox);
+ os.environ['TESTBOX_NAME'] = sTestBoxName;
+ os.environ['TESTBOX_CPU_COUNT'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_CPU_COUNT);
+ os.environ['TESTBOX_MEM_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_MEM_SIZE);
+ os.environ['TESTBOX_SCRATCH_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE);
+
+ testboxcommons.log('Successfully signed-on with Test Box ID #%s and given the name "%s"' \
+ % (self._idTestBox, self._sTestBoxName));
+
+ # Set up the scratch area.
+ self.reinitScratch(fUseTheForce = self._fFirstSignOn, cRetries = 2);
+
+ self._fFirstSignOn = False;
+ return True;
+
+ def _maybeSignOn(self):
+ """
+ Check if Test Box parameters were changed
+ and do sign-in in case of positive result
+ """
+
+ # Skip sign-on check if background command is currently in
+ # running state (avoid infinite signing on).
+ if self._oCommand.isRunning():
+ return None;
+
+ # Refresh sign-on parameters, changes triggers sign-on.
+ fNeedSignOn = not self._fSignedOn or self._fNeedReSignOn;
+ for sItem in self._ddSignOnParams: # pylint: disable=consider-using-dict-items
+ if self._ddSignOnParams[sItem][self.FN] is None:
+ continue
+
+ sOldValue = self._ddSignOnParams[sItem][self.VALUE]
+ self._ddSignOnParams[sItem][self.VALUE] = self._ddSignOnParams[sItem][self.FN]()
+ if sOldValue != self._ddSignOnParams[sItem][self.VALUE]:
+ fNeedSignOn = True
+ testboxcommons.log('Detected %s parameter change: %s -> %s'
+ % (sItem, sOldValue, self._ddSignOnParams[sItem][self.VALUE],))
+
+ if fNeedSignOn:
+ self._doSignOn();
+ return None;
+
+ def dispatch(self):
+ """
+ Receive orders from Test Manager and execute them
+ """
+
+ (self._idTestBox, self._sTestBoxName, self._fSignedOn) = self._oCommand.resumeIncompleteCommand();
+ self._fNeedReSignOn = self._fSignedOn;
+ if self._fSignedOn:
+ os.environ['TESTBOX_ID'] = str(self._idTestBox);
+ os.environ['TESTBOX_NAME'] = self._sTestBoxName;
+
+ while True:
+ # Make sure we're signed on before trying to do anything.
+ self._maybeSignOn();
+ while not self._fSignedOn:
+ iFactor = 1 if self._cSignOnAttempts < 100 else 4;
+ time.sleep(random.randint(self.kcSecMinSignOnDelay * iFactor, self.kcSecMaxSignOnDelay * iFactor));
+ self._maybeSignOn();
+
+ # Retrieve and handle command from the TM.
+ (oResponse, oConnection) = TestBoxConnection.requestCommandWithConnection(self._oOptions.sTestManagerUrl,
+ self._idTestBox,
+ self._sTestBoxUuid,
+ self._oCommand.isRunning());
+ if oResponse is not None:
+ self._oCommand.handleCommand(oResponse, oConnection);
+ if oConnection is not None:
+ if oConnection.isConnected():
+ self._oCommand.flushLogOnConnection(oConnection);
+ oConnection.close();
+
+ # Automatically reboot if scratch init fails.
+ #if self._cReinitScratchErrors > 8 and self.reinitScratch(cRetries = 3) is False:
+ # testboxcommons.log('Scratch does not initialize cleanly after %d attempts, rebooting...'
+ # % ( self._cReinitScratchErrors, ));
+ # self._oCommand.doReboot();
+
+ # delay a wee bit before looping.
+ ## @todo We shouldn't bother the server too frequently. We should try combine the test reporting done elsewhere
+ # with the command retrieval done here. I believe tinderclient.pl is capable of doing that.
+ iFactor = 1;
+ if self._cReinitScratchErrors > 0:
+ iFactor = 4;
+ time.sleep(random.randint(self.kcSecMinDelay * iFactor, self.kcSecMaxDelay * iFactor));
+
+ # Not reached.
+
+
+ @staticmethod
+ def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ #
+ # Parse arguments.
+ #
+ sDefShareType = 'nfs' if utils.getHostOs() == 'solaris' else 'cifs';
+ if utils.getHostOs() in ('win', 'os2'):
+ sDefTestRsrc = 'T:';
+ sDefBuilds = 'U:';
+ elif utils.getHostOs() == 'darwin':
+ sDefTestRsrc = '/Volumes/testrsrc';
+ sDefBuilds = '/Volumes/builds';
+ else:
+ sDefTestRsrc = '/mnt/testrsrc';
+ sDefBuilds = '/mnt/builds';
+
+ class MyOptionParser(OptionParser):
+ """ We need to override the exit code on --help, error and so on. """
+ def __init__(self, *args, **kwargs):
+ OptionParser.__init__(self, *args, **kwargs);
+ def exit(self, status = 0, msg = None):
+ OptionParser.exit(self, TBS_EXITCODE_SYNTAX, msg);
+
+ parser = MyOptionParser(version=__version__[11:-1].strip());
+ for sMixed, sDefault, sDesc in [('Builds', sDefBuilds, 'builds'), ('TestRsrc', sDefTestRsrc, 'test resources') ]:
+ sLower = sMixed.lower();
+ sPrefix = 's' + sMixed;
+ parser.add_option('--' + sLower + '-path',
+ dest=sPrefix + 'Path', metavar='<abs-path>', default=sDefault,
+ help='Where ' + sDesc + ' can be found');
+ parser.add_option('--' + sLower + '-server-type',
+ dest=sPrefix + 'ServerType', metavar='<nfs|cifs>', default=sDefShareType,
+ help='The type of server, cifs (default) or nfs. If empty, we won\'t try mount anything.');
+ parser.add_option('--' + sLower + '-server-name',
+ dest=sPrefix + 'ServerName', metavar='<server>',
+ default='vboxstor.de.oracle.com' if sLower == 'builds' else 'teststor.de.oracle.com',
+ help='The name of the server with the builds.');
+ parser.add_option('--' + sLower + '-server-share',
+ dest=sPrefix + 'ServerShare', metavar='<share>', default=sLower,
+ help='The name of the builds share.');
+ parser.add_option('--' + sLower + '-server-user',
+ dest=sPrefix + 'ServerUser', metavar='<user>', default='guestr',
+ help='The user name to use when accessing the ' + sDesc + ' share.');
+ parser.add_option('--' + sLower + '-server-passwd', '--' + sLower + '-server-password',
+ dest=sPrefix + 'ServerPasswd', metavar='<password>', default='guestr',
+ help='The password to use when accessing the ' + sDesc + ' share.');
+ parser.add_option('--' + sLower + '-server-mountopt',
+ dest=sPrefix + 'ServerMountOpt', metavar='<mountopt>', default='',
+ help='The mount options to use when accessing the ' + sDesc + ' share.');
+
+ parser.add_option("--test-manager", metavar="<url>",
+ dest="sTestManagerUrl",
+ help="Test Manager URL",
+ default="http://tindertux.de.oracle.com/testmanager")
+ parser.add_option("--scratch-root", metavar="<abs-path>",
+ dest="sScratchRoot",
+ help="Path to the scratch directory",
+ default=None)
+ parser.add_option("--system-uuid", metavar="<uuid>",
+ dest="sSystemUuid",
+ help="The system UUID of the testbox, used for uniquely identifiying the machine",
+ default=None)
+ parser.add_option("--hwvirt",
+ dest="fHasHwVirt", action="store_true", default=None,
+ help="Hardware virtualization available in the CPU");
+ parser.add_option("--no-hwvirt",
+ dest="fHasHwVirt", action="store_false", default=None,
+ help="Hardware virtualization not available in the CPU");
+ parser.add_option("--nested-paging",
+ dest="fHasNestedPaging", action="store_true", default=None,
+ help="Nested paging is available");
+ parser.add_option("--no-nested-paging",
+ dest="fHasNestedPaging", action="store_false", default=None,
+ help="Nested paging is not available");
+ parser.add_option("--64-bit-guest",
+ dest="fCan64BitGuest", action="store_true", default=None,
+ help="Host can execute 64-bit guests");
+ parser.add_option("--no-64-bit-guest",
+ dest="fCan64BitGuest", action="store_false", default=None,
+ help="Host cannot execute 64-bit guests");
+ parser.add_option("--io-mmu",
+ dest="fHasIoMmu", action="store_true", default=None,
+ help="I/O MMU available");
+ parser.add_option("--no-io-mmu",
+ dest="fHasIoMmu", action="store_false", default=None,
+ help="No I/O MMU available");
+ parser.add_option("--raw-mode",
+ dest="fWithRawMode", action="store_true", default=None,
+ help="Use raw-mode on this host.");
+ parser.add_option("--no-raw-mode",
+ dest="fWithRawMode", action="store_false", default=None,
+ help="Disables raw-mode tests on this host.");
+ parser.add_option("--pidfile",
+ dest="sPidFile", default=None,
+ help="For the parent script, ignored.");
+ parser.add_option("-E", "--putenv", metavar = "<variable>=<value>", action = "append",
+ dest = "asEnvVars", default = [],
+ help = "Sets an environment variable. Can be repeated.");
+ def sbp_callback(option, opt_str, value, parser):
+ _, _, _ = opt_str, value, option
+ parser.values.sTestManagerUrl = 'http://10.162.100.8/testmanager/'
+ parser.values.sBuildsServerName = 'vbox-st02.ru.oracle.com'
+ parser.values.sTestRsrcServerName = 'vbox-st02.ru.oracle.com'
+ parser.values.sTestRsrcServerShare = 'scratch/data/testrsrc'
+ parser.add_option("--spb", "--load-sbp-defaults", action="callback", callback=sbp_callback,
+ help="Load defaults for the sbp setup.")
+
+ (oOptions, args) = parser.parse_args()
+ # Check command line
+ if args != []:
+ parser.print_help();
+ return TBS_EXITCODE_SYNTAX;
+
+ if oOptions.sSystemUuid is not None:
+ uuid.UUID(oOptions.sSystemUuid);
+ if not oOptions.sTestManagerUrl.startswith('http://') \
+ and not oOptions.sTestManagerUrl.startswith('https://'):
+ print('Syntax error: Invalid test manager URL "%s"' % (oOptions.sTestManagerUrl,));
+ return TBS_EXITCODE_SYNTAX;
+
+ for sPrefix in ['sBuilds', 'sTestRsrc']:
+ sType = getattr(oOptions, sPrefix + 'ServerType');
+ if sType is None or not sType.strip():
+ setattr(oOptions, sPrefix + 'ServerType', None);
+ elif sType not in ['cifs', 'nfs']:
+ print('Syntax error: Invalid server type "%s"' % (sType,));
+ return TBS_EXITCODE_SYNTAX;
+
+
+ #
+ # Instantiate the testbox script and start dispatching work.
+ #
+ try:
+ oTestBoxScript = TestBoxScript(oOptions);
+ except TestBoxScriptException as oXcpt:
+ print('Error: %s' % (oXcpt,));
+ return TBS_EXITCODE_SYNTAX;
+ oTestBoxScript.dispatch();
+
+ # Not supposed to get here...
+ return TBS_EXITCODE_FAILURE;
+
+
+
+if __name__ == '__main__':
+ sys.exit(TestBoxScript.main());
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxtasks.py b/src/VBox/ValidationKit/testboxscript/testboxtasks.py
new file mode 100755
index 00000000..4d34cbd8
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxtasks.py
@@ -0,0 +1,944 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxtasks.py $
+
+"""
+TestBox Script - Async Tasks.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+from datetime import datetime
+import os
+import re
+import signal;
+import sys
+import subprocess
+import threading
+import time
+
+# Validation Kit imports.
+from common import constants
+from common import utils;
+from common import webutils;
+import testboxcommons
+
+# Figure where we are.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksTestScriptDir = os.path.dirname(os.path.abspath(__file__));
+
+
+
+class TestBoxBaseTask(object):
+ """
+ Asynchronous task employing a thread to do the actual work.
+ """
+
+ ## Time to wait for a task to terminate.
+ kcSecTerminateTimeout = 60
+
+ def __init__(self, oTestBoxScript, cSecTimeout, fnThreadProc):
+ self._oTestBoxScript = oTestBoxScript;
+ self._cSecTimeout = cSecTimeout;
+ self._tsSecStarted = utils.timestampSecond();
+ self.__oRLock = threading.RLock();
+ self._oCv = threading.Condition(self.__oRLock);
+ self._fRunning = True; # Protected by lock.
+ self._fShouldTerminate = False; # Protected by lock.
+
+ # Spawn the worker thread.
+ self._oThread = threading.Thread(target=fnThreadProc);
+ self._oThread.daemon = True;
+ self._oThread.start();
+
+ def _lock(self):
+ """ Take the CV lock. """
+ self._oCv.acquire();
+
+ def _unlock(self):
+ """ Release the CV lock. """
+ self._oCv.release();
+
+ def _complete(self):
+ """
+ Indicate that the task is complete, waking up the main thread.
+ Usually called at the end of the thread procedure.
+ """
+ self._lock();
+ self._fRunning = False;
+ self._oCv.notifyAll(); # pylint: disable=deprecated-method
+ self._unlock();
+
+ def isRunning(self):
+ """ Check if the task is still running. """
+ self._lock();
+ fRunning = self._fRunning;
+ self._unlock();
+ return fRunning;
+
+ def wait(self, cSecTimeout):
+ """ Wait for the task to complete. """
+ self._lock();
+ fRunning = self._fRunning;
+ if fRunning is True and cSecTimeout > 0:
+ self._oCv.wait(cSecTimeout)
+ self._unlock();
+ return fRunning;
+
+ def terminate(self, cSecTimeout = kcSecTerminateTimeout):
+ """ Terminate the task. """
+ self._lock();
+ self._fShouldTerminate = True;
+ self._unlock();
+
+ return self.wait(cSecTimeout);
+
+ def _shouldTerminate(self):
+ """
+ Returns True if we should terminate, False if not.
+ """
+ self._lock();
+ fShouldTerminate = self._fShouldTerminate is True;
+ self._unlock();
+ return fShouldTerminate;
+
+
+class TestBoxTestDriverTask(TestBoxBaseTask):
+ """
+ Base class for tasks involving test drivers.
+ """
+
+ ## When to flush the backlog of log messages.
+ kcchMaxBackLog = 32768;
+
+ ## The backlog sync time (seconds).
+ kcSecBackLogFlush = 30;
+
+ ## The timeout for the cleanup job (5 mins).
+ kcSecCleanupTimeout = 300;
+ ## The timeout to wait for the abort command before killing it.
+ kcSecAbortTimeout = 300;
+
+ ## The timeout to wait for the final output to be processed.
+ kcSecFinalOutputTimeout = 180;
+ ## The timeout to wait for the abort command output to be processed.
+ kcSecAbortCmdOutputTimeout = 30;
+ ## The timeout to wait for the terminate output to be processed.
+ kcSecTerminateOutputTimeout = 30;
+ ## The timeout to wait for the kill output to be processed.
+ kcSecKillOutputTimeout = 30;
+
+ ## The timeout for talking to the test manager.
+ ksecTestManagerTimeout = 60;
+
+
+ def __init__(self, oTestBoxScript, fnThreadProc, cSecTimeout, idResult, sScriptCmdLine):
+ """
+ Class instance init
+ """
+ # Init our instance data.
+ self._idResult = idResult;
+ self._sScriptCmdLine = sScriptCmdLine;
+ self._oChild = None;
+ self._oBackLogLock = threading.RLock();
+ self._oBackLogFlushLock = threading.RLock();
+ self._asBackLog = [];
+ self._cchBackLog = 0;
+ self._secTsBackLogFlush = utils.timestampSecond();
+
+ # Init super.
+ TestBoxBaseTask.__init__(self, oTestBoxScript, cSecTimeout, fnThreadProc);
+
+ def terminate(self, cSecTimeout = kcSecCleanupTimeout):
+ """ Reimplement with higher default timeout. """
+ return TestBoxBaseTask.terminate(self, cSecTimeout);
+
+ def _logFlush(self, oGivenConnection = None):
+ """
+ Flushes the log to the test manager.
+
+ No exceptions.
+ """
+ fRc = True;
+
+ with self._oBackLogFlushLock:
+ # Grab the current back log.
+ with self._oBackLogLock:
+ asBackLog = self._asBackLog;
+ self._asBackLog = [];
+ self._cchBackLog = 0;
+ self._secTsBackLogFlush = utils.timestampSecond();
+
+ # If there is anything to flush, flush it.
+ if asBackLog:
+ sBody = '';
+ for sLine in asBackLog:
+ sBody += sLine + '\n';
+
+ oConnection = None;
+ try:
+ if oGivenConnection is None:
+ oConnection = self._oTestBoxScript.openTestManagerConnection();
+ oConnection.postRequest(constants.tbreq.LOG_MAIN, {constants.tbreq.LOG_PARAM_BODY: sBody});
+ oConnection.close();
+ else:
+ oGivenConnection.postRequest(constants.tbreq.LOG_MAIN, {constants.tbreq.LOG_PARAM_BODY: sBody});
+ except Exception as oXcpt:
+ testboxcommons.log('_logFlush error: %s' % (oXcpt,));
+ if len(sBody) < self.kcchMaxBackLog * 4:
+ with self._oBackLogLock:
+ asBackLog.extend(self._asBackLog);
+ self._asBackLog = asBackLog;
+ # Don't restore _cchBackLog as there is no point in retrying immediately.
+ if oConnection is not None: # Be kind to apache.
+ try: oConnection.close();
+ except: pass;
+ fRc = False;
+
+ return fRc;
+
+ def flushLogOnConnection(self, oConnection):
+ """
+ Attempts to flush the logon the given connection.
+
+ No exceptions.
+ """
+ return self._logFlush(oConnection);
+
+ def _logInternal(self, sMessage, fPrefix = True, fFlushCheck = False):
+ """
+ Internal logging.
+ Won't flush the backlog, returns a flush indicator so the caller can
+ do it instead.
+ """
+ if fPrefix:
+ try:
+ oNow = datetime.utcnow();
+ sTs = '%02u:%02u:%02u.%06u ' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
+ except Exception as oXcpt:
+ sTs = 'oXcpt=%s ' % (oXcpt);
+ sFullMsg = sTs + sMessage;
+ else:
+ sFullMsg = sMessage;
+
+ with self._oBackLogLock:
+ self._asBackLog.append(sFullMsg);
+ cchBackLog = self._cchBackLog + len(sFullMsg) + 1;
+ self._cchBackLog = cchBackLog;
+ secTsBackLogFlush = self._secTsBackLogFlush;
+
+ testboxcommons.log(sFullMsg);
+ return fFlushCheck \
+ and ( cchBackLog >= self.kcchMaxBackLog \
+ or utils.timestampSecond() - secTsBackLogFlush >= self.kcSecBackLogFlush);
+
+ def _log(self, sMessage):
+ """
+ General logging function, will flush.
+ """
+ if self._logInternal(sMessage, fFlushCheck = True):
+ self._logFlush();
+ return True;
+
+ def _reportDone(self, sResult):
+ """
+ Report EXEC job done to the test manager.
+
+ sResult is a value from constants.result.
+ """
+ ## @todo optimize this to use one server connection.
+
+ #
+ # Log it.
+ #
+ assert sResult in constants.result.g_kasValidResults;
+ self._log('Done %s' % (sResult,));
+
+ #
+ # Report it.
+ #
+ fRc = True;
+ secStart = utils.timestampSecond();
+ while True:
+ self._logFlush(); ## @todo Combine this with EXEC_COMPLETED.
+ oConnection = None;
+ try:
+ oConnection = self._oTestBoxScript.openTestManagerConnection();
+ oConnection.postRequest(constants.tbreq.EXEC_COMPLETED, {constants.tbreq.EXEC_COMPLETED_PARAM_RESULT: sResult});
+ oConnection.close();
+ except Exception as oXcpt:
+ if utils.timestampSecond() - secStart < self.ksecTestManagerTimeout:
+ self._log('_reportDone exception (%s) - retrying...' % (oXcpt,));
+ time.sleep(2);
+ continue;
+ self._log('_reportDone error: %s' % (oXcpt,));
+ if oConnection is not None: # Be kind to apache.
+ try: oConnection.close();
+ except: pass;
+ fRc = False;
+ break;
+
+ #
+ # Mark the task as completed.
+ #
+ self._complete();
+ return fRc;
+
+ def _assembleArguments(self, sAction, fWithInterpreter = True):
+ """
+ Creates an argument array for subprocess.Popen, splitting the
+ sScriptCmdLine like bourne shell would.
+ fWithInterpreter is used (False) when checking that the script exists.
+
+ Returns None on bad input.
+ """
+
+ #
+ # This is a good place to export the test set id to the environment.
+ #
+ os.environ['TESTBOX_TEST_SET_ID'] = str(self._idResult);
+ cTimeoutLeft = utils.timestampSecond() - self._tsSecStarted;
+ cTimeoutLeft = 0 if cTimeoutLeft >= self._cSecTimeout else self._cSecTimeout - cTimeoutLeft;
+ os.environ['TESTBOX_TIMEOUT'] = str(cTimeoutLeft);
+ os.environ['TESTBOX_TIMEOUT_ABS'] = str(self._tsSecStarted + self._cSecTimeout);
+
+ #
+ # Do replacements and split the command line into arguments.
+ #
+ if self._sScriptCmdLine.find('@ACTION@') >= 0:
+ sCmdLine = self._sScriptCmdLine.replace('@ACTION@', sAction);
+ else:
+ sCmdLine = self._sScriptCmdLine + ' ' + sAction;
+ for sVar in [ 'TESTBOX_PATH_BUILDS', 'TESTBOX_PATH_RESOURCES', 'TESTBOX_PATH_SCRATCH', 'TESTBOX_PATH_SCRIPTS',
+ 'TESTBOX_PATH_UPLOAD', 'TESTBOX_UUID', 'TESTBOX_REPORTER', 'TESTBOX_ID', 'TESTBOX_TEST_SET_ID',
+ 'TESTBOX_TIMEOUT', 'TESTBOX_TIMEOUT_ABS' ]:
+ if sCmdLine.find('${' + sVar + '}') >= 0:
+ sCmdLine = sCmdLine.replace('${' + sVar + '}', os.environ[sVar]);
+
+ asArgs = utils.argsSplit(sCmdLine);
+
+ #
+ # Massage argv[0]:
+ # - Convert portable slashes ('/') to the flavor preferred by the
+ # OS we're currently running on.
+ # - Run python script thru the current python interpreter (important
+ # on systems that doesn't sport native hash-bang script execution).
+ #
+ asArgs[0] = asArgs[0].replace('/', os.path.sep);
+ if not os.path.isabs(asArgs[0]):
+ asArgs[0] = os.path.join(self._oTestBoxScript.getPathScripts(), asArgs[0]);
+
+ if asArgs[0].endswith('.py') and fWithInterpreter:
+ if sys.executable:
+ asArgs.insert(0, sys.executable);
+ else:
+ asArgs.insert(0, 'python');
+
+ return asArgs;
+
+ def _outputThreadProc(self, oChild, oStdOut, sAction):
+ """
+ Thread procedure for the thread that reads the output of the child
+ process. We use a dedicated thread for this purpose since non-blocking
+ I/O may be hard to keep portable according to hints around the web...
+ """
+ oThread = oChild.oOutputThread;
+ while not oThread.fPleaseQuit:
+ # Get a line.
+ try:
+ sLine = oStdOut.readline();
+ except Exception as oXcpt:
+ self._log('child (%s) pipe I/O error: %s' % (sAction, oXcpt,));
+ break;
+
+ # EOF?
+ if not sLine:
+ break;
+
+ # Strip trailing new line (DOS and UNIX).
+ if sLine.endswith("\r\n"):
+ sLine = sLine[0:-2];
+ elif sLine.endswith("\n"):
+ sLine = sLine[0:-1];
+
+ # Log it.
+ if self._logInternal(sLine, fPrefix = False, fFlushCheck = True):
+ self._logFlush();
+
+ # Close the stdout pipe in case we were told to get lost.
+ try:
+ oStdOut.close();
+ except Exception as oXcpt:
+ self._log('warning: Exception closing stdout pipe of "%s" child: %s' % (sAction, oXcpt,));
+
+ # This is a bit hacky, but try reap the child so it won't hang as
+ # defunkt during abort/timeout.
+ if oChild.poll() is None:
+ for _ in range(15):
+ time.sleep(0.2);
+ if oChild.poll() is not None:
+ break;
+
+ oChild = None;
+ return None;
+
+ def _spawnChild(self, sAction):
+ """
+ Spawns the child process, returning success indicator + child object.
+ """
+
+ # Argument list.
+ asArgs = self._assembleArguments(sAction)
+ if asArgs is None:
+ self._log('Malformed command line: "%s"' % (self._sScriptCmdLine,));
+ return (False, None);
+
+ # Spawn child.
+ try:
+ oChild = utils.processPopenSafe(asArgs,
+ shell = False,
+ bufsize = -1,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT,
+ cwd = self._oTestBoxScript.getPathSpill(),
+ universal_newlines = True,
+ close_fds = utils.getHostOs() != 'win',
+ preexec_fn = (None if utils.getHostOs() in ['win', 'os2']
+ else os.setsid)); # pylint: disable=no-member
+ except Exception as oXcpt:
+ self._log('Error creating child process %s: %s' % (asArgs, oXcpt));
+ return (False, None);
+
+ oChild.sTestBoxScriptAction = sAction;
+
+ # Start output thread, extending the child object to keep track of it.
+ oChild.oOutputThread = threading.Thread(target=self._outputThreadProc, args=(oChild, oChild.stdout, sAction))
+ oChild.oOutputThread.daemon = True;
+ oChild.oOutputThread.fPleaseQuit = False; # Our extension.
+ oChild.oOutputThread.start();
+
+ return (True, oChild);
+
+ def _monitorChild(self, cSecTimeout, fTryKillCommand = True, oChild = None):
+ """
+ Monitors the child process. If the child executes longer that
+ cSecTimeout allows, we'll terminate it.
+ Returns Success indicator and constants.result value.
+ """
+
+ if oChild is None:
+ oChild = self._oChild;
+
+ iProcGroup = oChild.pid;
+ if utils.getHostOs() in ['win', 'os2'] or iProcGroup <= 0:
+ iProcGroup = -2;
+
+ #
+ # Do timeout processing and check the health of the child.
+ #
+ sResult = constants.result.PASSED;
+ seStarted = utils.timestampSecond();
+ while True:
+ # Check status.
+ iRc = oChild.poll();
+ if iRc is not None:
+ self._log('Child doing "%s" completed with exit code %d' % (oChild.sTestBoxScriptAction, iRc));
+ oChild.oOutputThread.join(self.kcSecFinalOutputTimeout);
+
+ if oChild is self._oChild:
+ self._oChild = None;
+
+ if iRc == constants.rtexitcode.SUCCESS:
+ return (True, constants.result.PASSED);
+ if iRc == constants.rtexitcode.SKIPPED:
+ return (True, constants.result.SKIPPED);
+ if iRc == constants.rtexitcode.BAD_TESTBOX:
+ return (False, constants.result.BAD_TESTBOX);
+ return (False, constants.result.FAILED);
+
+ # Check for abort first, since that has less of a stigma.
+ if self._shouldTerminate() is True:
+ sResult = constants.result.ABORTED;
+ break;
+
+ # Check timeout.
+ cSecElapsed = utils.timestampSecond() - seStarted;
+ if cSecElapsed > cSecTimeout:
+ self._log('Timeout: %u secs (limit %u secs)' % (cSecElapsed, cSecTimeout));
+ sResult = constants.result.TIMED_OUT;
+ break;
+
+ # Wait.
+ cSecLeft = cSecTimeout - cSecElapsed;
+ oChild.oOutputThread.join(15 if cSecLeft > 15 else (cSecLeft + 1));
+
+ #
+ # If the child is still alive, try use the abort command to stop it
+ # very gently. This let's the testdriver clean up daemon processes
+ # and such that our code below won't catch.
+ #
+ if fTryKillCommand and oChild.poll() is None:
+ self._log('Attempting to abort child...');
+ (fRc2, oAbortChild) = self._spawnChild('abort');
+ if oAbortChild is not None and fRc2 is True:
+ self._monitorChild(self.kcSecAbortTimeout, False, oAbortChild);
+ oAbortChild = None;
+
+ #
+ # If the child is still alive, try the polite way.
+ #
+ if oChild.poll() is None:
+ self._log('Attempting to terminate child doing "%s"...' % (oChild.sTestBoxScriptAction,));
+
+ if iProcGroup > 0:
+ try:
+ os.killpg(iProcGroup, signal.SIGTERM); # pylint: disable=no-member
+ except Exception as oXcpt:
+ self._log('killpg() failed: %s' % (oXcpt,));
+
+ try:
+ self._oChild.terminate();
+ oChild.oOutputThread.join(self.kcSecTerminateOutputTimeout);
+ except Exception as oXcpt:
+ self._log('terminate() failed: %s' % (oXcpt,));
+
+ #
+ # If the child doesn't respond to polite, kill it. Always do a killpg
+ # should there be any processes left in the group.
+ #
+ if iProcGroup > 0:
+ try:
+ os.killpg(iProcGroup, signal.SIGKILL); # pylint: disable=no-member
+ except Exception as oXcpt:
+ self._log('killpg() failed: %s' % (oXcpt,));
+
+ if oChild.poll() is None:
+ self._log('Attemting to kill child doing "%s"...' % (oChild.sTestBoxScriptAction,));
+ try:
+ self._oChild.kill();
+ oChild.oOutputThread.join(self.kcSecKillOutputTimeout);
+ except Exception as oXcpt:
+ self._log('kill() failed: %s' % (oXcpt,));
+
+ #
+ # Give the whole mess a couple of more seconds to respond in case the
+ # output thread exitted prematurely for some weird reason.
+ #
+ if oChild.poll() is None:
+ time.sleep(2);
+ time.sleep(2);
+ time.sleep(2);
+
+ iRc = oChild.poll();
+ if iRc is not None:
+ self._log('Child doing "%s" aborted with exit code %d' % (oChild.sTestBoxScriptAction, iRc));
+ else:
+ self._log('Child doing "%s" is still running, giving up...' % (oChild.sTestBoxScriptAction,));
+ ## @todo in this case we should probably try reboot the testbox...
+ oChild.oOutputThread.fPleaseQuit = True;
+
+ if oChild is self._oChild:
+ self._oChild = None;
+ return (False, sResult);
+
+ def _terminateChild(self):
+ """
+ Terminates the child forcefully.
+ """
+ if self._oChild is not None:
+ pass;
+
+ def _cleanupAfter(self):
+ """
+ Cleans up after a test failure. (On success, cleanup is implicit.)
+ """
+ assert self._oChild is None;
+
+ #
+ # Tell the script to clean up.
+ #
+ if self._sScriptCmdLine: # can be empty if cleanup crashed.
+ (fRc, self._oChild) = self._spawnChild('cleanup-after');
+ if fRc is True:
+ (fRc, _) = self._monitorChild(self.kcSecCleanupTimeout, False);
+ self._terminateChild();
+ else:
+ fRc = False;
+
+ #
+ # Wipe the stuff clean.
+ #
+ fRc2 = self._oTestBoxScript.reinitScratch(fnLog = self._log, cRetries = 6);
+
+ return fRc and fRc2;
+
+
+
+class TestBoxCleanupTask(TestBoxTestDriverTask):
+ """
+ Special asynchronous task for cleaning up a stale test when starting the
+ testbox script. It's assumed that the reason for the stale test lies in
+ it causing a panic, reboot, or similar, so we'll also try collect some
+ info about recent system crashes and reboots.
+ """
+
+ def __init__(self, oTestBoxScript):
+ # Read the old state, throwing a fit if it's invalid.
+ sScriptState = oTestBoxScript.getPathState();
+ sScriptCmdLine = self._readStateFile(os.path.join(sScriptState, 'script-cmdline.txt'));
+ sResultId = self._readStateFile(os.path.join(sScriptState, 'result-id.txt'));
+ try:
+ idResult = int(sResultId);
+ if idResult <= 0 or idResult >= 0x7fffffff:
+ raise Exception('');
+ except:
+ raise Exception('Invalid id value "%s" found in %s' % (sResultId, os.path.join(sScriptState, 'result-id.txt')));
+
+ sTestBoxId = self._readStateFile(os.path.join(sScriptState, 'testbox-id.txt'));
+ try:
+ self.idTestBox = int(sTestBoxId);
+ if self.idTestBox <= 0 or self.idTestBox >= 0x7fffffff:
+ raise Exception('');
+ except:
+ raise Exception('Invalid id value "%s" found in %s' % (sTestBoxId, os.path.join(sScriptState, 'testbox-id.txt')));
+ self.sTestBoxName = self._readStateFile(os.path.join(sScriptState, 'testbox-name.txt'));
+
+ # Init super.
+ TestBoxTestDriverTask.__init__(self, oTestBoxScript, self._threadProc, self.kcSecCleanupTimeout,
+ idResult, sScriptCmdLine);
+
+ @staticmethod
+ def _readStateFile(sPath):
+ """
+ Reads a state file, returning a string on success and otherwise raising
+ an exception.
+ """
+ try:
+ with open(sPath, "rb") as oFile:
+ sStr = oFile.read();
+ sStr = sStr.decode('utf-8');
+ return sStr.strip();
+ except Exception as oXcpt:
+ raise Exception('Failed to read "%s": %s' % (sPath, oXcpt));
+
+ def _threadProc(self):
+ """
+ Perform the actual clean up on script startup.
+ """
+
+ #
+ # First make sure we won't repeat this exercise should it turn out to
+ # trigger another reboot/panic/whatever.
+ #
+ sScriptCmdLine = os.path.join(self._oTestBoxScript.getPathState(), 'script-cmdline.txt');
+ try:
+ os.remove(sScriptCmdLine);
+ open(sScriptCmdLine, 'wb').close(); # pylint: disable=consider-using-with
+ except Exception as oXcpt:
+ self._log('Error truncating "%s": %s' % (sScriptCmdLine, oXcpt));
+
+ #
+ # Report the incident.
+ #
+ self._log('Seems we rebooted!');
+ self._log('script-cmdline="%s"' % (self._sScriptCmdLine));
+ self._log('result-id=%d' % (self._idResult));
+ self._log('testbox-id=%d' % (self.idTestBox));
+ self._log('testbox-name=%s' % (self.sTestBoxName));
+ self._logFlush();
+
+ # System specific info.
+ sOs = utils.getHostOs();
+ if sOs == 'darwin':
+ self._log('NVRAM Panic Info:\n%s\n' % (self.darwinGetPanicInfo(),));
+
+ self._logFlush();
+ ## @todo Add some special command for reporting this situation so we get something
+ # useful in the event log.
+
+ #
+ # Do the cleaning up.
+ #
+ self._cleanupAfter();
+
+ self._reportDone(constants.result.REBOOTED);
+ return False;
+
+ def darwinGetPanicInfo(self):
+ """
+ Returns a string with the aapl,panic-info content.
+ """
+ # Retriev the info.
+ try:
+ sRawInfo = utils.processOutputChecked(['nvram', 'aapl,panic-info']);
+ except Exception as oXcpt:
+ return 'exception running nvram: %s' % (oXcpt,);
+
+ # Decode (%xx) and decompact it (7-bit -> 8-bit).
+ ahDigits = \
+ {
+ '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
+ '8': 8, '9': 9, 'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15,
+ };
+ sInfo = '';
+ off = len('aapl,panic-info') + 1;
+ iBit = 0;
+ bLow = 0;
+
+ while off < len(sRawInfo):
+ # isprint is used to determine whether to %xx or %c it, so we have to
+ # be a little careful before assuming % sequences are hex bytes.
+ if sRawInfo[off] == '%' \
+ and off + 3 <= len(sRawInfo) \
+ and sRawInfo[off + 1] in ahDigits \
+ and sRawInfo[off + 2] in ahDigits:
+ bCur = ahDigits[sRawInfo[off + 1]] * 0x10 + ahDigits[sRawInfo[off + 2]];
+ off += 3;
+ else:
+ bCur = ord(sRawInfo[off]);
+ off += 1;
+
+ sInfo += chr(((bCur & (0x7f >> iBit)) << iBit) | bLow);
+ bLow = bCur >> (7 - iBit);
+
+ if iBit < 6:
+ iBit += 1;
+ else:
+ # Final bit in sequence.
+ sInfo += chr(bLow);
+ bLow = 0;
+ iBit = 0;
+
+ # Expand shorthand.
+ sInfo = sInfo.replace('@', 'com.apple.');
+ sInfo = sInfo.replace('>', 'com.apple.driver.');
+ sInfo = sInfo.replace('|', 'com.apple.iokit.');
+ sInfo = sInfo.replace('$', 'com.apple.security.');
+ sInfo = sInfo.replace('!A', 'Apple');
+ sInfo = sInfo.replace('!a', 'Action');
+ sInfo = sInfo.replace('!B', 'Bluetooth');
+ sInfo = sInfo.replace('!C', 'Controller');
+ sInfo = sInfo.replace('!F', 'Family');
+ sInfo = sInfo.replace('!I', 'Intel');
+ sInfo = sInfo.replace('!U', 'AppleUSB');
+ sInfo = sInfo.replace('!P', 'Profile');
+
+ # Done.
+ return sInfo
+
+
+class TestBoxExecTask(TestBoxTestDriverTask):
+ """
+ Implementation of a asynchronous EXEC task.
+
+ This uses a thread for doing the actual work, i.e. starting and monitoring
+ the child process, processing its output, and more.
+ """
+
+ def __init__(self, oTestBoxScript, idResult, sScriptZips, sScriptCmdLine, cSecTimeout):
+ """
+ Class instance init
+ """
+ # Init our instance data.
+ self._sScriptZips = sScriptZips;
+
+ # Init super.
+ TestBoxTestDriverTask.__init__(self, oTestBoxScript, self._threadProc, cSecTimeout, idResult, sScriptCmdLine);
+
+ @staticmethod
+ def _writeStateFile(sPath, sContent):
+ """
+ Writes a state file, raising an exception on failure.
+ """
+ try:
+ with open(sPath, "wb") as oFile:
+ oFile.write(sContent.encode('utf-8'));
+ oFile.flush();
+ try: os.fsync(oFile.fileno());
+ except: pass;
+ except Exception as oXcpt:
+ raise Exception('Failed to write "%s": %s' % (sPath, oXcpt));
+ return True;
+
+ @staticmethod
+ def _environTxtContent():
+ """
+ Collects environment variables and values for the environ.txt stat file
+ (for external monitoring tool).
+ """
+ sText = '';
+ for sVar in [ 'TESTBOX_PATH_BUILDS', 'TESTBOX_PATH_RESOURCES', 'TESTBOX_PATH_SCRATCH', 'TESTBOX_PATH_SCRIPTS',
+ 'TESTBOX_PATH_UPLOAD', 'TESTBOX_HAS_HW_VIRT', 'TESTBOX_HAS_NESTED_PAGING', 'TESTBOX_HAS_IOMMU',
+ 'TESTBOX_SCRIPT_REV', 'TESTBOX_CPU_COUNT', 'TESTBOX_MEM_SIZE', 'TESTBOX_SCRATCH_SIZE',
+ 'TESTBOX_WITH_RAW_MODE', 'TESTBOX_WITH_RAW_MODE', 'TESTBOX_MANAGER_URL', 'TESTBOX_UUID',
+ 'TESTBOX_REPORTER', 'TESTBOX_NAME', 'TESTBOX_ID', 'TESTBOX_TEST_SET_ID',
+ 'TESTBOX_TIMEOUT', 'TESTBOX_TIMEOUT_ABS', ]:
+ sValue = os.environ.get(sVar);
+ if sValue:
+ sText += sVar + '=' + sValue + '\n';
+ return sText;
+
+ def _saveState(self):
+ """
+ Saves the task state on disk so we can launch a TestBoxCleanupTask job
+ if the test should cause system panic or similar.
+
+ Note! May later be extended to support tests that reboots the host.
+ """
+ sScriptState = self._oTestBoxScript.getPathState();
+ try:
+ self._writeStateFile(os.path.join(sScriptState, 'script-cmdline.txt'), self._sScriptCmdLine);
+ self._writeStateFile(os.path.join(sScriptState, 'result-id.txt'), str(self._idResult));
+ self._writeStateFile(os.path.join(sScriptState, 'testbox-id.txt'), str(self._oTestBoxScript.getTestBoxId()));
+ self._writeStateFile(os.path.join(sScriptState, 'testbox-name.txt'), self._oTestBoxScript.getTestBoxName());
+ self._writeStateFile(os.path.join(sScriptState, 'environ.txt'), self._environTxtContent());
+ except Exception as oXcpt:
+ self._log('Failed to write state: %s' % (oXcpt,));
+ return False;
+ return True;
+
+ def _downloadAndUnpackScriptZips(self):
+ """
+ Downloads/copies the script ZIPs into TESTBOX_SCRIPT and unzips them to
+ the same directory.
+
+ Raises no exceptions, returns log + success indicator instead.
+ """
+ sPathScript = self._oTestBoxScript.getPathScripts();
+ asArchives = self._sScriptZips.split(',');
+ for sArchive in asArchives:
+ sArchive = sArchive.strip();
+ if not sArchive:
+ continue;
+
+ # Figure the destination name (in scripts).
+ sDstFile = webutils.getFilename(sArchive);
+ if not sDstFile \
+ or re.search('[^a-zA-Z0-9 !#$%&\'()@^_`{}~.-]', sDstFile) is not None: # FAT charset sans 128-255 + '.'.
+ self._log('Malformed script zip filename: %s' % (sArchive,));
+ return False;
+ sDstFile = os.path.join(sPathScript, sDstFile);
+
+ # Do the work.
+ if webutils.downloadFile(sArchive, sDstFile, self._oTestBoxScript.getPathBuilds(), self._log, self._log) is not True:
+ return False;
+ asFiles = utils.unpackFile(sDstFile, sPathScript, self._log, self._log);
+ if asFiles is None:
+ return False;
+
+ # Since zip files doesn't always include mode masks, set the X bit
+ # of all of them so we can execute binaries and hash-bang scripts.
+ for sFile in asFiles:
+ utils.chmodPlusX(sFile);
+
+ return True;
+
+ def _threadProc(self):
+ """
+ Do the work of an EXEC command.
+ """
+
+ sResult = constants.result.PASSED;
+
+ #
+ # Start by preparing the scratch directories.
+ #
+ # Note! Failures at this stage are not treated as real errors since
+ # they may be caused by the previous test and other circumstances
+ # so we don't want to go fail a build because of this.
+ #
+ fRc = self._oTestBoxScript.reinitScratch(self._logInternal);
+ fNeedCleanUp = fRc;
+ if fRc is True:
+ fRc = self._downloadAndUnpackScriptZips();
+ testboxcommons.log2('_threadProc: _downloadAndUnpackScriptZips -> %s' % (fRc,));
+ if fRc is not True:
+ sResult = constants.result.BAD_TESTBOX;
+
+ #
+ # Make sure the script exists.
+ #
+ if fRc is True:
+ sScript = self._assembleArguments('none', fWithInterpreter = False)[0];
+ if not os.path.exists(sScript):
+ self._log('The test driver script "%s" cannot be found.' % (sScript,));
+ sDir = sScript;
+ while len(sDir) > 3:
+ sDir = os.path.dirname(sDir);
+ if os.path.exists(sDir):
+ self._log('First existing parent directory is "%s".' % (sDir,));
+ break;
+ fRc = False;
+
+ if fRc is True:
+ #
+ # Start testdriver script.
+ #
+ fRc = self._saveState();
+ if fRc:
+ (fRc, self._oChild) = self._spawnChild('all');
+ testboxcommons.log2('_threadProc: _spawnChild -> %s, %s' % (fRc, self._oChild));
+ if fRc:
+ (fRc, sResult) = self._monitorChild(self._cSecTimeout);
+ testboxcommons.log2('_threadProc: _monitorChild -> %s' % (fRc,));
+
+ # If the run failed, do explicit cleanup unless its a BAD_TESTBOX, since BAD_TESTBOX is
+ # intended for pre-cleanup problems caused by previous test failures. Do a cleanup on
+ # a BAD_TESTBOX could easily trigger an uninstallation error and change status to FAILED.
+ if fRc is not True:
+ if sResult != constants.result.BAD_TESTBOX:
+ testboxcommons.log2('_threadProc: explicit cleanups...');
+ self._terminateChild();
+ self._cleanupAfter();
+ fNeedCleanUp = False;
+ assert self._oChild is None;
+
+ #
+ # Clean up scratch.
+ #
+ if fNeedCleanUp:
+ if self._oTestBoxScript.reinitScratch(self._logInternal, cRetries = 6) is not True:
+ self._log('post run reinitScratch failed.');
+ fRc = False;
+
+ #
+ # Report status and everything back to the test manager.
+ #
+ if fRc is False and sResult == constants.result.PASSED:
+ sResult = constants.result.FAILED;
+ self._reportDone(sResult);
+ return fRc;
+
diff --git a/src/VBox/ValidationKit/testboxscript/testboxupgrade.py b/src/VBox/ValidationKit/testboxscript/testboxupgrade.py
new file mode 100755
index 00000000..765bff3c
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/testboxupgrade.py
@@ -0,0 +1,339 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxupgrade.py $
+
+"""
+TestBox Script - Upgrade from local file ZIP.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import os
+import shutil
+import sys
+import subprocess
+import threading
+import time
+import uuid;
+import zipfile
+
+# Validation Kit imports.
+from common import utils;
+import testboxcommons
+from testboxscript import TBS_EXITCODE_SYNTAX;
+
+# Figure where we are.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksTestScriptDir = os.path.dirname(os.path.abspath(__file__));
+g_ksValidationKitDir = os.path.dirname(g_ksTestScriptDir);
+
+
+def _doUpgradeThreadProc(oStdOut, asBuf):
+ """Thread procedure for the upgrade test drive."""
+ asBuf.append(oStdOut.read());
+ return True;
+
+
+def _doUpgradeCheckZip(oZip):
+ """
+ Check that the essential files are there.
+ Returns list of members on success, None on failure.
+ """
+ asMembers = oZip.namelist();
+ if ('testboxscript/testboxscript/testboxscript.py' not in asMembers) \
+ or ('testboxscript/testboxscript/testboxscript_real.py' not in asMembers):
+ testboxcommons.log('Missing one or both testboxscripts (members: %s)' % (asMembers,));
+ return None;
+
+ for sMember in asMembers:
+ if not sMember.startswith('testboxscript/'):
+ testboxcommons.log('zip file contains member outside testboxscript/: "%s"' % (sMember,));
+ return None;
+ if sMember.find('/../') > 0 or sMember.endswith('/..'):
+ testboxcommons.log('zip file contains member with escape sequence: "%s"' % (sMember,));
+ return None;
+
+ return asMembers;
+
+def _doUpgradeUnzipAndCheck(oZip, sUpgradeDir, asMembers):
+ """
+ Unzips the files into sUpdateDir, does chmod(755) on all files and
+ checks that there are no symlinks or special files.
+ Returns True/False.
+ """
+ #
+ # Extract the files.
+ #
+ if os.path.exists(sUpgradeDir):
+ shutil.rmtree(sUpgradeDir);
+ for sMember in asMembers:
+ if sMember.endswith('/'):
+ os.makedirs(os.path.join(sUpgradeDir, sMember.replace('/', os.path.sep)), 0o775);
+ else:
+ oZip.extract(sMember, sUpgradeDir);
+
+ #
+ # Make all files executable and make sure only owner can write to them.
+ # While at it, also check that there are only files and directory, no
+ # symbolic links or special stuff.
+ #
+ for sMember in asMembers:
+ sFull = os.path.join(sUpgradeDir, sMember);
+ if sMember.endswith('/'):
+ if not os.path.isdir(sFull):
+ testboxcommons.log('Not directory: "%s"' % sFull);
+ return False;
+ else:
+ if not os.path.isfile(sFull):
+ testboxcommons.log('Not regular file: "%s"' % sFull);
+ return False;
+ try:
+ os.chmod(sFull, 0o755);
+ except Exception as oXcpt:
+ testboxcommons.log('warning chmod error on %s: %s' % (sFull, oXcpt));
+ return True;
+
+def _doUpgradeTestRun(sUpgradeDir):
+ """
+ Do a testrun of the new script, to make sure it doesn't fail with
+ to run in any way because of old python, missing import or generally
+ busted upgrade.
+ Returns True/False.
+ """
+ asArgs = [os.path.join(sUpgradeDir, 'testboxscript', 'testboxscript', 'testboxscript.py'), '--version' ];
+ testboxcommons.log('Testing the new testbox script (%s)...' % (asArgs[0],));
+ if sys.executable:
+ asArgs.insert(0, sys.executable);
+ oChild = subprocess.Popen(asArgs, shell = False, # pylint: disable=consider-using-with
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
+
+ asBuf = []
+ oThread = threading.Thread(target=_doUpgradeThreadProc, args=(oChild.stdout, asBuf));
+ oThread.daemon = True;
+ oThread.start();
+ oThread.join(30);
+
+ # Give child up to 5 seconds to terminate after producing output.
+ if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
+ oChild.wait(5); # pylint: disable=too-many-function-args
+ else:
+ for _ in range(50):
+ iStatus = oChild.poll();
+ if iStatus is None:
+ break;
+ time.sleep(0.1);
+ iStatus = oChild.poll();
+ if iStatus is None:
+ testboxcommons.log('Checking the new testboxscript timed out.');
+ oChild.terminate();
+ oThread.join(5);
+ return False;
+ if iStatus is not TBS_EXITCODE_SYNTAX:
+ testboxcommons.log('The new testboxscript returned %d instead of %d during check.' \
+ % (iStatus, TBS_EXITCODE_SYNTAX));
+ return False;
+
+ sOutput = b''.join(asBuf).decode('utf-8');
+ sOutput = sOutput.strip();
+ try:
+ iNewVersion = int(sOutput);
+ except:
+ testboxcommons.log('The new testboxscript returned an unparseable version string: "%s"!' % (sOutput,));
+ return False;
+ testboxcommons.log('New script version: %s' % (iNewVersion,));
+ return True;
+
+def _doUpgradeApply(sUpgradeDir, asMembers):
+ """
+ # Apply the directories and files from the upgrade.
+ returns True/False/Exception.
+ """
+
+ #
+ # Create directories first since that's least intrusive.
+ #
+ for sMember in asMembers:
+ if sMember[-1] == '/':
+ sMember = sMember[len('testboxscript/'):];
+ if sMember != '':
+ sFull = os.path.join(g_ksValidationKitDir, sMember);
+ if not os.path.isdir(sFull):
+ os.makedirs(sFull, 0o755);
+
+ #
+ # Move the files into place.
+ #
+ fRc = True;
+ asOldFiles = [];
+ for sMember in asMembers:
+ if sMember[-1] != '/':
+ sSrc = os.path.join(sUpgradeDir, sMember);
+ sDst = os.path.join(g_ksValidationKitDir, sMember[len('testboxscript/'):]);
+
+ # Move the old file out of the way first.
+ sDstRm = None;
+ if os.path.exists(sDst):
+ testboxcommons.log2('Info: Installing "%s"' % (sDst,));
+ sDstRm = '%s-delete-me-%s' % (sDst, uuid.uuid4(),);
+ try:
+ os.rename(sDst, sDstRm);
+ except Exception as oXcpt:
+ testboxcommons.log('Error: failed to rename (old) "%s" to "%s": %s' % (sDst, sDstRm, oXcpt));
+ try:
+ shutil.copy(sDst, sDstRm);
+ except Exception as oXcpt:
+ testboxcommons.log('Error: failed to copy (old) "%s" to "%s": %s' % (sDst, sDstRm, oXcpt));
+ break;
+ try:
+ os.unlink(sDst);
+ except Exception as oXcpt:
+ testboxcommons.log('Error: failed to unlink (old) "%s": %s' % (sDst, oXcpt));
+ break;
+
+ # Move/copy the new one into place.
+ testboxcommons.log2('Info: Installing "%s"' % (sDst,));
+ try:
+ os.rename(sSrc, sDst);
+ except Exception as oXcpt:
+ testboxcommons.log('Warning: failed to rename (new) "%s" to "%s": %s' % (sSrc, sDst, oXcpt));
+ try:
+ shutil.copy(sSrc, sDst);
+ except:
+ testboxcommons.log('Error: failed to copy (new) "%s" to "%s": %s' % (sSrc, sDst, oXcpt));
+ fRc = False;
+ break;
+
+ #
+ # Roll back on failure.
+ #
+ if fRc is not True:
+ testboxcommons.log('Attempting to roll back old files...');
+ for sDstRm in asOldFiles:
+ sDst = sDstRm[:sDstRm.rfind('-delete-me')];
+ testboxcommons.log2('Info: Rolling back "%s" (%s)' % (sDst, os.path.basename(sDstRm)));
+ try:
+ shutil.move(sDstRm, sDst);
+ except:
+ testboxcommons.log('Error: failed to rollback "%s" onto "%s": %s' % (sDstRm, sDst, oXcpt));
+ return False;
+ return True;
+
+def _doUpgradeRemoveOldStuff(sUpgradeDir, asMembers):
+ """
+ Clean up all obsolete files and directories.
+ Returns True (shouldn't fail or raise any exceptions).
+ """
+
+ try:
+ shutil.rmtree(sUpgradeDir, ignore_errors = True);
+ except:
+ pass;
+
+ asKnownFiles = [];
+ asKnownDirs = [];
+ for sMember in asMembers:
+ sMember = sMember[len('testboxscript/'):];
+ if sMember == '':
+ continue;
+ if sMember[-1] == '/':
+ asKnownDirs.append(os.path.normpath(os.path.join(g_ksValidationKitDir, sMember[:-1])));
+ else:
+ asKnownFiles.append(os.path.normpath(os.path.join(g_ksValidationKitDir, sMember)));
+
+ for sDirPath, asDirs, asFiles in os.walk(g_ksValidationKitDir, topdown=False):
+ for sDir in asDirs:
+ sFull = os.path.normpath(os.path.join(sDirPath, sDir));
+ if sFull not in asKnownDirs:
+ testboxcommons.log2('Info: Removing obsolete directory "%s"' % (sFull,));
+ try:
+ os.rmdir(sFull);
+ except Exception as oXcpt:
+ testboxcommons.log('Warning: failed to rmdir obsolete dir "%s": %s' % (sFull, oXcpt));
+
+ for sFile in asFiles:
+ sFull = os.path.normpath(os.path.join(sDirPath, sFile));
+ if sFull not in asKnownFiles:
+ testboxcommons.log2('Info: Removing obsolete file "%s"' % (sFull,));
+ try:
+ os.unlink(sFull);
+ except Exception as oXcpt:
+ testboxcommons.log('Warning: failed to unlink obsolete file "%s": %s' % (sFull, oXcpt));
+ return True;
+
+def upgradeFromZip(sZipFile):
+ """
+ Upgrade the testboxscript install using the specified zip file.
+ Returns True/False.
+ """
+
+ # A little precaution.
+ if utils.isRunningFromCheckout():
+ testboxcommons.log('Use "svn up" to "upgrade" your source tree!');
+ return False;
+
+ #
+ # Prepare.
+ #
+ # Note! Don't bother cleaning up files and dirs in the error paths,
+ # they'll be restricted to the one zip and the one upgrade dir.
+ # We'll remove them next time we upgrade.
+ #
+ oZip = zipfile.ZipFile(sZipFile, 'r'); # No 'with' support in 2.6 class: pylint: disable=consider-using-with
+ asMembers = _doUpgradeCheckZip(oZip);
+ if asMembers is None:
+ return False;
+
+ sUpgradeDir = os.path.join(g_ksTestScriptDir, 'upgrade');
+ testboxcommons.log('Unzipping "%s" to "%s"...' % (sZipFile, sUpgradeDir));
+ if _doUpgradeUnzipAndCheck(oZip, sUpgradeDir, asMembers) is not True:
+ return False;
+ oZip.close();
+
+ if _doUpgradeTestRun(sUpgradeDir) is not True:
+ return False;
+
+ #
+ # Execute.
+ #
+ if _doUpgradeApply(sUpgradeDir, asMembers) is not True:
+ return False;
+ _doUpgradeRemoveOldStuff(sUpgradeDir, asMembers);
+ return True;
+
+
+# For testing purposes.
+if __name__ == '__main__':
+ sys.exit(upgradeFromZip(sys.argv[1]));
+
diff --git a/src/VBox/ValidationKit/testboxscript/win/autoexec-testbox.cmd b/src/VBox/ValidationKit/testboxscript/win/autoexec-testbox.cmd
new file mode 100644
index 00000000..3e10fbbb
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/win/autoexec-testbox.cmd
@@ -0,0 +1,72 @@
+@echo off
+REM $Id: autoexec-testbox.cmd $
+REM REM @file
+REM VirtualBox Validation Kit - testbox script, automatic execution wrapper.
+REM
+
+REM
+REM Copyright (C) 2006-2023 Oracle and/or its affiliates.
+REM
+REM This file is part of VirtualBox base platform packages, as
+REM available from https://www.virtualbox.org.
+REM
+REM This program is free software; you can redistribute it and/or
+REM modify it under the terms of the GNU General Public License
+REM as published by the Free Software Foundation, in version 3 of the
+REM License.
+REM
+REM This program is distributed in the hope that it will be useful, but
+REM WITHOUT ANY WARRANTY; without even the implied warranty of
+REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+REM General Public License for more details.
+REM
+REM You should have received a copy of the GNU General Public License
+REM along with this program; if not, see <https://www.gnu.org/licenses>.
+REM
+REM The contents of this file may alternatively be used under the terms
+REM of the Common Development and Distribution License Version 1.0
+REM (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+REM in the VirtualBox distribution, in which case the provisions of the
+REM CDDL are applicable instead of those of the GPL.
+REM
+REM You may elect to license modified versions of this file under the
+REM terms and conditions of either the GPL or the CDDL or both.
+REM
+REM SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+REM
+
+@echo "$Id: autoexec-testbox.cmd $"
+@echo on
+setlocal EnableExtensions
+set exe=python.exe
+for /f %%x in ('tasklist /NH /FI "IMAGENAME eq %exe%"') do if %%x == %exe% goto end
+
+if exist %SystemRoot%\System32\aim_ll.exe (
+ set RAMEXE=aim
+) else if exist %SystemRoot%\System32\imdisk.exe (
+ set RAMEXE=imdisk
+) else goto defaulttest
+
+REM Take presence of imdisk.exe or aim_ll.exe as order to test in ramdisk.
+set RAMDRIVE=D:
+if exist %RAMDRIVE%\TEMP goto skip
+if %RAMEXE% == aim (
+ aim_ll -a -t vm -s 16G -m %RAMDRIVE% -p "/fs:ntfs /q /y"
+) else if %RAMEXE% == imdisk (
+ imdisk -a -s 16GB -m %RAMDRIVE% -p "/fs:ntfs /q /y" -o "awe"
+) else goto defaulttest
+:skip
+
+set VBOX_INSTALL_PATH=%RAMDRIVE%\VBoxInstall
+set TMP=%RAMDRIVE%\TEMP
+set TEMP=%TMP%
+
+mkdir %VBOX_INSTALL_PATH%
+mkdir %TMP%
+
+set TESTBOXSCRIPT_OPTS=--scratch-root=%RAMDRIVE%\testbox
+
+:defaulttest
+%SystemDrive%\Python27\python.exe %SystemDrive%\testboxscript\testboxscript\testboxscript.py --testrsrc-server-type=cifs --builds-server-type=cifs %TESTBOXSCRIPT_OPTS%
+pause
+:end
diff --git a/src/VBox/ValidationKit/testboxscript/win/fix_stale_refs.py b/src/VBox/ValidationKit/testboxscript/win/fix_stale_refs.py
new file mode 100755
index 00000000..4800c99d
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/win/fix_stale_refs.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# $Id: fix_stale_refs.py $
+
+"""
+This module must be used interactively!
+Use with caution as it will delete some values from the regisry!
+
+It tries to locate client references to products that no longer exist.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+from _winreg import HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS
+from _winreg import OpenKey, CloseKey, EnumKey, QueryInfoKey, EnumValue, DeleteValue, QueryValueEx
+from distutils.util import strtobool
+
+def reverse_bytes(hex_string):
+ """
+ This function reverses the order of bytes in the provided string.
+ Each byte is represented by two characters which are reversed as well.
+ """
+ #print 'reverse_bytes(' + hex_string + ')'
+ chars = len(hex_string)
+ if chars > 2:
+ return reverse_bytes(hex_string[chars/2:]) + reverse_bytes(hex_string[:chars/2])
+ else:
+ return hex_string[1] + hex_string[0]
+
+def transpose_guid(guid):
+ """
+ Windows Installer uses different way to present GUID string. This function converts GUID
+ from installer's presentation to more conventional form.
+ """
+ return '{' + reverse_bytes(guid[0:8]) + '-' + reverse_bytes(guid[8:12]) + \
+ '-' + reverse_bytes(guid[12:16]) + \
+ '-' + reverse_bytes(guid[16:18]) + reverse_bytes(guid[18:20]) + \
+ '-' + ''.join([reverse_bytes(guid[i:i+2]) for i in range(20, 32, 2)]) + '}'
+
+PRODUCTS_KEY = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products'
+COMPONENTS_KEY = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components'
+
+def get_installed_products():
+ """
+ Enumerate all installed products.
+ """
+ products = {}
+ hkey_products = OpenKey(HKEY_LOCAL_MACHINE, PRODUCTS_KEY, 0, KEY_ALL_ACCESS)
+
+ try:
+ product_index = 0
+ while True:
+ product_guid = EnumKey(hkey_products, product_index)
+ hkey_product_properties = OpenKey(hkey_products, product_guid + r'\InstallProperties', 0, KEY_ALL_ACCESS)
+ try:
+ value = QueryValueEx(hkey_product_properties, 'DisplayName')[0]
+ except WindowsError as oXcpt:
+ if oXcpt.winerror != 2:
+ raise
+ value = '<unknown>'
+ CloseKey(hkey_product_properties)
+ products[product_guid] = value
+ product_index += 1
+ except WindowsError as oXcpt:
+ if oXcpt.winerror != 259:
+ print(oXcpt.strerror + '.', 'error', oXcpt.winerror)
+ CloseKey(hkey_products)
+
+ print('Installed products:')
+ for product_key in sorted(products.keys()):
+ print(transpose_guid(product_key), '=', products[product_key])
+
+ print()
+ return products
+
+def get_missing_products(hkey_components):
+ """
+ Detect references to missing products.
+ """
+ products = get_installed_products()
+
+ missing_products = {}
+
+ for component_index in xrange(0, QueryInfoKey(hkey_components)[0]):
+ component_guid = EnumKey(hkey_components, component_index)
+ hkey_component = OpenKey(hkey_components, component_guid, 0, KEY_ALL_ACCESS)
+ clients = []
+ for value_index in xrange(0, QueryInfoKey(hkey_component)[1]):
+ client_guid, client_path = EnumValue(hkey_component, value_index)[:2]
+ clients.append((client_guid, client_path))
+ if not client_guid in products:
+ if client_guid in missing_products:
+ missing_products[client_guid].append((component_guid, client_path))
+ else:
+ missing_products[client_guid] = [(component_guid, client_path)]
+ CloseKey(hkey_component)
+ return missing_products
+
+def main():
+ """
+ Enumerate all installed products, go through all components and check if client refences
+ point to valid products. Remove references to non-existing products if the user allowed it.
+ """
+ hkey_components = OpenKey(HKEY_LOCAL_MACHINE, COMPONENTS_KEY, 0, KEY_ALL_ACCESS)
+
+ missing_products = get_missing_products(hkey_components)
+
+ print('Missing products refer the following components:')
+ for product_guid in sorted(missing_products.keys()):
+ if product_guid[1:] == '0'*31:
+ continue
+ print('Product', transpose_guid(product_guid) + ':')
+ for component_guid, component_file in missing_products[product_guid]:
+ print(' ' + transpose_guid(component_guid), '=', component_file)
+
+ print('Remove all references to product', transpose_guid(product_guid) + '? [y/n]')
+ if strtobool(raw_input().lower()):
+ for component_guid, component_file in missing_products[product_guid]:
+ hkey_component = OpenKey(hkey_components, component_guid, 0, KEY_ALL_ACCESS)
+ print('Removing reference in ' + transpose_guid(component_guid), '=', component_file)
+ DeleteValue(hkey_component, product_guid)
+ CloseKey(hkey_component)
+ else:
+ print('Cancelled removal of product', transpose_guid(product_guid))
+
+ CloseKey(hkey_components)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/VBox/ValidationKit/testboxscript/win/readme.txt b/src/VBox/ValidationKit/testboxscript/win/readme.txt
new file mode 100644
index 00000000..3da82f9d
--- /dev/null
+++ b/src/VBox/ValidationKit/testboxscript/win/readme.txt
@@ -0,0 +1,157 @@
+$Id: readme.txt $
+
+
+Preparations:
+
+0. Make sure the computer name (what hostname prints) is the same as the DNS
+ returns (sans domain) for the host IP.
+
+1. Install Python 2.7.x from python.org to C:\Python27 or Python 3.y.x to
+ C:\Python3%y%, where y >= 5. Matching bit count as the host windows version.
+
+2. Install the win32 extension for python.
+
+3. Append C:\Python27 or C:\Python3%y% to the system PATH (tail).
+
+4. Disable UAC.
+
+ Windows 8 / 8.1 / Server 2012: Set the following key to zero:
+ "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system\EnableLUA"
+
+5. Disable Automatic updates. (No rebooting during tests, thank you!)
+
+ Ideally we would prevent windows from even checking for updates to avoid
+ influencing benchmarks and such, however the microsofties aren't keen on it.
+ So, disable it as much as possible.
+
+ W10: gpedit.msc -> "Administrative Templates" -> "Windows Components"
+ -> "Windows Update":
+ - "Configure Automatic Updates": Enable and select "2 - Notify for
+ download and notiy for install".
+ - "Allow Automatic Updates immediate installation": Disable.
+ - "No auto-restart with logged on users for scheduled automatic
+ updates installations": Enabled.
+
+6. Go to the group policy editor (gpedit.msc) and change "Computer Configuration"
+ -> "Windows Settings" -> "Security Settings" -> "Local Policies"
+ -> "Security Options" -> "Network security: LAN Manager authentication level"
+ to "Send LM & NTLM- use NTLMv2 session security if negotiated". This fixed
+ passing the password as an argument to "NET USE" (don't ask why!).
+
+6b. While in the group policy editor, make sure that "Computer Configuration"
+ -> "Windows Settings" -> "Security Settings" [ -> "Local Policies" ]
+ -> "Account Policy" -> "Password must meet complexity requirements" is
+ disabled so the vbox account can be created later one.
+
+7. Need to disable the error popups blocking testing.
+
+ Set "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Windows\ErrorMode"
+ to 2. This immediately disables hard error popups (missing DLLs and such).
+
+ Then there are the sending info to microsoft, debug, dump, look for solution
+ questions we don't want. Not entirely sure what's required here yet, but
+ the following stuff might hopefully help (update after testing):
+
+ On Windows XP:
+
+ Go "Control Panel" -> "System Properties" -> "Advanced"
+ -> "Error Reporting" and check "Disable error reporting"
+ and uncheck "But notify me when critical erorr occurs".
+
+ On Windows Vista and later:
+
+ In gpedit change the following settings under "Computer Configuration"
+ -> "Administrative Templates" -> "Windows Components"
+ -> "Windows Error Reporting":
+ 1) Enable "Prevent display of the user interface for critical errors".
+ ... -> "Advanced Error Reporting Settings":
+ 1) Enable "Configure Report Archive" and set it to "Store All" for
+ up to 500 (or less) reports.
+ 2) Disable "Configure Report Queue".
+
+ Run 'serverWerOptin /disable'.
+
+ Then set "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\DontShowUI"
+ to 1. (Could do all the above from regedit if we wanted...)
+
+7b. Configure application crash dumps on Vista SP1 and later:
+
+ Set the following values under the key
+ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps:
+ DumpFolder [string] = C:\CrashDumps
+ DumpCount [dword] = 10
+ DumpType [dword] = 1 (minidump)
+ CustomDumpFlags [dword] = 0
+
+ mkdir C:\CrashDumps
+
+ See also http://msdn.microsoft.com/en-us/library/windows/desktop/bb787181%28v=vs.85%29.aspx
+
+7c. Enable verbose driver installation logging (C:\Windows\setupapi.dev.log):
+
+ Create the following value under the key
+ HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup\
+ LogLevel [dword] = 0xFF (255)
+
+ If it already exists (typical on W10), just OR 0xff into the existing value.
+
+8. Install firefox or chrome, download the latest testboxscript*.zip from
+ the build box. If the testbox is very short on disk space, i.e. less than
+ 15GB free disk space after installing Windows Updates, install ImDisk 2.0.9
+ or later from e.g. http://www.ltr-data.se/opencode.html/
+
+9. Create a user named "vbox" with password "password". Must be an
+ Administrator user!
+
+10. Configure user "vbox" to log in automatically via "control userpasswords2".
+
+11. Open up the port ranges 6000-6100 (VRDP) for TCP traffic and 5000-5032
+ (NetPerf) for both TCP and UDP traffic in the Windows Firewall.
+ From the command line (recommended in vista):
+ for /L %i in (6000,1,6100) do netsh firewall add portopening TCP %i "VRDP %i"
+ for /L %i in (5000,1,5032) do netsh firewall add portopening TCP %i "NetPerf %i TCP"
+ for /L %i in (5000,1,5032) do netsh firewall add portopening UDP %i "NetPerf %i UDP"
+ netsh firewall set icmpsetting type=ALL
+
+11b. Set a hostname which the test script can resolve to the host's IP address.
+
+12. Setup time server to "wei01-time.de.oracle.com" and update date/time.
+
+13. Activate windows. "https://linserv.de.oracle.com/vbox/wiki/MSDN Volume License Keys"
+
+14. Windows 2012 R2: If you experience mouse pointer problems connecting with rdesktop,
+ open the mouse pointer settings and disable mouse pointer shadow.
+
+15. Enable RDP access by opening "System Properties" and selecting "Allow
+ remote connections to this computer" in the "Remote" tab. Ensure that
+ "Allow connections only from computers running Remote Desktop with Network
+ Level Authentication" is not checked or rdesktop can't access it.
+
+ W10: Make old rdesktop connect:
+ \HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\SecurityLayer
+ Change DWORD Hex '2' -> '1'
+
+15b. While you're in "System Properties", in the "Hardware" tab, button
+ "Driver Signing" tell it to ignore logo testing requirements.
+
+ W10: Doesn't exist any more.
+
+The install (as user vbox):
+
+16. Disable loading CONIME. Set "HKEY_CURRENT_USER\Console\LoadConIme" to 0.
+
+17. Unzip (/ copy) the content of the testboxscript-*.zip to C:\testboxscript.
+
+18. Copy C:\testboxscript\testboxscript\win\autoexec-testbox.cmd to C:\.
+
+19. Create a shortcut to C:\autoexec-testbox.cmd and drag it into
+ "Start" -> "All Programs" -> "Startup".
+
+ W10: Find startup folder by hitting Win+R and entering "shell:startup".
+
+20. If this is an Intel box and the CPU is capable of Nested Paging, edit C:\autoexec-testbox.cmd
+ and append '--nested-paging'
+
+
+That's currently it.
+
diff --git a/src/VBox/ValidationKit/testdriver/Makefile.kmk b/src/VBox/ValidationKit/testdriver/Makefile.kmk
new file mode 100644
index 00000000..4247abd8
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/Makefile.kmk
@@ -0,0 +1,48 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Python Test Driver.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(filter-out %/winbase.py %/vboxcon.py, $(wildcard $(PATH_SUB_CURRENT)/*.py))
+ifeq ($(KBUILD_HOST),win)
+ VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(PATH_SUB_CURRENT)/winbase.py
+endif
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testdriver/__init__.py b/src/VBox/ValidationKit/testdriver/__init__.py
new file mode 100644
index 00000000..0214450b
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/__init__.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Test driver package
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+
+__version__ = "$Revision: 155244 $";
+
diff --git a/src/VBox/ValidationKit/testdriver/base.py b/src/VBox/ValidationKit/testdriver/base.py
new file mode 100755
index 00000000..e7bd49de
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/base.py
@@ -0,0 +1,1860 @@
+# -*- coding: utf-8 -*-
+# $Id: base.py $
+# pylint: disable=too-many-lines
+
+"""
+Base testdriver module.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import os.path
+import signal
+import socket
+import stat
+import subprocess
+import sys
+import time
+if sys.version_info[0] < 3: import thread; # pylint: disable=import-error
+else: import _thread as thread; # pylint: disable=import-error
+import threading
+import traceback
+import tempfile;
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+from common.constants import rtexitcode;
+from testdriver import reporter;
+if sys.platform == 'win32':
+ from testdriver import winbase;
+
+# Figure where we are.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+#
+# Some utility functions.
+#
+
+def exeSuff():
+ """
+ Returns the executable suffix.
+ """
+ if os.name in ('nt', 'os2'):
+ return '.exe';
+ return '';
+
+def searchPath(sExecName):
+ """
+ Searches the PATH for the specified executable name, returning the first
+ existing file/directory/whatever. The return is abspath'ed.
+ """
+ sSuff = exeSuff();
+
+ sPath = os.getenv('PATH', os.getenv('Path', os.path.defpath));
+ aPaths = sPath.split(os.path.pathsep)
+ for sDir in aPaths:
+ sFullExecName = os.path.join(sDir, sExecName);
+ if os.path.exists(sFullExecName):
+ return os.path.abspath(sFullExecName);
+ sFullExecName += sSuff;
+ if os.path.exists(sFullExecName):
+ return os.path.abspath(sFullExecName);
+ return sExecName;
+
+def getEnv(sVar, sLocalAlternative = None):
+ """
+ Tries to get an environment variable, optionally with a local run alternative.
+ Will raise an exception if sLocalAlternative is None and the variable is
+ empty or missing.
+ """
+ try:
+ sVal = os.environ.get(sVar, None);
+ if sVal is None:
+ raise GenError('environment variable "%s" is missing' % (sVar));
+ if sVal == "":
+ raise GenError('environment variable "%s" is empty' % (sVar));
+ except:
+ if sLocalAlternative is None or not reporter.isLocal():
+ raise
+ sVal = sLocalAlternative;
+ return sVal;
+
+def getDirEnv(sVar, sAlternative = None, fLocalReq = False, fTryCreate = False):
+ """
+ Tries to get an environment variable specifying a directory path.
+
+ Resolves it into an absolute path and verifies its existance before
+ returning it.
+
+ If the environment variable is empty or isn't set, or if the directory
+ doesn't exist or isn't a directory, sAlternative is returned instead.
+ If sAlternative is None, then we'll raise a GenError. For local runs we'll
+ only do this if fLocalReq is True.
+ """
+ assert sAlternative is None or fTryCreate is False;
+ try:
+ sVal = os.environ.get(sVar, None);
+ if sVal is None:
+ raise GenError('environment variable "%s" is missing' % (sVar));
+ if sVal == "":
+ raise GenError('environment variable "%s" is empty' % (sVar));
+
+ sVal = os.path.abspath(sVal);
+ if not os.path.isdir(sVal):
+ if not fTryCreate or os.path.exists(sVal):
+ reporter.error('the value of env.var. "%s" is not a dir: "%s"' % (sVar, sVal));
+ raise GenError('the value of env.var. "%s" is not a dir: "%s"' % (sVar, sVal));
+ try:
+ os.makedirs(sVal, 0o700);
+ except:
+ reporter.error('makedirs failed on the value of env.var. "%s": "%s"' % (sVar, sVal));
+ raise GenError('makedirs failed on the value of env.var. "%s": "%s"' % (sVar, sVal));
+ except:
+ if sAlternative is None:
+ if reporter.isLocal() and fLocalReq:
+ raise;
+ sVal = None;
+ else:
+ sVal = os.path.abspath(sAlternative);
+ return sVal;
+
+def timestampMilli():
+ """
+ Gets a millisecond timestamp.
+ """
+ return utils.timestampMilli();
+
+def timestampNano():
+ """
+ Gets a nanosecond timestamp.
+ """
+ return utils.timestampNano();
+
+def tryGetHostByName(sName):
+ """
+ Wrapper around gethostbyname.
+ """
+ if sName is not None:
+ try:
+ sIpAddr = socket.gethostbyname(sName);
+ except:
+ reporter.errorXcpt('gethostbyname(%s)' % (sName));
+ else:
+ if sIpAddr != '0.0.0.0':
+ sName = sIpAddr;
+ else:
+ reporter.error('gethostbyname(%s) -> %s' % (sName, sIpAddr));
+ return sName;
+
+def __processSudoKill(uPid, iSignal, fSudo):
+ """
+ Does the sudo kill -signal pid thing if fSudo is true, else uses os.kill.
+ """
+ try:
+ if fSudo:
+ return utils.sudoProcessCall(['/bin/kill', '-%s' % (iSignal,), str(uPid)]) == 0;
+ os.kill(uPid, iSignal);
+ return True;
+ except:
+ reporter.logXcpt('uPid=%s' % (uPid,));
+ return False;
+
+def processInterrupt(uPid, fSudo = False):
+ """
+ Sends a SIGINT or equivalent to interrupt the specified process.
+ Returns True on success, False on failure.
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ if sys.platform == 'win32':
+ fRc = winbase.processInterrupt(uPid)
+ else:
+ fRc = __processSudoKill(uPid, signal.SIGINT, fSudo);
+ return fRc;
+
+def sendUserSignal1(uPid, fSudo = False):
+ """
+ Sends a SIGUSR1 or equivalent to nudge the process into shutting down
+ (VBoxSVC) or something.
+ Returns True on success, False on failure or if not supported (win).
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ if sys.platform == 'win32':
+ fRc = False;
+ else:
+ fRc = __processSudoKill(uPid, signal.SIGUSR1, fSudo); # pylint: disable=no-member
+ return fRc;
+
+def processTerminate(uPid, fSudo = False):
+ """
+ Terminates the process in a nice manner (SIGTERM or equivalent).
+ Returns True on success, False on failure (logged).
+ """
+ fRc = False;
+ if sys.platform == 'win32':
+ fRc = winbase.processTerminate(uPid);
+ else:
+ fRc = __processSudoKill(uPid, signal.SIGTERM, fSudo);
+ return fRc;
+
+def processKill(uPid, fSudo = False):
+ """
+ Terminates the process with extreme prejudice (SIGKILL).
+ Returns True on success, False on failure.
+ """
+ fRc = False;
+ if sys.platform == 'win32':
+ fRc = winbase.processKill(uPid);
+ else:
+ fRc = __processSudoKill(uPid, signal.SIGKILL, fSudo); # pylint: disable=no-member
+ return fRc;
+
+def processKillWithNameCheck(uPid, sName):
+ """
+ Like processKill(), but checks if the process name matches before killing
+ it. This is intended for killing using potentially stale pid values.
+
+ Returns True on success, False on failure.
+ """
+
+ if processCheckPidAndName(uPid, sName) is not True:
+ return False;
+ return processKill(uPid);
+
+
+def processExists(uPid):
+ """
+ Checks if the specified process exits.
+ This will only work if we can signal/open the process.
+
+ Returns True if it positively exists, False otherwise.
+ """
+ return utils.processExists(uPid);
+
+def processCheckPidAndName(uPid, sName):
+ """
+ Checks if a process PID and NAME matches.
+ """
+ if sys.platform == 'win32':
+ fRc = winbase.processCheckPidAndName(uPid, sName);
+ else:
+ sOs = utils.getHostOs();
+ if sOs == 'linux':
+ asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
+ elif sOs == 'solaris':
+ asPsCmd = ['/usr/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
+ elif sOs == 'darwin':
+ asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'ucomm='];
+ else:
+ asPsCmd = None;
+
+ if asPsCmd is not None:
+ try:
+ oPs = subprocess.Popen(asPsCmd, stdout=subprocess.PIPE); # pylint: disable=consider-using-with
+ sCurName = oPs.communicate()[0];
+ iExitCode = oPs.wait();
+ except:
+ reporter.logXcpt();
+ return False;
+
+ # ps fails with non-zero exit code if the pid wasn't found.
+ if iExitCode != 0:
+ return False;
+ if sCurName is None:
+ return False;
+ sCurName = sCurName.strip();
+ if sCurName == '':
+ return False;
+
+ if os.path.basename(sName) == sName:
+ sCurName = os.path.basename(sCurName);
+ elif os.path.basename(sCurName) == sCurName:
+ sName = os.path.basename(sName);
+
+ if sCurName != sName:
+ return False;
+
+ fRc = True;
+ return fRc;
+
+def wipeDirectory(sDir):
+ """
+ Deletes all file and sub-directories in sDir, leaving sDir in empty afterwards.
+ Returns the number of errors after logging them as errors.
+ """
+ if not os.path.exists(sDir):
+ return 0;
+
+ try:
+ asNames = os.listdir(sDir);
+ except:
+ return reporter.errorXcpt('os.listdir("%s")' % (sDir));
+
+ cErrors = 0;
+ for sName in asNames:
+ # Build full path and lstat the object.
+ sFullName = os.path.join(sDir, sName)
+ try:
+ oStat = os.lstat(sFullName);
+ except:
+ reporter.errorXcpt('lstat("%s")' % (sFullName,));
+ cErrors = cErrors + 1;
+ continue;
+
+ if stat.S_ISDIR(oStat.st_mode):
+ # Directory - recurse and try remove it.
+ cErrors = cErrors + wipeDirectory(sFullName);
+ try:
+ os.rmdir(sFullName);
+ except:
+ reporter.errorXcpt('rmdir("%s")' % (sFullName,));
+ cErrors = cErrors + 1;
+ else:
+ # File, symlink, fifo or something - remove/unlink.
+ try:
+ os.remove(sFullName);
+ except:
+ reporter.errorXcpt('remove("%s")' % (sFullName,));
+ cErrors = cErrors + 1;
+ return cErrors;
+
+
+#
+# Classes
+#
+
+class GenError(Exception):
+ """
+ Exception class which only purpose it is to allow us to only catch our own
+ exceptions. Better design later.
+ """
+
+ def __init__(self, sWhat = "whatever"):
+ Exception.__init__(self);
+ self.sWhat = sWhat
+
+ def str(self):
+ """Get the message string."""
+ return self.sWhat;
+
+
+class InvalidOption(GenError):
+ """
+ Exception thrown by TestDriverBase.parseOption(). It contains the error message.
+ """
+ def __init__(self, sWhat):
+ GenError.__init__(self, sWhat);
+
+
+class QuietInvalidOption(GenError):
+ """
+ Exception thrown by TestDriverBase.parseOption(). Error already printed, just
+ return failure.
+ """
+ def __init__(self):
+ GenError.__init__(self, "");
+
+
+class TdTaskBase(object):
+ """
+ The base task.
+ """
+
+ def __init__(self, sCaller, fnProcessEvents = None):
+ self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
+ self.fSignalled = False;
+ self.__oRLock = threading.RLock();
+ self.oCv = threading.Condition(self.__oRLock);
+ self.oOwner = None;
+ self.msStart = timestampMilli();
+ self.oLocker = None;
+
+ ## Callback function that takes no parameters and will not be called holding the lock.
+ ## It is a hack to work the XPCOM and COM event queues, so we won't hold back events
+ ## that could block task progress (i.e. hangs VM).
+ self.fnProcessEvents = fnProcessEvents;
+
+ def __del__(self):
+ """In case we need it later on."""
+ pass; # pylint: disable=unnecessary-pass
+
+ def toString(self):
+ """
+ Stringifies the object, mostly as a debug aid.
+ """
+ return '<%s: fSignalled=%s, __oRLock=%s, oCv=%s, oOwner=%s, oLocker=%s, msStart=%s, sDbgCreated=%s>' \
+ % (type(self).__name__, self.fSignalled, self.__oRLock, self.oCv, repr(self.oOwner), self.oLocker, self.msStart,
+ self.sDbgCreated,);
+
+ def __str__(self):
+ return self.toString();
+
+ def lockTask(self):
+ """ Wrapper around oCv.acquire(). """
+ if True is True: # change to False for debugging deadlocks. # pylint: disable=comparison-with-itself
+ self.oCv.acquire();
+ else:
+ msStartWait = timestampMilli();
+ while self.oCv.acquire(0) is False:
+ if timestampMilli() - msStartWait > 30*1000:
+ reporter.error('!!! timed out waiting for %s' % (self, ));
+ traceback.print_stack();
+ reporter.logAllStacks()
+ self.oCv.acquire();
+ break;
+ time.sleep(0.5);
+ self.oLocker = thread.get_ident()
+ return None;
+
+ def unlockTask(self):
+ """ Wrapper around oCv.release(). """
+ self.oLocker = None;
+ self.oCv.release();
+ return None;
+
+ def getAgeAsMs(self):
+ """
+ Returns the number of milliseconds the task has existed.
+ """
+ return timestampMilli() - self.msStart;
+
+ def setTaskOwner(self, oOwner):
+ """
+ Sets or clears the task owner. (oOwner can be None.)
+
+ Returns the previous owner, this means None if not owned.
+ """
+ self.lockTask();
+ oOldOwner = self.oOwner;
+ self.oOwner = oOwner;
+ self.unlockTask();
+ return oOldOwner;
+
+ def signalTaskLocked(self):
+ """
+ Variant of signalTask that can be called while owning the lock.
+ """
+ fOld = self.fSignalled;
+ if not fOld:
+ reporter.log2('signalTaskLocked(%s)' % (self,));
+ self.fSignalled = True;
+ self.oCv.notifyAll(); # pylint: disable=deprecated-method
+ if self.oOwner is not None:
+ self.oOwner.notifyAboutReadyTask(self);
+ return fOld;
+
+ def signalTask(self):
+ """
+ Signals the task, internal use only.
+
+ Returns the previous state.
+ """
+ self.lockTask();
+ fOld = self.signalTaskLocked();
+ self.unlockTask();
+ return fOld
+
+ def resetTaskLocked(self):
+ """
+ Variant of resetTask that can be called while owning the lock.
+ """
+ fOld = self.fSignalled;
+ self.fSignalled = False;
+ return fOld;
+
+ def resetTask(self):
+ """
+ Resets the task signal, internal use only.
+
+ Returns the previous state.
+ """
+ self.lockTask();
+ fOld = self.resetTaskLocked();
+ self.unlockTask();
+ return fOld
+
+ def pollTask(self, fLocked = False):
+ """
+ Poll the signal status of the task.
+ Returns True if signalled, False if not.
+
+ Override this method.
+ """
+ if not fLocked:
+ self.lockTask();
+ fState = self.fSignalled;
+ if not fLocked:
+ self.unlockTask();
+ return fState
+
+ def waitForTask(self, cMsTimeout = 0):
+ """
+ Waits for the task to be signalled.
+
+ Returns True if the task is/became ready before the timeout expired.
+ Returns False if the task is still not after cMsTimeout have elapsed.
+
+ Overriable.
+ """
+ if self.fnProcessEvents:
+ self.fnProcessEvents();
+
+ self.lockTask();
+
+ fState = self.pollTask(True);
+ if not fState:
+ # Don't wait more than 1s. This allow lazy state polling and avoid event processing trouble.
+ msStart = timestampMilli();
+ while not fState:
+ cMsElapsed = timestampMilli() - msStart;
+ if cMsElapsed >= cMsTimeout:
+ break;
+
+ cMsWait = cMsTimeout - cMsElapsed
+ cMsWait = min(cMsWait, 1000);
+ try:
+ self.oCv.wait(cMsWait / 1000.0);
+ except:
+ pass;
+
+ if self.fnProcessEvents:
+ self.unlockTask();
+ self.fnProcessEvents();
+ self.lockTask();
+
+ reporter.doPollWork('TdTaskBase.waitForTask');
+ fState = self.pollTask(True);
+
+ self.unlockTask();
+
+ if self.fnProcessEvents:
+ self.fnProcessEvents();
+
+ return fState;
+
+
+class Process(TdTaskBase):
+ """
+ Child Process.
+ """
+
+ def __init__(self, sName, asArgs, uPid, hWin = None, uTid = None):
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.sName = sName;
+ self.asArgs = asArgs;
+ self.uExitCode = -127;
+ self.uPid = uPid;
+ self.hWin = hWin;
+ self.uTid = uTid;
+ self.sKindCrashReport = None;
+ self.sKindCrashDump = None;
+
+ def toString(self):
+ return '<%s uExitcode=%s, uPid=%s, sName=%s, asArgs=%s, hWin=%s, uTid=%s>' \
+ % (TdTaskBase.toString(self), self.uExitCode, self.uPid, self.sName, self.asArgs, self.hWin, self.uTid);
+
+ #
+ # Instantiation methods.
+ #
+
+ @staticmethod
+ def spawn(sName, *asArgsIn):
+ """
+ Similar to os.spawnl(os.P_NOWAIT,).
+
+ """
+ # Make argument array (can probably use asArgsIn directly, but wtf).
+ asArgs = [];
+ for sArg in asArgsIn:
+ asArgs.append(sArg);
+
+ # Special case: Windows.
+ if sys.platform == 'win32':
+ (uPid, hProcess, uTid) = winbase.processCreate(searchPath(sName), asArgs);
+ if uPid == -1:
+ return None;
+ return Process(sName, asArgs, uPid, hProcess, uTid);
+
+ # Unixy.
+ try:
+ uPid = os.spawnv(os.P_NOWAIT, sName, asArgs);
+ except:
+ reporter.logXcpt('sName=%s' % (sName,));
+ return None;
+ return Process(sName, asArgs, uPid);
+
+ @staticmethod
+ def spawnp(sName, *asArgsIn):
+ """
+ Similar to os.spawnlp(os.P_NOWAIT,).
+
+ """
+ return Process.spawn(searchPath(sName), *asArgsIn);
+
+ #
+ # Task methods
+ #
+
+ def pollTask(self, fLocked = False):
+ """
+ Overridden pollTask method.
+ """
+ if not fLocked:
+ self.lockTask();
+
+ fRc = self.fSignalled;
+ if not fRc:
+ if sys.platform == 'win32':
+ if winbase.processPollByHandle(self.hWin):
+ try:
+ if hasattr(self.hWin, '__int__'): # Needed for newer pywin32 versions.
+ (uPid, uStatus) = os.waitpid(self.hWin.__int__(), 0);
+ else:
+ (uPid, uStatus) = os.waitpid(self.hWin, 0);
+ if uPid in (self.hWin, self.uPid,):
+ self.hWin.Detach(); # waitpid closed it, so it's now invalid.
+ self.hWin = None;
+ uPid = self.uPid;
+ except:
+ reporter.logXcpt();
+ uPid = self.uPid;
+ uStatus = 0xffffffff;
+ else:
+ uPid = 0;
+ uStatus = 0; # pylint: disable=redefined-variable-type
+ else:
+ try:
+ (uPid, uStatus) = os.waitpid(self.uPid, os.WNOHANG); # pylint: disable=no-member
+ except:
+ reporter.logXcpt();
+ uPid = self.uPid;
+ uStatus = 0xffffffff;
+
+ # Got anything?
+ if uPid == self.uPid:
+ self.uExitCode = uStatus;
+ reporter.log('Process %u -> %u (%#x)' % (uPid, uStatus, uStatus));
+ self.signalTaskLocked();
+ if self.uExitCode != 0 and (self.sKindCrashReport is not None or self.sKindCrashDump is not None):
+ reporter.error('Process "%s" returned/crashed with a non-zero status code!! rc=%u sig=%u%s (raw=%#x)'
+ % ( self.sName, self.uExitCode >> 8, self.uExitCode & 0x7f,
+ ' w/ core' if self.uExitCode & 0x80 else '', self.uExitCode))
+ utils.processCollectCrashInfo(self.uPid, reporter.log, self._addCrashFile);
+
+ fRc = self.fSignalled;
+ if not fLocked:
+ self.unlockTask();
+ return fRc;
+
+ def _addCrashFile(self, sFile, fBinary):
+ """
+ Helper for adding a crash report or dump to the test report.
+ """
+ sKind = self.sKindCrashDump if fBinary else self.sKindCrashReport;
+ if sKind is not None:
+ reporter.addLogFile(sFile, sKind);
+ return None;
+
+
+ #
+ # Methods
+ #
+
+ def enableCrashReporting(self, sKindCrashReport, sKindCrashDump):
+ """
+ Enabling (or disables) automatic crash reporting on systems where that
+ is possible. The two file kind parameters are on the form
+ 'crash/log/client' and 'crash/dump/client'. If both are None,
+ reporting will be disabled.
+ """
+ self.sKindCrashReport = sKindCrashReport;
+ self.sKindCrashDump = sKindCrashDump;
+
+ sCorePath = None;
+ sOs = utils.getHostOs();
+ if sOs == 'solaris':
+ if sKindCrashDump is not None: # Enable.
+ sCorePath = getDirEnv('TESTBOX_PATH_SCRATCH', sAlternative = '/var/cores', fTryCreate = False);
+ (iExitCode, _, sErr) = utils.processOutputUnchecked([ 'coreadm', '-e', 'global', '-e', 'global-setid', \
+ '-e', 'process', '-e', 'proc-setid', \
+ '-g', os.path.join(sCorePath, '%f.%p.core')]);
+ else: # Disable.
+ (iExitCode, _, sErr) = utils.processOutputUnchecked([ 'coreadm', \
+ '-d', 'global', '-d', 'global-setid', \
+ '-d', 'process', '-d', 'proc-setid' ]);
+ if iExitCode != 0: # Don't report an actual error, just log this.
+ reporter.log('%s coreadm failed: %s' % ('Enabling' if sKindCrashDump else 'Disabling', sErr));
+
+ if sKindCrashDump is not None:
+ if sCorePath is not None:
+ reporter.log('Crash dumps enabled -- path is "%s"' % (sCorePath,));
+ else:
+ reporter.log('Crash dumps disabled');
+
+ return True;
+
+ def isRunning(self):
+ """
+ Returns True if the process is still running, False if not.
+ """
+ return not self.pollTask();
+
+ def wait(self, cMsTimeout = 0):
+ """
+ Wait for the process to exit.
+
+ Returns True if the process exited withint the specified wait period.
+ Returns False if still running.
+ """
+ return self.waitForTask(cMsTimeout);
+
+ def getExitCode(self):
+ """
+ Returns the exit code of the process.
+ The process must have exited or the result will be wrong.
+ """
+ if self.isRunning():
+ return -127;
+ return self.uExitCode >> 8;
+
+ def isNormalExit(self):
+ """
+ Returns True if regular exit(), False if signal or still running.
+ """
+ if self.isRunning():
+ return False;
+ if sys.platform == 'win32':
+ return True;
+ return os.WIFEXITED(self.uExitCode); # pylint: disable=no-member
+
+ def interrupt(self):
+ """
+ Sends a SIGINT or equivalent to interrupt the process.
+ Returns True on success, False on failure.
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ if sys.platform == 'win32':
+ return winbase.postThreadMesssageQuit(self.uTid);
+ return processInterrupt(self.uPid);
+
+ def sendUserSignal1(self):
+ """
+ Sends a SIGUSR1 or equivalent to nudge the process into shutting down
+ (VBoxSVC) or something.
+ Returns True on success, False on failure.
+
+ On Windows hosts this may not work unless the process happens to be a
+ process group leader.
+ """
+ #if sys.platform == 'win32':
+ # return winbase.postThreadMesssageClose(self.uTid);
+ return sendUserSignal1(self.uPid);
+
+ def terminate(self):
+ """
+ Terminates the process in a nice manner (SIGTERM or equivalent).
+ Returns True on success, False on failure (logged).
+ """
+ if sys.platform == 'win32':
+ return winbase.processTerminateByHandle(self.hWin);
+ return processTerminate(self.uPid);
+
+ def getPid(self):
+ """ Returns the process id. """
+ return self.uPid;
+
+
+class SubTestDriverBase(object):
+ """
+ The base sub-test driver.
+
+ It helps thinking of these as units/sets/groups of tests, where the test
+ cases are (mostly) realized in python.
+
+ The sub-test drivers are subordinates of one or more test drivers. They
+ can be viewed as test code libraries that is responsible for parts of a
+ test driver run in different setups. One example would be testing a guest
+ additions component, which is applicable both to freshly installed guest
+ additions and VMs with old guest.
+
+ The test drivers invokes the sub-test drivers in a private manner during
+ test execution, but some of the generic bits are done automagically by the
+ base class: options, help, resources, various other actions.
+ """
+
+ def __init__(self, oTstDrv, sName, sTestName):
+ self.oTstDrv = oTstDrv # type: TestDriverBase
+ self.sName = sName; # For use with options (--enable-sub-driver sName:sName2)
+ self.sTestName = sTestName; # More descriptive for passing to reporter.testStart().
+ self.asRsrcs = [] # type: List(str)
+ self.fEnabled = True; # TestDriverBase --enable-sub-driver and --disable-sub-driver.
+
+ def showUsage(self):
+ """
+ Show usage information if any.
+
+ The default implementation only prints the name.
+ """
+ reporter.log('');
+ reporter.log('Options for sub-test driver %s (%s):' % (self.sTestName, self.sName,));
+ return True;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parse an option. Override this.
+
+ @param asArgs The argument vector.
+ @param iArg The index of the current argument.
+
+ @returns The index of the next argument if consumed, @a iArg if not.
+
+ @throws InvalidOption or QuietInvalidOption on syntax error or similar.
+ """
+ _ = asArgs;
+ return iArg;
+
+
+class TestDriverBase(object): # pylint: disable=too-many-instance-attributes
+ """
+ The base test driver.
+ """
+
+ def __init__(self):
+ self.fInterrupted = False;
+
+ # Actions.
+ self.asSpecialActions = ['extract', 'abort'];
+ self.asNormalActions = ['cleanup-before', 'verify', 'config', 'execute', 'cleanup-after' ];
+ self.asActions = [];
+ self.sExtractDstPath = None;
+
+ # Options.
+ self.fNoWipeClean = False;
+
+ # Tasks - only accessed by one thread atm, so no need for locking.
+ self.aoTasks = [];
+
+ # Host info.
+ self.sHost = utils.getHostOs();
+ self.sHostArch = utils.getHostArch();
+
+ # Skipped status modifier (see end of innerMain()).
+ self.fBadTestbox = False;
+
+ #
+ # Get our bearings and adjust the environment.
+ #
+ if not utils.isRunningFromCheckout():
+ self.sBinPath = os.path.join(g_ksValidationKitDir, utils.getHostOs(), utils.getHostArch());
+ else:
+ self.sBinPath = os.path.join(g_ksValidationKitDir, os.pardir, os.pardir, os.pardir, 'out', utils.getHostOsDotArch(),
+ os.environ.get('KBUILD_TYPE', 'debug'),
+ 'validationkit', utils.getHostOs(), utils.getHostArch());
+ self.sOrgShell = os.environ.get('SHELL');
+ self.sOurShell = os.path.join(self.sBinPath, 'vts_shell' + exeSuff()); # No shell yet.
+ os.environ['SHELL'] = self.sOurShell;
+
+ self.sScriptPath = getDirEnv('TESTBOX_PATH_SCRIPTS');
+ if self.sScriptPath is None:
+ self.sScriptPath = os.path.abspath(os.path.join(os.getcwd(), '..'));
+ os.environ['TESTBOX_PATH_SCRIPTS'] = self.sScriptPath;
+
+ self.sScratchPath = getDirEnv('TESTBOX_PATH_SCRATCH', fTryCreate = True);
+ if self.sScratchPath is None:
+ sTmpDir = tempfile.gettempdir();
+ if sTmpDir == '/tmp': # /var/tmp is generally more suitable on all platforms.
+ sTmpDir = '/var/tmp';
+ self.sScratchPath = os.path.abspath(os.path.join(sTmpDir, 'VBoxTestTmp'));
+ if not os.path.isdir(self.sScratchPath):
+ os.makedirs(self.sScratchPath, 0o700);
+ os.environ['TESTBOX_PATH_SCRATCH'] = self.sScratchPath;
+
+ self.sTestBoxName = getEnv( 'TESTBOX_NAME', 'local');
+ self.sTestSetId = getEnv( 'TESTBOX_TEST_SET_ID', 'local');
+ self.sBuildPath = getDirEnv('TESTBOX_PATH_BUILDS');
+ self.sUploadPath = getDirEnv('TESTBOX_PATH_UPLOAD');
+ self.sResourcePath = getDirEnv('TESTBOX_PATH_RESOURCES');
+ if self.sResourcePath is None:
+ if self.sHost == 'darwin': self.sResourcePath = "/Volumes/testrsrc/";
+ elif self.sHost == 'freebsd': self.sResourcePath = "/mnt/testrsrc/";
+ elif self.sHost == 'linux': self.sResourcePath = "/mnt/testrsrc/";
+ elif self.sHost == 'os2': self.sResourcePath = "T:/";
+ elif self.sHost == 'solaris': self.sResourcePath = "/mnt/testrsrc/";
+ elif self.sHost == 'win': self.sResourcePath = "T:/";
+ else: raise GenError('unknown host OS "%s"' % (self.sHost));
+
+ # PID file for the testdriver.
+ self.sPidFile = os.path.join(self.sScratchPath, 'testdriver.pid');
+
+ # Some stuff for the log...
+ reporter.log('scratch: %s' % (self.sScratchPath,));
+
+ # Get the absolute timeout (seconds since epoch, see
+ # utils.timestampSecond()). None if not available.
+ self.secTimeoutAbs = os.environ.get('TESTBOX_TIMEOUT_ABS', None);
+ if self.secTimeoutAbs is not None:
+ self.secTimeoutAbs = long(self.secTimeoutAbs);
+ reporter.log('secTimeoutAbs: %s' % (self.secTimeoutAbs,));
+ else:
+ reporter.log('TESTBOX_TIMEOUT_ABS not found in the environment');
+
+ # Distance from secTimeoutAbs that timeouts should be adjusted to.
+ self.secTimeoutFudge = 30;
+
+ # List of sub-test drivers (SubTestDriverBase derivatives).
+ self.aoSubTstDrvs = [] # type: list(SubTestDriverBase)
+
+ # Use the scratch path for temporary files.
+ if self.sHost in ['win', 'os2']:
+ os.environ['TMP'] = self.sScratchPath;
+ os.environ['TEMP'] = self.sScratchPath;
+ os.environ['TMPDIR'] = self.sScratchPath;
+ os.environ['IPRT_TMPDIR'] = self.sScratchPath; # IPRT/VBox specific.
+
+
+ #
+ # Resource utility methods.
+ #
+
+ def isResourceFile(self, sFile):
+ """
+ Checks if sFile is in in the resource set.
+ """
+ ## @todo need to deal with stuff in the validationkit.zip and similar.
+ asRsrcs = self.getResourceSet();
+ if sFile in asRsrcs:
+ return os.path.isfile(os.path.join(self.sResourcePath, sFile));
+ for sRsrc in asRsrcs:
+ if sFile.startswith(sRsrc):
+ sFull = os.path.join(self.sResourcePath, sRsrc);
+ if os.path.isdir(sFull):
+ return os.path.isfile(os.path.join(self.sResourcePath, sRsrc));
+ return False;
+
+ def getFullResourceName(self, sName):
+ """
+ Returns the full resource name.
+ """
+ if os.path.isabs(sName): ## @todo Hack. Need to deal properly with stuff in the validationkit.zip and similar.
+ return sName;
+ return os.path.join(self.sResourcePath, sName);
+
+ #
+ # Scratch related utility methods.
+ #
+
+ def wipeScratch(self):
+ """
+ Removes the content of the scratch directory.
+ Returns True on no errors, False + log entries on errors.
+ """
+ cErrors = wipeDirectory(self.sScratchPath);
+ return cErrors == 0;
+
+ #
+ # Sub-test driver related methods.
+ #
+
+ def addSubTestDriver(self, oSubTstDrv):
+ """
+ Adds a sub-test driver.
+
+ Returns True on success, false on failure.
+ """
+ assert isinstance(oSubTstDrv, SubTestDriverBase);
+ if oSubTstDrv in self.aoSubTstDrvs:
+ reporter.error('Attempt at adding sub-test driver %s twice.' % (oSubTstDrv.sName,));
+ return False;
+ self.aoSubTstDrvs.append(oSubTstDrv);
+ return True;
+
+ def showSubTstDrvUsage(self):
+ """
+ Shows the usage of the sub-test drivers.
+ """
+ for oSubTstDrv in self.aoSubTstDrvs:
+ oSubTstDrv.showUsage();
+ return True;
+
+ def subTstDrvParseOption(self, asArgs, iArgs):
+ """
+ Lets the sub-test drivers have a go at the option.
+ Returns the index of the next option if handled, otherwise iArgs.
+ """
+ for oSubTstDrv in self.aoSubTstDrvs:
+ iNext = oSubTstDrv.parseOption(asArgs, iArgs)
+ if iNext != iArgs:
+ assert iNext > iArgs;
+ assert iNext <= len(asArgs);
+ return iNext;
+ return iArgs;
+
+ def findSubTstDrvByShortName(self, sShortName):
+ """
+ Locates a sub-test driver by it's short name.
+ Returns sub-test driver object reference if found, None if not.
+ """
+ for oSubTstDrv in self.aoSubTstDrvs:
+ if oSubTstDrv.sName == sShortName:
+ return oSubTstDrv;
+ return None;
+
+
+ #
+ # Task related methods.
+ #
+
+ def addTask(self, oTask):
+ """
+ Adds oTask to the task list.
+
+ Returns True if the task was added.
+
+ Returns False if the task was already in the task list.
+ """
+ if oTask in self.aoTasks:
+ return False;
+ #reporter.log2('adding task %s' % (oTask,));
+ self.aoTasks.append(oTask);
+ oTask.setTaskOwner(self);
+ #reporter.log2('tasks now in list: %d - %s' % (len(self.aoTasks), self.aoTasks));
+ return True;
+
+ def removeTask(self, oTask):
+ """
+ Removes oTask to the task list.
+
+ Returns oTask on success and None on failure.
+ """
+ try:
+ #reporter.log2('removing task %s' % (oTask,));
+ self.aoTasks.remove(oTask);
+ except:
+ return None;
+ else:
+ oTask.setTaskOwner(None);
+ #reporter.log2('tasks left: %d - %s' % (len(self.aoTasks), self.aoTasks));
+ return oTask;
+
+ def removeAllTasks(self):
+ """
+ Removes all the task from the task list.
+
+ Returns None.
+ """
+ aoTasks = self.aoTasks;
+ self.aoTasks = [];
+ for oTask in aoTasks:
+ oTask.setTaskOwner(None);
+ return None;
+
+ def notifyAboutReadyTask(self, oTask):
+ """
+ Notificiation that there is a ready task. May be called owning the
+ task lock, so be careful wrt deadlocks.
+
+ Remember to call super when overriding this.
+ """
+ if oTask is None: pass; # lint
+ return None;
+
+ def pollTasks(self):
+ """
+ Polls the task to see if any of them are ready.
+ Returns the ready task, None if none are ready.
+ """
+ for oTask in self.aoTasks:
+ if oTask.pollTask():
+ return oTask;
+ return None;
+
+ def waitForTasksSleepWorker(self, cMsTimeout):
+ """
+ Overridable method that does the sleeping for waitForTask().
+
+ cMsTimeout will not be larger than 1000, so there is normally no need
+ to do any additional splitting up of the polling interval.
+
+ Returns True if cMillieSecs elapsed.
+ Returns False if some exception was raised while we waited or
+ there turned out to be nothing to wait on.
+ """
+ try:
+ self.aoTasks[0].waitForTask(cMsTimeout);
+ return True;
+ except Exception as oXcpt:
+ reporter.log("waitForTasksSleepWorker: %s" % (str(oXcpt),));
+ return False;
+
+ def waitForTasks(self, cMsTimeout):
+ """
+ Waits for any of the tasks to require attention or a KeyboardInterrupt.
+ Returns the ready task on success, None on timeout or interrupt.
+ """
+ try:
+ #reporter.log2('waitForTasks: cMsTimeout=%d' % (cMsTimeout,));
+
+ if cMsTimeout == 0:
+ return self.pollTasks();
+
+ if not self.aoTasks:
+ return None;
+
+ fMore = True;
+ if cMsTimeout < 0:
+ while fMore:
+ oTask = self.pollTasks();
+ if oTask is not None:
+ return oTask;
+ fMore = self.waitForTasksSleepWorker(1000);
+ else:
+ msStart = timestampMilli();
+ while fMore:
+ oTask = self.pollTasks();
+ if oTask is not None:
+ #reporter.log2('waitForTasks: returning %s, msStart=%d' % \
+ # (oTask, msStart));
+ return oTask;
+
+ cMsElapsed = timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout: # not ==, we want the final waitForEvents.
+ break;
+ cMsSleep = cMsTimeout - cMsElapsed;
+ cMsSleep = min(cMsSleep, 1000);
+ fMore = self.waitForTasksSleepWorker(cMsSleep);
+ except KeyboardInterrupt:
+ self.fInterrupted = True;
+ reporter.errorXcpt('KeyboardInterrupt', 6);
+ except:
+ reporter.errorXcpt(None, 6);
+ return None;
+
+ #
+ # PID file management methods.
+ #
+
+ def pidFileRead(self):
+ """
+ Worker that reads the PID file.
+ Returns dictionary of PID with value (sName, fSudo), empty if no file.
+ """
+ dPids = {};
+ if os.path.isfile(self.sPidFile):
+ try:
+ oFile = utils.openNoInherit(self.sPidFile, 'r');
+ sContent = str(oFile.read());
+ oFile.close();
+ except:
+ reporter.errorXcpt();
+ return dPids;
+
+ sContent = str(sContent).strip().replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');
+ for sProcess in sContent.split(' '):
+ asFields = sProcess.split(':');
+ if len(asFields) == 3 and asFields[0].isdigit():
+ try:
+ dPids[int(asFields[0])] = (asFields[2], asFields[1] == 'sudo');
+ except:
+ reporter.logXcpt('sProcess=%s' % (sProcess,));
+ else:
+ reporter.log('%s: "%s"' % (self.sPidFile, sProcess));
+
+ return dPids;
+
+ def pidFileAdd(self, iPid, sName, fSudo = False):
+ """
+ Adds a PID to the PID file, creating the file if necessary.
+ """
+ try:
+ oFile = utils.openNoInherit(self.sPidFile, 'a');
+ oFile.write('%s:%s:%s\n'
+ % ( iPid,
+ 'sudo' if fSudo else 'normal',
+ sName.replace(' ', '_').replace(':','_').replace('\n','_').replace('\r','_').replace('\t','_'),));
+ oFile.close();
+ except:
+ reporter.errorXcpt();
+ return False;
+ ## @todo s/log/log2/
+ reporter.log('pidFileAdd: added %s (%#x) %s fSudo=%s (new content: %s)'
+ % (iPid, iPid, sName, fSudo, self.pidFileRead(),));
+ return True;
+
+ def pidFileRemove(self, iPid, fQuiet = False):
+ """
+ Removes a PID from the PID file.
+ """
+ dPids = self.pidFileRead();
+ if iPid not in dPids:
+ if not fQuiet:
+ reporter.log('pidFileRemove could not find %s in the PID file (content: %s)' % (iPid, dPids));
+ return False;
+
+ sName = dPids[iPid][0];
+ del dPids[iPid];
+
+ sPid = '';
+ for iPid2, tNameSudo in dPids.items():
+ sPid += '%s:%s:%s\n' % (iPid2, 'sudo' if tNameSudo[1] else 'normal', tNameSudo[0]);
+
+ try:
+ oFile = utils.openNoInherit(self.sPidFile, 'w');
+ oFile.write(sPid);
+ oFile.close();
+ except:
+ reporter.errorXcpt();
+ return False;
+ ## @todo s/log/log2/
+ reporter.log('pidFileRemove: removed PID %d [%s] (new content: %s)' % (iPid, sName, self.pidFileRead(),));
+ return True;
+
+ def pidFileDelete(self):
+ """Creates the testdriver PID file."""
+ if os.path.isfile(self.sPidFile):
+ try:
+ os.unlink(self.sPidFile);
+ except:
+ reporter.logXcpt();
+ return False;
+ ## @todo s/log/log2/
+ reporter.log('pidFileDelete: deleted "%s"' % (self.sPidFile,));
+ return True;
+
+ #
+ # Misc helper methods.
+ #
+
+ def requireMoreArgs(self, cMinNeeded, asArgs, iArg):
+ """
+ Checks that asArgs has at least cMinNeeded args following iArg.
+
+ Returns iArg + 1 if it checks out fine.
+ Raise appropritate exception if not, ASSUMING that the current argument
+ is found at iArg.
+ """
+ assert cMinNeeded >= 1;
+ if iArg + cMinNeeded > len(asArgs):
+ if cMinNeeded > 1:
+ raise InvalidOption('The "%s" option takes %s values' % (asArgs[iArg], cMinNeeded,));
+ raise InvalidOption('The "%s" option takes 1 value' % (asArgs[iArg],));
+ return iArg + 1;
+
+ def getBinTool(self, sName):
+ """
+ Returns the full path to the given binary validation kit tool.
+ """
+ return os.path.join(self.sBinPath, sName) + exeSuff();
+
+ def adjustTimeoutMs(self, cMsTimeout, cMsMinimum = None):
+ """
+ Adjusts the given timeout (milliseconds) to take TESTBOX_TIMEOUT_ABS
+ and cMsMinimum (optional) into account.
+
+ Returns adjusted timeout.
+ Raises no exceptions.
+ """
+ if self.secTimeoutAbs is not None:
+ cMsToDeadline = self.secTimeoutAbs * 1000 - utils.timestampMilli();
+ if cMsToDeadline >= 0:
+ # Adjust for fudge and enforce the minimum timeout
+ cMsToDeadline -= self.secTimeoutFudge * 1000;
+ if cMsToDeadline < (cMsMinimum if cMsMinimum is not None else 10000):
+ cMsToDeadline = cMsMinimum if cMsMinimum is not None else 10000;
+
+ # Is the timeout beyond the (adjusted) deadline, if so change it.
+ if cMsTimeout > cMsToDeadline:
+ reporter.log('adjusting timeout: %s ms -> %s ms (deadline)\n' % (cMsTimeout, cMsToDeadline,));
+ return cMsToDeadline;
+ reporter.log('adjustTimeoutMs: cMsTimeout (%s) > cMsToDeadline (%s)' % (cMsTimeout, cMsToDeadline,));
+ else:
+ # Don't bother, we've passed the deadline.
+ reporter.log('adjustTimeoutMs: ooops! cMsToDeadline=%s (%s), timestampMilli()=%s, timestampSecond()=%s'
+ % (cMsToDeadline, cMsToDeadline*1000, utils.timestampMilli(), utils.timestampSecond()));
+
+ # Only enforce the minimum timeout if specified.
+ if cMsMinimum is not None and cMsTimeout < cMsMinimum:
+ reporter.log('adjusting timeout: %s ms -> %s ms (minimum)\n' % (cMsTimeout, cMsMinimum,));
+ cMsTimeout = cMsMinimum;
+
+ return cMsTimeout;
+
+ def prepareResultFile(self, sName = 'results.xml'):
+ """
+ Given a base name (no path, but extension if required), a scratch file
+ name is computed and any previous file removed.
+
+ Returns the full path to the file sName.
+ Raises exception on failure.
+ """
+ sXmlFile = os.path.join(self.sScratchPath, sName);
+ if os.path.exists(sXmlFile):
+ os.unlink(sXmlFile);
+ return sXmlFile;
+
+
+ #
+ # Overridable methods.
+ #
+
+ def showUsage(self):
+ """
+ Shows the usage.
+
+ When overriding this, call super first.
+ """
+ sName = os.path.basename(sys.argv[0]);
+ reporter.log('Usage: %s [options] <action(s)>' % (sName,));
+ reporter.log('');
+ reporter.log('Actions (in execution order):');
+ reporter.log(' cleanup-before');
+ reporter.log(' Cleanups done at the start of testing.');
+ reporter.log(' verify');
+ reporter.log(' Verify that all necessary resources are present.');
+ reporter.log(' config');
+ reporter.log(' Configure the tests.');
+ reporter.log(' execute');
+ reporter.log(' Execute the tests.');
+ reporter.log(' cleanup-after');
+ reporter.log(' Cleanups done at the end of the testing.');
+ reporter.log('');
+ reporter.log('Special Actions:');
+ reporter.log(' all');
+ reporter.log(' Alias for: %s' % (' '.join(self.asNormalActions),));
+ reporter.log(' extract <path>');
+ reporter.log(' Extract the test resources and put them in the specified');
+ reporter.log(' path for off side/line testing.');
+ reporter.log(' abort');
+ reporter.log(' Aborts the test.');
+ reporter.log('');
+ reporter.log('Base Options:');
+ reporter.log(' -h, --help');
+ reporter.log(' Show this help message.');
+ reporter.log(' -v, --verbose');
+ reporter.log(' Increase logging verbosity, repeat for more logging.');
+ reporter.log(' -d, --debug');
+ reporter.log(' Increase the debug logging level, repeat for more info.');
+ reporter.log(' --no-wipe-clean');
+ reporter.log(' Do not wipe clean the scratch area during the two clean up');
+ reporter.log(' actions. This is for facilitating nested test driver execution.');
+ if self.aoSubTstDrvs:
+ reporter.log(' --enable-sub-driver <sub1>[:..]');
+ reporter.log(' --disable-sub-driver <sub1>[:..]');
+ reporter.log(' Enables or disables one or more of the sub drivers: %s'
+ % (', '.join([oSubTstDrv.sName for oSubTstDrv in self.aoSubTstDrvs]),));
+ return True;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parse an option. Override this.
+
+ Keyword arguments:
+ asArgs -- The argument vector.
+ iArg -- The index of the current argument.
+
+ Returns iArg if the option was not recognized.
+ Returns the index of the next argument when something is consumed.
+ In the event of a syntax error, a InvalidOption or QuietInvalidOption
+ should be thrown.
+ """
+
+ if asArgs[iArg] in ('--help', '-help', '-h', '-?', '/?', '/help', '/H', '-H'):
+ self.showUsage();
+ self.showSubTstDrvUsage();
+ raise QuietInvalidOption();
+
+ # options
+ if asArgs[iArg] in ('--verbose', '-v'):
+ reporter.incVerbosity()
+ elif asArgs[iArg] in ('--debug', '-d'):
+ reporter.incDebug()
+ elif asArgs[iArg] == '--no-wipe-clean':
+ self.fNoWipeClean = True;
+ elif asArgs[iArg] in ('--enable-sub-driver', '--disable-sub-driver') and self.aoSubTstDrvs:
+ sOption = asArgs[iArg];
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for sSubTstDrvName in asArgs[iArg].split(':'):
+ oSubTstDrv = self.findSubTstDrvByShortName(sSubTstDrvName);
+ if oSubTstDrv is None:
+ raise InvalidOption('Unknown sub-test driver given to %s: %s' % (sOption, sSubTstDrvName,));
+ oSubTstDrv.fEnabled = sOption == '--enable-sub-driver';
+ elif (asArgs[iArg] == 'all' or asArgs[iArg] in self.asNormalActions) \
+ and self.asActions in self.asSpecialActions:
+ raise InvalidOption('selected special action "%s" already' % (self.asActions[0], ));
+ # actions
+ elif asArgs[iArg] == 'all':
+ self.asActions = [ 'all' ];
+ elif asArgs[iArg] in self.asNormalActions:
+ self.asActions.append(asArgs[iArg])
+ elif asArgs[iArg] in self.asSpecialActions:
+ if self.asActions:
+ raise InvalidOption('selected special action "%s" already' % (self.asActions[0], ));
+ self.asActions = [ asArgs[iArg] ];
+ # extact <destination>
+ if asArgs[iArg] == 'extract':
+ iArg = iArg + 1;
+ if iArg >= len(asArgs): raise InvalidOption('The "extract" action requires a destination directory');
+ self.sExtractDstPath = asArgs[iArg];
+ else:
+ return iArg;
+ return iArg + 1;
+
+ def completeOptions(self):
+ """
+ This method is called after parsing all the options.
+ Returns success indicator. Use the reporter to complain.
+
+ Overriable, call super.
+ """
+ return True;
+
+ def getResourceSet(self):
+ """
+ Returns a set of file and/or directory names relative to
+ TESTBOX_PATH_RESOURCES.
+
+ Override this, call super when using sub-test drivers.
+ """
+ asRsrcs = [];
+ for oSubTstDrv in self.aoSubTstDrvs:
+ asRsrcs.extend(oSubTstDrv.asRsrcs);
+ return asRsrcs;
+
+ def actionExtract(self):
+ """
+ Handle the action that extracts the test resources for off site use.
+ Returns a success indicator and error details with the reporter.
+
+ There is usually no need to override this.
+ """
+ fRc = True;
+ asRsrcs = self.getResourceSet();
+ for iRsrc, sRsrc in enumerate(asRsrcs):
+ reporter.log('Resource #%s: "%s"' % (iRsrc, sRsrc));
+ sSrcPath = os.path.normpath(os.path.abspath(os.path.join(self.sResourcePath, sRsrc.replace('/', os.path.sep))));
+ sDstPath = os.path.normpath(os.path.join(self.sExtractDstPath, sRsrc.replace('/', os.path.sep)));
+
+ sDstDir = os.path.dirname(sDstPath);
+ if not os.path.exists(sDstDir):
+ try: os.makedirs(sDstDir, 0o775);
+ except: fRc = reporter.errorXcpt('Error creating directory "%s":' % (sDstDir,));
+
+ if os.path.isfile(sSrcPath):
+ try: utils.copyFileSimple(sSrcPath, sDstPath);
+ except: fRc = reporter.errorXcpt('Error copying "%s" to "%s":' % (sSrcPath, sDstPath,));
+ elif os.path.isdir(sSrcPath):
+ fRc = reporter.error('Extracting directories have not been implemented yet');
+ else:
+ fRc = reporter.error('Missing or unsupported resource type: %s' % (sSrcPath,));
+ return fRc;
+
+ def actionVerify(self):
+ """
+ Handle the action that verify the test resources.
+ Returns a success indicator and error details with the reporter.
+
+ There is usually no need to override this.
+ """
+
+ asRsrcs = self.getResourceSet();
+ for sRsrc in asRsrcs:
+ # Go thru some pain to catch escape sequences.
+ if sRsrc.find("//") >= 0:
+ reporter.error('Double slash test resource name: "%s"' % (sRsrc));
+ return False;
+ if sRsrc == ".." \
+ or sRsrc.startswith("../") \
+ or sRsrc.find("/../") >= 0 \
+ or sRsrc.endswith("/.."):
+ reporter.error('Relative path in test resource name: "%s"' % (sRsrc));
+ return False;
+
+ sFull = os.path.normpath(os.path.abspath(os.path.join(self.sResourcePath, sRsrc)));
+ if not sFull.startswith(os.path.normpath(self.sResourcePath)):
+ reporter.error('sFull="%s" self.sResourcePath=%s' % (sFull, self.sResourcePath));
+ reporter.error('The resource "%s" seems to specify a relative path' % (sRsrc));
+ return False;
+
+ reporter.log2('Checking for resource "%s" at "%s" ...' % (sRsrc, sFull));
+ if os.path.isfile(sFull):
+ try:
+ oFile = utils.openNoInherit(sFull, "rb");
+ oFile.close();
+ except Exception as oXcpt:
+ reporter.error('The file resource "%s" cannot be accessed: %s' % (sFull, oXcpt));
+ return False;
+ elif os.path.isdir(sFull):
+ if not os.path.isdir(os.path.join(sFull, '.')):
+ reporter.error('The directory resource "%s" cannot be accessed' % (sFull));
+ return False;
+ elif os.path.exists(sFull):
+ reporter.error('The resource "%s" is not a file or directory' % (sFull));
+ return False;
+ else:
+ reporter.error('The resource "%s" was not found' % (sFull));
+ return False;
+ return True;
+
+ def actionConfig(self):
+ """
+ Handle the action that configures the test.
+ Returns True (success), False (failure) or None (skip the test),
+ posting complaints and explanations with the reporter.
+
+ Override this.
+ """
+ return True;
+
+ def actionExecute(self):
+ """
+ Handle the action that executes the test.
+
+ Returns True (success), False (failure) or None (skip the test),
+ posting complaints and explanations with the reporter.
+
+ Override this.
+ """
+ return True;
+
+ def actionCleanupBefore(self):
+ """
+ Handle the action that cleans up spills from previous tests before
+ starting the tests. This is mostly about wiping the scratch space
+ clean in local runs. On a testbox the testbox script will use the
+ cleanup-after if the test is interrupted.
+
+ Returns True (success), False (failure) or None (skip the test),
+ posting complaints and explanations with the reporter.
+
+ Override this, but call super to wipe the scratch directory.
+ """
+ if self.fNoWipeClean is False:
+ self.wipeScratch();
+ return True;
+
+ def actionCleanupAfter(self):
+ """
+ Handle the action that cleans up all spills from executing the test.
+
+ Returns True (success) or False (failure) posting complaints and
+ explanations with the reporter.
+
+ Override this, but call super to wipe the scratch directory.
+ """
+ if self.fNoWipeClean is False:
+ self.wipeScratch();
+ return True;
+
+ def actionAbort(self):
+ """
+ Handle the action that aborts a (presumed) running testdriver, making
+ sure to include all it's children.
+
+ Returns True (success) or False (failure) posting complaints and
+ explanations with the reporter.
+
+ Override this, but call super to kill the testdriver script and any
+ other process covered by the testdriver PID file.
+ """
+
+ dPids = self.pidFileRead();
+ reporter.log('The pid file contained: %s' % (dPids,));
+
+ #
+ # Try convince the processes to quit with increasing impoliteness.
+ #
+ if sys.platform == 'win32':
+ afnMethods = [ processInterrupt, processTerminate ];
+ else:
+ afnMethods = [ sendUserSignal1, processInterrupt, processTerminate, processKill ];
+ for fnMethod in afnMethods:
+ for iPid, tNameSudo in dPids.items():
+ fnMethod(iPid, fSudo = tNameSudo[1]);
+
+ for i in range(10):
+ if i > 0:
+ time.sleep(1);
+
+ dPidsToRemove = []; # Temporary dict to append PIDs to remove later.
+
+ for iPid, tNameSudo in dPids.items():
+ if not processExists(iPid):
+ reporter.log('%s (%s) terminated' % (tNameSudo[0], iPid,));
+ self.pidFileRemove(iPid, fQuiet = True);
+ dPidsToRemove.append(iPid);
+ continue;
+
+ # Remove PIDs from original dictionary, as removing keys from a
+ # dictionary while iterating on it won't work and will result in a RuntimeError.
+ for iPidToRemove in dPidsToRemove:
+ del dPids[iPidToRemove];
+
+ if not dPids:
+ reporter.log('All done.');
+ return True;
+
+ if i in [4, 8]:
+ reporter.log('Still waiting for: %s (method=%s)' % (dPids, fnMethod,));
+
+ reporter.log('Failed to terminate the following processes: %s' % (dPids,));
+ return False;
+
+
+ def onExit(self, iRc):
+ """
+ Hook for doing very important cleanups on the way out.
+
+ iRc is the exit code or -1 in the case of an unhandled exception.
+ Returns nothing and shouldn't raise exceptions (will be muted+ignored).
+ """
+ _ = iRc;
+ return None;
+
+
+ #
+ # main() - don't override anything!
+ #
+
+ def main(self, asArgs = None):
+ """
+ The main function of the test driver.
+
+ Keyword arguments:
+ asArgs -- The argument vector. Defaults to sys.argv.
+
+ Returns exit code. No exceptions.
+ """
+
+ #
+ # Wrap worker in exception handler and always call a 'finally' like
+ # method to do crucial cleanups on the way out.
+ #
+ try:
+ iRc = self.innerMain(asArgs);
+ except:
+ reporter.logXcpt(cFrames = None);
+ try:
+ self.onExit(-1);
+ except:
+ reporter.logXcpt();
+ raise;
+ self.onExit(iRc);
+ return iRc;
+
+
+ def innerMain(self, asArgs = None): # pylint: disable=too-many-statements
+ """
+ Exception wrapped main() worker.
+ """
+
+ #
+ # Parse the arguments.
+ #
+ if asArgs is None:
+ asArgs = list(sys.argv);
+ iArg = 1;
+ try:
+ while iArg < len(asArgs):
+ iNext = self.parseOption(asArgs, iArg);
+ if iNext == iArg:
+ iNext = self.subTstDrvParseOption(asArgs, iArg);
+ if iNext == iArg:
+ raise InvalidOption('unknown option: %s' % (asArgs[iArg]))
+ iArg = iNext;
+ except QuietInvalidOption:
+ return rtexitcode.RTEXITCODE_SYNTAX;
+ except InvalidOption as oXcpt:
+ reporter.error(oXcpt.str());
+ return rtexitcode.RTEXITCODE_SYNTAX;
+ except:
+ reporter.error('unexpected exception while parsing argument #%s' % (iArg));
+ traceback.print_exc();
+ return rtexitcode.RTEXITCODE_SYNTAX;
+
+ if not self.completeOptions():
+ return rtexitcode.RTEXITCODE_SYNTAX;
+
+ if not self.asActions:
+ reporter.error('no action was specified');
+ reporter.error('valid actions: %s' % (self.asNormalActions + self.asSpecialActions + ['all']));
+ return rtexitcode.RTEXITCODE_SYNTAX;
+
+ #
+ # Execte the actions.
+ #
+ fRc = True; # Tristate - True (success), False (failure), None (skipped).
+ asActions = list(self.asActions); # Must copy it or vboxinstaller.py breaks.
+ if 'extract' in asActions:
+ reporter.log('*** extract action ***');
+ asActions.remove('extract');
+ fRc = self.actionExtract();
+ reporter.log('*** extract action completed (fRc=%s) ***' % (fRc));
+ elif 'abort' in asActions:
+ reporter.appendToProcessName('/abort'); # Make it easier to spot in the log.
+ reporter.log('*** abort action ***');
+ asActions.remove('abort');
+ fRc = self.actionAbort();
+ reporter.log('*** abort action completed (fRc=%s) ***' % (fRc));
+ else:
+ if asActions == [ 'all' ]:
+ asActions = list(self.asNormalActions);
+
+ if 'verify' in asActions:
+ reporter.log('*** verify action ***');
+ asActions.remove('verify');
+ fRc = self.actionVerify();
+ if fRc is True: reporter.log("verified succeeded");
+ else: reporter.log("verified failed (fRc=%s)" % (fRc,));
+ reporter.log('*** verify action completed (fRc=%s) ***' % (fRc,));
+
+ if 'cleanup-before' in asActions:
+ reporter.log('*** cleanup-before action ***');
+ asActions.remove('cleanup-before');
+ fRc2 = self.actionCleanupBefore();
+ if fRc2 is not True: reporter.log("cleanup-before failed");
+ if fRc2 is not True and fRc is True: fRc = fRc2;
+ reporter.log('*** cleanup-before action completed (fRc2=%s, fRc=%s) ***' % (fRc2, fRc,));
+
+ self.pidFileAdd(os.getpid(), os.path.basename(sys.argv[0]));
+
+ if 'config' in asActions and fRc is True:
+ asActions.remove('config');
+ reporter.log('*** config action ***');
+ fRc = self.actionConfig();
+ if fRc is True: reporter.log("config succeeded");
+ elif fRc is None: reporter.log("config skipping test");
+ else: reporter.log("config failed");
+ reporter.log('*** config action completed (fRc=%s) ***' % (fRc,));
+
+ if 'execute' in asActions and fRc is True:
+ asActions.remove('execute');
+ reporter.log('*** execute action ***');
+ fRc = self.actionExecute();
+ if fRc is True: reporter.log("execute succeeded");
+ elif fRc is None: reporter.log("execute skipping test");
+ else: reporter.log("execute failed (fRc=%s)" % (fRc,));
+ reporter.testCleanup();
+ reporter.log('*** execute action completed (fRc=%s) ***' % (fRc,));
+
+ if 'cleanup-after' in asActions:
+ reporter.log('*** cleanup-after action ***');
+ asActions.remove('cleanup-after');
+ fRc2 = self.actionCleanupAfter();
+ if fRc2 is not True: reporter.log("cleanup-after failed");
+ if fRc2 is not True and fRc is True: fRc = fRc2;
+ reporter.log('*** cleanup-after action completed (fRc2=%s, fRc=%s) ***' % (fRc2, fRc,));
+
+ self.pidFileRemove(os.getpid());
+
+ if asActions and fRc is True:
+ reporter.error('unhandled actions: %s' % (asActions,));
+ fRc = False;
+
+ #
+ # Done - report the final result.
+ #
+ if fRc is None:
+ if self.fBadTestbox:
+ reporter.log('****************************************************************');
+ reporter.log('*** The test driver SKIPPED the test because of BAD_TESTBOX. ***');
+ reporter.log('****************************************************************');
+ return rtexitcode.RTEXITCODE_BAD_TESTBOX;
+ reporter.log('*****************************************');
+ reporter.log('*** The test driver SKIPPED the test. ***');
+ reporter.log('*****************************************');
+ return rtexitcode.RTEXITCODE_SKIPPED;
+ if fRc is not True:
+ reporter.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
+ reporter.error('!!! The test driver FAILED (in case we forgot to mention it). !!!');
+ reporter.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
+ return rtexitcode.RTEXITCODE_FAILURE;
+ reporter.log('*******************************************');
+ reporter.log('*** The test driver exits successfully. ***');
+ reporter.log('*******************************************');
+ return rtexitcode.RTEXITCODE_SUCCESS;
+
+# The old, deprecated name.
+TestDriver = TestDriverBase; # pylint: disable=invalid-name
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestDriverBaseTestCase(unittest.TestCase):
+ def setUp(self):
+ self.oTstDrv = TestDriverBase();
+ self.oTstDrv.pidFileDelete();
+
+ def tearDown(self):
+ pass; # clean up scratch dir and such.
+
+ def testPidFile(self):
+
+ iPid1 = os.getpid() + 1;
+ iPid2 = os.getpid() + 2;
+
+ self.assertTrue(self.oTstDrv.pidFileAdd(iPid1, 'test1'));
+ self.assertEqual(self.oTstDrv.pidFileRead(), {iPid1:('test1',False)});
+
+ self.assertTrue(self.oTstDrv.pidFileAdd(iPid2, 'test2', fSudo = True));
+ self.assertEqual(self.oTstDrv.pidFileRead(), {iPid1:('test1',False), iPid2:('test2',True)});
+
+ self.assertTrue(self.oTstDrv.pidFileRemove(iPid1));
+ self.assertEqual(self.oTstDrv.pidFileRead(), {iPid2:('test2',True)});
+
+ self.assertTrue(self.oTstDrv.pidFileRemove(iPid2));
+ self.assertEqual(self.oTstDrv.pidFileRead(), {});
+
+ self.assertTrue(self.oTstDrv.pidFileDelete());
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
diff --git a/src/VBox/ValidationKit/testdriver/btresolver.py b/src/VBox/ValidationKit/testdriver/btresolver.py
new file mode 100755
index 00000000..58d39853
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/btresolver.py
@@ -0,0 +1,626 @@
+# -*- coding: utf-8 -*-
+# $Id: btresolver.py $
+# pylint: disable=too-many-lines
+
+"""
+Backtrace resolver using external debugging symbols and RTLdrFlt.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import re;
+import shutil;
+import subprocess;
+
+# Validation Kit imports.
+from common import utils;
+
+def getRTLdrFltPath(asPaths):
+ """
+ Returns the path to the RTLdrFlt tool looking in the provided paths
+ or None if not found.
+ """
+
+ for sPath in asPaths:
+ for sDirPath, _, asFiles in os.walk(sPath):
+ if 'RTLdrFlt' in asFiles:
+ return os.path.join(sDirPath, 'RTLdrFlt');
+
+ return None;
+
+
+
+class BacktraceResolverOs(object):
+ """
+ Base class for all OS specific resolvers.
+ """
+
+ def __init__(self, sScratchPath, sBuildRoot, fnLog = None):
+ self.sScratchPath = sScratchPath;
+ self.sBuildRoot = sBuildRoot;
+ self.fnLog = fnLog;
+
+ def log(self, sText):
+ """
+ Internal logger callback.
+ """
+ if self.fnLog is not None:
+ self.fnLog(sText);
+
+
+
+class BacktraceResolverOsLinux(BacktraceResolverOs):
+ """
+ Linux specific backtrace resolver.
+ """
+
+ def __init__(self, sScratchPath, sBuildRoot, fnLog = None):
+ """
+ Constructs a Linux host specific backtrace resolver.
+ """
+ BacktraceResolverOs.__init__(self, sScratchPath, sBuildRoot, fnLog);
+
+ self.asDbgFiles = {};
+
+ def prepareEnv(self):
+ """
+ Prepares the environment for annotating Linux reports.
+ """
+ fRc = False;
+ try:
+ sDbgArchive = os.path.join(self.sBuildRoot, 'bin', 'VirtualBox-dbg.tar.bz2');
+
+ # Extract debug symbol archive if it was found.
+ if os.path.exists(sDbgArchive):
+ asMembers = utils.unpackFile(sDbgArchive, self.sScratchPath, self.fnLog,
+ self.fnLog);
+ if asMembers:
+ # Populate the list of debug files.
+ for sMember in asMembers:
+ if os.path.isfile(sMember):
+ self.asDbgFiles[os.path.basename(sMember)] = sMember;
+ fRc = True;
+ except:
+ self.log('Failed to setup debug symbols');
+
+ return fRc;
+
+ def cleanupEnv(self):
+ """
+ Cleans up the environment.
+ """
+ fRc = False;
+ try:
+ shutil.rmtree(self.sScratchPath, True);
+ fRc = True;
+ except:
+ pass;
+
+ return fRc;
+
+ def getDbgSymPathFromBinary(self, sBinary, sArch):
+ """
+ Returns the path to file containing the debug symbols for the specified binary.
+ """
+ _ = sArch;
+ sDbgFilePath = None;
+ try:
+ sDbgFilePath = self.asDbgFiles[sBinary];
+ except:
+ pass;
+
+ return sDbgFilePath;
+
+ def getBinaryListWithLoadAddrFromReport(self, asReport):
+ """
+ Parses the given VM state report and returns a list of binaries and their
+ load address.
+
+ Returns a list if tuples containing the binary and load addres or an empty
+ list on failure.
+ """
+ asListBinaries = [];
+
+ # Look for the line "Mapped address spaces:"
+ iLine = 0;
+ while iLine < len(asReport):
+ if asReport[iLine].startswith('Mapped address spaces:'):
+ break;
+ iLine += 1;
+
+ for sLine in asReport[iLine:]:
+ asCandidate = sLine.split();
+ if len(asCandidate) == 5 \
+ and asCandidate[0].startswith('0x') \
+ and asCandidate[1].startswith('0x') \
+ and asCandidate[2].startswith('0x') \
+ and (asCandidate[3] == '0x0' or asCandidate[3] == '0')\
+ and 'VirtualBox' in asCandidate[4]:
+ asListBinaries.append((asCandidate[0], os.path.basename(asCandidate[4])));
+
+ return asListBinaries;
+
+
+
+class BacktraceResolverOsDarwin(BacktraceResolverOs):
+ """
+ Darwin specific backtrace resolver.
+ """
+
+ def __init__(self, sScratchPath, sBuildRoot, fnLog = None):
+ """
+ Constructs a Linux host specific backtrace resolver.
+ """
+ BacktraceResolverOs.__init__(self, sScratchPath, sBuildRoot, fnLog);
+
+ self.asDbgFiles = {};
+
+ def prepareEnv(self):
+ """
+ Prepares the environment for annotating Darwin reports.
+ """
+ fRc = False;
+ try:
+ #
+ # Walk the scratch path directory and look for .dSYM directories, building a
+ # list of them.
+ #
+ asDSymPaths = [];
+
+ for sDirPath, asDirs, _ in os.walk(self.sBuildRoot):
+ for sDir in asDirs:
+ if sDir.endswith('.dSYM'):
+ asDSymPaths.append(os.path.join(sDirPath, sDir));
+
+ # Expand the dSYM paths to full DWARF debug files in the next step
+ # and add them to the debug files dictionary.
+ for sDSymPath in asDSymPaths:
+ sBinary = os.path.basename(sDSymPath).strip('.dSYM');
+ self.asDbgFiles[sBinary] = os.path.join(sDSymPath, 'Contents', 'Resources',
+ 'DWARF', sBinary);
+
+ fRc = True;
+ except:
+ self.log('Failed to setup debug symbols');
+
+ return fRc;
+
+ def cleanupEnv(self):
+ """
+ Cleans up the environment.
+ """
+ fRc = False;
+ try:
+ shutil.rmtree(self.sScratchPath, True);
+ fRc = True;
+ except:
+ pass;
+
+ return fRc;
+
+ def getDbgSymPathFromBinary(self, sBinary, sArch):
+ """
+ Returns the path to file containing the debug symbols for the specified binary.
+ """
+ # Hack to exclude executables as RTLdrFlt has some problems with it currently.
+ _ = sArch;
+ sDbgSym = None;
+ try:
+ sDbgSym = self.asDbgFiles[sBinary];
+ except:
+ pass;
+
+ if sDbgSym is not None and sDbgSym.endswith('.dylib'):
+ return sDbgSym;
+
+ return None;
+
+ def _getReportVersion(self, asReport):
+ """
+ Returns the version of the darwin report.
+ """
+ # Find the line starting with "Report Version:"
+ iLine = 0;
+ iVersion = 0;
+ while iLine < len(asReport):
+ if asReport[iLine].startswith('Report Version:'):
+ break;
+ iLine += 1;
+
+ if iLine < len(asReport):
+ # Look for the start of the number
+ sVersion = asReport[iLine];
+ iStartVersion = len('Report Version:');
+ iEndVersion = len(sVersion);
+
+ while iStartVersion < len(sVersion) \
+ and not sVersion[iStartVersion:iStartVersion+1].isdigit():
+ iStartVersion += 1;
+
+ while iEndVersion > 0 \
+ and not sVersion[iEndVersion-1:iEndVersion].isdigit():
+ iEndVersion -= 1;
+
+ iVersion = int(sVersion[iStartVersion:iEndVersion]);
+ else:
+ self.log('Couldn\'t find the report version');
+
+ return iVersion;
+
+ def _getListOfBinariesFromReportPreSierra(self, asReport):
+ """
+ Returns a list of loaded binaries with their load address obtained from
+ a pre Sierra report.
+ """
+ asListBinaries = [];
+
+ # Find the line starting with "Binary Images:"
+ iLine = 0;
+ while iLine < len(asReport):
+ if asReport[iLine].startswith('Binary Images:'):
+ break;
+ iLine += 1;
+
+ if iLine < len(asReport):
+ # List starts after that
+ iLine += 1;
+
+ # A line for a loaded binary looks like the following:
+ # 0x100042000 - 0x100095fff +VBoxDDU.dylib (4.3.15) <EB19C44D-F882-0803-DBDD-9995723111B7> /Application...
+ # We need the start address and the library name.
+ # To distinguish between our own libraries and ones from Apple we check whether the path at the end starts with
+ # /Applications/VirtualBox.app/Contents/MacOS
+ oRegExpPath = re.compile(r'/VirtualBox.app/Contents/MacOS');
+ oRegExpAddr = re.compile(r'0x\w+');
+ oRegExpBinPath = re.compile(r'VirtualBox.app/Contents/MacOS/\S*');
+ while iLine < len(asReport):
+ asMatches = oRegExpPath.findall(asReport[iLine]);
+ if asMatches:
+ # Line contains the path, extract start address and path to binary
+ sAddr = oRegExpAddr.findall(asReport[iLine]);
+ sPath = oRegExpBinPath.findall(asReport[iLine]);
+
+ if sAddr and sPath:
+ # Construct the path in into the build cache containing the debug symbols
+ oRegExp = re.compile(r'\w+\.{0,1}\w*$');
+ sFilename = oRegExp.findall(sPath[0]);
+
+ asListBinaries.append((sAddr[0], sFilename[0]));
+ else:
+ break; # End of image list
+ iLine += 1;
+ else:
+ self.log('Couldn\'t find the list of loaded binaries in the given report');
+
+ return asListBinaries;
+
+ def _getListOfBinariesFromReportSierra(self, asReport):
+ """
+ Returns a list of loaded binaries with their load address obtained from
+ a Sierra+ report.
+ """
+ asListBinaries = [];
+
+ # A line for a loaded binary looks like the following:
+ # 4 VBoxXPCOMIPCC.dylib 0x00000001139f17ea 0x1139e4000 + 55274
+ # We need the start address and the library name.
+ # To distinguish between our own libraries and ones from Apple we check whether the library
+ # name contains VBox or VirtualBox
+ iLine = 0;
+ while iLine < len(asReport):
+ asStackTrace = asReport[iLine].split();
+
+ # Check whether the line is made up of 6 elements separated by whitespace
+ # and the first one is a number.
+ if len(asStackTrace) == 6 and asStackTrace[0].isdigit() \
+ and (asStackTrace[1].find('VBox') != -1 or asStackTrace[1].find('VirtualBox') != -1) \
+ and asStackTrace[3].startswith('0x'):
+
+ # Check whether the library is already in our list an only add new ones
+ fFound = False;
+ for _, sLibrary in asListBinaries:
+ if asStackTrace[1] == sLibrary:
+ fFound = True;
+ break;
+
+ if not fFound:
+ asListBinaries.append((asStackTrace[3], asStackTrace[1]));
+ iLine += 1;
+
+ return asListBinaries;
+
+ def getBinaryListWithLoadAddrFromReport(self, asReport):
+ """
+ Parses the given VM state report and returns a list of binaries and their
+ load address.
+
+ Returns a list if tuples containing the binary and load addres or an empty
+ list on failure.
+ """
+ asListBinaries = [];
+
+ iVersion = self._getReportVersion(asReport);
+ if iVersion > 0:
+ if iVersion <= 11:
+ self.log('Pre Sierra Report');
+ asListBinaries = self._getListOfBinariesFromReportPreSierra(asReport);
+ elif iVersion == 12:
+ self.log('Sierra report');
+ asListBinaries = self._getListOfBinariesFromReportSierra(asReport);
+ else:
+ self.log('Unsupported report version %s' % (iVersion, ));
+
+ return asListBinaries;
+
+
+
+class BacktraceResolverOsSolaris(BacktraceResolverOs):
+ """
+ Solaris specific backtrace resolver.
+ """
+
+ def __init__(self, sScratchPath, sBuildRoot, fnLog = None):
+ """
+ Constructs a Linux host specific backtrace resolver.
+ """
+ BacktraceResolverOs.__init__(self, sScratchPath, sBuildRoot, fnLog);
+
+ self.asDbgFiles = {};
+
+ def prepareEnv(self):
+ """
+ Prepares the environment for annotating Linux reports.
+ """
+ fRc = False;
+ try:
+ sDbgArchive = os.path.join(self.sBuildRoot, 'bin', 'VirtualBoxDebug.tar.bz2');
+
+ # Extract debug symbol archive if it was found.
+ if os.path.exists(sDbgArchive):
+ asMembers = utils.unpackFile(sDbgArchive, self.sScratchPath, self.fnLog,
+ self.fnLog);
+ if asMembers:
+ # Populate the list of debug files.
+ for sMember in asMembers:
+ if os.path.isfile(sMember):
+ sArch = '';
+ if 'amd64' in sMember:
+ sArch = 'amd64';
+ else:
+ sArch = 'x86';
+ self.asDbgFiles[os.path.basename(sMember) + '/' + sArch] = sMember;
+ fRc = True;
+ else:
+ self.log('Unpacking the debug archive failed');
+ except:
+ self.log('Failed to setup debug symbols');
+
+ return fRc;
+
+ def cleanupEnv(self):
+ """
+ Cleans up the environment.
+ """
+ fRc = False;
+ try:
+ shutil.rmtree(self.sScratchPath, True);
+ fRc = True;
+ except:
+ pass;
+
+ return fRc;
+
+ def getDbgSymPathFromBinary(self, sBinary, sArch):
+ """
+ Returns the path to file containing the debug symbols for the specified binary.
+ """
+ sDbgFilePath = None;
+ try:
+ sDbgFilePath = self.asDbgFiles[sBinary + '/' + sArch];
+ except:
+ pass;
+
+ return sDbgFilePath;
+
+ def getBinaryListWithLoadAddrFromReport(self, asReport):
+ """
+ Parses the given VM state report and returns a list of binaries and their
+ load address.
+
+ Returns a list if tuples containing the binary and load addres or an empty
+ list on failure.
+ """
+ asListBinaries = [];
+
+ # Look for the beginning of the process address space mappings"
+ for sLine in asReport:
+ asItems = sLine.split();
+ if len(asItems) == 4 \
+ and asItems[3].startswith('/opt/VirtualBox') \
+ and ( asItems[2] == 'r-x--' \
+ or asItems[2] == 'r-x----'):
+ fFound = False;
+ sBinaryFile = os.path.basename(asItems[3]);
+ for _, sBinary in asListBinaries:
+ if sBinary == sBinaryFile:
+ fFound = True;
+ break;
+ if not fFound:
+ asListBinaries.append(('0x' + asItems[0], sBinaryFile));
+
+ return asListBinaries;
+
+
+
+class BacktraceResolver(object):
+ """
+ A backtrace resolving class.
+ """
+
+ def __init__(self, sScratchPath, sBuildRoot, sTargetOs, sArch, sRTLdrFltPath = None, fnLog = None):
+ """
+ Constructs a backtrace resolver object for the given target OS,
+ architecture and path to the directory containing the debug symbols and tools
+ we need.
+ """
+ # Initialize all members first.
+ self.sScratchPath = sScratchPath;
+ self.sBuildRoot = sBuildRoot;
+ self.sTargetOs = sTargetOs;
+ self.sArch = sArch;
+ self.sRTLdrFltPath = sRTLdrFltPath;
+ self.fnLog = fnLog;
+ self.sDbgSymPath = None;
+ self.oResolverOs = None;
+ self.sScratchDbgPath = os.path.join(self.sScratchPath, 'dbgsymbols');
+
+ if self.fnLog is None:
+ self.fnLog = self.logStub;
+
+ if self.sRTLdrFltPath is None:
+ self.sRTLdrFltPath = getRTLdrFltPath([self.sScratchPath, self.sBuildRoot]);
+ if self.sRTLdrFltPath is not None:
+ self.log('Found RTLdrFlt in %s' % (self.sRTLdrFltPath,));
+ else:
+ self.log('Couldn\'t find RTLdrFlt in either %s or %s' % (self.sScratchPath, self.sBuildRoot));
+
+ def log(self, sText):
+ """
+ Internal logger callback.
+ """
+ if self.fnLog is not None:
+ self.fnLog(sText);
+
+ def logStub(self, sText):
+ """
+ Logging stub doing nothing.
+ """
+ _ = sText;
+
+ def prepareEnv(self):
+ """
+ Prepares the environment to annotate backtraces, finding the required tools
+ and retrieving the debug symbols depending on the host OS.
+
+ Returns True on success and False on error or if not supported.
+ """
+
+ # No access to the RTLdrFlt tool means no symbols so no point in trying
+ # to set something up.
+ if self.sRTLdrFltPath is None:
+ return False;
+
+ # Create a directory containing the scratch space for the OS resolver backends.
+ fRc = True;
+ if not os.path.exists(self.sScratchDbgPath):
+ try:
+ os.makedirs(self.sScratchDbgPath, 0o750);
+ except:
+ fRc = False;
+ self.log('Failed to create scratch directory for debug symbols');
+
+ if fRc:
+ if self.sTargetOs == 'linux':
+ self.oResolverOs = BacktraceResolverOsLinux(self.sScratchDbgPath, self.sScratchPath, self.fnLog);
+ elif self.sTargetOs == 'darwin':
+ self.oResolverOs = BacktraceResolverOsDarwin(self.sScratchDbgPath, self.sScratchPath, self.fnLog); # pylint: disable=redefined-variable-type
+ elif self.sTargetOs == 'solaris':
+ self.oResolverOs = BacktraceResolverOsSolaris(self.sScratchDbgPath, self.sScratchPath, self.fnLog); # pylint: disable=redefined-variable-type
+ else:
+ self.log('The backtrace resolver is not supported on %s' % (self.sTargetOs,));
+ fRc = False;
+
+ if fRc:
+ fRc = self.oResolverOs.prepareEnv();
+ if not fRc:
+ self.oResolverOs = None;
+
+ if not fRc:
+ shutil.rmtree(self.sScratchDbgPath, True)
+
+ return fRc;
+
+ def cleanupEnv(self):
+ """
+ Prepares the environment to annotate backtraces, finding the required tools
+ and retrieving the debug symbols depending on the host OS.
+
+ Returns True on success and False on error or if not supported.
+ """
+ fRc = False;
+ if self.oResolverOs is not None:
+ fRc = self.oResolverOs.cleanupEnv();
+
+ shutil.rmtree(self.sScratchDbgPath, True);
+ return fRc;
+
+ def annotateReport(self, sReport):
+ """
+ Annotates the given report with the previously prepared environment.
+
+ Returns the annotated report on success or None on failure.
+ """
+ sReportAn = None;
+
+ if self.oResolverOs is not None:
+ asListBinaries = self.oResolverOs.getBinaryListWithLoadAddrFromReport(sReport.split('\n'));
+
+ if asListBinaries:
+ asArgs = [self.sRTLdrFltPath, ];
+
+ for sLoadAddr, sBinary in asListBinaries:
+ sDbgSymPath = self.oResolverOs.getDbgSymPathFromBinary(sBinary, self.sArch);
+ if sDbgSymPath is not None:
+ asArgs.append(sDbgSymPath);
+ asArgs.append(sLoadAddr);
+
+ oRTLdrFltProc = subprocess.Popen(asArgs, stdin=subprocess.PIPE, # pylint: disable=consider-using-with
+ stdout=subprocess.PIPE, bufsize=0);
+ if oRTLdrFltProc is not None:
+ try:
+ sReportAn, _ = oRTLdrFltProc.communicate(sReport);
+ except:
+ self.log('Retrieving annotation report failed (broken pipe / no matching interpreter?)');
+ else:
+ self.log('Error spawning RTLdrFlt process');
+ else:
+ self.log('Getting list of loaded binaries failed');
+ else:
+ self.log('Backtrace resolver not fully initialized, not possible to annotate');
+
+ return sReportAn;
+
diff --git a/src/VBox/ValidationKit/testdriver/reporter.py b/src/VBox/ValidationKit/testdriver/reporter.py
new file mode 100755
index 00000000..e5c55fd0
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/reporter.py
@@ -0,0 +1,1984 @@
+# -*- coding: utf-8 -*-
+# $Id: reporter.py $
+# pylint: disable=too-many-lines
+
+"""
+Testdriver reporter module.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import array
+import datetime
+import errno
+import gc
+import os
+import os.path
+import sys
+import time
+import threading
+import traceback
+
+# Validation Kit imports.
+from common import utils;
+
+## test reporter instance
+g_oReporter = None # type: ReporterBase
+g_sReporterName = None;
+
+
+class ReporterLock(object):
+ """
+ Work around problem with garbage collection triggering __del__ method with
+ logging while inside the logger lock and causing a deadlock.
+ """
+
+ def __init__(self, sName):
+ self.sName = sName;
+ self.oLock = threading.RLock();
+ self.oOwner = None;
+ self.cRecursion = 0;
+ self.fRestoreGC = False;
+
+ def acquire(self):
+ """ Acquire the lock. """
+ oSelf = threading.current_thread();
+
+ # Take the lock.
+ if not self.oLock.acquire(): # pylint: disable=consider-using-with
+ return False;
+
+ self.oOwner = oSelf;
+ self.cRecursion += 1;
+
+ # Disable GC to avoid __del__ w/ log statement randomly reenter the logger.
+ if self.cRecursion == 1:
+ self.fRestoreGC = gc.isenabled();
+ if self.fRestoreGC:
+ gc.disable();
+
+ return True;
+
+ def release(self):
+ """ Release the lock. """
+ oSelf = threading.current_thread();
+
+ # Check the ownership.
+ if oSelf != self.oOwner:
+ raise threading.ThreadError();
+
+ # Drop one recursion.
+ self.cRecursion -= 1;
+ if self.cRecursion <= 0:
+
+ # Final recursion. Clear owner and re-enable GC.
+ self.oOwner = None;
+ if self.fRestoreGC:
+ self.fRestoreGC = False;
+ gc.enable();
+
+ self.oLock.release();
+
+## Reporter lock.
+g_oLock = ReporterLock('reporter');
+
+
+
+class PythonLoggingStream(object):
+ """
+ Python logging => testdriver/reporter.py stream.
+ """
+
+ def write(self, sText):
+ """Writes python log message to our stream."""
+ if g_oReporter is not None:
+ sText = sText.rstrip("\r\n");
+ #g_oReporter.log(0, 'python: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
+ return True;
+
+ def flush(self):
+ """Flushes the stream."""
+ return True;
+
+
+class ReporterBase(object):
+ """
+ Base class for the reporters.
+ """
+
+ def __init__(self):
+ self.iVerbose = 1;
+ self.iDebug = 0;
+ self.cErrors = 0;
+ self.fTimedOut = False; # Once set, it trickles all the way up.
+ self.atTests = [];
+ self.sName = os.path.splitext(os.path.basename(sys.argv[0]))[0];
+
+ # Hook into the python logging.
+ import logging;
+ logging.basicConfig(stream = PythonLoggingStream(),
+ level = logging.DEBUG,
+ format = '%(name)-12s %(levelname)-8s %(message)s');
+ #
+ # Introspection and configuration.
+ #
+
+ def isLocal(self):
+ """Is this a local reporter?"""
+ return False;
+
+ def incVerbosity(self):
+ """Increases the verbosity level."""
+ self.iVerbose += 1;
+
+ def incDebug(self):
+ """Increases the debug level."""
+ self.iDebug += 1;
+
+ def getVerbosity(self):
+ """Returns the current verbosity level."""
+ return self.iVerbose;
+
+ def getDebug(self):
+ """Returns the current debug level."""
+ return self.iDebug;
+
+ def appendToProcessName(self, sAppend):
+ """
+ Appends sAppend to the base process name.
+ Returns the new process name.
+ """
+ self.sName = os.path.splitext(os.path.basename(sys.argv[0]))[0] + sAppend;
+ return self.sName;
+
+
+ #
+ # Generic logging.
+ #
+
+ def log(self, iLevel, sText, sCaller, sTsPrf):
+ """
+ Writes the specfied text to the log if iLevel is less or requal
+ to iVerbose.
+ """
+ _ = iLevel; _ = sText; _ = sCaller; _ = sTsPrf;
+ return 0;
+
+ #
+ # XML output from the reporter.
+ #
+
+ def _xmlEscAttr(self, sValue):
+ """Escapes an XML attribute value."""
+ sValue = sValue.replace('&', '&amp;');
+ sValue = sValue.replace('<', '&lt;');
+ sValue = sValue.replace('>', '&gt;');
+ #sValue = sValue.replace('\'', '&apos;');
+ sValue = sValue.replace('"', '&quot;');
+ sValue = sValue.replace('\n', '&#xA');
+ sValue = sValue.replace('\r', '&#xD');
+ return sValue;
+
+ def _xmlWrite(self, asText, fIndent = True):
+ """XML output function for the reporter."""
+ _ = asText; _ = fIndent;
+ return None;
+
+ def xmlFlush(self, fRetry = False, fForce = False):
+ """Flushes XML output if buffered."""
+ _ = fRetry; _ = fForce;
+ return True;
+
+ #
+ # XML output from child.
+ #
+
+ def subXmlStart(self, oFileWrapper):
+ """Called by the file wrapper when the first bytes are written to the test pipe."""
+ _ = oFileWrapper;
+ return None;
+
+ def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
+ """Called by the file wrapper write method for test pipes."""
+ return self.log(0, 'raw xml%s: %s' % (oFileWrapper.sPrefix, sRawXml), sCaller, utils.getTimePrefix());
+
+ def subXmlEnd(self, oFileWrapper):
+ """Called by the file wrapper __del__ method for test pipes."""
+ _ = oFileWrapper;
+ return None;
+
+ #
+ # File output.
+ #
+
+ def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
+ """
+ Adds the file to the report.
+ Returns True on success, False on failure.
+ """
+ _ = oSrcFile; _ = sSrcFilename; _ = sAltName; _ = sDescription; _ = sKind; _ = sCaller; _ = sTsPrf;
+ return True;
+
+ def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
+ """
+ Adds the file to the report.
+ Returns True on success, False on failure.
+ """
+ _ = sLog; _ = sLogName; _ = sDescription; _ = sKind; _ = sCaller; _ = sTsPrf;
+ return True;
+
+ #
+ # Test reporting
+ #
+
+ def _testGetFullName(self):
+ """
+ Mangles the test names in atTest into a single name to make it easier
+ to spot where we are.
+ """
+ sName = '';
+ for t in self.atTests:
+ if sName != '':
+ sName += ', ';
+ sName += t[0];
+ return sName;
+
+ def testIncErrors(self):
+ """Increates the error count."""
+ self.cErrors += 1;
+ return self.cErrors;
+
+ def testSetTimedOut(self):
+ """Sets time out indicator for the current test and increases the error counter."""
+ self.fTimedOut = True;
+ self.cErrors += 1;
+ return None;
+
+ def testStart(self, sName, sCaller):
+ """ Starts a new test, may be nested. """
+ (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
+ self._xmlWrite([ '<Test timestamp="%s" name="%s">' % (sTsIso, self._xmlEscAttr(sName),), ]);
+ self.atTests.append((sName, self.cErrors, self.fTimedOut));
+ self.fTimedOut = False;
+ return self.log(1, ' %-50s: TESTING' % (self._testGetFullName()), sCaller, sTsPrf);
+
+ def testValue(self, sName, sValue, sUnit, sCaller):
+ """ Reports a benchmark value or something simiarlly useful. """
+ (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
+ self._xmlWrite([ '<Value timestamp="%s" name="%s" unit="%s" value="%s"/>'
+ % (sTsIso, self._xmlEscAttr(sName), self._xmlEscAttr(sUnit), self._xmlEscAttr(sValue)), ]);
+ return self.log(0, '** %-48s: %12s %s' % (sName, sValue, sUnit), sCaller, sTsPrf);
+
+ def testFailure(self, sDetails, sCaller):
+ """ Reports a failure. """
+ (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
+ self.cErrors = self.cErrors + 1;
+ self._xmlWrite([ '<FailureDetails timestamp="%s" text="%s"/>' % (sTsIso, self._xmlEscAttr(sDetails),), ]);
+ return self.log(0, sDetails, sCaller, sTsPrf);
+
+ def testDone(self, fSkipped, sCaller):
+ """
+ Marks the current test as DONE, pops it and maks the next test on the
+ stack current.
+ Returns (name, errors).
+ """
+ (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
+ sFullName = self._testGetFullName();
+
+ # safe pop
+ if not self.atTests:
+ self.log(0, 'testDone on empty test stack!', sCaller, sTsPrf);
+ return ('internal error', 0);
+ fTimedOut = self.fTimedOut;
+ sName, cErrorsStart, self.fTimedOut = self.atTests.pop();
+
+ # log + xml.
+ cErrors = self.cErrors - cErrorsStart;
+ if cErrors == 0:
+ if fSkipped is not True:
+ self._xmlWrite([ ' <Passed timestamp="%s"/>' % (sTsIso,), '</Test>' ],);
+ self.log(1, '** %-50s: PASSED' % (sFullName,), sCaller, sTsPrf);
+ else:
+ self._xmlWrite([ ' <Skipped timestamp="%s"/>' % (sTsIso,), '</Test>' ]);
+ self.log(1, '** %-50s: SKIPPED' % (sFullName,), sCaller, sTsPrf);
+ elif fTimedOut:
+ self._xmlWrite([ ' <TimedOut timestamp="%s" errors="%d"/>' % (sTsIso, cErrors), '</Test>' ]);
+ self.log(0, '** %-50s: TIMED-OUT - %d errors' % (sFullName, cErrors), sCaller, sTsPrf);
+ else:
+ self._xmlWrite([ ' <Failed timestamp="%s" errors="%d"/>' % (sTsIso, cErrors), '</Test>' ]);
+ self.log(0, '** %-50s: FAILED - %d errors' % (sFullName, cErrors), sCaller, sTsPrf);
+
+ # Flush buffers when reaching the last test.
+ if not self.atTests:
+ self.xmlFlush(fRetry = True);
+
+ return (sName, cErrors);
+
+ def testErrorCount(self):
+ """
+ Returns the number of errors accumulated by the current test.
+ """
+ cTests = len(self.atTests);
+ if cTests <= 0:
+ return self.cErrors;
+ return self.cErrors - self.atTests[cTests - 1][1];
+
+ def testCleanup(self, sCaller):
+ """
+ Closes all open test as failed.
+ Returns True if no open tests, False if there were open tests.
+ """
+ if not self.atTests:
+ return True;
+ for _ in range(len(self.atTests)):
+ self.testFailure('Test not closed by test drver', sCaller)
+ self.testDone(False, sCaller);
+ return False;
+
+ #
+ # Misc.
+ #
+
+ def doPollWork(self, sDebug = None):
+ """
+ Check if any pending stuff expired and needs doing.
+ """
+ _ = sDebug;
+ return None;
+
+
+
+
+class LocalReporter(ReporterBase):
+ """
+ Local reporter instance.
+ """
+
+ def __init__(self):
+ ReporterBase.__init__(self);
+ self.oLogFile = None;
+ self.oXmlFile = None;
+ self.fXmlOk = True;
+ self.iSubXml = 0;
+ self.iOtherFile = 0;
+ self.fnGetIsoTimestamp = utils.getIsoTimestamp; # Hack to get a timestamp in __del__.
+ self.oStdErr = sys.stderr; # Hack for __del__ output.
+
+ #
+ # Figure the main log directory.
+ #
+ try:
+ self.sDefLogDir = os.path.abspath(os.path.expanduser(os.path.join('~', 'VBoxTestLogs')));
+ except:
+ self.sDefLogDir = os.path.abspath("VBoxTestLogs");
+ try:
+ sLogDir = os.path.abspath(os.environ.get('TESTBOX_REPORTER_LOG_DIR', self.sDefLogDir));
+ if not os.path.isdir(sLogDir):
+ os.makedirs(sLogDir, 0o750);
+ except:
+ sLogDir = self.sDefLogDir;
+ if not os.path.isdir(sLogDir):
+ os.makedirs(sLogDir, 0o750);
+
+ #
+ # Make a subdirectory for this test run.
+ #
+ sTs = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H-%M-%S.log');
+ self.sLogDir = sLogDir = os.path.join(sLogDir, '%s-%s' % (sTs, self.sName));
+ try:
+ os.makedirs(self.sLogDir, 0o750);
+ except:
+ self.sLogDir = '%s-%s' % (self.sLogDir, os.getpid());
+ os.makedirs(self.sLogDir, 0o750);
+
+ #
+ # Open the log file and write a header.
+ #
+ sLogName = os.path.join(self.sLogDir, 'testsuite.log');
+ sTsIso = utils.getIsoTimestamp();
+ if sys.version_info[0] >= 3: # Add 'b' to prevent write taking issue with encode('utf-8') not returning a string.
+ self.oLogFile = utils.openNoInherit(sLogName, "wb");
+ else:
+ self.oLogFile = utils.openNoInherit(sLogName, "w");
+ self.oLogFile.write(('Created log file at %s.\nRunning: %s' % (sTsIso, sys.argv)).encode('utf-8'));
+
+ #
+ # Open the xml log file and write the mandatory introduction.
+ #
+ # Note! This is done here and not in the base class because the remote
+ # logger doesn't really need this. It doesn't need the outer
+ # test wrapper either.
+ #
+ sXmlName = os.path.join(self.sLogDir, 'testsuite.xml');
+ if sys.version_info[0] >= 3: # Add 'b' to prevent write taking issue with encode('utf-8') not returning a string.
+ self.oXmlFile = utils.openNoInherit(sXmlName, "wb");
+ else:
+ self.oXmlFile = utils.openNoInherit(sXmlName, "w");
+ self._xmlWrite([ '<?xml version="1.0" encoding="UTF-8" ?>',
+ '<Test timestamp="%s" name="%s">' % (sTsIso, self._xmlEscAttr(self.sName),), ],
+ fIndent = False);
+
+ def __del__(self):
+ """Ends and completes the log files."""
+ try: sTsIso = self.fnGetIsoTimestamp();
+ except Exception as oXcpt:
+ sTsIso = str(oXcpt);
+
+ if self.oLogFile is not None:
+ try:
+ self.oLogFile.write(('\nThe End %s\n' % (sTsIso,)).encode('utf-8'));
+ self.oLogFile.close();
+ except: pass;
+ self.oLogFile = None;
+
+ if self.oXmlFile is not None:
+ self._closeXml(sTsIso);
+ self.oXmlFile = None;
+
+ def _closeXml(self, sTsIso):
+ """Closes the XML file."""
+ if self.oXmlFile is not None:
+ # pop the test stack
+ while self.atTests:
+ sName, cErrorsStart, self.fTimedOut = self.atTests.pop();
+ self._xmlWrite([ '<End timestamp="%s" errors="%d"/>' % (sTsIso, self.cErrors - cErrorsStart,),
+ '</%s>' % (sName,), ]);
+
+ # The outer one is not on the stack.
+ self._xmlWrite([ ' <End timestamp="%s"/>' % (sTsIso,),
+ '</Test>', ], fIndent = False);
+ try:
+ self.oXmlFile.close();
+ self.oXmlFile = None;
+ except:
+ pass;
+
+ def _xmlWrite(self, asText, fIndent = True):
+ """Writes to the XML file."""
+ for sText in asText:
+ if fIndent:
+ sIndent = ''.ljust((len(self.atTests) + 1) * 2);
+ sText = sIndent + sText;
+ sText += '\n';
+
+ try:
+ self.oXmlFile.write(sText.encode('utf-8'));
+ except:
+ if self.fXmlOk:
+ traceback.print_exc();
+ self.fXmlOk = False;
+ return False;
+ return True;
+
+ #
+ # Overridden methods.
+ #
+
+ def isLocal(self):
+ """Is this a local reporter?"""
+ return True;
+
+ def log(self, iLevel, sText, sCaller, sTsPrf):
+ if iLevel <= self.iVerbose:
+ # format it.
+ if self.iDebug <= 0:
+ sLogText = '%s %s' % (sTsPrf, sText);
+ elif self.iDebug <= 1:
+ sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
+ else:
+ sLogText = '%s e=%u %30s: %s' % (sTsPrf, self.cErrors, sCaller, sText);
+
+ # output it.
+ if sys.version_info[0] >= 3:
+ sAscii = sLogText;
+ else:
+ sAscii = sLogText.encode('ascii', 'replace');
+ if self.iDebug == 0:
+ print('%s: %s' % (self.sName, sAscii), file = self.oStdErr);
+ else:
+ print('%s' % (sAscii), file = self.oStdErr);
+ sLogText += '\n';
+ try:
+ self.oLogFile.write(sLogText.encode('utf-8'));
+ except:
+ pass;
+ return 0;
+
+ def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
+ # Figure the destination filename.
+ iOtherFile = self.iOtherFile;
+ self.iOtherFile += 1;
+ sDstFilename = os.path.join(self.sLogDir, 'other-%d-%s.log' \
+ % (iOtherFile, os.path.splitext(os.path.basename(sSrcFilename))[0]));
+ self.log(0, '** Other log file: %s - %s (%s)' % (sDstFilename, sDescription, sSrcFilename), sCaller, sTsPrf);
+
+ # Open the destination file and copy over the data.
+ fRc = True;
+ try:
+ oDstFile = utils.openNoInherit(sDstFilename, 'wb');
+ except Exception as oXcpt:
+ self.log(0, 'error opening %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
+ else:
+ while True:
+ try:
+ abBuf = oSrcFile.read(65536);
+ except Exception as oXcpt:
+ fRc = False;
+ self.log(0, 'error reading %s: %s' % (sSrcFilename, oXcpt), sCaller, sTsPrf);
+ else:
+ try:
+ oDstFile.write(abBuf);
+ except Exception as oXcpt:
+ fRc = False;
+ self.log(0, 'error writing %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
+ else:
+ if abBuf:
+ continue;
+ break;
+ oDstFile.close();
+
+ # Leave a mark in the XML log.
+ self._xmlWrite(['<LogFile timestamp="%s" filename="%s" source="%s" kind="%s" ok="%s">%s</LogFile>\n'
+ % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sDstFilename)), self._xmlEscAttr(sSrcFilename), \
+ self._xmlEscAttr(sKind), fRc, self._xmlEscAttr(sDescription))] );
+ _ = sAltName;
+ return fRc;
+
+ def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
+ # Figure the destination filename.
+ iOtherFile = self.iOtherFile;
+ self.iOtherFile += 1;
+ sDstFilename = os.path.join(self.sLogDir, 'other-%d-%s.log' \
+ % (iOtherFile, os.path.splitext(os.path.basename(sLogName))[0]));
+ self.log(0, '** Other log file: %s - %s (%s)' % (sDstFilename, sDescription, sLogName), sCaller, sTsPrf);
+
+ # Open the destination file and copy over the data.
+ fRc = True;
+ try:
+ oDstFile = utils.openNoInherit(sDstFilename, 'w');
+ except Exception as oXcpt:
+ self.log(0, 'error opening %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
+ else:
+ try:
+ oDstFile.write(sLog);
+ except Exception as oXcpt:
+ fRc = False;
+ self.log(0, 'error writing %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
+
+ oDstFile.close();
+
+ # Leave a mark in the XML log.
+ self._xmlWrite(['<LogFile timestamp="%s" filename="%s" source="%s" kind="%s" ok="%s">%s</LogFile>\n'
+ % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sDstFilename)), self._xmlEscAttr(sLogName), \
+ self._xmlEscAttr(sKind), fRc, self._xmlEscAttr(sDescription))] );
+ return fRc;
+
+ def subXmlStart(self, oFileWrapper):
+ # Open a new file and just include it from the main XML.
+ iSubXml = self.iSubXml;
+ self.iSubXml += 1;
+ sSubXmlName = os.path.join(self.sLogDir, 'sub-%d.xml' % (iSubXml,));
+ try:
+ oFileWrapper.oSubXmlFile = utils.openNoInherit(sSubXmlName, "w");
+ except:
+ errorXcpt('open(%s)' % oFileWrapper.oSubXmlName);
+ oFileWrapper.oSubXmlFile = None;
+ else:
+ self._xmlWrite(['<Include timestamp="%s" filename="%s"/>\n'
+ % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sSubXmlName)))]);
+ return None;
+
+ def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
+ if oFileWrapper.oSubXmlFile is not None:
+ try:
+ oFileWrapper.oSubXmlFile.write(sRawXml);
+ except:
+ pass;
+ if sCaller is None: pass; # pychecker - NOREF
+ return None;
+
+ def subXmlEnd(self, oFileWrapper):
+ if oFileWrapper.oSubXmlFile is not None:
+ try:
+ oFileWrapper.oSubXmlFile.close();
+ oFileWrapper.oSubXmlFile = None;
+ except:
+ pass;
+ return None;
+
+
+
+class RemoteReporter(ReporterBase):
+ """
+ Reporter that talks to the test manager server.
+ """
+
+
+ ## The XML sync min time (seconds).
+ kcSecXmlFlushMin = 30;
+ ## The XML sync max time (seconds).
+ kcSecXmlFlushMax = 120;
+ ## The XML sync idle time before flushing (seconds).
+ kcSecXmlFlushIdle = 5;
+ ## The XML sync line count threshold.
+ kcLinesXmlFlush = 512;
+
+ ## The retry timeout.
+ kcSecTestManagerRetryTimeout = 120;
+ ## The request timeout.
+ kcSecTestManagerRequestTimeout = 30;
+
+
+ def __init__(self):
+ ReporterBase.__init__(self);
+ self.sTestManagerUrl = os.environ.get('TESTBOX_MANAGER_URL');
+ self.sTestBoxUuid = os.environ.get('TESTBOX_UUID');
+ self.idTestBox = int(os.environ.get('TESTBOX_ID'));
+ self.idTestSet = int(os.environ.get('TESTBOX_TEST_SET_ID'));
+ self._asXml = [];
+ self._secTsXmlFlush = utils.timestampSecond();
+ self._secTsXmlLast = self._secTsXmlFlush;
+ self._fXmlFlushing = False;
+ self.oOutput = sys.stdout; # Hack for __del__ output.
+ self.fFlushEachLine = True;
+ self.fDebugXml = 'TESTDRIVER_REPORTER_DEBUG_XML' in os.environ;
+
+ # Prepare the TM connecting.
+ from common import constants;
+ if sys.version_info[0] >= 3:
+ import urllib;
+ self._fnUrlEncode = urllib.parse.urlencode; # pylint: disable=no-member
+ self._fnUrlParseQs = urllib.parse.parse_qs; # pylint: disable=no-member
+ self._oParsedTmUrl = urllib.parse.urlparse(self.sTestManagerUrl); # pylint: disable=no-member
+ import http.client as httplib; # pylint: disable=no-name-in-module,import-error
+ else:
+ import urllib;
+ self._fnUrlEncode = urllib.urlencode; # pylint: disable=no-member
+ import urlparse; # pylint: disable=import-error
+ self._fnUrlParseQs = urlparse.parse_qs; # pylint: disable=no-member
+ self._oParsedTmUrl = urlparse.urlparse(self.sTestManagerUrl); # pylint: disable=no-member
+ import httplib; # pylint: disable=no-name-in-module,import-error
+
+ if sys.version_info[0] >= 3 \
+ or (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
+ if self._oParsedTmUrl.scheme == 'https': # pylint: disable=no-member
+ self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname,
+ timeout = self.kcSecTestManagerRequestTimeout);
+ else:
+ self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname,
+ timeout = self.kcSecTestManagerRequestTimeout);
+ else:
+ if self._oParsedTmUrl.scheme == 'https': # pylint: disable=no-member
+ self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname);
+ else:
+ self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname);
+ self._dHttpHeader = \
+ {
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
+ 'User-Agent': 'TestDriverReporter/%s.0 (%s, %s)' % (__version__, utils.getHostOs(), utils.getHostArch(),),
+ 'Accept': 'text/plain,application/x-www-form-urlencoded',
+ 'Accept-Encoding': 'identity',
+ 'Cache-Control': 'max-age=0',
+ #'Connection': 'keep-alive',
+ };
+
+ dParams = {
+ constants.tbreq.ALL_PARAM_TESTBOX_UUID: self.sTestBoxUuid,
+ constants.tbreq.ALL_PARAM_TESTBOX_ID: self.idTestBox,
+ constants.tbreq.RESULT_PARAM_TEST_SET_ID: self.idTestSet,
+ };
+ self._sTmServerPath = '/%s/testboxdisp.py?%s' \
+ % ( self._oParsedTmUrl.path.strip('/'), # pylint: disable=no-member
+ self._fnUrlEncode(dParams), );
+
+ def __del__(self):
+ """Flush pending log messages?"""
+ if self._asXml:
+ self._xmlDoFlush(self._asXml, fRetry = True, fDtor = True);
+
+ def _writeOutput(self, sText):
+ """ Does the actual writing and flushing. """
+ if sys.version_info[0] >= 3:
+ print(sText, file = self.oOutput);
+ else:
+ print(sText.encode('ascii', 'replace'), file = self.oOutput);
+ if self.fFlushEachLine: self.oOutput.flush();
+ return None;
+
+ #
+ # Talking to TM.
+ #
+
+ def _processTmStatusResponse(self, oConn, sOperation, fClose = True):
+ """
+ Processes HTTP reponse from the test manager.
+ Returns True, False or None. None should be retried, the others not.
+ May raise exception on HTTP issue (retry ok).
+ """
+ if sys.version_info[0] >= 3: import http.client as httplib; # pylint: disable=no-name-in-module,import-error
+ else: import httplib; # pylint: disable=import-error
+ from common import constants;
+
+ # Read the response and (optionally) close the connection.
+ oResponse = oConn.getresponse();
+ try:
+ sRspBody = oResponse.read();
+ except httplib.IncompleteRead as oXcpt:
+ self._writeOutput('%s: %s: Warning: httplib.IncompleteRead: %s [expected %s, got %s]'
+ % (utils.getTimePrefix(), sOperation, oXcpt, oXcpt.expected, len(oXcpt.partial),));
+ sRspBody = oXcpt.partial;
+ if fClose is True:
+ try: oConn.close();
+ except: pass;
+
+ # Make sure it's a string which encoding we grok.
+ if hasattr(sRspBody, 'decode'):
+ sRspBody = sRspBody.decode('utf-8', 'ignore');
+
+ # Check the content type.
+ sContentType = oResponse.getheader('Content-Type');
+ if sContentType is not None and sContentType == 'application/x-www-form-urlencoded; charset=utf-8':
+
+ # Parse the body and check the RESULT parameter.
+ dResponse = self._fnUrlParseQs(sRspBody, strict_parsing = True);
+ sResult = dResponse.get(constants.tbresp.ALL_PARAM_RESULT, None);
+ if isinstance(sResult, list):
+ sResult = sResult[0] if len(sResult) == 1 else '%d results' % (len(sResult),);
+
+ if sResult is not None:
+ if sResult == constants.tbresp.STATUS_ACK:
+ return True;
+ if sResult == constants.tbresp.STATUS_NACK:
+ self._writeOutput('%s: %s: Failed (%s). (dResponse=%s)'
+ % (utils.getTimePrefix(), sOperation, sResult, dResponse,));
+ return False;
+
+ self._writeOutput('%s: %s: Failed - dResponse=%s' % (utils.getTimePrefix(), sOperation, dResponse,));
+ else:
+ self._writeOutput('%s: %s: Unexpected Content-Type: %s' % (utils.getTimePrefix(), sOperation, sContentType,));
+ self._writeOutput('%s: %s: Body: %s' % (utils.getTimePrefix(), sOperation, sRspBody,));
+ return None;
+
+ def _doUploadFile(self, oSrcFile, sSrcFilename, sDescription, sKind, sMime):
+ """ Uploads the given file to the test manager. """
+
+ # Prepare header and url.
+ dHeader = dict(self._dHttpHeader);
+ dHeader['Content-Type'] = 'application/octet-stream';
+ self._writeOutput('%s: _doUploadFile: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
+ oSrcFile.seek(0, 2);
+ cbFileSize = oSrcFile.tell();
+ self._writeOutput('%s: _doUploadFile: size=%d' % (utils.getTimePrefix(), cbFileSize,));
+ oSrcFile.seek(0);
+
+ if cbFileSize <= 0: # The Test Manager will bitch if the file size is 0, so skip uploading.
+ self._writeOutput('%s: _doUploadFile: Empty file, skipping upload' % utils.getTimePrefix());
+ return False;
+
+ from common import constants;
+ sUrl = self._sTmServerPath + '&' \
+ + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcFilename),
+ constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
+ constants.tbreq.UPLOAD_PARAM_KIND: sKind,
+ constants.tbreq.UPLOAD_PARAM_MIME: sMime,
+ constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
+ });
+
+ # Retry loop.
+ secStart = utils.timestampSecond();
+ while True:
+ try:
+ oConn = self._fnTmConnect();
+ oConn.request('POST', sUrl, oSrcFile.read(), dHeader);
+ fRc = self._processTmStatusResponse(oConn, '_doUploadFile', fClose = True);
+ oConn.close();
+ if fRc is not None:
+ return fRc;
+ except:
+ logXcpt('warning: exception during UPLOAD request');
+
+ if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
+ self._writeOutput('%s: _doUploadFile: Timed out.' % (utils.getTimePrefix(),));
+ break;
+ try: oSrcFile.seek(0);
+ except:
+ logXcpt();
+ break;
+ self._writeOutput('%s: _doUploadFile: Retrying...' % (utils.getTimePrefix(), ));
+ time.sleep(2);
+
+ return False;
+
+ def _doUploadString(self, sSrc, sSrcName, sDescription, sKind, sMime):
+ """ Uploads the given string as a separate file to the test manager. """
+
+ # Prepare header and url.
+ dHeader = dict(self._dHttpHeader);
+ dHeader['Content-Type'] = 'application/octet-stream';
+ self._writeOutput('%s: _doUploadString: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
+ self._writeOutput('%s: _doUploadString: size=%d' % (utils.getTimePrefix(), sys.getsizeof(sSrc),));
+
+ from common import constants;
+ sUrl = self._sTmServerPath + '&' \
+ + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcName),
+ constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
+ constants.tbreq.UPLOAD_PARAM_KIND: sKind,
+ constants.tbreq.UPLOAD_PARAM_MIME: sMime,
+ constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
+ });
+
+ # Retry loop.
+ secStart = utils.timestampSecond();
+ while True:
+ try:
+ oConn = self._fnTmConnect();
+ oConn.request('POST', sUrl, sSrc, dHeader);
+ fRc = self._processTmStatusResponse(oConn, '_doUploadString', fClose = True);
+ oConn.close();
+ if fRc is not None:
+ return fRc;
+ except:
+ logXcpt('warning: exception during UPLOAD request');
+
+ if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
+ self._writeOutput('%s: _doUploadString: Timed out.' % (utils.getTimePrefix(),));
+ break;
+ self._writeOutput('%s: _doUploadString: Retrying...' % (utils.getTimePrefix(), ));
+ time.sleep(2);
+
+ return False;
+
+ def _xmlDoFlush(self, asXml, fRetry = False, fDtor = False):
+ """
+ The code that does the actual talking to the server.
+ Used by both xmlFlush and __del__.
+ """
+ secStart = utils.timestampSecond();
+ while True:
+ fRc = None;
+ try:
+ # Post.
+ from common import constants;
+ sPostBody = self._fnUrlEncode({constants.tbreq.XML_RESULT_PARAM_BODY: '\n'.join(asXml),});
+ oConn = self._fnTmConnect();
+ oConn.request('POST',
+ self._sTmServerPath + ('&%s=%s' % (constants.tbreq.ALL_PARAM_ACTION, constants.tbreq.XML_RESULTS)),
+ sPostBody,
+ self._dHttpHeader);
+
+ fRc = self._processTmStatusResponse(oConn, '_xmlDoFlush', fClose = True);
+ if fRc is True:
+ if self.fDebugXml:
+ self._writeOutput('_xmlDoFlush:\n%s' % ('\n'.join(asXml),));
+ return (None, False);
+ if fRc is False:
+ self._writeOutput('_xmlDoFlush: Failed - we should abort the test, really.');
+ return (None, True);
+ except Exception as oXcpt:
+ if not fDtor:
+ logXcpt('warning: exception during XML_RESULTS request');
+ else:
+ self._writeOutput('warning: exception during XML_RESULTS request: %s' % (oXcpt,));
+
+ if fRetry is not True \
+ or utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
+ break;
+ time.sleep(2);
+
+ return (asXml, False);
+
+
+ #
+ # Overridden methods.
+ #
+
+ def isLocal(self):
+ return False;
+
+ def log(self, iLevel, sText, sCaller, sTsPrf):
+ if iLevel <= self.iVerbose:
+ if self.iDebug <= 0:
+ sLogText = '%s %s' % (sTsPrf, sText);
+ elif self.iDebug <= 1:
+ sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
+ else:
+ sLogText = '%s e=%u %30s: %s' % (sTsPrf, self.cErrors, sCaller, sText);
+ self._writeOutput(sLogText);
+ return 0;
+
+ def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
+ fRc = True;
+ if sKind in [ 'text', 'log', 'process'] \
+ or sKind.startswith('log/') \
+ or sKind.startswith('info/') \
+ or sKind.startswith('process/'):
+ self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
+ % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
+ self.xmlFlush();
+ g_oLock.release();
+ try:
+ self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'text/plain');
+ finally:
+ g_oLock.acquire();
+ elif sKind.startswith('screenshot/'):
+ self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
+ % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
+ self.xmlFlush();
+ g_oLock.release();
+ try:
+ self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'image/png');
+ finally:
+ g_oLock.acquire();
+ elif sKind.startswith('screenrecording/'):
+ self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
+ % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
+ self.xmlFlush();
+ g_oLock.release();
+ try:
+ self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'video/webm');
+ finally:
+ g_oLock.acquire();
+ elif sKind.startswith('misc/'):
+ self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
+ % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
+ self.xmlFlush();
+ g_oLock.release();
+ try:
+ self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'application/octet-stream');
+ finally:
+ g_oLock.acquire();
+ else:
+ self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
+ % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
+ return fRc;
+
+ def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
+ fRc = True;
+ if sKind in [ 'text', 'log', 'process'] \
+ or sKind.startswith('log/') \
+ or sKind.startswith('info/') \
+ or sKind.startswith('process/'):
+ self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
+ % (sLogName, sKind, sDescription), sCaller, sTsPrf);
+ self.xmlFlush();
+ g_oLock.release();
+ try:
+ self._doUploadString(sLog, sLogName, sDescription, sKind, 'text/plain');
+ finally:
+ g_oLock.acquire();
+ else:
+ self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
+ % (sLogName, sKind, sDescription), sCaller, sTsPrf);
+ return fRc;
+
+ def xmlFlush(self, fRetry = False, fForce = False):
+ """
+ Flushes the XML back log. Called with the lock held, may leave it
+ while communicating with the server.
+ """
+ if not self._fXmlFlushing:
+ asXml = self._asXml;
+ self._asXml = [];
+ if asXml or fForce is True:
+ self._fXmlFlushing = True;
+
+ g_oLock.release();
+ try:
+ (asXml, fIncErrors) = self._xmlDoFlush(asXml, fRetry = fRetry);
+ finally:
+ g_oLock.acquire();
+
+ if fIncErrors:
+ self.testIncErrors();
+
+ self._fXmlFlushing = False;
+ if asXml is None:
+ self._secTsXmlFlush = utils.timestampSecond();
+ else:
+ self._asXml = asXml + self._asXml;
+ return True;
+
+ self._secTsXmlFlush = utils.timestampSecond();
+ return False;
+
+ def _xmlFlushIfNecessary(self, fPolling = False, sDebug = None):
+ """Flushes the XML back log if necessary."""
+ tsNow = utils.timestampSecond();
+ cSecs = tsNow - self._secTsXmlFlush;
+ cSecsLast = tsNow - self._secTsXmlLast;
+ if fPolling is not True:
+ self._secTsXmlLast = tsNow;
+
+ # Absolute flush thresholds.
+ if cSecs >= self.kcSecXmlFlushMax:
+ return self.xmlFlush();
+ if len(self._asXml) >= self.kcLinesXmlFlush:
+ return self.xmlFlush();
+
+ # Flush if idle long enough.
+ if cSecs >= self.kcSecXmlFlushMin \
+ and cSecsLast >= self.kcSecXmlFlushIdle:
+ return self.xmlFlush();
+
+ _ = sDebug;
+ return False;
+
+ def _xmlWrite(self, asText, fIndent = True):
+ """XML output function for the reporter."""
+ self._asXml += asText;
+ self._xmlFlushIfNecessary();
+ _ = fIndent; # No pretty printing, thank you.
+ return None;
+
+ def subXmlStart(self, oFileWrapper):
+ oFileWrapper.sXmlBuffer = '';
+ return None;
+
+ def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
+ oFileWrapper.sXmlBuffer += sRawXml;
+ _ = sCaller;
+ return None;
+
+ def subXmlEnd(self, oFileWrapper):
+ sRawXml = oFileWrapper.sXmlBuffer;
+ ## @todo should validate the document here and maybe auto terminate things. Adding some hints to have the server do
+ # this instead.
+ g_oLock.acquire();
+ try:
+ self._asXml += [ '<PushHint testdepth="%d"/>' % (len(self.atTests),),
+ sRawXml,
+ '<PopHint testdepth="%d"/>' % (len(self.atTests),),];
+ self._xmlFlushIfNecessary();
+ finally:
+ g_oLock.release();
+ return None;
+
+ def doPollWork(self, sDebug = None):
+ if self._asXml:
+ g_oLock.acquire();
+ try:
+ self._xmlFlushIfNecessary(fPolling = True, sDebug = sDebug);
+ finally:
+ g_oLock.release();
+ return None;
+
+
+#
+# Helpers
+#
+
+g_fnComXcptFormatter = None;
+
+def setComXcptFormatter(fnCallback):
+ """
+ Install callback for prettier COM exception formatting.
+
+ The callback replaces the work done by format_exception_only() and
+ takes the same arguments. It returns None if not interested in the
+ exception.
+ """
+ global g_fnComXcptFormatter;
+ g_fnComXcptFormatter = fnCallback;
+ return True;
+
+def formatExceptionOnly(oType, oXcpt, sCaller, sTsPrf):
+ """
+ Wrapper around traceback.format_exception_only and __g_fnComXcptFormatter.
+ """
+ #asRet = ['oType=%s type(oXcpt)=%s' % (oType, type(oXcpt),)];
+ asRet = [];
+
+ # Try the callback first.
+ fnCallback = g_fnComXcptFormatter;
+ if fnCallback:
+ try:
+ asRetCb = fnCallback(oType, oXcpt);
+ if asRetCb:
+ return asRetCb;
+ #asRet += asRetCb;
+ except:
+ g_oReporter.log(0, '** internal-error: Hit exception #2 in __g_fnComXcptFormatter! %s'
+ % (traceback.format_exc()), sCaller, sTsPrf);
+ asRet += ['internal error: exception in __g_fnComXcptFormatter'];
+
+ # Now try format_exception_only:
+ try:
+ asRet += traceback.format_exception_only(oType, oXcpt);
+ except:
+ g_oReporter.log(0, '** internal-error: Hit exception #2 in format_exception_only! %s'
+ % (traceback.format_exc()), sCaller, sTsPrf);
+ asRet += ['internal error: Exception in format_exception_only!'];
+ return asRet;
+
+
+def logXcptWorker(iLevel, fIncErrors, sPrefix="", sText=None, cFrames=1):
+ """
+ Log an exception, optionally with a preceeding message and more than one
+ call frame.
+ """
+ g_oLock.acquire();
+ try:
+
+ if fIncErrors:
+ g_oReporter.testIncErrors();
+
+ ## @todo skip all this if iLevel is too high!
+
+ # Try get exception info.
+ sTsPrf = utils.getTimePrefix();
+ try:
+ oType, oValue, oTraceback = sys.exc_info();
+ except:
+ oType = oValue = oTraceback = None;
+ if oType is not None:
+
+ # Try format the info
+ try:
+ rc = 0;
+ sCaller = utils.getCallerName(oTraceback.tb_frame);
+ if sText is not None:
+ rc = g_oReporter.log(iLevel, "%s%s" % (sPrefix, sText), sCaller, sTsPrf);
+ asInfo = None;
+ try:
+ asInfo = formatExceptionOnly(oType, oValue, sCaller, sTsPrf);
+ atEntries = traceback.extract_tb(oTraceback);
+ atEntries.reverse();
+ if cFrames is not None and cFrames <= 1:
+ if atEntries:
+ asInfo = asInfo + traceback.format_list(atEntries[:1]);
+ else:
+ asInfo.append('Traceback (stack order):')
+ if cFrames is not None and cFrames < len(atEntries):
+ asInfo = asInfo + traceback.format_list(atEntries[:cFrames]);
+ else:
+ asInfo = asInfo + traceback.format_list(atEntries);
+ asInfo.append('Stack:')
+ asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
+ except:
+ g_oReporter.log(0, '** internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
+
+ if asInfo:
+ # Do the logging.
+ for sItem in asInfo:
+ asLines = sItem.splitlines();
+ for sLine in asLines:
+ rc = g_oReporter.log(iLevel, '%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
+
+ else:
+ g_oReporter.log(iLevel, 'No exception info...', sCaller, sTsPrf);
+ rc = -3;
+ except:
+ g_oReporter.log(0, '** internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
+ rc = -2;
+ else:
+ g_oReporter.log(0, '** internal-error: No exception! %s'
+ % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
+ rc = -1;
+
+ finally:
+ g_oLock.release();
+ return rc;
+
+
+#
+# The public Classes
+#
+class FileWrapper(object):
+ """ File like class for TXS EXEC and similar. """
+ def __init__(self, sPrefix):
+ self.sPrefix = sPrefix;
+
+ def __del__(self):
+ self.close();
+
+ def close(self):
+ """ file.close """
+ # Nothing to be done.
+ return;
+
+ def read(self, cb):
+ """file.read"""
+ _ = cb;
+ return "";
+
+ def write(self, sText):
+ """file.write"""
+ if not utils.isString(sText):
+ if isinstance(sText, array.array):
+ try:
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ sText = sText.tostring(); # pylint: disable=no-member
+ else:
+ sText = sText.tobytes();
+ except:
+ pass;
+ if hasattr(sText, 'decode'):
+ try:
+ sText = sText.decode('utf-8', 'ignore');
+ except:
+ pass;
+ g_oLock.acquire();
+ try:
+ sTsPrf = utils.getTimePrefix();
+ sCaller = utils.getCallerName();
+ asLines = sText.splitlines();
+ for sLine in asLines:
+ g_oReporter.log(0, '%s: %s' % (self.sPrefix, sLine), sCaller, sTsPrf);
+ except:
+ traceback.print_exc();
+ finally:
+ g_oLock.release();
+ return None;
+
+class FileWrapperTestPipe(object):
+ """
+ File like class for the test pipe (TXS EXEC and similar).
+
+ This is also used to submit XML test result files.
+ """
+ def __init__(self):
+ self.sPrefix = '';
+ self.fStarted = False;
+ self.fClosed = False;
+ self.sTagBuffer = None;
+ self.cTestDepth = 0;
+ self.acTestErrors = [];
+
+ def __del__(self):
+ self.close();
+
+ def close(self):
+ """ file.close """
+ if self.fStarted is True and self.fClosed is False:
+ self.fClosed = True;
+
+ # Close open <Test> elements:
+ if self.cTestDepth > 0:
+ sNow = utils.getIsoTimestamp()
+ cErrors = 0;
+ while self.cTestDepth > 0:
+ self.cTestDepth -= 1;
+ if self.acTestErrors:
+ cErrors += self.acTestErrors.pop();
+ cErrors += 1;
+ g_oReporter.subXmlWrite(self,
+ '\n%s <Failed timestamp="%s" errors="%s"/>\n%s</Test>\n'
+ % (' ' * self.cTestDepth, sNow, cErrors, ' ' * self.cTestDepth),
+ utils.getCallerName());
+
+ # Tell the reporter that the XML input is done.
+ try: g_oReporter.subXmlEnd(self);
+ except:
+ try: traceback.print_exc();
+ except: pass;
+ return True;
+
+ def read(self, cb = None):
+ """file.read"""
+ _ = cb;
+ return "";
+
+ def write(self, sText):
+ """file.write"""
+ # lazy start.
+ if self.fStarted is not True:
+ try:
+ g_oReporter.subXmlStart(self);
+ except:
+ traceback.print_exc();
+ self.fStarted = True;
+
+ # Turn non-string stuff into strings.
+ if not utils.isString(sText):
+ if isinstance(sText, array.array):
+ try:
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ sText = sText.tostring(); # pylint: disable=no-member
+ else:
+ sText = sText.tobytes();
+ except:
+ pass;
+ if hasattr(sText, 'decode'):
+ try: sText = sText.decode('utf-8', 'ignore');
+ except: pass;
+
+ try:
+ #
+ # Write the XML to the reporter.
+ #
+ g_oReporter.subXmlWrite(self, sText, utils.getCallerName());
+
+ #
+ # Parse the supplied text and look for <Failed.../> tags to keep track of the
+ # error counter. This is only a very lazy aproach.
+ #
+ idxText = 0;
+ while sText:
+ if self.sTagBuffer is None:
+ # Look for the start of a tag.
+ idxStart = sText.find('<', idxText);
+ if idxStart != -1:
+ # If the end was found inside the current buffer, parse the line,
+ # otherwise we have to save it for later.
+ idxEnd = sText.find('>', idxStart);
+ if idxEnd != -1:
+ self._processXmlElement(sText[idxStart:idxEnd+1]);
+ idxText = idxEnd;
+ else:
+ self.sTagBuffer = sText[idxStart:];
+ break;
+ else:
+ break;
+ else:
+ # Search for the end of the tag and parse the whole tag.
+ assert(idxText == 0);
+ idxEnd = sText.find('>');
+ if idxEnd != -1:
+ self._processXmlElement(self.sTagBuffer + sText[:idxEnd+1]);
+ self.sTagBuffer = None;
+ idxText = idxEnd;
+ else:
+ self.sTagBuffer = self.sTagBuffer + sText[idxText:];
+ break;
+ except:
+ traceback.print_exc();
+ return None;
+
+ def _processXmlElement(self, sElement):
+ """
+ Processes a complete XML tag.
+
+ We handle the 'Failed' tag to keep track of the error counter.
+ We also track 'Test' tags to make sure we close with all of them properly closed.
+ """
+ # Make sure we don't parse any space between < and the element name.
+ sElement = sElement.strip();
+
+ # Find the end of the name
+ idxEndName = sElement.find(' ');
+ if idxEndName == -1:
+ idxEndName = sElement.find('>');
+ if idxEndName >= 0:
+ if sElement[idxEndName - 1] == '/':
+ idxEndName -= 1;
+ else:
+ idxEndName = len(sElement);
+ sElementName = sElement[1:idxEndName];
+
+ # <Failed>:
+ if sElementName == 'Failed':
+ g_oLock.acquire();
+ try:
+ g_oReporter.testIncErrors();
+ finally:
+ g_oLock.release();
+ if self.acTestErrors:
+ self.acTestErrors[-1] += 1; # get errors attrib
+ # <Test>
+ elif sElementName == 'Test':
+ self.cTestDepth += 1;
+ self.acTestErrors.append(0);
+ # </Test>
+ elif sElementName == '/Test':
+ self.cTestDepth -= 1;
+ if self.acTestErrors:
+ cErrors = self.acTestErrors.pop();
+ if self.acTestErrors:
+ self.acTestErrors[-1] += cErrors;
+
+
+#
+# The public APIs.
+#
+
+def log(sText, sCaller = None):
+ """Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(1, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def logXcpt(sText=None, cFrames=1):
+ """
+ Log an exception, optionally with a preceeding message and more than one
+ call frame.
+ """
+ return logXcptWorker(1, False, "", sText, cFrames);
+
+def log2(sText, sCaller = None):
+ """Log level 2: Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(2, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def log2Xcpt(sText=None, cFrames=1):
+ """
+ Log level 2: Log an exception, optionally with a preceeding message and
+ more than one call frame.
+ """
+ return logXcptWorker(2, False, "", sText, cFrames);
+
+def log3(sText, sCaller = None):
+ """Log level 3: Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(3, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def log3Xcpt(sText=None, cFrames=1):
+ """
+ Log level 3: Log an exception, optionally with a preceeding message and
+ more than one call frame.
+ """
+ return logXcptWorker(3, False, "", sText, cFrames);
+
+def log4(sText, sCaller = None):
+ """Log level 4: Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(4, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def log4Xcpt(sText=None, cFrames=1):
+ """
+ Log level 4: Log an exception, optionally with a preceeding message and
+ more than one call frame.
+ """
+ return logXcptWorker(4, False, "", sText, cFrames);
+
+def log5(sText, sCaller = None):
+ """Log level 2: Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(5, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def log5Xcpt(sText=None, cFrames=1):
+ """
+ Log level 5: Log an exception, optionally with a preceeding message and
+ more than one call frame.
+ """
+ return logXcptWorker(5, False, "", sText, cFrames);
+
+def log6(sText, sCaller = None):
+ """Log level 6: Writes the specfied text to the log."""
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.log(6, sText, sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ rc = -1;
+ finally:
+ g_oLock.release();
+ return rc;
+
+def log6Xcpt(sText=None, cFrames=1):
+ """
+ Log level 6: Log an exception, optionally with a preceeding message and
+ more than one call frame.
+ """
+ return logXcptWorker(6, False, "", sText, cFrames);
+
+def maybeErr(fIsError, sText):
+ """ Maybe error or maybe normal log entry. """
+ if fIsError is True:
+ return error(sText, sCaller = utils.getCallerName());
+ return log(sText, sCaller = utils.getCallerName());
+
+def maybeErrXcpt(fIsError, sText=None, cFrames=1):
+ """ Maybe error or maybe normal log exception entry. """
+ if fIsError is True:
+ return errorXcpt(sText, cFrames);
+ return logXcpt(sText, cFrames);
+
+def maybeLog(fIsNotError, sText):
+ """ Maybe error or maybe normal log entry. """
+ if fIsNotError is not True:
+ return error(sText, sCaller = utils.getCallerName());
+ return log(sText, sCaller = utils.getCallerName());
+
+def maybeLogXcpt(fIsNotError, sText=None, cFrames=1):
+ """ Maybe error or maybe normal log exception entry. """
+ if fIsNotError is not True:
+ return errorXcpt(sText, cFrames);
+ return logXcpt(sText, cFrames);
+
+def error(sText, sCaller = None):
+ """
+ Writes the specfied error message to the log.
+
+ This will add an error to the current test.
+
+ Always returns False for the convenience of methods returning boolean
+ success indicators.
+ """
+ g_oLock.acquire();
+ try:
+ g_oReporter.testIncErrors();
+ g_oReporter.log(0, '** error: %s' % (sText), sCaller if sCaller else utils.getCallerName(), utils.getTimePrefix());
+ except:
+ pass;
+ finally:
+ g_oLock.release();
+ return False;
+
+def errorXcpt(sText=None, cFrames=1):
+ """
+ Log an error caused by an exception. If sText is given, it will preceed
+ the exception information. cFrames can be used to display more stack.
+
+ This will add an error to the current test.
+
+ Always returns False for the convenience of methods returning boolean
+ success indicators.
+ """
+ logXcptWorker(0, True, '** error: ', sText, cFrames);
+ return False;
+
+def errorTimeout(sText):
+ """
+ Flags the current test as having timed out and writes the specified message to the log.
+
+ This will add an error to the current test.
+
+ Always returns False for the convenience of methods returning boolean
+ success indicators.
+ """
+ g_oLock.acquire();
+ try:
+ g_oReporter.testSetTimedOut();
+ g_oReporter.log(0, '** timeout-error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
+ except:
+ pass;
+ finally:
+ g_oLock.release();
+ return False;
+
+def fatal(sText):
+ """
+ Writes a fatal error to the log.
+
+ This will add an error to the current test.
+
+ Always returns False for the convenience of methods returning boolean
+ success indicators.
+ """
+ g_oLock.acquire();
+ try:
+ g_oReporter.testIncErrors();
+ g_oReporter.log(0, '** fatal error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
+ except:
+ pass
+ finally:
+ g_oLock.release();
+ return False;
+
+def fatalXcpt(sText=None, cFrames=1):
+ """
+ Log a fatal error caused by an exception. If sText is given, it will
+ preceed the exception information. cFrames can be used to display more
+ stack.
+
+ This will add an error to the current test.
+
+ Always returns False for the convenience of methods returning boolean
+ success indicators.
+ """
+ logXcptWorker(0, True, "** fatal error: ", sText, cFrames);
+ return False;
+
+def addLogFile(sFilename, sKind, sDescription = '', sAltName = None):
+ """
+ Adds the specified log file to the report if the file exists.
+
+ The sDescription is a free form description of the log file.
+
+ The sKind parameter is for adding some machine parsable hint what kind of
+ log file this really is.
+
+ Returns True on success, False on failure (no ENOENT errors are logged).
+ """
+ sTsPrf = utils.getTimePrefix();
+ sCaller = utils.getCallerName();
+ fRc = False;
+ if sAltName is None:
+ sAltName = sFilename;
+
+ try:
+ oSrcFile = utils.openNoInherit(sFilename, 'rb');
+ except IOError as oXcpt:
+ if oXcpt.errno != errno.ENOENT:
+ logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
+ else:
+ logXcpt('addLogFile(%s,%s,%s) IOError' % (sFilename, sDescription, sKind));
+ except:
+ logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
+ else:
+ g_oLock.acquire();
+ try:
+ fRc = g_oReporter.addLogFile(oSrcFile, sFilename, sAltName, sDescription, sKind, sCaller, sTsPrf);
+ finally:
+ g_oLock.release();
+ oSrcFile.close();
+ return fRc;
+
+def addLogString(sLog, sLogName, sKind, sDescription = ''):
+ """
+ Adds the specified log string to the report.
+
+ The sLog parameter sets the name of the log file.
+
+ The sDescription is a free form description of the log file.
+
+ The sKind parameter is for adding some machine parsable hint what kind of
+ log file this really is.
+
+ Returns True on success, False on failure (no ENOENT errors are logged).
+ """
+ sTsPrf = utils.getTimePrefix();
+ sCaller = utils.getCallerName();
+ fRc = False;
+
+ g_oLock.acquire();
+ try:
+ fRc = g_oReporter.addLogString(sLog, sLogName, sDescription, sKind, sCaller, sTsPrf);
+ finally:
+ g_oLock.release();
+ return fRc;
+
+def isLocal():
+ """Is this a local reporter?"""
+ return g_oReporter.isLocal()
+
+def incVerbosity():
+ """Increases the verbosity level."""
+ return g_oReporter.incVerbosity()
+
+def incDebug():
+ """Increases the debug level."""
+ return g_oReporter.incDebug()
+
+def getVerbosity():
+ """Returns the current verbosity level."""
+ return g_oReporter.getVerbosity()
+
+def getDebug():
+ """Returns the current debug level."""
+ return g_oReporter.getDebug()
+
+def appendToProcessName(sAppend):
+ """
+ Appends sAppend to the base process name.
+ Returns the new process name.
+ """
+ return g_oReporter.appendToProcessName(sAppend);
+
+def getErrorCount():
+ """
+ Get the current error count for the entire test run.
+ """
+ g_oLock.acquire();
+ try:
+ cErrors = g_oReporter.cErrors;
+ finally:
+ g_oLock.release();
+ return cErrors;
+
+def doPollWork(sDebug = None):
+ """
+ This can be called from wait loops and similar to make the reporter call
+ home with pending XML and such.
+ """
+ g_oReporter.doPollWork(sDebug);
+ return None;
+
+
+#
+# Test reporting, a bit similar to RTTestI*.
+#
+
+def testStart(sName):
+ """
+ Starts a new test (pushes it).
+ """
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.testStart(sName, utils.getCallerName());
+ finally:
+ g_oLock.release();
+ return rc;
+
+def testValue(sName, sValue, sUnit):
+ """
+ Reports a benchmark value or something simiarlly useful.
+ """
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.testValue(sName, str(sValue), sUnit, utils.getCallerName());
+ finally:
+ g_oLock.release();
+ return rc;
+
+def testFailure(sDetails):
+ """
+ Reports a failure.
+ We count these calls and testDone will use them to report PASSED or FAILED.
+
+ Returns False so that a return False line can be saved.
+ """
+ g_oLock.acquire();
+ try:
+ g_oReporter.testFailure(sDetails, utils.getCallerName());
+ finally:
+ g_oLock.release();
+ return False;
+
+def testFailureXcpt(sDetails = ''):
+ """
+ Reports a failure with exception.
+ We count these calls and testDone will use them to report PASSED or FAILED.
+
+ Returns False so that a return False line can be saved.
+ """
+ # Extract exception info.
+ try:
+ oType, oValue, oTraceback = sys.exc_info();
+ except:
+ oType = oValue, oTraceback = None;
+ if oType is not None:
+ sCaller = utils.getCallerName(oTraceback.tb_frame);
+ sXcpt = ' '.join(formatExceptionOnly(oType, oValue, sCaller, utils.getTimePrefix()));
+ else:
+ sCaller = utils.getCallerName();
+ sXcpt = 'No exception at %s' % (sCaller,);
+
+ # Use testFailure to do the work.
+ g_oLock.acquire();
+ try:
+ if sDetails == '':
+ g_oReporter.testFailure('Exception: %s' % (sXcpt,), sCaller);
+ else:
+ g_oReporter.testFailure('%s: %s' % (sDetails, sXcpt), sCaller);
+ finally:
+ g_oLock.release();
+ return False;
+
+def testDone(fSkipped = False):
+ """
+ Completes the current test (pops it), logging PASSED / FAILURE.
+
+ Returns a tuple with the name of the test and its error count.
+ """
+ g_oLock.acquire();
+ try:
+ rc = g_oReporter.testDone(fSkipped, utils.getCallerName());
+ finally:
+ g_oLock.release();
+ return rc;
+
+def testErrorCount():
+ """
+ Gets the error count of the current test.
+
+ Returns the number of errors.
+ """
+ g_oLock.acquire();
+ try:
+ cErrors = g_oReporter.testErrorCount();
+ finally:
+ g_oLock.release();
+ return cErrors;
+
+def testCleanup():
+ """
+ Closes all open tests with a generic error condition.
+
+ Returns True if no open tests, False if something had to be closed with failure.
+ """
+ g_oLock.acquire();
+ try:
+ fRc = g_oReporter.testCleanup(utils.getCallerName());
+ g_oReporter.xmlFlush(fRetry = False, fForce = True);
+ finally:
+ g_oLock.release();
+ fRc = False;
+ return fRc;
+
+
+#
+# Sub XML stuff.
+#
+
+def addSubXmlFile(sFilename):
+ """
+ Adds a sub-xml result file to the party.
+ """
+ fRc = False;
+ try:
+ oSrcFile = utils.openNoInherit(sFilename, 'r');
+ except IOError as oXcpt:
+ if oXcpt.errno != errno.ENOENT:
+ logXcpt('addSubXmlFile(%s)' % (sFilename,));
+ except:
+ logXcpt('addSubXmlFile(%s)' % (sFilename,));
+ else:
+ try:
+ oWrapper = FileWrapperTestPipe()
+ oWrapper.write(oSrcFile.read());
+ oWrapper.close();
+ except:
+ logXcpt('addSubXmlFile(%s)' % (sFilename,));
+ oSrcFile.close();
+
+ return fRc;
+
+
+#
+# Other useful debugging tools.
+#
+
+def logAllStacks(cFrames = None):
+ """
+ Logs the stacks of all python threads.
+ """
+ sTsPrf = utils.getTimePrefix();
+ sCaller = utils.getCallerName();
+ g_oLock.acquire();
+
+ cThread = 0;
+ for idThread, oStack in sys._current_frames().items(): # >=2.5, a bit ugly - pylint: disable=protected-access
+ try:
+ if cThread > 0:
+ g_oReporter.log(1, '', sCaller, sTsPrf);
+ g_oReporter.log(1, 'Thread %s (%#x)' % (idThread, idThread), sCaller, sTsPrf);
+ try:
+ asInfo = traceback.format_stack(oStack, cFrames);
+ except:
+ g_oReporter.log(1, ' Stack formatting failed w/ exception', sCaller, sTsPrf);
+ else:
+ for sInfo in asInfo:
+ asLines = sInfo.splitlines();
+ for sLine in asLines:
+ g_oReporter.log(1, sLine, sCaller, sTsPrf);
+ except:
+ pass;
+ cThread += 1;
+
+ g_oLock.release();
+ return None;
+
+def checkTestManagerConnection():
+ """
+ Checks the connection to the test manager.
+
+ Returns True if the connection is fine, False if not, None if not remote
+ reporter.
+
+ Note! This as the sideeffect of flushing XML.
+ """
+ g_oLock.acquire();
+ try:
+ fRc = g_oReporter.xmlFlush(fRetry = False, fForce = True);
+ finally:
+ g_oLock.release();
+ fRc = False;
+ return fRc;
+
+def flushall(fSkipXml = False):
+ """
+ Flushes all output streams, both standard and logger related.
+ This may also push data to the remote test manager.
+ """
+ try: sys.stdout.flush();
+ except: pass;
+ try: sys.stderr.flush();
+ except: pass;
+
+ if fSkipXml is not True:
+ g_oLock.acquire();
+ try:
+ g_oReporter.xmlFlush(fRetry = False);
+ finally:
+ g_oLock.release();
+
+ return True;
+
+
+#
+# Module initialization.
+#
+
+def _InitReporterModule():
+ """
+ Instantiate the test reporter.
+ """
+ global g_oReporter, g_sReporterName
+
+ g_sReporterName = os.getenv("TESTBOX_REPORTER", "local");
+ if g_sReporterName == "local":
+ g_oReporter = LocalReporter();
+ elif g_sReporterName == "remote":
+ g_oReporter = RemoteReporter(); # Correct, but still plain stupid. pylint: disable=redefined-variable-type
+ else:
+ print(os.path.basename(__file__) + ": Unknown TESTBOX_REPORTER value: '" + g_sReporterName + "'", file = sys.stderr);
+ raise Exception("Unknown TESTBOX_REPORTER value '" + g_sReporterName + "'");
+
+if __name__ != "checker": # pychecker avoidance.
+ _InitReporterModule();
diff --git a/src/VBox/ValidationKit/testdriver/testfileset.py b/src/VBox/ValidationKit/testdriver/testfileset.py
new file mode 100755
index 00000000..33ea8c72
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/testfileset.py
@@ -0,0 +1,690 @@
+# -*- coding: utf-8 -*-
+# $Id: testfileset.py $
+# pylint: disable=too-many-lines
+
+"""
+Test File Set
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import random;
+import string;
+import sys;
+import tarfile;
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+from common import pathutils;
+from testdriver import reporter;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+
+class TestFsObj(object):
+ """ A file system object we created in for test purposes. """
+ def __init__(self, oParent, sPath, sName = None):
+ self.oParent = oParent # type: TestDir
+ self.sPath = sPath # type: str
+ self.sName = sName # type: str
+ if oParent:
+ assert sPath.startswith(oParent.sPath);
+ assert sName is None;
+ self.sName = sPath[len(oParent.sPath) + 1:];
+ # Add to parent.
+ oParent.aoChildren.append(self);
+ oParent.dChildrenUpper[self.sName.upper()] = self;
+
+ def buildPath(self, sRoot, sSep):
+ """
+ Build the path from sRoot using sSep.
+
+ This is handy for getting the path to an object in a different context
+ (OS, path) than what it was generated for.
+ """
+ if self.oParent:
+ return self.oParent.buildPath(sRoot, sSep) + sSep + self.sName;
+ return sRoot + sSep + self.sName;
+
+
+class TestFile(TestFsObj):
+ """ A file object in the guest. """
+ def __init__(self, oParent, sPath, abContent):
+ TestFsObj.__init__(self, oParent, sPath);
+ self.abContent = abContent # type: bytearray
+ self.cbContent = len(abContent);
+ self.off = 0;
+
+ def read(self, cbToRead):
+ """ read() emulation. """
+ assert self.off <= self.cbContent;
+ cbLeft = self.cbContent - self.off;
+ if cbLeft < cbToRead:
+ cbToRead = cbLeft;
+ abRet = self.abContent[self.off:(self.off + cbToRead)];
+ assert len(abRet) == cbToRead;
+ self.off += cbToRead;
+ if sys.version_info[0] < 3:
+ return bytes(abRet);
+ return abRet;
+
+ def equalFile(self, oFile):
+ """ Compares the content of oFile with self.abContent. """
+
+ # Check the size first.
+ try:
+ cbFile = os.fstat(oFile.fileno()).st_size;
+ except:
+ return reporter.errorXcpt();
+ if cbFile != self.cbContent:
+ return reporter.error('file size differs: %s, cbContent=%s' % (cbFile, self.cbContent));
+
+ # Compare the bytes next.
+ offFile = 0;
+ try:
+ oFile.seek(offFile);
+ except:
+ return reporter.error('seek error');
+ while offFile < self.cbContent:
+ cbToRead = self.cbContent - offFile;
+ if cbToRead > 256*1024:
+ cbToRead = 256*1024;
+ try:
+ abRead = oFile.read(cbToRead);
+ except:
+ return reporter.error('read error at offset %s' % (offFile,));
+ cbRead = len(abRead);
+ if cbRead == 0:
+ return reporter.error('premature end of file at offset %s' % (offFile,));
+ if not utils.areBytesEqual(abRead, self.abContent[offFile:(offFile + cbRead)]):
+ return reporter.error('%s byte block at offset %s differs' % (cbRead, offFile,));
+ # Advance:
+ offFile += cbRead;
+
+ return True;
+
+ @staticmethod
+ def hexFormatBytes(abBuf):
+ """ Formats a buffer/string/whatever as a string of hex bytes """
+ if sys.version_info[0] >= 3:
+ if utils.isString(abBuf):
+ try: abBuf = bytes(abBuf, 'utf-8');
+ except: pass;
+ else:
+ if utils.isString(abBuf):
+ try: abBuf = bytearray(abBuf, 'utf-8'); # pylint: disable=redefined-variable-type
+ except: pass;
+ sRet = '';
+ off = 0;
+ for off, bByte in enumerate(abBuf):
+ if off > 0:
+ sRet += ' ' if off & 7 else '-';
+ if isinstance(bByte, int):
+ sRet += '%02x' % (bByte,);
+ else:
+ sRet += '%02x' % (ord(bByte),);
+ return sRet;
+
+ def checkRange(self, cbRange, offFile = 0):
+ """ Check if the specified range is entirely within the file or not. """
+ if offFile >= self.cbContent:
+ return reporter.error('buffer @ %s LB %s is beyond the end of the file (%s bytes)!'
+ % (offFile, cbRange, self.cbContent,));
+ if offFile + cbRange > self.cbContent:
+ return reporter.error('buffer @ %s LB %s is partially beyond the end of the file (%s bytes)!'
+ % (offFile, cbRange, self.cbContent,));
+ return True;
+
+ def equalMemory(self, abBuf, offFile = 0):
+ """
+ Compares the content of the given buffer with the file content at that
+ file offset.
+
+ Returns True if it matches, False + error logging if it does not match.
+ """
+ if not abBuf:
+ return True;
+
+ if not self.checkRange(len(abBuf), offFile):
+ return False;
+
+ if sys.version_info[0] >= 3:
+ if utils.areBytesEqual(abBuf, self.abContent[offFile:(offFile + len(abBuf))]):
+ return True;
+ else:
+ if utils.areBytesEqual(abBuf, buffer(self.abContent, offFile, len(abBuf))): # pylint: disable=undefined-variable
+ return True;
+
+ reporter.error('mismatch with buffer @ %s LB %s (cbContent=%s)!' % (offFile, len(abBuf), self.cbContent,));
+ reporter.error(' type(abBuf): %s' % (type(abBuf),));
+ #if isinstance(abBuf, memoryview):
+ # reporter.error(' nbytes=%s len=%s itemsize=%s type(obj)=%s'
+ # % (abBuf.nbytes, len(abBuf), abBuf.itemsize, type(abBuf.obj),));
+ reporter.error('type(abContent): %s' % (type(self.abContent),));
+
+ offBuf = 0;
+ cbLeft = len(abBuf);
+ while cbLeft > 0:
+ cbLine = min(16, cbLeft);
+ abBuf1 = abBuf[offBuf:(offBuf + cbLine)];
+ abBuf2 = self.abContent[offFile:(offFile + cbLine)];
+ if not utils.areBytesEqual(abBuf1, abBuf2):
+ try: sStr1 = self.hexFormatBytes(abBuf1);
+ except: sStr1 = 'oops';
+ try: sStr2 = self.hexFormatBytes(abBuf2);
+ except: sStr2 = 'oops';
+ reporter.log('%#10x: %s' % (offBuf, sStr1,));
+ reporter.log('%#10x: %s' % (offFile, sStr2,));
+
+ # Advance.
+ offBuf += 16;
+ offFile += 16;
+ cbLeft -= 16;
+
+ return False;
+
+
+class TestFileZeroFilled(TestFile):
+ """
+ Zero filled test file.
+ """
+
+ def __init__(self, oParent, sPath, cbContent):
+ TestFile.__init__(self, oParent, sPath, bytearray(1));
+ self.cbContent = cbContent;
+
+ def read(self, cbToRead):
+ """ read() emulation. """
+ assert self.off <= self.cbContent;
+ cbLeft = self.cbContent - self.off;
+ if cbLeft < cbToRead:
+ cbToRead = cbLeft;
+ abRet = bytearray(cbToRead);
+ assert len(abRet) == cbToRead;
+ self.off += cbToRead;
+ if sys.version_info[0] < 3:
+ return bytes(abRet);
+ return abRet;
+
+ def equalFile(self, oFile):
+ _ = oFile;
+ assert False, "not implemented";
+ return False;
+
+ def equalMemory(self, abBuf, offFile = 0):
+ if not abBuf:
+ return True;
+
+ if not self.checkRange(len(abBuf), offFile):
+ return False;
+
+ if utils.areBytesEqual(abBuf, bytearray(len(abBuf))):
+ return True;
+
+ cErrors = 0;
+ offBuf = 0
+ while offBuf < len(abBuf):
+ bByte = abBuf[offBuf];
+ if not isinstance(bByte, int):
+ bByte = ord(bByte);
+ if bByte != 0:
+ reporter.error('Mismatch @ %s/%s: %#x, expected 0!' % (offFile, offBuf, bByte,));
+ cErrors += 1;
+ if cErrors > 32:
+ return False;
+ offBuf += 1;
+ return cErrors == 0;
+
+
+class TestDir(TestFsObj):
+ """ A file object in the guest. """
+ def __init__(self, oParent, sPath, sName = None):
+ TestFsObj.__init__(self, oParent, sPath, sName);
+ self.aoChildren = [] # type: list(TestFsObj)
+ self.dChildrenUpper = {} # type: dict(str, TestFsObj)
+
+ def contains(self, sName):
+ """ Checks if the directory contains the given name. """
+ return sName.upper() in self.dChildrenUpper
+
+
+class TestFileSet(object):
+ """
+ A generated set of files and directories for use in a test.
+
+ Can be wrapped up into a tarball or written directly to the file system.
+ """
+
+ ksReservedWinOS2 = '/\\"*:<>?|\t\v\n\r\f\a\b';
+ ksReservedUnix = '/';
+ ksReservedTrailingWinOS2 = ' .';
+ ksReservedTrailingUnix = '';
+
+ ## @name Path style.
+ ## @{
+
+ ## @}
+
+ def __init__(self, fDosStyle, sBasePath, sSubDir, # pylint: disable=too-many-arguments
+ asCompatibleWith = None, # List of getHostOs values to the names must be compatible with.
+ oRngFileSizes = xrange(0, 16384),
+ oRngManyFiles = xrange(128, 512),
+ oRngTreeFiles = xrange(128, 384),
+ oRngTreeDepth = xrange(92, 256),
+ oRngTreeDirs = xrange(2, 16),
+ cchMaxPath = 230,
+ cchMaxName = 230,
+ uSeed = None):
+ ## @name Parameters
+ ## @{
+ self.fDosStyle = fDosStyle;
+ self.sMinStyle = 'win' if fDosStyle else 'linux';
+ if asCompatibleWith is not None:
+ for sOs in asCompatibleWith:
+ assert sOs in ('win', 'os2', 'darwin', 'linux', 'solaris', 'cross'), sOs;
+ if 'os2' in asCompatibleWith:
+ self.sMinStyle = 'os2';
+ elif 'win' in asCompatibleWith:
+ self.sMinStyle = 'win';
+ # 'cross' marks a lowest common denominator for all supported platforms.
+ # Used for Guest Control testing.
+ elif 'cross' in asCompatibleWith:
+ self.sMinStyle = 'cross';
+ self.sBasePath = sBasePath;
+ self.sSubDir = sSubDir;
+ self.oRngFileSizes = oRngFileSizes;
+ self.oRngManyFiles = oRngManyFiles;
+ self.oRngTreeFiles = oRngTreeFiles;
+ self.oRngTreeDepth = oRngTreeDepth;
+ self.oRngTreeDirs = oRngTreeDirs;
+ self.cchMaxPath = cchMaxPath;
+ self.cchMaxName = cchMaxName
+ ## @}
+
+ ## @name Charset stuff
+ ## @todo allow more chars for unix hosts + guests.
+ ## @todo include unicode stuff, except on OS/2 and DOS.
+ ## @{
+ ## The filename charset.
+ self.sFileCharset = string.printable;
+ ## Set of characters that should not trail a guest filename.
+ self.sReservedTrailing = self.ksReservedTrailingWinOS2;
+ if self.sMinStyle in ('win', 'os2'):
+ for ch in self.ksReservedWinOS2:
+ self.sFileCharset = self.sFileCharset.replace(ch, '');
+ elif self.sMinStyle in ('darwin', 'linux', 'solaris'):
+ self.sReservedTrailing = self.ksReservedTrailingUnix;
+ for ch in self.ksReservedUnix:
+ self.sFileCharset = self.sFileCharset.replace(ch, '');
+ else: # 'cross'
+ # Filter out all reserved charsets from all platforms.
+ for ch in self.ksReservedWinOS2:
+ self.sFileCharset = self.sFileCharset.replace(ch, '');
+ for ch in self.ksReservedUnix:
+ self.sFileCharset = self.sFileCharset.replace(ch, '');
+ self.sReservedTrailing = self.ksReservedTrailingWinOS2 \
+ + self.ksReservedTrailingUnix;
+ # More spaces and dot:
+ self.sFileCharset += ' ...';
+ ## @}
+
+ ## The root directory.
+ self.oRoot = None # type: TestDir;
+ ## An empty directory (under root).
+ self.oEmptyDir = None # type: TestDir;
+
+ ## A directory with a lot of files in it.
+ self.oManyDir = None # type: TestDir;
+
+ ## A directory with a mixed tree structure under it.
+ self.oTreeDir = None # type: TestDir;
+ ## Number of files in oTreeDir.
+ self.cTreeFiles = 0;
+ ## Number of directories under oTreeDir.
+ self.cTreeDirs = 0;
+ ## Number of other file types under oTreeDir.
+ self.cTreeOthers = 0;
+
+ ## All directories in creation order.
+ self.aoDirs = [] # type: list(TestDir);
+ ## All files in creation order.
+ self.aoFiles = [] # type: list(TestFile);
+ ## Path to object lookup.
+ self.dPaths = {} # type: dict(str, TestFsObj);
+
+ #
+ # Do the creating.
+ #
+ self.uSeed = uSeed if uSeed is not None else utils.timestampMilli();
+ self.oRandom = random.Random();
+ self.oRandom.seed(self.uSeed);
+ reporter.log('prepareGuestForTesting: random seed %s' % (self.uSeed,));
+
+ self.__createTestStuff();
+
+ def __createFilename(self, oParent, sCharset, sReservedTrailing):
+ """
+ Creates a filename contains random characters from sCharset and together
+ with oParent.sPath doesn't exceed the given max chars in length.
+ """
+ ## @todo Consider extending this to take UTF-8 and UTF-16 encoding so we
+ ## can safely use the full unicode range. Need to check how
+ ## RTZipTarCmd handles file name encoding in general...
+
+ if oParent:
+ cchMaxName = self.cchMaxPath - len(oParent.sPath) - 1;
+ else:
+ cchMaxName = self.cchMaxPath - 4;
+ if cchMaxName > self.cchMaxName:
+ cchMaxName = self.cchMaxName;
+ if cchMaxName <= 1:
+ cchMaxName = 2;
+
+ while True:
+ cchName = self.oRandom.randrange(1, cchMaxName);
+ sName = ''.join(self.oRandom.choice(sCharset) for _ in xrange(cchName));
+ if oParent is None or not oParent.contains(sName):
+ if sName[-1] not in sReservedTrailing:
+ if sName not in ('.', '..',):
+ return sName;
+ return ''; # never reached, but makes pylint happy.
+
+ def generateFilenameEx(self, cchMax = -1, cchMin = -1):
+ """
+ Generates a filename according to the given specs.
+
+ This is for external use, whereas __createFilename is for internal.
+
+ Returns generated filename.
+ """
+ assert cchMax == -1 or (cchMax >= 1 and cchMax > cchMin);
+ if cchMin <= 0:
+ cchMin = 1;
+ if cchMax < cchMin:
+ cchMax = self.cchMaxName;
+
+ while True:
+ cchName = self.oRandom.randrange(cchMin, cchMax + 1);
+ sName = ''.join(self.oRandom.choice(self.sFileCharset) for _ in xrange(cchName));
+ if sName[-1] not in self.sReservedTrailing:
+ if sName not in ('.', '..',):
+ return sName;
+ return ''; # never reached, but makes pylint happy.
+
+ def __createTestDir(self, oParent, sDir, sName = None):
+ """
+ Creates a test directory.
+ """
+ oDir = TestDir(oParent, sDir, sName);
+ self.aoDirs.append(oDir);
+ self.dPaths[sDir] = oDir;
+ return oDir;
+
+ def __createTestFile(self, oParent, sFile):
+ """
+ Creates a test file with random size up to cbMaxContent and random content.
+ """
+ cbFile = self.oRandom.choice(self.oRngFileSizes);
+ abContent = bytearray(self.oRandom.getrandbits(8) for _ in xrange(cbFile));
+
+ oFile = TestFile(oParent, sFile, abContent);
+ self.aoFiles.append(oFile);
+ self.dPaths[sFile] = oFile;
+ return oFile;
+
+ def __createTestStuff(self):
+ """
+ Create a random file set that we can work on in the tests.
+ Returns True/False.
+ """
+
+ #
+ # Create the root test dir.
+ #
+ sRoot = pathutils.joinEx(self.fDosStyle, self.sBasePath, self.sSubDir);
+ self.oRoot = self.__createTestDir(None, sRoot, self.sSubDir);
+ self.oEmptyDir = self.__createTestDir(self.oRoot, pathutils.joinEx(self.fDosStyle, sRoot, 'empty'));
+
+ #
+ # Create a directory with lots of files in it:
+ #
+ oDir = self.__createTestDir(self.oRoot, pathutils.joinEx(self.fDosStyle, sRoot, 'many'));
+ self.oManyDir = oDir;
+ cManyFiles = self.oRandom.choice(self.oRngManyFiles);
+ for _ in xrange(cManyFiles):
+ sName = self.__createFilename(oDir, self.sFileCharset, self.sReservedTrailing);
+ self.__createTestFile(oDir, pathutils.joinEx(self.fDosStyle, oDir.sPath, sName));
+
+ #
+ # Generate a tree of files and dirs.
+ #
+ oDir = self.__createTestDir(self.oRoot, pathutils.joinEx(self.fDosStyle, sRoot, 'tree'));
+ uMaxDepth = self.oRandom.choice(self.oRngTreeDepth);
+ cMaxFiles = self.oRandom.choice(self.oRngTreeFiles);
+ cMaxDirs = self.oRandom.choice(self.oRngTreeDirs);
+ self.oTreeDir = oDir;
+ self.cTreeFiles = 0;
+ self.cTreeDirs = 0;
+ uDepth = 0;
+ while self.cTreeFiles < cMaxFiles and self.cTreeDirs < cMaxDirs:
+ iAction = self.oRandom.randrange(0, 2+1);
+ # 0: Add a file:
+ if iAction == 0 and self.cTreeFiles < cMaxFiles and len(oDir.sPath) < 230 - 2:
+ sName = self.__createFilename(oDir, self.sFileCharset, self.sReservedTrailing);
+ self.__createTestFile(oDir, pathutils.joinEx(self.fDosStyle, oDir.sPath, sName));
+ self.cTreeFiles += 1;
+ # 1: Add a subdirector and descend into it:
+ elif iAction == 1 and self.cTreeDirs < cMaxDirs and uDepth < uMaxDepth and len(oDir.sPath) < 220:
+ sName = self.__createFilename(oDir, self.sFileCharset, self.sReservedTrailing);
+ oDir = self.__createTestDir(oDir, pathutils.joinEx(self.fDosStyle, oDir.sPath, sName));
+ self.cTreeDirs += 1;
+ uDepth += 1;
+ # 2: Ascend to parent dir:
+ elif iAction == 2 and uDepth > 0:
+ oDir = oDir.oParent;
+ uDepth -= 1;
+
+ return True;
+
+ def createTarball(self, sTarFileHst):
+ """
+ Creates a tarball on the host.
+ Returns success indicator.
+ """
+ reporter.log('Creating tarball "%s" with test files for the guest...' % (sTarFileHst,));
+
+ cchSkip = len(self.sBasePath) + 1;
+
+ # Open the tarball:
+ try:
+ # Make sure to explicitly set GNU_FORMAT here, as with Python 3.8 the default format (tarfile.DEFAULT_FORMAT)
+ # has been changed to tarfile.PAX_FORMAT, which our extraction code (vts_tar) currently can't handle.
+ ## @todo Remove tarfile.GNU_FORMAT and use tarfile.PAX_FORMAT as soon as we have PAX support.
+ oTarFile = tarfile.open(sTarFileHst, 'w:gz', format = tarfile.GNU_FORMAT); # pylint: disable=consider-using-with
+ except:
+ return reporter.errorXcpt('Failed to open new tar file: %s' % (sTarFileHst,));
+
+ # Directories:
+ for oDir in self.aoDirs:
+ sPath = oDir.sPath[cchSkip:];
+ if self.fDosStyle:
+ sPath = sPath.replace('\\', '/');
+ oTarInfo = tarfile.TarInfo(sPath + '/');
+ oTarInfo.mode = 0o777;
+ oTarInfo.type = tarfile.DIRTYPE;
+ try:
+ oTarFile.addfile(oTarInfo);
+ except:
+ return reporter.errorXcpt('Failed adding directory tarfile: %s' % (oDir.sPath,));
+
+ # Files:
+ for oFile in self.aoFiles:
+ sPath = oFile.sPath[cchSkip:];
+ if self.fDosStyle:
+ sPath = sPath.replace('\\', '/');
+ oTarInfo = tarfile.TarInfo(sPath);
+ oTarInfo.mode = 0o666;
+ oTarInfo.size = len(oFile.abContent);
+ oFile.off = 0;
+ try:
+ oTarFile.addfile(oTarInfo, oFile);
+ except:
+ return reporter.errorXcpt('Failed adding directory tarfile: %s' % (oFile.sPath,));
+
+ # Complete the tarball.
+ try:
+ oTarFile.close();
+ except:
+ return reporter.errorXcpt('Error closing new tar file: %s' % (sTarFileHst,));
+ return True;
+
+ def writeToDisk(self, sAltBase = None):
+ """
+ Writes out the files to disk.
+ Returns True on success, False + error logging on failure.
+ """
+
+ # We only need to flip DOS slashes to unix ones, since windows & OS/2 can handle unix slashes.
+ fDosToUnix = self.fDosStyle and os.path.sep != '\\';
+
+ # The directories:
+ for oDir in self.aoDirs:
+ sPath = oDir.sPath;
+ if sAltBase:
+ if fDosToUnix:
+ sPath = sAltBase + sPath[len(self.sBasePath):].replace('\\', os.path.sep);
+ else:
+ sPath = sAltBase + sPath[len(self.sBasePath):];
+ elif fDosToUnix:
+ sPath = sPath.replace('\\', os.path.sep);
+
+ try:
+ os.mkdir(sPath, 0o770);
+ except:
+ return reporter.errorXcpt('mkdir(%s) failed' % (sPath,));
+
+ # The files:
+ for oFile in self.aoFiles:
+ sPath = oFile.sPath;
+ if sAltBase:
+ if fDosToUnix:
+ sPath = sAltBase + sPath[len(self.sBasePath):].replace('\\', os.path.sep);
+ else:
+ sPath = sAltBase + sPath[len(self.sBasePath):];
+ elif fDosToUnix:
+ sPath = sPath.replace('\\', os.path.sep);
+
+ try:
+ oOutFile = open(sPath, 'wb'); # pylint: disable=consider-using-with
+ except:
+ return reporter.errorXcpt('open(%s, "wb") failed' % (sPath,));
+ try:
+ if sys.version_info[0] < 3:
+ oOutFile.write(bytes(oFile.abContent));
+ else:
+ oOutFile.write(oFile.abContent);
+ except:
+ try: oOutFile.close();
+ except: pass;
+ return reporter.errorXcpt('%s: write(%s bytes) failed' % (sPath, oFile.cbContent,));
+ try:
+ oOutFile.close();
+ except:
+ return reporter.errorXcpt('%s: close() failed' % (sPath,));
+
+ return True;
+
+
+ def chooseRandomFile(self):
+ """
+ Returns a random file.
+ """
+ return self.aoFiles[self.oRandom.choice(xrange(len(self.aoFiles)))];
+
+ def chooseRandomDirFromTree(self, fLeaf = False, fNonEmpty = False, cMaxRetries = 1024):
+ """
+ Returns a random directory from the tree (self.oTreeDir).
+ Will return None if no directory with given parameters was found.
+ """
+ cRetries = 0;
+ while cRetries < cMaxRetries:
+ oDir = self.aoDirs[self.oRandom.choice(xrange(len(self.aoDirs)))];
+ # Check fNonEmpty requirement:
+ if not fNonEmpty or oDir.aoChildren:
+ # Check leaf requirement:
+ if not fLeaf:
+ for oChild in oDir.aoChildren:
+ if isinstance(oChild, TestDir):
+ continue; # skip it.
+
+ # Return if in the tree:
+ oParent = oDir.oParent;
+ while oParent is not None:
+ if oParent is self.oTreeDir:
+ return oDir;
+ oParent = oParent.oParent;
+ cRetries += 1;
+
+ return None; # make pylint happy
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+# pylint: disable=undefined-variable
+class TestFileSetUnitTests(unittest.TestCase):
+ def testGeneral(self):
+ oSet = TestFileSet(False, '/tmp', 'unittest');
+ self.assertTrue(isinstance(oSet.chooseRandomDirFromTree(), TestDir));
+ self.assertTrue(isinstance(oSet.chooseRandomFile(), TestFile));
+
+ def testHexFormatBytes(self):
+ self.assertEqual(TestFile.hexFormatBytes(bytearray([0,1,2,3,4,5,6,7,8,9])),
+ '00 01 02 03 04 05 06 07-08 09');
+ self.assertEqual(TestFile.hexFormatBytes(memoryview(bytearray([0,1,2,3,4,5,6,7,8,9,10, 16]))),
+ '00 01 02 03 04 05 06 07-08 09 0a 10');
+
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testdriver/tst-txsclient.py b/src/VBox/ValidationKit/testdriver/tst-txsclient.py
new file mode 100755
index 00000000..6b7353b5
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/tst-txsclient.py
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+# $Id: tst-txsclient.py $
+
+"""
+Simple testcase for txsclient.py.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import os
+import sys
+
+# Validation Kit imports.
+sys.path.insert(0, '.');
+sys.path.insert(0, '..');
+from common import utils;
+from testdriver import txsclient;
+from testdriver import reporter;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+g_cTests = 0;
+g_cFailures = 0
+
+def boolRes(rc, fExpect = True):
+ """Checks a boolean result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if isinstance(rc, bool):
+ if rc == fExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def stringRes(rc, sExpect):
+ """Checks a string result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if utils.isString(rc):
+ if rc == sExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def main(asArgs): # pylint: disable=missing-docstring,too-many-locals,too-many-statements
+ cMsTimeout = long(30*1000);
+ sAddress = 'localhost';
+ uPort = None;
+ fReversedSetup = False;
+ fReboot = False;
+ fShutdown = False;
+ fStdTests = True;
+
+ i = 1;
+ while i < len(asArgs):
+ if asArgs[i] == '--hostname':
+ sAddress = asArgs[i + 1];
+ i = i + 2;
+ elif asArgs[i] == '--port':
+ uPort = int(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--reversed-setup':
+ fReversedSetup = True;
+ i = i + 1;
+ elif asArgs[i] == '--timeout':
+ cMsTimeout = long(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--reboot':
+ fReboot = True;
+ fShutdown = False;
+ fStdTests = False;
+ i = i + 1;
+ elif asArgs[i] == '--shutdown':
+ fShutdown = True;
+ fReboot = False;
+ fStdTests = False;
+ i = i + 1;
+ elif asArgs[i] == '--help':
+ print('tst-txsclient.py [--hostname <addr|name>] [--port <num>] [--timeout <cMS>] '
+ '[--reboot|--shutdown] [--reversed-setup]');
+ return 0;
+ else:
+ print('Unknown argument: %s' % (asArgs[i]));
+ return 2;
+
+ if uPort is None:
+ oSession = txsclient.openTcpSession(cMsTimeout, sAddress, fReversedSetup = fReversedSetup);
+ else:
+ oSession = txsclient.openTcpSession(cMsTimeout, sAddress, uPort = uPort, fReversedSetup = fReversedSetup);
+ if oSession is None:
+ print('openTcpSession failed');
+ return 1;
+
+ fDone = oSession.waitForTask(30*1000);
+ print('connect: waitForTask -> %s, result %s' % (fDone, oSession.getResult()));
+ if fDone is True and oSession.isSuccess():
+ if fStdTests:
+ # Get the UUID of the remote instance.
+ sUuid = oSession.syncUuid();
+ if sUuid is not False:
+ print('%s: UUID = %s' % (boolRes(True), sUuid));
+ else:
+ print('%s: UUID' % (boolRes(False),));
+
+ # Create and remove a directory on the scratch area.
+ rc = oSession.syncMkDir('${SCRATCH}/testdir1');
+ print('%s: MKDIR(${SCRATCH}/testdir1) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncIsDir('${SCRATCH}/testdir1');
+ print('%s: ISDIR(${SCRATCH}/testdir1) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncRmDir('${SCRATCH}/testdir1');
+ print('%s: RMDIR(${SCRATCH}/testdir1) -> %s' % (boolRes(rc), rc));
+
+ # Create a two-level subdir.
+ rc = oSession.syncMkDirPath('${SCRATCH}/testdir2/subdir1');
+ print('%s: MKDRPATH(${SCRATCH}/testdir2/subdir1) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncIsDir('${SCRATCH}/testdir2');
+ print('%s: ISDIR(${SCRATCH}/testdir2) -> %s' % (boolRes(rc), rc));
+ rc = oSession.syncIsDir('${SCRATCH}/testdir2/');
+ print('%s: ISDIR(${SCRATCH}/testdir2/) -> %s' % (boolRes(rc), rc));
+ rc = oSession.syncIsDir('${SCRATCH}/testdir2/subdir1');
+ print('%s: ISDIR(${SCRATCH}/testdir2/subdir1) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncRmTree('${SCRATCH}/testdir2');
+ print('%s: RMTREE(${SCRATCH}/testdir2) -> %s' % (boolRes(rc), rc));
+
+ # Check out a simple file.
+ rc = oSession.syncUploadString('howdy', '${SCRATCH}/howdyfile');
+ print('%s: PUT FILE(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncUploadString('howdy-replaced', '${SCRATCH}/howdyfile');
+ print('%s: PUT FILE(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc), rc));
+
+ rc = oSession.syncDownloadString('${SCRATCH}/howdyfile');
+ print('%s: GET FILE(${SCRATCH}/howdyfile) -> "%s" expected "howdy-replaced"' % (stringRes(rc, 'howdy-replaced'), rc));
+
+ rc = oSession.syncIsFile('${SCRATCH}/howdyfile');
+ print('%s: ISFILE(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc), rc));
+ rc = oSession.syncIsDir('${SCRATCH}/howdyfile');
+ print('%s: ISDIR(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc, False), rc));
+ rc = oSession.syncIsSymlink('${SCRATCH}/howdyfile');
+ print('%s: ISSYMLNK(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc, False), rc));
+
+ rc = oSession.syncRmFile('${SCRATCH}/howdyfile');
+ print('%s: RMFILE(${SCRATCH}/howdyfile) -> %s' % (boolRes(rc), rc));
+
+ # Unicode filename (may or may not work, LANG/LC_TYPE dependent on some hosts).
+ rc = oSession.syncUploadString('howdy', u'${SCRATCH}/Schröder');
+ print((u'%s: PUT FILE(${SCRATCH}/Schröder) -> %s' % (boolRes(rc), rc)).encode('ascii', 'replace'));
+
+ rc = oSession.syncIsFile(u'${SCRATCH}/Schröder');
+ print((u'%s: ISFILE(${SCRATCH}/Schröder) -> %s' % (boolRes(rc), rc)).encode('ascii', 'replace'));
+
+ rc = oSession.syncRmFile(u'${SCRATCH}/Schröder');
+ print((u'%s: RMFILE(${SCRATCH}/Schröder) -> %s' % (boolRes(rc), rc)).encode('ascii', 'replace'));
+
+ # Finally, some file uploading and downloading with unicode filenames.
+ strUpFile = 'tst-txsclient-upload.bin';
+ strDwnFile = 'tst-txsclient-download.bin';
+ try:
+ abRandFile = os.urandom(257897);
+ except:
+ print('INFO: no urandom... falling back on a simple string.');
+ abRandFile = 'asdflkjasdlfkjasdlfkjq023942relwjgkna9epr865u2nm345;hndafgoukhasre5kb2453km';
+ for i in range(1, 64):
+ abRandFile += abRandFile;
+ try:
+ oLocalFile = utils.openNoInherit(strUpFile, 'w+b');
+ oLocalFile.write(abRandFile);
+ oLocalFile.close();
+ rc = True;
+ except:
+ rc = False;
+ print('%s: creating file (%s) to upload failed....' % (boolRes(rc), strUpFile));
+
+ if rc is True:
+ rc = oSession.syncUploadFile(strUpFile, '${SCRATCH}/tst-txsclient-uploaded.bin')
+ print('%s: PUT FILE(%s, ${SCRATCH}/tst-txsclient-uploaded.bin) -> %s' % (boolRes(rc), strUpFile, rc));
+
+ rc = oSession.syncDownloadFile('${SCRATCH}/tst-txsclient-uploaded.bin', strDwnFile)
+ print('%s: GET FILE(${SCRATCH}/tst-txsclient-uploaded.bin, tst-txsclient-downloaded.txt) -> %s'
+ % (boolRes(rc), rc));
+
+ try:
+ oLocalFile = utils.openNoInherit(strDwnFile, "rb");
+ abDwnFile = oLocalFile.read();
+ oLocalFile.close();
+ if abRandFile == abDwnFile:
+ print('%s: downloaded file matches the uploaded file' % (boolRes(True),));
+ else:
+ print('%s: downloaded file does not match the uploaded file' % (boolRes(False),));
+ print('abRandFile=%s' % (abRandFile,));
+ print('abDwnFile =%s' % (abRandFile,));
+ except:
+ print('%s: reading downloaded file (%s) failed....' % (boolRes(False), strDwnFile));
+
+ rc = oSession.syncRmFile(u'${SCRATCH}/tst-txsclient-uploaded.bin');
+ print('%s: RMFILE(${SCRATCH}/tst-txsclient-uploaded.bin) -> %s' % (boolRes(rc), rc));
+
+ try: os.remove(strUpFile);
+ except: pass;
+ try: os.remove(strDwnFile);
+ except: pass;
+
+ # Execute some simple thing, if available.
+ # Intentionally skip this test if file is not available due to
+ # another inserted CD-ROM (e.g. not TestSuite.iso).
+ sProg = '${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}';
+ rc = oSession.syncIsFile(sProg, 30 * 1000, True);
+ if rc is True:
+ rc = oSession.syncExecEx(sProg, (sProg, '--help'));
+ print('%s: EXEC(%s ${SCRATCH}) -> %s' % (boolRes(rc), sProg, rc));
+
+ rc = oSession.syncExecEx(sProg, (sProg, 'there', 'is no such', 'parameter'), \
+ oStdOut='${SCRATCH}/stdout', \
+ oStdErr='${SCRATCH}/stderr');
+ print('%s: EXEC(%s there is not such parameter > ${SCRATCH}/stdout 2> ${SCRATCH}/stderr) -> %s'
+ % (boolRes(rc, False), sProg, rc));
+
+ rc = oSession.syncDownloadString('${SCRATCH}/stdout');
+ print('INFO: GET FILE(${SCRATCH}/stdout) -> "%s"' % (rc));
+ rc = oSession.syncDownloadString('${SCRATCH}/stderr');
+ print('INFO: GET FILE(${SCRATCH}/stderr) -> "%s"' % (rc));
+
+ print('TESTING: syncExec...');
+ rc = oSession.syncExec(sProg, (sProg, '--version'));
+ print('%s: EXEC(%s --version) -> %s' % (boolRes(rc), sProg, rc));
+
+ print('TESTING: syncExec...');
+ rc = oSession.syncExec(sProg, (sProg, '--help'));
+ print('%s: EXEC(%s --help) -> %s' % (boolRes(rc), sProg, rc));
+
+ #print('TESTING: syncExec sleep 30...'
+ #rc = oSession.syncExec('/usr/bin/sleep', ('/usr/bin/sleep', '30')));
+ #print('%s: EXEC(/bin/sleep 30) -> %s' % (boolRes(rc), rc));
+ else:
+ print('SKIP: Execution of %s skipped, does not exist on CD-ROM' % (sProg,));
+
+ # Execute a non-existing file on CD-ROM.
+ sProg = '${CDROM}/${OS/ARCH}/NonExisting${EXESUFF}';
+ rc = oSession.syncExecEx(sProg, (sProg,), oStdIn = '/dev/null', oStdOut = '/dev/null', \
+ oStdErr = '/dev/null', oTestPipe = '/dev/null', \
+ sAsUser = '', cMsTimeout = 3600000, fIgnoreErrors = True);
+ if rc is None:
+ rc = True;
+ else:
+ reporter.error('Unexpected value \"%s\" while executing non-existent file "%s"' % (rc, sProg));
+ print('%s: EXEC(%s ${SCRATCH}) -> %s' % (boolRes(rc), sProg, rc));
+
+ # Done
+ rc = oSession.syncDisconnect();
+ print('%s: disconnect() -> %s' % (boolRes(rc), rc));
+
+ elif fReboot:
+ print('TESTING: syncReboot...');
+ rc = oSession.syncReboot();
+ print('%s: REBOOT() -> %s' % (boolRes(rc), rc));
+ elif fShutdown:
+ print('TESTING: syncShutdown...');
+ rc = oSession.syncShutdown();
+ print('%s: SHUTDOWN() -> %s' % (boolRes(rc), rc));
+
+
+ if g_cFailures != 0:
+ print('tst-txsclient.py: %u out of %u test failed' % (g_cFailures, g_cTests));
+ return 1;
+ print('tst-txsclient.py: all %u tests passed!' % (g_cTests));
+ return 0;
+
+
+if __name__ == '__main__':
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ sys.exit(main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/testdriver/txsclient.py b/src/VBox/ValidationKit/testdriver/txsclient.py
new file mode 100755
index 00000000..343071d6
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/txsclient.py
@@ -0,0 +1,2376 @@
+# -*- coding: utf-8 -*-
+# $Id: txsclient.py $
+# pylint: disable=too-many-lines
+
+"""
+Test eXecution Service Client.
+"""
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import array;
+import errno;
+import os;
+import select;
+import socket;
+import sys;
+import threading;
+import time;
+import zlib;
+import uuid;
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver.base import TdTaskBase;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+#
+# Helpers for decoding data received from the TXS.
+# These are used both the Session and Transport classes.
+#
+
+def getU32(abData, off):
+ """Get a U32 field."""
+ return abData[off] \
+ + abData[off + 1] * 256 \
+ + abData[off + 2] * 65536 \
+ + abData[off + 3] * 16777216;
+
+def getSZ(abData, off, sDefault = None):
+ """
+ Get a zero-terminated string field.
+ Returns sDefault if the string is invalid.
+ """
+ cchStr = getSZLen(abData, off);
+ if cchStr >= 0:
+ abStr = abData[off:(off + cchStr)];
+ try:
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ sStr = abStr.tostring(); # pylint: disable=no-member
+ else:
+ sStr = abStr.tobytes();
+ return sStr.decode('utf_8');
+ except:
+ reporter.errorXcpt('getSZ(,%u)' % (off));
+ return sDefault;
+
+def getSZLen(abData, off):
+ """
+ Get the length of a zero-terminated string field, in bytes.
+ Returns -1 if off is beyond the data packet or not properly terminated.
+ """
+ cbData = len(abData);
+ if off >= cbData:
+ return -1;
+
+ offCur = off;
+ while abData[offCur] != 0:
+ offCur = offCur + 1;
+ if offCur >= cbData:
+ return -1;
+
+ return offCur - off;
+
+def isValidOpcodeEncoding(sOpcode):
+ """
+ Checks if the specified opcode is valid or not.
+ Returns True on success.
+ Returns False if it is invalid, details in the log.
+ """
+ sSet1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ sSet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ ";
+ if len(sOpcode) != 8:
+ reporter.error("invalid opcode length: %s" % (len(sOpcode)));
+ return False;
+ for i in range(0, 1):
+ if sSet1.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ for i in range(2, 7):
+ if sSet2.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ return True;
+
+#
+# Helper for encoding data sent to the TXS.
+#
+
+def u32ToByteArray(u32):
+ """Encodes the u32 value as a little endian byte (B) array."""
+ return array.array('B',
+ ( u32 % 256,
+ (u32 // 256) % 256,
+ (u32 // 65536) % 256,
+ (u32 // 16777216) % 256) );
+
+def escapeString(sString):
+ """
+ Does $ escaping of the string so TXS doesn't try do variable expansion.
+ """
+ return sString.replace('$', '$$');
+
+
+
+class TransportBase(object):
+ """
+ Base class for the transport layer.
+ """
+
+ def __init__(self, sCaller):
+ self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
+ self.fDummy = 0;
+ self.abReadAheadHdr = array.array('B');
+
+ def toString(self):
+ """
+ Stringify the instance for logging and debugging.
+ """
+ return '<%s: abReadAheadHdr=%s, sDbgCreated=%s>' % (type(self).__name__, self.abReadAheadHdr, self.sDbgCreated);
+
+ def __str__(self):
+ return self.toString();
+
+ def cancelConnect(self):
+ """
+ Cancels any pending connect() call.
+ Returns None;
+ """
+ return None;
+
+ def connect(self, cMsTimeout):
+ """
+ Quietly attempts to connect to the TXS.
+
+ Returns True on success.
+ Returns False on retryable errors (no logging).
+ Returns None on fatal errors with details in the log.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def disconnect(self, fQuiet = False):
+ """
+ Disconnect from the TXS.
+
+ Returns True.
+
+ Override this method, don't call super.
+ """
+ _ = fQuiet;
+ return True;
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ """
+ Sends the bytes in the buffer abBuf to the TXS.
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: len(abBuf) is always a multiple of 16.
+ """
+ _ = abBuf; _ = cMsTimeout;
+ return False;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ """
+ Receive cb number of bytes from the TXS.
+
+ Returns the bytes (array('B')) on success.
+ Returns None on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: cb is always a multiple of 16.
+ """
+ _ = cb; _ = cMsTimeout; _ = fNoDataOk;
+ return None;
+
+ def isConnectionOk(self):
+ """
+ Checks if the connection is OK.
+
+ Returns True if it is.
+ Returns False if it isn't (caller should call diconnect).
+
+ Override this method, don't call super.
+ """
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ """
+ Checks if there is incoming bytes, optionally waiting cMsTimeout
+ milliseconds for something to arrive.
+
+ Returns True if there is, False if there isn't.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def sendMsgInt(self, sOpcode, cMsTimeout, abPayload = array.array('B')):
+ """
+ Sends a message (opcode + encoded payload).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ """
+ # Fix + check the opcode.
+ if len(sOpcode) < 2:
+ reporter.fatal('sendMsgInt: invalid opcode length: %d (\"%s\")' % (len(sOpcode), sOpcode));
+ return False;
+ sOpcode = sOpcode.ljust(8);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('sendMsgInt: invalid opcode encoding: \"%s\"' % (sOpcode));
+ return False;
+
+ # Start construct the message.
+ cbMsg = 16 + len(abPayload);
+ abMsg = array.array('B');
+ abMsg.extend(u32ToByteArray(cbMsg));
+ abMsg.extend((0, 0, 0, 0)); # uCrc32
+ try:
+ abMsg.extend(array.array('B', \
+ ( ord(sOpcode[0]), \
+ ord(sOpcode[1]), \
+ ord(sOpcode[2]), \
+ ord(sOpcode[3]), \
+ ord(sOpcode[4]), \
+ ord(sOpcode[5]), \
+ ord(sOpcode[6]), \
+ ord(sOpcode[7]) ) ) );
+ if abPayload:
+ abMsg.extend(abPayload);
+ except:
+ reporter.fatalXcpt('sendMsgInt: packing problem...');
+ return False;
+
+ # checksum it, padd it and send it off.
+ uCrc32 = zlib.crc32(abMsg[8:]);
+ abMsg[4:8] = u32ToByteArray(uCrc32);
+
+ while len(abMsg) % 16:
+ abMsg.append(0);
+
+ reporter.log2('sendMsgInt: op=%s len=%d timeout=%d' % (sOpcode, len(abMsg), cMsTimeout));
+ return self.sendBytes(abMsg, cMsTimeout);
+
+ def recvMsg(self, cMsTimeout, fNoDataOk = False):
+ """
+ Receives a message from the TXS.
+
+ Returns the message three-tuple: length, opcode, payload.
+ Returns (None, None, None) on failure and error details in the log.
+ """
+
+ # Read the header.
+ if self.abReadAheadHdr:
+ assert(len(self.abReadAheadHdr) == 16);
+ abHdr = self.abReadAheadHdr;
+ self.abReadAheadHdr = array.array('B');
+ else:
+ abHdr = self.recvBytes(16, cMsTimeout, fNoDataOk); # (virtual method) # pylint: disable=assignment-from-none
+ if abHdr is None:
+ return (None, None, None);
+ if len(abHdr) != 16:
+ reporter.fatal('recvBytes(16) returns %d bytes!' % (len(abHdr)));
+ return (None, None, None);
+
+ # Unpack and validate the header.
+ cbMsg = getU32(abHdr, 0);
+ uCrc32 = getU32(abHdr, 4);
+
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ sOpcode = abHdr[8:16].tostring(); # pylint: disable=no-member
+ else:
+ sOpcode = abHdr[8:16].tobytes();
+ sOpcode = sOpcode.decode('ascii');
+
+ if cbMsg < 16:
+ reporter.fatal('recvMsg: message length is out of range: %s (min 16 bytes)' % (cbMsg));
+ return (None, None, None);
+ if cbMsg > 1024*1024:
+ reporter.fatal('recvMsg: message length is out of range: %s (max 1MB)' % (cbMsg));
+ return (None, None, None);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('recvMsg: invalid opcode \"%s\"' % (sOpcode));
+ return (None, None, None);
+
+ # Get the payload (if any), dropping the padding.
+ abPayload = array.array('B');
+ if cbMsg > 16:
+ if cbMsg % 16:
+ cbPadding = 16 - (cbMsg % 16);
+ else:
+ cbPadding = 0;
+ abPayload = self.recvBytes(cbMsg - 16 + cbPadding, cMsTimeout, False); # pylint: disable=assignment-from-none
+ if abPayload is None:
+ self.abReadAheadHdr = abHdr;
+ if not fNoDataOk :
+ reporter.log('recvMsg: failed to recv payload bytes!');
+ return (None, None, None);
+
+ while cbPadding > 0:
+ abPayload.pop();
+ cbPadding = cbPadding - 1;
+
+ # Check the CRC-32.
+ if uCrc32 != 0:
+ uActualCrc32 = zlib.crc32(abHdr[8:]);
+ if cbMsg > 16:
+ uActualCrc32 = zlib.crc32(abPayload, uActualCrc32);
+ uActualCrc32 = uActualCrc32 & 0xffffffff;
+ if uCrc32 != uActualCrc32:
+ reporter.fatal('recvMsg: crc error: expected %s, got %s' % (hex(uCrc32), hex(uActualCrc32)));
+ return (None, None, None);
+
+ reporter.log2('recvMsg: op=%s len=%d' % (sOpcode, len(abPayload)));
+ return (cbMsg, sOpcode, abPayload);
+
+ def sendMsg(self, sOpcode, cMsTimeout, aoPayload = ()):
+ """
+ Sends a message (opcode + payload tuple).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ Returns None if you pass the incorrectly typed parameters.
+ """
+ # Encode the payload.
+ abPayload = array.array('B');
+ for o in aoPayload:
+ try:
+ if utils.isString(o):
+ if sys.version_info[0] >= 3:
+ abPayload.extend(o.encode('utf_8'));
+ else:
+ # the primitive approach...
+ sUtf8 = o.encode('utf_8');
+ for ch in sUtf8:
+ abPayload.append(ord(ch))
+ abPayload.append(0);
+ elif isinstance(o, (long, int)):
+ if o < 0 or o > 0xffffffff:
+ reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
+ return None;
+ abPayload.extend(u32ToByteArray(o));
+ elif isinstance(o, array.array):
+ abPayload.extend(o);
+ else:
+ reporter.fatal('sendMsg: unexpected payload type: %s (%s) (aoPayload=%s)' % (type(o), o, aoPayload));
+ return None;
+ except:
+ reporter.fatalXcpt('sendMsg: screwed up the encoding code...');
+ return None;
+ return self.sendMsgInt(sOpcode, cMsTimeout, abPayload);
+
+
+class Session(TdTaskBase):
+ """
+ A Test eXecution Service (TXS) client session.
+ """
+
+ def __init__(self, oTransport, cMsTimeout, cMsIdleFudge, fTryConnect = False, fnProcessEvents = None):
+ """
+ Construct a TXS session.
+
+ This starts by connecting to the TXS and will enter the signalled state
+ when connected or the timeout has been reached.
+ """
+ TdTaskBase.__init__(self, utils.getCallerName(), fnProcessEvents);
+ self.oTransport = oTransport;
+ self.sStatus = "";
+ self.cMsTimeout = 0;
+ self.fErr = True; # Whether to report errors as error.
+ self.msStart = 0;
+ self.oThread = None;
+ self.fnTask = self.taskDummy;
+ self.aTaskArgs = None;
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.fScrewedUpMsgState = False;
+ self.fTryConnect = fTryConnect;
+
+ if not self.startTask(cMsTimeout, False, "connecting", self.taskConnect, (cMsIdleFudge,)):
+ raise base.GenError("startTask failed");
+
+ def __del__(self):
+ """Make sure to cancel the task when deleted."""
+ self.cancelTask();
+
+ def toString(self):
+ return '<%s fnTask=%s, aTaskArgs=%s, sStatus=%s, oTaskRc=%s, cMsTimeout=%s,' \
+ ' msStart=%s, fTryConnect=%s, fErr=%s, fScrewedUpMsgState=%s, t3oReply=%s oTransport=%s, oThread=%s>' \
+ % (TdTaskBase.toString(self), self.fnTask, self.aTaskArgs, self.sStatus, self.oTaskRc, self.cMsTimeout,
+ self.msStart, self.fTryConnect, self.fErr, self.fScrewedUpMsgState, self.t3oReply, self.oTransport, self.oThread);
+
+ def taskDummy(self):
+ """Place holder to catch broken state handling."""
+ raise Exception();
+
+ def startTask(self, cMsTimeout, fIgnoreErrors, sStatus, fnTask, aArgs = ()):
+ """
+ Kicks of a new task.
+
+ cMsTimeout: The task timeout in milliseconds. Values less than
+ 500 ms will be adjusted to 500 ms. This means it is
+ OK to use negative value.
+ sStatus: The task status.
+ fnTask: The method that'll execute the task.
+ aArgs: Arguments to pass to fnTask.
+
+ Returns True on success, False + error in log on failure.
+ """
+ if not self.cancelTask():
+ reporter.maybeErr(not fIgnoreErrors, 'txsclient.Session.startTask: failed to cancel previous task.');
+ return False;
+
+ # Change status and make sure we're the
+ self.lockTask();
+ if self.sStatus != "":
+ self.unlockTask();
+ reporter.maybeErr(not fIgnoreErrors, 'txsclient.Session.startTask: race.');
+ return False;
+ self.sStatus = "setup";
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.resetTaskLocked();
+ self.unlockTask();
+
+ self.cMsTimeout = max(cMsTimeout, 500);
+ self.fErr = not fIgnoreErrors;
+ self.fnTask = fnTask;
+ self.aTaskArgs = aArgs;
+ self.oThread = threading.Thread(target=self.taskThread, args=(), name=('TXS-%s' % (sStatus)));
+ self.oThread.setDaemon(True); # pylint: disable=deprecated-method
+ self.msStart = base.timestampMilli();
+
+ self.lockTask();
+ self.sStatus = sStatus;
+ self.unlockTask();
+ self.oThread.start();
+
+ return True;
+
+ def cancelTask(self, fSync = True):
+ """
+ Attempts to cancel any pending tasks.
+ Returns success indicator (True/False).
+ """
+ self.lockTask();
+
+ if self.sStatus == "":
+ self.unlockTask();
+ return True;
+ if self.sStatus == "setup":
+ self.unlockTask();
+ return False;
+ if self.sStatus == "cancelled":
+ self.unlockTask();
+ return False;
+
+ reporter.log('txsclient: cancelling "%s"...' % (self.sStatus));
+ if self.sStatus == 'connecting':
+ self.oTransport.cancelConnect();
+
+ self.sStatus = "cancelled";
+ oThread = self.oThread;
+ self.unlockTask();
+
+ if not fSync:
+ return False;
+
+ oThread.join(61.0);
+
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ return oThread.isAlive(); # pylint: disable=no-member
+ return oThread.is_alive();
+
+ def taskThread(self):
+ """
+ The task thread function.
+ This does some housekeeping activities around the real task method call.
+ """
+ if not self.isCancelled():
+ try:
+ fnTask = self.fnTask;
+ oTaskRc = fnTask(*self.aTaskArgs);
+ except:
+ reporter.fatalXcpt('taskThread', 15);
+ oTaskRc = None;
+ else:
+ reporter.log('taskThread: cancelled already');
+
+ self.lockTask();
+
+ reporter.log('taskThread: signalling task with status "%s", oTaskRc=%s' % (self.sStatus, oTaskRc));
+ self.oTaskRc = oTaskRc;
+ self.oThread = None;
+ self.sStatus = '';
+ self.signalTaskLocked();
+
+ self.unlockTask();
+ return None;
+
+ def isCancelled(self):
+ """Internal method for checking if the task has been cancelled."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ self.unlockTask();
+ if sStatus == "cancelled":
+ return True;
+ return False;
+
+ def hasTimedOut(self):
+ """Internal method for checking if the task has timed out or not."""
+ cMsLeft = self.getMsLeft();
+ if cMsLeft <= 0:
+ return True;
+ return False;
+
+ def getMsLeft(self, cMsMin = 0, cMsMax = -1):
+ """Gets the time left until the timeout."""
+ cMsElapsed = base.timestampMilli() - self.msStart;
+ if cMsElapsed < 0:
+ return cMsMin;
+ cMsLeft = self.cMsTimeout - cMsElapsed;
+ if cMsLeft <= cMsMin:
+ return cMsMin;
+ if cMsLeft > cMsMax > 0:
+ return cMsMax
+ return cMsLeft;
+
+ def recvReply(self, cMsTimeout = None, fNoDataOk = False):
+ """
+ Wrapper for TransportBase.recvMsg that stashes the response away
+ so the client can inspect it later on.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(cMsTimeout, fNoDataOk);
+ self.lockTask();
+ self.t3oReply = (cbMsg, sOpcode, abPayload);
+ self.unlockTask();
+ return (cbMsg, sOpcode, abPayload);
+
+ def recvAck(self, fNoDataOk = False):
+ """
+ Receives an ACK or error response from the TXS.
+
+ Returns True on success.
+ Returns False on timeout or transport error.
+ Returns (sOpcode, sDetails) tuple on failure. The opcode is stripped
+ and there are always details of some sort or another.
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply(None, fNoDataOk);
+ if cbMsg is None:
+ return False;
+ sOpcode = sOpcode.strip()
+ if sOpcode == "ACK":
+ return True;
+ return (sOpcode, getSZ(abPayload, 0, sOpcode));
+
+ def recvAckLogged(self, sCommand, fNoDataOk = False):
+ """
+ Wrapper for recvAck and logging.
+ Returns True on success (ACK).
+ Returns False on time, transport error and errors signalled by TXS.
+ """
+ rc = self.recvAck(fNoDataOk);
+ if rc is not True and not fNoDataOk:
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ else:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, rc[0], rc[1]));
+ rc = False;
+ return rc;
+
+ def recvTrueFalse(self, sCommand):
+ """
+ Receives a TRUE/FALSE response from the TXS.
+ Returns True on TRUE, False on FALSE and None on error/other (logged).
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is None:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ return None;
+
+ sOpcode = sOpcode.strip()
+ if sOpcode == "TRUE":
+ return True;
+ if sOpcode == "FALSE":
+ return False;
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, sOpcode, getSZ(abPayload, 0, sOpcode)));
+ return None;
+
+ def sendMsg(self, sOpcode, aoPayload = (), cMsTimeout = None):
+ """
+ Wrapper for TransportBase.sendMsg that inserts the correct timeout.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ return self.oTransport.sendMsg(sOpcode, cMsTimeout, aoPayload);
+
+ def asyncToSync(self, fnAsync, *aArgs):
+ """
+ Wraps an asynchronous task into a synchronous operation.
+
+ Returns False on failure, task return status on success.
+ """
+ rc = fnAsync(*aArgs);
+ if rc is False:
+ reporter.log2('asyncToSync(%s): returns False (#1)' % (fnAsync));
+ return rc;
+
+ rc = self.waitForTask(self.cMsTimeout + 5000);
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'asyncToSync: waitForTask (timeout %d) failed...' % (self.cMsTimeout,));
+ self.cancelTask();
+ #reporter.log2('asyncToSync(%s): returns False (#2)' % (fnAsync, rc));
+ return False;
+
+ rc = self.getResult();
+ #reporter.log2('asyncToSync(%s): returns %s' % (fnAsync, rc));
+ return rc;
+
+ #
+ # Connection tasks.
+ #
+
+ def taskConnect(self, cMsIdleFudge):
+ """Tries to connect to the TXS"""
+ while not self.isCancelled():
+ reporter.log2('taskConnect: connecting ...');
+ rc = self.oTransport.connect(self.getMsLeft(500));
+ if rc is True:
+ reporter.log('taskConnect: succeeded');
+ return self.taskGreet(cMsIdleFudge);
+ if rc is None:
+ reporter.log2('taskConnect: unable to connect');
+ return None;
+ if self.hasTimedOut():
+ reporter.log2('taskConnect: timed out');
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: timed out');
+ return False;
+ time.sleep(self.getMsLeft(1, 1000) / 1000.0);
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: cancelled');
+ return False;
+
+ def taskGreet(self, cMsIdleFudge):
+ """Greets the TXS"""
+ rc = self.sendMsg("HOWDY", ());
+ if rc is True:
+ rc = self.recvAckLogged("HOWDY", self.fTryConnect);
+ if rc is True:
+ while cMsIdleFudge > 0:
+ cMsIdleFudge -= 1000;
+ time.sleep(1);
+ else:
+ self.oTransport.disconnect(self.fTryConnect);
+ return rc;
+
+ def taskBye(self):
+ """Says goodbye to the TXS"""
+ rc = self.sendMsg("BYE");
+ if rc is True:
+ rc = self.recvAckLogged("BYE");
+ self.oTransport.disconnect();
+ return rc;
+
+ def taskVer(self):
+ """Requests version information from TXS"""
+ rc = self.sendMsg("VER");
+ if rc is True:
+ rc = False;
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is not None:
+ sOpcode = sOpcode.strip();
+ if sOpcode == "ACK VER":
+ sVer = getSZ(abPayload, 0);
+ if sVer is not None:
+ rc = sVer;
+ else:
+ reporter.maybeErr(self.fErr, 'taskVer got a bad reply: %s' % (sOpcode,));
+ else:
+ reporter.maybeErr(self.fErr, 'taskVer got 3xNone from recvReply.');
+ return rc;
+
+ def taskUuid(self):
+ """Gets the TXS UUID"""
+ rc = self.sendMsg("UUID");
+ if rc is True:
+ rc = False;
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is not None:
+ sOpcode = sOpcode.strip()
+ if sOpcode == "ACK UUID":
+ sUuid = getSZ(abPayload, 0);
+ if sUuid is not None:
+ sUuid = '{%s}' % (sUuid,)
+ try:
+ _ = uuid.UUID(sUuid);
+ rc = sUuid;
+ except:
+ reporter.errorXcpt('taskUuid got an invalid UUID string %s' % (sUuid,));
+ else:
+ reporter.maybeErr(self.fErr, 'taskUuid did not get a UUID string.');
+ else:
+ reporter.maybeErr(self.fErr, 'taskUuid got a bad reply: %s' % (sOpcode,));
+ else:
+ reporter.maybeErr(self.fErr, 'taskUuid got 3xNone from recvReply.');
+ return rc;
+
+ #
+ # Process task
+ # pylint: disable=missing-docstring
+ #
+
+ def taskExecEx(self, sExecName, fFlags, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr, oTestPipe, sAsUser): # pylint: disable=too-many-arguments,too-many-locals,too-many-statements,line-too-long
+ # Construct the payload.
+ aoPayload = [long(fFlags), '%s' % (sExecName), long(len(asArgs))];
+ for sArg in asArgs:
+ aoPayload.append('%s' % (sArg));
+ aoPayload.append(long(len(asAddEnv)));
+ for sPutEnv in asAddEnv:
+ aoPayload.append('%s' % (sPutEnv));
+ for o in (oStdIn, oStdOut, oStdErr, oTestPipe):
+ if utils.isString(o):
+ aoPayload.append(o);
+ elif o is not None:
+ aoPayload.append('|');
+ o.uTxsClientCrc32 = zlib.crc32(b'');
+ else:
+ aoPayload.append('');
+ aoPayload.append('%s' % (sAsUser));
+ aoPayload.append(long(self.cMsTimeout));
+
+ # Kick of the EXEC command.
+ rc = self.sendMsg('EXEC', aoPayload)
+ if rc is True:
+ rc = self.recvAckLogged('EXEC');
+ if rc is True:
+ # Loop till the process completes, feed input to the TXS and
+ # receive output from it.
+ sFailure = "";
+ msPendingInputReply = None;
+ cbMsg, sOpcode, abPayload = (None, None, None);
+ while True:
+ # Pending input?
+ if msPendingInputReply is None \
+ and oStdIn is not None \
+ and not utils.isString(oStdIn):
+ try:
+ sInput = oStdIn.read(65536);
+ except:
+ reporter.errorXcpt('read standard in');
+ sFailure = 'exception reading stdin';
+ rc = None;
+ break;
+ if sInput:
+ # Convert to a byte array before handing it of to sendMsg or the string
+ # will get some zero termination added breaking the CRC (and injecting
+ # unwanted bytes).
+ abInput = array.array('B', sInput.encode('utf-8'));
+ oStdIn.uTxsClientCrc32 = zlib.crc32(abInput, oStdIn.uTxsClientCrc32);
+ rc = self.sendMsg('STDIN', (long(oStdIn.uTxsClientCrc32 & 0xffffffff), abInput));
+ if rc is not True:
+ sFailure = 'sendMsg failure';
+ break;
+ msPendingInputReply = base.timestampMilli();
+ continue;
+
+ rc = self.sendMsg('STDINEOS');
+ oStdIn = None;
+ if rc is not True:
+ sFailure = 'sendMsg failure';
+ break;
+ msPendingInputReply = base.timestampMilli();
+
+ # Wait for input (500 ms timeout).
+ if cbMsg is None:
+ cbMsg, sOpcode, abPayload = self.recvReply(cMsTimeout=500, fNoDataOk=True);
+ if cbMsg is None:
+ # Check for time out before restarting the loop.
+ # Note! Only doing timeout checking here does mean that
+ # the TXS may prevent us from timing out by
+ # flooding us with data. This is unlikely though.
+ if self.hasTimedOut() \
+ and ( msPendingInputReply is None \
+ or base.timestampMilli() - msPendingInputReply > 30000):
+ reporter.maybeErr(self.fErr, 'taskExecEx: timed out');
+ sFailure = 'timeout';
+ rc = None;
+ break;
+ # Check that the connection is OK.
+ if not self.oTransport.isConnectionOk():
+ self.oTransport.disconnect();
+ sFailure = 'disconnected';
+ rc = False;
+ break;
+ continue;
+
+ # Handle the response.
+ sOpcode = sOpcode.rstrip();
+ if sOpcode == 'STDOUT':
+ oOut = oStdOut;
+ elif sOpcode == 'STDERR':
+ oOut = oStdErr;
+ elif sOpcode == 'TESTPIPE':
+ oOut = oTestPipe;
+ else:
+ oOut = None;
+ if oOut is not None:
+ # Output from the process.
+ if len(abPayload) < 4:
+ sFailure = 'malformed output packet (%s, %u bytes)' % (sOpcode, cbMsg);
+ reporter.maybeErr(self.fErr, 'taskExecEx: %s' % (sFailure));
+ rc = None;
+ break;
+ uStreamCrc32 = getU32(abPayload, 0);
+ oOut.uTxsClientCrc32 = zlib.crc32(abPayload[4:], oOut.uTxsClientCrc32);
+ if uStreamCrc32 != (oOut.uTxsClientCrc32 & 0xffffffff):
+ sFailure = 'crc error - mine=%#x their=%#x (%s, %u bytes)' \
+ % (oOut.uTxsClientCrc32 & 0xffffffff, uStreamCrc32, sOpcode, cbMsg);
+ reporter.maybeErr(self.fErr, 'taskExecEx: %s' % (sFailure));
+ rc = None;
+ break;
+ try:
+ oOut.write(abPayload[4:]);
+ except:
+ sFailure = 'exception writing %s' % (sOpcode);
+ reporter.errorXcpt('taskExecEx: %s' % (sFailure));
+ rc = None;
+ break;
+ elif sOpcode == 'STDINIGN' and msPendingInputReply is not None:
+ # Standard input is ignored. Ignore this condition for now.
+ msPendingInputReply = None;
+ reporter.log('taskExecEx: Standard input is ignored... why?');
+ del oStdIn.uTxsClientCrc32;
+ oStdIn = '/dev/null';
+ elif sOpcode in ('STDINMEM', 'STDINBAD', 'STDINCRC',)\
+ and msPendingInputReply is not None:
+ # TXS STDIN error, abort.
+ # TODO: STDINMEM - consider undoing the previous stdin read and try resubmitt it.
+ msPendingInputReply = None;
+ sFailure = 'TXS is out of memory for std input buffering';
+ reporter.maybeErr(self.fErr, 'taskExecEx: %s' % (sFailure));
+ rc = None;
+ break;
+ elif sOpcode == 'ACK' and msPendingInputReply is not None:
+ msPendingInputReply = None;
+ elif sOpcode.startswith('PROC '):
+ # Process status message, handle it outside the loop.
+ rc = True;
+ break;
+ else:
+ sFailure = 'Unexpected opcode %s' % (sOpcode);
+ reporter.maybeErr(self.fErr, 'taskExecEx: %s' % (sFailure));
+ rc = None;
+ break;
+ # Clear the message.
+ cbMsg, sOpcode, abPayload = (None, None, None);
+
+ # If we sent an STDIN packet and didn't get a reply yet, we'll give
+ # TXS some 5 seconds to reply to this. If we don't wait here we'll
+ # get screwed later on if we mix it up with the reply to some other
+ # command. Hackish.
+ if msPendingInputReply is not None:
+ cbMsg2, sOpcode2, abPayload2 = self.oTransport.recvMsg(5000);
+ if cbMsg2 is not None:
+ reporter.log('taskExecEx: Out of order STDIN, got reply: %s, %s, %s [ignored]'
+ % (cbMsg2, sOpcode2, abPayload2));
+ msPendingInputReply = None;
+ else:
+ reporter.maybeErr(self.fErr, 'taskExecEx: Pending STDIN, no reply after 5 secs!');
+ self.fScrewedUpMsgState = True;
+
+ # Parse the exit status (True), abort (None) or do nothing (False).
+ if rc is True:
+ if sOpcode == 'PROC OK':
+ pass;
+ else:
+ rc = False;
+ # Do proper parsing some other day if needed:
+ # PROC TOK, PROC TOA, PROC DWN, PROC DOO,
+ # PROC NOK + rc, PROC SIG + sig, PROC ABD, FAILED.
+ if sOpcode == 'PROC DOO':
+ reporter.log('taskExecEx: PROC DOO[FUS]: %s' % (abPayload,));
+ elif sOpcode.startswith('PROC NOK'):
+ reporter.log('taskExecEx: PROC NOK: rcExit=%s' % (abPayload,));
+ elif abPayload and sOpcode.startswith('PROC '):
+ reporter.log('taskExecEx: %s payload=%s' % (sOpcode, abPayload,));
+
+ else:
+ if rc is None:
+ # Abort it.
+ reporter.log('taskExecEx: sending ABORT...');
+ rc = self.sendMsg('ABORT');
+ while rc is True:
+ cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(30000);
+ if cbMsg is None:
+ reporter.maybeErr(self.fErr, 'taskExecEx: Pending ABORT, no reply after 30 secs!')
+ self.fScrewedUpMsgState = True;
+ break;
+ if sOpcode.startswith('PROC '):
+ reporter.log('taskExecEx: ABORT reply: %s, %s, %s [ignored]' % (cbMsg, sOpcode, abPayload));
+ break;
+ reporter.log('taskExecEx: ABORT in process, ignoring reply: %s, %s, %s' % (cbMsg, sOpcode, abPayload));
+ # Check that the connection is OK before looping.
+ if not self.oTransport.isConnectionOk():
+ self.oTransport.disconnect();
+ break;
+
+ # Fake response with the reason why we quit.
+ if sFailure is not None:
+ self.t3oReply = (0, 'EXECFAIL', sFailure);
+ rc = None;
+ else:
+ rc = None;
+
+ # Cleanup.
+ for o in (oStdIn, oStdOut, oStdErr, oTestPipe):
+ if o is not None and not utils.isString(o):
+ del o.uTxsClientCrc32; # pylint: disable=maybe-no-member
+ # Make sure all files are closed
+ o.close(); # pylint: disable=maybe-no-member
+ reporter.log('taskExecEx: returns %s' % (rc));
+ return rc;
+
+ #
+ # Admin tasks
+ #
+
+ def hlpRebootShutdownWaitForAck(self, sCmd):
+ """Wait for reboot/shutodwn ACK."""
+ rc = self.recvAckLogged(sCmd);
+ if rc is True:
+ # poll a little while for server to disconnect.
+ uMsStart = base.timestampMilli();
+ while self.oTransport.isConnectionOk() \
+ and base.timestampMilli() - uMsStart >= 5000:
+ if self.oTransport.isRecvPending(min(500, self.getMsLeft())):
+ break;
+ self.oTransport.disconnect();
+ return rc;
+
+ def taskReboot(self):
+ rc = self.sendMsg('REBOOT');
+ if rc is True:
+ rc = self.hlpRebootShutdownWaitForAck('REBOOT');
+ return rc;
+
+ def taskShutdown(self):
+ rc = self.sendMsg('SHUTDOWN');
+ if rc is True:
+ rc = self.hlpRebootShutdownWaitForAck('SHUTDOWN');
+ return rc;
+
+ #
+ # CD/DVD control tasks.
+ #
+
+ ## TODO
+
+ #
+ # File system tasks
+ #
+
+ def taskMkDir(self, sRemoteDir, fMode):
+ rc = self.sendMsg('MKDIR', (fMode, sRemoteDir));
+ if rc is True:
+ rc = self.recvAckLogged('MKDIR');
+ return rc;
+
+ def taskMkDirPath(self, sRemoteDir, fMode):
+ rc = self.sendMsg('MKDRPATH', (fMode, sRemoteDir));
+ if rc is True:
+ rc = self.recvAckLogged('MKDRPATH');
+ return rc;
+
+ def taskMkSymlink(self, sLinkTarget, sLink):
+ rc = self.sendMsg('MKSYMLNK', (sLinkTarget, sLink));
+ if rc is True:
+ rc = self.recvAckLogged('MKSYMLNK');
+ return rc;
+
+ def taskRmDir(self, sRemoteDir):
+ rc = self.sendMsg('RMDIR', (sRemoteDir,));
+ if rc is True:
+ rc = self.recvAckLogged('RMDIR');
+ return rc;
+
+ def taskRmFile(self, sRemoteFile):
+ rc = self.sendMsg('RMFILE', (sRemoteFile,));
+ if rc is True:
+ rc = self.recvAckLogged('RMFILE');
+ return rc;
+
+ def taskRmSymlink(self, sRemoteSymlink):
+ rc = self.sendMsg('RMSYMLNK', (sRemoteSymlink,));
+ if rc is True:
+ rc = self.recvAckLogged('RMSYMLNK');
+ return rc;
+
+ def taskRmTree(self, sRemoteTree):
+ rc = self.sendMsg('RMTREE', (sRemoteTree,));
+ if rc is True:
+ rc = self.recvAckLogged('RMTREE');
+ return rc;
+
+ def taskChMod(self, sRemotePath, fMode):
+ rc = self.sendMsg('CHMOD', (int(fMode), sRemotePath,));
+ if rc is True:
+ rc = self.recvAckLogged('CHMOD');
+ return rc;
+
+ def taskChOwn(self, sRemotePath, idUser, idGroup):
+ rc = self.sendMsg('CHOWN', (int(idUser), int(idGroup), sRemotePath,));
+ if rc is True:
+ rc = self.recvAckLogged('CHOWN');
+ return rc;
+
+ def taskIsDir(self, sRemoteDir):
+ rc = self.sendMsg('ISDIR', (sRemoteDir,));
+ if rc is True:
+ rc = self.recvTrueFalse('ISDIR');
+ return rc;
+
+ def taskIsFile(self, sRemoteFile):
+ rc = self.sendMsg('ISFILE', (sRemoteFile,));
+ if rc is True:
+ rc = self.recvTrueFalse('ISFILE');
+ return rc;
+
+ def taskIsSymlink(self, sRemoteSymlink):
+ rc = self.sendMsg('ISSYMLNK', (sRemoteSymlink,));
+ if rc is True:
+ rc = self.recvTrueFalse('ISSYMLNK');
+ return rc;
+
+ #def "STAT "
+ #def "LSTAT "
+ #def "LIST "
+
+ def taskCopyFile(self, sSrcFile, sDstFile, fMode, fFallbackOkay):
+ """ Copies a file within the remote from source to destination. """
+ _ = fFallbackOkay; # Not used yet.
+ # Note: If fMode is set to 0, it's up to the target OS' implementation with
+ # what a file mode the destination file gets created (i.e. via umask).
+ rc = self.sendMsg('CPFILE', (int(fMode), sSrcFile, sDstFile,));
+ if rc is True:
+ rc = self.recvAckLogged('CPFILE');
+ return rc;
+
+ def taskUploadFile(self, sLocalFile, sRemoteFile, fMode, fFallbackOkay):
+ #
+ # Open the local file (make sure it exist before bothering TXS) and
+ # tell TXS that we want to upload a file.
+ #
+ try:
+ oLocalFile = utils.openNoInherit(sLocalFile, 'rb');
+ except:
+ reporter.errorXcpt('taskUpload: failed to open "%s"' % (sLocalFile));
+ return False;
+
+ # Common cause with taskUploadStr
+ rc = self.taskUploadCommon(oLocalFile, sRemoteFile, fMode, fFallbackOkay);
+
+ # Cleanup.
+ oLocalFile.close();
+ return rc;
+
+ def taskUploadString(self, sContent, sRemoteFile, fMode, fFallbackOkay):
+ # Wrap sContent in a file like class.
+ class InStringFile(object): # pylint: disable=too-few-public-methods
+ def __init__(self, sContent):
+ self.sContent = sContent;
+ self.off = 0;
+
+ def read(self, cbMax):
+ cbLeft = len(self.sContent) - self.off;
+ if cbLeft == 0:
+ return "";
+ if cbLeft <= cbMax:
+ sRet = self.sContent[self.off:(self.off + cbLeft)];
+ else:
+ sRet = self.sContent[self.off:(self.off + cbMax)];
+ self.off = self.off + len(sRet);
+ return sRet;
+
+ oLocalString = InStringFile(sContent);
+ return self.taskUploadCommon(oLocalString, sRemoteFile, fMode, fFallbackOkay);
+
+ def taskUploadCommon(self, oLocalFile, sRemoteFile, fMode, fFallbackOkay):
+ """Common worker used by taskUploadFile and taskUploadString."""
+ #
+ # Command + ACK.
+ #
+ # Only used the new PUT2FILE command if we've got a non-zero mode mask.
+ # Fall back on the old command if the new one is not known by the TXS.
+ #
+ if fMode == 0:
+ rc = self.sendMsg('PUT FILE', (sRemoteFile,));
+ if rc is True:
+ rc = self.recvAckLogged('PUT FILE');
+ else:
+ rc = self.sendMsg('PUT2FILE', (fMode, sRemoteFile));
+ if rc is True:
+ rc = self.recvAck();
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: PUT2FILE transport error');
+ elif rc is not True:
+ if rc[0] == 'UNKNOWN' and fFallbackOkay:
+ # Fallback:
+ rc = self.sendMsg('PUT FILE', (sRemoteFile,));
+ if rc is True:
+ rc = self.recvAckLogged('PUT FILE');
+ else:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: PUT2FILE response was %s: %s' % (rc[0], rc[1],));
+ rc = False;
+ if rc is True:
+ #
+ # Push data packets until eof.
+ #
+ uMyCrc32 = zlib.crc32(b'');
+ while True:
+ # Read up to 64 KB of data.
+ try:
+ sRaw = oLocalFile.read(65536);
+ except:
+ rc = None;
+ break;
+
+ # Convert to array - this is silly!
+ abBuf = array.array('B');
+ if utils.isString(sRaw):
+ for i, _ in enumerate(sRaw):
+ abBuf.append(ord(sRaw[i]));
+ else:
+ abBuf.extend(sRaw);
+ sRaw = None;
+
+ # Update the file stream CRC and send it off.
+ uMyCrc32 = zlib.crc32(abBuf, uMyCrc32);
+ if not abBuf:
+ rc = self.sendMsg('DATA EOF', (long(uMyCrc32 & 0xffffffff), ));
+ else:
+ rc = self.sendMsg('DATA ', (long(uMyCrc32 & 0xffffffff), abBuf));
+ if rc is False:
+ break;
+
+ # Wait for the reply.
+ rc = self.recvAck();
+ if rc is not True:
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'taskUpload: transport error waiting for ACK');
+ else:
+ reporter.maybeErr(self.fErr, 'taskUpload: DATA response was %s: %s' % (rc[0], rc[1]));
+ rc = False;
+ break;
+
+ # EOF?
+ if not abBuf:
+ break;
+
+ # Send ABORT on ACK and I/O errors.
+ if rc is None:
+ rc = self.sendMsg('ABORT');
+ if rc is True:
+ self.recvAckLogged('ABORT');
+ rc = False;
+ return rc;
+
+ def taskDownloadFile(self, sRemoteFile, sLocalFile):
+ try:
+ oLocalFile = utils.openNoInherit(sLocalFile, 'wb');
+ except:
+ reporter.errorXcpt('taskDownload: failed to open "%s"' % (sLocalFile));
+ return False;
+
+ rc = self.taskDownloadCommon(sRemoteFile, oLocalFile);
+
+ oLocalFile.close();
+ if rc is False:
+ try:
+ os.remove(sLocalFile);
+ except:
+ reporter.errorXcpt();
+ return rc;
+
+ def taskDownloadString(self, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True):
+ # Wrap sContent in a file like class.
+ class OutStringFile(object): # pylint: disable=too-few-public-methods
+ def __init__(self):
+ self.asContent = [];
+
+ def write(self, sBuf):
+ self.asContent.append(sBuf);
+ return None;
+
+ oLocalString = OutStringFile();
+ rc = self.taskDownloadCommon(sRemoteFile, oLocalString);
+ if rc is True:
+ rc = '';
+ for sBuf in oLocalString.asContent:
+ if hasattr(sBuf, 'decode'):
+ rc += sBuf.decode(sEncoding, 'ignore' if fIgnoreEncodingErrors else 'strict');
+ else:
+ rc += sBuf;
+ return rc;
+
+ def taskDownloadCommon(self, sRemoteFile, oLocalFile):
+ """Common worker for taskDownloadFile and taskDownloadString."""
+ rc = self.sendMsg('GET FILE', (sRemoteFile,))
+ if rc is True:
+ #
+ # Process data packets until eof.
+ #
+ uMyCrc32 = zlib.crc32(b'');
+ while rc is True:
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is None:
+ reporter.maybeErr(self.fErr, 'taskDownload got 3xNone from recvReply.');
+ rc = None;
+ break;
+
+ # Validate.
+ sOpcode = sOpcode.rstrip();
+ if sOpcode not in ('DATA', 'DATA EOF',):
+ reporter.maybeErr(self.fErr, 'taskDownload got a error reply: opcode="%s" details="%s"'
+ % (sOpcode, getSZ(abPayload, 0, "None")));
+ rc = False;
+ break;
+ if sOpcode == 'DATA' and len(abPayload) < 4:
+ reporter.maybeErr(self.fErr, 'taskDownload got a bad DATA packet: len=%u' % (len(abPayload)));
+ rc = None;
+ break;
+ if sOpcode == 'DATA EOF' and len(abPayload) != 4:
+ reporter.maybeErr(self.fErr, 'taskDownload got a bad EOF packet: len=%u' % (len(abPayload)));
+ rc = None;
+ break;
+
+ # Check the CRC (common for both packets).
+ uCrc32 = getU32(abPayload, 0);
+ if sOpcode == 'DATA':
+ uMyCrc32 = zlib.crc32(abPayload[4:], uMyCrc32);
+ if uCrc32 != (uMyCrc32 & 0xffffffff):
+ reporter.maybeErr(self.fErr, 'taskDownload got a bad CRC: mycrc=%s remotecrc=%s'
+ % (hex(uMyCrc32), hex(uCrc32)));
+ rc = None;
+ break;
+ if sOpcode == 'DATA EOF':
+ rc = self.sendMsg('ACK');
+ break;
+
+ # Finally, push the data to the file.
+ try:
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ abData = abPayload[4:].tostring();
+ else:
+ abData = abPayload[4:].tobytes();
+ oLocalFile.write(abData);
+ except:
+ reporter.errorXcpt('I/O error writing to "%s"' % (sRemoteFile));
+ rc = None;
+ break;
+ rc = self.sendMsg('ACK');
+
+ # Send NACK on validation and I/O errors.
+ if rc is None:
+ rc = self.sendMsg('NACK');
+ rc = False;
+ return rc;
+
+ def taskPackFile(self, sRemoteFile, sRemoteSource):
+ rc = self.sendMsg('PKFILE', (sRemoteFile, sRemoteSource));
+ if rc is True:
+ rc = self.recvAckLogged('PKFILE');
+ return rc;
+
+ def taskUnpackFile(self, sRemoteFile, sRemoteDir):
+ rc = self.sendMsg('UNPKFILE', (sRemoteFile, sRemoteDir));
+ if rc is True:
+ rc = self.recvAckLogged('UNPKFILE');
+ return rc;
+
+ def taskExpandString(self, sString):
+ rc = self.sendMsg('EXP STR ', (sString,));
+ if rc is True:
+ rc = False;
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is not None:
+ sOpcode = sOpcode.strip();
+ if sOpcode == "STRING":
+ sStringExp = getSZ(abPayload, 0);
+ if sStringExp is not None:
+ rc = sStringExp;
+ else: # Also handles SHORTSTR reply (not enough space to store result).
+ reporter.maybeErr(self.fErr, 'taskExpandString got a bad reply: %s' % (sOpcode,));
+ else:
+ reporter.maybeErr(self.fErr, 'taskExpandString got 3xNone from recvReply.');
+ return rc;
+
+ # pylint: enable=missing-docstring
+
+
+ #
+ # Public methods - generic task queries
+ #
+
+ def isSuccess(self):
+ """Returns True if the task completed successfully, otherwise False."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return False;
+ if oTaskRc is False or oTaskRc is None:
+ return False;
+ return True;
+
+ def getResult(self):
+ """
+ Returns the result of a completed task.
+ Returns None if not completed yet or no previous task.
+ """
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return None;
+ return oTaskRc;
+
+ def getLastReply(self):
+ """
+ Returns the last reply three-tuple: cbMsg, sOpcode, abPayload.
+ Returns a None, None, None three-tuple if there was no last reply.
+ """
+ self.lockTask();
+ t3oReply = self.t3oReply;
+ self.unlockTask();
+ return t3oReply;
+
+ #
+ # Public methods - connection.
+ #
+
+ def asyncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a disconnect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "bye", self.taskBye);
+
+ def syncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncDisconnect, cMsTimeout, fIgnoreErrors);
+
+ def asyncVer(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a task for getting the TXS version information.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns the version string on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "ver", self.taskVer);
+
+ def syncVer(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncVer, cMsTimeout, fIgnoreErrors);
+
+ def asyncUuid(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a task for getting the TXS UUID.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns UUID string (in {}) on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "uuid", self.taskUuid);
+
+ def syncUuid(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncUuid, cMsTimeout, fIgnoreErrors);
+
+ #
+ # Public methods - execution.
+ #
+
+ def asyncExecEx(self, sExecName, asArgs = (), asAddEnv = (), # pylint: disable=too-many-arguments
+ oStdIn = None, oStdOut = None, oStdErr = None, oTestPipe = None,
+ sAsUser = "", cMsTimeout = 3600000, fIgnoreErrors = False):
+ """
+ Initiates a exec process task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True if the process exited normally with status code 0.
+ The task returns None if on failure prior to executing the process, and
+ False if the process exited with a different status or in an abnormal
+ manner. Both None and False are logged of course and further info can
+ also be obtained by getLastReply().
+
+ The oStdIn, oStdOut, oStdErr and oTestPipe specifiy how to deal with
+ these streams. If None, no special action is taken and the output goes
+ to where ever the TXS sends its output, and ditto for input.
+ - To send to / read from the bitbucket, pass '/dev/null'.
+ - To redirect to/from a file, just specify the remote filename.
+ - To append to a file use '>>' followed by the remote filename.
+ - To pipe the stream to/from the TXS, specify a file like
+ object. For StdIn a non-blocking read() method is required. For
+ the other a write() method is required. Watch out for deadlock
+ conditions between StdIn and StdOut/StdErr/TestPipe piping.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "exec", self.taskExecEx,
+ (sExecName, long(0), asArgs, asAddEnv, oStdIn,
+ oStdOut, oStdErr, oTestPipe, sAsUser));
+
+ def syncExecEx(self, sExecName, asArgs = (), asAddEnv = (), # pylint: disable=too-many-arguments
+ oStdIn = '/dev/null', oStdOut = '/dev/null',
+ oStdErr = '/dev/null', oTestPipe = '/dev/null',
+ sAsUser = '', cMsTimeout = 3600000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncExecEx, sExecName, asArgs, asAddEnv, oStdIn, oStdOut, \
+ oStdErr, oTestPipe, sAsUser, cMsTimeout, fIgnoreErrors);
+
+ def asyncExec(self, sExecName, asArgs = (), asAddEnv = (), sAsUser = "", fWithTestPipe = True, sPrefix = '', \
+ cMsTimeout = 3600000, fIgnoreErrors = False):
+ """
+ Initiates a exec process test task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True if the process exited normally with status code 0.
+ The task returns None if on failure prior to executing the process, and
+ False if the process exited with a different status or in an abnormal
+ manner. Both None and False are logged of course and further info can
+ also be obtained by getLastReply().
+
+ Standard in is taken from /dev/null. While both standard output and
+ standard error goes directly to reporter.log(). The testpipe is piped
+ to reporter.xxxx.
+ """
+
+ sStdIn = '/dev/null';
+ oStdOut = reporter.FileWrapper('%sstdout' % sPrefix);
+ oStdErr = reporter.FileWrapper('%sstderr' % sPrefix);
+ if fWithTestPipe: oTestPipe = reporter.FileWrapperTestPipe();
+ else: oTestPipe = '/dev/null'; # pylint: disable=redefined-variable-type
+
+ return self.startTask(cMsTimeout, fIgnoreErrors, "exec", self.taskExecEx,
+ (sExecName, long(0), asArgs, asAddEnv, sStdIn, oStdOut, oStdErr, oTestPipe, sAsUser));
+
+ def syncExec(self, sExecName, asArgs = (), asAddEnv = (), sAsUser = '', fWithTestPipe = True, sPrefix = '',
+ cMsTimeout = 3600000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncExec, sExecName, asArgs, asAddEnv, sAsUser, fWithTestPipe, sPrefix, \
+ cMsTimeout, fIgnoreErrors);
+
+ #
+ # Public methods - system
+ #
+
+ def asyncReboot(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a reboot task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged). The
+ session will be disconnected on successful task completion.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "reboot", self.taskReboot, ());
+
+ def syncReboot(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncReboot, cMsTimeout, fIgnoreErrors);
+
+ def asyncShutdown(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a shutdown task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "shutdown", self.taskShutdown, ());
+
+ def syncShutdown(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncShutdown, cMsTimeout, fIgnoreErrors);
+
+
+ #
+ # Public methods - file system
+ #
+
+ def asyncMkDir(self, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a mkdir task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "mkDir", self.taskMkDir, (sRemoteDir, long(fMode)));
+
+ def syncMkDir(self, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncMkDir, sRemoteDir, long(fMode), cMsTimeout, fIgnoreErrors);
+
+ def asyncMkDirPath(self, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a mkdir -p task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "mkDirPath", self.taskMkDirPath, (sRemoteDir, long(fMode)));
+
+ def syncMkDirPath(self, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncMkDirPath, sRemoteDir, long(fMode), cMsTimeout, fIgnoreErrors);
+
+ def asyncMkSymlink(self, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a symlink task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "mkSymlink", self.taskMkSymlink, (sLinkTarget, sLink));
+
+ def syncMkSymlink(self, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncMkSymlink, sLinkTarget, sLink, cMsTimeout, fIgnoreErrors);
+
+ def asyncRmDir(self, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a rmdir task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "rmDir", self.taskRmDir, (sRemoteDir,));
+
+ def syncRmDir(self, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncRmDir, sRemoteDir, cMsTimeout, fIgnoreErrors);
+
+ def asyncRmFile(self, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a rmfile task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "rmFile", self.taskRmFile, (sRemoteFile,));
+
+ def syncRmFile(self, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncRmFile, sRemoteFile, cMsTimeout, fIgnoreErrors);
+
+ def asyncRmSymlink(self, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a rmsymlink task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "rmSymlink", self.taskRmSymlink, (sRemoteSymlink,));
+
+ def syncRmSymlink(self, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncRmSymlink, sRemoteSymlink, cMsTimeout, fIgnoreErrors);
+
+ def asyncRmTree(self, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a rmtree task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "rmTree", self.taskRmTree, (sRemoteTree,));
+
+ def syncRmTree(self, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncRmTree, sRemoteTree, cMsTimeout, fIgnoreErrors);
+
+ def asyncChMod(self, sRemotePath, fMode, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a chmod task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "chMod", self.taskChMod, (sRemotePath, fMode));
+
+ def syncChMod(self, sRemotePath, fMode, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncChMod, sRemotePath, fMode, cMsTimeout, fIgnoreErrors);
+
+ def asyncChOwn(self, sRemotePath, idUser, idGroup, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a chown task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "chOwn", self.taskChOwn, (sRemotePath, idUser, idGroup));
+
+ def syncChOwn(self, sRemotePath, idUser, idGroup, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncChMod, sRemotePath, idUser, idGroup, cMsTimeout, fIgnoreErrors);
+
+ def asyncIsDir(self, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a is-dir query task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True if it's a directory, False if it isn't, and
+ None on error (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "isDir", self.taskIsDir, (sRemoteDir,));
+
+ def syncIsDir(self, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncIsDir, sRemoteDir, cMsTimeout, fIgnoreErrors);
+
+ def asyncIsFile(self, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a is-file query task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True if it's a file, False if it isn't, and None on
+ error (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "isFile", self.taskIsFile, (sRemoteFile,));
+
+ def syncIsFile(self, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncIsFile, sRemoteFile, cMsTimeout, fIgnoreErrors);
+
+ def asyncIsSymlink(self, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a is-symbolic-link query task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True if it's a symbolic linke, False if it isn't, and
+ None on error (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "isSymlink", self.taskIsSymlink, (sRemoteSymlink,));
+
+ def syncIsSymlink(self, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncIsSymlink, sRemoteSymlink, cMsTimeout, fIgnoreErrors);
+
+ #def "STAT "
+ #def "LSTAT "
+ #def "LIST "
+
+ @staticmethod
+ def calcFileXferTimeout(cbFile):
+ """
+ Calculates a reasonable timeout for an upload/download given the file size.
+
+ Returns timeout in milliseconds.
+ """
+ return 30000 + cbFile / 32; # 32 KiB/s (picked out of thin air)
+
+ @staticmethod
+ def calcUploadTimeout(sLocalFile):
+ """
+ Calculates a reasonable timeout for an upload given the file (will stat it).
+
+ Returns timeout in milliseconds.
+ """
+ try: cbFile = os.path.getsize(sLocalFile);
+ except: cbFile = 1024*1024;
+ return Session.calcFileXferTimeout(cbFile);
+
+ def asyncCopyFile(self, sSrcFile, sDstFile,
+ fMode = 0, fFallbackOkay = True, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a file copying task on the remote.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "cpfile",
+ self.taskCopyFile, (sSrcFile, sDstFile, fMode, fFallbackOkay));
+
+ def syncCopyFile(self, sSrcFile, sDstFile, fMode = 0, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncCopyFile, sSrcFile, sDstFile, fMode, cMsTimeout, fIgnoreErrors);
+
+ def asyncUploadFile(self, sLocalFile, sRemoteFile,
+ fMode = 0, fFallbackOkay = True, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a download query task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "upload",
+ self.taskUploadFile, (sLocalFile, sRemoteFile, fMode, fFallbackOkay));
+
+ def syncUploadFile(self, sLocalFile, sRemoteFile, fMode = 0, fFallbackOkay = True, cMsTimeout = 0, fIgnoreErrors = False):
+ """Synchronous version."""
+ if cMsTimeout <= 0:
+ cMsTimeout = self.calcUploadTimeout(sLocalFile);
+ return self.asyncToSync(self.asyncUploadFile, sLocalFile, sRemoteFile, fMode, fFallbackOkay, cMsTimeout, fIgnoreErrors);
+
+ def asyncUploadString(self, sContent, sRemoteFile,
+ fMode = 0, fFallbackOkay = True, cMsTimeout = 0, fIgnoreErrors = False):
+ """
+ Initiates a upload string task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ if cMsTimeout <= 0:
+ cMsTimeout = self.calcFileXferTimeout(len(sContent));
+ return self.startTask(cMsTimeout, fIgnoreErrors, "uploadString",
+ self.taskUploadString, (sContent, sRemoteFile, fMode, fFallbackOkay));
+
+ def syncUploadString(self, sContent, sRemoteFile, fMode = 0, fFallbackOkay = True, cMsTimeout = 0, fIgnoreErrors = False):
+ """Synchronous version."""
+ if cMsTimeout <= 0:
+ cMsTimeout = self.calcFileXferTimeout(len(sContent));
+ return self.asyncToSync(self.asyncUploadString, sContent, sRemoteFile, fMode, fFallbackOkay, cMsTimeout, fIgnoreErrors);
+
+ def asyncDownloadFile(self, sRemoteFile, sLocalFile, cMsTimeout = 120000, fIgnoreErrors = False):
+ """
+ Initiates a download file task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "downloadFile", self.taskDownloadFile, (sRemoteFile, sLocalFile));
+
+ def syncDownloadFile(self, sRemoteFile, sLocalFile, cMsTimeout = 120000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncDownloadFile, sRemoteFile, sLocalFile, cMsTimeout, fIgnoreErrors);
+
+ def asyncDownloadString(self, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
+ cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a download string task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns a byte string on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "downloadString",
+ self.taskDownloadString, (sRemoteFile, sEncoding, fIgnoreEncodingErrors));
+
+ def syncDownloadString(self, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
+ cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncDownloadString, sRemoteFile, sEncoding, fIgnoreEncodingErrors,
+ cMsTimeout, fIgnoreErrors);
+
+ def asyncPackFile(self, sRemoteFile, sRemoteSource, cMsTimeout = 120000, fIgnoreErrors = False):
+ """
+ Initiates a packing file/directory task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "packFile", self.taskPackFile,
+ (sRemoteFile, sRemoteSource));
+
+ def syncPackFile(self, sRemoteFile, sRemoteSource, cMsTimeout = 120000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncPackFile, sRemoteFile, sRemoteSource, cMsTimeout, fIgnoreErrors);
+
+ def asyncUnpackFile(self, sRemoteFile, sRemoteDir, cMsTimeout = 120000, fIgnoreErrors = False):
+ """
+ Initiates a unpack file task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "unpackFile", self.taskUnpackFile,
+ (sRemoteFile, sRemoteDir));
+
+ def syncUnpackFile(self, sRemoteFile, sRemoteDir, cMsTimeout = 120000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncUnpackFile, sRemoteFile, sRemoteDir, cMsTimeout, fIgnoreErrors);
+
+ def asyncExpandString(self, sString, cMsTimeout = 120000, fIgnoreErrors = False):
+ """
+ Initiates an expand string task.
+
+ Returns expanded string on success, False on failure (logged).
+
+ The task returns True on success, False on failure (logged).
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "expandString",
+ self.taskExpandString, (sString,));
+
+ def syncExpandString(self, sString, cMsTimeout = 120000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncExpandString, sString, cMsTimeout, fIgnoreErrors);
+
+
+class TransportTcp(TransportBase):
+ """
+ TCP transport layer for the TXS client session class.
+ """
+
+ def __init__(self, sHostname, uPort, fReversedSetup):
+ """
+ Save the parameters. The session will call us back to make the
+ connection later on its worker thread.
+ """
+ TransportBase.__init__(self, utils.getCallerName());
+ self.sHostname = sHostname;
+ self.fReversedSetup = fReversedSetup;
+ self.uPort = uPort if uPort is not None else 5042 if fReversedSetup is False else 5048;
+ self.oSocket = None;
+ self.oWakeupW = None;
+ self.oWakeupR = None;
+ self.fConnectCanceled = False;
+ self.fIsConnecting = False;
+ self.oCv = threading.Condition();
+ self.abReadAhead = array.array('B');
+
+ def toString(self):
+ return '<%s sHostname=%s, fReversedSetup=%s, uPort=%s, oSocket=%s,'\
+ ' fConnectCanceled=%s, fIsConnecting=%s, oCv=%s, abReadAhead=%s>' \
+ % (TransportBase.toString(self), self.sHostname, self.fReversedSetup, self.uPort, self.oSocket,
+ self.fConnectCanceled, self.fIsConnecting, self.oCv, self.abReadAhead);
+
+ def __isInProgressXcpt(self, oXcpt):
+ """ In progress exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt.errno == errno.EINPROGRESS:
+ return True;
+ except: pass;
+ # Windows?
+ try:
+ if oXcpt.errno == errno.EWOULDBLOCK:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isWouldBlockXcpt(self, oXcpt):
+ """ Would block exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt.errno == errno.EWOULDBLOCK:
+ return True;
+ except: pass;
+ try:
+ if oXcpt.errno == errno.EAGAIN:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isConnectionReset(self, oXcpt):
+ """ Connection reset by Peer or others. """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt.errno == errno.ECONNRESET:
+ return True;
+ except: pass;
+ try:
+ if oXcpt.errno == errno.ENETRESET:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def _closeWakeupSockets(self):
+ """ Closes the wakup sockets. Caller should own the CV. """
+ oWakeupR = self.oWakeupR;
+ self.oWakeupR = None;
+ if oWakeupR is not None:
+ oWakeupR.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ oWakeupW.close();
+
+ return None;
+
+ def cancelConnect(self):
+ # This is bad stuff.
+ self.oCv.acquire();
+ reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
+ self.fConnectCanceled = True;
+ if self.fIsConnecting:
+ oSocket = self.oSocket;
+ self.oSocket = None;
+ if oSocket is not None:
+ reporter.log2('TransportTcp::cancelConnect: closing the socket');
+ oSocket.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ reporter.log2('TransportTcp::cancelConnect: wakeup call');
+ try: oWakeupW.send(b'cancelled!\n');
+ except: reporter.logXcpt();
+ try: oWakeupW.shutdown(socket.SHUT_WR);
+ except: reporter.logXcpt();
+ oWakeupW.close();
+ self.oCv.release();
+
+ def _connectAsServer(self, oSocket, oWakeupR, cMsTimeout):
+ """ Connects to the TXS server as server, i.e. the reversed setup. """
+ assert(self.fReversedSetup);
+
+ reporter.log2('TransportTcp::_connectAsServer: oSocket=%s, cMsTimeout=%u' % (oSocket, cMsTimeout));
+
+ # Workaround for bind() failure...
+ try:
+ oSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
+ except:
+ reporter.errorXcpt('socket.listen(1) failed');
+ return None;
+
+ # Bind the socket and make it listen.
+ try:
+ oSocket.bind((self.sHostname, self.uPort));
+ except:
+ reporter.errorXcpt('socket.bind((%s,%s)) failed' % (self.sHostname, self.uPort));
+ return None;
+ try:
+ oSocket.listen(1);
+ except:
+ reporter.errorXcpt('socket.listen(1) failed');
+ return None;
+
+ # Accept connections.
+ oClientSocket = None;
+ tClientAddr = None;
+ try:
+ (oClientSocket, tClientAddr) = oSocket.accept();
+ except socket.error as e:
+ if not self.__isInProgressXcpt(e):
+ raise;
+
+ # Do the actual waiting.
+ reporter.log2('TransportTcp::accept: operation in progress (%s)...' % (e,));
+ try:
+ select.select([oSocket, oWakeupR], [], [oSocket, oWakeupR], cMsTimeout / 1000.0);
+ except socket.error as oXctp:
+ if oXctp.errno != errno.EBADF or not self.fConnectCanceled:
+ raise;
+ reporter.log('socket.select() on accept was canceled');
+ return None;
+ except:
+ reporter.logXcpt('socket.select() on accept');
+
+ # Try accept again.
+ try:
+ (oClientSocket, tClientAddr) = oSocket.accept();
+ except socket.error as oXcpt:
+ if not self.__isInProgressXcpt(e):
+ if oXcpt.errno != errno.EBADF or not self.fConnectCanceled:
+ raise;
+ reporter.log('socket.accept() was canceled');
+ return None;
+ reporter.log('socket.accept() timed out');
+ return False;
+ except:
+ reporter.errorXcpt('socket.accept() failed');
+ return None;
+ except:
+ reporter.errorXcpt('socket.accept() failed');
+ return None;
+
+ # Store the connected socket and throw away the server socket.
+ self.oCv.acquire();
+ if not self.fConnectCanceled:
+ self.oSocket.close();
+ self.oSocket = oClientSocket;
+ self.sHostname = "%s:%s" % (tClientAddr[0], tClientAddr[1]);
+ self.oCv.release();
+ return True;
+
+ def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
+ """ Connects to the TXS server as client. """
+ assert(not self.fReversedSetup);
+
+ # Connect w/ timeouts.
+ rc = None;
+ try:
+ oSocket.connect((self.sHostname, self.uPort));
+ rc = True;
+ except socket.error as oXcpt:
+ iRc = oXcpt.errno;
+ if self.__isInProgressXcpt(oXcpt):
+ # Do the actual waiting.
+ reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
+ try:
+ ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
+ if len(ttRc[1]) + len(ttRc[2]) == 0:
+ raise socket.error(errno.ETIMEDOUT, 'select timed out');
+ iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
+ rc = iRc == 0;
+ except socket.error as oXcpt2:
+ iRc = oXcpt2.errno;
+ except:
+ iRc = -42;
+ reporter.fatalXcpt('socket.select() on connect failed');
+
+ if rc is True:
+ pass;
+ elif iRc in (errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.EINTR, errno.ENETDOWN, errno.ENETUNREACH, errno.ETIMEDOUT):
+ rc = False; # try again.
+ else:
+ if iRc != errno.EBADF or not self.fConnectCanceled:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
+ reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
+ except:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
+ return rc;
+
+
+ def connect(self, cMsTimeout):
+ # Create a non-blocking socket.
+ reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
+ try:
+ oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
+ except:
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+ try:
+ oSocket.setblocking(0);
+ except:
+ oSocket.close();
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+
+ # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
+ oWakeupR = None;
+ oWakeupW = None;
+ if hasattr(socket, 'socketpair'):
+ try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=no-member
+ except: reporter.logXcpt('socket.socketpair() failed');
+
+ # Update the state.
+ self.oCv.acquire();
+ rc = None;
+ if not self.fConnectCanceled:
+ self.oSocket = oSocket;
+ self.oWakeupW = oWakeupW;
+ self.oWakeupR = oWakeupR;
+ self.fIsConnecting = True;
+ self.oCv.release();
+
+ # Try connect.
+ if oWakeupR is None:
+ oWakeupR = oSocket; # Avoid select failure.
+ if self.fReversedSetup:
+ rc = self._connectAsServer(oSocket, oWakeupR, cMsTimeout);
+ else:
+ rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
+ oSocket = None;
+
+ # Update the state and cleanup on failure/cancel.
+ self.oCv.acquire();
+ if rc is True and self.fConnectCanceled:
+ rc = False;
+ self.fIsConnecting = False;
+
+ if rc is not True:
+ if self.oSocket is not None:
+ self.oSocket.close();
+ self.oSocket = None;
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ reporter.log2('TransportTcp::connect: returning %s' % (rc,));
+ return rc;
+
+ def disconnect(self, fQuiet = False):
+ if self.oSocket is not None:
+ self.abReadAhead = array.array('B');
+
+ # Try a shutting down the socket gracefully (draining it).
+ try:
+ self.oSocket.shutdown(socket.SHUT_WR);
+ except:
+ if not fQuiet:
+ reporter.error('shutdown(SHUT_WR)');
+ try:
+ self.oSocket.setblocking(0); # just in case it's not set.
+ sData = "1";
+ while sData:
+ sData = self.oSocket.recv(16384);
+ except:
+ pass;
+
+ # Close it.
+ self.oCv.acquire();
+ try: self.oSocket.setblocking(1);
+ except: pass;
+ self.oSocket.close();
+ self.oSocket = None;
+ else:
+ self.oCv.acquire();
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.sendBytes: No connection.');
+ return False;
+
+ # Try send it all.
+ try:
+ cbSent = self.oSocket.send(abBuf);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ return False;
+ cbSent = 0;
+
+ # Do a timed send.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
+ break;
+
+ # wait.
+ try:
+ ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: select returned with exception');
+ break;
+ if not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.sendBytes: select failed');
+ break;
+
+ # Try send more.
+ try:
+ cbSent += self.oSocket.send(abBuf[cbSent:]);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ break;
+
+ return False;
+
+ def __returnReadAheadBytes(self, cb):
+ """ Internal worker for recvBytes. """
+ assert(len(self.abReadAhead) >= cb);
+ abRet = self.abReadAhead[:cb];
+ self.abReadAhead = self.abReadAhead[cb:];
+ return abRet;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
+ return None;
+
+ # Try read in some more data without bothering with timeout handling first.
+ if len(self.abReadAhead) < cb:
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if abBuf:
+ self.abReadAhead.extend(array.array('B', abBuf));
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
+ return None;
+
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ # Timeout loop.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
+ break;
+
+ # Wait.
+ try:
+ ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[0]:
+ reporter.error('TranportTcp.recvBytes: select returned with exception');
+ break;
+ if not ttRc[0]:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.recvBytes: select failed');
+ break;
+
+ # Try read more.
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if not abBuf:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ self.disconnect();
+ return None;
+
+ self.abReadAhead.extend(array.array('B', abBuf));
+
+ except Exception as oXcpt:
+ reporter.log('recv => exception %s' % (oXcpt,));
+ if not self.__isWouldBlockXcpt(oXcpt):
+ if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
+ reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+
+ # Done?
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
+ return None;
+
+ def isConnectionOk(self):
+ if self.oSocket is None:
+ return False;
+ try:
+ ttRc = select.select([], [], [self.oSocket], 0.0);
+ if ttRc[2]:
+ return False;
+
+ self.oSocket.send(array.array('B')); # send zero bytes.
+ except:
+ return False;
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ try:
+ ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
+ if not ttRc[0]:
+ return False;
+ except:
+ pass;
+ return True;
+
+
+def openTcpSession(cMsTimeout, sHostname, uPort = None, fReversedSetup = False, cMsIdleFudge = 0, fnProcessEvents = None):
+ """
+ Opens a connection to a Test Execution Service via TCP, given its name.
+
+ The optional fnProcessEvents callback should be set to vbox.processPendingEvents
+ or similar.
+ """
+ reporter.log2('openTcpSession(%s, %s, %s, %s, %s)' %
+ (cMsTimeout, sHostname, uPort, fReversedSetup, cMsIdleFudge));
+ try:
+ oTransport = TransportTcp(sHostname, uPort, fReversedSetup);
+ oSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fnProcessEvents = fnProcessEvents);
+ except:
+ reporter.errorXcpt(None, 15);
+ return None;
+ return oSession;
+
+
+def tryOpenTcpSession(cMsTimeout, sHostname, uPort = None, fReversedSetup = False, cMsIdleFudge = 0, fnProcessEvents = None):
+ """
+ Tries to open a connection to a Test Execution Service via TCP, given its name.
+
+ This differs from openTcpSession in that it won't log a connection failure
+ as an error.
+ """
+ try:
+ oTransport = TransportTcp(sHostname, uPort, fReversedSetup);
+ oSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect = True, fnProcessEvents = fnProcessEvents);
+ except:
+ reporter.errorXcpt(None, 15);
+ return None;
+ return oSession;
diff --git a/src/VBox/ValidationKit/testdriver/vbox.py b/src/VBox/ValidationKit/testdriver/vbox.py
new file mode 100755
index 00000000..0958e5a7
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vbox.py
@@ -0,0 +1,4581 @@
+# -*- coding: utf-8 -*-
+# $Id: vbox.py $
+# pylint: disable=too-many-lines
+
+"""
+VirtualBox Specific base testdriver.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155472 $"
+
+# pylint: disable=unnecessary-semicolon
+
+# Standard Python imports.
+import datetime
+import os
+import platform
+import re;
+import sys
+import threading
+import time
+import traceback
+
+# Figure out where the validation kit lives and make sure it's in the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+if g_ksValidationKitDir not in sys.path:
+ sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import base;
+from testdriver import btresolver;
+from testdriver import reporter;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+#
+# Exception and Error Unification Hacks.
+# Note! This is pretty gross stuff. Be warned!
+# TODO: Find better ways of doing these things, preferrably in vboxapi.
+#
+
+ComException = None; # pylint: disable=invalid-name
+__fnComExceptionGetAttr__ = None; # pylint: disable=invalid-name
+
+def __MyDefaultGetAttr(oSelf, sName):
+ """ __getattribute__/__getattr__ default fake."""
+ try:
+ oAttr = oSelf.__dict__[sName];
+ except:
+ oAttr = dir(oSelf)[sName];
+ return oAttr;
+
+def __MyComExceptionGetAttr(oSelf, sName):
+ """ ComException.__getattr__ wrapper - both XPCOM and COM. """
+ try:
+ oAttr = __fnComExceptionGetAttr__(oSelf, sName);
+ except AttributeError:
+ if platform.system() == 'Windows':
+ if sName == 'errno':
+ oAttr = __fnComExceptionGetAttr__(oSelf, 'hresult');
+ elif sName == 'msg':
+ oAttr = __fnComExceptionGetAttr__(oSelf, 'strerror');
+ else:
+ raise;
+ else:
+ if sName == 'hresult':
+ oAttr = __fnComExceptionGetAttr__(oSelf, 'errno');
+ elif sName == 'strerror':
+ oAttr = __fnComExceptionGetAttr__(oSelf, 'msg');
+ elif sName == 'excepinfo':
+ oAttr = None;
+ elif sName == 'argerror':
+ oAttr = None;
+ else:
+ raise;
+ #print '__MyComExceptionGetAttr(,%s) -> "%s"' % (sName, oAttr);
+ return oAttr;
+
+def __deployExceptionHacks__(oNativeComExceptionClass):
+ """
+ Deploys the exception and error hacks that helps unifying COM and XPCOM
+ exceptions and errors.
+ """
+ global ComException # pylint: disable=invalid-name
+ global __fnComExceptionGetAttr__ # pylint: disable=invalid-name
+
+ # Hook up our attribute getter for the exception class (ASSUMES new-style).
+ if __fnComExceptionGetAttr__ is None:
+ try:
+ __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattr__');
+ except:
+ try:
+ __fnComExceptionGetAttr__ = getattr(oNativeComExceptionClass, '__getattribute__');
+ except:
+ __fnComExceptionGetAttr__ = __MyDefaultGetAttr;
+ setattr(oNativeComExceptionClass, '__getattr__', __MyComExceptionGetAttr)
+
+ # Make the modified classes accessible (are there better ways to do this?)
+ ComException = oNativeComExceptionClass
+ return None;
+
+
+
+#
+# Utility functions.
+#
+
+def isIpAddrValid(sIpAddr):
+ """
+ Checks if a IPv4 address looks valid. This will return false for
+ localhost and similar.
+ Returns True / False.
+ """
+ if sIpAddr is None: return False;
+ if len(sIpAddr.split('.')) != 4: return False;
+ if sIpAddr.endswith('.0'): return False;
+ if sIpAddr.endswith('.255'): return False;
+ if sIpAddr.startswith('127.'): return False;
+ if sIpAddr.startswith('169.254.'): return False;
+ if sIpAddr.startswith('192.0.2.'): return False;
+ if sIpAddr.startswith('224.0.0.'): return False;
+ return True;
+
+def stringifyErrorInfo(oErrInfo):
+ """
+ Stringifies the error information in a IVirtualBoxErrorInfo object.
+
+ Returns string with error info.
+ """
+ try:
+ rc = oErrInfo.resultCode;
+ sText = oErrInfo.text;
+ sIid = oErrInfo.interfaceID;
+ sComponent = oErrInfo.component;
+ except:
+ sRet = 'bad error object (%s)?' % (oErrInfo,);
+ traceback.print_exc();
+ else:
+ sRet = 'rc=%s text="%s" IID=%s component=%s' % (ComError.toString(rc), sText, sIid, sComponent);
+ return sRet;
+
+def reportError(oErr, sText):
+ """
+ Report a VirtualBox error on oErr. oErr can be IVirtualBoxErrorInfo
+ or IProgress. Anything else is ignored.
+
+ Returns the same a reporter.error().
+ """
+ try:
+ oErrObj = oErr.errorInfo; # IProgress.
+ except:
+ oErrObj = oErr;
+ reporter.error(sText);
+ return reporter.error(stringifyErrorInfo(oErrObj));
+
+def formatComOrXpComException(oType, oXcpt):
+ """
+ Callback installed with the reporter to better format COM exceptions.
+ Similar to format_exception_only, only it returns None if not interested.
+ """
+ _ = oType;
+ oVBoxMgr = vboxcon.goHackModuleClass.oVBoxMgr;
+ if oVBoxMgr is None:
+ return None;
+ if not oVBoxMgr.xcptIsOurXcptKind(oXcpt): # pylint: disable=not-callable
+ return None;
+
+ if platform.system() == 'Windows':
+ hrc = oXcpt.hresult;
+ if hrc == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None and len(oXcpt.excepinfo) > 5:
+ hrc = oXcpt.excepinfo[5];
+ sWhere = oXcpt.excepinfo[1];
+ sMsg = oXcpt.excepinfo[2];
+ else:
+ sWhere = None;
+ sMsg = oXcpt.strerror;
+ else:
+ hrc = oXcpt.errno;
+ sWhere = None;
+ sMsg = oXcpt.msg;
+
+ sHrc = oVBoxMgr.xcptToString(hrc); # pylint: disable=not-callable
+ if sHrc.find('(') < 0:
+ sHrc = '%s (%#x)' % (sHrc, hrc & 0xffffffff,);
+
+ asRet = ['COM-Xcpt: %s' % (sHrc,)];
+ if sMsg and sWhere:
+ asRet.append('--------- %s: %s' % (sWhere, sMsg,));
+ elif sMsg:
+ asRet.append('--------- %s' % (sMsg,));
+ return asRet;
+ #if sMsg and sWhere:
+ # return ['COM-Xcpt: %s - %s: %s' % (sHrc, sWhere, sMsg,)];
+ #if sMsg:
+ # return ['COM-Xcpt: %s - %s' % (sHrc, sMsg,)];
+ #return ['COM-Xcpt: %s' % (sHrc,)];
+
+#
+# Classes
+#
+
+class ComError(object):
+ """
+ Unified COM and XPCOM status code repository.
+ This works more like a module than a class since it's replacing a module.
+ """
+
+ # The VBOX_E_XXX bits:
+ __VBOX_E_BASE = -2135228416;
+ VBOX_E_OBJECT_NOT_FOUND = __VBOX_E_BASE + 1;
+ VBOX_E_INVALID_VM_STATE = __VBOX_E_BASE + 2;
+ VBOX_E_VM_ERROR = __VBOX_E_BASE + 3;
+ VBOX_E_FILE_ERROR = __VBOX_E_BASE + 4;
+ VBOX_E_IPRT_ERROR = __VBOX_E_BASE + 5;
+ VBOX_E_PDM_ERROR = __VBOX_E_BASE + 6;
+ VBOX_E_INVALID_OBJECT_STATE = __VBOX_E_BASE + 7;
+ VBOX_E_HOST_ERROR = __VBOX_E_BASE + 8;
+ VBOX_E_NOT_SUPPORTED = __VBOX_E_BASE + 9;
+ VBOX_E_XML_ERROR = __VBOX_E_BASE + 10;
+ VBOX_E_INVALID_SESSION_STATE = __VBOX_E_BASE + 11;
+ VBOX_E_OBJECT_IN_USE = __VBOX_E_BASE + 12;
+ VBOX_E_DONT_CALL_AGAIN = __VBOX_E_BASE + 13;
+
+ # Reverse lookup table.
+ dDecimalToConst = {}; # pylint: disable=invalid-name
+
+ def __init__(self):
+ raise base.GenError('No instances, please');
+
+ @staticmethod
+ def copyErrors(oNativeComErrorClass):
+ """
+ Copy all error codes from oNativeComErrorClass to this class and
+ install compatability mappings.
+ """
+
+ # First, add the VBOX_E_XXX constants to dDecimalToConst.
+ for sAttr in dir(ComError):
+ if sAttr.startswith('VBOX_E'):
+ oAttr = getattr(ComError, sAttr);
+ ComError.dDecimalToConst[oAttr] = sAttr;
+
+ # Copy all error codes from oNativeComErrorClass to this class.
+ for sAttr in dir(oNativeComErrorClass):
+ if sAttr[0].isupper():
+ oAttr = getattr(oNativeComErrorClass, sAttr);
+ setattr(ComError, sAttr, oAttr);
+ if isinstance(oAttr, int):
+ ComError.dDecimalToConst[oAttr] = sAttr;
+
+ # Install mappings to the other platform.
+ if platform.system() == 'Windows':
+ ComError.NS_OK = ComError.S_OK;
+ ComError.NS_ERROR_FAILURE = ComError.E_FAIL;
+ ComError.NS_ERROR_ABORT = ComError.E_ABORT;
+ ComError.NS_ERROR_NULL_POINTER = ComError.E_POINTER;
+ ComError.NS_ERROR_NO_INTERFACE = ComError.E_NOINTERFACE;
+ ComError.NS_ERROR_INVALID_ARG = ComError.E_INVALIDARG;
+ ComError.NS_ERROR_OUT_OF_MEMORY = ComError.E_OUTOFMEMORY;
+ ComError.NS_ERROR_NOT_IMPLEMENTED = ComError.E_NOTIMPL;
+ ComError.NS_ERROR_UNEXPECTED = ComError.E_UNEXPECTED;
+ else:
+ ComError.E_ACCESSDENIED = -2147024891; # see VBox/com/defs.h
+ ComError.S_OK = ComError.NS_OK;
+ ComError.E_FAIL = ComError.NS_ERROR_FAILURE;
+ ComError.E_ABORT = ComError.NS_ERROR_ABORT;
+ ComError.E_POINTER = ComError.NS_ERROR_NULL_POINTER;
+ ComError.E_NOINTERFACE = ComError.NS_ERROR_NO_INTERFACE;
+ ComError.E_INVALIDARG = ComError.NS_ERROR_INVALID_ARG;
+ ComError.E_OUTOFMEMORY = ComError.NS_ERROR_OUT_OF_MEMORY;
+ ComError.E_NOTIMPL = ComError.NS_ERROR_NOT_IMPLEMENTED;
+ ComError.E_UNEXPECTED = ComError.NS_ERROR_UNEXPECTED;
+ ComError.DISP_E_EXCEPTION = -2147352567; # For COM compatability only.
+ return True;
+
+ @staticmethod
+ def getXcptResult(oXcpt):
+ """
+ Gets the result code for an exception.
+ Returns COM status code (or E_UNEXPECTED).
+ """
+ if platform.system() == 'Windows':
+ # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
+ # empirical info on it so far.
+ try:
+ hrXcpt = oXcpt.hresult;
+ except AttributeError:
+ hrXcpt = ComError.E_UNEXPECTED;
+ if hrXcpt == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None:
+ hrXcpt = oXcpt.excepinfo[5];
+ else:
+ try:
+ hrXcpt = oXcpt.errno;
+ except AttributeError:
+ hrXcpt = ComError.E_UNEXPECTED;
+ return hrXcpt;
+
+ @staticmethod
+ def equal(oXcpt, hr):
+ """
+ Checks if the ComException e is not equal to the COM status code hr.
+ This takes DISP_E_EXCEPTION & excepinfo into account.
+
+ This method can be used with any Exception derivate, however it will
+ only return True for classes similar to the two ComException variants.
+ """
+ if platform.system() == 'Windows':
+ # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
+ # empirical info on it so far.
+ try:
+ hrXcpt = oXcpt.hresult;
+ except AttributeError:
+ return False;
+ if hrXcpt == ComError.DISP_E_EXCEPTION and oXcpt.excepinfo is not None:
+ hrXcpt = oXcpt.excepinfo[5];
+ else:
+ try:
+ hrXcpt = oXcpt.errno;
+ except AttributeError:
+ return False;
+ return hrXcpt == hr;
+
+ @staticmethod
+ def notEqual(oXcpt, hr):
+ """
+ Checks if the ComException e is not equal to the COM status code hr.
+ See equal() for more details.
+ """
+ return not ComError.equal(oXcpt, hr)
+
+ @staticmethod
+ def toString(hr):
+ """
+ Converts the specified COM status code to a string.
+ """
+ try:
+ sStr = ComError.dDecimalToConst[int(hr)];
+ except KeyError:
+ hrLong = long(hr);
+ sStr = '%#x (%d)' % (hrLong, hrLong);
+ return sStr;
+
+
+class Build(object): # pylint: disable=too-few-public-methods
+ """
+ A VirtualBox build.
+
+ Note! After dropping the installation of VBox from this code and instead
+ realizing that with the vboxinstall.py wrapper driver, this class is
+ of much less importance and contains unnecessary bits and pieces.
+ """
+
+ def __init__(self, oDriver, strInstallPath):
+ """
+ Construct a build object from a build file name and/or install path.
+ """
+ # Initialize all members first.
+ self.oDriver = oDriver;
+ self.sInstallPath = strInstallPath;
+ self.sSdkPath = None;
+ self.sSrcRoot = None;
+ self.sKind = None;
+ self.sDesignation = None;
+ self.sType = None;
+ self.sOs = None;
+ self.sArch = None;
+ self.sGuestAdditionsIso = None;
+
+ # Figure out the values as best we can.
+ if strInstallPath is None:
+ #
+ # Both parameters are None, which means we're falling back on a
+ # build in the development tree.
+ #
+ self.sKind = "development";
+
+ if self.sType is None:
+ self.sType = os.environ.get("KBUILD_TYPE", "release");
+ if self.sOs is None:
+ self.sOs = os.environ.get("KBUILD_TARGET", oDriver.sHost);
+ if self.sArch is None:
+ self.sArch = os.environ.get("KBUILD_TARGET_ARCH", oDriver.sHostArch);
+
+ sOut = os.path.join('out', self.sOs + '.' + self.sArch, self.sType);
+ sSearch = os.environ.get('VBOX_TD_DEV_TREE', os.path.dirname(__file__)); # Env.var. for older trees or testboxscript.
+ sCandidat = None;
+ for i in range(0, 10): # pylint: disable=unused-variable
+ sBldDir = os.path.join(sSearch, sOut);
+ if os.path.isdir(sBldDir):
+ sCandidat = os.path.join(sBldDir, 'bin', 'VBoxSVC' + base.exeSuff());
+ if os.path.isfile(sCandidat):
+ self.sSdkPath = os.path.join(sBldDir, 'bin/sdk');
+ break;
+ sCandidat = os.path.join(sBldDir, 'dist/VirtualBox.app/Contents/MacOS/VBoxSVC');
+ if os.path.isfile(sCandidat):
+ self.sSdkPath = os.path.join(sBldDir, 'dist/sdk');
+ break;
+ sSearch = os.path.abspath(os.path.join(sSearch, '..'));
+ if sCandidat is None or not os.path.isfile(sCandidat):
+ raise base.GenError();
+ self.sInstallPath = os.path.abspath(os.path.dirname(sCandidat));
+ self.sSrcRoot = os.path.abspath(sSearch);
+
+ self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', None);
+ if self.sDesignation is None:
+ try:
+ oFile = utils.openNoInherit(os.path.join(self.sSrcRoot, sOut, 'revision.kmk'), 'r');
+ except:
+ pass;
+ else:
+ s = oFile.readline();
+ oFile.close();
+ oMatch = re.search("VBOX_SVN_REV=(\\d+)", s);
+ if oMatch is not None:
+ self.sDesignation = oMatch.group(1);
+
+ if self.sDesignation is None:
+ self.sDesignation = 'XXXXX'
+ else:
+ #
+ # We've been pointed to an existing installation, this could be
+ # in the out dir of a svn checkout, untarred VBoxAll or a real
+ # installation directory.
+ #
+ self.sKind = "preinstalled";
+ self.sType = "release";
+ self.sOs = oDriver.sHost;
+ self.sArch = oDriver.sHostArch;
+ self.sInstallPath = os.path.abspath(strInstallPath);
+ self.sSdkPath = os.path.join(self.sInstallPath, 'sdk');
+ self.sSrcRoot = None;
+ self.sDesignation = os.environ.get('TEST_BUILD_DESIGNATION', 'XXXXX');
+ ## @todo Much more work is required here.
+
+ # Try Determine the build type.
+ sVBoxManage = os.path.join(self.sInstallPath, 'VBoxManage' + base.exeSuff());
+ if os.path.isfile(sVBoxManage):
+ try:
+ (iExit, sStdOut, _) = utils.processOutputUnchecked([sVBoxManage, '--dump-build-type']);
+ sStdOut = sStdOut.strip();
+ if iExit == 0 and sStdOut in ('release', 'debug', 'strict', 'dbgopt', 'asan'):
+ self.sType = sStdOut;
+ reporter.log('Build: Detected build type: %s' % (self.sType));
+ else:
+ reporter.log('Build: --dump-build-type -> iExit=%u sStdOut=%s' % (iExit, sStdOut,));
+ except:
+ reporter.logXcpt('Build: Running "%s --dump-build-type" failed!' % (sVBoxManage,));
+ else:
+ reporter.log3('Build: sVBoxManage=%s not found' % (sVBoxManage,));
+
+ # Do some checks.
+ sVMMR0 = os.path.join(self.sInstallPath, 'VMMR0.r0');
+ if not os.path.isfile(sVMMR0) and utils.getHostOs() == 'solaris': # solaris is special.
+ sVMMR0 = os.path.join(self.sInstallPath, 'amd64' if utils.getHostArch() == 'amd64' else 'i386', 'VMMR0.r0');
+ if not os.path.isfile(sVMMR0):
+ raise base.GenError('%s is missing' % (sVMMR0,));
+
+ # Guest additions location is different on windows for some _stupid_ reason.
+ if self.sOs == 'win' and self.sKind != 'development':
+ self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
+ elif self.sOs == 'darwin':
+ self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
+ elif self.sOs == 'solaris':
+ self.sGuestAdditionsIso = '%s/VBoxGuestAdditions.iso' % (self.sInstallPath,);
+ else:
+ self.sGuestAdditionsIso = '%s/additions/VBoxGuestAdditions.iso' % (self.sInstallPath,);
+
+ # __init__ end;
+
+ def isDevBuild(self):
+ """ Returns True if it's development build (kind), otherwise False. """
+ return self.sKind == 'development';
+
+
+class EventHandlerBase(object):
+ """
+ Base class for both Console and VirtualBox event handlers.
+ """
+
+ def __init__(self, dArgs, fpApiVer, sName = None):
+ self.oVBoxMgr = dArgs['oVBoxMgr'];
+ self.oEventSrc = dArgs['oEventSrc']; # Console/VirtualBox for < 3.3
+ self.oListener = dArgs['oListener'];
+ self.fPassive = self.oListener is not None;
+ self.sName = sName
+ self.fShutdown = False;
+ self.oThread = None;
+ self.fpApiVer = fpApiVer;
+ self.dEventNo2Name = {};
+ for sKey, iValue in self.oVBoxMgr.constants.all_values('VBoxEventType').items():
+ self.dEventNo2Name[iValue] = sKey;
+
+ def threadForPassiveMode(self):
+ """
+ The thread procedure for the event processing thread.
+ """
+ assert self.fPassive is not None;
+ while not self.fShutdown:
+ try:
+ oEvt = self.oEventSrc.getEvent(self.oListener, 500);
+ except:
+ if not self.oVBoxMgr.xcptIsDeadInterface(): reporter.logXcpt();
+ else: reporter.log('threadForPassiveMode/%s: interface croaked (ignored)' % (self.sName,));
+ break;
+ if oEvt:
+ self.handleEvent(oEvt);
+ if not self.fShutdown:
+ try:
+ self.oEventSrc.eventProcessed(self.oListener, oEvt);
+ except:
+ reporter.logXcpt();
+ break;
+ self.unregister(fWaitForThread = False);
+ return None;
+
+ def startThreadForPassiveMode(self):
+ """
+ Called when working in passive mode.
+ """
+ self.oThread = threading.Thread(target = self.threadForPassiveMode, \
+ args=(), name=('PAS-%s' % (self.sName,)));
+ self.oThread.setDaemon(True); # pylint: disable=deprecated-method
+ self.oThread.start();
+ return None;
+
+ def unregister(self, fWaitForThread = True):
+ """
+ Unregister the event handler.
+ """
+ fRc = False;
+ if not self.fShutdown:
+ self.fShutdown = True;
+
+ if self.oEventSrc is not None:
+ if self.fpApiVer < 3.3:
+ try:
+ self.oEventSrc.unregisterCallback(self.oListener);
+ fRc = True;
+ except:
+ reporter.errorXcpt('unregisterCallback failed on %s' % (self.oListener,));
+ else:
+ try:
+ self.oEventSrc.unregisterListener(self.oListener);
+ fRc = True;
+ except:
+ if self.oVBoxMgr.xcptIsDeadInterface():
+ reporter.log('unregisterListener failed on %s because of dead interface (%s)'
+ % (self.oListener, self.oVBoxMgr.xcptToString(),));
+ else:
+ reporter.errorXcpt('unregisterListener failed on %s' % (self.oListener,));
+
+ if self.oThread is not None \
+ and self.oThread != threading.current_thread():
+ self.oThread.join();
+ self.oThread = None;
+
+ _ = fWaitForThread;
+ return fRc;
+
+ def handleEvent(self, oEvt):
+ """
+ Compatibility wrapper that child classes implement.
+ """
+ _ = oEvt;
+ return None;
+
+ @staticmethod
+ def registerDerivedEventHandler(oVBoxMgr, fpApiVer, oSubClass, dArgsCopy, # pylint: disable=too-many-arguments
+ oSrcParent, sSrcParentNm, sICallbackNm,
+ fMustSucceed = True, sLogSuffix = '', aenmEvents = None):
+ """
+ Registers the callback / event listener.
+ """
+ dArgsCopy['oVBoxMgr'] = oVBoxMgr;
+ dArgsCopy['oListener'] = None;
+ if fpApiVer < 3.3:
+ dArgsCopy['oEventSrc'] = oSrcParent;
+ try:
+ oRet = oVBoxMgr.createCallback(sICallbackNm, oSubClass, dArgsCopy);
+ except:
+ reporter.errorXcpt('%s::registerCallback(%s) failed%s' % (sSrcParentNm, oRet, sLogSuffix));
+ else:
+ try:
+ oSrcParent.registerCallback(oRet);
+ return oRet;
+ except Exception as oXcpt:
+ if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
+ reporter.errorXcpt('%s::registerCallback(%s)%s' % (sSrcParentNm, oRet, sLogSuffix));
+ else:
+ #
+ # Scalable event handling introduced in VBox 4.0.
+ #
+ fPassive = sys.platform == 'win32'; # or webservices.
+
+ if not aenmEvents:
+ aenmEvents = (vboxcon.VBoxEventType_Any,);
+
+ try:
+ oEventSrc = oSrcParent.eventSource;
+ dArgsCopy['oEventSrc'] = oEventSrc;
+ if not fPassive:
+ oListener = oRet = oVBoxMgr.createListener(oSubClass, dArgsCopy);
+ else:
+ oListener = oEventSrc.createListener();
+ dArgsCopy['oListener'] = oListener;
+ oRet = oSubClass(dArgsCopy);
+ except:
+ reporter.errorXcpt('%s::eventSource.createListener(%s) failed%s' % (sSrcParentNm, oListener, sLogSuffix));
+ else:
+ try:
+ oEventSrc.registerListener(oListener, aenmEvents, not fPassive);
+ except Exception as oXcpt:
+ if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
+ reporter.errorXcpt('%s::eventSource.registerListener(%s) failed%s'
+ % (sSrcParentNm, oListener, sLogSuffix));
+ else:
+ if not fPassive:
+ if sys.platform == 'win32':
+ from win32com.server.util import unwrap # pylint: disable=import-error
+ oRet = unwrap(oRet);
+ oRet.oListener = oListener;
+ else:
+ oRet.startThreadForPassiveMode();
+ return oRet;
+ return None;
+
+
+
+
+class ConsoleEventHandlerBase(EventHandlerBase):
+ """
+ Base class for handling IConsole events.
+
+ The class has IConsoleCallback (<=3.2) compatible callback methods which
+ the user can override as needed.
+
+ Note! This class must not inherit from object or we'll get type errors in VBoxPython.
+ """
+ def __init__(self, dArgs, sName = None):
+ self.oSession = dArgs['oSession'];
+ self.oConsole = dArgs['oConsole'];
+ if sName is None:
+ sName = self.oSession.sName;
+ EventHandlerBase.__init__(self, dArgs, self.oSession.fpApiVer, sName);
+
+
+ # pylint: disable=missing-docstring,too-many-arguments,unused-argument
+ def onMousePointerShapeChange(self, fVisible, fAlpha, xHot, yHot, cx, cy, abShape):
+ reporter.log2('onMousePointerShapeChange/%s' % (self.sName));
+ def onMouseCapabilityChange(self, fSupportsAbsolute, *aArgs): # Extra argument was added in 3.2.
+ reporter.log2('onMouseCapabilityChange/%s' % (self.sName));
+ def onKeyboardLedsChange(self, fNumLock, fCapsLock, fScrollLock):
+ reporter.log2('onKeyboardLedsChange/%s' % (self.sName));
+ def onStateChange(self, eState):
+ reporter.log2('onStateChange/%s' % (self.sName));
+ def onAdditionsStateChange(self):
+ reporter.log2('onAdditionsStateChange/%s' % (self.sName));
+ def onNetworkAdapterChange(self, oNic):
+ reporter.log2('onNetworkAdapterChange/%s' % (self.sName));
+ def onSerialPortChange(self, oPort):
+ reporter.log2('onSerialPortChange/%s' % (self.sName));
+ def onParallelPortChange(self, oPort):
+ reporter.log2('onParallelPortChange/%s' % (self.sName));
+ def onStorageControllerChange(self):
+ reporter.log2('onStorageControllerChange/%s' % (self.sName));
+ def onMediumChange(self, attachment):
+ reporter.log2('onMediumChange/%s' % (self.sName));
+ def onCPUChange(self, iCpu, fAdd):
+ reporter.log2('onCPUChange/%s' % (self.sName));
+ def onVRDPServerChange(self):
+ reporter.log2('onVRDPServerChange/%s' % (self.sName));
+ def onRemoteDisplayInfoChange(self):
+ reporter.log2('onRemoteDisplayInfoChange/%s' % (self.sName));
+ def onUSBControllerChange(self):
+ reporter.log2('onUSBControllerChange/%s' % (self.sName));
+ def onUSBDeviceStateChange(self, oDevice, fAttached, oError):
+ reporter.log2('onUSBDeviceStateChange/%s' % (self.sName));
+ def onSharedFolderChange(self, fGlobal):
+ reporter.log2('onSharedFolderChange/%s' % (self.sName));
+ def onRuntimeError(self, fFatal, sErrId, sMessage):
+ reporter.log2('onRuntimeError/%s' % (self.sName));
+ def onCanShowWindow(self):
+ reporter.log2('onCanShowWindow/%s' % (self.sName));
+ return True
+ def onShowWindow(self):
+ reporter.log2('onShowWindow/%s' % (self.sName));
+ return None;
+ # pylint: enable=missing-docstring,too-many-arguments,unused-argument
+
+ def handleEvent(self, oEvt):
+ """
+ Compatibility wrapper.
+ """
+ try:
+ oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
+ eType = oEvtBase.type;
+ except:
+ reporter.logXcpt();
+ return None;
+ if eType == vboxcon.VBoxEventType_OnRuntimeError:
+ try:
+ oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IRuntimeErrorEvent');
+ return self.onRuntimeError(oEvtIt.fatal, oEvtIt.id, oEvtIt.message)
+ except:
+ reporter.logXcpt();
+ ## @todo implement the other events.
+ try:
+ if eType not in (vboxcon.VBoxEventType_OnMousePointerShapeChanged,
+ vboxcon.VBoxEventType_OnCursorPositionChanged):
+ if eType in self.dEventNo2Name:
+ reporter.log2('%s(%s)/%s' % (self.dEventNo2Name[eType], str(eType), self.sName));
+ else:
+ reporter.log2('%s/%s' % (str(eType), self.sName));
+ except AttributeError: # Handle older VBox versions which don't have a specific event.
+ pass;
+ return None;
+
+
+class VirtualBoxEventHandlerBase(EventHandlerBase):
+ """
+ Base class for handling IVirtualBox events.
+
+ The class has IConsoleCallback (<=3.2) compatible callback methods which
+ the user can override as needed.
+
+ Note! This class must not inherit from object or we'll get type errors in VBoxPython.
+ """
+ def __init__(self, dArgs, sName = "emanon"):
+ self.oVBoxMgr = dArgs['oVBoxMgr'];
+ self.oVBox = dArgs['oVBox'];
+ EventHandlerBase.__init__(self, dArgs, self.oVBox.fpApiVer, sName);
+
+ # pylint: disable=missing-docstring,unused-argument
+ def onMachineStateChange(self, sMachineId, eState):
+ pass;
+ def onMachineDataChange(self, sMachineId):
+ pass;
+ def onExtraDataCanChange(self, sMachineId, sKey, sValue):
+ # The COM bridge does tuples differently. Not very funny if you ask me... ;-)
+ if self.oVBoxMgr.type == 'MSCOM':
+ return '', 0, True;
+ return True, ''
+ def onExtraDataChange(self, sMachineId, sKey, sValue):
+ pass;
+ def onMediumRegistered(self, sMediumId, eMediumType, fRegistered):
+ pass;
+ def onMachineRegistered(self, sMachineId, fRegistered):
+ pass;
+ def onSessionStateChange(self, sMachineId, eState):
+ pass;
+ def onSnapshotTaken(self, sMachineId, sSnapshotId):
+ pass;
+ def onSnapshotDiscarded(self, sMachineId, sSnapshotId):
+ pass;
+ def onSnapshotChange(self, sMachineId, sSnapshotId):
+ pass;
+ def onGuestPropertyChange(self, sMachineId, sName, sValue, sFlags, fWasDeleted):
+ pass;
+ # pylint: enable=missing-docstring,unused-argument
+
+ def handleEvent(self, oEvt):
+ """
+ Compatibility wrapper.
+ """
+ try:
+ oEvtBase = self.oVBoxMgr.queryInterface(oEvt, 'IEvent');
+ eType = oEvtBase.type;
+ except:
+ reporter.logXcpt();
+ return None;
+ if eType == vboxcon.VBoxEventType_OnMachineStateChanged:
+ try:
+ oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IMachineStateChangedEvent');
+ return self.onMachineStateChange(oEvtIt.machineId, oEvtIt.state)
+ except:
+ reporter.logXcpt();
+ elif eType == vboxcon.VBoxEventType_OnGuestPropertyChanged:
+ try:
+ oEvtIt = self.oVBoxMgr.queryInterface(oEvtBase, 'IGuestPropertyChangedEvent');
+ if hasattr(oEvtIt, 'fWasDeleted'): # Since 7.0 we have a dedicated flag
+ fWasDeleted = oEvtIt.fWasDeleted;
+ else:
+ fWasDeleted = False; # Don't indicate deletion here -- there can be empty guest properties.
+ return self.onGuestPropertyChange(oEvtIt.machineId, oEvtIt.name, oEvtIt.value, oEvtIt.flags, fWasDeleted);
+ except:
+ reporter.logXcpt();
+ ## @todo implement the other events.
+ if eType in self.dEventNo2Name:
+ reporter.log2('%s(%s)/%s' % (self.dEventNo2Name[eType], str(eType), self.sName));
+ else:
+ reporter.log2('%s/%s' % (str(eType), self.sName));
+ return None;
+
+
+class SessionConsoleEventHandler(ConsoleEventHandlerBase):
+ """
+ For catching machine state changes and waking up the task machinery at that point.
+ """
+ def __init__(self, dArgs):
+ ConsoleEventHandlerBase.__init__(self, dArgs);
+
+ def onMachineStateChange(self, sMachineId, eState): # pylint: disable=unused-argument
+ """ Just interrupt the wait loop here so it can check again. """
+ _ = sMachineId; _ = eState;
+ self.oVBoxMgr.interruptWaitEvents();
+
+ def onRuntimeError(self, fFatal, sErrId, sMessage):
+ reporter.log('onRuntimeError/%s: fFatal=%d sErrId=%s sMessage=%s' % (self.sName, fFatal, sErrId, sMessage));
+ oSession = self.oSession;
+ if oSession is not None: # paranoia
+ if sErrId == 'HostMemoryLow':
+ oSession.signalHostMemoryLow();
+ if sys.platform == 'win32':
+ from testdriver import winbase;
+ winbase.logMemoryStats();
+ oSession.signalTask();
+ self.oVBoxMgr.interruptWaitEvents();
+
+
+
+class TestDriver(base.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ This is the VirtualBox test driver.
+ """
+
+ def __init__(self):
+ base.TestDriver.__init__(self);
+ self.fImportedVBoxApi = False;
+ self.fpApiVer = 3.2;
+ self.uRevision = 0;
+ self.uApiRevision = 0;
+ self.oBuild = None;
+ self.oVBoxMgr = None;
+ self.oVBox = None;
+ self.aoRemoteSessions = [];
+ self.aoVMs = []; ## @todo not sure if this list will be of any use.
+ self.oTestVmManager = vboxtestvms.TestVmManager(self.sResourcePath);
+ self.oTestVmSet = vboxtestvms.TestVmSet();
+ self.sSessionTypeDef = 'headless';
+ self.sSessionType = self.sSessionTypeDef;
+ self.fEnableVrdp = True;
+ self.uVrdpBasePortDef = 6000;
+ self.uVrdpBasePort = self.uVrdpBasePortDef;
+ self.sDefBridgedNic = None;
+ self.fUseDefaultSvc = False;
+ self.sLogSelfGroups = '';
+ self.sLogSelfFlags = 'time';
+ self.sLogSelfDest = '';
+ self.sLogSessionGroups = '';
+ self.sLogSessionFlags = 'time';
+ self.sLogSessionDest = '';
+ self.sLogSvcGroups = '';
+ self.sLogSvcFlags = 'time';
+ self.sLogSvcDest = '';
+ self.sSelfLogFile = None;
+ self.sSessionLogFile = None;
+ self.sVBoxSvcLogFile = None;
+ self.oVBoxSvcProcess = None;
+ self.sVBoxSvcPidFile = None;
+ self.fVBoxSvcInDebugger = False;
+ self.fVBoxSvcWaitForDebugger = False;
+ self.sVBoxValidationKit = None;
+ self.sVBoxValidationKitIso = None;
+ self.sVBoxBootSectors = None;
+ self.fAlwaysUploadLogs = False;
+ self.fAlwaysUploadScreenshots = False;
+ self.fAlwaysUploadRecordings = False; # Only upload recording files on failure by default.
+ self.fEnableDebugger = True;
+ self.fVmNoTerminate = False; # Whether to skip exit handling and tearing down the VMs.
+ self.adRecordingFiles = [];
+ self.fRecordingEnabled = False; # Don't record by default (yet).
+ self.fRecordingAudio = False; # Don't record audio by default.
+ self.cSecsRecordingMax = 0; # No recording time limit in seconds.
+ self.cMbRecordingMax = 195; # The test manager web server has a configured upload limit of 200 MiBs.
+ ## @todo Can we query the configured value here
+ # (via `from testmanager import config`)?
+
+ # Drop LD_PRELOAD and enable memory leak detection in LSAN_OPTIONS from vboxinstall.py
+ # before doing build detection. This is a little crude and inflexible...
+ if 'LD_PRELOAD' in os.environ:
+ del os.environ['LD_PRELOAD'];
+ if 'LSAN_OPTIONS' in os.environ:
+ asLSanOptions = os.environ['LSAN_OPTIONS'].split(':');
+ try: asLSanOptions.remove('detect_leaks=0');
+ except: pass;
+ if asLSanOptions: os.environ['LSAN_OPTIONS'] = ':'.join(asLSanOptions);
+ else: del os.environ['LSAN_OPTIONS'];
+
+ # Quietly detect build and validation kit.
+ self._detectBuild(False);
+ self._detectValidationKit(False);
+
+ # Make sure all debug logs goes to the scratch area unless
+ # specified otherwise (more of this later on).
+ if 'VBOX_LOG_DEST' not in os.environ:
+ os.environ['VBOX_LOG_DEST'] = 'nodeny dir=%s' % (self.sScratchPath);
+
+
+ def _detectBuild(self, fQuiet = False):
+ """
+ This is used internally to try figure a locally installed build when
+ running tests manually.
+ """
+ if self.oBuild is not None:
+ return True;
+
+ # Try dev build first since that's where I'll be using it first...
+ if True is True: # pylint: disable=comparison-with-itself
+ try:
+ self.oBuild = Build(self, None);
+ reporter.log('VBox %s build at %s (%s).'
+ % (self.oBuild.sType, self.oBuild.sInstallPath, self.oBuild.sDesignation,));
+ return True;
+ except base.GenError:
+ pass;
+
+ # Try default installation locations.
+ if self.sHost == 'win':
+ sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
+ asLocs = [
+ os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
+ os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
+ os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
+ ];
+ elif self.sHost == 'solaris':
+ asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
+ elif self.sHost == 'darwin':
+ asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
+ elif self.sHost == 'linux':
+ asLocs = [ '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0', '/opt/VirtualBox' ];
+ else:
+ asLocs = [ '/opt/VirtualBox' ];
+ if 'VBOX_INSTALL_PATH' in os.environ:
+ asLocs.insert(0, os.environ['VBOX_INSTALL_PATH']);
+
+ for sLoc in asLocs:
+ try:
+ self.oBuild = Build(self, sLoc);
+ reporter.log('VBox %s build at %s (%s).'
+ % (self.oBuild.sType, self.oBuild.sInstallPath, self.oBuild.sDesignation,));
+ return True;
+ except base.GenError:
+ pass;
+
+ if not fQuiet:
+ reporter.error('failed to find VirtualBox installation');
+ return False;
+
+ def _detectValidationKit(self, fQuiet = False):
+ """
+ This is used internally by the constructor to try locate an unzipped
+ VBox Validation Kit somewhere in the immediate proximity.
+ """
+ if self.sVBoxValidationKit is not None:
+ return True;
+
+ #
+ # Normally it's found where we're running from, which is the same as
+ # the script directly on the testboxes.
+ #
+ asCandidates = [self.sScriptPath, ];
+ if g_ksValidationKitDir not in asCandidates:
+ asCandidates.append(g_ksValidationKitDir);
+ if os.getcwd() not in asCandidates:
+ asCandidates.append(os.getcwd());
+ if self.oBuild is not None and self.oBuild.sInstallPath not in asCandidates:
+ asCandidates.append(self.oBuild.sInstallPath);
+
+ #
+ # When working out of the tree, we'll search the current directory
+ # as well as parent dirs.
+ #
+ for sDir in list(asCandidates):
+ for i in range(10):
+ sDir = os.path.dirname(sDir);
+ if sDir not in asCandidates:
+ asCandidates.append(sDir);
+
+ #
+ # Do the searching.
+ #
+ sCandidate = None;
+ for i, _ in enumerate(asCandidates):
+ sCandidate = asCandidates[i];
+ if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
+ break;
+ sCandidate = os.path.join(sCandidate, 'validationkit');
+ if os.path.isfile(os.path.join(sCandidate, 'VBoxValidationKit.iso')):
+ break;
+ sCandidate = None;
+
+ fRc = sCandidate is not None;
+ if fRc is False:
+ if not fQuiet:
+ reporter.error('failed to find VBox Validation Kit installation (candidates: %s)' % (asCandidates,));
+ sCandidate = os.path.join(self.sScriptPath, 'validationkit'); # Don't leave the values as None.
+
+ #
+ # Set the member values.
+ #
+ self.sVBoxValidationKit = sCandidate;
+ self.sVBoxValidationKitIso = os.path.join(sCandidate, 'VBoxValidationKit.iso');
+ self.sVBoxBootSectors = os.path.join(sCandidate, 'bootsectors');
+ return fRc;
+
+ def _makeEnvironmentChanges(self):
+ """
+ Make the necessary VBox related environment changes.
+ Children not importing the VBox API should call this.
+ """
+ # Make sure we've got our own VirtualBox config and VBoxSVC (on XPCOM at least).
+ if not self.fUseDefaultSvc:
+ os.environ['VBOX_USER_HOME'] = os.path.join(self.sScratchPath, 'VBoxUserHome');
+ sUser = os.environ.get('USERNAME', os.environ.get('USER', os.environ.get('LOGNAME', 'unknown')));
+ os.environ['VBOX_IPC_SOCKETID'] = sUser + '-VBoxTest';
+ return True;
+
+ @staticmethod
+ def makeApiRevision(uMajor, uMinor, uBuild, uApiRevision):
+ """ Calculates an API revision number. """
+ return (long(uMajor) << 56) | (long(uMinor) << 48) | (long(uBuild) << 40) | uApiRevision;
+
+ def importVBoxApi(self):
+ """
+ Import the 'vboxapi' module from the VirtualBox build we're using and
+ instantiate the two basic objects.
+
+ This will try detect an development or installed build if no build has
+ been associated with the driver yet.
+ """
+ if self.fImportedVBoxApi:
+ return True;
+
+ self._makeEnvironmentChanges();
+
+ # Do the detecting.
+ self._detectBuild();
+ if self.oBuild is None:
+ return False;
+
+ # Avoid crashing when loading the 32-bit module (or whatever it is that goes bang).
+ if self.oBuild.sArch == 'x86' \
+ and self.sHost == 'darwin' \
+ and platform.architecture()[0] == '64bit' \
+ and self.oBuild.sKind == 'development' \
+ and os.getenv('VERSIONER_PYTHON_PREFER_32_BIT') != 'yes':
+ reporter.log("WARNING: 64-bit python on darwin, 32-bit VBox development build => crash");
+ reporter.log("WARNING: bash-3.2$ /usr/bin/python2.5 ./testdriver");
+ reporter.log("WARNING: or");
+ reporter.log("WARNING: bash-3.2$ VERSIONER_PYTHON_PREFER_32_BIT=yes ./testdriver");
+ return False;
+
+ # Start VBoxSVC and load the vboxapi bits.
+ if self._startVBoxSVC() is True:
+ assert(self.oVBoxSvcProcess is not None);
+
+ sSavedSysPath = sys.path;
+ self._setupVBoxApi();
+ sys.path = sSavedSysPath;
+
+ # Adjust the default machine folder.
+ if self.fImportedVBoxApi and not self.fUseDefaultSvc and self.fpApiVer >= 4.0:
+ sNewFolder = os.path.join(self.sScratchPath, 'VBoxUserHome', 'Machines');
+ try:
+ self.oVBox.systemProperties.defaultMachineFolder = sNewFolder;
+ except:
+ self.fImportedVBoxApi = False;
+ self.oVBoxMgr = None;
+ self.oVBox = None;
+ reporter.logXcpt("defaultMachineFolder exception (sNewFolder=%s)" % (sNewFolder,));
+
+ # Kill VBoxSVC on failure.
+ if self.oVBoxMgr is None:
+ self._stopVBoxSVC();
+ else:
+ assert(self.oVBoxSvcProcess is None);
+ return self.fImportedVBoxApi;
+
+ def _startVBoxSVC(self): # pylint: disable=too-many-statements
+ """ Starts VBoxSVC. """
+ assert(self.oVBoxSvcProcess is None);
+
+ # Setup vbox logging for VBoxSVC now and start it manually. This way
+ # we can control both logging and shutdown.
+ self.sVBoxSvcLogFile = '%s/VBoxSVC-debug.log' % (self.sScratchPath,);
+ try: os.remove(self.sVBoxSvcLogFile);
+ except: pass;
+ os.environ['VBOX_LOG'] = self.sLogSvcGroups;
+ os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSvcFlags,); # Append becuse of VBoxXPCOMIPCD.
+ if self.sLogSvcDest:
+ os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSvcDest;
+ else:
+ os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sVBoxSvcLogFile,);
+ os.environ['VBOXSVC_RELEASE_LOG_FLAGS'] = 'time append';
+
+ reporter.log2('VBoxSVC environment:');
+ for sKey, sVal in sorted(os.environ.items()):
+ reporter.log2('%s=%s' % (sKey, sVal));
+
+ # Always leave a pid file behind so we can kill it during cleanup-before.
+ self.sVBoxSvcPidFile = '%s/VBoxSVC.pid' % (self.sScratchPath,);
+ fWritePidFile = True;
+
+ cMsFudge = 1;
+ sVBoxSVC = '%s/VBoxSVC' % (self.oBuild.sInstallPath,); ## @todo .exe and stuff.
+ if self.fVBoxSvcInDebugger:
+ if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
+ # Start VBoxSVC in gdb in a new terminal.
+ #sTerm = '/usr/bin/gnome-terminal'; - doesn't work, some fork+exec stuff confusing us.
+ sTerm = '/usr/bin/xterm';
+ if not os.path.isfile(sTerm): sTerm = '/usr/X11/bin/xterm';
+ if not os.path.isfile(sTerm): sTerm = '/usr/X11R6/bin/xterm';
+ if not os.path.isfile(sTerm): sTerm = '/usr/bin/xterm';
+ if not os.path.isfile(sTerm): sTerm = 'xterm';
+ sGdb = '/usr/bin/gdb';
+ if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
+ if not os.path.isfile(sGdb): sGdb = '/usr/sfw/bin/gdb';
+ if not os.path.isfile(sGdb): sGdb = 'gdb';
+ sGdbCmdLine = '%s --args %s --pidfile %s' % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
+ # Cool tweak to run performance analysis instead of gdb:
+ #sGdb = '/usr/bin/valgrind';
+ #sGdbCmdLine = '%s --tool=callgrind --collect-atstart=no -- %s --pidfile %s' \
+ # % (sGdb, sVBoxSVC, self.sVBoxSvcPidFile);
+ reporter.log('term="%s" gdb="%s"' % (sTerm, sGdbCmdLine));
+ os.environ['SHELL'] = self.sOrgShell; # Non-working shell may cause gdb and/or the term problems.
+ ## @todo -e is deprecated; use "-- <args>".
+ self.oVBoxSvcProcess = base.Process.spawnp(sTerm, sTerm, '-e', sGdbCmdLine);
+ os.environ['SHELL'] = self.sOurShell;
+ if self.oVBoxSvcProcess is not None:
+ reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
+ sys.stdin.read(1);
+ fWritePidFile = False;
+
+ elif self.sHost == 'win':
+ sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows\\windbg.exe';
+ if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe';
+ if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows\\windbg.exe'; # Localization rulez! pylint: disable=line-too-long
+ if not os.path.isfile(sWinDbg): sWinDbg = 'c:\\Programme\\Debugging Tools for Windows (x64)\\windbg.exe';
+ if not os.path.isfile(sWinDbg): sWinDbg = 'windbg'; # WinDbg must be in the path; better than nothing.
+ # Assume that everything WinDbg needs is defined using the environment variables.
+ # See WinDbg help for more information.
+ reporter.log('windbg="%s"' % (sWinDbg));
+ self.oVBoxSvcProcess = base.Process.spawn(sWinDbg, sWinDbg, sVBoxSVC + base.exeSuff());
+ if self.oVBoxSvcProcess is not None:
+ reporter.log('Press enter or return after starting VBoxSVC in the debugger...');
+ sys.stdin.read(1);
+ fWritePidFile = False;
+ ## @todo add a pipe interface similar to xpcom if feasible, i.e. if
+ # we can get actual handle values for pipes in python.
+
+ else:
+ reporter.error('Port me!');
+ else: # Run without a debugger attached.
+ if self.sHost in ('darwin', 'freebsd', 'linux', 'solaris', ):
+ #
+ # XPCOM - We can use a pipe to let VBoxSVC notify us when it's ready.
+ #
+ iPipeR, iPipeW = os.pipe();
+ if hasattr(os, 'set_inheritable'):
+ os.set_inheritable(iPipeW, True); # pylint: disable=no-member
+ os.environ['NSPR_INHERIT_FDS'] = 'vboxsvc:startup-pipe:5:0x%x' % (iPipeW,);
+ reporter.log2("NSPR_INHERIT_FDS=%s" % (os.environ['NSPR_INHERIT_FDS']));
+
+ self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC, '--auto-shutdown'); # SIGUSR1 requirement.
+ try: # Try make sure we get the SIGINT and not VBoxSVC.
+ os.setpgid(self.oVBoxSvcProcess.getPid(), 0); # pylint: disable=no-member
+ os.setpgid(0, 0); # pylint: disable=no-member
+ except:
+ reporter.logXcpt();
+
+ os.close(iPipeW);
+ try:
+ sResponse = os.read(iPipeR, 32);
+ except:
+ reporter.logXcpt();
+ sResponse = None;
+ os.close(iPipeR);
+
+ if hasattr(sResponse, 'decode'):
+ sResponse = sResponse.decode('utf-8', 'ignore');
+
+ if sResponse is None or sResponse.strip() != 'READY':
+ reporter.error('VBoxSVC failed starting up... (sResponse=%s)' % (sResponse,));
+ if not self.oVBoxSvcProcess.wait(5000):
+ self.oVBoxSvcProcess.terminate();
+ self.oVBoxSvcProcess.wait(5000);
+ self.oVBoxSvcProcess = None;
+
+ elif self.sHost == 'win':
+ #
+ # Windows - Just fudge it for now.
+ #
+ cMsFudge = 2000;
+ self.oVBoxSvcProcess = base.Process.spawn(sVBoxSVC, sVBoxSVC);
+
+ else:
+ reporter.error('Port me!');
+
+ #
+ # Enable automatic crash reporting if we succeeded.
+ #
+ if self.oVBoxSvcProcess is not None:
+ self.oVBoxSvcProcess.enableCrashReporting('crash/report/svc', 'crash/dump/svc');
+
+ #
+ # Wait for debugger to attach.
+ #
+ if self.oVBoxSvcProcess is not None and self.fVBoxSvcWaitForDebugger:
+ reporter.log('Press any key after attaching to VBoxSVC (pid %s) with a debugger...'
+ % (self.oVBoxSvcProcess.getPid(),));
+ sys.stdin.read(1);
+
+ #
+ # Fudge and pid file.
+ #
+ if self.oVBoxSvcProcess is not None and not self.oVBoxSvcProcess.wait(cMsFudge):
+ if fWritePidFile:
+ iPid = self.oVBoxSvcProcess.getPid();
+ try:
+ oFile = utils.openNoInherit(self.sVBoxSvcPidFile, "w+");
+ oFile.write('%s' % (iPid,));
+ oFile.close();
+ except:
+ reporter.logXcpt('sPidFile=%s' % (self.sVBoxSvcPidFile,));
+ reporter.log('VBoxSVC PID=%u' % (iPid,));
+
+ #
+ # Finally add the task so we'll notice when it dies in a relatively timely manner.
+ #
+ self.addTask(self.oVBoxSvcProcess);
+ else:
+ self.oVBoxSvcProcess = None;
+ try: os.remove(self.sVBoxSvcPidFile);
+ except: pass;
+
+ return self.oVBoxSvcProcess is not None;
+
+
+ def _killVBoxSVCByPidFile(self, sPidFile):
+ """ Kill a VBoxSVC given the pid from it's pid file. """
+
+ # Read the pid file.
+ if not os.path.isfile(sPidFile):
+ return False;
+ try:
+ oFile = utils.openNoInherit(sPidFile, "r");
+ sPid = oFile.readline().strip();
+ oFile.close();
+ except:
+ reporter.logXcpt('sPidfile=%s' % (sPidFile,));
+ return False;
+
+ # Convert the pid to an integer and validate the range a little bit.
+ try:
+ iPid = long(sPid);
+ except:
+ reporter.logXcpt('sPidfile=%s sPid="%s"' % (sPidFile, sPid));
+ return False;
+ if iPid <= 0:
+ reporter.log('negative pid - sPidfile=%s sPid="%s" iPid=%d' % (sPidFile, sPid, iPid));
+ return False;
+
+ # Take care checking that it's VBoxSVC we're about to inhume.
+ if base.processCheckPidAndName(iPid, "VBoxSVC") is not True:
+ reporter.log('Ignoring stale VBoxSVC pid file (pid=%s)' % (iPid,));
+ return False;
+
+ # Loop thru our different ways of getting VBoxSVC to terminate.
+ for aHow in [ [ base.sendUserSignal1, 5000, 'Dropping VBoxSVC a SIGUSR1 hint...'], \
+ [ base.processInterrupt, 5000, 'Dropping VBoxSVC a SIGINT hint...'], \
+ [ base.processTerminate, 7500, 'VBoxSVC is still around, killing it...'] ]:
+ reporter.log(aHow[2]);
+ if aHow[0](iPid) is True:
+ msStart = base.timestampMilli();
+ while base.timestampMilli() - msStart < 5000 \
+ and base.processExists(iPid):
+ time.sleep(0.2);
+
+ fRc = not base.processExists(iPid);
+ if fRc is True:
+ break;
+ if fRc:
+ reporter.log('Successfully killed VBoxSVC (pid=%s)' % (iPid,));
+ else:
+ reporter.log('Failed to kill VBoxSVC (pid=%s)' % (iPid,));
+ return fRc;
+
+ def _stopVBoxSVC(self):
+ """
+ Stops VBoxSVC. Try the polite way first.
+ """
+
+ if self.oVBoxSvcProcess:
+ self.removeTask(self.oVBoxSvcProcess);
+ self.oVBoxSvcProcess.enableCrashReporting(None, None); # Disables it.
+
+ fRc = False;
+ if self.oVBoxSvcProcess is not None \
+ and not self.fVBoxSvcInDebugger:
+ # by process object.
+ if self.oVBoxSvcProcess.isRunning():
+ reporter.log('Dropping VBoxSVC a SIGUSR1 hint...');
+ if not self.oVBoxSvcProcess.sendUserSignal1() \
+ or not self.oVBoxSvcProcess.wait(5000):
+ reporter.log('Dropping VBoxSVC a SIGINT hint...');
+ if not self.oVBoxSvcProcess.interrupt() \
+ or not self.oVBoxSvcProcess.wait(5000):
+ reporter.log('VBoxSVC is still around, killing it...');
+ self.oVBoxSvcProcess.terminate();
+ self.oVBoxSvcProcess.wait(7500);
+ else:
+ reporter.log('VBoxSVC is no longer running...');
+
+ if not self.oVBoxSvcProcess.isRunning():
+ iExit = self.oVBoxSvcProcess.getExitCode();
+ if iExit != 0 or not self.oVBoxSvcProcess.isNormalExit():
+ reporter.error("VBoxSVC exited with status %d (%#x)" % (iExit, self.oVBoxSvcProcess.uExitCode));
+ self.oVBoxSvcProcess = None;
+ else:
+ # by pid file.
+ self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
+ return fRc;
+
+ def _setupVBoxApi(self):
+ """
+ Import and set up the vboxapi.
+ The caller saves and restores sys.path.
+ """
+
+ # Setup vbox logging for self (the test driver).
+ self.sSelfLogFile = '%s/VBoxTestDriver.log' % (self.sScratchPath,);
+ try: os.remove(self.sSelfLogFile);
+ except: pass;
+ os.environ['VBOX_LOG'] = self.sLogSelfGroups;
+ os.environ['VBOX_LOG_FLAGS'] = '%s append' % (self.sLogSelfFlags, );
+ if self.sLogSelfDest:
+ os.environ['VBOX_LOG_DEST'] = 'nodeny ' + self.sLogSelfDest;
+ else:
+ os.environ['VBOX_LOG_DEST'] = 'nodeny file=%s' % (self.sSelfLogFile,);
+ os.environ['VBOX_RELEASE_LOG_FLAGS'] = 'time append';
+
+ reporter.log2('Self environment:');
+ for sKey, sVal in sorted(os.environ.items()):
+ reporter.log2('%s=%s' % (sKey, sVal));
+
+ # Hack the sys.path + environment so the vboxapi can be found.
+ sys.path.insert(0, self.oBuild.sInstallPath);
+ if self.oBuild.sSdkPath is not None:
+ sys.path.insert(0, os.path.join(self.oBuild.sSdkPath, 'installer'))
+ sys.path.insert(1, os.path.join(self.oBuild.sSdkPath, 'install')); # stupid stupid windows installer!
+ sys.path.insert(2, os.path.join(self.oBuild.sSdkPath, 'bindings', 'xpcom', 'python'))
+ os.environ['VBOX_PROGRAM_PATH'] = self.oBuild.sInstallPath;
+ reporter.log("sys.path: %s" % (sys.path));
+
+ try:
+ from vboxapi import VirtualBoxManager; # pylint: disable=import-error
+ except:
+ reporter.logXcpt('Error importing vboxapi');
+ return False;
+
+ # Exception and error hacks.
+ try:
+ # pylint: disable=import-error
+ if self.sHost == 'win':
+ from pythoncom import com_error as NativeComExceptionClass # pylint: disable=no-name-in-module
+ import winerror as NativeComErrorClass
+ else:
+ from xpcom import Exception as NativeComExceptionClass
+ from xpcom import nsError as NativeComErrorClass
+ # pylint: enable=import-error
+ except:
+ reporter.logXcpt('Error importing (XP)COM related stuff for exception hacks and errors');
+ return False;
+ __deployExceptionHacks__(NativeComExceptionClass)
+ ComError.copyErrors(NativeComErrorClass);
+
+ # Create the manager.
+ try:
+ self.oVBoxMgr = VirtualBoxManager(None, None)
+ except:
+ self.oVBoxMgr = None;
+ reporter.logXcpt('VirtualBoxManager exception');
+ return False;
+
+ # Figure the API version.
+ try:
+ oVBox = self.oVBoxMgr.getVirtualBox();
+
+ try:
+ sVer = oVBox.version;
+ except:
+ reporter.logXcpt('Failed to get VirtualBox version, assuming 4.0.0');
+ sVer = "4.0.0";
+ reporter.log("IVirtualBox.version=%s" % (sVer,));
+
+ # Convert the string to three integer values and check ranges.
+ asVerComponents = sVer.split('.');
+ try:
+ sLast = asVerComponents[2].split('_')[0].split('r')[0];
+ aiVerComponents = (int(asVerComponents[0]), int(asVerComponents[1]), int(sLast));
+ except:
+ raise base.GenError('Malformed version "%s"' % (sVer,));
+ if aiVerComponents[0] < 3 or aiVerComponents[0] > 19:
+ raise base.GenError('Malformed version "%s" - 1st component is out of bounds 3..19: %u'
+ % (sVer, aiVerComponents[0]));
+ if aiVerComponents[1] < 0 or aiVerComponents[1] > 9:
+ raise base.GenError('Malformed version "%s" - 2nd component is out of bounds 0..9: %u'
+ % (sVer, aiVerComponents[1]));
+ if aiVerComponents[2] < 0 or aiVerComponents[2] > 99:
+ raise base.GenError('Malformed version "%s" - 3rd component is out of bounds 0..99: %u'
+ % (sVer, aiVerComponents[2]));
+
+ # Convert the three integers into a floating point value. The API is stable within a
+ # x.y release, so the third component only indicates whether it's a stable or
+ # development build of the next release.
+ self.fpApiVer = aiVerComponents[0] + 0.1 * aiVerComponents[1];
+ if aiVerComponents[2] >= 51:
+ if self.fpApiVer not in [6.1, 5.2, 4.3, 3.2,]:
+ self.fpApiVer += 0.1;
+ else:
+ self.fpApiVer = int(self.fpApiVer) + 1.0;
+ # fudge value to be always bigger than the nominal value (0.1 gets rounded down)
+ if round(self.fpApiVer, 1) > self.fpApiVer:
+ self.fpApiVer += sys.float_info.epsilon * self.fpApiVer / 2.0;
+
+ try:
+ self.uRevision = oVBox.revision;
+ except:
+ reporter.logXcpt('Failed to get VirtualBox revision, assuming 0');
+ self.uRevision = 0;
+ reporter.log("IVirtualBox.revision=%u" % (self.uRevision,));
+
+ try:
+ self.uApiRevision = oVBox.APIRevision;
+ except:
+ reporter.logXcpt('Failed to get VirtualBox APIRevision, faking it.');
+ self.uApiRevision = self.makeApiRevision(aiVerComponents[0], aiVerComponents[1], aiVerComponents[2], 0);
+ reporter.log("IVirtualBox.APIRevision=%#x" % (self.uApiRevision,));
+
+ # Patch VBox manage to gloss over portability issues (error constants, etc).
+ self._patchVBoxMgr();
+
+ # Wrap oVBox.
+ from testdriver.vboxwrappers import VirtualBoxWrapper;
+ self.oVBox = VirtualBoxWrapper(oVBox, self.oVBoxMgr, self.fpApiVer, self);
+
+ # Install the constant wrapping hack.
+ vboxcon.goHackModuleClass.oVBoxMgr = self.oVBoxMgr; # VBoxConstantWrappingHack.
+ vboxcon.fpApiVer = self.fpApiVer;
+ reporter.setComXcptFormatter(formatComOrXpComException);
+
+ except:
+ self.oVBoxMgr = None;
+ self.oVBox = None;
+ reporter.logXcpt("getVirtualBox / API version exception");
+ return False;
+
+ # Done
+ self.fImportedVBoxApi = True;
+ reporter.log('Found version %s (%s)' % (self.fpApiVer, sVer));
+ return True;
+
+ def _patchVBoxMgr(self):
+ """
+ Glosses over missing self.oVBoxMgr methods on older VBox versions.
+ """
+
+ def _xcptGetResult(oSelf, oXcpt = None):
+ """ See vboxapi. """
+ _ = oSelf;
+ if oXcpt is None: oXcpt = sys.exc_info()[1];
+ if sys.platform == 'win32':
+ import winerror; # pylint: disable=import-error
+ hrXcpt = oXcpt.hresult;
+ if hrXcpt == winerror.DISP_E_EXCEPTION:
+ hrXcpt = oXcpt.excepinfo[5];
+ else:
+ hrXcpt = oXcpt.error;
+ return hrXcpt;
+
+ def _xcptIsDeadInterface(oSelf, oXcpt = None):
+ """ See vboxapi. """
+ return oSelf.xcptGetStatus(oXcpt) in [
+ 0x80004004, -2147467260, # NS_ERROR_ABORT
+ 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
+ 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
+ 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
+ 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
+ 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
+ 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
+ ];
+
+ def _xcptIsOurXcptKind(oSelf, oXcpt = None):
+ """ See vboxapi. """
+ _ = oSelf;
+ if oXcpt is None: oXcpt = sys.exc_info()[1];
+ if sys.platform == 'win32':
+ from pythoncom import com_error as NativeComExceptionClass # pylint: disable=import-error,no-name-in-module
+ else:
+ from xpcom import Exception as NativeComExceptionClass # pylint: disable=import-error
+ return isinstance(oXcpt, NativeComExceptionClass);
+
+ def _xcptIsEqual(oSelf, oXcpt, hrStatus):
+ """ See vboxapi. """
+ hrXcpt = oSelf.xcptGetResult(oXcpt);
+ return hrXcpt == hrStatus or hrXcpt == hrStatus - 0x100000000; # pylint: disable=consider-using-in
+
+ def _xcptToString(oSelf, oXcpt):
+ """ See vboxapi. """
+ _ = oSelf;
+ if oXcpt is None: oXcpt = sys.exc_info()[1];
+ return str(oXcpt);
+
+ def _getEnumValueName(oSelf, sEnumTypeNm, oEnumValue, fTypePrefix = False):
+ """ See vboxapi. """
+ _ = oSelf; _ = fTypePrefix;
+ return '%s::%s' % (sEnumTypeNm, oEnumValue);
+
+ # Add utilities found in newer vboxapi revision.
+ if not hasattr(self.oVBoxMgr, 'xcptIsDeadInterface'):
+ import types;
+ self.oVBoxMgr.xcptGetResult = types.MethodType(_xcptGetResult, self.oVBoxMgr);
+ self.oVBoxMgr.xcptIsDeadInterface = types.MethodType(_xcptIsDeadInterface, self.oVBoxMgr);
+ self.oVBoxMgr.xcptIsOurXcptKind = types.MethodType(_xcptIsOurXcptKind, self.oVBoxMgr);
+ self.oVBoxMgr.xcptIsEqual = types.MethodType(_xcptIsEqual, self.oVBoxMgr);
+ self.oVBoxMgr.xcptToString = types.MethodType(_xcptToString, self.oVBoxMgr);
+ if not hasattr(self.oVBoxMgr, 'getEnumValueName'):
+ import types;
+ self.oVBoxMgr.getEnumValueName = types.MethodType(_getEnumValueName, self.oVBoxMgr);
+
+
+ def _teardownVBoxApi(self): # pylint: disable=too-many-statements
+ """
+ Drop all VBox object references and shutdown com/xpcom.
+ """
+ if not self.fImportedVBoxApi:
+ return True;
+ import gc;
+
+ # Drop all references we've have to COM objects.
+ self.aoRemoteSessions = [];
+ self.aoVMs = [];
+ self.oVBoxMgr = None;
+ self.oVBox = None;
+ vboxcon.goHackModuleClass.oVBoxMgr = None; # VBoxConstantWrappingHack.
+ reporter.setComXcptFormatter(None);
+
+ # Do garbage collection to try get rid of those objects.
+ try:
+ gc.collect();
+ except:
+ reporter.logXcpt();
+ self.fImportedVBoxApi = False;
+
+ # Check whether the python is still having any COM objects/interfaces around.
+ cVBoxMgrs = 0;
+ aoObjsLeftBehind = [];
+ if self.sHost == 'win':
+ import pythoncom; # pylint: disable=import-error
+ try:
+ cIfs = pythoncom._GetInterfaceCount(); # pylint: disable=no-member,protected-access
+ cObjs = pythoncom._GetGatewayCount(); # pylint: disable=no-member,protected-access
+ if cObjs == 0 and cIfs == 0:
+ reporter.log('_teardownVBoxApi: no interfaces or objects left behind.');
+ else:
+ reporter.log('_teardownVBoxApi: Python COM still has %s objects and %s interfaces...' % ( cObjs, cIfs));
+
+ from win32com.client import DispatchBaseClass; # pylint: disable=import-error
+ for oObj in gc.get_objects():
+ if isinstance(oObj, DispatchBaseClass):
+ reporter.log('_teardownVBoxApi: %s' % (oObj,));
+ aoObjsLeftBehind.append(oObj);
+ elif utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
+ reporter.log('_teardownVBoxApi: %s' % (oObj,));
+ cVBoxMgrs += 1;
+ aoObjsLeftBehind.append(oObj);
+ oObj = None;
+ except:
+ reporter.logXcpt();
+
+ # If not being used, we can safely uninitialize COM.
+ if cIfs == 0 and cObjs == 0 and cVBoxMgrs == 0 and not aoObjsLeftBehind:
+ reporter.log('_teardownVBoxApi: Calling CoUninitialize...');
+ try: pythoncom.CoUninitialize(); # pylint: disable=no-member
+ except: reporter.logXcpt();
+ else:
+ reporter.log('_teardownVBoxApi: Returned from CoUninitialize.');
+ else:
+ try:
+ # XPCOM doesn't crash and burn like COM if you shut it down with interfaces and objects around.
+ # Also, it keeps a number of internal objects and interfaces around to do its job, so shutting
+ # it down before we go looking for dangling interfaces is more or less required.
+ from xpcom import _xpcom as _xpcom; # pylint: disable=import-error,useless-import-alias
+ hrc = _xpcom.DeinitCOM();
+ cIfs = _xpcom._GetInterfaceCount(); # pylint: disable=protected-access
+ cObjs = _xpcom._GetGatewayCount(); # pylint: disable=protected-access
+
+ if cObjs == 0 and cIfs == 0:
+ reporter.log('_teardownVBoxApi: No XPCOM interfaces or objects active. (hrc=%#x)' % (hrc,));
+ else:
+ reporter.log('_teardownVBoxApi: %s XPCOM objects and %s interfaces still around! (hrc=%#x)'
+ % (cObjs, cIfs, hrc));
+ if hasattr(_xpcom, '_DumpInterfaces'):
+ try: _xpcom._DumpInterfaces(); # pylint: disable=protected-access
+ except: reporter.logXcpt('_teardownVBoxApi: _DumpInterfaces failed');
+
+ from xpcom.client import Component; # pylint: disable=import-error
+ for oObj in gc.get_objects():
+ if isinstance(oObj, Component):
+ reporter.log('_teardownVBoxApi: %s' % (oObj,));
+ aoObjsLeftBehind.append(oObj);
+ if utils.getObjectTypeName(oObj) == 'VirtualBoxManager':
+ reporter.log('_teardownVBoxApi: %s' % (oObj,));
+ cVBoxMgrs += 1;
+ aoObjsLeftBehind.append(oObj);
+ oObj = None;
+ except:
+ reporter.logXcpt();
+
+ # Try get the referrers to (XP)COM interfaces and objects that was left behind.
+ for iObj in range(len(aoObjsLeftBehind)): # pylint: disable=consider-using-enumerate
+ try:
+ aoReferrers = gc.get_referrers(aoObjsLeftBehind[iObj]);
+ reporter.log('_teardownVBoxApi: Found %u referrers to %s:' % (len(aoReferrers), aoObjsLeftBehind[iObj],));
+ for oReferrer in aoReferrers:
+ oMyFrame = sys._getframe(0); # pylint: disable=protected-access
+ if oReferrer is oMyFrame:
+ reporter.log('_teardownVBoxApi: - frame of this function');
+ elif oReferrer is aoObjsLeftBehind:
+ reporter.log('_teardownVBoxApi: - aoObjsLeftBehind');
+ else:
+ fPrinted = False;
+ if isinstance(oReferrer, (dict, list, tuple)):
+ try:
+ aoSubReferreres = gc.get_referrers(oReferrer);
+ for oSubRef in aoSubReferreres:
+ if not isinstance(oSubRef, list) \
+ and not isinstance(oSubRef, dict) \
+ and oSubRef is not oMyFrame \
+ and oSubRef is not aoSubReferreres:
+ reporter.log('_teardownVBoxApi: - %s :: %s:'
+ % (utils.getObjectTypeName(oSubRef), utils.getObjectTypeName(oReferrer)));
+ fPrinted = True;
+ break;
+ del aoSubReferreres;
+ except:
+ reporter.logXcpt('subref');
+ if not fPrinted:
+ reporter.log('_teardownVBoxApi: - %s:' % (utils.getObjectTypeName(oReferrer),));
+ try:
+ import pprint;
+ for sLine in pprint.pformat(oReferrer, width = 130).split('\n'):
+ reporter.log('_teardownVBoxApi: %s' % (sLine,));
+ except:
+ reporter.log('_teardownVBoxApi: %s' % (oReferrer,));
+ except:
+ reporter.logXcpt();
+ del aoObjsLeftBehind;
+
+ # Force garbage collection again, just for good measure.
+ try:
+ gc.collect();
+ time.sleep(0.5); # fudge factor
+ except:
+ reporter.logXcpt();
+ return True;
+
+ def _powerOffAllVms(self):
+ """
+ Tries to power off all running VMs.
+ """
+ for oSession in self.aoRemoteSessions:
+ uPid = oSession.getPid();
+ if uPid is not None:
+ reporter.log('_powerOffAllVms: PID is %s for %s, trying to kill it.' % (uPid, oSession.sName,));
+ base.processKill(uPid);
+ else:
+ reporter.log('_powerOffAllVms: No PID for %s' % (oSession.sName,));
+ oSession.close();
+ return None;
+
+
+
+ #
+ # Build type, OS and arch getters.
+ #
+
+ def getBuildType(self):
+ """
+ Get the build type.
+ """
+ if not self._detectBuild():
+ return 'release';
+ return self.oBuild.sType;
+
+ def getBuildOs(self):
+ """
+ Get the build OS.
+ """
+ if not self._detectBuild():
+ return self.sHost;
+ return self.oBuild.sOs;
+
+ def getBuildArch(self):
+ """
+ Get the build arch.
+ """
+ if not self._detectBuild():
+ return self.sHostArch;
+ return self.oBuild.sArch;
+
+ def getGuestAdditionsIso(self):
+ """
+ Get the path to the guest addition iso.
+ """
+ if not self._detectBuild():
+ return None;
+ return self.oBuild.sGuestAdditionsIso;
+
+ #
+ # Override everything from the base class so the testdrivers don't have to
+ # check whether we have overridden a method or not.
+ #
+
+ def showUsage(self):
+ rc = base.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('Generic VirtualBox Options:');
+ reporter.log(' --vbox-session-type <type>');
+ reporter.log(' Sets the session type. Typical values are: gui, headless, sdl');
+ reporter.log(' Default: %s' % (self.sSessionTypeDef));
+ reporter.log(' --vrdp, --no-vrdp');
+ reporter.log(' Enables VRDP, ports starting at 6000');
+ reporter.log(' Default: --vrdp');
+ reporter.log(' --vrdp-base-port <port>');
+ reporter.log(' Sets the base for VRDP port assignments.');
+ reporter.log(' Default: %s' % (self.uVrdpBasePortDef));
+ reporter.log(' --vbox-default-bridged-nic <interface>');
+ reporter.log(' Sets the default interface for bridged networking.');
+ reporter.log(' Default: autodetect');
+ reporter.log(' --vbox-use-svc-defaults');
+ reporter.log(' Use default locations and files for VBoxSVC. This is useful');
+ reporter.log(' for automatically configuring the test VMs for debugging.');
+ reporter.log(' --vbox-log');
+ reporter.log(' The VBox logger group settings for everyone.');
+ reporter.log(' --vbox-log-flags');
+ reporter.log(' The VBox logger flags settings for everyone.');
+ reporter.log(' --vbox-log-dest');
+ reporter.log(' The VBox logger destination settings for everyone.');
+ reporter.log(' --vbox-self-log');
+ reporter.log(' The VBox logger group settings for the testdriver.');
+ reporter.log(' --vbox-self-log-flags');
+ reporter.log(' The VBox logger flags settings for the testdriver.');
+ reporter.log(' --vbox-self-log-dest');
+ reporter.log(' The VBox logger destination settings for the testdriver.');
+ reporter.log(' --vbox-session-log');
+ reporter.log(' The VM session logger group settings.');
+ reporter.log(' --vbox-session-log-flags');
+ reporter.log(' The VM session logger flags.');
+ reporter.log(' --vbox-session-log-dest');
+ reporter.log(' The VM session logger destination settings.');
+ reporter.log(' --vbox-svc-log');
+ reporter.log(' The VBoxSVC logger group settings.');
+ reporter.log(' --vbox-svc-log-flags');
+ reporter.log(' The VBoxSVC logger flag settings.');
+ reporter.log(' --vbox-svc-log-dest');
+ reporter.log(' The VBoxSVC logger destination settings.');
+ reporter.log(' --vbox-svc-debug');
+ reporter.log(' Start VBoxSVC in a debugger.');
+ reporter.log(' --vbox-svc-wait-debug');
+ reporter.log(' Start VBoxSVC and wait for debugger to attach to it.');
+ reporter.log(' --vbox-always-upload-logs');
+ reporter.log(' Whether to always upload log files, or only do so on failure.');
+ reporter.log(' --vbox-always-upload-screenshots');
+ reporter.log(' Whether to always upload final screen shots, or only do so on failure.');
+ reporter.log(' --vbox-always-upload-recordings, --no-vbox-always-upload-recordings');
+ reporter.log(' Whether to always upload recordings, or only do so on failure.');
+ reporter.log(' Default: --no-vbox-always-upload-recordings');
+ reporter.log(' --vbox-debugger, --no-vbox-debugger');
+ reporter.log(' Enables the VBox debugger, port at 5000');
+ reporter.log(' Default: --vbox-debugger');
+ reporter.log(' --vbox-recording, --no-vbox-recording');
+ reporter.log(' Enables/disables recording.');
+ reporter.log(' Default: --no-vbox-recording');
+ reporter.log(' --vbox-recording-audio, --no-vbox-recording-audio');
+ reporter.log(' Enables/disables audio recording.');
+ reporter.log(' Default: --no-vbox-recording-audio');
+ reporter.log(' --vbox-recording-max-time <seconds>');
+ reporter.log(' Limits the maximum recording time in seconds.');
+ reporter.log(' Default: Unlimited.');
+ reporter.log(' --vbox-recording-max-file-size <MiB>');
+ reporter.log(' Limits the maximum per-file size in MiB.');
+ reporter.log(' Explicitly specify 0 for unlimited size.');
+ reporter.log(' Default: 195 MB.');
+ reporter.log(' --vbox-vm-no-terminate');
+ reporter.log(' Does not terminate the test VM after running the test driver.');
+ if self.oTestVmSet is not None:
+ self.oTestVmSet.showUsage();
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--vbox-session-type':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-session-type" takes an argument');
+ self.sSessionType = asArgs[iArg];
+ elif asArgs[iArg] == '--vrdp':
+ self.fEnableVrdp = True;
+ elif asArgs[iArg] == '--no-vrdp':
+ self.fEnableVrdp = False;
+ elif asArgs[iArg] == '--vrdp-base-port':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vrdp-base-port" takes an argument');
+ try: self.uVrdpBasePort = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not a valid integer' % (asArgs[iArg],));
+ if self.uVrdpBasePort <= 0 or self.uVrdpBasePort >= 65530:
+ raise base.InvalidOption('The "--vrdp-base-port" value "%s" is not in the valid range (1..65530)'
+ % (asArgs[iArg],));
+ elif asArgs[iArg] == '--vbox-default-bridged-nic':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-default-bridged-nic" takes an argument');
+ self.sDefBridgedNic = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-use-svc-defaults':
+ self.fUseDefaultSvc = True;
+ elif asArgs[iArg] == '--vbox-self-log':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-self-log" takes an argument');
+ self.sLogSelfGroups = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-self-log-flags':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-self-log-flags" takes an argument');
+ self.sLogSelfFlags = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-self-log-dest':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-self-log-dest" takes an argument');
+ self.sLogSelfDest = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-session-log':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-session-log" takes an argument');
+ self.sLogSessionGroups = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-session-log-flags':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-session-log-flags" takes an argument');
+ self.sLogSessionFlags = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-session-log-dest':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-session-log-dest" takes an argument');
+ self.sLogSessionDest = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-svc-log':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-svc-log" takes an argument');
+ self.sLogSvcGroups = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-svc-log-flags':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-svc-log-flags" takes an argument');
+ self.sLogSvcFlags = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-svc-log-dest':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-svc-log-dest" takes an argument');
+ self.sLogSvcDest = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-log':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-log" takes an argument');
+ self.sLogSelfGroups = asArgs[iArg];
+ self.sLogSessionGroups = asArgs[iArg];
+ self.sLogSvcGroups = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-log-flags':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-svc-flags" takes an argument');
+ self.sLogSelfFlags = asArgs[iArg];
+ self.sLogSessionFlags = asArgs[iArg];
+ self.sLogSvcFlags = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-log-dest':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-log-dest" takes an argument');
+ self.sLogSelfDest = asArgs[iArg];
+ self.sLogSessionDest = asArgs[iArg];
+ self.sLogSvcDest = asArgs[iArg];
+ elif asArgs[iArg] == '--vbox-svc-debug':
+ self.fVBoxSvcInDebugger = True;
+ elif asArgs[iArg] == '--vbox-svc-wait-debug':
+ self.fVBoxSvcWaitForDebugger = True;
+ elif asArgs[iArg] == '--vbox-always-upload-logs':
+ self.fAlwaysUploadLogs = True;
+ elif asArgs[iArg] == '--vbox-always-upload-screenshots':
+ self.fAlwaysUploadScreenshots = True;
+ elif asArgs[iArg] == '--no-vbox-always-upload-recordings':
+ self.fAlwaysUploadRecordings = False;
+ elif asArgs[iArg] == '--vbox-always-upload-recordings':
+ self.fAlwaysUploadRecordings = True;
+ elif asArgs[iArg] == '--vbox-debugger':
+ self.fEnableDebugger = True;
+ elif asArgs[iArg] == '--no-vbox-debugger':
+ self.fEnableDebugger = False;
+ elif asArgs[iArg] == '--vbox-recording':
+ self.fRecordingEnabled = True;
+ elif asArgs[iArg] == '--vbox-no-recording':
+ self.fRecordingEnabled = False;
+ elif asArgs[iArg] == '--no-vbox-recording-audio':
+ self.fRecordingAudio = False;
+ elif asArgs[iArg] == '--vbox-recording-audio':
+ self.fRecordingAudio = True;
+ elif asArgs[iArg] == '--vbox-recording-max-time':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-recording-max-time" takes an argument');
+ self.cSecsRecordingMax = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--vbox-recording-max-file-size':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--vbox-recording-max-file-size" takes an argument');
+ self.cMbRecordingMax = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--vbox-vm-no-terminate':
+ self.fVmNoTerminate = True;
+ else:
+ # Relevant for selecting VMs to test?
+ if self.oTestVmSet is not None:
+ iRc = self.oTestVmSet.parseOption(asArgs, iArg);
+ if iRc != iArg:
+ return iRc;
+
+ # Hand it to the base class.
+ return base.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ return base.TestDriver.completeOptions(self);
+
+ def getNetworkAdapterNameFromType(self, oNic):
+ """
+ Returns the network adapter name from a given adapter type.
+
+ Returns an empty string if not found / invalid.
+ """
+ sAdpName = '';
+ if oNic.adapterType in (vboxcon.NetworkAdapterType_Am79C970A, \
+ vboxcon.NetworkAdapterType_Am79C973, \
+ vboxcon.NetworkAdapterType_Am79C960):
+ sAdpName = 'pcnet';
+ elif oNic.adapterType in (vboxcon.NetworkAdapterType_I82540EM, \
+ vboxcon.NetworkAdapterType_I82543GC, \
+ vboxcon.NetworkAdapterType_I82545EM):
+ sAdpName = 'e1000';
+ elif oNic.adapterType == vboxcon.NetworkAdapterType_Virtio:
+ sAdpName = 'virtio-net';
+ return sAdpName;
+
+ def getResourceSet(self):
+ asRsrcs = [];
+ if self.oTestVmSet is not None:
+ asRsrcs.extend(self.oTestVmSet.getResourceSet());
+ asRsrcs.extend(base.TestDriver.getResourceSet(self));
+ return asRsrcs;
+
+ def actionExtract(self):
+ return base.TestDriver.actionExtract(self);
+
+ def actionVerify(self):
+ return base.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ return base.TestDriver.actionConfig(self);
+
+ def actionExecute(self):
+ return base.TestDriver.actionExecute(self);
+
+ def actionCleanupBefore(self):
+ """
+ Kill any VBoxSVC left behind by a previous test run.
+ """
+ self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
+ return base.TestDriver.actionCleanupBefore(self);
+
+ def actionCleanupAfter(self):
+ """
+ Clean up the VBox bits and then call the base driver.
+
+ If your test driver overrides this, it should normally call us at the
+ end of the job.
+ """
+ cErrorsEntry = reporter.getErrorCount();
+
+ # Kill any left over VM processes.
+ self._powerOffAllVms();
+
+ # Drop all VBox object references and shutdown xpcom then
+ # terminating VBoxSVC, with extreme prejudice if need be.
+ self._teardownVBoxApi();
+ self._stopVBoxSVC();
+
+ # Add the VBoxSVC and testdriver debug+release log files.
+ if self.fAlwaysUploadLogs or reporter.getErrorCount() > 0:
+ if self.sVBoxSvcLogFile is not None and os.path.isfile(self.sVBoxSvcLogFile):
+ reporter.addLogFile(self.sVBoxSvcLogFile, 'log/debug/svc', 'Debug log file for VBoxSVC');
+ self.sVBoxSvcLogFile = None;
+
+ if self.sSelfLogFile is not None and os.path.isfile(self.sSelfLogFile):
+ reporter.addLogFile(self.sSelfLogFile, 'log/debug/client', 'Debug log file for the test driver');
+ self.sSelfLogFile = None;
+
+ if self.sSessionLogFile is not None and os.path.isfile(self.sSessionLogFile):
+ reporter.addLogFile(self.sSessionLogFile, 'log/debug/session', 'Debug log file for the VM session');
+ self.sSessionLogFile = None;
+
+ sVBoxSvcRelLog = os.path.join(self.sScratchPath, 'VBoxUserHome', 'VBoxSVC.log');
+ if os.path.isfile(sVBoxSvcRelLog):
+ reporter.addLogFile(sVBoxSvcRelLog, 'log/release/svc', 'Release log file for VBoxSVC');
+ for sSuff in [ '.1', '.2', '.3', '.4', '.5', '.6', '.7', '.8' ]:
+ if os.path.isfile(sVBoxSvcRelLog + sSuff):
+ reporter.addLogFile(sVBoxSvcRelLog + sSuff, 'log/release/svc', 'Release log file for VBoxSVC');
+
+ # Finally, call the base driver to wipe the scratch space.
+ fRc = base.TestDriver.actionCleanupAfter(self);
+
+ # Flag failure if the error count increased.
+ if reporter.getErrorCount() > cErrorsEntry:
+ fRc = False;
+ return fRc;
+
+
+ def actionAbort(self):
+ """
+ Terminate VBoxSVC if we've got a pid file.
+ """
+ #
+ # Take default action first, then kill VBoxSVC. The other way around
+ # is problematic since the testscript would continue running and possibly
+ # trigger a new VBoxSVC to start.
+ #
+ fRc1 = base.TestDriver.actionAbort(self);
+ fRc2 = self._killVBoxSVCByPidFile('%s/VBoxSVC.pid' % (self.sScratchPath,));
+ return fRc1 is True and fRc2 is True;
+
+ def onExit(self, iRc):
+ """
+ Stop VBoxSVC if we've started it.
+ """
+ if not self.fVmNoTerminate \
+ and self.oVBoxSvcProcess is not None:
+ reporter.log('*** Shutting down the VBox API... (iRc=%s)' % (iRc,));
+ self._powerOffAllVms();
+ self._teardownVBoxApi();
+ self._stopVBoxSVC();
+ reporter.log('*** VBox API shutdown done.');
+ return base.TestDriver.onExit(self, iRc);
+
+
+ #
+ # Task wait method override.
+ #
+
+ def notifyAboutReadyTask(self, oTask):
+ """
+ Overriding base.TestDriver.notifyAboutReadyTask.
+ """
+ try:
+ self.oVBoxMgr.interruptWaitEvents();
+ reporter.log2('vbox.notifyAboutReadyTask: called interruptWaitEvents');
+ except:
+ reporter.logXcpt('vbox.notifyAboutReadyTask');
+ return base.TestDriver.notifyAboutReadyTask(self, oTask);
+
+ def waitForTasksSleepWorker(self, cMsTimeout):
+ """
+ Overriding base.TestDriver.waitForTasksSleepWorker.
+ """
+ try:
+ rc = self.oVBoxMgr.waitForEvents(int(cMsTimeout));
+ _ = rc; #reporter.log2('vbox.waitForTasksSleepWorker(%u): true (waitForEvents -> %s)' % (cMsTimeout, rc));
+ reporter.doPollWork('vbox.TestDriver.waitForTasksSleepWorker');
+ return True;
+ except KeyboardInterrupt:
+ raise;
+ except:
+ reporter.logXcpt('vbox.waitForTasksSleepWorker');
+ return False;
+
+ #
+ # Utility methods.
+ #
+
+ def processEvents(self, cMsTimeout = 0):
+ """
+ Processes events, returning after the first batch has been processed
+ or the time limit has been reached.
+
+ Only Ctrl-C exception, no return.
+ """
+ try:
+ self.oVBoxMgr.waitForEvents(cMsTimeout);
+ except KeyboardInterrupt:
+ raise;
+ except:
+ pass;
+ return None;
+
+ def processPendingEvents(self):
+ """ processEvents(0) - no waiting. """
+ return self.processEvents(0);
+
+ def sleep(self, cSecs):
+ """
+ Sleep for a specified amount of time, processing XPCOM events all the while.
+ """
+ cMsTimeout = long(cSecs * 1000);
+ msStart = base.timestampMilli();
+ self.processEvents(0);
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ break;
+ #reporter.log2('cMsTimeout=%s - cMsElapsed=%d => %s' % (cMsTimeout, cMsElapsed, cMsTimeout - cMsElapsed));
+ self.processEvents(cMsTimeout - cMsElapsed);
+ return None;
+
+ def _logVmInfoUnsafe(self, oVM): # pylint: disable=too-many-statements,too-many-branches
+ """
+ Internal worker for logVmInfo that is wrapped in try/except.
+ """
+ reporter.log(" Name: %s" % (oVM.name,));
+ reporter.log(" ID: %s" % (oVM.id,));
+ oOsType = self.oVBox.getGuestOSType(oVM.OSTypeId);
+ reporter.log(" OS Type: %s - %s" % (oVM.OSTypeId, oOsType.description,));
+ reporter.log(" Machine state: %s" % (oVM.state,));
+ reporter.log(" Session state: %s" % (oVM.sessionState,));
+ if self.fpApiVer >= 4.2:
+ reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPID, oVM.sessionPID,));
+ else:
+ reporter.log(" Session PID: %u (%#x)" % (oVM.sessionPid, oVM.sessionPid,));
+ if self.fpApiVer >= 5.0:
+ reporter.log(" Session Name: %s" % (oVM.sessionName,));
+ else:
+ reporter.log(" Session Name: %s" % (oVM.sessionType,));
+ reporter.log(" CPUs: %s" % (oVM.CPUCount,));
+ reporter.log(" RAM: %sMB" % (oVM.memorySize,));
+ if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
+ reporter.log(" VRAM: %sMB" % (oVM.graphicsAdapter.VRAMSize,));
+ reporter.log(" Monitors: %s" % (oVM.graphicsAdapter.monitorCount,));
+ reporter.log(" GraphicsController: %s"
+ % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', # pylint: disable=not-callable
+ oVM.graphicsAdapter.graphicsControllerType),));
+ else:
+ reporter.log(" VRAM: %sMB" % (oVM.VRAMSize,));
+ reporter.log(" Monitors: %s" % (oVM.monitorCount,));
+ reporter.log(" GraphicsController: %s"
+ % (self.oVBoxMgr.getEnumValueName('GraphicsControllerType', oVM.graphicsControllerType),)); # pylint: disable=not-callable
+ reporter.log(" Chipset: %s" % (self.oVBoxMgr.getEnumValueName('ChipsetType', oVM.chipsetType),)); # pylint: disable=not-callable
+ if self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_None'):
+ reporter.log(" IOMMU: %s" % (self.oVBoxMgr.getEnumValueName('IommuType', oVM.iommuType),)); # pylint: disable=not-callable
+ reporter.log(" Firmware: %s" % (self.oVBoxMgr.getEnumValueName('FirmwareType', oVM.firmwareType),)); # pylint: disable=not-callable
+ reporter.log(" HwVirtEx: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled),));
+ reporter.log(" VPID support: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_VPID),));
+ reporter.log(" Nested paging: %s" % (oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging),));
+ atTypes = [
+ ( 'CPUPropertyType_PAE', 'PAE: '),
+ ( 'CPUPropertyType_LongMode', 'Long-mode: '),
+ ( 'CPUPropertyType_HWVirt', 'Nested VT-x/AMD-V: '),
+ ( 'CPUPropertyType_APIC', 'APIC: '),
+ ( 'CPUPropertyType_X2APIC', 'X2APIC: '),
+ ( 'CPUPropertyType_TripleFaultReset', 'TripleFaultReset: '),
+ ( 'CPUPropertyType_IBPBOnVMExit', 'IBPBOnVMExit: '),
+ ( 'CPUPropertyType_SpecCtrl', 'SpecCtrl: '),
+ ( 'CPUPropertyType_SpecCtrlByHost', 'SpecCtrlByHost: '),
+ ];
+ for sEnumValue, sDesc in atTypes:
+ if hasattr(vboxcon, sEnumValue):
+ reporter.log(" %s%s" % (sDesc, oVM.getCPUProperty(getattr(vboxcon, sEnumValue)),));
+ reporter.log(" ACPI: %s" % (oVM.BIOSSettings.ACPIEnabled,));
+ reporter.log(" IO-APIC: %s" % (oVM.BIOSSettings.IOAPICEnabled,));
+ if self.fpApiVer >= 3.2:
+ if self.fpApiVer >= 4.2:
+ reporter.log(" HPET: %s" % (oVM.HPETEnabled,));
+ else:
+ reporter.log(" HPET: %s" % (oVM.hpetEnabled,));
+ if self.fpApiVer >= 6.1 and hasattr(oVM, 'graphicsAdapter'):
+ reporter.log(" 3D acceleration: %s" % (oVM.graphicsAdapter.accelerate3DEnabled,));
+ reporter.log(" 2D acceleration: %s" % (oVM.graphicsAdapter.accelerate2DVideoEnabled,));
+ else:
+ reporter.log(" 3D acceleration: %s" % (oVM.accelerate3DEnabled,));
+ reporter.log(" 2D acceleration: %s" % (oVM.accelerate2DVideoEnabled,));
+ reporter.log(" TeleporterEnabled: %s" % (oVM.teleporterEnabled,));
+ reporter.log(" TeleporterPort: %s" % (oVM.teleporterPort,));
+ reporter.log(" TeleporterAddress: %s" % (oVM.teleporterAddress,));
+ reporter.log(" TeleporterPassword: %s" % (oVM.teleporterPassword,));
+ reporter.log(" Clipboard mode: %s" % (oVM.clipboardMode,));
+ if self.fpApiVer >= 5.0:
+ reporter.log(" Drag and drop mode: %s" % (oVM.dnDMode,));
+ elif self.fpApiVer >= 4.3:
+ reporter.log(" Drag and drop mode: %s" % (oVM.dragAndDropMode,));
+ if self.fpApiVer >= 4.0:
+ reporter.log(" VRDP server: %s" % (oVM.VRDEServer.enabled,));
+ try: sPorts = oVM.VRDEServer.getVRDEProperty("TCP/Ports");
+ except: sPorts = "";
+ reporter.log(" VRDP server ports: %s" % (sPorts,));
+ reporter.log(" VRDP auth: %s (%s)" % (oVM.VRDEServer.authType, oVM.VRDEServer.authLibrary,));
+ else:
+ reporter.log(" VRDP server: %s" % (oVM.VRDPServer.enabled,));
+ reporter.log(" VRDP server ports: %s" % (oVM.VRDPServer.ports,));
+ reporter.log(" Last changed: %s" % (oVM.lastStateChange,));
+
+ aoControllers = self.oVBoxMgr.getArray(oVM, 'storageControllers')
+ if aoControllers:
+ reporter.log(" Controllers:");
+ for oCtrl in aoControllers:
+ reporter.log(" %s %s bus: %s type: %s" % (oCtrl.name, oCtrl.controllerType, oCtrl.bus, oCtrl.controllerType,));
+ if self.fpApiVer >= 7.0:
+ oAdapter = oVM.audioSettings.adapter;
+ else:
+ oAdapter = oVM.audioAdapter;
+ reporter.log(" AudioController: %s"
+ % (self.oVBoxMgr.getEnumValueName('AudioControllerType', oAdapter.audioController),)); # pylint: disable=not-callable
+ reporter.log(" AudioEnabled: %s" % (oAdapter.enabled,));
+ reporter.log(" Host AudioDriver: %s"
+ % (self.oVBoxMgr.getEnumValueName('AudioDriverType', oAdapter.audioDriver),)); # pylint: disable=not-callable
+
+ self.processPendingEvents();
+ aoAttachments = self.oVBoxMgr.getArray(oVM, 'mediumAttachments')
+ if aoAttachments:
+ reporter.log(" Attachments:");
+ for oAtt in aoAttachments:
+ sCtrl = "Controller: %s port: %s device: %s type: %s" % (oAtt.controller, oAtt.port, oAtt.device, oAtt.type);
+ oMedium = oAtt.medium
+ if oAtt.type == vboxcon.DeviceType_HardDisk:
+ reporter.log(" %s: HDD" % sCtrl);
+ reporter.log(" Id: %s" % (oMedium.id,));
+ reporter.log(" Name: %s" % (oMedium.name,));
+ reporter.log(" Format: %s" % (oMedium.format,));
+ reporter.log(" Location: %s" % (oMedium.location,));
+
+ if oAtt.type == vboxcon.DeviceType_DVD:
+ reporter.log(" %s: DVD" % sCtrl);
+ if oMedium:
+ reporter.log(" Id: %s" % (oMedium.id,));
+ reporter.log(" Name: %s" % (oMedium.name,));
+ if oMedium.hostDrive:
+ reporter.log(" Host DVD %s" % (oMedium.location,));
+ if oAtt.passthrough:
+ reporter.log(" [passthrough mode]");
+ else:
+ reporter.log(" Virtual image: %s" % (oMedium.location,));
+ reporter.log(" Size: %s" % (oMedium.size,));
+ else:
+ reporter.log(" empty");
+
+ if oAtt.type == vboxcon.DeviceType_Floppy:
+ reporter.log(" %s: Floppy" % sCtrl);
+ if oMedium:
+ reporter.log(" Id: %s" % (oMedium.id,));
+ reporter.log(" Name: %s" % (oMedium.name,));
+ if oMedium.hostDrive:
+ reporter.log(" Host floppy: %s" % (oMedium.location,));
+ else:
+ reporter.log(" Virtual image: %s" % (oMedium.location,));
+ reporter.log(" Size: %s" % (oMedium.size,));
+ else:
+ reporter.log(" empty");
+ self.processPendingEvents();
+
+ reporter.log(" Network Adapter:");
+ for iSlot in range(0, 32):
+ try: oNic = oVM.getNetworkAdapter(iSlot)
+ except: break;
+ if not oNic.enabled:
+ reporter.log2(" slot #%d found but not enabled, skipping" % (iSlot,));
+ continue;
+ reporter.log(" slot #%d: type: %s (%s) MAC Address: %s lineSpeed: %s"
+ % (iSlot, self.oVBoxMgr.getEnumValueName('NetworkAdapterType', oNic.adapterType), # pylint: disable=not-callable
+ oNic.adapterType, oNic.MACAddress, oNic.lineSpeed) );
+
+ if oNic.attachmentType == vboxcon.NetworkAttachmentType_NAT:
+ reporter.log(" attachmentType: NAT (%s)" % (oNic.attachmentType,));
+ if self.fpApiVer >= 4.1:
+ reporter.log(" nat-network: %s" % (oNic.NATNetwork,));
+ if self.fpApiVer >= 7.0 and hasattr(oNic.NATEngine, 'localhostReachable'):
+ reporter.log(" localhostReachable: %s" % (oNic.NATEngine.localhostReachable,));
+
+ elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Bridged:
+ reporter.log(" attachmentType: Bridged (%s)" % (oNic.attachmentType,));
+ if self.fpApiVer >= 4.1:
+ reporter.log(" hostInterface: %s" % (oNic.bridgedInterface,));
+ else:
+ reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
+ elif oNic.attachmentType == vboxcon.NetworkAttachmentType_Internal:
+ reporter.log(" attachmentType: Internal (%s)" % (oNic.attachmentType,));
+ reporter.log(" intnet-name: %s" % (oNic.internalNetwork,));
+ elif oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnly:
+ reporter.log(" attachmentType: HostOnly (%s)" % (oNic.attachmentType,));
+ if self.fpApiVer >= 4.1:
+ reporter.log(" hostInterface: %s" % (oNic.hostOnlyInterface,));
+ else:
+ reporter.log(" hostInterface: %s" % (oNic.hostInterface,));
+ else:
+ if self.fpApiVer >= 7.0:
+ if oNic.attachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
+ reporter.log(" attachmentType: HostOnlyNetwork (%s)" % (oNic.attachmentType,));
+ reporter.log(" hostonly-net: %s" % (oNic.hostOnlyNetwork,));
+ elif self.fpApiVer >= 4.1:
+ if oNic.attachmentType == vboxcon.NetworkAttachmentType_Generic:
+ reporter.log(" attachmentType: Generic (%s)" % (oNic.attachmentType,));
+ reporter.log(" generic-driver: %s" % (oNic.GenericDriver,));
+ else:
+ reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
+ else:
+ reporter.log(" attachmentType: unknown-%s" % (oNic.attachmentType,));
+ if oNic.traceEnabled:
+ reporter.log(" traceFile: %s" % (oNic.traceFile,));
+ self.processPendingEvents();
+
+ reporter.log(" Serial ports:");
+ for iSlot in range(0, 8):
+ try: oPort = oVM.getSerialPort(iSlot)
+ except: break;
+ if oPort is not None and oPort.enabled:
+ enmHostMode = oPort.hostMode;
+ reporter.log(" slot #%d: hostMode: %s (%s) I/O port: %s IRQ: %s server: %s path: %s" %
+ (iSlot, self.oVBoxMgr.getEnumValueName('PortMode', enmHostMode), # pylint: disable=not-callable
+ enmHostMode, oPort.IOBase, oPort.IRQ, oPort.server, oPort.path,) );
+ self.processPendingEvents();
+
+ return True;
+
+ def logVmInfo(self, oVM): # pylint: disable=too-many-statements,too-many-branches
+ """
+ Logs VM configuration details.
+
+ This is copy, past, search, replace and edit of infoCmd from vboxshell.py.
+ """
+ try:
+ fRc = self._logVmInfoUnsafe(oVM);
+ except:
+ reporter.logXcpt();
+ fRc = False;
+ return fRc;
+
+ def logVmInfoByName(self, sName):
+ """
+ logVmInfo + getVmByName.
+ """
+ return self.logVmInfo(self.getVmByName(sName));
+
+ def tryFindGuestOsId(self, sIdOrDesc):
+ """
+ Takes a guest OS ID or Description and returns the ID.
+ If nothing matching it is found, the input is returned unmodified.
+ """
+
+ if self.fpApiVer >= 4.0:
+ if sIdOrDesc == 'Solaris (64 bit)':
+ sIdOrDesc = 'Oracle Solaris 10 5/09 and earlier (64 bit)';
+
+ try:
+ aoGuestTypes = self.oVBoxMgr.getArray(self.oVBox, 'GuestOSTypes');
+ except:
+ reporter.logXcpt();
+ else:
+ for oGuestOS in aoGuestTypes:
+ try:
+ sId = oGuestOS.id;
+ sDesc = oGuestOS.description;
+ except:
+ reporter.logXcpt();
+ else:
+ if sIdOrDesc in (sId, sDesc,):
+ sIdOrDesc = sId;
+ break;
+ self.processPendingEvents();
+ return sIdOrDesc
+
+ def resourceFindVmHd(self, sVmName, sFlavor):
+ """
+ Search the test resources for the most recent VM HD.
+
+ Returns path relative to the test resource root.
+ """
+ ## @todo implement a proper search algo here.
+ return '4.2/' + sFlavor + '/' + sVmName + '/t-' + sVmName + '.vdi';
+
+
+ #
+ # VM Api wrappers that logs errors, hides exceptions and other details.
+ #
+
+ def createTestVMOnly(self, sName, sKind):
+ """
+ Creates and register a test VM without doing any kind of configuration.
+
+ Returns VM object (IMachine) on success, None on failure.
+ """
+ if not self.importVBoxApi():
+ return None;
+
+ # create + register the VM
+ try:
+ if self.fpApiVer >= 7.0: # Introduces VM encryption (three new parameters, empty for now).
+ oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "", "", "", "");
+ elif self.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
+ oVM = self.oVBox.createMachine("", sName, [], self.tryFindGuestOsId(sKind), "");
+ elif self.fpApiVer >= 4.0:
+ oVM = self.oVBox.createMachine("", sName, self.tryFindGuestOsId(sKind), "", False);
+ elif self.fpApiVer >= 3.2:
+ oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "", False);
+ else:
+ oVM = self.oVBox.createMachine(sName, self.tryFindGuestOsId(sKind), "", "");
+ try:
+ oVM.saveSettings();
+ try:
+ self.oVBox.registerMachine(oVM);
+ return oVM;
+ except:
+ reporter.logXcpt();
+ raise;
+ except:
+ reporter.logXcpt();
+ if self.fpApiVer >= 4.0:
+ try:
+ if self.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([]);
+ else:
+ oProgress = oVM.delete(None);
+ self.waitOnProgress(oProgress);
+ except:
+ reporter.logXcpt();
+ else:
+ try: oVM.deleteSettings();
+ except: reporter.logXcpt();
+ raise;
+ except:
+ reporter.errorXcpt('failed to create vm "%s"' % (sName));
+ return None;
+
+ # pylint: disable=too-many-arguments,too-many-locals,too-many-statements,too-many-branches
+ def createTestVM(self,
+ sName,
+ iGroup,
+ sHd = None,
+ cMbRam = None,
+ cCpus = 1,
+ fVirtEx = None,
+ fNestedPaging = None,
+ sDvdImage = None,
+ sKind = "Other",
+ fIoApic = None,
+ fNstHwVirt = None,
+ fPae = None,
+ fFastBootLogo = True,
+ eNic0Type = None,
+ eNic0AttachType = None,
+ sNic0NetName = 'default',
+ sNic0MacAddr = 'grouped',
+ sFloppy = None,
+ fNatForwardingForTxs = None,
+ sHddControllerType = 'IDE Controller',
+ fVmmDevTestingPart = None,
+ fVmmDevTestingMmio = False,
+ sFirmwareType = 'bios',
+ sChipsetType = 'piix3',
+ sIommuType = 'none',
+ sDvdControllerType = 'IDE Controller',
+ sCom1RawFile = None):
+ """
+ Creates a test VM with a immutable HD from the test resources.
+ """
+ # create + register the VM
+ oVM = self.createTestVMOnly(sName, sKind);
+ if not oVM:
+ return None;
+
+ # Configure the VM.
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.setupPreferredConfig();
+
+ if fRc and cMbRam is not None :
+ fRc = oSession.setRamSize(cMbRam);
+ if fRc and cCpus is not None:
+ fRc = oSession.setCpuCount(cCpus);
+ if fRc and fVirtEx is not None:
+ fRc = oSession.enableVirtEx(fVirtEx);
+ if fRc and fNestedPaging is not None:
+ fRc = oSession.enableNestedPaging(fNestedPaging);
+ if fRc and fIoApic is not None:
+ fRc = oSession.enableIoApic(fIoApic);
+ if fRc and fNstHwVirt is not None:
+ fRc = oSession.enableNestedHwVirt(fNstHwVirt);
+ if fRc and fPae is not None:
+ fRc = oSession.enablePae(fPae);
+ if fRc and sDvdImage is not None:
+ fRc = oSession.attachDvd(sDvdImage, sDvdControllerType);
+ if fRc and sHd is not None:
+ fRc = oSession.attachHd(sHd, sHddControllerType);
+ if fRc and sFloppy is not None:
+ fRc = oSession.attachFloppy(sFloppy);
+ if fRc and eNic0Type is not None:
+ fRc = oSession.setNicType(eNic0Type, 0);
+ if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
+ fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
+ if fRc and sNic0MacAddr is not None:
+ if sNic0MacAddr == 'grouped':
+ sNic0MacAddr = '%02X' % (iGroup);
+ fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
+ # Needed to reach the host (localhost) from the guest. See xTracker #9896.
+ if fRc and self.fpApiVer >= 7.0:
+ fRc = oSession.setNicLocalhostReachable(True, 0);
+ if fRc and fNatForwardingForTxs is True:
+ fRc = oSession.setupNatForwardingForTxs();
+ if fRc and fFastBootLogo is not None:
+ fRc = oSession.setupBootLogo(fFastBootLogo);
+ if fRc and self.fEnableVrdp:
+ fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
+ if fRc and fVmmDevTestingPart is not None:
+ fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
+ if fRc and sFirmwareType == 'bios':
+ fRc = oSession.setFirmwareType(vboxcon.FirmwareType_BIOS);
+ elif fRc and sFirmwareType == 'efi':
+ fRc = oSession.setFirmwareType(vboxcon.FirmwareType_EFI);
+ if fRc and self.fEnableDebugger:
+ fRc = oSession.setExtraData('VBoxInternal/DBGC/Enabled', '1');
+ if fRc and self.fRecordingEnabled:
+ try:
+ if self.fpApiVer >= 6.1: # Only for VBox 6.1 and up now.
+ reporter.log('Recording enabled');
+ if self.cSecsRecordingMax > 0:
+ reporter.log('Recording time limit is set to %d seconds' % (self.cSecsRecordingMax));
+ if self.cMbRecordingMax > 0:
+ reporter.log('Recording file limit is set to %d MB' % (self.cMbRecordingMax));
+ oRecSettings = oSession.o.machine.recordingSettings;
+ oRecSettings.enabled = True;
+ aoScreens = self.oVBoxMgr.getArray(oRecSettings, 'screens');
+ for oScreen in aoScreens:
+ try:
+ oScreen.enabled = True;
+ sRecFile = os.path.join(self.sScratchPath, "recording-%s.webm" % (sName));
+ oScreen.filename = sRecFile;
+ sRecFile = oScreen.filename; # Get back the file from Main, in case it was modified somehow.
+ dRecFile = { 'id' : oScreen.id, 'file' : sRecFile };
+ self.adRecordingFiles.append(dRecFile);
+ if self.fpApiVer >= 7.0:
+ aFeatures = [ vboxcon.RecordingFeature_Video, ];
+ if self.fRecordingAudio:
+ aFeatures.append(vboxcon.RecordingFeature_Audio);
+ try:
+ oScreen.setFeatures(aFeatures);
+ except: ## @todo Figure out why this is needed on Windows.
+ oScreen.features = aFeatures;
+ else: # <= VBox 6.1 the feature were kept as a ULONG.
+ uFeatures = vboxcon.RecordingFeature_Video;
+ if self.fRecordingAudio:
+ uFeatures = uFeatures | vboxcon.RecordingFeature_Audio;
+ oScreen.features = uFeatures;
+ reporter.log2('Recording screen %d to "%s"' % (dRecFile['id'], dRecFile['file'],));
+ oScreen.maxTime = self.cSecsRecordingMax;
+ oScreen.maxFileSize = self.cMbRecordingMax;
+ except:
+ reporter.errorXcpt('failed to configure recording for "%s" (screen %d)' % (sName, oScreen.id));
+ else:
+ # Not fatal.
+ reporter.log('Recording only available for VBox >= 6.1, sorry!')
+ except:
+ reporter.errorXcpt('failed to configure recording for "%s"' % (sName));
+ if fRc and sChipsetType == 'piix3':
+ fRc = oSession.setChipsetType(vboxcon.ChipsetType_PIIX3);
+ elif fRc and sChipsetType == 'ich9':
+ fRc = oSession.setChipsetType(vboxcon.ChipsetType_ICH9);
+ if fRc and sCom1RawFile:
+ fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
+ if fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_AMD') and sIommuType == 'amd':
+ fRc = oSession.setIommuType(vboxcon.IommuType_AMD);
+ elif fRc and self.fpApiVer >= 6.2 and hasattr(vboxcon, 'IommuType_Intel') and sIommuType == 'intel':
+ fRc = oSession.setIommuType(vboxcon.IommuType_Intel);
+
+ if fRc: fRc = oSession.saveSettings();
+ if not fRc: oSession.discardSettings(True);
+ oSession.close();
+ if not fRc:
+ if self.fpApiVer >= 4.0:
+ try: oVM.unregister(vboxcon.CleanupMode_Full);
+ except: reporter.logXcpt();
+ try:
+ if self.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([]);
+ else:
+ oProgress = oVM.delete([]);
+ self.waitOnProgress(oProgress);
+ except:
+ reporter.logXcpt();
+ else:
+ try: self.oVBox.unregisterMachine(oVM.id);
+ except: reporter.logXcpt();
+ try: oVM.deleteSettings();
+ except: reporter.logXcpt();
+ return None;
+
+ # success.
+ reporter.log('created "%s" with name "%s"' % (oVM.id, sName));
+ self.aoVMs.append(oVM);
+ self.logVmInfo(oVM); # testing...
+ return oVM;
+ # pylint: enable=too-many-arguments,too-many-locals,too-many-statements
+
+ def createTestVmWithDefaults(self, # pylint: disable=too-many-arguments
+ sName,
+ iGroup,
+ sKind,
+ sDvdImage = None,
+ fFastBootLogo = True,
+ eNic0AttachType = None,
+ sNic0NetName = 'default',
+ sNic0MacAddr = 'grouped',
+ fVmmDevTestingPart = None,
+ fVmmDevTestingMmio = False,
+ sCom1RawFile = None):
+ """
+ Creates a test VM with all defaults and no HDs.
+ """
+ # create + register the VM
+ oVM = self.createTestVMOnly(sName, sKind);
+ if oVM is not None:
+ # Configure the VM with defaults according to sKind.
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ if self.fpApiVer >= 6.0:
+ try:
+ oSession.o.machine.applyDefaults('');
+ except:
+ reporter.errorXcpt('failed to apply defaults to vm "%s"' % (sName,));
+ fRc = False;
+ else:
+ reporter.error("Implement applyDefaults for vbox version %s" % (self.fpApiVer,));
+ #fRc = oSession.setupPreferredConfig();
+ fRc = False;
+
+ # Apply the specified configuration:
+ if fRc and sDvdImage is not None:
+ #fRc = oSession.insertDvd(sDvdImage); # attachDvd
+ reporter.error('Implement: oSession.insertDvd(%s)' % (sDvdImage,));
+ fRc = False;
+
+ if fRc and fFastBootLogo is not None:
+ fRc = oSession.setupBootLogo(fFastBootLogo);
+
+ if fRc and (eNic0AttachType is not None or (sNic0NetName is not None and sNic0NetName != 'default')):
+ fRc = oSession.setNicAttachment(eNic0AttachType, sNic0NetName, 0);
+ if fRc and sNic0MacAddr is not None:
+ if sNic0MacAddr == 'grouped':
+ sNic0MacAddr = '%02X' % (iGroup,);
+ fRc = oSession.setNicMacAddress(sNic0MacAddr, 0);
+ # Needed to reach the host (localhost) from the guest. See xTracker #9896.
+ if fRc and self.fpApiVer >= 7.0:
+ fRc = oSession.setNicLocalhostReachable(True, 0);
+
+ if fRc and self.fEnableVrdp:
+ fRc = oSession.setupVrdp(True, self.uVrdpBasePort + iGroup);
+
+ if fRc and fVmmDevTestingPart is not None:
+ fRc = oSession.enableVmmDevTestingPart(fVmmDevTestingPart, fVmmDevTestingMmio);
+
+ if fRc and sCom1RawFile:
+ fRc = oSession.setupSerialToRawFile(0, sCom1RawFile);
+
+ # Save the settings if we were successfull, otherwise discard them.
+ if fRc:
+ fRc = oSession.saveSettings();
+ if not fRc:
+ oSession.discardSettings(True);
+ oSession.close();
+
+ if fRc is True:
+ # If we've been successful, add the VM to the list and return it.
+ # success.
+ reporter.log('created "%s" with name "%s"' % (oVM.id, sName, ));
+ self.aoVMs.append(oVM);
+ self.logVmInfo(oVM); # testing...
+ return oVM;
+
+ # Failed. Unregister the machine and delete it.
+ if self.fpApiVer >= 4.0:
+ try: oVM.unregister(vboxcon.CleanupMode_Full);
+ except: reporter.logXcpt();
+ try:
+ if self.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([]);
+ else:
+ oProgress = oVM.delete([]);
+ self.waitOnProgress(oProgress);
+ except:
+ reporter.logXcpt();
+ else:
+ try: self.oVBox.unregisterMachine(oVM.id);
+ except: reporter.logXcpt();
+ try: oVM.deleteSettings();
+ except: reporter.logXcpt();
+ return None;
+
+ def addTestMachine(self, sNameOrId, fQuiet = False):
+ """
+ Adds an already existing (that is, configured) test VM to the
+ test VM list.
+
+ Returns the VM object on success, None if failed.
+ """
+ # find + add the VM to the list.
+ oVM = None;
+ try:
+ if self.fpApiVer >= 4.0:
+ oVM = self.oVBox.findMachine(sNameOrId);
+ else:
+ reporter.error('fpApiVer=%s - did you remember to initialize the API' % (self.fpApiVer,));
+ except:
+ reporter.errorXcpt('could not find vm "%s"' % (sNameOrId,));
+
+ if oVM:
+ self.aoVMs.append(oVM);
+ if not fQuiet:
+ reporter.log('Added "%s" with name "%s"' % (oVM.id, sNameOrId));
+ self.logVmInfo(oVM);
+ return oVM;
+
+ def forgetTestMachine(self, oVM, fQuiet = False):
+ """
+ Forget about an already known test VM in the test VM list.
+
+ Returns True on success, False if failed.
+ """
+ try:
+ sUuid = oVM.id;
+ sName = oVM.name;
+ except:
+ reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
+ return False;
+ try:
+ self.aoVMs.remove(oVM);
+ if not fQuiet:
+ reporter.log('Removed "%s" with name "%s"' % (sUuid, sName));
+ except:
+ reporter.errorXcpt('could not find vm "%s"' % (sName,));
+ return False;
+ return True;
+
+ def openSession(self, oVM):
+ """
+ Opens a session for the VM. Returns the a Session wrapper object that
+ will automatically close the session when the wrapper goes out of scope.
+
+ On failure None is returned and an error is logged.
+ """
+ try:
+ sUuid = oVM.id;
+ except:
+ reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM,));
+ return None;
+
+ # This loop is a kludge to deal with us racing the closing of the
+ # direct session of a previous VM run. See waitOnDirectSessionClose.
+ for i in range(10):
+ try:
+ if self.fpApiVer <= 3.2:
+ oSession = self.oVBoxMgr.openMachineSession(sUuid);
+ else:
+ oSession = self.oVBoxMgr.openMachineSession(oVM);
+ break;
+ except:
+ if i == 9:
+ reporter.errorXcpt('failed to open session for "%s" ("%s")' % (sUuid, oVM));
+ return None;
+ if i > 0:
+ reporter.logXcpt('warning: failed to open session for "%s" ("%s") - retrying in %u secs' % (sUuid, oVM, i));
+ self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
+ from testdriver.vboxwrappers import SessionWrapper;
+ return SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, False);
+
+ #
+ # Guest locations.
+ #
+
+ @staticmethod
+ def getGuestTempDir(oTestVm):
+ """
+ Helper for finding a temporary directory in the test VM.
+
+ Note! It may be necessary to create it!
+ """
+ if oTestVm.isWindows():
+ return "C:\\Temp";
+ if oTestVm.isOS2():
+ return "C:\\Temp";
+ return '/var/tmp';
+
+ @staticmethod
+ def getGuestSystemDir(oTestVm, sPathPrefix = ''):
+ """
+ Helper for finding a system directory in the test VM that we can play around with.
+ sPathPrefix can be used to specify other directories, such as /usr/local/bin/ or /usr/bin, for instance.
+
+ On Windows this is always the System32 directory, so this function can be used as
+ basis for locating other files in or under that directory.
+ """
+ if oTestVm.isWindows():
+ return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
+ if oTestVm.isOS2():
+ return 'C:\\OS2\\DLL';
+
+ # OL / RHEL symlinks "/bin"/ to "/usr/bin". To avoid (unexpectedly) following symlinks, use "/usr/bin" then instead.
+ if not sPathPrefix \
+ and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
+ return "/usr/bin";
+
+ return sPathPrefix + "/bin";
+
+ @staticmethod
+ def getGuestSystemAdminDir(oTestVm, sPathPrefix = ''):
+ """
+ Helper for finding a system admin directory ("sbin") in the test VM that we can play around with.
+ sPathPrefix can be used to specify other directories, such as /usr/local/sbin/ or /usr/sbin, for instance.
+
+ On Windows this is always the System32 directory, so this function can be used as
+ basis for locating other files in or under that directory.
+ On UNIX-y systems this always is the "sh" shell to guarantee a common shell syntax.
+ """
+ if oTestVm.isWindows():
+ return oTestVm.pathJoin(TestDriver.getGuestWinDir(oTestVm), 'System32');
+ if oTestVm.isOS2():
+ return 'C:\\OS2\\DLL'; ## @todo r=andy Not sure here.
+
+ # OL / RHEL symlinks "/sbin"/ to "/usr/sbin". To avoid (unexpectedly) following symlinks, use "/usr/sbin" then instead.
+ if not sPathPrefix \
+ and oTestVm.sKind in ('Oracle_64', 'Oracle'): ## @todo Does this apply for "RedHat" as well?
+ return "/usr/sbin";
+
+ return sPathPrefix + "/sbin";
+
+ @staticmethod
+ def getGuestWinDir(oTestVm):
+ """
+ Helper for finding the Windows directory in the test VM that we can play around with.
+ ASSUMES that we always install Windows on drive C.
+
+ Returns the Windows directory, or an empty string when executed on a non-Windows guest (asserts).
+ """
+ sWinDir = '';
+ if oTestVm.isWindows():
+ if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x',]:
+ sWinDir = 'C:\\WinNT\\';
+ else:
+ sWinDir = 'C:\\Windows\\';
+ assert sWinDir != '', 'Retrieving Windows directory for non-Windows OS';
+ return sWinDir;
+
+ @staticmethod
+ def getGuestSystemShell(oTestVm):
+ """
+ Helper for finding the default system shell in the test VM.
+ """
+ if oTestVm.isWindows():
+ return TestDriver.getGuestSystemDir(oTestVm) + '\\cmd.exe';
+ if oTestVm.isOS2():
+ return TestDriver.getGuestSystemDir(oTestVm) + '\\..\\CMD.EXE';
+ return "/bin/sh";
+
+ @staticmethod
+ def getGuestSystemFileForReading(oTestVm):
+ """
+ Helper for finding a file in the test VM that we can read.
+ """
+ if oTestVm.isWindows():
+ return TestDriver.getGuestSystemDir(oTestVm) + '\\ntdll.dll';
+ if oTestVm.isOS2():
+ return TestDriver.getGuestSystemDir(oTestVm) + '\\DOSCALL1.DLL';
+ return "/bin/sh";
+
+ def getVmByName(self, sName):
+ """
+ Get a test VM by name. Returns None if not found, logged.
+ """
+ # Look it up in our 'cache'.
+ for oVM in self.aoVMs:
+ try:
+ #reporter.log2('cur: %s / %s (oVM=%s)' % (oVM.name, oVM.id, oVM));
+ if oVM.name == sName:
+ return oVM;
+ except:
+ reporter.errorXcpt('failed to get the name from the VM "%s"' % (oVM));
+
+ # Look it up the standard way.
+ return self.addTestMachine(sName, fQuiet = True);
+
+ def getVmByUuid(self, sUuid):
+ """
+ Get a test VM by uuid. Returns None if not found, logged.
+ """
+ # Look it up in our 'cache'.
+ for oVM in self.aoVMs:
+ try:
+ if oVM.id == sUuid:
+ return oVM;
+ except:
+ reporter.errorXcpt('failed to get the UUID from the VM "%s"' % (oVM));
+
+ # Look it up the standard way.
+ return self.addTestMachine(sUuid, fQuiet = True);
+
+ def waitOnProgress(self, oProgress, cMsTimeout = 1000000, fErrorOnTimeout = True, cMsInterval = 1000):
+ """
+ Waits for a progress object to complete. Returns the status code.
+ """
+ # Wait for progress no longer than cMsTimeout time period.
+ tsStart = datetime.datetime.now()
+ while True:
+ self.processPendingEvents();
+ try:
+ if oProgress.completed:
+ break;
+ except:
+ return -1;
+ self.processPendingEvents();
+
+ tsNow = datetime.datetime.now()
+ tsDelta = tsNow - tsStart
+ if ((tsDelta.microseconds + tsDelta.seconds * 1000000) // 1000) > cMsTimeout:
+ if fErrorOnTimeout:
+ reporter.errorTimeout('Timeout while waiting for progress.')
+ return -1
+
+ reporter.doPollWork('vbox.TestDriver.waitOnProgress');
+ try: oProgress.waitForCompletion(cMsInterval);
+ except: return -2;
+
+ try: rc = oProgress.resultCode;
+ except: rc = -2;
+ self.processPendingEvents();
+ return rc;
+
+ def waitOnDirectSessionClose(self, oVM, cMsTimeout):
+ """
+ Waits for the VM process to close it's current direct session.
+
+ Returns None.
+ """
+ # Get the original values so we're not subject to
+ try:
+ eCurState = oVM.sessionState;
+ if self.fpApiVer >= 5.0:
+ sCurName = sOrgName = oVM.sessionName;
+ else:
+ sCurName = sOrgName = oVM.sessionType;
+ if self.fpApiVer >= 4.2:
+ iCurPid = iOrgPid = oVM.sessionPID;
+ else:
+ iCurPid = iOrgPid = oVM.sessionPid;
+ except Exception as oXcpt:
+ if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
+ reporter.logXcpt();
+ self.processPendingEvents();
+ return None;
+ self.processPendingEvents();
+
+ msStart = base.timestampMilli();
+ while iCurPid == iOrgPid \
+ and sCurName == sOrgName \
+ and sCurName != '' \
+ and base.timestampMilli() - msStart < cMsTimeout \
+ and eCurState in (vboxcon.SessionState_Unlocking, vboxcon.SessionState_Spawning, vboxcon.SessionState_Locked,):
+ self.processEvents(1000);
+ try:
+ eCurState = oVM.sessionState;
+ sCurName = oVM.sessionName if self.fpApiVer >= 5.0 else oVM.sessionType;
+ iCurPid = oVM.sessionPID if self.fpApiVer >= 4.2 else oVM.sessionPid;
+ except Exception as oXcpt:
+ if ComError.notEqual(oXcpt, ComError.E_ACCESSDENIED):
+ reporter.logXcpt();
+ break;
+ self.processPendingEvents();
+ self.processPendingEvents();
+ return None;
+
+ def uploadStartupLogFile(self, oVM, sVmName):
+ """
+ Uploads the VBoxStartup.log when present.
+ """
+ fRc = True;
+ try:
+ sLogFile = os.path.join(oVM.logFolder, 'VBoxHardening.log');
+ except:
+ reporter.logXcpt();
+ fRc = False;
+ else:
+ if os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (sVmName, ),
+ sAltName = '%s-%s' % (sVmName, os.path.basename(sLogFile),));
+ return fRc;
+
+ def annotateAndUploadProcessReport(self, sProcessReport, sFilename, sKind, sDesc):
+ """
+ Annotates the given VM process report and uploads it if successfull.
+ """
+ fRc = False;
+ if self.oBuild is not None and self.oBuild.sInstallPath is not None:
+ oResolver = btresolver.BacktraceResolver(self.sScratchPath, self.oBuild.sInstallPath,
+ self.getBuildOs(), self.getBuildArch(),
+ fnLog = reporter.log);
+ fRcTmp = oResolver.prepareEnv();
+ if fRcTmp:
+ reporter.log('Successfully prepared environment');
+ sReportDbgSym = oResolver.annotateReport(sProcessReport);
+ if sReportDbgSym and len(sReportDbgSym) > 8:
+ reporter.addLogString(sReportDbgSym, sFilename, sKind, sDesc);
+ fRc = True;
+ else:
+ reporter.log('Annotating report failed');
+ oResolver.cleanupEnv();
+ return fRc;
+
+ def startVmEx(self, oVM, fWait = True, sType = None, sName = None, asEnv = None): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Start the VM, returning the VM session and progress object on success.
+ The session is also added to the task list and to the aoRemoteSessions set.
+
+ asEnv is a list of string on the putenv() form.
+
+ On failure (None, None) is returned and an error is logged.
+ """
+ # Massage and check the input.
+ if sType is None:
+ sType = self.sSessionType;
+ if sName is None:
+ try: sName = oVM.name;
+ except: sName = 'bad-vm-handle';
+ reporter.log('startVmEx: sName=%s fWait=%s sType=%s' % (sName, fWait, sType));
+ if oVM is None:
+ return (None, None);
+
+ ## @todo Do this elsewhere.
+ # Hack alert. Disables all annoying GUI popups.
+ if sType == 'gui' and not self.aoRemoteSessions:
+ try:
+ self.oVBox.setExtraData('GUI/Input/AutoCapture', 'false');
+ if self.fpApiVer >= 3.2:
+ self.oVBox.setExtraData('GUI/LicenseAgreed', '8');
+ else:
+ self.oVBox.setExtraData('GUI/LicenseAgreed', '7');
+ self.oVBox.setExtraData('GUI/RegistrationData', 'triesLeft=0');
+ self.oVBox.setExtraData('GUI/SUNOnlineData', 'triesLeft=0');
+ self.oVBox.setExtraData('GUI/SuppressMessages', 'confirmVMReset,remindAboutMouseIntegrationOn,'
+ 'remindAboutMouseIntegrationOff,remindAboutPausedVMInput,confirmInputCapture,'
+ 'confirmGoingFullscreen,remindAboutInaccessibleMedia,remindAboutWrongColorDepth,'
+ 'confirmRemoveMedium,allPopupPanes,allMessageBoxes,all');
+ self.oVBox.setExtraData('GUI/UpdateDate', 'never');
+ self.oVBox.setExtraData('GUI/PreventBetaWarning', self.oVBox.version);
+ except:
+ reporter.logXcpt();
+
+ # The UUID for the name.
+ try:
+ sUuid = oVM.id;
+ except:
+ reporter.errorXcpt('failed to get the UUID for VM "%s"' % (oVM));
+ return (None, None);
+ self.processPendingEvents();
+
+ # Construct the environment.
+ self.sSessionLogFile = '%s/VM-%s.log' % (self.sScratchPath, sUuid);
+ try: os.remove(self.sSessionLogFile);
+ except: pass;
+ if self.sLogSessionDest:
+ sLogDest = self.sLogSessionDest;
+ else:
+ sLogDest = 'file=%s' % (self.sSessionLogFile,);
+ asEnvFinal = [
+ 'VBOX_LOG=%s' % (self.sLogSessionGroups,),
+ 'VBOX_LOG_FLAGS=%s' % (self.sLogSessionFlags,),
+ 'VBOX_LOG_DEST=nodeny %s' % (sLogDest,),
+ 'VBOX_RELEASE_LOG_FLAGS=append time',
+ ];
+ if sType == 'gui':
+ asEnvFinal.append('VBOX_GUI_DBG_ENABLED=1');
+ if asEnv is not None and asEnv:
+ asEnvFinal += asEnv;
+
+ reporter.log2('Session environment:\n%s' % (asEnvFinal,));
+
+ # Shortcuts for local testing.
+ oProgress = oWrapped = None;
+ oTestVM = self.oTestVmSet.findTestVmByName(sName) if self.oTestVmSet is not None else None;
+ try:
+ if oTestVM is not None \
+ and oTestVM.fSnapshotRestoreCurrent is True:
+ if oVM.state is vboxcon.MachineState_Running:
+ reporter.log2('Machine "%s" already running.' % (sName,));
+ oProgress = None;
+ oWrapped = self.openSession(oVM);
+ else:
+ reporter.log2('Checking if snapshot for machine "%s" exists.' % (sName,));
+ oSessionWrapperRestore = self.openSession(oVM);
+ if oSessionWrapperRestore is not None:
+ oSnapshotCur = oVM.currentSnapshot;
+ if oSnapshotCur is not None:
+ reporter.log2('Restoring snapshot for machine "%s".' % (sName,));
+ oSessionWrapperRestore.restoreSnapshot(oSnapshotCur);
+ reporter.log2('Current snapshot for machine "%s" restored.' % (sName,));
+ else:
+ reporter.log('warning: no current snapshot for machine "%s" found.' % (sName,));
+ oSessionWrapperRestore.close();
+ except:
+ reporter.errorXcpt();
+ return (None, None);
+
+ oSession = None; # Must be initialized, otherwise the log statement at the end of the function can fail.
+
+ # Open a remote session, wait for this operation to complete.
+ # (The loop is a kludge to deal with us racing the closing of the
+ # direct session of a previous VM run. See waitOnDirectSessionClose.)
+ if oWrapped is None:
+ for i in range(10):
+ try:
+ if self.fpApiVer < 4.3 \
+ or (self.fpApiVer == 4.3 and not hasattr(self.oVBoxMgr, 'getSessionObject')):
+ oSession = self.oVBoxMgr.mgr.getSessionObject(self.oVBox); # pylint: disable=no-member
+ elif self.fpApiVer < 5.2 \
+ or (self.fpApiVer == 5.2 and hasattr(self.oVBoxMgr, 'vbox')):
+ oSession = self.oVBoxMgr.getSessionObject(self.oVBox); # pylint: disable=no-member
+ else:
+ oSession = self.oVBoxMgr.getSessionObject(); # pylint: disable=no-member,no-value-for-parameter
+ if self.fpApiVer < 3.3:
+ oProgress = self.oVBox.openRemoteSession(oSession, sUuid, sType, '\n'.join(asEnvFinal));
+ else:
+ if self.uApiRevision >= self.makeApiRevision(6, 1, 0, 1):
+ oProgress = oVM.launchVMProcess(oSession, sType, asEnvFinal);
+ else:
+ oProgress = oVM.launchVMProcess(oSession, sType, '\n'.join(asEnvFinal));
+ break;
+ except:
+ if i == 9:
+ reporter.errorXcpt('failed to start VM "%s" ("%s"), aborting.' % (sUuid, sName));
+ return (None, None);
+ oSession = None;
+ if i >= 0:
+ reporter.logXcpt('warning: failed to start VM "%s" ("%s") - retrying in %u secs.' % (sUuid, oVM, i)); # pylint: disable=line-too-long
+ self.waitOnDirectSessionClose(oVM, 5000 + i * 1000);
+ if fWait and oProgress is not None:
+ rc = self.waitOnProgress(oProgress);
+ if rc < 0:
+ self.waitOnDirectSessionClose(oVM, 5000);
+
+ # VM failed to power up, still collect VBox.log, need to wrap the session object
+ # in order to use the helper for adding the log files to the report.
+ from testdriver.vboxwrappers import SessionWrapper;
+ oTmp = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
+ oTmp.addLogsToReport();
+
+ # Try to collect a stack trace of the process for further investigation of any startup hangs.
+ uPid = oTmp.getPid();
+ if uPid is not None:
+ sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
+ if sHostProcessInfoHung is not None:
+ reporter.log('Trying to annotate the hung VM startup process report, please stand by...');
+ fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-startup-hung.log',
+ 'process/report/vm', 'Annotated hung VM process state during startup'); # pylint: disable=line-too-long
+ # Upload the raw log for manual annotation in case resolving failed.
+ if not fRcTmp:
+ reporter.log('Failed to annotate hung VM process report, uploading raw report');
+ reporter.addLogString(sHostProcessInfoHung, 'vmprocess-startup-hung.log', 'process/report/vm',
+ 'Hung VM process state during startup');
+
+ try:
+ if oSession is not None:
+ oSession.close();
+ except: pass;
+ reportError(oProgress, 'failed to open session for "%s"' % (sName));
+ self.uploadStartupLogFile(oVM, sName);
+ return (None, None);
+ reporter.log2('waitOnProgress -> %s' % (rc,));
+
+ # Wrap up the session object and push on to the list before returning it.
+ if oWrapped is None:
+ from testdriver.vboxwrappers import SessionWrapper;
+ oWrapped = SessionWrapper(oSession, oVM, self.oVBox, self.oVBoxMgr, self, True, sName, self.sSessionLogFile);
+
+ oWrapped.registerEventHandlerForTask();
+ self.aoRemoteSessions.append(oWrapped);
+ if oWrapped is not self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]:
+ reporter.error('not by reference: oWrapped=%s aoRemoteSessions[%s]=%s'
+ % (oWrapped, len(self.aoRemoteSessions) - 1,
+ self.aoRemoteSessions[len(self.aoRemoteSessions) - 1]));
+ self.addTask(oWrapped);
+
+ reporter.log2('startVmEx: oSession=%s, oSessionWrapper=%s, oProgress=%s' % (oSession, oWrapped, oProgress));
+
+ from testdriver.vboxwrappers import ProgressWrapper;
+ return (oWrapped, ProgressWrapper(oProgress, self.oVBoxMgr, self,
+ 'starting %s' % (sName,)) if oProgress else None);
+
+ def startVm(self, oVM, sType=None, sName = None, asEnv = None):
+ """ Simplified version of startVmEx. """
+ oSession, _ = self.startVmEx(oVM, True, sType, sName, asEnv = asEnv);
+ return oSession;
+
+ def startVmByNameEx(self, sName, fWait=True, sType=None, asEnv = None):
+ """
+ Start the VM, returning the VM session and progress object on success.
+ The session is also added to the task list and to the aoRemoteSessions set.
+
+ On failure (None, None) is returned and an error is logged.
+ """
+ oVM = self.getVmByName(sName);
+ if oVM is None:
+ return (None, None);
+ return self.startVmEx(oVM, fWait, sType, sName, asEnv = asEnv);
+
+ def startVmByName(self, sName, sType=None, asEnv = None):
+ """
+ Start the VM, returning the VM session on success. The session is
+ also added to the task list and to the aoRemoteSessions set.
+
+ On failure None is returned and an error is logged.
+ """
+ oSession, _ = self.startVmByNameEx(sName, True, sType, asEnv = asEnv);
+ return oSession;
+
+ def terminateVmBySession(self, oSession, oProgress = None, fTakeScreenshot = None): # pylint: disable=too-many-statements
+ """
+ Terminates the VM specified by oSession and adds the release logs to
+ the test report.
+
+ This will try achieve this by using powerOff, but will resort to
+ tougher methods if that fails.
+
+ The session will always be removed from the task list.
+ The session will be closed unless we fail to kill the process.
+ The session will be removed from the remote session list if closed.
+
+ The progress object (a wrapper!) is for teleportation and similar VM
+ operations, it will be attempted canceled before powering off the VM.
+ Failures are logged but ignored.
+ The progress object will always be removed from the task list.
+
+ Returns True if powerOff and session close both succeed.
+ Returns False if on failure (logged), including when we successfully
+ kill the VM process.
+ """
+
+ reporter.log2('terminateVmBySession: oSession=%s (pid=%s) oProgress=%s' % (oSession.sName, oSession.getPid(), oProgress));
+
+ if self.fVmNoTerminate:
+ reporter.log('terminateVmBySession: Skipping, as --vbox-vm-no-terminate was specified');
+ # Make sure that we still process the events the VM needs.
+ self.sleep(24 * 60 * 60 * 1000);
+
+ # Call getPid first to make sure the PID is cached in the wrapper.
+ oSession.getPid();
+
+ #
+ # If the host is out of memory, just skip all the info collection as it
+ # requires memory too and seems to wedge.
+ #
+ sHostProcessInfo = None;
+ sHostProcessInfoHung = None;
+ sLastScreenshotPath = None;
+ sOsKernelLog = None;
+ sVgaText = None;
+ asMiscInfos = [];
+
+ if not oSession.fHostMemoryLow:
+ # Try to fetch the VM process info before meddling with its state.
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ sHostProcessInfo = utils.processGetInfo(oSession.getPid(), fSudo = True);
+
+ #
+ # Pause the VM if we're going to take any screenshots or dig into the
+ # guest. Failures are quitely ignored.
+ #
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ try:
+ if oSession.oVM.state in [ vboxcon.MachineState_Running,
+ vboxcon.MachineState_LiveSnapshotting,
+ vboxcon.MachineState_Teleporting ]:
+ oSession.o.console.pause();
+ except:
+ reporter.logXcpt();
+
+ #
+ # Take Screenshot and upload it (see below) to Test Manager if appropriate/requested.
+ #
+ if fTakeScreenshot is True or self.fAlwaysUploadScreenshots or reporter.testErrorCount() > 0:
+ sLastScreenshotPath = os.path.join(self.sScratchPath, "LastScreenshot-%s.png" % oSession.sName);
+ fRc = oSession.takeScreenshot(sLastScreenshotPath);
+ if fRc is not True:
+ sLastScreenshotPath = None;
+
+ # Query the OS kernel log from the debugger if appropriate/requested.
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ sOsKernelLog = oSession.queryOsKernelLog();
+
+ # Do "info vgatext all" separately.
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ sVgaText = oSession.queryDbgInfoVgaText();
+
+ # Various infos (do after kernel because of symbols).
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ # Dump the guest stack for all CPUs.
+ cCpus = oSession.getCpuCount();
+ if cCpus > 0:
+ for iCpu in xrange(0, cCpus):
+ sThis = oSession.queryDbgGuestStack(iCpu);
+ if sThis:
+ asMiscInfos += [
+ '================ start guest stack VCPU %s ================\n' % (iCpu,),
+ sThis,
+ '================ end guest stack VCPU %s ==================\n' % (iCpu,),
+ ];
+
+ for sInfo, sArg in [ ('mode', 'all'),
+ ('fflags', ''),
+ ('cpumguest', 'verbose all'),
+ ('cpumguestinstr', 'symbol all'),
+ ('exits', ''),
+ ('pic', ''),
+ ('apic', ''),
+ ('apiclvt', ''),
+ ('apictimer', ''),
+ ('ioapic', ''),
+ ('pit', ''),
+ ('phys', ''),
+ ('clocks', ''),
+ ('timers', ''),
+ ('gdt', ''),
+ ('ldt', ''),
+ ]:
+ if sInfo in ['apic',] and self.fpApiVer < 5.1: # asserts and burns
+ continue;
+ sThis = oSession.queryDbgInfo(sInfo, sArg);
+ if sThis:
+ if sThis[-1] != '\n':
+ sThis += '\n';
+ asMiscInfos += [
+ '================ start %s %s ================\n' % (sInfo, sArg),
+ sThis,
+ '================ end %s %s ==================\n' % (sInfo, sArg),
+ ];
+
+ #
+ # Terminate the VM
+ #
+
+ # Cancel the progress object if specified.
+ if oProgress is not None:
+ if not oProgress.isCompleted() and oProgress.isCancelable():
+ reporter.log2('terminateVmBySession: canceling "%s"...' % (oProgress.sName));
+ try:
+ oProgress.o.cancel();
+ except:
+ reporter.logXcpt();
+ else:
+ oProgress.wait();
+ self.removeTask(oProgress);
+
+ # Check if the VM has terminated by itself before powering it off.
+ fClose = True;
+ fRc = True;
+ if oSession.needsPoweringOff():
+ reporter.log('terminateVmBySession: powering off "%s"...' % (oSession.sName,));
+ fRc = oSession.powerOff(fFudgeOnFailure = False);
+ if fRc is not True:
+ # power off failed, try terminate it in a nice manner.
+ fRc = False;
+ uPid = oSession.getPid();
+ if uPid is not None:
+ #
+ # Collect some information about the VM process first to have
+ # some state information for further investigation why powering off failed.
+ #
+ sHostProcessInfoHung = utils.processGetInfo(uPid, fSudo = True);
+
+ # Exterminate...
+ reporter.error('terminateVmBySession: Terminating PID %u (VM %s)' % (uPid, oSession.sName));
+ fClose = base.processTerminate(uPid);
+ if fClose is True:
+ self.waitOnDirectSessionClose(oSession.oVM, 5000);
+ fClose = oSession.waitForTask(1000);
+
+ if fClose is not True:
+ # Being nice failed...
+ reporter.error('terminateVmBySession: Termination failed, trying to kill PID %u (VM %s) instead' \
+ % (uPid, oSession.sName));
+ fClose = base.processKill(uPid);
+ if fClose is True:
+ self.waitOnDirectSessionClose(oSession.oVM, 5000);
+ fClose = oSession.waitForTask(1000);
+ if fClose is not True:
+ reporter.error('terminateVmBySession: Failed to kill PID %u (VM %s)' % (uPid, oSession.sName));
+
+ # The final steps.
+ if fClose is True:
+ reporter.log('terminateVmBySession: closing session "%s"...' % (oSession.sName,));
+ oSession.close();
+ self.waitOnDirectSessionClose(oSession.oVM, 10000);
+ try:
+ eState = oSession.oVM.state;
+ except:
+ reporter.logXcpt();
+ else:
+ if eState == vboxcon.MachineState_Aborted:
+ reporter.error('terminateVmBySession: The VM "%s" aborted!' % (oSession.sName,));
+ self.removeTask(oSession);
+
+ #
+ # Add the release log, debug log and a screenshot of the VM to the test report.
+ #
+ if self.fAlwaysUploadLogs or reporter.testErrorCount() > 0:
+ oSession.addLogsToReport();
+
+ # Add a screenshot if it has been requested and taken successfully.
+ if sLastScreenshotPath is not None:
+ if reporter.testErrorCount() > 0:
+ reporter.addLogFile(sLastScreenshotPath, 'screenshot/failure', 'Last VM screenshot');
+ else:
+ reporter.addLogFile(sLastScreenshotPath, 'screenshot/success', 'Last VM screenshot');
+
+ # Add the guest OS log if it has been requested and taken successfully.
+ if sOsKernelLog is not None:
+ reporter.addLogString(sOsKernelLog, 'kernel.log', 'log/guest/kernel', 'Guest OS kernel log');
+
+ # Add "info vgatext all" if we've got it.
+ if sVgaText is not None:
+ reporter.addLogString(sVgaText, 'vgatext.txt', 'info/vgatext', 'info vgatext all');
+
+ # Add the "info xxxx" items if we've got any.
+ if asMiscInfos:
+ reporter.addLogString(u''.join(asMiscInfos), 'info.txt', 'info/collection', 'A bunch of info items.');
+
+ # Add the host process info if we were able to retrieve it.
+ if sHostProcessInfo is not None:
+ reporter.log('Trying to annotate the VM process report, please stand by...');
+ fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfo, 'vmprocess.log',
+ 'process/report/vm', 'Annotated VM process state');
+ # Upload the raw log for manual annotation in case resolving failed.
+ if not fRcTmp:
+ reporter.log('Failed to annotate VM process report, uploading raw report');
+ reporter.addLogString(sHostProcessInfo, 'vmprocess.log', 'process/report/vm', 'VM process state');
+
+ # Add the host process info for failed power off attempts if we were able to retrieve it.
+ if sHostProcessInfoHung is not None:
+ reporter.log('Trying to annotate the hung VM process report, please stand by...');
+ fRcTmp = self.annotateAndUploadProcessReport(sHostProcessInfoHung, 'vmprocess-hung.log',
+ 'process/report/vm', 'Annotated hung VM process state');
+ # Upload the raw log for manual annotation in case resolving failed.
+ if not fRcTmp:
+ reporter.log('Failed to annotate hung VM process report, uploading raw report');
+ fRcTmp = reporter.addLogString(sHostProcessInfoHung, 'vmprocess-hung.log', 'process/report/vm',
+ 'Hung VM process state');
+ if not fRcTmp:
+ try: reporter.log('******* START vmprocess-hung.log *******\n%s\n******* END vmprocess-hung.log *******\n'
+ % (sHostProcessInfoHung,));
+ except: pass; # paranoia
+
+ # Upload the screen video recordings if appropriate.
+ if self.fAlwaysUploadRecordings or reporter.testErrorCount() > 0:
+ reporter.log2('Uploading %d screen recordings ...' % (len(self.adRecordingFiles),));
+ for dRecFile in self.adRecordingFiles:
+ reporter.log2('Uploading screen recording "%s" (screen %d)' % (dRecFile['file'], dRecFile['id']));
+ reporter.addLogFile(dRecFile['file'],
+ 'screenrecording/failure' if reporter.testErrorCount() > 0 else 'screenrecording/success',
+ 'Recording of screen #%d' % (dRecFile['id'],));
+
+ return fRc;
+
+
+ #
+ # Some information query functions (mix).
+ #
+ # Methods require the VBox API. If the information is provided by both
+ # the testboxscript as well as VBox API, we'll check if it matches.
+ #
+
+ def _hasHostCpuFeature(self, sEnvVar, sEnum, fpApiMinVer, fQuiet):
+ """
+ Common Worker for hasHostNestedPaging() and hasHostHwVirt().
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ fEnv = os.environ.get(sEnvVar, None);
+ if fEnv is not None:
+ fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
+
+ fVBox = None;
+ self.importVBoxApi();
+ if self.fpApiVer >= fpApiMinVer and hasattr(vboxcon, sEnum):
+ try:
+ fVBox = self.oVBox.host.getProcessorFeature(getattr(vboxcon, sEnum));
+ except:
+ if not fQuiet:
+ reporter.logXcpt();
+
+ if fVBox is not None:
+ if fEnv is not None:
+ if fEnv != fVBox and not fQuiet:
+ reporter.log('TestBox configuration overwritten: fVBox=%s (%s) vs. fEnv=%s (%s)'
+ % (fVBox, sEnum, fEnv, sEnvVar));
+ return fEnv;
+ return fVBox;
+ if fEnv is not None:
+ return fEnv;
+ return False;
+
+ def hasHostHwVirt(self, fQuiet = False):
+ """
+ Checks if hardware assisted virtualization is supported by the host.
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ return self._hasHostCpuFeature('TESTBOX_HAS_HW_VIRT', 'ProcessorFeature_HWVirtEx', 3.1, fQuiet);
+
+ def hasHostNestedPaging(self, fQuiet = False):
+ """
+ Checks if nested paging is supported by the host.
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_PAGING', 'ProcessorFeature_NestedPaging', 4.2, fQuiet) \
+ and self.hasHostHwVirt(fQuiet);
+
+ def hasHostNestedHwVirt(self, fQuiet = False):
+ """
+ Checks if nested hardware-assisted virtualization is supported by the host.
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ return self._hasHostCpuFeature('TESTBOX_HAS_NESTED_HWVIRT', 'ProcessorFeature_NestedHWVirt', 6.0, fQuiet) \
+ and self.hasHostHwVirt(fQuiet);
+
+ def hasHostLongMode(self, fQuiet = False):
+ """
+ Checks if the host supports 64-bit guests.
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ # Note that the testboxscript doesn't export this variable atm.
+ return self._hasHostCpuFeature('TESTBOX_HAS_LONG_MODE', 'ProcessorFeature_LongMode', 3.1, fQuiet);
+
+ def getHostCpuCount(self, fQuiet = False):
+ """
+ Returns the number of CPUs on the host.
+
+ Returns True / False.
+ Raises exception on environment / host mismatch.
+ """
+ cEnv = os.environ.get('TESTBOX_CPU_COUNT', None);
+ if cEnv is not None:
+ cEnv = int(cEnv);
+
+ try:
+ cVBox = self.oVBox.host.processorOnlineCount;
+ except:
+ if not fQuiet:
+ reporter.logXcpt();
+ cVBox = None;
+
+ if cVBox is not None:
+ if cEnv is not None:
+ assert cVBox == cEnv, 'Misconfigured TestBox: VBox: %u CPUs, testboxscript: %u CPUs' % (cVBox, cEnv);
+ return cVBox;
+ if cEnv is not None:
+ return cEnv;
+ return 1;
+
+ def _getHostCpuDesc(self, fQuiet = False):
+ """
+ Internal method used for getting the host CPU description from VBoxSVC.
+ Returns description string, on failure an empty string is returned.
+ """
+ try:
+ return self.oVBox.host.getProcessorDescription(0);
+ except:
+ if not fQuiet:
+ reporter.logXcpt();
+ return '';
+
+ def isHostCpuAmd(self, fQuiet = False):
+ """
+ Checks if the host CPU vendor is AMD.
+
+ Returns True / False.
+ """
+ sCpuDesc = self._getHostCpuDesc(fQuiet);
+ return 'AMD' in sCpuDesc or sCpuDesc == 'AuthenticAMD';
+
+ def isHostCpuIntel(self, fQuiet = False):
+ """
+ Checks if the host CPU vendor is Intel.
+
+ Returns True / False.
+ """
+ sCpuDesc = self._getHostCpuDesc(fQuiet);
+ return sCpuDesc.startswith("Intel") or sCpuDesc == 'GenuineIntel';
+
+ def isHostCpuVia(self, fQuiet = False):
+ """
+ Checks if the host CPU vendor is VIA (or Centaur).
+
+ Returns True / False.
+ """
+ sCpuDesc = self._getHostCpuDesc(fQuiet);
+ return sCpuDesc.startswith("VIA") or sCpuDesc == 'CentaurHauls';
+
+ def isHostCpuShanghai(self, fQuiet = False):
+ """
+ Checks if the host CPU vendor is Shanghai (or Zhaoxin).
+
+ Returns True / False.
+ """
+ sCpuDesc = self._getHostCpuDesc(fQuiet);
+ return sCpuDesc.startswith("ZHAOXIN") or sCpuDesc.strip(' ') == 'Shanghai';
+
+ def isHostCpuP4(self, fQuiet = False):
+ """
+ Checks if the host CPU is a Pentium 4 / Pentium D.
+
+ Returns True / False.
+ """
+ if not self.isHostCpuIntel(fQuiet):
+ return False;
+
+ (uFamilyModel, _, _, _) = self.oVBox.host.getProcessorCPUIDLeaf(0, 0x1, 0);
+ return ((uFamilyModel >> 8) & 0xf) == 0xf;
+
+ def hasRawModeSupport(self, fQuiet = False):
+ """
+ Checks if raw-mode is supported by VirtualBox that the testbox is
+ configured for it.
+
+ Returns True / False.
+ Raises no exceptions.
+
+ Note! Differs from the rest in that we don't require the
+ TESTBOX_WITH_RAW_MODE value to match the API. It is
+ sometimes helpful to disable raw-mode on individual
+ test boxes. (This probably goes for
+ """
+ # The environment variable can be used to disable raw-mode.
+ fEnv = os.environ.get('TESTBOX_WITH_RAW_MODE', None);
+ if fEnv is not None:
+ fEnv = fEnv.lower() not in [ 'false', 'f', 'not', 'no', 'n', '0', ];
+ if fEnv is False:
+ return False;
+
+ # Starting with 5.0 GA / RC2 the API can tell us whether VBox was built
+ # with raw-mode support or not.
+ self.importVBoxApi();
+ if self.fpApiVer >= 5.0:
+ try:
+ fVBox = self.oVBox.systemProperties.rawModeSupported;
+ except:
+ if not fQuiet:
+ reporter.logXcpt();
+ fVBox = True;
+ if fVBox is False:
+ return False;
+
+ return True;
+
+ #
+ # Testdriver execution methods.
+ #
+
+ def handleTask(self, oTask, sMethod):
+ """
+ Callback method for handling unknown tasks in the various run loops.
+
+ The testdriver should override this if it already tasks running when
+ calling startVmAndConnectToTxsViaTcp, txsRunTest or similar methods.
+ Call super to handle unknown tasks.
+
+ Returns True if handled, False if not.
+ """
+ reporter.error('%s: unknown task %s' % (sMethod, oTask));
+ return False;
+
+ def txsDoTask(self, oSession, oTxsSession, fnAsync, aArgs):
+ """
+ Generic TXS task wrapper which waits both on the TXS and the session tasks.
+
+ Returns False on error, logged.
+ Returns task result on success.
+ """
+ # All async methods ends with the following two args.
+ cMsTimeout = aArgs[-2];
+ fIgnoreErrors = aArgs[-1];
+
+ fRemoveVm = self.addTask(oSession);
+ fRemoveTxs = self.addTask(oTxsSession);
+
+ rc = fnAsync(*aArgs); # pylint: disable=star-args
+ if rc is True:
+ rc = False;
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ if oTask is oTxsSession:
+ if oTxsSession.isSuccess():
+ rc = oTxsSession.getResult();
+ elif fIgnoreErrors is True:
+ reporter.log( 'txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
+ else:
+ reporter.error('txsDoTask: task failed (%s)' % (oTxsSession.getLastReply()[1],));
+ else:
+ oTxsSession.cancelTask();
+ if oTask is None:
+ if fIgnoreErrors is True:
+ reporter.log( 'txsDoTask: The task timed out.');
+ else:
+ reporter.errorTimeout('txsDoTask: The task timed out.');
+ elif oTask is oSession:
+ reporter.error('txsDoTask: The VM terminated unexpectedly');
+ else:
+ if fIgnoreErrors is True:
+ reporter.log( 'txsDoTask: An unknown task %s was returned' % (oTask,));
+ else:
+ reporter.error('txsDoTask: An unknown task %s was returned' % (oTask,));
+ else:
+ reporter.error('txsDoTask: fnAsync returned %s' % (rc,));
+
+ if fRemoveTxs:
+ self.removeTask(oTxsSession);
+ if fRemoveVm:
+ self.removeTask(oSession);
+ return rc;
+
+ # pylint: disable=missing-docstring
+
+ def txsDisconnect(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDisconnect,
+ (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsVer(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncVer,
+ (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsUuid(self, oSession, oTxsSession, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
+ (self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsMkDir(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDir,
+ (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsMkDirPath(self, oSession, oTxsSession, sRemoteDir, fMode = 0o700, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkDirPath,
+ (sRemoteDir, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsMkSymlink(self, oSession, oTxsSession, sLinkTarget, sLink, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncMkSymlink,
+ (sLinkTarget, sLink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsRmDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmDir,
+ (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsRmFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmFile,
+ (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsRmSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmSymlink,
+ (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsRmTree(self, oSession, oTxsSession, sRemoteTree, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncRmTree,
+ (sRemoteTree, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsIsDir(self, oSession, oTxsSession, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsDir,
+ (sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsIsFile(self, oSession, oTxsSession, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsFile,
+ (sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsIsSymlink(self, oSession, oTxsSession, sRemoteSymlink, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncIsSymlink,
+ (sRemoteSymlink, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsCopyFile(self, oSession, oTxsSession, sSrcFile, sDstFile, fMode = 0, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncCopyFile, \
+ (sSrcFile, sDstFile, fMode, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsUploadFile(self, oSession, oTxsSession, sLocalFile, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadFile, \
+ (sLocalFile, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsUploadString(self, oSession, oTxsSession, sContent, sRemoteFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUploadString, \
+ (sContent, sRemoteFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsDownloadFile(self, oSession, oTxsSession, sRemoteFile, sLocalFile, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadFile, \
+ (sRemoteFile, sLocalFile, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsDownloadFiles(self, oSession, oTxsSession, aasFiles, fAddToLog = True, fIgnoreErrors = False):
+ """
+ Convenience function to get files from the guest, storing them in the
+ scratch and adding them to the test result set (optional, but default).
+
+ The aasFiles parameter contains an array of with guest-path + host-path
+ pairs, optionally a file 'kind', description and an alternative upload
+ filename can also be specified.
+
+ Host paths are relative to the scratch directory or they must be given
+ in absolute form. The guest path should be using guest path style.
+
+ Returns True on success.
+ Returns False on failure (unless fIgnoreErrors is set), logged.
+ """
+ for asEntry in aasFiles:
+ # Unpack:
+ sGstFile = asEntry[0];
+ sHstFile = asEntry[1];
+ sKind = asEntry[2] if len(asEntry) > 2 and asEntry[2] else 'misc/other';
+ sDescription = asEntry[3] if len(asEntry) > 3 and asEntry[3] else '';
+ sAltName = asEntry[4] if len(asEntry) > 4 and asEntry[4] else None;
+ assert len(asEntry) <= 5 and sGstFile and sHstFile;
+ if not os.path.isabs(sHstFile):
+ sHstFile = os.path.join(self.sScratchPath, sHstFile);
+
+ reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sHstFile,));
+
+ try: os.unlink(sHstFile); ## @todo txsDownloadFile doesn't truncate the output file.
+ except: pass;
+
+ fRc = self.txsDownloadFile(oSession, oTxsSession, sGstFile, sHstFile, 30 * 1000, fIgnoreErrors);
+ if fRc:
+ if fAddToLog:
+ reporter.addLogFile(sHstFile, sKind, sDescription, sAltName);
+ else:
+ if fIgnoreErrors is not True:
+ return reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sHstFile));
+ reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
+ return True;
+
+ def txsDownloadString(self, oSession, oTxsSession, sRemoteFile, sEncoding = 'utf-8', fIgnoreEncodingErrors = True,
+ cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncDownloadString,
+ (sRemoteFile, sEncoding, fIgnoreEncodingErrors, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsPackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteSource, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncPackFile, \
+ (sRemoteFile, sRemoteSource, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsUnpackFile(self, oSession, oTxsSession, sRemoteFile, sRemoteDir, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUnpackFile, \
+ (sRemoteFile, sRemoteDir, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ def txsExpandString(self, oSession, oTxsSession, sString, cMsTimeout = 30000, fIgnoreErrors = False):
+ return self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncExpandString, \
+ (sString, self.adjustTimeoutMs(cMsTimeout), fIgnoreErrors));
+
+ # pylint: enable=missing-docstring
+
+ def txsCdWait(self,
+ oSession, # type: vboxwrappers.SessionWrapper
+ oTxsSession, # type: txsclient.Session
+ cMsTimeout = 30000, # type: int
+ sFile = None # type: String
+ ): # -> bool
+ """
+ Mostly an internal helper for txsRebootAndReconnectViaTcp and
+ startVmAndConnectToTxsViaTcp that waits for the CDROM drive to become
+ ready. It does this by polling for a file it knows to exist on the CD.
+
+ Returns True on success.
+
+ Returns False on failure, logged.
+ """
+
+ if sFile is None:
+ sFile = 'valkit.txt';
+
+ reporter.log('txsCdWait: Waiting for file "%s" to become available ...' % (sFile,));
+
+ fRemoveVm = self.addTask(oSession);
+ fRemoveTxs = self.addTask(oTxsSession);
+ cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
+ msStart = base.timestampMilli();
+ cMsTimeout2 = cMsTimeout;
+ fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
+ if fRc is True:
+ while True:
+ # wait for it to complete.
+ oTask = self.waitForTasks(cMsTimeout2 + 1);
+ if oTask is not oTxsSession:
+ oTxsSession.cancelTask();
+ if oTask is None:
+ reporter.errorTimeout('txsCdWait: The task timed out (after %s ms).'
+ % (base.timestampMilli() - msStart,));
+ elif oTask is oSession:
+ reporter.error('txsCdWait: The VM terminated unexpectedly');
+ else:
+ reporter.error('txsCdWait: An unknown task %s was returned' % (oTask,));
+ fRc = False;
+ break;
+ if oTxsSession.isSuccess():
+ break;
+
+ # Check for timeout.
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed >= cMsTimeout:
+ reporter.error('txsCdWait: timed out');
+ fRc = False;
+ break;
+ # delay.
+ self.sleep(1);
+
+ # resubmit the task.
+ cMsTimeout2 = msStart + cMsTimeout - base.timestampMilli();
+ cMsTimeout2 = max(cMsTimeout2, 500);
+ fRc = oTxsSession.asyncIsFile('${CDROM}/%s' % (sFile,), cMsTimeout2);
+ if fRc is not True:
+ reporter.error('txsCdWait: asyncIsFile failed');
+ break;
+ else:
+ reporter.error('txsCdWait: asyncIsFile failed');
+
+ if not fRc:
+ # Do some diagnosis to find out why this failed.
+ ## @todo Identify guest OS type and only run one of the following commands.
+ fIsNotWindows = True;
+ reporter.log('txsCdWait: Listing root contents of ${CDROM}:');
+ if fIsNotWindows:
+ reporter.log('txsCdWait: Tiggering udevadm ...');
+ oTxsSession.syncExec("/sbin/udevadm", ("/sbin/udevadm", "trigger", "--verbose"), fIgnoreErrors = True);
+ time.sleep(15);
+ oTxsSession.syncExec("/bin/ls", ("/bin/ls", "-al", "${CDROM}"), fIgnoreErrors = True);
+ reporter.log('txsCdWait: Listing media directory:');
+ oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
+ reporter.log('txsCdWait: Listing mount points / drives:');
+ oTxsSession.syncExec('/bin/mount', ('/bin/mount',), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/cat', ('/bin/cat', '/etc/fstab'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/dmesg', ('/bin/dmesg',), fIgnoreErrors = True);
+ oTxsSession.syncExec('/usr/bin/lshw', ('/usr/bin/lshw', '-c', 'disk'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/journalctl',
+ ('/bin/journalctl', '-x', '-b'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/journalctl',
+ ('/bin/journalctl', '-x', '-b', '/usr/lib/udisks2/udisksd'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/usr/bin/udisksctl',
+ ('/usr/bin/udisksctl', 'info', '-b', '/dev/sr0'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/systemctl',
+ ('/bin/systemctl', 'status', 'udisks2'), fIgnoreErrors = True);
+ oTxsSession.syncExec('/bin/ps',
+ ('/bin/ps', '-a', '-u', '-x'), fIgnoreErrors = True);
+ reporter.log('txsCdWait: Mounting manually ...');
+ for _ in range(3):
+ oTxsSession.syncExec('/bin/mount', ('/bin/mount', '/dev/sr0', '${CDROM}'), fIgnoreErrors = True);
+ time.sleep(5);
+ reporter.log('txsCdWait: Re-Listing media directory:');
+ oTxsSession.syncExec('/bin/ls', ('/bin/ls', '-l', '-a', '-R', '/media'), fIgnoreErrors = True);
+ else:
+ # ASSUMES that we always install Windows on drive C right now.
+ sWinDir = "C:\\Windows\\System32\\";
+ # Should work since WinXP Pro.
+ oTxsSession.syncExec(sWinDir + "wbem\\WMIC.exe",
+ ("WMIC.exe", "logicaldisk", "get",
+ "deviceid, volumename, description"),
+ fIgnoreErrors = True);
+ oTxsSession.syncExec(sWinDir + " cmd.exe",
+ ('cmd.exe', '/C', 'dir', '${CDROM}'),
+ fIgnoreErrors = True);
+
+ if fRemoveTxs:
+ self.removeTask(oTxsSession);
+ if fRemoveVm:
+ self.removeTask(oSession);
+ return fRc;
+
+ def txsDoConnectViaTcp(self, oSession, cMsTimeout, fNatForwardingForTxs = False):
+ """
+ Mostly an internal worker for connecting to TXS via TCP used by the
+ *ViaTcp methods.
+
+ Returns a tuplet with True/False and TxsSession/None depending on the
+ result. Errors are logged.
+ """
+
+ reporter.log2('txsDoConnectViaTcp: oSession=%s, cMsTimeout=%s, fNatForwardingForTxs=%s'
+ % (oSession, cMsTimeout, fNatForwardingForTxs));
+
+ cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
+ oTxsConnect = oSession.txsConnectViaTcp(cMsTimeout, fNatForwardingForTxs = fNatForwardingForTxs);
+ if oTxsConnect is not None:
+ self.addTask(oTxsConnect);
+ fRemoveVm = self.addTask(oSession);
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ reporter.log2('txsDoConnectViaTcp: waitForTasks returned %s' % (oTask,));
+ self.removeTask(oTxsConnect);
+ if oTask is oTxsConnect:
+ oTxsSession = oTxsConnect.getResult();
+ if oTxsSession is not None:
+ reporter.log('txsDoConnectViaTcp: Connected to TXS on %s.' % (oTxsSession.oTransport.sHostname,));
+ return (True, oTxsSession);
+
+ reporter.error('txsDoConnectViaTcp: failed to connect to TXS.');
+ else:
+ oTxsConnect.cancelTask();
+ if oTask is None:
+ reporter.errorTimeout('txsDoConnectViaTcp: connect stage 1 timed out');
+ elif oTask is oSession:
+ oSession.reportPrematureTermination('txsDoConnectViaTcp: ');
+ else:
+ reporter.error('txsDoConnectViaTcp: unknown/wrong task %s' % (oTask,));
+ if fRemoveVm:
+ self.removeTask(oSession);
+ else:
+ reporter.error('txsDoConnectViaTcp: txsConnectViaTcp failed');
+ return (False, None);
+
+ def startVmAndConnectToTxsViaTcp(self, sVmName, fCdWait = False, cMsTimeout = 15*60000, \
+ cMsCdWait = 30000, sFileCdWait = None, \
+ fNatForwardingForTxs = False):
+ """
+ Starts the specified VM and tries to connect to its TXS via TCP.
+ The VM will be powered off if TXS doesn't respond before the specified
+ time has elapsed.
+
+ Returns a the VM and TXS sessions (a two tuple) on success. The VM
+ session is in the task list, the TXS session is not.
+ Returns (None, None) on failure, fully logged.
+ """
+
+ # Zap the guest IP to make sure we're not getting a stale entry
+ # (unless we're restoring the VM of course).
+ oTestVM = self.oTestVmSet.findTestVmByName(sVmName) if self.oTestVmSet is not None else None;
+ if oTestVM is None \
+ or oTestVM.fSnapshotRestoreCurrent is False:
+ try:
+ oSession1 = self.openSession(self.getVmByName(sVmName));
+ oSession1.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
+ oSession1.saveSettings(True);
+ del oSession1;
+ except:
+ reporter.logXcpt();
+
+ # Start the VM.
+ reporter.log('startVmAndConnectToTxsViaTcp: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
+ reporter.flushall();
+ oSession = self.startVmByName(sVmName);
+ if oSession is not None:
+ # Connect to TXS.
+ reporter.log2('startVmAndConnectToTxsViaTcp: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout, fNatForwardingForTxs);
+ if fRc is True:
+ if fCdWait:
+ # Wait for CD?
+ reporter.log2('startVmAndConnectToTxsViaTcp: Waiting for file "%s" to become available ...' % (sFileCdWait,));
+ fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
+ if fRc is not True:
+ reporter.error('startVmAndConnectToTxsViaTcp: txsCdWait failed');
+
+ sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
+ if sVer is not False:
+ reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sVer,));
+ else:
+ reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
+
+ if fRc is True:
+ # Success!
+ return (oSession, oTxsSession);
+ else:
+ reporter.error('startVmAndConnectToTxsViaTcp: txsDoConnectViaTcp failed');
+ # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
+ self.terminateVmBySession(oSession);
+ return (None, None);
+
+ def txsRebootAndReconnectViaTcp(self, oSession, oTxsSession, fCdWait = False, cMsTimeout = 15*60000, \
+ cMsCdWait = 30000, sFileCdWait = None, fNatForwardingForTxs = False):
+ """
+ Executes the TXS reboot command
+
+ Returns A tuple of True and the new TXS session on success.
+
+ Returns A tuple of False and either the old TXS session or None on failure.
+ """
+ reporter.log2('txsRebootAndReconnect: cMsTimeout=%u' % (cMsTimeout,));
+
+ #
+ # This stuff is a bit complicated because of rebooting being kind of
+ # disruptive to the TXS and such... The protocol is that TXS will:
+ # - ACK the reboot command.
+ # - Shutdown the transport layer, implicitly disconnecting us.
+ # - Execute the reboot operation.
+ # - On failure, it will be re-init the transport layer and be
+ # available pretty much immediately. UUID unchanged.
+ # - On success, it will be respawed after the reboot (hopefully),
+ # with a different UUID.
+ #
+ fRc = False;
+ iStart = base.timestampMilli();
+
+ # Get UUID.
+ cMsTimeout2 = min(60000, cMsTimeout);
+ sUuidBefore = self.txsUuid(oSession, oTxsSession, self.adjustTimeoutMs(cMsTimeout2, 60000));
+ if sUuidBefore is not False:
+ # Reboot.
+ cMsElapsed = base.timestampMilli() - iStart;
+ cMsTimeout2 = cMsTimeout - cMsElapsed;
+ fRc = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncReboot,
+ (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
+ if fRc is True:
+ # Reconnect.
+ if fNatForwardingForTxs is True:
+ self.sleep(22); # NAT fudge - Two fixes are wanted: 1. TXS connect retries. 2. Main API reboot/reset hint.
+ cMsElapsed = base.timestampMilli() - iStart;
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, cMsTimeout - cMsElapsed, fNatForwardingForTxs);
+ if fRc is True:
+ # Check the UUID.
+ cMsElapsed = base.timestampMilli() - iStart;
+ cMsTimeout2 = min(60000, cMsTimeout - cMsElapsed);
+ sUuidAfter = self.txsDoTask(oSession, oTxsSession, oTxsSession.asyncUuid,
+ (self.adjustTimeoutMs(cMsTimeout2, 60000), False));
+ if sUuidBefore is not False:
+ if sUuidAfter != sUuidBefore:
+ reporter.log('The guest rebooted (UUID %s -> %s)' % (sUuidBefore, sUuidAfter))
+
+ # Do CD wait if specified.
+ if fCdWait:
+ fRc = self.txsCdWait(oSession, oTxsSession, cMsCdWait, sFileCdWait);
+ if fRc is not True:
+ reporter.error('txsRebootAndReconnectViaTcp: txsCdWait failed');
+
+ sVer = self.txsVer(oSession, oTxsSession, cMsTimeout, fIgnoreErrors = True);
+ if sVer is not False:
+ reporter.log('txsRebootAndReconnectViaTcp: TestExecService version %s' % (sVer,));
+ else:
+ reporter.log('txsRebootAndReconnectViaTcp: Unable to retrieve TestExecService version');
+ else:
+ reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (after)');
+ else:
+ reporter.error('txsRebootAndReconnectViaTcp: did not reboot (UUID %s)' % (sUuidBefore,));
+ else:
+ reporter.error('txsRebootAndReconnectViaTcp: txsDoConnectViaTcp failed');
+ else:
+ reporter.error('txsRebootAndReconnectViaTcp: reboot failed');
+ else:
+ reporter.error('txsRebootAndReconnectViaTcp: failed to get UUID (before)');
+ return (fRc, oTxsSession);
+
+ # pylint: disable=too-many-locals,too-many-arguments
+
+ def txsRunTest(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
+ fCheckSessionStatus = False):
+ """
+ Executes the specified test task, waiting till it completes or times out.
+
+ The VM session (if any) must be in the task list.
+
+ Returns True if we executed the task and nothing abnormal happend.
+ Query the process status from the TXS session.
+
+ Returns False if some unexpected task was signalled or we failed to
+ submit the job.
+
+ If fCheckSessionStatus is set to True, the overall session status will be
+ taken into account and logged as an error on failure.
+ """
+ reporter.testStart(sTestName);
+ reporter.log2('txsRunTest: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
+
+ # Submit the job.
+ fRc = False;
+ if oTxsSession.asyncExec(sExecName, asArgs, asAddEnv, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
+ self.addTask(oTxsSession);
+
+ # Wait for the job to complete.
+ while True:
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ if oTask is None:
+ if fCheckSessionStatus:
+ reporter.error('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
+ else:
+ reporter.log('txsRunTest: waitForTasks for test "%s" timed out' % (sTestName,));
+ break;
+ if oTask is oTxsSession:
+ if fCheckSessionStatus \
+ and not oTxsSession.isSuccess():
+ reporter.error('txsRunTest: Test "%s" failed' % (sTestName,));
+ else:
+ fRc = True;
+ reporter.log('txsRunTest: isSuccess=%s getResult=%s' \
+ % (oTxsSession.isSuccess(), oTxsSession.getResult()));
+ break;
+ if not self.handleTask(oTask, 'txsRunTest'):
+ break;
+
+ self.removeTask(oTxsSession);
+ if not oTxsSession.pollTask():
+ oTxsSession.cancelTask();
+ else:
+ reporter.error('txsRunTest: asyncExec failed');
+
+ reporter.testDone();
+ return fRc;
+
+ def txsRunTestRedirectStd(self, oTxsSession, sTestName, cMsTimeout, sExecName, asArgs = (), asAddEnv = (), sAsUser = "",
+ oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
+ """
+ Executes the specified test task, waiting till it completes or times out,
+ redirecting stdin, stdout and stderr to the given objects.
+
+ The VM session (if any) must be in the task list.
+
+ Returns True if we executed the task and nothing abnormal happend.
+ Query the process status from the TXS session.
+
+ Returns False if some unexpected task was signalled or we failed to
+ submit the job.
+ """
+ reporter.testStart(sTestName);
+ reporter.log2('txsRunTestRedirectStd: cMsTimeout=%u sExecName=%s asArgs=%s' % (cMsTimeout, sExecName, asArgs));
+
+ # Submit the job.
+ fRc = False;
+ if oTxsSession.asyncExecEx(sExecName, asArgs, asAddEnv, oStdIn, oStdOut, oStdErr,
+ oTestPipe, sAsUser, cMsTimeout = self.adjustTimeoutMs(cMsTimeout)):
+ self.addTask(oTxsSession);
+
+ # Wait for the job to complete.
+ while True:
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ if oTask is None:
+ reporter.log('txsRunTestRedirectStd: waitForTasks timed out');
+ break;
+ if oTask is oTxsSession:
+ fRc = True;
+ reporter.log('txsRunTestRedirectStd: isSuccess=%s getResult=%s'
+ % (oTxsSession.isSuccess(), oTxsSession.getResult()));
+ break;
+ if not self.handleTask(oTask, 'txsRunTestRedirectStd'):
+ break;
+
+ self.removeTask(oTxsSession);
+ if not oTxsSession.pollTask():
+ oTxsSession.cancelTask();
+ else:
+ reporter.error('txsRunTestRedirectStd: asyncExec failed');
+
+ reporter.testDone();
+ return fRc;
+
+ def txsRunTest2(self, oTxsSession1, oTxsSession2, sTestName, cMsTimeout,
+ sExecName1, asArgs1,
+ sExecName2, asArgs2,
+ asAddEnv1 = (), sAsUser1 = '', fWithTestPipe1 = True,
+ asAddEnv2 = (), sAsUser2 = '', fWithTestPipe2 = True):
+ """
+ Executes the specified test tasks, waiting till they complete or
+ times out. The 1st task is started after the 2nd one.
+
+ The VM session (if any) must be in the task list.
+
+ Returns True if we executed the task and nothing abnormal happend.
+ Query the process status from the TXS sessions.
+
+ Returns False if some unexpected task was signalled or we failed to
+ submit the job.
+ """
+ reporter.testStart(sTestName);
+
+ # Submit the jobs.
+ fRc = False;
+ if oTxsSession1.asyncExec(sExecName1, asArgs1, asAddEnv1, sAsUser1, fWithTestPipe1, '1-',
+ self.adjustTimeoutMs(cMsTimeout)):
+ self.addTask(oTxsSession1);
+
+ self.sleep(2); # fudge! grr
+
+ if oTxsSession2.asyncExec(sExecName2, asArgs2, asAddEnv2, sAsUser2, fWithTestPipe2, '2-',
+ self.adjustTimeoutMs(cMsTimeout)):
+ self.addTask(oTxsSession2);
+
+ # Wait for the jobs to complete.
+ cPendingJobs = 2;
+ while True:
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ if oTask is None:
+ reporter.log('txsRunTest2: waitForTasks timed out');
+ break;
+
+ if oTask is oTxsSession1 or oTask is oTxsSession2:
+ if oTask is oTxsSession1: iTask = 1;
+ else: iTask = 2;
+ reporter.log('txsRunTest2: #%u - isSuccess=%s getResult=%s' \
+ % (iTask, oTask.isSuccess(), oTask.getResult()));
+ self.removeTask(oTask);
+ cPendingJobs -= 1;
+ if cPendingJobs <= 0:
+ fRc = True;
+ break;
+
+ elif not self.handleTask(oTask, 'txsRunTest'):
+ break;
+
+ self.removeTask(oTxsSession2);
+ if not oTxsSession2.pollTask():
+ oTxsSession2.cancelTask();
+ else:
+ reporter.error('txsRunTest2: asyncExec #2 failed');
+
+ self.removeTask(oTxsSession1);
+ if not oTxsSession1.pollTask():
+ oTxsSession1.cancelTask();
+ else:
+ reporter.error('txsRunTest2: asyncExec #1 failed');
+
+ reporter.testDone();
+ return fRc;
+
+ # pylint: enable=too-many-locals,too-many-arguments
+
+
+ #
+ # Working with test results via serial port.
+ #
+
+ class TxsMonitorComFile(base.TdTaskBase):
+ """
+ Class that monitors a COM output file.
+ """
+
+ def __init__(self, sComRawFile, asStopWords = None):
+ base.TdTaskBase.__init__(self, utils.getCallerName());
+ self.sComRawFile = sComRawFile;
+ self.oStopRegExp = re.compile('\\b(' + '|'.join(asStopWords if asStopWords else ('PASSED', 'FAILED',)) + ')\\b');
+ self.sResult = None; ##< The result.
+ self.cchDisplayed = 0; ##< Offset into the file string of what we've already fed to the logger.
+
+ def toString(self):
+ return '<%s sComRawFile=%s oStopRegExp=%s sResult=%s cchDisplayed=%s>' \
+ % (base.TdTaskBase.toString(self), self.sComRawFile, self.oStopRegExp, self.sResult, self.cchDisplayed,);
+
+ def pollTask(self, fLocked = False):
+ """
+ Overrides TdTaskBase.pollTask() for the purpose of polling the file.
+ """
+ if not fLocked:
+ self.lockTask();
+
+ sFile = utils.noxcptReadFile(self.sComRawFile, '', 'rU');
+ if len(sFile) > self.cchDisplayed:
+ sNew = sFile[self.cchDisplayed:];
+ oMatch = self.oStopRegExp.search(sNew);
+ if oMatch:
+ # Done! Get result, flush all the output and signal the task.
+ self.sResult = oMatch.group(1);
+ for sLine in sNew.split('\n'):
+ reporter.log('COM OUTPUT: %s' % (sLine,));
+ self.cchDisplayed = len(sFile);
+ self.signalTaskLocked();
+ else:
+ # Output whole lines only.
+ offNewline = sFile.find('\n', self.cchDisplayed);
+ while offNewline >= 0:
+ reporter.log('COM OUTPUT: %s' % (sFile[self.cchDisplayed:offNewline]))
+ self.cchDisplayed = offNewline + 1;
+ offNewline = sFile.find('\n', self.cchDisplayed);
+
+ fRet = self.fSignalled;
+ if not fLocked:
+ self.unlockTask();
+ return fRet;
+
+ # Our stuff.
+ def getResult(self):
+ """
+ Returns the connected TXS session object on success.
+ Returns None on failure or if the task has not yet completed.
+ """
+ self.oCv.acquire();
+ sResult = self.sResult;
+ self.oCv.release();
+ return sResult;
+
+ def cancelTask(self):
+ """ Cancels the task. """
+ self.signalTask();
+ return True;
+
+
+ def monitorComRawFile(self, oSession, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
+ """
+ Monitors the COM output file for stop words (PASSED and FAILED by default).
+
+ Returns the stop word.
+ Returns None on VM error and timeout.
+ """
+
+ reporter.log2('monitorComRawFile: oSession=%s, cMsTimeout=%s, sComRawFile=%s' % (oSession, cMsTimeout, sComRawFile));
+
+ oMonitorTask = self.TxsMonitorComFile(sComRawFile, asStopWords);
+ self.addTask(oMonitorTask);
+
+ cMsTimeout = self.adjustTimeoutMs(cMsTimeout);
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ reporter.log2('monitorComRawFile: waitForTasks returned %s' % (oTask,));
+
+ if oTask is not oMonitorTask:
+ oMonitorTask.cancelTask();
+ self.removeTask(oMonitorTask);
+
+ oMonitorTask.pollTask();
+ return oMonitorTask.getResult();
+
+
+ def runVmAndMonitorComRawFile(self, sVmName, sComRawFile, cMsTimeout = 15*60000, asStopWords = None):
+ """
+ Runs the specified VM and monitors the given COM output file for stop
+ words (PASSED and FAILED by default).
+
+ The caller is assumed to have configured the VM to use the given
+ file. The method will take no action to verify this.
+
+ Returns the stop word.
+ Returns None on VM error and timeout.
+ """
+
+ # Start the VM.
+ reporter.log('runVmAndMonitorComRawFile: Starting(/preparing) "%s" (timeout %s s)...' % (sVmName, cMsTimeout / 1000));
+ reporter.flushall();
+ oSession = self.startVmByName(sVmName);
+ if oSession is not None:
+ # Let it run and then terminate it.
+ sRet = self.monitorComRawFile(oSession, sComRawFile, cMsTimeout, asStopWords);
+ self.terminateVmBySession(oSession);
+ else:
+ sRet = None;
+ return sRet;
+
+ #
+ # Other stuff
+ #
+
+ def waitForGAs(self,
+ oSession, # type: vboxwrappers.SessionWrapper
+ cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
+ """
+ Waits for the guest additions to enter a certain state.
+
+ aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
+ aenmWaitForActive - List facilities (type values) that must be active.
+ aenmWaitForInactive - List facilities (type values) that must be inactive.
+
+ Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
+
+ Returns True on success, False w/ error logging on timeout or failure.
+ """
+ reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
+
+ #
+ # Get IGuest:
+ #
+ try:
+ oIGuest = oSession.o.console.guest;
+ except:
+ return reporter.errorXcpt();
+
+ #
+ # Create a wait task:
+ #
+ from testdriver.vboxwrappers import AdditionsStatusTask;
+ try:
+ oGaStatusTask = AdditionsStatusTask(oSession = oSession,
+ oIGuest = oIGuest,
+ cMsTimeout = cMsTimeout,
+ aenmWaitForRunLevels = aenmWaitForRunLevels,
+ aenmWaitForActive = aenmWaitForActive,
+ aenmWaitForInactive = aenmWaitForInactive);
+ except:
+ return reporter.errorXcpt();
+
+ #
+ # Add the task and make sure the VM session is also present.
+ #
+ self.addTask(oGaStatusTask);
+ fRemoveSession = self.addTask(oSession);
+ oTask = self.waitForTasks(cMsTimeout + 1);
+ reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
+ self.removeTask(oGaStatusTask);
+ if fRemoveSession:
+ self.removeTask(oSession);
+
+ #
+ # Digest the result.
+ #
+ if oTask is oGaStatusTask:
+ fSucceeded = oGaStatusTask.getResult();
+ if fSucceeded is True:
+ reporter.log('waitForGAs: Succeeded.');
+ else:
+ reporter.error('waitForGAs: Failed.');
+ else:
+ oGaStatusTask.cancelTask();
+ if oTask is None:
+ reporter.error('waitForGAs: Timed out.');
+ elif oTask is oSession:
+ oSession.reportPrematureTermination('waitForGAs: ');
+ else:
+ reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
+ fSucceeded = False;
+ return fSucceeded;
+
+ @staticmethod
+ def controllerTypeToName(eControllerType):
+ """
+ Translate a controller type to a standard controller name.
+ """
+ if eControllerType in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
+ sName = "IDE Controller";
+ elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
+ sName = "SATA Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
+ sName = "SAS Controller";
+ elif eControllerType in (vboxcon.StorageControllerType_LsiLogic, vboxcon.StorageControllerType_BusLogic,):
+ sName = "SCSI Controller";
+ elif eControllerType == vboxcon.StorageControllerType_NVMe:
+ sName = "NVMe Controller";
+ elif eControllerType == vboxcon.StorageControllerType_VirtioSCSI:
+ sName = "VirtIO SCSI Controller";
+ else:
+ sName = "Storage Controller";
+ return sName;
diff --git a/src/VBox/ValidationKit/testdriver/vboxcon.py b/src/VBox/ValidationKit/testdriver/vboxcon.py
new file mode 100755
index 00000000..1ab7321f
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxcon.py
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxcon.py $
+
+"""
+VirtualBox Constants.
+
+See VBoxConstantWrappingHack for details.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import sys
+
+
+class VBoxConstantWrappingHack(object): # pylint: disable=too-few-public-methods
+ """
+ This is a hack to avoid the self.oVBoxMgr.constants.MachineState_Running
+ ugliness that forces one into the right margin... Anyone using this module
+ can get to the constants easily by:
+
+ from testdriver import vboxcon
+ if self.o.machine.state == vboxcon.MachineState_Running:
+ do stuff;
+
+ For our own convenience there's a vboxcon attribute set up in vbox.py,
+ class TestDriver which is the basis for the VirtualBox testcases. It takes
+ care of setting things up properly through the global variable
+ 'goHackModuleClass' that refers to the instance of this class(if we didn't
+ we'd have to use testdriver.vboxcon.MachineState_Running).
+ """
+ def __init__(self, oWrapped):
+ self.oWrapped = oWrapped;
+ self.oVBoxMgr = None;
+ self.fpApiVer = 99.0;
+
+ def __getattr__(self, sName):
+ # Our self.
+ try:
+ return getattr(self.oWrapped, sName)
+ except AttributeError:
+ # The VBox constants.
+ if self.oVBoxMgr is None:
+ raise;
+ try:
+ return getattr(self.oVBoxMgr.constants, sName);
+ except AttributeError:
+ # Do some compatability mappings to keep it working with
+ # older versions.
+ if self.fpApiVer < 3.3:
+ if sName == 'SessionState_Locked':
+ return getattr(self.oVBoxMgr.constants, 'SessionState_Open');
+ if sName == 'SessionState_Unlocked':
+ return getattr(self.oVBoxMgr.constants, 'SessionState_Closed');
+ if sName == 'SessionState_Unlocking':
+ return getattr(self.oVBoxMgr.constants, 'SessionState_Closing');
+ raise;
+
+
+goHackModuleClass = VBoxConstantWrappingHack(sys.modules[__name__]); # pylint: disable=invalid-name
+sys.modules[__name__] = goHackModuleClass;
+
diff --git a/src/VBox/ValidationKit/testdriver/vboxinstaller.py b/src/VBox/ValidationKit/testdriver/vboxinstaller.py
new file mode 100755
index 00000000..6c72ac91
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxinstaller.py
@@ -0,0 +1,1251 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+VirtualBox Installer Wrapper Driver.
+
+This installs VirtualBox, starts a sub driver which does the real testing,
+and then uninstall VirtualBox afterwards. This reduces the complexity of the
+other VBox test drivers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import re
+import socket
+import tempfile
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils, webutils;
+from common.constants import rtexitcode;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+
+class VBoxInstallerTestDriver(TestDriverBase):
+ """
+ Implementation of a top level test driver.
+ """
+
+
+ ## State file indicating that we've skipped installation.
+ ksVar_Skipped = 'vboxinstaller-skipped';
+
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+ self._asSubDriver = []; # The sub driver and it's arguments.
+ self._asBuildUrls = []; # The URLs passed us on the command line.
+ self._asBuildFiles = []; # The downloaded file names.
+ self._fUnpackedBuildFiles = False;
+ self._fAutoInstallPuelExtPack = True;
+ self._fKernelDrivers = True;
+ self._fWinForcedInstallTimestampCA = True;
+ self._fInstallMsCrt = False; # By default we don't install the Microsoft CRT (only needed once).
+
+ #
+ # Base method we override
+ #
+
+ def showUsage(self):
+ rc = TestDriverBase.showUsage(self);
+ # 0 1 2 3 4 5 6 7 8
+ # 012345678901234567890123456789012345678901234567890123456789012345678901234567890
+ reporter.log('');
+ reporter.log('vboxinstaller Options:');
+ reporter.log(' --vbox-build <url[,url2[,...]]>');
+ reporter.log(' Comma separated list of URL to file to download and install or/and');
+ reporter.log(' unpack. URLs without a schema are assumed to be files on the');
+ reporter.log(' build share and will be copied off it.');
+ reporter.log(' --no-puel-extpack');
+ reporter.log(' Indicates that the PUEL extension pack should not be installed if found.');
+ reporter.log(' The default is to install it when found in the vbox-build.');
+ reporter.log(' --no-kernel-drivers');
+ reporter.log(' Indicates that the kernel drivers should not be installed on platforms');
+ reporter.log(' where this is optional. The default is to install them.');
+ reporter.log(' --forced-win-install-timestamp-ca, --no-forced-win-install-timestamp-ca');
+ reporter.log(' Whether to force installation of the legacy Windows timestamp CA.');
+ reporter.log(' If not forced, it will only installed on the hosts that needs it.');
+ reporter.log(' Default: --no-forced-win-install-timestamp-ca');
+ reporter.log(' --win-install-mscrt, --no-win-install-mscrt');
+ reporter.log(' Whether to install the MS Visual Studio Redistributable.');
+ reporter.log(' Default: --no-win-install-mscrt');
+ reporter.log(' --');
+ reporter.log(' Indicates the end of our parameters and the start of the sub');
+ reporter.log(' testdriver and its arguments.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parse our arguments.
+ """
+ if asArgs[iArg] == '--':
+ # End of our parameters and start of the sub driver invocation.
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ assert not self._asSubDriver;
+ self._asSubDriver = asArgs[iArg:];
+ self._asSubDriver[0] = self._asSubDriver[0].replace('/', os.path.sep);
+ iArg = len(asArgs) - 1;
+ elif asArgs[iArg] == '--vbox-build':
+ # List of files to copy/download and install.
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self._asBuildUrls = asArgs[iArg].split(',');
+ elif asArgs[iArg] == '--no-puel-extpack':
+ self._fAutoInstallPuelExtPack = False;
+ elif asArgs[iArg] == '--puel-extpack':
+ self._fAutoInstallPuelExtPack = True;
+ elif asArgs[iArg] == '--no-kernel-drivers':
+ self._fKernelDrivers = False;
+ elif asArgs[iArg] == '--kernel-drivers':
+ self._fKernelDrivers = True;
+ elif asArgs[iArg] == '--no-forced-win-install-timestamp-ca':
+ self._fWinForcedInstallTimestampCA = False;
+ elif asArgs[iArg] == '--forced-win-install-timestamp-ca':
+ self._fWinForcedInstallTimestampCA = True;
+ elif asArgs[iArg] == '--no-win-install-mscrt':
+ self._fInstallMsCrt = False;
+ elif asArgs[iArg] == '--win-install-mscrt':
+ self._fInstallMsCrt = True;
+ else:
+ return TestDriverBase.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ #
+ # Check that we've got what we need.
+ #
+ if not self._asBuildUrls:
+ reporter.error('No build files specified ("--vbox-build file1[,file2[...]]")');
+ return False;
+ if not self._asSubDriver:
+ reporter.error('No sub testdriver specified. (" -- test/stuff/tdStuff1.py args")');
+ return False;
+
+ #
+ # Construct _asBuildFiles as an array parallel to _asBuildUrls.
+ #
+ for sUrl in self._asBuildUrls:
+ sDstFile = os.path.join(self.sScratchPath, webutils.getFilename(sUrl));
+ self._asBuildFiles.append(sDstFile);
+
+ return TestDriverBase.completeOptions(self);
+
+ def actionExtract(self):
+ reporter.error('vboxinstall does not support extracting resources, you have to do that using the sub testdriver.');
+ return False;
+
+ def actionCleanupBefore(self):
+ """
+ Kills all VBox process we see.
+
+ This is only supposed to execute on a testbox so we don't need to go
+ all complicated wrt other users.
+ """
+ return self._killAllVBoxProcesses();
+
+ def actionConfig(self):
+ """
+ Install VBox and pass on the configure request to the sub testdriver.
+ """
+ fRc = self._installVBox();
+ if fRc is None:
+ self._persistentVarSet(self.ksVar_Skipped, 'true');
+ self.fBadTestbox = True;
+ else:
+ self._persistentVarUnset(self.ksVar_Skipped);
+
+ ## @todo vbox.py still has bugs preventing us from invoking it seperately with each action.
+ if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'verify', ]);
+ if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'config', ], fPreloadASan = True);
+ return fRc;
+
+ def actionExecute(self):
+ """
+ Execute the sub testdriver.
+ """
+ return self._executeSubDriver(self.asActions, fPreloadASan = True);
+
+ def actionCleanupAfter(self):
+ """
+ Forward this to the sub testdriver, then uninstall VBox.
+ """
+ fRc = True;
+ if 'execute' not in self.asActions and 'all' not in self.asActions:
+ fRc = self._executeSubDriver([ 'cleanup-after', ], fMaySkip = False);
+
+ if not self._killAllVBoxProcesses():
+ fRc = False;
+
+ if not self._uninstallVBox(self._persistentVarExists(self.ksVar_Skipped)):
+ fRc = False;
+
+ if utils.getHostOs() == 'darwin':
+ self._darwinUnmountDmg(fIgnoreError = True); # paranoia
+
+ if not TestDriverBase.actionCleanupAfter(self):
+ fRc = False;
+
+ return fRc;
+
+
+ def actionAbort(self):
+ """
+ Forward this to the sub testdriver first, then wipe all VBox like
+ processes, and finally do the pid file processing (again).
+ """
+ fRc1 = self._executeSubDriver([ 'abort', ], fMaySkip = False, fPreloadASan = True);
+ fRc2 = self._killAllVBoxProcesses();
+ fRc3 = TestDriverBase.actionAbort(self);
+ return fRc1 and fRc2 and fRc3;
+
+
+ #
+ # Persistent variables.
+ #
+ ## @todo integrate into the base driver. Persistent accross scratch wipes?
+
+ def __persistentVarCalcName(self, sVar):
+ """Returns the (full) filename for the given persistent variable."""
+ assert re.match(r'^[a-zA-Z0-9_-]*$', sVar) is not None;
+ return os.path.join(self.sScratchPath, 'persistent-%s.var' % (sVar,));
+
+ def _persistentVarSet(self, sVar, sValue = ''):
+ """
+ Sets a persistent variable.
+
+ Returns True on success, False + reporter.error on failure.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ try:
+ with open(sFull, 'w') as oFile: # pylint: disable=unspecified-encoding
+ if sValue:
+ oFile.write(sValue.encode('utf-8'));
+ except:
+ reporter.errorXcpt('Error creating "%s"' % (sFull,));
+ return False;
+ return True;
+
+ def _persistentVarUnset(self, sVar):
+ """
+ Unsets a persistent variable.
+
+ Returns True on success, False + reporter.error on failure.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ if os.path.exists(sFull):
+ try:
+ os.unlink(sFull);
+ except:
+ reporter.errorXcpt('Error unlinking "%s"' % (sFull,));
+ return False;
+ return True;
+
+ def _persistentVarExists(self, sVar):
+ """
+ Checks if a persistent variable exists.
+
+ Returns true/false.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ return os.path.exists(self.__persistentVarCalcName(sVar));
+
+ def _persistentVarGet(self, sVar):
+ """
+ Gets the value of a persistent variable.
+
+ Returns variable value on success.
+ Returns None if the variable doesn't exist or if an
+ error (reported) occured.
+
+ May raise exception if the variable name is invalid or something
+ unexpected happens.
+ """
+ sFull = self.__persistentVarCalcName(sVar);
+ if not os.path.exists(sFull):
+ return None;
+ try:
+ with open(sFull, 'r') as oFile: # pylint: disable=unspecified-encoding
+ sValue = oFile.read().decode('utf-8');
+ except:
+ reporter.errorXcpt('Error creating "%s"' % (sFull,));
+ return None;
+ return sValue;
+
+
+ #
+ # Helpers.
+ #
+
+ def _killAllVBoxProcesses(self):
+ """
+ Kills all virtual box related processes we find in the system.
+ """
+ sHostOs = utils.getHostOs();
+ asDebuggers = [ 'cdb', 'windbg', ] if sHostOs == 'windows' else [ 'gdb', 'gdb-i386-apple-darwin', 'lldb' ];
+
+ for iIteration in range(22):
+ # Gather processes to kill.
+ aoTodo = [];
+ aoDebuggers = [];
+ for oProcess in utils.processListAll():
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is None:
+ continue;
+ sBase = sBase.lower();
+ if sBase in [ 'vboxsvc', 'vboxsds', 'virtualbox', 'virtualboxvm', 'vboxheadless', 'vboxmanage', 'vboxsdl',
+ 'vboxwebsrv', 'vboxautostart', 'vboxballoonctrl', 'vboxbfe', 'vboxextpackhelperapp', 'vboxnetdhcp',
+ 'vboxnetnat', 'vboxnetadpctl', 'vboxtestogl', 'vboxtunctl', 'vboxvmmpreload', 'vboxxpcomipcd', ]:
+ aoTodo.append(oProcess);
+ if sBase.startswith('virtualbox-') and sBase.endswith('-multiarch.exe'):
+ aoTodo.append(oProcess);
+ if sBase in asDebuggers:
+ aoDebuggers.append(oProcess);
+ if iIteration in [0, 21]:
+ reporter.log('Warning: debugger running: %s (%s %s)' % (oProcess.iPid, sBase, oProcess.asArgs));
+ if not aoTodo:
+ return True;
+
+ # Are any of the debugger processes hooked up to a VBox process?
+ if sHostOs == 'windows':
+ # On demand debugging windows: windbg -p <decimal-pid> -e <decimal-event> -g
+ for oDebugger in aoDebuggers:
+ for oProcess in aoTodo:
+ # The whole command line is asArgs[0] here. Fix if that changes.
+ if oDebugger.asArgs and oDebugger.asArgs[0].find('-p %s ' % (oProcess.iPid,)) >= 0:
+ aoTodo.append(oDebugger);
+ break;
+ else:
+ for oDebugger in aoDebuggers:
+ for oProcess in aoTodo:
+ # Simplistic approach: Just check for argument equaling our pid.
+ if oDebugger.asArgs and ('%s' % oProcess.iPid) in oDebugger.asArgs:
+ aoTodo.append(oDebugger);
+ break;
+
+ # Kill.
+ for oProcess in aoTodo:
+ reporter.log('Loop #%d - Killing %s (%s, uid=%s)'
+ % ( iIteration, oProcess.iPid, oProcess.sImage if oProcess.sName is None else oProcess.sName,
+ oProcess.iUid, ));
+ if not utils.processKill(oProcess.iPid) \
+ and sHostOs != 'windows' \
+ and utils.processExists(oProcess.iPid):
+ # Many of the vbox processes are initially set-uid-to-root and associated debuggers are running
+ # via sudo, so we might not be able to kill them unless we sudo and use /bin/kill.
+ try: utils.sudoProcessCall(['/bin/kill', '-9', '%s' % (oProcess.iPid,)]);
+ except: reporter.logXcpt();
+
+ # Check if they're all dead like they should be.
+ time.sleep(0.1);
+ for oProcess in aoTodo:
+ if utils.processExists(oProcess.iPid):
+ time.sleep(2);
+ break;
+
+ return False;
+
+ def _executeSync(self, asArgs, fMaySkip = False):
+ """
+ Executes a child process synchronously.
+
+ Returns True if the process executed successfully and returned 0.
+ Returns None if fMaySkip is true and the child exits with RTEXITCODE_SKIPPED.
+ Returns False for all other cases.
+ """
+ reporter.log('Executing: %s' % (asArgs, ));
+ reporter.flushall();
+ try:
+ iRc = utils.processCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return False;
+ reporter.log('Exit code: %s (%s)' % (iRc, asArgs));
+ if fMaySkip and iRc == rtexitcode.RTEXITCODE_SKIPPED:
+ return None;
+ return iRc == 0;
+
+ def _sudoExecuteSync(self, asArgs):
+ """
+ Executes a sudo child process synchronously.
+ Returns a tuple [True, 0] if the process executed successfully
+ and returned 0, otherwise [False, rc] is returned.
+ """
+ reporter.log('Executing [sudo]: %s' % (asArgs, ));
+ reporter.flushall();
+ iRc = 0;
+ try:
+ iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return (False, 0);
+ reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
+ return (iRc == 0, iRc);
+
+ def _findASanLibsForASanBuild(self):
+ """
+ Returns a list of (address) santizier related libraries to preload
+ when launching the sub driver.
+ Returns empty list for non-asan builds or on platforms where this isn't needed.
+ """
+ # Note! We include libasan.so.X in the VBoxAll tarball for asan builds, so we
+ # can use its presence both to detect an 'asan' build and to return it.
+ # Only the libasan.so.X library needs preloading at present.
+ if self.sHost in ('linux',):
+ sLibASan = self._findFile(r'libasan\.so\..*');
+ if sLibASan:
+ return [sLibASan,];
+ return [];
+
+ def _executeSubDriver(self, asActions, fMaySkip = True, fPreloadASan = True):
+ """
+ Execute the sub testdriver with the specified action.
+ """
+ asArgs = list(self._asSubDriver)
+ asArgs.append('--no-wipe-clean');
+ asArgs.extend(asActions);
+
+ asASanLibs = [];
+ if fPreloadASan:
+ asASanLibs = self._findASanLibsForASanBuild();
+ if asASanLibs:
+ os.environ['LD_PRELOAD'] = ':'.join(asASanLibs);
+ os.environ['LSAN_OPTIONS'] = 'detect_leaks=0'; # We don't want python leaks. vbox.py disables this.
+
+ # Because of https://github.com/google/sanitizers/issues/856 we must try use setarch to disable
+ # address space randomization.
+
+ reporter.log('LD_PRELOAD...')
+ if utils.getHostArch() == 'amd64':
+ sSetArch = utils.whichProgram('setarch');
+ reporter.log('sSetArch=%s' % (sSetArch,));
+ if sSetArch:
+ asArgs = [ sSetArch, 'x86_64', '-R', sys.executable ] + asArgs;
+ reporter.log('asArgs=%s' % (asArgs,));
+
+ rc = self._executeSync(asArgs, fMaySkip = fMaySkip);
+
+ del os.environ['LSAN_OPTIONS'];
+ del os.environ['LD_PRELOAD'];
+ return rc;
+
+ return self._executeSync(asArgs, fMaySkip = fMaySkip);
+
+ def _maybeUnpackArchive(self, sMaybeArchive, fNonFatal = False):
+ """
+ Attempts to unpack the given build file.
+ Updates _asBuildFiles.
+ Returns True/False. No exceptions.
+ """
+ def unpackFilter(sMember):
+ # type: (string) -> bool
+ """ Skips debug info. """
+ sLower = sMember.lower();
+ if sLower.endswith('.pdb'):
+ return False;
+ return True;
+
+ asMembers = utils.unpackFile(sMaybeArchive, self.sScratchPath, reporter.log,
+ reporter.log if fNonFatal else reporter.error,
+ fnFilter = unpackFilter);
+ if asMembers is None:
+ return False;
+ self._asBuildFiles.extend(asMembers);
+ return True;
+
+
+ def _installVBox(self):
+ """
+ Download / copy the build files into the scratch area and install them.
+ """
+ reporter.testStart('Installing VirtualBox');
+ reporter.log('CWD=%s' % (os.getcwd(),)); # curious
+
+ #
+ # Download the build files.
+ #
+ for i, sBuildUrl in enumerate(self._asBuildUrls):
+ if webutils.downloadFile(sBuildUrl, self._asBuildFiles[i], self.sBuildPath, reporter.log, reporter.log) is not True:
+ reporter.testDone(fSkipped = True);
+ return None; # Failed to get binaries, probably deleted. Skip the test run.
+
+ #
+ # Unpack anything we know what is and append it to the build files
+ # list. This allows us to use VBoxAll*.tar.gz files.
+ #
+ for sFile in list(self._asBuildFiles): # Note! We copy the list as _maybeUnpackArchive updates it.
+ if self._maybeUnpackArchive(sFile, fNonFatal = True) is not True:
+ reporter.testDone(fSkipped = True);
+ return None; # Failed to unpack. Probably local error, like busy
+ # DLLs on windows, no reason for failing the build.
+ self._fUnpackedBuildFiles = True;
+
+ #
+ # Go to system specific installation code.
+ #
+ sHost = utils.getHostOs()
+ if sHost == 'darwin': fRc = self._installVBoxOnDarwin();
+ elif sHost == 'linux': fRc = self._installVBoxOnLinux();
+ elif sHost == 'solaris': fRc = self._installVBoxOnSolaris();
+ elif sHost == 'win': fRc = self._installVBoxOnWindows();
+ else:
+ reporter.error('Unsupported host "%s".' % (sHost,));
+ if fRc is False:
+ reporter.testFailure('Installation error.');
+ elif fRc is not True:
+ reporter.log('Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!');
+
+ #
+ # Install the extension pack.
+ #
+ if fRc is True and self._fAutoInstallPuelExtPack:
+ fRc = self._installExtPack();
+ if fRc is False:
+ reporter.testFailure('Extension pack installation error.');
+
+ # Some debugging...
+ try:
+ cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
+ reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
+ except:
+ reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
+
+ reporter.testDone(fRc is None);
+ return fRc;
+
+ def _uninstallVBox(self, fIgnoreError = False):
+ """
+ Uninstall VirtualBox.
+ """
+ reporter.testStart('Uninstalling VirtualBox');
+
+ sHost = utils.getHostOs()
+ if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
+ elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
+ elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris(True);
+ elif sHost == 'win': fRc = self._uninstallVBoxOnWindows('uninstall');
+ else:
+ reporter.error('Unsupported host "%s".' % (sHost,));
+ if fRc is False and not fIgnoreError:
+ reporter.testFailure('Uninstallation failed.');
+
+ fRc2 = self._uninstallAllExtPacks();
+ if not fRc2 and fRc:
+ fRc = fRc2;
+
+ reporter.testDone(fSkipped = (fRc is None));
+ return fRc;
+
+ def _findFile(self, sRegExp, fMandatory = False):
+ """
+ Returns the first build file that matches the given regular expression
+ (basename only).
+
+ Returns None if no match was found, logging it as an error if
+ fMandatory is set.
+ """
+ oRegExp = re.compile(sRegExp);
+
+ reporter.log('_findFile: %s' % (sRegExp,));
+ for sFile in self._asBuildFiles:
+ if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
+ return sFile;
+
+ # If we didn't unpack the build files, search all the files in the scratch area:
+ if not self._fUnpackedBuildFiles:
+ for sDir, _, asFiles in os.walk(self.sScratchPath):
+ for sFile in asFiles:
+ #reporter.log('_findFile: considering %s' % (sFile,));
+ if oRegExp.match(sFile):
+ return os.path.join(sDir, sFile);
+
+ if fMandatory:
+ reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
+ return None;
+
+ def _waitForTestManagerConnectivity(self, cSecTimeout):
+ """
+ Check and wait for network connectivity to the test manager.
+
+ This is used with the windows installation and uninstallation since
+ these usually disrupts network connectivity when installing the filter
+ driver. If we proceed to quickly, we might finish the test at a time
+ when we cannot report to the test manager and thus end up with an
+ abandonded test error.
+ """
+ cSecElapsed = 0;
+ secStart = utils.timestampSecond();
+ while reporter.checkTestManagerConnection() is False:
+ cSecElapsed = utils.timestampSecond() - secStart;
+ if cSecElapsed >= cSecTimeout:
+ reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
+ return False;
+ time.sleep(2);
+
+ if cSecElapsed > 0:
+ reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
+ return True;
+
+
+ #
+ # Darwin (Mac OS X).
+ #
+
+ def _darwinDmgPath(self):
+ """ Returns the path to the DMG mount."""
+ return os.path.join(self.sScratchPath, 'DmgMountPoint');
+
+ def _darwinUnmountDmg(self, fIgnoreError):
+ """
+ Umount any DMG on at the default mount point.
+ """
+ sMountPath = self._darwinDmgPath();
+ if not os.path.exists(sMountPath):
+ return True;
+
+ # Unmount.
+ fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
+ if not fRc and not fIgnoreError:
+ # In case it's busy for some reason or another, just retry after a little delay.
+ for iTry in range(6):
+ time.sleep(5);
+ reporter.error('Retry #%s unmount DMT at %s' % (iTry + 1, sMountPath,));
+ fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
+ if fRc:
+ break;
+ if not fRc:
+ reporter.error('Failed to unmount DMG at %s' % (sMountPath,));
+
+ # Remove dir.
+ try:
+ os.rmdir(sMountPath);
+ except:
+ if not fIgnoreError:
+ reporter.errorXcpt('Failed to remove directory %s' % (sMountPath,));
+ return fRc;
+
+ def _darwinMountDmg(self, sDmg):
+ """
+ Mount the DMG at the default mount point.
+ """
+ self._darwinUnmountDmg(fIgnoreError = True)
+
+ sMountPath = self._darwinDmgPath();
+ if not os.path.exists(sMountPath):
+ try:
+ os.mkdir(sMountPath, 0o755);
+ except:
+ reporter.logXcpt();
+ return False;
+
+ return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
+
+ def _generateWithoutKextsChoicesXmlOnDarwin(self):
+ """
+ Generates the choices XML when kernel drivers are disabled.
+ None is returned on failure.
+ """
+ sPath = os.path.join(self.sScratchPath, 'DarwinChoices.xml');
+ oFile = utils.openNoInherit(sPath, 'wt');
+ oFile.write('<?xml version="1.0" encoding="UTF-8"?>\n'
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
+ '<plist version="1.0">\n'
+ '<array>\n'
+ ' <dict>\n'
+ ' <key>attributeSetting</key>\n'
+ ' <integer>0</integer>\n'
+ ' <key>choiceAttribute</key>\n'
+ ' <string>selected</string>\n'
+ ' <key>choiceIdentifier</key>\n'
+ ' <string>choiceVBoxKEXTs</string>\n'
+ ' </dict>\n'
+ '</array>\n'
+ '</plist>\n');
+ oFile.close();
+ return sPath;
+
+ def _installVBoxOnDarwin(self):
+ """ Installs VBox on Mac OS X."""
+
+ # TEMPORARY HACK - START
+ # Don't install the kernel drivers on the testboxes with BigSur and later
+ # Needs a more generic approach but that one needs more effort.
+ sHostName = socket.getfqdn();
+ if sHostName.startswith('testboxmac10') \
+ or sHostName.startswith('testboxmac11'):
+ self._fKernelDrivers = False;
+ # TEMPORARY HACK - END
+
+ sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
+ if sDmg is None:
+ return False;
+
+ # Mount the DMG.
+ fRc = self._darwinMountDmg(sDmg);
+ if fRc is not True:
+ return False;
+
+ # Uninstall any previous vbox version first.
+ sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
+ fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
+ if fRc is True:
+
+ # Install the package.
+ sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
+ if self._fKernelDrivers:
+ fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
+ else:
+ sChoicesXml = self._generateWithoutKextsChoicesXmlOnDarwin();
+ if sChoicesXml is not None:
+ fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, \
+ '-applyChoiceChangesXML', sChoicesXml, '-target', '/']);
+ else:
+ fRc = False;
+
+ # Unmount the DMG and we're done.
+ if not self._darwinUnmountDmg(fIgnoreError = False):
+ fRc = False;
+ return fRc;
+
+ def _uninstallVBoxOnDarwin(self):
+ """ Uninstalls VBox on Mac OS X."""
+
+ # Is VirtualBox installed? If not, don't try uninstall it.
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ # Find the dmg.
+ sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
+ if sDmg is None:
+ return False;
+ if not os.path.exists(sDmg):
+ return True;
+
+ # Mount the DMG.
+ fRc = self._darwinMountDmg(sDmg);
+ if fRc is not True:
+ return False;
+
+ # Execute the uninstaller.
+ sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
+ fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
+
+ # Unmount the DMG and we're done.
+ if not self._darwinUnmountDmg(fIgnoreError = False):
+ fRc = False;
+ return fRc;
+
+ #
+ # GNU/Linux
+ #
+
+ def _installVBoxOnLinux(self):
+ """ Installs VBox on Linux."""
+ sRun = self._findFile('^VirtualBox-.*\\.run$');
+ if sRun is None:
+ return False;
+ utils.chmodPlusX(sRun);
+
+ # Install the new one.
+ fRc, _ = self._sudoExecuteSync([sRun,]);
+ return fRc;
+
+ def _uninstallVBoxOnLinux(self):
+ """ Uninstalls VBox on Linux."""
+
+ # Is VirtualBox installed? If not, don't try uninstall it.
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ # Find the .run file and use it.
+ sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
+ if sRun is not None:
+ utils.chmodPlusX(sRun);
+ fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
+ return fRc;
+
+ # Try the installed uninstaller.
+ for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
+ if os.path.isfile(sUninstaller):
+ reporter.log('Invoking "%s"...' % (sUninstaller,));
+ fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
+ return fRc;
+
+ reporter.log('Did not find any VirtualBox install to uninstall.');
+ return True;
+
+
+ #
+ # Solaris
+ #
+
+ def _generateAutoResponseOnSolaris(self):
+ """
+ Generates an autoresponse file on solaris, returning the name.
+ None is return on failure.
+ """
+ sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
+ oFile = utils.openNoInherit(sPath, 'wt');
+ oFile.write('basedir=default\n'
+ 'runlevel=nocheck\n'
+ 'conflict=quit\n'
+ 'setuid=nocheck\n'
+ 'action=nocheck\n'
+ 'partial=quit\n'
+ 'instance=unique\n'
+ 'idepend=quit\n'
+ 'rdepend=quit\n'
+ 'space=quit\n'
+ 'mail=\n');
+ oFile.close();
+ return sPath;
+
+ def _installVBoxOnSolaris(self):
+ """ Installs VBox on Solaris."""
+ sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
+ if sPkg is None:
+ sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
+ if sTar is not None:
+ if self._maybeUnpackArchive(sTar) is not True:
+ return False;
+ sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
+ sRsp = self._findFile('^autoresponse$', fMandatory = True);
+ if sPkg is None or sRsp is None:
+ return False;
+
+ # Uninstall first (ignore result).
+ self._uninstallVBoxOnSolaris(False);
+
+ # Install the new one.
+ fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
+ return fRc;
+
+ def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
+ """ Uninstalls VBox on Solaris."""
+ reporter.flushall();
+ if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
+ return True;
+ sRsp = self._generateAutoResponseOnSolaris();
+ fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
+
+ #
+ # Restart the svc.configd as it has a tendency to clog up with time and
+ # become unresponsive. It will handle SIGHUP by exiting the sigwait()
+ # look in the main function and shut down the service nicely (backend_fini).
+ # The restarter will then start a new instance of it.
+ #
+ if fRestartSvcConfigD:
+ time.sleep(1); # Give it a chance to flush pkgrm stuff.
+ self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
+ time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
+
+ return fRc;
+
+ #
+ # Windows
+ #
+
+ ## VBox windows services we can query the status of.
+ kasWindowsServices = [ 'vboxsup', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
+
+ def _installVBoxOnWindows(self):
+ """ Installs VBox on Windows."""
+ sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
+ if sExe is None:
+ return False;
+
+ # TEMPORARY HACK - START
+ # It seems that running the NDIS cleanup script upon uninstallation is not
+ # a good idea, so let's run it before installing VirtualBox.
+ #sHostName = socket.getfqdn();
+ #if not sHostName.startswith('testboxwin3') \
+ # and not sHostName.startswith('testboxharp2') \
+ # and not sHostName.startswith('wei01-b6ka-3') \
+ # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
+ # reporter.log('Peforming extra NDIS cleanup...');
+ # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
+ # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
+ # if not fRc2:
+ # reporter.log('set-executionpolicy failed.');
+ # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
+ # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
+ # if not fRc2:
+ # reporter.log('NDIS cleanup failed.');
+ # TEMPORARY HACK - END
+
+ # Uninstall any previous vbox version first.
+ fRc = self._uninstallVBoxOnWindows('install');
+ if fRc is not True:
+ return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
+
+ # Install the MS Visual Studio Redistributable, if requested. (VBox 7.0+ needs this installed once.)
+ if self._fInstallMsCrt:
+ reporter.log('Installing MS Visual Studio Redistributable (untested code)...');
+ ## @todo Test this.
+ ## @todo We could cache this on the testrsrc share.
+ sName = "vc_redist.x64.exe"
+ sUrl = "https://aka.ms/vs/17/release/" + sName # Permalink, according to MS.
+ sExe = os.path.join(self.sBuildPath, sName);
+ if webutils.downloadFile(sUrl, sExe, None, reporter.log, reporter.log):
+ asArgs = [ sExe, '/Q' ];
+ fRc2, iRc = self._sudoExecuteSync(asArgs);
+ if fRc2 is False:
+ return reporter.error('Installing MS Visual Studio Redistributable failed, exit code: %s' % (iRc,));
+ reporter.log('Installing MS Visual Studio Redistributable done');
+ else:
+ return False;
+
+ # We need the help text to detect supported options below.
+ reporter.log('Executing: %s' % ([sExe, '--silent', '--help'], ));
+ reporter.flushall();
+ (iExitCode, sHelp, _) = utils.processOutputUnchecked([sExe, '--silent', '--help'], fIgnoreEncoding = True);
+ reporter.log('Exit code: %d, %u chars of help text' % (iExitCode, len(sHelp),));
+
+ # Gather installer arguments.
+ asArgs = [sExe, '-vvvv', '--silent', '--logging'];
+ asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
+ sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
+ if sVBoxInstallPath is not None:
+ asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
+
+ if sHelp.find("--msi-log-file") >= 0:
+ sLogFile = os.path.join(self.sScratchPath, 'VBoxInstallLog.txt'); # Specify location to prevent a random one.
+ asArgs.extend(['--msi-log-file', sLogFile]);
+ else:
+ sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt'); # Hardcoded TMP location.
+
+ if self._fWinForcedInstallTimestampCA and sHelp.find("--force-install-timestamp-ca") >= 0:
+ asArgs.extend(['--force-install-timestamp-ca']);
+
+ # Install it.
+ fRc2, iRc = self._sudoExecuteSync(asArgs);
+ if fRc2 is False:
+ if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
+ reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
+ else:
+ reporter.error('Installer failed, exit code: %s' % (iRc,));
+ fRc = False;
+
+ # Add the installer log if present and wait for the network connection to be restore after the filter driver upset.
+ if os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
+ self._waitForTestManagerConnectivity(30);
+
+ return fRc;
+
+ def _isProcessPresent(self, sName):
+ """ Checks whether the named process is present or not. """
+ for oProcess in utils.processListAll():
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName:
+ return True;
+ return False;
+
+ def _killProcessesByName(self, sName, sDesc, fChildren = False):
+ """ Kills the named process, optionally including children. """
+ cKilled = 0;
+ aoProcesses = utils.processListAll();
+ for oProcess in aoProcesses:
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName:
+ reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
+ utils.processKill(oProcess.iPid);
+ cKilled += 1;
+
+ if fChildren:
+ for oChild in aoProcesses:
+ if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
+ reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
+ utils.processKill(oChild.iPid);
+ cKilled += 1;
+ return cKilled;
+
+ def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
+ """
+ Terminates the named process using taskkill.exe, if any of its args
+ contains the passed string.
+ """
+ cKilled = 0;
+ aoProcesses = utils.processListAll();
+ for oProcess in aoProcesses:
+ sBase = oProcess.getBaseImageNameNoExeSuff();
+ if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
+
+ reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
+ self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
+ cKilled += 1;
+ return cKilled;
+
+ def _uninstallVBoxOnWindows(self, sMode):
+ """
+ Uninstalls VBox on Windows, all installations we find to be on the safe side...
+ """
+ assert sMode in ['install', 'uninstall',];
+
+ import win32com.client; # pylint: disable=import-error
+ win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
+ oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
+ resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
+
+ # Search installed products for VirtualBox.
+ asProdCodes = [];
+ for sProdCode in oInstaller.Products:
+ try:
+ sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
+ except:
+ reporter.logXcpt();
+ continue;
+ #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
+ if sProdName.startswith('Oracle VM VirtualBox') \
+ or sProdName.startswith('Sun VirtualBox'):
+ asProdCodes.append([sProdCode, sProdName]);
+
+ # Before we start uninstalling anything, just ruthlessly kill any cdb,
+ # msiexec, drvinst and some rundll process we might find hanging around.
+ if self._isProcessPresent('rundll32'):
+ cTimes = 0;
+ while cTimes < 3:
+ cTimes += 1;
+ cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
+ 'MSI driver installation');
+ if cKilled <= 0:
+ break;
+ time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
+
+ if self._isProcessPresent('drvinst'):
+ time.sleep(15); # In the hope that it goes away.
+ cTimes = 0;
+ while cTimes < 4:
+ cTimes += 1;
+ cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
+ if cKilled <= 0:
+ break;
+ time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
+
+ if self._isProcessPresent('msiexec'):
+ cTimes = 0;
+ while cTimes < 3:
+ reporter.log('found running msiexec process, waiting a bit...');
+ time.sleep(20) # In the hope that it goes away.
+ if not self._isProcessPresent('msiexec'):
+ break;
+ cTimes += 1;
+ ## @todo this could also be the msiexec system service, try to detect this case!
+ if cTimes >= 6:
+ cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
+ if cKilled > 0:
+ time.sleep(16); # fudge.
+
+ # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
+ # the scratch directory. No idea why.
+ if self._isProcessPresent('cdb'):
+ cTimes = 0;
+ while cTimes < 3:
+ cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
+ if cKilled <= 0:
+ break;
+ time.sleep(2); # fudge.
+
+ # Do the uninstalling.
+ fRc = True;
+ sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
+ for sProdCode, sProdName in asProdCodes:
+ reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
+ fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
+ '/L*v', '%s' % (sLogFile), ]);
+ if fRc2 is False:
+ if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
+ reporter.error('Uninstaller required a reboot to complete uninstallation');
+ else:
+ reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
+ fRc = False;
+
+ self._waitForTestManagerConnectivity(30);
+
+ # Upload the log on failure. Do it early if the extra cleanups below causes trouble.
+ if fRc is False and os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
+ sLogFile = None;
+
+ # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
+ fHadLeftovers = False;
+ asLeftovers = [];
+ for sService in reversed(self.kasWindowsServices):
+ cTries = 0;
+ while True:
+ fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
+ if not fRc2:
+ break;
+ fHadLeftovers = True;
+
+ cTries += 1;
+ if cTries > 3:
+ asLeftovers.append(sService,);
+ break;
+
+ # Get the status output.
+ try:
+ sOutput = utils.sudoProcessOutputChecked(['sc.exe', 'query', sService]);
+ except:
+ reporter.logXcpt();
+ else:
+ if re.search(r'STATE\s+:\s*1\s*STOPPED', sOutput) is None:
+ reporter.log('Trying to stop %s...' % (sService,));
+ fRc2, _ = self._sudoExecuteSync(['sc.exe', 'stop', sService]);
+ time.sleep(1); # fudge
+
+ reporter.log('Trying to delete %s...' % (sService,));
+ self._sudoExecuteSync(['sc.exe', 'delete', sService]);
+
+ time.sleep(1); # fudge
+
+ if asLeftovers:
+ reporter.log('Warning! Leftover VBox drivers: %s' % (', '.join(asLeftovers),));
+ fRc = False;
+
+ if fHadLeftovers:
+ self._waitForTestManagerConnectivity(30);
+
+ # Upload the log if we have any leftovers and didn't upload it already.
+ if sLogFile is not None and (fRc is False or fHadLeftovers) and os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
+
+ return fRc;
+
+
+ #
+ # Extension pack.
+ #
+
+ def _getVBoxInstallPath(self, fFailIfNotFound):
+ """ Returns the default VBox installation path. """
+ sHost = utils.getHostOs();
+ if sHost == 'win':
+ sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
+ asLocs = [
+ os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
+ os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
+ os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
+ ];
+ elif sHost in ('linux', 'solaris',):
+ asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
+ elif sHost == 'darwin':
+ asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
+ else:
+ asLocs = [ '/opt/VirtualBox' ];
+ if 'VBOX_INSTALL_PATH' in os.environ:
+ asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
+
+ for sLoc in asLocs:
+ if os.path.isdir(sLoc):
+ return sLoc;
+ if fFailIfNotFound:
+ reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
+ else:
+ reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
+ return None;
+
+ def _installExtPack(self):
+ """ Installs the extension pack. """
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
+ if sVBox is None:
+ return False;
+ sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
+
+ if self._uninstallAllExtPacks() is not True:
+ return False;
+
+ sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
+ if sExtPack is None:
+ sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
+ if sExtPack is None:
+ return True;
+
+ sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
+ reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
+ fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
+ '--extract',
+ '--verbose',
+ '--gzip',
+ '--file', sExtPack,
+ '--directory', sDstDir,
+ '--file-mode-and-mask', '0644',
+ '--file-mode-or-mask', '0644',
+ '--dir-mode-and-mask', '0755',
+ '--dir-mode-or-mask', '0755',
+ '--owner', '0',
+ '--group', '0',
+ ]);
+ return fRc;
+
+ def _uninstallAllExtPacks(self):
+ """ Uninstalls all extension packs. """
+ sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
+ if sVBox is None:
+ return True;
+
+ sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
+ if not os.path.exists(sExtPackDir):
+ return True;
+
+ fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(VBoxInstallerTestDriver().main(sys.argv));
diff --git a/src/VBox/ValidationKit/testdriver/vboxtestfileset.py b/src/VBox/ValidationKit/testdriver/vboxtestfileset.py
new file mode 100755
index 00000000..e5b88664
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxtestfileset.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxtestfileset.py $
+# pylint: disable=too-many-lines
+
+"""
+Test File Set
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver import testfileset;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestFileSet(testfileset.TestFileSet):
+ """
+ A generated set of files and directories for uploading to a VM.
+
+ The file and directory names are compatible with the host, so it is
+ possible to copy them to the host without changing any names.
+
+ Uploaded as a tarball and expanded via TXS (if new enough) or uploaded vts_tar
+ utility from the validation kit.
+ """
+
+ def __init__(self, oTestVm, sBasePath, sSubDir, # pylint: disable=too-many-arguments
+ oRngFileSizes = xrange(0, 16384),
+ oRngManyFiles = xrange(128, 512),
+ oRngTreeFiles = xrange(128, 384),
+ oRngTreeDepth = xrange(92, 256),
+ oRngTreeDirs = xrange(2, 16),
+ cchMaxPath = 230,
+ cchMaxName = 230,
+ asCompatibleWith = None,
+ uSeed = None):
+
+ asCompOses = [oTestVm.getGuestOs(), ];
+ sHostOs = utils.getHostOs();
+ if sHostOs not in asCompOses:
+ asCompOses.append(sHostOs);
+
+ testfileset.TestFileSet.__init__(self,
+ fDosStyle = oTestVm.isWindows() or oTestVm.isOS2(),
+ asCompatibleWith = asCompOses,
+ sBasePath = sBasePath,
+ sSubDir = sSubDir,
+ oRngFileSizes = oRngFileSizes,
+ oRngManyFiles = oRngManyFiles,
+ oRngTreeFiles = oRngTreeFiles,
+ oRngTreeDepth = oRngTreeDepth,
+ oRngTreeDirs = oRngTreeDirs,
+ cchMaxPath = cchMaxPath,
+ cchMaxName = cchMaxName,
+ uSeed = uSeed);
+ self.oTestVm = oTestVm;
+
+ def __uploadFallback(self, oTxsSession, sTarFileGst, oTstDrv):
+ """
+ Fallback upload method.
+ """
+ sVtsTarExe = 'vts_tar' + self.oTestVm.getGuestExeSuff();
+ sVtsTarHst = os.path.join(oTstDrv.sVBoxValidationKit, self.oTestVm.getGuestOs(),
+ self.oTestVm.getGuestArch(), sVtsTarExe);
+ sVtsTarGst = self.oTestVm.pathJoin(self.sBasePath, sVtsTarExe);
+
+ if oTxsSession.syncUploadFile(sVtsTarHst, sVtsTarGst) is not True:
+ return reporter.error('Failed to upload "%s" to the guest as "%s"!' % (sVtsTarHst, sVtsTarGst,));
+
+ fRc = oTxsSession.syncExec(sVtsTarGst, [sVtsTarGst, '-xzf', sTarFileGst, '-C', self.sBasePath,], fWithTestPipe = False);
+ if fRc is not True:
+ return reporter.error('vts_tar failed!');
+ return True;
+
+ def upload(self, oTxsSession, oTstDrv):
+ """
+ Uploads the files into the guest via the given TXS session.
+
+ Returns True / False.
+ """
+
+ #
+ # Create a tarball.
+ #
+ sTarFileHst = os.path.join(oTstDrv.sScratchPath, 'tdAddGuestCtrl-1-Stuff.tar.gz');
+ sTarFileGst = self.oTestVm.pathJoin(self.sBasePath, 'tdAddGuestCtrl-1-Stuff.tar.gz');
+ if self.createTarball(sTarFileHst) is not True:
+ return False;
+
+ #
+ # Upload it.
+ #
+ reporter.log('Uploading tarball "%s" to the guest as "%s"...' % (sTarFileHst, sTarFileGst));
+ if oTxsSession.syncUploadFile(sTarFileHst, sTarFileGst) is not True:
+ return reporter.error('Failed upload tarball "%s" as "%s"!' % (sTarFileHst, sTarFileGst,));
+
+ #
+ # Try unpack it.
+ #
+ reporter.log('Unpacking "%s" into "%s"...' % (sTarFileGst, self.sBasePath));
+ if oTxsSession.syncUnpackFile(sTarFileGst, self.sBasePath, fIgnoreErrors = True) is not True:
+ reporter.log('Failed to expand tarball "%s" into "%s", falling back on individual directory and file creation...'
+ % (sTarFileGst, self.sBasePath,));
+ if self.__uploadFallback(oTxsSession, sTarFileGst, oTstDrv) is not True:
+ return False;
+ reporter.log('Successfully placed test files and directories in the VM.');
+ return True;
+
diff --git a/src/VBox/ValidationKit/testdriver/vboxtestvms.py b/src/VBox/ValidationKit/testdriver/vboxtestvms.py
new file mode 100755
index 00000000..fbadf7b5
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxtestvms.py
@@ -0,0 +1,2105 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxtestvms.py $
+
+"""
+VirtualBox Test VMs
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import copy;
+import os;
+import re;
+import random;
+import socket;
+import string;
+import uuid;
+
+# Validation Kit imports.
+from common import pathutils;
+from common import utils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxcon;
+
+
+# All virtualization modes.
+g_asVirtModes = ['hwvirt', 'hwvirt-np', 'raw',];
+# All virtualization modes except for raw-mode.
+g_asVirtModesNoRaw = ['hwvirt', 'hwvirt-np',];
+# Dictionary mapping the virtualization mode mnemonics to a little less cryptic
+# strings used in test descriptions.
+g_dsVirtModeDescs = {
+ 'raw' : 'Raw-mode',
+ 'hwvirt' : 'HwVirt',
+ 'hwvirt-np' : 'NestedPaging'
+};
+
+## @name VM grouping flags
+## @{
+g_kfGrpSmoke = 0x0001; ##< Smoke test VM.
+g_kfGrpStandard = 0x0002; ##< Standard test VM.
+g_kfGrpStdSmoke = g_kfGrpSmoke | g_kfGrpStandard; ##< shorthand.
+g_kfGrpWithGAs = 0x0004; ##< The VM has guest additions installed.
+g_kfGrpNoTxs = 0x0008; ##< The VM lacks test execution service.
+g_kfGrpAncient = 0x1000; ##< Ancient OS.
+g_kfGrpExotic = 0x2000; ##< Exotic OS.
+## @}
+
+
+## @name Flags.
+## @{
+g_k32 = 32; # pylint: disable=invalid-name
+g_k64 = 64; # pylint: disable=invalid-name
+g_k32_64 = 96; # pylint: disable=invalid-name
+g_kiArchMask = 96;
+g_kiNoRaw = 128; ##< No raw mode.
+## @}
+
+# Array indexes.
+g_iGuestOsType = 0;
+g_iKind = 1;
+g_iFlags = 2;
+g_iMinCpu = 3;
+g_iMaxCpu = 4;
+g_iRegEx = 5;
+
+# Table translating from VM name core to a more detailed guest info.
+# pylint: disable=line-too-long
+## @todo what's the difference between the first two columns again?
+g_aaNameToDetails = \
+[
+ [ 'WindowsNT3x', 'WindowsNT3x', g_k32, 1, 32, ['nt3', 'nt3[0-9]*']], # max cpus??
+ [ 'WindowsNT4', 'WindowsNT4', g_k32, 1, 32, ['nt4', 'nt4sp[0-9]']], # max cpus??
+ [ 'Windows2000', 'Windows2000', g_k32, 1, 32, ['w2k', 'w2ksp[0-9]', 'win2k', 'win2ksp[0-9]']], # max cpus??
+ [ 'WindowsXP', 'WindowsXP', g_k32, 1, 32, ['xp', 'xpsp[0-9]']],
+ [ 'WindowsXP_64', 'WindowsXP_64', g_k64, 1, 32, ['xp64', 'xp64sp[0-9]']],
+ [ 'Windows2003', 'Windows2003', g_k32, 1, 32, ['w2k3', 'w2k3sp[0-9]', 'win2k3', 'win2k3sp[0-9]']],
+ [ 'WindowsVista', 'WindowsVista', g_k32, 1, 32, ['vista', 'vistasp[0-9]']],
+ [ 'WindowsVista_64','WindowsVista_64', g_k64, 1, 64, ['vista-64', 'vistasp[0-9]-64',]], # max cpus/cores??
+ [ 'Windows2008', 'Windows2008', g_k32, 1, 64, ['w2k8', 'w2k8sp[0-9]', 'win2k8', 'win2k8sp[0-9]']], # max cpus/cores??
+ [ 'Windows2008_64', 'Windows2008_64', g_k64, 1, 64, ['w2k8r2', 'w2k8r2sp[0-9]', 'win2k8r2', 'win2k8r2sp[0-9]']], # max cpus/cores??
+ [ 'Windows7', 'Windows7', g_k32, 1, 32, ['w7', 'w7sp[0-9]', 'win7',]], # max cpus/cores??
+ [ 'Windows7_64', 'Windows7_64', g_k64, 1, 64, ['w7-64', 'w7sp[0-9]-64', 'win7-64',]], # max cpus/cores??
+ [ 'Windows2012', 'Windows2012', g_k64, 1, 64, ['w2k12', 'w2k12sp[0-9]', 'win2k12', 'win2k12sp[0-9]',]], # max cpus/cores??
+ [ 'Windows8', 'Windows8', g_k32 | g_kiNoRaw, 1, 32, ['w8', 'w8sp[0-9]', 'win8',]], # max cpus/cores??
+ [ 'Windows8_64', 'Windows8_64', g_k64, 1, 64, ['w8-64', 'w8sp[0-9]-64', 'win8-64',]], # max cpus/cores??
+ [ 'Windows81', 'Windows81', g_k32 | g_kiNoRaw, 1, 32, ['w81', 'w81sp[0-9]', 'win81',]], # max cpus/cores??
+ [ 'Windows81_64', 'Windows81_64', g_k64, 1, 64, ['w81-64', 'w81sp[0-9]-64', 'win81-64',]], # max cpus/cores??
+ [ 'Windows10', 'Windows10', g_k32 | g_kiNoRaw, 1, 32, ['w10', 'w10sp[0-9]', 'win10',]], # max cpus/cores??
+ [ 'Windows10_64', 'Windows10_64', g_k64, 1, 64, ['w10-64', 'w10sp[0-9]-64', 'win10-64',]], # max cpus/cores??
+ [ 'Windows2016', 'Windows2016', g_k64, 1, 64, ['w2k16', 'w2k16sp[0-9]', 'win2k16', 'win2k16sp[0-9]',]], # max cpus/cores??
+ [ 'Windows2019', 'Windows2019', g_k64, 1, 64, ['w2k19', 'w2k19sp[0-9]', 'win2k19', 'win2k19sp[0-9]',]], # max cpus/cores??
+ [ 'Windows2022', 'Windows2022', g_k64, 1, 64, ['w2k22', 'w2k22sp[0-9]', 'win2k22', 'win2k22sp[0-9]',]], # max cpus/cores??
+ [ 'Windows11', 'Windows11', g_k64, 1, 64, ['w11', 'w11-64', 'w11sp[0-9]-64', 'win11', 'win11-64',]], # max cpus/cores??
+ [ 'Linux', 'Debian', g_k32, 1, 256, ['deb[0-9]*', 'debian[0-9]*', ]],
+ [ 'Linux_64', 'Debian_64', g_k64, 1, 256, ['deb[0-9]*-64', 'debian[0-9]*-64', ]],
+ [ 'Linux', 'RedHat', g_k32, 1, 256, ['rhel', 'rhel[0-9]', 'rhel[0-9]u[0-9]']],
+ [ 'Linux', 'Fedora', g_k32, 1, 256, ['fedora', 'fedora[0-9]*', ]],
+ [ 'Linux_64', 'Fedora_64', g_k64, 1, 256, ['fedora-64', 'fedora[0-9]*-64', ]],
+ [ 'Linux', 'Oracle', g_k32, 1, 256, ['ols[0-9]*', 'oel[0-9]*', ]],
+ [ 'Linux_64', 'Oracle_64', g_k64, 1, 256, ['ols[0-9]*-64', 'oel[0-9]*-64', ]],
+ [ 'Linux', 'OpenSUSE', g_k32, 1, 256, ['opensuse[0-9]*', 'suse[0-9]*', ]],
+ [ 'Linux_64', 'OpenSUSE_64', g_k64, 1, 256, ['opensuse[0-9]*-64', 'suse[0-9]*-64', ]],
+ [ 'Linux', 'Ubuntu', g_k32, 1, 256, ['ubuntu[0-9]*', ]],
+ [ 'Linux_64', 'Ubuntu_64', g_k64, 1, 256, ['ubuntu[0-9]*-64', ]],
+ [ 'Linux', 'ArchLinux', g_k32, 1, 256, ['arch[0-9]*', ]],
+ [ 'Linux_64', 'ArchLinux_64', g_k64, 1, 256, ['arch[0-9]*-64', ]],
+ [ 'OS2Warp45', 'OS2Warp45', g_k32 | g_kiNoRaw, 1, 1, ['os2.*', 'acp.*','mcp.*', ]], # smp does busy spinning and unattended installer only does UNI at the momen.
+ [ 'Solaris', 'Solaris', g_k32, 1, 256, ['sol10', 'sol10u[0-9]']],
+ [ 'Solaris_64', 'Solaris_64', g_k64, 1, 256, ['sol10-64', 'sol10u-64[0-9]']],
+ [ 'Solaris_64', 'Solaris11_64', g_k64, 1, 256, ['sol11u1']],
+ [ 'BSD', 'FreeBSD_64', g_k32_64, 1, 1, ['bs-.*']], # boot sectors, wanted 64-bit type.
+ [ 'DOS', 'DOS', g_k32, 1, 1, ['bs-.*']],
+];
+
+
+## @name Guest OS type string constants.
+## @{
+g_ksGuestOsTypeDarwin = 'darwin';
+g_ksGuestOsTypeDOS = 'dos';
+g_ksGuestOsTypeFreeBSD = 'freebsd';
+g_ksGuestOsTypeLinux = 'linux';
+g_ksGuestOsTypeOS2 = 'os2';
+g_ksGuestOsTypeSolaris = 'solaris';
+g_ksGuestOsTypeWindows = 'windows';
+## @}
+
+## @name String constants for paravirtualization providers.
+## @{
+g_ksParavirtProviderNone = 'none';
+g_ksParavirtProviderDefault = 'default';
+g_ksParavirtProviderLegacy = 'legacy';
+g_ksParavirtProviderMinimal = 'minimal';
+g_ksParavirtProviderHyperV = 'hyperv';
+g_ksParavirtProviderKVM = 'kvm';
+## @}
+
+## Valid paravirtualization providers.
+g_kasParavirtProviders = ( g_ksParavirtProviderNone, g_ksParavirtProviderDefault, g_ksParavirtProviderLegacy,
+ g_ksParavirtProviderMinimal, g_ksParavirtProviderHyperV, g_ksParavirtProviderKVM );
+
+# Mapping for support of paravirtualisation providers per guest OS.
+#g_kdaParavirtProvidersSupported = {
+# g_ksGuestOsTypeDarwin : ( g_ksParavirtProviderMinimal, ),
+# g_ksGuestOsTypeFreeBSD : ( g_ksParavirtProviderNone, g_ksParavirtProviderMinimal, ),
+# g_ksGuestOsTypeLinux : ( g_ksParavirtProviderNone, g_ksParavirtProviderMinimal, g_ksParavirtProviderHyperV, g_ksParavirtProviderKVM),
+# g_ksGuestOsTypeOS2 : ( g_ksParavirtProviderNone, ),
+# g_ksGuestOsTypeSolaris : ( g_ksParavirtProviderNone, ),
+# g_ksGuestOsTypeWindows : ( g_ksParavirtProviderNone, g_ksParavirtProviderMinimal, g_ksParavirtProviderHyperV, )
+#}
+# Temporary tweak:
+# since for the most guests g_ksParavirtProviderNone is almost the same as g_ksParavirtProviderMinimal,
+# g_ksParavirtProviderMinimal is removed from the list in order to get maximum number of unique choices
+# during independent test runs when paravirt provider is taken randomly.
+g_kdaParavirtProvidersSupported = {
+ g_ksGuestOsTypeDarwin : ( g_ksParavirtProviderMinimal, ),
+ g_ksGuestOsTypeDOS : ( g_ksParavirtProviderNone, ),
+ g_ksGuestOsTypeFreeBSD : ( g_ksParavirtProviderNone, ),
+ g_ksGuestOsTypeLinux : ( g_ksParavirtProviderNone, g_ksParavirtProviderHyperV, g_ksParavirtProviderKVM),
+ g_ksGuestOsTypeOS2 : ( g_ksParavirtProviderNone, ),
+ g_ksGuestOsTypeSolaris : ( g_ksParavirtProviderNone, ),
+ g_ksGuestOsTypeWindows : ( g_ksParavirtProviderNone, g_ksParavirtProviderHyperV, )
+}
+
+
+# pylint: enable=line-too-long
+
+def _intersects(asSet1, asSet2):
+ """
+ Checks if any of the strings in set 1 matches any of the regular
+ expressions in set 2.
+ """
+ for sStr1 in asSet1:
+ for sRx2 in asSet2:
+ if re.match(sStr1, sRx2 + '$'):
+ return True;
+ return False;
+
+
+
+class BaseTestVm(object):
+ """
+ Base class for Test VMs.
+ """
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ sVmName, # type: str
+ fGrouping = 0, # type: int
+ oSet = None, # type: TestVmSet
+ sKind = None, # type: str
+ acCpusSup = None, # type: List[int]
+ asVirtModesSup = None, # type: List[str]
+ asParavirtModesSup = None, # type: List[str]
+ fRandomPvPModeCrap = False, # type: bool
+ fVmmDevTestingPart = None, # type: bool
+ fVmmDevTestingMmio = False, # type: bool
+ iGroup = 1, # type: int
+ ):
+ self.oSet = oSet # type: TestVmSet
+ self.sVmName = sVmName;
+ self.iGroup = iGroup; # Startup group (for MAC address uniqueness and non-NAT networking).
+ self.fGrouping = fGrouping;
+ self.sKind = sKind; # API Guest OS type.
+ self.acCpusSup = acCpusSup;
+ self.asVirtModesSup = asVirtModesSup;
+ self.asParavirtModesSup = asParavirtModesSup;
+ self.asParavirtModesSupOrg = asParavirtModesSup; # HACK ALERT! Trick to make the 'effing random mess not get in the
+ # way of actively selecting virtualization modes.
+
+ self.fSkip = False; # All VMs are included in the configured set by default.
+ self.fSnapshotRestoreCurrent = False; # Whether to restore execution on the current snapshot.
+
+ # VMMDev and serial (TXS++) settings:
+ self.fVmmDevTestingPart = fVmmDevTestingPart;
+ self.fVmmDevTestingMmio = fVmmDevTestingMmio;
+ self.fCom1RawFile = False;
+
+ # Cached stuff (use getters):
+ self.__sCom1RawFile = None; # Set by createVmInner and getReconfiguredVm if fCom1RawFile is set.
+ self.__tHddCtrlPortDev = (None, None, None); # The HDD controller, port and device.
+ self.__tDvdCtrlPortDev = (None, None, None); # The DVD controller, port and device.
+ self.__cbHdd = -1; # The recommended HDD size.
+
+ # Derived stuff:
+ self.aInfo = None;
+ self.sGuestOsType = None; # ksGuestOsTypeXxxx value, API GuestOS Type is in the sKind member.
+ ## @todo rename sGuestOsType
+ self._guessStuff(fRandomPvPModeCrap);
+
+ def _mkCanonicalGuestOSType(self, sType):
+ """
+ Convert guest OS type into constant representation.
+ Raise exception if specified @param sType is unknown.
+ """
+ if sType.lower().startswith('darwin'):
+ return g_ksGuestOsTypeDarwin
+ if sType.lower().startswith('bsd'):
+ return g_ksGuestOsTypeFreeBSD
+ if sType.lower().startswith('dos'):
+ return g_ksGuestOsTypeDOS
+ if sType.lower().startswith('linux'):
+ return g_ksGuestOsTypeLinux
+ if sType.lower().startswith('os2'):
+ return g_ksGuestOsTypeOS2
+ if sType.lower().startswith('solaris'):
+ return g_ksGuestOsTypeSolaris
+ if sType.lower().startswith('windows'):
+ return g_ksGuestOsTypeWindows
+ raise base.GenError(sWhat="unknown guest OS kind: %s" % str(sType))
+
+ def _guessStuff(self, fRandomPvPModeCrap):
+ """
+ Used by the constructor to guess stuff.
+ """
+
+ sNm = self.sVmName.lower().strip();
+ asSplit = sNm.replace('-', ' ').split(' ');
+
+ if self.sKind is None:
+ # From name.
+ for aInfo in g_aaNameToDetails:
+ if _intersects(asSplit, aInfo[g_iRegEx]):
+ self.aInfo = aInfo;
+ self.sGuestOsType = self._mkCanonicalGuestOSType(aInfo[g_iGuestOsType])
+ self.sKind = aInfo[g_iKind];
+ break;
+ if self.sKind is None:
+ reporter.fatal('The OS of test VM "%s" cannot be guessed' % (self.sVmName,));
+
+ # Check for 64-bit, if required and supported.
+ if (self.aInfo[g_iFlags] & g_kiArchMask) == g_k32_64 and _intersects(asSplit, ['64', 'amd64']):
+ self.sKind = self.sKind + '_64';
+ else:
+ # Lookup the kind.
+ for aInfo in g_aaNameToDetails:
+ if self.sKind == aInfo[g_iKind]:
+ self.aInfo = aInfo;
+ break;
+ if self.aInfo is None:
+ reporter.fatal('The OS of test VM "%s" with sKind="%s" cannot be guessed' % (self.sVmName, self.sKind));
+
+ # Translate sKind into sGuest OS Type.
+ if self.sGuestOsType is None:
+ if self.aInfo is not None:
+ self.sGuestOsType = self._mkCanonicalGuestOSType(self.aInfo[g_iGuestOsType])
+ elif self.sKind.find("Windows") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeWindows
+ elif self.sKind.find("Linux") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeLinux;
+ elif self.sKind.find("Solaris") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeSolaris;
+ elif self.sKind.find("DOS") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeDOS;
+ else:
+ reporter.fatal('The OS of test VM "%s", sKind="%s" cannot be guessed' % (self.sVmName, self.sKind));
+
+ # Restrict modes and such depending on the OS.
+ if self.asVirtModesSup is None:
+ self.asVirtModesSup = list(g_asVirtModes);
+ if self.sGuestOsType in (g_ksGuestOsTypeOS2, g_ksGuestOsTypeDarwin) \
+ or self.sKind.find('_64') > 0 \
+ or (self.aInfo is not None and (self.aInfo[g_iFlags] & g_kiNoRaw)):
+ self.asVirtModesSup = [sVirtMode for sVirtMode in self.asVirtModesSup if sVirtMode != 'raw'];
+ # TEMPORARY HACK - START
+ sHostName = os.environ.get("COMPUTERNAME", None);
+ if sHostName: sHostName = sHostName.lower();
+ else: sHostName = socket.getfqdn(); # Horribly slow on windows without IPv6 DNS/whatever.
+ if sHostName.startswith('testboxpile1'):
+ self.asVirtModesSup = [sVirtMode for sVirtMode in self.asVirtModesSup if sVirtMode != 'raw'];
+ # TEMPORARY HACK - END
+
+ # Restrict the CPU count depending on the OS and/or percieved SMP readiness.
+ if self.acCpusSup is None:
+ if _intersects(asSplit, ['uni']):
+ self.acCpusSup = [1];
+ elif self.aInfo is not None:
+ self.acCpusSup = list(range(self.aInfo[g_iMinCpu], self.aInfo[g_iMaxCpu] + 1));
+ else:
+ self.acCpusSup = [1];
+
+ # Figure relevant PV modes based on the OS.
+ if self.asParavirtModesSup is None:
+ self.asParavirtModesSup = g_kdaParavirtProvidersSupported[self.sGuestOsType];
+ ## @todo Remove this hack as soon as we've got around to explictly configure test variations
+ ## on the server side. Client side random is interesting but not the best option.
+ self.asParavirtModesSupOrg = self.asParavirtModesSup;
+ if fRandomPvPModeCrap:
+ random.seed();
+ self.asParavirtModesSup = (random.choice(self.asParavirtModesSup),);
+
+ return True;
+
+ def _generateRawPortFilename(self, oTestDrv, sInfix, sSuffix):
+ """ Generates a raw port filename. """
+ random.seed();
+ sRandom = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10));
+ return os.path.join(oTestDrv.sScratchPath, self.sVmName + sInfix + sRandom + sSuffix);
+
+ def _createVmPre(self, oTestDrv, eNic0AttachType, sDvdImage):
+ """
+ Prepares for creating the VM.
+
+ Returns True / False.
+ """
+ _ = eNic0AttachType; _ = sDvdImage;
+ if self.fCom1RawFile:
+ self.__sCom1RawFile = self._generateRawPortFilename(oTestDrv, '-com1-', '.out');
+ return True;
+
+ def _createVmDoIt(self, oTestDrv, eNic0AttachType, sDvdImage):
+ """
+ Creates the VM.
+
+ The default implementation creates a VM with defaults, no disks created or attached.
+
+ Returns Wrapped VM object on success, None on failure.
+ """
+ return oTestDrv.createTestVmWithDefaults(self.sVmName,
+ iGroup = self.iGroup,
+ sKind = self.sKind,
+ eNic0AttachType = eNic0AttachType,
+ sDvdImage = sDvdImage,
+ fVmmDevTestingPart = self.fVmmDevTestingPart,
+ fVmmDevTestingMmio = self.fVmmDevTestingMmio,
+ sCom1RawFile = self.__sCom1RawFile if self.fCom1RawFile else None
+ );
+
+ def _createVmPost(self, oTestDrv, oVM, eNic0AttachType, sDvdImage): # type: (base.testdriver, Any, int, str) -> Any
+ """
+ Returns same oVM on success, None on failure (createVm cleans up).
+ """
+ _ = oTestDrv; _ = eNic0AttachType; _ = sDvdImage;
+ return oVM;
+
+ def _skipVmTest(self, oTestDrv, oVM):
+ """
+ Called by getReconfiguredVm to figure out whether to skip the VM or not.
+
+ Returns True if the VM should be skipped, False otherwise.
+ """
+ _ = oVM;
+ fHostSupports64bit = oTestDrv.hasHostLongMode();
+ if self.is64bitRequired() and not fHostSupports64bit:
+ reporter.log('Skipping 64-bit VM on non-64 capable host.');
+ elif self.isViaIncompatible() and oTestDrv.isHostCpuVia():
+ reporter.log('Skipping VIA incompatible VM.');
+ elif self.isShanghaiIncompatible() and oTestDrv.isHostCpuShanghai():
+ reporter.log('Skipping Shanghai (Zhaoxin) incompatible VM.');
+ elif self.isP4Incompatible() and oTestDrv.isHostCpuP4():
+ reporter.log('Skipping P4 incompatible VM.');
+ else:
+ return False;
+ return True;
+
+
+ def _childVmReconfig(self, oTestDrv, oVM, oSession):
+ """
+ Hook into getReconfiguredVm() for children.
+ """
+ _ = oTestDrv; _ = oVM; _ = oSession;
+ return True;
+
+ def _storageCtrlAndBusToName(self, oVBoxMgr, oVM, eCtrl, eBus):
+ """
+ Resolves the storage controller name given type and bus.
+
+ Returns String on success, None on failure w/ errors logged.
+ """
+ try:
+ aoControllers = oVBoxMgr.getArray(oVM, 'storageControllers');
+ except:
+ reporter.errorXcpt();
+ return None;
+ asSummary = [];
+ for oController in aoControllers:
+ try:
+ eCurCtrl = oController.controllerType;
+ eCurBus = oController.bus;
+ sName = oController.name;
+ except:
+ reporter.errorXcpt();
+ return None;
+ if eCurCtrl == eCtrl and eCurBus == eBus:
+ return sName;
+ asSummary.append('%s-%s-%s' % (eCurCtrl, eCurBus, sName,));
+ reporter.error('Unable to find controller of type %s and bus %s (searched: %s)' % (eCtrl, eBus, ', '.join(asSummary),));
+ return None;
+
+
+ #
+ # Public interface.
+ #
+
+ def getResourceSet(self):
+ """
+ Resturns a list of reosurces that the VM needs.
+ """
+ return [];
+
+ def getMissingResources(self, sResourcePath):
+ """
+ Returns a list of missing resources (paths, stuff) that the VM needs.
+ """
+ asRet = [];
+ asResources = self.getResourceSet();
+ for sPath in asResources:
+ if not os.path.isabs(sPath):
+ sPath = os.path.join(sResourcePath, sPath);
+ if not os.path.exists(sPath):
+ asRet.append(sPath);
+ return asRet;
+
+ def skipCreatingVm(self, oTestDrv):
+ """
+ Called before VM creation to determine whether the VM should be skipped
+ due to host incompatibility or something along those lines.
+
+ returns True if it should be skipped, False if not. Caller updates fSkip.
+
+ See also _skipVmTest().
+ """
+ _ = oTestDrv;
+ return False;
+
+
+ def createVm(self, oTestDrv, eNic0AttachType = None, sDvdImage = None):
+ """
+ Creates the VM with defaults and the few tweaks as per the arguments.
+
+ Returns same as vbox.TestDriver.createTestVM.
+ """
+ reporter.log2('');
+ reporter.log2('Creating %s...' % (self.sVmName,))
+ oVM = None;
+ fRc = self._createVmPre(oTestDrv, eNic0AttachType, sDvdImage);
+ if fRc is True:
+ oVM = self._createVmDoIt(oTestDrv, eNic0AttachType, sDvdImage);
+ if oVM:
+ oVM = self._createVmPost(oTestDrv, oVM, eNic0AttachType, sDvdImage);
+ return oVM;
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ """
+ actionExecute worker that finds and reconfigure a test VM.
+
+ Returns (fRc, oVM) where fRc is True, None or False and oVM is a
+ VBox VM object that is only present when rc is True.
+ """
+
+ fRc = False;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is not None:
+ if self.fSnapshotRestoreCurrent is True:
+ fRc = True;
+ else:
+ fHostSupports64bit = oTestDrv.hasHostLongMode();
+ if self._skipVmTest(oTestDrv, oVM):
+ fRc = None; # Skip the test.
+ else:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.enableVirtEx(sVirtMode != 'raw');
+ fRc = fRc and oSession.enableNestedPaging(sVirtMode == 'hwvirt-np');
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ if cCpus > 1:
+ fRc = fRc and oSession.enableIoApic(True);
+
+ if sParavirtMode is not None and oSession.fpApiVer >= 5.0:
+ adParavirtProviders = {
+ g_ksParavirtProviderNone : vboxcon.ParavirtProvider_None,
+ g_ksParavirtProviderDefault: vboxcon.ParavirtProvider_Default,
+ g_ksParavirtProviderLegacy : vboxcon.ParavirtProvider_Legacy,
+ g_ksParavirtProviderMinimal: vboxcon.ParavirtProvider_Minimal,
+ g_ksParavirtProviderHyperV : vboxcon.ParavirtProvider_HyperV,
+ g_ksParavirtProviderKVM : vboxcon.ParavirtProvider_KVM,
+ };
+ fRc = fRc and oSession.setParavirtProvider(adParavirtProviders[sParavirtMode]);
+
+ fCfg64Bit = self.is64bitRequired() or (self.is64bit() and fHostSupports64bit and sVirtMode != 'raw');
+ fRc = fRc and oSession.enableLongMode(fCfg64Bit);
+ if fCfg64Bit: # This is to avoid GUI pedantic warnings in the GUI. Sigh.
+ oOsType = oSession.getOsType();
+ if oOsType is not None:
+ if oOsType.is64Bit and sVirtMode == 'raw':
+ assert(oOsType.id[-3:] == '_64');
+ fRc = fRc and oSession.setOsType(oOsType.id[:-3]);
+ elif not oOsType.is64Bit and sVirtMode != 'raw':
+ fRc = fRc and oSession.setOsType(oOsType.id + '_64');
+
+ # New serial raw file.
+ if fRc and self.fCom1RawFile:
+ self.__sCom1RawFile = self._generateRawPortFilename(oTestDrv, '-com1-', '.out');
+ utils.noxcptDeleteFile(self.__sCom1RawFile);
+ fRc = oSession.setupSerialToRawFile(0, self.__sCom1RawFile);
+
+ # Make life simpler for child classes.
+ if fRc:
+ fRc = self._childVmReconfig(oTestDrv, oVM, oSession);
+
+ fRc = fRc and oSession.saveSettings();
+ if not oSession.close():
+ fRc = False;
+ if fRc is True:
+ return (True, oVM);
+ return (fRc, None);
+
+ def getNonCanonicalGuestOsType(self):
+ """
+ Gets the non-canonical OS type (self.sGuestOsType is canonical).
+ """
+ return self.sKind; #self.aInfo[g_iGuestOsType];
+
+ def getGuestArch(self):
+ """ Same as util.getHostArch. """
+ return 'amd64' if self.sKind.find('_64') >= 0 else 'x86';
+
+ def getGuestOs(self):
+ """ Same as util.getHostOs. """
+ if self.isWindows(): return 'win';
+ if self.isOS2(): return 'os2';
+ if self.isLinux(): return 'linux';
+ reporter.error('getGuestOs does not what to return!');
+ raise Exception();
+
+ def getGuestOsDotArch(self):
+ """ Same as util.getHostOsDotArch. """
+ return self.getGuestOs() + '.' + self.getGuestArch();
+
+ def getGuestExeSuff(self):
+ """ The executable image suffix for the guest. """
+ if self.isWindows() or self.isOS2():
+ return '.exe';
+ return '';
+
+ def isWindows(self):
+ """ Checks if it's a Windows VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeWindows;
+
+ def isOS2(self):
+ """ Checks if it's an OS/2 VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeOS2;
+
+ def isLinux(self):
+ """ Checks if it's an Linux VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeLinux;
+
+ def is64bit(self):
+ """ Checks if it's a 64-bit VM. """
+ return self.sKind.find('_64') >= 0;
+
+ def is64bitRequired(self):
+ """ Check if 64-bit is required or not. """
+ return (self.aInfo[g_iFlags] & g_k64) != 0;
+
+ def isLoggedOntoDesktop(self):
+ """ Checks if the test VM is logging onto a graphical desktop by default. """
+ if self.isWindows():
+ return True;
+ if self.isOS2():
+ return True;
+ if self.sVmName.find('-desktop'):
+ return True;
+ return False;
+
+ def isViaIncompatible(self):
+ """
+ Identifies VMs that doesn't work on VIA.
+
+ Returns True if NOT supported on VIA, False if it IS supported.
+ """
+ # Oracle linux doesn't like VIA in our experience
+ if self.aInfo[g_iKind] in ['Oracle', 'Oracle_64']:
+ return True;
+ # OS/2: "The system detected an internal processing error at location
+ # 0168:fff1da1f - 000e:ca1f. 0a8606fd
+ if self.isOS2():
+ return True;
+ # Windows NT4 before SP4 won't work because of cmpxchg8b not being
+ # detected, leading to a STOP 3e(80,0,0,0).
+ if self.aInfo[g_iKind] == 'WindowsNT4':
+ if self.sVmName.find('sp') < 0:
+ return True; # no service pack.
+ if self.sVmName.find('sp0') >= 0 \
+ or self.sVmName.find('sp1') >= 0 \
+ or self.sVmName.find('sp2') >= 0 \
+ or self.sVmName.find('sp3') >= 0:
+ return True;
+ # XP x64 on a physical VIA box hangs exactly like a VM.
+ if self.aInfo[g_iKind] in ['WindowsXP_64', 'Windows2003_64']:
+ return True;
+ # Vista 64 throws BSOD 0x5D (UNSUPPORTED_PROCESSOR)
+ if self.aInfo[g_iKind] in ['WindowsVista_64']:
+ return True;
+ # Solaris 11 hangs on VIA, tested on a physical box (testboxvqc)
+ if self.aInfo[g_iKind] in ['Solaris11_64']:
+ return True;
+ return False;
+
+ def isShanghaiIncompatible(self):
+ """
+ Identifies VMs that doesn't work on Shanghai.
+
+ Returns True if NOT supported on Shanghai, False if it IS supported.
+ """
+ # For now treat it just like VIA, to be adjusted later
+ return self.isViaIncompatible()
+
+ def isP4Incompatible(self):
+ """
+ Identifies VMs that doesn't work on Pentium 4 / Pentium D.
+
+ Returns True if NOT supported on P4, False if it IS supported.
+ """
+ # Stupid 1 kHz timer. Too much for antique CPUs.
+ if self.sVmName.find('rhel5') >= 0:
+ return True;
+ # Due to the boot animation the VM takes forever to boot.
+ if self.aInfo[g_iKind] == 'Windows2000':
+ return True;
+ return False;
+
+ def isHostCpuAffectedByUbuntuNewAmdBug(self, oTestDrv):
+ """
+ Checks if the host OS is affected by older ubuntu installers being very
+ picky about which families of AMD CPUs it would run on.
+
+ The installer checks for family 15, later 16, later 20, and in 11.10
+ they remove the family check for AMD CPUs.
+ """
+ if not oTestDrv.isHostCpuAmd():
+ return False;
+ try:
+ (uMaxExt, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000000, 0);
+ (uFamilyModel, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000001, 0);
+ except:
+ reporter.logXcpt();
+ return False;
+ if uMaxExt < 0x80000001 or uMaxExt > 0x8000ffff:
+ return False;
+
+ uFamily = (uFamilyModel >> 8) & 0xf
+ if uFamily == 0xf:
+ uFamily = ((uFamilyModel >> 20) & 0x7f) + 0xf;
+ ## @todo Break this down into which old ubuntu release supports exactly
+ ## which AMD family, if we care.
+ if uFamily <= 15:
+ return False;
+ reporter.log('Skipping "%s" because host CPU is a family %u AMD, which may cause trouble for the guest OS installer.'
+ % (self.sVmName, uFamily,));
+ return True;
+
+ def getTestUser(self):
+ """
+ Gets the primary test user name.
+ """
+ if self.isWindows():
+ return 'Administrator';
+ return 'vbox';
+
+ def getTestUserPassword(self, sUser = None):
+ """
+ Gets the password for the primary user (or other specified one).
+ """
+ if sUser == 'test':
+ return '';
+ if sUser == 'vboxuser': # Default unattended installation user and password.
+ return 'changeme';
+ return 'password';
+
+ def getCom1RawFile(self, oVM):
+ """
+ Gets the name of the COM1 raw file.
+
+ Returns string, None on failure or if not active.
+
+ Note! Do not access __sCom1RawFile directly as it will not be set unless the
+ 'config' action was executed in the same run.
+ """
+ if self.fCom1RawFile:
+ # Retrieve it from the IMachine object and cache the result if needed:
+ if self.__sCom1RawFile is None:
+ try:
+ oPort = oVM.machine.getSerialPort(0);
+ except:
+ reporter.errorXcpt('failed to get serial port #0');
+ else:
+ try:
+ self.__sCom1RawFile = oPort.path;
+ except:
+ reporter.errorXcpt('failed to get the "path" property on serial port #0');
+ return self.__sCom1RawFile;
+
+ reporter.error('getCom1RawFile called when fCom1RawFile is False');
+ return None;
+
+ def getIGuestOSType(self, oVBoxWrapped):
+ """
+ Gets the IGuestOSType object corresponding to self.sKind.
+
+ Returns object on success, None on failure (logged as error).
+ """
+ try:
+ return oVBoxWrapped.o.getGuestOSType(self.sKind);
+ except:
+ reporter.errorXcpt('sVmName=%s sKind=%s' % (self.sVmName, self.sKind,));
+ return None;
+
+ def getRecommendedHddSize(self, oVBoxWrapped):
+ """
+ Gets the recommended HDD size from the IGuestOSType matching self.sKind.
+
+ Returns size in bytes on success, -1 on failure.
+ """
+ if self.__cbHdd < 0:
+ oGuestOSType = self.getIGuestOSType(oVBoxWrapped);
+ if oGuestOSType:
+ try:
+ self.__cbHdd = oGuestOSType.recommendedHDD;
+ except:
+ reporter.errorXcpt();
+ return -1;
+ return self.__cbHdd;
+
+ def getHddAddress(self, oVM, oVBoxWrapped):
+ """
+ Gets the HDD attachment address.
+
+ Returns (sController, iPort, iDevice) on success; (None, None, None) on failure.
+
+ Note! Do not access the cached value directly!
+ """
+ # Cached already?
+ if self.__tHddCtrlPortDev[0] is not None:
+ return self.__tHddCtrlPortDev;
+
+ # First look for HDs attached to the VM:
+ try:
+ aoAttachments = oVBoxWrapped.oVBoxMgr.getArray(oVM, 'mediumAttachments')
+ except:
+ reporter.errorXcpt();
+ else:
+ for oAtt in aoAttachments:
+ try:
+ sCtrl = oAtt.controller
+ iPort = oAtt.port;
+ iDev = oAtt.device;
+ eType = oAtt.type;
+ except:
+ reporter.errorXcpt();
+ return self.__tHddCtrlPortDev;
+ if eType == vboxcon.DeviceType_HardDisk:
+ self.__tHddCtrlPortDev = (sCtrl, iPort, iDev);
+ reporter.log2('getHddAddress: %s, %s, %s' % self.__tHddCtrlPortDev);
+ return self.__tHddCtrlPortDev;
+
+ # Then consult IGuestOSType:
+ oGuestOSType = self.getIGuestOSType(oVBoxWrapped);
+ if oGuestOSType:
+ try:
+ eCtrl = oGuestOSType.recommendedHDStorageController;
+ eBus = oGuestOSType.recommendedHDStorageBus;
+ except:
+ reporter.errorXcpt();
+ else:
+ # ASSUMES port 0, device 0.
+ self.__tHddCtrlPortDev = (self._storageCtrlAndBusToName(oVBoxWrapped.oVBoxMgr, oVM, eCtrl, eBus), 0, 0);
+ reporter.log2('getHddAddress: %s, %s, %s [IGuestOSType]' % self.__tHddCtrlPortDev);
+ return self.__tHddCtrlPortDev;
+
+ def getDvdAddress(self, oVM, oVBoxWrapped):
+ """
+ Gets the DVD attachment address.
+
+ Returns (sController, iPort, iDevice) on success; (None, None, None) on failure.
+
+ Note! Do not access the cached value directly!
+ """
+ # Cached already?
+ if self.__tDvdCtrlPortDev[0] is not None:
+ return self.__tDvdCtrlPortDev;
+
+ # First look for DVD attached to the VM:
+ try:
+ aoAttachments = oVBoxWrapped.oVBoxMgr.getArray(oVM, 'mediumAttachments')
+ except:
+ reporter.errorXcpt();
+ else:
+ for oAtt in aoAttachments:
+ try:
+ sCtrl = oAtt.controller
+ iPort = oAtt.port;
+ iDev = oAtt.device;
+ eType = oAtt.type;
+ except:
+ reporter.errorXcpt();
+ return self.__tDvdCtrlPortDev;
+ if eType == vboxcon.DeviceType_DVD:
+ self.__tDvdCtrlPortDev = (sCtrl, iPort, iDev);
+ reporter.log2('getDvdAddress: %s, %s, %s' % self.__tDvdCtrlPortDev);
+ return self.__tDvdCtrlPortDev;
+
+ # Then consult IGuestOSType:
+ oGuestOSType = self.getIGuestOSType(oVBoxWrapped);
+ if oGuestOSType:
+ try:
+ eCtrl = oGuestOSType.recommendedDVDStorageController;
+ eBus = oGuestOSType.recommendedDVDStorageBus;
+ except:
+ reporter.errorXcpt();
+ else:
+ # ASSUMES port 1, device 0.
+ self.__tDvdCtrlPortDev = (self._storageCtrlAndBusToName(oVBoxWrapped.oVBoxMgr, oVM, eCtrl, eBus), 1, 0);
+ reporter.log2('getDvdAddress: %s, %s, %s [IGuestOSType]' % self.__tDvdCtrlPortDev);
+ return self.__tDvdCtrlPortDev;
+
+ def recreateRecommendedHdd(self, oVM, oTestDrv, sHddPath = None):
+ """
+ Detaches and delete any current hard disk and then ensures that a new
+ one with the recommended size is created and attached to the recommended
+ controller/port/device.
+
+ Returns True/False (errors logged).
+ """
+ # Generate a name if none was given:
+ if not sHddPath:
+ try:
+ sHddPath = oVM.settingsFilePath;
+ except:
+ return reporter.errorXcpt();
+ sHddPath = os.path.join(os.path.dirname(sHddPath), '%s-%s.vdi' % (self.sVmName, uuid.uuid4(),));
+
+ fRc = False;
+
+ # Get the hard disk specs first:
+ cbHdd = self.getRecommendedHddSize(oTestDrv.oVBox);
+ tHddAddress = self.getHddAddress(oVM, oTestDrv.oVBox);
+ assert len(tHddAddress) == 3;
+ if tHddAddress[0] and cbHdd > 0:
+ # Open an session so we can make changes:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ # Detach the old disk (this will succeed with oOldHd set to None the first time around).
+ (fRc, oOldHd) = oSession.detachHd(tHddAddress[0], tHddAddress[1], tHddAddress[2]);
+ if fRc:
+ # Create a new disk and attach it.
+ fRc = oSession.createAndAttachHd(sHddPath,
+ cb = cbHdd,
+ sController = tHddAddress[0],
+ iPort = tHddAddress[1],
+ iDevice = tHddAddress[2],
+ fImmutable = False);
+ if fRc:
+ # Save the changes.
+ fRc = oSession.saveSettings();
+
+ # Delete the old HD:
+ if fRc and oOldHd is not None:
+ fRc = fRc and oTestDrv.oVBox.deleteHdByMedium(oOldHd);
+ fRc = fRc and oSession.saveSettings(); # Necessary for media reg??
+ else:
+ oSession.discardSettings();
+ fRc = oSession.close() and fRc;
+ return fRc;
+
+ def pathJoin(self, sBase, *asAppend):
+ """ See common.pathutils.joinEx(). """
+ return pathutils.joinEx(self.isWindows() or self.isOS2(), sBase, *asAppend);
+
+ def pathSep(self):
+ """ Returns the preferred paths separator for the guest OS. """
+ return '\\' if self.isWindows() or self.isOS2() else '/';
+
+
+## @todo Inherit from BaseTestVm
+class TestVm(object):
+ """
+ A Test VM - name + VDI/whatever.
+
+ This is just a data object.
+ """
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ sVmName, # type: str
+ fGrouping = 0, # type: int
+ oSet = None, # type: TestVmSet
+ sHd = None, # type: str
+ sKind = None, # type: str
+ acCpusSup = None, # type: List[int]
+ asVirtModesSup = None, # type: List[str]
+ fIoApic = None, # type: bool
+ fNstHwVirt = False, # type: bool
+ fPae = None, # type: bool
+ sNic0AttachType = None, # type: str
+ sFloppy = None, # type: str
+ fVmmDevTestingPart = None, # type: bool
+ fVmmDevTestingMmio = False, # type: bool
+ asParavirtModesSup = None, # type: List[str]
+ fRandomPvPMode = False, # type: bool
+ sFirmwareType = 'bios', # type: str
+ sChipsetType = 'piix3', # type: str
+ sIommuType = 'none', # type: str
+ sHddControllerType = 'IDE Controller', # type: str
+ sDvdControllerType = 'IDE Controller' # type: str
+ ):
+ self.oSet = oSet;
+ self.sVmName = sVmName;
+ self.fGrouping = fGrouping;
+ self.sHd = sHd; # Relative to the testrsrc root.
+ self.acCpusSup = acCpusSup;
+ self.asVirtModesSup = asVirtModesSup;
+ self.asParavirtModesSup = asParavirtModesSup;
+ self.asParavirtModesSupOrg = asParavirtModesSup; # HACK ALERT! Trick to make the 'effing random mess not get in the
+ # way of actively selecting virtualization modes.
+ self.sKind = sKind;
+ self.sGuestOsType = None;
+ self.sDvdImage = None; # Relative to the testrsrc root.
+ self.sDvdControllerType = sDvdControllerType;
+ self.fIoApic = fIoApic;
+ self.fNstHwVirt = fNstHwVirt;
+ self.fPae = fPae;
+ self.sNic0AttachType = sNic0AttachType;
+ self.sHddControllerType = sHddControllerType;
+ self.sFloppy = sFloppy; # Relative to the testrsrc root, except when it isn't...
+ self.fVmmDevTestingPart = fVmmDevTestingPart;
+ self.fVmmDevTestingMmio = fVmmDevTestingMmio;
+ self.sFirmwareType = sFirmwareType;
+ self.sChipsetType = sChipsetType;
+ self.sIommuType = sIommuType;
+ self.fCom1RawFile = False;
+
+ self.fSnapshotRestoreCurrent = False; # Whether to restore execution on the current snapshot.
+ self.fSkip = False; # All VMs are included in the configured set by default.
+ self.aInfo = None;
+ self.sCom1RawFile = None; # Set by createVmInner and getReconfiguredVm if fCom1RawFile is set.
+ self._guessStuff(fRandomPvPMode);
+
+ def _mkCanonicalGuestOSType(self, sType):
+ """
+ Convert guest OS type into constant representation.
+ Raise exception if specified @param sType is unknown.
+ """
+ if sType.lower().startswith('darwin'):
+ return g_ksGuestOsTypeDarwin
+ if sType.lower().startswith('bsd'):
+ return g_ksGuestOsTypeFreeBSD
+ if sType.lower().startswith('dos'):
+ return g_ksGuestOsTypeDOS
+ if sType.lower().startswith('linux'):
+ return g_ksGuestOsTypeLinux
+ if sType.lower().startswith('os2'):
+ return g_ksGuestOsTypeOS2
+ if sType.lower().startswith('solaris'):
+ return g_ksGuestOsTypeSolaris
+ if sType.lower().startswith('windows'):
+ return g_ksGuestOsTypeWindows
+ raise base.GenError(sWhat="unknown guest OS kind: %s" % str(sType))
+
+ def _guessStuff(self, fRandomPvPMode):
+ """
+ Used by the constructor to guess stuff.
+ """
+
+ sNm = self.sVmName.lower().strip();
+ asSplit = sNm.replace('-', ' ').split(' ');
+
+ if self.sKind is None:
+ # From name.
+ for aInfo in g_aaNameToDetails:
+ if _intersects(asSplit, aInfo[g_iRegEx]):
+ self.aInfo = aInfo;
+ self.sGuestOsType = self._mkCanonicalGuestOSType(aInfo[g_iGuestOsType])
+ self.sKind = aInfo[g_iKind];
+ break;
+ if self.sKind is None:
+ reporter.fatal('The OS of test VM "%s" cannot be guessed' % (self.sVmName,));
+
+ # Check for 64-bit, if required and supported.
+ if (self.aInfo[g_iFlags] & g_kiArchMask) == g_k32_64 and _intersects(asSplit, ['64', 'amd64']):
+ self.sKind = self.sKind + '_64';
+ else:
+ # Lookup the kind.
+ for aInfo in g_aaNameToDetails:
+ if self.sKind == aInfo[g_iKind]:
+ self.aInfo = aInfo;
+ break;
+ if self.aInfo is None:
+ reporter.fatal('The OS of test VM "%s" with sKind="%s" cannot be guessed' % (self.sVmName, self.sKind));
+
+ # Translate sKind into sGuest OS Type.
+ if self.sGuestOsType is None:
+ if self.aInfo is not None:
+ self.sGuestOsType = self._mkCanonicalGuestOSType(self.aInfo[g_iGuestOsType])
+ elif self.sKind.find("Windows") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeWindows
+ elif self.sKind.find("Linux") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeLinux;
+ elif self.sKind.find("Solaris") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeSolaris;
+ elif self.sKind.find("DOS") >= 0:
+ self.sGuestOsType = g_ksGuestOsTypeDOS;
+ else:
+ reporter.fatal('The OS of test VM "%s", sKind="%s" cannot be guessed' % (self.sVmName, self.sKind));
+
+ # Restrict modes and such depending on the OS.
+ if self.asVirtModesSup is None:
+ self.asVirtModesSup = list(g_asVirtModes);
+ if self.sGuestOsType in (g_ksGuestOsTypeOS2, g_ksGuestOsTypeDarwin) \
+ or self.sKind.find('_64') > 0 \
+ or (self.aInfo is not None and (self.aInfo[g_iFlags] & g_kiNoRaw)):
+ self.asVirtModesSup = [sVirtMode for sVirtMode in self.asVirtModesSup if sVirtMode != 'raw'];
+ # TEMPORARY HACK - START
+ sHostName = os.environ.get("COMPUTERNAME", None);
+ if sHostName: sHostName = sHostName.lower();
+ else: sHostName = socket.getfqdn(); # Horribly slow on windows without IPv6 DNS/whatever.
+ if sHostName.startswith('testboxpile1'):
+ self.asVirtModesSup = [sVirtMode for sVirtMode in self.asVirtModesSup if sVirtMode != 'raw'];
+ # TEMPORARY HACK - END
+
+ # Restrict the CPU count depending on the OS and/or percieved SMP readiness.
+ if self.acCpusSup is None:
+ if _intersects(asSplit, ['uni']):
+ self.acCpusSup = [1];
+ elif self.aInfo is not None:
+ self.acCpusSup = list(range(self.aInfo[g_iMinCpu], self.aInfo[g_iMaxCpu] + 1));
+ else:
+ self.acCpusSup = [1];
+
+ # Figure relevant PV modes based on the OS.
+ if self.asParavirtModesSup is None:
+ self.asParavirtModesSup = g_kdaParavirtProvidersSupported[self.sGuestOsType];
+ ## @todo Remove this hack as soon as we've got around to explictly configure test variations
+ ## on the server side. Client side random is interesting but not the best option.
+ self.asParavirtModesSupOrg = self.asParavirtModesSup;
+ if fRandomPvPMode:
+ random.seed();
+ self.asParavirtModesSup = (random.choice(self.asParavirtModesSup),);
+
+ return True;
+
+ def getNonCanonicalGuestOsType(self):
+ """
+ Gets the non-canonical OS type (self.sGuestOsType is canonical).
+ """
+ return self.aInfo[g_iGuestOsType];
+
+ def getMissingResources(self, sTestRsrc):
+ """
+ Returns a list of missing resources (paths, stuff) that the VM needs.
+ """
+ asRet = [];
+ for sPath in [ self.sHd, self.sDvdImage, self.sFloppy]:
+ if sPath is not None:
+ if not os.path.isabs(sPath):
+ sPath = os.path.join(sTestRsrc, sPath);
+ if not os.path.exists(sPath):
+ asRet.append(sPath);
+ return asRet;
+
+ def skipCreatingVm(self, oTestDrv):
+ """
+ Called before VM creation to determine whether the VM should be skipped
+ due to host incompatibility or something along those lines.
+
+ returns True if it should be skipped, False if not.
+ """
+ if self.fNstHwVirt and not oTestDrv.hasHostNestedHwVirt():
+ reporter.log('Ignoring VM %s (Nested hardware-virtualization not support on this host).' % (self.sVmName,));
+ return True;
+ return False;
+
+ def createVm(self, oTestDrv, eNic0AttachType = None, sDvdImage = None):
+ """
+ Creates the VM with defaults and the few tweaks as per the arguments.
+
+ Returns same as vbox.TestDriver.createTestVM.
+ """
+ if sDvdImage is not None:
+ sMyDvdImage = sDvdImage;
+ else:
+ sMyDvdImage = self.sDvdImage;
+
+ if eNic0AttachType is not None:
+ eMyNic0AttachType = eNic0AttachType;
+ elif self.sNic0AttachType is None:
+ eMyNic0AttachType = None;
+ elif self.sNic0AttachType == 'nat':
+ eMyNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ elif self.sNic0AttachType == 'bridged':
+ eMyNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ else:
+ assert False, self.sNic0AttachType;
+
+ return self.createVmInner(oTestDrv, eMyNic0AttachType, sMyDvdImage);
+
+ def _generateRawPortFilename(self, oTestDrv, sInfix, sSuffix):
+ """ Generates a raw port filename. """
+ random.seed();
+ sRandom = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10));
+ return os.path.join(oTestDrv.sScratchPath, self.sVmName + sInfix + sRandom + sSuffix);
+
+ def createVmInner(self, oTestDrv, eNic0AttachType, sDvdImage):
+ """
+ Same as createVm but parameters resolved.
+
+ Returns same as vbox.TestDriver.createTestVM.
+ """
+ reporter.log2('');
+ reporter.log2('Calling createTestVM on %s...' % (self.sVmName,))
+ if self.fCom1RawFile:
+ self.sCom1RawFile = self._generateRawPortFilename(oTestDrv, '-com1-', '.out');
+ return oTestDrv.createTestVM(self.sVmName,
+ 1, # iGroup
+ sHd = self.sHd,
+ sKind = self.sKind,
+ fIoApic = self.fIoApic,
+ fNstHwVirt = self.fNstHwVirt,
+ fPae = self.fPae,
+ eNic0AttachType = eNic0AttachType,
+ sDvdImage = sDvdImage,
+ sDvdControllerType = self.sDvdControllerType,
+ sHddControllerType = self.sHddControllerType,
+ sFloppy = self.sFloppy,
+ fVmmDevTestingPart = self.fVmmDevTestingPart,
+ fVmmDevTestingMmio = self.fVmmDevTestingMmio,
+ sFirmwareType = self.sFirmwareType,
+ sChipsetType = self.sChipsetType,
+ sIommuType = self.sIommuType,
+ sCom1RawFile = self.sCom1RawFile if self.fCom1RawFile else None
+ );
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ """
+ actionExecute worker that finds and reconfigure a test VM.
+
+ Returns (fRc, oVM) where fRc is True, None or False and oVM is a
+ VBox VM object that is only present when rc is True.
+ """
+
+ fRc = False;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is not None:
+ if self.fSnapshotRestoreCurrent is True:
+ fRc = True;
+ else:
+ fHostSupports64bit = oTestDrv.hasHostLongMode();
+ if self.is64bitRequired() and not fHostSupports64bit:
+ fRc = None; # Skip the test.
+ elif self.isViaIncompatible() and oTestDrv.isHostCpuVia():
+ fRc = None; # Skip the test.
+ elif self.isShanghaiIncompatible() and oTestDrv.isHostCpuShanghai():
+ fRc = None; # Skip the test.
+ elif self.isP4Incompatible() and oTestDrv.isHostCpuP4():
+ fRc = None; # Skip the test.
+ else:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.enableVirtEx(sVirtMode != 'raw');
+ fRc = fRc and oSession.enableNestedPaging(sVirtMode == 'hwvirt-np');
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ if cCpus > 1:
+ fRc = fRc and oSession.enableIoApic(True);
+
+ if sParavirtMode is not None and oSession.fpApiVer >= 5.0:
+ adParavirtProviders = {
+ g_ksParavirtProviderNone : vboxcon.ParavirtProvider_None,
+ g_ksParavirtProviderDefault: vboxcon.ParavirtProvider_Default,
+ g_ksParavirtProviderLegacy : vboxcon.ParavirtProvider_Legacy,
+ g_ksParavirtProviderMinimal: vboxcon.ParavirtProvider_Minimal,
+ g_ksParavirtProviderHyperV : vboxcon.ParavirtProvider_HyperV,
+ g_ksParavirtProviderKVM : vboxcon.ParavirtProvider_KVM,
+ };
+ fRc = fRc and oSession.setParavirtProvider(adParavirtProviders[sParavirtMode]);
+
+ fCfg64Bit = self.is64bitRequired() or (self.is64bit() and fHostSupports64bit and sVirtMode != 'raw');
+ fRc = fRc and oSession.enableLongMode(fCfg64Bit);
+ if fCfg64Bit: # This is to avoid GUI pedantic warnings in the GUI. Sigh.
+ oOsType = oSession.getOsType();
+ if oOsType is not None:
+ if oOsType.is64Bit and sVirtMode == 'raw':
+ assert(oOsType.id[-3:] == '_64');
+ fRc = fRc and oSession.setOsType(oOsType.id[:-3]);
+ elif not oOsType.is64Bit and sVirtMode != 'raw':
+ fRc = fRc and oSession.setOsType(oOsType.id + '_64');
+
+ # New serial raw file.
+ if fRc and self.fCom1RawFile:
+ self.sCom1RawFile = self._generateRawPortFilename(oTestDrv, '-com1-', '.out');
+ utils.noxcptDeleteFile(self.sCom1RawFile);
+ fRc = oSession.setupSerialToRawFile(0, self.sCom1RawFile);
+
+ # Make life simpler for child classes.
+ if fRc:
+ fRc = self._childVmReconfig(oTestDrv, oVM, oSession);
+
+ fRc = fRc and oSession.saveSettings();
+ if not oSession.close():
+ fRc = False;
+ if fRc is True:
+ return (True, oVM);
+ return (fRc, None);
+
+ def _childVmReconfig(self, oTestDrv, oVM, oSession):
+ """ Hook into getReconfiguredVm() for children. """
+ _ = oTestDrv; _ = oVM; _ = oSession;
+ return True;
+
+ def getGuestArch(self):
+ """ Same as util.getHostArch. """
+ return 'amd64' if self.sKind.find('_64') >= 0 else 'x86';
+
+ def getGuestOs(self):
+ """ Same as util.getHostOs. """
+ if self.isWindows(): return 'win';
+ if self.isOS2(): return 'os2';
+ if self.isLinux(): return 'linux';
+ reporter.error('getGuestOs does not what to return!');
+ raise Exception();
+
+ def getGuestExeSuff(self):
+ """ The executable image suffix for the guest. """
+ if self.isWindows() or self.isOS2():
+ return '.exe';
+ return '';
+
+ def getGuestOsDotArch(self):
+ """ Same as util.getHostOsDotArch."""
+ return self.getGuestOs() + '.' + self.getGuestArch();
+
+ def isWindows(self):
+ """ Checks if it's a Windows VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeWindows;
+
+ def isOS2(self):
+ """ Checks if it's an OS/2 VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeOS2;
+
+ def isLinux(self):
+ """ Checks if it's an Linux VM. """
+ return self.sGuestOsType == g_ksGuestOsTypeLinux;
+
+ def is64bit(self):
+ """ Checks if it's a 64-bit VM. """
+ return self.sKind.find('_64') >= 0;
+
+ def is64bitRequired(self):
+ """ Check if 64-bit is required or not. """
+ return (self.aInfo[g_iFlags] & g_k64) != 0;
+
+ def isLoggedOntoDesktop(self):
+ """ Checks if the test VM is logging onto a graphical desktop by default. """
+ if self.isWindows():
+ return True;
+ if self.isOS2():
+ return True;
+ if self.sVmName.find('-desktop'):
+ return True;
+ return False;
+
+ def isViaIncompatible(self):
+ """
+ Identifies VMs that doesn't work on VIA.
+
+ Returns True if NOT supported on VIA, False if it IS supported.
+ """
+ # Oracle linux doesn't like VIA in our experience
+ if self.aInfo[g_iKind] in ['Oracle', 'Oracle_64']:
+ return True;
+ # OS/2: "The system detected an internal processing error at location
+ # 0168:fff1da1f - 000e:ca1f. 0a8606fd
+ if self.isOS2():
+ return True;
+ # Windows NT4 before SP4 won't work because of cmpxchg8b not being
+ # detected, leading to a STOP 3e(80,0,0,0).
+ if self.aInfo[g_iKind] == 'WindowsNT4':
+ if self.sVmName.find('sp') < 0:
+ return True; # no service pack.
+ if self.sVmName.find('sp0') >= 0 \
+ or self.sVmName.find('sp1') >= 0 \
+ or self.sVmName.find('sp2') >= 0 \
+ or self.sVmName.find('sp3') >= 0:
+ return True;
+ # XP x64 on a physical VIA box hangs exactly like a VM.
+ if self.aInfo[g_iKind] in ['WindowsXP_64', 'Windows2003_64']:
+ return True;
+ # Vista 64 throws BSOD 0x5D (UNSUPPORTED_PROCESSOR)
+ if self.aInfo[g_iKind] in ['WindowsVista_64']:
+ return True;
+ # Solaris 11 hangs on VIA, tested on a physical box (testboxvqc)
+ if self.aInfo[g_iKind] in ['Solaris11_64']:
+ return True;
+ return False;
+
+ def isShanghaiIncompatible(self):
+ """
+ Identifies VMs that doesn't work on Shanghai.
+
+ Returns True if NOT supported on Shanghai, False if it IS supported.
+ """
+ # For now treat it just like VIA, to be adjusted later
+ return self.isViaIncompatible()
+
+ def isP4Incompatible(self):
+ """
+ Identifies VMs that doesn't work on Pentium 4 / Pentium D.
+
+ Returns True if NOT supported on P4, False if it IS supported.
+ """
+ # Stupid 1 kHz timer. Too much for antique CPUs.
+ if self.sVmName.find('rhel5') >= 0:
+ return True;
+ # Due to the boot animation the VM takes forever to boot.
+ if self.aInfo[g_iKind] == 'Windows2000':
+ return True;
+ return False;
+
+ def isHostCpuAffectedByUbuntuNewAmdBug(self, oTestDrv):
+ """
+ Checks if the host OS is affected by older ubuntu installers being very
+ picky about which families of AMD CPUs it would run on.
+
+ The installer checks for family 15, later 16, later 20, and in 11.10
+ they remove the family check for AMD CPUs.
+ """
+ if not oTestDrv.isHostCpuAmd():
+ return False;
+ try:
+ (uMaxExt, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000000, 0);
+ (uFamilyModel, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000001, 0);
+ except:
+ reporter.logXcpt();
+ return False;
+ if uMaxExt < 0x80000001 or uMaxExt > 0x8000ffff:
+ return False;
+
+ uFamily = (uFamilyModel >> 8) & 0xf
+ if uFamily == 0xf:
+ uFamily = ((uFamilyModel >> 20) & 0x7f) + 0xf;
+ ## @todo Break this down into which old ubuntu release supports exactly
+ ## which AMD family, if we care.
+ if uFamily <= 15:
+ return False;
+ reporter.log('Skipping "%s" because host CPU is a family %u AMD, which may cause trouble for the guest OS installer.'
+ % (self.sVmName, uFamily,));
+ return True;
+
+ def getTestUser(self):
+ """
+ Gets the primary test user name.
+ """
+ if self.isWindows():
+ return 'Administrator';
+ return 'vbox';
+
+ def getTestUserPassword(self, sUser = None):
+ """
+ Gets the password for the primary user (or other specified one).
+ """
+ if sUser == 'test':
+ return '';
+ if sUser == 'vboxuser': # Default unattended installation user and password.
+ return 'changeme';
+ return 'password';
+
+ def pathJoin(self, sBase, *asAppend):
+ """ See common.pathutils.joinEx(). """
+ return pathutils.joinEx(self.isWindows() or self.isOS2(), sBase, *asAppend);
+
+ def pathSep(self):
+ """ Returns the preferred paths separator for the guest OS. """
+ return '\\' if self.isWindows() or self.isOS2() else '/';
+
+
+class BootSectorTestVm(TestVm):
+ """
+ A Boot Sector Test VM.
+ """
+
+ def __init__(self, oSet, sVmName, sFloppy = None, asVirtModesSup = None, f64BitRequired = False):
+ self.f64BitRequired = f64BitRequired;
+ if asVirtModesSup is None:
+ asVirtModesSup = list(g_asVirtModes);
+ TestVm.__init__(self, sVmName,
+ oSet = oSet,
+ acCpusSup = [1,],
+ sFloppy = sFloppy,
+ asVirtModesSup = asVirtModesSup,
+ fPae = True,
+ fIoApic = True,
+ fVmmDevTestingPart = True,
+ fVmmDevTestingMmio = True,
+ );
+
+ def is64bitRequired(self):
+ return self.f64BitRequired;
+
+
+class AncientTestVm(TestVm):
+ """
+ A ancient Test VM, using the serial port for communicating results.
+
+ We're looking for 'PASSED' and 'FAILED' lines in the COM1 output.
+ """
+
+
+ def __init__(self, # pylint: disable=too-many-arguments
+ sVmName, # type: str
+ fGrouping = g_kfGrpAncient | g_kfGrpNoTxs, # type: int
+ sHd = None, # type: str
+ sKind = None, # type: str
+ acCpusSup = None, # type: List[int]
+ asVirtModesSup = None, # type: List[str]
+ sNic0AttachType = None, # type: str
+ sFloppy = None, # type: str
+ sFirmwareType = 'bios', # type: str
+ sChipsetType = 'piix3', # type: str
+ sHddControllerName = 'IDE Controller', # type: str
+ sDvdControllerName = 'IDE Controller', # type: str
+ cMBRamMax = None, # type: int
+ ):
+ TestVm.__init__(self,
+ sVmName,
+ fGrouping = fGrouping,
+ sHd = sHd,
+ sKind = sKind,
+ acCpusSup = [1] if acCpusSup is None else acCpusSup,
+ asVirtModesSup = asVirtModesSup,
+ sNic0AttachType = sNic0AttachType,
+ sFloppy = sFloppy,
+ sFirmwareType = sFirmwareType,
+ sChipsetType = sChipsetType,
+ sHddControllerType = sHddControllerName,
+ sDvdControllerType = sDvdControllerName,
+ asParavirtModesSup = (g_ksParavirtProviderNone,)
+ );
+ self.fCom1RawFile = True;
+ self.cMBRamMax= cMBRamMax;
+
+
+ def _childVmReconfig(self, oTestDrv, oVM, oSession):
+ _ = oVM; _ = oTestDrv;
+ fRc = True;
+
+ # DOS 4.01 doesn't like the default 32MB of memory.
+ if fRc and self.cMBRamMax is not None:
+ try:
+ cMBRam = oSession.o.machine.memorySize;
+ except:
+ cMBRam = self.cMBRamMax + 4;
+ if self.cMBRamMax < cMBRam:
+ fRc = oSession.setRamSize(self.cMBRamMax);
+
+ return fRc;
+
+
+class TestVmSet(object):
+ """
+ A set of Test VMs.
+ """
+
+ def __init__(self, oTestVmManager = None, acCpus = None, asVirtModes = None, fIgnoreSkippedVm = False):
+ self.oTestVmManager = oTestVmManager;
+ if acCpus is None:
+ acCpus = [1, 2];
+ self.acCpusDef = acCpus;
+ self.acCpus = acCpus;
+ if asVirtModes is None:
+ asVirtModes = list(g_asVirtModes);
+ self.asVirtModesDef = asVirtModes;
+ self.asVirtModes = asVirtModes;
+ self.aoTestVms = [] # type: list(BaseTestVm)
+ self.fIgnoreSkippedVm = fIgnoreSkippedVm;
+ self.asParavirtModes = None; ##< If None, use the first PV mode of the test VM, otherwise all modes in this list.
+
+ def findTestVmByName(self, sVmName):
+ """
+ Returns the TestVm object with the given name.
+ Returns None if not found.
+ """
+
+ # The 'tst-' prefix is optional.
+ sAltName = sVmName if sVmName.startswith('tst-') else 'tst-' + sVmName;
+
+ for oTestVm in self.aoTestVms:
+ if oTestVm.sVmName in (sVmName, sAltName):
+ return oTestVm;
+ return None;
+
+ def getAllVmNames(self, sSep = ':'):
+ """
+ Returns names of all the test VMs in the set separated by
+ sSep (defaults to ':').
+ """
+ sVmNames = '';
+ for oTestVm in self.aoTestVms:
+ sName = oTestVm.sVmName;
+ if sName.startswith('tst-'):
+ sName = sName[4:];
+ if sVmNames == '':
+ sVmNames = sName;
+ else:
+ sVmNames = sVmNames + sSep + sName;
+ return sVmNames;
+
+ def showUsage(self):
+ """
+ Invoked by vbox.TestDriver.
+ """
+ reporter.log('');
+ reporter.log('Test VM selection and general config options:');
+ reporter.log(' --virt-modes <m1[:m2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --skip-virt-modes <m1[:m2[:...]]>');
+ reporter.log(' Use this to avoid hwvirt or hwvirt-np when not supported by the host');
+ reporter.log(' since we cannot detect it using the main API. Use after --virt-modes.');
+ reporter.log(' --cpu-counts <c1[:c2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (self.getAllVmNames(),));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --snapshot-restore-current');
+ reporter.log(' Restores the current snapshot and resumes execution.');
+ reporter.log(' --paravirt-modes <pv1[:pv2[:...]]>');
+ reporter.log(' Set of paravirtualized providers (modes) to tests. Intersected with what the test VM supports.');
+ reporter.log(' Default is the first PV mode the test VMs support, generally same as "legacy".');
+ reporter.log(' --with-nested-hwvirt-only');
+ reporter.log(' Test VMs using nested hardware-virtualization only.');
+ reporter.log(' --without-nested-hwvirt-only');
+ reporter.log(' Test VMs not using nested hardware-virtualization only.');
+ ## @todo Add more options for controlling individual VMs.
+ return True;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parses the set test vm set options (--test-vms and --skip-vms), modifying the set
+ Invoked by the testdriver method with the same name.
+
+ Keyword arguments:
+ asArgs -- The argument vector.
+ iArg -- The index of the current argument.
+
+ Returns iArg if the option was not recognized and the caller should handle it.
+ Returns the index of the next argument when something is consumed.
+
+ In the event of a syntax error, a InvalidOption or QuietInvalidOption
+ is thrown.
+ """
+
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+
+ elif asArgs[iArg] == '--skip-virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--skip-virt-modes" takes a colon separated list of modes');
+
+ for s in asArgs[iArg].split(':'):
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ if s in self.asVirtModes:
+ self.asVirtModes.remove(s);
+
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--test-vms" takes colon separated list');
+
+ for oTestVm in self.aoTestVms:
+ oTestVm.fSkip = True;
+
+ asTestVMs = asArgs[iArg].split(':');
+ for s in asTestVMs:
+ oTestVm = self.findTestVmByName(s);
+ if oTestVm is None:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, self.getAllVmNames(' ')));
+ oTestVm.fSkip = False;
+
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+
+ asTestVMs = asArgs[iArg].split(':');
+ for s in asTestVMs:
+ oTestVm = self.findTestVmByName(s);
+ if oTestVm is None:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s,));
+ else:
+ oTestVm.fSkip = True;
+
+ elif asArgs[iArg] == '--snapshot-restore-current':
+ for oTestVm in self.aoTestVms:
+ if oTestVm.fSkip is False:
+ oTestVm.fSnapshotRestoreCurrent = True;
+ reporter.log('VM "%s" will be restored.' % (oTestVm.sVmName));
+
+ elif asArgs[iArg] == '--paravirt-modes':
+ iArg += 1
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--paravirt-modes" takes a colon separated list of modes');
+
+ self.asParavirtModes = asArgs[iArg].split(':')
+ for sPvMode in self.asParavirtModes:
+ if sPvMode not in g_kasParavirtProviders:
+ raise base.InvalidOption('The "--paravirt-modes" value "%s" is not valid; valid values are: %s'
+ % (sPvMode, ', '.join(g_kasParavirtProviders),));
+ if not self.asParavirtModes:
+ self.asParavirtModes = None;
+
+ # HACK ALERT! Reset the random paravirt selection for members.
+ for oTestVm in self.aoTestVms:
+ oTestVm.asParavirtModesSup = oTestVm.asParavirtModesSupOrg;
+
+ elif asArgs[iArg] == '--with-nested-hwvirt-only':
+ for oTestVm in self.aoTestVms:
+ if oTestVm.fNstHwVirt is False:
+ oTestVm.fSkip = True;
+
+ elif asArgs[iArg] == '--without-nested-hwvirt-only':
+ for oTestVm in self.aoTestVms:
+ if oTestVm.fNstHwVirt is True:
+ oTestVm.fSkip = True;
+
+ else:
+ return iArg;
+ return iArg + 1;
+
+ def getResourceSet(self):
+ """
+ Called vbox.TestDriver.getResourceSet and returns a list of paths of resources.
+ """
+ asResources = [];
+ for oTestVm in self.aoTestVms:
+ if not oTestVm.fSkip:
+ if isinstance(oTestVm, BaseTestVm): # Temporarily...
+ asResources.extend(oTestVm.getResourceSet());
+ else:
+ if oTestVm.sHd is not None:
+ asResources.append(oTestVm.sHd);
+ if oTestVm.sDvdImage is not None:
+ asResources.append(oTestVm.sDvdImage);
+ return asResources;
+
+ def actionConfig(self, oTestDrv, eNic0AttachType = None, sDvdImage = None):
+ """
+ For base.TestDriver.actionConfig. Configure the VMs with defaults and
+ a few tweaks as per arguments.
+
+ Returns True if successful.
+ Returns False if not.
+ """
+
+ for oTestVm in self.aoTestVms:
+ if oTestVm.fSkip:
+ continue;
+ if oTestVm.skipCreatingVm(oTestDrv):
+ oTestVm.fSkip = True;
+ continue;
+
+ if oTestVm.fSnapshotRestoreCurrent:
+ # If we want to restore a VM we don't need to create
+ # the machine anymore -- so just add it to the test VM list.
+ oVM = oTestDrv.addTestMachine(oTestVm.sVmName);
+ else:
+ oVM = oTestVm.createVm(oTestDrv, eNic0AttachType, sDvdImage);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def _removeUnsupportedVirtModes(self, oTestDrv):
+ """
+ Removes unsupported virtualization modes.
+ """
+ if 'hwvirt' in self.asVirtModes and not oTestDrv.hasHostHwVirt():
+ reporter.log('Hardware assisted virtualization is not available on the host, skipping it.');
+ self.asVirtModes.remove('hwvirt');
+
+ if 'hwvirt-np' in self.asVirtModes and not oTestDrv.hasHostNestedPaging():
+ reporter.log('Nested paging not supported by the host, skipping it.');
+ self.asVirtModes.remove('hwvirt-np');
+
+ if 'raw' in self.asVirtModes and not oTestDrv.hasRawModeSupport():
+ reporter.log('Raw-mode virtualization is not available in this build (or perhaps for this host), skipping it.');
+ self.asVirtModes.remove('raw');
+
+ return True;
+
+ def actionExecute(self, oTestDrv, fnCallback): # pylint: disable=too-many-locals
+ """
+ For base.TestDriver.actionExecute. Calls the callback function for
+ each of the VMs and basic configuration variations (virt-mode and cpu
+ count).
+
+ Returns True if all fnCallback calls returned True, otherwise False.
+
+ The callback can return True, False or None. The latter is for when the
+ test is skipped. (True is for success, False is for failure.)
+ """
+
+ self._removeUnsupportedVirtModes(oTestDrv);
+ cMaxCpus = oTestDrv.getHostCpuCount();
+
+ #
+ # The test loop.
+ #
+ fRc = True;
+ for oTestVm in self.aoTestVms:
+ if oTestVm.fSkip and self.fIgnoreSkippedVm:
+ reporter.log2('Ignoring VM %s (fSkip = True).' % (oTestVm.sVmName,));
+ continue;
+ reporter.testStart(oTestVm.sVmName);
+ if oTestVm.fSkip:
+ reporter.testDone(fSkipped = True);
+ continue;
+
+ # Intersect the supported modes and the ones being testing.
+ asVirtModesSup = [sMode for sMode in oTestVm.asVirtModesSup if sMode in self.asVirtModes];
+
+ # Ditto for CPUs.
+ acCpusSup = [cCpus for cCpus in oTestVm.acCpusSup if cCpus in self.acCpus];
+
+ # Ditto for paravirtualization modes, except if not specified we got a less obvious default.
+ if self.asParavirtModes is not None and oTestDrv.fpApiVer >= 5.0:
+ asParavirtModes = [sPvMode for sPvMode in oTestVm.asParavirtModesSup if sPvMode in self.asParavirtModes];
+ assert None not in asParavirtModes;
+ elif oTestDrv.fpApiVer >= 5.0:
+ asParavirtModes = (oTestVm.asParavirtModesSup[0],);
+ assert asParavirtModes[0] is not None;
+ else:
+ asParavirtModes = (None,);
+
+ for cCpus in acCpusSup:
+ if cCpus == 1:
+ reporter.testStart('1 cpu');
+ else:
+ reporter.testStart('%u cpus' % (cCpus));
+ if cCpus > cMaxCpus:
+ reporter.testDone(fSkipped = True);
+ continue;
+
+ cTests = 0;
+ for sVirtMode in asVirtModesSup:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ reporter.testStart('%s' % ( g_dsVirtModeDescs[sVirtMode], ) );
+ cStartTests = cTests;
+
+ for sParavirtMode in asParavirtModes:
+ if sParavirtMode is not None:
+ assert oTestDrv.fpApiVer >= 5.0;
+ reporter.testStart('%s' % ( sParavirtMode, ) );
+
+ # Reconfigure the VM.
+ try:
+ (rc2, oVM) = oTestVm.getReconfiguredVm(oTestDrv, cCpus, sVirtMode, sParavirtMode = sParavirtMode);
+ except KeyboardInterrupt:
+ raise;
+ except:
+ reporter.errorXcpt(cFrames = 9);
+ rc2 = False;
+ if rc2 is True:
+ # Do the testing.
+ try:
+ rc2 = fnCallback(oVM, oTestVm);
+ except KeyboardInterrupt:
+ raise;
+ except:
+ reporter.errorXcpt(cFrames = 9);
+ rc2 = False;
+ if rc2 is False:
+ reporter.maybeErr(reporter.testErrorCount() == 0, 'fnCallback failed');
+ elif rc2 is False:
+ reporter.log('getReconfiguredVm failed');
+ if rc2 is False:
+ fRc = False;
+
+ cTests = cTests + (rc2 is not None);
+ if sParavirtMode is not None:
+ reporter.testDone(fSkipped = (rc2 is None));
+
+ reporter.testDone(fSkipped = cTests == cStartTests);
+
+ reporter.testDone(fSkipped = cTests == 0);
+
+ _, cErrors = reporter.testDone();
+ if cErrors > 0:
+ fRc = False;
+ return fRc;
+
+ def enumerateTestVms(self, fnCallback):
+ """
+ Enumerates all the 'active' VMs.
+
+ Returns True if all fnCallback calls returned True.
+ Returns False if any returned False.
+ Returns None immediately if fnCallback returned None.
+ """
+ fRc = True;
+ for oTestVm in self.aoTestVms:
+ if not oTestVm.fSkip:
+ fRc2 = fnCallback(oTestVm);
+ if fRc2 is None:
+ return fRc2;
+ fRc = fRc and fRc2;
+ return fRc;
+
+
+
+class TestVmManager(object):
+ """
+ Test VM manager.
+ """
+
+ ## @name VM grouping flags
+ ## @{
+ kfGrpSmoke = g_kfGrpSmoke;
+ kfGrpStandard = g_kfGrpStandard;
+ kfGrpStdSmoke = g_kfGrpStdSmoke;
+ kfGrpWithGAs = g_kfGrpWithGAs;
+ kfGrpNoTxs = g_kfGrpNoTxs;
+ kfGrpAncient = g_kfGrpAncient;
+ kfGrpExotic = g_kfGrpExotic;
+ ## @}
+
+ kaTestVMs = (
+ # Note: The images in the 6.1 folder all have been pre-configured to allow for Guest Additions installation
+ # (come with build essentials, kernel headers).
+ # Linux
+ TestVm('tst-ubuntu-18_04_3-64', kfGrpStdSmoke, sHd = '6.1/ubuntu-18_04_3-amd64-2.vdi',
+ sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True,
+ asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ # Note: Deprecated; had SELinux + Screensaver (black screen) enabled.
+ #TestVm('tst-ol-8_1-64-efi', kfGrpStdSmoke, sHd = '6.1/efi/ol-8_1-efi-amd64.vdi',
+ # sKind = 'Oracle_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi',
+ # asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ TestVm('tst-ol-8_1-64-efi', kfGrpStdSmoke, sHd = '6.1/efi/ol-8_1-efi-amd64-2.vdi',
+ sKind = 'Oracle_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi',
+ asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ TestVm('tst-ol-6u2-32', kfGrpStdSmoke, sHd = '6.1/ol-6u2-x86.vdi',
+ sKind = 'Oracle', acCpusSup = range(1, 33), fIoApic = True,
+ asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ TestVm('tst-ubuntu-15_10-64-efi', kfGrpStdSmoke, sHd = '6.1/efi/ubuntu-15_10-efi-amd64-3.vdi',
+ sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi',
+ asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ # Note: Deprecated / buggy; use the one in the 6.1 folder.
+ #TestVm('tst-ubuntu-15_10-64-efi', kfGrpStdSmoke, sHd = '4.2/efi/ubuntu-15_10-efi-amd64.vdi',
+ # sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi',
+ # asParavirtModesSup = [g_ksParavirtProviderKVM,]),
+ TestVm('tst-rhel5', kfGrpSmoke, sHd = '3.0/tcp/rhel5.vdi',
+ sKind = 'RedHat', acCpusSup = range(1, 33), fIoApic = True, sNic0AttachType = 'nat'),
+ TestVm('tst-arch', kfGrpStandard, sHd = '4.2/usb/tst-arch.vdi',
+ sKind = 'ArchLinux_64', acCpusSup = range(1, 33), fIoApic = True, sNic0AttachType = 'nat'),
+ # disabled 2019-03-08 klaus - fails all over the place and pollutes the test results
+ #TestVm('tst-ubuntu-1804-64', kfGrpStdSmoke, sHd = '4.2/ubuntu-1804/t-ubuntu-1804-64.vdi',
+ # sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-ol76-64', kfGrpStdSmoke, sHd = '4.2/ol76/t-ol76-64.vdi',
+ sKind = 'Oracle_64', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-ubuntu-20_04-64-amdvi', kfGrpStdSmoke, sHd = '6.1/ubuntu-20_04-64.vdi',
+ sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True,
+ asParavirtModesSup = [g_ksParavirtProviderKVM,], sNic0AttachType = 'nat', sChipsetType = 'ich9',
+ sIommuType = 'amd'),
+ TestVm('tst-ubuntu-20_04-64-vtd', kfGrpStdSmoke, sHd = '6.1/ubuntu-20_04-64.vdi',
+ sKind = 'Ubuntu_64', acCpusSup = range(1, 33), fIoApic = True,
+ asParavirtModesSup = [g_ksParavirtProviderKVM,], sNic0AttachType = 'nat', sChipsetType = 'ich9',
+ sIommuType = 'intel'),
+
+ # Solaris
+ TestVm('tst-sol10', kfGrpSmoke, sHd = '3.0/tcp/solaris10.vdi',
+ sKind = 'Solaris', acCpusSup = range(1, 33), fPae = True, sNic0AttachType = 'bridged'),
+ TestVm('tst-sol10-64', kfGrpSmoke, sHd = '3.0/tcp/solaris10.vdi',
+ sKind = 'Solaris_64', acCpusSup = range(1, 33), sNic0AttachType = 'bridged'),
+ TestVm('tst-sol11u1', kfGrpSmoke, sHd = '4.2/nat/sol11u1/t-sol11u1.vdi',
+ sKind = 'Solaris11_64', acCpusSup = range(1, 33), sNic0AttachType = 'nat', fIoApic = True,
+ sHddControllerType = 'SATA Controller'),
+ #TestVm('tst-sol11u1-ich9', kfGrpSmoke, sHd = '4.2/nat/sol11u1/t-sol11u1.vdi',
+ # sKind = 'Solaris11_64', acCpusSup = range(1, 33), sNic0AttachType = 'nat', fIoApic = True,
+ # sHddControllerType = 'SATA Controller', sChipsetType = 'ich9'),
+
+ # NT 3.x
+ TestVm('tst-nt310', kfGrpAncient, sHd = '5.2/great-old-ones/t-nt310/t-nt310.vdi',
+ sKind = 'WindowsNT3x', acCpusSup = [1], sHddControllerType = 'BusLogic SCSI Controller',
+ sDvdControllerType = 'BusLogic SCSI Controller'),
+ TestVm('tst-nt350', kfGrpAncient, sHd = '5.2/great-old-ones/t-nt350/t-nt350.vdi',
+ sKind = 'WindowsNT3x', acCpusSup = [1], sHddControllerType = 'BusLogic SCSI Controller',
+ sDvdControllerType = 'BusLogic SCSI Controller'),
+ TestVm('tst-nt351', kfGrpAncient, sHd = '5.2/great-old-ones/t-nt350/t-nt351.vdi',
+ sKind = 'WindowsNT3x', acCpusSup = [1], sHddControllerType = 'BusLogic SCSI Controller',
+ sDvdControllerType = 'BusLogic SCSI Controller'),
+
+ # NT 4
+ TestVm('tst-nt4sp1', kfGrpStdSmoke, sHd = '4.2/nat/nt4sp1/t-nt4sp1.vdi',
+ sKind = 'WindowsNT4', acCpusSup = [1], sNic0AttachType = 'nat'),
+
+ TestVm('tst-nt4sp6', kfGrpStdSmoke, sHd = '4.2/nt4sp6/t-nt4sp6.vdi',
+ sKind = 'WindowsNT4', acCpusSup = range(1, 33)),
+
+ # W2K
+ TestVm('tst-w2ksp4', kfGrpStdSmoke, sHd = '4.2/win2ksp4/t-win2ksp4.vdi',
+ sKind = 'Windows2000', acCpusSup = range(1, 33)),
+
+ # XP
+ TestVm('tst-xppro', kfGrpStdSmoke, sHd = '4.2/nat/xppro/t-xppro.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(1, 33), sNic0AttachType = 'nat'),
+ TestVm('tst-xpsp2', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxpsp2.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-xpsp2-halaacpi', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxp-halaacpi.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-xpsp2-halacpi', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxp-halacpi.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-xpsp2-halapic', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxp-halapic.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(1, 33), fIoApic = True),
+ TestVm('tst-xpsp2-halmacpi', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxp-halmacpi.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(2, 33), fIoApic = True),
+ TestVm('tst-xpsp2-halmps', kfGrpStdSmoke, sHd = '4.2/xpsp2/t-winxp-halmps.vdi',
+ sKind = 'WindowsXP', acCpusSup = range(2, 33), fIoApic = True),
+
+ # W2K3
+ TestVm('tst-win2k3ent', kfGrpSmoke, sHd = '3.0/tcp/win2k3ent-acpi.vdi',
+ sKind = 'Windows2003', acCpusSup = range(1, 33), fPae = True, sNic0AttachType = 'bridged'),
+
+ # W7
+ TestVm('tst-win7', kfGrpStdSmoke, sHd = '6.1/win7-32/t-win7-32-1.vdi',
+ sKind = 'Windows7', acCpusSup = range(1, 33), fIoApic = True),
+ # Note: Deprecated due to activation issues; use t-win7-32-1 instead.
+ #TestVm('tst-win7', kfGrpStdSmoke, sHd = '6.1/win7-32/t-win7-32.vdi',
+ # sKind = 'Windows7', acCpusSup = range(1, 33), fIoApic = True),
+ # Note: Deprecated; use the one in the 6.1 folder.
+ #TestVm('tst-win7', kfGrpStdSmoke, sHd = '4.2/win7-32/t-win7.vdi',
+ # sKind = 'Windows7', acCpusSup = range(1, 33), fIoApic = True),
+
+ # W8
+ TestVm('tst-win8-64', kfGrpStdSmoke, sHd = '4.2/win8-64/t-win8-64.vdi',
+ sKind = 'Windows8_64', acCpusSup = range(1, 33), fIoApic = True),
+ #TestVm('tst-win8-64-ich9', kfGrpStdSmoke, sHd = '4.2/win8-64/t-win8-64.vdi',
+ # sKind = 'Windows8_64', acCpusSup = range(1, 33), fIoApic = True, sChipsetType = 'ich9'),
+
+ # W10
+ TestVm('tst-win10-efi', kfGrpStdSmoke, sHd = '4.2/efi/win10-efi-x86.vdi',
+ sKind = 'Windows10', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi'),
+ TestVm('tst-win10-64-efi', kfGrpStdSmoke, sHd = '4.2/efi/win10-efi-amd64.vdi',
+ sKind = 'Windows10_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi'),
+ #TestVm('tst-win10-64-efi-ich9', kfGrpStdSmoke, sHd = '4.2/efi/win10-efi-amd64.vdi',
+ # sKind = 'Windows10_64', acCpusSup = range(1, 33), fIoApic = True, sFirmwareType = 'efi', sChipsetType = 'ich9'),
+
+ # Nested hardware-virtualization
+ TestVm('tst-nsthwvirt-ubuntu-64', kfGrpStdSmoke, sHd = '5.3/nat/nsthwvirt-ubuntu64/t-nsthwvirt-ubuntu64.vdi',
+ sKind = 'Ubuntu_64', acCpusSup = range(1, 2), asVirtModesSup = ['hwvirt-np',], fIoApic = True, fNstHwVirt = True,
+ sNic0AttachType = 'nat'),
+
+ # Audio testing.
+ TestVm('tst-audio-debian10-64', kfGrpStdSmoke, sHd = '6.1/audio/debian10-amd64-7.vdi',
+ sKind = 'Debian_64', acCpusSup = range(1, 33), fIoApic = True),
+
+ # DOS and Old Windows.
+ AncientTestVm('tst-dos20', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos20/t-dos20.vdi'),
+ AncientTestVm('tst-dos401-win30me', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos401-win30me/t-dos401-win30me.vdi', cMBRamMax = 4),
+ AncientTestVm('tst-dos401-emm386-win30me', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos401-emm386-win30me/t-dos401-emm386-win30me.vdi', cMBRamMax = 4),
+ AncientTestVm('tst-dos50-win31', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos50-win31/t-dos50-win31.vdi'),
+ AncientTestVm('tst-dos50-emm386-win31', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos50-emm386-win31/t-dos50-emm386-win31.vdi'),
+ AncientTestVm('tst-dos622', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos622/t-dos622.vdi'),
+ AncientTestVm('tst-dos622-emm386', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos622-emm386/t-dos622-emm386.vdi'),
+ AncientTestVm('tst-dos71', sKind = 'DOS',
+ sHd = '5.2/great-old-ones/t-dos71/t-dos71.vdi'),
+
+ #AncientTestVm('tst-dos5-win311a', sKind = 'DOS', sHd = '5.2/great-old-ones/t-dos5-win311a/t-dos5-win311a.vdi'),
+ );
+
+
+ def __init__(self, sResourcePath):
+ self.sResourcePath = sResourcePath;
+
+ def selectSet(self, fGrouping, sTxsTransport = None, fCheckResources = True):
+ """
+ Returns a VM set with the selected VMs.
+ """
+ oSet = TestVmSet(oTestVmManager = self);
+ for oVm in self.kaTestVMs:
+ if oVm.fGrouping & fGrouping:
+ if sTxsTransport is None or oVm.sNic0AttachType is None or sTxsTransport == oVm.sNic0AttachType:
+ if not fCheckResources or not oVm.getMissingResources(self.sResourcePath):
+ oCopyVm = copy.deepcopy(oVm);
+ oCopyVm.oSet = oSet;
+ oSet.aoTestVms.append(oCopyVm);
+ return oSet;
+
+ def getStandardVmSet(self, sTxsTransport):
+ """
+ Gets the set of standard test VMs.
+
+ This is supposed to do something seriously clever, like searching the
+ testrsrc tree for usable VMs, but for the moment it's all hard coded. :-)
+ """
+ return self.selectSet(self.kfGrpStandard, sTxsTransport)
+
+ def getSmokeVmSet(self, sTxsTransport = None):
+ """Gets a representative set of VMs for smoke testing. """
+ return self.selectSet(self.kfGrpSmoke, sTxsTransport);
+
+ def shutUpPyLint(self):
+ """ Shut up already! """
+ return self.sResourcePath;
diff --git a/src/VBox/ValidationKit/testdriver/vboxwrappers.py b/src/VBox/ValidationKit/testdriver/vboxwrappers.py
new file mode 100755
index 00000000..179e9cdc
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/vboxwrappers.py
@@ -0,0 +1,3666 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxwrappers.py $
+# pylint: disable=too-many-lines
+
+"""
+VirtualBox Wrapper Classes
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import socket;
+import sys;
+
+# Validation Kit imports.
+from common import utils;
+from common import netutils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import txsclient;
+from testdriver import vboxcon;
+from testdriver import vbox;
+from testdriver.base import TdTaskBase;
+
+
+def _ControllerNameToBusAndType(sController):
+ """ Translate a controller name to a storage bus. """
+ if sController == "IDE Controller":
+ eBus = vboxcon.StorageBus_IDE;
+ eType = vboxcon.StorageControllerType_PIIX4;
+ elif sController == "SATA Controller":
+ eBus = vboxcon.StorageBus_SATA;
+ eType = vboxcon.StorageControllerType_IntelAhci;
+ elif sController == "Floppy Controller":
+ eType = vboxcon.StorageControllerType_I82078;
+ eBus = vboxcon.StorageBus_Floppy;
+ elif sController == "SAS Controller":
+ eBus = vboxcon.StorageBus_SAS;
+ eType = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sController == "SCSI Controller":
+ eBus = vboxcon.StorageBus_SCSI;
+ eType = vboxcon.StorageControllerType_LsiLogic;
+ elif sController == "BusLogic SCSI Controller":
+ eBus = vboxcon.StorageBus_SCSI;
+ eType = vboxcon.StorageControllerType_BusLogic;
+ elif sController == "NVMe Controller":
+ eBus = vboxcon.StorageBus_PCIe;
+ eType = vboxcon.StorageControllerType_NVMe;
+ elif sController == "VirtIO SCSI Controller":
+ eBus = vboxcon.StorageBus_VirtioSCSI;
+ eType = vboxcon.StorageControllerType_VirtioSCSI;
+ else:
+ eBus = vboxcon.StorageBus_Null;
+ eType = vboxcon.StorageControllerType_Null;
+ return (eBus, eType);
+
+
+def _nameMachineState(eState):
+ """ Gets the name (string) of a machine state."""
+ if eState == vboxcon.MachineState_PoweredOff: return 'PoweredOff';
+ if eState == vboxcon.MachineState_Saved: return 'Saved';
+ if eState == vboxcon.MachineState_Teleported: return 'Teleported';
+ if eState == vboxcon.MachineState_Aborted: return 'Aborted';
+ if eState == vboxcon.MachineState_Running: return 'Running';
+ if eState == vboxcon.MachineState_Paused: return 'Paused';
+ if eState == vboxcon.MachineState_Stuck: return 'GuruMeditation';
+ if eState == vboxcon.MachineState_Teleporting: return 'Teleporting';
+ if eState == vboxcon.MachineState_LiveSnapshotting: return 'LiveSnapshotting';
+ if eState == vboxcon.MachineState_Starting: return 'Starting';
+ if eState == vboxcon.MachineState_Stopping: return 'Stopping';
+ if eState == vboxcon.MachineState_Saving: return 'Saving';
+ if eState == vboxcon.MachineState_Restoring: return 'Restoring';
+ if eState == vboxcon.MachineState_TeleportingPausedVM: return 'TeleportingPausedVM';
+ if eState == vboxcon.MachineState_TeleportingIn: return 'TeleportingIn';
+ if eState == vboxcon.MachineState_DeletingSnapshotOnline: return 'DeletingSnapshotOnline';
+ if eState == vboxcon.MachineState_DeletingSnapshotPaused: return 'DeletingSnapshotPaused';
+ if eState == vboxcon.MachineState_RestoringSnapshot: return 'RestoringSnapshot';
+ if eState == vboxcon.MachineState_DeletingSnapshot: return 'DeletingSnapshot';
+ if eState == vboxcon.MachineState_SettingUp: return 'SettingUp';
+ if hasattr(vboxcon, 'MachineState_FaultTolerantSyncing'):
+ if eState == vboxcon.MachineState_FaultTolerantSyncing: return 'FaultTolerantSyncing';
+ if hasattr(vboxcon, 'MachineState_AbortedSaved'): # since r147033 / 7.0
+ if eState == vboxcon.MachineState_AbortedSaved: return 'Aborted-Saved';
+ return 'Unknown-%s' % (eState,);
+
+
+class VirtualBoxWrapper(object): # pylint: disable=too-few-public-methods
+ """
+ Wrapper around the IVirtualBox object that adds some (hopefully) useful
+ utility methods
+
+ The real object can be accessed thru the o member. That said, members can
+ be accessed directly as well.
+ """
+
+ def __init__(self, oVBox, oVBoxMgr, fpApiVer, oTstDrv):
+ self.o = oVBox;
+ self.oVBoxMgr = oVBoxMgr;
+ self.fpApiVer = fpApiVer;
+ self.oTstDrv = oTstDrv;
+
+ def __getattr__(self, sName):
+ # Try ourselves first.
+ try:
+ oAttr = self.__dict__[sName];
+ except:
+ #try:
+ # oAttr = dir(self)[sName];
+ #except AttributeError:
+ oAttr = getattr(self.o, sName);
+ return oAttr;
+
+ #
+ # Utilities.
+ #
+
+ def registerDerivedEventHandler(self, oSubClass, dArgs = None):
+ """
+ Create an instance of the given VirtualBoxEventHandlerBase sub-class
+ and register it.
+
+ The new instance is returned on success. None is returned on error.
+ """
+ dArgsCopy = dArgs.copy() if dArgs is not None else {};
+ dArgsCopy['oVBox'] = self;
+ return oSubClass.registerDerivedEventHandler(self.oVBoxMgr, self.fpApiVer, oSubClass, dArgsCopy,
+ self.o, 'IVirtualBox', 'IVirtualBoxCallback');
+
+ def deleteHdByLocation(self, sHdLocation):
+ """
+ Deletes a disk image from the host, given it's location.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ oIMedium = self.o.findHardDisk(sHdLocation);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oIMedium = self.o.openMedium(sHdLocation, vboxcon.DeviceType_HardDisk,
+ vboxcon.AccessMode_ReadWrite, False);
+ elif self.fpApiVer >= 4.0:
+ oIMedium = self.o.openMedium(sHdLocation, vboxcon.DeviceType_HardDisk,
+ vboxcon.AccessMode_ReadWrite);
+ else:
+ oIMedium = self.o.openHardDisk(sHdLocation, vboxcon.AccessMode_ReadOnly, False, "", False, "");
+ except:
+ return reporter.errorXcpt('failed to open hd "%s"' % (sHdLocation));
+ return self.deleteHdByMedium(oIMedium)
+
+ def deleteHdByMedium(self, oIMedium):
+ """
+ Deletes a disk image from the host, given an IMedium reference.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try: oProgressCom = oIMedium.deleteStorage();
+ except: return reporter.errorXcpt('deleteStorage() for disk %s failed' % (oIMedium,));
+ try: oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'delete disk %s' % (oIMedium.location));
+ except: return reporter.errorXcpt();
+ oProgress.wait();
+ oProgress.logResult();
+ return oProgress.isSuccess();
+
+
+
+class ProgressWrapper(TdTaskBase):
+ """
+ Wrapper around a progress object for making it a task and providing useful
+ utility methods.
+ The real progress object can be accessed thru the o member.
+ """
+
+ def __init__(self, oProgress, oVBoxMgr, oTstDrv, sName):
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.o = oProgress;
+ self.oVBoxMgr = oVBoxMgr;
+ self.oTstDrv = oTstDrv;
+ self.sName = sName;
+
+ def toString(self):
+ return '<%s sName=%s, oProgress=%s >' \
+ % (TdTaskBase.toString(self), self.sName, self.o);
+
+ #
+ # TdTaskBase overrides.
+ #
+
+ def pollTask(self, fLocked = False):
+ """
+ Overrides TdTaskBase.pollTask().
+
+ This method returns False until the progress object has completed.
+ """
+ self.doQuickApiTest();
+ try:
+ try:
+ if self.o.completed:
+ return True;
+ except:
+ pass;
+ finally:
+ self.oTstDrv.processPendingEvents();
+ return False;
+
+ def waitForTask(self, cMsTimeout = 0):
+ """
+ Overrides TdTaskBase.waitForTask().
+ Process XPCOM/COM events while waiting.
+ """
+ msStart = base.timestampMilli();
+ fState = self.pollTask(False);
+ while not fState:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ break;
+ cMsToWait = cMsTimeout - cMsElapsed;
+ cMsToWait = min(cMsToWait, 500);
+ try:
+ self.o.waitForCompletion(cMsToWait);
+ except KeyboardInterrupt: raise;
+ except: pass;
+ if self.fnProcessEvents:
+ self.fnProcessEvents();
+ reporter.doPollWork('ProgressWrapper.waitForTask');
+ fState = self.pollTask(False);
+ return fState;
+
+ #
+ # Utility methods.
+ #
+
+ def isSuccess(self):
+ """
+ Tests if the progress object completed successfully.
+ Returns True on success, False on failure or incomplete.
+ """
+ if not self.isCompleted():
+ return False;
+ return self.getResult() >= 0;
+
+ def isCompleted(self):
+ """
+ Wrapper around IProgress.completed.
+ """
+ return self.pollTask();
+
+ def isCancelable(self):
+ """
+ Wrapper around IProgress.cancelable.
+ """
+ try:
+ fRc = self.o.cancelable;
+ except:
+ reporter.logXcpt();
+ fRc = False;
+ return fRc;
+
+ def wasCanceled(self):
+ """
+ Wrapper around IProgress.canceled.
+ """
+ try:
+ fRc = self.o.canceled;
+ except:
+ reporter.logXcpt(self.sName);
+ fRc = False;
+ return fRc;
+
+ def cancel(self):
+ """
+ Wrapper around IProgress.cancel()
+ Returns True on success, False on failure (logged as error).
+ """
+ try:
+ self.o.cancel();
+ except:
+ reporter.errorXcpt(self.sName);
+ return False;
+ return True;
+
+ def getResult(self):
+ """
+ Wrapper around IProgress.resultCode.
+ """
+ try:
+ iRc = self.o.resultCode;
+ except:
+ reporter.logXcpt(self.sName);
+ iRc = -1;
+ return iRc;
+
+ def getErrInfoResultCode(self):
+ """
+ Wrapper around IProgress.errorInfo.resultCode.
+
+ Returns the string on success, -1 on bad objects (logged as error), and
+ -2 on missing errorInfo object.
+ """
+ iRc = -1;
+ try:
+ oErrInfo = self.o.errorInfo;
+ except:
+ reporter.errorXcpt(self.sName);
+ else:
+ if oErrInfo is None:
+ iRc = -2;
+ else:
+ try:
+ iRc = oErrInfo.resultCode;
+ except:
+ reporter.errorXcpt();
+ return iRc;
+
+ def getErrInfoText(self):
+ """
+ Wrapper around IProgress.errorInfo.text.
+
+ Returns the string on success, None on failure. Missing errorInfo is
+ not logged as an error, all other failures are.
+ """
+ sText = None;
+ try:
+ oErrInfo = self.o.errorInfo;
+ except:
+ reporter.log2Xcpt(self.sName);
+ else:
+ if oErrInfo is not None:
+ try:
+ sText = oErrInfo.text;
+ except:
+ reporter.errorXcpt();
+ return sText;
+
+ def stringifyErrorInfo(self):
+ """
+ Formats IProgress.errorInfo into a string.
+ """
+ try:
+ oErrInfo = self.o.errorInfo;
+ except:
+ reporter.logXcpt(self.sName);
+ sErr = 'no error info';
+ else:
+ sErr = vbox.stringifyErrorInfo(oErrInfo);
+ return sErr;
+
+ def stringifyResult(self):
+ """
+ Stringify the result.
+ """
+ if self.isCompleted():
+ if self.wasCanceled():
+ sRet = 'Progress %s: Canceled, hrc=%s' % (self.sName, vbox.ComError.toString(self.getResult()));
+ elif self.getResult() == 0:
+ sRet = 'Progress %s: Success' % (self.sName,);
+ elif self.getResult() > 0:
+ sRet = 'Progress %s: Success (hrc=%s)' % (self.sName, vbox.ComError.toString(self.getResult()));
+ else:
+ sRet = 'Progress %s: Failed! %s' % (self.sName, self.stringifyErrorInfo());
+ else:
+ sRet = 'Progress %s: Not completed yet...' % (self.sName);
+ return sRet;
+
+ def logResult(self, fIgnoreErrors = False):
+ """
+ Logs the result, failure logged as error unless fIgnoreErrors is True.
+ Return True on success, False on failure (and fIgnoreErrors is false).
+ """
+ sText = self.stringifyResult();
+ if self.isCompleted() and self.getResult() < 0 and fIgnoreErrors is False:
+ return reporter.error(sText);
+ reporter.log(sText);
+ return True;
+
+ def waitOnProgress(self, cMsInterval = 1000):
+ """
+ See vbox.TestDriver.waitOnProgress.
+ """
+ self.doQuickApiTest();
+ return self.oTstDrv.waitOnProgress(self.o, cMsInterval);
+
+ def wait(self, cMsTimeout = 60000, fErrorOnTimeout = True, cMsInterval = 1000):
+ """
+ Wait on the progress object for a while.
+
+ Returns the resultCode of the progress object if completed.
+ Returns -1 on timeout, logged as error if fErrorOnTimeout is set.
+ Returns -2 is the progress object is invalid or waitForCompletion
+ fails (logged as errors).
+ """
+ msStart = base.timestampMilli();
+ while True:
+ self.oTstDrv.processPendingEvents();
+ self.doQuickApiTest();
+ try:
+ if self.o.completed:
+ break;
+ except:
+ reporter.errorXcpt(self.sName);
+ return -2;
+ self.oTstDrv.processPendingEvents();
+
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ if fErrorOnTimeout:
+ reporter.error('Timing out after waiting for %u s on "%s"' % (cMsTimeout / 1000, self.sName))
+ return -1;
+
+ try:
+ self.o.waitForCompletion(cMsInterval);
+ except:
+ reporter.errorXcpt(self.sName);
+ return -2;
+ reporter.doPollWork('ProgressWrapper.wait');
+
+ try:
+ rc = self.o.resultCode;
+ except:
+ rc = -2;
+ reporter.errorXcpt(self.sName);
+ self.oTstDrv.processPendingEvents();
+ return rc;
+
+ def waitForOperation(self, iOperation, cMsTimeout = 60000, fErrorOnTimeout = True, cMsInterval = 1000, \
+ fIgnoreErrors = False):
+ """
+ Wait for the completion of a operation.
+
+ Negative iOperation values are relative to operationCount (this
+ property may changed at runtime).
+
+ Returns 0 if the operation completed normally.
+ Returns -1 on timeout, logged as error if fErrorOnTimeout is set.
+ Returns -2 is the progress object is invalid or waitForCompletion
+ fails (logged as errors).
+ Returns -3 if if the operation completed with an error, this is logged
+ as an error.
+ """
+ msStart = base.timestampMilli();
+ while True:
+ self.oTstDrv.processPendingEvents();
+ self.doQuickApiTest();
+ try:
+ iCurrentOperation = self.o.operation;
+ cOperations = self.o.operationCount;
+ if iOperation >= 0:
+ iRealOperation = iOperation;
+ else:
+ iRealOperation = cOperations + iOperation;
+
+ if iCurrentOperation > iRealOperation:
+ return 0;
+ if iCurrentOperation == iRealOperation \
+ and iRealOperation >= cOperations - 1 \
+ and self.o.completed:
+ if self.o.resultCode < 0:
+ self.logResult(fIgnoreErrors);
+ return -3;
+ return 0;
+ except:
+ if fIgnoreErrors:
+ reporter.logXcpt();
+ else:
+ reporter.errorXcpt();
+ return -2;
+ self.oTstDrv.processPendingEvents();
+
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ if fErrorOnTimeout:
+ if fIgnoreErrors:
+ reporter.log('Timing out after waiting for %s s on "%s" operation %d' \
+ % (cMsTimeout / 1000, self.sName, iOperation))
+ else:
+ reporter.error('Timing out after waiting for %s s on "%s" operation %d' \
+ % (cMsTimeout / 1000, self.sName, iOperation))
+ return -1;
+
+ try:
+ self.o.waitForOperationCompletion(iRealOperation, cMsInterval);
+ except:
+ if fIgnoreErrors:
+ reporter.logXcpt(self.sName);
+ else:
+ reporter.errorXcpt(self.sName);
+ return -2;
+ reporter.doPollWork('ProgressWrapper.waitForOperation');
+ # Not reached.
+ return -3; # Make pylin happy (for now).
+
+ def doQuickApiTest(self):
+ """
+ Queries everything that is stable and easy to get at and checks that
+ they don't throw errors.
+ """
+ if True is True: # pylint: disable=comparison-with-itself
+ try:
+ iPct = self.o.operationPercent;
+ sDesc = self.o.description;
+ fCancelable = self.o.cancelable;
+ cSecsRemain = self.o.timeRemaining;
+ fCanceled = self.o.canceled;
+ fCompleted = self.o.completed;
+ iOp = self.o.operation;
+ cOps = self.o.operationCount;
+ iOpPct = self.o.operationPercent;
+ sOpDesc = self.o.operationDescription;
+ except:
+ reporter.errorXcpt('%s: %s' % (self.sName, self.o,));
+ return False;
+ try:
+ # Very noisy -- only enable for debugging purposes.
+ #reporter.log2('%s: op=%u/%u/%s: %u%%; total=%u%% cancel=%s/%s compl=%s rem=%us; desc=%s' \
+ # % (self.sName, iOp, cOps, sOpDesc, iOpPct, iPct, fCanceled, fCancelable, fCompleted, \
+ # cSecsRemain, sDesc));
+ _ = iPct; _ = sDesc; _ = fCancelable; _ = cSecsRemain; _ = fCanceled; _ = fCompleted; _ = iOp;
+ _ = cOps; _ = iOpPct; _ = sOpDesc;
+ except:
+ reporter.errorXcpt();
+ return False;
+
+ return True;
+
+
+class SessionWrapper(TdTaskBase):
+ """
+ Wrapper around a machine session. The real session object can be accessed
+ thru the o member (short is good, right :-).
+ """
+
+ def __init__(self, oSession, oVM, oVBox, oVBoxMgr, oTstDrv, fRemoteSession, sFallbackName = None, sLogFile = None):
+ """
+ Initializes the session wrapper.
+ """
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.o = oSession;
+ self.oVBox = oVBox;
+ self.oVBoxMgr = oVBoxMgr;
+ self.oVM = oVM; # Not the session machine. Useful backdoor...
+ self.oTstDrv = oTstDrv;
+ self.fpApiVer = oTstDrv.fpApiVer;
+ self.fRemoteSession = fRemoteSession;
+ self.sLogFile = sLogFile;
+ self.oConsoleEventHandler = None;
+ self.uPid = None;
+ self.fPidFile = True;
+ self.fHostMemoryLow = False; # see signalHostMemoryLow; read-only for outsiders.
+
+ try:
+ self.sName = oSession.machine.name;
+ except:
+ if sFallbackName is not None:
+ self.sName = sFallbackName;
+ else:
+ try: self.sName = str(oSession.machine);
+ except: self.sName = 'is-this-vm-already-off'
+
+ try:
+ self.sUuid = oSession.machine.id;
+ except:
+ self.sUuid = None;
+
+ # Try cache the SessionPID.
+ self.getPid();
+
+ def __del__(self):
+ """
+ Destructor that makes sure the callbacks are deregistered and
+ that the session is closed.
+ """
+ self.deregisterEventHandlerForTask();
+
+ if self.o is not None:
+ try:
+ self.close();
+ reporter.log('close session %s' % (self.o));
+ except:
+ pass;
+ self.o = None;
+
+ TdTaskBase.__del__(self);
+
+ def toString(self):
+ return '<%s: sUuid=%s, sName=%s, uPid=%s, sDbgCreated=%s, fRemoteSession=%s, oSession=%s,' \
+ ' oConsoleEventHandler=%s, oVM=%s >' \
+ % (type(self).__name__, self.sUuid, self.sName, self.uPid, self.sDbgCreated, self.fRemoteSession,
+ self.o, self.oConsoleEventHandler, self.oVM,);
+
+ def __str__(self):
+ return self.toString();
+
+ #
+ # TdTaskBase overrides.
+ #
+
+ def __pollTask(self):
+ """ Internal poller """
+ # Poll for events after doing the remote GetState call, otherwise we
+ # might end up sleepless because XPCOM queues a cleanup event.
+ try:
+ try:
+ eState = self.o.machine.state;
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED):
+ reporter.logXcpt();
+ return True;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ # Switch
+ if eState == vboxcon.MachineState_Running:
+ return False;
+ if eState == vboxcon.MachineState_Paused:
+ return False;
+ if eState == vboxcon.MachineState_Teleporting:
+ return False;
+ if eState == vboxcon.MachineState_LiveSnapshotting:
+ return False;
+ if eState == vboxcon.MachineState_Starting:
+ return False;
+ if eState == vboxcon.MachineState_Stopping:
+ return False;
+ if eState == vboxcon.MachineState_Saving:
+ return False;
+ if eState == vboxcon.MachineState_Restoring:
+ return False;
+ if eState == vboxcon.MachineState_TeleportingPausedVM:
+ return False;
+ if eState == vboxcon.MachineState_TeleportingIn:
+ return False;
+
+ # *Beeep* fudge!
+ if self.fpApiVer < 3.2 \
+ and eState == vboxcon.MachineState_PoweredOff \
+ and self.getAgeAsMs() < 3000:
+ return False;
+
+ reporter.log('SessionWrapper::pollTask: eState=%s' % (eState));
+ return True;
+
+
+ def pollTask(self, fLocked = False):
+ """
+ Overrides TdTaskBase.pollTask().
+
+ This method returns False while the VM is online and running normally.
+ """
+
+ # Call super to check if the task was signalled by runtime error or similar,
+ # if not then check the VM state via __pollTask.
+ fRc = super(SessionWrapper, self).pollTask(fLocked);
+ if not fRc:
+ fRc = self.__pollTask();
+
+ # HACK ALERT: Lazily try registering the console event handler if
+ # we're not ready.
+ if not fRc and self.oConsoleEventHandler is None:
+ self.registerEventHandlerForTask();
+
+ # HACK ALERT: Lazily try get the PID and add it to the PID file.
+ if not fRc and self.uPid is None:
+ self.getPid();
+
+ return fRc;
+
+ def waitForTask(self, cMsTimeout = 0):
+ """
+ Overrides TdTaskBase.waitForTask().
+ Process XPCOM/COM events while waiting.
+ """
+ msStart = base.timestampMilli();
+ fState = self.pollTask(False);
+ while not fState:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ break;
+ cMsSleep = cMsTimeout - cMsElapsed;
+ cMsSleep = min(cMsSleep, 10000);
+ try: self.oVBoxMgr.waitForEvents(cMsSleep);
+ except KeyboardInterrupt: raise;
+ except: pass;
+ if self.fnProcessEvents:
+ self.fnProcessEvents();
+ reporter.doPollWork('SessionWrapper.waitForTask');
+ fState = self.pollTask(False);
+ return fState;
+
+ def setTaskOwner(self, oOwner):
+ """
+ HACK ALERT!
+ Overrides TdTaskBase.setTaskOwner() so we can try call
+ registerEventHandlerForTask() again when when the testdriver calls
+ addTask() after VM has been spawned. Related to pollTask() above.
+
+ The testdriver must not add the task too early for this to work!
+ """
+ if oOwner is not None:
+ self.registerEventHandlerForTask()
+ return TdTaskBase.setTaskOwner(self, oOwner);
+
+
+ #
+ # Task helpers.
+ #
+
+ def registerEventHandlerForTask(self):
+ """
+ Registers the console event handlers for working the task state.
+ """
+ if self.oConsoleEventHandler is not None:
+ return True;
+ self.oConsoleEventHandler = self.registerDerivedEventHandler(vbox.SessionConsoleEventHandler, {}, False);
+ return self.oConsoleEventHandler is not None;
+
+ def deregisterEventHandlerForTask(self):
+ """
+ Deregisters the console event handlers.
+ """
+ if self.oConsoleEventHandler is not None:
+ self.oConsoleEventHandler.unregister();
+ self.oConsoleEventHandler = None;
+
+ def signalHostMemoryLow(self):
+ """
+ Used by a runtime error event handler to indicate that we're low on memory.
+ Signals the task.
+ """
+ self.fHostMemoryLow = True;
+ self.signalTask();
+ return True;
+
+ def needsPoweringOff(self):
+ """
+ Examins the machine state to see if the VM needs powering off.
+ """
+ try:
+ try:
+ eState = self.o.machine.state;
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED):
+ reporter.logXcpt();
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ # Switch
+ if eState == vboxcon.MachineState_Running:
+ return True;
+ if eState == vboxcon.MachineState_Paused:
+ return True;
+ if eState == vboxcon.MachineState_Stuck:
+ return True;
+ if eState == vboxcon.MachineState_Teleporting:
+ return True;
+ if eState == vboxcon.MachineState_LiveSnapshotting:
+ return True;
+ if eState == vboxcon.MachineState_Starting:
+ return True;
+ if eState == vboxcon.MachineState_Saving:
+ return True;
+ if eState == vboxcon.MachineState_Restoring:
+ return True;
+ if eState == vboxcon.MachineState_TeleportingPausedVM:
+ return True;
+ if eState == vboxcon.MachineState_TeleportingIn:
+ return True;
+ if hasattr(vboxcon, 'MachineState_FaultTolerantSyncing'):
+ if eState == vboxcon.MachineState_FaultTolerantSyncing:
+ return True;
+ return False;
+
+ def assertPoweredOff(self):
+ """
+ Asserts that the VM is powered off, reporting an error if not.
+ Returns True if powered off, False + error msg if not.
+ """
+ try:
+ try:
+ eState = self.oVM.state;
+ except Exception:
+ reporter.errorXcpt();
+ return True;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ if eState == vboxcon.MachineState_PoweredOff:
+ return True;
+ reporter.error('Expected machine state "PoweredOff", machine is in the "%s" state instead.'
+ % (_nameMachineState(eState),));
+ return False;
+
+ def getMachineStateWithName(self):
+ """
+ Gets the current machine state both as a constant number/whatever and
+ as a human readable string. On error, the constants will be set to
+ None and the string will be the error message.
+ """
+ try:
+ eState = self.oVM.state;
+ except:
+ return (None, '[error getting state: %s]' % (self.oVBoxMgr.xcptToString(),));
+ finally:
+ self.oTstDrv.processPendingEvents();
+ return (eState, _nameMachineState(eState));
+
+ def reportPrematureTermination(self, sPrefix = ''):
+ """
+ Reports a premature virtual machine termination.
+ Returns False to facilitate simpler error paths.
+ """
+
+ reporter.error(sPrefix + 'The virtual machine terminated prematurely!!');
+ (enmState, sStateNm) = self.getMachineStateWithName();
+ reporter.error(sPrefix + 'Machine state: %s' % (sStateNm,));
+
+ if enmState is not None \
+ and enmState == vboxcon.MachineState_Aborted \
+ and self.uPid is not None:
+ #
+ # Look for process crash info.
+ #
+ def addCrashFile(sLogFile, fBinary):
+ """ processCollectCrashInfo callback. """
+ reporter.addLogFile(sLogFile, 'crash/dump/vm' if fBinary else 'crash/report/vm');
+ utils.processCollectCrashInfo(self.uPid, reporter.log, addCrashFile);
+
+ return False;
+
+
+
+ #
+ # ISession / IMachine / ISomethingOrAnother wrappers.
+ #
+
+ def close(self):
+ """
+ Closes the session if it's open and removes it from the
+ vbox.TestDriver.aoRemoteSessions list.
+ Returns success indicator.
+ """
+ fRc = True;
+ if self.o is not None:
+ # Get the pid in case we need to kill the process later on.
+ self.getPid();
+
+ # Try close it.
+ try:
+ if self.fpApiVer < 3.3:
+ self.o.close();
+ else:
+ self.o.unlockMachine();
+ self.o = None;
+ except KeyboardInterrupt:
+ raise;
+ except:
+ # Kludge to ignore VBoxSVC's closing of our session when the
+ # direct session closes / VM process terminates. Fun!
+ try: fIgnore = self.o.state == vboxcon.SessionState_Unlocked;
+ except: fIgnore = False;
+ if fIgnore:
+ self.o = None; # Must prevent a retry during GC.
+ else:
+ reporter.errorXcpt('ISession::unlockMachine failed on %s' % (self.o));
+ fRc = False;
+
+ # Remove it from the remote session list if applicable (not 100% clean).
+ if fRc and self.fRemoteSession:
+ try:
+ if self in self.oTstDrv.aoRemoteSessions:
+ reporter.log2('SessionWrapper::close: Removing myself from oTstDrv.aoRemoteSessions');
+ self.oTstDrv.aoRemoteSessions.remove(self)
+ except:
+ reporter.logXcpt();
+
+ if self.uPid is not None and self.fPidFile:
+ self.oTstDrv.pidFileRemove(self.uPid);
+ self.fPidFile = False;
+
+ # It's only logical to deregister the event handler after the session
+ # is closed. It also avoids circular references between the session
+ # and the listener, which causes trouble with garbage collection.
+ self.deregisterEventHandlerForTask();
+
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def saveSettings(self, fClose = False):
+ """
+ Saves the settings and optionally closes the session.
+ Returns success indicator.
+ """
+ try:
+ try:
+ self.o.machine.saveSettings();
+ except:
+ reporter.errorXcpt('saveSettings failed on %s' % (self.o));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+ if fClose:
+ return self.close();
+ return True;
+
+ def discardSettings(self, fClose = False):
+ """
+ Discards the settings and optionally closes the session.
+ """
+ try:
+ try:
+ self.o.machine.discardSettings();
+ except:
+ reporter.errorXcpt('discardSettings failed on %s' % (self.o));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+ if fClose:
+ return self.close();
+ return True;
+
+ def enableVirtEx(self, fEnable):
+ """
+ Enables or disables AMD-V/VT-x.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Enable/disable it.
+ fRc = True;
+ try:
+ self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_Enabled, fEnable);
+ except:
+ reporter.errorXcpt('failed to set HWVirtExPropertyType_Enabled=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set HWVirtExPropertyType_Enabled=%s for "%s"' % (fEnable, self.sName));
+
+ # Force/unforce it.
+ if fRc and hasattr(vboxcon, 'HWVirtExPropertyType_Force'):
+ try:
+ self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_Force, fEnable);
+ except:
+ reporter.errorXcpt('failed to set HWVirtExPropertyType_Force=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set HWVirtExPropertyType_Force=%s for "%s"' % (fEnable, self.sName));
+ else:
+ reporter.log('Warning! vboxcon has no HWVirtExPropertyType_Force attribute.');
+ ## @todo Modify CFGM to do the same for old VBox versions?
+
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableNestedPaging(self, fEnable):
+ """
+ Enables or disables nested paging..
+ Returns True on success and False on failure. Error information is logged.
+ """
+ ## @todo Add/remove force CFGM thing, we don't want fallback logic when testing.
+ fRc = True;
+ try:
+ self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging, fEnable);
+ except:
+ reporter.errorXcpt('failed to set HWVirtExPropertyType_NestedPaging=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set HWVirtExPropertyType_NestedPaging=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableLongMode(self, fEnable):
+ """
+ Enables or disables LongMode.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Supported.
+ if self.fpApiVer < 4.2 or not hasattr(vboxcon, 'HWVirtExPropertyType_LongMode'):
+ return True;
+
+ # Enable/disable it.
+ fRc = True;
+ try:
+ self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_LongMode, fEnable);
+ except:
+ reporter.errorXcpt('failed to set CPUPropertyType_LongMode=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set CPUPropertyType_LongMode=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableNestedHwVirt(self, fEnable):
+ """
+ Enables or disables Nested Hardware-Virtualization.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Supported.
+ if self.fpApiVer < 5.3 or not hasattr(vboxcon, 'CPUPropertyType_HWVirt'):
+ return True;
+
+ # Enable/disable it.
+ fRc = True;
+ try:
+ self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_HWVirt, fEnable);
+ except:
+ reporter.errorXcpt('failed to set CPUPropertyType_HWVirt=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set CPUPropertyType_HWVirt=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enablePae(self, fEnable):
+ """
+ Enables or disables PAE
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 3.2: # great, ain't it?
+ self.o.machine.setCPUProperty(vboxcon.CPUPropertyType_PAE, fEnable);
+ else:
+ self.o.machine.setCpuProperty(vboxcon.CpuPropertyType_PAE, fEnable);
+ except:
+ reporter.errorXcpt('failed to set CPUPropertyType_PAE=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set CPUPropertyType_PAE=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableIoApic(self, fEnable):
+ """
+ Enables or disables the IO-APIC
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.BIOSSettings.IOAPICEnabled = fEnable;
+ except:
+ reporter.errorXcpt('failed to set BIOSSettings.IOAPICEnabled=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set BIOSSettings.IOAPICEnabled=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableHpet(self, fEnable):
+ """
+ Enables or disables the HPET
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 4.2:
+ self.o.machine.HPETEnabled = fEnable;
+ else:
+ self.o.machine.hpetEnabled = fEnable;
+ except:
+ reporter.errorXcpt('failed to set HpetEnabled=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set HpetEnabled=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableUsbHid(self, fEnable):
+ """
+ Enables or disables the USB HID
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if fEnable:
+ if self.fpApiVer >= 4.3:
+ cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI);
+ if cOhciCtls == 0:
+ self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI);
+ else:
+ self.o.machine.usbController.enabled = True;
+
+ if self.fpApiVer >= 4.2:
+ self.o.machine.pointingHIDType = vboxcon.PointingHIDType_ComboMouse;
+ self.o.machine.keyboardHIDType = vboxcon.KeyboardHIDType_ComboKeyboard;
+ else:
+ self.o.machine.pointingHidType = vboxcon.PointingHidType_ComboMouse;
+ self.o.machine.keyboardHidType = vboxcon.KeyboardHidType_ComboKeyboard;
+ else:
+ if self.fpApiVer >= 4.2:
+ self.o.machine.pointingHIDType = vboxcon.PointingHIDType_PS2Mouse;
+ self.o.machine.keyboardHIDType = vboxcon.KeyboardHIDType_PS2Keyboard;
+ else:
+ self.o.machine.pointingHidType = vboxcon.PointingHidType_PS2Mouse;
+ self.o.machine.keyboardHidType = vboxcon.KeyboardHidType_PS2Keyboard;
+ except:
+ reporter.errorXcpt('failed to change UsbHid to %s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('changed UsbHid to %s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableUsbOhci(self, fEnable):
+ """
+ Enables or disables the USB OHCI controller
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if fEnable:
+ if self.fpApiVer >= 4.3:
+ cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI);
+ if cOhciCtls == 0:
+ self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI);
+ else:
+ self.o.machine.usbController.enabled = True;
+ else:
+ if self.fpApiVer >= 4.3:
+ cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI);
+ if cOhciCtls == 1:
+ self.o.machine.removeUSBController('OHCI');
+ else:
+ self.o.machine.usbController.enabled = False;
+ except:
+ reporter.errorXcpt('failed to change OHCI to %s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('changed OHCI to %s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableUsbEhci(self, fEnable):
+ """
+ Enables or disables the USB EHCI controller, enables also OHCI if it is still disabled.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if fEnable:
+ if self.fpApiVer >= 4.3:
+ cOhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_OHCI);
+ if cOhciCtls == 0:
+ self.o.machine.addUSBController('OHCI', vboxcon.USBControllerType_OHCI);
+
+ cEhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_EHCI);
+ if cEhciCtls == 0:
+ self.o.machine.addUSBController('EHCI', vboxcon.USBControllerType_EHCI);
+ else:
+ self.o.machine.usbController.enabled = True;
+ self.o.machine.usbController.enabledEHCI = True;
+ else:
+ if self.fpApiVer >= 4.3:
+ cEhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_EHCI);
+ if cEhciCtls == 1:
+ self.o.machine.removeUSBController('EHCI');
+ else:
+ self.o.machine.usbController.enabledEHCI = False;
+ except:
+ reporter.errorXcpt('failed to change EHCI to %s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('changed EHCI to %s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def enableUsbXhci(self, fEnable):
+ """
+ Enables or disables the USB XHCI controller. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if fEnable:
+ cXhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_XHCI);
+ if cXhciCtls == 0:
+ self.o.machine.addUSBController('XHCI', vboxcon.USBControllerType_XHCI);
+ else:
+ cXhciCtls = self.o.machine.getUSBControllerCountByType(vboxcon.USBControllerType_XHCI);
+ if cXhciCtls == 1:
+ self.o.machine.removeUSBController('XHCI');
+ except:
+ reporter.errorXcpt('failed to change XHCI to %s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('changed XHCI to %s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setFirmwareType(self, eType):
+ """
+ Sets the firmware type.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.firmwareType = eType;
+ except:
+ reporter.errorXcpt('failed to set firmwareType=%s for "%s"' % (eType, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set firmwareType=%s for "%s"' % (eType, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setChipsetType(self, eType):
+ """
+ Sets the chipset type.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.chipsetType = eType;
+ except:
+ reporter.errorXcpt('failed to set chipsetType=%s for "%s"' % (eType, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set chipsetType=%s for "%s"' % (eType, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setIommuType(self, eType):
+ """
+ Sets the IOMMU type.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Supported.
+ if self.fpApiVer < 6.2 or not hasattr(vboxcon, 'IommuType_Intel') or not hasattr(vboxcon, 'IommuType_AMD'):
+ return True;
+ fRc = True;
+ try:
+ self.o.machine.iommuType = eType;
+ except:
+ reporter.errorXcpt('failed to set iommuType=%s for "%s"' % (eType, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set iommuType=%s for "%s"' % (eType, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setupBootLogo(self, fEnable, cMsLogoDisplay = 0):
+ """
+ Sets up the boot logo. fEnable toggles the fade and boot menu
+ settings as well as the mode.
+ """
+ fRc = True;
+ try:
+ self.o.machine.BIOSSettings.logoFadeIn = not fEnable;
+ self.o.machine.BIOSSettings.logoFadeOut = not fEnable;
+ self.o.machine.BIOSSettings.logoDisplayTime = cMsLogoDisplay;
+ if fEnable:
+ self.o.machine.BIOSSettings.bootMenuMode = vboxcon.BIOSBootMenuMode_Disabled;
+ else:
+ self.o.machine.BIOSSettings.bootMenuMode = vboxcon.BIOSBootMenuMode_MessageAndMenu;
+ except:
+ reporter.errorXcpt('failed to set logoFadeIn/logoFadeOut/bootMenuMode=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+ else:
+ reporter.log('set logoFadeIn/logoFadeOut/bootMenuMode=%s for "%s"' % (fEnable, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setupVrdp(self, fEnable, uPort = None):
+ """
+ Configures VRDP.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.VRDEServer.enabled = fEnable;
+ else:
+ self.o.machine.VRDPServer.enabled = fEnable;
+ except:
+ reporter.errorXcpt('failed to set VRDEServer::enabled=%s for "%s"' % (fEnable, self.sName));
+ fRc = False;
+
+ if uPort is not None and fRc:
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.VRDEServer.setVRDEProperty("TCP/Ports", str(uPort));
+ else:
+ self.o.machine.VRDPServer.ports = str(uPort);
+ except:
+ reporter.errorXcpt('failed to set VRDEServer::ports=%s for "%s"' % (uPort, self.sName));
+ fRc = False;
+ if fRc:
+ reporter.log('set VRDEServer.enabled/ports=%s/%s for "%s"' % (fEnable, uPort, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def getNicDriverNameFromType(self, eNicType):
+ """
+ Helper that translate the adapter type into a driver name.
+ """
+ if eNicType in (vboxcon.NetworkAdapterType_Am79C970A, vboxcon.NetworkAdapterType_Am79C973):
+ sName = 'pcnet';
+ elif eNicType in (vboxcon.NetworkAdapterType_I82540EM,
+ vboxcon.NetworkAdapterType_I82543GC,
+ vboxcon.NetworkAdapterType_I82545EM):
+ sName = 'e1000';
+ elif eNicType == vboxcon.NetworkAdapterType_Virtio:
+ sName = 'virtio-net';
+ else:
+ reporter.error('Unknown adapter type "%s" (VM: "%s")' % (eNicType, self.sName));
+ sName = 'pcnet';
+ return sName;
+
+ def setupNatForwardingForTxs(self, iNic = 0, iHostPort = 5042):
+ """
+ Sets up NAT forwarding for port 5042 if applicable, cleans up if not.
+ """
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName));
+ return False;
+
+ # Nuke the old setup for all possible adapter types (in case we're
+ # called after it changed).
+ for sName in ('pcnet', 'e1000', 'virtio-net'):
+ for sConfig in ('VBoxInternal/Devices/%s/%u/LUN#0/AttachedDriver/Config' % (sName, iNic), \
+ 'VBoxInternal/Devices/%s/%u/LUN#0/Config' % (sName, iNic)):
+ try:
+ self.o.machine.setExtraData('%s/txs/Protocol' % (sConfig), '');
+ self.o.machine.setExtraData('%s/txs/HostPort' % (sConfig), '');
+ self.o.machine.setExtraData('%s/txs/GuestPort' % (sConfig), '');
+ except:
+ reporter.errorXcpt();
+
+ # Set up port forwarding if NAT attachment.
+ try:
+ eAttType = oNic.attachmentType;
+ except:
+ reporter.errorXcpt('attachmentType on %s failed for "%s"' % (iNic, self.sName));
+ return False;
+ if eAttType != vboxcon.NetworkAttachmentType_NAT:
+ return True;
+
+ try:
+ eNicType = oNic.adapterType;
+ fTraceEnabled = oNic.traceEnabled;
+ except:
+ reporter.errorXcpt('attachmentType/traceEnabled on %s failed for "%s"' % (iNic, self.sName));
+ return False;
+
+ if self.fpApiVer >= 4.1:
+ try:
+ if self.fpApiVer >= 4.2:
+ oNatEngine = oNic.NATEngine;
+ else:
+ oNatEngine = oNic.natDriver;
+ except:
+ reporter.errorXcpt('Failed to get INATEngine data on "%s"' % (self.sName));
+ return False;
+ try: oNatEngine.removeRedirect('txs');
+ except: pass;
+ try:
+ oNatEngine.addRedirect('txs', vboxcon.NATProtocol_TCP, '127.0.0.1', '%s' % (iHostPort), '', '5042');
+ except:
+ reporter.errorXcpt('Failed to add a addRedirect redirect on "%s"' % (self.sName));
+ return False;
+
+ else:
+ sName = self.getNicDriverNameFromType(eNicType);
+ if fTraceEnabled:
+ sConfig = 'VBoxInternal/Devices/%s/%u/LUN#0/AttachedDriver/Config' % (sName, iNic)
+ else:
+ sConfig = 'VBoxInternal/Devices/%s/%u/LUN#0/Config' % (sName, iNic)
+
+ try:
+ self.o.machine.setExtraData('%s/txs/Protocol' % (sConfig), 'TCP');
+ self.o.machine.setExtraData('%s/txs/HostPort' % (sConfig), '%s' % (iHostPort));
+ self.o.machine.setExtraData('%s/txs/GuestPort' % (sConfig), '5042');
+ except:
+ reporter.errorXcpt('Failed to set NAT extra data on "%s"' % (self.sName));
+ return False;
+ return True;
+
+ def setNicType(self, eType, iNic = 0):
+ """
+ Sets the NIC type of the specified NIC.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName));
+ return False;
+ try:
+ oNic.adapterType = eType;
+ except:
+ reporter.errorXcpt('failed to set NIC type on slot %s to %s for VM "%s"' % (iNic, eType, self.sName));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ if not self.setupNatForwardingForTxs(iNic):
+ return False;
+ reporter.log('set NIC type on slot %s to %s for VM "%s"' % (iNic, eType, self.sName));
+ return True;
+
+ def setNicTraceEnabled(self, fTraceEnabled, sTraceFile, iNic = 0):
+ """
+ Sets the NIC trace enabled flag and file path.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName));
+ return False;
+ try:
+ oNic.traceEnabled = fTraceEnabled;
+ oNic.traceFile = sTraceFile;
+ except:
+ reporter.errorXcpt('failed to set NIC trace flag on slot %s to %s for VM "%s"' \
+ % (iNic, fTraceEnabled, self.sName));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ if not self.setupNatForwardingForTxs(iNic):
+ return False;
+ reporter.log('set NIC trace on slot %s to "%s" (path "%s") for VM "%s"' %
+ (iNic, fTraceEnabled, sTraceFile, self.sName));
+ return True;
+
+ def getDefaultNicName(self, eAttachmentType):
+ """
+ Return the default network / interface name for the NIC attachment type.
+ """
+ sRetName = '';
+ if eAttachmentType == vboxcon.NetworkAttachmentType_Bridged:
+ if self.oTstDrv.sDefBridgedNic is not None:
+ sRetName = self.oTstDrv.sDefBridgedNic;
+ else:
+ sRetName = 'eth0';
+ try:
+ aoHostNics = self.oVBoxMgr.getArray(self.oVBox.host, 'networkInterfaces');
+ for oHostNic in aoHostNics:
+ if oHostNic.interfaceType == vboxcon.HostNetworkInterfaceType_Bridged \
+ and oHostNic.status == vboxcon.HostNetworkInterfaceStatus_Up:
+ sRetName = oHostNic.name;
+ break;
+ except:
+ reporter.errorXcpt();
+
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly:
+ try:
+ aoHostNics = self.oVBoxMgr.getArray(self.oVBox.host, 'networkInterfaces');
+ for oHostNic in aoHostNics:
+ if oHostNic.interfaceType == vboxcon.HostNetworkInterfaceType_HostOnly:
+ if oHostNic.status == vboxcon.HostNetworkInterfaceStatus_Up:
+ sRetName = oHostNic.name;
+ break;
+ if sRetName == '':
+ sRetName = oHostNic.name;
+ except:
+ reporter.errorXcpt();
+ if sRetName == '':
+ # Create a new host-only interface.
+ reporter.log("Creating host only NIC ...");
+ try:
+ (oIProgress, oIHostOnly) = self.oVBox.host.createHostOnlyNetworkInterface();
+ oProgress = ProgressWrapper(oIProgress, self.oVBoxMgr, self.oTstDrv, 'Create host only NIC');
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return '';
+ sRetName = oIHostOnly.name;
+ except:
+ reporter.errorXcpt();
+ return '';
+ reporter.log("Created host only NIC: '%s'" % (sRetName,));
+
+ elif self.fpApiVer >= 7.0 and eAttachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
+ aoHostNetworks = self.oVBoxMgr.getArray(self.oVBox, 'hostOnlyNetworks');
+ if aoHostNetworks:
+ sRetName = aoHostNetworks[0].networkName;
+ else:
+ try:
+ oHostOnlyNet = self.oVBox.createHostOnlyNetwork('Host-only Test Network');
+ oHostOnlyNet.lowerIP = '192.168.56.1';
+ oHostOnlyNet.upperIP = '192.168.56.199';
+ oHostOnlyNet.networkMask = '255.255.255.0';
+ sRetName = oHostOnlyNet.networkName;
+ except:
+ reporter.errorXcpt();
+ return '';
+
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal:
+ sRetName = 'VBoxTest';
+
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ sRetName = '';
+
+ else: ## @todo Support NetworkAttachmentType_NATNetwork
+ reporter.error('eAttachmentType=%s is not known' % (eAttachmentType));
+ return sRetName;
+
+ def setNicAttachment(self, eAttachmentType, sName = None, iNic = 0):
+ """
+ Sets the attachment type of the specified NIC.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName));
+ return False;
+
+ try:
+ if eAttachmentType is not None:
+ try:
+ if self.fpApiVer >= 4.1:
+ oNic.attachmentType = eAttachmentType;
+ else:
+ if eAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ oNic.attachToNAT();
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_Bridged:
+ oNic.attachToBridgedInterface();
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal:
+ oNic.attachToInternalNetwork();
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly:
+ oNic.attachToHostOnlyInterface();
+ else:
+ raise base.GenError("eAttachmentType=%s is invalid" % (eAttachmentType));
+ except:
+ reporter.errorXcpt('failed to set the attachment type on slot %s to %s for VM "%s"' \
+ % (iNic, eAttachmentType, self.sName));
+ return False;
+ else:
+ try:
+ eAttachmentType = oNic.attachmentType;
+ except:
+ reporter.errorXcpt('failed to get the attachment type on slot %s for VM "%s"' % (iNic, self.sName));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ if sName is not None:
+ # Resolve the special 'default' name.
+ if sName == 'default':
+ sName = self.getDefaultNicName(eAttachmentType);
+
+ # The name translate to different attributes depending on the
+ # attachment type.
+ try:
+ if eAttachmentType == vboxcon.NetworkAttachmentType_Bridged:
+ ## @todo check this out on windows, may have to do a
+ # translation of the name there or smth IIRC.
+ try:
+ if self.fpApiVer >= 4.1:
+ oNic.bridgedInterface = sName;
+ else:
+ oNic.hostInterface = sName;
+ except:
+ reporter.errorXcpt('failed to set the hostInterface property on slot %s to "%s" for VM "%s"'
+ % (iNic, sName, self.sName,));
+ return False;
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_HostOnly:
+ try:
+ if self.fpApiVer >= 4.1:
+ oNic.hostOnlyInterface = sName;
+ else:
+ oNic.hostInterface = sName;
+ except:
+ reporter.errorXcpt('failed to set the internalNetwork property on slot %s to "%s" for VM "%s"'
+ % (iNic, sName, self.sName,));
+ return False;
+ elif self.fpApiVer >= 7.0 and eAttachmentType == vboxcon.NetworkAttachmentType_HostOnlyNetwork:
+ try:
+ oNic.hostOnlyNetwork = sName;
+ except:
+ reporter.errorXcpt('failed to set the hostOnlyNetwork property on slot %s to "%s" for VM "%s"'
+ % (iNic, sName, self.sName,));
+ return False;
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_Internal:
+ try:
+ oNic.internalNetwork = sName;
+ except:
+ reporter.errorXcpt('failed to set the internalNetwork property on slot %s to "%s" for VM "%s"'
+ % (iNic, sName, self.sName,));
+ return False;
+ elif eAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ try:
+ oNic.NATNetwork = sName;
+ except:
+ reporter.errorXcpt('failed to set the NATNetwork property on slot %s to "%s" for VM "%s"'
+ % (iNic, sName, self.sName,));
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+
+ if not self.setupNatForwardingForTxs(iNic):
+ return False;
+ reporter.log('set NIC attachment type on slot %s to %s for VM "%s"' % (iNic, eAttachmentType, self.sName));
+ return True;
+
+ def setNicLocalhostReachable(self, fReachable, iNic = 0):
+ """
+ Sets whether the specified NIC can reach the host or not.
+ Only affects (enabled) NICs configured to NAT at the moment.
+
+ Returns True on success and False on failure. Error information is logged.
+ """
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ return reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName,));
+
+ try:
+ if not oNic.enabled: # NIC not enabled? Nothing to do here.
+ return True;
+ except:
+ return reporter.errorXcpt('NIC enabled status (%s) failed for "%s"' % (iNic, self.sName,));
+
+ reporter.log('Setting "LocalhostReachable" for network adapter in slot %d to %s' % (iNic, fReachable));
+
+ try:
+ oNatEngine = oNic.NATEngine;
+ except:
+ return reporter.errorXcpt('Getting NIC NAT engine (%s) failed for "%s"' % (iNic, self.sName,));
+
+ try:
+ if hasattr(oNatEngine, "localhostReachable"):
+ oNatEngine.localhostReachable = fReachable;
+ else:
+ oNatEngine.LocalhostReachable = fReachable;
+ except:
+ return reporter.errorXcpt('LocalhostReachable (%s) failed for "%s"' % (iNic, self.sName,));
+
+ return True;
+
+ def setNicMacAddress(self, sMacAddr, iNic = 0):
+ """
+ Sets the MAC address of the specified NIC.
+
+ The sMacAddr parameter is a string supplying the tail end of the MAC
+ address, missing quads are supplied from a constant byte (2), the IPv4
+ address of the host, and the NIC number.
+
+ Returns True on success and False on failure. Error information is logged.
+ """
+
+ # Resolve missing MAC address prefix by feeding in the host IP address bytes.
+ cchMacAddr = len(sMacAddr);
+ if 0 < cchMacAddr < 12:
+ sHostIP = netutils.getPrimaryHostIp();
+ abHostIP = socket.inet_aton(sHostIP);
+ if sys.version_info[0] < 3:
+ abHostIP = (ord(abHostIP[0]), ord(abHostIP[1]), ord(abHostIP[2]), ord(abHostIP[3]));
+
+ if abHostIP[0] == 127 \
+ or (abHostIP[0] == 169 and abHostIP[1] == 254) \
+ or (abHostIP[0] == 192 and abHostIP[1] == 168 and abHostIP[2] == 56):
+ return reporter.error('host IP for "%s" is %s, most likely not unique.' % (netutils.getHostnameFqdn(), sHostIP,));
+
+ sDefaultMac = '%02X%02X%02X%02X%02X%02X' % (0x02, abHostIP[0], abHostIP[1], abHostIP[2], abHostIP[3], iNic);
+ sMacAddr = sDefaultMac[0:(12 - cchMacAddr)] + sMacAddr;
+
+ # Get the NIC object and try set it address.
+ try:
+ oNic = self.o.machine.getNetworkAdapter(iNic);
+ except:
+ return reporter.errorXcpt('getNetworkAdapter(%s) failed for "%s"' % (iNic, self.sName,));
+
+ try:
+ oNic.MACAddress = sMacAddr;
+ except:
+ return reporter.errorXcpt('failed to set the MAC address on slot %s to "%s" for VM "%s"'
+ % (iNic, sMacAddr, self.sName));
+
+ reporter.log('set MAC address on slot %s to %s for VM "%s"' % (iNic, sMacAddr, self.sName,));
+ return True;
+
+ def setRamSize(self, cMB):
+ """
+ Set the RAM size of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.memorySize = cMB;
+ except:
+ reporter.errorXcpt('failed to set the RAM size of "%s" to %s' % (self.sName, cMB));
+ fRc = False;
+ else:
+ reporter.log('set the RAM size of "%s" to %s' % (self.sName, cMB));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setLargePages(self, fUseLargePages):
+ """
+ Configures whether the VM should use large pages or not.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.setHWVirtExProperty(vboxcon.HWVirtExPropertyType_LargePages, fUseLargePages);
+ except:
+ reporter.errorXcpt('failed to set large pages of "%s" to %s' % (self.sName, fUseLargePages));
+ fRc = False;
+ else:
+ reporter.log('set the large pages of "%s" to %s' % (self.sName, fUseLargePages));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setVRamSize(self, cMB):
+ """
+ Set the RAM size of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.VRAMSize = cMB;
+ else:
+ self.o.machine.VRAMSize = cMB;
+ except:
+ reporter.errorXcpt('failed to set the VRAM size of "%s" to %s' % (self.sName, cMB));
+ fRc = False;
+ else:
+ reporter.log('set the VRAM size of "%s" to %s' % (self.sName, cMB));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setVideoControllerType(self, eControllerType):
+ """
+ Set the video controller type of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.graphicsControllerType = eControllerType;
+ else:
+ self.o.machine.graphicsControllerType = eControllerType;
+ except:
+ reporter.errorXcpt('failed to set the video controller type of "%s" to %s' % (self.sName, eControllerType));
+ fRc = False;
+ else:
+ reporter.log('set the video controller type of "%s" to %s' % (self.sName, eControllerType));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setAccelerate3DEnabled(self, fEnabled):
+ """
+ Set the video controller type of the VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ if self.fpApiVer >= 6.1 and hasattr(self.o.machine, 'graphicsAdapter'):
+ self.o.machine.graphicsAdapter.accelerate3DEnabled = fEnabled;
+ else:
+ self.o.machine.accelerate3DEnabled = fEnabled;
+ except:
+ reporter.errorXcpt('failed to set the accelerate3DEnabled of "%s" to %s' % (self.sName, fEnabled));
+ fRc = False;
+ else:
+ reporter.log('set the accelerate3DEnabled of "%s" to %s' % (self.sName, fEnabled));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setCpuCount(self, cCpus):
+ """
+ Set the number of CPUs.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.CPUCount = cCpus;
+ except:
+ reporter.errorXcpt('failed to set the CPU count of "%s" to %s' % (self.sName, cCpus));
+ fRc = False;
+ else:
+ reporter.log('set the CPU count of "%s" to %s' % (self.sName, cCpus));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def getCpuCount(self):
+ """
+ Returns the number of CPUs.
+ Returns the number of CPUs on success and 0 on failure. Error information is logged.
+ """
+ cCpus = 0;
+ try:
+ cCpus = self.o.machine.CPUCount;
+ except:
+ reporter.errorXcpt('failed to get the CPU count of "%s"' % (self.sName,));
+
+ self.oTstDrv.processPendingEvents();
+ return cCpus;
+
+ def ensureControllerAttached(self, sController):
+ """
+ Makes sure the specified controller is attached to the VM, attaching it
+ if necessary.
+ """
+ try:
+ try:
+ self.o.machine.getStorageControllerByName(sController);
+ except:
+ (eBus, eType) = _ControllerNameToBusAndType(sController);
+ try:
+ oCtl = self.o.machine.addStorageController(sController, eBus);
+ except:
+ reporter.errorXcpt('addStorageController("%s",%s) failed on "%s"' % (sController, eBus, self.sName) );
+ return False;
+ else:
+ try:
+ oCtl.controllerType = eType;
+ reporter.log('added storage controller "%s" (bus %s, type %s) to %s'
+ % (sController, eBus, eType, self.sName));
+ except:
+ reporter.errorXcpt('controllerType = %s on ("%s" / %s) failed on "%s"'
+ % (eType, sController, eBus, self.sName) );
+ return False;
+ finally:
+ self.oTstDrv.processPendingEvents();
+ return True;
+
+ def setStorageControllerPortCount(self, sController, iPortCount):
+ """
+ Set maximum ports count for storage controller
+ """
+ try:
+ oCtl = self.o.machine.getStorageControllerByName(sController)
+ oCtl.portCount = iPortCount
+ self.oTstDrv.processPendingEvents()
+ reporter.log('set controller "%s" port count to value %d' % (sController, iPortCount))
+ return True
+ except:
+ reporter.log('unable to set storage controller "%s" ports count to %d' % (sController, iPortCount))
+
+ return False
+
+ def setStorageControllerHostIoCache(self, sController, fUseHostIoCache):
+ """
+ Set maximum ports count for storage controller
+ """
+ try:
+ oCtl = self.o.machine.getStorageControllerByName(sController);
+ oCtl.useHostIOCache = fUseHostIoCache;
+ self.oTstDrv.processPendingEvents();
+ reporter.log('set controller "%s" host I/O cache setting to %r' % (sController, fUseHostIoCache));
+ return True;
+ except:
+ reporter.log('unable to set storage controller "%s" host I/O cache setting to %r' % (sController, fUseHostIoCache));
+
+ return False;
+
+ def setBootOrder(self, iPosition, eType):
+ """
+ Set guest boot order type
+ @param iPosition boot order position
+ @param eType device type (vboxcon.DeviceType_HardDisk,
+ vboxcon.DeviceType_DVD, vboxcon.DeviceType_Floppy)
+ """
+ try:
+ self.o.machine.setBootOrder(iPosition, eType)
+ except:
+ return reporter.errorXcpt('Unable to set boot order.')
+
+ reporter.log('Set boot order [%d] for device %s' % (iPosition, str(eType)))
+ self.oTstDrv.processPendingEvents();
+
+ return True
+
+ def setStorageControllerType(self, eType, sController = "IDE Controller"):
+ """
+ Similar to ensureControllerAttached, except it will change the type.
+ """
+ try:
+ oCtl = self.o.machine.getStorageControllerByName(sController);
+ except:
+ (eBus, _) = _ControllerNameToBusAndType(sController);
+ try:
+ oCtl = self.o.machine.addStorageController(sController, eBus);
+ reporter.log('added storage controller "%s" (bus %s) to %s' % (sController, eBus, self.sName));
+ except:
+ reporter.errorXcpt('addStorageController("%s",%s) failed on "%s"' % (sController, eBus, self.sName) );
+ return False;
+ try:
+ oCtl.controllerType = eType;
+ except:
+ reporter.errorXcpt('failed to set controller type of "%s" on "%s" to %s' % (sController, self.sName, eType) );
+ return False;
+ reporter.log('set controller type of "%s" on "%s" to %s' % (sController, self.sName, eType) );
+ self.oTstDrv.processPendingEvents();
+ return True;
+
+ def attachDvd(self, sImage = None, sController = "IDE Controller", iPort = 1, iDevice = 0):
+ """
+ Attaches a DVD drive to a VM, optionally with an ISO inserted.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Input validation.
+ if sImage is not None and not self.oTstDrv.isResourceFile(sImage)\
+ and not os.path.isabs(sImage): ## fixme - testsuite unzip ++
+ reporter.fatal('"%s" is not in the resource set' % (sImage));
+ return None;
+
+ if not self.ensureControllerAttached(sController):
+ return False;
+
+ # Find/register the image if specified.
+ oImage = None;
+ sImageUuid = "";
+ if sImage is not None:
+ sFullName = self.oTstDrv.getFullResourceName(sImage)
+ try:
+ oImage = self.oVBox.findDVDImage(sFullName);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oImage = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_DVD, vboxcon.AccessMode_ReadOnly, False);
+ elif self.fpApiVer >= 4.0:
+ oImage = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_DVD, vboxcon.AccessMode_ReadOnly);
+ else:
+ oImage = self.oVBox.openDVDImage(sFullName, "");
+ except vbox.ComException as oXcpt:
+ if oXcpt.errno != -1:
+ reporter.errorXcpt('failed to open DVD image "%s" xxx' % (sFullName));
+ else:
+ reporter.errorXcpt('failed to open DVD image "%s" yyy' % (sFullName));
+ return False;
+ except:
+ reporter.errorXcpt('failed to open DVD image "%s"' % (sFullName));
+ return False;
+ try:
+ sImageUuid = oImage.id;
+ except:
+ reporter.errorXcpt('failed to get the UUID of "%s"' % (sFullName));
+ return False;
+
+ # Attach the DVD.
+ fRc = True;
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_DVD, oImage);
+ else:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_DVD, sImageUuid);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (sController, iPort, iDevice, sImageUuid, self.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached DVD to %s, image="%s"' % (self.sName, sImage));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def attachHd(self, sHd, sController = "IDE Controller", iPort = 0, iDevice = 0, fImmutable = True, fForceResource = True):
+ """
+ Attaches a HD to a VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Input validation.
+ if fForceResource and not self.oTstDrv.isResourceFile(sHd):
+ reporter.fatal('"%s" is not in the resource set' % (sHd,));
+ return None;
+
+ if not self.ensureControllerAttached(sController):
+ return False;
+
+ # Find the HD, registering it if necessary (as immutable).
+ if fForceResource:
+ sFullName = self.oTstDrv.getFullResourceName(sHd);
+ else:
+ sFullName = sHd;
+ try:
+ oHd = self.oVBox.findHardDisk(sFullName);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly, False);
+ elif self.fpApiVer >= 4.0:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly);
+ else:
+ oHd = self.oVBox.openHardDisk(sFullName, vboxcon.AccessMode_ReadOnly, False, "", False, "");
+ except:
+ reporter.errorXcpt('failed to open hd "%s"' % (sFullName));
+ return False;
+ try:
+ if fImmutable:
+ oHd.type = vboxcon.MediumType_Immutable;
+ else:
+ oHd.type = vboxcon.MediumType_Normal;
+ except:
+ if fImmutable:
+ reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd));
+ else:
+ reporter.errorXcpt('failed to set hd "%s" normal' % (sHd));
+ return False;
+
+ # Attach it.
+ fRc = True;
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (sController, iPort, iDevice, oHd.id, self.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sHd, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def createBaseHd(self, sHd, sFmt = "VDI", cb = 10*1024*1024*1024, cMsTimeout = 60000, tMediumVariant = None):
+ """
+ Creates a base HD.
+ Returns Medium object on success and None on failure. Error information is logged.
+ """
+ if tMediumVariant is None:
+ tMediumVariant = (vboxcon.MediumVariant_Standard, );
+
+ try:
+ if self.fpApiVer >= 5.0:
+ oHd = self.oVBox.createMedium(sFmt, sHd, vboxcon.AccessMode_ReadWrite, vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = self.oVBox.createHardDisk(sFmt, sHd);
+ oProgressXpcom = oHd.createBaseStorage(cb, tMediumVariant);
+ oProgress = ProgressWrapper(oProgressXpcom, self.oVBoxMgr, self.oTstDrv, 'create base disk %s' % (sHd));
+ oProgress.wait(cMsTimeout);
+ oProgress.logResult();
+ except:
+ reporter.errorXcpt('failed to create base hd "%s"' % (sHd));
+ oHd = None
+
+ return oHd;
+
+ def createDiffHd(self, oParentHd, sHd, sFmt = "VDI"):
+ """
+ Creates a differencing HD.
+ Returns Medium object on success and None on failure. Error information is logged.
+ """
+ # Detect the proper format if requested
+ if sFmt is None:
+ try:
+ oHdFmt = oParentHd.mediumFormat;
+ lstCaps = self.oVBoxMgr.getArray(oHdFmt, 'capabilities');
+ if vboxcon.MediumFormatCapabilities_Differencing in lstCaps:
+ sFmt = oHdFmt.id;
+ else:
+ sFmt = 'VDI';
+ except:
+ reporter.errorXcpt('failed to get preferred diff format for "%s"' % (sHd));
+ return None;
+ try:
+ if self.fpApiVer >= 5.0:
+ oHd = self.oVBox.createMedium(sFmt, sHd, vboxcon.AccessMode_ReadWrite, vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = self.oVBox.createHardDisk(sFmt, sHd);
+ oProgressXpcom = oParentHd.createDiffStorage(oHd, (vboxcon.MediumVariant_Standard, ))
+ oProgress = ProgressWrapper(oProgressXpcom, self.oVBoxMgr, self.oTstDrv, 'create diff disk %s' % (sHd));
+ oProgress.wait();
+ oProgress.logResult();
+ except:
+ reporter.errorXcpt('failed to create diff hd "%s"' % (sHd));
+ oHd = None
+
+ return oHd;
+
+ def createAndAttachHd(self, sHd, sFmt = "VDI", sController = "IDE Controller", cb = 10*1024*1024*1024, # pylint: disable=too-many-arguments
+ iPort = 0, iDevice = 0, fImmutable = True, cMsTimeout = 60000, tMediumVariant = None):
+ """
+ Creates and attaches a HD to a VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ if not self.ensureControllerAttached(sController):
+ return False;
+
+ oHd = self.createBaseHd(sHd, sFmt, cb, cMsTimeout, tMediumVariant);
+ if oHd is None:
+ return False;
+
+ fRc = True;
+ try:
+ if fImmutable:
+ oHd.type = vboxcon.MediumType_Immutable;
+ else:
+ oHd.type = vboxcon.MediumType_Normal;
+ except:
+ if fImmutable:
+ reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd));
+ else:
+ reporter.errorXcpt('failed to set hd "%s" normal' % (sHd));
+ fRc = False;
+
+ # Attach it.
+ if fRc is True:
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (sController, iPort, iDevice, oHd.id, self.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sHd, self.sName));
+
+ # Delete disk in case of an error
+ if fRc is False:
+ try:
+ oProgressCom = oHd.deleteStorage();
+ except:
+ reporter.errorXcpt('deleteStorage() for disk %s failed' % (sHd,));
+ else:
+ oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'delete disk %s' % (sHd));
+ oProgress.wait();
+ oProgress.logResult();
+
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def detachHd(self, sController = "IDE Controller", iPort = 0, iDevice = 0):
+ """
+ Detaches a HD, if attached, and returns a reference to it (IMedium).
+
+ In order to delete the detached medium, the caller must first save
+ the changes made in this session.
+
+ Returns (fRc, oHd), where oHd is None unless fRc is True, and fRc is
+ your standard success indicator. Error information is logged.
+ """
+
+ # What's attached?
+ try:
+ oHd = self.o.machine.getMedium(sController, iPort, iDevice);
+ except:
+ if self.oVBoxMgr.xcptIsOurXcptKind() \
+ and self.oVBoxMgr.xcptIsEqual(None, self.oVBoxMgr.constants.VBOX_E_OBJECT_NOT_FOUND):
+ reporter.log('No HD attached (to %s %s:%s)' % (sController, iPort, iDevice));
+ return (True, None);
+ return (reporter.errorXcpt('Error getting media at port %s, device %s, on %s.'
+ % (iPort, iDevice, sController)), None);
+ # Detach it.
+ try:
+ self.o.machine.detachDevice(sController, iPort, iDevice);
+ except:
+ return (reporter.errorXcpt('detachDevice("%s",%s,%s) failed on "%s"' \
+ % (sController, iPort, iDevice, self.sName) ), None);
+ reporter.log('detached HD ("%s",%s,%s) from %s' % (sController, iPort, iDevice, self.sName));
+ return (True, oHd);
+
+ def attachFloppy(self, sFloppy, sController = "Floppy Controller", iPort = 0, iDevice = 0):
+ """
+ Attaches a floppy image to a VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ # Input validation.
+ ## @todo Fix this wrt to bootsector-xxx.img from the validationkit.zip.
+ ##if not self.oTstDrv.isResourceFile(sFloppy):
+ ## reporter.fatal('"%s" is not in the resource set' % (sFloppy));
+ ## return None;
+
+ if not self.ensureControllerAttached(sController):
+ return False;
+
+ # Find the floppy image, registering it if necessary (as immutable).
+ sFullName = self.oTstDrv.getFullResourceName(sFloppy);
+ try:
+ oFloppy = self.oVBox.findFloppyImage(sFullName);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oFloppy = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_Floppy, vboxcon.AccessMode_ReadOnly, False);
+ elif self.fpApiVer >= 4.0:
+ oFloppy = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_Floppy, vboxcon.AccessMode_ReadOnly);
+ else:
+ oFloppy = self.oVBox.openFloppyImage(sFullName, "");
+ except:
+ reporter.errorXcpt('failed to open floppy "%s"' % (sFullName));
+ return False;
+ ## @todo the following works but causes trouble below (asserts in main).
+ #try:
+ # oFloppy.type = vboxcon.MediumType_Immutable;
+ #except:
+ # reporter.errorXcpt('failed to make floppy "%s" immutable' % (sFullName));
+ # return False;
+
+ # Attach it.
+ fRc = True;
+ try:
+ if self.fpApiVer >= 4.0:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_Floppy, oFloppy);
+ else:
+ self.o.machine.attachDevice(sController, iPort, iDevice, vboxcon.DeviceType_Floppy, oFloppy.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,Floppy,"%s") failed on "%s"' \
+ % (sController, iPort, iDevice, oFloppy.id, self.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sFloppy, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ def setupNic(self, sType, sXXX):
+ """
+ Sets up a NIC to a VM.
+ Returns True on success and False on failure. Error information is logged.
+ """
+ if sType == "PCNet": enmType = vboxcon.NetworkAdapterType_Am79C973;
+ elif sType == "PCNetOld": enmType = vboxcon.NetworkAdapterType_Am79C970A;
+ elif sType == "E1000": enmType = vboxcon.NetworkAdapterType_I82545EM; # MT Server
+ elif sType == "E1000Desk": enmType = vboxcon.NetworkAdapterType_I82540EM; # MT Desktop
+ elif sType == "E1000Srv2": enmType = vboxcon.NetworkAdapterType_I82543GC; # T Server
+ elif sType == "Virtio": enmType = vboxcon.NetworkAdapterType_Virtio;
+ else:
+ reporter.error('Invalid NIC type: "%s" (sXXX=%s)' % (sType, sXXX));
+ return False;
+ ## @todo Implement me!
+ if enmType is not None: pass
+ return True;
+
+ def setupAudio(self, eAudioControllerType, fEnable = True, fEnableIn = False, fEnableOut = True, eAudioDriverType = None):
+ """
+ Sets up audio.
+
+ :param eAudioControllerType: The audio controller type (vboxcon.AudioControllerType_XXX).
+ :param fEnable: Whether to enable or disable the audio controller (default enable).
+ :param fEnableIn: Whether to enable or disable audio input (default disable).
+ :param fEnableOut: Whether to enable or disable audio output (default enable).
+ :param eAudioDriverType: The audio driver type (vboxcon.AudioDriverType_XXX), picks something suitable
+ if None is passed (default).
+ """
+ try:
+ if self.fpApiVer >= 7.0:
+ oAdapter = self.o.machine.audioSettings.adapter;
+ else:
+ oAdapter = self.o.machine.audioAdapter;
+ except: return reporter.errorXcpt('Failed to get the audio adapter.');
+
+ try: oAdapter.audioController = eAudioControllerType;
+ except: return reporter.errorXcpt('Failed to set the audio controller to %s.' % (eAudioControllerType,));
+
+ if eAudioDriverType is None:
+ sHost = utils.getHostOs()
+ if sHost == 'darwin': eAudioDriverType = vboxcon.AudioDriverType_CoreAudio;
+ elif sHost == 'win': eAudioDriverType = vboxcon.AudioDriverType_DirectSound;
+ elif sHost == 'linux': eAudioDriverType = vboxcon.AudioDriverType_Pulse;
+ elif sHost == 'solaris': eAudioDriverType = vboxcon.AudioDriverType_OSS;
+ else:
+ reporter.error('PORTME: Do not know which audio driver to pick for: %s!' % (sHost,));
+ eAudioDriverType = vboxcon.AudioDriverType_Null;
+
+ try: oAdapter.audioDriver = eAudioDriverType;
+ except: return reporter.errorXcpt('Failed to set the audio driver to %s.' % (eAudioDriverType,))
+
+ try: oAdapter.enabled = fEnable;
+ except: return reporter.errorXcpt('Failed to set the "enabled" property to %s.' % (fEnable,));
+
+ try: oAdapter.enabledIn = fEnableIn;
+ except: return reporter.errorXcpt('Failed to set the "enabledIn" property to %s.' % (fEnable,));
+
+ try: oAdapter.enabledOut = fEnableOut;
+ except: return reporter.errorXcpt('Failed to set the "enabledOut" property to %s.' % (fEnable,));
+
+ reporter.log('set audio adapter type to %d, driver to %d, and enabled to %s (input is %s, output is %s)'
+ % (eAudioControllerType, eAudioDriverType, fEnable, fEnableIn, fEnableOut,));
+ self.oTstDrv.processPendingEvents();
+ return True;
+
+ def setupPreferredConfig(self): # pylint: disable=too-many-locals
+ """
+ Configures the VM according to the preferences of the guest type.
+ """
+ try:
+ sOsTypeId = self.o.machine.OSTypeId;
+ except:
+ reporter.errorXcpt('failed to obtain the OSTypeId for "%s"' % (self.sName));
+ return False;
+
+ try:
+ oOsType = self.oVBox.getGuestOSType(sOsTypeId);
+ except:
+ reporter.errorXcpt('getGuestOSType("%s") failed for "%s"' % (sOsTypeId, self.sName));
+ return False;
+
+ # get the attributes.
+ try:
+ #sFamilyId = oOsType.familyId;
+ #f64Bit = oOsType.is64Bit;
+ fIoApic = oOsType.recommendedIOAPIC;
+ fVirtEx = oOsType.recommendedVirtEx;
+ cMBRam = oOsType.recommendedRAM;
+ cMBVRam = oOsType.recommendedVRAM;
+ #cMBHdd = oOsType.recommendedHDD;
+ eNicType = oOsType.adapterType;
+ if self.fpApiVer >= 3.2:
+ if self.fpApiVer >= 4.2:
+ fPae = oOsType.recommendedPAE;
+ fUsbHid = oOsType.recommendedUSBHID;
+ fHpet = oOsType.recommendedHPET;
+ eStorCtlType = oOsType.recommendedHDStorageController;
+ else:
+ fPae = oOsType.recommendedPae;
+ fUsbHid = oOsType.recommendedUsbHid;
+ fHpet = oOsType.recommendedHpet;
+ eStorCtlType = oOsType.recommendedHdStorageController;
+ eFirmwareType = oOsType.recommendedFirmware;
+ else:
+ fPae = False;
+ fUsbHid = False;
+ fHpet = False;
+ eFirmwareType = -1;
+ eStorCtlType = vboxcon.StorageControllerType_PIIX4;
+ if self.fpApiVer >= 4.0:
+ eAudioCtlType = oOsType.recommendedAudioController;
+ except:
+ reporter.errorXcpt('exception reading IGuestOSType(%s) attribute' % (sOsTypeId));
+ self.oTstDrv.processPendingEvents();
+ return False;
+ self.oTstDrv.processPendingEvents();
+
+ # Do the setting. Continue applying settings on error in case the
+ # caller ignores the return code
+ fRc = True;
+ if not self.enableIoApic(fIoApic): fRc = False;
+ if not self.enableVirtEx(fVirtEx): fRc = False;
+ if not self.enablePae(fPae): fRc = False;
+ if not self.setRamSize(cMBRam): fRc = False;
+ if not self.setVRamSize(cMBVRam): fRc = False;
+ if not self.setNicType(eNicType, 0): fRc = False;
+ if self.fpApiVer >= 3.2:
+ if not self.setFirmwareType(eFirmwareType): fRc = False;
+ if not self.enableUsbHid(fUsbHid): fRc = False;
+ if not self.enableHpet(fHpet): fRc = False;
+ if eStorCtlType in (vboxcon.StorageControllerType_PIIX3,
+ vboxcon.StorageControllerType_PIIX4,
+ vboxcon.StorageControllerType_ICH6,):
+ if not self.setStorageControllerType(eStorCtlType, "IDE Controller"):
+ fRc = False;
+ if self.fpApiVer >= 4.0:
+ if not self.setupAudio(eAudioCtlType): fRc = False;
+
+ return fRc;
+
+ def addUsbDeviceFilter(self, sName, sVendorId = None, sProductId = None, sRevision = None, # pylint: disable=too-many-arguments
+ sManufacturer = None, sProduct = None, sSerialNumber = None,
+ sPort = None, sRemote = None):
+ """
+ Creates a USB device filter and inserts it into the VM.
+ Returns True on success.
+ Returns False on failure (logged).
+ """
+ fRc = True;
+
+ try:
+ oUsbDevFilter = self.o.machine.USBDeviceFilters.createDeviceFilter(sName);
+ oUsbDevFilter.active = True;
+ if sVendorId is not None:
+ oUsbDevFilter.vendorId = sVendorId;
+ if sProductId is not None:
+ oUsbDevFilter.productId = sProductId;
+ if sRevision is not None:
+ oUsbDevFilter.revision = sRevision;
+ if sManufacturer is not None:
+ oUsbDevFilter.manufacturer = sManufacturer;
+ if sProduct is not None:
+ oUsbDevFilter.product = sProduct;
+ if sSerialNumber is not None:
+ oUsbDevFilter.serialnumber = sSerialNumber;
+ if sPort is not None:
+ oUsbDevFilter.port = sPort;
+ if sRemote is not None:
+ oUsbDevFilter.remote = sRemote;
+ try:
+ self.o.machine.USBDeviceFilters.insertDeviceFilter(0, oUsbDevFilter);
+ except:
+ reporter.errorXcpt('insertDeviceFilter(%s) failed on "%s"' \
+ % (0, self.sName) );
+ fRc = False;
+ else:
+ reporter.log('inserted USB device filter "%s" to %s' % (sName, self.sName));
+ except:
+ reporter.errorXcpt('createDeviceFilter("%s") failed on "%s"' \
+ % (sName, self.sName) );
+ fRc = False;
+ return fRc;
+
+ def getGuestPropertyValue(self, sName):
+ """
+ Gets a guest property value.
+ Returns the value on success, None on failure (logged).
+ """
+ try:
+ sValue = self.o.machine.getGuestPropertyValue(sName);
+ except:
+ reporter.errorXcpt('IMachine::getGuestPropertyValue("%s") failed' % (sName));
+ return None;
+ return sValue;
+
+ def setGuestPropertyValue(self, sName, sValue):
+ """
+ Sets a guest property value.
+ Returns the True on success, False on failure (logged).
+ """
+ try:
+ self.o.machine.setGuestPropertyValue(sName, sValue);
+ except:
+ reporter.errorXcpt('IMachine::setGuestPropertyValue("%s","%s") failed' % (sName, sValue));
+ return False;
+ return True;
+
+ def delGuestPropertyValue(self, sName):
+ """
+ Deletes a guest property value.
+ Returns the True on success, False on failure (logged).
+ """
+ try:
+ oMachine = self.o.machine;
+ if self.fpApiVer >= 4.2:
+ oMachine.deleteGuestProperty(sName);
+ else:
+ oMachine.setGuestPropertyValue(sName, '');
+ except:
+ reporter.errorXcpt('Unable to delete guest property "%s"' % (sName,));
+ return False;
+ return True;
+
+ def setExtraData(self, sKey, sValue):
+ """
+ Sets extra data.
+ Returns the True on success, False on failure (logged).
+ """
+ try:
+ self.o.machine.setExtraData(sKey, sValue);
+ except:
+ reporter.errorXcpt('IMachine::setExtraData("%s","%s") failed' % (sKey, sValue));
+ return False;
+ return True;
+
+ def getExtraData(self, sKey):
+ """
+ Gets extra data.
+ Returns value on success, None on failure.
+ """
+ try:
+ sValue = self.o.machine.getExtraData(sKey)
+ except:
+ reporter.errorXcpt('IMachine::setExtraData("%s","%s") failed' % (sKey, sValue))
+ return None
+ return sValue
+
+ def setupTeleporter(self, fEnabled=True, uPort = 6500, sAddress = '', sPassword = ''):
+ """
+ Sets up the teleporter for the VM.
+ Returns True on success, False on failure (logged).
+ """
+ try:
+ self.o.machine.teleporterAddress = sAddress;
+ self.o.machine.teleporterPort = uPort;
+ self.o.machine.teleporterPassword = sPassword;
+ self.o.machine.teleporterEnabled = fEnabled;
+ except:
+ reporter.errorXcpt('setupTeleporter(%s, %s, %s, %s)' % (fEnabled, sPassword, uPort, sAddress));
+ return False;
+ return True;
+
+ def enableTeleporter(self, fEnable=True):
+ """
+ Enables or disables the teleporter of the VM.
+ Returns True on success, False on failure (logged).
+ """
+ try:
+ self.o.machine.teleporterEnabled = fEnable;
+ except:
+ reporter.errorXcpt('IMachine::teleporterEnabled=%s failed' % (fEnable));
+ return False;
+ return True;
+
+ def teleport(self, sHostname = 'localhost', uPort = 6500, sPassword = 'password', cMsMaxDowntime = 250):
+ """
+ Wrapper around the IConsole::teleport() method.
+ Returns a progress object on success, None on failure (logged).
+ """
+ reporter.log2('"%s"::teleport(%s,%s,%s,%s)...' % (self.sName, sHostname, uPort, sPassword, cMsMaxDowntime));
+ try:
+ oProgress = self.o.console.teleport(sHostname, uPort, sPassword, cMsMaxDowntime)
+ except:
+ reporter.errorXcpt('IConsole::teleport(%s,%s,%s,%s) failed' % (sHostname, uPort, sPassword, cMsMaxDowntime));
+ return None;
+ return ProgressWrapper(oProgress, self.oVBoxMgr, self.oTstDrv, 'teleport %s' % (self.sName,));
+
+ def getOsType(self):
+ """
+ Gets the IGuestOSType interface for the machine.
+
+ return IGuestOSType interface on success, None + errorXcpt on failure.
+ No exceptions raised.
+ """
+ try:
+ sOsTypeId = self.o.machine.OSTypeId;
+ except:
+ reporter.errorXcpt('failed to obtain the OSTypeId for "%s"' % (self.sName));
+ return None;
+
+ try:
+ oOsType = self.oVBox.getGuestOSType(sOsTypeId);
+ except:
+ reporter.errorXcpt('getGuestOSType("%s") failed for "%s"' % (sOsTypeId, self.sName));
+ return None;
+
+ return oOsType;
+
+ def setOsType(self, sNewTypeId):
+ """
+ Changes the OS type.
+
+ returns True on success, False + errorXcpt on failure.
+ No exceptions raised.
+ """
+ try:
+ self.o.machine.OSTypeId = sNewTypeId;
+ except:
+ reporter.errorXcpt('failed to set the OSTypeId for "%s" to "%s"' % (self.sName, sNewTypeId));
+ return False;
+ return True;
+
+
+ def setParavirtProvider(self, iProvider):
+ """
+ Sets a paravirtualisation provider.
+ Returns the True on success, False on failure (logged).
+ """
+ try:
+ self.o.machine.paravirtProvider = iProvider
+ except:
+ reporter.errorXcpt('Unable to set paravirtualisation provider "%s"' % (iProvider,))
+ return False;
+ return True;
+
+
+ def setupSerialToRawFile(self, iSerialPort, sRawFile):
+ """
+ Enables the given serial port (zero based) and redirects it to sRawFile.
+ Returns the True on success, False on failure (logged).
+ """
+ try:
+ oPort = self.o.machine.getSerialPort(iSerialPort);
+ except:
+ fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,));
+ else:
+ try:
+ oPort.path = sRawFile;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "path" property on serial port #%u to "%s"'
+ % (iSerialPort, sRawFile));
+ else:
+ try:
+ oPort.hostMode = vboxcon.PortMode_RawFile;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_RawFile'
+ % (iSerialPort,));
+ else:
+ try:
+ oPort.enabled = True;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "enable" property on serial port #%u to True'
+ % (iSerialPort,));
+ else:
+ reporter.log('set SerialPort[%s].enabled/hostMode/path=True/RawFile/%s' % (iSerialPort, sRawFile,));
+ fRc = True;
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+
+ def enableSerialPort(self, iSerialPort):
+ """
+ Enables the given serial port setting the initial port mode to disconnected.
+ """
+ try:
+ oPort = self.o.machine.getSerialPort(iSerialPort);
+ except:
+ fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,));
+ else:
+ try:
+ oPort.hostMode = vboxcon.PortMode_Disconnected;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_Disconnected'
+ % (iSerialPort,));
+ else:
+ try:
+ oPort.enabled = True;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "enable" property on serial port #%u to True'
+ % (iSerialPort,));
+ else:
+ reporter.log('set SerialPort[%s].enabled/hostMode/=True/Disconnected' % (iSerialPort,));
+ fRc = True;
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+
+ def changeSerialPortAttachment(self, iSerialPort, ePortMode, sPath, fServer):
+ """
+ Changes the attachment of the given serial port to the attachment config given.
+ """
+ try:
+ oPort = self.o.machine.getSerialPort(iSerialPort);
+ except:
+ fRc = reporter.errorXcpt('failed to get serial port #%u' % (iSerialPort,));
+ else:
+ try:
+ # Change port mode to disconnected first so changes get picked up by a potentially running VM.
+ oPort.hostMode = vboxcon.PortMode_Disconnected;
+ except:
+ fRc = reporter.errorXcpt('failed to set the "hostMode" property on serial port #%u to PortMode_Disconnected'
+ % (iSerialPort,));
+ else:
+ try:
+ oPort.path = sPath;
+ oPort.server = fServer;
+ oPort.hostMode = ePortMode;
+ except:
+ fRc = reporter.errorXcpt('failed to configure the serial port');
+ else:
+ reporter.log('set SerialPort[%s].hostMode/path/server=%s/%s/%s'
+ % (iSerialPort, ePortMode, sPath, fServer));
+ fRc = True;
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ #
+ # IConsole wrappers.
+ #
+
+ def powerOff(self, fFudgeOnFailure = True):
+ """
+ Powers off the VM.
+
+ Returns True on success.
+ Returns False on IConsole::powerDown() failure.
+ Returns None if the progress object returns failure.
+ """
+ #
+ # Deregister event handler before we power off the VM, otherwise we're
+ # racing for VM process termination and cause misleading spurious
+ # error messages in the event handling code, because the event objects
+ # disappear.
+ #
+ # Note! Doing this before powerDown to try prevent numerous smoketest
+ # timeouts on XPCOM hosts.
+ #
+ self.deregisterEventHandlerForTask();
+
+
+ # Try power if off.
+ try:
+ oProgress = self.o.console.powerDown();
+ except:
+ reporter.logXcpt('IConsole::powerDown failed on %s' % (self.sName));
+ if fFudgeOnFailure:
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ self.waitForTask(1000); # fudge
+ return False;
+
+ # Wait on power off operation to complete.
+ rc = self.oTstDrv.waitOnProgress(oProgress);
+ if rc < 0:
+ self.close();
+ if fFudgeOnFailure:
+ vbox.reportError(oProgress, 'powerDown for "%s" failed' % (self.sName));
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ return None;
+
+ # Wait for the VM to really power off or we'll fail to open a new session to it.
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ return self.waitForTask(30 * 1000); # fudge
+
+ def saveState(self, fPause = True):
+ """
+ Saves state of the VM.
+
+ Returns True on success.
+ Returns False on IConsole::saveState() failure.
+ Returns None if the progress object returns Failure.
+ """
+
+ if fPause is True \
+ and self.oVM.state is vboxcon.MachineState_Running:
+ self.o.console.pause();
+ if self.oVM.state is not vboxcon.MachineState_Paused:
+ reporter.error('pause for "%s" failed' % (self.sName));
+ # Try saving state.
+ try:
+ if self.fpApiVer >= 5.0:
+ oProgress = self.o.machine.saveState()
+ else:
+ oProgress = self.o.console.saveState()
+ except:
+ reporter.logXcpt('IMachine::saveState failed on %s' % (self.sName));
+ return False;
+
+ # Wait for saving state operation to complete.
+ rc = self.oTstDrv.waitOnProgress(oProgress);
+ if rc < 0:
+ self.close();
+ return None;
+
+ # Wait for the VM to really terminate or we'll fail to open a new session to it.
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ return self.waitForTask(30 * 1000); # fudge
+
+ def discardSavedState(self, fRemove = True):
+ """
+ Discards saved state of the VM.
+
+ Returns True on success.
+ Returns False on IConsole::discardSaveState() failure.
+ """
+
+ try:
+ if self.fpApiVer >= 5.0:
+ self.o.machine.discardSavedState(fRemove)
+ else:
+ self.o.console.discardSavedState(fRemove)
+ except:
+ reporter.logXcpt('IMachine::discardSavedState failed on %s' % (self.sName))
+ return False
+ return True
+
+ def restoreSnapshot(self, oSnapshot, fFudgeOnFailure = True):
+ """
+ Restores the given snapshot.
+
+ Returns True on success.
+ Returns False on IMachine::restoreSnapshot() failure.
+ Returns None if the progress object returns failure.
+ """
+ try:
+ if self.fpApiVer >= 5.0:
+ oProgress = self.o.machine.restoreSnapshot(oSnapshot);
+ else:
+ oProgress = self.o.console.restoreSnapshot(oSnapshot);
+ except:
+ reporter.logXcpt('IMachine::restoreSnapshot failed on %s' % (self.sName));
+ if fFudgeOnFailure:
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ self.waitForTask(1000); # fudge
+ return False;
+
+ rc = self.oTstDrv.waitOnProgress(oProgress);
+ if rc < 0:
+ self.close();
+ if fFudgeOnFailure:
+ vbox.reportError(oProgress, 'restoreSnapshot for "%s" failed' % (self.sName));
+ return None;
+
+ return self.waitForTask(30 * 1000);
+
+ def deleteSnapshot(self, oSnapshot, fFudgeOnFailure = True, cMsTimeout = 30 * 1000):
+ """
+ Deletes the given snapshot merging the diff image into the base.
+
+ Returns True on success.
+ Returns False on IMachine::deleteSnapshot() failure.
+ """
+ try:
+ if self.fpApiVer >= 5.0:
+ oProgressCom = self.o.machine.deleteSnapshot(oSnapshot);
+ else:
+ oProgressCom = self.o.console.deleteSnapshot(oSnapshot);
+ oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'Delete Snapshot %s' % (oSnapshot));
+ oProgress.wait(cMsTimeout);
+ oProgress.logResult();
+ except:
+ reporter.logXcpt('IMachine::deleteSnapshot failed on %s' % (self.sName));
+ if fFudgeOnFailure:
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ self.waitForTask(1000); # fudge
+ return False;
+
+ return True;
+
+ def takeSnapshot(self, sName, sDescription = '', fPause = True, fFudgeOnFailure = True, cMsTimeout = 30 * 1000):
+ """
+ Takes a snapshot with the given name
+
+ Returns True on success.
+ Returns False on IMachine::takeSnapshot() or VM state change failure.
+ """
+ try:
+ if fPause is True \
+ and self.oVM.state is vboxcon.MachineState_Running:
+ self.o.console.pause();
+ if self.fpApiVer >= 5.0:
+ (oProgressCom, _) = self.o.machine.takeSnapshot(sName, sDescription, True);
+ else:
+ oProgressCom = self.o.console.takeSnapshot(sName, sDescription);
+ oProgress = ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oTstDrv, 'Take Snapshot %s' % (sName));
+ oProgress.wait(cMsTimeout);
+ oProgress.logResult();
+ except:
+ reporter.logXcpt('IMachine::takeSnapshot failed on %s' % (self.sName));
+ if fFudgeOnFailure:
+ self.oTstDrv.waitOnDirectSessionClose(self.oVM, 5000); # fudge
+ self.waitForTask(1000); # fudge
+ return False;
+
+ if fPause is True \
+ and self.oVM.state is vboxcon.MachineState_Paused:
+ self.o.console.resume();
+
+ return True;
+
+ def findSnapshot(self, sName):
+ """
+ Returns the snapshot object with the given name
+
+ Returns snapshot object on success.
+ Returns None if there is no snapshot with the given name.
+ """
+ return self.oVM.findSnapshot(sName);
+
+ def takeScreenshot(self, sFilename, iScreenId=0):
+ """
+ Take screenshot from the given display and save it to specified file.
+
+ Returns True on success
+ Returns False on failure.
+ """
+ try:
+ if self.fpApiVer >= 5.0:
+ iWidth, iHeight, _, _, _, _ = self.o.console.display.getScreenResolution(iScreenId)
+ aPngData = self.o.console.display.takeScreenShotToArray(iScreenId, iWidth, iHeight,
+ vboxcon.BitmapFormat_PNG)
+ else:
+ iWidth, iHeight, _, _, _ = self.o.console.display.getScreenResolution(iScreenId)
+ aPngData = self.o.console.display.takeScreenShotPNGToArray(iScreenId, iWidth, iHeight)
+ except:
+ reporter.logXcpt("Unable to take screenshot")
+ return False
+
+ with open(sFilename, 'wb') as oFile: # pylint: disable=unspecified-encoding
+ oFile.write(aPngData)
+
+ return True
+
+ def attachUsbDevice(self, sUuid, sCaptureFilename = None):
+ """
+ Attach given USB device UUID to the VM.
+
+ Returns True on success
+ Returns False on failure.
+ """
+ fRc = True;
+ try:
+ if sCaptureFilename is None:
+ self.o.console.attachUSBDevice(sUuid, '');
+ else:
+ self.o.console.attachUSBDevice(sUuid, sCaptureFilename);
+ except:
+ reporter.logXcpt('Unable to attach USB device %s' % (sUuid,));
+ fRc = False;
+
+ return fRc;
+
+ def detachUsbDevice(self, sUuid):
+ """
+ Detach given USB device UUID from the VM.
+
+ Returns True on success
+ Returns False on failure.
+ """
+ fRc = True;
+ try:
+ _ = self.o.console.detachUSBDevice(sUuid);
+ except:
+ reporter.logXcpt('Unable to detach USB device %s' % (sUuid,));
+ fRc = False;
+
+ return fRc;
+
+
+ #
+ # IMachineDebugger wrappers.
+ #
+
+ def queryOsKernelLog(self):
+ """
+ Tries to get the OS kernel log using the VM debugger interface.
+
+ Returns string containing the kernel log on success.
+ Returns None on failure.
+ """
+ sOsKernelLog = None;
+ try:
+ self.o.console.debugger.loadPlugIn('all');
+ except:
+ reporter.logXcpt('Unable to load debugger plugins');
+ else:
+ try:
+ sOsDetected = self.o.console.debugger.detectOS();
+ except:
+ reporter.logXcpt('Failed to detect the guest OS');
+ else:
+ try:
+ sOsKernelLog = self.o.console.debugger.queryOSKernelLog(0);
+ except:
+ reporter.logXcpt('Unable to get the guest OS (%s) kernel log' % (sOsDetected,));
+ return sOsKernelLog;
+
+ def queryDbgInfo(self, sItem, sArg = '', sDefault = None):
+ """
+ Simple wrapper around IMachineDebugger::info.
+
+ Returns string on success, sDefault on failure (logged).
+ """
+ try:
+ return self.o.console.debugger.info(sItem, sArg);
+ except:
+ reporter.logXcpt('Unable to query "%s" with arg "%s"' % (sItem, sArg,));
+ return sDefault;
+
+ def queryDbgInfoVgaText(self, sArg = 'all'):
+ """
+ Tries to get the 'info vgatext' output, provided we're in next mode.
+
+ Returns string containing text on success.
+ Returns None on failure or not text mode.
+ """
+ sVgaText = None;
+ try:
+ sVgaText = self.o.console.debugger.info('vgatext', sArg);
+ if sVgaText.startswith('Not in text mode!'):
+ sVgaText = None;
+ except:
+ reporter.logXcpt('Unable to query vgatext with arg "%s"' % (sArg,));
+ return sVgaText;
+
+ def queryDbgGuestStack(self, iCpu = 0):
+ """
+ Returns the guest stack for the given VCPU.
+
+ Returns string containing the guest stack for the selected VCPU on success.
+ Returns None on failure.
+ """
+
+ #
+ # Load all plugins first and try to detect the OS so we can
+ # get nicer stack traces.
+ #
+ try:
+ self.o.console.debugger.loadPlugIn('all');
+ except:
+ reporter.logXcpt('Unable to load debugger plugins');
+ else:
+ try:
+ sOsDetected = self.o.console.debugger.detectOS();
+ _ = sOsDetected;
+ except:
+ reporter.logXcpt('Failed to detect the guest OS');
+
+ sGuestStack = None;
+ try:
+ sGuestStack = self.o.console.debugger.dumpGuestStack(iCpu);
+ except:
+ reporter.logXcpt('Unable to query guest stack for CPU %s' % (iCpu, ));
+
+ return sGuestStack;
+
+
+ #
+ # Other methods.
+ #
+
+ def getPrimaryIp(self):
+ """
+ Tries to obtain the primary IP address of the guest via the guest
+ properties.
+
+ Returns IP address on success.
+ Returns empty string on failure.
+ """
+ sIpAddr = self.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
+ if vbox.isIpAddrValid(sIpAddr):
+ return sIpAddr;
+ return '';
+
+ def getPid(self):
+ """
+ Gets the process ID for the direct session unless it's ourselves.
+ """
+ if self.uPid is None and self.o is not None and self.fRemoteSession:
+ try:
+ if self.fpApiVer >= 4.2:
+ uPid = self.o.machine.sessionPID;
+ else:
+ uPid = self.o.machine.sessionPid;
+ if uPid != os.getpid() and uPid != 0xffffffff:
+ self.uPid = uPid;
+ except Exception as oXcpt:
+ if vbox.ComError.equal(oXcpt, vbox.ComError.E_UNEXPECTED):
+ try:
+ if self.fpApiVer >= 4.2:
+ uPid = self.oVM.sessionPID;
+ else:
+ uPid = self.oVM.sessionPid;
+ if uPid != os.getpid() and uPid != 0xffffffff:
+ self.uPid = uPid;
+ except:
+ reporter.log2Xcpt();
+ else:
+ reporter.log2Xcpt();
+ if self.uPid is not None:
+ reporter.log2('getPid: %u' % (self.uPid,));
+ self.fPidFile = self.oTstDrv.pidFileAdd(self.uPid, 'vm_%s' % (self.sName,), # Set-uid-to-root is similar to SUDO.
+ fSudo = True);
+ return self.uPid;
+
+ def addLogsToReport(self, cReleaseLogs = 1):
+ """
+ Retrieves and adds the release and debug logs to the test report.
+ """
+ fRc = True;
+
+ # Add each of the requested release logs to the report.
+ for iLog in range(0, cReleaseLogs):
+ try:
+ if self.fpApiVer >= 3.2:
+ sLogFile = self.oVM.queryLogFilename(iLog);
+ elif iLog > 0:
+ sLogFile = '%s/VBox.log' % (self.oVM.logFolder,);
+ else:
+ sLogFile = '%s/VBox.log.%u' % (self.oVM.logFolder, iLog);
+ except:
+ reporter.logXcpt('iLog=%s' % (iLog,));
+ fRc = False;
+ else:
+ if sLogFile is not None and sLogFile != '': # the None bit is for a 3.2.0 bug.
+ reporter.addLogFile(sLogFile, 'log/release/vm', '%s #%u' % (self.sName, iLog),
+ sAltName = '%s-%s' % (self.sName, os.path.basename(sLogFile),));
+
+ # Now for the hardened windows startup log.
+ try:
+ sLogFile = os.path.join(self.oVM.logFolder, 'VBoxHardening.log');
+ except:
+ reporter.logXcpt();
+ fRc = False;
+ else:
+ if os.path.isfile(sLogFile):
+ reporter.addLogFile(sLogFile, 'log/release/vm', '%s hardening log' % (self.sName, ),
+ sAltName = '%s-%s' % (self.sName, os.path.basename(sLogFile),));
+
+ # Now for the debug log.
+ if self.sLogFile is not None and os.path.isfile(self.sLogFile):
+ reporter.addLogFile(self.sLogFile, 'log/debug/vm', '%s debug' % (self.sName, ),
+ sAltName = '%s-%s' % (self.sName, os.path.basename(self.sLogFile),));
+
+ return fRc;
+
+ def registerDerivedEventHandler(self, oSubClass, dArgs = None, fMustSucceed = True):
+ """
+ Create an instance of the given ConsoleEventHandlerBase sub-class and
+ register it.
+
+ The new instance is returned on success. None is returned on error.
+ """
+
+ # We need a console object.
+ try:
+ oConsole = self.o.console;
+ except Exception as oXcpt:
+ if fMustSucceed or vbox.ComError.notEqual(oXcpt, vbox.ComError.E_UNEXPECTED):
+ reporter.errorXcpt('Failed to get ISession::console for "%s"' % (self.sName, ));
+ return None;
+
+ # Add the base class arguments.
+ dArgsCopy = dArgs.copy() if dArgs is not None else {};
+ dArgsCopy['oSession'] = self;
+ dArgsCopy['oConsole'] = oConsole;
+ sLogSuffix = 'on %s' % (self.sName,)
+ return oSubClass.registerDerivedEventHandler(self.oVBoxMgr, self.fpApiVer, oSubClass, dArgsCopy,
+ oConsole, 'IConsole', 'IConsoleCallback',
+ fMustSucceed = fMustSucceed, sLogSuffix = sLogSuffix);
+
+ def enableVmmDevTestingPart(self, fEnabled, fEnableMMIO = False):
+ """
+ Enables the testing part of the VMMDev.
+
+ Returns True on success and False on failure. Error information is logged.
+ """
+ fRc = True;
+ try:
+ self.o.machine.setExtraData('VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled',
+ '1' if fEnabled else '');
+ self.o.machine.setExtraData('VBoxInternal/Devices/VMMDev/0/Config/TestingMMIO',
+ '1' if fEnableMMIO and fEnabled else '');
+ except:
+ reporter.errorXcpt('VM name "%s", fEnabled=%s' % (self.sName, fEnabled));
+ fRc = False;
+ else:
+ reporter.log('set VMMDevTesting=%s for "%s"' % (fEnabled, self.sName));
+ self.oTstDrv.processPendingEvents();
+ return fRc;
+
+ #
+ # Test eXecution Service methods.
+ #
+
+ def txsConnectViaTcp(self, cMsTimeout = 10*60000, sIpAddr = None, fNatForwardingForTxs = False):
+ """
+ Connects to the TXS using TCP/IP as transport. If no IP or MAC is
+ addresses are specified, we'll get the IP from the guest additions.
+
+ Returns a TxsConnectTask object on success, None + log on failure.
+ """
+ # If the VM is configured with a NAT interface, connect to local host.
+ fReversedSetup = False;
+ fUseNatForTxs = False;
+ sMacAddr = None;
+ oIDhcpServer = None;
+ if sIpAddr is None:
+ try:
+ oNic = self.oVM.getNetworkAdapter(0);
+ enmAttachmentType = oNic.attachmentType;
+ if enmAttachmentType == vboxcon.NetworkAttachmentType_NAT:
+ fUseNatForTxs = True;
+ elif enmAttachmentType == vboxcon.NetworkAttachmentType_HostOnly and not sIpAddr:
+ # Get the MAC address and find the DHCP server.
+ sMacAddr = oNic.MACAddress;
+ sHostOnlyNIC = oNic.hostOnlyInterface;
+ oIHostOnlyIf = self.oVBox.host.findHostNetworkInterfaceByName(sHostOnlyNIC);
+ sHostOnlyNet = oIHostOnlyIf.networkName;
+ oIDhcpServer = self.oVBox.findDHCPServerByNetworkName(sHostOnlyNet);
+ except:
+ reporter.errorXcpt();
+ return None;
+
+ if fUseNatForTxs:
+ fReversedSetup = not fNatForwardingForTxs;
+ sIpAddr = '127.0.0.1';
+
+ # Kick off the task.
+ try:
+ oTask = TxsConnectTask(self, cMsTimeout, sIpAddr, sMacAddr, oIDhcpServer, fReversedSetup,
+ fnProcessEvents = self.oTstDrv.processPendingEvents);
+ except:
+ reporter.errorXcpt();
+ oTask = None;
+ return oTask;
+
+ def txsTryConnectViaTcp(self, cMsTimeout, sHostname, fReversed = False):
+ """
+ Attempts to connect to a TXS instance.
+
+ Returns True if a connection was established, False if not (only grave
+ failures are logged as errors).
+
+ Note! The timeout is more of a guideline...
+ """
+
+ if sHostname is None or sHostname.strip() == '':
+ raise base.GenError('Empty sHostname is not implemented yet');
+
+ oTxsSession = txsclient.tryOpenTcpSession(cMsTimeout, sHostname, fReversedSetup = fReversed,
+ cMsIdleFudge = cMsTimeout // 2,
+ fnProcessEvents = self.oTstDrv.processPendingEvents);
+ if oTxsSession is None:
+ return False;
+
+ # Wait for the connect task to time out.
+ self.oTstDrv.addTask(oTxsSession);
+ self.oTstDrv.processPendingEvents();
+ oRc = self.oTstDrv.waitForTasks(cMsTimeout);
+ self.oTstDrv.removeTask(oTxsSession);
+ if oRc != oTxsSession:
+ if oRc is not None:
+ reporter.log('oRc=%s, expected %s' % (oRc, oTxsSession));
+ self.oTstDrv.processPendingEvents();
+ oTxsSession.cancelTask(); # this is synchronous
+ return False;
+
+ # Check the status.
+ reporter.log2('TxsSession is ready, isSuccess() -> %s.' % (oTxsSession.isSuccess(),));
+ if not oTxsSession.isSuccess():
+ return False;
+
+ reporter.log2('Disconnecting from TXS...');
+ return oTxsSession.syncDisconnect();
+
+
+
+class TxsConnectTask(TdTaskBase):
+ """
+ Class that takes care of connecting to a VM.
+ """
+
+ class TxsConnectTaskVBoxCallback(vbox.VirtualBoxEventHandlerBase):
+ """ Class for looking for IPv4 address changes on interface 0."""
+ def __init__(self, dArgs):
+ vbox.VirtualBoxEventHandlerBase.__init__(self, dArgs);
+ self.oParentTask = dArgs['oParentTask'];
+ self.sMachineId = dArgs['sMachineId'];
+
+ def onGuestPropertyChange(self, sMachineId, sName, sValue, sFlags, fWasDeleted):
+ """Look for IP address."""
+ reporter.log2('onGuestPropertyChange(,%s,%s,%s,%s,%s)' % (sMachineId, sName, sValue, sFlags, fWasDeleted));
+ if sMachineId == self.sMachineId \
+ and sName == '/VirtualBox/GuestInfo/Net/0/V4/IP':
+ oParentTask = self.oParentTask;
+ if oParentTask:
+ oParentTask._setIp(sValue); # pylint: disable=protected-access
+
+
+ def __init__(self, oSession, cMsTimeout, sIpAddr, sMacAddr, oIDhcpServer, fReversedSetup, fnProcessEvents = None):
+ TdTaskBase.__init__(self, utils.getCallerName(), fnProcessEvents = fnProcessEvents);
+ self.cMsTimeout = cMsTimeout;
+ self.fnProcessEvents = fnProcessEvents;
+ self.sIpAddr = None;
+ self.sNextIpAddr = None;
+ self.sMacAddr = sMacAddr;
+ self.oIDhcpServer = oIDhcpServer;
+ self.fReversedSetup = fReversedSetup;
+ self.oVBoxEventHandler = None;
+ self.oTxsSession = None;
+
+ # Check that the input makes sense:
+ if (sMacAddr is None) != (oIDhcpServer is None) \
+ or (sMacAddr and fReversedSetup) \
+ or (sMacAddr and sIpAddr):
+ reporter.error('TxsConnectTask sMacAddr=%s oIDhcpServer=%s sIpAddr=%s fReversedSetup=%s'
+ % (sMacAddr, oIDhcpServer, sIpAddr, fReversedSetup,));
+ raise base.GenError();
+
+ reporter.log2('TxsConnectTask: sIpAddr=%s fReversedSetup=%s' % (sIpAddr, fReversedSetup))
+ if fReversedSetup is True:
+ self._openTcpSession(sIpAddr, fReversedSetup = True);
+ elif sIpAddr is not None and sIpAddr.strip() != '':
+ self._openTcpSession(sIpAddr, cMsIdleFudge = 5000);
+ else:
+ #
+ # If we've got no IP address, register callbacks that listens for
+ # the primary network adaptor of the VM to set a IPv4 guest prop.
+ # Note! The order in which things are done here is kind of important.
+ #
+
+ # 0. The caller zaps the property before starting the VM.
+ #try:
+ # oSession.delGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
+ #except:
+ # reporter.logXcpt();
+
+ # 1. Register the callback / event listener object.
+ dArgs = {'oParentTask':self, 'sMachineId':oSession.o.machine.id};
+ self.oVBoxEventHandler = oSession.oVBox.registerDerivedEventHandler(self.TxsConnectTaskVBoxCallback, dArgs);
+
+ # 2. Query the guest properties.
+ try:
+ sIpAddr = oSession.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/0/V4/IP');
+ except:
+ reporter.errorXcpt('IMachine::getGuestPropertyValue("/VirtualBox/GuestInfo/Net/0/V4/IP") failed');
+ self._deregisterEventHandler();
+ raise;
+ else:
+ if sIpAddr is not None:
+ self._setIp(sIpAddr);
+
+ #
+ # If the network adapter of the VM is host-only we can talk poll IDHCPServer
+ # for the guest IP, allowing us to detect it for VMs without guest additions.
+ # This will when we're polled.
+ #
+ if sMacAddr is not None:
+ assert self.oIDhcpServer is not None;
+
+
+ # end __init__
+
+ def __del__(self):
+ """ Make sure we deregister the callback. """
+ self._deregisterEventHandler();
+ return TdTaskBase.__del__(self);
+
+ def toString(self):
+ return '<%s cMsTimeout=%s, sIpAddr=%s, sNextIpAddr=%s, sMacAddr=%s, fReversedSetup=%s,' \
+ ' oTxsSession=%s oVBoxEventHandler=%s>' \
+ % (TdTaskBase.toString(self), self.cMsTimeout, self.sIpAddr, self.sNextIpAddr, self.sMacAddr, self.fReversedSetup,
+ self.oTxsSession, self.oVBoxEventHandler);
+
+ def _deregisterEventHandler(self):
+ """Deregisters the event handler."""
+ fRc = True;
+ oVBoxEventHandler = self.oVBoxEventHandler;
+ if oVBoxEventHandler is not None:
+ self.oVBoxEventHandler = None;
+ fRc = oVBoxEventHandler.unregister();
+ oVBoxEventHandler.oParentTask = None; # Try avoid cylic deps.
+ return fRc;
+
+ def _setIp(self, sIpAddr, fInitCall = False):
+ """Called when we get an IP. Will create a TXS session and signal the task."""
+ sIpAddr = sIpAddr.strip();
+
+ if sIpAddr is not None \
+ and sIpAddr != '':
+ if vbox.isIpAddrValid(sIpAddr) or fInitCall:
+ try:
+ for s in sIpAddr.split('.'):
+ i = int(s);
+ if str(i) != s:
+ raise Exception();
+ except:
+ reporter.fatalXcpt();
+ else:
+ reporter.log('TxsConnectTask: opening session to ip "%s"' % (sIpAddr));
+ self._openTcpSession(sIpAddr, cMsIdleFudge = 5000);
+ return None;
+
+ reporter.log('TxsConnectTask: Ignoring Bad ip "%s"' % (sIpAddr));
+ else:
+ reporter.log2('TxsConnectTask: Ignoring empty ip "%s"' % (sIpAddr));
+ return None;
+
+ def _openTcpSession(self, sIpAddr, uPort = None, fReversedSetup = False, cMsIdleFudge = 0):
+ """
+ Calls txsclient.openTcpSession and switches our task to reflect the
+ state of the subtask.
+ """
+ self.oCv.acquire();
+ if self.oTxsSession is None:
+ reporter.log2('_openTcpSession: sIpAddr=%s, uPort=%d, fReversedSetup=%s' %
+ (sIpAddr, uPort if uPort is not None else 0, fReversedSetup));
+ self.sIpAddr = sIpAddr;
+ self.oTxsSession = txsclient.openTcpSession(self.cMsTimeout, sIpAddr, uPort, fReversedSetup,
+ cMsIdleFudge, fnProcessEvents = self.fnProcessEvents);
+ self.oTxsSession.setTaskOwner(self);
+ else:
+ self.sNextIpAddr = sIpAddr;
+ reporter.log2('_openTcpSession: sNextIpAddr=%s' % (sIpAddr,));
+ self.oCv.release();
+ return None;
+
+ def notifyAboutReadyTask(self, oTxsSession):
+ """
+ Called by the TXS session task when it's done.
+
+ We'll signal the task completed or retry depending on the result.
+ """
+
+ self.oCv.acquire();
+
+ # Disassociate ourselves with the session (avoid cyclic ref)
+ oTxsSession.setTaskOwner(None);
+ fSuccess = oTxsSession.isSuccess();
+ if self.oTxsSession is not None:
+ if not fSuccess:
+ self.oTxsSession = None;
+ if fSuccess and self.fReversedSetup:
+ self.sIpAddr = oTxsSession.oTransport.sHostname;
+ else:
+ fSuccess = False;
+
+ # Signal done, or retry?
+ fDeregister = False;
+ if fSuccess \
+ or self.fReversedSetup \
+ or self.getAgeAsMs() >= self.cMsTimeout:
+ self.signalTaskLocked();
+ fDeregister = True;
+ else:
+ sIpAddr = self.sNextIpAddr if self.sNextIpAddr is not None else self.sIpAddr;
+ self._openTcpSession(sIpAddr, cMsIdleFudge = 5000);
+
+ self.oCv.release();
+
+ # If we're done, deregister the callback (w/o owning lock). It will
+ if fDeregister:
+ self._deregisterEventHandler();
+ return True;
+
+ def _pollDhcpServer(self):
+ """
+ Polls the DHCP server by MAC address in host-only setups.
+ """
+
+ if self.sIpAddr:
+ return False;
+
+ if self.oIDhcpServer is None or not self.sMacAddr:
+ return False;
+
+ try:
+ (sIpAddr, sState, secIssued, secExpire) = self.oIDhcpServer.findLeaseByMAC(self.sMacAddr, 0);
+ except:
+ reporter.log4Xcpt('sMacAddr=%s' % (self.sMacAddr,));
+ return False;
+
+ secNow = utils.secondsSinceUnixEpoch();
+ reporter.log2('dhcp poll: secNow=%s secExpire=%s secIssued=%s sState=%s sIpAddr=%s'
+ % (secNow, secExpire, secIssued, sState, sIpAddr,));
+ if secNow > secExpire or sState != 'acked' or not sIpAddr:
+ return False;
+
+ reporter.log('dhcp poll: sIpAddr=%s secExpire=%s (%s TTL) secIssued=%s (%s ago)'
+ % (sIpAddr, secExpire, secExpire - secNow, secIssued, secNow - secIssued,));
+ self._setIp(sIpAddr);
+ return True;
+
+ #
+ # Task methods
+ #
+
+ def pollTask(self, fLocked = False):
+ """
+ Overridden pollTask method.
+ """
+ self._pollDhcpServer();
+ return TdTaskBase.pollTask(self, fLocked);
+
+ #
+ # Public methods
+ #
+
+ def getResult(self):
+ """
+ Returns the connected TXS session object on success.
+ Returns None on failure or if the task has not yet completed.
+ """
+ self.oCv.acquire();
+ oTxsSession = self.oTxsSession;
+ self.oCv.release();
+
+ if oTxsSession is not None and not oTxsSession.isSuccess():
+ oTxsSession = None;
+ return oTxsSession;
+
+ def cancelTask(self):
+ """ Cancels the task. """
+ self._deregisterEventHandler(); # (make sure to avoid cyclic fun)
+ self.oCv.acquire();
+ if not self.fSignalled:
+ oTxsSession = self.oTxsSession;
+ if oTxsSession is not None:
+ self.oCv.release();
+ oTxsSession.setTaskOwner(None);
+ oTxsSession.cancelTask();
+ oTxsSession.waitForTask(1000);
+ self.oCv.acquire();
+ self.signalTaskLocked();
+ self.oCv.release();
+ return True;
+
+
+
+class AdditionsStatusTask(TdTaskBase):
+ """
+ Class that takes care of waiting till the guest additions are in a given state.
+ """
+
+ class AdditionsStatusTaskCallback(vbox.EventHandlerBase):
+ """ Class for looking for IPv4 address changes on interface 0."""
+ def __init__(self, dArgs):
+ self.oParentTask = dArgs['oParentTask'];
+ vbox.EventHandlerBase.__init__(self, dArgs, self.oParentTask.oSession.fpApiVer,
+ 'AdditionsStatusTaskCallback/%s' % (self.oParentTask.oSession.sName,));
+
+ def handleEvent(self, oEvt):
+ try:
+ enmType = oEvt.type;
+ except:
+ reporter.errorXcpt();
+ else:
+ reporter.log2('AdditionsStatusTaskCallback:handleEvent: enmType=%s' % (enmType,));
+ if enmType == vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged:
+ oParentTask = self.oParentTask;
+ if oParentTask:
+ oParentTask.pollTask();
+
+ # end
+
+
+ def __init__(self, oSession, oIGuest, cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None,
+ aenmWaitForInactive = None):
+ """
+ aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
+ aenmWaitForActive - List facilities (type values) that must be active.
+ aenmWaitForInactive - List facilities (type values) that must be inactive.
+
+ The default is to wait for AdditionsRunLevelType_Userland if all three lists
+ are unspecified or empty.
+ """
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.oSession = oSession # type: vboxwrappers.SessionWrapper
+ self.oIGuest = oIGuest;
+ self.cMsTimeout = cMsTimeout;
+ self.fSucceeded = False;
+ self.oVBoxEventHandler = None;
+ self.aenmWaitForRunLevels = aenmWaitForRunLevels if aenmWaitForRunLevels else [];
+ self.aenmWaitForActive = aenmWaitForActive if aenmWaitForActive else [];
+ self.aenmWaitForInactive = aenmWaitForInactive if aenmWaitForInactive else [];
+
+ # Provide a sensible default if nothing is given.
+ if not self.aenmWaitForRunLevels and not self.aenmWaitForActive and not self.aenmWaitForInactive:
+ self.aenmWaitForRunLevels = [vboxcon.AdditionsRunLevelType_Userland,];
+
+ # Register the event handler on hosts which has it:
+ if oSession.fpApiVer >= 6.1 or hasattr(vboxcon, 'VBoxEventType_OnGuestAdditionsStatusChanged'):
+ aenmEvents = (vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged,);
+ dArgs = {
+ 'oParentTask': self,
+ };
+ self.oVBoxEventHandler = vbox.EventHandlerBase.registerDerivedEventHandler(oSession.oVBoxMgr,
+ oSession.fpApiVer,
+ self.AdditionsStatusTaskCallback,
+ dArgs,
+ oIGuest,
+ 'IGuest',
+ 'AdditionsStatusTaskCallback',
+ aenmEvents = aenmEvents);
+ reporter.log2('AdditionsStatusTask: %s' % (self.toString(), ));
+
+ def __del__(self):
+ """ Make sure we deregister the callback. """
+ self._deregisterEventHandler();
+ self.oIGuest = None;
+ return TdTaskBase.__del__(self);
+
+ def toString(self):
+ return '<%s cMsTimeout=%s, fSucceeded=%s, aenmWaitForRunLevels=%s, aenmWaitForActive=%s, aenmWaitForInactive=%s, ' \
+ 'oVBoxEventHandler=%s>' \
+ % (TdTaskBase.toString(self), self.cMsTimeout, self.fSucceeded, self.aenmWaitForRunLevels, self.aenmWaitForActive,
+ self.aenmWaitForInactive, self.oVBoxEventHandler,);
+
+ def _deregisterEventHandler(self):
+ """Deregisters the event handler."""
+ fRc = True;
+ oVBoxEventHandler = self.oVBoxEventHandler;
+ if oVBoxEventHandler is not None:
+ self.oVBoxEventHandler = None;
+ fRc = oVBoxEventHandler.unregister();
+ oVBoxEventHandler.oParentTask = None; # Try avoid cylic deps.
+ return fRc;
+
+ def _poll(self):
+ """
+ Internal worker for pollTask() that returns the new signalled state.
+ """
+
+ #
+ # Check if any of the runlevels we wait for have been reached:
+ #
+ if self.aenmWaitForRunLevels:
+ try:
+ enmRunLevel = self.oIGuest.additionsRunLevel;
+ except:
+ reporter.errorXcpt();
+ return True;
+ if enmRunLevel not in self.aenmWaitForRunLevels:
+ reporter.log6('AdditionsStatusTask/poll: enmRunLevel=%s not in %s' % (enmRunLevel, self.aenmWaitForRunLevels,));
+ return False;
+ reporter.log2('AdditionsStatusTask/poll: enmRunLevel=%s matched %s!' % (enmRunLevel, self.aenmWaitForRunLevels,));
+
+
+ #
+ # Check for the facilities that must all be active.
+ #
+ for enmFacility in self.aenmWaitForActive:
+ try:
+ (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility);
+ except:
+ reporter.errorXcpt('enmFacility=%s' % (enmFacility,));
+ return True;
+ if enmStatus != vboxcon.AdditionsFacilityStatus_Active:
+ reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not active: %s' % (enmFacility, enmStatus,));
+ return False;
+
+ #
+ # Check for the facilities that must all be inactive or terminated.
+ #
+ for enmFacility in self.aenmWaitForInactive:
+ try:
+ (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility);
+ except:
+ reporter.errorXcpt('enmFacility=%s' % (enmFacility,));
+ return True;
+ if enmStatus not in (vboxcon.AdditionsFacilityStatus_Inactive,
+ vboxcon.AdditionsFacilityStatus_Terminated):
+ reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not inactive: %s' % (enmFacility, enmStatus,));
+ return False;
+
+
+ reporter.log('AdditionsStatusTask: Poll succeeded, signalling...');
+ self.fSucceeded = True;
+ return True;
+
+
+ #
+ # Task methods
+ #
+
+ def pollTask(self, fLocked = False):
+ """
+ Overridden pollTask method.
+ """
+ if not fLocked:
+ self.lockTask();
+
+ fDeregister = False;
+ fRc = self.fSignalled;
+ if not fRc:
+ fRc = self._poll();
+ if fRc or self.getAgeAsMs() >= self.cMsTimeout:
+ self.signalTaskLocked();
+ fDeregister = True;
+
+ if not fLocked:
+ self.unlockTask();
+
+ # If we're done, deregister the event callback (w/o owning lock).
+ if fDeregister:
+ self._deregisterEventHandler();
+ return fRc;
+
+ def getResult(self):
+ """
+ Returns true if the we succeeded.
+ Returns false if not. If the task is signalled already, then we
+ encountered a problem while polling.
+ """
+ return self.fSucceeded;
+
+ def cancelTask(self):
+ """
+ Cancels the task.
+ Just to actively disengage the event handler.
+ """
+ self._deregisterEventHandler();
+ return True;
+
diff --git a/src/VBox/ValidationKit/testdriver/win-vbox-net-drvstore-cleanup.ps1 b/src/VBox/ValidationKit/testdriver/win-vbox-net-drvstore-cleanup.ps1
new file mode 100644
index 00000000..aca5d547
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/win-vbox-net-drvstore-cleanup.ps1
@@ -0,0 +1,71 @@
+# $Id: win-vbox-net-drvstore-cleanup.ps1 $
+## @file
+# VirtualBox Validation Kit - network cleanup script (powershell).
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+param([switch]$confirm)
+
+Function AskForConfirmation ($title_text, $message_text, $yes_text, $no_text)
+{
+ if ($confirm) {
+ $title = $title_text
+ $message = $message_text
+
+ $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", $yes_text
+
+ $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", $no_text
+
+ $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
+
+ $result = $host.ui.PromptForChoice($title, $message, $options, 0)
+ } else {
+ $result = 0
+ }
+
+ return $result
+}
+
+pnputil -e | ForEach-Object { if ($_ -match "Published name :.*(oem\d+\.inf)") {$inf=$matches[1]} elseif ($_ -match "Driver package provider :.*Oracle") {$inf + " " + $_} }
+
+$result = AskForConfirmation "Clean up the driver store" `
+ "Do you want to delete all VirtualBox drivers from the driver store?" `
+ "Deletes all VirtualBox drivers from the driver store." `
+ "No modifications to the driver store will be made."
+
+switch ($result)
+ {
+ 0 {pnputil -e | ForEach-Object { if ($_ -match "Published name :.*(oem\d+\.inf)") {$inf=$matches[1]} elseif ($_ -match "Driver package provider :.*Oracle") {$inf} } | ForEach-Object { pnputil -d $inf } }
+ 1 {"Removal cancelled."}
+ }
+
diff --git a/src/VBox/ValidationKit/testdriver/win-vbox-net-uninstall.ps1 b/src/VBox/ValidationKit/testdriver/win-vbox-net-uninstall.ps1
new file mode 100644
index 00000000..f03a842e
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/win-vbox-net-uninstall.ps1
@@ -0,0 +1,253 @@
+# $Id: win-vbox-net-uninstall.ps1 $
+## @file
+# VirtualBox Validation Kit - network cleanup script (powershell).
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+param([switch]$confirm)
+
+Function AskForConfirmation ($title_text, $message_text, $yes_text, $no_text)
+{
+ if ($confirm) {
+ $title = $title_text
+ $message = $message_text
+
+ $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", $yes_text
+
+ $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", $no_text
+
+ $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
+
+ $result = $host.ui.PromptForChoice($title, $message, $options, 0)
+ } else {
+ $result = 0
+ }
+
+ return $result
+}
+
+Function DeleteUnmatchingKeys ($title_text, $reg_key)
+{
+ $ghostcon = @(Get-ChildItem ($reg_key) | Where-Object { !$connections.ContainsKey($_.PSChildName) } )
+ if ($ghostcon.count -eq 0) {
+ Write-Host "`nNo ghost connections has been found -- nothing to do"
+ } else {
+ Write-Host "`nParameter keys for the following connections will be removed:"
+ Write-Host ($ghostcon | Out-String)
+
+ $result = AskForConfirmation $title_text `
+ "Do you want to delete the keys listed above?" `
+ "Deletes all ghost connection keys from the registry." `
+ "No modifications to the registry will be made."
+
+ switch ($result)
+ {
+ 0 {$ghostcon.GetEnumerator() | ForEach-Object { Remove-Item -Path $_ -Recurse }}
+ 1 {"Removal cancelled."}
+ }
+ }
+}
+
+
+Push-Location
+cd "Registry::"
+Write-Host "Retrieving valid connections:"
+$iftypes = @{}
+$connections = @{}
+$ghostcon_names = @{}
+Get-Item ".\HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0*" | `
+ ForEach-Object {
+ $prop = (Get-ItemProperty $_.PSPath)
+ $conn = $null
+ if (Test-Path ("HKLM\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\" + $prop.NetCfgInstanceId + "\Connection")) {
+ $conn = (Get-ItemProperty ("HKLM\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\" + $prop.NetCfgInstanceId + "\Connection"))
+ }
+ $iftype = $prop."*IfType"
+ if ($iftypes.ContainsKey($iftype)) {
+ $iftypes[$iftype] = $iftypes[$iftype] + [Math]::pow(2,$prop.NetLuidIndex)
+ } else {
+ $iftypes[$iftype] = [Math]::pow(2,$prop.NetLuidIndex)
+ }
+ if ($conn -ne $null) {
+ $connections[$prop.NetCfgInstanceId] = $conn.Name
+ Write-Host $prop.NetCfgInstanceId $conn.Name "|" $prop."*IfType" $prop.NetLuidIndex $prop.DriverDesc
+ } else {
+ Write-Host $prop.NetCfgInstanceId [MISSING] "|" $prop."*IfType" $prop.NetLuidIndex $prop.DriverDesc
+ }
+ }
+
+# Someday we may want to process other types than Ethernet as well: $iftypes.GetEnumerator() | ForEach-Object {
+if ($iftypes[6] -gt 9223372036854775808) {
+ Write-Host "Found more than 63 interfaces (mask=" $iftypes[6] ") -- bailing out"
+ exit
+}
+Write-Host "`nChecking if the used LUID index mask is correct:"
+$correctmask = [BitConverter]::GetBytes([int64]($iftypes[6]))
+$actualmask = (Get-ItemProperty -Path "HKLM\SYSTEM\CurrentControlSet\Services\NDIS\IfTypes\6" -Name "IfUsedNetLuidIndices").IfUsedNetLuidIndices
+$needcorrection = $FALSE
+$ai = 0
+$lastnonzero = 0
+for ($ci = 0; $ci -lt $correctmask.Length; $ci++) {
+ if ($ai -lt $actualmask.Length) {
+ $aval = $actualmask[$ai++]
+ } else {
+ $aval = 0
+ }
+ if ($correctmask[$ci] -ne 0) {
+ $lastnonzero = $ci
+ }
+ if ($correctmask[$ci] -eq $aval) {
+ Write-Host "DEBUG: " $correctmask[$ci].ToString("X2") " == " $aval.ToString("X2")
+ } else {
+ Write-Host "DEBUG: " $correctmask[$ci].ToString("X2") " != " $aval.ToString("X2")
+ $needcorrection = $TRUE
+ }
+}
+if ($ai -lt $actualmask.Length) {
+ for (; $ai -lt $actualmask.Length; $ai++) {
+ if ($actualmask[$ai] -eq 0) {
+ Write-Host "DEBUG: 0 == 0"
+ } else {
+ Write-Host "DEBUG: " $actualmask[$ai].ToString("X2") " != 0"
+ $needcorrection = $TRUE
+ }
+ }
+}
+if ($needcorrection) {
+ Write-Host "Current mask is " ($actualmask|foreach {$_.ToString("X2")}) ", while it should be" ($correctmask|foreach {$_.ToString("X2")})
+ if ($confirm) {
+ Set-ItemProperty -Path "HKLM\SYSTEM\CurrentControlSet\Services\NDIS\IfTypes\6" -Name "IfUsedNetLuidIndices" -Value $correctmask -Type Binary -Confirm
+ } else {
+ Set-ItemProperty -Path "HKLM\SYSTEM\CurrentControlSet\Services\NDIS\IfTypes\6" -Name "IfUsedNetLuidIndices" -Value $correctmask -Type Binary
+ }
+} else {
+ Write-Host "The used LUID index mask is correct -- nothing to do"
+}
+
+#Write-Host ($connections | Out-String)
+$ghostcon = @(Get-ChildItem ("HKLM\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}") | Where-Object { !$connections.ContainsKey($_.PSChildName) -and $_.PSChildName -ne "Descriptions" } )
+if ($ghostcon -eq $null) {
+ Write-Host "`nNo ghost connections has been found -- nothing to do"
+} else {
+ Write-Host "`nThe following connections will be removed:"
+ #Write-Host ($ghostcon | Out-String)
+
+ $ghostcon.GetEnumerator() | ForEach-Object {
+ $prop = (Get-ItemProperty "$_\Connection")
+ if ($prop.PnPInstanceId -eq $null) {
+ Write-Host "WARNING! PnPInstanceId does not exist for" $_.PSChildName
+ } elseif (!($prop.PnPInstanceId.ToString() -match "SUN_VBOXNETFLTMP")) {
+ Write-Host "WARNING! PnPInstanceId (" $prop.PnPInstanceId.ToString() ") does not match ROOT\SUN_VBOXNETFLTMP for" $_.PSChildName
+ }
+ if ($prop.Name -eq $null) {
+ Write-Host "WARNING! Name does not exist for" $_.PSChildName
+ } else {
+ $ghostcon_names.Add($_.PSChildName, $prop.Name)
+ Write-Host $_.PSChildName -nonewline
+ Write-Host " " -nonewline
+ Write-Host $prop.Name
+ }
+ }
+
+ $result = AskForConfirmation "Delete Registry Keys" `
+ "Do you want to delete the keys listed above?" `
+ "Deletes all ghost connection keys from the registry." `
+ "No modifications to the registry will be made."
+
+ switch ($result)
+ {
+ 0 {$ghostcon.GetEnumerator() | ForEach-Object { Remove-Item -Path $_.PSPath -Recurse }}
+ 1 {"Removal cancelled."}
+ }
+}
+
+# Delete WFPLWFS parameter keys
+DeleteUnmatchingKeys "Delete WFPLWFS Parameter Keys (Adapter subkey)" "HKLM\SYSTEM\CurrentControlSet\Services\WFPLWFS\Parameters\Adapters"
+DeleteUnmatchingKeys "Delete WFPLWFS Parameter Keys (NdisAdapter subkey)" "HKLM\SYSTEM\CurrentControlSet\Services\WFPLWFS\Parameters\NdisAdapters"
+# Delete Psched parameter keys
+DeleteUnmatchingKeys "Delete Psched Parameter Keys (Adapter subkey)" "HKLM\SYSTEM\CurrentControlSet\Services\Psched\Parameters\Adapters"
+DeleteUnmatchingKeys "Delete Psched Parameter Keys (NdisAdapter subkey)" "HKLM\SYSTEM\CurrentControlSet\Services\Psched\Parameters\NdisAdapters"
+
+# Clean up NSI entries
+$nsi_obsolete = New-Object System.Collections.ArrayList
+$nsi_path = "HKLM\SYSTEM\CurrentControlSet\Control\Nsi\{EB004A11-9B1A-11D4-9123-0050047759BC}\10"
+$nsi = (Get-Item $nsi_path) | Select-Object -ExpandProperty property
+$nsi | ForEach-Object {
+ $value = (Get-ItemProperty -Path $nsi_path -Name $_).$_
+ [byte[]]$guid_bytes = $value[1040..1055]
+ $guid = New-Object -TypeName System.Guid -ArgumentList (,$guid_bytes)
+ $guid_string = $guid.ToString("B").ToUpper()
+ $nsi_conn_name_last = 6 + $value[4] + $value[5]*256
+ $nsi_conn_name = [Text.Encoding]::Unicode.GetString($value[6..$nsi_conn_name_last])
+ $nsi_if_name_last = 522 + $value[520] + $value[521]*256
+ $nsi_if_name = [Text.Encoding]::Unicode.GetString($value[522..$nsi_if_name_last])
+ Write-Host $_ -nonewline
+ Write-Host " " -nonewline
+ Write-Host $guid_string -nonewline
+ Write-Host " " -nonewline
+ if ($connections.ContainsKey($guid_string)) {
+ Write-Host $nsi_if_name
+ } else {
+ [void] $nsi_obsolete.Add($_)
+ Write-Host "[OBSOLETE] " $nsi_if_name -foregroundcolor red
+ }
+}
+
+$result = AskForConfirmation "Delete NSI Entries" `
+ "Do you want to delete the entries marked in red above?" `
+ "Deletes all marked entries from the NSI registry key." `
+ "No modifications to the registry will be made."
+
+switch ($result)
+ {
+ 0 {$nsi_obsolete.GetEnumerator() | ForEach-Object { Remove-ItemProperty -Path $nsi_path -Name $_ }}
+ 1 {"Removal cancelled."}
+ }
+
+# Clean up uninstalled connections
+if ( (Get-ChildItem "HKLM\SYSTEM\CurrentControlSet\Control\Network\Uninstalled" | Measure-Object).Count -gt 10 ) {
+ $result = AskForConfirmation "Delete Uninstalled Network Connection Registry Keys" `
+ "There are over 10 uninstalled network connections accumulated in the registry. Do you want to delete them?" `
+ "Deletes uninstalled connection keys from the registry." `
+ "No modifications to the registry will be made."
+
+ switch ($result)
+ {
+ 0 {Remove-Item -Path "HKLM\SYSTEM\CurrentControlSet\Control\Network\Uninstalled\*" -Recurse}
+ 1 {"Removal cancelled."}
+ }
+} else {
+ Write-Host "Less than 10 uninstalled connections -- no action yet required."
+}
+
+Pop-Location
diff --git a/src/VBox/ValidationKit/testdriver/winbase.py b/src/VBox/ValidationKit/testdriver/winbase.py
new file mode 100755
index 00000000..f043c3f2
--- /dev/null
+++ b/src/VBox/ValidationKit/testdriver/winbase.py
@@ -0,0 +1,336 @@
+# -*- coding: utf-8 -*-
+# $Id: winbase.py $
+
+"""
+This module is here to externalize some Windows specifics that gives pychecker
+a hard time when running on non-Windows systems.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import ctypes;
+import os;
+import sys;
+
+# Windows specific imports.
+import pywintypes; # pylint: disable=import-error
+import winerror; # pylint: disable=import-error
+import win32con; # pylint: disable=import-error
+import win32api; # pylint: disable=import-error
+import win32console; # pylint: disable=import-error
+import win32event; # pylint: disable=import-error
+import win32process; # pylint: disable=import-error
+
+# Validation Kit imports.
+from testdriver import reporter;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+#
+# Windows specific implementation of base functions.
+#
+
+def processInterrupt(uPid):
+ """
+ The Windows version of base.processInterrupt
+
+ Note! This doesn't work terribly well with a lot of processes.
+ """
+ try:
+ # pylint: disable=no-member
+ win32console.GenerateConsoleCtrlEvent(win32con.CTRL_BREAK_EVENT, uPid); # pylint: disable=c-extension-no-member
+ #GenerateConsoleCtrlEvent = ctypes.windll.kernel32.GenerateConsoleCtrlEvent
+ #rc = GenerateConsoleCtrlEvent(1, uPid);
+ #reporter.log('GenerateConsoleCtrlEvent -> %s' % (rc,));
+ fRc = True;
+ except:
+ reporter.logXcpt('uPid=%s' % (uPid,));
+ fRc = False;
+ return fRc;
+
+def postThreadMesssageClose(uTid):
+ """ Posts a WM_CLOSE message to the specified thread."""
+ fRc = False;
+ try:
+ win32api.PostThreadMessage(uTid, win32con.WM_CLOSE, 0, 0); # pylint: disable=no-member,c-extension-no-member
+ fRc = True;
+ except:
+ reporter.logXcpt('uTid=%s' % (uTid,));
+ return fRc;
+
+def postThreadMesssageQuit(uTid):
+ """ Posts a WM_QUIT message to the specified thread."""
+ fRc = False;
+ try:
+ win32api.PostThreadMessage(uTid, win32con.WM_QUIT, # pylint: disable=no-member,c-extension-no-member
+ 0x40010004, 0); # DBG_TERMINATE_PROCESS
+ fRc = True;
+ except:
+ reporter.logXcpt('uTid=%s' % (uTid,));
+ return fRc;
+
+def processTerminate(uPid):
+ """ The Windows version of base.processTerminate """
+ # pylint: disable=no-member
+ fRc = False;
+ try:
+ hProcess = win32api.OpenProcess(win32con.PROCESS_TERMINATE, # pylint: disable=no-member,c-extension-no-member
+ False, uPid);
+ except:
+ reporter.logXcpt('uPid=%s' % (uPid,));
+ else:
+ try:
+ win32process.TerminateProcess(hProcess, # pylint: disable=no-member,c-extension-no-member
+ 0x40010004); # DBG_TERMINATE_PROCESS
+ fRc = True;
+ except:
+ reporter.logXcpt('uPid=%s' % (uPid,));
+ hProcess.Close(); #win32api.CloseHandle(hProcess)
+ return fRc;
+
+def processKill(uPid):
+ """ The Windows version of base.processKill """
+ return processTerminate(uPid);
+
+def processExists(uPid):
+ """ The Windows version of base.processExists """
+ # We try open the process for waiting since this is generally only forbidden in a very few cases.
+ try:
+ hProcess = win32api.OpenProcess(win32con.SYNCHRONIZE, False, uPid); # pylint: disable=no-member,c-extension-no-member
+ except pywintypes.error as oXcpt: # pylint: disable=no-member
+ if oXcpt.winerror == winerror.ERROR_INVALID_PARAMETER:
+ return False;
+ if oXcpt.winerror != winerror.ERROR_ACCESS_DENIED:
+ reporter.logXcpt('uPid=%s oXcpt=%s' % (uPid, oXcpt));
+ return False;
+ reporter.logXcpt('uPid=%s oXcpt=%s' % (uPid, oXcpt));
+ except Exception as oXcpt:
+ reporter.logXcpt('uPid=%s' % (uPid,));
+ return False;
+ else:
+ hProcess.Close(); #win32api.CloseHandle(hProcess)
+ return True;
+
+def processCheckPidAndName(uPid, sName):
+ """ The Windows version of base.processCheckPidAndName """
+ fRc = processExists(uPid);
+ if fRc is True:
+ try:
+ from win32com.client import GetObject; # pylint: disable=import-error
+ oWmi = GetObject('winmgmts:');
+ aoProcesses = oWmi.InstancesOf('Win32_Process');
+ for oProcess in aoProcesses:
+ if long(oProcess.Properties_("ProcessId").Value) == uPid:
+ sCurName = oProcess.Properties_("Name").Value;
+ reporter.log2('uPid=%s sName=%s sCurName=%s' % (uPid, sName, sCurName));
+ sName = sName.lower();
+ sCurName = sCurName.lower();
+ if os.path.basename(sName) == sName:
+ sCurName = os.path.basename(sCurName);
+
+ if sCurName == sName \
+ or sCurName + '.exe' == sName \
+ or sCurName == sName + '.exe':
+ fRc = True;
+ break;
+ except:
+ reporter.logXcpt('uPid=%s sName=%s' % (uPid, sName));
+ return fRc;
+
+#
+# Some helper functions.
+#
+def processCreate(sName, asArgs):
+ """
+ Returns a (pid, handle, tid) tuple on success. (-1, None) on failure (logged).
+ """
+
+ # Construct a command line.
+ sCmdLine = '';
+ for sArg in asArgs:
+ if sCmdLine == '':
+ sCmdLine += '"';
+ else:
+ sCmdLine += ' "';
+ sCmdLine += sArg;
+ sCmdLine += '"';
+
+ # Try start the process.
+ # pylint: disable=no-member
+ dwCreationFlags = win32con.CREATE_NEW_PROCESS_GROUP;
+ oStartupInfo = win32process.STARTUPINFO(); # pylint: disable=c-extension-no-member
+ try:
+ (hProcess, hThread, uPid, uTid) = win32process.CreateProcess(sName, # pylint: disable=c-extension-no-member
+ sCmdLine, # CommandLine
+ None, # ProcessAttributes
+ None, # ThreadAttibutes
+ 1, # fInheritHandles
+ dwCreationFlags,
+ None, # Environment
+ None, # CurrentDirectory.
+ oStartupInfo);
+ except:
+ reporter.logXcpt('sName="%s" sCmdLine="%s"' % (sName, sCmdLine));
+ return (-1, None, -1);
+
+ # Dispense with the thread handle.
+ try:
+ hThread.Close(); # win32api.CloseHandle(hThread);
+ except:
+ reporter.logXcpt();
+
+ # Try get full access to the process.
+ try:
+ hProcessFullAccess = win32api.DuplicateHandle( # pylint: disable=c-extension-no-member
+ win32api.GetCurrentProcess(), # pylint: disable=c-extension-no-member
+ hProcess,
+ win32api.GetCurrentProcess(), # pylint: disable=c-extension-no-member
+ win32con.PROCESS_TERMINATE
+ | win32con.PROCESS_QUERY_INFORMATION
+ | win32con.SYNCHRONIZE
+ | win32con.DELETE,
+ False,
+ 0);
+ hProcess.Close(); # win32api.CloseHandle(hProcess);
+ hProcess = hProcessFullAccess;
+ except:
+ reporter.logXcpt();
+ reporter.log2('processCreate -> %#x, hProcess=%s %#x' % (uPid, hProcess, hProcess.handle,));
+ return (uPid, hProcess, uTid);
+
+def processPollByHandle(hProcess):
+ """
+ Polls the process handle to see if it has finished (True) or not (False).
+ """
+ try:
+ dwWait = win32event.WaitForSingleObject(hProcess, 0); # pylint: disable=no-member,c-extension-no-member
+ except:
+ reporter.logXcpt('hProcess=%s %#x' % (hProcess, hProcess.handle,));
+ return True;
+ return dwWait != win32con.WAIT_TIMEOUT; #0x102; #
+
+
+def processTerminateByHandle(hProcess):
+ """
+ Terminates the process.
+ """
+ try:
+ win32api.TerminateProcess(hProcess, # pylint: disable=no-member,c-extension-no-member
+ 0x40010004); # DBG_TERMINATE_PROCESS
+ except:
+ reporter.logXcpt('hProcess=%s %#x' % (hProcess, hProcess.handle,));
+ return False;
+ return True;
+
+#
+# Misc
+#
+
+def logMemoryStats():
+ """
+ Logs windows memory stats.
+ """
+ class MemoryStatusEx(ctypes.Structure):
+ """ MEMORYSTATUSEX """
+ kaFields = [
+ ( 'dwLength', ctypes.c_ulong ),
+ ( 'dwMemoryLoad', ctypes.c_ulong ),
+ ( 'ullTotalPhys', ctypes.c_ulonglong ),
+ ( 'ullAvailPhys', ctypes.c_ulonglong ),
+ ( 'ullTotalPageFile', ctypes.c_ulonglong ),
+ ( 'ullAvailPageFile', ctypes.c_ulonglong ),
+ ( 'ullTotalVirtual', ctypes.c_ulonglong ),
+ ( 'ullAvailVirtual', ctypes.c_ulonglong ),
+ ( 'ullAvailExtendedVirtual', ctypes.c_ulonglong ),
+ ];
+ _fields_ = kaFields; # pylint: disable=invalid-name
+
+ def __init__(self):
+ super(MemoryStatusEx, self).__init__();
+ self.dwLength = ctypes.sizeof(self);
+
+ try:
+ oStats = MemoryStatusEx();
+ ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(oStats));
+ except:
+ reporter.logXcpt();
+ return False;
+
+ reporter.log('Memory statistics:');
+ for sField, _ in MemoryStatusEx.kaFields:
+ reporter.log(' %32s: %s' % (sField, getattr(oStats, sField)));
+ return True;
+
+def checkProcessHeap():
+ """
+ Calls HeapValidate(GetProcessHeap(), 0, NULL);
+ """
+
+ # Get the process heap.
+ try:
+ hHeap = ctypes.windll.kernel32.GetProcessHeap();
+ except:
+ reporter.logXcpt();
+ return False;
+
+ # Check it.
+ try:
+ fIsOkay = ctypes.windll.kernel32.HeapValidate(hHeap, 0, None);
+ except:
+ reporter.logXcpt();
+ return False;
+
+ if fIsOkay == 0:
+ reporter.log('HeapValidate failed!');
+
+ # Try trigger a dump using c:\utils\procdump64.exe.
+ from common import utils;
+
+ iPid = os.getpid();
+ asArgs = [ 'e:\\utils\\procdump64.exe', '-ma', '%s' % (iPid,), 'c:\\CrashDumps\\python.exe-%u-heap.dmp' % (iPid,)];
+ if utils.getHostArch() != 'amd64':
+ asArgs[0] = 'c:\\utils\\procdump.exe'
+ reporter.log('Trying to dump this process using: %s' % (asArgs,));
+ utils.processCall(asArgs);
+
+ # Generate a crash exception.
+ ctypes.windll.msvcrt.strcpy(None, None, 1024);
+
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/Makefile.kmk b/src/VBox/ValidationKit/testmanager/Makefile.kmk
new file mode 100644
index 00000000..de7c4cc0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/Makefile.kmk
@@ -0,0 +1,54 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+include $(PATH_SUB_CURRENT)/cgi/Makefile.kmk
+include $(PATH_SUB_CURRENT)/core/Makefile.kmk
+include $(PATH_SUB_CURRENT)/batch/Makefile.kmk
+include $(PATH_SUB_CURRENT)/debug/Makefile.kmk
+include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
+include $(PATH_SUB_CURRENT)/webui/Makefile.kmk
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+VBOX_VALIDATIONKIT_JS_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/htdocs/js/*.js)
+
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/__init__.py b/src/VBox/ValidationKit/testmanager/__init__.py
new file mode 100644
index 00000000..71137716
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Test Manager.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/testmanager/apache-template-2.2.conf b/src/VBox/ValidationKit/testmanager/apache-template-2.2.conf
new file mode 100644
index 00000000..0064cfaa
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/apache-template-2.2.conf
@@ -0,0 +1,87 @@
+# $Id: apache-template-2.2.conf $
+## @file
+# Test Manager - Apache 2.2 configuration sample.
+#
+# Requires TestManagerRootDir to be set in the environment (envvars file for instance).
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+<LocationMatch "^/testmanager/logout.py">
+ AuthType Basic
+ AuthName "Test Manager"
+ AuthUserFile ${TestManagerRootDir}/misc/htpasswd-logout
+ Require user logout
+</LocationMatch>
+
+<LocationMatch "^/testmanager/(?!(testboxdisp.py|logout.py|/*htdocs/downloads/.*))">
+ AuthType Basic
+ AuthName "Test Manager"
+ AuthUserFile ${TestManagerRootDir}/misc/htpasswd-sample
+ Require valid-user
+</LocationMatch>
+
+# These two directives are only for local testing!
+Alias /testmanager/htdocs/downloads/VBoxValidationKit.zip ${VBoxBuildOutputDir}/VBoxValidationKit.zip
+<Location /testmanager/htdocs/downloads/VBoxValidationKit.zip>
+ Options Indexes
+ Order allow,deny
+ Allow from all
+</Location>
+
+Alias /testmanager/htdocs/ ${TestManagerRootDir}/htdocs/
+<Directory ${TestManagerRootDir}/htdocs/>
+ AllowOverride None
+ Options Indexes
+ Order allow,deny
+ Allow from all
+</Directory>
+
+Alias /testmanager/logs/ /var/tmp/testmanager/
+<Directory /var/tmp/testmanager/>
+ AllowOverride None
+ Options Indexes
+ Order allow,deny
+ Allow from all
+</Directory>
+
+Alias /testmanager/ ${TestManagerRootDir}/cgi/
+<Directory ${TestManagerRootDir}/cgi/>
+ AllowOverride None
+ Options Indexes ExecCGI
+ DirectoryIndex index.py
+ AddHandler cgi-script .py
+ Order allow,deny
+ Allow from all
+</Directory>
+
diff --git a/src/VBox/ValidationKit/testmanager/apache-template-2.4.conf b/src/VBox/ValidationKit/testmanager/apache-template-2.4.conf
new file mode 100644
index 00000000..e38c5924
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/apache-template-2.4.conf
@@ -0,0 +1,81 @@
+# $Id: apache-template-2.4.conf $
+## @file
+# Test Manager - Apache 2.4 configuration sample.
+#
+# Use the new Define directive to define TestManagerRootDir and
+# VBoxBuildOutputDir before including this file.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+<LocationMatch "^/testmanager/logout.py">
+ AuthType Basic
+ AuthName "Test Manager"
+ AuthUserFile ${TestManagerRootDir}/misc/htpasswd-logout
+ Require user logout
+</LocationMatch>
+
+<LocationMatch "^/testmanager/(?!(testboxdisp.py|logout.py|/*htdocs/downloads/.*))">
+ AuthType Basic
+ AuthName "Test Manager"
+ AuthUserFile ${TestManagerRootDir}/misc/htpasswd-sample
+ Require valid-user
+</LocationMatch>
+
+# These two directives are only for local testing!
+Alias /testmanager/htdocs/downloads/VBoxValidationKit.zip ${VBoxBuildOutputDir}/VBoxValidationKit.zip
+<Location /testmanager/htdocs/downloads/VBoxValidationKit.zip>
+ Options Indexes
+ Require all granted
+</Location>
+
+Alias /testmanager/htdocs/ ${TestManagerRootDir}/htdocs/
+<Directory ${TestManagerRootDir}/htdocs/>
+ AllowOverride None
+ Options Indexes
+</Directory>
+
+Alias /testmanager/logs/ /var/tmp/testmanager/
+<Directory /var/tmp/testmanager/>
+ AllowOverride None
+ Options Indexes
+</Directory>
+
+Alias /testmanager/ ${TestManagerRootDir}/cgi/
+<Directory ${TestManagerRootDir}/cgi/>
+ AllowOverride None
+ Options Indexes ExecCGI
+ DirectoryIndex index.py
+ AddHandler cgi-script .py
+</Directory>
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/Makefile.kmk b/src/VBox/ValidationKit/testmanager/batch/Makefile.kmk
new file mode 100644
index 00000000..74d882cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/add_build.py b/src/VBox/ValidationKit/testmanager/batch/add_build.py
new file mode 100755
index 00000000..a82da5eb
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/add_build.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: add_build.py $
+# pylint: disable=line-too-long
+
+"""
+Interface used by the tinderbox server side software to add a fresh build.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys;
+import os;
+from optparse import OptionParser; # pylint: disable=deprecated-module
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.build import BuildDataEx, BuildLogic, BuildCategoryData;
+
+class Build(object): # pylint: disable=too-few-public-methods
+ """
+ Add build info into Test Manager database.
+ """
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+
+ oParser = OptionParser();
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true',
+ help = 'Quiet execution');
+ oParser.add_option('-b', '--branch', dest = 'sBranch', metavar = '<branch>',
+ help = 'branch name (default: trunk)', default = 'trunk');
+ oParser.add_option('-p', '--product', dest = 'sProductName', metavar = '<name>',
+ help = 'The product name.');
+ oParser.add_option('-r', '--revision', dest = 'iRevision', metavar = '<rev>',
+ help = 'revision number');
+ oParser.add_option('-R', '--repository', dest = 'sRepository', metavar = '<repository>',
+ help = 'Version control repository name.');
+ oParser.add_option('-t', '--type', dest = 'sBuildType', metavar = '<type>',
+ help = 'build type (debug, release etc.)');
+ oParser.add_option('-v', '--version', dest = 'sProductVersion', metavar = '<ver>',
+ help = 'The product version number (suitable for RTStrVersionCompare)');
+ oParser.add_option('-o', '--os-arch', dest = 'asTargetOsArches', metavar = '<os.arch>', action = 'append',
+ help = 'Target OS and architecture. This option can be repeated.');
+ oParser.add_option('-l', '--log', dest = 'sBuildLogPath', metavar = '<url>',
+ help = 'URL to the build logs (optional).');
+ oParser.add_option('-f', '--file', dest = 'asFiles', metavar = '<file|url>', action = 'append',
+ help = 'URLs or build share relative path to a build output file. This option can be repeated.');
+
+ (self.oConfig, _) = oParser.parse_args();
+
+ # Check command line
+ asMissing = [];
+ if self.oConfig.sBranch is None: asMissing.append('--branch');
+ if self.oConfig.iRevision is None: asMissing.append('--revision');
+ if self.oConfig.sProductVersion is None: asMissing.append('--version');
+ if self.oConfig.sProductName is None: asMissing.append('--product');
+ if self.oConfig.sBuildType is None: asMissing.append('--type');
+ if self.oConfig.asTargetOsArches is None: asMissing.append('--os-arch');
+ if self.oConfig.asFiles is None: asMissing.append('--file');
+ if asMissing:
+ sys.stderr.write('syntax error: Missing: %s\n' % (asMissing,));
+ sys.exit(1);
+ # Temporary default.
+ if self.oConfig.sRepository is None:
+ self.oConfig.sRepository = 'vbox';
+
+ def add(self):
+ """
+ Add build data record into database.
+ """
+ oDb = TMDatabaseConnection()
+
+ # Assemble the build data.
+ oBuildData = BuildDataEx()
+ oBuildData.idBuildCategory = None;
+ oBuildData.iRevision = self.oConfig.iRevision
+ oBuildData.sVersion = self.oConfig.sProductVersion
+ oBuildData.sLogUrl = self.oConfig.sBuildLogPath
+ oBuildData.sBinaries = ','.join(self.oConfig.asFiles);
+ oBuildData.oCat = BuildCategoryData().initFromValues(sProduct = self.oConfig.sProductName,
+ sRepository = self.oConfig.sRepository,
+ sBranch = self.oConfig.sBranch,
+ sType = self.oConfig.sBuildType,
+ asOsArches = self.oConfig.asTargetOsArches);
+
+ # Add record to database
+ try:
+ BuildLogic(oDb).addEntry(oBuildData, fCommit = True);
+ except:
+ if self.oConfig.fQuiet:
+ sys.exit(1);
+ raise;
+ oDb.close();
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(Build().add());
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/check_for_deleted_builds.py b/src/VBox/ValidationKit/testmanager/batch/check_for_deleted_builds.py
new file mode 100755
index 00000000..1ac5ab95
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/check_for_deleted_builds.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: check_for_deleted_builds.py $
+# pylint: disable=line-too-long
+
+"""
+Admin job for checking detecting deleted builds.
+
+This is necessary when the tinderbox <-> test manager interface was
+busted and the build info in is out of sync. The result is generally
+a lot of skipped tests because of missing builds, typically during
+bisecting problems.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys;
+import os;
+from optparse import OptionParser; # pylint: disable=deprecated-module
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.build import BuildLogic;
+
+
+
+class BuildChecker(object): # pylint: disable=too-few-public-methods
+ """
+ Add build info into Test Manager database.
+ """
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+
+ oParser = OptionParser();
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true', default = False,
+ help = 'Quiet execution');
+ oParser.add_option('--dry-run', dest = 'fRealRun', action = 'store_false', default = False,
+ help = 'Dry run');
+ oParser.add_option('--real-run', dest = 'fRealRun', action = 'store_true', default = False,
+ help = 'Real run');
+
+ (self.oConfig, _) = oParser.parse_args();
+ if not self.oConfig.fQuiet:
+ if not self.oConfig.fRealRun:
+ print('Dry run.');
+ else:
+ print('Real run! Will commit findings!');
+
+
+ def checkBuilds(self):
+ """
+ Add build data record into database.
+ """
+ oDb = TMDatabaseConnection();
+ oBuildLogic = BuildLogic(oDb);
+
+ tsNow = oDb.getCurrentTimestamp();
+ cMaxRows = 1024;
+ iStart = 0;
+ while True:
+ aoBuilds = oBuildLogic.fetchForListing(iStart, cMaxRows, tsNow);
+ if not self.oConfig.fQuiet and aoBuilds:
+ print('Processing builds #%s thru #%s' % (aoBuilds[0].idBuild, aoBuilds[-1].idBuild));
+
+ for oBuild in aoBuilds:
+ if oBuild.fBinariesDeleted is False:
+ rc = oBuild.areFilesStillThere();
+ if rc is False:
+ if not self.oConfig.fQuiet:
+ print('missing files for build #%s / r%s / %s / %s / %s / %s / %s'
+ % (oBuild.idBuild, oBuild.iRevision, oBuild.sVersion, oBuild.oCat.sType,
+ oBuild.oCat.sBranch, oBuild.oCat.sProduct, oBuild.oCat.asOsArches,));
+ print(' %s' % (oBuild.sBinaries,));
+ if self.oConfig.fRealRun is True:
+ oBuild.fBinariesDeleted = True;
+ oBuildLogic.editEntry(oBuild, fCommit = True);
+ elif rc is True and not self.oConfig.fQuiet:
+ print('build #%s still have its files' % (oBuild.idBuild,));
+ elif rc is None and not self.oConfig.fQuiet:
+ print('Unable to determine state of build #%s' % (oBuild.idBuild,));
+
+ # advance
+ if len(aoBuilds) < cMaxRows:
+ break;
+ iStart += len(aoBuilds);
+
+ oDb.close();
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(BuildChecker().checkBuilds());
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/close_orphaned_testsets.py b/src/VBox/ValidationKit/testmanager/batch/close_orphaned_testsets.py
new file mode 100755
index 00000000..932122b5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/close_orphaned_testsets.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: close_orphaned_testsets.py $
+# pylint: disable=line-too-long
+
+"""
+Maintenance tool for closing orphaned testsets.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys
+import os
+from optparse import OptionParser; # pylint: disable=deprecated-module
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksTestManagerDir)
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection
+from testmanager.core.testset import TestSetLogic;
+
+
+class CloseOrphanedTestSets(object):
+ """
+ Finds and closes orphaned testsets.
+ """
+
+ def __init__(self):
+ """
+ Parse command line
+ """
+ oParser = OptionParser();
+ oParser.add_option('-d', '--just-do-it', dest='fJustDoIt', action='store_true',
+ help='Do the database changes.');
+
+
+ (self.oConfig, _) = oParser.parse_args();
+
+
+ def main(self):
+ """ Main method. """
+ oDb = TMDatabaseConnection();
+
+ # Get a list of orphans.
+ oLogic = TestSetLogic(oDb);
+ aoOrphans = oLogic.fetchOrphaned();
+ if aoOrphans:
+ # Complete them.
+ if self.oConfig.fJustDoIt:
+ print('Completing %u test sets as abandoned:' % (len(aoOrphans),));
+ for oTestSet in aoOrphans:
+ print('#%-7u: idTestBox=%-3u tsCreated=%s tsDone=%s'
+ % (oTestSet.idTestSet, oTestSet.idTestBox, oTestSet.tsCreated, oTestSet.tsDone));
+ oLogic.completeAsAbandoned(oTestSet.idTestSet);
+ print('Committing...');
+ oDb.commit();
+ else:
+ for oTestSet in aoOrphans:
+ print('#%-7u: idTestBox=%-3u tsCreated=%s tsDone=%s'
+ % (oTestSet.idTestSet, oTestSet.idTestBox, oTestSet.tsCreated, oTestSet.tsDone));
+ print('Not completing any testsets without seeing the --just-do-it option.');
+ else:
+ print('No orphaned test sets.\n');
+ return 0;
+
+
+if __name__ == '__main__':
+ sys.exit(CloseOrphanedTestSets().main())
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/del_build.py b/src/VBox/ValidationKit/testmanager/batch/del_build.py
new file mode 100755
index 00000000..76e43344
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/del_build.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: del_build.py $
+# pylint: disable=line-too-long
+
+"""
+Interface used by the tinderbox server side software to mark build binaries
+deleted.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys
+import os
+from optparse import OptionParser; # pylint: disable=deprecated-module
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksTestManagerDir)
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection
+from testmanager.core.build import BuildLogic
+
+
+def markBuildsDeleted():
+ """
+ Marks the builds using the specified binaries as deleted.
+ """
+
+ oParser = OptionParser()
+ oParser.add_option('-q', '--quiet', dest='fQuiet', action='store_true',
+ help='Quiet execution');
+
+ (oConfig, asArgs) = oParser.parse_args()
+ if not asArgs:
+ if not oConfig.fQuiet:
+ sys.stderr.write('syntax error: No builds binaries specified\n');
+ return 1;
+
+
+ oDb = TMDatabaseConnection()
+ oLogic = BuildLogic(oDb)
+
+ for sBuildBin in asArgs:
+ try:
+ cBuilds = oLogic.markDeletedByBinaries(sBuildBin, fCommit = True)
+ except:
+ if oConfig.fQuiet:
+ sys.exit(1);
+ raise;
+ else:
+ if not oConfig.fQuiet:
+ print("del_build.py: Marked %u builds associated with '%s' as deleted." % (cBuilds, sBuildBin,));
+
+ oDb.close()
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(markBuildsDeleted())
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/filearchiver.py b/src/VBox/ValidationKit/testmanager/batch/filearchiver.py
new file mode 100755
index 00000000..10a772ae
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/filearchiver.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: filearchiver.py $
+# pylint: disable=line-too-long
+
+"""
+A cronjob that compresses logs and other files, moving them to the
+g_ksZipFileAreaRootDir storage area.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys
+import os
+from optparse import OptionParser; # pylint: disable=deprecated-module
+import time;
+import zipfile;
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksTestManagerDir)
+
+# Test Manager imports
+from common import utils;
+from testmanager import config;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.testset import TestSetData, TestSetLogic;
+
+
+
+class FileArchiverBatchJob(object): # pylint: disable=too-few-public-methods
+ """
+ Log+files comp
+ """
+
+ def __init__(self, oOptions):
+ """
+ Parse command line
+ """
+ self.fVerbose = oOptions.fVerbose;
+ self.sSrcDir = config.g_ksFileAreaRootDir;
+ self.sDstDir = config.g_ksZipFileAreaRootDir;
+ #self.oTestSetLogic = TestSetLogic(TMDatabaseConnection(self.dprint if self.fVerbose else None));
+ self.oTestSetLogic = TestSetLogic(TMDatabaseConnection(None));
+ self.fDryRun = oOptions.fDryRun;
+
+ def dprint(self, sText):
+ """ Verbose output. """
+ if self.fVerbose:
+ print(sText);
+ return True;
+
+ def warning(self, sText):
+ """Prints a warning."""
+ print(sText);
+ return True;
+
+ def _processTestSet(self, idTestSet, asFiles, sCurDir):
+ """
+ Worker for processDir.
+ Same return codes as processDir.
+ """
+
+ sBaseFilename = os.path.join(sCurDir, 'TestSet-%d' % (idTestSet,));
+ if sBaseFilename[0:2] == ('.' + os.path.sep):
+ sBaseFilename = sBaseFilename[2:];
+ sSrcFileBase = os.path.join(self.sSrcDir, sBaseFilename + '-');
+
+ #
+ # Skip the file if the test set is still running.
+ # But delete them if the testset is not found.
+ #
+ oTestSet = self.oTestSetLogic.tryFetch(idTestSet);
+ if oTestSet is not None and sBaseFilename != oTestSet.sBaseFilename:
+ self.warning('TestSet %d: Deleting because sBaseFilename differs: "%s" (disk) vs "%s" (db)' \
+ % (idTestSet, sBaseFilename, oTestSet.sBaseFilename,));
+ oTestSet = None;
+
+ if oTestSet is not None:
+ if oTestSet.enmStatus == TestSetData.ksTestStatus_Running:
+ self.dprint('Skipping test set #%d, still running' % (idTestSet,));
+ return True;
+
+ #
+ # If we have a zip file already, don't try recreate it as we might
+ # have had trouble removing the source files.
+ #
+ sDstDirPath = os.path.join(self.sDstDir, sCurDir);
+ sZipFileNm = os.path.join(sDstDirPath, 'TestSet-%d.zip' % (idTestSet,));
+ if not os.path.exists(sZipFileNm):
+ #
+ # Create zip file with all testset files as members.
+ #
+ self.dprint('TestSet %d: Creating %s...' % (idTestSet, sZipFileNm,));
+ if not self.fDryRun:
+
+ if not os.path.exists(sDstDirPath):
+ os.makedirs(sDstDirPath, 0o755);
+
+ utils.noxcptDeleteFile(sZipFileNm + '.tmp');
+ with zipfile.ZipFile(sZipFileNm + '.tmp', 'w', zipfile.ZIP_DEFLATED, allowZip64 = True) as oZipFile:
+ for sFile in asFiles:
+ sSuff = os.path.splitext(sFile)[1];
+ if sSuff in [ '.png', '.webm', '.gz', '.bz2', '.zip', '.mov', '.avi', '.mpg', '.gif', '.jpg' ]:
+ ## @todo Consider storing these files outside the zip if they are a little largish.
+ self.dprint('TestSet %d: Storing %s...' % (idTestSet, sFile));
+ oZipFile.write(sSrcFileBase + sFile, sFile, zipfile.ZIP_STORED);
+ else:
+ self.dprint('TestSet %d: Deflating %s...' % (idTestSet, sFile));
+ oZipFile.write(sSrcFileBase + sFile, sFile, zipfile.ZIP_DEFLATED);
+
+ #
+ # .zip.tmp -> .zip.
+ #
+ utils.noxcptDeleteFile(sZipFileNm);
+ os.rename(sZipFileNm + '.tmp', sZipFileNm);
+
+ #else: Dry run.
+ else:
+ self.dprint('TestSet %d: zip file exists already (%s)' % (idTestSet, sZipFileNm,));
+
+ #
+ # Delete the files.
+ #
+ fRc = True;
+ if self.fVerbose:
+ self.dprint('TestSet %d: deleting file: %s' % (idTestSet, asFiles));
+ if not self.fDryRun:
+ for sFile in asFiles:
+ if utils.noxcptDeleteFile(sSrcFileBase + sFile) is False:
+ self.warning('TestSet %d: Failed to delete "%s" (%s)' % (idTestSet, sFile, sSrcFileBase + sFile,));
+ fRc = False;
+
+ return fRc;
+
+
+ def processDir(self, sCurDir):
+ """
+ Process the given directory (relative to sSrcDir and sDstDir).
+ Returns success indicator.
+ """
+ if self.fVerbose:
+ self.dprint('processDir: %s' % (sCurDir,));
+
+ #
+ # Sift thought the directory content, collecting subdirectories and
+ # sort relevant files by test set.
+ # Generally there will either be subdirs or there will be files.
+ #
+ asSubDirs = [];
+ dTestSets = {};
+ sCurPath = os.path.abspath(os.path.join(self.sSrcDir, sCurDir));
+ for sFile in os.listdir(sCurPath):
+ if os.path.isdir(os.path.join(sCurPath, sFile)):
+ if sFile not in [ '.', '..' ]:
+ asSubDirs.append(sFile);
+ elif sFile.startswith('TestSet-'):
+ # Parse the file name. ASSUMES 'TestSet-%d-filename' format.
+ iSlash1 = sFile.find('-');
+ iSlash2 = sFile.find('-', iSlash1 + 1);
+ if iSlash2 <= iSlash1:
+ self.warning('Bad filename (1): "%s"' % (sFile,));
+ continue;
+
+ try: idTestSet = int(sFile[(iSlash1 + 1):iSlash2]);
+ except:
+ self.warning('Bad filename (2): "%s"' % (sFile,));
+ if self.fVerbose:
+ self.dprint('\n'.join(utils.getXcptInfo(4)));
+ continue;
+
+ if idTestSet <= 0:
+ self.warning('Bad filename (3): "%s"' % (sFile,));
+ continue;
+
+ if iSlash2 + 2 >= len(sFile):
+ self.warning('Bad filename (4): "%s"' % (sFile,));
+ continue;
+ sName = sFile[(iSlash2 + 1):];
+
+ # Add it.
+ if idTestSet not in dTestSets:
+ dTestSets[idTestSet] = [];
+ asTestSet = dTestSets[idTestSet];
+ asTestSet.append(sName);
+
+ #
+ # Test sets.
+ #
+ fRc = True;
+ for idTestSet, oTestSet in dTestSets.items():
+ try:
+ if self._processTestSet(idTestSet, oTestSet, sCurDir) is not True:
+ fRc = False;
+ except:
+ self.warning('TestSet %d: Exception in _processTestSet:\n%s' % (idTestSet, '\n'.join(utils.getXcptInfo()),));
+ fRc = False;
+
+ #
+ # Sub dirs.
+ #
+ for sSubDir in asSubDirs:
+ if self.processDir(os.path.join(sCurDir, sSubDir)) is not True:
+ fRc = False;
+
+ #
+ # Try Remove the directory iff it's not '.' and it's been unmodified
+ # for the last 24h (race protection).
+ #
+ if sCurDir != '.':
+ try:
+ fpModTime = float(os.path.getmtime(sCurPath));
+ if fpModTime + (24*3600) <= time.time():
+ if utils.noxcptRmDir(sCurPath) is True:
+ self.dprint('Removed "%s".' % (sCurPath,));
+ except:
+ pass;
+
+ return fRc;
+
+ @staticmethod
+ def main():
+ """ C-style main(). """
+ #
+ # Parse options.
+ #
+ oParser = OptionParser();
+ oParser.add_option('-v', '--verbose', dest = 'fVerbose', action = 'store_true', default = False,
+ help = 'Verbose output.');
+ oParser.add_option('-q', '--quiet', dest = 'fVerbose', action = 'store_false', default = False,
+ help = 'Quiet operation.');
+ oParser.add_option('-d', '--dry-run', dest = 'fDryRun', action = 'store_true', default = False,
+ help = 'Dry run, do not make any changes.');
+ (oOptions, asArgs) = oParser.parse_args()
+ if asArgs != []:
+ oParser.print_help();
+ return 1;
+
+ #
+ # Do the work.
+ #
+ oBatchJob = FileArchiverBatchJob(oOptions);
+ fRc = oBatchJob.processDir('.');
+ return 0 if fRc is True else 1;
+
+if __name__ == '__main__':
+ sys.exit(FileArchiverBatchJob.main());
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/quota.py b/src/VBox/ValidationKit/testmanager/batch/quota.py
new file mode 100755
index 00000000..e2854881
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/quota.py
@@ -0,0 +1,319 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: quota.py $
+# pylint: disable=line-too-long
+
+"""
+A cronjob that applies quotas to large files in testsets.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys
+import os
+from optparse import OptionParser; # pylint: disable=deprecated-module
+import shutil
+import tempfile;
+import zipfile;
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksTestManagerDir)
+
+# Test Manager imports
+from testmanager import config;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.testset import TestSetLogic;
+
+
+class ArchiveDelFilesBatchJob(object): # pylint: disable=too-few-public-methods
+ """
+ Log+files comp
+ """
+
+ def __init__(self, oOptions):
+ """
+ Parse command line
+ """
+ self.fDryRun = oOptions.fDryRun;
+ self.fVerbose = oOptions.fVerbose;
+ self.sTempDir = tempfile.gettempdir();
+
+ self.dprint('Connecting to DB ...');
+ self.oTestSetLogic = TestSetLogic(TMDatabaseConnection(self.dprint if self.fVerbose else None));
+
+ ## Fetches (and handles) all testsets up to this age (in hours).
+ self.uHoursAgeToHandle = 24;
+ ## Always remove files with these extensions.
+ self.asRemoveFileExt = [ 'webm' ];
+ ## Always remove files which are bigger than this limit.
+ # Set to 0 to disable.
+ self.cbRemoveBiggerThan = 128 * 1024 * 1024;
+
+ def dprint(self, sText):
+ """ Verbose output. """
+ if self.fVerbose:
+ print(sText);
+ return True;
+
+ def warning(self, sText):
+ """Prints a warning."""
+ print(sText);
+ return True;
+
+ def _replaceFile(self, sDstFile, sSrcFile, fDryRun = False, fForce = False):
+ """
+ Replaces / moves a file safely by backing up the existing destination file (if any).
+
+ Returns success indicator.
+ """
+
+ fRc = True;
+
+ # Rename the destination file first (if any).
+ sDstFileTmp = None;
+ if os.path.exists(sDstFile):
+ sDstFileTmp = sDstFile + ".bak";
+ if os.path.exists(sDstFileTmp):
+ if not fForce:
+ print('Replace file: Warning: Temporary destination file "%s" already exists, skipping' % (sDstFileTmp,));
+ fRc = False;
+ else:
+ try:
+ os.remove(sDstFileTmp);
+ except Exception as e:
+ print('Replace file: Error deleting old temporary destination file "%s": %s' % (sDstFileTmp, e));
+ fRc = False;
+ try:
+ if not fDryRun:
+ shutil.move(sDstFile, sDstFileTmp);
+ except Exception as e:
+ print('Replace file: Error moving old destination file "%s" to temporary file "%s": %s' \
+ % (sDstFile, sDstFileTmp, e));
+ fRc = False;
+
+ if not fRc:
+ return False;
+
+ try:
+ if not fDryRun:
+ shutil.move(sSrcFile, sDstFile);
+ except Exception as e:
+ print('Replace file: Error moving source file "%s" to destination "%s": %s' % (sSrcFile, sDstFile, e,));
+ fRc = False;
+
+ if sDstFileTmp:
+ if fRc: # Move succeeded, remove backup.
+ try:
+ if not fDryRun:
+ os.remove(sDstFileTmp);
+ except Exception as e:
+ print('Replace file: Error deleting temporary destination file "%s": %s' % (sDstFileTmp, e));
+ fRc = False;
+ else: # Final move failed, roll back.
+ try:
+ if not fDryRun:
+ shutil.move(sDstFileTmp, sDstFile);
+ except Exception as e:
+ print('Replace file: Error restoring old destination file "%s": %s' % (sDstFile, e));
+ fRc = False;
+ return fRc;
+
+ def _processTestSetZip(self, idTestSet, sSrcZipFileAbs):
+ """
+ Worker for processOneTestSet, which processes the testset's ZIP file.
+
+ Returns success indicator.
+ """
+ _ = idTestSet
+
+ with tempfile.NamedTemporaryFile(dir=self.sTempDir, delete=False) as tmpfile:
+ sDstZipFileAbs = tmpfile.name;
+
+ fRc = True;
+
+ try:
+ oSrcZipFile = zipfile.ZipFile(sSrcZipFileAbs, 'r'); # pylint: disable=consider-using-with
+ self.dprint('Processing ZIP archive "%s" ...' % (sSrcZipFileAbs));
+ try:
+ if not self.fDryRun:
+ oDstZipFile = zipfile.ZipFile(sDstZipFileAbs, 'w'); # pylint: disable=consider-using-with
+ self.dprint('Using temporary ZIP archive "%s"' % (sDstZipFileAbs));
+ try:
+ #
+ # First pass: Gather information if we need to do some re-packing.
+ #
+ fDoRepack = False;
+ aoFilesToRepack = [];
+ for oCurFile in oSrcZipFile.infolist():
+ self.dprint('Handling File "%s" ...' % (oCurFile.filename))
+ sFileExt = os.path.splitext(oCurFile.filename)[1];
+
+ if sFileExt \
+ and sFileExt[1:] in self.asRemoveFileExt:
+ self.dprint('\tMatches excluded extensions')
+ fDoRepack = True;
+ elif self.cbRemoveBiggerThan \
+ and oCurFile.file_size > self.cbRemoveBiggerThan:
+ self.dprint('\tIs bigger than %d bytes (%d bytes)' % (self.cbRemoveBiggerThan, oCurFile.file_size))
+ fDoRepack = True;
+ else:
+ aoFilesToRepack.append(oCurFile);
+
+ if not fDoRepack:
+ oSrcZipFile.close();
+ self.dprint('No re-packing necessary, skipping ZIP archive');
+ return True;
+
+ #
+ # Second pass: Re-pack all needed files into our temporary ZIP archive.
+ #
+ for oCurFile in aoFilesToRepack:
+ self.dprint('Re-packing file "%s"' % (oCurFile.filename,))
+ if not self.fDryRun:
+ oBuf = oSrcZipFile.read(oCurFile);
+ oDstZipFile.writestr(oCurFile, oBuf);
+
+ if not self.fDryRun:
+ oDstZipFile.close();
+
+ except Exception as oXcpt4:
+ print('Error handling file "%s" of archive "%s": %s' % (oCurFile.filename, sSrcZipFileAbs, oXcpt4,));
+ return False;
+
+ oSrcZipFile.close();
+
+ if fRc:
+ self.dprint('Moving file "%s" to "%s"' % (sDstZipFileAbs, sSrcZipFileAbs));
+ fRc = self._replaceFile(sSrcZipFileAbs, sDstZipFileAbs, self.fDryRun);
+
+ except Exception as oXcpt3:
+ print('Error creating temporary ZIP archive "%s": %s' % (sDstZipFileAbs, oXcpt3,));
+ return False;
+
+ except Exception as oXcpt1:
+ # Construct a meaningful error message.
+ if os.path.exists(sSrcZipFileAbs):
+ print('Error: Opening file "%s" failed: %s' % (sSrcZipFileAbs, oXcpt1));
+ else:
+ print('Error: File "%s" not found.' % (sSrcZipFileAbs,));
+ return False;
+
+ return fRc;
+
+
+ def processOneTestSet(self, idTestSet, sBasename):
+ """
+ Processes one single testset.
+
+ Returns success indicator.
+ """
+
+ fRc = True;
+ self.dprint('Processing testset %d' % (idTestSet,));
+
+ # Construct absolute ZIP file path.
+ # ZIP is hardcoded in config, so do here.
+ sSrcZipFileAbs = os.path.join(config.g_ksZipFileAreaRootDir, sBasename + '.zip');
+
+ if self._processTestSetZip(idTestSet, sSrcZipFileAbs) is not True:
+ fRc = False;
+
+ return fRc;
+
+ def processTestSets(self):
+ """
+ Processes all testsets according to the set configuration.
+
+ Returns success indicator.
+ """
+
+ aoTestSets = self.oTestSetLogic.fetchByAge(cHoursBack = self.uHoursAgeToHandle);
+ cTestSets = len(aoTestSets);
+ print('Found %d entries in DB' % cTestSets);
+ if not cTestSets:
+ return True; # Nothing to do (yet).
+
+ fRc = True;
+ for oTestSet in aoTestSets:
+ fRc = self.processOneTestSet(oTestSet.idTestSet, oTestSet.sBaseFilename) and fRc;
+ # Keep going.
+
+ return fRc;
+
+ @staticmethod
+ def main():
+ """ C-style main(). """
+ #
+ # Parse options.
+ #
+
+ oParser = OptionParser();
+
+ # Generic options.
+ oParser.add_option('-v', '--verbose', dest = 'fVerbose', action = 'store_true', default = False,
+ help = 'Verbose output.');
+ oParser.add_option('-q', '--quiet', dest = 'fVerbose', action = 'store_false', default = False,
+ help = 'Quiet operation.');
+ oParser.add_option('-d', '--dry-run', dest = 'fDryRun', action = 'store_true', default = False,
+ help = 'Dry run, do not make any changes.');
+
+ (oOptions, asArgs) = oParser.parse_args(sys.argv[1:]);
+ if asArgs != []:
+ oParser.print_help();
+ return 1;
+
+ if oOptions.fDryRun:
+ print('***********************************');
+ print('*** DRY RUN - NO FILES MODIFIED ***');
+ print('***********************************');
+
+ #
+ # Do the work.
+ #
+ fRc = False;
+
+ oBatchJob = ArchiveDelFilesBatchJob(oOptions);
+ fRc = oBatchJob.processTestSets();
+
+ if oOptions.fVerbose:
+ print('SUCCESS' if fRc else 'FAILURE');
+
+ return 0 if fRc is True else 1;
+
+if __name__ == '__main__':
+ sys.exit(ArchiveDelFilesBatchJob.main());
diff --git a/src/VBox/ValidationKit/testmanager/batch/regen_sched_queues.py b/src/VBox/ValidationKit/testmanager/batch/regen_sched_queues.py
new file mode 100755
index 00000000..a20d6cbe
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/regen_sched_queues.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: regen_sched_queues.py $
+# pylint: disable=line-too-long
+
+"""
+Interface used by the admin to regenerate scheduling queues.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys;
+import os;
+from optparse import OptionParser; # pylint: disable=deprecated-module
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.schedulerbase import SchedulerBase;
+from testmanager.core.schedgroup import SchedGroupLogic;
+
+
+
+class RegenSchedQueues(object): # pylint: disable=too-few-public-methods
+ """
+ Regenerates all the scheduling queues.
+ """
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+
+ oParser = OptionParser();
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true', default = False,
+ help = 'Quiet execution');
+ oParser.add_option('-u', '--uid', dest = 'uid', action = 'store', type = 'int', default = 1,
+ help = 'User ID to accredit with this job');
+ oParser.add_option('--profile', dest = 'fProfile', action = 'store_true', default = False,
+ help = 'User ID to accredit with this job');
+
+ (self.oConfig, _) = oParser.parse_args();
+
+
+ def doIt(self):
+ """
+ Does the job.
+ """
+ oDb = TMDatabaseConnection();
+
+ aoGroups = SchedGroupLogic(oDb).getAll();
+ iRc = 0;
+ for oGroup in aoGroups:
+ if not self.oConfig.fQuiet:
+ print('%s (ID %#d):' % (oGroup.sName, oGroup.idSchedGroup,));
+ try:
+ (aoErrors, asMessages) = SchedulerBase.recreateQueue(oDb, self.oConfig.uid, oGroup.idSchedGroup, 2);
+ except Exception as oXcpt:
+ oDb.rollback();
+ print(' !!Hit exception processing "%s": %s' % (oGroup.sName, oXcpt,));
+ else:
+ if not aoErrors:
+ if not self.oConfig.fQuiet:
+ print(' Successfully regenerated.');
+ else:
+ iRc = 1;
+ print(' %d errors:' % (len(aoErrors,)));
+ for oError in aoErrors:
+ if oError[1] is None:
+ print(' !!%s' % (oError[0],));
+ else:
+ print(' !!%s (%s)' % (oError[0], oError[1]));
+ if asMessages and not self.oConfig.fQuiet:
+ print(' %d messages:' % (len(asMessages),));
+ for sMsg in asMessages:
+ print(' ##%s' % (sMsg,));
+ return iRc;
+
+ @staticmethod
+ def main():
+ """ Main function. """
+ oMain = RegenSchedQueues();
+ if oMain.oConfig.fProfile is not True:
+ iRc = oMain.doIt();
+ else:
+ import cProfile;
+ oProfiler = cProfile.Profile();
+ iRc = oProfiler.runcall(oMain.doIt);
+ oProfiler.print_stats(sort = 'time');
+ oProfiler = None;
+ return iRc;
+
+if __name__ == '__main__':
+ sys.exit(RegenSchedQueues().main());
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/vcs_import.py b/src/VBox/ValidationKit/testmanager/batch/vcs_import.py
new file mode 100755
index 00000000..bffa576b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/vcs_import.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: vcs_import.py $
+
+"""
+Cron job for importing revision history for a repository.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys;
+import os;
+from optparse import OptionParser; # pylint: disable=deprecated-module
+import xml.etree.ElementTree as ET;
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from testmanager.config import g_kdBugTrackers;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.vcsrevisions import VcsRevisionData, VcsRevisionLogic;
+from testmanager.core.vcsbugreference import VcsBugReferenceData, VcsBugReferenceLogic;
+from common import utils;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class VcsImport(object): # pylint: disable=too-few-public-methods
+ """
+ Imports revision history from a VSC into the Test Manager database.
+ """
+
+ class BugTracker(object):
+ def __init__(self, sDbName, sTag):
+ self.sDbName = sDbName;
+ self.sTag = sTag;
+
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+
+ oParser = OptionParser()
+ oParser.add_option('-b', '--only-bug-refs', dest = 'fBugRefsOnly', action = 'store_true',
+ help = 'Only do bug references, not revisions.');
+ oParser.add_option('-e', '--extra-option', dest = 'asExtraOptions', metavar = 'vcsoption', action = 'append',
+ help = 'Adds a extra option to the command retrieving the log.');
+ oParser.add_option('-f', '--full', dest = 'fFull', action = 'store_true',
+ help = 'Full revision history import.');
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true',
+ help = 'Quiet execution');
+ oParser.add_option('-R', '--repository', dest = 'sRepository', metavar = '<repository>',
+ help = 'Version control repository name.');
+ oParser.add_option('-s', '--start-revision', dest = 'iStartRevision', metavar = 'start-revision',
+ type = "int", default = 0,
+ help = 'The revision to start at when doing a full import.');
+ oParser.add_option('-t', '--type', dest = 'sType', metavar = '<type>',
+ help = 'The VCS type (default: svn)', choices = [ 'svn', ], default = 'svn');
+ oParser.add_option('-u', '--url', dest = 'sUrl', metavar = '<url>',
+ help = 'The VCS URL');
+
+ (self.oConfig, _) = oParser.parse_args();
+
+ # Check command line
+ asMissing = [];
+ if self.oConfig.sUrl is None: asMissing.append('--url');
+ if self.oConfig.sRepository is None: asMissing.append('--repository');
+ if asMissing:
+ sys.stderr.write('syntax error: Missing: %s\n' % (asMissing,));
+ sys.exit(1);
+
+ assert self.oConfig.sType == 'svn';
+
+ def main(self):
+ """
+ Main function.
+ """
+ oDb = TMDatabaseConnection();
+ oLogic = VcsRevisionLogic(oDb);
+ oBugLogic = VcsBugReferenceLogic(oDb);
+
+ # Where to start.
+ iStartRev = 0;
+ if not self.oConfig.fFull:
+ if not self.oConfig.fBugRefsOnly:
+ iStartRev = oLogic.getLastRevision(self.oConfig.sRepository);
+ else:
+ iStartRev = oBugLogic.getLastRevision(self.oConfig.sRepository);
+ if iStartRev == 0:
+ iStartRev = self.oConfig.iStartRevision;
+
+ # Construct a command line.
+ os.environ['LC_ALL'] = 'en_US.utf-8';
+ asArgs = [
+ 'svn',
+ 'log',
+ '--xml',
+ '--revision', str(iStartRev) + ':HEAD',
+ ];
+ if self.oConfig.asExtraOptions is not None:
+ asArgs.extend(self.oConfig.asExtraOptions);
+ asArgs.append(self.oConfig.sUrl);
+ if not self.oConfig.fQuiet:
+ print('Executing: %s' % (asArgs,));
+ sLogXml = utils.processOutputChecked(asArgs);
+
+ # Parse the XML and add the entries to the database.
+ oParser = ET.XMLParser(target = ET.TreeBuilder(), encoding = 'utf-8');
+ oParser.feed(sLogXml.encode('utf-8')); # Does its own decoding; processOutputChecked always gives us decoded utf-8 now.
+ oRoot = oParser.close();
+
+ for oLogEntry in oRoot.findall('logentry'):
+ iRevision = int(oLogEntry.get('revision'));
+ sAuthor = oLogEntry.findtext('author', 'unspecified').strip(); # cvs2svn entries doesn't have an author.
+ sDate = oLogEntry.findtext('date').strip();
+ sRawMsg = oLogEntry.findtext('msg', '').strip();
+ sMessage = sRawMsg;
+ if sMessage == '':
+ sMessage = ' ';
+ elif len(sMessage) > VcsRevisionData.kcchMax_sMessage:
+ sMessage = sMessage[:VcsRevisionData.kcchMax_sMessage - 4] + ' ...';
+ if not self.oConfig.fQuiet:
+ utils.printOut(u'sDate=%s iRev=%u sAuthor=%s sMsg[%s]=%s'
+ % (sDate, iRevision, sAuthor, type(sMessage).__name__, sMessage));
+
+ if not self.oConfig.fBugRefsOnly:
+ oData = VcsRevisionData().initFromValues(self.oConfig.sRepository, iRevision, sDate, sAuthor, sMessage);
+ oLogic.addVcsRevision(oData);
+
+ # Analyze the raw message looking for bug tracker references.
+ for oBugTracker in g_kdBugTrackers.values():
+ for sTag in oBugTracker.asCommitTags:
+ off = sRawMsg.find(sTag);
+ while off >= 0:
+ off += len(sTag);
+ while off < len(sRawMsg) and sRawMsg[off].isspace():
+ off += 1;
+
+ if off < len(sRawMsg) and sRawMsg[off].isdigit():
+ offNum = off;
+ while off < len(sRawMsg) and sRawMsg[off].isdigit():
+ off += 1;
+ try:
+ iBugNo = long(sRawMsg[offNum:off]);
+ except Exception as oXcpt:
+ utils.printErr(u'error! exception(r%s,"%s"): -> %s' % (iRevision, sRawMsg[offNum:off], oXcpt,));
+ else:
+ if not self.oConfig.fQuiet:
+ utils.printOut(u' r%u -> sBugTracker=%s iBugNo=%s'
+ % (iRevision, oBugTracker.sDbId, iBugNo,));
+
+ oBugData = VcsBugReferenceData().initFromValues(self.oConfig.sRepository, iRevision,
+ oBugTracker.sDbId, iBugNo);
+ oBugLogic.addVcsBugReference(oBugData);
+
+ # next
+ off = sRawMsg.find(sTag, off);
+
+ oDb.commit();
+
+ oDb.close();
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(VcsImport().main());
+
diff --git a/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py b/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py
new file mode 100755
index 00000000..51999e21
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py
@@ -0,0 +1,1832 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: virtual_test_sheriff.py $
+# pylint: disable=line-too-long
+
+"""
+Virtual Test Sheriff.
+
+Duties:
+ - Try to a assign failure reasons to recently failed tests.
+ - Reboot or disable bad test boxes.
+
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports
+import hashlib;
+import os;
+import re;
+import smtplib;
+#import subprocess;
+import sys;
+from email.mime.multipart import MIMEMultipart;
+from email.mime.text import MIMEText;
+from email.utils import COMMASPACE;
+
+if sys.version_info[0] >= 3:
+ from io import BytesIO as BytesIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+else:
+ from StringIO import StringIO as BytesIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+from optparse import OptionParser; # pylint: disable=deprecated-module
+from PIL import Image; # pylint: disable=import-error
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from common import utils;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.build import BuildDataEx;
+from testmanager.core.failurereason import FailureReasonLogic;
+from testmanager.core.testbox import TestBoxLogic, TestBoxData;
+from testmanager.core.testcase import TestCaseDataEx;
+from testmanager.core.testgroup import TestGroupData;
+from testmanager.core.testset import TestSetLogic, TestSetData;
+from testmanager.core.testresults import TestResultLogic, TestResultFileData;
+from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
+from testmanager.core.useraccount import UserAccountLogic;
+from testmanager.config import g_ksSmtpHost, g_kcSmtpPort, g_ksAlertFrom, \
+ g_ksAlertSubject, g_asAlertList #, g_ksLomPassword;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class VirtualTestSheriffCaseFile(object):
+ """
+ A failure investigation case file.
+
+ """
+
+
+ ## Max log file we'll read into memory. (256 MB)
+ kcbMaxLogRead = 0x10000000;
+
+ def __init__(self, oSheriff, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase):
+ self.oSheriff = oSheriff;
+ self.oTestSet = oTestSet; # TestSetData
+ self.oTree = oTree; # TestResultDataEx
+ self.oBuild = oBuild; # BuildDataEx
+ self.oTestBox = oTestBox; # TestBoxData
+ self.oTestGroup = oTestGroup; # TestGroupData
+ self.oTestCase = oTestCase; # TestCaseDataEx
+ self.sMainLog = ''; # The main log file. Empty string if not accessible.
+ self.sSvcLog = ''; # The VBoxSVC log file. Empty string if not accessible.
+
+ # Generate a case file name.
+ self.sName = '#%u: %s' % (self.oTestSet.idTestSet, self.oTestCase.sName,)
+ self.sLongName = '#%u: "%s" on "%s" running %s %s (%s), "%s" by %s, using %s %s %s r%u' \
+ % ( self.oTestSet.idTestSet,
+ self.oTestCase.sName,
+ self.oTestBox.sName,
+ self.oTestBox.sOs,
+ self.oTestBox.sOsVersion,
+ self.oTestBox.sCpuArch,
+ self.oTestBox.sCpuName,
+ self.oTestBox.sCpuVendor,
+ self.oBuild.oCat.sProduct,
+ self.oBuild.oCat.sBranch,
+ self.oBuild.oCat.sType,
+ self.oBuild.iRevision, );
+
+ # Investigation notes.
+ self.tReason = None; # None or one of the ktReason_XXX constants.
+ self.dReasonForResultId = {}; # Reason assignments indexed by idTestResult.
+ self.dCommentForResultId = {}; # Comment assignments indexed by idTestResult.
+
+ #
+ # Reason.
+ #
+
+ def noteReason(self, tReason):
+ """ Notes down a possible reason. """
+ self.oSheriff.dprint(u'noteReason: %s -> %s' % (self.tReason, tReason,));
+ self.tReason = tReason;
+ return True;
+
+ def noteReasonForId(self, tReason, idTestResult, sComment = None):
+ """ Notes down a possible reason for a specific test result. """
+ self.oSheriff.dprint(u'noteReasonForId: %u: %s -> %s%s'
+ % (idTestResult, self.dReasonForResultId.get(idTestResult, None), tReason,
+ (u' (%s)' % (sComment,)) if sComment is not None else ''));
+ self.dReasonForResultId[idTestResult] = tReason;
+ if sComment is not None:
+ self.dCommentForResultId[idTestResult] = sComment;
+ return True;
+
+
+ #
+ # Test classification.
+ #
+
+ def isVBoxTest(self):
+ """ Test classification: VirtualBox (using the build) """
+ return self.oBuild.oCat.sProduct.lower() in [ 'virtualbox', 'vbox' ];
+
+ def isVBoxUnitTest(self):
+ """ Test case classification: The unit test doing all our testcase/*.cpp stuff. """
+ return self.isVBoxTest() \
+ and (self.oTestCase.sName.lower() == 'unit tests' or self.oTestCase.sName.lower().startswith('misc: unit tests'));
+
+ def isVBoxInstallTest(self):
+ """ Test case classification: VirtualBox Guest installation test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('install:');
+
+ def isVBoxUnattendedInstallTest(self):
+ """ Test case classification: VirtualBox Guest installation test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('uinstall:');
+
+ def isVBoxUSBTest(self):
+ """ Test case classification: VirtualBox USB test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('usb:');
+
+ def isVBoxStorageTest(self):
+ """ Test case classification: VirtualBox Storage test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('storage:');
+
+ def isVBoxGAsTest(self):
+ """ Test case classification: VirtualBox Guest Additions test. """
+ return self.isVBoxTest() \
+ and ( self.oTestCase.sName.lower().startswith('guest additions')
+ or self.oTestCase.sName.lower().startswith('ga\'s tests'));
+
+ def isVBoxAPITest(self):
+ """ Test case classification: VirtualBox API test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('api:');
+
+ def isVBoxBenchmarkTest(self):
+ """ Test case classification: VirtualBox Benchmark test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('benchmark:');
+
+ def isVBoxSmokeTest(self):
+ """ Test case classification: Smoke test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('smoketest');
+
+ def isVBoxSerialTest(self):
+ """ Test case classification: Smoke test. """
+ return self.isVBoxTest() \
+ and self.oTestCase.sName.lower().startswith('serial:');
+
+
+ #
+ # Utility methods.
+ #
+
+ def getMainLog(self):
+ """
+ Tries to read the main log file since this will be the first source of information.
+ """
+ if self.sMainLog:
+ return self.sMainLog;
+ (oFile, oSizeOrError, _) = self.oTestSet.openFile('main.log', 'rb');
+ if oFile is not None:
+ try:
+ self.sMainLog = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace');
+ except Exception as oXcpt:
+ self.oSheriff.vprint(u'Error reading main log file: %s' % (oXcpt,))
+ self.sMainLog = '';
+ else:
+ self.oSheriff.vprint(u'Error opening main log file: %s' % (oSizeOrError,));
+ return self.sMainLog;
+
+ def getLogFile(self, oFile):
+ """
+ Tries to read the given file as a utf-8 log file.
+ oFile is a TestFileDataEx instance.
+ Returns empty string if problems opening or reading the file.
+ """
+ sContent = '';
+ (oFile, oSizeOrError, _) = self.oTestSet.openFile(oFile.sFile, 'rb');
+ if oFile is not None:
+ try:
+ sContent = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace');
+ except Exception as oXcpt:
+ self.oSheriff.vprint(u'Error reading the "%s" log file: %s' % (oFile.sFile, oXcpt,))
+ else:
+ self.oSheriff.vprint(u'Error opening the "%s" log file: %s' % (oFile.sFile, oSizeOrError,));
+ return sContent;
+
+ def getSvcLog(self):
+ """
+ Tries to read the VBoxSVC log file as it typically not associated with a failing test result.
+ Note! Returns the first VBoxSVC log file we find.
+ """
+ if not self.sSvcLog:
+ aoSvcLogFiles = self.oTree.getListOfLogFilesByKind(TestResultFileData.ksKind_LogReleaseSvc);
+ if aoSvcLogFiles:
+ self.sSvcLog = self.getLogFile(aoSvcLogFiles[0]);
+ return self.sSvcLog;
+
+ def getScreenshotSha256(self, oFile):
+ """
+ Tries to read the given screenshot file, uncompress it, and do SHA-2
+ on the raw pixels.
+ Returns SHA-2 digest string on success, None on failure.
+ """
+ (oImgFile, _, _) = self.oTestSet.openFile(oFile.sFile, 'rb');
+ try:
+ abImageFile = oImgFile.read();
+ except Exception as oXcpt:
+ self.oSheriff.vprint(u'Error reading the "%s" image file: %s' % (oFile.sFile, oXcpt,))
+ else:
+ try:
+ oImage = Image.open(BytesIO(abImageFile));
+ except Exception as oXcpt:
+ self.oSheriff.vprint(u'Error opening the "%s" image bytes using PIL.Image.open: %s' % (oFile.sFile, oXcpt,))
+ else:
+ try:
+ oHash = hashlib.sha256();
+ if hasattr(oImage, 'tobytes'):
+ oHash.update(oImage.tobytes());
+ else:
+ oHash.update(oImage.tostring()); # pylint: disable=no-member
+ except Exception as oXcpt:
+ self.oSheriff.vprint(u'Error hashing the uncompressed image bytes for "%s": %s' % (oFile.sFile, oXcpt,))
+ else:
+ return oHash.hexdigest();
+ return None;
+
+
+
+ def isSingleTestFailure(self):
+ """
+ Figure out if this is a single test failing or if it's one of the
+ more complicated ones.
+ """
+ if self.oTree.cErrors == 1:
+ return True;
+ if self.oTree.deepCountErrorContributers() <= 1:
+ return True;
+ return False;
+
+
+
+class VirtualTestSheriff(object): # pylint: disable=too-few-public-methods
+ """
+ Add build info into Test Manager database.
+ """
+
+ ## The user account for the virtual sheriff.
+ ksLoginName = 'vsheriff';
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+ self.oDb = None;
+ self.tsNow = None;
+ self.oTestResultLogic = None;
+ self.oTestSetLogic = None;
+ self.oFailureReasonLogic = None; # FailureReasonLogic;
+ self.oTestResultFailureLogic = None; # TestResultFailureLogic
+ self.oLogin = None;
+ self.uidSelf = -1;
+ self.oLogFile = None;
+ self.asBsodReasons = [];
+ self.asUnitTestReasons = [];
+
+ oParser = OptionParser();
+ oParser.add_option('--start-hours-ago', dest = 'cStartHoursAgo', metavar = '<hours>', default = 0, type = 'int',
+ help = 'When to start specified as hours relative to current time. Defauls is right now.', );
+ oParser.add_option('--hours-period', dest = 'cHoursBack', metavar = '<period-in-hours>', default = 2, type = 'int',
+ help = 'Work period specified in hours. Defauls is 2 hours.');
+ oParser.add_option('--real-run-back', dest = 'fRealRun', action = 'store_true', default = False,
+ help = 'Whether to commit the findings to the database. Default is a dry run.');
+ oParser.add_option('--testset', dest = 'aidTestSets', metavar = '<id>', default = [], type = 'int', action = 'append',
+ help = 'Only investigate this one. Accumulates IDs when repeated.');
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true', default = False,
+ help = 'Quiet execution');
+ oParser.add_option('-l', '--log', dest = 'sLogFile', metavar = '<logfile>', default = None,
+ help = 'Where to log messages.');
+ oParser.add_option('--debug', dest = 'fDebug', action = 'store_true', default = False,
+ help = 'Enables debug mode.');
+
+ (self.oConfig, _) = oParser.parse_args();
+
+ if self.oConfig.sLogFile:
+ self.oLogFile = open(self.oConfig.sLogFile, "a"); # pylint: disable=consider-using-with,unspecified-encoding
+ self.oLogFile.write('VirtualTestSheriff: $Revision: 155244 $ \n');
+
+
+ def eprint(self, sText):
+ """
+ Prints error messages.
+ Returns 1 (for exit code usage.)
+ """
+ print('error: %s' % (sText,));
+ if self.oLogFile is not None:
+ if sys.version_info[0] >= 3:
+ self.oLogFile.write(u'error: %s\n' % (sText,));
+ else:
+ self.oLogFile.write((u'error: %s\n' % (sText,)).encode('utf-8'));
+ return 1;
+
+ def dprint(self, sText):
+ """
+ Prints debug info.
+ """
+ if self.oConfig.fDebug:
+ if not self.oConfig.fQuiet:
+ print('debug: %s' % (sText, ));
+ if self.oLogFile is not None:
+ if sys.version_info[0] >= 3:
+ self.oLogFile.write(u'debug: %s\n' % (sText,));
+ else:
+ self.oLogFile.write((u'debug: %s\n' % (sText,)).encode('utf-8'));
+ return 0;
+
+ def vprint(self, sText):
+ """
+ Prints verbose info.
+ """
+ if not self.oConfig.fQuiet:
+ print('info: %s' % (sText,));
+ if self.oLogFile is not None:
+ if sys.version_info[0] >= 3:
+ self.oLogFile.write(u'info: %s\n' % (sText,));
+ else:
+ self.oLogFile.write((u'info: %s\n' % (sText,)).encode('utf-8'));
+ return 0;
+
+ def getFailureReason(self, tReason):
+ """ Gets the failure reason object for tReason. """
+ return self.oFailureReasonLogic.cachedLookupByNameAndCategory(tReason[1], tReason[0]);
+
+ def selfCheck(self):
+ """ Does some self checks, looking up things we expect to be in the database and such. """
+ rcExit = 0;
+ for sAttr in dir(self.__class__):
+ if sAttr.startswith('ktReason_'):
+ tReason = getattr(self.__class__, sAttr);
+ oFailureReason = self.getFailureReason(tReason);
+ if oFailureReason is None:
+ rcExit = self.eprint(u'Failed to find failure reason "%s" in category "%s" in the database!'
+ % (tReason[1], tReason[0],));
+
+ # Check the user account as well.
+ if self.oLogin is None:
+ oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName);
+ if oLogin is None:
+ rcExit = self.eprint(u'Cannot find my user account "%s"!' % (VirtualTestSheriff.ksLoginName,));
+ return rcExit;
+
+ def sendEmailAlert(self, uidAuthor, sBodyText):
+ """
+ Sends email alert.
+ """
+
+ # Get author email
+ self.oDb.execute('SELECT sEmail FROM Users WHERE uid=%s', (uidAuthor,));
+ sFrom = self.oDb.fetchOne();
+ if sFrom is not None:
+ sFrom = sFrom[0];
+ else:
+ sFrom = g_ksAlertFrom;
+
+ # Gather recipient list.
+ asEmailList = [];
+ for sUser in g_asAlertList:
+ self.oDb.execute('SELECT sEmail FROM Users WHERE sUsername=%s', (sUser,));
+ sEmail = self.oDb.fetchOne();
+ if sEmail:
+ asEmailList.append(sEmail[0]);
+ if not asEmailList:
+ return self.eprint('No email addresses to send alter to!');
+
+ # Compose the message.
+ oMsg = MIMEMultipart();
+ oMsg['From'] = sFrom;
+ oMsg['To'] = COMMASPACE.join(asEmailList);
+ oMsg['Subject'] = g_ksAlertSubject;
+ oMsg.attach(MIMEText(sBodyText, 'plain'))
+
+ # Try send it.
+ try:
+ oSMTP = smtplib.SMTP(g_ksSmtpHost, g_kcSmtpPort);
+ oSMTP.sendmail(sFrom, asEmailList, oMsg.as_string())
+ oSMTP.quit()
+ except smtplib.SMTPException as oXcpt:
+ return self.eprint('Failed to send mail: %s' % (oXcpt,));
+
+ return 0;
+
+ def badTestBoxManagement(self):
+ """
+ Looks for bad test boxes and first tries once to reboot them then disables them.
+ """
+ rcExit = 0;
+
+ #
+ # We skip this entirely if we're running in the past and not in harmless debug mode.
+ #
+ if self.oConfig.cStartHoursAgo != 0 \
+ and (not self.oConfig.fDebug or self.oConfig.fRealRun):
+ return rcExit;
+ tsNow = self.tsNow if self.oConfig.fDebug else None;
+ cHoursBack = self.oConfig.cHoursBack if self.oConfig.fDebug else 2;
+ oTestBoxLogic = TestBoxLogic(self.oDb);
+
+ #
+ # Generate a list of failures reasons we consider bad-testbox behavior.
+ #
+ aidFailureReasons = [
+ self.getFailureReason(self.ktReason_Host_DriverNotLoaded).idFailureReason,
+ self.getFailureReason(self.ktReason_Host_DriverNotUnloading).idFailureReason,
+ self.getFailureReason(self.ktReason_Host_DriverNotCompilable).idFailureReason,
+ self.getFailureReason(self.ktReason_Host_InstallationFailed).idFailureReason,
+ ];
+
+ #
+ # Get list of bad test boxes for given period and check them out individually.
+ #
+ aidBadTestBoxes = self.oTestSetLogic.fetchBadTestBoxIds(cHoursBack = cHoursBack, tsNow = tsNow,
+ aidFailureReasons = aidFailureReasons);
+ for idTestBox in aidBadTestBoxes:
+ # Skip if the testbox is already disabled or has a pending reboot command.
+ try:
+ oTestBox = TestBoxData().initFromDbWithId(self.oDb, idTestBox);
+ except Exception as oXcpt:
+ rcExit = self.eprint('Failed to get data for test box #%u in badTestBoxManagement: %s' % (idTestBox, oXcpt,));
+ continue;
+ if not oTestBox.fEnabled:
+ self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has been disabled already.'
+ % ( idTestBox, oTestBox.sName, ));
+ continue;
+ if oTestBox.enmPendingCmd != TestBoxData.ksTestBoxCmd_None:
+ self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has a command pending: %s'
+ % ( idTestBox, oTestBox.sName, oTestBox.enmPendingCmd));
+ continue;
+
+ # Get the most recent testsets for this box (descending on tsDone) and see how bad it is.
+ aoSets = self.oTestSetLogic.fetchSetsForTestBox(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow);
+ cOkay = 0;
+ cBad = 0;
+ iFirstOkay = len(aoSets);
+ for iSet, oSet in enumerate(aoSets):
+ if oSet.enmStatus == TestSetData.ksTestStatus_BadTestBox:
+ cBad += 1;
+ else:
+ # Check for bad failure reasons.
+ oFailure = None;
+ if oSet.enmStatus in TestSetData.kasBadTestStatuses:
+ (oTree, _ ) = self.oTestResultLogic.fetchResultTree(oSet.idTestSet)
+ aoFailedResults = oTree.getListOfFailures();
+ for oFailedResult in aoFailedResults:
+ oFailure = self.oTestResultFailureLogic.getById(oFailedResult.idTestResult);
+ if oFailure is not None and oFailure.idFailureReason in aidFailureReasons:
+ break;
+ oFailure = None;
+ if oFailure is not None:
+ cBad += 1;
+ else:
+ # This is an okay test result then.
+ ## @todo maybe check the elapsed time here, it could still be a bad run?
+ cOkay += 1;
+ iFirstOkay = min(iFirstOkay, iSet);
+ if iSet > 10:
+ break;
+
+ # We react if there are two or more bad-testbox statuses at the head of the
+ # history and at least three in the last 10 results.
+ if iFirstOkay >= 2 and cBad > 2:
+ if oTestBoxLogic.hasTestBoxRecentlyBeenRebooted(idTestBox, cHoursBack = cHoursBack, tsNow = tsNow):
+ sComment = u'Disabling testbox #%u (%s) - iFirstOkay=%u cBad=%u cOkay=%u' \
+ % (idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay);
+ self.vprint(sComment);
+ self.sendEmailAlert(self.uidSelf, sComment);
+ if self.oConfig.fRealRun is True:
+ try:
+ oTestBoxLogic.disableTestBox(idTestBox, self.uidSelf, fCommit = True,
+ sComment = 'Automatically disabled (iFirstOkay=%u cBad=%u cOkay=%u)'
+ % (iFirstOkay, cBad, cOkay),);
+ except Exception as oXcpt:
+ rcExit = self.eprint(u'Error disabling testbox #%u (%u): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
+ else:
+ sComment = u'Rebooting testbox #%u (%s) - iFirstOkay=%u cBad=%u cOkay=%u' \
+ % (idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay);
+ self.vprint(sComment);
+ self.sendEmailAlert(self.uidSelf, sComment);
+ if self.oConfig.fRealRun is True:
+ try:
+ oTestBoxLogic.rebootTestBox(idTestBox, self.uidSelf, fCommit = True,
+ sComment = 'Automatically rebooted (iFirstOkay=%u cBad=%u cOkay=%u)'
+ % (iFirstOkay, cBad, cOkay),);
+ except Exception as oXcpt:
+ rcExit = self.eprint(u'Error rebooting testbox #%u (%s): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
+ else:
+ self.dprint(u'badTestBoxManagement: #%u (%s) looks ok: iFirstOkay=%u cBad=%u cOkay=%u'
+ % ( idTestBox, oTestBox.sName, iFirstOkay, cBad, cOkay));
+
+ ## @todo r=bird: review + rewrite;
+ ## - no selecting here, that belongs in the core/*.py files.
+ ## - preserve existing comments.
+ ## - doing way too much in the try/except block.
+ ## - No password quoting in the sshpass command that always fails (127).
+ ## - Timeout is way to low. testboxmem1 need more than 10 min to take a dump, ages to
+ ## get thru POST and another 5 just to time out in grub. Should be an hour or so.
+ ## Besides, it need to be constant elsewhere in the file, not a variable here.
+ ##
+ ##
+ ## Reset hanged testboxes
+ ##
+ #cStatusTimeoutMins = 10;
+ #
+ #self.oDb.execute('SELECT TestBoxStatuses.idTestBox\n'
+ # ' FROM TestBoxStatuses, TestBoxes\n'
+ # ' WHERE TestBoxStatuses.tsUpdated >= (CURRENT_TIMESTAMP - interval \'%s hours\')\n'
+ # ' AND TestBoxStatuses.tsUpdated < (CURRENT_TIMESTAMP - interval \'%s minutes\')\n'
+ # ' AND TestBoxStatuses.idTestBox = TestBoxes.idTestBox\n'
+ # ' AND Testboxes.tsExpire = \'infinity\'::timestamp', (cHoursBack,cStatusTimeoutMins));
+ #for idTestBox in self.oDb.fetchAll():
+ # idTestBox = idTestBox[0];
+ # try:
+ # oTestBox = TestBoxData().initFromDbWithId(self.oDb, idTestBox);
+ # except Exception as oXcpt:
+ # rcExit = self.eprint('Failed to get data for test box #%u in badTestBoxManagement: %s' % (idTestBox, oXcpt,));
+ # continue;
+ # # Skip if the testbox is already disabled, already reset or there's no iLOM
+ # if not oTestBox.fEnabled or oTestBox.ipLom is None or oTestBox.sComment is not None and oTestBox.sComment.find('Automatically reset') >= 0:
+ # self.dprint(u'badTestBoxManagement: Skipping test box #%u (%s) as it has been disabled already.'
+ # % ( idTestBox, oTestBox.sName, ));
+ # continue;
+ # ## @todo get iLOM credentials from a table?
+ # sCmd = 'sshpass -p%s ssh -oStrictHostKeyChecking=no root@%s show /SP && reset /SYS' % (g_ksLomPassword, oTestBox.ipLom,);
+ # try:
+ # oPs = subprocess.Popen(sCmd, stdout=subprocess.PIPE, shell=True);
+ # sStdout = oPs.communicate()[0];
+ # iRC = oPs.wait();
+ #
+ # oTestBox.sComment = 'Automatically reset (iRC=%u sStdout=%s)' % (iRC, sStdout,);
+ # oTestBoxLogic.editEntry(oTestBox, self.uidSelf, fCommit = True);
+ #
+ # sComment = u'Reset testbox #%u (%s) - iRC=%u sStduot=%s' % ( idTestBox, oTestBox.sName, iRC, sStdout);
+ # self.vprint(sComment);
+ # self.sendEmailAlert(self.uidSelf, sComment);
+ #
+ # except Exception as oXcpt:
+ # rcExit = self.eprint(u'Error resetting testbox #%u (%s): %s\n' % (idTestBox, oTestBox.sName, oXcpt,));
+ #
+ return rcExit;
+
+
+ ## @name Failure reasons we know.
+ ## @{
+
+ ktReason_Add_Installer_Win_Failed = ( 'Additions', 'Win GA install' );
+ ktReason_Add_ShFl_Automount = ( 'Additions', 'Automounting' );
+ ktReason_Add_ShFl_FsPerf = ( 'Additions', 'FsPerf' );
+ ktReason_Add_ShFl_FsPerf_Abend = ( 'Additions', 'FsPerf abend' );
+ ktReason_Add_GstCtl_Preparations = ( 'Additions', 'GstCtl preparations' );
+ ktReason_Add_GstCtl_SessionBasics = ( 'Additions', 'Session basics' );
+ ktReason_Add_GstCtl_SessionProcRefs = ( 'Additions', 'Session process' );
+ ktReason_Add_GstCtl_Session_Reboot = ( 'Additions', 'Session reboot' );
+ ktReason_Add_GstCtl_CopyFromGuest_Timeout = ( 'Additions', 'CopyFromGuest timeout' );
+ ktReason_Add_GstCtl_CopyToGuest_Timeout = ( 'Additions', 'CopyToGuest timeout' );
+ ktReason_Add_GstCtl_CopyToGuest_DstEmpty = ( 'Additions', 'CopyToGuest dst empty' );
+ ktReason_Add_GstCtl_CopyToGuest_DstExists = ( 'Additions', 'CopyToGuest dst exists' );
+ ktReason_Add_FlushViewOfFile = ( 'Additions', 'FlushViewOfFile' );
+ ktReason_Add_Mmap_Coherency = ( 'Additions', 'mmap coherency' );
+ ktReason_BSOD_Recovery = ( 'BSOD', 'Recovery' );
+ ktReason_BSOD_Automatic_Repair = ( 'BSOD', 'Automatic Repair' );
+ ktReason_BSOD_0000007F = ( 'BSOD', '0x0000007F' );
+ ktReason_BSOD_000000D1 = ( 'BSOD', '0x000000D1' );
+ ktReason_BSOD_C0000225 = ( 'BSOD', '0xC0000225 (boot)' );
+ ktReason_Guru_Generic = ( 'Guru Meditations', 'Generic Guru Meditation' );
+ ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_INSTR_NOT_IMPLEMENTED' );
+ ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' );
+ ktReason_Guru_VERR_TRPM_DONT_PANIC = ( 'Guru Meditations', 'VERR_TRPM_DONT_PANIC' );
+ ktReason_Guru_VERR_PGM_PHYS_PAGE_RESERVED = ( 'Guru Meditations', 'VERR_PGM_PHYS_PAGE_RESERVED' );
+ ktReason_Guru_VERR_VMX_INVALID_GUEST_STATE = ( 'Guru Meditations', 'VERR_VMX_INVALID_GUEST_STATE' );
+ ktReason_Guru_VINF_EM_TRIPLE_FAULT = ( 'Guru Meditations', 'VINF_EM_TRIPLE_FAULT' );
+ ktReason_Host_HostMemoryLow = ( 'Host', 'HostMemoryLow' );
+ ktReason_Host_DriverNotLoaded = ( 'Host', 'Driver not loaded' );
+ ktReason_Host_DriverNotUnloading = ( 'Host', 'Driver not unloading' );
+ ktReason_Host_DriverNotCompilable = ( 'Host', 'Driver not compilable' );
+ ktReason_Host_InstallationFailed = ( 'Host', 'Installation failed' );
+ ktReason_Host_InstallationWantReboot = ( 'Host', 'Installation want reboot' );
+ ktReason_Host_InvalidPackage = ( 'Host', 'ERROR_INSTALL_PACKAGE_INVALID' );
+ ktReason_Host_InstallSourceAbsent = ( 'Host', 'ERROR_INSTALL_SOURCE_ABSENT' );
+ ktReason_Host_NotSignedWithBuildCert = ( 'Host', 'Not signed with build cert' );
+ ktReason_Host_DiskFull = ( 'Host', 'Host disk full' );
+ ktReason_Host_DoubleFreeHeap = ( 'Host', 'Double free or corruption' );
+ ktReason_Host_LeftoverService = ( 'Host', 'Leftover service' );
+ ktReason_Host_win32com_gen_py = ( 'Host', 'win32com.gen_py' );
+ ktReason_Host_Reboot_OSX_Watchdog_Timeout = ( 'Host Reboot', 'OSX Watchdog Timeout' );
+ ktReason_Host_Modprobe_Failed = ( 'Host', 'Modprobe failed' );
+ ktReason_Host_Install_Hang = ( 'Host', 'Install hang' );
+ ktReason_Host_NetworkMisconfiguration = ( 'Host', 'Network misconfiguration' );
+ ktReason_Host_TSTInfo_Accuracy_OOR = ( 'Host', 'TSTInfo accuracy out of range' );
+ ktReason_Networking_Nonexistent_host_nic = ( 'Networking', 'Nonexistent host networking interface' );
+ ktReason_Networking_VERR_INTNET_FLT_IF_NOT_FOUND = ( 'Networking', 'VERR_INTNET_FLT_IF_NOT_FOUND' );
+ ktReason_OSInstall_GRUB_hang = ( 'O/S Install', 'GRUB hang' );
+ ktReason_OSInstall_Udev_hang = ( 'O/S Install', 'udev hang' );
+ ktReason_OSInstall_Sata_no_BM = ( 'O/S Install', 'SATA busmaster bit not set' );
+ ktReason_Panic_BootManagerC000000F = ( 'Panic', 'Hardware Changed' );
+ ktReason_Panic_MP_BIOS_IO_APIC = ( 'Panic', 'MP-BIOS/IO-APIC' );
+ ktReason_Panic_HugeMemory = ( 'Panic', 'Huge memory assertion' );
+ ktReason_Panic_IOAPICDoesntWork = ( 'Panic', 'IO-APIC and timer does not work' );
+ ktReason_Panic_TxUnitHang = ( 'Panic', 'Tx Unit Hang' );
+ ktReason_API_std_bad_alloc = ( 'API / (XP)COM', 'std::bad_alloc' );
+ ktReason_API_Digest_Mismatch = ( 'API / (XP)COM', 'Digest mismatch' );
+ ktReason_API_MoveVM_SharingViolation = ( 'API / (XP)COM', 'MoveVM sharing violation' );
+ ktReason_API_MoveVM_InvalidParameter = ( 'API / (XP)COM', 'MoveVM invalid parameter' );
+ ktReason_API_Open_Session_Failed = ( 'API / (XP)COM', 'Open session failed' );
+ ktReason_XPCOM_Exit_Minus_11 = ( 'API / (XP)COM', 'exit -11' );
+ ktReason_XPCOM_VBoxSVC_Hang = ( 'API / (XP)COM', 'VBoxSVC hang' );
+ ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption = ( 'API / (XP)COM', 'VBoxSVC hang + heap corruption' );
+ ktReason_XPCOM_NS_ERROR_CALL_FAILED = ( 'API / (XP)COM', 'NS_ERROR_CALL_FAILED' );
+ ktReason_BootManager_Image_corrupt = ( 'Unknown', 'BOOTMGR Image corrupt' );
+ ktReason_Unknown_Heap_Corruption = ( 'Unknown', 'Heap corruption' );
+ ktReason_Unknown_Reboot_Loop = ( 'Unknown', 'Reboot loop' );
+ ktReason_Unknown_File_Not_Found = ( 'Unknown', 'File not found' );
+ ktReason_Unknown_HalReturnToFirmware = ( 'Unknown', 'HalReturnToFirmware' );
+ ktReason_Unknown_VM_Crash = ( 'Unknown', 'VM crash' );
+ ktReason_Unknown_VM_Terminated = ( 'Unknown', 'VM terminated' );
+ ktReason_Unknown_VM_Start_Error = ( 'Unknown', 'VM Start Error' );
+ ktReason_Unknown_VM_Runtime_Error = ( 'Unknown', 'VM Runtime Error' );
+ ktReason_VMM_kvm_lock_spinning = ( 'VMM', 'kvm_lock_spinning' );
+ ktReason_Ignore_Buggy_Test_Driver = ( 'Ignore', 'Buggy test driver' );
+ ktReason_Ignore_Stale_Files = ( 'Ignore', 'Stale files' );
+ ktReason_Buggy_Build_Broken_Build = ( 'Broken Build', 'Buggy build' );
+ ktReason_GuestBug_CompizVBoxQt = ( 'Guest Bug', 'Compiz + VirtualBox Qt GUI crash' );
+ ## @}
+
+ ## BSOD category.
+ ksBsodCategory = 'BSOD';
+ ## Special reason indicating that the flesh and blood sheriff has work to do.
+ ksBsodAddNew = 'Add new BSOD';
+
+ ## Unit test category.
+ ksUnitTestCategory = 'Unit';
+ ## Special reason indicating that the flesh and blood sheriff has work to do.
+ ksUnitTestAddNew = 'Add new';
+
+ ## Used for indica that we shouldn't report anything for this test result ID and
+ ## consider promoting the previous error to test set level if it's the only one.
+ ktHarmless = ( 'Probably', 'Caused by previous error' );
+
+
+ def caseClosed(self, oCaseFile):
+ """
+ Reports the findings in the case and closes it.
+ """
+ #
+ # Log it and create a dReasonForReasultId we can use below.
+ #
+ dCommentForResultId = oCaseFile.dCommentForResultId;
+ if oCaseFile.dReasonForResultId:
+ # Must weed out ktHarmless.
+ dReasonForResultId = {};
+ for idKey, tReason in oCaseFile.dReasonForResultId.items():
+ if tReason is not self.ktHarmless:
+ dReasonForResultId[idKey] = tReason;
+ if not dReasonForResultId:
+ self.vprint(u'TODO: Closing %s without a real reason, only %s.'
+ % (oCaseFile.sName, oCaseFile.dReasonForResultId));
+ return False;
+
+ # Try promote to single reason.
+ atValues = dReasonForResultId.values();
+ fSingleReason = True;
+ if len(dReasonForResultId) == 1 and next(iter(dReasonForResultId.keys())) != oCaseFile.oTestSet.idTestResult:
+ self.dprint(u'Promoting single reason to whole set: %s' % (next(iter(atValues)),));
+ elif len(dReasonForResultId) > 1 and len(atValues) == list(atValues).count(next(iter(atValues))):
+ self.dprint(u'Merged %d reasons to a single one: %s' % (len(atValues), next(iter(atValues))));
+ else:
+ fSingleReason = False;
+ if fSingleReason:
+ dReasonForResultId = { oCaseFile.oTestSet.idTestResult: next(iter(atValues)), };
+ if dCommentForResultId:
+ dCommentForResultId = { oCaseFile.oTestSet.idTestResult: next(iter(dCommentForResultId.values())), };
+ elif oCaseFile.tReason is not None:
+ dReasonForResultId = { oCaseFile.oTestSet.idTestResult: oCaseFile.tReason, };
+ else:
+ self.vprint(u'Closing %s without a reason - this should not happen!' % (oCaseFile.sName,));
+ return False;
+
+ self.vprint(u'Closing %s with following reason%s: %s'
+ % ( oCaseFile.sName, 's' if len(dReasonForResultId) > 1 else '', dReasonForResultId, ));
+
+ #
+ # Add the test failure reason record(s).
+ #
+ for idTestResult, tReason in dReasonForResultId.items():
+ oFailureReason = self.getFailureReason(tReason);
+ if oFailureReason is not None:
+ sComment = 'Set by $Revision: 155244 $' # Handy for reverting later.
+ if idTestResult in dCommentForResultId:
+ sComment += ': ' + dCommentForResultId[idTestResult];
+
+ oAdd = TestResultFailureData();
+ oAdd.initFromValues(idTestResult = idTestResult,
+ idFailureReason = oFailureReason.idFailureReason,
+ uidAuthor = self.uidSelf,
+ idTestSet = oCaseFile.oTestSet.idTestSet,
+ sComment = sComment,);
+ if self.oConfig.fRealRun:
+ try:
+ self.oTestResultFailureLogic.addEntry(oAdd, self.uidSelf, fCommit = True);
+ except Exception as oXcpt:
+ self.eprint(u'caseClosed: Exception "%s" while adding reason %s for %s'
+ % (oXcpt, oAdd, oCaseFile.sLongName,));
+ else:
+ self.eprint(u'caseClosed: Cannot locate failure reason: %s / %s' % ( tReason[0], tReason[1],));
+ return True;
+
+ #
+ # Tools for assiting log parsing.
+ #
+
+ @staticmethod
+ def matchFollowedByLines(sStr, off, asFollowingLines):
+ """ Worker for isThisFollowedByTheseLines. """
+
+ # Advance off to the end of the line.
+ off = sStr.find('\n', off);
+ if off < 0:
+ return False;
+ off += 1;
+
+ # Match each string with the subsequent lines.
+ for iLine, sLine in enumerate(asFollowingLines):
+ offEnd = sStr.find('\n', off);
+ if offEnd < 0:
+ return iLine + 1 == len(asFollowingLines) and sStr.find(sLine, off) < 0;
+ if sLine and sStr.find(sLine, off, offEnd) < 0:
+ return False;
+
+ # next line.
+ off = offEnd + 1;
+
+ return True;
+
+ @staticmethod
+ def isThisFollowedByTheseLines(sStr, sFirst, asFollowingLines):
+ """
+ Looks for a line contining sFirst which is then followed by lines
+ with the strings in asFollowingLines. (No newline chars anywhere!)
+ Returns True / False.
+ """
+ off = sStr.find(sFirst, 0);
+ while off >= 0:
+ if VirtualTestSheriff.matchFollowedByLines(sStr, off, asFollowingLines):
+ return True;
+ off = sStr.find(sFirst, off + 1);
+ return False;
+
+ @staticmethod
+ def findAndReturnRestOfLine(sHaystack, sNeedle):
+ """
+ Looks for sNeedle in sHaystack.
+ Returns The text following the needle up to the end of the line.
+ Returns None if not found.
+ """
+ if sHaystack is None:
+ return None;
+ off = sHaystack.find(sNeedle);
+ if off < 0:
+ return None;
+ off += len(sNeedle)
+ offEol = sHaystack.find('\n', off);
+ if offEol < 0:
+ offEol = len(sHaystack);
+ return sHaystack[off:offEol]
+
+ @staticmethod
+ def findInAnyAndReturnRestOfLine(asHaystacks, sNeedle):
+ """
+ Looks for sNeedle in zeroe or more haystacks (asHaystack).
+ Returns The text following the first needed found up to the end of the line.
+ Returns None if not found.
+ """
+ for sHaystack in asHaystacks:
+ sRet = VirtualTestSheriff.findAndReturnRestOfLine(sHaystack, sNeedle);
+ if sRet is not None:
+ return sRet;
+ return None;
+
+
+ #
+ # The investigative units.
+ #
+
+ katSimpleInstallUninstallMainLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( False, ktReason_Host_LeftoverService,
+ 'SERVICE_NAME: vbox' ),
+ ( False, ktReason_Host_LeftoverService,
+ 'Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!'),
+ ];
+
+ kdatSimpleInstallUninstallMainLogReasonsPerOs = {
+ 'darwin': [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Host_DriverNotUnloading,
+ 'Can\'t remove kext org.virtualbox.kext.VBoxDrv; services failed to terminate - 0xe00002c7' ),
+ ],
+ 'linux': [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Host_DriverNotCompilable,
+ 'This system is not currently set up to build kernel modules' ),
+ ( True, ktReason_Host_DriverNotCompilable,
+ 'This system is currently not set up to build kernel modules' ),
+ ( True, ktReason_Host_InstallationFailed,
+ 'vboxdrv.sh: failed: Look at /var/log/vbox-install.log to find out what went wrong.' ),
+ ( True, ktReason_Host_DriverNotUnloading,
+ 'Cannot unload module vboxdrv'),
+ ],
+ 'solaris': [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Host_DriverNotUnloading, 'can\'t unload the module: Device busy' ),
+ ( True, ktReason_Host_DriverNotUnloading, 'Unloading: Host module ...FAILED!' ),
+ ( True, ktReason_Host_DriverNotUnloading, 'Unloading: NetFilter (Crossbow) module ...FAILED!' ),
+ ( True, ktReason_Host_InstallationFailed, 'svcadm: Couldn\'t bind to svc.configd.' ),
+ ( True, ktReason_Host_InstallationFailed, 'pkgadd: ERROR: postinstall script did not complete successfully' ),
+ ],
+ 'win': [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Host_InstallationWantReboot, 'ERROR_SUCCESS_REBOOT_REQUIRED' ),
+ ( False, ktReason_Host_InstallationFailed, 'Installation error.' ),
+ ( True, ktReason_Host_InvalidPackage, 'Uninstaller failed, exit code: 1620' ),
+ ( True, ktReason_Host_InstallSourceAbsent, 'Uninstaller failed, exit code: 1612' ),
+ ],
+ };
+
+
+ def investigateInstallUninstallFailure(self, oCaseFile, oFailedResult, sResultLog, fInstall):
+ """
+ Investigates an install or uninstall failure.
+
+ We lump the two together since the installation typically also performs
+ an uninstall first and will be seeing similar issues to the uninstall.
+ """
+ self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
+
+ if fInstall and oFailedResult.enmStatus == TestSetData.ksTestStatus_TimedOut:
+ oCaseFile.noteReasonForId(self.ktReason_Host_Install_Hang, oFailedResult.idTestResult)
+ return True;
+
+ atSimple = self.katSimpleInstallUninstallMainLogReasons;
+ if oCaseFile.oTestBox.sOs in self.kdatSimpleInstallUninstallMainLogReasonsPerOs:
+ atSimple = self.kdatSimpleInstallUninstallMainLogReasonsPerOs[oCaseFile.oTestBox.sOs] + atSimple;
+
+ fFoundSomething = False;
+ for fStopOnHit, tReason, sNeedle in atSimple:
+ if sResultLog.find(sNeedle) > 0:
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
+ if fStopOnHit:
+ return True;
+ fFoundSomething = True;
+
+ return fFoundSomething if fFoundSomething else None;
+
+
+ def investigateBadTestBox(self, oCaseFile):
+ """
+ Checks out bad-testbox statuses.
+ """
+ _ = oCaseFile;
+ return False;
+
+
+ def investigateVBoxUnitTest(self, oCaseFile):
+ """
+ Checks out a VBox unittest problem.
+ """
+
+ #
+ # Process simple test case failures first, using their name as reason.
+ # We do the reason management just like for BSODs.
+ #
+ cRelevantOnes = 0;
+ sMainLog = oCaseFile.getMainLog();
+ aoFailedResults = oCaseFile.oTree.getListOfFailures();
+ for oFailedResult in aoFailedResults:
+ if oFailedResult is oCaseFile.oTree:
+ self.vprint('TODO: toplevel failure');
+ cRelevantOnes += 1
+
+ elif oFailedResult.sName == 'Installing VirtualBox':
+ sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
+ cRelevantOnes += 1
+
+ elif oFailedResult.sName == 'Uninstalling VirtualBox':
+ sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
+ cRelevantOnes += 1
+
+ elif oFailedResult.oParent is not None:
+ # Get the 2nd level node because that's where we'll find the unit test name.
+ while oFailedResult.oParent.oParent is not None:
+ oFailedResult = oFailedResult.oParent;
+
+ # Only report a failure once.
+ if oFailedResult.idTestResult not in oCaseFile.dReasonForResultId:
+ sKey = oFailedResult.sName;
+ if sKey.startswith('testcase/'):
+ sKey = sKey[9:];
+ if sKey in self.asUnitTestReasons:
+ tReason = ( self.ksUnitTestCategory, sKey );
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
+ else:
+ self.dprint(u'Unit test failure "%s" not found in %s;' % (sKey, self.asUnitTestReasons));
+ tReason = ( self.ksUnitTestCategory, self.ksUnitTestAddNew );
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = sKey);
+ cRelevantOnes += 1
+ else:
+ self.vprint(u'Internal error: expected oParent to NOT be None for %s' % (oFailedResult,));
+
+ #
+ # If we've caught all the relevant ones by now, report the result.
+ #
+ if len(oCaseFile.dReasonForResultId) >= cRelevantOnes:
+ return self.caseClosed(oCaseFile);
+ return False;
+
+ def extractGuestCpuStack(self, sInfoText):
+ """
+ Extracts the guest CPU stacks from the input file.
+
+ Returns a dictionary keyed by the CPU number, value being a list of
+ raw stack lines (no header).
+ Returns empty dictionary if no stacks where found.
+ """
+ dRet = {};
+ off = 0;
+ while True:
+ # Find the stack.
+ offStart = sInfoText.find('=== start guest stack VCPU ', off);
+ if offStart < 0:
+ break;
+ offEnd = sInfoText.find('=== end guest stack', offStart + 20);
+ if offEnd >= 0:
+ offEnd += 3;
+ else:
+ offEnd = sInfoText.find('=== start guest stack VCPU', offStart + 20);
+ if offEnd < 0:
+ offEnd = len(sInfoText);
+
+ sStack = sInfoText[offStart : offEnd];
+ sStack = sStack.replace('\r',''); # paranoia
+ asLines = sStack.split('\n');
+
+ # Figure the CPU.
+ asWords = asLines[0].split();
+ if len(asWords) < 6 or not asWords[5].isdigit():
+ break;
+ iCpu = int(asWords[5]);
+
+ # Add it and advance.
+ dRet[iCpu] = [sLine.rstrip() for sLine in asLines[2:-1]]
+ off = offEnd;
+ return dRet;
+
+ def investigateInfoKvmLockSpinning(self, oCaseFile, sInfoText, dLogs):
+ """ Investigates kvm_lock_spinning deadlocks """
+ #
+ # Extract the stacks. We need more than one CPU to create a deadlock.
+ #
+ dStacks = self.extractGuestCpuStack(sInfoText);
+ self.dprint('kvm_lock_spinning: found %s stacks' % (len(dStacks),));
+ if len(dStacks) >= 2:
+ #
+ # Examin each of the stacks. Each must have kvm_lock_spinning in
+ # one of the first three entries.
+ #
+ cHits = 0;
+ for asBacktrace in dStacks.values():
+ for iFrame in xrange(min(3, len(asBacktrace))):
+ if asBacktrace[iFrame].find('kvm_lock_spinning') >= 0:
+ cHits += 1;
+ break;
+ self.dprint('kvm_lock_spinning: %s/%s hits' % (cHits, len(dStacks),));
+ if cHits == len(dStacks):
+ return (True, self.ktReason_VMM_kvm_lock_spinning);
+
+ _ = dLogs; _ = oCaseFile;
+ return (False, None);
+
+ def investigateInfoHalReturnToFirmware(self, oCaseFile, sInfoText, dLogs):
+ """ Investigates HalReturnToFirmware hangs """
+ del oCaseFile
+ del sInfoText
+ del dLogs
+ # hope that's sufficient
+ return (True, self.ktReason_Unknown_HalReturnToFirmware);
+
+ ## Things we search a main or VM log for to figure out why something went bust.
+ ## @note DO NOT ADD MORE STUFF HERE!
+ ## Please use katSimpleMainLogReasons and katSimpleVmLogReasons instead!
+ katSimpleMainAndVmLogReasonsDeprecated = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( False, ktReason_Guru_Generic, 'GuruMeditation' ),
+ ( False, ktReason_Guru_Generic, 'Guru Meditation' ),
+ ( True, ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED, 'VERR_IEM_INSTR_NOT_IMPLEMENTED' ),
+ ( True, ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED, 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' ),
+ ( True, ktReason_Guru_VERR_TRPM_DONT_PANIC, 'VERR_TRPM_DONT_PANIC' ),
+ ( True, ktReason_Guru_VERR_PGM_PHYS_PAGE_RESERVED, 'VERR_PGM_PHYS_PAGE_RESERVED' ),
+ ( True, ktReason_Guru_VERR_VMX_INVALID_GUEST_STATE, 'VERR_VMX_INVALID_GUEST_STATE' ),
+ ( True, ktReason_Guru_VINF_EM_TRIPLE_FAULT, 'VINF_EM_TRIPLE_FAULT' ),
+ ( True, ktReason_Networking_Nonexistent_host_nic,
+ 'rc=E_FAIL text="Nonexistent host networking interface, name \'eth0\' (VERR_INTERNAL_ERROR)"' ),
+ ( True, ktReason_Networking_VERR_INTNET_FLT_IF_NOT_FOUND,
+ 'Failed to attach the network LUN (VERR_INTNET_FLT_IF_NOT_FOUND)' ),
+ ( True, ktReason_Host_Reboot_OSX_Watchdog_Timeout, ': "OSX Watchdog Timeout: ' ),
+ ( False, ktReason_XPCOM_NS_ERROR_CALL_FAILED,
+ 'Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))' ),
+ ( True, ktReason_API_std_bad_alloc, 'Unexpected exception: std::bad_alloc' ),
+ ( True, ktReason_Host_HostMemoryLow, 'HostMemoryLow' ),
+ ( True, ktReason_Host_HostMemoryLow, 'Failed to procure handy pages; rc=VERR_NO_MEMORY' ),
+ ( True, ktReason_Unknown_File_Not_Found,
+ 'Error: failed to start machine. Error message: File not found. (VERR_FILE_NOT_FOUND)' ),
+ ( True, ktReason_Unknown_File_Not_Found, # lump it in with file-not-found for now.
+ 'Error: failed to start machine. Error message: Not supported. (VERR_NOT_SUPPORTED)' ),
+ ( False, ktReason_Unknown_VM_Crash, 'txsDoConnectViaTcp: Machine state: Aborted' ),
+ ( True, ktReason_Host_Modprobe_Failed, 'Kernel driver not installed' ),
+ ( True, ktReason_OSInstall_Sata_no_BM, 'PCHS=14128/14134/8224' ),
+ ( True, ktReason_Host_DoubleFreeHeap, 'double free or corruption' ),
+ #( False, ktReason_Unknown_VM_Start_Error, 'VMSetError: ' ), - false positives for stuff like:
+ # "VMSetError: VD: Backend 'VBoxIsoMaker' does not support async I/O"
+ ( False, ktReason_Unknown_VM_Start_Error, 'error: failed to open session for' ),
+ ( False, ktReason_Unknown_VM_Runtime_Error, 'Console: VM runtime error: fatal=true' ),
+ ];
+
+ ## This we search a main log for to figure out why something went bust.
+ katSimpleMainLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( False, ktReason_Host_win32com_gen_py, 'ModuleNotFoundError: No module named \'win32com.gen_py' ),
+
+ ];
+
+ ## This we search a VM log for to figure out why something went bust.
+ katSimpleVmLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ # Note: Works for ATA and VD drivers.
+ ( False, ktReason_Host_DiskFull, '_DISKFULL' ),
+ ];
+
+ ## Things we search a VBoxHardening.log file for to figure out why something went bust.
+ katSimpleVBoxHardeningLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Host_DriverNotLoaded, 'Error opening VBoxDrvStub: STATUS_OBJECT_NAME_NOT_FOUND' ),
+ ( True, ktReason_Host_NotSignedWithBuildCert, 'Not signed with the build certificate' ),
+ ( True, ktReason_Host_TSTInfo_Accuracy_OOR, 'RTCRTSPTSTINFO::Accuracy::Millis: Out of range' ),
+ ( False, ktReason_Unknown_VM_Crash, 'Quitting: ExitCode=0xc0000005 (rcNtWait=' ),
+ ];
+
+ ## Things we search a kernel.log file for to figure out why something went bust.
+ katSimpleKernelLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Panic_HugeMemory, 'mm/huge_memory.c:1988' ),
+ ( True, ktReason_Panic_IOAPICDoesntWork, 'IO-APIC + timer doesn\'t work' ),
+ ( True, ktReason_Panic_TxUnitHang, 'Detected Tx Unit Hang' ),
+ ( True, ktReason_GuestBug_CompizVBoxQt, 'error 4 in libQt5CoreVBox' ),
+ ( True, ktReason_GuestBug_CompizVBoxQt, 'error 4 in libgtk-3' ),
+ ];
+
+ ## Things we search the _RIGHT_ _STRIPPED_ vgatext for.
+ katSimpleVgaTextReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Panic_MP_BIOS_IO_APIC,
+ "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n\n" ),
+ ( True, ktReason_Panic_MP_BIOS_IO_APIC,
+ "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"
+ "...trying to set up timer (IRQ0) through the 8259A ... failed.\n"
+ "...trying to set up timer as Virtual Wire IRQ... failed.\n"
+ "...trying to set up timer as ExtINT IRQ... failed :(.\n"
+ "Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug\n"
+ "and send a report. Then try booting with the 'noapic' option\n"
+ "\n" ),
+ ( True, ktReason_OSInstall_GRUB_hang,
+ "-----\nGRUB Loading stage2..\n\n\n\n" ),
+ ( True, ktReason_OSInstall_GRUB_hang,
+ "-----\nGRUB Loading stage2...\n\n\n\n" ), # the 3 dot hang appears to be less frequent
+ ( True, ktReason_OSInstall_GRUB_hang,
+ "-----\nGRUB Loading stage2....\n\n\n\n" ), # the 4 dot hang appears to be very infrequent
+ ( True, ktReason_OSInstall_GRUB_hang,
+ "-----\nGRUB Loading stage2.....\n\n\n\n" ), # the 5 dot hang appears to be more frequent again
+ ( True, ktReason_OSInstall_Udev_hang,
+ "\nStarting udev:\n\n\n\n" ),
+ ( True, ktReason_OSInstall_Udev_hang,
+ "\nStarting udev:\n------" ),
+ ( True, ktReason_Panic_BootManagerC000000F,
+ "Windows failed to start. A recent hardware or software change might be the" ),
+ ( True, ktReason_BootManager_Image_corrupt,
+ "BOOTMGR image is corrupt. The system cannot boot." ),
+ ];
+
+ ## Things we search for in the info.txt file. Require handlers for now.
+ katInfoTextHandlers = [
+ # ( Trigger text, handler method )
+ ( "kvm_lock_spinning", investigateInfoKvmLockSpinning ),
+ ( "HalReturnToFirmware", investigateInfoHalReturnToFirmware ),
+ ];
+
+ ## Mapping screenshot/failure SHA-256 hashes to failure reasons.
+ katSimpleScreenshotHashReasons = [
+ # ( Whether to stop on hit, reason tuple, lowercased sha-256 of PIL.Image.tostring output )
+ ( True, ktReason_BSOD_Recovery, '576f8e38d62b311cac7e3dc3436a0d0b9bd8cfd7fa9c43aafa95631520a45eac' ),
+ ( True, ktReason_BSOD_Automatic_Repair, 'c6a72076cc619937a7a39cfe9915b36d94cee0d4e3ce5ce061485792dcee2749' ),
+ ( True, ktReason_BSOD_Automatic_Repair, '26c4d8a724ff2c5e1051f3d5b650dbda7b5fdee0aa3e3c6059797f7484a515df' ),
+ ( True, ktReason_BSOD_0000007F, '57e1880619e13042a87100e7a38c8974b85ce3866501be621bea0cc696bb2c63' ),
+ ( True, ktReason_BSOD_000000D1, '134621281f00a3f8aeeb7660064bffbf6187ed56d5852142328d0bcb18ef0ede' ),
+ ( True, ktReason_BSOD_000000D1, '279f11258150c9d2fef041eca65501f3141da8df39256d8f6377e897e3b45a93' ),
+ ( True, ktReason_BSOD_C0000225, 'bd13a144be9dcdfb16bc863ff4c8f02a86e263c174f2cd5ffd27ca5f3aa31789' ),
+ ( True, ktReason_BSOD_C0000225, '8348b465e7ee9e59dd4e785880c57fd8677de05d11ac21e786bfde935307b42f' ),
+ ( True, ktReason_BSOD_C0000225, '1316e1fc818a73348412788e6910b8c016f237d8b4e15b20caf4a866f7a7840e' ),
+ ( True, ktReason_BSOD_C0000225, '54e0acbff365ce20a85abbe42bcd53647b8b9e80c68e45b2cd30e86bf177a0b5' ),
+ ( True, ktReason_BSOD_C0000225, '50fec50b5199923fa48b3f3e782687cc381e1c8a788ebda14e6a355fbe3bb1b3' ),
+ ];
+
+
+ def scanLog(self, asLogs, atNeedles, oCaseFile, idTestResult):
+ """
+ Scans for atNeedles in sLog.
+
+ Returns True if a stop-on-hit neelde was found.
+ Returns None if a no-stop reason was found.
+ Returns False if no hit.
+ """
+ fRet = False;
+ for fStopOnHit, tReason, oNeedle in atNeedles:
+ fMatch = False;
+ if utils.isString(oNeedle):
+ for sLog in asLogs:
+ if sLog:
+ fMatch |= sLog.find(oNeedle) > 0;
+ else:
+ for sLog in asLogs:
+ if sLog:
+ fMatch |= oNeedle.search(sLog) is not None;
+ if fMatch:
+ oCaseFile.noteReasonForId(tReason, idTestResult);
+ if fStopOnHit:
+ return True;
+ fRet = None;
+ return fRet;
+
+
+ def investigateGATest(self, oCaseFile, oFailedResult, sResultLog):
+ """
+ Investigates a failed VM run.
+ """
+ enmReason = None;
+ sParentName = oFailedResult.oParent.sName if oFailedResult.oParent else '';
+ if oFailedResult.sName == 'VBoxWindowsAdditions.exe' or sResultLog.find('VBoxWindowsAdditions.exe" failed with') > 0:
+ enmReason = self.ktReason_Add_Installer_Win_Failed;
+ # guest control:
+ elif sParentName == 'Guest Control' and oFailedResult.sName == 'Preparations':
+ enmReason = self.ktReason_Add_GstCtl_Preparations;
+ elif oFailedResult.sName == 'Session Basics':
+ enmReason = self.ktReason_Add_GstCtl_SessionBasics;
+ elif oFailedResult.sName == 'Session Process References':
+ enmReason = self.ktReason_Add_GstCtl_SessionProcRefs;
+ elif oFailedResult.sName == 'Copy from guest':
+ if sResultLog.find('*** abort action ***') >= 0:
+ enmReason = self.ktReason_Add_GstCtl_CopyFromGuest_Timeout;
+ elif oFailedResult.sName == 'Copy to guest':
+ off = sResultLog.find('"Guest directory "');
+ if off > 0 and sResultLog.find('" already exists"', off, off + 80):
+ enmReason = self.ktReason_Add_GstCtl_CopyToGuest_DstExists;
+ elif sResultLog.find('Guest destination must not be empty') >= 0:
+ enmReason = self.ktReason_Add_GstCtl_CopyToGuest_DstEmpty;
+ elif sResultLog.find('*** abort action ***') >= 0:
+ enmReason = self.ktReason_Add_GstCtl_CopyToGuest_Timeout;
+ elif oFailedResult.sName.find('Session w/ Guest Reboot') >= 0:
+ enmReason = self.ktReason_Add_GstCtl_Session_Reboot;
+ # shared folders:
+ elif sParentName == 'Shared Folders' and oFailedResult.sName == 'Automounting':
+ enmReason = self.ktReason_Add_ShFl_Automount;
+ elif oFailedResult.sName == 'mmap':
+ if sResultLog.find('FsPerf: Flush issue at offset ') >= 0:
+ enmReason = self.ktReason_Add_Mmap_Coherency;
+ elif sResultLog.find('FlushViewOfFile') >= 0:
+ enmReason = self.ktReason_Add_FlushViewOfFile;
+ elif sParentName == 'Shared Folders' and oFailedResult.sName == 'Running FsPerf':
+ enmReason = self.ktReason_Add_ShFl_FsPerf; ## Maybe it would be better to be more specific...
+
+ if enmReason is not None:
+ return oCaseFile.noteReasonForId(enmReason, oFailedResult.idTestResult);
+
+ self.vprint(u'TODO: Cannot place GA failure idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
+ self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
+ return False;
+
+ def isResultFromGATest(self, oCaseFile, oFailedResult):
+ """
+ Checks if this result and corresponding log snippet looks like a GA test run.
+ """
+ while oFailedResult is not None:
+ if oFailedResult.sName in [ 'Guest Control', 'Shared Folders', 'FsPerf', 'VBoxWindowsAdditions.exe' ]:
+ return True;
+ if oCaseFile.oTestCase.sName == 'Guest Additions' and oFailedResult.sName in [ 'Install', ]:
+ return True;
+ oFailedResult = oFailedResult.oParent;
+ return False;
+
+
+ def investigateVMResult(self, oCaseFile, oFailedResult, sResultLog):
+ """
+ Investigates a failed VM run.
+ """
+
+ def investigateLogSet():
+ """
+ Investigates the current set of VM related logs.
+ """
+ self.dprint('investigateLogSet: log lengths: result %u, VM %u, kernel %u, vga text %u, info text %u, hard %u'
+ % ( len(sResultLog if sResultLog else ''),
+ len(sVMLog if sVMLog else ''),
+ len(sKrnlLog if sKrnlLog else ''),
+ len(sVgaText if sVgaText else ''),
+ len(sInfoText if sInfoText else ''),
+ len(sNtHardLog if sNtHardLog else ''),));
+
+ #self.dprint(u'main.log<<<\n%s\n<<<\n' % (sResultLog,));
+ #self.dprint(u'vbox.log<<<\n%s\n<<<\n' % (sVMLog,));
+ #self.dprint(u'krnl.log<<<\n%s\n<<<\n' % (sKrnlLog,));
+ #self.dprint(u'vgatext.txt<<<\n%s\n<<<\n' % (sVgaText,));
+ #self.dprint(u'info.txt<<<\n%s\n<<<\n' % (sInfoText,));
+ #self.dprint(u'hard.txt<<<\n%s\n<<<\n' % (sNtHardLog,));
+
+ # TODO: more
+
+ #
+ # Look for BSODs. Some stupid stupid inconsistencies in reason and log messages here, so don't try prettify this.
+ #
+ sDetails = self.findInAnyAndReturnRestOfLine([ sVMLog, sResultLog ],
+ 'GIM: HyperV: Guest indicates a fatal condition! P0=');
+ if sDetails is not None:
+ # P0=%#RX64 P1=%#RX64 P2=%#RX64 P3=%#RX64 P4=%#RX64 "
+ sKey = sDetails.split(' ', 1)[0];
+ try: sKey = '0x%08X' % (int(sKey, 16),);
+ except: pass;
+ if sKey in self.asBsodReasons:
+ tReason = ( self.ksBsodCategory, sKey );
+ elif sKey.lower() in self.asBsodReasons: # just in case.
+ tReason = ( self.ksBsodCategory, sKey.lower() );
+ else:
+ self.dprint(u'BSOD "%s" not found in %s;' % (sKey, self.asBsodReasons));
+ tReason = ( self.ksBsodCategory, self.ksBsodAddNew );
+ return oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = sDetails.strip());
+
+ fFoundSomething = False;
+
+ #
+ # Look for linux panic.
+ #
+ if sKrnlLog is not None:
+ fRet = self.scanLog([sKrnlLog,], self.katSimpleKernelLogReasons, oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ #
+ # Loop thru the simple stuff.
+ #
+
+ # Main log.
+ fRet = self.scanLog([sResultLog,], self.katSimpleMainLogReasons, oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ # VM log.
+ fRet = self.scanLog([sVMLog,], self.katSimpleVmLogReasons, oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ # Old main + vm log.
+ fRet = self.scanLog([sResultLog, sVMLog], self.katSimpleMainAndVmLogReasonsDeprecated,
+ oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ # Continue with vga text.
+ if sVgaText:
+ fRet = self.scanLog([sVgaText,], self.katSimpleVgaTextReasons, oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ # Continue with screen hashes.
+ if sScreenHash is not None:
+ for fStopOnHit, tReason, sHash in self.katSimpleScreenshotHashReasons:
+ if sScreenHash == sHash:
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
+ if fStopOnHit:
+ return True;
+ fFoundSomething = True;
+
+ # Check VBoxHardening.log.
+ if sNtHardLog is not None:
+ fRet = self.scanLog([sNtHardLog,], self.katSimpleVBoxHardeningLogReasons, oCaseFile, oFailedResult.idTestResult);
+ if fRet is True:
+ return fRet;
+ fFoundSomething |= fRet is None;
+
+ #
+ # Complicated stuff.
+ #
+ dLogs = {
+ 'sVMLog': sVMLog,
+ 'sNtHardLog': sNtHardLog,
+ 'sScreenHash': sScreenHash,
+ 'sKrnlLog': sKrnlLog,
+ 'sVgaText': sVgaText,
+ 'sInfoText': sInfoText,
+ };
+
+ # info.txt.
+ if sInfoText:
+ for sNeedle, fnHandler in self.katInfoTextHandlers:
+ if sInfoText.find(sNeedle) > 0:
+ (fStop, tReason) = fnHandler(self, oCaseFile, sInfoText, dLogs);
+ if tReason is not None:
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
+ if fStop:
+ return True;
+ fFoundSomething = True;
+
+ #
+ # Check for repeated reboots...
+ #
+ if sVMLog is not None:
+ cResets = sVMLog.count('Changing the VM state from \'RUNNING\' to \'RESETTING\'');
+ if cResets > 10:
+ return oCaseFile.noteReasonForId(self.ktReason_Unknown_Reboot_Loop, oFailedResult.idTestResult,
+ sComment = 'Counted %s reboots' % (cResets,));
+
+ return fFoundSomething;
+
+ #
+ # Check if we got any VM or/and kernel logs. Treat them as sets in
+ # case we run multiple VMs here (this is of course ASSUMING they
+ # appear in the order that terminateVmBySession uploads them).
+ #
+ cTimes = 0;
+ sVMLog = None;
+ sNtHardLog = None;
+ sScreenHash = None;
+ sKrnlLog = None;
+ sVgaText = None;
+ sInfoText = None;
+ for oFile in oFailedResult.aoFiles:
+ if oFile.sKind == TestResultFileData.ksKind_LogReleaseVm:
+ if 'VBoxHardening.log' not in oFile.sFile:
+ if sVMLog is not None:
+ if investigateLogSet() is True:
+ return True;
+ cTimes += 1;
+ sInfoText = None;
+ sVgaText = None;
+ sKrnlLog = None;
+ sScreenHash = None;
+ sNtHardLog = None;
+ sVMLog = oCaseFile.getLogFile(oFile);
+ else:
+ sNtHardLog = oCaseFile.getLogFile(oFile);
+ elif oFile.sKind == TestResultFileData.ksKind_LogGuestKernel:
+ sKrnlLog = oCaseFile.getLogFile(oFile);
+ elif oFile.sKind == TestResultFileData.ksKind_InfoVgaText:
+ sVgaText = '\n'.join([sLine.rstrip() for sLine in oCaseFile.getLogFile(oFile).split('\n')]);
+ elif oFile.sKind == TestResultFileData.ksKind_InfoCollection:
+ sInfoText = oCaseFile.getLogFile(oFile);
+ elif oFile.sKind == TestResultFileData.ksKind_ScreenshotFailure:
+ sScreenHash = oCaseFile.getScreenshotSha256(oFile);
+ if sScreenHash is not None:
+ sScreenHash = sScreenHash.lower();
+ self.vprint(u'%s %s' % ( sScreenHash, oFile.sFile,));
+
+ if ( sVMLog is not None \
+ or sNtHardLog is not None \
+ or cTimes == 0) \
+ and investigateLogSet() is True:
+ return True;
+
+ return None;
+
+ def isResultFromVMRun(self, oFailedResult, sResultLog):
+ """
+ Checks if this result and corresponding log snippet looks like a VM run.
+ """
+
+ # Look for startVmEx/ startVmAndConnectToTxsViaTcp and similar output in the log.
+ if sResultLog.find(' startVm') > 0:
+ return True;
+
+ # Any other indicators? No?
+ _ = oFailedResult;
+ return False;
+
+
+ ## Things we search a VBoxSVC log for to figure out why something went bust.
+ katSimpleSvcLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* exited normally: -1073741819 \(0xc0000005\)') ),
+ ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: 11 \(0xb\)') ), # For VBox < 6.1.
+ ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: SIGABRT.*') ), # Since VBox 7.0.
+ ( False, ktReason_Unknown_VM_Crash, re.compile(r'Reaper.* was signalled: SIGSEGV.*') ),
+ ( False, ktReason_Unknown_VM_Terminated, re.compile(r'Reaper.* was signalled: SIGTERM.*') ),
+ ( False, ktReason_Unknown_VM_Terminated, re.compile(r'Reaper.* was signalled: SIGKILL.*') ),
+ ];
+
+ def investigateSvcLogForVMRun(self, oCaseFile, sSvcLog):
+ """
+ Check the VBoxSVC log for a single VM run.
+ """
+ if sSvcLog:
+ fRet = self.scanLog([sSvcLog,], self.katSimpleSvcLogReasons, oCaseFile, oCaseFile.oTree.idTestResult);
+ if fRet is True or fRet is None:
+ return True;
+ return False;
+
+ def investigateNtHardLogForVMRun(self, oCaseFile):
+ """
+ Check if the hardening log for a single VM run contains VM crash indications.
+ """
+ aoLogFiles = oCaseFile.oTree.getListOfLogFilesByKind(TestResultFileData.ksKind_LogReleaseVm);
+ for oLogFile in aoLogFiles:
+ if oLogFile.sFile.find('VBoxHardening.log') >= 0:
+ sLog = oCaseFile.getLogFile(oLogFile);
+ if sLog.find('Quitting: ExitCode=0xc0000005') >= 0:
+ return oCaseFile.noteReasonForId(self.ktReason_Unknown_VM_Crash, oCaseFile.oTree.idTestResult);
+ return False;
+
+
+ def investigateVBoxVMTest(self, oCaseFile, fSingleVM):
+ """
+ Checks out a VBox VM test.
+
+ This is generic investigation of a test running one or more VMs, like
+ for example a smoke test or a guest installation test.
+
+ The fSingleVM parameter is a hint, which probably won't come in useful.
+ """
+ _ = fSingleVM;
+
+ #
+ # Get a list of test result failures we should be looking into and the main log.
+ #
+ aoFailedResults = oCaseFile.oTree.getListOfFailures();
+ sMainLog = oCaseFile.getMainLog();
+
+ #
+ # There are a set of errors ending up on the top level result record.
+ # Should deal with these first.
+ #
+ if len(aoFailedResults) == 1 and aoFailedResults[0] == oCaseFile.oTree:
+ # Check if we've just got that XPCOM client smoke test shutdown issue. This will currently always
+ # be reported on the top result because vboxinstall.py doesn't add an error for it. It is easy to
+ # ignore other failures in the test if we're not a little bit careful here.
+ if sMainLog.find('vboxinstaller: Exit code: -11 (') > 0:
+ oCaseFile.noteReason(self.ktReason_XPCOM_Exit_Minus_11);
+ return self.caseClosed(oCaseFile);
+
+ # Hang after starting VBoxSVC (e.g. idTestSet=136307258)
+ if self.isThisFollowedByTheseLines(sMainLog, 'oVBoxMgr=<vboxapi.VirtualBoxManager object at',
+ (' Timeout: ', ' Attempting to abort child...',) ):
+ if sMainLog.find('*** glibc detected *** /') > 0:
+ oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption);
+ else:
+ oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang);
+ return self.caseClosed(oCaseFile);
+
+ # Look for heap corruption without visible hang.
+ if sMainLog.find('*** glibc detected *** /') > 0 \
+ or sMainLog.find("-1073740940") > 0: # STATUS_HEAP_CORRUPTION / 0xc0000374
+ oCaseFile.noteReason(self.ktReason_Unknown_Heap_Corruption);
+ return self.caseClosed(oCaseFile);
+
+ # Out of memory w/ timeout.
+ if sMainLog.find('sErrId=HostMemoryLow') > 0:
+ oCaseFile.noteReason(self.ktReason_Host_HostMemoryLow);
+ return self.caseClosed(oCaseFile);
+
+ # Stale files like vts_rm.exe (windows).
+ offEnd = sMainLog.rfind('*** The test driver exits successfully. ***');
+ if offEnd > 0 and sMainLog.find('[Error 145] The directory is not empty: ', offEnd) > 0:
+ oCaseFile.noteReason(self.ktReason_Ignore_Stale_Files);
+ return self.caseClosed(oCaseFile);
+
+ #
+ # XPCOM screwup
+ #
+ if sMainLog.find('AttributeError: \'NoneType\' object has no attribute \'addObserver\'') > 0:
+ oCaseFile.noteReason(self.ktReason_Buggy_Build_Broken_Build);
+ return self.caseClosed(oCaseFile);
+
+ #
+ # Go thru each failed result.
+ #
+ for oFailedResult in aoFailedResults:
+ self.dprint(u'Looking at test result #%u - %s' % (oFailedResult.idTestResult, oFailedResult.getFullName(),));
+ sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
+ if oFailedResult.sName == 'Installing VirtualBox':
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
+
+ elif oFailedResult.sName == 'Uninstalling VirtualBox':
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
+
+ elif self.isResultFromVMRun(oFailedResult, sResultLog):
+ self.investigateVMResult(oCaseFile, oFailedResult, sResultLog);
+
+ elif self.isResultFromGATest(oCaseFile, oFailedResult):
+ self.investigateGATest(oCaseFile, oFailedResult, sResultLog);
+
+ elif sResultLog.find('most likely not unique') > 0:
+ oCaseFile.noteReasonForId(self.ktReason_Host_NetworkMisconfiguration, oFailedResult.idTestResult)
+ elif sResultLog.find('Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))') > 0:
+ oCaseFile.noteReasonForId(self.ktReason_XPCOM_NS_ERROR_CALL_FAILED, oFailedResult.idTestResult);
+
+ elif sResultLog.find('The machine is not mutable (state is ') > 0:
+ self.vprint('Ignoring "machine not mutable" error as it is probably due to an earlier problem');
+ oCaseFile.noteReasonForId(self.ktHarmless, oFailedResult.idTestResult);
+
+ elif sResultLog.find('** error: no action was specified') > 0 \
+ or sResultLog.find('(len(self._asXml, asText))') > 0:
+ oCaseFile.noteReasonForId(self.ktReason_Ignore_Buggy_Test_Driver, oFailedResult.idTestResult);
+
+ else:
+ self.vprint(u'TODO: Cannot place idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
+ self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
+
+ #
+ # Windows python/com screwup.
+ #
+ if sMainLog.find('ModuleNotFoundError: No module named \'win32com.gen_py') > 0:
+ oCaseFile.noteReason(self.ktReason_Host_win32com_gen_py);
+ return self.caseClosed(oCaseFile);
+
+ #
+ # Check VBoxSVC.log and VBoxHardening.log for VM crashes if inconclusive on single VM runs.
+ #
+ if fSingleVM and len(oCaseFile.dReasonForResultId) < len(aoFailedResults):
+ self.dprint(u'Got %u out of %u - checking VBoxSVC.log...'
+ % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
+ if self.investigateSvcLogForVMRun(oCaseFile, oCaseFile.getSvcLog()):
+ return self.caseClosed(oCaseFile);
+ if self.investigateNtHardLogForVMRun(oCaseFile):
+ return self.caseClosed(oCaseFile);
+
+ #
+ # Report home and close the case if we got them all, otherwise log it.
+ #
+ if len(oCaseFile.dReasonForResultId) >= len(aoFailedResults):
+ return self.caseClosed(oCaseFile);
+
+ if oCaseFile.dReasonForResultId:
+ self.vprint(u'TODO: Got %u out of %u - close, but no cigar. :-/'
+ % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
+ else:
+ self.vprint(u'XXX: Could not figure out anything at all! :-(');
+ return False;
+
+
+ ## Things we search a main log for to figure out why something in the API test went bust.
+ katSimpleApiMainLogReasons = [
+ # ( Whether to stop on hit, reason tuple, needle text. )
+ ( True, ktReason_Networking_Nonexistent_host_nic,
+ 'rc=E_FAIL text="Nonexistent host networking interface, name \'eth0\' (VERR_INTERNAL_ERROR)"' ),
+ ( False, ktReason_XPCOM_NS_ERROR_CALL_FAILED,
+ 'Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))' ),
+ ( True, ktReason_API_std_bad_alloc, 'Unexpected exception: std::bad_alloc' ),
+ ( True, ktReason_API_Digest_Mismatch, 'Digest mismatch (VERR_NOT_EQUAL)' ),
+ ( True, ktReason_API_MoveVM_SharingViolation, 'rc=VBOX_E_IPRT_ERROR text="Could not copy the log file ' ),
+ ( True, ktReason_API_MoveVM_InvalidParameter,
+ 'rc=VBOX_E_IPRT_ERROR text="Could not copy the setting file ' ),
+ ( True, ktReason_API_Open_Session_Failed, 'error: failed to open session for' ),
+ ];
+
+ def investigateVBoxApiTest(self, oCaseFile):
+ """
+ Checks out a VBox API test.
+ """
+
+ #
+ # Get a list of test result failures we should be looking into and the main log.
+ #
+ aoFailedResults = oCaseFile.oTree.getListOfFailures();
+ sMainLog = oCaseFile.getMainLog();
+
+ #
+ # Go thru each failed result.
+ #
+ for oFailedResult in aoFailedResults:
+ self.dprint(u'Looking at test result #%u - %s' % (oFailedResult.idTestResult, oFailedResult.getFullName(),));
+ sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed);
+ if oFailedResult.sName == 'Installing VirtualBox':
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = True)
+
+ elif oFailedResult.sName == 'Uninstalling VirtualBox':
+ self.investigateInstallUninstallFailure(oCaseFile, oFailedResult, sResultLog, fInstall = False)
+
+ elif sResultLog.find('Exception: 0x800706be (Call to remote object failed (NS_ERROR_CALL_FAILED))') > 0:
+ oCaseFile.noteReasonForId(self.ktReason_XPCOM_NS_ERROR_CALL_FAILED, oFailedResult.idTestResult);
+
+ else:
+ fFoundSomething = False;
+ for fStopOnHit, tReason, sNeedle in self.katSimpleApiMainLogReasons:
+ if sResultLog.find(sNeedle) > 0:
+ oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult);
+ fFoundSomething = True;
+ if fStopOnHit:
+ break;
+ if fFoundSomething:
+ self.vprint(u'TODO: Cannot place idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,));
+ self.dprint(u'%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,));
+
+ #
+ # Report home and close the case if we got them all, otherwise log it.
+ #
+ if len(oCaseFile.dReasonForResultId) >= len(aoFailedResults):
+ return self.caseClosed(oCaseFile);
+
+ if oCaseFile.dReasonForResultId:
+ self.vprint(u'TODO: Got %u out of %u - close, but no cigar. :-/'
+ % (len(oCaseFile.dReasonForResultId), len(aoFailedResults)));
+ else:
+ self.vprint(u'XXX: Could not figure out anything at all! :-(');
+ return False;
+
+
+ def reasoningFailures(self):
+ """
+ Guess the reason for failures.
+ """
+ #
+ # Get a list of failed test sets without any assigned failure reason.
+ #
+ cGot = 0;
+ if not self.oConfig.aidTestSets:
+ aoTestSets = self.oTestSetLogic.fetchFailedSetsWithoutReason(cHoursBack = self.oConfig.cHoursBack,
+ tsNow = self.tsNow);
+ else:
+ aoTestSets = [self.oTestSetLogic.getById(idTestSet) for idTestSet in self.oConfig.aidTestSets];
+ for oTestSet in aoTestSets:
+ self.dprint(u'----------------------------------- #%u, status %s -----------------------------------'
+ % ( oTestSet.idTestSet, oTestSet.enmStatus,));
+
+ #
+ # Open a case file and assign it to the right investigator.
+ #
+ (oTree, _ ) = self.oTestResultLogic.fetchResultTree(oTestSet.idTestSet);
+ oBuild = BuildDataEx().initFromDbWithId( self.oDb, oTestSet.idBuild, oTestSet.tsCreated);
+ oTestBox = TestBoxData().initFromDbWithGenId( self.oDb, oTestSet.idGenTestBox);
+ oTestGroup = TestGroupData().initFromDbWithId( self.oDb, oTestSet.idTestGroup, oTestSet.tsCreated);
+ oTestCase = TestCaseDataEx().initFromDbWithGenId( self.oDb, oTestSet.idGenTestCase, oTestSet.tsConfig);
+
+ oCaseFile = VirtualTestSheriffCaseFile(self, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase);
+
+ if oTestSet.enmStatus == TestSetData.ksTestStatus_BadTestBox:
+ self.dprint(u'investigateBadTestBox is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateBadTestBox(oCaseFile);
+
+ elif oCaseFile.isVBoxUnitTest():
+ self.dprint(u'investigateVBoxUnitTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxUnitTest(oCaseFile);
+
+ elif oCaseFile.isVBoxInstallTest() or oCaseFile.isVBoxUnattendedInstallTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
+
+ elif oCaseFile.isVBoxUSBTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
+
+ elif oCaseFile.isVBoxStorageTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
+
+ elif oCaseFile.isVBoxGAsTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);
+
+ elif oCaseFile.isVBoxAPITest():
+ self.dprint(u'investigateVBoxApiTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxApiTest(oCaseFile);
+
+ elif oCaseFile.isVBoxBenchmarkTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
+
+ elif oCaseFile.isVBoxSmokeTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
+
+ elif oCaseFile.isVBoxSerialTest():
+ self.dprint(u'investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,));
+ fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);
+
+ else:
+ self.vprint(u'reasoningFailures: Unable to classify test set: %s' % (oCaseFile.sLongName,));
+ fRc = False;
+ cGot += fRc is True;
+
+ self.vprint(u'reasoningFailures: Got %u out of %u' % (cGot, len(aoTestSets), ));
+ return 0;
+
+
+ def main(self):
+ """
+ The 'main' function.
+ Return exit code (0, 1, etc).
+ """
+ # Database stuff.
+ self.oDb = TMDatabaseConnection()
+ self.oTestResultLogic = TestResultLogic(self.oDb);
+ self.oTestSetLogic = TestSetLogic(self.oDb);
+ self.oFailureReasonLogic = FailureReasonLogic(self.oDb);
+ self.oTestResultFailureLogic = TestResultFailureLogic(self.oDb);
+ self.asBsodReasons = self.oFailureReasonLogic.fetchForSheriffByNamedCategory(self.ksBsodCategory);
+ self.asUnitTestReasons = self.oFailureReasonLogic.fetchForSheriffByNamedCategory(self.ksUnitTestCategory);
+
+ # Get a fix on our 'now' before we do anything..
+ self.oDb.execute('SELECT CURRENT_TIMESTAMP - interval \'%s hours\'', (self.oConfig.cStartHoursAgo,));
+ self.tsNow = self.oDb.fetchOne();
+
+ # If we're suppost to commit anything we need to get our user ID.
+ rcExit = 0;
+ if self.oConfig.fRealRun:
+ self.oLogin = UserAccountLogic(self.oDb).tryFetchAccountByLoginName(VirtualTestSheriff.ksLoginName);
+ if self.oLogin is None:
+ rcExit = self.eprint('Cannot find my user account "%s"!' % (VirtualTestSheriff.ksLoginName,));
+ else:
+ self.uidSelf = self.oLogin.uid;
+
+ #
+ # Do the stuff.
+ #
+ if rcExit == 0:
+ rcExit = self.selfCheck();
+ if rcExit == 0:
+ rcExit = self.badTestBoxManagement();
+ rcExit2 = self.reasoningFailures();
+ if rcExit == 0:
+ rcExit = rcExit2;
+ # Redo the bad testbox management after failure reasons have been assigned (got timing issues).
+ if rcExit == 0:
+ rcExit = self.badTestBoxManagement();
+
+ # Cleanup.
+ self.oFailureReasonLogic = None;
+ self.oTestResultFailureLogic = None;
+ self.oTestSetLogic = None;
+ self.oTestResultLogic = None;
+ self.oDb.close();
+ self.oDb = None;
+ if self.oLogFile is not None:
+ self.oLogFile.close();
+ self.oLogFile = None;
+ return rcExit;
+
+if __name__ == '__main__':
+ sys.exit(VirtualTestSheriff().main());
diff --git a/src/VBox/ValidationKit/testmanager/cgi/Makefile.kmk b/src/VBox/ValidationKit/testmanager/cgi/Makefile.kmk
new file mode 100644
index 00000000..74d882cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/admin.py b/src/VBox/ValidationKit/testmanager/cgi/admin.py
new file mode 100755
index 00000000..b8c10f76
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/admin.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: admin.py $
+
+"""
+CGI - Administrator Web-UI.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+from testmanager.webui.wuiadmin import WuiAdmin;
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = True);
+ try:
+ oWui = WuiAdmin(oSrvGlue);
+ oWui.dispatchRequest();
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),), sys.exc_info());
+
+ return 0;
+
+if __name__ == '__main__':
+ if config.g_kfProfileAdmin:
+ from testmanager.debug import cgiprofiling;
+ sys.exit(cgiprofiling.profileIt(main));
+ else:
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/debuginfo.py b/src/VBox/ValidationKit/testmanager/cgi/debuginfo.py
new file mode 100755
index 00000000..a2663436
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/debuginfo.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: debuginfo.py $
+
+"""
+CGI - Debug Info Page.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = True);
+ try:
+ oSrvGlue.debugInfoPage();
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),), sys.exc_info());
+
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/index.py b/src/VBox/ValidationKit/testmanager/cgi/index.py
new file mode 100755
index 00000000..b9b546c2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/index.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: index.py $
+
+"""
+CGI - Web UI - Main (index) page.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+from testmanager.webui.wuimain import WuiMain;
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = False);
+ try:
+ oWui = WuiMain(oSrvGlue);
+ oWui.dispatchRequest();
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),), sys.exc_info());
+
+ return 0;
+
+if __name__ == '__main__':
+ if config.g_kfProfileIndex:
+ from testmanager.debug import cgiprofiling;
+ sys.exit(cgiprofiling.profileIt(main));
+ else:
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/logout.py b/src/VBox/ValidationKit/testmanager/cgi/logout.py
new file mode 100755
index 00000000..109bfa86
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/logout.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: logout.py $
+
+"""
+VirtualBox Validation Kit - CGI - Log out page.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager.core.webservergluecgi import WebServerGlueCgi
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = True)
+ sUser = oSrvGlue.getLoginName()
+ if sUser not in (oSrvGlue.ksUnknownUser, 'logout'):
+ oSrvGlue.write('<p>Broken apache config!\n'
+ 'The logout.py script should be configured with .htaccess-logout and require user logout!</p>')
+ else:
+ oSrvGlue.write('<p>Successfully logged out!</p>')
+ oSrvGlue.write('<p><a href="%sadmin.py">Log in</a> under another user name.</p>' %
+ (oSrvGlue.getBaseUrl(),))
+
+
+ oSrvGlue.write('<hr/><p>debug info:</p>')
+ oSrvGlue.debugInfoPage()
+ oSrvGlue.flush()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/logout2.py b/src/VBox/ValidationKit/testmanager/cgi/logout2.py
new file mode 100755
index 00000000..1ab24cbc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/logout2.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: logout2.py $
+
+"""
+VirtualBox Validation Kit - CGI - Log out page for Safari.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = True);
+ sUserAgent = oSrvGlue.getUserAgent();
+ oSrvGlue.setHeaderField('Status', '401 Unauthorized to access the document');
+ oSrvGlue.setHeaderField('WWW-authenticate', 'Basic realm="Test Manager"');
+ if sUserAgent.startswith('Mozilla/') and sUserAgent.find('AppleWebKit/') > 0:
+ oSrvGlue.write('<p>Attempting to log out an Apple browser...</p>');
+ else:
+ oSrvGlue.write('<p>Sorry, not sure this will work...</p>');
+ oSrvGlue.write('<p>User-Agent:' + sUserAgent + '</p>');
+
+ oSrvGlue.write('<p><a href="%sadmin.py">Log in</a> under another user name.</p>' %
+ (oSrvGlue.getBaseUrl(),))
+
+ oSrvGlue.write('<hr/><p>debug info:</p>');
+ oSrvGlue.debugInfoPage();
+ oSrvGlue.flush();
+
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/rest.py b/src/VBox/ValidationKit/testmanager/cgi/rest.py
new file mode 100755
index 00000000..89a5238c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/rest.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: rest.py $
+
+"""
+CGI - REST - sPath=path variant.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+from testmanager.core.restdispatcher import RestMain, RestDispException;
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = False);
+ try:
+ oMain = RestMain(oSrvGlue);
+ oMain.dispatchRequest();
+ oSrvGlue.flush();
+ except RestDispException as oXcpt:
+ oSrvGlue.setStatus(oXcpt.iStatus);
+ oSrvGlue.setHeaderField('tm-error-message', str(oXcpt));
+ oSrvGlue.write('error: ' + str(oXcpt));
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),),
+ sys.exc_info(),
+ config.g_ksTestBoxDispXpctLog);
+
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/status.py b/src/VBox/ValidationKit/testmanager/cgi/status.py
new file mode 100755
index 00000000..39c8af03
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/status.py
@@ -0,0 +1,519 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: status.py $
+
+"""
+CGI - Administrator Web-UI.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+
+from common import constants;
+from testmanager.core.base import TMExceptionBase;
+from testmanager.core.db import TMDatabaseConnection;
+
+
+
+def timeDeltaToHours(oTimeDelta):
+ return oTimeDelta.days * 24 + oTimeDelta.seconds // 3600
+
+
+def testbox_data_processing(oDb):
+ testboxes_dict = {}
+ while True:
+ line = oDb.fetchOne();
+ if line is None:
+ break;
+ testbox_name = line[0]
+ test_result = line[1]
+ oTimeDeltaSinceStarted = line[2]
+ test_box_os = line[3]
+ test_sched_group = line[4]
+
+ # idle testboxes might have an assigned testsets, skipping them
+ if test_result not in g_kdTestStatuses:
+ continue
+
+ testboxes_dict = dict_update(testboxes_dict, testbox_name, test_result)
+
+ if "testbox_os" not in testboxes_dict[testbox_name]:
+ testboxes_dict[testbox_name].update({"testbox_os": test_box_os})
+
+ if "sched_group" not in testboxes_dict[testbox_name]:
+ testboxes_dict[testbox_name].update({"sched_group": test_sched_group})
+ elif test_sched_group not in testboxes_dict[testbox_name]["sched_group"]:
+ testboxes_dict[testbox_name]["sched_group"] += "," + test_sched_group
+
+ if test_result == "running":
+ testboxes_dict[testbox_name].update({"hours_running": timeDeltaToHours(oTimeDeltaSinceStarted)})
+
+ return testboxes_dict;
+
+
+def os_results_separating(vb_dict, test_name, testbox_os, test_result):
+ if testbox_os == "linux":
+ dict_update(vb_dict, test_name + " / linux", test_result)
+ elif testbox_os == "win":
+ dict_update(vb_dict, test_name + " / windows", test_result)
+ elif testbox_os == "darwin":
+ dict_update(vb_dict, test_name + " / darwin", test_result)
+ elif testbox_os == "solaris":
+ dict_update(vb_dict, test_name + " / solaris", test_result)
+ else:
+ dict_update(vb_dict, test_name + " / other", test_result)
+
+
+# const/immutable.
+g_kdTestStatuses = {
+ 'running': 0,
+ 'success': 0,
+ 'skipped': 0,
+ 'bad-testbox': 0,
+ 'aborted': 0,
+ 'failure': 0,
+ 'timed-out': 0,
+ 'rebooted': 0,
+}
+
+def dict_update(target_dict, key_name, test_result):
+ if key_name not in target_dict:
+ target_dict.update({key_name: g_kdTestStatuses.copy()})
+ if test_result in g_kdTestStatuses:
+ target_dict[key_name][test_result] += 1
+ return target_dict
+
+
+def formatDataEntry(sKey, dEntry):
+ # There are variations in the first and second "columns".
+ if "hours_running" in dEntry:
+ sRet = "%s;%s;%s | running: %s;%s" \
+ % (sKey, dEntry["testbox_os"], dEntry["sched_group"], dEntry["running"], dEntry["hours_running"]);
+ else:
+ if "testbox_os" in dEntry:
+ sRet = "%s;%s;%s" % (sKey, dEntry["testbox_os"], dEntry["sched_group"],);
+ else:
+ sRet = sKey;
+ sRet += " | running: %s" % (dEntry["running"],)
+
+ # The rest is currently identical:
+ sRet += " | success: %s | skipped: %s | bad-testbox: %s | aborted: %s | failure: %s | timed-out: %s | rebooted: %s | \n" \
+ % (dEntry["success"], dEntry["skipped"], dEntry["bad-testbox"], dEntry["aborted"],
+ dEntry["failure"], dEntry["timed-out"], dEntry["rebooted"],);
+ return sRet;
+
+
+def format_data(dData, fSorted):
+ sRet = "";
+ if not fSorted:
+ for sKey in dData:
+ sRet += formatDataEntry(sKey, dData[sKey]);
+ else:
+ for sKey in sorted(dData.keys()):
+ sRet += formatDataEntry(sKey, dData[sKey]);
+ return sRet;
+
+######
+
+class StatusDispatcherException(TMExceptionBase):
+ """
+ Exception class for TestBoxController.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class StatusDispatcher(object): # pylint: disable=too-few-public-methods
+ """
+ Status dispatcher class.
+ """
+
+
+ def __init__(self, oSrvGlue):
+ """
+ Won't raise exceptions.
+ """
+ self._oSrvGlue = oSrvGlue;
+ self._sAction = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._dParams = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._asCheckedParams = [];
+ self._dActions = \
+ {
+ 'MagicMirrorTestResults': self._actionMagicMirrorTestResults,
+ 'MagicMirrorTestBoxes': self._actionMagicMirrorTestBoxes,
+ };
+
+ def _getStringParam(self, sName, asValidValues = None, fStrip = False, sDefValue = None):
+ """
+ Gets a string parameter (stripped).
+
+ Raises exception if not found and no default is provided, or if the
+ value isn't found in asValidValues.
+ """
+ if sName not in self._dParams:
+ if sDefValue is None:
+ raise StatusDispatcherException('%s parameter %s is missing' % (self._sAction, sName));
+ return sDefValue;
+ sValue = self._dParams[sName];
+ if fStrip:
+ sValue = sValue.strip();
+
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if asValidValues is not None and sValue not in asValidValues:
+ raise StatusDispatcherException('%s parameter %s value "%s" not in %s '
+ % (self._sAction, sName, sValue, asValidValues));
+ return sValue;
+
+ def _getIntParam(self, sName, iMin = None, iMax = None, iDefValue = None):
+ """
+ Gets a string parameter.
+ Raises exception if not found, not a valid integer, or if the value
+ isn't in the range defined by iMin and iMax.
+ """
+ if sName not in self._dParams:
+ if iDefValue is None:
+ raise StatusDispatcherException('%s parameter %s is missing' % (self._sAction, sName));
+ return iDefValue;
+ sValue = self._dParams[sName];
+ try:
+ iValue = int(sValue, 0);
+ except:
+ raise StatusDispatcherException('%s parameter %s value "%s" cannot be convert to an integer'
+ % (self._sAction, sName, sValue));
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise StatusDispatcherException('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sAction, sName, iValue, iMin, iMax));
+ return iValue;
+
+ def _getBoolParam(self, sName, fDefValue = None):
+ """
+ Gets a boolean parameter.
+
+ Raises exception if not found and no default is provided, or if not a
+ valid boolean.
+ """
+ sValue = self._getStringParam(sName, [ 'True', 'true', '1', 'False', 'false', '0'], sDefValue = str(fDefValue));
+ return sValue in ('True', 'true', '1',);
+
+ def _checkForUnknownParameters(self):
+ """
+ Check if we've handled all parameters, raises exception if anything
+ unknown was found.
+ """
+
+ if len(self._asCheckedParams) != len(self._dParams):
+ sUnknownParams = '';
+ for sKey in self._dParams:
+ if sKey not in self._asCheckedParams:
+ sUnknownParams += ' ' + sKey + '=' + self._dParams[sKey];
+ raise StatusDispatcherException('Unknown parameters: ' + sUnknownParams);
+
+ return True;
+
+ def _connectToDb(self):
+ """
+ Connects to the database.
+
+ Returns (TMDatabaseConnection, (more later perhaps) ) on success.
+ Returns (None, ) on failure after sending the box an appropriate response.
+ May raise exception on DB error.
+ """
+ return (TMDatabaseConnection(self._oSrvGlue.dprint),);
+
+ def _actionMagicMirrorTestBoxes(self):
+ """
+ Produces test result status for the magic mirror dashboard
+ """
+
+ #
+ # Parse arguments and connect to the database.
+ #
+ cHoursBack = self._getIntParam('cHours', 1, 24*14, 12);
+ fSorted = self._getBoolParam('fSorted', False);
+ self._checkForUnknownParameters();
+
+ #
+ # Get the data.
+ #
+ # Note! We're not joining on TestBoxesWithStrings.idTestBox =
+ # TestSets.idGenTestBox here because of indexes. This is
+ # also more consistent with the rest of the query.
+ # Note! The original SQL is slow because of the 'OR TestSets.tsDone'
+ # part, using AND and UNION is significatly faster because
+ # it matches the TestSetsGraphBoxIdx (index).
+ #
+ (oDb,) = self._connectToDb();
+ if oDb is None:
+ return False;
+
+ #
+ # some comments regarding select below:
+ # first part is about fetching all finished tests for last cHoursBack hours
+ # second part is fetching all tests which isn't done
+ # both old (running more than cHoursBack) and fresh (less than cHoursBack) ones
+ # 'cause we want to know if there's a hanging tests together with currently running
+ #
+ # there's also testsets without status at all, likely because disabled testboxes still have an assigned testsets
+ #
+ oDb.execute('''
+( SELECT TestBoxesWithStrings.sName,
+ TestSets.enmStatus,
+ CURRENT_TIMESTAMP - TestSets.tsCreated,
+ TestBoxesWithStrings.sOS,
+ SchedGroupNames.sSchedGroupNames
+ FROM (
+ SELECT TestBoxesInSchedGroups.idTestBox AS idTestBox,
+ STRING_AGG(SchedGroups.sName, ',') AS sSchedGroupNames
+ FROM TestBoxesInSchedGroups
+ INNER JOIN SchedGroups
+ ON SchedGroups.idSchedGroup = TestBoxesInSchedGroups.idSchedGroup
+ WHERE TestBoxesInSchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ AND SchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ GROUP BY TestBoxesInSchedGroups.idTestBox
+ ) AS SchedGroupNames,
+ TestBoxesWithStrings
+ LEFT OUTER JOIN TestSets
+ ON TestSets.idTestBox = TestBoxesWithStrings.idTestBox
+ AND TestSets.tsCreated >= (CURRENT_TIMESTAMP - '%s hours'::interval)
+ AND TestSets.tsDone IS NOT NULL
+ WHERE TestBoxesWithStrings.tsExpire = 'infinity'::TIMESTAMP
+ AND SchedGroupNames.idTestBox = TestBoxesWithStrings.idTestBox
+) UNION (
+ SELECT TestBoxesWithStrings.sName,
+ TestSets.enmStatus,
+ CURRENT_TIMESTAMP - TestSets.tsCreated,
+ TestBoxesWithStrings.sOS,
+ SchedGroupNames.sSchedGroupNames
+ FROM (
+ SELECT TestBoxesInSchedGroups.idTestBox AS idTestBox,
+ STRING_AGG(SchedGroups.sName, ',') AS sSchedGroupNames
+ FROM TestBoxesInSchedGroups
+ INNER JOIN SchedGroups
+ ON SchedGroups.idSchedGroup = TestBoxesInSchedGroups.idSchedGroup
+ WHERE TestBoxesInSchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ AND SchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ GROUP BY TestBoxesInSchedGroups.idTestBox
+ ) AS SchedGroupNames,
+ TestBoxesWithStrings
+ LEFT OUTER JOIN TestSets
+ ON TestSets.idTestBox = TestBoxesWithStrings.idTestBox
+ AND TestSets.tsDone IS NULL
+ WHERE TestBoxesWithStrings.tsExpire = 'infinity'::TIMESTAMP
+ AND SchedGroupNames.idTestBox = TestBoxesWithStrings.idTestBox
+)
+''', (cHoursBack, cHoursBack,));
+
+
+ #
+ # Process, format and output data.
+ #
+ dResult = testbox_data_processing(oDb);
+ self._oSrvGlue.setContentType('text/plain');
+ self._oSrvGlue.write(format_data(dResult, fSorted));
+
+ return True;
+
+ def _actionMagicMirrorTestResults(self):
+ """
+ Produces test result status for the magic mirror dashboard
+ """
+
+ #
+ # Parse arguments and connect to the database.
+ #
+ sBranch = self._getStringParam('sBranch');
+ cHoursBack = self._getIntParam('cHours', 1, 24*14, 6); ## @todo why 6 hours here and 12 for test boxes?
+ fSorted = self._getBoolParam('fSorted', False);
+ self._checkForUnknownParameters();
+
+ #
+ # Get the data.
+ #
+ # Note! These queries should be joining TestBoxesWithStrings and TestSets
+ # on idGenTestBox rather than on idTestBox and tsExpire=inf, but
+ # we don't have any index matching those. So, we'll ignore tests
+ # performed by deleted testboxes for the present as that doesn't
+ # happen often and we want the ~1000x speedup.
+ #
+ (oDb,) = self._connectToDb();
+ if oDb is None:
+ return False;
+
+ if sBranch == 'all':
+ oDb.execute('''
+SELECT TestSets.enmStatus,
+ TestCases.sName,
+ TestBoxesWithStrings.sOS
+FROM TestSets
+INNER JOIN TestCases
+ ON TestCases.idGenTestCase = TestSets.idGenTestCase
+INNER JOIN TestBoxesWithStrings
+ ON TestBoxesWithStrings.idTestBox = TestSets.idTestBox
+ AND TestBoxesWithStrings.tsExpire = 'infinity'::TIMESTAMP
+WHERE TestSets.tsCreated >= (CURRENT_TIMESTAMP - '%s hours'::interval)
+''', (cHoursBack,));
+ else:
+ oDb.execute('''
+SELECT TestSets.enmStatus,
+ TestCases.sName,
+ TestBoxesWithStrings.sOS
+FROM TestSets
+INNER JOIN BuildCategories
+ ON BuildCategories.idBuildCategory = TestSets.idBuildCategory
+ AND BuildCategories.sBranch = %s
+INNER JOIN TestCases
+ ON TestCases.idGenTestCase = TestSets.idGenTestCase
+INNER JOIN TestBoxesWithStrings
+ ON TestBoxesWithStrings.idTestBox = TestSets.idTestBox
+ AND TestBoxesWithStrings.tsExpire = 'infinity'::TIMESTAMP
+WHERE TestSets.tsCreated >= (CURRENT_TIMESTAMP - '%s hours'::interval)
+''', (sBranch, cHoursBack,));
+
+ # Process the data
+ dResult = {};
+ while True:
+ aoRow = oDb.fetchOne();
+ if aoRow is None:
+ break;
+ os_results_separating(dResult, aoRow[1], aoRow[2], aoRow[0]) # save all test results
+
+ # Format and output it.
+ self._oSrvGlue.setContentType('text/plain');
+ self._oSrvGlue.write(format_data(dResult, fSorted));
+
+ return True;
+
+ def _getStandardParams(self, dParams):
+ """
+ Gets the standard parameters and validates them.
+
+ The parameters are returned as a tuple: sAction, (more later, maybe)
+ Note! the sTextBoxId can be None if it's a SIGNON request.
+
+ Raises StatusDispatcherException on invalid input.
+ """
+ #
+ # Get the action parameter and validate it.
+ #
+ if constants.tbreq.ALL_PARAM_ACTION not in dParams:
+ raise StatusDispatcherException('No "%s" parameter in request (params: %s)'
+ % (constants.tbreq.ALL_PARAM_ACTION, dParams,));
+ sAction = dParams[constants.tbreq.ALL_PARAM_ACTION];
+
+ if sAction not in self._dActions:
+ raise StatusDispatcherException('Unknown action "%s" in request (params: %s; action: %s)'
+ % (sAction, dParams, self._dActions));
+ #
+ # Update the list of checked parameters.
+ #
+ self._asCheckedParams.extend([constants.tbreq.ALL_PARAM_ACTION,]);
+
+ return (sAction,);
+
+ def dispatchRequest(self):
+ """
+ Dispatches the incoming request.
+
+ Will raise StatusDispatcherException on failure.
+ """
+
+ #
+ # Must be a GET request.
+ #
+ try:
+ sMethod = self._oSrvGlue.getMethod();
+ except Exception as oXcpt:
+ raise StatusDispatcherException('Error retriving request method: %s' % (oXcpt,));
+ if sMethod != 'GET':
+ raise StatusDispatcherException('Error expected POST request not "%s"' % (sMethod,));
+
+ #
+ # Get the parameters and checks for duplicates.
+ #
+ try:
+ dParams = self._oSrvGlue.getParameters();
+ except Exception as oXcpt:
+ raise StatusDispatcherException('Error retriving parameters: %s' % (oXcpt,));
+ for sKey in dParams.keys():
+ if len(dParams[sKey]) > 1:
+ raise StatusDispatcherException('Parameter "%s" is given multiple times: %s' % (sKey, dParams[sKey]));
+ dParams[sKey] = dParams[sKey][0];
+ self._dParams = dParams;
+
+ #
+ # Get+validate the standard action parameters and dispatch the request.
+ #
+ (self._sAction, ) = self._getStandardParams(dParams);
+ return self._dActions[self._sAction]();
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = False);
+ try:
+ oDisp = StatusDispatcher(oSrvGlue);
+ oDisp.dispatchRequest();
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),), sys.exc_info());
+
+ return 0;
+
+if __name__ == '__main__':
+ if config.g_kfProfileAdmin:
+ from testmanager.debug import cgiprofiling;
+ sys.exit(cgiprofiling.profileIt(main));
+ else:
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/cgi/testboxdisp.py b/src/VBox/ValidationKit/testmanager/cgi/testboxdisp.py
new file mode 100755
index 00000000..c5c704bb
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/cgi/testboxdisp.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: testboxdisp.py $
+
+"""
+CGI - TestBox Interaction (see testboxscript or the other party).
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.webservergluecgi import WebServerGlueCgi;
+from testmanager.core.testboxcontroller import TestBoxController;
+
+
+def main():
+ """
+ Main function a la C/C++. Returns exit code.
+ """
+
+ oSrvGlue = WebServerGlueCgi(g_ksValidationKitDir, fHtmlOutput = False);
+ oCtrl = TestBoxController(oSrvGlue);
+ try:
+ oCtrl.dispatchRequest()
+ oSrvGlue.flush();
+ except Exception as oXcpt:
+ return oSrvGlue.errorPage('Internal error: %s' % (str(oXcpt),),
+ sys.exc_info(),
+ config.g_ksTestBoxDispXpctLog);
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(main());
+
diff --git a/src/VBox/ValidationKit/testmanager/config.py b/src/VBox/ValidationKit/testmanager/config.py
new file mode 100644
index 00000000..b4ef94cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/config.py
@@ -0,0 +1,261 @@
+# -*- coding: utf-8 -*-
+# $Id: config.py $
+
+"""
+Test Manager Configuration.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+import os;
+
+## Test Manager version string.
+g_ksVersion = 'v0.1.0';
+## Test Manager revision string.
+g_ksRevision = ('$Revision: 155244 $')[11:-2];
+
+## Enable VBox specific stuff.
+g_kfVBoxSpecific = True;
+
+
+## @name Used by the TMDatabaseConnection class.
+# @{
+g_ksDatabaseName = 'testmanager';
+g_ksDatabaseAddress = None;
+g_ksDatabasePort = None;
+g_ksDatabaseUser = 'postgres';
+g_ksDatabasePassword = '';
+## @}
+
+
+## @name User handling.
+## @{
+
+## Whether login names are case insensitive (True) or case sensitive (False).
+## @note Implemented by inserting lower case names into DB and lower case
+## bind variables in WHERE clauses.
+g_kfLoginNameCaseInsensitive = True;
+
+## @}
+
+
+## @name File locations
+## @{
+
+## The TestManager directory.
+g_ksTestManagerDir = os.path.dirname(os.path.abspath(__file__));
+## The Validation Kit directory.
+g_ksValidationKitDir = os.path.dirname(g_ksTestManagerDir);
+## The TestManager htdoc directory.
+g_ksTmHtDocDir = os.path.join(g_ksTestManagerDir, 'htdocs');
+## The TestManager download directory (under htdoc somewhere), for validationkit zips.
+g_ksTmDownloadDir = os.path.join(g_ksTmHtDocDir, 'download');
+## The base URL relative path of the TM download directory (g_ksTmDownloadDir).
+g_ksTmDownloadBaseUrlRel = 'htdocs/downloads';
+## The root of the file area (referred to as TM_FILE_DIR in database docs).
+g_ksFileAreaRootDir = '/var/tmp/testmanager'
+## The root of the file area with the zip files (best put on a big storage server).
+g_ksZipFileAreaRootDir = '/var/tmp/testmanager2'
+## URL prefix for trac log viewer.
+g_ksTracLogUrlPrefix = 'https://linserv.de.oracle.com/vbox/log/'
+## URL prefix for trac log viewer.
+g_ksTracChangsetUrlFmt = 'https://linserv.de.oracle.com/%(sRepository)s/changeset/%(iRevision)s'
+## URL prefix for unprefixed build logs.
+g_ksBuildLogUrlPrefix = ''
+## URL prefix for unprefixed build binaries.
+g_ksBuildBinUrlPrefix = '/builds/'
+## The local path prefix for unprefixed build binaries. (Host file system, not web server.)
+g_ksBuildBinRootDir = '/mnt/builds/'
+## File on the build binary share that can be used to check that it's mounted.
+g_ksBuildBinRootFile = 'builds.txt'
+## Template for paratial database dump output files. One argument: UID
+g_ksTmDbDumpOutFileTmpl = '/var/tmp/tm-partial-db-dump-for-%u.zip'
+## Template for paratial database dump temporary files. One argument: UID
+g_ksTmDbDumpTmpFileTmpl = '/var/tmp/tm-partial-db-dump-for-%u.pgtxt'
+## @}
+
+
+## @name Scheduling parameters
+## @{
+
+## The time to wait for a gang to gather (in seconds).
+g_kcSecGangGathering = 600;
+## The max time allowed to spend looking for a new task (in seconds).
+g_kcSecMaxNewTask = 60;
+## Minimum time since last task started.
+g_kcSecMinSinceLastTask = 120; # (2 min)
+## Minimum time since last failed task.
+g_kcSecMinSinceLastFailedTask = 180; # (3 min)
+
+## @}
+
+
+
+## @name Test result limits.
+## In general, we will fail the test when reached and stop accepting further results.
+## @{
+
+## The max number of test results per test set.
+g_kcMaxTestResultsPerTS = 4096;
+## The max number of test results (children) per test result.
+g_kcMaxTestResultsPerTR = 512;
+## The max number of test result values per test set.
+g_kcMaxTestValuesPerTS = 4096;
+## The max number of test result values per test result.
+g_kcMaxTestValuesPerTR = 256;
+## The max number of test result message per test result.
+g_kcMaxTestMsgsPerTR = 4;
+## The max test result nesting depth.
+g_kcMaxTestResultDepth = 10;
+
+## The max length of a test result name.
+g_kcchMaxTestResultName = 64;
+## The max length of a test result value name.
+g_kcchMaxTestValueName = 56;
+## The max length of a test result message.
+g_kcchMaxTestMsg = 128;
+
+## The max size of the main log file.
+g_kcMbMaxMainLog = 32;
+## The max size of an uploaded file (individual).
+g_kcMbMaxUploadSingle = 150;
+## The max size of all uploaded file.
+g_kcMbMaxUploadTotal = 200;
+## The max number of files that can be uploaded.
+g_kcMaxUploads = 256;
+## @}
+
+
+## @name Bug Trackers and VCS reference tags.
+## @{
+class BugTrackerConfig(object):
+ """ Bug tracker config """
+ def __init__(self, sDbId, sName, sBugUrl, asCommitTags):
+ assert len(sDbId) == 4;
+ self.sDbId = sDbId;
+ self.sName = sName;
+ self.sBugUrl = sBugUrl;
+ self.asCommitTags = asCommitTags;
+
+## The key is the database table
+g_kdBugTrackers = {
+ 'xtrk': BugTrackerConfig('xtrk', 'xTracker', 'https://linserv.de.oracle.com/vbox/xTracker/index.php?bug=',
+ ['bugref:', '@bugref{', 'bugef:', 'bugrf:', ], ),
+ 'bgdb': BugTrackerConfig('bgdb', 'BugDB', 'https://bug.oraclecorp.com/pls/bug/webbug_edit.edit_info_top?rptno=',
+ ['bugdbref:', '@bugdbref{', 'bugdb:', ], ),
+ 'vorg': BugTrackerConfig('vorg', 'External Trac', 'https://www.virtualbox.org/ticket/',
+ ['ticketref:', '@ticketref{', 'ticket:', ], ),
+};
+## @}
+
+
+
+## @name Virtual Sheriff email alerts
+## @{
+
+## SMTP server host name.
+g_ksSmtpHost = 'internal-mail-router.oracle.com';
+## SMTP server port number.
+g_kcSmtpPort = 25;
+## Default email 'From' for email alert.
+g_ksAlertFrom = 'vsheriff@oracle.com';
+## Subject for email alert.
+g_ksAlertSubject = 'Virtual Test Sheriff Alert';
+## List of users to send alerts.
+g_asAlertList = ['alertuser1', 'alertuser2'];
+## iLOM password.
+g_ksLomPassword = 'put_your_ILOM_password_here_if_applicable';
+
+## @}
+
+
+## @name Partial Database Dump
+## @{
+
+## Minimum number of day. Set higher than g_kcTmDbDumpMaxDays to disable.
+g_kcTmDbDumpMinDays = 1;
+## Maximum number of day. Keep low - consider space and runtime.
+g_kcTmDbDumpMaxDays = 31;
+## The default number of days.
+g_kcTmDbDumpDefaultDays = 14;
+## @}
+
+
+## @name Debug Features
+## @{
+
+## Enables extra DB exception information.
+g_kfDebugDbXcpt = True;
+
+## Where to write the glue debug.
+# None indicates apache error log, string indicates a file.
+#g_ksSrvGlueDebugLogDst = '/tmp/testmanager-srv-glue.log';
+g_ksSrvGlueDebugLogDst = None;
+## Whether to enable CGI trace back in the server glue.
+g_kfSrvGlueCgiTb = False;
+## Enables glue debug output.
+g_kfSrvGlueDebug = False;
+## Timestamp and pid prefix the glue debug output.
+g_kfSrvGlueDebugTS = True;
+## Whether to dumping CGI environment variables.
+g_kfSrvGlueCgiDumpEnv = False;
+## Whether to dumping CGI script arguments.
+g_kfSrvGlueCgiDumpArgs = False;
+## Enables task scheduler debug output to g_ksSrvGlueDebugLogDst.
+g_kfSrvGlueDebugScheduler = False;
+
+## Enables the SQL trace back.
+g_kfWebUiSqlTrace = False;
+## Enables the explain in the SQL trace back.
+g_kfWebUiSqlTraceExplain = False;
+## Whether the postgresql version supports the TIMING option on EXPLAIN (>= 9.2).
+g_kfWebUiSqlTraceExplainTiming = False;
+## Display time spent processing the page.
+g_kfWebUiProcessedIn = True;
+## Enables WebUI debug output.
+g_kfWebUiDebug = False;
+## Enables WebUI SQL debug output print() calls (requires g_kfWebUiDebug).
+g_kfWebUiSqlDebug = False;
+## Enables the debug panel at the bottom of the page.
+g_kfWebUiDebugPanel = True;
+
+## Profile cgi/admin.py.
+g_kfProfileAdmin = False;
+## Profile cgi/index.py.
+g_kfProfileIndex = False;
+
+## When not None,
+g_ksTestBoxDispXpctLog = '/tmp/testmanager-testboxdisp-xcpt.log'
+## @}
+
diff --git a/src/VBox/ValidationKit/testmanager/core/Makefile.kmk b/src/VBox/ValidationKit/testmanager/core/Makefile.kmk
new file mode 100644
index 00000000..74d882cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/core/__init__.py b/src/VBox/ValidationKit/testmanager/core/__init__.py
new file mode 100644
index 00000000..bfcc7f8b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+TestBox Script - Core Logic.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/testmanager/core/base.py b/src/VBox/ValidationKit/testmanager/core/base.py
new file mode 100755
index 00000000..eac3d921
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/base.py
@@ -0,0 +1,1514 @@
+# -*- coding: utf-8 -*-
+# $Id: base.py $
+# pylint: disable=too-many-lines
+
+"""
+Test Manager Core - Base Class(es).
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import copy;
+import datetime;
+import json;
+import re;
+import socket;
+import sys;
+import uuid;
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int # pylint: disable=redefined-builtin,invalid-name
+
+
+class TMExceptionBase(Exception):
+ """
+ For exceptions raised by any TestManager component.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMTooManyRows(TMExceptionBase):
+ """
+ Too many rows in the result.
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMRowNotFound(TMExceptionBase):
+ """
+ Database row not found.
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMRowAlreadyExists(TMExceptionBase):
+ """
+ Database row already exists (typically raised by addEntry).
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMInvalidData(TMExceptionBase):
+ """
+ Data validation failed.
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMRowInUse(TMExceptionBase):
+ """
+ Database row is in use and cannot be deleted.
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMInFligthCollision(TMExceptionBase):
+ """
+ Database update failed because someone else had already made changes to
+ the data there.
+ Used by ModelLogicBase decendants.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class ModelBase(object): # pylint: disable=too-few-public-methods
+ """
+ Something all classes in the logical model inherits from.
+
+ Not sure if 'logical model' is the right term here.
+ Will see if it has any purpose later on...
+ """
+
+ def __init__(self):
+ pass;
+
+
+class ModelDataBase(ModelBase): # pylint: disable=too-few-public-methods
+ """
+ Something all classes in the data classes in the logical model inherits from.
+ """
+
+ ## Child classes can use this to list array attributes which should use
+ # an empty array ([]) instead of None as database NULL value.
+ kasAltArrayNull = [];
+
+ ## validate
+ ## @{
+ ksValidateFor_Add = 'add';
+ ksValidateFor_AddForeignId = 'add-foreign-id';
+ ksValidateFor_Edit = 'edit';
+ ksValidateFor_Other = 'other';
+ ## @}
+
+
+ ## List of internal attributes which should be ignored by
+ ## getDataAttributes and related machinery
+ kasInternalAttributes = [];
+
+ def __init__(self):
+ ModelBase.__init__(self);
+
+
+ #
+ # Standard methods implemented by combining python magic and hungarian prefixes.
+ #
+
+ def getDataAttributes(self):
+ """
+ Returns a list of data attributes.
+ """
+ asRet = [];
+ asAttrs = dir(self);
+ for sAttr in asAttrs:
+ if sAttr[0] == '_' or sAttr[0] == 'k':
+ continue;
+ if sAttr in self.kasInternalAttributes:
+ continue;
+ oValue = getattr(self, sAttr);
+ if callable(oValue):
+ continue;
+ asRet.append(sAttr);
+ return asRet;
+
+ def initFromOther(self, oOther):
+ """
+ Initialize this object with the values from another instance (child
+ class instance is accepted).
+
+ This serves as a kind of copy constructor.
+
+ Returns self. May raise exception if the type of other object differs
+ or is damaged.
+ """
+ for sAttr in self.getDataAttributes():
+ setattr(self, sAttr, getattr(oOther, sAttr));
+ return self;
+
+ @staticmethod
+ def getHungarianPrefix(sName):
+ """
+ Returns the hungarian prefix of the given name.
+ """
+ for i, _ in enumerate(sName):
+ if sName[i] not in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']:
+ assert re.search('^[A-Z][a-zA-Z0-9]*$', sName[i:]) is not None;
+ return sName[:i];
+ return sName;
+
+ def getAttributeParamNullValues(self, sAttr):
+ """
+ Returns a list of parameter NULL values, with the preferred one being
+ the first element.
+
+ Child classes can override this to handle one or more attributes specially.
+ """
+ sPrefix = self.getHungarianPrefix(sAttr);
+ if sPrefix in ['id', 'uid', 'i', 'off', 'pct']:
+ return [-1, '', '-1',];
+ if sPrefix in ['l', 'c',]:
+ return [long(-1), '', '-1',];
+ if sPrefix == 'f':
+ return ['',];
+ if sPrefix in ['enm', 'ip', 's', 'ts', 'uuid']:
+ return ['',];
+ if sPrefix in ['ai', 'aid', 'al', 'as']:
+ return [[], '', None]; ## @todo ??
+ if sPrefix == 'bm':
+ return ['', [],]; ## @todo bitmaps.
+ raise TMExceptionBase('Unable to classify "%s" (prefix %s)' % (sAttr, sPrefix));
+
+ def isAttributeNull(self, sAttr, oValue):
+ """
+ Checks if the specified attribute value indicates NULL.
+ Return True/False.
+
+ Note! This isn't entirely kosher actually.
+ """
+ if oValue is None:
+ return True;
+ aoNilValues = self.getAttributeParamNullValues(sAttr);
+ return oValue in aoNilValues;
+
+ def _convertAttributeFromParamNull(self, sAttr, oValue):
+ """
+ Converts an attribute from parameter NULL to database NULL value.
+ Returns the new attribute value.
+ """
+ aoNullValues = self.getAttributeParamNullValues(sAttr);
+ if oValue in aoNullValues:
+ oValue = None if sAttr not in self.kasAltArrayNull else [];
+ #
+ # Perform deep conversion on ModelDataBase object and lists of them.
+ #
+ elif isinstance(oValue, list) and oValue and isinstance(oValue[0], ModelDataBase):
+ oValue = copy.copy(oValue);
+ for i, _ in enumerate(oValue):
+ assert isinstance(oValue[i], ModelDataBase);
+ oValue[i] = copy.copy(oValue[i]);
+ oValue[i].convertFromParamNull();
+
+ elif isinstance(oValue, ModelDataBase):
+ oValue = copy.copy(oValue);
+ oValue.convertFromParamNull();
+
+ return oValue;
+
+ def convertFromParamNull(self):
+ """
+ Converts from parameter NULL values to database NULL values (None).
+ Returns self.
+ """
+ for sAttr in self.getDataAttributes():
+ oValue = getattr(self, sAttr);
+ oNewValue = self._convertAttributeFromParamNull(sAttr, oValue);
+ if oValue != oNewValue:
+ setattr(self, sAttr, oNewValue);
+ return self;
+
+ def _convertAttributeToParamNull(self, sAttr, oValue):
+ """
+ Converts an attribute from database NULL to a sepcial value we can pass
+ thru parameter list.
+ Returns the new attribute value.
+ """
+ if oValue is None:
+ oValue = self.getAttributeParamNullValues(sAttr)[0];
+ #
+ # Perform deep conversion on ModelDataBase object and lists of them.
+ #
+ elif isinstance(oValue, list) and oValue and isinstance(oValue[0], ModelDataBase):
+ oValue = copy.copy(oValue);
+ for i, _ in enumerate(oValue):
+ assert isinstance(oValue[i], ModelDataBase);
+ oValue[i] = copy.copy(oValue[i]);
+ oValue[i].convertToParamNull();
+
+ elif isinstance(oValue, ModelDataBase):
+ oValue = copy.copy(oValue);
+ oValue.convertToParamNull();
+
+ return oValue;
+
+ def convertToParamNull(self):
+ """
+ Converts from database NULL values (None) to special values we can
+ pass thru parameters list.
+ Returns self.
+ """
+ for sAttr in self.getDataAttributes():
+ oValue = getattr(self, sAttr);
+ oNewValue = self._convertAttributeToParamNull(sAttr, oValue);
+ if oValue != oNewValue:
+ setattr(self, sAttr, oNewValue);
+ return self;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ """
+ Validates and convert one attribute.
+ Returns the converted value.
+
+ Child classes can override this to handle one or more attributes specially.
+ Note! oDb can be None.
+ """
+ sPrefix = self.getHungarianPrefix(sAttr);
+
+ if sPrefix in ['id', 'uid']:
+ (oNewValue, sError) = self.validateInt( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ elif sPrefix in ['i', 'off', 'pct']:
+ (oNewValue, sError) = self.validateInt( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ iMin = getattr(self, 'kiMin_' + sAttr, 0),
+ iMax = getattr(self, 'kiMax_' + sAttr, 0x7ffffffe));
+ elif sPrefix in ['l', 'c']:
+ (oNewValue, sError) = self.validateLong(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ lMin = getattr(self, 'klMin_' + sAttr, 0),
+ lMax = getattr(self, 'klMax_' + sAttr, None));
+ elif sPrefix == 'f':
+ if not oValue and not fAllowNull: oValue = '0'; # HACK ALERT! Checkboxes are only added when checked.
+ (oNewValue, sError) = self.validateBool(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ elif sPrefix == 'ts':
+ (oNewValue, sError) = self.validateTs( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ elif sPrefix == 'ip':
+ (oNewValue, sError) = self.validateIp( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ elif sPrefix == 'uuid':
+ (oNewValue, sError) = self.validateUuid(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ elif sPrefix == 'enm':
+ (oNewValue, sError) = self.validateWord(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ asValid = getattr(self, 'kasValidValues_' + sAttr)); # The list is required.
+ elif sPrefix == 's':
+ (oNewValue, sError) = self.validateStr( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ cchMin = getattr(self, 'kcchMin_' + sAttr, 0),
+ cchMax = getattr(self, 'kcchMax_' + sAttr, 4096),
+ fAllowUnicodeSymbols = getattr(self, 'kfAllowUnicode_' + sAttr, False) );
+ ## @todo al.
+ elif sPrefix == 'aid':
+ (oNewValue, sError) = self.validateListOfInts(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ iMin = 1, iMax = 0x7ffffffe);
+ elif sPrefix == 'as':
+ (oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ asValidValues = getattr(self, 'kasValidValues_' + sAttr, None),
+ cchMin = getattr(self, 'kcchMin_' + sAttr, 0 if fAllowNull else 1),
+ cchMax = getattr(self, 'kcchMax_' + sAttr, 4096));
+
+ elif sPrefix == 'bm':
+ ## @todo figure out bitfields.
+ (oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+ else:
+ raise TMExceptionBase('Unable to classify "%s" (prefix %s)' % (sAttr, sPrefix));
+
+ _ = sParam; _ = oDb;
+ return (oNewValue, sError);
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ksValidateFor_Other):
+ """
+ Worker for implementing validateAndConvert().
+ """
+ dErrors = {};
+ for sAttr in self.getDataAttributes():
+ oValue = getattr(self, sAttr);
+ sParam = getattr(self, 'ksParam_' + sAttr);
+ aoNilValues = self.getAttributeParamNullValues(sAttr);
+ aoNilValues.append(None);
+
+ (oNewValue, sError) = self._validateAndConvertAttribute(sAttr, sParam, oValue, aoNilValues,
+ sAttr in asAllowNullAttributes, oDb);
+ if oValue != oNewValue:
+ setattr(self, sAttr, oNewValue);
+ if sError is not None:
+ dErrors[sParam] = sError;
+
+ # Check the NULL requirements of the primary ID(s) for the 'add' and 'edit' actions.
+ if enmValidateFor in (ModelDataBase.ksValidateFor_Add,
+ ModelDataBase.ksValidateFor_AddForeignId,
+ ModelDataBase.ksValidateFor_Edit,):
+ fMustBeNull = enmValidateFor == ModelDataBase.ksValidateFor_Add;
+ sAttr = getattr(self, 'ksIdAttr', None);
+ if sAttr is not None:
+ oValue = getattr(self, sAttr);
+ if self.isAttributeNull(sAttr, oValue) != fMustBeNull:
+ sParam = getattr(self, 'ksParam_' + sAttr);
+ sErrMsg = 'Must be NULL!' if fMustBeNull else 'Must not be NULL!'
+ if sParam in dErrors:
+ dErrors[sParam] += ' ' + sErrMsg;
+ else:
+ dErrors[sParam] = sErrMsg;
+
+ return dErrors;
+
+ def validateAndConvert(self, oDb, enmValidateFor = ksValidateFor_Other):
+ """
+ Validates the input and converts valid fields to their right type.
+ Returns a dictionary with per field reports, only invalid fields will
+ be returned, so an empty dictionary means that the data is valid.
+
+ The dictionary keys are ksParam_*.
+
+ Child classes can override _validateAndConvertAttribute to handle
+ selected fields specially. There are also a few class variables that
+ can be used to advice the validation: kcchMin_sAttr, kcchMax_sAttr,
+ kiMin_iAttr, kiMax_iAttr, klMin_lAttr, klMax_lAttr,
+ kasValidValues_enmAttr, and kasAllowNullAttributes.
+ """
+ return self._validateAndConvertWorker(getattr(self, 'kasAllowNullAttributes', []), oDb,
+ enmValidateFor = enmValidateFor);
+
+ def validateAndConvertEx(self, asAllowNullAttributes, oDb, enmValidateFor = ksValidateFor_Other):
+ """
+ Same as validateAndConvert but with custom allow-null list.
+ """
+ return self._validateAndConvertWorker(asAllowNullAttributes, oDb, enmValidateFor = enmValidateFor);
+
+ def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
+ """
+ Calculate the attribute value when initialized from a parameter.
+
+ Returns the new value, with parameter NULL values. Raises exception on
+ invalid parameter value.
+
+ Child classes can override to do special parameter conversion jobs.
+ """
+ sPrefix = self.getHungarianPrefix(sAttr);
+ asValidValues = getattr(self, 'kasValidValues_' + sAttr, None);
+ fAllowNull = sAttr in getattr(self, 'kasAllowNullAttributes', []);
+ if fStrict:
+ if sPrefix == 'f':
+ # HACK ALERT! Checkboxes are only present when checked, so we always have to provide a default.
+ oNewValue = oDisp.getStringParam(sParam, asValidValues, '0');
+ elif sPrefix[0] == 'a':
+ # HACK ALERT! Lists are not present if empty.
+ oNewValue = oDisp.getListOfStrParams(sParam, []);
+ else:
+ oNewValue = oDisp.getStringParam(sParam, asValidValues, None, fAllowNull = fAllowNull);
+ else:
+ if sPrefix[0] == 'a':
+ oNewValue = oDisp.getListOfStrParams(sParam, []);
+ else:
+ assert oValue is not None, 'sAttr=%s' % (sAttr,);
+ oNewValue = oDisp.getStringParam(sParam, asValidValues, oValue, fAllowNull = fAllowNull);
+ return oNewValue;
+
+ def initFromParams(self, oDisp, fStrict = True):
+ """
+ Initialize the object from parameters.
+ The input is not validated at all, except that all parameters must be
+ present when fStrict is True.
+
+ Returns self. Raises exception on invalid parameter value.
+
+ Note! The returned object has parameter NULL values, not database ones!
+ """
+
+ self.convertToParamNull()
+ for sAttr in self.getDataAttributes():
+ oValue = getattr(self, sAttr);
+ oNewValue = self.convertParamToAttribute(sAttr, getattr(self, 'ksParam_' + sAttr), oValue, oDisp, fStrict);
+ if oNewValue != oValue:
+ setattr(self, sAttr, oNewValue);
+ return self;
+
+ def areAttributeValuesEqual(self, sAttr, sPrefix, oValue1, oValue2):
+ """
+ Called to compare two attribute values and python thinks differs.
+
+ Returns True/False.
+
+ Child classes can override this to do special compares of things like arrays.
+ """
+ # Just in case someone uses it directly.
+ if oValue1 == oValue2:
+ return True;
+
+ #
+ # Timestamps can be both string (param) and object (db)
+ # depending on the data source. Compare string values to make
+ # sure we're doing the right thing here.
+ #
+ if sPrefix == 'ts':
+ return str(oValue1) == str(oValue2);
+
+ #
+ # Some generic code handling ModelDataBase children.
+ #
+ if isinstance(oValue1, list) and isinstance(oValue2, list):
+ if len(oValue1) == len(oValue2):
+ for i, _ in enumerate(oValue1):
+ if not isinstance(oValue1[i], ModelDataBase) \
+ or type(oValue1) is not type(oValue2):
+ return False;
+ if not oValue1[i].isEqual(oValue2[i]):
+ return False;
+ return True;
+
+ elif isinstance(oValue1, ModelDataBase) \
+ and type(oValue1) is type(oValue2):
+ return oValue1[i].isEqual(oValue2[i]);
+
+ _ = sAttr;
+ return False;
+
+ def isEqual(self, oOther):
+ """ Compares two instances. """
+ for sAttr in self.getDataAttributes():
+ if getattr(self, sAttr) != getattr(oOther, sAttr):
+ # Delegate the final decision to an overridable method.
+ if not self.areAttributeValuesEqual(sAttr, self.getHungarianPrefix(sAttr),
+ getattr(self, sAttr), getattr(oOther, sAttr)):
+ return False;
+ return True;
+
+ def isEqualEx(self, oOther, asExcludeAttrs):
+ """ Compares two instances, omitting the given attributes. """
+ for sAttr in self.getDataAttributes():
+ if sAttr not in asExcludeAttrs \
+ and getattr(self, sAttr) != getattr(oOther, sAttr):
+ # Delegate the final decision to an overridable method.
+ if not self.areAttributeValuesEqual(sAttr, self.getHungarianPrefix(sAttr),
+ getattr(self, sAttr), getattr(oOther, sAttr)):
+ return False;
+ return True;
+
+ def reinitToNull(self):
+ """
+ Reinitializes the object to (database) NULL values.
+ Returns self.
+ """
+ for sAttr in self.getDataAttributes():
+ setattr(self, sAttr, None);
+ return self;
+
+ def toString(self):
+ """
+ Stringifies the object.
+ Returns string representation.
+ """
+
+ sMembers = '';
+ for sAttr in self.getDataAttributes():
+ oValue = getattr(self, sAttr);
+ sMembers += ', %s=%s' % (sAttr, oValue);
+
+ oClass = type(self);
+ if sMembers == '':
+ return '<%s>' % (oClass.__name__);
+ return '<%s: %s>' % (oClass.__name__, sMembers[2:]);
+
+ def __str__(self):
+ return self.toString();
+
+
+
+ #
+ # New validation helpers.
+ #
+ # These all return (oValue, sError), where sError is None when the value
+ # is valid and an error message when not. On success and in case of
+ # range errors, oValue is converted into the requested type.
+ #
+
+ @staticmethod
+ def validateInt(sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, '']), fAllowNull = True):
+ """ Validates an integer field. """
+ if sValue in aoNilValues:
+ if fAllowNull:
+ return (None if sValue is None else aoNilValues[0], None);
+ return (sValue, 'Mandatory.');
+
+ try:
+ if utils.isString(sValue):
+ iValue = int(sValue, 0);
+ else:
+ iValue = int(sValue);
+ except:
+ return (sValue, 'Not an integer');
+
+ if iValue in aoNilValues:
+ return (aoNilValues[0], None if fAllowNull else 'Mandatory.');
+
+ if iValue < iMin:
+ return (iValue, 'Value too small (min %d)' % (iMin,));
+ if iValue > iMax:
+ return (iValue, 'Value too high (max %d)' % (iMax,));
+ return (iValue, None);
+
+ @staticmethod
+ def validateLong(sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, '']), fAllowNull = True):
+ """ Validates an long integer field. """
+ if sValue in aoNilValues:
+ if fAllowNull:
+ return (None if sValue is None else aoNilValues[0], None);
+ return (sValue, 'Mandatory.');
+ try:
+ if utils.isString(sValue):
+ lValue = long(sValue, 0);
+ else:
+ lValue = long(sValue);
+ except:
+ return (sValue, 'Not a long integer');
+
+ if lValue in aoNilValues:
+ return (aoNilValues[0], None if fAllowNull else 'Mandatory.');
+
+ if lMin is not None and lValue < lMin:
+ return (lValue, 'Value too small (min %d)' % (lMin,));
+ if lMax is not None and lValue > lMax:
+ return (lValue, 'Value too high (max %d)' % (lMax,));
+ return (lValue, None);
+
+ kdTimestampRegex = {
+ len('2012-10-08 01:54:06'): r'(\d{4})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d)$',
+ len('2012-10-08 01:54:06.00'): r'(\d{4})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d).\d{2}$',
+ len('2012-10-08 01:54:06.000'): r'(\d{4})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d).\d{3}$',
+ len('999999-12-31 00:00:00.00'): r'(\d{6})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d).\d{2}$',
+ len('9999-12-31 23:59:59.999999'): r'(\d{4})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d).\d{6}$',
+ len('9999-12-31T23:59:59.999999999'): r'(\d{4})-([01]\d)-([0123]\d)[ Tt]([012]\d):[0-5]\d:([0-6]\d).\d{9}$',
+ };
+
+ @staticmethod
+ def validateTs(sValue, aoNilValues = tuple([None, '']), fAllowNull = True, fRelative = False):
+ """ Validates a timestamp field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+ if not utils.isString(sValue):
+ return (sValue, None);
+
+ # Validate and strip off the timezone stuff.
+ if sValue[-1] in 'Zz':
+ sStripped = sValue[:-1];
+ sValue = sStripped + 'Z';
+ elif len(sValue) >= 19 + 3:
+ oRes = re.match(r'^.*[+-](\d\d):(\d\d)$', sValue);
+ if oRes is not None:
+ if int(oRes.group(1)) > 12 or int(oRes.group(2)) >= 60:
+ return (sValue, 'Invalid timezone offset.');
+ sStripped = sValue[:-6];
+ else:
+ sStripped = sValue;
+ else:
+ sStripped = sValue;
+
+ # Used the stripped value length to find regular expression for validating and parsing the timestamp.
+ sError = None;
+ sRegExp = ModelDataBase.kdTimestampRegex.get(len(sStripped), None);
+ if sRegExp:
+ oRes = re.match(sRegExp, sStripped);
+ if oRes is not None:
+ iYear = int(oRes.group(1));
+ if iYear % 4 == 0 and (iYear % 100 != 0 or iYear % 400 == 0):
+ acDaysOfMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ else:
+ acDaysOfMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ iMonth = int(oRes.group(2));
+ iDay = int(oRes.group(3));
+ iHour = int(oRes.group(4));
+ iSec = int(oRes.group(5));
+ if iMonth > 12 or (iMonth <= 0 and not fRelative):
+ sError = 'Invalid timestamp month.';
+ elif iDay > acDaysOfMonth[iMonth - 1]:
+ sError = 'Invalid timestamp day-of-month (%02d has %d days).' % (iMonth, acDaysOfMonth[iMonth - 1]);
+ elif iHour > 23:
+ sError = 'Invalid timestamp hour.'
+ elif iSec >= 61:
+ sError = 'Invalid timestamp second.'
+ elif iSec >= 60:
+ sError = 'Invalid timestamp: no leap seconds, please.'
+ else:
+ sError = 'Invalid timestamp (validation regexp: %s).' % (sRegExp,);
+ else:
+ sError = 'Invalid timestamp length.';
+ return (sValue, sError);
+
+ @staticmethod
+ def validateIp(sValue, aoNilValues = tuple([None, '']), fAllowNull = True):
+ """ Validates an IP address field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ if sValue == '::1':
+ return (sValue, None);
+
+ try:
+ socket.inet_pton(socket.AF_INET, sValue); # pylint: disable=no-member
+ except:
+ try:
+ socket.inet_pton(socket.AF_INET6, sValue); # pylint: disable=no-member
+ except:
+ return (sValue, 'Not a valid IP address.');
+
+ return (sValue, None);
+
+ @staticmethod
+ def validateBool(sValue, aoNilValues = tuple([None, '']), fAllowNull = True):
+ """ Validates a boolean field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ if sValue in ('True', 'true', '1', True):
+ return (True, None);
+ if sValue in ('False', 'false', '0', False):
+ return (False, None);
+ return (sValue, 'Invalid boolean value.');
+
+ @staticmethod
+ def validateUuid(sValue, aoNilValues = tuple([None, '']), fAllowNull = True):
+ """ Validates an UUID field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ try:
+ sValue = str(uuid.UUID(sValue));
+ except:
+ return (sValue, 'Invalid UUID value.');
+ return (sValue, None);
+
+ @staticmethod
+ def validateWord(sValue, cchMin = 1, cchMax = 64, asValid = None, aoNilValues = tuple([None, '']), fAllowNull = True):
+ """ Validates a word field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ if re.search('[^a-zA-Z0-9_-]', sValue) is not None:
+ sError = 'Single word ([a-zA-Z0-9_-]), please.';
+ elif cchMin is not None and len(sValue) < cchMin:
+ sError = 'Too short, min %s chars' % (cchMin,);
+ elif cchMax is not None and len(sValue) > cchMax:
+ sError = 'Too long, max %s chars' % (cchMax,);
+ elif asValid is not None and sValue not in asValid:
+ sError = 'Invalid value "%s", must be one of: %s' % (sValue, asValid);
+ else:
+ sError = None;
+ return (sValue, sError);
+
+ @staticmethod
+ def validateStr(sValue, cchMin = 0, cchMax = 4096, aoNilValues = tuple([None, '']), fAllowNull = True,
+ fAllowUnicodeSymbols = False):
+ """ Validates a string field. """
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ if cchMin is not None and len(sValue) < cchMin:
+ sError = 'Too short, min %s chars' % (cchMin,);
+ elif cchMax is not None and len(sValue) > cchMax:
+ sError = 'Too long, max %s chars' % (cchMax,);
+ elif fAllowUnicodeSymbols is False and utils.hasNonAsciiCharacters(sValue):
+ sError = 'Non-ascii characters not allowed'
+ else:
+ sError = None;
+ return (sValue, sError);
+
+ @staticmethod
+ def validateEmail(sValue, aoNilValues = tuple([None, '']), fAllowNull = True):
+ """ Validates a email field."""
+ if sValue in aoNilValues:
+ return (sValue, None if fAllowNull else 'Mandatory.');
+
+ if re.match(r'.+@.+\..+', sValue) is None:
+ return (sValue,'Invalid e-mail format.');
+ return (sValue, None);
+
+ @staticmethod
+ def validateListOfSomething(asValues, aoNilValues = tuple([[], None]), fAllowNull = True):
+ """ Validate a list of some uniform values. Returns a copy of the list (if list it is). """
+ if asValues in aoNilValues or (not asValues and not fAllowNull):
+ return (asValues, None if fAllowNull else 'Mandatory.')
+
+ if not isinstance(asValues, list):
+ return (asValues, 'Invalid data type (%s).' % (type(asValues),));
+
+ asValues = list(asValues); # copy the list.
+ if asValues:
+ oType = type(asValues[0]);
+ for i in range(1, len(asValues)):
+ if type(asValues[i]) is not oType: # pylint: disable=unidiomatic-typecheck
+ return (asValues, 'Invalid entry data type ([0]=%s vs [%d]=%s).' % (oType, i, type(asValues[i])) );
+
+ return (asValues, None);
+
+ @staticmethod
+ def validateListOfStr(asValues, cchMin = None, cchMax = None, asValidValues = None,
+ aoNilValues = tuple([[], None]), fAllowNull = True):
+ """ Validates a list of text items."""
+ (asValues, sError) = ModelDataBase.validateListOfSomething(asValues, aoNilValues, fAllowNull);
+
+ if sError is None and asValues not in aoNilValues and asValues:
+ if not utils.isString(asValues[0]):
+ return (asValues, 'Invalid item data type.');
+
+ if not fAllowNull and cchMin is None:
+ cchMin = 1;
+
+ for sValue in asValues:
+ if asValidValues is not None and sValue not in asValidValues:
+ sThisErr = 'Invalid value "%s".' % (sValue,);
+ elif cchMin is not None and len(sValue) < cchMin:
+ sThisErr = 'Value "%s" is too short, min length is %u chars.' % (sValue, cchMin);
+ elif cchMax is not None and len(sValue) > cchMax:
+ sThisErr = 'Value "%s" is too long, max length is %u chars.' % (sValue, cchMax);
+ else:
+ continue;
+
+ if sError is None:
+ sError = sThisErr;
+ else:
+ sError += ' ' + sThisErr;
+
+ return (asValues, sError);
+
+ @staticmethod
+ def validateListOfInts(asValues, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([[], None]), fAllowNull = True):
+ """ Validates a list of integer items."""
+ (asValues, sError) = ModelDataBase.validateListOfSomething(asValues, aoNilValues, fAllowNull);
+
+ if sError is None and asValues not in aoNilValues and asValues:
+ for i, _ in enumerate(asValues):
+ sValue = asValues[i];
+
+ sThisErr = '';
+ try:
+ iValue = int(sValue);
+ except:
+ sThisErr = 'Invalid integer value "%s".' % (sValue,);
+ else:
+ asValues[i] = iValue;
+ if iValue < iMin:
+ sThisErr = 'Value %d is too small (min %d)' % (iValue, iMin,);
+ elif iValue > iMax:
+ sThisErr = 'Value %d is too high (max %d)' % (iValue, iMax,);
+ else:
+ continue;
+
+ if sError is None:
+ sError = sThisErr;
+ else:
+ sError += ' ' + sThisErr;
+
+ return (asValues, sError);
+
+
+
+ #
+ # Old validation helpers.
+ #
+
+ @staticmethod
+ def _validateInt(dErrors, sName, sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, ''])):
+ """ Validates an integer field. """
+ (sValue, sError) = ModelDataBase.validateInt(sValue, iMin, iMax, aoNilValues, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateIntNN(dErrors, sName, sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, ''])):
+ """ Validates an integer field, not null. """
+ (sValue, sError) = ModelDataBase.validateInt(sValue, iMin, iMax, aoNilValues, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateLong(dErrors, sName, sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, ''])):
+ """ Validates an long integer field. """
+ (sValue, sError) = ModelDataBase.validateLong(sValue, lMin, lMax, aoNilValues, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateLongNN(dErrors, sName, sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, ''])):
+ """ Validates an long integer field, not null. """
+ (sValue, sError) = ModelDataBase.validateLong(sValue, lMin, lMax, aoNilValues, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateTs(dErrors, sName, sValue):
+ """ Validates a timestamp field. """
+ (sValue, sError) = ModelDataBase.validateTs(sValue, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateTsNN(dErrors, sName, sValue):
+ """ Validates a timestamp field, not null. """
+ (sValue, sError) = ModelDataBase.validateTs(sValue, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateIp(dErrors, sName, sValue):
+ """ Validates an IP address field. """
+ (sValue, sError) = ModelDataBase.validateIp(sValue, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateIpNN(dErrors, sName, sValue):
+ """ Validates an IP address field, not null. """
+ (sValue, sError) = ModelDataBase.validateIp(sValue, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateBool(dErrors, sName, sValue):
+ """ Validates a boolean field. """
+ (sValue, sError) = ModelDataBase.validateBool(sValue, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateBoolNN(dErrors, sName, sValue):
+ """ Validates a boolean field, not null. """
+ (sValue, sError) = ModelDataBase.validateBool(sValue, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateUuid(dErrors, sName, sValue):
+ """ Validates an UUID field. """
+ (sValue, sError) = ModelDataBase.validateUuid(sValue, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateUuidNN(dErrors, sName, sValue):
+ """ Validates an UUID field, not null. """
+ (sValue, sError) = ModelDataBase.validateUuid(sValue, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateWord(dErrors, sName, sValue, cchMin = 1, cchMax = 64, asValid = None):
+ """ Validates a word field. """
+ (sValue, sError) = ModelDataBase.validateWord(sValue, cchMin, cchMax, asValid, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateWordNN(dErrors, sName, sValue, cchMin = 1, cchMax = 64, asValid = None):
+ """ Validates a boolean field, not null. """
+ (sValue, sError) = ModelDataBase.validateWord(sValue, cchMin, cchMax, asValid, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateStr(dErrors, sName, sValue, cchMin = 0, cchMax = 4096):
+ """ Validates a string field. """
+ (sValue, sError) = ModelDataBase.validateStr(sValue, cchMin, cchMax, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateStrNN(dErrors, sName, sValue, cchMin = 0, cchMax = 4096):
+ """ Validates a string field, not null. """
+ (sValue, sError) = ModelDataBase.validateStr(sValue, cchMin, cchMax, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateEmail(dErrors, sName, sValue):
+ """ Validates a email field."""
+ (sValue, sError) = ModelDataBase.validateEmail(sValue, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateEmailNN(dErrors, sName, sValue):
+ """ Validates a email field."""
+ (sValue, sError) = ModelDataBase.validateEmail(sValue, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateListOfStr(dErrors, sName, asValues, asValidValues = None):
+ """ Validates a list of text items."""
+ (sValue, sError) = ModelDataBase.validateListOfStr(asValues, asValidValues = asValidValues, fAllowNull = True);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ @staticmethod
+ def _validateListOfStrNN(dErrors, sName, asValues, asValidValues = None):
+ """ Validates a list of text items, not null and len >= 1."""
+ (sValue, sError) = ModelDataBase.validateListOfStr(asValues, asValidValues = asValidValues, fAllowNull = False);
+ if sError is not None:
+ dErrors[sName] = sError;
+ return sValue;
+
+ #
+ # Various helpers.
+ #
+
+ @staticmethod
+ def formatSimpleNowAndPeriod(oDb, tsNow = None, sPeriodBack = None,
+ sTablePrefix = '', sExpCol = 'tsExpire', sEffCol = 'tsEffective'):
+ """
+ Formats a set of tsNow and sPeriodBack arguments for a standard testmanager
+ table.
+
+ If sPeriodBack is given, the query is effective for the period
+ (tsNow - sPeriodBack) thru (tsNow).
+
+ If tsNow isn't given, it defaults to current time.
+
+ Returns the final portion of a WHERE query (start with AND) and maybe an
+ ORDER BY and LIMIT bit if sPeriodBack is given.
+ """
+ if tsNow is not None:
+ if sPeriodBack is not None:
+ sRet = oDb.formatBindArgs(' AND ' + sTablePrefix + sExpCol + ' > (%s::timestamp - %s::interval)\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY ' + sTablePrefix + sExpCol + ' DESC\n'
+ 'LIMIT 1\n'
+ , ( tsNow, sPeriodBack, tsNow));
+ else:
+ sRet = oDb.formatBindArgs(' AND ' + sTablePrefix + sExpCol + ' > %s\n'
+ ' AND ' + sTablePrefix + sEffCol + ' <= %s\n'
+ , ( tsNow, tsNow, ));
+ else:
+ if sPeriodBack is not None:
+ sRet = oDb.formatBindArgs(' AND ' + sTablePrefix + sExpCol + ' > (CURRENT_TIMESTAMP - %s::interval)\n'
+ ' AND ' + sTablePrefix + sEffCol + ' <= CURRENT_TIMESTAMP\n'
+ 'ORDER BY ' + sTablePrefix + sExpCol + ' DESC\n'
+ 'LIMIT 1\n'
+ , ( sPeriodBack, ));
+ else:
+ sRet = ' AND ' + sTablePrefix + sExpCol + ' = \'infinity\'::timestamp\n';
+ return sRet;
+
+ @staticmethod
+ def formatSimpleNowAndPeriodQuery(oDb, sQuery, aBindArgs, tsNow = None, sPeriodBack = None,
+ sTablePrefix = '', sExpCol = 'tsExpire', sEffCol = 'tsEffective'):
+ """
+ Formats a simple query for a standard testmanager table with optional
+ tsNow and sPeriodBack arguments.
+
+ The sQuery and sBindArgs are passed along to oDb.formatBindArgs to form
+ the first part of the query. Must end with an open WHERE statement as
+ we'll be adding the time part starting with 'AND something...'.
+
+ See formatSimpleNowAndPeriod for tsNow and sPeriodBack description.
+
+ Returns the final portion of a WHERE query (start with AND) and maybe an
+ ORDER BY and LIMIT bit if sPeriodBack is given.
+
+ """
+ return oDb.formatBindArgs(sQuery, aBindArgs) \
+ + ModelDataBase.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix, sExpCol, sEffCol);
+
+
+ #
+ # JSON
+ #
+
+ @staticmethod
+ def stringToJson(sString):
+ """ Converts a string to a JSON value string. """
+ if not utils.isString(sString):
+ sString = utils.toUnicode(sString);
+ if not utils.isString(sString):
+ sString = str(sString);
+ return json.dumps(sString);
+
+ @staticmethod
+ def dictToJson(dDict, dOptions = None):
+ """ Converts a dictionary to a JSON string. """
+ sJson = u'{ ';
+ for i, oKey in enumerate(dDict):
+ if i > 0:
+ sJson += ', ';
+ sJson += '%s: %s' % (ModelDataBase.stringToJson(oKey),
+ ModelDataBase.genericToJson(dDict[oKey], dOptions));
+ return sJson + ' }';
+
+ @staticmethod
+ def listToJson(aoList, dOptions = None):
+ """ Converts list of something to a JSON string. """
+ sJson = u'[ ';
+ for i, oValue in enumerate(aoList):
+ if i > 0:
+ sJson += u', ';
+ sJson += ModelDataBase.genericToJson(oValue, dOptions);
+ return sJson + u' ]';
+
+ @staticmethod
+ def datetimeToJson(oDateTime):
+ """ Converts a datetime instance to a JSON string. """
+ return '"%s"' % (oDateTime,);
+
+
+ @staticmethod
+ def genericToJson(oValue, dOptions = None):
+ """ Converts a generic object to a JSON string. """
+ if isinstance(oValue, ModelDataBase):
+ return oValue.toJson();
+ if isinstance(oValue, dict):
+ return ModelDataBase.dictToJson(oValue, dOptions);
+ if isinstance(oValue, (list, tuple, set, frozenset)):
+ return ModelDataBase.listToJson(oValue, dOptions);
+ if isinstance(oValue, datetime.datetime):
+ return ModelDataBase.datetimeToJson(oValue)
+ return json.dumps(oValue);
+
+ def attribValueToJson(self, sAttr, oValue, dOptions = None):
+ """
+ Converts the attribute value to JSON.
+ Returns JSON (string).
+ """
+ _ = sAttr;
+ return self.genericToJson(oValue, dOptions);
+
+ def toJson(self, dOptions = None):
+ """
+ Converts the object to JSON.
+ Returns JSON (string).
+ """
+ sJson = u'{ ';
+ for iAttr, sAttr in enumerate(self.getDataAttributes()):
+ oValue = getattr(self, sAttr);
+ if iAttr > 0:
+ sJson += ', ';
+ sJson += u'"%s": ' % (sAttr,);
+ sJson += self.attribValueToJson(sAttr, oValue, dOptions);
+ return sJson + u' }';
+
+
+ #
+ # Sub-classes.
+ #
+
+ class DispWrapper(object):
+ """Proxy object."""
+ def __init__(self, oDisp, sAttrFmt):
+ self.oDisp = oDisp;
+ self.sAttrFmt = sAttrFmt;
+ def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
+ """See WuiDispatcherBase.getStringParam."""
+ return self.oDisp.getStringParam(self.sAttrFmt % (sName,), asValidValues, sDefault, fAllowNull = fAllowNull);
+ def getListOfStrParams(self, sName, asDefaults = None):
+ """See WuiDispatcherBase.getListOfStrParams."""
+ return self.oDisp.getListOfStrParams(self.sAttrFmt % (sName,), asDefaults);
+ def getListOfIntParams(self, sName, iMin = None, iMax = None, aiDefaults = None):
+ """See WuiDispatcherBase.getListOfIntParams."""
+ return self.oDisp.getListOfIntParams(self.sAttrFmt % (sName,), iMin, iMax, aiDefaults);
+
+
+
+
+# pylint: disable=no-member,missing-docstring,too-few-public-methods
+class ModelDataBaseTestCase(unittest.TestCase):
+ """
+ Base testcase for ModelDataBase decendants.
+ Derive from this and override setUp.
+ """
+
+ def setUp(self):
+ """
+ Override this! Don't call super!
+ The subclasses are expected to set aoSamples to an array of instance
+ samples. The first entry must be a default object, the subsequent ones
+ are optional and their contents freely choosen.
+ """
+ self.aoSamples = [ModelDataBase(),];
+
+ def testEquality(self):
+ for oSample in self.aoSamples:
+ self.assertEqual(oSample.isEqual(copy.copy(oSample)), True);
+ self.assertIsNotNone(oSample.isEqual(self.aoSamples[0]));
+
+ def testNullConversion(self):
+ if not self.aoSamples[0].getDataAttributes():
+ return;
+ for oSample in self.aoSamples:
+ oCopy = copy.copy(oSample);
+ self.assertEqual(oCopy.convertToParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oSample), False);
+ self.assertEqual(oCopy.convertFromParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oSample), True, '\ngot : %s\nexpected: %s' % (oCopy, oSample,));
+
+ oCopy = copy.copy(oSample);
+ self.assertEqual(oCopy.convertToParamNull(), oCopy);
+ oCopy2 = copy.copy(oCopy);
+ self.assertEqual(oCopy.convertToParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oCopy2), True);
+ self.assertEqual(oCopy.convertToParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oCopy2), True);
+
+ oCopy = copy.copy(oSample);
+ self.assertEqual(oCopy.convertFromParamNull(), oCopy);
+ oCopy2 = copy.copy(oCopy);
+ self.assertEqual(oCopy.convertFromParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oCopy2), True);
+ self.assertEqual(oCopy.convertFromParamNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oCopy2), True);
+
+ def testReinitToNull(self):
+ oFirst = copy.copy(self.aoSamples[0]);
+ self.assertEqual(oFirst.reinitToNull(), oFirst);
+ for oSample in self.aoSamples:
+ oCopy = copy.copy(oSample);
+ self.assertEqual(oCopy.reinitToNull(), oCopy);
+ self.assertEqual(oCopy.isEqual(oFirst), True);
+
+ def testValidateAndConvert(self):
+ for oSample in self.aoSamples:
+ oCopy = copy.copy(oSample);
+ oCopy.convertToParamNull();
+ dError1 = oCopy.validateAndConvert(None);
+
+ oCopy2 = copy.copy(oCopy);
+ self.assertEqual(oCopy.validateAndConvert(None), dError1);
+ self.assertEqual(oCopy.isEqual(oCopy2), True);
+
+ def testInitFromParams(self):
+ class DummyDisp(object):
+ def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
+ _ = sName; _ = asValidValues; _ = fAllowNull;
+ return sDefault;
+ def getListOfStrParams(self, sName, asDefaults = None):
+ _ = sName;
+ return asDefaults;
+ def getListOfIntParams(self, sName, iMin = None, iMax = None, aiDefaults = None):
+ _ = sName; _ = iMin; _ = iMax;
+ return aiDefaults;
+
+ for oSample in self.aoSamples:
+ oCopy = copy.copy(oSample);
+ self.assertEqual(oCopy.initFromParams(DummyDisp(), fStrict = False), oCopy);
+
+ def testToString(self):
+ for oSample in self.aoSamples:
+ self.assertIsNotNone(oSample.toString());
+
+
+class FilterCriterionValueAndDescription(object):
+ """
+ A filter criterion value and its description.
+ """
+
+ def __init__(self, oValue, sDesc, cTimes = None, sHover = None, fIrrelevant = False):
+ self.oValue = oValue; ##< Typically the ID of something in the database.
+ self.sDesc = sDesc; ##< What to display.
+ self.cTimes = cTimes; ##< Number of times the value occurs in the result set. None if not given.
+ self.sHover = sHover; ##< Optional hover/title string.
+ self.fIrrelevant = fIrrelevant; ##< Irrelevant filter option, only present because it's selected
+ self.aoSubs = []; ##< References to FilterCriterion.oSub.aoPossible.
+
+
+class FilterCriterion(object):
+ """
+ A filter criterion.
+ """
+
+ ## @name The state.
+ ## @{
+ ksState_NotSelected = 'not-selected';
+ ksState_Selected = 'selected';
+ ## @}
+
+ ## @name The kind of filtering.
+ ## @{
+ ## 'Element of' by default, 'not an element of' when fInverted is False.
+ ksKind_ElementOfOrNot = 'element-of-or-not';
+ ## The criterion is a special one and cannot be inverted.
+ ksKind_Special = 'special';
+ ## @}
+
+ ## @name The value type.
+ ## @{
+ ksType_UInt = 'uint'; ##< unsigned integer value.
+ ksType_UIntNil = 'uint-nil'; ##< unsigned integer value, with nil.
+ ksType_String = 'string'; ##< string value.
+ ksType_Ranges = 'ranges'; ##< List of (unsigned) integer ranges.
+ ## @}
+
+ def __init__(self, sName, sVarNm = None, sType = ksType_UInt, # pylint: disable=too-many-arguments
+ sState = ksState_NotSelected, sKind = ksKind_ElementOfOrNot,
+ sTable = None, sColumn = None, asTables = None, oSub = None):
+ assert len(sVarNm) == 2; # required by wuimain.py for filtering.
+ self.sName = sName;
+ self.sState = sState;
+ self.sType = sType;
+ self.sKind = sKind;
+ self.sVarNm = sVarNm;
+ self.aoSelected = []; ##< User input from sVarNm. Single value, type according to sType.
+ self.sInvVarNm = 'i' + sVarNm if sKind == self.ksKind_ElementOfOrNot else None;
+ self.fInverted = False; ##< User input from sInvVarNm. Inverts the operation (-> not an element of).
+ self.aoPossible = []; ##< type: list[FilterCriterionValueAndDescription]
+ assert (sTable is None and asTables is None) or ((sTable is not None) != (asTables is not None)), \
+ '%s %s' % (sTable, asTables);
+ self.asTables = [sTable,] if sTable is not None else asTables;
+ assert sColumn is None or len(self.asTables) == 1, '%s %s' % (self.asTables, sColumn);
+ self.sColumn = sColumn; ##< Normally only applicable if one table.
+ self.fExpanded = None; ##< Tristate (None, False, True)
+ self.oSub = oSub; ##< type: FilterCriterion
+
+
+class ModelFilterBase(ModelBase):
+ """
+ Base class for filters.
+
+ Filters are used to narrow down data that is displayed in a list or
+ report. This class differs a little from ModelDataBase in that it is not
+ tied to a database table, but one or more database queries that are
+ typically rather complicated.
+
+ The filter object has two roles:
+
+ 1. It is used by a ModelLogicBase descendant to store the available
+ filtering options for data begin displayed.
+
+ 2. It decodes and stores the filtering options submitted by the user so
+ a ModeLogicBase descendant can use it to construct WHERE statements.
+
+ The ModelFilterBase class is related to the ModelDataBase class in that it
+ decodes user parameters and stores data, however it is not a descendant.
+
+ Note! In order to reduce URL lengths, we use very very brief parameter
+ names for the filters.
+ """
+
+ def __init__(self):
+ ModelBase.__init__(self);
+ self.aCriteria = [] # type: list[FilterCriterion]
+
+ def _initFromParamsWorker(self, oDisp, oCriterion): # (,FilterCriterion)
+ """ Worker for initFromParams. """
+ if oCriterion.sType == FilterCriterion.ksType_UInt:
+ oCriterion.aoSelected = oDisp.getListOfIntParams(oCriterion.sVarNm, iMin = 0, aiDefaults = []);
+ elif oCriterion.sType == FilterCriterion.ksType_UIntNil:
+ oCriterion.aoSelected = oDisp.getListOfIntParams(oCriterion.sVarNm, iMin = -1, aiDefaults = []);
+ elif oCriterion.sType == FilterCriterion.ksType_String:
+ oCriterion.aoSelected = oDisp.getListOfStrParams(oCriterion.sVarNm, asDefaults = []);
+ if len(oCriterion.aoSelected) > 100:
+ raise TMExceptionBase('Variable %s has %u value, max allowed is 100!'
+ % (oCriterion.sVarNm, len(oCriterion.aoSelected)));
+ for sValue in oCriterion.aoSelected:
+ if len(sValue) > 64 \
+ or '\'' in sValue \
+ or sValue[-1] == '\\':
+ raise TMExceptionBase('Variable %s has an illegal value "%s"!' % (oCriterion.sVarNm, sValue));
+ elif oCriterion.sType == FilterCriterion.ksType_Ranges:
+ def convertRangeNumber(sValue):
+ """ Helper """
+ sValue = sValue.strip();
+ if sValue and sValue not in ('inf', 'Inf', 'INf', 'INF', 'InF', 'iNf', 'iNF', 'inF',):
+ try: return int(sValue);
+ except: pass;
+ return None;
+
+ for sRange in oDisp.getStringParam(oCriterion.sVarNm, sDefault = '').split(','):
+ sRange = sRange.strip();
+ if sRange and sRange != '-' and any(ch.isdigit() for ch in sRange):
+ asValues = sRange.split('-');
+ if len(asValues) == 1:
+ asValues = [asValues[0], asValues[0]];
+ elif len(asValues) > 2:
+ asValues = [asValues[0], asValues[-1]];
+ tTuple = (convertRangeNumber(asValues[0]), convertRangeNumber(asValues[1]));
+ if tTuple[0] is not None and tTuple[1] is not None and tTuple[0] > tTuple[1]:
+ tTuple = (tTuple[1], tTuple[0]);
+ oCriterion.aoSelected.append(tTuple);
+ else:
+ assert False;
+ if oCriterion.aoSelected:
+ oCriterion.sState = FilterCriterion.ksState_Selected;
+ else:
+ oCriterion.sState = FilterCriterion.ksState_NotSelected;
+
+ if oCriterion.sKind == FilterCriterion.ksKind_ElementOfOrNot:
+ oCriterion.fInverted = oDisp.getBoolParam(oCriterion.sInvVarNm, fDefault = False);
+
+ if oCriterion.oSub is not None:
+ self._initFromParamsWorker(oDisp, oCriterion.oSub);
+ return;
+
+ def initFromParams(self, oDisp): # type: (WuiDispatcherBase) -> self
+ """
+ Initialize the object from parameters.
+
+ Returns self. Raises exception on invalid parameter value.
+ """
+
+ for oCriterion in self.aCriteria:
+ self._initFromParamsWorker(oDisp, oCriterion);
+ return self;
+
+ def strainParameters(self, dParams, aAdditionalParams = None):
+ """ Filters just the parameters relevant to this filter, returning a copy. """
+
+ # Collect the parameter names.
+ dWanted = {};
+ for oCrit in self.aCriteria:
+ dWanted[oCrit.sVarNm] = 1;
+ if oCrit.sInvVarNm:
+ dWanted[oCrit.sInvVarNm] = 1;
+
+ # Add additional stuff.
+ if aAdditionalParams:
+ for sParam in aAdditionalParams:
+ dWanted[sParam] = 1;
+
+ # To the straining.
+ dRet = {};
+ for sKey in dParams:
+ if sKey in dWanted:
+ dRet[sKey] = dParams[sKey];
+ return dRet;
+
+
+class ModelLogicBase(ModelBase): # pylint: disable=too-few-public-methods
+ """
+ Something all classes in the logic classes the logical model inherits from.
+ """
+
+ def __init__(self, oDb):
+ ModelBase.__init__(self);
+
+ #
+ # Note! Do not create a connection here if None, we need to DB share
+ # connection with all other logic objects so we can perform half
+ # complex transactions involving several logic objects.
+ #
+ self._oDb = oDb;
+
+ def getDbConnection(self):
+ """
+ Gets the database connection.
+ This should only be used for instantiating other ModelLogicBase children.
+ """
+ return self._oDb;
+
+ def _dbRowsToModelDataList(self, oModelDataType, aaoRows = None):
+ """
+ Helper for conerting a simple fetch into a list of ModelDataType python objects.
+
+ If aaoRows is None, we'll fetchAll from the database ourselves.
+
+ The oModelDataType must be a class derived from ModelDataBase and implement
+ the initFormDbRow method.
+
+ Returns a list of oModelDataType instances.
+ """
+ assert issubclass(oModelDataType, ModelDataBase);
+ aoRet = [];
+ if aaoRows is None:
+ aaoRows = self._oDb.fetchAll();
+ for aoRow in aaoRows:
+ aoRet.append(oModelDataType().initFromDbRow(aoRow));
+ return aoRet;
+
+
+
+class AttributeChangeEntry(object): # pylint: disable=too-few-public-methods
+ """
+ Data class representing the changes made to one attribute.
+ """
+
+ def __init__(self, sAttr, oNewRaw, oOldRaw, sNewText, sOldText):
+ self.sAttr = sAttr;
+ self.oNewRaw = oNewRaw;
+ self.oOldRaw = oOldRaw;
+ self.sNewText = sNewText;
+ self.sOldText = sOldText;
+
+class AttributeChangeEntryPre(AttributeChangeEntry): # pylint: disable=too-few-public-methods
+ """
+ AttributeChangeEntry for preformatted values.
+ """
+
+ def __init__(self, sAttr, oNewRaw, oOldRaw, sNewText, sOldText):
+ AttributeChangeEntry.__init__(self, sAttr, oNewRaw, oOldRaw, sNewText, sOldText);
+
+class ChangeLogEntry(object): # pylint: disable=too-few-public-methods
+ """
+ A change log entry returned by the fetchChangeLog method typically
+ implemented by ModelLogicBase child classes.
+ """
+
+ def __init__(self, uidAuthor, sAuthor, tsEffective, tsExpire, oNewRaw, oOldRaw, aoChanges):
+ self.uidAuthor = uidAuthor;
+ self.sAuthor = sAuthor;
+ self.tsEffective = tsEffective;
+ self.tsExpire = tsExpire;
+ self.oNewRaw = oNewRaw;
+ self.oOldRaw = oOldRaw; # Note! NULL for the last entry.
+ self.aoChanges = aoChanges;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/build.py b/src/VBox/ValidationKit/testmanager/core/build.py
new file mode 100755
index 00000000..bfe50ca5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/build.py
@@ -0,0 +1,891 @@
+# -*- coding: utf-8 -*-
+# $Id: build.py $
+
+"""
+Test Manager - Builds.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os;
+import unittest;
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core import coreconsts;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
+ TMTooManyRows, TMInvalidData, TMRowNotFound, TMRowInUse;
+
+
+class BuildCategoryData(ModelDataBase):
+ """
+ A build category.
+ """
+
+ ksIdAttr = 'idBuildCategory';
+
+ ksParam_idBuildCategory = 'BuildCategory_idBuildCategory';
+ ksParam_sProduct = 'BuildCategory_sProduct';
+ ksParam_sRepository = 'BuildCategory_sRepository';
+ ksParam_sBranch = 'BuildCategory_sBranch';
+ ksParam_sType = 'BuildCategory_sType';
+ ksParam_asOsArches = 'BuildCategory_asOsArches';
+
+ kasAllowNullAttributes = ['idBuildCategory', ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idBuildCategory = None;
+ self.sProduct = None;
+ self.sRepository = None;
+ self.sBranch = None;
+ self.sType = None;
+ self.asOsArches = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM BuildCategories row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('BuildCategory not found.');
+
+ self.idBuildCategory = aoRow[0];
+ self.sProduct = aoRow[1];
+ self.sRepository = aoRow[2];
+ self.sBranch = aoRow[3];
+ self.sType = aoRow[4];
+ self.asOsArches = sorted(aoRow[5]);
+ return self;
+
+ def initFromDbWithId(self, oDb, idBuildCategory, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ _ = tsNow; _ = sPeriodBack; # No history in this table.
+ oDb.execute('SELECT * FROM BuildCategories WHERE idBuildCategory = %s', (idBuildCategory,));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idBuildCategory=%s not found' % (idBuildCategory, ));
+ return self.initFromDbRow(aoRow);
+
+ def initFromValues(self, sProduct, sRepository, sBranch, sType, asOsArches, idBuildCategory = None):
+ """
+ Reinitializes form a set of values.
+ return self.
+ """
+ self.idBuildCategory = idBuildCategory;
+ self.sProduct = sProduct;
+ self.sRepository = sRepository;
+ self.sBranch = sBranch;
+ self.sType = sType;
+ self.asOsArches = asOsArches;
+ return self;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ # Handle sType and asOsArches specially.
+ if sAttr == 'sType':
+ (oNewValue, sError) = ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue,
+ aoNilValues, fAllowNull, oDb);
+ if sError is None and self.sType.lower() != self.sType:
+ sError = 'Invalid build type value';
+
+ elif sAttr == 'asOsArches':
+ (oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ asValidValues = coreconsts.g_kasOsDotCpusAll);
+ if sError is not None and oNewValue is not None:
+ oNewValue = sorted(oNewValue); # Must be sorted!
+
+ else:
+ return ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ return (oNewValue, sError);
+
+ def matchesOsArch(self, sOs, sArch):
+ """ Checks if the build matches the given OS and architecture. """
+ if sOs + '.' + sArch in self.asOsArches:
+ return True;
+ if sOs + '.noarch' in self.asOsArches:
+ return True;
+ if 'os-agnostic.' + sArch in self.asOsArches:
+ return True;
+ if 'os-agnostic.noarch' in self.asOsArches:
+ return True;
+ return False;
+
+
+class BuildCategoryLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Build categories database logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches testboxes for listing.
+
+ Returns an array (list) of UserAccountData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = tsNow; _ = aiSortColumns;
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildCategories\n'
+ 'ORDER BY sProduct, sRepository, sBranch, sType, idBuildCategory\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(BuildCategoryData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+ def fetchForCombo(self):
+ """
+ Gets the list of Build Categories for a combo box.
+ Returns an array of (value [idBuildCategory], drop-down-name [info],
+ hover-text [info]) tuples.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildCategories\n'
+ 'ORDER BY sProduct, sBranch, sType, asOsArches')
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ oData = BuildCategoryData().initFromDbRow(aoRow)
+
+ sInfo = '%s / %s / %s / %s' % \
+ (oData.sProduct,
+ oData.sBranch,
+ oData.sType,
+ ', '.join(oData.asOsArches))
+
+ # Make short info string if necessary
+ sInfo = sInfo if len(sInfo) < 70 else (sInfo[:70] + '...')
+
+ oInfoItem = (oData.idBuildCategory, sInfo, sInfo)
+ aoRet.append(oInfoItem)
+
+ return aoRet
+
+ def addEntry(self, oData, uidAuthor = None, fCommit = False):
+ """
+ Standard method for adding a build category.
+ """
+
+ # Lazy bird warning! Reuse the soft addBuildCategory method.
+ self.addBuildCategory(oData, fCommit);
+ _ = uidAuthor;
+ return True;
+
+ def removeEntry(self, uidAuthor, idBuildCategory, fCascade = False, fCommit = False):
+ """
+ Tries to delete the build category.
+ Note! Does not implement cascading. This is intentional!
+ """
+
+ #
+ # Check that the build category isn't used by anyone.
+ #
+ self._oDb.execute('SELECT COUNT(idBuild)\n'
+ 'FROM Builds\n'
+ 'WHERE idBuildCategory = %s\n'
+ , (idBuildCategory,));
+ cBuilds = self._oDb.fetchOne()[0];
+ if cBuilds > 0:
+ raise TMRowInUse('Build category #%d is used by %d builds and can therefore not be deleted.'
+ % (idBuildCategory, cBuilds,));
+
+ #
+ # Ok, it's not used, so just delete it.
+ # (No history on this table. This code is for typos.)
+ #
+ self._oDb.execute('DELETE FROM Builds\n'
+ 'WHERE idBuildCategory = %s\n'
+ , (idBuildCategory,));
+
+ self._oDb.maybeCommit(fCommit);
+ _ = uidAuthor; _ = fCascade;
+ return True;
+
+ def cachedLookup(self, idBuildCategory):
+ """
+ Looks up the most recent BuildCategoryData object for idBuildCategory
+ via an object cache.
+
+ Returns a shared BuildCategoryData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('BuildCategoryData');
+ oEntry = self.dCache.get(idBuildCategory, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildCategories\n'
+ 'WHERE idBuildCategory = %s\n'
+ , (idBuildCategory, ));
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = BuildCategoryData();
+ oEntry.initFromDbRow(aaoRow);
+ self.dCache[idBuildCategory] = oEntry;
+ return oEntry;
+
+ #
+ # Other methods.
+ #
+
+ def tryFetch(self, idBuildCategory):
+ """
+ Try fetch the build category with the given ID.
+ Returns BuildCategoryData instance if found, None if not found.
+ May raise exception on database error.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildCategories\n'
+ 'WHERE idBuildCategory = %s\n'
+ , (idBuildCategory,))
+ aaoRows = self._oDb.fetchAll()
+ if not aaoRows:
+ return None;
+ if len(aaoRows) != 1:
+ raise self._oDb.integrityException('Duplicates in BuildCategories: %s' % (aaoRows,));
+ return BuildCategoryData().initFromDbRow(aaoRows[0])
+
+ def tryFindByData(self, oData):
+ """
+ Tries to find the matching build category from the sProduct, sBranch,
+ sType and asOsArches members of oData.
+
+ Returns a valid build category ID and an updated oData object if found.
+ Returns None and unmodified oData object if not found.
+ May raise exception on database error.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildCategories\n'
+ 'WHERE sProduct = %s\n'
+ ' AND sRepository = %s\n'
+ ' AND sBranch = %s\n'
+ ' AND sType = %s\n'
+ ' AND asOsArches = %s\n'
+ , ( oData.sProduct,
+ oData.sRepository,
+ oData.sBranch,
+ oData.sType,
+ sorted(oData.asOsArches),
+ ));
+ aaoRows = self._oDb.fetchAll();
+ if not aaoRows:
+ return None;
+ if len(aaoRows) > 1:
+ raise self._oDb.integrityException('Duplicates in BuildCategories: %s' % (aaoRows,));
+
+ oData.initFromDbRow(aaoRows[0]);
+ return oData.idBuildCategory;
+
+ def addBuildCategory(self, oData, fCommit = False):
+ """
+ Add Build Category record into the database if needed, returning updated oData.
+ Raises exception on input and database errors.
+ """
+
+ # Check BuildCategoryData before do anything
+ dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dDataErrors:
+ raise TMInvalidData('Invalid data passed to addBuildCategory(): %s' % (dDataErrors,));
+
+ # Does it already exist?
+ if self.tryFindByData(oData) is None:
+ # No, We'll have to add it.
+ self._oDb.execute('INSERT INTO BuildCategories (sProduct, sRepository, sBranch, sType, asOsArches)\n'
+ 'VALUES (%s, %s, %s, %s, %s)\n'
+ 'RETURNING idBuildCategory'
+ , ( oData.sProduct,
+ oData.sRepository,
+ oData.sBranch,
+ oData.sType,
+ sorted(oData.asOsArches),
+ ));
+ oData.idBuildCategory = self._oDb.fetchOne()[0];
+
+ self._oDb.maybeCommit(fCommit);
+ return oData;
+
+
+class BuildData(ModelDataBase):
+ """
+ A build.
+ """
+
+ ksIdAttr = 'idBuild';
+
+ ksParam_idBuild = 'Build_idBuild';
+ ksParam_tsCreated = 'Build_tsCreated';
+ ksParam_tsEffective = 'Build_tsEffective';
+ ksParam_tsExpire = 'Build_tsExpire';
+ ksParam_uidAuthor = 'Build_uidAuthor';
+ ksParam_idBuildCategory = 'Build_idBuildCategory';
+ ksParam_iRevision = 'Build_iRevision';
+ ksParam_sVersion = 'Build_sVersion';
+ ksParam_sLogUrl = 'Build_sLogUrl';
+ ksParam_sBinaries = 'Build_sBinaries';
+ ksParam_fBinariesDeleted = 'Build_fBinariesDeleted';
+
+ kasAllowNullAttributes = ['idBuild', 'tsCreated', 'tsEffective', 'tsExpire', 'uidAuthor', 'tsCreated', 'sLogUrl'];
+
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idBuild = None;
+ self.tsCreated = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.idBuildCategory = None;
+ self.iRevision = None;
+ self.sVersion = None;
+ self.sLogUrl = None;
+ self.sBinaries = None;
+ self.fBinariesDeleted = False;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM Builds row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Build not found.');
+
+ self.idBuild = aoRow[0];
+ self.tsCreated = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ self.idBuildCategory = aoRow[5];
+ self.iRevision = aoRow[6];
+ self.sVersion = aoRow[7];
+ self.sLogUrl = aoRow[8];
+ self.sBinaries = aoRow[9];
+ self.fBinariesDeleted = aoRow[10];
+ return self;
+
+ def initFromDbWithId(self, oDb, idBuild, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM Builds\n'
+ 'WHERE idBuild = %s\n'
+ , ( idBuild,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idBuild=%s not found (tsNow=%s sPeriodBack=%s)' % (idBuild, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def areFilesStillThere(self):
+ """
+ Try check if the build files are still there.
+
+ Returns True if they are, None if we cannot tell, and False if one or
+ more are missing.
+ """
+ if self.fBinariesDeleted:
+ return False;
+
+ for sBinary in self.sBinaries.split(','):
+ sBinary = sBinary.strip();
+ if not sBinary:
+ continue;
+ # Same URL tests as in webutils.downloadFile().
+ if sBinary.startswith('http://') \
+ or sBinary.startswith('https://') \
+ or sBinary.startswith('ftp://'):
+ # URL - don't bother trying to verify that (we don't use it atm).
+ fRc = None;
+ else:
+ # File.
+ if config.g_ksBuildBinRootDir is not None:
+ sFullPath = os.path.join(config.g_ksBuildBinRootDir, sBinary);
+ fRc = os.path.isfile(sFullPath);
+ if not fRc \
+ and not os.path.isfile(os.path.join(config.g_ksBuildBinRootDir, config.g_ksBuildBinRootFile)):
+ fRc = None; # Root file missing, so the share might not be mounted correctly.
+ else:
+ fRc = None;
+ if fRc is not True:
+ return fRc;
+
+ return True;
+
+
+class BuildDataEx(BuildData):
+ """
+ Complete data set.
+ """
+
+ kasInternalAttributes = [ 'oCat', ];
+
+ def __init__(self):
+ BuildData.__init__(self);
+ self.oCat = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT Builds.*, BuildCategories.* FROM Builds, BuildCategories query.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Build not found.');
+ BuildData.initFromDbRow(self, aoRow);
+ self.oCat = BuildCategoryData().initFromDbRow(aoRow[11:]);
+ return self;
+
+ def initFromDbWithId(self, oDb, idBuild, tsNow = None, sPeriodBack = None):
+ """
+ Reinitialize from database given a row ID.
+ Returns self. Raises exception on database error or if the ID is invalid.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE idBuild = %s\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ , ( idBuild,), tsNow, sPeriodBack, 'Builds.'));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idBuild=%s not found (tsNow=%s sPeriodBack=%s)' % (idBuild, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def convertFromParamNull(self):
+ raise TMExceptionBase('Not implemented');
+
+ def isEqual(self, oOther):
+ raise TMExceptionBase('Not implemented');
+
+
+
+class BuildLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Build database logic (covers build categories as well as builds).
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches builds for listing.
+
+ Returns an array (list) of BuildDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY tsCreated DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire > %s\n'
+ ' AND Builds.tsEffective <= %s\n'
+ 'ORDER BY tsCreated DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(BuildDataEx().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+ def addEntry(self, oBuildData, uidAuthor = None, fCommit = False):
+ """
+ Adds the build to the database, optionally adding the build category if
+ a BuildDataEx object used and it's necessary.
+
+ Returns updated data object. Raises exception on failure.
+ """
+
+ # Find/Add the build category if specified.
+ if isinstance(oBuildData, BuildDataEx) \
+ and oBuildData.idBuildCategory is None:
+ BuildCategoryLogic(self._oDb).addBuildCategory(oBuildData.oCat, fCommit = False);
+ oBuildData.idBuildCategory = oBuildData.oCat.idBuildCategory;
+
+ # Add the build.
+ self._oDb.execute('INSERT INTO Builds (uidAuthor,\n'
+ ' idBuildCategory,\n'
+ ' iRevision,\n'
+ ' sVersion,\n'
+ ' sLogUrl,\n'
+ ' sBinaries,\n'
+ ' fBinariesDeleted)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
+ 'RETURNING idBuild, tsCreated\n'
+ , ( uidAuthor,
+ oBuildData.idBuildCategory,
+ oBuildData.iRevision,
+ oBuildData.sVersion,
+ oBuildData.sLogUrl,
+ oBuildData.sBinaries,
+ oBuildData.fBinariesDeleted,
+ ));
+ aoRow = self._oDb.fetchOne();
+ oBuildData.idBuild = aoRow[0];
+ oBuildData.tsCreated = aoRow[1];
+
+ self._oDb.maybeCommit(fCommit);
+ return oBuildData;
+
+ def editEntry(self, oData, uidAuthor = None, fCommit = False):
+ """Modify database record"""
+
+ #
+ # Validate input and get current data.
+ #
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+ oOldData = BuildData().initFromDbWithId(self._oDb, oData.idBuild);
+
+ #
+ # Do the work.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor' ]):
+ self._historizeBuild(oData.idBuild);
+ self._oDb.execute('INSERT INTO Builds (uidAuthor,\n'
+ ' idBuild,\n'
+ ' tsCreated,\n'
+ ' idBuildCategory,\n'
+ ' iRevision,\n'
+ ' sVersion,\n'
+ ' sLogUrl,\n'
+ ' sBinaries,\n'
+ ' fBinariesDeleted)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)\n'
+ 'RETURNING idBuild, tsCreated\n'
+ , ( uidAuthor,
+ oData.idBuild,
+ oData.tsCreated,
+ oData.idBuildCategory,
+ oData.iRevision,
+ oData.sVersion,
+ oData.sLogUrl,
+ oData.sBinaries,
+ oData.fBinariesDeleted,
+ ));
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, idBuild, fCascade = False, fCommit = False):
+ """
+ Historize record
+ """
+
+ #
+ # No non-historic refs here, so just go ahead and expire the build.
+ #
+ _ = fCascade;
+ _ = uidAuthor; ## @todo record deleter.
+
+ self._historizeBuild(idBuild, None);
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def cachedLookup(self, idBuild):
+ """
+ Looks up the most recent BuildDataEx object for idBuild
+ via an object cache.
+
+ Returns a shared BuildDataEx object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('BuildDataEx');
+ oEntry = self.dCache.get(idBuild, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuild = %s\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idBuild, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuild = %s\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idBuild, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idBuild));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = BuildDataEx();
+ oEntry.initFromDbRow(aaoRow);
+ self.dCache[idBuild] = oEntry;
+ return oEntry;
+
+
+ #
+ # Other methods.
+ #
+
+ def tryFindSameBuildForOsArch(self, oBuildEx, sOs, sCpuArch):
+ """
+ Attempts to find a matching build for the given OS.ARCH. May return
+ the input build if if matches.
+
+ Returns BuildDataEx instance if found, None if none. May raise
+ exception on database error.
+ """
+
+ if oBuildEx.oCat.matchesOsArch(sOs, sCpuArch):
+ return oBuildEx;
+
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE BuildCategories.sProduct = %s\n'
+ ' AND BuildCategories.sBranch = %s\n'
+ ' AND BuildCategories.sType = %s\n'
+ ' AND ( %s = ANY(BuildCategories.asOsArches)\n'
+ ' OR %s = ANY(BuildCategories.asOsArches)\n'
+ ' OR %s = ANY(BuildCategories.asOsArches))\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND Builds.iRevision = %s\n'
+ ' AND Builds.sRelease = %s\n'
+ ' AND Builds.fBinariesDeleted IS FALSE\n'
+ 'ORDER BY tsCreated DESC\n'
+ 'LIMIT 4096\n' # stay sane.
+ , (oBuildEx.oCat.sProduct,
+ oBuildEx.oCat.sBranch,
+ oBuildEx.oCat.sType,
+ '%s.%s' % (sOs, sCpuArch),
+ '%s.noarch' % (sOs,),
+ 'os-agnostic.%s' % (sCpuArch,),
+ 'os-agnostic.noarch',
+ oBuildEx.iRevision,
+ oBuildEx.sRelease,
+ ) );
+ aaoRows = self._oDb.fetchAll();
+
+ for aoRow in aaoRows:
+ oBuildExRet = BuildDataEx().initFromDbRow(aoRow);
+ if not self.isBuildBlacklisted(oBuildExRet):
+ return oBuildExRet;
+
+ return None;
+
+ def isBuildBlacklisted(self, oBuildEx):
+ """
+ Checks if the given build is blacklisted
+ Returns True/False. May raise exception on database error.
+ """
+
+ asOsAgnosticArch = [];
+ asOsNoArch = [];
+ for sOsArch in oBuildEx.oCat.asOsArches:
+ asParts = sOsArch.split('.');
+ if len(asParts) != 2 or not asParts[0] or not asParts[1]:
+ raise self._oDb.integrityException('Bad build asOsArches value: %s (idBuild=%s idBuildCategory=%s)'
+ % (sOsArch, oBuildEx.idBuild, oBuildEx.idBuildCategory));
+ asOsNoArch.append(asParts[0] + '.noarch');
+ asOsNoArch.append('os-agnostic.' + asParts[1]);
+
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE BuildBlacklist.tsExpire > CURRENT_TIMESTAMP\n'
+ ' AND BuildBlacklist.tsEffective <= CURRENT_TIMESTAMP\n'
+ ' AND BuildBlacklist.sProduct = %s\n'
+ ' AND BuildBlacklist.sBranch = %s\n'
+ ' AND ( BuildBlacklist.asTypes is NULL\n'
+ ' OR %s = ANY(BuildBlacklist.asTypes))\n'
+ ' AND ( BuildBlacklist.asOsArches is NULL\n'
+ ' OR %s && BuildBlacklist.asOsArches\n' ## @todo check array rep! Need overload?
+ ' OR %s && BuildBlacklist.asOsArches\n'
+ ' OR %s && BuildBlacklist.asOsArches\n'
+ ' OR %s = ANY(BuildBlacklist.asOsArches))\n'
+ ' AND BuildBlacklist.iFirstRevision <= %s\n'
+ ' AND BuildBlacklist.iLastRevision >= %s\n'
+ , (oBuildEx.oCat.sProduct,
+ oBuildEx.oCat.sBranch,
+ oBuildEx.oCat.sType,
+ oBuildEx.oCat.asOsArches,
+ asOsAgnosticArch,
+ asOsNoArch,
+ 'os-agnostic.noarch',
+ oBuildEx.iRevision,
+ oBuildEx.iRevision,
+ ) );
+ return self._oDb.fetchOne()[0] > 0;
+
+
+ def getById(self, idBuild):
+ """
+ Get build record by its id
+ """
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuild=%s\n'
+ ' AND Builds.idBuildCategory=BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire = \'infinity\'::TIMESTAMP\n', (idBuild,))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise TMTooManyRows('Found more than one build with the same credentials. Database structure is corrupted.')
+ try:
+ return BuildDataEx().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+
+ def getAll(self, tsEffective = None):
+ """
+ Gets the list of all builds.
+ Returns an array of BuildDataEx instances.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND Builds.idBuildCategory=BuildCategories.idBuildCategory')
+ else:
+ self._oDb.execute('SELECT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.tsExpire > %s\n'
+ ' AND Builds.tsEffective <= %s'
+ ' AND Builds.idBuildCategory=BuildCategories.idBuildCategory'
+ , (tsEffective, tsEffective))
+ aoRet = []
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(BuildDataEx().initFromDbRow(aoRow))
+ return aoRet
+
+
+ def markDeletedByBinaries(self, sBinaries, fCommit = False):
+ """
+ Marks zero or more builds deleted given the build binaries.
+
+ Returns the number of affected builds.
+ """
+ # Fetch a list of affected build IDs (generally 1 build), and used the
+ # editEntry method to do the rest. This isn't 100% optimal, but it's
+ # short and simple, the main effort is anyway the first query.
+ self._oDb.execute('SELECT idBuild\n'
+ 'FROM Builds\n'
+ 'WHERE sBinaries = %s\n'
+ ' AND fBinariesDeleted = FALSE\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (sBinaries,));
+ aaoRows = self._oDb.fetchAll();
+ for aoRow in aaoRows:
+ oData = BuildData().initFromDbWithId(self._oDb, aoRow[0]);
+ assert not oData.fBinariesDeleted;
+ oData.fBinariesDeleted = True;
+ self.editEntry(oData, fCommit = False);
+ self._oDb.maybeCommit(fCommit);
+ return len(aaoRows);
+
+
+
+ #
+ # Internal helpers.
+ #
+
+ def _historizeBuild(self, idBuild, tsExpire = None):
+ """ Historizes the current entry for the specified build. """
+ if tsExpire is None:
+ self._oDb.execute('UPDATE Builds\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idBuild = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idBuild,));
+ else:
+ self._oDb.execute('UPDATE Builds\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idBuild = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (tsExpire, idBuild,));
+ return True;
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class BuildCategoryDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [BuildCategoryData(),];
+
+class BuildDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [BuildData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/buildblacklist.py b/src/VBox/ValidationKit/testmanager/core/buildblacklist.py
new file mode 100755
index 00000000..487fb9f0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/buildblacklist.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+# $Id: buildblacklist.py $
+
+"""
+Test Manager - Builds Blacklist.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelLogicBase, TMInvalidData, TMRowNotFound;
+
+
+class BuildBlacklistData(ModelDataBase):
+ """
+ Build Blacklist Data.
+ """
+
+ ksIdAttr = 'idBlacklisting';
+
+ ksParam_idBlacklisting = 'BuildBlacklist_idBlacklisting'
+ ksParam_tsEffective = 'BuildBlacklist_tsEffective'
+ ksParam_tsExpire = 'BuildBlacklist_tsExpire'
+ ksParam_uidAuthor = 'BuildBlacklist_uidAuthor'
+ ksParam_idFailureReason = 'BuildBlacklist_idFailureReason'
+ ksParam_sProduct = 'BuildBlacklist_sProduct'
+ ksParam_sBranch = 'BuildBlacklist_sBranch'
+ ksParam_asTypes = 'BuildBlacklist_asTypes'
+ ksParam_asOsArches = 'BuildBlacklist_asOsArches'
+ ksParam_iFirstRevision = 'BuildBlacklist_iFirstRevision'
+ ksParam_iLastRevision = 'BuildBlacklist_iLastRevision'
+
+ kasAllowNullAttributes = [ 'idBlacklisting',
+ 'tsEffective',
+ 'tsExpire',
+ 'uidAuthor',
+ 'asTypes',
+ 'asOsArches' ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idBlacklisting = None
+ self.tsEffective = None
+ self.tsExpire = None
+ self.uidAuthor = None
+ self.idFailureReason = None
+ self.sProduct = None
+ self.sBranch = None
+ self.asTypes = None
+ self.asOsArches = None
+ self.iFirstRevision = None
+ self.iLastRevision = None
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a SELECT * FROM BuildBlacklist.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('Build Blacklist item not found.')
+
+ self.idBlacklisting = aoRow[0]
+ self.tsEffective = aoRow[1]
+ self.tsExpire = aoRow[2]
+ self.uidAuthor = aoRow[3]
+ self.idFailureReason = aoRow[4]
+ self.sProduct = aoRow[5]
+ self.sBranch = aoRow[6]
+ self.asTypes = aoRow[7]
+ self.asOsArches = aoRow[8]
+ self.iFirstRevision = aoRow[9]
+ self.iLastRevision = aoRow[10]
+
+ return self;
+
+ def initFromDbWithId(self, oDb, idBlacklisting, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE idBlacklisting = %s\n'
+ , ( idBlacklisting,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idBlacklisting=%s not found (tsNow=%s sPeriodBack=%s)'
+ % (idBlacklisting, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+
+class BuildBlacklistLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Build Back List logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches Build Blacklist records.
+
+ Returns an array (list) of BuildBlacklistData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idBlacklisting DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idBlacklisting DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(BuildBlacklistData().initFromDbRow(aoRow))
+ return aoRows
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Adds a blacklisting to the database.
+ """
+ self._oDb.execute('INSERT INTO BuildBlacklist (\n'
+ ' uidAuthor,\n'
+ ' idFailureReason,\n'
+ ' sProduct,\n'
+ ' sBranch,\n'
+ ' asTypes,\n'
+ ' asOsArches,\n'
+ ' iFirstRevision,\n'
+ ' iLastRevision)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
+ , ( uidAuthor,
+ oData.idFailureReason,
+ oData.sProduct,
+ oData.sBranch,
+ oData.asTypes,
+ oData.asOsArches,
+ oData.iFirstRevision,
+ oData.iLastRevision,) );
+ self._oDb.maybeCommit(fCommit);
+ return True
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a blacklisting.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, BuildBlacklistData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ oOldData = BuildBlacklistData().initFromDbWithId(self._oDb, oData.idBlacklisting);
+
+ #
+ # Update the data that needs updating.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeEntry(oData.idBlacklisting, None);
+ self._readdEntry(uidAuthor, oData, None);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def removeEntry(self, uidAuthor, idBlacklisting, fCascade = False, fCommit = False):
+ """
+ Deletes a test group.
+ """
+ _ = fCascade; # Not applicable.
+
+ oData = BuildBlacklistData().initFromDbWithId(self._oDb, idBlacklisting);
+
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oData.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeEntry(idBlacklisting, tsCurMinusOne);
+ self._readdEntry(uidAuthor, oData, tsCurMinusOne);
+ self._historizeEntry(idBlacklisting);
+ self._oDb.execute('UPDATE BuildBlacklist\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idBlacklisting = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idBlacklisting,));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def cachedLookup(self, idBlacklisting):
+ """
+ Looks up the most recent BuildBlacklistData object for idBlacklisting
+ via an object cache.
+
+ Returns a shared BuildBlacklistData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('BuildBlacklistData');
+ oEntry = self.dCache.get(idBlacklisting, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE idBlacklisting = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idBlacklisting, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE idBlacklisting = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idBlacklisting, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idBlacklisting));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = BuildBlacklistData();
+ oEntry.initFromDbRow(aaoRow);
+ self.dCache[idBlacklisting] = oEntry;
+ return oEntry;
+
+
+ #
+ # Helpers.
+ #
+
+ def _historizeEntry(self, idBlacklisting, tsExpire = None):
+ """
+ Historizes the current entry for the given backlisting.
+ """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE BuildBlacklist\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idBlacklisting = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( tsExpire, idBlacklisting, ));
+ return True;
+
+ def _readdEntry(self, uidAuthor, oData, tsEffective = None):
+ """
+ Re-adds the BuildBlacklist entry. Used by editEntry and removeEntry.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO BuildBlacklist (\n'
+ ' uidAuthor,\n'
+ ' tsEffective,\n'
+ ' idBlacklisting,\n'
+ ' idFailureReason,\n'
+ ' sProduct,\n'
+ ' sBranch,\n'
+ ' asTypes,\n'
+ ' asOsArches,\n'
+ ' iFirstRevision,\n'
+ ' iLastRevision)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ tsEffective,
+ oData.idBlacklisting,
+ oData.idFailureReason,
+ oData.sProduct,
+ oData.sBranch,
+ oData.asTypes,
+ oData.asOsArches,
+ oData.iFirstRevision,
+ oData.iLastRevision,) );
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/buildsource.py b/src/VBox/ValidationKit/testmanager/core/buildsource.py
new file mode 100755
index 00000000..782b5ee0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/buildsource.py
@@ -0,0 +1,524 @@
+# -*- coding: utf-8 -*-
+# $Id: buildsource.py $
+
+"""
+Test Manager - Build Sources.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMRowAlreadyExists, \
+ TMRowInUse, TMInvalidData, TMRowNotFound;
+from testmanager.core import coreconsts;
+
+
+class BuildSourceData(ModelDataBase):
+ """
+ A build source.
+ """
+
+ ksIdAttr = 'idBuildSrc';
+
+ ksParam_idBuildSrc = 'BuildSource_idBuildSrc';
+ ksParam_tsEffective = 'BuildSource_tsEffective';
+ ksParam_tsExpire = 'BuildSource_tsExpire';
+ ksParam_uidAuthor = 'BuildSource_uidAuthor';
+ ksParam_sName = 'BuildSource_sName';
+ ksParam_sDescription = 'BuildSource_sDescription';
+ ksParam_sProduct = 'BuildSource_sProduct';
+ ksParam_sBranch = 'BuildSource_sBranch';
+ ksParam_asTypes = 'BuildSource_asTypes';
+ ksParam_asOsArches = 'BuildSource_asOsArches';
+ ksParam_iFirstRevision = 'BuildSource_iFirstRevision';
+ ksParam_iLastRevision = 'BuildSource_iLastRevision';
+ ksParam_cSecMaxAge = 'BuildSource_cSecMaxAge';
+
+ kasAllowNullAttributes = [ 'idBuildSrc', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription', 'asTypes',
+ 'asOsArches', 'iFirstRevision', 'iLastRevision', 'cSecMaxAge' ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idBuildSrc = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.sName = None;
+ self.sDescription = None;
+ self.sProduct = None;
+ self.sBranch = None;
+ self.asTypes = None;
+ self.asOsArches = None;
+ self.iFirstRevision = None;
+ self.iLastRevision = None;
+ self.cSecMaxAge = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM BuildSources row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Build source not found.');
+
+ self.idBuildSrc = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.sName = aoRow[4];
+ self.sDescription = aoRow[5];
+ self.sProduct = aoRow[6];
+ self.sBranch = aoRow[7];
+ self.asTypes = aoRow[8];
+ self.asOsArches = aoRow[9];
+ self.iFirstRevision = aoRow[10];
+ self.iLastRevision = aoRow[11];
+ self.cSecMaxAge = aoRow[12];
+ return self;
+
+ def initFromDbWithId(self, oDb, idBuildSrc, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE idBuildSrc = %s\n'
+ , ( idBuildSrc,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idBuildSrc=%s not found (tsNow=%s sPeriodBack=%s)' % (idBuildSrc, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ # Handle asType and asOsArches specially.
+ if sAttr == 'sType':
+ (oNewValue, sError) = ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue,
+ aoNilValues, fAllowNull, oDb);
+ if sError is None:
+ if not self.asTypes:
+ oNewValue = None;
+ else:
+ for sType in oNewValue:
+ if len(sType) < 2 or sType.lower() != sType:
+ if sError is None: sError = '';
+ else: sError += ', ';
+ sError += 'invalid value "%s"' % (sType,);
+
+ elif sAttr == 'asOsArches':
+ (oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ asValidValues = coreconsts.g_kasOsDotCpusAll);
+ if sError is not None and oNewValue is not None:
+ oNewValue = sorted(oNewValue); # Must be sorted!
+
+ elif sAttr == 'cSecMaxAge' and oValue not in aoNilValues: # Allow human readable interval formats.
+ (oNewValue, sError) = utils.parseIntervalSeconds(oValue);
+ else:
+ return ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ return (oNewValue, sError);
+
+class BuildSourceLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Build source database logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches build sources.
+
+ Returns an array (list) of BuildSourceData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idBuildSrc DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idBuildSrc DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(BuildSourceData().initFromDbRow(aoRow))
+ return aoRows
+
+ def fetchForCombo(self):
+ """Fetch data which is aimed to be passed to HTML form"""
+ self._oDb.execute('SELECT idBuildSrc, sName, sProduct\n'
+ 'FROM BuildSources\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idBuildSrc DESC\n')
+ asRet = self._oDb.fetchAll();
+ asRet.insert(0, (-1, 'None', 'None'));
+ return asRet;
+
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add a new build source to the database.
+ """
+
+ #
+ # Validate the input.
+ #
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dErrors:
+ raise TMInvalidData('addEntry invalid input: %s' % (dErrors,));
+ self._assertUnique(oData, None);
+
+ #
+ # Add it.
+ #
+ self._oDb.execute('INSERT INTO BuildSources (\n'
+ ' uidAuthor,\n'
+ ' sName,\n'
+ ' sDescription,\n'
+ ' sProduct,\n'
+ ' sBranch,\n'
+ ' asTypes,\n'
+ ' asOsArches,\n'
+ ' iFirstRevision,\n'
+ ' iLastRevision,\n'
+ ' cSecMaxAge)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ oData.sName,
+ oData.sDescription,
+ oData.sProduct,
+ oData.sBranch,
+ oData.asTypes,
+ oData.asOsArches,
+ oData.iFirstRevision,
+ oData.iLastRevision,
+ oData.cSecMaxAge, ));
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a build source.
+ """
+
+ #
+ # Validate the input and read the old entry.
+ #
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('addEntry invalid input: %s' % (dErrors,));
+ self._assertUnique(oData, oData.idBuildSrc);
+ oOldData = BuildSourceData().initFromDbWithId(self._oDb, oData.idBuildSrc);
+
+ #
+ # Make the changes (if something actually changed).
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeBuildSource(oData.idBuildSrc);
+ self._oDb.execute('INSERT INTO BuildSources (\n'
+ ' uidAuthor,\n'
+ ' idBuildSrc,\n'
+ ' sName,\n'
+ ' sDescription,\n'
+ ' sProduct,\n'
+ ' sBranch,\n'
+ ' asTypes,\n'
+ ' asOsArches,\n'
+ ' iFirstRevision,\n'
+ ' iLastRevision,\n'
+ ' cSecMaxAge)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ oData.idBuildSrc,
+ oData.sName,
+ oData.sDescription,
+ oData.sProduct,
+ oData.sBranch,
+ oData.asTypes,
+ oData.asOsArches,
+ oData.iFirstRevision,
+ oData.iLastRevision,
+ oData.cSecMaxAge, ));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, idBuildSrc, fCascade = False, fCommit = False):
+ """
+ Deletes a build sources.
+ """
+
+ #
+ # Check cascading.
+ #
+ if fCascade is not True:
+ self._oDb.execute('SELECT idSchedGroup, sName\n'
+ 'FROM SchedGroups\n'
+ 'WHERE idBuildSrc = %s\n'
+ ' OR idBuildSrcTestSuite = %s\n'
+ , (idBuildSrc, idBuildSrc,));
+ if self._oDb.getRowCount() > 0:
+ asGroups = [];
+ for aoRow in self._oDb.fetchAll():
+ asGroups.append('%s (#%d)' % (aoRow[1], aoRow[0]));
+ raise TMRowInUse('Build source #%d is used by one or more scheduling groups: %s'
+ % (idBuildSrc, ', '.join(asGroups),));
+ else:
+ self._oDb.execute('UPDATE SchedGroups\n'
+ 'SET idBuildSrc = NULL\n'
+ 'WHERE idBuildSrc = %s'
+ , ( idBuildSrc,));
+ self._oDb.execute('UPDATE SchedGroups\n'
+ 'SET idBuildSrcTestSuite = NULL\n'
+ 'WHERE idBuildSrcTestSuite = %s'
+ , ( idBuildSrc,));
+
+ #
+ # Do the job.
+ #
+ self._historizeBuildSource(idBuildSrc, None);
+ _ = uidAuthor; ## @todo record deleter.
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def cachedLookup(self, idBuildSrc):
+ """
+ Looks up the most recent BuildSourceData object for idBuildSrc
+ via an object cache.
+
+ Returns a shared BuildSourceData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('BuildSourceData');
+ oEntry = self.dCache.get(idBuildSrc, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE idBuildSrc = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idBuildSrc, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE idBuildSrc = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idBuildSrc, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idBuildSrc));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = BuildSourceData();
+ oEntry.initFromDbRow(aaoRow);
+ self.dCache[idBuildSrc] = oEntry;
+ return oEntry;
+
+ #
+ # Other methods.
+ #
+
+ def openBuildCursor(self, oBuildSource, sOs, sCpuArch, tsNow):
+ """
+ Opens a cursor (SELECT) using the criteria found in the build source
+ and the given OS.CPUARCH.
+
+ Returns database cursor. May raise exception on bad input or logic error.
+
+ Used by SchedulerBase.
+ """
+
+ oCursor = self._oDb.openCursor();
+
+ #
+ # Construct the extra conditionals.
+ #
+ sExtraConditions = '';
+
+ # Types
+ if oBuildSource.asTypes is not None and oBuildSource.asTypes:
+ if len(oBuildSource.asTypes) == 1:
+ sExtraConditions += oCursor.formatBindArgs(' AND BuildCategories.sType = %s', (oBuildSource.asTypes[0],));
+ else:
+ sExtraConditions += oCursor.formatBindArgs(' AND BuildCategories.sType IN (%s', (oBuildSource.asTypes[0],))
+ for i in range(1, len(oBuildSource.asTypes) - 1):
+ sExtraConditions += oCursor.formatBindArgs(', %s', (oBuildSource.asTypes[i],));
+ sExtraConditions += oCursor.formatBindArgs(', %s)\n', (oBuildSource.asTypes[-1],));
+
+ # BuildSource OSes.ARCHes. (Paranoia: use a dictionary to avoid duplicate values.)
+ if oBuildSource.asOsArches is not None and oBuildSource.asOsArches:
+ sExtraConditions += oCursor.formatBindArgs(' AND BuildCategories.asOsArches && %s', (oBuildSource.asOsArches,));
+
+ # TestBox OSes.ARCHes. (Paranoia: use a dictionary to avoid duplicate values.)
+ dOsDotArches = {};
+ dOsDotArches[sOs + '.' + sCpuArch] = 1;
+ dOsDotArches[sOs + '.' + coreconsts.g_ksCpuArchAgnostic] = 1;
+ dOsDotArches[coreconsts.g_ksOsAgnostic + '.' + sCpuArch] = 1;
+ dOsDotArches[coreconsts.g_ksOsDotArchAgnostic] = 1;
+ sExtraConditions += oCursor.formatBindArgs(' AND BuildCategories.asOsArches && %s', (list(dOsDotArches.keys()),));
+
+ # Revision range.
+ if oBuildSource.iFirstRevision is not None:
+ sExtraConditions += oCursor.formatBindArgs(' AND Builds.iRevision >= %s\n', (oBuildSource.iFirstRevision,));
+ if oBuildSource.iLastRevision is not None:
+ sExtraConditions += oCursor.formatBindArgs(' AND Builds.iRevision <= %s\n', (oBuildSource.iLastRevision,));
+
+ # Max age.
+ if oBuildSource.cSecMaxAge is not None:
+ sExtraConditions += oCursor.formatBindArgs(' AND Builds.tsCreated >= (%s - \'%s seconds\'::INTERVAL)\n',
+ (tsNow, oBuildSource.cSecMaxAge,));
+
+ #
+ # Execute the query.
+ #
+ oCursor.execute('SELECT Builds.*, BuildCategories.*,\n'
+ ' EXISTS( SELECT tsExpire\n'
+ ' FROM BuildBlacklist\n'
+ ' WHERE BuildBlacklist.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND BuildBlacklist.sProduct = %s\n'
+ ' AND BuildBlacklist.sBranch = %s\n'
+ ' AND BuildBlacklist.iFirstRevision <= Builds.iRevision\n'
+ ' AND BuildBlacklist.iLastRevision >= Builds.iRevision ) AS fMaybeBlacklisted\n'
+ 'FROM Builds, BuildCategories\n'
+ 'WHERE Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND Builds.tsEffective <= %s\n'
+ ' AND Builds.fBinariesDeleted is FALSE\n'
+ ' AND BuildCategories.sProduct = %s\n'
+ ' AND BuildCategories.sBranch = %s\n'
+ + sExtraConditions +
+ 'ORDER BY Builds.idBuild DESC\n'
+ 'LIMIT 256\n'
+ , ( oBuildSource.sProduct, oBuildSource.sBranch,
+ tsNow, oBuildSource.sProduct, oBuildSource.sBranch,));
+
+ return oCursor;
+
+
+ def getById(self, idBuildSrc):
+ """Get Build Source data by idBuildSrc"""
+
+ self._oDb.execute('SELECT *\n'
+ 'FROM BuildSources\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idBuildSrc = %s;', (idBuildSrc,))
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException(
+ 'Found more than one build sources with the same credentials. Database structure is corrupted.')
+ try:
+ return BuildSourceData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+ #
+ # Internal helpers.
+ #
+
+ def _assertUnique(self, oData, idBuildSrcIgnore):
+ """ Checks that the build source name is unique, raises exception if it isn't. """
+ self._oDb.execute('SELECT idBuildSrc\n'
+ 'FROM BuildSources\n'
+ 'WHERE sName = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ + ('' if idBuildSrcIgnore is None else ' AND idBuildSrc <> %d\n' % (idBuildSrcIgnore,))
+ , ( oData.sName, ))
+ if self._oDb.getRowCount() > 0:
+ raise TMRowAlreadyExists('A build source with name "%s" already exist.' % (oData.sName,));
+ return True;
+
+
+ def _historizeBuildSource(self, idBuildSrc, tsExpire = None):
+ """ Historizes the current build source entry. """
+ if tsExpire is None:
+ self._oDb.execute('UPDATE BuildSources\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idBuildSrc = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( idBuildSrc, ));
+ else:
+ self._oDb.execute('UPDATE BuildSources\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idBuildSrc = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( tsExpire, idBuildSrc, ));
+ return True;
+
+
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class BuildSourceDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [BuildSourceData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/coreconsts.py b/src/VBox/ValidationKit/testmanager/core/coreconsts.py
new file mode 100644
index 00000000..bbdc7712
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/coreconsts.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+# $Id: coreconsts.py $
+
+"""
+Test Manager - Test Manager Constants (without a more appropriate home).
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+## OS agnostic.
+g_ksOsAgnostic = 'os-agnostic';
+## All known OSes, except the agnostic one.
+# See KBUILD_OSES in kBuild/header.kmk for reference.
+g_kasOses = ['darwin', 'dos', 'dragonfly', 'freebsd', 'haiku', 'l4', 'linux', 'netbsd', 'nt', 'openbsd', 'os2',
+ 'solaris', 'win'];
+## All known OSes, including the agnostic one.
+# See KBUILD_OSES in kBuild/header.kmk for reference.
+g_kasOsesAll = g_kasOses + [g_ksOsAgnostic,];
+
+
+## Architecture agnostic.
+g_ksCpuArchAgnostic = 'noarch';
+## All known CPU architectures, except the agnostic one.
+# See KBUILD_ARCHES in kBuild/header.kmk for reference.
+g_kasCpuArches = ['amd64', 'x86', 'sparc32', 'sparc64', 's390', 's390x', 'ppc32', 'ppc64', 'mips32', 'mips64', 'ia64',
+ 'hppa32', 'hppa64', 'arm', 'alpha'];
+## All known CPU architectures, except the agnostic one.
+# See KBUILD_ARCHES in kBuild/header.kmk for reference.
+g_kasCpuArchesAll = g_kasCpuArches + [g_ksCpuArchAgnostic,];
+
+## All known build types
+# See KBUILD_TYPE in kBuild/header.kmk for reference.
+# @note 'blessed' is a special type used for release builds that has been notarized
+# or attestation signed by the OS vendor.
+g_kasBuildTypesAll = [ 'release', 'strict', 'profile', 'debug', 'asan', 'blessed' ];
+
+## OS and CPU architecture agnostic.
+g_ksOsDotArchAgnostic = 'os-agnostic.noarch';
+## Combinations of all OSes and CPU architectures, except the two agnostic ones.
+# We do some of them by hand to avoid offering too many choices.
+g_kasOsDotCpus = \
+[
+ 'darwin.amd64', 'darwin.x86', 'darwin.ppc32', 'darwin.ppc64', 'darwin.arm',
+ 'dos.x86',
+ 'dragonfly.amd64', 'dragonfly.x86',
+ 'freebsd.amd64', 'freebsd.x86', 'freebsd.sparc64', 'freebsd.ia64', 'freebsd.ppc32', 'freebsd.ppc64', 'freebsd.arm',
+ 'freebsd.mips32', 'freebsd.mips64',
+ 'haiku.amd64', 'haiku.x86',
+ 'l4.amd64', 'l4.x86', 'l4.ppc32', 'l4.ppc64', 'l4.arm',
+ 'nt.amd64', 'nt.x86', 'nt.arm', 'nt.ia64', 'nt.mips32', 'nt.ppc32', 'nt.alpha',
+ 'win.amd64', 'win.x86', 'win.arm', 'win.ia64', 'win.mips32', 'win.ppc32', 'win.alpha',
+ 'os2.x86',
+ 'solaris.amd64', 'solaris.x86', 'solaris.sparc32', 'solaris.sparc64',
+];
+for sOs in g_kasOses:
+ if sOs not in ['darwin', 'dos', 'dragonfly', 'freebsd', 'haiku', 'l4', 'nt', 'win', 'os2', 'solaris']:
+ for sArch in g_kasCpuArches:
+ g_kasOsDotCpus.append(sOs + '.' + sArch);
+g_kasOsDotCpus.sort();
+
+## Combinations of all OSes and CPU architectures, including the two agnostic ones.
+g_kasOsDotCpusAll = [g_ksOsDotArchAgnostic]
+g_kasOsDotCpusAll.extend(g_kasOsDotCpus);
+for sOs in g_kasOsesAll:
+ g_kasOsDotCpusAll.append(sOs + '.' + g_ksCpuArchAgnostic);
+for sArch in g_kasCpuArchesAll:
+ g_kasOsDotCpusAll.append(g_ksOsAgnostic + '.' + sArch);
+g_kasOsDotCpusAll.sort();
+
diff --git a/src/VBox/ValidationKit/testmanager/core/db.py b/src/VBox/ValidationKit/testmanager/core/db.py
new file mode 100755
index 00000000..915336c3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/db.py
@@ -0,0 +1,745 @@
+# -*- coding: utf-8 -*-
+# $Id: db.py $
+
+"""
+Test Manager - Database Interface.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import datetime;
+import os;
+import sys;
+import psycopg2; # pylint: disable=import-error
+import psycopg2.extensions; # pylint: disable=import-error
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager import config;
+
+# Fix psycho unicode handling in psycopg2 with python 2.x.
+if sys.version_info[0] < 3:
+ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE);
+ psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY);
+else:
+ unicode = str; # pylint: disable=redefined-builtin,invalid-name
+
+
+
+def isDbTimestampInfinity(tsValue):
+ """
+ Checks if tsValue is an infinity timestamp.
+ """
+ ## @todo improve this test...
+ return tsValue.year >= 9999;
+
+def isDbTimestamp(oValue):
+ """
+ Checks if oValue is a DB timestamp object.
+ """
+ if isinstance(oValue, datetime.datetime):
+ return True;
+ if utils.isString(oValue):
+ ## @todo detect strings as well.
+ return False;
+ return getattr(oValue, 'pydatetime', None) is not None;
+
+def dbTimestampToDatetime(oValue):
+ """
+ Converts a database timestamp to a datetime instance.
+ """
+ if isinstance(oValue, datetime.datetime):
+ return oValue;
+ if utils.isString(oValue):
+ return utils.parseIsoTimestamp(oValue);
+ return oValue.pydatetime();
+
+def dbTimestampToZuluDatetime(oValue):
+ """
+ Converts a database timestamp to a zulu datetime instance.
+ """
+ tsValue = dbTimestampToDatetime(oValue);
+
+ class UTC(datetime.tzinfo):
+ """UTC TZ Info Class"""
+ def utcoffset(self, _):
+ return datetime.timedelta(0);
+ def tzname(self, _):
+ return "UTC";
+ def dst(self, _):
+ return datetime.timedelta(0);
+ if tsValue.tzinfo is not None:
+ tsValue = tsValue.astimezone(UTC());
+ else:
+ tsValue = tsValue.replace(tzinfo=UTC());
+ return tsValue;
+
+def dbTimestampPythonNow():
+ """
+ Gets the current python timestamp in a database compatible way.
+ """
+ return dbTimestampToZuluDatetime(datetime.datetime.utcnow());
+
+def dbOneTickIntervalString():
+ """
+ Returns the interval string for one tick.
+
+ Mogrify the return value into the SQL:
+ "... %s::INTERVAL ..."
+ or
+ "INTERVAL %s"
+ The completed SQL will contain the necessary ticks.
+ """
+ return '1 microsecond';
+
+def dbTimestampMinusOneTick(oValue):
+ """
+ Returns a new timestamp that's one tick before the given one.
+ """
+ oValue = dbTimestampToZuluDatetime(oValue);
+ return oValue - datetime.timedelta(microseconds = 1);
+
+def dbTimestampPlusOneTick(oValue):
+ """
+ Returns a new timestamp that's one tick after the given one.
+ """
+ oValue = dbTimestampToZuluDatetime(oValue);
+ return oValue + datetime.timedelta(microseconds = 1);
+
+def isDbInterval(oValue):
+ """
+ Checks if oValue is a DB interval object.
+ """
+ if isinstance(oValue, datetime.timedelta):
+ return True;
+ return False;
+
+
+class TMDatabaseIntegrityException(Exception):
+ """
+ Herolds a database integrity error up the callstack.
+
+ Do NOT use directly, only thru TMDatabaseConnection.integrityException.
+ Otherwise, we won't be able to log the issue.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TMDatabaseCursor(object):
+ """ Cursor wrapper class. """
+
+ def __init__(self, oDb, oCursor):
+ self._oDb = oDb;
+ self._oCursor = oCursor;
+
+ def execute(self, sOperation, aoArgs = None):
+ """ See TMDatabaseConnection.execute()"""
+ return self._oDb.executeInternal(self._oCursor, sOperation, aoArgs, utils.getCallerName());
+
+ def callProc(self, sProcedure, aoArgs = None):
+ """ See TMDatabaseConnection.callProc()"""
+ return self._oDb.callProcInternal(self._oCursor, sProcedure, aoArgs, utils.getCallerName());
+
+ def insertList(self, sInsertSql, aoList, fnEntryFmt):
+ """ See TMDatabaseConnection.insertList. """
+ return self._oDb.insertListInternal(self._oCursor, sInsertSql, aoList, fnEntryFmt, utils.getCallerName());
+
+ def fetchOne(self):
+ """Wrapper around Psycopg2.cursor.fetchone."""
+ return self._oCursor.fetchone();
+
+ def fetchMany(self, cRows = None):
+ """Wrapper around Psycopg2.cursor.fetchmany."""
+ return self._oCursor.fetchmany(cRows if cRows is not None else self._oCursor.arraysize);
+
+ def fetchAll(self):
+ """Wrapper around Psycopg2.cursor.fetchall."""
+ return self._oCursor.fetchall();
+
+ def getRowCount(self):
+ """Wrapper around Psycopg2.cursor.rowcount."""
+ return self._oCursor.rowcount;
+
+ def formatBindArgs(self, sStatement, aoArgs):
+ """Wrapper around Psycopg2.cursor.mogrify."""
+ oRet = self._oCursor.mogrify(sStatement, aoArgs);
+ if sys.version_info[0] >= 3 and not isinstance(oRet, str):
+ oRet = oRet.decode('utf-8');
+ return oRet;
+
+ def copyExpert(self, sSqlCopyStmt, oFile, cbBuf = 8192):
+ """ See TMDatabaseConnection.copyExpert()"""
+ return self._oCursor.copy_expert(sSqlCopyStmt, oFile, cbBuf);
+
+ @staticmethod
+ def isTsInfinity(tsValue):
+ """ Checks if tsValue is an infinity timestamp. """
+ return isDbTimestampInfinity(tsValue);
+
+
+class TMDatabaseConnection(object):
+ """
+ Test Manager Database Access class.
+
+ This class contains no logic, just raw access abstraction and utilities,
+ as well as some debug help and some statistics.
+ """
+
+ def __init__(self, fnDPrint = None, oSrvGlue = None):
+ """
+ Database connection wrapper.
+ The fnDPrint is for debug logging of all database activity.
+
+ Raises an exception on failure.
+ """
+
+ sAppName = '%s-%s' % (os.getpid(), os.path.basename(sys.argv[0]),)
+ if len(sAppName) >= 64:
+ sAppName = sAppName[:64];
+ os.environ['PGAPPNAME'] = sAppName;
+
+ dArgs = \
+ { \
+ 'database': config.g_ksDatabaseName,
+ 'user': config.g_ksDatabaseUser,
+ 'password': config.g_ksDatabasePassword,
+ # 'application_name': sAppName, - Darn stale debian! :/
+ };
+ if config.g_ksDatabaseAddress is not None:
+ dArgs['host'] = config.g_ksDatabaseAddress;
+ if config.g_ksDatabasePort is not None:
+ dArgs['port'] = config.g_ksDatabasePort;
+ self._oConn = psycopg2.connect(**dArgs); # pylint: disable=star-args
+ self._oConn.set_client_encoding('UTF-8');
+ self._oCursor = self._oConn.cursor();
+ self._oExplainConn = None;
+ self._oExplainCursor = None;
+ if config.g_kfWebUiSqlTraceExplain and config.g_kfWebUiSqlTrace:
+ self._oExplainConn = psycopg2.connect(**dArgs); # pylint: disable=star-args
+ self._oExplainConn.set_client_encoding('UTF-8');
+ self._oExplainCursor = self._oExplainConn.cursor();
+ self._fTransaction = False;
+ self._tsCurrent = None;
+ self._tsCurrentMinusOne = None;
+
+ assert self.isAutoCommitting() is False;
+
+ # Debug and introspection.
+ self._fnDPrint = fnDPrint;
+ self._aoTraceBack = [];
+
+ # Exception class handles.
+ self.oXcptError = psycopg2.Error;
+
+ if oSrvGlue is not None:
+ oSrvGlue.registerDebugInfoCallback(self.debugInfoCallback);
+
+ # Object caches (used by database logic classes).
+ self.ddCaches = {};
+
+ def isAutoCommitting(self):
+ """ Work around missing autocommit attribute in older versions."""
+ return getattr(self._oConn, 'autocommit', False);
+
+ def close(self):
+ """
+ Closes the connection and renders all cursors useless.
+ """
+ if self._oCursor is not None:
+ self._oCursor.close();
+ self._oCursor = None;
+
+ if self._oConn is not None:
+ self._oConn.close();
+ self._oConn = None;
+
+ if self._oExplainCursor is not None:
+ self._oExplainCursor.close();
+ self._oExplainCursor = None;
+
+ if self._oExplainConn is not None:
+ self._oExplainConn.close();
+ self._oExplainConn = None;
+
+
+ def _startedTransaction(self):
+ """
+ Called to work the _fTransaction and related variables when starting
+ a transaction.
+ """
+ self._fTransaction = True;
+ self._tsCurrent = None;
+ self._tsCurrentMinusOne = None;
+ return None;
+
+ def _endedTransaction(self):
+ """
+ Called to work the _fTransaction and related variables when ending
+ a transaction.
+ """
+ self._fTransaction = False;
+ self._tsCurrent = None;
+ self._tsCurrentMinusOne = None;
+ return None;
+
+ def begin(self):
+ """
+ Currently just for marking where a transaction starts in the code.
+ """
+ assert self._oConn is not None;
+ assert self.isAutoCommitting() is False;
+ self._aoTraceBack.append([utils.timestampNano(), 'START TRANSACTION', 0, 0, utils.getCallerName(), None]);
+ self._startedTransaction();
+ return True;
+
+ def commit(self, sCallerName = None):
+ """ Wrapper around Psycopg2.connection.commit."""
+ assert self._fTransaction is True;
+
+ nsStart = utils.timestampNano();
+ oRc = self._oConn.commit();
+ cNsElapsed = utils.timestampNano() - nsStart;
+
+ if sCallerName is None:
+ sCallerName = utils.getCallerName();
+ self._aoTraceBack.append([nsStart, 'COMMIT', cNsElapsed, 0, sCallerName, None]);
+ self._endedTransaction();
+ return oRc;
+
+ def maybeCommit(self, fCommit):
+ """
+ Commits if fCommit is True.
+ Returns True if committed, False if not.
+ """
+ if fCommit is True:
+ self.commit(utils.getCallerName());
+ return True;
+ return False;
+
+ def rollback(self):
+ """ Wrapper around Psycopg2.connection.rollback."""
+ nsStart = utils.timestampNano();
+ oRc = self._oConn.rollback();
+ cNsElapsed = utils.timestampNano() - nsStart;
+
+ self._aoTraceBack.append([nsStart, 'ROLLBACK', cNsElapsed, 0, utils.getCallerName(), None]);
+ self._endedTransaction();
+ return oRc;
+
+ #
+ # Internal cursor workers.
+ #
+
+ def executeInternal(self, oCursor, sOperation, aoArgs, sCallerName):
+ """
+ Execute a query or command.
+
+ Mostly a wrapper around the psycopg2 cursor method with the same name,
+ but collect data for traceback.
+ """
+ if aoArgs is not None:
+ sBound = oCursor.mogrify(unicode(sOperation), aoArgs);
+ elif sOperation.find('%') < 0:
+ sBound = oCursor.mogrify(unicode(sOperation), []);
+ else:
+ sBound = unicode(sOperation);
+
+ if sys.version_info[0] >= 3 and not isinstance(sBound, str):
+ sBound = sBound.decode('utf-8'); # pylint: disable=redefined-variable-type
+
+ aasExplain = None;
+ if self._oExplainCursor is not None and not sBound.startswith('DROP'):
+ try:
+ if config.g_kfWebUiSqlTraceExplainTiming:
+ self._oExplainCursor.execute('EXPLAIN (ANALYZE, BUFFERS, COSTS, VERBOSE, TIMING) ' + sBound);
+ else:
+ self._oExplainCursor.execute('EXPLAIN (ANALYZE, BUFFERS, COSTS, VERBOSE) ' + sBound);
+ except Exception as oXcpt:
+ aasExplain = [ ['Explain exception: '], [str(oXcpt)]];
+ try: self._oExplainConn.rollback();
+ except: pass;
+ else:
+ aasExplain = self._oExplainCursor.fetchall();
+
+ nsStart = utils.timestampNano();
+ try:
+ oRc = oCursor.execute(sBound);
+ except Exception as oXcpt:
+ cNsElapsed = utils.timestampNano() - nsStart;
+ self._aoTraceBack.append([nsStart, 'oXcpt=%s; Statement: %s' % (oXcpt, sBound), cNsElapsed, 0, sCallerName, None]);
+ if self._fnDPrint is not None:
+ self._fnDPrint('db::execute %u ns, caller %s: oXcpt=%s; Statement: %s'
+ % (cNsElapsed, sCallerName, oXcpt, sBound));
+ raise;
+ cNsElapsed = utils.timestampNano() - nsStart;
+
+ if self._fTransaction is False and not self.isAutoCommitting(): # Even SELECTs starts transactions with psycopg2, see FAQ.
+ self._aoTraceBack.append([nsStart, '[START TRANSACTION]', 0, 0, sCallerName, None]);
+ self._startedTransaction();
+ self._aoTraceBack.append([nsStart, sBound, cNsElapsed, oCursor.rowcount, sCallerName, aasExplain]);
+ if self._fnDPrint is not None:
+ self._fnDPrint('db::execute %u ns, caller %s: "\n%s"' % (cNsElapsed, sCallerName, sBound));
+ if self.isAutoCommitting():
+ self._aoTraceBack.append([nsStart, '[AUTO COMMIT]', 0, 0, sCallerName, None]);
+
+ return oRc;
+
+ def callProcInternal(self, oCursor, sProcedure, aoArgs, sCallerName):
+ """
+ Call a stored procedure.
+
+ Mostly a wrapper around the psycopg2 cursor method 'callproc', but
+ collect data for traceback.
+ """
+ if aoArgs is None:
+ aoArgs = [];
+
+ nsStart = utils.timestampNano();
+ try:
+ oRc = oCursor.callproc(sProcedure, aoArgs);
+ except Exception as oXcpt:
+ cNsElapsed = utils.timestampNano() - nsStart;
+ self._aoTraceBack.append([nsStart, 'oXcpt=%s; Calling: %s(%s)' % (oXcpt, sProcedure, aoArgs),
+ cNsElapsed, 0, sCallerName, None]);
+ if self._fnDPrint is not None:
+ self._fnDPrint('db::callproc %u ns, caller %s: oXcpt=%s; Calling: %s(%s)'
+ % (cNsElapsed, sCallerName, oXcpt, sProcedure, aoArgs));
+ raise;
+ cNsElapsed = utils.timestampNano() - nsStart;
+
+ if self._fTransaction is False and not self.isAutoCommitting(): # Even SELECTs starts transactions with psycopg2, see FAQ.
+ self._aoTraceBack.append([nsStart, '[START TRANSACTION]', 0, 0, sCallerName, None]);
+ self._startedTransaction();
+ self._aoTraceBack.append([nsStart, '%s(%s)' % (sProcedure, aoArgs), cNsElapsed, oCursor.rowcount, sCallerName, None]);
+ if self._fnDPrint is not None:
+ self._fnDPrint('db::callproc %u ns, caller %s: "%s(%s)"' % (cNsElapsed, sCallerName, sProcedure, aoArgs));
+ if self.isAutoCommitting():
+ self._aoTraceBack.append([nsStart, '[AUTO COMMIT]', 0, 0, sCallerName, sCallerName, None]);
+
+ return oRc;
+
+ def insertListInternal(self, oCursor, sInsertSql, aoList, fnEntryFmt, sCallerName):
+ """
+ Optimizes the insertion of a list of values.
+ """
+ oRc = None;
+ asValues = [];
+ for aoEntry in aoList:
+ asValues.append(fnEntryFmt(aoEntry));
+ if len(asValues) > 256:
+ oRc = self.executeInternal(oCursor, sInsertSql + 'VALUES' + ', '.join(asValues), None, sCallerName);
+ asValues = [];
+ if asValues:
+ oRc = self.executeInternal(oCursor, sInsertSql + 'VALUES' + ', '.join(asValues), None, sCallerName);
+ return oRc
+
+ def _fetchOne(self, oCursor):
+ """Wrapper around Psycopg2.cursor.fetchone."""
+ oRow = oCursor.fetchone()
+ if self._fnDPrint is not None:
+ self._fnDPrint('db:fetchOne returns: %s' % (oRow,));
+ return oRow;
+
+ def _fetchMany(self, oCursor, cRows):
+ """Wrapper around Psycopg2.cursor.fetchmany."""
+ return oCursor.fetchmany(cRows if cRows is not None else oCursor.arraysize);
+
+ def _fetchAll(self, oCursor):
+ """Wrapper around Psycopg2.cursor.fetchall."""
+ return oCursor.fetchall()
+
+ def _getRowCountWorker(self, oCursor):
+ """Wrapper around Psycopg2.cursor.rowcount."""
+ return oCursor.rowcount;
+
+
+ #
+ # Default cursor access.
+ #
+
+ def execute(self, sOperation, aoArgs = None):
+ """
+ Execute a query or command.
+
+ Mostly a wrapper around the psycopg2 cursor method with the same name,
+ but collect data for traceback.
+ """
+ return self.executeInternal(self._oCursor, sOperation, aoArgs, utils.getCallerName());
+
+ def callProc(self, sProcedure, aoArgs = None):
+ """
+ Call a stored procedure.
+
+ Mostly a wrapper around the psycopg2 cursor method 'callproc', but
+ collect data for traceback.
+ """
+ return self.callProcInternal(self._oCursor, sProcedure, aoArgs, utils.getCallerName());
+
+ def insertList(self, sInsertSql, aoList, fnEntryFmt):
+ """
+ Optimizes the insertion of a list of values.
+ """
+ return self.insertListInternal(self._oCursor, sInsertSql, aoList, fnEntryFmt, utils.getCallerName());
+
+ def fetchOne(self):
+ """Wrapper around Psycopg2.cursor.fetchone."""
+ return self._oCursor.fetchone();
+
+ def fetchMany(self, cRows = None):
+ """Wrapper around Psycopg2.cursor.fetchmany."""
+ return self._oCursor.fetchmany(cRows if cRows is not None else self._oCursor.arraysize);
+
+ def fetchAll(self):
+ """Wrapper around Psycopg2.cursor.fetchall."""
+ return self._oCursor.fetchall();
+
+ def getRowCount(self):
+ """Wrapper around Psycopg2.cursor.rowcount."""
+ return self._oCursor.rowcount;
+
+ def formatBindArgs(self, sStatement, aoArgs):
+ """Wrapper around Psycopg2.cursor.mogrify."""
+ oRet = self._oCursor.mogrify(sStatement, aoArgs);
+ if sys.version_info[0] >= 3 and not isinstance(oRet, str):
+ oRet = oRet.decode('utf-8');
+ return oRet;
+
+ def copyExpert(self, sSqlCopyStmt, oFile, cbBuf = 8192):
+ """ Wrapper around Psycopg2.cursor.copy_expert. """
+ return self._oCursor.copy_expert(sSqlCopyStmt, oFile, cbBuf);
+
+ def getCurrentTimestamps(self):
+ """
+ Returns the current timestamp and the current timestamp minus one tick.
+ This will start a transaction if necessary.
+ """
+ if self._tsCurrent is None:
+ self.execute('SELECT CURRENT_TIMESTAMP, CURRENT_TIMESTAMP - INTERVAL \'1 microsecond\'');
+ (self._tsCurrent, self._tsCurrentMinusOne) = self.fetchOne();
+ return (self._tsCurrent, self._tsCurrentMinusOne);
+
+ def getCurrentTimestamp(self):
+ """
+ Returns the current timestamp.
+ This will start a transaction if necessary.
+ """
+ if self._tsCurrent is None:
+ self.getCurrentTimestamps();
+ return self._tsCurrent;
+
+ def getCurrentTimestampMinusOne(self):
+ """
+ Returns the current timestamp minus one tick.
+ This will start a transaction if necessary.
+ """
+ if self._tsCurrentMinusOne is None:
+ self.getCurrentTimestamps();
+ return self._tsCurrentMinusOne;
+
+
+ #
+ # Additional cursors.
+ #
+ def openCursor(self):
+ """
+ Opens a new cursor (TMDatabaseCursor).
+ """
+ oCursor = self._oConn.cursor();
+ return TMDatabaseCursor(self, oCursor);
+
+ #
+ # Cache support.
+ #
+ def getCache(self, sType):
+ """ Returns the cache dictionary for this data type. """
+ dRet = self.ddCaches.get(sType, None);
+ if dRet is None:
+ dRet = {};
+ self.ddCaches[sType] = dRet;
+ return dRet;
+
+
+ #
+ # Utilities.
+ #
+
+ @staticmethod
+ def isTsInfinity(tsValue):
+ """ Checks if tsValue is an infinity timestamp. """
+ return isDbTimestampInfinity(tsValue);
+
+ #
+ # Error stuff.
+ #
+ def integrityException(self, sMessage):
+ """
+ Database integrity reporter and exception factory.
+ Returns an TMDatabaseIntegrityException which the caller can raise.
+ """
+ ## @todo Create a new database connection and log the issue in the SystemLog table.
+ ## Alternatively, rollback whatever is going on and do it using the current one.
+ return TMDatabaseIntegrityException(sMessage);
+
+
+ #
+ # Debugging.
+ #
+
+ def dprint(self, sText):
+ """
+ Debug output.
+ """
+ if not self._fnDPrint:
+ return False;
+ self._fnDPrint(sText);
+ return True;
+
+ def debugHtmlReport(self, tsStart = 0):
+ """
+ Used to get a SQL activity dump as HTML, usually for WuiBase._sDebug.
+ """
+ cNsElapsed = 0;
+ for aEntry in self._aoTraceBack:
+ cNsElapsed += aEntry[2];
+
+ sDebug = '<h3>SQL Debug Log (total time %s ns):</h3>\n' \
+ '<table class="tmsqltable">\n' \
+ ' <tr>\n' \
+ ' <th>No.</th>\n' \
+ ' <th>Timestamp (ns)</th>\n' \
+ ' <th>Elapsed (ns)</th>\n' \
+ ' <th>Rows Returned</th>\n' \
+ ' <th>Command</th>\n' \
+ ' <th>Caller</th>\n' \
+ ' </tr>\n' \
+ % (utils.formatNumber(cNsElapsed, '&nbsp;'),);
+
+ iEntry = 0;
+ for aEntry in self._aoTraceBack:
+ iEntry += 1;
+ sDebug += ' <tr>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td><pre>%s</pre></td>\n' \
+ ' <td>%s</td>\n' \
+ ' </tr>\n' \
+ % (iEntry,
+ utils.formatNumber(aEntry[0] - tsStart, '&nbsp;'),
+ utils.formatNumber(aEntry[2], '&nbsp;'),
+ utils.formatNumber(aEntry[3], '&nbsp;'),
+ webutils.escapeElem(aEntry[1]),
+ webutils.escapeElem(aEntry[4]),
+ );
+ if aEntry[5] is not None:
+ sDebug += ' <tr>\n' \
+ ' <td colspan="6"><pre style="white-space: pre-wrap;">%s</pre></td>\n' \
+ ' </tr>\n' \
+ % (webutils.escapeElem('\n'.join([aoRow[0] for aoRow in aEntry[5]])),);
+
+ sDebug += '</table>';
+ return sDebug;
+
+ def debugTextReport(self, tsStart = 0):
+ """
+ Used to get a SQL activity dump as text.
+ """
+ cNsElapsed = 0;
+ for aEntry in self._aoTraceBack:
+ cNsElapsed += aEntry[2];
+
+ sHdr = 'SQL Debug Log (total time %s ns)' % (utils.formatNumber(cNsElapsed),);
+ sDebug = sHdr + '\n' + '-' * len(sHdr) + '\n';
+
+ iEntry = 0;
+ for aEntry in self._aoTraceBack:
+ iEntry += 1;
+ sHdr = 'Query #%s Timestamp: %s ns Elapsed: %s ns Rows: %s Caller: %s' \
+ % ( iEntry,
+ utils.formatNumber(aEntry[0] - tsStart),
+ utils.formatNumber(aEntry[2]),
+ utils.formatNumber(aEntry[3]),
+ aEntry[4], );
+ sDebug += '\n' + sHdr + '\n' + '-' * len(sHdr) + '\n';
+
+ sDebug += aEntry[1];
+ if sDebug[-1] != '\n':
+ sDebug += '\n';
+
+ if aEntry[5] is not None:
+ sDebug += 'Explain:\n' \
+ ' %s\n' \
+ % ( '\n'.join([aoRow[0] for aoRow in aEntry[5]]),);
+
+ return sDebug;
+
+ def debugInfoCallback(self, oGlue, fHtml):
+ """ Called back by the glue code on error. """
+ oGlue.write('\n');
+ if not fHtml: oGlue.write(self.debugTextReport());
+ else: oGlue.write(self.debugHtmlReport());
+ oGlue.write('\n');
+ return True;
+
+ def debugEnableExplain(self):
+ """ Enabled explain. """
+ if self._oExplainConn is None:
+ dArgs = \
+ { \
+ 'database': config.g_ksDatabaseName,
+ 'user': config.g_ksDatabaseUser,
+ 'password': config.g_ksDatabasePassword,
+ # 'application_name': sAppName, - Darn stale debian! :/
+ };
+ if config.g_ksDatabaseAddress is not None:
+ dArgs['host'] = config.g_ksDatabaseAddress;
+ if config.g_ksDatabasePort is not None:
+ dArgs['port'] = config.g_ksDatabasePort;
+ self._oExplainConn = psycopg2.connect(**dArgs); # pylint: disable=star-args
+ self._oExplainCursor = self._oExplainConn.cursor();
+ return True;
+
+ def debugDisableExplain(self):
+ """ Disables explain. """
+ self._oExplainCursor = None;
+ self._oExplainConn = None
+ return True;
+
+ def debugIsExplainEnabled(self):
+ """ Check if explaining of SQL statements is enabled. """
+ return self._oExplainConn is not None;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/dbobjcache.py b/src/VBox/ValidationKit/testmanager/core/dbobjcache.py
new file mode 100755
index 00000000..b059dccd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/dbobjcache.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+# $Id: dbobjcache.py $
+
+"""
+Test Manager - Database object cache.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.base import ModelLogicBase;
+
+
+class DatabaseObjCache(ModelLogicBase):
+ """
+ Database object cache.
+
+ This is mainly for reports and test results where we wish to get further
+ information on a data series or similar. The cache should reduce database
+ lookups as well as pyhon memory footprint.
+
+ Note! Dependecies are imported when needed to avoid potential cylic dependency issues.
+ """
+
+ ## @name Cache object types.
+ ## @{
+ ksObjType_TestResultStrTab_idStrName = 0;
+ ksObjType_BuildCategory_idBuildCategory = 1;
+ ksObjType_TestBox_idTestBox = 2;
+ ksObjType_TestBox_idGenTestBox = 3;
+ ksObjType_TestCase_idTestCase = 4;
+ ksObjType_TestCase_idGenTestCase = 5;
+ ksObjType_TestCaseArgs_idTestCaseArgs = 6;
+ ksObjType_TestCaseArgs_idGenTestCaseArgs = 7;
+ ksObjType_VcsRevision_sRepository_iRevision = 8;
+ ksObjType_End = 9;
+ ## @}
+
+ def __init__(self, oDb, tsNow = None, sPeriodBack = None, cHoursBack = None):
+ ModelLogicBase.__init__(self, oDb);
+
+ self.tsNow = tsNow;
+ self.sPeriodBack = sPeriodBack;
+ if sPeriodBack is None and cHoursBack is not None:
+ self.sPeriodBack = '%u hours' % cHoursBack;
+
+ self._adCache = (
+ {}, {}, {}, {},
+ {}, {}, {}, {},
+ {},
+ );
+ assert(len(self._adCache) == self.ksObjType_End);
+
+ def _handleDbException(self):
+ """ Deals with database exceptions. """
+ #self._oDb.rollback();
+ return False;
+
+ def getTestResultString(self, idStrName):
+ """ Gets a string from the TestResultStrTab. """
+ sRet = self._adCache[self.ksObjType_TestResultStrTab_idStrName].get(idStrName);
+ if sRet is None:
+ # Load cache entry.
+ self._oDb.execute('SELECT sValue FROM TestResultStrTab WHERE idStr = %s', (idStrName,));
+ sRet = self._oDb.fetchOne()[0];
+ self._adCache[self.ksObjType_TestResultStrTab_idStrName][idStrName] = sRet
+ return sRet;
+
+ def getBuildCategory(self, idBuildCategory):
+ """ Gets the corresponding BuildCategoryData object. """
+ oRet = self._adCache[self.ksObjType_BuildCategory_idBuildCategory].get(idBuildCategory);
+ if oRet is None:
+ # Load cache entry.
+ from testmanager.core.build import BuildCategoryData;
+ oRet = BuildCategoryData();
+ try: oRet.initFromDbWithId(self._oDb, idBuildCategory);
+ except: self._handleDbException(); raise;
+ self._adCache[self.ksObjType_BuildCategory_idBuildCategory][idBuildCategory] = oRet;
+ return oRet;
+
+ def getTestBox(self, idTestBox):
+ """ Gets the corresponding TestBoxData object. """
+ oRet = self._adCache[self.ksObjType_TestBox_idTestBox].get(idTestBox);
+ if oRet is None:
+ # Load cache entry.
+ from testmanager.core.testbox import TestBoxData;
+ oRet = TestBoxData();
+ try: oRet.initFromDbWithId(self._oDb, idTestBox, self.tsNow, self.sPeriodBack);
+ except: self._handleDbException(); raise;
+ else: self._adCache[self.ksObjType_TestBox_idGenTestBox][oRet.idGenTestBox] = oRet;
+ self._adCache[self.ksObjType_TestBox_idTestBox][idTestBox] = oRet;
+ return oRet;
+
+ def getTestCase(self, idTestCase):
+ """ Gets the corresponding TestCaseData object. """
+ oRet = self._adCache[self.ksObjType_TestCase_idTestCase].get(idTestCase);
+ if oRet is None:
+ # Load cache entry.
+ from testmanager.core.testcase import TestCaseData;
+ oRet = TestCaseData();
+ try: oRet.initFromDbWithId(self._oDb, idTestCase, self.tsNow, self.sPeriodBack);
+ except: self._handleDbException(); raise;
+ else: self._adCache[self.ksObjType_TestCase_idGenTestCase][oRet.idGenTestCase] = oRet;
+ self._adCache[self.ksObjType_TestCase_idTestCase][idTestCase] = oRet;
+ return oRet;
+
+ def getTestCaseArgs(self, idTestCaseArgs):
+ """ Gets the corresponding TestCaseArgsData object. """
+ oRet = self._adCache[self.ksObjType_TestCaseArgs_idTestCaseArgs].get(idTestCaseArgs);
+ if oRet is None:
+ # Load cache entry.
+ from testmanager.core.testcaseargs import TestCaseArgsData;
+ oRet = TestCaseArgsData();
+ try: oRet.initFromDbWithId(self._oDb, idTestCaseArgs, self.tsNow, self.sPeriodBack);
+ except: self._handleDbException(); raise;
+ else: self._adCache[self.ksObjType_TestCaseArgs_idGenTestCaseArgs][oRet.idGenTestCaseArgs] = oRet;
+ self._adCache[self.ksObjType_TestCaseArgs_idTestCaseArgs][idTestCaseArgs] = oRet;
+ return oRet;
+
+ def preloadVcsRevInfo(self, sRepository, aiRevisions):
+ """
+ Preloads VCS revision information.
+ ASSUMES aiRevisions does not contain duplicate keys.
+ """
+ from testmanager.core.vcsrevisions import VcsRevisionData;
+ dRepo = self._adCache[self.ksObjType_VcsRevision_sRepository_iRevision].get(sRepository);
+ if dRepo is None:
+ dRepo = {};
+ self._adCache[self.ksObjType_VcsRevision_sRepository_iRevision][sRepository] = dRepo;
+ aiFiltered = aiRevisions;
+ else:
+ aiFiltered = [];
+ for iRevision in aiRevisions:
+ if iRevision not in dRepo:
+ aiFiltered.append(iRevision);
+ if aiFiltered:
+ self._oDb.execute('SELECT *\n'
+ 'FROM VcsRevisions\n'
+ 'WHERE sRepository = %s\n'
+ ' AND iRevision IN (' + ','.join([str(i) for i in aiFiltered]) + ')'
+ , ( sRepository, ));
+ for aoRow in self._oDb.fetchAll():
+ oInfo = VcsRevisionData().initFromDbRow(aoRow);
+ dRepo[oInfo.iRevision] = oInfo;
+ return True;
+
+ def getVcsRevInfo(self, sRepository, iRevision):
+ """
+ Gets the corresponding VcsRevisionData object.
+ May return a default (all NULLs) VcsRevisionData object if the revision
+ information isn't available in the database yet.
+ """
+ dRepo = self._adCache[self.ksObjType_VcsRevision_sRepository_iRevision].get(sRepository);
+ if dRepo is not None:
+ oRet = dRepo.get(iRevision);
+ else:
+ dRepo = {};
+ self._adCache[self.ksObjType_VcsRevision_sRepository_iRevision][sRepository] = dRepo;
+ oRet = None;
+ if oRet is None:
+ from testmanager.core.vcsrevisions import VcsRevisionLogic;
+ oRet = VcsRevisionLogic(self._oDb).tryFetch(sRepository, iRevision);
+ if oRet is None:
+ from testmanager.core.vcsrevisions import VcsRevisionData;
+ oRet = VcsRevisionData();
+ dRepo[iRevision] = oRet;
+ return oRet;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/failurecategory.py b/src/VBox/ValidationKit/testmanager/core/failurecategory.py
new file mode 100755
index 00000000..3da0f84b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/failurecategory.py
@@ -0,0 +1,392 @@
+# -*- coding: utf-8 -*-
+# $Id: failurecategory.py $
+
+"""
+Test Manager - Failure Categories.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import sys;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelLogicBase, TMRowInUse, TMInvalidData, TMRowNotFound, \
+ ChangeLogEntry, AttributeChangeEntry;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class FailureCategoryData(ModelDataBase):
+ """
+ Failure Category Data.
+ """
+
+ ksIdAttr = 'idFailureCategory';
+
+ ksParam_idFailureCategory = 'FailureCategory_idFailureCategory'
+ ksParam_tsEffective = 'FailureCategory_tsEffective'
+ ksParam_tsExpire = 'FailureCategory_tsExpire'
+ ksParam_uidAuthor = 'FailureCategory_uidAuthor'
+ ksParam_sShort = 'FailureCategory_sShort'
+ ksParam_sFull = 'FailureCategory_sFull'
+
+ kasAllowNullAttributes = [ 'idFailureCategory', 'tsEffective', 'tsExpire', 'uidAuthor' ]
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+
+ self.idFailureCategory = None
+ self.tsEffective = None
+ self.tsExpire = None
+ self.uidAuthor = None
+ self.sShort = None
+ self.sFull = None
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a SELECT * FROM FailureCategoryes.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('Failure Category not found.');
+
+ self.idFailureCategory = aoRow[0]
+ self.tsEffective = aoRow[1]
+ self.tsExpire = aoRow[2]
+ self.uidAuthor = aoRow[3]
+ self.sShort = aoRow[4]
+ self.sFull = aoRow[5]
+
+ return self
+
+ def initFromDbWithId(self, oDb, idFailureCategory, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE idFailureCategory = %s\n'
+ , ( idFailureCategory,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idFailureCategory=%s not found (tsNow=%s sPeriodBack=%s)'
+ % (idFailureCategory, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+
+class FailureCategoryLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Failure Category logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches Failure Category records.
+
+ Returns an array (list) of FailureCategoryData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idFailureCategory ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idFailureCategory ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(FailureCategoryData().initFromDbRow(aoRow))
+ return aoRows
+
+
+ def fetchForChangeLog(self, idFailureCategory, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
+ """
+ Fetches change log entries for a failure reason.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ # 1. Get a list of the relevant changes.
+ self._oDb.execute('SELECT * FROM FailureCategories WHERE idFailureCategory = %s AND tsEffective <= %s\n'
+ 'ORDER BY tsEffective DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idFailureCategory, tsNow, cMaxRows + 1, iStart, ));
+ aoRows = [];
+ for aoChange in self._oDb.fetchAll():
+ aoRows.append(FailureCategoryData().initFromDbRow(aoChange));
+
+ # 2. Calculate the changes.
+ aoEntries = [];
+ for i in xrange(0, len(aoRows) - 1):
+ oNew = aoRows[i];
+ oOld = aoRows[i + 1];
+
+ aoChanges = [];
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
+
+ # If we're at the end of the log, add the initial entry.
+ if len(aoRows) <= cMaxRows and aoRows:
+ oNew = aoRows[-1];
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
+
+ return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aoRows) > cMaxRows);
+
+
+ def getFailureCategoriesForCombo(self, tsEffective = None):
+ """
+ Gets the list of Failure Categories for a combo box.
+ Returns an array of (value [idFailureCategory], drop-down-name [sShort],
+ hover-text [sFull]) tuples.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT idFailureCategory, sShort, sFull\n'
+ 'FROM FailureCategories\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sShort')
+ else:
+ self._oDb.execute('SELECT idFailureCategory, sShort, sFull\n'
+ 'FROM FailureCategories\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sShort'
+ , (tsEffective, tsEffective))
+ return self._oDb.fetchAll()
+
+
+ def getById(self, idFailureCategory):
+ """Get Failure Category data by idFailureCategory"""
+
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idFailureCategory = %s;', (idFailureCategory,))
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException(
+ 'Found more than one failure categories with the same credentials. Database structure is corrupted.')
+ try:
+ return FailureCategoryData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add a failure reason category.
+ """
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, FailureCategoryData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ #
+ # Add the record.
+ #
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a failure reason category.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, FailureCategoryData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ oOldData = FailureCategoryData().initFromDbWithId(self._oDb, oData.idFailureCategory);
+
+ #
+ # Update the data that needs updating.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeEntry(oData.idFailureCategory);
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def removeEntry(self, uidAuthor, idFailureCategory, fCascade = False, fCommit = False):
+ """
+ Deletes a failure reason category.
+ """
+ _ = fCascade; # too complicated for now.
+
+ #
+ # Check whether it's being used by other tables and bitch if it is .
+ # We currently do not implement cascading.
+ #
+ self._oDb.execute('SELECT CONCAT(idFailureReason, \' - \', sShort)\n'
+ 'FROM FailureReasons\n'
+ 'WHERE idFailureCategory = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idFailureCategory,));
+ aaoRows = self._oDb.fetchAll();
+ if aaoRows:
+ raise TMRowInUse('Cannot remove failure reason category %u because its being used by: %s'
+ % (idFailureCategory, ', '.join(aoRow[0] for aoRow in aaoRows),));
+
+ #
+ # Do the job.
+ #
+ oData = FailureCategoryData().initFromDbWithId(self._oDb, idFailureCategory);
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oData.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeEntry(idFailureCategory, tsCurMinusOne);
+ self._readdEntry(uidAuthor, oData, tsCurMinusOne);
+ self._historizeEntry(idFailureCategory);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def cachedLookup(self, idFailureCategory):
+ """
+ Looks up the most recent FailureCategoryData object for idFailureCategory
+ via an object cache.
+
+ Returns a shared FailureCategoryData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('FailureCategory');
+
+ oEntry = self.dCache.get(idFailureCategory, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE idFailureCategory = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idFailureCategory, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureCategories\n'
+ 'WHERE idFailureCategory = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idFailureCategory, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idFailureCategory));
+
+ if self._oDb.getRowCount() == 1:
+ oEntry = FailureCategoryData().initFromDbRow(self._oDb.fetchOne());
+ self.dCache[idFailureCategory] = oEntry;
+ return oEntry;
+
+
+ #
+ # Helpers.
+ #
+
+ def _readdEntry(self, uidAuthor, oData, tsEffective = None):
+ """
+ Re-adds the FailureCategories entry. Used by addEntry, editEntry and removeEntry.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO FailureCategories (\n'
+ ' uidAuthor,\n'
+ ' tsEffective,\n'
+ ' idFailureCategory,\n'
+ ' sShort,\n'
+ ' sFull)\n'
+ 'VALUES (%s, %s, '
+ + ('DEFAULT' if oData.idFailureCategory is None else str(oData.idFailureCategory))
+ + ', %s, %s)\n'
+ , ( uidAuthor,
+ tsEffective,
+ oData.sShort,
+ oData.sFull,) );
+ return True;
+
+
+ def _historizeEntry(self, idFailureCategory, tsExpire = None):
+ """ Historizes the current entry. """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE FailureCategories\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idFailureCategory = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (tsExpire, idFailureCategory,));
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/failurereason.py b/src/VBox/ValidationKit/testmanager/core/failurereason.py
new file mode 100755
index 00000000..0d9294b0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/failurereason.py
@@ -0,0 +1,580 @@
+# -*- coding: utf-8 -*-
+# $Id: failurereason.py $
+
+"""
+Test Manager - Failure Reasons.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import sys;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelLogicBase, TMRowNotFound, TMInvalidData, TMRowInUse, \
+ AttributeChangeEntry, ChangeLogEntry;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class FailureReasonData(ModelDataBase):
+ """
+ Failure Reason Data.
+ """
+
+ ksIdAttr = 'idFailureReason';
+
+ ksParam_idFailureReason = 'FailureReasonData_idFailureReason'
+ ksParam_tsEffective = 'FailureReasonData_tsEffective'
+ ksParam_tsExpire = 'FailureReasonData_tsExpire'
+ ksParam_uidAuthor = 'FailureReasonData_uidAuthor'
+ ksParam_idFailureCategory = 'FailureReasonData_idFailureCategory'
+ ksParam_sShort = 'FailureReasonData_sShort'
+ ksParam_sFull = 'FailureReasonData_sFull'
+ ksParam_iTicket = 'FailureReasonData_iTicket'
+ ksParam_asUrls = 'FailureReasonData_asUrls'
+
+ kasAllowNullAttributes = [ 'idFailureReason', 'tsEffective', 'tsExpire',
+ 'uidAuthor', 'iTicket', 'asUrls' ]
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+
+ self.idFailureReason = None
+ self.tsEffective = None
+ self.tsExpire = None
+ self.uidAuthor = None
+ self.idFailureCategory = None
+ self.sShort = None
+ self.sFull = None
+ self.iTicket = None
+ self.asUrls = None
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a SELECT * FROM FailureReasons.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('Failure Reason not found.');
+
+ self.idFailureReason = aoRow[0]
+ self.tsEffective = aoRow[1]
+ self.tsExpire = aoRow[2]
+ self.uidAuthor = aoRow[3]
+ self.idFailureCategory = aoRow[4]
+ self.sShort = aoRow[5]
+ self.sFull = aoRow[6]
+ self.iTicket = aoRow[7]
+ self.asUrls = aoRow[8]
+
+ return self;
+
+ def initFromDbWithId(self, oDb, idFailureReason, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE idFailureReason = %s\n'
+ , ( idFailureReason,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idFailureReason=%s not found (tsNow=%s sPeriodBack=%s)'
+ % (idFailureReason, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+
+class FailureReasonDataEx(FailureReasonData):
+ """
+ Failure Reason Data, extended version that includes the category.
+ """
+
+ def __init__(self):
+ FailureReasonData.__init__(self);
+ self.oCategory = None;
+ self.oAuthor = None;
+
+ def initFromDbRowEx(self, aoRow, oCategoryLogic, oUserAccountLogic):
+ """
+ Re-initializes the data with a row from a SELECT * FROM FailureReasons.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ self.initFromDbRow(aoRow);
+ self.oCategory = oCategoryLogic.cachedLookup(self.idFailureCategory);
+ self.oAuthor = oUserAccountLogic.cachedLookup(self.uidAuthor);
+
+ return self;
+
+
+class FailureReasonLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Failure Reason logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+ self.dCacheNameAndCat = None;
+ self.oCategoryLogic = None;
+ self.oUserAccountLogic = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches Failure Category records.
+
+ Returns an array (list) of FailureReasonDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ self._ensureCachesPresent();
+
+ if tsNow is None:
+ self._oDb.execute('SELECT FailureReasons.*,\n'
+ ' FailureCategories.sShort AS sCategory\n'
+ 'FROM FailureReasons,\n'
+ ' FailureCategories\n'
+ 'WHERE FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND FailureCategories.idFailureCategory = FailureReasons.idFailureCategory\n'
+ ' AND FailureCategories.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sCategory ASC, sShort ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT FailureReasons.*,\n'
+ ' FailureCategories.sShort AS sCategory\n'
+ 'FROM FailureReasons,\n'
+ ' FailureCategories\n'
+ 'WHERE FailureReasons.tsExpire > %s\n'
+ ' AND FailureReasons.tsEffective <= %s\n'
+ ' AND FailureCategories.idFailureCategory = FailureReasons.idFailureCategory\n'
+ ' AND FailureReasons.tsExpire > %s\n'
+ ' AND FailureReasons.tsEffective <= %s\n'
+ 'ORDER BY sCategory ASC, sShort ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(FailureReasonDataEx().initFromDbRowEx(aoRow, self.oCategoryLogic, self.oUserAccountLogic));
+ return aoRows
+
+ def fetchForListingInCategory(self, iStart, cMaxRows, tsNow, idFailureCategory, aiSortColumns = None):
+ """
+ Fetches Failure Category records.
+
+ Returns an array (list) of FailureReasonDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ self._ensureCachesPresent();
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND idFailureCategory = %s\n'
+ 'ORDER BY sShort ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idFailureCategory, cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE idFailureCategory = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sShort ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idFailureCategory, tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(FailureReasonDataEx().initFromDbRowEx(aoRow, self.oCategoryLogic, self.oUserAccountLogic));
+ return aoRows
+
+
+ def fetchForSheriffByNamedCategory(self, sFailureCategory):
+ """
+ Fetches the short names of the reasons in the named category.
+
+ Returns array of strings.
+ Raises exception on error.
+ """
+ self._oDb.execute('SELECT FailureReasons.sShort\n'
+ 'FROM FailureReasons,\n'
+ ' FailureCategories\n'
+ 'WHERE FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND FailureReasons.idFailureCategory = FailureCategories.idFailureCategory\n'
+ ' AND FailureCategories.sShort = %s\n'
+ 'ORDER BY FailureReasons.sShort ASC\n'
+ , ( sFailureCategory,));
+ return [aoRow[0] for aoRow in self._oDb.fetchAll()];
+
+
+ def fetchForCombo(self, sFirstEntry = 'Select a failure reason', tsEffective = None):
+ """
+ Gets the list of Failure Reasons for a combo box.
+ Returns an array of (value [idFailureReason], drop-down-name [sShort],
+ hover-text [sFull]) tuples.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT fr.idFailureReason, CONCAT(fc.sShort, \' / \', fr.sShort) as sComboText, fr.sFull\n'
+ 'FROM FailureReasons fr,\n'
+ ' FailureCategories fc\n'
+ 'WHERE fr.idFailureCategory = fc.idFailureCategory\n'
+ ' AND fr.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND fc.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sComboText')
+ else:
+ self._oDb.execute('SELECT fr.idFailureReason, CONCAT(fc.sShort, \' / \', fr.sShort) as sComboText, fr.sFull\n'
+ 'FROM FailureReasons fr,\n'
+ ' FailureCategories fc\n'
+ 'WHERE fr.idFailureCategory = fc.idFailureCategory\n'
+ ' AND fr.tsExpire > %s\n'
+ ' AND fr.tsEffective <= %s\n'
+ ' AND fc.tsExpire > %s\n'
+ ' AND fc.tsEffective <= %s\n'
+ 'ORDER BY sComboText'
+ , (tsEffective, tsEffective, tsEffective, tsEffective));
+ aoRows = self._oDb.fetchAll();
+ return [(-1, sFirstEntry, '')] + aoRows;
+
+
+ def fetchForChangeLog(self, idFailureReason, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
+ """
+ Fetches change log entries for a failure reason.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+ self._ensureCachesPresent();
+
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ # 1. Get a list of the relevant changes.
+ self._oDb.execute('SELECT * FROM FailureReasons WHERE idFailureReason = %s AND tsEffective <= %s\n'
+ 'ORDER BY tsEffective DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idFailureReason, tsNow, cMaxRows + 1, iStart, ));
+ aoRows = [];
+ for aoChange in self._oDb.fetchAll():
+ aoRows.append(FailureReasonData().initFromDbRow(aoChange));
+
+ # 2. Calculate the changes.
+ aoEntries = [];
+ for i in xrange(0, len(aoRows) - 1):
+ oNew = aoRows[i];
+ oOld = aoRows[i + 1];
+
+ aoChanges = [];
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ if sAttr == 'idFailureCategory':
+ oCat = self.oCategoryLogic.cachedLookup(oOldAttr);
+ if oCat is not None:
+ oOldAttr = '%s (%s)' % (oOldAttr, oCat.sShort, );
+ oCat = self.oCategoryLogic.cachedLookup(oNewAttr);
+ if oCat is not None:
+ oNewAttr = '%s (%s)' % (oNewAttr, oCat.sShort, );
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
+
+ # If we're at the end of the log, add the initial entry.
+ if len(aoRows) <= cMaxRows and aoRows:
+ oNew = aoRows[-1];
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
+
+ return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aoRows) > cMaxRows);
+
+
+ def getById(self, idFailureReason):
+ """Get Failure Reason data by idFailureReason"""
+
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idFailureReason = %s;', (idFailureReason,))
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException(
+ 'Found more than one failure reasons with the same credentials. Database structure is corrupted.')
+ try:
+ return FailureReasonData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add a failure reason.
+ """
+ #
+ # Validate.
+ #
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dErrors:
+ raise TMInvalidData('addEntry invalid input: %s' % (dErrors,));
+
+ #
+ # Add the record.
+ #
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a failure reason.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, FailureReasonData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ oOldData = FailureReasonData().initFromDbWithId(self._oDb, oData.idFailureReason);
+
+ #
+ # Update the data that needs updating.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeEntry(oData.idFailureReason);
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def removeEntry(self, uidAuthor, idFailureReason, fCascade = False, fCommit = False):
+ """
+ Deletes a failure reason.
+ """
+ _ = fCascade; # too complicated for now.
+
+ #
+ # Check whether it's being used by other tables and bitch if it is .
+ # We currently do not implement cascading.
+ #
+ self._oDb.execute('SELECT CONCAT(idBlacklisting, \' - blacklisting\')\n'
+ 'FROM BuildBlacklist\n'
+ 'WHERE idFailureReason = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'UNION\n'
+ 'SELECT CONCAT(idTestResult, \' - test result failure reason\')\n'
+ 'FROM TestResultFailures\n'
+ 'WHERE idFailureReason = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idFailureReason, idFailureReason,));
+ aaoRows = self._oDb.fetchAll();
+ if aaoRows:
+ raise TMRowInUse('Cannot remove failure reason %u because its being used by: %s'
+ % (idFailureReason, ', '.join(aoRow[0] for aoRow in aaoRows),));
+
+ #
+ # Do the job.
+ #
+ oData = FailureReasonData().initFromDbWithId(self._oDb, idFailureReason);
+ assert oData.idFailureReason == idFailureReason;
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oData.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeEntry(idFailureReason, tsCurMinusOne);
+ self._readdEntry(uidAuthor, oData, tsCurMinusOne);
+ self._historizeEntry(idFailureReason);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def cachedLookup(self, idFailureReason):
+ """
+ Looks up the most recent FailureReasonDataEx object for idFailureReason
+ via an object cache.
+
+ Returns a shared FailureReasonData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('FailureReasonDataEx');
+ oEntry = self.dCache.get(idFailureReason, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE idFailureReason = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idFailureReason, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons\n'
+ 'WHERE idFailureReason = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idFailureReason, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idFailureReason));
+
+ if self._oDb.getRowCount() == 1:
+ self._ensureCachesPresent();
+ oEntry = FailureReasonDataEx().initFromDbRowEx(self._oDb.fetchOne(), self.oCategoryLogic,
+ self.oUserAccountLogic);
+ self.dCache[idFailureReason] = oEntry;
+ return oEntry;
+
+
+ def cachedLookupByNameAndCategory(self, sName, sCategory):
+ """
+ Looks up a failure reason by it's name and category.
+
+ Should the request be ambigiuos, we'll return the oldest one.
+
+ Returns a shared FailureReasonData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCacheNameAndCat is None:
+ self.dCacheNameAndCat = self._oDb.getCache('FailureReasonDataEx-By-Name-And-Category');
+ sKey = '%s:::%s' % (sName, sCategory,);
+ oEntry = self.dCacheNameAndCat.get(sKey, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM FailureReasons,\n'
+ ' FailureCategories\n'
+ 'WHERE FailureReasons.sShort = %s\n'
+ ' AND FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND FailureReasons.idFailureCategory = FailureCategories.idFailureCategory '
+ ' AND FailureCategories.sShort = %s\n'
+ ' AND FailureCategories.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY FailureReasons.tsEffective\n'
+ , ( sName, sCategory));
+ if self._oDb.getRowCount() == 0:
+ sLikeSucks = self._oDb.formatBindArgs(
+ 'SELECT *\n'
+ 'FROM FailureReasons,\n'
+ ' FailureCategories\n'
+ 'WHERE ( FailureReasons.sShort ILIKE @@@@@@@! %s !@@@@@@@\n'
+ ' OR FailureReasons.sFull ILIKE @@@@@@@! %s !@@@@@@@)\n'
+ ' AND FailureCategories.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND FailureReasons.idFailureCategory = FailureCategories.idFailureCategory\n'
+ ' AND ( FailureCategories.sShort = %s\n'
+ ' OR FailureCategories.sFull = %s)\n'
+ ' AND FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY FailureReasons.tsEffective\n'
+ , ( sName, sName, sCategory, sCategory ));
+ sLikeSucks = sLikeSucks.replace('LIKE @@@@@@@! \'', 'LIKE \'%').replace('\' !@@@@@@@', '%\'');
+ self._oDb.execute(sLikeSucks);
+ if self._oDb.getRowCount() > 0:
+ self._ensureCachesPresent();
+ oEntry = FailureReasonDataEx().initFromDbRowEx(self._oDb.fetchOne(), self.oCategoryLogic,
+ self.oUserAccountLogic);
+ self.dCacheNameAndCat[sKey] = oEntry;
+ if sName != oEntry.sShort or sCategory != oEntry.oCategory.sShort:
+ sKey2 = '%s:::%s' % (oEntry.sShort, oEntry.oCategory.sShort,);
+ self.dCacheNameAndCat[sKey2] = oEntry;
+ return oEntry;
+
+
+ #
+ # Helpers.
+ #
+
+ def _readdEntry(self, uidAuthor, oData, tsEffective = None):
+ """
+ Re-adds the FailureReasons entry. Used by addEntry, editEntry and removeEntry.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO FailureReasons (\n'
+ ' uidAuthor,\n'
+ ' tsEffective,\n'
+ ' idFailureReason,\n'
+ ' idFailureCategory,\n'
+ ' sShort,\n'
+ ' sFull,\n'
+ ' iTicket,\n'
+ ' asUrls)\n'
+ 'VALUES (%s, %s, '
+ + ( 'DEFAULT' if oData.idFailureReason is None else str(oData.idFailureReason) )
+ + ', %s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ tsEffective,
+ oData.idFailureCategory,
+ oData.sShort,
+ oData.sFull,
+ oData.iTicket,
+ oData.asUrls,) );
+ return True;
+
+
+ def _historizeEntry(self, idFailureReason, tsExpire = None):
+ """ Historizes the current entry. """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE FailureReasons\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idFailureReason = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (tsExpire, idFailureReason,));
+ return True;
+
+
+ def _ensureCachesPresent(self):
+ """ Ensures we've got the cache references resolved. """
+ if self.oCategoryLogic is None:
+ from testmanager.core.failurecategory import FailureCategoryLogic;
+ self.oCategoryLogic = FailureCategoryLogic(self._oDb);
+ if self.oUserAccountLogic is None:
+ self.oUserAccountLogic = UserAccountLogic(self._oDb);
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/globalresource.pgsql b/src/VBox/ValidationKit/testmanager/core/globalresource.pgsql
new file mode 100644
index 00000000..44b5c473
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/globalresource.pgsql
@@ -0,0 +1,118 @@
+-- $Id: globalresource.pgsql $
+--- @file
+-- VBox Test Manager Database Stored Procedures.
+--
+
+--
+-- Copyright (C) 2006-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+-- Args: uidAuthor, sName, sDescription, fEnabled
+CREATE OR REPLACE function add_globalresource(integer, text, text, bool) RETURNS integer AS $$
+ DECLARE
+ _idGlobalRsrc integer;
+ _uidAuthor ALIAS FOR $1;
+ _sName ALIAS FOR $2;
+ _sDescription ALIAS FOR $3;
+ _fEnabled ALIAS FOR $4;
+ BEGIN
+ -- Check if Global Resource name is unique
+ IF EXISTS(SELECT * FROM GlobalResources
+ WHERE sName=_sName AND
+ tsExpire='infinity'::timestamp) THEN
+ RAISE EXCEPTION 'Duplicate Global Resource name';
+ END IF;
+ INSERT INTO GlobalResources (uidAuthor, sName, sDescription, fEnabled)
+ VALUES (_uidAuthor, _sName, _sDescription, _fEnabled) RETURNING idGlobalRsrc INTO _idGlobalRsrc;
+ RETURN _idGlobalRsrc;
+ END;
+$$ LANGUAGE plpgsql;
+
+-- Args: uidAuthor, idGlobalRsrc
+CREATE OR REPLACE function del_globalresource(integer, integer) RETURNS VOID AS $$
+ DECLARE
+ _uidAuthor ALIAS FOR $1;
+ _idGlobalRsrc ALIAS FOR $2;
+ BEGIN
+
+ -- Check if record exist
+ IF NOT EXISTS(SELECT * FROM GlobalResources WHERE idGlobalRsrc=_idGlobalRsrc AND tsExpire='infinity'::timestamp) THEN
+ RAISE EXCEPTION 'Global resource (%) does not exist', _idGlobalRsrc;
+ END IF;
+
+ -- Historize record: GlobalResources
+ UPDATE GlobalResources
+ SET tsExpire=CURRENT_TIMESTAMP,
+ uidAuthor=_uidAuthor
+ WHERE idGlobalRsrc=_idGlobalRsrc AND
+ tsExpire='infinity'::timestamp;
+
+
+ -- Delete record: GlobalResourceStatuses
+ DELETE FROM GlobalResourceStatuses WHERE idGlobalRsrc=_idGlobalRsrc;
+
+ -- Historize record: TestCaseGlobalRsrcDeps
+ UPDATE TestCaseGlobalRsrcDeps
+ SET tsExpire=CURRENT_TIMESTAMP,
+ uidAuthor=_uidAuthor
+ WHERE idGlobalRsrc=_idGlobalRsrc AND
+ tsExpire='infinity'::timestamp;
+
+ END;
+$$ LANGUAGE plpgsql;
+
+-- Args: uidAuthor, idGlobalRsrc, sName, sDescription, fEnabled
+CREATE OR REPLACE function update_globalresource(integer, integer, text, text, bool) RETURNS VOID AS $$
+ DECLARE
+ _uidAuthor ALIAS FOR $1;
+ _idGlobalRsrc ALIAS FOR $2;
+ _sName ALIAS FOR $3;
+ _sDescription ALIAS FOR $4;
+ _fEnabled ALIAS FOR $5;
+ BEGIN
+ -- Hostorize record
+ UPDATE GlobalResources
+ SET tsExpire=CURRENT_TIMESTAMP
+ WHERE idGlobalRsrc=_idGlobalRsrc AND
+ tsExpire='infinity'::timestamp;
+ -- Check if Global Resource name is unique
+ IF EXISTS(SELECT * FROM GlobalResources
+ WHERE sName=_sName AND
+ tsExpire='infinity'::timestamp) THEN
+ RAISE EXCEPTION 'Duplicate Global Resource name';
+ END IF;
+ -- Add new record
+ INSERT INTO GlobalResources(uidAuthor, idGlobalRsrc, sName, sDescription, fEnabled)
+ VALUES (_uidAuthor, _idGlobalRsrc, _sName, _sDescription, _fEnabled);
+ END;
+$$ LANGUAGE plpgsql;
diff --git a/src/VBox/ValidationKit/testmanager/core/globalresource.py b/src/VBox/ValidationKit/testmanager/core/globalresource.py
new file mode 100755
index 00000000..bd6f0e9e
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/globalresource.py
@@ -0,0 +1,328 @@
+# -*- coding: utf-8 -*-
+# $Id: globalresource.py $
+
+"""
+Test Manager - Global Resources.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMRowNotFound;
+
+
+class GlobalResourceData(ModelDataBase):
+ """
+ Global resource data
+ """
+
+ ksIdAttr = 'idGlobalRsrc';
+
+ ksParam_idGlobalRsrc = 'GlobalResource_idGlobalRsrc'
+ ksParam_tsEffective = 'GlobalResource_tsEffective'
+ ksParam_tsExpire = 'GlobalResource_tsExpire'
+ ksParam_uidAuthor = 'GlobalResource_uidAuthor'
+ ksParam_sName = 'GlobalResource_sName'
+ ksParam_sDescription = 'GlobalResource_sDescription'
+ ksParam_fEnabled = 'GlobalResource_fEnabled'
+
+ kasAllowNullAttributes = ['idGlobalRsrc', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription' ];
+ kcchMin_sName = 2;
+ kcchMax_sName = 64;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idGlobalRsrc = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.sName = None;
+ self.sDescription = None;
+ self.fEnabled = False
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM GlobalResources row.
+ Returns self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Global resource not found.')
+
+ self.idGlobalRsrc = aoRow[0]
+ self.tsEffective = aoRow[1]
+ self.tsExpire = aoRow[2]
+ self.uidAuthor = aoRow[3]
+ self.sName = aoRow[4]
+ self.sDescription = aoRow[5]
+ self.fEnabled = aoRow[6]
+ return self
+
+ def initFromDbWithId(self, oDb, idGlobalRsrc, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE idGlobalRsrc = %s\n'
+ , ( idGlobalRsrc,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idGlobalRsrc=%s not found (tsNow=%s sPeriodBack=%s)' % (idGlobalRsrc, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def isEqual(self, oOther):
+ """
+ Compares two instances.
+ """
+ return self.idGlobalRsrc == oOther.idGlobalRsrc \
+ and str(self.tsEffective) == str(oOther.tsEffective) \
+ and str(self.tsExpire) == str(oOther.tsExpire) \
+ and self.uidAuthor == oOther.uidAuthor \
+ and self.sName == oOther.sName \
+ and self.sDescription == oOther.sDescription \
+ and self.fEnabled == oOther.fEnabled
+
+
+class GlobalResourceLogic(ModelLogicBase):
+ """
+ Global resource logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Returns an array (list) of FailureReasonData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idGlobalRsrc DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idGlobalRsrc DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,))
+
+ aoRows = []
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(GlobalResourceData().initFromDbRow(aoRow))
+ return aoRows
+
+
+ def cachedLookup(self, idGlobalRsrc):
+ """
+ Looks up the most recent GlobalResourceData object for idGlobalRsrc
+ via an object cache.
+
+ Returns a shared GlobalResourceData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('GlobalResourceData');
+ oEntry = self.dCache.get(idGlobalRsrc, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE idGlobalRsrc = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idGlobalRsrc, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE idGlobalRsrc = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idGlobalRsrc, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idGlobalRsrc));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = GlobalResourceData();
+ oEntry.initFromDbRow(aaoRow);
+ self.dCache[idGlobalRsrc] = oEntry;
+ return oEntry;
+
+
+ def getAll(self, tsEffective = None):
+ """
+ Gets all global resources.
+
+ Returns an array of GlobalResourceData instances on success (can be
+ empty). Raises exception on database error.
+ """
+ if tsEffective is not None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ , (tsEffective, tsEffective));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
+ aaoRows = self._oDb.fetchAll();
+ aoRet = [];
+ for aoRow in aaoRows:
+ aoRet.append(GlobalResourceData().initFromDbRow(aoRow));
+
+ return aoRet;
+
+ def addGlobalResource(self, uidAuthor, oData):
+ """Add Global Resource DB record"""
+ self._oDb.execute('SELECT * FROM add_globalresource(%s, %s, %s, %s);',
+ (uidAuthor,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled))
+ self._oDb.commit()
+ return True
+
+ def editGlobalResource(self, uidAuthor, idGlobalRsrc, oData):
+ """Modify Global Resource DB record"""
+ # Check if anything has been changed
+ oGlobalResourcesDataOld = self.getById(idGlobalRsrc)
+ if oGlobalResourcesDataOld.isEqual(oData):
+ # Nothing has been changed, do nothing
+ return True
+
+ self._oDb.execute('SELECT * FROM update_globalresource(%s, %s, %s, %s, %s);',
+ (uidAuthor,
+ idGlobalRsrc,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled))
+ self._oDb.commit()
+ return True
+
+ def remove(self, uidAuthor, idGlobalRsrc):
+ """Delete Global Resource DB record"""
+ self._oDb.execute('SELECT * FROM del_globalresource(%s, %s);',
+ (uidAuthor, idGlobalRsrc))
+ self._oDb.commit()
+ return True
+
+ def getById(self, idGlobalRsrc):
+ """
+ Get global resource record by its id
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM GlobalResources\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idGlobalRsrc=%s;', (idGlobalRsrc,))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException('Duplicate global resource entry with ID %u (current)' % (idGlobalRsrc,));
+ try:
+ return GlobalResourceData().initFromDbRow(aRows[0])
+ except IndexError:
+ raise TMRowNotFound('Global resource not found.')
+
+ def allocateResources(self, idTestBox, aoGlobalRsrcs, fCommit = False):
+ """
+ Allocates the given global resource.
+
+ Returns True of successfully allocated the resources, False if not.
+ May raise exception on DB error.
+ """
+ # Quit quickly if there is nothing to alloocate.
+ if not aoGlobalRsrcs:
+ return True;
+
+ #
+ # Note! Someone else might have allocated the resources since the
+ # scheduler check that they were available. In such case we
+ # need too quietly rollback and return FALSE.
+ #
+ self._oDb.execute('SAVEPOINT allocateResources');
+
+ for oGlobalRsrc in aoGlobalRsrcs:
+ try:
+ self._oDb.execute('INSERT INTO GlobalResourceStatuses (idGlobalRsrc, idTestBox)\n'
+ 'VALUES (%s, %s)', (oGlobalRsrc.idGlobalRsrc, idTestBox, ) );
+ except self._oDb.oXcptError:
+ self._oDb.execute('ROLLBACK TO SAVEPOINT allocateResources');
+ return False;
+
+ self._oDb.execute('RELEASE SAVEPOINT allocateResources');
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def freeGlobalResourcesByTestBox(self, idTestBox, fCommit = False):
+ """
+ Frees all global resources own by the given testbox.
+ Returns True. May raise exception on DB error.
+ """
+ self._oDb.execute('DELETE FROM GlobalResourceStatuses\n'
+ 'WHERE idTestBox = %s\n', (idTestBox, ) );
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class GlobalResourceDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [GlobalResourceData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/report.py b/src/VBox/ValidationKit/testmanager/core/report.py
new file mode 100755
index 00000000..f07e75e8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/report.py
@@ -0,0 +1,1307 @@
+# -*- coding: utf-8 -*-
+# $Id: report.py $
+
+"""
+Test Manager - Report models.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import sys;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelLogicBase, TMExceptionBase;
+from testmanager.core.build import BuildCategoryData;
+from testmanager.core.dbobjcache import DatabaseObjCache;
+from testmanager.core.failurereason import FailureReasonLogic;
+from testmanager.core.testbox import TestBoxLogic, TestBoxData;
+from testmanager.core.testcase import TestCaseLogic;
+from testmanager.core.testcaseargs import TestCaseArgsLogic;
+from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+from common import constants;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+
+class ReportFilter(TestResultFilter):
+ """
+ Same as TestResultFilter for now.
+ """
+
+ def __init__(self):
+ TestResultFilter.__init__(self);
+
+
+
+class ReportModelBase(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Something all report logic(/miner) classes inherit from.
+ """
+
+ ## @name Report subjects
+ ## @{
+ ksSubEverything = 'Everything';
+ ksSubSchedGroup = 'SchedGroup';
+ ksSubTestGroup = 'TestGroup';
+ ksSubTestCase = 'TestCase';
+ ksSubTestCaseArgs = 'TestCaseArgs';
+ ksSubTestBox = 'TestBox';
+ ksSubBuild = 'Build';
+ ## @}
+ kasSubjects = [ ksSubEverything, ksSubSchedGroup, ksSubTestGroup, ksSubTestCase, ksSubTestBox, ksSubBuild, ];
+
+
+ ## @name TestStatus_T
+ # @{
+ ksTestStatus_Running = 'running';
+ ksTestStatus_Success = 'success';
+ ksTestStatus_Skipped = 'skipped';
+ ksTestStatus_BadTestBox = 'bad-testbox';
+ ksTestStatus_Aborted = 'aborted';
+ ksTestStatus_Failure = 'failure';
+ ksTestStatus_TimedOut = 'timed-out';
+ ksTestStatus_Rebooted = 'rebooted';
+ ## @}
+
+
+ def __init__(self, oDb, tsNow, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter):
+ ModelLogicBase.__init__(self, oDb);
+ # Public so the report generator can easily access them.
+ self.tsNow = tsNow; # (Can be None.)
+ self.__tsNowDateTime = None;
+ self.cPeriods = cPeriods;
+ self.cHoursPerPeriod = cHoursPerPeriod;
+ self.sSubject = sSubject;
+ self.aidSubjects = aidSubjects;
+ self.oFilter = oFilter;
+ if self.oFilter is None:
+ class DummyFilter(object):
+ """ Dummy """
+ def getTableJoins(self, sExtraIndent = '', iOmit = -1, dOmitTables = None):
+ """ Dummy """
+ _ = sExtraIndent; _ = iOmit; _ = dOmitTables; # pylint: disable=redefined-variable-type
+ return '';
+ def getWhereConditions(self, sExtraIndent = '', iOmit = -1):
+ """ Dummy """
+ _ = sExtraIndent; _ = iOmit; # pylint: disable=redefined-variable-type
+ return '';
+ def isJoiningWithTable(self, sTable):
+ """ Dummy """;
+ _ = sTable;
+ return False;
+ self.oFilter = DummyFilter();
+
+ def getExtraSubjectTables(self):
+ """
+ Returns a list of additional tables needed by the subject.
+ """
+ return [];
+
+ def getExtraSubjectWhereExpr(self):
+ """
+ Returns additional WHERE expression relating to the report subject. It starts
+ with an AND so that it can simply be appended to the WHERE clause.
+ """
+ if self.sSubject == self.ksSubEverything:
+ return '';
+
+ if self.sSubject == self.ksSubSchedGroup:
+ sWhere = ' AND TestSets.idSchedGroup';
+ elif self.sSubject == self.ksSubTestGroup:
+ sWhere = ' AND TestSets.idTestGroup';
+ elif self.sSubject == self.ksSubTestCase:
+ sWhere = ' AND TestSets.idTestCase';
+ elif self.sSubject == self.ksSubTestCaseArgs:
+ sWhere = ' AND TestSets.idTestCaseArgs';
+ elif self.sSubject == self.ksSubTestBox:
+ sWhere = ' AND TestSets.idTestBox';
+ elif self.sSubject == self.ksSubBuild:
+ sWhere = ' AND TestSets.idBuild';
+ else:
+ raise TMExceptionBase(self.sSubject);
+
+ if len(self.aidSubjects) == 1:
+ sWhere += self._oDb.formatBindArgs(' = %s\n', (self.aidSubjects[0],));
+ else:
+ assert self.aidSubjects;
+ sWhere += self._oDb.formatBindArgs(' IN (%s', (self.aidSubjects[0],));
+ for i in range(1, len(self.aidSubjects)):
+ sWhere += self._oDb.formatBindArgs(', %s', (self.aidSubjects[i],));
+ sWhere += ')\n';
+
+ return sWhere;
+
+ def getNowAsDateTime(self):
+ """ Returns a datetime instance corresponding to tsNow. """
+ if self.__tsNowDateTime is None:
+ if self.tsNow is None:
+ self.__tsNowDateTime = self._oDb.getCurrentTimestamp();
+ else:
+ self._oDb.execute('SELECT %s::TIMESTAMP WITH TIME ZONE', (self.tsNow,));
+ self.__tsNowDateTime = self._oDb.fetchOne()[0];
+ return self.__tsNowDateTime;
+
+ def getPeriodStart(self, iPeriod):
+ """ Gets the python timestamp for the start of the given period. """
+ from datetime import timedelta;
+ cHoursStart = (self.cPeriods - iPeriod ) * self.cHoursPerPeriod;
+ return self.getNowAsDateTime() - timedelta(hours = cHoursStart);
+
+ def getPeriodEnd(self, iPeriod):
+ """ Gets the python timestamp for the end of the given period. """
+ from datetime import timedelta;
+ cHoursEnd = (self.cPeriods - iPeriod - 1) * self.cHoursPerPeriod;
+ return self.getNowAsDateTime() - timedelta(hours = cHoursEnd);
+
+ def getExtraWhereExprForPeriod(self, iPeriod):
+ """
+ Returns additional WHERE expression for getting test sets for the
+ specified period. It starts with an AND so that it can simply be
+ appended to the WHERE clause.
+ """
+ if self.tsNow is None:
+ sNow = 'CURRENT_TIMESTAMP';
+ else:
+ sNow = self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,));
+
+ cHoursStart = (self.cPeriods - iPeriod ) * self.cHoursPerPeriod;
+ cHoursEnd = (self.cPeriods - iPeriod - 1) * self.cHoursPerPeriod;
+ if cHoursEnd == 0:
+ return ' AND TestSets.tsDone >= (%s - interval \'%u hours\')\n' \
+ ' AND TestSets.tsDone < %s\n' \
+ % (sNow, cHoursStart, sNow);
+ return ' AND TestSets.tsDone >= (%s - interval \'%u hours\')\n' \
+ ' AND TestSets.tsDone < (%s - interval \'%u hours\')\n' \
+ % (sNow, cHoursStart, sNow, cHoursEnd);
+
+ def getPeriodDesc(self, iPeriod):
+ """
+ Returns the period description, usually for graph data.
+ """
+ if iPeriod == 0:
+ return 'now' if self.tsNow is None else 'then';
+ sTerm = 'ago' if self.tsNow is None else 'earlier';
+ if self.cHoursPerPeriod == 24:
+ return '%dd %s' % (iPeriod, sTerm, );
+ if (iPeriod * self.cHoursPerPeriod) % 24 == 0:
+ return '%dd %s' % (iPeriod * self.cHoursPerPeriod / 24, sTerm, );
+ return '%dh %s' % (iPeriod * self.cHoursPerPeriod, sTerm);
+
+ def getStraightPeriodDesc(self, iPeriod):
+ """
+ Returns the period description, usually for graph data.
+ """
+ iWickedPeriod = self.cPeriods - iPeriod - 1;
+ return self.getPeriodDesc(iWickedPeriod);
+
+
+#
+# Data structures produced and returned by the ReportLazyModel.
+#
+
+class ReportTransientBase(object):
+ """ Details on the test where a problem was first/last seen. """
+ def __init__(self, idBuild, iRevision, sRepository, idTestSet, idTestResult, tsDone, # pylint: disable=too-many-arguments
+ iPeriod, fEnter, idSubject, oSubject):
+ self.idBuild = idBuild; # Build ID.
+ self.iRevision = iRevision; # SVN revision for build.
+ self.sRepository = sRepository; # SVN repository for build.
+ self.idTestSet = idTestSet; # Test set.
+ self.idTestResult = idTestResult; # Test result.
+ self.tsDone = tsDone; # When the test set was done.
+ self.iPeriod = iPeriod; # Data set period.
+ self.fEnter = fEnter; # True if enter event, False if leave event.
+ self.idSubject = idSubject;
+ self.oSubject = oSubject;
+
+class ReportFailureReasonTransient(ReportTransientBase):
+ """ Details on the test where a failure reason was first/last seen. """
+ def __init__(self, idBuild, iRevision, sRepository, idTestSet, idTestResult, tsDone, # pylint: disable=too-many-arguments
+ iPeriod, fEnter, oReason):
+ ReportTransientBase.__init__(self, idBuild, iRevision, sRepository, idTestSet, idTestResult, tsDone, iPeriod, fEnter,
+ oReason.idFailureReason, oReason);
+ self.oReason = oReason; # FailureReasonDataEx
+
+
+class ReportHitRowBase(object):
+ """ A row in a period. """
+ def __init__(self, idSubject, oSubject, cHits, tsMin = None, tsMax = None):
+ self.idSubject = idSubject;
+ self.oSubject = oSubject;
+ self.cHits = cHits;
+ self.tsMin = tsMin;
+ self.tsMax = tsMax;
+
+class ReportHitRowWithTotalBase(ReportHitRowBase):
+ """ A row in a period. """
+ def __init__(self, idSubject, oSubject, cHits, cTotal, tsMin = None, tsMax = None):
+ ReportHitRowBase.__init__(self, idSubject, oSubject, cHits, tsMin, tsMax)
+ self.cTotal = cTotal;
+ self.uPct = cHits * 100 / cTotal;
+
+class ReportFailureReasonRow(ReportHitRowBase):
+ """ The account of one failure reason for a period. """
+ def __init__(self, aoRow, oReason):
+ ReportHitRowBase.__init__(self, aoRow[0], oReason, aoRow[1], aoRow[2], aoRow[3]);
+ self.idFailureReason = aoRow[0];
+ self.oReason = oReason; # FailureReasonDataEx
+
+
+class ReportPeriodBase(object):
+ """ A period in ReportFailureReasonSet. """
+ def __init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo):
+ self.oSet = oSet # Reference to the parent ReportSetBase derived object.
+ self.iPeriod = iPeriod; # Period number in the set.
+ self.sDesc = sDesc; # Short period description.
+ self.tsStart = tsFrom; # Start of the period.
+ self.tsEnd = tsTo; # End of the period (exclusive).
+ self.tsMin = tsTo; # The earlierst hit of the period (only valid for cHits > 0).
+ self.tsMax = tsFrom; # The latest hit of the period (only valid for cHits > 0).
+ self.aoRows = []; # Rows in order the database returned them (ReportHitRowBase descendant).
+ self.dRowsById = {}; # Same as aoRows but indexed by object ID (see ReportSetBase::sIdAttr).
+ self.dFirst = {}; # The subjects seen for the first time - data object, keyed by ID.
+ self.dLast = {}; # The subjects seen for the last time - data object, keyed by ID.
+ self.cHits = 0; # Total number of hits in this period.
+ self.cMaxHits = 0; # Max hits in a row.
+ self.cMinHits = 99999999; # Min hits in a row (only valid for cHits > 0).
+
+ def appendRow(self, oRow, idRow, oData):
+ """ Adds a row. """
+ assert isinstance(oRow, ReportHitRowBase);
+ self.aoRows.append(oRow);
+ self.dRowsById[idRow] = oRow;
+ if idRow not in self.oSet.dSubjects:
+ self.oSet.dSubjects[idRow] = oData;
+ self._doStatsForRow(oRow, idRow, oData);
+
+ def _doStatsForRow(self, oRow, idRow, oData):
+ """ Does the statistics for a row. Helper for appendRow as well as helpRecalcStats. """
+ if oRow.tsMin is not None and oRow.tsMin < self.tsMin:
+ self.tsMin = oRow.tsMin;
+ if oRow.tsMax is not None and oRow.tsMax < self.tsMax:
+ self.tsMax = oRow.tsMax;
+
+ self.cHits += oRow.cHits;
+ if oRow.cHits > self.cMaxHits:
+ self.cMaxHits = oRow.cHits;
+ if oRow.cHits < self.cMinHits:
+ self.cMinHits = oRow.cHits;
+
+ if idRow in self.oSet.dcHitsPerId:
+ self.oSet.dcHitsPerId[idRow] += oRow.cHits;
+ else:
+ self.oSet.dcHitsPerId[idRow] = oRow.cHits;
+
+ if oRow.cHits > 0:
+ if idRow not in self.oSet.diPeriodFirst:
+ self.dFirst[idRow] = oData;
+ self.oSet.diPeriodFirst[idRow] = self.iPeriod;
+ self.oSet.diPeriodLast[idRow] = self.iPeriod;
+
+ def helperSetRecalcStats(self):
+ """ Recalc the statistics (do resetStats first on set). """
+ for idRow, oRow in self.dRowsById.items():
+ self._doStatsForRow(oRow, idRow, self.oSet.dSubjects[idRow]);
+
+ def helperSetResetStats(self):
+ """ Resets the statistics. """
+ self.tsMin = self.tsEnd;
+ self.tsMax = self.tsStart;
+ self.cHits = 0;
+ self.cMaxHits = 0;
+ self.cMinHits = 99999999;
+ self.dFirst = {};
+ self.dLast = {};
+
+ def helperSetDeleteKeyFromSet(self, idKey):
+ """ Helper for ReportPeriodSetBase::deleteKey """
+ if idKey in self.dRowsById:
+ oRow = self.dRowsById[idKey];
+ self.aoRows.remove(oRow);
+ del self.dRowsById[idKey]
+ self.cHits -= oRow.cHits;
+ if idKey in self.dFirst:
+ del self.dFirst[idKey];
+ if idKey in self.dLast:
+ del self.dLast[idKey];
+
+class ReportPeriodWithTotalBase(ReportPeriodBase):
+ """ In addition to the cHits, we also have a total to relate it too. """
+ def __init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo):
+ ReportPeriodBase.__init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo);
+ self.cTotal = 0;
+ self.cMaxTotal = 0;
+ self.cMinTotal = 99999999;
+ self.uMaxPct = 0; # Max percentage in a row (100 = 100%).
+
+ def _doStatsForRow(self, oRow, idRow, oData):
+ assert isinstance(oRow, ReportHitRowWithTotalBase);
+ super(ReportPeriodWithTotalBase, self)._doStatsForRow(oRow, idRow, oData);
+ self.cTotal += oRow.cTotal;
+ if oRow.cTotal > self.cMaxTotal:
+ self.cMaxTotal = oRow.cTotal;
+ if oRow.cTotal < self.cMinTotal:
+ self.cMinTotal = oRow.cTotal;
+
+ if oRow.uPct > self.uMaxPct:
+ self.uMaxPct = oRow.uPct;
+
+ if idRow in self.oSet.dcTotalPerId:
+ self.oSet.dcTotalPerId[idRow] += oRow.cTotal;
+ else:
+ self.oSet.dcTotalPerId[idRow] = oRow.cTotal;
+
+ def helperSetResetStats(self):
+ super(ReportPeriodWithTotalBase, self).helperSetResetStats();
+ self.cTotal = 0;
+ self.cMaxTotal = 0;
+ self.cMinTotal = 99999999;
+ self.uMaxPct = 0;
+
+class ReportFailureReasonPeriod(ReportPeriodBase):
+ """ A period in ReportFailureReasonSet. """
+ def __init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo):
+ ReportPeriodBase.__init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo);
+ self.cWithoutReason = 0; # Number of failed test sets without any assigned reason.
+
+
+
+class ReportPeriodSetBase(object):
+ """ Period data set base class. """
+ def __init__(self, sIdAttr):
+ self.sIdAttr = sIdAttr; # The name of the key attribute. Mainly for documentation purposes.
+ self.aoPeriods = []; # Periods (ReportPeriodBase descendant) in ascending order (time wise).
+ self.dSubjects = {}; # The subject data objects, keyed by the subject ID.
+ self.dcHitsPerId = {}; # Sum hits per subject ID (key).
+ self.cHits = 0; # Sum number of hits in all periods and all reasons.
+ self.cMaxHits = 0; # Max hits in a row.
+ self.cMinHits = 99999999; # Min hits in a row.
+ self.cMaxRows = 0; # Max number of rows in a period.
+ self.cMinRows = 99999999; # Min number of rows in a period.
+ self.diPeriodFirst = {}; # The period number a reason was first seen (keyed by subject ID).
+ self.diPeriodLast = {}; # The period number a reason was last seen (keyed by subject ID).
+ self.aoEnterInfo = []; # Array of ReportTransientBase children order by iRevision. Excludes
+ # the first period of course. (Child class populates this.)
+ self.aoLeaveInfo = []; # Array of ReportTransientBase children order in descending order by
+ # iRevision. Excludes the last priod. (Child class populates this.)
+
+ def appendPeriod(self, oPeriod):
+ """ Appends a period to the set. """
+ assert isinstance(oPeriod, ReportPeriodBase);
+ self.aoPeriods.append(oPeriod);
+ self._doStatsForPeriod(oPeriod);
+
+ def _doStatsForPeriod(self, oPeriod):
+ """ Worker for appendPeriod and recalcStats. """
+ self.cHits += oPeriod.cHits;
+ if oPeriod.cMaxHits > self.cMaxHits:
+ self.cMaxHits = oPeriod.cMaxHits;
+ if oPeriod.cMinHits < self.cMinHits:
+ self.cMinHits = oPeriod.cMinHits;
+
+ if len(oPeriod.aoRows) > self.cMaxRows:
+ self.cMaxRows = len(oPeriod.aoRows);
+ if len(oPeriod.aoRows) < self.cMinRows:
+ self.cMinRows = len(oPeriod.aoRows);
+
+ def recalcStats(self):
+ """ Recalculates the statistics. ASSUMES finalizePass1 hasn't been done yet. """
+ self.cHits = 0;
+ self.cMaxHits = 0;
+ self.cMinHits = 99999999;
+ self.cMaxRows = 0;
+ self.cMinRows = 99999999;
+ self.diPeriodFirst = {};
+ self.diPeriodLast = {};
+ self.dcHitsPerId = {};
+ for oPeriod in self.aoPeriods:
+ oPeriod.helperSetResetStats();
+
+ for oPeriod in self.aoPeriods:
+ oPeriod.helperSetRecalcStats();
+ self._doStatsForPeriod(oPeriod);
+
+ def deleteKey(self, idKey):
+ """ Deletes a key from the set. May leave cMaxHits and cMinHits with outdated values. """
+ self.cHits -= self.dcHitsPerId[idKey];
+ del self.dcHitsPerId[idKey];
+ if idKey in self.diPeriodFirst:
+ del self.diPeriodFirst[idKey];
+ if idKey in self.diPeriodLast:
+ del self.diPeriodLast[idKey];
+ if idKey in self.aoEnterInfo:
+ del self.aoEnterInfo[idKey];
+ if idKey in self.aoLeaveInfo:
+ del self.aoLeaveInfo[idKey];
+ del self.dSubjects[idKey];
+ for oPeriod in self.aoPeriods:
+ oPeriod.helperSetDeleteKeyFromSet(idKey);
+
+ def pruneRowsWithZeroSumHits(self):
+ """ Discards rows with zero sum hits across all periods. Works around lazy selects counting both totals and hits. """
+ cDeleted = 0;
+ aidKeys = list(self.dcHitsPerId);
+ for idKey in aidKeys:
+ if self.dcHitsPerId[idKey] == 0:
+ self.deleteKey(idKey);
+ cDeleted += 1;
+ if cDeleted > 0:
+ self.recalcStats();
+ return cDeleted;
+
+ def finalizePass1(self):
+ """ Finished all but aoEnterInfo and aoLeaveInfo. """
+ # All we need to do here is to populate the dLast members.
+ for idKey, iPeriod in self.diPeriodLast.items():
+ self.aoPeriods[iPeriod].dLast[idKey] = self.dSubjects[idKey];
+ return self;
+
+ def finalizePass2(self):
+ """ Called after aoEnterInfo and aoLeaveInfo has been populated to sort them. """
+ self.aoEnterInfo = sorted(self.aoEnterInfo, key = lambda oTrans: oTrans.iRevision);
+ self.aoLeaveInfo = sorted(self.aoLeaveInfo, key = lambda oTrans: oTrans.iRevision, reverse = True);
+ return self;
+
+class ReportPeriodSetWithTotalBase(ReportPeriodSetBase):
+ """ In addition to the cHits, we also have a total to relate it too. """
+ def __init__(self, sIdAttr):
+ ReportPeriodSetBase.__init__(self, sIdAttr);
+ self.dcTotalPerId = {}; # Sum total per subject ID (key).
+ self.cTotal = 0; # Sum number of total in all periods and all reasons.
+ self.cMaxTotal = 0; # Max total in a row.
+ self.cMinTotal = 0; # Min total in a row.
+ self.uMaxPct = 0; # Max percentage in a row (100 = 100%).
+
+ def _doStatsForPeriod(self, oPeriod):
+ assert isinstance(oPeriod, ReportPeriodWithTotalBase);
+ super(ReportPeriodSetWithTotalBase, self)._doStatsForPeriod(oPeriod);
+ self.cTotal += oPeriod.cTotal;
+ if oPeriod.cMaxTotal > self.cMaxTotal:
+ self.cMaxTotal = oPeriod.cMaxTotal;
+ if oPeriod.cMinTotal < self.cMinTotal:
+ self.cMinTotal = oPeriod.cMinTotal;
+
+ if oPeriod.uMaxPct > self.uMaxPct:
+ self.uMaxPct = oPeriod.uMaxPct;
+
+ def recalcStats(self):
+ self.dcTotalPerId = {};
+ self.cTotal = 0;
+ self.cMaxTotal = 0;
+ self.cMinTotal = 0;
+ self.uMaxPct = 0;
+ super(ReportPeriodSetWithTotalBase, self).recalcStats();
+
+ def deleteKey(self, idKey):
+ self.cTotal -= self.dcTotalPerId[idKey];
+ del self.dcTotalPerId[idKey];
+ super(ReportPeriodSetWithTotalBase, self).deleteKey(idKey);
+
+class ReportFailureReasonSet(ReportPeriodSetBase):
+ """ What ReportLazyModel.getFailureReasons returns. """
+ def __init__(self):
+ ReportPeriodSetBase.__init__(self, 'idFailureReason');
+
+
+
+class ReportLazyModel(ReportModelBase): # pylint: disable=too-few-public-methods
+ """
+ The 'lazy bird' report model class.
+
+ We may want to have several classes, maybe one for each report even. But,
+ I'm thinking that's a bit overkill so we'll start with this and split it
+ if/when it becomes necessary.
+ """
+
+ kdsStatusSimplificationMap = {
+ ReportModelBase.ksTestStatus_Running: ReportModelBase.ksTestStatus_Running,
+ ReportModelBase.ksTestStatus_Success: ReportModelBase.ksTestStatus_Success,
+ ReportModelBase.ksTestStatus_Skipped: ReportModelBase.ksTestStatus_Skipped,
+ ReportModelBase.ksTestStatus_BadTestBox: ReportModelBase.ksTestStatus_Skipped,
+ ReportModelBase.ksTestStatus_Aborted: ReportModelBase.ksTestStatus_Skipped,
+ ReportModelBase.ksTestStatus_Failure: ReportModelBase.ksTestStatus_Failure,
+ ReportModelBase.ksTestStatus_TimedOut: ReportModelBase.ksTestStatus_Failure,
+ ReportModelBase.ksTestStatus_Rebooted: ReportModelBase.ksTestStatus_Failure,
+ };
+
+ def getSuccessRates(self):
+ """
+ Gets the success rates of the subject in the specified period.
+
+ Returns an array of data per period (0 is the oldes, self.cPeriods-1 is
+ the latest) where each entry is a status (TestStatus_T) dictionary with
+ the number of occurences of each final status (i.e. not running).
+ """
+
+ sBaseQuery = 'SELECT TestSets.enmStatus, COUNT(TestSets.idTestSet)\n' \
+ 'FROM TestSets\n' \
+ + self.oFilter.getTableJoins();
+ for sTable in self.getExtraSubjectTables():
+ sBaseQuery = sBaseQuery[:-1] + ',\n ' + sTable + '\n';
+ sBaseQuery += 'WHERE enmStatus <> \'running\'\n' \
+ + self.oFilter.getWhereConditions() \
+ + self.getExtraSubjectWhereExpr();
+
+ adPeriods = [];
+ for iPeriod in xrange(self.cPeriods):
+ self._oDb.execute(sBaseQuery + self.getExtraWhereExprForPeriod(iPeriod) + 'GROUP BY enmStatus\n');
+
+ dRet = \
+ {
+ self.ksTestStatus_Skipped: 0,
+ self.ksTestStatus_Failure: 0,
+ self.ksTestStatus_Success: 0,
+ };
+
+ for aoRow in self._oDb.fetchAll():
+ sKey = self.kdsStatusSimplificationMap[aoRow[0]]
+ if sKey in dRet:
+ dRet[sKey] += aoRow[1];
+ else:
+ dRet[sKey] = aoRow[1];
+
+ assert len(dRet) == 3;
+
+ adPeriods.insert(0, dRet);
+
+ return adPeriods;
+
+
+ def getFailureReasons(self):
+ """
+ Gets the failure reasons of the subject in the specified period.
+
+ Returns a ReportFailureReasonSet instance.
+ """
+
+ oFailureReasonLogic = FailureReasonLogic(self._oDb);
+
+ #
+ # Create a temporary table
+ #
+ sTsNow = 'CURRENT_TIMESTAMP' if self.tsNow is None else self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,));
+ sTsFirst = '(%s - interval \'%s hours\')' \
+ % (sTsNow, self.cHoursPerPeriod * self.cPeriods,);
+ sQuery = 'CREATE TEMPORARY TABLE TmpReasons ON COMMIT DROP AS\n' \
+ 'SELECT TestResultFailures.idFailureReason AS idFailureReason,\n' \
+ ' TestResultFailures.idTestResult AS idTestResult,\n' \
+ ' TestSets.idTestSet AS idTestSet,\n' \
+ ' TestSets.tsDone AS tsDone,\n' \
+ ' TestSets.tsCreated AS tsCreated,\n' \
+ ' TestSets.idBuild AS idBuild\n' \
+ 'FROM TestResultFailures,\n' \
+ ' TestResults,\n' \
+ ' TestSets\n' \
+ + self.oFilter.getTableJoins(dOmitTables = {'TestResults': True, 'TestResultFailures': True});
+ for sTable in self.getExtraSubjectTables():
+ if sTable not in [ 'TestResults', 'TestResultFailures' ] and not self.oFilter.isJoiningWithTable(sTable):
+ sQuery = sQuery[:-1] + ',\n ' + sTable + '\n';
+ sQuery += 'WHERE TestResultFailures.idTestResult = TestResults.idTestResult\n' \
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n' \
+ ' AND TestResultFailures.tsEffective >= ' + sTsFirst + '\n' \
+ ' AND TestResults.enmStatus <> \'running\'\n' \
+ ' AND TestResults.enmStatus <> \'success\'\n' \
+ ' AND TestResults.tsCreated >= ' + sTsFirst + '\n' \
+ ' AND TestResults.tsCreated < ' + sTsNow + '\n' \
+ ' AND TestResults.idTestSet = TestSets.idTestSet\n' \
+ ' AND TestSets.tsDone >= ' + sTsFirst + '\n' \
+ ' AND TestSets.tsDone < ' + sTsNow + '\n' \
+ + self.oFilter.getWhereConditions() \
+ + self.getExtraSubjectWhereExpr();
+ self._oDb.execute(sQuery);
+ self._oDb.execute('SELECT idFailureReason FROM TmpReasons;');
+
+ #
+ # Retrieve the period results.
+ #
+ oSet = ReportFailureReasonSet();
+ for iPeriod in xrange(self.cPeriods):
+ self._oDb.execute('SELECT idFailureReason,\n'
+ ' COUNT(idTestResult),\n'
+ ' MIN(tsDone),\n'
+ ' MAX(tsDone)\n'
+ 'FROM TmpReasons\n'
+ 'WHERE TRUE\n'
+ + self.getExtraWhereExprForPeriod(iPeriod).replace('TestSets.', '') +
+ 'GROUP BY idFailureReason\n');
+ aaoRows = self._oDb.fetchAll()
+
+ oPeriod = ReportFailureReasonPeriod(oSet, iPeriod, self.getStraightPeriodDesc(iPeriod),
+ self.getPeriodStart(iPeriod), self.getPeriodEnd(iPeriod));
+
+ for aoRow in aaoRows:
+ oReason = oFailureReasonLogic.cachedLookup(aoRow[0]);
+ oPeriodRow = ReportFailureReasonRow(aoRow, oReason);
+ oPeriod.appendRow(oPeriodRow, oReason.idFailureReason, oReason);
+
+ # Count how many test sets we've got without any reason associated with them.
+ self._oDb.execute('SELECT COUNT(TestSets.idTestSet)\n'
+ 'FROM TestSets\n'
+ ' LEFT OUTER JOIN TestResultFailures\n'
+ ' ON TestSets.idTestSet = TestResultFailures.idTestSet\n'
+ ' AND TestResultFailures.tsEffective = \'infinity\'::TIMESTAMP\n'
+ 'WHERE TestSets.enmStatus <> \'running\'\n'
+ ' AND TestSets.enmStatus <> \'success\'\n'
+ + self.getExtraWhereExprForPeriod(iPeriod) +
+ ' AND TestResultFailures.idTestSet IS NULL\n');
+ oPeriod.cWithoutReason = self._oDb.fetchOne()[0];
+
+ oSet.appendPeriod(oPeriod);
+
+
+ #
+ # For reasons entering after the first period, look up the build and
+ # test set it first occured with.
+ #
+ oSet.finalizePass1();
+
+ for iPeriod in xrange(1, self.cPeriods):
+ oPeriod = oSet.aoPeriods[iPeriod];
+ for oReason in oPeriod.dFirst.values():
+ oSet.aoEnterInfo.append(self._getEdgeFailureReasonOccurence(oReason, iPeriod, fEnter = True));
+
+ # Ditto for reasons leaving before the last.
+ for iPeriod in xrange(self.cPeriods - 1):
+ oPeriod = oSet.aoPeriods[iPeriod];
+ for oReason in oPeriod.dLast.values():
+ oSet.aoLeaveInfo.append(self._getEdgeFailureReasonOccurence(oReason, iPeriod, fEnter = False));
+
+ oSet.finalizePass2();
+
+ self._oDb.execute('DROP TABLE TmpReasons\n');
+ return oSet;
+
+
+ def _getEdgeFailureReasonOccurence(self, oReason, iPeriod, fEnter = True):
+ """
+ Helper for the failure reason report that finds the oldest or newest build
+ (SVN rev) and test set (start time) it occured with.
+
+ If fEnter is set the oldest occurence is return, if fEnter clear the newest
+ is is returned.
+
+ Returns ReportFailureReasonTransient instant.
+
+ """
+
+
+ sSorting = 'ASC' if fEnter else 'DESC';
+ self._oDb.execute('SELECT TmpReasons.idTestResult,\n'
+ ' TmpReasons.idTestSet,\n'
+ ' TmpReasons.tsDone,\n'
+ ' TmpReasons.idBuild,\n'
+ ' Builds.iRevision,\n'
+ ' BuildCategories.sRepository\n'
+ 'FROM TmpReasons,\n'
+ ' Builds,\n'
+ ' BuildCategories\n'
+ 'WHERE TmpReasons.idFailureReason = %s\n'
+ ' AND TmpReasons.idBuild = Builds.idBuild\n'
+ ' AND Builds.tsExpire > TmpReasons.tsCreated\n'
+ ' AND Builds.tsEffective <= TmpReasons.tsCreated\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ 'ORDER BY Builds.iRevision ' + sSorting + ',\n'
+ ' TmpReasons.tsCreated ' + sSorting + '\n'
+ 'LIMIT 1\n'
+ , ( oReason.idFailureReason, ));
+ aoRow = self._oDb.fetchOne();
+ if aoRow is None:
+ return ReportFailureReasonTransient(-1, -1, 'internal-error', -1, -1, self._oDb.getCurrentTimestamp(),
+ iPeriod, fEnter, oReason);
+ return ReportFailureReasonTransient(idBuild = aoRow[3], iRevision = aoRow[4], sRepository = aoRow[5],
+ idTestSet = aoRow[1], idTestResult = aoRow[0], tsDone = aoRow[2],
+ iPeriod = iPeriod, fEnter = fEnter, oReason = oReason);
+
+
+ def getTestCaseFailures(self):
+ """
+ Gets the test case failures of the subject in the specified period.
+
+ Returns a ReportPeriodSetWithTotalBase instance.
+
+ """
+ return self._getSimpleFailures('idTestCase', TestCaseLogic);
+
+
+ def getTestCaseVariationFailures(self):
+ """
+ Gets the test case failures of the subject in the specified period.
+
+ Returns a ReportPeriodSetWithTotalBase instance.
+
+ """
+ return self._getSimpleFailures('idTestCaseArgs', TestCaseArgsLogic);
+
+
+ def getTestBoxFailures(self):
+ """
+ Gets the test box failures of the subject in the specified period.
+
+ Returns a ReportPeriodSetWithTotalBase instance.
+
+ """
+ return self._getSimpleFailures('idTestBox', TestBoxLogic);
+
+
+ def _getSimpleFailures(self, sIdColumn, oCacheLogicType, sIdAttr = None):
+ """
+ Gets the test box failures of the subject in the specified period.
+
+ Returns a ReportPeriodSetWithTotalBase instance.
+
+ """
+
+ oLogic = oCacheLogicType(self._oDb);
+ oSet = ReportPeriodSetWithTotalBase(sIdColumn if sIdAttr is None else sIdAttr);
+
+ # Construct base query.
+ sBaseQuery = 'SELECT TestSets.' + sIdColumn + ',\n' \
+ ' COUNT(CASE WHEN TestSets.enmStatus >= \'failure\' THEN 1 END),\n' \
+ ' MIN(TestSets.tsDone),\n' \
+ ' MAX(TestSets.tsDone),\n' \
+ ' COUNT(TestSets.idTestResult)\n' \
+ 'FROM TestSets\n' \
+ + self.oFilter.getTableJoins();
+ for sTable in self.getExtraSubjectTables():
+ sBaseQuery = sBaseQuery[:-1] + ',\n ' + sTable + '\n';
+ sBaseQuery += 'WHERE TRUE\n' \
+ + self.oFilter.getWhereConditions() \
+ + self.getExtraSubjectWhereExpr() + '\n';
+
+ # Retrieve the period results.
+ for iPeriod in xrange(self.cPeriods):
+ self._oDb.execute(sBaseQuery + self.getExtraWhereExprForPeriod(iPeriod) + 'GROUP BY TestSets.' + sIdColumn + '\n');
+ aaoRows = self._oDb.fetchAll()
+
+ oPeriod = ReportPeriodWithTotalBase(oSet, iPeriod, self.getStraightPeriodDesc(iPeriod),
+ self.getPeriodStart(iPeriod), self.getPeriodEnd(iPeriod));
+
+ for aoRow in aaoRows:
+ oSubject = oLogic.cachedLookup(aoRow[0]);
+ oPeriodRow = ReportHitRowWithTotalBase(aoRow[0], oSubject, aoRow[1], aoRow[4], aoRow[2], aoRow[3]);
+ oPeriod.appendRow(oPeriodRow, aoRow[0], oSubject);
+
+ oSet.appendPeriod(oPeriod);
+ oSet.pruneRowsWithZeroSumHits();
+
+
+
+ #
+ # For reasons entering after the first period, look up the build and
+ # test set it first occured with.
+ #
+ oSet.finalizePass1();
+
+ for iPeriod in xrange(1, self.cPeriods):
+ oPeriod = oSet.aoPeriods[iPeriod];
+ for idSubject, oSubject in oPeriod.dFirst.items():
+ oSet.aoEnterInfo.append(self._getEdgeSimpleFailureOccurence(idSubject, sIdColumn, oSubject,
+ iPeriod, fEnter = True));
+
+ # Ditto for reasons leaving before the last.
+ for iPeriod in xrange(self.cPeriods - 1):
+ oPeriod = oSet.aoPeriods[iPeriod];
+ for idSubject, oSubject in oPeriod.dLast.items():
+ oSet.aoLeaveInfo.append(self._getEdgeSimpleFailureOccurence(idSubject, sIdColumn, oSubject,
+ iPeriod, fEnter = False));
+
+ oSet.finalizePass2();
+
+ return oSet;
+
+ def _getEdgeSimpleFailureOccurence(self, idSubject, sIdColumn, oSubject, iPeriod, fEnter = True):
+ """
+ Helper for the failure reason report that finds the oldest or newest build
+ (SVN rev) and test set (start time) it occured with.
+
+ If fEnter is set the oldest occurence is return, if fEnter clear the newest
+ is is returned.
+
+ Returns ReportTransientBase instant.
+
+ """
+ sSorting = 'ASC' if fEnter else 'DESC';
+ sQuery = 'SELECT TestSets.idTestResult,\n' \
+ ' TestSets.idTestSet,\n' \
+ ' TestSets.tsDone,\n' \
+ ' TestSets.idBuild,\n' \
+ ' Builds.iRevision,\n' \
+ ' BuildCategories.sRepository\n' \
+ 'FROM TestSets\n' \
+ + self.oFilter.getTableJoins(dOmitTables = {'Builds': True, 'BuildCategories': True});
+ sQuery = sQuery[:-1] + ',\n' \
+ ' Builds,\n' \
+ ' BuildCategories\n';
+ for sTable in self.getExtraSubjectTables():
+ if sTable not in [ 'Builds', 'BuildCategories' ] and not self.oFilter.isJoiningWithTable(sTable):
+ sQuery = sQuery[:-1] + ',\n ' + sTable + '\n';
+ sQuery += 'WHERE TestSets.' + sIdColumn + ' = ' + str(idSubject) + '\n' \
+ ' AND TestSets.idBuild = Builds.idBuild\n' \
+ ' AND TestSets.enmStatus >= \'failure\'\n' \
+ + self.getExtraWhereExprForPeriod(iPeriod) + \
+ ' AND Builds.tsExpire > TestSets.tsCreated\n' \
+ ' AND Builds.tsEffective <= TestSets.tsCreated\n' \
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n' \
+ + self.oFilter.getWhereConditions() \
+ + self.getExtraSubjectWhereExpr() + '\n' \
+ 'ORDER BY Builds.iRevision ' + sSorting + ',\n' \
+ ' TestSets.tsCreated ' + sSorting + '\n' \
+ 'LIMIT 1\n';
+ self._oDb.execute(sQuery);
+ aoRow = self._oDb.fetchOne();
+ if aoRow is None:
+ return ReportTransientBase(-1, -1, 'internal-error', -1, -1, self._oDb.getCurrentTimestamp(),
+ iPeriod, fEnter, idSubject, oSubject);
+ return ReportTransientBase(idBuild = aoRow[3], iRevision = aoRow[4], sRepository = aoRow[5],
+ idTestSet = aoRow[1], idTestResult = aoRow[0], tsDone = aoRow[2],
+ iPeriod = iPeriod, fEnter = fEnter, idSubject = idSubject, oSubject = oSubject);
+
+ def fetchPossibleFilterOptions(self, oFilter, tsNow, sPeriod):
+ """
+ Fetches possible filtering options.
+ """
+ return TestResultLogic(self._oDb).fetchPossibleFilterOptions(oFilter, tsNow, sPeriod, oReportModel = self);
+
+
+
+class ReportGraphModel(ReportModelBase): # pylint: disable=too-few-public-methods
+ """
+ Extended report model used when generating the more complicated graphs
+ detailing results, time elapsed and values over time.
+ """
+
+ ## @name Subject ID types.
+ ## These prefix the values in the aidSubjects array. The prefix is
+ ## followed by a colon and then a list of string IDs. Following the prefix
+ ## is one or more string table IDs separated by colons. These are used to
+ ## drill down the exact test result we're looking for, by matching against
+ ## TestResult::idStrName (in the db).
+ ## @{
+ ksTypeResult = 'result';
+ ksTypeElapsed = 'elapsed';
+ ## The last string table ID gives the name of the value.
+ ksTypeValue = 'value';
+ ## List of types.
+ kasTypes = (ksTypeResult, ksTypeElapsed, ksTypeValue);
+ ## @}
+
+ class SampleSource(object):
+ """ A sample source. """
+ def __init__(self, sType, aidStrTests, idStrValue):
+ self.sType = sType;
+ self.aidStrTests = aidStrTests;
+ self.idStrValue = idStrValue;
+
+ def getTestResultTables(self):
+ """ Retrieves the list of TestResults tables to join with."""
+ sRet = '';
+ for i in range(len(self.aidStrTests)):
+ sRet += ' TestResults TR%u,\n' % (i,);
+ return sRet;
+
+ def getTestResultConditions(self):
+ """ Retrieves the join conditions for the TestResults tables."""
+ sRet = '';
+ cItems = len(self.aidStrTests);
+ for i in range(cItems - 1):
+ sRet += ' AND TR%u.idStrName = %u\n' \
+ ' AND TR%u.idTestResultParent = TR%u.idTestResult\n' \
+ % ( i, self.aidStrTests[cItems - i - 1], i, i + 1 );
+ sRet += ' AND TR%u.idStrName = %u\n' % (cItems - 1, self.aidStrTests[0]);
+ return sRet;
+
+ class DataSeries(object):
+ """ A data series. """
+ def __init__(self, oCache, idBuildCategory, idTestBox, idTestCase, idTestCaseArgs, iUnit):
+ _ = oCache;
+ self.idBuildCategory = idBuildCategory;
+ self.oBuildCategory = oCache.getBuildCategory(idBuildCategory);
+ self.idTestBox = idTestBox;
+ self.oTestBox = oCache.getTestBox(idTestBox);
+ self.idTestCase = idTestCase;
+ self.idTestCaseArgs = idTestCaseArgs;
+ if idTestCase is not None:
+ self.oTestCase = oCache.getTestCase(idTestCase);
+ self.oTestCaseArgs = None;
+ else:
+ self.oTestCaseArgs = oCache.getTestCaseArgs(idTestCaseArgs);
+ self.oTestCase = oCache.getTestCase(self.oTestCaseArgs.idTestCase);
+ self.iUnit = iUnit;
+ # Six parallel arrays.
+ self.aiRevisions = []; # The X values.
+ self.aiValues = []; # The Y values.
+ self.aiErrorBarBelow = []; # The Y value minimum errorbars, relative to the Y value (positive).
+ self.aiErrorBarAbove = []; # The Y value maximum errorbars, relative to the Y value (positive).
+ self.acSamples = []; # The number of samples at this X value.
+ self.aoRevInfo = []; # VcsRevisionData objects for each revision. Empty/SQL-NULL objects if no info.
+
+ class DataSeriesCollection(object):
+ """ A collection of data series corresponding to one input sample source. """
+ def __init__(self, oSampleSrc, asTests, sValue = None):
+ self.sType = oSampleSrc.sType;
+ self.aidStrTests = oSampleSrc.aidStrTests;
+ self.asTests = list(asTests);
+ self.idStrValue = oSampleSrc.idStrValue;
+ self.sValue = sValue;
+ self.aoSeries = [];
+
+ def addDataSeries(self, oDataSeries):
+ """ Appends a data series to the collection. """
+ self.aoSeries.append(oDataSeries);
+ return oDataSeries;
+
+
+ def __init__(self, oDb, tsNow, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, # pylint: disable=too-many-arguments
+ aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars):
+ assert(sSubject == self.ksSubEverything); # dummy
+ ReportModelBase.__init__(self, oDb, tsNow, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter = None);
+ self.aidTestBoxes = aidTestBoxes;
+ self.aidBuildCats = aidBuildCats;
+ self.aidTestCases = aidTestCases;
+ self.fOnTestCase = not fSepTestVars; # (Separates testcase variations into separate data series.)
+ self.oCache = DatabaseObjCache(self._oDb, self.tsNow, None, self.cPeriods * self.cHoursPerPeriod);
+
+
+ # Quickly validate and convert the subject "IDs".
+ self.aoLookups = [];
+ for sCur in self.aidSubjects:
+ asParts = sCur.split(':');
+ if len(asParts) < 2:
+ raise TMExceptionBase('Invalid graph value "%s"' % (sCur,));
+
+ sType = asParts[0];
+ if sType not in ReportGraphModel.kasTypes:
+ raise TMExceptionBase('Invalid graph value type "%s" (full: "%s")' % (sType, sCur,));
+
+ aidStrTests = [];
+ for sIdStr in asParts[1:]:
+ try: idStr = int(sIdStr);
+ except: raise TMExceptionBase('Invalid graph value id "%s" (full: "%s")' % (sIdStr, sCur,));
+ if idStr < 0:
+ raise TMExceptionBase('Invalid graph value id "%u" (full: "%s")' % (idStr, sCur,));
+ aidStrTests.append(idStr);
+
+ idStrValue = None;
+ if sType == ReportGraphModel.ksTypeValue:
+ idStrValue = aidStrTests.pop();
+ self.aoLookups.append(ReportGraphModel.SampleSource(sType, aidStrTests, idStrValue));
+
+ # done
+
+
+ def getExtraWhereExprForTotalPeriod(self, sTimestampField):
+ """
+ Returns additional WHERE expression for getting test sets for the
+ specified period. It starts with an AND so that it can simply be
+ appended to the WHERE clause.
+ """
+ return self.getExtraWhereExprForTotalPeriodEx(sTimestampField, sTimestampField, True);
+
+ def getExtraWhereExprForTotalPeriodEx(self, sStartField = 'tsCreated', sEndField = 'tsDone', fLeadingAnd = True):
+ """
+ Returns additional WHERE expression for getting test sets for the
+ specified period.
+ """
+ if self.tsNow is None:
+ sNow = 'CURRENT_TIMESTAMP';
+ else:
+ sNow = self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,));
+
+ sRet = ' AND %s >= (%s - interval \'%u hours\')\n' \
+ ' AND %s <= %s\n' \
+ % ( sStartField, sNow, self.cPeriods * self.cHoursPerPeriod,
+ sEndField, sNow);
+
+ if not fLeadingAnd:
+ assert sRet[8] == ' ' and sRet[7] == 'D';
+ return sRet[9:];
+ return sRet;
+
+ def _getEligibleTestSetPeriod(self, sPrefix = 'TestSets.', fLeadingAnd = False):
+ """
+ Returns additional WHERE expression for getting TestSets rows
+ potentially relevant for the selected period.
+ """
+ if self.tsNow is None:
+ sNow = 'CURRENT_TIMESTAMP';
+ else:
+ sNow = self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,));
+
+ # The 2nd line is a performance hack on TestSets. It nudges postgresql
+ # into useing the TestSetsCreatedDoneIdx index instead of doing a table
+ # scan when we look for eligible bits there.
+ # ASSUMES no relevant test runs longer than 7 days!
+ sRet = ' AND %stsCreated <= %s\n' \
+ ' AND %stsCreated >= (%s - interval \'%u hours\' - interval \'%u days\')\n' \
+ ' AND %stsDone >= (%s - interval \'%u hours\')\n' \
+ % ( sPrefix, sNow,
+ sPrefix, sNow, self.cPeriods * self.cHoursPerPeriod, 7,
+ sPrefix, sNow, self.cPeriods * self.cHoursPerPeriod, );
+
+ if not fLeadingAnd:
+ assert sRet[8] == ' ' and sRet[7] == 'D';
+ return sRet[9:];
+ return sRet;
+
+
+ def _getNameStrings(self, aidStrTests):
+ """ Returns an array of names corresponding to the array of string table entries. """
+ return [self.oCache.getTestResultString(idStr) for idStr in aidStrTests];
+
+ def fetchGraphData(self):
+ """ returns data """
+ sWantedTestCaseId = 'idTestCase' if self.fOnTestCase else 'idTestCaseArgs';
+
+ aoRet = [];
+ for oLookup in self.aoLookups:
+ #
+ # Set up the result collection.
+ #
+ if oLookup.sType == self.ksTypeValue:
+ oCollection = self.DataSeriesCollection(oLookup, self._getNameStrings(oLookup.aidStrTests),
+ self.oCache.getTestResultString(oLookup.idStrValue));
+ else:
+ oCollection = self.DataSeriesCollection(oLookup, self._getNameStrings(oLookup.aidStrTests));
+
+ #
+ # Construct the query.
+ #
+ sQuery = 'SELECT Builds.iRevision,\n' \
+ ' TestSets.idBuildCategory,\n' \
+ ' TestSets.idTestBox,\n' \
+ ' TestSets.' + sWantedTestCaseId + ',\n';
+ if oLookup.sType == self.ksTypeValue:
+ sQuery += ' TestResultValues.iUnit as iUnit,\n' \
+ ' MIN(TestResultValues.lValue),\n' \
+ ' CAST(ROUND(AVG(TestResultValues.lValue)) AS BIGINT),\n' \
+ ' MAX(TestResultValues.lValue),\n' \
+ ' COUNT(TestResultValues.lValue)\n';
+ elif oLookup.sType == self.ksTypeElapsed:
+ sQuery += ' %u as iUnit,\n' \
+ ' CAST((EXTRACT(EPOCH FROM MIN(TR0.tsElapsed)) * 1000) AS INTEGER),\n' \
+ ' CAST((EXTRACT(EPOCH FROM AVG(TR0.tsElapsed)) * 1000) AS INTEGER),\n' \
+ ' CAST((EXTRACT(EPOCH FROM MAX(TR0.tsElapsed)) * 1000) AS INTEGER),\n' \
+ ' COUNT(TR0.tsElapsed)\n' \
+ % (constants.valueunit.MS,);
+ else:
+ sQuery += ' %u as iUnit,\n'\
+ ' MIN(TR0.cErrors),\n' \
+ ' CAST(ROUND(AVG(TR0.cErrors)) AS INTEGER),\n' \
+ ' MAX(TR0.cErrors),\n' \
+ ' COUNT(TR0.cErrors)\n' \
+ % (constants.valueunit.OCCURRENCES,);
+
+ if oLookup.sType == self.ksTypeValue:
+ sQuery += 'FROM TestResultValues,\n';
+ sQuery += ' TestSets,\n'
+ sQuery += oLookup.getTestResultTables();
+ else:
+ sQuery += 'FROM ' + oLookup.getTestResultTables().lstrip();
+ sQuery += ' TestSets,\n';
+ sQuery += ' Builds\n';
+
+ if oLookup.sType == self.ksTypeValue:
+ sQuery += 'WHERE TestResultValues.idStrName = %u\n' % ( oLookup.idStrValue, );
+ sQuery += self.getExtraWhereExprForTotalPeriod('TestResultValues.tsCreated');
+ sQuery += ' AND TestResultValues.idTestSet = TestSets.idTestSet\n';
+ sQuery += self._getEligibleTestSetPeriod(fLeadingAnd = True);
+ else:
+ sQuery += 'WHERE ' + (self.getExtraWhereExprForTotalPeriod('TR0.tsCreated').lstrip()[4:]).lstrip();
+ sQuery += ' AND TR0.idTestSet = TestSets.idTestSet\n';
+
+ if len(self.aidTestBoxes) == 1:
+ sQuery += ' AND TestSets.idTestBox = %u\n' % (self.aidTestBoxes[0],);
+ elif self.aidTestBoxes:
+ sQuery += ' AND TestSets.idTestBox IN (' + ','.join([str(i) for i in self.aidTestBoxes]) + ')\n';
+
+ if len(self.aidBuildCats) == 1:
+ sQuery += ' AND TestSets.idBuildCategory = %u\n' % (self.aidBuildCats[0],);
+ elif self.aidBuildCats:
+ sQuery += ' AND TestSets.idBuildCategory IN (' + ','.join([str(i) for i in self.aidBuildCats]) + ')\n';
+
+ if len(self.aidTestCases) == 1:
+ sQuery += ' AND TestSets.idTestCase = %u\n' % (self.aidTestCases[0],);
+ elif self.aidTestCases:
+ sQuery += ' AND TestSets.idTestCase IN (' + ','.join([str(i) for i in self.aidTestCases]) + ')\n';
+
+ if oLookup.sType == self.ksTypeElapsed:
+ sQuery += ' AND TestSets.enmStatus = \'%s\'::TestStatus_T\n' % (self.ksTestStatus_Success,);
+
+ if oLookup.sType == self.ksTypeValue:
+ sQuery += ' AND TestResultValues.idTestResult = TR0.idTestResult\n'
+ sQuery += self.getExtraWhereExprForTotalPeriod('TR0.tsCreated'); # For better index matching in some cases.
+
+ if oLookup.sType != self.ksTypeResult:
+ sQuery += ' AND TR0.enmStatus = \'%s\'::TestStatus_T\n' % (self.ksTestStatus_Success,);
+
+ sQuery += oLookup.getTestResultConditions();
+ sQuery += ' AND TestSets.idBuild = Builds.idBuild\n';
+
+ sQuery += 'GROUP BY TestSets.idBuildCategory,\n' \
+ ' TestSets.idTestBox,\n' \
+ ' TestSets.' + sWantedTestCaseId + ',\n' \
+ ' iUnit,\n' \
+ ' Builds.iRevision\n';
+ sQuery += 'ORDER BY TestSets.idBuildCategory,\n' \
+ ' TestSets.idTestBox,\n' \
+ ' TestSets.' + sWantedTestCaseId + ',\n' \
+ ' iUnit,\n' \
+ ' Builds.iRevision\n';
+
+ #
+ # Execute it and collect the result.
+ #
+ sCurRepository = None;
+ dRevisions = {};
+ oLastSeries = None;
+ idLastBuildCat = -1;
+ idLastTestBox = -1;
+ idLastTestCase = -1;
+ iLastUnit = -1;
+ self._oDb.execute(sQuery);
+ for aoRow in self._oDb.fetchAll(): # Fetching all here so we can make cache queries below.
+ if aoRow[1] != idLastBuildCat \
+ or aoRow[2] != idLastTestBox \
+ or aoRow[3] != idLastTestCase \
+ or aoRow[4] != iLastUnit:
+ idLastBuildCat = aoRow[1];
+ idLastTestBox = aoRow[2];
+ idLastTestCase = aoRow[3];
+ iLastUnit = aoRow[4];
+ if self.fOnTestCase:
+ oLastSeries = self.DataSeries(self.oCache, idLastBuildCat, idLastTestBox,
+ idLastTestCase, None, iLastUnit);
+ else:
+ oLastSeries = self.DataSeries(self.oCache, idLastBuildCat, idLastTestBox,
+ None, idLastTestCase, iLastUnit);
+ oCollection.addDataSeries(oLastSeries);
+ if oLastSeries.oBuildCategory.sRepository != sCurRepository:
+ if sCurRepository is not None:
+ self.oCache.preloadVcsRevInfo(sCurRepository, dRevisions.keys());
+ sCurRepository = oLastSeries.oBuildCategory.sRepository
+ dRevisions = {};
+ oLastSeries.aiRevisions.append(aoRow[0]);
+ oLastSeries.aiValues.append(aoRow[6]);
+ oLastSeries.aiErrorBarBelow.append(aoRow[6] - aoRow[5]);
+ oLastSeries.aiErrorBarAbove.append(aoRow[7] - aoRow[6]);
+ oLastSeries.acSamples.append(aoRow[8]);
+ dRevisions[aoRow[0]] = 1;
+
+ if sCurRepository is not None:
+ self.oCache.preloadVcsRevInfo(sCurRepository, dRevisions.keys());
+ del dRevisions;
+
+ #
+ # Look up the VCS revision details.
+ #
+ for oSeries in oCollection.aoSeries:
+ for iRevision in oSeries.aiRevisions:
+ oSeries.aoRevInfo.append(self.oCache.getVcsRevInfo(sCurRepository, iRevision));
+ aoRet.append(oCollection);
+
+ return aoRet;
+
+ def getEligibleTestBoxes(self):
+ """
+ Returns a list of TestBoxData objects with eligible testboxes for
+ the total period of time defined for this graph.
+ """
+
+ # Taking the simple way out now, getting all active testboxes at the
+ # time without filtering out on sample sources.
+
+ # 1. Collect the relevant testbox generation IDs.
+ self._oDb.execute('SELECT DISTINCT idTestBox, idGenTestBox\n'
+ 'FROM TestSets\n'
+ 'WHERE ' + self._getEligibleTestSetPeriod(fLeadingAnd = False) +
+ 'ORDER BY idTestBox, idGenTestBox DESC');
+ idPrevTestBox = -1;
+ asIdGenTestBoxes = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRow = self._oDb.fetchOne();
+ if aoRow[0] != idPrevTestBox:
+ idPrevTestBox = aoRow[0];
+ asIdGenTestBoxes.append(str(aoRow[1]));
+
+ # 2. Query all the testbox data in one go.
+ aoRet = [];
+ if asIdGenTestBoxes:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE idGenTestBox IN (' + ','.join(asIdGenTestBoxes) + ')\n'
+ 'ORDER BY sName');
+ for _ in range(self._oDb.getRowCount()):
+ aoRet.append(TestBoxData().initFromDbRow(self._oDb.fetchOne()));
+
+ return aoRet;
+
+ def getEligibleBuildCategories(self):
+ """
+ Returns a list of BuildCategoryData objects with eligible build
+ categories for the total period of time defined for this graph. In
+ addition it will add any currently selected categories that aren't
+ really relevant to the period, just to simplify the WUI code.
+
+ """
+
+ # Taking the simple way out now, getting all used build cat without
+ # any testbox or testcase filtering.
+
+ sSelectedBuildCats = '';
+ if self.aidBuildCats:
+ sSelectedBuildCats = ' OR idBuildCategory IN (' + ','.join([str(i) for i in self.aidBuildCats]) + ')\n';
+
+ self._oDb.execute('SELECT DISTINCT *\n'
+ 'FROM BuildCategories\n'
+ 'WHERE idBuildCategory IN (\n'
+ ' SELECT DISTINCT idBuildCategory\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getEligibleTestSetPeriod(fLeadingAnd = False) +
+ ')\n'
+ + sSelectedBuildCats +
+ 'ORDER BY sProduct,\n'
+ ' sBranch,\n'
+ ' asOsArches,\n'
+ ' sType\n');
+ aoRet = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRet.append(BuildCategoryData().initFromDbRow(self._oDb.fetchOne()));
+
+ return aoRet;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/restdispatcher.py b/src/VBox/ValidationKit/testmanager/core/restdispatcher.py
new file mode 100755
index 00000000..75a1aa7c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/restdispatcher.py
@@ -0,0 +1,455 @@
+# -*- coding: utf-8 -*-
+# $Id: restdispatcher.py $
+
+"""
+Test Manager Core - REST cgi handler.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os;
+import sys;
+
+# Validation Kit imports.
+#from common import constants;
+from common import utils;
+from testmanager import config;
+#from testmanager.core import coreconsts;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.base import TMExceptionBase, ModelDataBase;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+#
+# Exceptions
+#
+
+class RestDispException(TMExceptionBase):
+ """
+ Exception class for the REST dispatcher.
+ """
+ def __init__(self, sMsg, iStatus):
+ TMExceptionBase.__init__(self, sMsg);
+ self.iStatus = iStatus;
+
+# 400
+class RestDispException400(RestDispException):
+ """ A 400 error """
+ def __init__(self, sMsg):
+ RestDispException.__init__(self, sMsg, 400);
+
+class RestUnknownParameters(RestDispException400):
+ """ Unknown parameter(s). """
+ pass; # pylint: disable=unnecessary-pass
+
+# 404
+class RestDispException404(RestDispException):
+ """ A 404 error """
+ def __init__(self, sMsg):
+ RestDispException.__init__(self, sMsg, 404);
+
+class RestBadPathException(RestDispException404):
+ """ We've got a bad path. """
+ pass; # pylint: disable=unnecessary-pass
+
+class RestBadParameter(RestDispException404):
+ """ Bad parameter. """
+ pass; # pylint: disable=unnecessary-pass
+
+class RestMissingParameter(RestDispException404):
+ """ Missing parameter. """
+ pass; # pylint: disable=unnecessary-pass
+
+
+
+class RestMain(object): # pylint: disable=too-few-public-methods
+ """
+ REST main dispatcher class.
+ """
+
+ ksParam_sPath = 'sPath';
+
+
+ def __init__(self, oSrvGlue):
+ self._oSrvGlue = oSrvGlue;
+ self._oDb = TMDatabaseConnection(oSrvGlue.dprint);
+ self._iFirstHandlerPath = 0;
+ self._iNextHandlerPath = 0;
+ self._sPath = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._asPath = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._sMethod = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._dParams = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._asCheckedParams = [];
+ self._dGetTree = {
+ 'vcs': {
+ 'changelog': self._handleVcsChangelog_Get,
+ 'bugreferences': self._handleVcsBugReferences_Get,
+ },
+ };
+ self._dMethodTrees = {
+ 'GET': self._dGetTree,
+ }
+
+ #
+ # Helpers.
+ #
+
+ def _getStringParam(self, sName, asValidValues = None, fStrip = False, sDefValue = None):
+ """
+ Gets a string parameter (stripped).
+
+ Raises exception if not found and no default is provided, or if the
+ value isn't found in asValidValues.
+ """
+ if sName not in self._dParams:
+ if sDefValue is None:
+ raise RestMissingParameter('%s parameter %s is missing' % (self._sPath, sName));
+ return sDefValue;
+ sValue = self._dParams[sName];
+ if isinstance(sValue, list):
+ if len(sValue) == 1:
+ sValue = sValue[0];
+ else:
+ raise RestBadParameter('%s parameter %s value is not a string but list: %s'
+ % (self._sPath, sName, sValue));
+ if fStrip:
+ sValue = sValue.strip();
+
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if asValidValues is not None and sValue not in asValidValues:
+ raise RestBadParameter('%s parameter %s value "%s" not in %s '
+ % (self._sPath, sName, sValue, asValidValues));
+ return sValue;
+
+ def _getBoolParam(self, sName, fDefValue = None):
+ """
+ Gets a boolean parameter.
+
+ Raises exception if not found and no default is provided, or if not a
+ valid boolean.
+ """
+ sValue = self._getStringParam(sName, [ 'True', 'true', '1', 'False', 'false', '0'], sDefValue = str(fDefValue));
+ return sValue in ('True', 'true', '1',);
+
+ def _getIntParam(self, sName, iMin = None, iMax = None):
+ """
+ Gets a string parameter.
+ Raises exception if not found, not a valid integer, or if the value
+ isn't in the range defined by iMin and iMax.
+ """
+ sValue = self._getStringParam(sName);
+ try:
+ iValue = int(sValue, 0);
+ except:
+ raise RestBadParameter('%s parameter %s value "%s" cannot be convert to an integer'
+ % (self._sPath, sName, sValue));
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise RestBadParameter('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sPath, sName, iValue, iMin, iMax));
+ return iValue;
+
+ def _getLongParam(self, sName, lMin = None, lMax = None, lDefValue = None):
+ """
+ Gets a string parameter.
+ Raises exception if not found, not a valid long integer, or if the value
+ isn't in the range defined by lMin and lMax.
+ """
+ sValue = self._getStringParam(sName, sDefValue = (str(lDefValue) if lDefValue is not None else None));
+ try:
+ lValue = long(sValue, 0);
+ except Exception as oXcpt:
+ raise RestBadParameter('%s parameter %s value "%s" cannot be convert to an integer (%s)'
+ % (self._sPath, sName, sValue, oXcpt));
+
+ if (lMin is not None and lValue < lMin) \
+ or (lMax is not None and lValue > lMax):
+ raise RestBadParameter('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sPath, sName, lValue, lMin, lMax));
+ return lValue;
+
+ def _checkForUnknownParameters(self):
+ """
+ Check if we've handled all parameters, raises exception if anything
+ unknown was found.
+ """
+
+ if len(self._asCheckedParams) != len(self._dParams):
+ sUnknownParams = '';
+ for sKey in self._dParams:
+ if sKey not in self._asCheckedParams:
+ sUnknownParams += ' ' + sKey + '=' + self._dParams[sKey];
+ raise RestUnknownParameters('Unknown parameters: ' + sUnknownParams);
+
+ return True;
+
+ def writeToMainLog(self, oTestSet, sText, fIgnoreSizeCheck = False):
+ """ Writes the text to the main log file. """
+
+ # Calc the file name and open the file.
+ sFile = os.path.join(config.g_ksFileAreaRootDir, oTestSet.sBaseFilename + '-main.log');
+ if not os.path.exists(os.path.dirname(sFile)):
+ os.makedirs(os.path.dirname(sFile), 0o755);
+
+ with open(sFile, 'ab') as oFile:
+ # Check the size.
+ fSizeOk = True;
+ if not fIgnoreSizeCheck:
+ oStat = os.fstat(oFile.fileno());
+ fSizeOk = oStat.st_size / (1024 * 1024) < config.g_kcMbMaxMainLog;
+
+ # Write the text.
+ if fSizeOk:
+ if sys.version_info[0] >= 3:
+ oFile.write(bytes(sText, 'utf-8'));
+ else:
+ oFile.write(sText);
+
+ return fSizeOk;
+
+ def _getNextPathElementString(self, sName, oDefault = None):
+ """
+ Gets the next handler specific path element.
+ Returns unprocessed string.
+ Throws exception
+ """
+ i = self._iNextHandlerPath;
+ if i < len(self._asPath):
+ self._iNextHandlerPath = i + 1;
+ return self._asPath[i];
+ if oDefault is None:
+ raise RestBadPathException('Requires a "%s" element after "%s"' % (sName, self._sPath,));
+ return oDefault;
+
+ def _getNextPathElementInt(self, sName, iDefault = None, iMin = None, iMax = None):
+ """
+ Gets the next handle specific path element as an integer.
+ Returns integer value.
+ Throws exception if not found or not a valid integer.
+ """
+ sValue = self._getNextPathElementString(sName, oDefault = iDefault);
+ try:
+ iValue = int(sValue);
+ except:
+ raise RestBadPathException('Not an integer "%s" (%s)' % (sValue, sName,));
+ if iMin is not None and iValue < iMin:
+ raise RestBadPathException('Integer "%s" value (%s) is too small, min %s' % (sValue, sName, iMin));
+ if iMax is not None and iValue > iMax:
+ raise RestBadPathException('Integer "%s" value (%s) is too large, max %s' % (sValue, sName, iMax));
+ return iValue;
+
+ def _getNextPathElementLong(self, sName, iDefault = None, iMin = None, iMax = None):
+ """
+ Gets the next handle specific path element as a long integer.
+ Returns integer value.
+ Throws exception if not found or not a valid integer.
+ """
+ sValue = self._getNextPathElementString(sName, oDefault = iDefault);
+ try:
+ iValue = long(sValue);
+ except:
+ raise RestBadPathException('Not an integer "%s" (%s)' % (sValue, sName,));
+ if iMin is not None and iValue < iMin:
+ raise RestBadPathException('Integer "%s" value (%s) is too small, min %s' % (sValue, sName, iMin));
+ if iMax is not None and iValue > iMax:
+ raise RestBadPathException('Integer "%s" value (%s) is too large, max %s' % (sValue, sName, iMax));
+ return iValue;
+
+ def _checkNoMorePathElements(self):
+ """
+ Checks that there are no more path elements.
+ Throws exception if there are.
+ """
+ i = self._iNextHandlerPath;
+ if i < len(self._asPath):
+ raise RestBadPathException('Unknown subpath "%s" below "%s"' %
+ ('/'.join(self._asPath[i:]), '/'.join(self._asPath[:i]),));
+ return True;
+
+ def _doneParsingArguments(self):
+ """
+ Checks that there are no more path elements or unhandled parameters.
+ Throws exception if there are.
+ """
+ self._checkNoMorePathElements();
+ self._checkForUnknownParameters();
+ return True;
+
+ def _dataArrayToJsonReply(self, aoData, sName = 'aoData', dExtraFields = None, iStatus = 200):
+ """
+ Converts aoData into an array objects
+ return True.
+ """
+ self._oSrvGlue.setContentType('application/json');
+ self._oSrvGlue.setStatus(iStatus);
+ self._oSrvGlue.write(u'{\n');
+ if dExtraFields:
+ for sKey in dExtraFields:
+ self._oSrvGlue.write(u' "%s": %s,\n' % (sKey, ModelDataBase.genericToJson(dExtraFields[sKey]),));
+ self._oSrvGlue.write(u' "c%s": %u,\n' % (sName[2:],len(aoData),));
+ self._oSrvGlue.write(u' "%s": [\n' % (sName,));
+ for i, oData in enumerate(aoData):
+ if i > 0:
+ self._oSrvGlue.write(u',\n');
+ self._oSrvGlue.write(ModelDataBase.genericToJson(oData));
+ self._oSrvGlue.write(u' ]\n');
+ ## @todo if config.g_kfWebUiSqlDebug:
+ self._oSrvGlue.write(u'}\n');
+ self._oSrvGlue.flush();
+ return True;
+
+
+ #
+ # Handlers.
+ #
+
+ def _handleVcsChangelog_Get(self):
+ """ GET /vcs/changelog/{sRepository}/{iStartRev}[/{cEntriesBack}] """
+ # Parse arguments
+ sRepository = self._getNextPathElementString('sRepository');
+ iStartRev = self._getNextPathElementInt('iStartRev', iMin = 0);
+ cEntriesBack = self._getNextPathElementInt('cEntriesBack', iDefault = 32, iMin = 0, iMax = 8192);
+ self._checkNoMorePathElements();
+ self._checkForUnknownParameters();
+
+ # Execute it.
+ from testmanager.core.vcsrevisions import VcsRevisionLogic;
+ oLogic = VcsRevisionLogic(self._oDb);
+ return self._dataArrayToJsonReply(oLogic.fetchTimeline(sRepository, iStartRev, cEntriesBack), 'aoCommits',
+ { 'sTracChangesetUrlFmt':
+ config.g_ksTracChangsetUrlFmt.replace('%(sRepository)s', sRepository), } );
+
+ def _handleVcsBugReferences_Get(self):
+ """ GET /vcs/bugreferences/{sTrackerId}/{lBugId} """
+ # Parse arguments
+ sTrackerId = self._getNextPathElementString('sTrackerId');
+ lBugId = self._getNextPathElementLong('lBugId', iMin = 0);
+ self._checkNoMorePathElements();
+ self._checkForUnknownParameters();
+
+ # Execute it.
+ from testmanager.core.vcsbugreference import VcsBugReferenceLogic;
+ oLogic = VcsBugReferenceLogic(self._oDb);
+ oLogic.fetchForBug(sTrackerId, lBugId)
+ return self._dataArrayToJsonReply(oLogic.fetchForBug(sTrackerId, lBugId), 'aoCommits',
+ { 'sTracChangesetUrlFmt': config.g_ksTracChangsetUrlFmt, } );
+
+
+ #
+ # Dispatching.
+ #
+
+ def _dispatchRequestCommon(self):
+ """
+ Dispatches the incoming request after have gotten the path and parameters.
+
+ Will raise RestDispException on failure.
+ """
+
+ #
+ # Split up the path.
+ #
+ asPath = self._sPath.split('/');
+ self._asPath = asPath;
+
+ #
+ # Get the method and the corresponding handler tree.
+ #
+ try:
+ sMethod = self._oSrvGlue.getMethod();
+ except Exception as oXcpt:
+ raise RestDispException('Error retriving request method: %s' % (oXcpt,), 400);
+ self._sMethod = sMethod;
+
+ try:
+ dTree = self._dMethodTrees[sMethod];
+ except KeyError:
+ raise RestDispException('Unsupported method %s' % (sMethod,), 405);
+
+ #
+ # Walk the path till we find a handler for it.
+ #
+ iPath = 0;
+ while iPath < len(asPath):
+ try:
+ oTreeOrHandler = dTree[asPath[iPath]];
+ except KeyError:
+ raise RestBadPathException('Path element #%u "%s" not found (path="%s")' % (iPath, asPath[iPath], self._sPath));
+ iPath += 1;
+ if isinstance(oTreeOrHandler, dict):
+ dTree = oTreeOrHandler;
+ else:
+ #
+ # Call the handler.
+ #
+ self._iFirstHandlerPath = iPath;
+ self._iNextHandlerPath = iPath;
+ return oTreeOrHandler();
+
+ raise RestBadPathException('Empty path (%s)' % (self._sPath,));
+
+ def dispatchRequest(self):
+ """
+ Dispatches the incoming request where the path is given as an argument.
+
+ Will raise RestDispException on failure.
+ """
+
+ #
+ # Get the parameters.
+ #
+ try:
+ dParams = self._oSrvGlue.getParameters();
+ except Exception as oXcpt:
+ raise RestDispException('Error retriving parameters: %s' % (oXcpt,), 500);
+ self._dParams = dParams;
+
+ #
+ # Get the path parameter.
+ #
+ if self.ksParam_sPath not in dParams:
+ raise RestDispException('No "%s" parameter in request (params: %s)' % (self.ksParam_sPath, dParams,), 500);
+ self._sPath = self._getStringParam(self.ksParam_sPath);
+ assert utils.isString(self._sPath);
+
+ return self._dispatchRequestCommon();
+
diff --git a/src/VBox/ValidationKit/testmanager/core/schedgroup.py b/src/VBox/ValidationKit/testmanager/core/schedgroup.py
new file mode 100755
index 00000000..a586a6bf
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/schedgroup.py
@@ -0,0 +1,1352 @@
+# -*- coding: utf-8 -*-
+# $Id: schedgroup.py $
+
+"""
+Test Manager - Scheduling Group.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
+ TMRowInUse, TMInvalidData, TMRowAlreadyExists, TMRowNotFound, \
+ ChangeLogEntry, AttributeChangeEntry, AttributeChangeEntryPre;
+from testmanager.core.buildsource import BuildSourceData;
+from testmanager.core import db;
+from testmanager.core.testcase import TestCaseData;
+from testmanager.core.testcaseargs import TestCaseArgsData;
+from testmanager.core.testbox import TestBoxLogic, TestBoxDataForSchedGroup;
+from testmanager.core.testgroup import TestGroupData;
+from testmanager.core.useraccount import UserAccountLogic;
+
+
+
+class SchedGroupMemberData(ModelDataBase):
+ """
+ SchedGroupMember Data.
+ """
+
+ ksIdAttr = 'idSchedGroup';
+
+ ksParam_idSchedGroup = 'SchedGroupMember_idSchedGroup';
+ ksParam_idTestGroup = 'SchedGroupMember_idTestGroup';
+ ksParam_tsEffective = 'SchedGroupMember_tsEffective';
+ ksParam_tsExpire = 'SchedGroupMember_tsExpire';
+ ksParam_uidAuthor = 'SchedGroupMember_uidAuthor';
+ ksParam_iSchedPriority = 'SchedGroupMember_iSchedPriority';
+ ksParam_bmHourlySchedule = 'SchedGroupMember_bmHourlySchedule';
+ ksParam_idTestGroupPreReq = 'SchedGroupMember_idTestGroupPreReq';
+
+ kasAllowNullAttributes = [ 'idSchedGroup', 'idTestGroup', 'tsEffective', 'tsExpire',
+ 'uidAuthor', 'bmHourlySchedule', 'idTestGroupPreReq' ];
+ kiMin_iSchedPriority = 0;
+ kiMax_iSchedPriority = 32;
+
+ kcDbColumns = 8
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idSchedGroup = None;
+ self.idTestGroup = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.iSchedPriority = 16;
+ self.bmHourlySchedule = None;
+ self.idTestGroupPreReq = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a SELECT * FROM SchedGroupMembers.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('SchedGroupMember not found.');
+
+ self.idSchedGroup = aoRow[0];
+ self.idTestGroup = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ self.iSchedPriority = aoRow[5];
+ self.bmHourlySchedule = aoRow[6]; ## @todo figure out how bitmaps are returned...
+ self.idTestGroupPreReq = aoRow[7];
+ return self;
+
+
+class SchedGroupMemberDataEx(SchedGroupMemberData):
+ """
+ Extended SchedGroupMember data class.
+ This adds the testgroups.
+ """
+
+ def __init__(self):
+ SchedGroupMemberData.__init__(self);
+ self.oTestGroup = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a query like this:
+
+ SELECT SchedGroupMembers.*, TestGroups.*
+ FROM SchedGroupMembers
+ JOIN TestGroups
+ ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup);
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+ SchedGroupMemberData.initFromDbRow(self, aoRow);
+ self.oTestGroup = TestGroupData().initFromDbRow(aoRow[SchedGroupMemberData.kcDbColumns:]);
+ return self;
+
+ def getDataAttributes(self):
+ asAttributes = SchedGroupMemberData.getDataAttributes(self);
+ asAttributes.remove('oTestGroup');
+ return asAttributes;
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ dErrors = SchedGroupMemberData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+ if self.ksParam_idTestGroup not in dErrors:
+ self.oTestGroup = TestGroupData();
+ try:
+ self.oTestGroup.initFromDbWithId(oDb, self.idTestGroup);
+ except Exception as oXcpt:
+ self.oTestGroup = TestGroupData()
+ dErrors[self.ksParam_idTestGroup] = str(oXcpt);
+ return dErrors;
+
+
+
+
+class SchedGroupData(ModelDataBase):
+ """
+ SchedGroup Data.
+ """
+
+ ## @name TestBoxState_T
+ # @{
+ ksScheduler_BestEffortContinuousIntegration = 'bestEffortContinousItegration'; # sic*2
+ ksScheduler_Reserved = 'reserved';
+ ## @}
+
+
+ ksIdAttr = 'idSchedGroup';
+
+ ksParam_idSchedGroup = 'SchedGroup_idSchedGroup';
+ ksParam_tsEffective = 'SchedGroup_tsEffective';
+ ksParam_tsExpire = 'SchedGroup_tsExpire';
+ ksParam_uidAuthor = 'SchedGroup_uidAuthor';
+ ksParam_sName = 'SchedGroup_sName';
+ ksParam_sDescription = 'SchedGroup_sDescription';
+ ksParam_fEnabled = 'SchedGroup_fEnabled';
+ ksParam_enmScheduler = 'SchedGroup_enmScheduler';
+ ksParam_idBuildSrc = 'SchedGroup_idBuildSrc';
+ ksParam_idBuildSrcTestSuite = 'SchedGroup_idBuildSrcTestSuite';
+ ksParam_sComment = 'SchedGroup_sComment';
+
+ kasAllowNullAttributes = ['idSchedGroup', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription',
+ 'idBuildSrc', 'idBuildSrcTestSuite', 'sComment' ];
+ kasValidValues_enmScheduler = [ ksScheduler_BestEffortContinuousIntegration, ];
+
+ kcDbColumns = 11;
+
+ # Scheduler types
+ kasSchedulerDesc = \
+ [
+ ( ksScheduler_BestEffortContinuousIntegration, 'Best-Effort-Continuous-Integration (BECI) scheduler.', ''),
+ ]
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idSchedGroup = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.sName = None;
+ self.sDescription = None;
+ self.fEnabled = None;
+ self.enmScheduler = SchedGroupData.ksScheduler_BestEffortContinuousIntegration;
+ self.idBuildSrc = None;
+ self.idBuildSrcTestSuite = None;
+ self.sComment = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the data with a row from a SELECT * FROM SchedGroups.
+
+ Returns self. Raises exception if the row is None or otherwise invalid.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('SchedGroup not found.');
+
+ self.idSchedGroup = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.sName = aoRow[4];
+ self.sDescription = aoRow[5];
+ self.fEnabled = aoRow[6];
+ self.enmScheduler = aoRow[7];
+ self.idBuildSrc = aoRow[8];
+ self.idBuildSrcTestSuite = aoRow[9];
+ self.sComment = aoRow[10];
+ return self;
+
+ def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE idSchedGroup = %s\n'
+ , ( idSchedGroup,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idSchedGroup=%s not found (tsNow=%s, sPeriodBack=%s)' % (idSchedGroup, tsNow, sPeriodBack));
+ return self.initFromDbRow(aoRow);
+
+
+class SchedGroupDataEx(SchedGroupData):
+ """
+ Extended scheduling group data.
+
+ Note! Similar to TestGroupDataEx.
+ """
+
+ ksParam_aoMembers = 'SchedGroup_aoMembers';
+ ksParam_aoTestBoxes = 'SchedGroup_aoTestboxes';
+ kasAltArrayNull = [ 'aoMembers', 'aoTestboxes' ];
+
+ ## Helper parameter containing the comma separated list with the IDs of
+ # potential members found in the parameters.
+ ksParam_aidTestGroups = 'TestGroupDataEx_aidTestGroups';
+ ## Ditto for testbox meembers.
+ ksParam_aidTestBoxes = 'TestGroupDataEx_aidTestBoxes';
+
+
+ def __init__(self):
+ SchedGroupData.__init__(self);
+ self.aoMembers = [] # type: list[SchedGroupMemberDataEx]
+ self.aoTestBoxes = [] # type: list[TestBoxDataForSchedGroup]
+
+ # The two build sources for the sake of convenience.
+ self.oBuildSrc = None # type: BuildSourceData
+ self.oBuildSrcValidationKit = None # type: BuildSourceData
+
+ def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Worker shared by the initFromDb* methods.
+ Returns self. Raises exception if no row or database error.
+ """
+ #
+ # Clear all members upfront so the object has some kind of consistency
+ # if anything below raises exceptions.
+ #
+ self.oBuildSrc = None;
+ self.oBuildSrcValidationKit = None;
+ self.aoTestBoxes = [];
+ self.aoMembers = [];
+
+ #
+ # Build source.
+ #
+ if self.idBuildSrc:
+ self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc, tsNow, sPeriodBack);
+
+ if self.idBuildSrcTestSuite:
+ self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite,
+ tsNow, sPeriodBack);
+
+ #
+ # Test Boxes.
+ #
+ self.aoTestBoxes = TestBoxLogic(oDb).fetchForSchedGroup(self.idSchedGroup, tsNow);
+
+ #
+ # Test groups.
+ # The fetchForChangeLog method makes ASSUMPTIONS about sorting!
+ #
+ oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
+ 'FROM SchedGroupMembers\n'
+ 'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n'
+ 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
+ + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'SchedGroupMembers.')
+ + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'TestGroups.') +
+ 'ORDER BY SchedGroupMembers.idTestGroupPreReq ASC NULLS FIRST,\n'
+ ' TestGroups.sName,\n'
+ ' SchedGroupMembers.idTestGroup\n'
+ , (self.idSchedGroup,));
+ for aoRow in oDb.fetchAll():
+ self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
+ return self;
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
+ """
+ Reinitialize from a SELECT * FROM SchedGroups row. Will query the
+ necessary additional data from oDb using tsNow.
+ Returns self. Raises exception if no row or database error.
+ """
+ SchedGroupData.initFromDbRow(self, aoRow);
+ return self._initExtraMembersFromDb(oDb, tsNow);
+
+ def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ SchedGroupData.initFromDbWithId(self, oDb, idSchedGroup, tsNow, sPeriodBack);
+ return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
+
+ def getDataAttributes(self):
+ asAttributes = SchedGroupData.getDataAttributes(self);
+ asAttributes.remove('oBuildSrc');
+ asAttributes.remove('oBuildSrcValidationKit');
+ return asAttributes;
+
+ def getAttributeParamNullValues(self, sAttr):
+ if sAttr not in [ 'aoMembers', 'aoTestBoxes' ]:
+ return SchedGroupData.getAttributeParamNullValues(self, sAttr);
+ return ['', [], None];
+
+ def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
+ aoNewValue = [];
+ if sAttr == 'aoMembers':
+ aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
+ sIds = oDisp.getStringParam(self.ksParam_aidTestGroups, sDefault = '');
+ for idTestGroup in sIds.split(','):
+ try: idTestGroup = int(idTestGroup);
+ except: pass;
+ oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoMembers, idTestGroup,))
+ oMember = SchedGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
+ if idTestGroup in aidSelected:
+ oMember.idTestGroup = idTestGroup;
+ aoNewValue.append(oMember);
+ elif sAttr == 'aoTestBoxes':
+ aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
+ sIds = oDisp.getStringParam(self.ksParam_aidTestBoxes, sDefault = '');
+ for idTestBox in sIds.split(','):
+ try: idTestBox = int(idTestBox);
+ except: pass;
+ oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoTestBoxes, idTestBox,))
+ oBoxInGrp = TestBoxDataForSchedGroup().initFromParams(oDispWrapper, fStrict = False);
+ if idTestBox in aidSelected:
+ oBoxInGrp.idTestBox = idTestBox;
+ aoNewValue.append(oBoxInGrp);
+ else:
+ return SchedGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
+ return aoNewValue;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ if sAttr not in [ 'aoMembers', 'aoTestBoxes' ]:
+ return SchedGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ if oValue in aoNilValues:
+ return ([], None);
+
+ asErrors = [];
+ aoNewMembers = [];
+ if sAttr == 'aoMembers':
+ asAllowNulls = ['bmHourlySchedule', 'idTestGroupPreReq', 'tsEffective', 'tsExpire', 'uidAuthor', ];
+ if self.idSchedGroup in [None, '-1', -1]:
+ asAllowNulls.append('idSchedGroup'); # Probably new group, so allow null scheduling group.
+
+ for oOldMember in oValue:
+ oNewMember = SchedGroupMemberDataEx().initFromOther(oOldMember);
+ aoNewMembers.append(oNewMember);
+
+ dErrors = oNewMember.validateAndConvertEx(asAllowNulls, oDb, ModelDataBase.ksValidateFor_Other);
+ if dErrors:
+ asErrors.append(str(dErrors));
+
+ if not asErrors:
+ for i, _ in enumerate(aoNewMembers):
+ idTestGroup = aoNewMembers[i];
+ for j in range(i + 1, len(aoNewMembers)):
+ if aoNewMembers[j].idTestGroup == idTestGroup:
+ asErrors.append('Duplicate test group #%d!' % (idTestGroup, ));
+ break;
+ else:
+ asAllowNulls = list(TestBoxDataForSchedGroup.kasAllowNullAttributes);
+ if self.idSchedGroup in [None, '-1', -1]:
+ asAllowNulls.append('idSchedGroup'); # Probably new group, so allow null scheduling group.
+
+ for oOldMember in oValue:
+ oNewMember = TestBoxDataForSchedGroup().initFromOther(oOldMember);
+ aoNewMembers.append(oNewMember);
+
+ dErrors = oNewMember.validateAndConvertEx(asAllowNulls, oDb, ModelDataBase.ksValidateFor_Other);
+ if dErrors:
+ asErrors.append(str(dErrors));
+
+ if not asErrors:
+ for i, _ in enumerate(aoNewMembers):
+ idTestBox = aoNewMembers[i];
+ for j in range(i + 1, len(aoNewMembers)):
+ if aoNewMembers[j].idTestBox == idTestBox:
+ asErrors.append('Duplicate test box #%d!' % (idTestBox, ));
+ break;
+
+ return (aoNewMembers, None if not asErrors else '<br>\n'.join(asErrors));
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ dErrors = SchedGroupData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+
+ #
+ # Fetch the extended build source bits.
+ #
+ if self.ksParam_idBuildSrc not in dErrors:
+ if self.idBuildSrc in self.getAttributeParamNullValues('idBuildSrc') \
+ or self.idBuildSrc is None:
+ self.oBuildSrc = None;
+ else:
+ try:
+ self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc);
+ except Exception as oXcpt:
+ self.oBuildSrc = BuildSourceData();
+ dErrors[self.ksParam_idBuildSrc] = str(oXcpt);
+
+ if self.ksParam_idBuildSrcTestSuite not in dErrors:
+ if self.idBuildSrcTestSuite in self.getAttributeParamNullValues('idBuildSrcTestSuite') \
+ or self.idBuildSrcTestSuite is None:
+ self.oBuildSrcValidationKit = None;
+ else:
+ try:
+ self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite);
+ except Exception as oXcpt:
+ self.oBuildSrcValidationKit = BuildSourceData();
+ dErrors[self.ksParam_idBuildSrcTestSuite] = str(oXcpt);
+
+ return dErrors;
+
+
+
+class SchedGroupLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ SchedGroup logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+ self.dCache = None;
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches build sources.
+
+ Returns an array (list) of BuildSourceData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY fEnabled DESC, sName DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY fEnabled DESC, sName DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
+ return aoRet;
+
+ def fetchForChangeLog(self, idSchedGroup, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Fetches change log entries for a scheduling group.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+
+ ## @todo calc changes to scheduler group!
+
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ #
+ # First gather the change log timeline using the effective dates.
+ # (ASSUMES that we'll always have a separate delete entry, rather
+ # than just setting tsExpire.)
+ #
+ self._oDb.execute('''
+(
+SELECT tsEffective,
+ uidAuthor
+FROM SchedGroups
+WHERE idSchedGroup = %s
+ AND tsEffective <= %s
+ORDER BY tsEffective DESC
+) UNION (
+SELECT CASE WHEN tsEffective + %s::INTERVAL = tsExpire THEN tsExpire ELSE tsEffective END,
+ uidAuthor
+FROM SchedGroupMembers
+WHERE idSchedGroup = %s
+ AND tsEffective <= %s
+ORDER BY tsEffective DESC
+) UNION (
+SELECT CASE WHEN tsEffective + %s::INTERVAL = tsExpire THEN tsExpire ELSE tsEffective END,
+ uidAuthor
+FROM TestBoxesInSchedGroups
+WHERE idSchedGroup = %s
+ AND tsEffective <= %s
+ORDER BY tsEffective DESC
+)
+ORDER BY tsEffective DESC
+LIMIT %s OFFSET %s
+''', (idSchedGroup, tsNow,
+ db.dbOneTickIntervalString(), idSchedGroup, tsNow,
+ db.dbOneTickIntervalString(), idSchedGroup, tsNow,
+ cMaxRows + 1, iStart, ));
+
+ aoEntries = [] # type: list[ChangeLogEntry]
+ tsPrevious = tsNow;
+ for aoDbRow in self._oDb.fetchAll():
+ (tsEffective, uidAuthor) = aoDbRow;
+ aoEntries.append(ChangeLogEntry(uidAuthor, None, tsEffective, tsPrevious, None, None, []));
+ tsPrevious = db.dbTimestampPlusOneTick(tsEffective);
+
+ if True: # pylint: disable=using-constant-test
+ #
+ # Fetch data for each for each change log entry point.
+ #
+ # We add one tick to the timestamp here to skip past delete records
+ # that only there to record the user doing the deletion.
+ #
+ for iEntry, oEntry in enumerate(aoEntries):
+ oEntry.oNewRaw = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup, oEntry.tsEffective);
+ if iEntry > 0:
+ aoEntries[iEntry - 1].oOldRaw = oEntry.oNewRaw;
+
+ # Chop off the +1 entry, if any.
+ fMore = len(aoEntries) > cMaxRows;
+ if fMore:
+ aoEntries = aoEntries[:-1];
+
+ # Figure out the changes.
+ for oEntry in aoEntries:
+ oOld = oEntry.oOldRaw;
+ if not oOld:
+ break;
+ oNew = oEntry.oNewRaw;
+ aoChanges = oEntry.aoChanges;
+ for sAttr in oNew.getDataAttributes():
+ if sAttr in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ continue;
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr == oNewAttr:
+ continue;
+ if sAttr in [ 'aoMembers', 'aoTestBoxes', ]:
+ iNew = 0;
+ iOld = 0;
+ asNewAttr = [];
+ asOldAttr = [];
+ if sAttr == 'aoMembers':
+ # ASSUMES aoMembers is sorted by idTestGroupPreReq (nulls first), oTestGroup.sName, idTestGroup!
+ while iNew < len(oNewAttr) and iOld < len(oOldAttr):
+ if oNewAttr[iNew].idTestGroup == oOldAttr[iOld].idTestGroup:
+ if oNewAttr[iNew].idTestGroupPreReq != oOldAttr[iOld].idTestGroupPreReq:
+ if oNewAttr[iNew].idTestGroupPreReq is None:
+ asOldAttr.append('Dropped test group #%s (%s) dependency on #%s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oOldAttr[iOld].idTestGroupPreReq));
+ elif oOldAttr[iOld].idTestGroupPreReq is None:
+ asNewAttr.append('Added test group #%s (%s) dependency on #%s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oNewAttr[iOld].idTestGroupPreReq));
+ else:
+ asNewAttr.append('Test group #%s (%s) dependency on #%s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oNewAttr[iNew].idTestGroupPreReq));
+ asOldAttr.append('Test group #%s (%s) dependency on #%s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oOldAttr[iOld].idTestGroupPreReq));
+ if oNewAttr[iNew].iSchedPriority != oOldAttr[iOld].iSchedPriority:
+ asNewAttr.append('Test group #%s (%s) priority %s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oNewAttr[iNew].iSchedPriority));
+ asOldAttr.append('Test group #%s (%s) priority %s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
+ oOldAttr[iOld].iSchedPriority));
+ iNew += 1;
+ iOld += 1;
+ elif oNewAttr[iNew].oTestGroup.sName < oOldAttr[iOld].oTestGroup.sName \
+ or ( oNewAttr[iNew].oTestGroup.sName == oOldAttr[iOld].oTestGroup.sName
+ and oNewAttr[iNew].idTestGroup < oOldAttr[iOld].idTestGroup):
+ asNewAttr.append('New test group #%s - %s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName));
+ iNew += 1;
+ else:
+ asOldAttr.append('Removed test group #%s - %s'
+ % (oOldAttr[iOld].idTestGroup, oOldAttr[iOld].oTestGroup.sName));
+ iOld += 1;
+ while iNew < len(oNewAttr):
+ asNewAttr.append('New test group #%s - %s'
+ % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName));
+ iNew += 1;
+ while iOld < len(oOldAttr):
+ asOldAttr.append('Removed test group #%s - %s'
+ % (oOldAttr[iOld].idTestGroup, oOldAttr[iOld].oTestGroup.sName));
+ iOld += 1;
+ else:
+ dNewIds = { oBoxInGrp.idTestBox: oBoxInGrp for oBoxInGrp in oNewAttr };
+ dOldIds = { oBoxInGrp.idTestBox: oBoxInGrp for oBoxInGrp in oOldAttr };
+ hCommonIds = set(dNewIds.keys()) & set(dOldIds.keys());
+ for idTestBox in hCommonIds:
+ oNewBoxInGrp = dNewIds[idTestBox];
+ oOldBoxInGrp = dOldIds[idTestBox];
+ if oNewBoxInGrp.iSchedPriority != oOldBoxInGrp.iSchedPriority:
+ asNewAttr.append('Test box \'%s\' (#%s) priority %s'
+ % (getattr(oNewBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
+ oNewBoxInGrp.idTestBox, oNewBoxInGrp.iSchedPriority));
+ asOldAttr.append('Test box \'%s\' (#%s) priority %s'
+ % (getattr(oOldBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
+ oOldBoxInGrp.idTestBox, oOldBoxInGrp.iSchedPriority));
+ asNewAttr = sorted(asNewAttr);
+ asOldAttr = sorted(asOldAttr);
+ for idTestBox in set(dNewIds.keys()) - hCommonIds:
+ oNewBoxInGrp = dNewIds[idTestBox];
+ asNewAttr.append('New test box \'%s\' (#%s) priority %s'
+ % (getattr(oNewBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
+ oNewBoxInGrp.idTestBox, oNewBoxInGrp.iSchedPriority));
+ for idTestBox in set(dOldIds.keys()) - hCommonIds:
+ oOldBoxInGrp = dOldIds[idTestBox];
+ asOldAttr.append('Removed test box \'%s\' (#%s) priority %s'
+ % (getattr(oOldBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
+ oOldBoxInGrp.idTestBox, oOldBoxInGrp.iSchedPriority));
+
+ if asNewAttr or asOldAttr:
+ aoChanges.append(AttributeChangeEntryPre(sAttr, oNewAttr, oOldAttr,
+ '\n'.join(asNewAttr), '\n'.join(asOldAttr)));
+ else:
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+
+ else:
+ ##
+ ## @todo Incomplete: A more complicate apporach, probably faster though.
+ ##
+ def findEntry(tsEffective, iPrev = 0):
+ """ Find entry with matching effective + expiration time """
+ self._oDb.dprint('findEntry: iPrev=%s len(aoEntries)=%s tsEffective=%s' % (iPrev, len(aoEntries), tsEffective));
+ while iPrev < len(aoEntries):
+ self._oDb.dprint('%s iPrev=%u' % (aoEntries[iPrev].tsEffective, iPrev, ));
+ if aoEntries[iPrev].tsEffective > tsEffective:
+ iPrev += 1;
+ elif aoEntries[iPrev].tsEffective == tsEffective:
+ self._oDb.dprint('hit %u' % (iPrev,));
+ return iPrev;
+ else:
+ break;
+ self._oDb.dprint('%s not found!' % (tsEffective,));
+ return -1;
+
+ fMore = True;
+
+ #
+ # Track scheduling group changes. Not terribly efficient for large cMaxRows
+ # values, but not in the mood for figure out if there is any way to optimize that.
+ #
+ self._oDb.execute('''
+SELECT *
+FROM SchedGroups
+WHERE idSchedGroup = %s
+ AND tsEffective <= %s
+ORDER BY tsEffective DESC
+LIMIT %s''', (idSchedGroup, aoEntries[0].tsEffective, cMaxRows + 1,));
+
+ iEntry = 0;
+ aaoRows = self._oDb.fetchAll();
+ for iRow, oRow in enumerate(aaoRows):
+ oNew = SchedGroupDataEx().initFromDbRow(oRow);
+ iEntry = findEntry(oNew.tsEffective, iEntry);
+ self._oDb.dprint('iRow=%s iEntry=%s' % (iRow, iEntry));
+ if iEntry < 0:
+ break;
+ oEntry = aoEntries[iEntry];
+ aoChanges = oEntry.aoChanges;
+ oEntry.oNewRaw = oNew;
+ if iRow + 1 < len(aaoRows):
+ oOld = SchedGroupDataEx().initFromDbRow(aaoRows[iRow + 1]);
+ self._oDb.dprint('oOld=%s' % (oOld,));
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+ else:
+ self._oDb.dprint('New');
+
+ #
+ # ...
+ #
+
+ # FInally
+ UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
+ return (aoEntries, fMore);
+
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """Add Scheduling Group record"""
+
+ #
+ # Validate.
+ #
+ dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dDataErrors:
+ raise TMInvalidData('Invalid data passed to addEntry: %s' % (dDataErrors,));
+ if self.exists(oData.sName):
+ raise TMRowAlreadyExists('Scheduling group "%s" already exists.' % (oData.sName,));
+
+ #
+ # Add it.
+ #
+ self._oDb.execute('INSERT INTO SchedGroups (\n'
+ ' uidAuthor,\n'
+ ' sName,\n'
+ ' sDescription,\n'
+ ' fEnabled,\n'
+ ' enmScheduler,\n'
+ ' idBuildSrc,\n'
+ ' idBuildSrcTestSuite,\n'
+ ' sComment)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)\n'
+ 'RETURNING idSchedGroup\n'
+ , ( uidAuthor,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled,
+ oData.enmScheduler,
+ oData.idBuildSrc,
+ oData.idBuildSrcTestSuite,
+ oData.sComment ));
+ idSchedGroup = self._oDb.fetchOne()[0];
+ oData.idSchedGroup = idSchedGroup;
+
+ for oBoxInGrp in oData.aoTestBoxes:
+ oBoxInGrp.idSchedGroup = idSchedGroup;
+ self._addSchedGroupTestBox(uidAuthor, oBoxInGrp);
+
+ for oMember in oData.aoMembers:
+ oMember.idSchedGroup = idSchedGroup;
+ self._addSchedGroupMember(uidAuthor, oMember);
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """Edit Scheduling Group record"""
+
+ #
+ # Validate input and retrieve the old data.
+ #
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry got invalid data: %s' % (dErrors,));
+ self._assertUnique(oData.sName, oData.idSchedGroup);
+ oOldData = SchedGroupDataEx().initFromDbWithId(self._oDb, oData.idSchedGroup);
+
+ #
+ # Make the changes.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoMembers', 'aoTestBoxes',
+ 'oBuildSrc', 'oBuildSrcValidationKit', ]):
+ self._historizeEntry(oData.idSchedGroup);
+ self._readdEntry(uidAuthor, oData);
+
+ # Remove groups.
+ for oOld in oOldData.aoMembers:
+ fRemove = True;
+ for oNew in oData.aoMembers:
+ if oNew.idTestGroup == oOld.idTestGroup:
+ fRemove = False;
+ break;
+ if fRemove:
+ self._removeSchedGroupMember(uidAuthor, oOld);
+
+ # Add / modify groups.
+ for oMember in oData.aoMembers:
+ oOldMember = None;
+ for oOld in oOldData.aoMembers:
+ if oOld.idTestGroup == oMember.idTestGroup:
+ oOldMember = oOld;
+ break;
+
+ oMember.idSchedGroup = oData.idSchedGroup;
+ if oOldMember is None:
+ self._addSchedGroupMember(uidAuthor, oMember);
+ elif not oMember.isEqualEx(oOldMember, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestGroup']):
+ self._historizeSchedGroupMember(oMember);
+ self._addSchedGroupMember(uidAuthor, oMember);
+
+ # Remove testboxes.
+ for oOld in oOldData.aoTestBoxes:
+ fRemove = True;
+ for oNew in oData.aoTestBoxes:
+ if oNew.idTestBox == oOld.idTestBox:
+ fRemove = False;
+ break;
+ if fRemove:
+ self._removeSchedGroupTestBox(uidAuthor, oOld);
+
+ # Add / modify testboxes.
+ for oBoxInGrp in oData.aoTestBoxes:
+ oOldBoxInGrp = None;
+ for oOld in oOldData.aoTestBoxes:
+ if oOld.idTestBox == oBoxInGrp.idTestBox:
+ oOldBoxInGrp = oOld;
+ break;
+
+ oBoxInGrp.idSchedGroup = oData.idSchedGroup;
+ if oOldBoxInGrp is None:
+ self._addSchedGroupTestBox(uidAuthor, oBoxInGrp);
+ elif not oBoxInGrp.isEqualEx(oOldBoxInGrp, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestBox']):
+ self._historizeSchedGroupTestBox(oBoxInGrp);
+ self._addSchedGroupTestBox(uidAuthor, oBoxInGrp);
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
+ """
+ Deletes a scheduling group.
+ """
+ _ = fCascade;
+
+ #
+ # Input validation and retrival of current data.
+ #
+ if idSchedGroup == 1:
+ raise TMRowInUse('Cannot remove the default scheduling group (id 1).');
+ oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
+
+ #
+ # Remove the test box member records.
+ #
+ for oBoxInGrp in oData.aoTestBoxes:
+ self._removeSchedGroupTestBox(uidAuthor, oBoxInGrp);
+ self._oDb.execute('UPDATE TestBoxesInSchedGroups\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idSchedGroup,));
+
+ #
+ # Remove the test group member records.
+ #
+ for oMember in oData.aoMembers:
+ self._removeSchedGroupMember(uidAuthor, oMember);
+ self._oDb.execute('UPDATE SchedGroupMembers\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idSchedGroup,));
+
+ #
+ # Now the SchedGroups entry.
+ #
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oData.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeEntry(idSchedGroup, tsCurMinusOne);
+ self._readdEntry(uidAuthor, oData, tsCurMinusOne);
+ self._historizeEntry(idSchedGroup);
+ self._oDb.execute('UPDATE SchedGroups\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idSchedGroup,))
+
+ self._oDb.maybeCommit(fCommit)
+ return True;
+
+
+ def cachedLookup(self, idSchedGroup):
+ """
+ Looks up the most recent SchedGroupData object for idSchedGroup
+ via an object cache.
+
+ Returns a shared SchedGroupData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('SchedGroup');
+
+ oEntry = self.dCache.get(idSchedGroup, None);
+ if oEntry is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idSchedGroup, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE idSchedGroup = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idSchedGroup, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idSchedGroup));
+
+ if self._oDb.getRowCount() == 1:
+ oEntry = SchedGroupData().initFromDbRow(self._oDb.fetchOne());
+ self.dCache[idSchedGroup] = oEntry;
+ return oEntry;
+
+
+ #
+ # Other methods.
+ #
+
+ def fetchOrderedByName(self, tsNow = None):
+ """
+ Return list of objects of type SchedGroups ordered by name.
+ May raise exception on database error.
+ """
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sName ASC\n');
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sName ASC\n'
+ , (tsNow, tsNow,));
+ aoRet = []
+ for _ in range(self._oDb.getRowCount()):
+ aoRet.append(SchedGroupData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRet;
+
+
+ def getAll(self, tsEffective = None):
+ """
+ Gets the list of all scheduling groups.
+ Returns an array of SchedGroupData instances.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ , (tsEffective, tsEffective));
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(SchedGroupData().initFromDbRow(aoRow));
+ return aoRet;
+
+ def getSchedGroupsForCombo(self, tsEffective = None):
+ """
+ Gets the list of active scheduling groups for a combo box.
+ Returns an array of (value [idSchedGroup], drop-down-name [sName],
+ hover-text [sDescription]) tuples.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sName');
+ else:
+ self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sName'
+ , (tsEffective, tsEffective));
+ return self._oDb.fetchAll();
+
+
+ def getMembers(self, idSchedGroup, tsEffective = None):
+ """
+ Gets the scheduling groups members for the given scheduling group.
+
+ Returns an array of SchedGroupMemberDataEx instances (sorted by
+ priority (descending) and idTestGroup). May raise exception DB error.
+ """
+
+ if tsEffective is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroupMembers, TestGroups\n'
+ 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
+ ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
+ ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
+ , (idSchedGroup,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroupMembers, TestGroups\n'
+ 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
+ ' AND SchedGroupMembers.tsExpire < %s\n'
+ ' AND SchedGroupMembers.tsEffective >= %s\n'
+ ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
+ ' AND TestGroups.tsExpire < %s\n'
+ ' AND TestGroups.tsEffective >= %s\n'
+ 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
+ , (idSchedGroup, tsEffective, tsEffective, tsEffective, tsEffective, ));
+ aaoRows = self._oDb.fetchAll();
+ aoRet = [];
+ for aoRow in aaoRows:
+ aoRet.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
+ return aoRet;
+
+ def getTestCasesForGroup(self, idSchedGroup, cMax = None):
+ """
+ Gets the enabled testcases w/ testgroup+priority for the given scheduling group.
+
+ Returns an array of TestCaseData instances (ordered by group id, descending
+ testcase priority, and testcase IDs) with an extra iSchedPriority member.
+ May raise exception on DB error or if the result exceeds cMax.
+ """
+
+ self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.*\n'
+ 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCases\n'
+ 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
+ ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
+ ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
+ ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
+ ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestCases.fEnabled = TRUE\n'
+ 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority DESC, TestCases.idTestCase\n'
+ , (idSchedGroup,));
+
+ if cMax is not None and self._oDb.getRowCount() > cMax:
+ raise TMExceptionBase('Too many testcases for scheduling group %s: %s, max %s'
+ % (idSchedGroup, cMax, self._oDb.getRowCount(),));
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ oTestCase = TestCaseData().initFromDbRow(aoRow[2:]);
+ oTestCase.idTestGroup = aoRow[0];
+ oTestCase.iSchedPriority = aoRow[1];
+ aoRet.append(oTestCase);
+ return aoRet;
+
+ def getTestCaseArgsForGroup(self, idSchedGroup, cMax = None):
+ """
+ Gets the testcase argument variation w/ testgroup+priority for the given scheduling group.
+
+ Returns an array TestCaseArgsData instance (sorted by group and
+ variation id) with an extra iSchedPriority member.
+ May raise exception on DB error or if the result exceeds cMax.
+ """
+
+ self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCaseArgs.*\n'
+ 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCaseArgs, TestCases\n'
+ 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
+ ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
+ ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
+ ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestCaseArgs.idTestCase = TestGroupMembers.idTestCase\n'
+ ' AND TestCaseArgs.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND ( TestGroupMembers.aidTestCaseArgs is NULL\n'
+ ' OR TestCaseArgs.idTestCaseArgs = ANY(TestGroupMembers.aidTestCaseArgs) )\n'
+ ' AND TestCases.idTestCase = TestCaseArgs.idTestCase\n'
+ ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestCases.fEnabled = TRUE\n'
+ 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.idTestCase, TestCaseArgs.idTestCaseArgs\n'
+ , (idSchedGroup,));
+
+ if cMax is not None and self._oDb.getRowCount() > cMax:
+ raise TMExceptionBase('Too many argument variations for scheduling group %s: %s, max %s'
+ % (idSchedGroup, cMax, self._oDb.getRowCount(),));
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ oVariation = TestCaseArgsData().initFromDbRow(aoRow[2:]);
+ oVariation.idTestGroup = aoRow[0];
+ oVariation.iSchedPriority = aoRow[1];
+ aoRet.append(oVariation);
+ return aoRet;
+
+ def exists(self, sName):
+ """Checks if a group with the given name exists."""
+ self._oDb.execute('SELECT idSchedGroup\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND sName = %s\n'
+ 'LIMIT 1\n'
+ , (sName,));
+ return self._oDb.getRowCount() > 0;
+
+ def getById(self, idSchedGroup):
+ """Get Scheduling Group data by idSchedGroup"""
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idSchedGroup = %s;', (idSchedGroup,))
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException(
+ 'Found more than one scheduling groups with the same credentials. Database structure is corrupted.')
+ try:
+ return SchedGroupData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+
+ #
+ # Internal helpers.
+ #
+
+ def _assertUnique(self, sName, idSchedGroupIgnore = None):
+ """
+ Checks that the scheduling group name is unique.
+ Raises exception if the name is already in use.
+ """
+ if idSchedGroupIgnore is None:
+ self._oDb.execute('SELECT idSchedGroup\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND sName = %s\n'
+ , ( sName, ) );
+ else:
+ self._oDb.execute('SELECT idSchedGroup\n'
+ 'FROM SchedGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND sName = %s\n'
+ ' AND idSchedGroup <> %s\n'
+ , ( sName, idSchedGroupIgnore, ) );
+ if self._oDb.getRowCount() > 0:
+ raise TMRowInUse('Scheduling group name (%s) is already in use.' % (sName,));
+ return True;
+
+ def _readdEntry(self, uidAuthor, oData, tsEffective = None):
+ """
+ Re-adds the SchedGroups entry. Used by editEntry and removeEntry.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO SchedGroups (\n'
+ ' uidAuthor,\n'
+ ' tsEffective,\n'
+ ' idSchedGroup,\n'
+ ' sName,\n'
+ ' sDescription,\n'
+ ' fEnabled,\n'
+ ' enmScheduler,\n'
+ ' idBuildSrc,\n'
+ ' idBuildSrcTestSuite,\n'
+ ' sComment )\n'
+ 'VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )\n'
+ , ( uidAuthor,
+ tsEffective,
+ oData.idSchedGroup,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled,
+ oData.enmScheduler,
+ oData.idBuildSrc,
+ oData.idBuildSrcTestSuite,
+ oData.sComment, ));
+ return True;
+
+ def _historizeEntry(self, idSchedGroup, tsExpire = None):
+ """
+ Historizes the current entry for the given scheduling group.
+ """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE SchedGroups\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( tsExpire, idSchedGroup, ));
+ return True;
+
+ def _addSchedGroupMember(self, uidAuthor, oMember, tsEffective = None):
+ """
+ addEntry worker for adding a scheduling group member.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO SchedGroupMembers(\n'
+ ' idSchedGroup,\n'
+ ' idTestGroup,\n'
+ ' tsEffective,\n'
+ ' uidAuthor,\n'
+ ' iSchedPriority,\n'
+ ' bmHourlySchedule,\n'
+ ' idTestGroupPreReq)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
+ , ( oMember.idSchedGroup,
+ oMember.idTestGroup,
+ tsEffective,
+ uidAuthor,
+ oMember.iSchedPriority,
+ oMember.bmHourlySchedule,
+ oMember.idTestGroupPreReq, ));
+ return True;
+
+ def _removeSchedGroupMember(self, uidAuthor, oMember):
+ """
+ Removes a scheduling group member.
+ """
+
+ # Try record who removed it by adding an dummy entry that expires immediately.
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oMember.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeSchedGroupMember(oMember, tsCurMinusOne);
+ self._addSchedGroupMember(uidAuthor, oMember, tsCurMinusOne); # lazy bird.
+ self._historizeSchedGroupMember(oMember);
+ else:
+ self._historizeSchedGroupMember(oMember);
+ return True;
+
+ def _historizeSchedGroupMember(self, oMember, tsExpire = None):
+ """
+ Historizes the current entry for the given scheduling group.
+ """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE SchedGroupMembers\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( tsExpire, oMember.idSchedGroup, oMember.idTestGroup, ));
+ return True;
+
+ #
+ def _addSchedGroupTestBox(self, uidAuthor, oBoxInGroup, tsEffective = None):
+ """
+ addEntry worker for adding a test box to a scheduling group.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO TestBoxesInSchedGroups(\n'
+ ' idSchedGroup,\n'
+ ' idTestBox,\n'
+ ' tsEffective,\n'
+ ' uidAuthor,\n'
+ ' iSchedPriority)\n'
+ 'VALUES (%s, %s, %s, %s, %s)\n'
+ , ( oBoxInGroup.idSchedGroup,
+ oBoxInGroup.idTestBox,
+ tsEffective,
+ uidAuthor,
+ oBoxInGroup.iSchedPriority, ));
+ return True;
+
+ def _removeSchedGroupTestBox(self, uidAuthor, oBoxInGroup):
+ """
+ Removes a testbox from a scheduling group.
+ """
+
+ # Try record who removed it by adding an dummy entry that expires immediately.
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oBoxInGroup.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeSchedGroupTestBox(oBoxInGroup, tsCurMinusOne);
+ self._addSchedGroupTestBox(uidAuthor, oBoxInGroup, tsCurMinusOne); # lazy bird.
+ self._historizeSchedGroupTestBox(oBoxInGroup);
+ else:
+ self._historizeSchedGroupTestBox(oBoxInGroup);
+ return True;
+
+ def _historizeSchedGroupTestBox(self, oBoxInGroup, tsExpire = None):
+ """
+ Historizes the current entry for the given scheduling group.
+ """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE TestBoxesInSchedGroups\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND idTestBox = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( tsExpire, oBoxInGroup.idSchedGroup, oBoxInGroup.idTestBox, ));
+ return True;
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class SchedGroupMemberDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [SchedGroupMemberData(),];
+
+class SchedGroupDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [SchedGroupData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/schedqueue.py b/src/VBox/ValidationKit/testmanager/core/schedqueue.py
new file mode 100755
index 00000000..f49c4ad3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/schedqueue.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+# "$Id: schedqueue.py $"
+
+"""
+Test Manager - Test Case Queue.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+## Standard python imports.
+#import unittest
+
+from testmanager.core.base import ModelDataBase, ModelLogicBase, TMExceptionBase #, ModelDataBaseTestCase
+
+
+class SchedQueueEntry(ModelDataBase):
+ """
+ SchedQueue listing entry
+
+ Note! This could be turned into a SchedQueueDataEx class if we start
+ fetching all the fields from the scheduing queue.
+ """
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+
+ self.idItem = None
+ self.tsLastScheduled = None
+ self.sSchedGroup = None
+ self.sTestGroup = None
+ self.sTestCase = None
+ self.fUpToDate = None
+ self.iPerSchedGroupRowNumber = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SchedQueueLogic::fetchForListing select.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMExceptionBase('TestCaseQueue row not found.')
+
+ self.idItem = aoRow[0]
+ self.tsLastScheduled = aoRow[1]
+ self.sSchedGroup = aoRow[2]
+ self.sTestGroup = aoRow[3]
+ self.sTestCase = aoRow[4]
+ self.fUpToDate = aoRow[5]
+ self.iPerSchedGroupRowNumber = aoRow[6];
+ return self
+
+
+class SchedQueueLogic(ModelLogicBase):
+ """
+ SchedQueues logic.
+ """
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches SchedQueues entries.
+
+ Returns an array (list) of SchedQueueEntry items, empty list if none.
+ Raises exception on error.
+ """
+ _, _ = tsNow, aiSortColumns
+ self._oDb.execute('''
+SELECT SchedQueues.idItem,
+ SchedQueues.tsLastScheduled,
+ SchedGroups.sName,
+ TestGroups.sName,
+ TestCases.sName,
+ SchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ AND TestGroups.tsExpire = 'infinity'::TIMESTAMP
+ AND TestGroups.tsExpire = 'infinity'::TIMESTAMP
+ AND TestCaseArgs.tsExpire = 'infinity'::TIMESTAMP
+ AND TestCases.tsExpire = 'infinity'::TIMESTAMP AS fUpToDate,
+ ROW_NUMBER() OVER (PARTITION BY SchedQueues.idSchedGroup
+ ORDER BY SchedQueues.tsLastScheduled,
+ SchedQueues.idItem) AS iPerSchedGroupRowNumber
+FROM SchedQueues
+ INNER JOIN SchedGroups
+ ON SchedGroups.idSchedGroup = SchedQueues.idSchedGroup
+ AND SchedGroups.tsExpire > SchedQueues.tsConfig
+ AND SchedGroups.tsEffective <= SchedQueues.tsConfig
+ INNER JOIN TestGroups
+ ON TestGroups.idTestGroup = SchedQueues.idTestGroup
+ AND TestGroups.tsExpire > SchedQueues.tsConfig
+ AND TestGroups.tsEffective <= SchedQueues.tsConfig
+ INNER JOIN TestCaseArgs
+ ON TestCaseArgs.idGenTestCaseArgs = SchedQueues.idGenTestCaseArgs
+ INNER JOIN TestCases
+ ON TestCases.idTestCase = TestCaseArgs.idTestCase
+ AND TestCases.tsExpire > SchedQueues.tsConfig
+ AND TestCases.tsEffective <= SchedQueues.tsConfig
+ORDER BY iPerSchedGroupRowNumber,
+ SchedGroups.sName DESC
+LIMIT %s OFFSET %s''' % (cMaxRows, iStart,))
+ aoRows = []
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(SchedQueueEntry().initFromDbRow(self._oDb.fetchOne()))
+ return aoRows
+
+#
+# Unit testing.
+#
+
+## @todo SchedQueueEntry isn't a typical ModelDataBase child (not fetching all
+## fields; is an extended data class mixing data from multiple tables), so
+## this won't work yet.
+#
+## pylint: disable=missing-docstring
+#class TestCaseQueueDataTestCase(ModelDataBaseTestCase):
+# def setUp(self):
+# self.aoSamples = [SchedQueueEntry(),]
+#
+#
+#if __name__ == '__main__':
+# unittest.main()
+# # not reached.
+#
diff --git a/src/VBox/ValidationKit/testmanager/core/schedulerbase.py b/src/VBox/ValidationKit/testmanager/core/schedulerbase.py
new file mode 100755
index 00000000..c28b43cf
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/schedulerbase.py
@@ -0,0 +1,1570 @@
+# -*- coding: utf-8 -*-
+# $Id: schedulerbase.py $
+# pylint: disable=too-many-lines
+
+
+"""
+Test Manager - Base class and utilities for the schedulers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import sys;
+import unittest;
+
+# Validation Kit imports.
+from common import utils, constants;
+from testmanager import config;
+from testmanager.core.build import BuildDataEx, BuildLogic;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, TMExceptionBase;
+from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+from testmanager.core.globalresource import GlobalResourceLogic;
+from testmanager.core.schedgroup import SchedGroupData, SchedGroupLogic;
+from testmanager.core.systemlog import SystemLogData, SystemLogLogic;
+from testmanager.core.testbox import TestBoxData, TestBoxDataEx;
+from testmanager.core.testboxstatus import TestBoxStatusData, TestBoxStatusLogic;
+from testmanager.core.testcase import TestCaseLogic;
+from testmanager.core.testcaseargs import TestCaseArgsDataEx, TestCaseArgsLogic;
+from testmanager.core.testset import TestSetData, TestSetLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+
+class ReCreateQueueData(object):
+ """
+ Data object for recreating a scheduling queue.
+
+ It's mostly a storage object, but has a few data checking operation
+ associated with it.
+ """
+
+ def __init__(self, oDb, idSchedGroup):
+ #
+ # Load data from the database.
+ #
+ oSchedGroupLogic = SchedGroupLogic(oDb);
+ self.oSchedGroup = oSchedGroupLogic.cachedLookup(idSchedGroup);
+
+ # Will extend the entries with aoTestCases and dTestCases members
+ # further down (SchedGroupMemberDataEx). checkForGroupDepCycles
+ # will add aidTestGroupPreReqs.
+ self.aoTestGroups = oSchedGroupLogic.getMembers(idSchedGroup);
+
+ # aoTestCases entries are TestCaseData instance with iSchedPriority
+ # and idTestGroup added for our purposes.
+ # We will add oTestGroup and aoArgsVariations members to each further down.
+ self.aoTestCases = oSchedGroupLogic.getTestCasesForGroup(idSchedGroup, cMax = 4096);
+
+ # Load dependencies.
+ oTestCaseLogic = TestCaseLogic(oDb)
+ for oTestCase in self.aoTestCases:
+ oTestCase.aidPreReqs = oTestCaseLogic.getTestCasePreReqIds(oTestCase.idTestCase, cMax = 4096);
+
+ # aoTestCases entries are TestCaseArgsData instance with iSchedPriority
+ # and idTestGroup added for our purposes.
+ # We will add oTestGroup and oTestCase members to each further down.
+ self.aoArgsVariations = oSchedGroupLogic.getTestCaseArgsForGroup(idSchedGroup, cMax = 65536);
+
+ #
+ # Generate global lookups.
+ #
+
+ # Generate a testcase lookup dictionary for use when working on
+ # argument variations.
+ self.dTestCases = {};
+ for oTestCase in self.aoTestCases:
+ self.dTestCases[oTestCase.idTestCase] = oTestCase;
+ assert len(self.dTestCases) <= len(self.aoTestCases); # Note! Can be shorter!
+
+ # Generate a testgroup lookup dictionary.
+ self.dTestGroups = {};
+ for oTestGroup in self.aoTestGroups:
+ self.dTestGroups[oTestGroup.idTestGroup] = oTestGroup;
+ assert len(self.dTestGroups) == len(self.aoTestGroups);
+
+ #
+ # Associate extra members with the base data.
+ #
+ if self.aoTestGroups:
+ # Prep the test groups.
+ for oTestGroup in self.aoTestGroups:
+ oTestGroup.aoTestCases = [];
+ oTestGroup.dTestCases = {};
+
+ # Link testcases to their group, both directions. Prep testcases for
+ # argument varation association.
+ oTestGroup = self.aoTestGroups[0];
+ for oTestCase in self.aoTestCases:
+ if oTestGroup.idTestGroup != oTestCase.idTestGroup:
+ oTestGroup = self.dTestGroups[oTestCase.idTestGroup];
+
+ assert oTestCase.idTestCase not in oTestGroup.dTestCases;
+ oTestGroup.dTestCases[oTestCase.idTestCase] = oTestCase;
+ oTestGroup.aoTestCases.append(oTestCase);
+ oTestCase.oTestGroup = oTestGroup;
+ oTestCase.aoArgsVariations = [];
+
+ # Associate testcase argument variations with their testcases (group)
+ # in both directions.
+ oTestGroup = self.aoTestGroups[0];
+ oTestCase = self.aoTestCases[0] if self.aoTestCases else None;
+ for oArgVariation in self.aoArgsVariations:
+ if oTestGroup.idTestGroup != oArgVariation.idTestGroup:
+ oTestGroup = self.dTestGroups[oArgVariation.idTestGroup];
+ if oTestCase.idTestCase != oArgVariation.idTestCase or oTestCase.idTestGroup != oArgVariation.idTestGroup:
+ oTestCase = oTestGroup.dTestCases[oArgVariation.idTestCase];
+
+ oTestCase.aoArgsVariations.append(oArgVariation);
+ oArgVariation.oTestCase = oTestCase;
+ oArgVariation.oTestGroup = oTestGroup;
+
+ else:
+ assert not self.aoTestCases;
+ assert not self.aoArgsVariations;
+ # done.
+
+ @staticmethod
+ def _addPreReqError(aoErrors, aidChain, oObj, sMsg):
+ """ Returns a chain of IDs error entry. """
+
+ sMsg += ' Dependency chain: %s' % (aidChain[0],);
+ for i in range(1, len(aidChain)):
+ sMsg += ' -> %s' % (aidChain[i],);
+
+ aoErrors.append([sMsg, oObj]);
+ return aoErrors;
+
+ def checkForGroupDepCycles(self):
+ """
+ Checks for testgroup depencency cycles and any missing testgroup
+ dependencies.
+ Returns array of errors (see SchedulderBase.recreateQueue()).
+ """
+ aoErrors = [];
+ for oTestGroup in self.aoTestGroups:
+ idPreReq = oTestGroup.idTestGroupPreReq;
+ if idPreReq is None:
+ oTestGroup.aidTestGroupPreReqs = [];
+ continue;
+
+ aidChain = [oTestGroup.idTestGroup,];
+ while idPreReq is not None:
+ aidChain.append(idPreReq);
+ if len(aidChain) >= 10:
+ self._addPreReqError(aoErrors, aidChain, oTestGroup,
+ 'TestGroup #%s prerequisite chain is too long!'
+ % (oTestGroup.idTestGroup,));
+ break;
+
+ oDep = self.dTestGroups.get(idPreReq, None);
+ if oDep is None:
+ self._addPreReqError(aoErrors, aidChain, oTestGroup,
+ 'TestGroup #%s prerequisite #%s is not in the scheduling group!'
+ % (oTestGroup.idTestGroup, idPreReq,));
+ break;
+
+ idPreReq = oDep.idTestGroupPreReq;
+ oTestGroup.aidTestGroupPreReqs = aidChain[1:];
+
+ return aoErrors;
+
+
+ def checkForMissingTestCaseDeps(self):
+ """
+ Checks that testcase dependencies stays within bounds. We do not allow
+ dependencies outside a testgroup, no dependency cycles or even remotely
+ long dependency chains.
+
+ Returns array of errors (see SchedulderBase.recreateQueue()).
+ """
+ aoErrors = [];
+ for oTestGroup in self.aoTestGroups:
+ for oTestCase in oTestGroup.aoTestCases:
+ if not oTestCase.aidPreReqs:
+ continue;
+
+ # Stupid recursion code using special stack(s).
+ aiIndexes = [[oTestCase, 0], ];
+ aidChain = [oTestCase.idTestGroup,];
+ while aiIndexes:
+ (oCur, i) = aiIndexes[-1];
+ if i >= len(oCur.aidPreReqs):
+ aiIndexes.pop();
+ aidChain.pop();
+ else:
+ aiIndexes[-1][1] = i + 1; # whatever happens, we'll advance on the current level.
+
+ idPreReq = oTestCase.aidPreReqs[i];
+ oDep = oTestGroup.dTestCases.get(idPreReq, None);
+ if oDep is None:
+ self._addPreReqError(aoErrors, aidChain, oTestCase,
+ 'TestCase #%s prerequisite #%s is not in the scheduling group!'
+ % (oTestCase.idTestCase, idPreReq));
+ elif idPreReq in aidChain:
+ self._addPreReqError(aoErrors, aidChain, oTestCase,
+ 'TestCase #%s prerequisite #%s creates a cycle!'
+ % (oTestCase.idTestCase, idPreReq));
+ elif not oDep.aiPreReqs:
+ pass;
+ elif len(aidChain) >= 10:
+ self._addPreReqError(aoErrors, aidChain, oTestCase,
+ 'TestCase #%s prerequisite chain is too long!' % (oTestCase.idTestCase,));
+ else:
+ aiIndexes.append([oDep, 0]);
+ aidChain.append(idPreReq);
+
+ return aoErrors;
+
+ def deepTestGroupSort(self):
+ """
+ Sorts the testgroups and their testcases by priority and dependencies.
+ Note! Don't call this before checking for dependency cycles!
+ """
+ if not self.aoTestGroups:
+ return;
+
+ #
+ # ASSUMES groups as well as testcases are sorted by priority by the
+ # database. So we only have to concern ourselves with the dependency
+ # sorting.
+ #
+ iGrpPrio = self.aoTestGroups[0].iSchedPriority;
+ for iTestGroup, oTestGroup in enumerate(self.aoTestGroups):
+ if oTestGroup.iSchedPriority > iGrpPrio:
+ raise TMExceptionBase('Incorrectly sorted testgroups returned by database: iTestGroup=%s prio=%s %s'
+ % ( iTestGroup, iGrpPrio,
+ ', '.join(['(%s: %s)' % (oCur.idTestGroup, oCur.iSchedPriority)
+ for oCur in self.aoTestGroups]), ) );
+ iGrpPrio = oTestGroup.iSchedPriority;
+
+ if oTestGroup.aoTestCases:
+ iTstPrio = oTestGroup.aoTestCases[0].iSchedPriority;
+ for iTestCase, oTestCase in enumerate(oTestGroup.aoTestCases):
+ if oTestCase.iSchedPriority > iTstPrio:
+ raise TMExceptionBase('Incorrectly sorted testcases returned by database: i=%s prio=%s idGrp=%s %s'
+ % ( iTestCase, iTstPrio, oTestGroup.idTestGroup,
+ ', '.join(['(%s: %s)' % (oCur.idTestCase, oCur.iSchedPriority)
+ for oCur in oTestGroup.aoTestCases]),));
+
+ #
+ # Sort the testgroups by dependencies.
+ #
+ i = 0;
+ while i < len(self.aoTestGroups):
+ oTestGroup = self.aoTestGroups[i];
+ if oTestGroup.idTestGroupPreReq is not None:
+ iPreReq = self.aoTestGroups.index(self.dTestGroups[oTestGroup.idTestGroupPreReq]);
+ if iPreReq > i:
+ # The prerequisite is after the current entry. Move the
+ # current entry so that it's following it's prereq entry.
+ self.aoTestGroups.insert(iPreReq + 1, oTestGroup);
+ self.aoTestGroups.pop(i);
+ continue;
+ assert iPreReq < i;
+ i += 1; # Advance.
+
+ #
+ # Sort the testcases by dependencies.
+ # Same algorithm as above, just more prerequisites.
+ #
+ for oTestGroup in self.aoTestGroups:
+ i = 0;
+ while i < len(oTestGroup.aoTestCases):
+ oTestCase = oTestGroup.aoTestCases[i];
+ if oTestCase.aidPreReqs:
+ for idPreReq in oTestCase.aidPreReqs:
+ iPreReq = oTestGroup.aoTestCases.index(oTestGroup.dTestCases[idPreReq]);
+ if iPreReq > i:
+ # The prerequisite is after the current entry. Move the
+ # current entry so that it's following it's prereq entry.
+ oTestGroup.aoTestGroups.insert(iPreReq + 1, oTestCase);
+ oTestGroup.aoTestGroups.pop(i);
+ i -= 1; # Don't advance.
+ break;
+ assert iPreReq < i;
+ i += 1; # Advance.
+
+
+
+class SchedQueueData(ModelDataBase):
+ """
+ Scheduling queue data item.
+ """
+
+ ksIdAttr = 'idSchedGroup';
+
+ ksParam_idSchedGroup = 'SchedQueueData_idSchedGroup';
+ ksParam_idItem = 'SchedQueueData_idItem';
+ ksParam_offQueue = 'SchedQueueData_offQueue';
+ ksParam_idGenTestCaseArgs = 'SchedQueueData_idGenTestCaseArgs';
+ ksParam_idTestGroup = 'SchedQueueData_idTestGroup';
+ ksParam_aidTestGroupPreReqs = 'SchedQueueData_aidTestGroupPreReqs';
+ ksParam_bmHourlySchedule = 'SchedQueueData_bmHourlySchedule';
+ ksParam_tsConfig = 'SchedQueueData_tsConfig';
+ ksParam_tsLastScheduled = 'SchedQueueData_tsLastScheduled';
+ ksParam_idTestSetGangLeader = 'SchedQueueData_idTestSetGangLeader';
+ ksParam_cMissingGangMembers = 'SchedQueueData_cMissingGangMembers';
+
+ kasAllowNullAttributes = [ 'idItem', 'offQueue', 'aidTestGroupPreReqs', 'bmHourlySchedule', 'idTestSetGangLeader',
+ 'tsConfig', 'tsLastScheduled' ];
+
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idSchedGroup = None;
+ self.idItem = None;
+ self.offQueue = None;
+ self.idGenTestCaseArgs = None;
+ self.idTestGroup = None;
+ self.aidTestGroupPreReqs = None;
+ self.bmHourlySchedule = None;
+ self.tsConfig = None;
+ self.tsLastScheduled = None;
+ self.idTestSetGangLeader = None;
+ self.cMissingGangMembers = 1;
+
+ def initFromValues(self, idSchedGroup, idGenTestCaseArgs, idTestGroup, aidTestGroupPreReqs, # pylint: disable=too-many-arguments
+ bmHourlySchedule, cMissingGangMembers,
+ idItem = None, offQueue = None, tsConfig = None, tsLastScheduled = None, idTestSetGangLeader = None):
+ """
+ Reinitialize with all attributes potentially given as inputs.
+ Return self.
+ """
+ self.idSchedGroup = idSchedGroup;
+ self.idItem = idItem;
+ self.offQueue = offQueue;
+ self.idGenTestCaseArgs = idGenTestCaseArgs;
+ self.idTestGroup = idTestGroup;
+ self.aidTestGroupPreReqs = aidTestGroupPreReqs;
+ self.bmHourlySchedule = bmHourlySchedule;
+ self.tsConfig = tsConfig;
+ self.tsLastScheduled = tsLastScheduled;
+ self.idTestSetGangLeader = idTestSetGangLeader;
+ self.cMissingGangMembers = cMissingGangMembers;
+ return self;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Initialize from database row (SELECT * FROM SchedQueues).
+ Returns self.
+ Raises exception if no row is specfied.
+ """
+ if aoRow is None:
+ raise TMExceptionBase('SchedQueueData not found.');
+
+ self.idSchedGroup = aoRow[0];
+ self.idItem = aoRow[1];
+ self.offQueue = aoRow[2];
+ self.idGenTestCaseArgs = aoRow[3];
+ self.idTestGroup = aoRow[4];
+ self.aidTestGroupPreReqs = aoRow[5];
+ self.bmHourlySchedule = aoRow[6];
+ self.tsConfig = aoRow[7];
+ self.tsLastScheduled = aoRow[8];
+ self.idTestSetGangLeader = aoRow[9];
+ self.cMissingGangMembers = aoRow[10];
+ return self;
+
+
+
+
+
+
+class SchedulerBase(object):
+ """
+ The scheduler base class.
+
+ The scheduler classes have two functions:
+ 1. Recreate the scheduling queue.
+ 2. Pick the next task from the queue.
+
+ The first is scheduler specific, the latter isn't.
+ """
+
+ class BuildCache(object):
+ """ Build cache. """
+
+ class BuildCacheIterator(object):
+ """ Build class iterator. """
+ def __init__(self, oCache):
+ self.oCache = oCache;
+ self.iCur = 0;
+
+ def __iter__(self):
+ """Returns self, required by the language."""
+ return self;
+
+ def __next__(self):
+ """Returns the next build, raises StopIteration when the end has been reached."""
+ while True:
+ if self.iCur >= len(self.oCache.aoEntries):
+ oEntry = self.oCache.fetchFromCursor();
+ if oEntry is None:
+ raise StopIteration;
+ else:
+ oEntry = self.oCache.aoEntries[self.iCur];
+ self.iCur += 1;
+ if not oEntry.fRemoved:
+ return oEntry;
+ return None; # not reached, but make pylint happy (for now).
+
+ def next(self):
+ """ For python 2.x. """
+ return self.__next__();
+
+ class BuildCacheEntry(object):
+ """ Build cache entry. """
+
+ def __init__(self, oBuild, fMaybeBlacklisted):
+ self.oBuild = oBuild;
+ self._fBlacklisted = None if fMaybeBlacklisted is True else False;
+ self.fRemoved = False;
+ self._dPreReqDecisions = {};
+
+ def remove(self):
+ """
+ Marks the cache entry as removed.
+ This doesn't actually remove it from the cache array, only marks
+ it as removed. It has no effect on open iterators.
+ """
+ self.fRemoved = True;
+
+ def getPreReqDecision(self, sPreReqSet):
+ """
+ Retrieves a cached prerequisite decision.
+ Returns boolean if found, None if not.
+ """
+ return self._dPreReqDecisions.get(sPreReqSet);
+
+ def setPreReqDecision(self, sPreReqSet, fDecision):
+ """
+ Caches a prerequistie decision.
+ """
+ self._dPreReqDecisions[sPreReqSet] = fDecision;
+ return fDecision;
+
+ def isBlacklisted(self, oDb):
+ """ Checks if the build is blacklisted. """
+ if self._fBlacklisted is None:
+ self._fBlacklisted = BuildLogic(oDb).isBuildBlacklisted(self.oBuild);
+ return self._fBlacklisted;
+
+
+ def __init__(self):
+ self.aoEntries = [];
+ self.oCursor = None;
+
+ def setupSource(self, oDb, idBuildSrc, sOs, sCpuArch, tsNow):
+ """ Configures the build cursor for the cache. """
+ if not self.aoEntries and self.oCursor is None:
+ oBuildSource = BuildSourceData().initFromDbWithId(oDb, idBuildSrc, tsNow);
+ self.oCursor = BuildSourceLogic(oDb).openBuildCursor(oBuildSource, sOs, sCpuArch, tsNow);
+ return True;
+
+ def __iter__(self):
+ """Return an iterator."""
+ return self.BuildCacheIterator(self);
+
+ def fetchFromCursor(self):
+ """ Fetches a build from the cursor and adds it to the cache."""
+ if self.oCursor is None:
+ return None;
+
+ try:
+ aoRow = self.oCursor.fetchOne();
+ except:
+ return None;
+ if aoRow is None:
+ return None;
+
+ oBuild = BuildDataEx().initFromDbRow(aoRow);
+ oEntry = self.BuildCacheEntry(oBuild, aoRow[-1]);
+ self.aoEntries.append(oEntry);
+ return oEntry;
+
+ def __init__(self, oDb, oSchedGrpData, iVerbosity = 0, tsSecStart = None):
+ self._oDb = oDb;
+ self._oSchedGrpData = oSchedGrpData;
+ self._iVerbosity = iVerbosity;
+ self._asMessages = [];
+ self._tsSecStart = tsSecStart if tsSecStart is not None else utils.timestampSecond();
+ self.oBuildCache = self.BuildCache();
+ self.dTestGroupMembers = {};
+
+ @staticmethod
+ def _instantiate(oDb, oSchedGrpData, iVerbosity = 0, tsSecStart = None):
+ """
+ Instantiate the scheduler specified by the scheduling group.
+ Returns scheduler child class instance. May raise exception if
+ the input is invalid.
+ """
+ if oSchedGrpData.enmScheduler == SchedGroupData.ksScheduler_BestEffortContinuousIntegration:
+ from testmanager.core.schedulerbeci import SchdulerBeci;
+ oScheduler = SchdulerBeci(oDb, oSchedGrpData, iVerbosity, tsSecStart);
+ else:
+ raise oDb.integrityException('Invalid scheduler "%s", idSchedGroup=%d' \
+ % (oSchedGrpData.enmScheduler, oSchedGrpData.idSchedGroup));
+ return oScheduler;
+
+
+ #
+ # Misc.
+ #
+
+ def msgDebug(self, sText):
+ """Debug printing."""
+ if self._iVerbosity > 1:
+ self._asMessages.append('debug:' + sText);
+ return None;
+
+ def msgInfo(self, sText):
+ """Info printing."""
+ if self._iVerbosity > 1:
+ self._asMessages.append('info: ' + sText);
+ return None;
+
+ def dprint(self, sMsg):
+ """Prints a debug message to the srv glue log (see config.py). """
+ if config.g_kfSrvGlueDebugScheduler:
+ self._oDb.dprint(sMsg);
+ return None;
+
+ def getElapsedSecs(self):
+ """ Returns the number of seconds this scheduling task has been running. """
+ tsSecNow = utils.timestampSecond();
+ if tsSecNow < self._tsSecStart: # paranoia
+ self._tsSecStart = tsSecNow;
+ return tsSecNow - self._tsSecStart;
+
+
+ #
+ # Create schedule.
+ #
+
+ def _recreateQueueCancelGatherings(self):
+ """
+ Cancels all pending gang gatherings on the current queue.
+ """
+ self._oDb.execute('SELECT idTestSetGangLeader\n'
+ 'FROM SchedQueues\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND idTestSetGangLeader is not NULL\n'
+ , (self._oSchedGrpData.idSchedGroup,));
+ if self._oDb.getRowCount() > 0:
+ oTBStatusLogic = TestBoxStatusLogic(self._oDb);
+ for aoRow in self._oDb.fetchAll():
+ idTestSetGangLeader = aoRow[0];
+ oTBStatusLogic.updateGangStatus(idTestSetGangLeader,
+ TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut,
+ fCommit = False);
+ return True;
+
+ def _recreateQueueItems(self, oData):
+ """
+ Returns an array of queue items (SchedQueueData).
+ Child classes must override this.
+ """
+ _ = oData;
+ return [];
+
+ def recreateQueueWorker(self):
+ """
+ Worker for recreateQueue.
+ """
+
+ #
+ # Collect the necessary data and validate it.
+ #
+ oData = ReCreateQueueData(self._oDb, self._oSchedGrpData.idSchedGroup);
+ aoErrors = oData.checkForGroupDepCycles();
+ aoErrors.extend(oData.checkForMissingTestCaseDeps());
+ if not aoErrors:
+ oData.deepTestGroupSort();
+
+ #
+ # The creation of the scheduling queue is done by the child class.
+ #
+ # We will try guess where in queue we're currently at and rotate
+ # the items such that we will resume execution in the approximately
+ # same position. The goal of the scheduler is to provide a 100%
+ # deterministic result so that if we regenerate the queue when there
+ # are no changes to the testcases, testgroups or scheduling groups
+ # involved, test execution will be unchanged (save for maybe just a
+ # little for gang gathering).
+ #
+ aoItems = [];
+ if not oData.oSchedGroup.fEnabled:
+ self.msgInfo('Disabled.');
+ elif not oData.aoArgsVariations:
+ self.msgInfo('Found no test case argument variations.');
+ else:
+ aoItems = self._recreateQueueItems(oData);
+ self.msgDebug('len(aoItems)=%s' % (len(aoItems),));
+ #for i in range(len(aoItems)):
+ # self.msgDebug('aoItems[%2d]=%s' % (i, aoItems[i]));
+ if aoItems:
+ self._oDb.execute('SELECT offQueue FROM SchedQueues WHERE idSchedGroup = %s ORDER BY idItem LIMIT 1'
+ , (self._oSchedGrpData.idSchedGroup,));
+ if self._oDb.getRowCount() > 0:
+ offQueue = self._oDb.fetchOne()[0];
+ self._oDb.execute('SELECT COUNT(*) FROM SchedQueues WHERE idSchedGroup = %s'
+ , (self._oSchedGrpData.idSchedGroup,));
+ cItems = self._oDb.fetchOne()[0];
+ offQueueNew = (offQueue * cItems) // len(aoItems);
+ if offQueueNew != 0:
+ aoItems = aoItems[offQueueNew:] + aoItems[:offQueueNew];
+
+ #
+ # Replace the scheduling queue.
+ # Care need to be take to first timeout/abort any gangs in the
+ # gathering state since these use the queue to set up the date.
+ #
+ self._recreateQueueCancelGatherings();
+ self._oDb.execute('DELETE FROM SchedQueues WHERE idSchedGroup = %s\n', (self._oSchedGrpData.idSchedGroup,));
+ if aoItems:
+ self._oDb.insertList('INSERT INTO SchedQueues (\n'
+ ' idSchedGroup,\n'
+ ' offQueue,\n'
+ ' idGenTestCaseArgs,\n'
+ ' idTestGroup,\n'
+ ' aidTestGroupPreReqs,\n'
+ ' bmHourlySchedule,\n'
+ ' cMissingGangMembers )\n',
+ aoItems, self._formatItemForInsert);
+ return (aoErrors, self._asMessages);
+
+ def _formatItemForInsert(self, oItem):
+ """
+ Used by recreateQueueWorker together with TMDatabaseConnect::insertList
+ """
+ return self._oDb.formatBindArgs('(%s,%s,%s,%s,%s,%s,%s)'
+ , ( oItem.idSchedGroup,
+ oItem.offQueue,
+ oItem.idGenTestCaseArgs,
+ oItem.idTestGroup,
+ oItem.aidTestGroupPreReqs if oItem.aidTestGroupPreReqs else None,
+ oItem.bmHourlySchedule,
+ oItem.cMissingGangMembers
+ ));
+
+ @staticmethod
+ def recreateQueue(oDb, uidAuthor, idSchedGroup, iVerbosity = 1):
+ """
+ (Re-)creates the scheduling queue for the given group.
+
+ Returns (asMessages, asMessages). On success the array with the error
+ will be empty, on failure it will contain (sError, oRelatedObject)
+ entries. The messages is for debugging and are simple strings.
+
+ Raises exception database error.
+ """
+
+ aoExtraMsgs = [];
+ if oDb.debugIsExplainEnabled():
+ aoExtraMsgs += ['Warning! Disabling SQL explain to avoid deadlocking against locked tables.'];
+ oDb.debugDisableExplain();
+
+ aoErrors = [];
+ asMessages = [];
+ try:
+ #
+ # To avoid concurrency issues (SchedQueues) and inconsistent data (*),
+ # we lock quite a few tables while doing this work. We access more
+ # data than scheduleNewTask so we lock some additional tables.
+ #
+ oDb.rollback();
+ oDb.begin();
+ oDb.execute('LOCK TABLE SchedGroups, SchedGroupMembers, TestGroups, TestGroupMembers IN SHARE MODE');
+ oDb.execute('LOCK TABLE TestBoxes, TestCaseArgs, TestCases IN SHARE MODE');
+ oDb.execute('LOCK TABLE TestBoxStatuses, SchedQueues IN EXCLUSIVE MODE');
+
+ #
+ # Instantiate the scheduler and call the worker function.
+ #
+ oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, idSchedGroup);
+ oScheduler = SchedulerBase._instantiate(oDb, oSchedGrpData, iVerbosity);
+
+ (aoErrors, asMessages) = oScheduler.recreateQueueWorker();
+ if not aoErrors:
+ SystemLogLogic(oDb).addEntry(SystemLogData.ksEvent_SchedQueueRecreate,
+ 'User #%d recreated sched queue #%d.' % (uidAuthor, idSchedGroup,));
+ oDb.commit();
+ else:
+ oDb.rollback();
+
+ except:
+ oDb.rollback();
+ raise;
+
+ return (aoErrors, aoExtraMsgs + asMessages);
+
+
+ @staticmethod
+ def cleanUpOrphanedQueues(oDb):
+ """
+ Removes orphan scheduling queues from the SchedQueues table.
+
+ Queues becomes orphaned when the scheduling group they belongs to has been deleted.
+
+ Returns number of orphaned queues.
+ Raises exception database error.
+ """
+ cRet = 0;
+ try:
+ oDb.rollback();
+ oDb.begin();
+ oDb.execute('''
+SELECT SchedQueues.idSchedGroup
+FROM SchedQueues
+ LEFT OUTER JOIN SchedGroups
+ ON SchedGroups.idSchedGroup = SchedQueues.idSchedGroup
+ AND SchedGroups.tsExpire = 'infinity'::TIMESTAMP
+WHERE SchedGroups.idSchedGroup is NULL
+GROUP BY SchedQueues.idSchedGroup''');
+ aaoOrphanRows = oDb.fetchAll();
+ cRet = len(aaoOrphanRows);
+ if cRet > 0:
+ oDb.execute('DELETE FROM SchedQueues WHERE idSchedGroup IN (%s)'
+ % (','.join([str(aoRow[0]) for aoRow in aaoOrphanRows]),));
+ oDb.commit();
+ except:
+ oDb.rollback();
+ raise;
+ return cRet;
+
+
+ #
+ # Schedule Task.
+ #
+
+ def _composeGangArguments(self, idTestSet):
+ """
+ Composes the gang specific testdriver arguments.
+ Returns command line string, including a leading space.
+ """
+
+ oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ aoGangMembers = TestSetLogic(self._oDb).getGang(oTestSet.idTestSetGangLeader);
+
+ sArgs = ' --gang-member-no %s --gang-members %s' % (oTestSet.iGangMemberNo, len(aoGangMembers));
+ for i, _ in enumerate(aoGangMembers):
+ sArgs = ' --gang-ipv4-%s %s' % (i, aoGangMembers[i].ip); ## @todo IPv6
+
+ return sArgs;
+
+
+ def composeExecResponseWorker(self, idTestSet, oTestEx, oTestBox, oBuild, oValidationKitBuild, sBaseUrl):
+ """
+ Given all the bits of data, compose an EXEC command response to the testbox.
+ """
+ sScriptZips = oTestEx.oTestCase.sValidationKitZips;
+ if sScriptZips is None or sScriptZips.find('@VALIDATIONKIT_ZIP@') >= 0:
+ assert oValidationKitBuild;
+ if sScriptZips is None:
+ sScriptZips = oValidationKitBuild.sBinaries;
+ else:
+ sScriptZips = sScriptZips.replace('@VALIDATIONKIT_ZIP@', oValidationKitBuild.sBinaries);
+ sScriptZips = sScriptZips.replace('@DOWNLOAD_BASE_URL@', sBaseUrl + config.g_ksTmDownloadBaseUrlRel);
+
+ sCmdLine = oTestEx.oTestCase.sBaseCmd + ' ' + oTestEx.sArgs;
+ sCmdLine = sCmdLine.replace('@BUILD_BINARIES@', oBuild.sBinaries);
+ sCmdLine = sCmdLine.strip();
+ if oTestEx.cGangMembers > 1:
+ sCmdLine += ' ' + self._composeGangArguments(idTestSet);
+
+ cSecTimeout = oTestEx.cSecTimeout if oTestEx.cSecTimeout is not None else oTestEx.oTestCase.cSecTimeout;
+ cSecTimeout = cSecTimeout * oTestBox.pctScaleTimeout // 100;
+
+ dResponse = \
+ {
+ constants.tbresp.ALL_PARAM_RESULT: constants.tbresp.CMD_EXEC,
+ constants.tbresp.EXEC_PARAM_RESULT_ID: idTestSet,
+ constants.tbresp.EXEC_PARAM_SCRIPT_ZIPS: sScriptZips,
+ constants.tbresp.EXEC_PARAM_SCRIPT_CMD_LINE: sCmdLine,
+ constants.tbresp.EXEC_PARAM_TIMEOUT: cSecTimeout,
+ };
+ return dResponse;
+
+ @staticmethod
+ def composeExecResponse(oDb, idTestSet, sBaseUrl, iVerbosity = 0):
+ """
+ Composes an EXEC response for a gang member (other than the last).
+ Returns a EXEC response or raises an exception (DB/input error).
+ """
+ #
+ # Gather the necessary data.
+ #
+ oTestSet = TestSetData().initFromDbWithId(oDb, idTestSet);
+ oTestBox = TestBoxData().initFromDbWithGenId(oDb, oTestSet.idGenTestBox);
+ oTestEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(oDb, oTestSet.idGenTestCaseArgs,
+ tsConfigEff = oTestSet.tsConfig,
+ tsRsrcEff = oTestSet.tsConfig);
+ oBuild = BuildDataEx().initFromDbWithId(oDb, oTestSet.idBuild);
+ oValidationKitBuild = None;
+ if oTestSet.idBuildTestSuite is not None:
+ oValidationKitBuild = BuildDataEx().initFromDbWithId(oDb, oTestSet.idBuildTestSuite);
+
+ #
+ # Instantiate the specified scheduler and let it do the rest.
+ #
+ oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestSet.idSchedGroup, oTestSet.tsCreated);
+ assert oSchedGrpData.fEnabled is True;
+ assert oSchedGrpData.idBuildSrc is not None;
+ oScheduler = SchedulerBase._instantiate(oDb, oSchedGrpData, iVerbosity);
+
+ return oScheduler.composeExecResponseWorker(idTestSet, oTestEx, oTestBox, oBuild, oValidationKitBuild, sBaseUrl);
+
+
+ def _updateTask(self, oTask, tsNow):
+ """
+ Updates a gang schedule task.
+ """
+ assert oTask.cMissingGangMembers >= 1;
+ assert oTask.idTestSetGangLeader is not None;
+ assert oTask.idTestSetGangLeader >= 1;
+ if tsNow is not None:
+ self._oDb.execute('UPDATE SchedQueues\n'
+ ' SET idTestSetGangLeader = %s,\n'
+ ' cMissingGangMembers = %s,\n'
+ ' tsLastScheduled = %s\n'
+ 'WHERE idItem = %s\n'
+ , (oTask.idTestSetGangLeader, oTask.cMissingGangMembers, tsNow, oTask.idItem,) );
+ else:
+ self._oDb.execute('UPDATE SchedQueues\n'
+ ' SET cMissingGangMembers = %s\n'
+ 'WHERE idItem = %s\n'
+ , (oTask.cMissingGangMembers, oTask.idItem,) );
+ return True;
+
+ def _moveTaskToEndOfQueue(self, oTask, cGangMembers, tsNow):
+ """
+ The task has been scheduled successfully, reset it's data move it to
+ the end of the queue.
+ """
+ if cGangMembers > 1:
+ self._oDb.execute('UPDATE SchedQueues\n'
+ ' SET idItem = NEXTVAL(\'SchedQueueItemIdSeq\'),\n'
+ ' idTestSetGangLeader = NULL,\n'
+ ' cMissingGangMembers = %s\n'
+ 'WHERE idItem = %s\n'
+ , (cGangMembers, oTask.idItem,) );
+ else:
+ self._oDb.execute('UPDATE SchedQueues\n'
+ ' SET idItem = NEXTVAL(\'SchedQueueItemIdSeq\'),\n'
+ ' idTestSetGangLeader = NULL,\n'
+ ' cMissingGangMembers = 1,\n'
+ ' tsLastScheduled = %s\n'
+ 'WHERE idItem = %s\n'
+ , (tsNow, oTask.idItem,) );
+ return True;
+
+
+
+
+ def _createTestSet(self, oTask, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, tsNow):
+ # type: (SchedQueueData, TestCaseArgsDataEx, TestBoxData, BuildDataEx, BuildDataEx, datetime.datetime) -> int
+ """
+ Creates a test set for using the given data.
+ Will not commit, someone up the callstack will that later on.
+
+ Returns the test set ID, may raise an exception on database error.
+ """
+ # Lazy bird doesn't want to write testset.py and does it all here.
+
+ #
+ # We're getting the TestSet ID first in order to include it in the base
+ # file name (that way we can directly relate files on the disk to the
+ # test set when doing batch work), and also for idTesetSetGangLeader.
+ #
+ self._oDb.execute('SELECT NEXTVAL(\'TestSetIdSeq\')');
+ idTestSet = self._oDb.fetchOne()[0];
+
+ sBaseFilename = '%04d/%02d/%02d/%02d/TestSet-%s' \
+ % (tsNow.year, tsNow.month, tsNow.day, (tsNow.hour // 6) * 6, idTestSet);
+
+ #
+ # Gang scheduling parameters. Changes the oTask data for updating by caller.
+ #
+ iGangMemberNo = 0;
+
+ if oTestEx.cGangMembers <= 1:
+ assert oTask.idTestSetGangLeader is None;
+ assert oTask.cMissingGangMembers <= 1;
+ elif oTask.idTestSetGangLeader is None:
+ assert oTask.cMissingGangMembers == oTestEx.cGangMembers;
+ oTask.cMissingGangMembers = oTestEx.cGangMembers - 1;
+ oTask.idTestSetGangLeader = idTestSet;
+ else:
+ assert oTask.cMissingGangMembers > 0 and oTask.cMissingGangMembers < oTestEx.cGangMembers;
+ oTask.cMissingGangMembers -= 1;
+
+ #
+ # Do the database stuff.
+ #
+ self._oDb.execute('INSERT INTO TestSets (\n'
+ ' idTestSet,\n'
+ ' tsConfig,\n'
+ ' tsCreated,\n'
+ ' idBuild,\n'
+ ' idBuildCategory,\n'
+ ' idBuildTestSuite,\n'
+ ' idGenTestBox,\n'
+ ' idTestBox,\n'
+ ' idSchedGroup,\n'
+ ' idTestGroup,\n'
+ ' idGenTestCase,\n'
+ ' idTestCase,\n'
+ ' idGenTestCaseArgs,\n'
+ ' idTestCaseArgs,\n'
+ ' sBaseFilename,\n'
+ ' iGangMemberNo,\n'
+ ' idTestSetGangLeader )\n'
+ 'VALUES ( %s,\n' # idTestSet
+ ' %s,\n' # tsConfig
+ ' %s,\n' # tsCreated
+ ' %s,\n' # idBuild
+ ' %s,\n' # idBuildCategory
+ ' %s,\n' # idBuildTestSuite
+ ' %s,\n' # idGenTestBox
+ ' %s,\n' # idTestBox
+ ' %s,\n' # idSchedGroup
+ ' %s,\n' # idTestGroup
+ ' %s,\n' # idGenTestCase
+ ' %s,\n' # idTestCase
+ ' %s,\n' # idGenTestCaseArgs
+ ' %s,\n' # idTestCaseArgs
+ ' %s,\n' # sBaseFilename
+ ' %s,\n' # iGangMemberNo
+ ' %s)\n' # idTestSetGangLeader
+ , ( idTestSet,
+ oTask.tsConfig,
+ tsNow,
+ oBuild.idBuild,
+ oBuild.idBuildCategory,
+ oValidationKitBuild.idBuild if oValidationKitBuild is not None else None,
+ oTestBoxData.idGenTestBox,
+ oTestBoxData.idTestBox,
+ oTask.idSchedGroup,
+ oTask.idTestGroup,
+ oTestEx.oTestCase.idGenTestCase,
+ oTestEx.oTestCase.idTestCase,
+ oTestEx.idGenTestCaseArgs,
+ oTestEx.idTestCaseArgs,
+ sBaseFilename,
+ iGangMemberNo,
+ oTask.idTestSetGangLeader,
+ ));
+
+ self._oDb.execute('INSERT INTO TestResults (\n'
+ ' idTestResultParent,\n'
+ ' idTestSet,\n'
+ ' tsCreated,\n'
+ ' idStrName,\n'
+ ' cErrors,\n'
+ ' enmStatus,\n'
+ ' iNestingDepth)\n'
+ 'VALUES ( NULL,\n' # idTestResultParent
+ ' %s,\n' # idTestSet
+ ' %s,\n' # tsCreated
+ ' 0,\n' # idStrName
+ ' 0,\n' # cErrors
+ ' \'running\'::TestStatus_T,\n'
+ ' 0)\n' # iNestingDepth
+ 'RETURNING idTestResult'
+ , ( idTestSet, tsNow, ));
+ idTestResult = self._oDb.fetchOne()[0];
+
+ self._oDb.execute('UPDATE TestSets\n'
+ ' SET idTestResult = %s\n'
+ 'WHERE idTestSet = %s\n'
+ , (idTestResult, idTestSet, ));
+
+ return idTestSet;
+
+ def _tryFindValidationKitBit(self, oTestBoxData, tsNow):
+ """
+ Tries to find the most recent validation kit build suitable for the given testbox.
+ Returns BuildDataEx or None. Raise exception on database error.
+
+ Can be overridden by child classes to change the default build requirements.
+ """
+ oBuildLogic = BuildLogic(self._oDb);
+ oBuildSource = BuildSourceData().initFromDbWithId(self._oDb, self._oSchedGrpData.idBuildSrcTestSuite, tsNow);
+ oCursor = BuildSourceLogic(self._oDb).openBuildCursor(oBuildSource, oTestBoxData.sOs, oTestBoxData.sCpuArch, tsNow);
+ for _ in range(oCursor.getRowCount()):
+ oBuild = BuildDataEx().initFromDbRow(oCursor.fetchOne());
+ if not oBuildLogic.isBuildBlacklisted(oBuild):
+ return oBuild;
+ return None;
+
+ def _tryFindBuild(self, oTask, oTestEx, oTestBoxData, tsNow):
+ """
+ Tries to find a fitting build.
+ Returns BuildDataEx or None. Raise exception on database error.
+
+ Can be overridden by child classes to change the default build requirements.
+ """
+
+ #
+ # Gather the set of prerequisites we have and turn them into a value
+ # set for use in the loop below.
+ #
+ # Note! We're scheduling on testcase level and ignoring argument variation
+ # selections in TestGroupMembers is intentional.
+ #
+ dPreReqs = {};
+
+ # Direct prerequisites. We assume they're all enabled as this can be
+ # checked at queue creation time.
+ for oPreReq in oTestEx.aoTestCasePreReqs:
+ dPreReqs[oPreReq.idTestCase] = 1;
+
+ # Testgroup dependencies from the scheduling group config.
+ if oTask.aidTestGroupPreReqs is not None:
+ for iTestGroup in oTask.aidTestGroupPreReqs:
+ # Make sure the _active_ test group members are in the cache.
+ if iTestGroup not in self.dTestGroupMembers:
+ self._oDb.execute('SELECT DISTINCT TestGroupMembers.idTestCase\n'
+ 'FROM TestGroupMembers, TestCases\n'
+ 'WHERE TestGroupMembers.idTestGroup = %s\n'
+ ' AND TestGroupMembers.tsExpire > %s\n'
+ ' AND TestGroupMembers.tsEffective <= %s\n'
+ ' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
+ ' AND TestCases.tsExpire > %s\n'
+ ' AND TestCases.tsEffective <= %s\n'
+ ' AND TestCases.fEnabled is TRUE\n'
+ , (iTestGroup, oTask.tsConfig, oTask.tsConfig, oTask.tsConfig, oTask.tsConfig,));
+ aidTestCases = [];
+ for aoRow in self._oDb.fetchAll():
+ aidTestCases.append(aoRow[0]);
+ self.dTestGroupMembers[iTestGroup] = aidTestCases;
+
+ # Add the testgroup members to the prerequisites.
+ for idTestCase in self.dTestGroupMembers[iTestGroup]:
+ dPreReqs[idTestCase] = 1;
+
+ # Create a SQL values table out of them.
+ sPreReqSet = ''
+ if dPreReqs:
+ for idPreReq in sorted(dPreReqs):
+ sPreReqSet += ', (' + str(idPreReq) + ')';
+ sPreReqSet = sPreReqSet[2:]; # drop the leading ', '.
+
+ #
+ # Try the builds.
+ #
+ self.oBuildCache.setupSource(self._oDb, self._oSchedGrpData.idBuildSrc, oTestBoxData.sOs, oTestBoxData.sCpuArch, tsNow);
+ for oEntry in self.oBuildCache:
+ #
+ # Check build requirements set by the test.
+ #
+ if not oTestEx.matchesBuildProps(oEntry.oBuild):
+ continue;
+
+ if oEntry.isBlacklisted(self._oDb):
+ oEntry.remove();
+ continue;
+
+ #
+ # Check prerequisites. The default scheduler is satisfied if one
+ # argument variation has been executed successfully. It is not
+ # satisfied if there are any failure runs.
+ #
+ if sPreReqSet:
+ fDecision = oEntry.getPreReqDecision(sPreReqSet);
+ if fDecision is None:
+ # Check for missing prereqs.
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM (VALUES ' + sPreReqSet + ') AS PreReqs(idTestCase)\n'
+ 'LEFT OUTER JOIN (SELECT idTestSet\n'
+ ' FROM TestSets\n'
+ ' WHERE enmStatus IN (%s, %s)\n'
+ ' AND idBuild = %s\n'
+ ' ) AS TestSets\n'
+ ' ON (PreReqs.idTestCase = TestSets.idTestCase)\n'
+ 'WHERE TestSets.idTestSet is NULL\n'
+ , ( TestSetData.ksTestStatus_Success, TestSetData.ksTestStatus_Skipped,
+ oEntry.oBuild.idBuild, ));
+ cMissingPreReqs = self._oDb.fetchOne()[0];
+ if cMissingPreReqs > 0:
+ self.dprint('build %s is missing %u prerequisites (out of %s)'
+ % (oEntry.oBuild.idBuild, cMissingPreReqs, sPreReqSet,));
+ oEntry.setPreReqDecision(sPreReqSet, False);
+ continue;
+
+ # Check for failed prereq runs.
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM (VALUES ' + sPreReqSet + ') AS PreReqs(idTestCase),\n'
+ ' TestSets\n'
+ 'WHERE PreReqs.idTestCase = TestSets.idTestCase\n'
+ ' AND TestSets.idBuild = %s\n'
+ ' AND TestSets.enmStatus IN (%s, %s, %s)\n'
+ , ( oEntry.oBuild.idBuild,
+ TestSetData.ksTestStatus_Failure,
+ TestSetData.ksTestStatus_TimedOut,
+ TestSetData.ksTestStatus_Rebooted,
+ )
+ );
+ cFailedPreReqs = self._oDb.fetchOne()[0];
+ if cFailedPreReqs > 0:
+ self.dprint('build %s is has %u prerequisite failures (out of %s)'
+ % (oEntry.oBuild.idBuild, cFailedPreReqs, sPreReqSet,));
+ oEntry.setPreReqDecision(sPreReqSet, False);
+ continue;
+
+ oEntry.setPreReqDecision(sPreReqSet, True);
+ elif not fDecision:
+ continue;
+
+ #
+ # If we can, check if the build files still exist.
+ #
+ if oEntry.oBuild.areFilesStillThere() is False:
+ self.dprint('build %s no longer exists' % (oEntry.oBuild.idBuild,));
+ oEntry.remove();
+ continue;
+
+ self.dprint('found oBuild=%s' % (oEntry.oBuild,));
+ return oEntry.oBuild;
+ return None;
+
+ def _tryFindMatchingBuild(self, oLeaderBuild, oTestBoxData, idBuildSrc):
+ """
+ Tries to find a matching build for gang scheduling.
+ Returns BuildDataEx or None. Raise exception on database error.
+
+ Can be overridden by child classes to change the default build requirements.
+ """
+ #
+ # Note! Should probably check build prerequisites if we get a different
+ # build back, so that we don't use a build which hasn't passed
+ # the smoke test.
+ #
+ _ = idBuildSrc;
+ return BuildLogic(self._oDb).tryFindSameBuildForOsArch(oLeaderBuild, oTestBoxData.sOs, oTestBoxData.sCpuArch);
+
+
+ def _tryAsLeader(self, oTask, oTestEx, oTestBoxData, tsNow, sBaseUrl):
+ """
+ Try schedule the task as a gang leader (can be a gang of one).
+ Returns response or None. May raise exception on DB error.
+ """
+
+ # We don't wait for busy resources, we just try the next test.
+ oTestArgsLogic = TestCaseArgsLogic(self._oDb);
+ if not oTestArgsLogic.areResourcesFree(oTestEx):
+ self.dprint('Cannot get global test resources!');
+ return None;
+
+ #
+ # Find a matching build (this is the difficult bit).
+ #
+ oBuild = self._tryFindBuild(oTask, oTestEx, oTestBoxData, tsNow);
+ if oBuild is None:
+ self.dprint('No build!');
+ return None;
+ if oTestEx.oTestCase.needValidationKitBit():
+ oValidationKitBuild = self._tryFindValidationKitBit(oTestBoxData, tsNow);
+ if oValidationKitBuild is None:
+ self.dprint('No validation kit build!');
+ return None;
+ else:
+ oValidationKitBuild = None;
+
+ #
+ # Create a testset, allocate the resources and update the state.
+ # Note! Since resource allocation may still fail, we create a nested
+ # transaction so we can roll back. (Heed lock warning in docs!)
+ #
+ self._oDb.execute('SAVEPOINT tryAsLeader');
+ idTestSet = self._createTestSet(oTask, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, tsNow);
+
+ if GlobalResourceLogic(self._oDb).allocateResources(oTestBoxData.idTestBox, oTestEx.aoGlobalRsrc, fCommit = False) \
+ is not True:
+ self._oDb.execute('ROLLBACK TO SAVEPOINT tryAsLeader');
+ self.dprint('Failed to allocate global resources!');
+ return False;
+
+ if oTestEx.cGangMembers <= 1:
+ # We're alone, put the task back at the end of the queue and issue EXEC cmd.
+ self._moveTaskToEndOfQueue(oTask, oTestEx.cGangMembers, tsNow);
+ dResponse = self.composeExecResponseWorker(idTestSet, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, sBaseUrl);
+ sTBState = TestBoxStatusData.ksTestBoxState_Testing;
+ else:
+ # We're missing gang members, issue WAIT cmd.
+ self._updateTask(oTask, tsNow if idTestSet == oTask.idTestSetGangLeader else None);
+ dResponse = { constants.tbresp.ALL_PARAM_RESULT: constants.tbresp.CMD_WAIT, };
+ sTBState = TestBoxStatusData.ksTestBoxState_GangGathering;
+
+ TestBoxStatusLogic(self._oDb).updateState(oTestBoxData.idTestBox, sTBState, idTestSet, fCommit = False);
+ self._oDb.execute('RELEASE SAVEPOINT tryAsLeader');
+ return dResponse;
+
+ def _tryAsGangMember(self, oTask, oTestEx, oTestBoxData, tsNow, sBaseUrl):
+ """
+ Try schedule the task as a gang member.
+ Returns response or None. May raise exception on DB error.
+ """
+
+ #
+ # The leader has choosen a build, we need to find a matching one for our platform.
+ # (It's up to the scheduler decide upon how strict dependencies are to be enforced
+ # upon subordinate group members.)
+ #
+ oLeaderTestSet = TestSetData().initFromDbWithId(self._oDb, oTestBoxData.idTestSetGangLeader);
+
+ oLeaderBuild = BuildDataEx().initFromDbWithId(self._oDb, oLeaderTestSet.idBuild);
+ oBuild = self._tryFindMatchingBuild(oLeaderBuild, oTestBoxData, self._oSchedGrpData.idBuildSrc);
+ if oBuild is None:
+ return None;
+
+ oValidationKitBuild = None;
+ if oLeaderTestSet.idBuildTestSuite is not None:
+ oLeaderValidationKitBit = BuildDataEx().initFromDbWithId(self._oDb, oLeaderTestSet.idBuildTestSuite);
+ oValidationKitBuild = self._tryFindMatchingBuild(oLeaderValidationKitBit, oTestBoxData,
+ self._oSchedGrpData.idBuildSrcTestSuite);
+
+ #
+ # Create a testset and update the state(s).
+ #
+ idTestSet = self._createTestSet(oTask, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, tsNow);
+
+ oTBStatusLogic = TestBoxStatusLogic(self._oDb);
+ if oTask.cMissingGangMembers < 1:
+ # The whole gang is there, move the task to the end of the queue
+ # and update the status on the other gang members.
+ self._moveTaskToEndOfQueue(oTask, oTestEx.cGangMembers, tsNow);
+ dResponse = self.composeExecResponseWorker(idTestSet, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, sBaseUrl);
+ sTBState = TestBoxStatusData.ksTestBoxState_GangTesting;
+ oTBStatusLogic.updateGangStatus(oTask.idTestSetGangLeader, sTBState, fCommit = False);
+ else:
+ # We're still missing some gang members, issue WAIT cmd.
+ self._updateTask(oTask, tsNow if idTestSet == oTask.idTestSetGangLeader else None);
+ dResponse = { constants.tbresp.ALL_PARAM_RESULT: constants.tbresp.CMD_WAIT, };
+ sTBState = TestBoxStatusData.ksTestBoxState_GangGathering;
+
+ oTBStatusLogic.updateState(oTestBoxData.idTestBox, sTBState, idTestSet, fCommit = False);
+ return dResponse;
+
+
+ def scheduleNewTaskWorker(self, oTestBoxData, tsNow, sBaseUrl):
+ """
+ Worker for schduling a new task.
+ """
+
+ #
+ # Iterate the scheduler queue (fetch all to avoid having to concurrent
+ # queries), trying out each task to see if the testbox can execute it.
+ #
+ dRejected = {}; # variations we've already checked out and rejected.
+ self._oDb.execute('SELECT *\n'
+ 'FROM SchedQueues\n'
+ 'WHERE idSchedGroup = %s\n'
+ ' AND ( bmHourlySchedule IS NULL\n'
+ ' OR get_bit(bmHourlySchedule, %s) = 1 )\n'
+ 'ORDER BY idItem ASC\n'
+ , (self._oSchedGrpData.idSchedGroup, utils.getLocalHourOfWeek()) );
+ aaoRows = self._oDb.fetchAll();
+ for aoRow in aaoRows:
+ # Don't loop forever.
+ if self.getElapsedSecs() >= config.g_kcSecMaxNewTask:
+ break;
+
+ # Unpack the data and check if we've rejected the testcasevar/group variation already (they repeat).
+ oTask = SchedQueueData().initFromDbRow(aoRow);
+ if config.g_kfSrvGlueDebugScheduler:
+ self.dprint('** Considering: idItem=%s idGenTestCaseArgs=%s idTestGroup=%s Deps=%s last=%s cfg=%s\n'
+ % ( oTask.idItem, oTask.idGenTestCaseArgs, oTask.idTestGroup, oTask.aidTestGroupPreReqs,
+ oTask.tsLastScheduled, oTask.tsConfig,));
+
+ sRejectNm = '%s:%s' % (oTask.idGenTestCaseArgs, oTask.idTestGroup,);
+ if sRejectNm in dRejected:
+ self.dprint('Duplicate, already rejected! (%s)' % (sRejectNm,));
+ continue;
+ dRejected[sRejectNm] = 1;
+
+ # Fetch all the test case info (too much, but who cares right now).
+ oTestEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTask.idGenTestCaseArgs,
+ tsConfigEff = oTask.tsConfig,
+ tsRsrcEff = oTask.tsConfig);
+ if config.g_kfSrvGlueDebugScheduler:
+ self.dprint('TestCase "%s": %s %s' % (oTestEx.oTestCase.sName, oTestEx.oTestCase.sBaseCmd, oTestEx.sArgs,));
+
+ # This shouldn't happen, but just in case it does...
+ if oTestEx.oTestCase.fEnabled is not True:
+ self.dprint('Testcase is not enabled!!');
+ continue;
+
+ # Check if the testbox properties matches the test.
+ if not oTestEx.matchesTestBoxProps(oTestBoxData):
+ self.dprint('Testbox mismatch!');
+ continue;
+
+ # Try schedule it.
+ if oTask.idTestSetGangLeader is None or oTestEx.cGangMembers <= 1:
+ dResponse = self._tryAsLeader(oTask, oTestEx, oTestBoxData, tsNow, sBaseUrl);
+ elif oTask.cMissingGangMembers > 1:
+ dResponse = self._tryAsGangMember(oTask, oTestEx, oTestBoxData, tsNow, sBaseUrl);
+ else:
+ dResponse = None; # Shouldn't happen!
+ if dResponse is not None:
+ self.dprint('Found a task! dResponse=%s' % (dResponse,));
+ return dResponse;
+
+ # Found no suitable task.
+ return None;
+
+ @staticmethod
+ def _pickSchedGroup(oTestBoxDataEx, iWorkItem, dIgnoreSchedGroupIds):
+ """
+ Picks the next scheduling group for the given testbox.
+ """
+ if len(oTestBoxDataEx.aoInSchedGroups) == 1:
+ oSchedGroup = oTestBoxDataEx.aoInSchedGroups[0].oSchedGroup;
+ if oSchedGroup.fEnabled \
+ and oSchedGroup.idBuildSrc is not None \
+ and oSchedGroup.idSchedGroup not in dIgnoreSchedGroupIds:
+ return (oSchedGroup, 0);
+ iWorkItem = 0;
+
+ elif oTestBoxDataEx.aoInSchedGroups:
+ # Construct priority table of currently enabled scheduling groups.
+ aaoList1 = [];
+ for oInGroup in oTestBoxDataEx.aoInSchedGroups:
+ oSchedGroup = oInGroup.oSchedGroup;
+ if oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None:
+ iSchedPriority = oInGroup.iSchedPriority;
+ if iSchedPriority > 31: # paranoia
+ iSchedPriority = 31;
+ elif iSchedPriority < 0: # paranoia
+ iSchedPriority = 0;
+
+ for iSchedPriority in xrange(min(iSchedPriority, len(aaoList1))):
+ aaoList1[iSchedPriority].append(oSchedGroup);
+ while len(aaoList1) <= iSchedPriority:
+ aaoList1.append([oSchedGroup,]);
+
+ # Flatten it into a single list, mixing the priorities a little so it doesn't
+ # take forever before low priority stuff is executed.
+ aoFlat = [];
+ iLo = 0;
+ iHi = len(aaoList1) - 1;
+ while iHi >= iLo:
+ aoFlat += aaoList1[iHi];
+ if iLo < iHi:
+ aoFlat += aaoList1[iLo];
+ iLo += 1;
+ iHi -= 1;
+
+ # Pick the next one.
+ cLeft = len(aoFlat);
+ while cLeft > 0:
+ cLeft -= 1;
+ iWorkItem += 1;
+ if iWorkItem >= len(aoFlat) or iWorkItem < 0:
+ iWorkItem = 0;
+ if aoFlat[iWorkItem].idSchedGroup not in dIgnoreSchedGroupIds:
+ return (aoFlat[iWorkItem], iWorkItem);
+ else:
+ iWorkItem = 0;
+
+ # No active group.
+ return (None, iWorkItem);
+
+ @staticmethod
+ def scheduleNewTask(oDb, oTestBoxData, iWorkItem, sBaseUrl, iVerbosity = 0):
+ # type: (TMDatabaseConnection, TestBoxData, int, str, int) -> None
+ """
+ Schedules a new task for a testbox.
+ """
+ oTBStatusLogic = TestBoxStatusLogic(oDb);
+
+ try:
+ #
+ # To avoid concurrency issues in SchedQueues we lock all the rows
+ # related to our scheduling queue. Also, since this is a very
+ # expensive operation we lock the testbox status row to fend of
+ # repeated retires by faulty testbox scripts.
+ #
+ tsSecStart = utils.timestampSecond();
+ oDb.rollback();
+ oDb.begin();
+ oDb.execute('SELECT idTestBox FROM TestBoxStatuses WHERE idTestBox = %s FOR UPDATE NOWAIT'
+ % (oTestBoxData.idTestBox,));
+ oDb.execute('SELECT SchedQueues.idSchedGroup\n'
+ ' FROM SchedQueues, TestBoxesInSchedGroups\n'
+ 'WHERE TestBoxesInSchedGroups.idTestBox = %s\n'
+ ' AND TestBoxesInSchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestBoxesInSchedGroups.idSchedGroup = SchedQueues.idSchedGroup\n'
+ ' FOR UPDATE'
+ % (oTestBoxData.idTestBox,));
+
+ # We need the current timestamp.
+ tsNow = oDb.getCurrentTimestamp();
+
+ # Re-read the testbox data with scheduling group relations.
+ oTestBoxDataEx = TestBoxDataEx().initFromDbWithId(oDb, oTestBoxData.idTestBox, tsNow);
+ if oTestBoxDataEx.fEnabled \
+ and oTestBoxDataEx.idGenTestBox == oTestBoxData.idGenTestBox:
+
+ # We may have to skip scheduling groups that are out of work (e.g. 'No build').
+ iInitialWorkItem = iWorkItem;
+ dIgnoreSchedGroupIds = {};
+ while True:
+ # Now, pick the scheduling group.
+ (oSchedGroup, iWorkItem) = SchedulerBase._pickSchedGroup(oTestBoxDataEx, iWorkItem, dIgnoreSchedGroupIds);
+ if oSchedGroup is None:
+ break;
+ assert oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None;
+
+ # Instantiate the specified scheduler and let it do the rest.
+ oScheduler = SchedulerBase._instantiate(oDb, oSchedGroup, iVerbosity, tsSecStart);
+ dResponse = oScheduler.scheduleNewTaskWorker(oTestBoxDataEx, tsNow, sBaseUrl);
+ if dResponse is not None:
+ oTBStatusLogic.updateWorkItem(oTestBoxDataEx.idTestBox, iWorkItem);
+ oDb.commit();
+ return dResponse;
+
+ # Check out the next work item?
+ if oScheduler.getElapsedSecs() > config.g_kcSecMaxNewTask:
+ break;
+ dIgnoreSchedGroupIds[oSchedGroup.idSchedGroup] = oSchedGroup;
+
+ # No luck, but best if we update the work item if we've made progress.
+ # Note! In case of a config.g_kcSecMaxNewTask timeout, this may accidentally skip
+ # a work item with actually work to do. But that's a small price to pay.
+ if iWorkItem != iInitialWorkItem:
+ oTBStatusLogic.updateWorkItem(oTestBoxDataEx.idTestBox, iWorkItem);
+ oDb.commit();
+ return None;
+ except:
+ oDb.rollback();
+ raise;
+
+ # Not enabled, rollback and return no task.
+ oDb.rollback();
+ return None;
+
+ @staticmethod
+ def tryCancelGangGathering(oDb, oStatusData):
+ """
+ Try canceling a gang gathering.
+
+ Returns True if successfully cancelled.
+ Returns False if not (someone raced us to the SchedQueue table).
+
+ Note! oStatusData is re-initialized.
+ """
+ assert oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGathering;
+ try:
+ #
+ # Lock the tables we're updating so we don't run into concurrency
+ # issues (we're racing both scheduleNewTask and other callers of
+ # this method).
+ #
+ oDb.rollback();
+ oDb.begin();
+ oDb.execute('LOCK TABLE TestBoxStatuses, SchedQueues IN EXCLUSIVE MODE');
+
+ #
+ # Re-read the testbox data and check that we're still in the same state.
+ #
+ oStatusData.initFromDbWithId(oDb, oStatusData.idTestBox);
+ if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGathering:
+ #
+ # Get the leader thru the test set and change the state of the whole gang.
+ #
+ oTestSetData = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
+
+ oTBStatusLogic = TestBoxStatusLogic(oDb);
+ oTBStatusLogic.updateGangStatus(oTestSetData.idTestSetGangLeader,
+ TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut,
+ fCommit = False);
+
+ #
+ # Move the scheduling queue item to the end.
+ #
+ oDb.execute('SELECT *\n'
+ 'FROM SchedQueues\n'
+ 'WHERE idTestSetGangLeader = %s\n'
+ , (oTestSetData.idTestSetGangLeader,) );
+ oTask = SchedQueueData().initFromDbRow(oDb.fetchOne());
+ oTestEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(oDb, oTask.idGenTestCaseArgs,
+ tsConfigEff = oTask.tsConfig,
+ tsRsrcEff = oTask.tsConfig);
+ oDb.execute('UPDATE SchedQueues\n'
+ ' SET idItem = NEXTVAL(\'SchedQueueItemIdSeq\'),\n'
+ ' idTestSetGangLeader = NULL,\n'
+ ' cMissingGangMembers = %s\n'
+ 'WHERE idItem = %s\n'
+ , (oTestEx.cGangMembers, oTask.idItem,) );
+
+ oDb.commit();
+ return True;
+
+ if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut:
+ oDb.rollback();
+ return True;
+ except:
+ oDb.rollback();
+ raise;
+
+ # Not enabled, rollback and return no task.
+ oDb.rollback();
+ return False;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class SchedQueueDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [SchedQueueData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/schedulerbeci.py b/src/VBox/ValidationKit/testmanager/core/schedulerbeci.py
new file mode 100755
index 00000000..5cd800d5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/schedulerbeci.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+# $Id: schedulerbeci.py $
+
+"""
+Test Manager - Best-Effort-Continuous-Integration (BECI) scheduler.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.schedulerbase import SchedulerBase, SchedQueueData;
+
+
+class SchdulerBeci(SchedulerBase): # pylint: disable=too-few-public-methods
+ """
+ The best-effort-continuous-integration scheduler, BECI for short.
+ """
+
+ def __init__(self, oDb, oSchedGrpData, iVerbosity, tsSecStart):
+ SchedulerBase.__init__(self, oDb, oSchedGrpData, iVerbosity, tsSecStart);
+
+ def _recreateQueueItems(self, oData):
+ #
+ # Prepare the input data for the loop below. We compress the priority
+ # to reduce the number of loops we need to executes below.
+ #
+ # Note! For BECI test group priority only applies to the ordering of
+ # test groups, which has been resolved by the done sorting in the
+ # base class.
+ #
+ iMinPriority = 0x7fff;
+ iMaxPriority = 0;
+ for oTestGroup in oData.aoTestGroups:
+ for oTestCase in oTestGroup.aoTestCases:
+ iPrio = oTestCase.iSchedPriority;
+ assert iPrio in range(32);
+ iPrio = iPrio // 4;
+ assert iPrio in range(8);
+ if iPrio > iMaxPriority:
+ iMaxPriority = iPrio;
+ if iPrio < iMinPriority:
+ iMinPriority = iPrio;
+
+ oTestCase.iBeciPrio = iPrio;
+ oTestCase.iNextVariation = -1;
+
+ assert iMinPriority in range(8);
+ assert iMaxPriority in range(8);
+ assert iMinPriority <= iMaxPriority;
+
+ #
+ # Generate the
+ #
+ cMaxItems = len(oData.aoArgsVariations) * 64;
+ cMaxItems = min(cMaxItems, 1048576);
+
+ aoItems = [];
+ cNotAtEnd = len(oData.aoTestCases);
+ while len(aoItems) < cMaxItems:
+ self.msgDebug('outer loop: %s items' % (len(aoItems),));
+ for iPrio in range(iMaxPriority, iMinPriority - 1, -1):
+ #self.msgDebug('prio loop: %s' % (iPrio,));
+ for oTestGroup in oData.aoTestGroups:
+ #self.msgDebug('testgroup loop: %s' % (oTestGroup,));
+ for oTestCase in oTestGroup.aoTestCases:
+ #self.msgDebug('testcase loop: idTestCase=%s' % (oTestCase.idTestCase,));
+ if iPrio <= oTestCase.iBeciPrio and oTestCase.aoArgsVariations:
+ # Get variation.
+ iNext = oTestCase.iNextVariation;
+ if iNext != 0:
+ if iNext == -1: iNext = 0;
+ cNotAtEnd -= 1;
+ oArgsVariation = oTestCase.aoArgsVariations[iNext];
+
+ # Update next variation.
+ iNext = (iNext + 1) % len(oTestCase.aoArgsVariations);
+ cNotAtEnd += iNext != 0;
+ oTestCase.iNextVariation = iNext;
+
+ # Create queue item and append it.
+ oItem = SchedQueueData();
+ oItem.initFromValues(idSchedGroup = self._oSchedGrpData.idSchedGroup,
+ idGenTestCaseArgs = oArgsVariation.idGenTestCaseArgs,
+ idTestGroup = oTestGroup.idTestGroup,
+ aidTestGroupPreReqs = oTestGroup.aidTestGroupPreReqs,
+ bmHourlySchedule = oTestGroup.bmHourlySchedule,
+ cMissingGangMembers = oArgsVariation.cGangMembers,
+ offQueue = len(aoItems));
+ aoItems.append(oItem);
+
+ # Done?
+ if cNotAtEnd == 0:
+ self.msgDebug('returns %s items' % (len(aoItems),));
+ return aoItems;
+ return aoItems;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/systemchangelog.py b/src/VBox/ValidationKit/testmanager/core/systemchangelog.py
new file mode 100755
index 00000000..7c85c76a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/systemchangelog.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+# $Id: systemchangelog.py $
+
+"""
+Test Manager - System changelog compilation.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.base import ModelLogicBase;
+from testmanager.core.useraccount import UserAccountLogic;
+from testmanager.core.systemlog import SystemLogData;
+
+
+class SystemChangelogEntry(object): # pylint: disable=too-many-instance-attributes
+ """
+ System changelog entry.
+ """
+
+ def __init__(self, tsEffective, oAuthor, sEvent, idWhat, sDesc):
+ self.tsEffective = tsEffective;
+ self.oAuthor = oAuthor;
+ self.sEvent = sEvent;
+ self.idWhat = idWhat;
+ self.sDesc = sDesc;
+
+
+class SystemChangelogLogic(ModelLogicBase):
+ """
+ System changelog compilation logic.
+ """
+
+ ## @name What kind of change.
+ ## @{
+ ksWhat_TestBox = 'chlog::TestBox';
+ ksWhat_TestCase = 'chlog::TestCase';
+ ksWhat_Blacklisting = 'chlog::Blacklisting';
+ ksWhat_Build = 'chlog::Build';
+ ksWhat_BuildSource = 'chlog::BuildSource';
+ ksWhat_FailureCategory = 'chlog::FailureCategory';
+ ksWhat_FailureReason = 'chlog::FailureReason';
+ ksWhat_GlobalRsrc = 'chlog::GlobalRsrc';
+ ksWhat_SchedGroup = 'chlog::SchedGroup';
+ ksWhat_TestGroup = 'chlog::TestGroup';
+ ksWhat_User = 'chlog::User';
+ ksWhat_TestResult = 'chlog::TestResult';
+ ## @}
+
+ ## Mapping a changelog entry kind to a table, key and clue.
+ kdWhatToTable = dict({ # pylint: disable=star-args
+ ksWhat_TestBox: ( 'TestBoxes', 'idTestBox', None, ),
+ ksWhat_TestCase: ( 'TestCasees', 'idTestCase', None, ),
+ ksWhat_Blacklisting: ( 'Blacklist', 'idBlacklisting', None, ),
+ ksWhat_Build: ( 'Builds', 'idBuild', None, ),
+ ksWhat_BuildSource: ( 'BuildSources', 'idBuildSrc', None, ),
+ ksWhat_FailureCategory: ( 'FailureCategories', 'idFailureCategory', None, ),
+ ksWhat_FailureReason: ( 'FailureReasons', 'idFailureReason', None, ),
+ ksWhat_GlobalRsrc: ( 'GlobalResources', 'idGlobalRsrc', None, ),
+ ksWhat_SchedGroup: ( 'SchedGroups', 'idSchedGroup', None, ),
+ ksWhat_TestGroup: ( 'TestGroups', 'idTestGroup', None, ),
+ ksWhat_User: ( 'Users', 'idUser', None, ),
+ ksWhat_TestResult: ( 'TestResults', 'idTestResult', None, ),
+ }, **{sEvent: ( 'SystemLog', 'tsCreated', 'TimestampId', ) for sEvent in SystemLogData.kasEvents});
+
+ ## The table key is the effective timestamp. (Can't be used above for some weird scoping reason.)
+ ksClue_TimestampId = 'TimestampId';
+
+ ## @todo move to config.py?
+ ksVSheriffLoginName = 'vsheriff';
+
+
+ ## @name for kaasChangelogTables
+ ## @internal
+ ## @{
+ ksTweak_None = '';
+ ksTweak_NotNullAuthor = 'uidAuthorNotNull';
+ ksTweak_NotNullAuthorOrVSheriff = 'uidAuthorNotNullOrVSheriff';
+ ## @}
+
+ ## @internal
+ kaasChangelogTables = (
+ # [0]: change name, [1]: Table name, [2]: key column, [3]:later, [4]: tweak
+ ( ksWhat_TestBox, 'TestBoxes', 'idTestBox', None, ksTweak_NotNullAuthor, ),
+ ( ksWhat_TestBox, 'TestBoxesInSchedGroups', 'idTestBox', None, ksTweak_None, ),
+ ( ksWhat_TestCase, 'TestCases', 'idTestCase', None, ksTweak_None, ),
+ ( ksWhat_TestCase, 'TestCaseArgs', 'idTestCase', None, ksTweak_None, ),
+ ( ksWhat_TestCase, 'TestCaseDeps', 'idTestCase', None, ksTweak_None, ),
+ ( ksWhat_TestCase, 'TestCaseGlobalRsrcDeps', 'idTestCase', None, ksTweak_None, ),
+ ( ksWhat_Blacklisting, 'BuildBlacklist', 'idBlacklisting', None, ksTweak_None, ),
+ ( ksWhat_Build, 'Builds', 'idBuild', None, ksTweak_NotNullAuthor, ),
+ ( ksWhat_BuildSource, 'BuildSources', 'idBuildSrc', None, ksTweak_None, ),
+ ( ksWhat_FailureCategory, 'FailureCategories', 'idFailureCategory', None, ksTweak_None, ),
+ ( ksWhat_FailureReason, 'FailureReasons', 'idFailureReason', None, ksTweak_None, ),
+ ( ksWhat_GlobalRsrc, 'GlobalResources', 'idGlobalRsrc', None, ksTweak_None, ),
+ ( ksWhat_SchedGroup, 'SchedGroups', 'idSchedGroup', None, ksTweak_None, ),
+ ( ksWhat_SchedGroup, 'SchedGroupMembers', 'idSchedGroup', None, ksTweak_None, ),
+ ( ksWhat_TestGroup, 'TestGroups', 'idTestGroup', None, ksTweak_None, ),
+ ( ksWhat_TestGroup, 'TestGroupMembers', 'idTestGroup', None, ksTweak_None, ),
+ ( ksWhat_User, 'Users', 'uid', None, ksTweak_None, ),
+ ( ksWhat_TestResult, 'TestResultFailures', 'idTestResult', None, ksTweak_NotNullAuthorOrVSheriff, ),
+ );
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+
+
+ def fetchForListingEx(self, iStart, cMaxRows, tsNow, cDaysBack, aiSortColumns = None):
+ """
+ Fetches SystemLog entries.
+
+ Returns an array (list) of SystemLogData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+
+ #
+ # Construct the query.
+ #
+ oUserAccountLogic = UserAccountLogic(self._oDb);
+ oVSheriff = oUserAccountLogic.tryFetchAccountByLoginName(self.ksVSheriffLoginName);
+ uidVSheriff = oVSheriff.uid if oVSheriff is not None else -1;
+
+ if tsNow is None:
+ sWhereTime = self._oDb.formatBindArgs(' WHERE tsEffective >= CURRENT_TIMESTAMP - \'%s days\'::interval\n',
+ (cDaysBack,));
+ else:
+ sWhereTime = self._oDb.formatBindArgs(' WHERE tsEffective >= (%s::timestamptz - \'%s days\'::interval)\n'
+ ' AND tsEffective <= %s\n',
+ (tsNow, cDaysBack, tsNow));
+
+ # Special entry for the system log.
+ sQuery = '(\n'
+ sQuery += ' SELECT NULL AS uidAuthor,\n';
+ sQuery += ' tsCreated AS tsEffective,\n';
+ sQuery += ' sEvent AS sEvent,\n';
+ sQuery += ' NULL AS idWhat,\n';
+ sQuery += ' sLogText AS sDesc\n';
+ sQuery += ' FROM SystemLog\n';
+ sQuery += sWhereTime.replace('tsEffective', 'tsCreated');
+ sQuery += ' ORDER BY tsCreated DESC\n'
+ sQuery += ')'
+
+ for asEntry in self.kaasChangelogTables:
+ sQuery += ' UNION (\n'
+ sQuery += ' SELECT uidAuthor, tsEffective, \'' + asEntry[0] + '\', ' + asEntry[2] + ', \'\'\n';
+ sQuery += ' FROM ' + asEntry[1] + '\n'
+ sQuery += sWhereTime;
+ if asEntry[4] == self.ksTweak_NotNullAuthor or asEntry[4] == self.ksTweak_NotNullAuthorOrVSheriff:
+ sQuery += ' AND uidAuthor IS NOT NULL\n';
+ if asEntry[4] == self.ksTweak_NotNullAuthorOrVSheriff:
+ sQuery += ' AND uidAuthor <> %u\n' % (uidVSheriff,);
+ sQuery += ' ORDER BY tsEffective DESC\n'
+ sQuery += ')';
+ sQuery += ' ORDER BY 2 DESC\n';
+ sQuery += ' LIMIT %u OFFSET %u\n' % (cMaxRows, iStart, );
+
+
+ #
+ # Execute the query and construct the return data.
+ #
+ self._oDb.execute(sQuery);
+ aoRows = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(SystemChangelogEntry(aoRow[1], oUserAccountLogic.cachedLookup(aoRow[0]),
+ aoRow[2], aoRow[3], aoRow[4]));
+
+
+ return aoRows;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/systemlog.py b/src/VBox/ValidationKit/testmanager/core/systemlog.py
new file mode 100755
index 00000000..85929c51
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/systemlog.py
@@ -0,0 +1,186 @@
+# -*- coding: utf-8 -*-
+# $Id: systemlog.py $
+
+"""
+Test Manager - SystemLog.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase;
+
+
+class SystemLogData(ModelDataBase): # pylint: disable=too-many-instance-attributes
+ """
+ SystemLog Data.
+ """
+
+ ## @name Event Constants
+ # @{
+ ksEvent_CmdNacked = 'CmdNack ';
+ ksEvent_TestBoxUnknown = 'TBoxUnkn';
+ ksEvent_TestSetAbandoned = 'TSetAbdd';
+ ksEvent_UserAccountUnknown = 'TAccUnkn';
+ ksEvent_XmlResultMalformed = 'XmlRMalf';
+ ksEvent_SchedQueueRecreate = 'SchQRecr';
+ ## @}
+
+ ## Valid event types.
+ kasEvents = \
+ [ \
+ ksEvent_CmdNacked,
+ ksEvent_TestBoxUnknown,
+ ksEvent_TestSetAbandoned,
+ ksEvent_UserAccountUnknown,
+ ksEvent_XmlResultMalformed,
+ ksEvent_SchedQueueRecreate,
+ ];
+
+ ksParam_tsCreated = 'tsCreated';
+ ksParam_sEvent = 'sEvent';
+ ksParam_sLogText = 'sLogText';
+
+ kasValidValues_sEvent = kasEvents;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.tsCreated = None;
+ self.sEvent = None;
+ self.sLogText = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Internal worker for initFromDbWithId and initFromDbWithGenId as well as
+ SystemLogLogic.
+ """
+
+ if aoRow is None:
+ raise TMExceptionBase('SystemLog row not found.');
+
+ self.tsCreated = aoRow[0];
+ self.sEvent = aoRow[1];
+ self.sLogText = aoRow[2];
+ return self;
+
+
+class SystemLogLogic(ModelLogicBase):
+ """
+ SystemLog logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches SystemLog entries.
+
+ Returns an array (list) of SystemLogData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SystemLog\n'
+ 'ORDER BY tsCreated DESC\n'
+ 'LIMIT %s OFFSET %s\n',
+ (cMaxRows, iStart));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM SystemLog\n'
+ 'WHERE tsCreated <= %s\n'
+ 'ORDER BY tsCreated DESC\n'
+ 'LIMIT %s OFFSET %s\n',
+ (tsNow, cMaxRows, iStart));
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ oData = SystemLogData();
+ oData.initFromDbRow(self._oDb.fetchOne());
+ aoRows.append(oData);
+ return aoRows;
+
+ def addEntry(self, sEvent, sLogText, cHoursRepeat = 0, fCommit = False):
+ """
+ Adds an entry to the SystemLog table.
+ Raises exception on problem.
+ """
+ if sEvent not in SystemLogData.kasEvents:
+ raise TMExceptionBase('Unknown event type "%s"' % (sEvent,));
+
+ # Check the repeat restriction first.
+ if cHoursRepeat > 0:
+ self._oDb.execute('SELECT COUNT(*) as Stuff\n'
+ 'FROM SystemLog\n'
+ 'WHERE tsCreated >= (current_timestamp - interval \'%s hours\')\n'
+ ' AND sEvent = %s\n'
+ ' AND sLogText = %s\n',
+ (cHoursRepeat,
+ sEvent,
+ sLogText));
+ aRow = self._oDb.fetchOne();
+ if aRow[0] > 0:
+ return None;
+
+ # Insert it.
+ self._oDb.execute('INSERT INTO SystemLog (sEvent, sLogText)\n'
+ 'VALUES (%s, %s)\n',
+ (sEvent, sLogText));
+
+ if fCommit:
+ self._oDb.commit();
+ return True;
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class SystemLogDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [SystemLogData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testbox.pgsql b/src/VBox/ValidationKit/testmanager/core/testbox.pgsql
new file mode 100644
index 00000000..a6a085b8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testbox.pgsql
@@ -0,0 +1,635 @@
+-- $Id: testbox.pgsql $
+--- @file
+-- VBox Test Manager Database Stored Procedures - TestBoxes.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+--
+-- Old type signatures.
+--
+DROP FUNCTION IF EXISTS TestBoxLogic_addEntry(a_uidAuthor INTEGER,
+ a_ip inet,
+ a_uuidSystem uuid,
+ a_sName TEXT,
+ a_sDescription TEXT,
+ a_idSchedGroup INTEGER,
+ a_fEnabled BOOLEAN,
+ a_enmLomKind LomKind_T,
+ a_ipLom inet,
+ a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun.
+ a_sComment TEXT,
+ a_enmPendingCmd TestBoxCmd_T,
+ OUT r_idTestBox INTEGER,
+ OUT r_idGenTestBox INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE);
+DROP FUNCTION IF EXISTS TestBoxLogic_editEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_ip inet,
+ a_uuidSystem uuid,
+ a_sName TEXT,
+ a_sDescription TEXT,
+ a_idSchedGroup INTEGER,
+ a_fEnabled BOOLEAN,
+ a_enmLomKind LomKind_T,
+ a_ipLom inet,
+ a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun.
+ a_sComment TEXT,
+ a_enmPendingCmd TestBoxCmd_T,
+ OUT r_idGenTestBox INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE);
+DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN);
+DROP FUNCTION IF EXISTS TestBoxLogic_addGroupEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_idSchedGroup INTEGER,
+ a_iSchedPriority INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE);
+DROP FUNCTION IF EXISTS TestBoxLogic_editGroupEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_idSchedGroup INTEGER,
+ a_iSchedPriority INTEGER,
+ OUT r_tsEffective INTEGER);
+
+
+---
+-- Checks if the test box name is unique, ignoring a_idTestCaseIgnore.
+-- Raises exception if duplicates are found.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ SELECT COUNT(*) INTO v_cRows
+ FROM TestBoxes
+ WHERE sName = a_sName
+ AND tsExpire = 'infinity'::TIMESTAMP
+ AND idTestBox <> a_idTestBoxIgnore;
+ IF v_cRows <> 0 THEN
+ RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Checks that the given scheduling group exists.
+-- Raises exception if it doesn't.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_checkSchedGroupExists(a_idSchedGroup INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ SELECT COUNT(*) INTO v_cRows
+ FROM SchedGroups
+ WHERE idSchedGroup = a_idSchedGroup
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ IF v_cRows <> 1 THEN
+ IF v_cRows = 0 THEN
+ RAISE EXCEPTION 'Scheduling group with ID % was not found', a_idSchedGroup;
+ END IF;
+ RAISE EXCEPTION 'Integrity error in SchedGroups: % current rows with idSchedGroup=%', v_cRows, a_idSchedGroup;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Checks that the given testbxo + scheduling group pair does not currently exists.
+-- Raises exception if it does.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox INTEGER, a_idSchedGroup INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ SELECT COUNT(*) INTO v_cRows
+ FROM TestBoxesInSchedGroups
+ WHERE idTestBox = a_idTestBox
+ AND idSchedGroup = a_idSchedGroup
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ IF v_cRows <> 0 THEN
+ RAISE EXCEPTION 'TestBox % is already a member of scheduling group %', a_idTestBox, a_idSchedGroup;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Historize a row.
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cUpdatedRows INTEGER;
+ BEGIN
+ UPDATE TestBoxes
+ SET tsExpire = a_tsExpire
+ WHERE idGenTestBox = a_idGenTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
+ IF v_cUpdatedRows <> 1 THEN
+ IF v_cUpdatedRows = 0 THEN
+ RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox;
+ END IF;
+ RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Historize a in-scheduling-group row.
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_historizeGroupEntry(a_idTestBox INTEGER,
+ a_idSchedGroup INTEGER,
+ a_tsExpire TIMESTAMP WITH TIME ZONE)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cUpdatedRows INTEGER;
+ BEGIN
+ UPDATE TestBoxesInSchedGroups
+ SET tsExpire = a_tsExpire
+ WHERE idTestBox = a_idTestBox
+ AND idSchedGroup = a_idSchedGroup
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
+ IF v_cUpdatedRows <> 1 THEN
+ IF v_cUpdatedRows = 0 THEN
+ RAISE EXCEPTION 'TestBox ID % / SchedGroup ID % is no longer a valid combination', a_idTestBox, a_idSchedGroup;
+ END IF;
+ RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: % current rows for % / %',
+ v_cUpdatedRows, a_idTestBox, a_idSchedGroup;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Translate string via the string table.
+--
+-- @returns NULL if a_sValue is NULL, otherwise a string ID.
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT)
+ RETURNS INTEGER AS $$
+ DECLARE
+ v_idStr INTEGER;
+ v_cRows INTEGER;
+ BEGIN
+ IF a_sValue IS NULL THEN
+ RETURN NULL;
+ END IF;
+
+ SELECT idStr
+ INTO v_idStr
+ FROM TestBoxStrTab
+ WHERE sValue = a_sValue;
+ GET DIAGNOSTICS v_cRows = ROW_COUNT;
+ IF v_cRows = 0 THEN
+ INSERT INTO TestBoxStrTab (sValue)
+ VALUES (a_sValue)
+ RETURNING idStr INTO v_idStr;
+ END IF;
+ RETURN v_idStr;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
+--
+CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor INTEGER,
+ a_ip inet,
+ a_uuidSystem uuid,
+ a_sName TEXT,
+ a_sDescription TEXT,
+ a_fEnabled BOOLEAN,
+ a_enmLomKind LomKind_T,
+ a_ipLom inet,
+ a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun.
+ a_sComment TEXT,
+ a_enmPendingCmd TestBoxCmd_T,
+ OUT r_idTestBox INTEGER,
+ OUT r_idGenTestBox INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE
+ ) AS $$
+ DECLARE
+ v_idStrDescription INTEGER;
+ v_idStrComment INTEGER;
+ BEGIN
+ PERFORM TestBoxLogic_checkUniqueName(a_sName, -1);
+
+ SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
+ SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment;
+
+ INSERT INTO TestBoxes (
+ tsEffective, -- 1
+ uidAuthor, -- 2
+ ip, -- 3
+ uuidSystem, -- 4
+ sName, -- 5
+ idStrDescription, -- 6
+ fEnabled, -- 7
+ enmLomKind, -- 8
+ ipLom, -- 9
+ pctScaleTimeout, -- 10
+ idStrComment, -- 11
+ enmPendingCmd ) -- 12
+ VALUES (CURRENT_TIMESTAMP, -- 1
+ a_uidAuthor, -- 2
+ a_ip, -- 3
+ a_uuidSystem, -- 4
+ a_sName, -- 5
+ v_idStrDescription, -- 6
+ a_fEnabled, -- 7
+ a_enmLomKind, -- 8
+ a_ipLom, -- 9
+ a_pctScaleTimeout, -- 10
+ v_idStrComment, -- 11
+ a_enmPendingCmd ) -- 12
+ RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE function TestBoxLogic_addGroupEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_idSchedGroup INTEGER,
+ a_iSchedPriority INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE
+ ) AS $$
+ BEGIN
+ PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup);
+ PERFORM TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox, a_idSchedGroup);
+
+ INSERT INTO TestBoxesInSchedGroups (
+ idTestBox,
+ idSchedGroup,
+ tsEffective,
+ tsExpire,
+ uidAuthor,
+ iSchedPriority)
+ VALUES (a_idTestBox,
+ a_idSchedGroup,
+ CURRENT_TIMESTAMP,
+ 'infinity'::TIMESTAMP,
+ a_uidAuthor,
+ a_iSchedPriority)
+ RETURNING tsEffective INTO r_tsEffective;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
+--
+CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_ip inet,
+ a_uuidSystem uuid,
+ a_sName TEXT,
+ a_sDescription TEXT,
+ a_fEnabled BOOLEAN,
+ a_enmLomKind LomKind_T,
+ a_ipLom inet,
+ a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun.
+ a_sComment TEXT,
+ a_enmPendingCmd TestBoxCmd_T,
+ OUT r_idGenTestBox INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE
+ ) AS $$
+ DECLARE
+ v_Row TestBoxes%ROWTYPE;
+ v_idStrDescription INTEGER;
+ v_idStrComment INTEGER;
+ BEGIN
+ PERFORM TestBoxLogic_checkUniqueName(a_sName, a_idTestBox);
+
+ SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
+ SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment;
+
+ -- Fetch and historize the current row - there must be one.
+ UPDATE TestBoxes
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestBox = a_idTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP
+ RETURNING * INTO STRICT v_Row;
+
+ -- Modify the row with the new data.
+ v_Row.uidAuthor := a_uidAuthor;
+ v_Row.ip := a_ip;
+ v_Row.uuidSystem := a_uuidSystem;
+ v_Row.sName := a_sName;
+ v_Row.idStrDescription := v_idStrDescription;
+ v_Row.fEnabled := a_fEnabled;
+ v_Row.enmLomKind := a_enmLomKind;
+ v_Row.ipLom := a_ipLom;
+ v_Row.pctScaleTimeout := a_pctScaleTimeout;
+ v_Row.idStrComment := v_idStrComment;
+ v_Row.enmPendingCmd := a_enmPendingCmd;
+ v_Row.tsEffective := v_Row.tsExpire;
+ r_tsEffective := v_Row.tsExpire;
+ v_Row.tsExpire := 'infinity'::TIMESTAMP;
+
+ -- Get a new generation ID.
+ SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+ r_idGenTestBox := v_Row.idGenTestBox;
+
+ -- Insert the modified row.
+ INSERT INTO TestBoxes VALUES (v_Row.*);
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE function TestBoxLogic_editGroupEntry(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_idSchedGroup INTEGER,
+ a_iSchedPriority INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE
+ ) AS $$
+ DECLARE
+ v_Row TestBoxesInSchedGroups%ROWTYPE;
+ v_idStrDescription INTEGER;
+ v_idStrComment INTEGER;
+ BEGIN
+ PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup);
+
+ -- Fetch and historize the current row - there must be one.
+ UPDATE TestBoxesInSchedGroups
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestBox = a_idTestBox
+ AND idSchedGroup = a_idSchedGroup
+ AND tsExpire = 'infinity'::TIMESTAMP
+ RETURNING * INTO STRICT v_Row;
+
+ -- Modify the row with the new data.
+ v_Row.uidAuthor := a_uidAuthor;
+ v_Row.iSchedPriority := a_iSchedPriority;
+ v_Row.tsEffective := v_Row.tsExpire;
+ r_tsEffective := v_Row.tsExpire;
+ v_Row.tsExpire := 'infinity'::TIMESTAMP;
+
+ -- Insert the modified row.
+ INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*);
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION TestBoxLogic_removeEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_fCascade BOOLEAN)
+ RETURNS VOID AS $$
+ DECLARE
+ v_Row TestBoxes%ROWTYPE;
+ v_tsEffective TIMESTAMP WITH TIME ZONE;
+ v_Rec RECORD;
+ v_sErrors TEXT;
+ BEGIN
+ --
+ -- Check preconditions.
+ --
+ IF a_fCascade <> TRUE THEN
+ -- @todo implement checks which throws useful exceptions.
+ ELSE
+ RAISE EXCEPTION 'CASCADE test box deletion is not implemented';
+ END IF;
+
+ --
+ -- Delete all current groups, skipping history since we're also deleting the testbox.
+ --
+ UPDATE TestBoxesInSchedGroups
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestBox = a_idTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ --
+ -- To preserve the information about who deleted the record, we try to
+ -- add a dummy record which expires immediately. I say try because of
+ -- the primary key, we must let the new record be valid for 1 us. :-(
+ --
+ SELECT * INTO STRICT v_Row
+ FROM TestBoxes
+ WHERE idTestBox = a_idTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
+ IF v_Row.tsEffective < v_tsEffective THEN
+ PERFORM TestBoxLogic_historizeEntry(v_Row.idGenTestBox, v_tsEffective);
+
+ v_Row.tsEffective := v_tsEffective;
+ v_Row.tsExpire := CURRENT_TIMESTAMP;
+ v_Row.uidAuthor := a_uidAuthor;
+ SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+ INSERT INTO TestBoxes VALUES (v_Row.*);
+ ELSE
+ PERFORM TestBoxLogic_historizeEntry(v_Row.idGenTestBox, CURRENT_TIMESTAMP);
+ END IF;
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'Test box with ID % does not currently exist', a_idTestBox;
+ WHEN TOO_MANY_ROWS THEN
+ RAISE EXCEPTION 'Integrity error in TestBoxes: Too many current rows for %', a_idTestBox;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION TestBoxLogic_removeGroupEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_idSchedGroup INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_Row TestBoxesInSchedGroups%ROWTYPE;
+ v_tsEffective TIMESTAMP WITH TIME ZONE;
+ BEGIN
+ --
+ -- To preserve the information about who deleted the record, we try to
+ -- add a dummy record which expires immediately. I say try because of
+ -- the primary key, we must let the new record be valid for 1 us. :-(
+ --
+ SELECT * INTO STRICT v_Row
+ FROM TestBoxesInSchedGroups
+ WHERE idTestBox = a_idTestBox
+ AND idSchedGroup = a_idSchedGroup
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
+ IF v_Row.tsEffective < v_tsEffective THEN
+ PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, v_tsEffective);
+
+ v_Row.tsEffective := v_tsEffective;
+ v_Row.tsExpire := CURRENT_TIMESTAMP;
+ v_Row.uidAuthor := a_uidAuthor;
+ INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*);
+ ELSE
+ PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, CURRENT_TIMESTAMP);
+ END IF;
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'TestBox #% does is not currently a member of scheduling group #%', a_idTestBox, a_idSchedGroup;
+ WHEN TOO_MANY_ROWS THEN
+ RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: Too many current rows for % / %',
+ a_idTestBox, a_idSchedGroup;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Sign on update
+--
+CREATE OR REPLACE function TestBoxLogic_updateOnSignOn(a_idTestBox INTEGER,
+ a_ip inet,
+ a_sOs TEXT,
+ a_sOsVersion TEXT,
+ a_sCpuVendor TEXT,
+ a_sCpuArch TEXT,
+ a_sCpuName TEXT,
+ a_lCpuRevision bigint,
+ a_cCpus INTEGER, -- Actually smallint, but default typing fun.
+ a_fCpuHwVirt boolean,
+ a_fCpuNestedPaging boolean,
+ a_fCpu64BitGuest boolean,
+ a_fChipsetIoMmu boolean,
+ a_fRawMode boolean,
+ a_cMbMemory bigint,
+ a_cMbScratch bigint,
+ a_sReport TEXT,
+ a_iTestBoxScriptRev INTEGER,
+ a_iPythonHexVersion INTEGER,
+ OUT r_idGenTestBox INTEGER
+ ) AS $$
+ DECLARE
+ v_Row TestBoxes%ROWTYPE;
+ v_idStrOs INTEGER;
+ v_idStrOsVersion INTEGER;
+ v_idStrCpuVendor INTEGER;
+ v_idStrCpuArch INTEGER;
+ v_idStrCpuName INTEGER;
+ v_idStrReport INTEGER;
+ BEGIN
+ SELECT TestBoxLogic_lookupOrFindString(a_sOs) INTO v_idStrOs;
+ SELECT TestBoxLogic_lookupOrFindString(a_sOsVersion) INTO v_idStrOsVersion;
+ SELECT TestBoxLogic_lookupOrFindString(a_sCpuVendor) INTO v_idStrCpuVendor;
+ SELECT TestBoxLogic_lookupOrFindString(a_sCpuArch) INTO v_idStrCpuArch;
+ SELECT TestBoxLogic_lookupOrFindString(a_sCpuName) INTO v_idStrCpuName;
+ SELECT TestBoxLogic_lookupOrFindString(a_sReport) INTO v_idStrReport;
+
+ -- Fetch and historize the current row - there must be one.
+ UPDATE TestBoxes
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestBox = a_idTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP
+ RETURNING * INTO STRICT v_Row;
+
+ -- Modify the row with the new data.
+ v_Row.uidAuthor := NULL;
+ v_Row.ip := a_ip;
+ v_Row.idStrOs := v_idStrOs;
+ v_Row.idStrOsVersion := v_idStrOsVersion;
+ v_Row.idStrCpuVendor := v_idStrCpuVendor;
+ v_Row.idStrCpuArch := v_idStrCpuArch;
+ v_Row.idStrCpuName := v_idStrCpuName;
+ v_Row.lCpuRevision := a_lCpuRevision;
+ v_Row.cCpus := a_cCpus;
+ v_Row.fCpuHwVirt := a_fCpuHwVirt;
+ v_Row.fCpuNestedPaging := a_fCpuNestedPaging;
+ v_Row.fCpu64BitGuest := a_fCpu64BitGuest;
+ v_Row.fChipsetIoMmu := a_fChipsetIoMmu;
+ v_Row.fRawMode := a_fRawMode;
+ v_Row.cMbMemory := a_cMbMemory;
+ v_Row.cMbScratch := a_cMbScratch;
+ v_Row.idStrReport := v_idStrReport;
+ v_Row.iTestBoxScriptRev := a_iTestBoxScriptRev;
+ v_Row.iPythonHexVersion := a_iPythonHexVersion;
+ v_Row.tsEffective := v_Row.tsExpire;
+ v_Row.tsExpire := 'infinity'::TIMESTAMP;
+
+ -- Get a new generation ID.
+ SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+ r_idGenTestBox := v_Row.idGenTestBox;
+
+ -- Insert the modified row.
+ INSERT INTO TestBoxes VALUES (v_Row.*);
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Set new command.
+--
+CREATE OR REPLACE function TestBoxLogic_setCommand(a_uidAuthor INTEGER,
+ a_idTestBox INTEGER,
+ a_enmOldCmd TestBoxCmd_T,
+ a_enmNewCmd TestBoxCmd_T,
+ a_sComment TEXT,
+ OUT r_idGenTestBox INTEGER,
+ OUT r_tsEffective TIMESTAMP WITH TIME ZONE
+ ) AS $$
+ DECLARE
+ v_Row TestBoxes%ROWTYPE;
+ v_idStrComment INTEGER;
+ BEGIN
+ SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment;
+
+ -- Fetch and historize the current row - there must be one.
+ UPDATE TestBoxes
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestBox = a_idTestBox
+ AND tsExpire = 'infinity'::TIMESTAMP
+ AND enmPendingCmd = a_enmOldCmd
+ RETURNING * INTO STRICT v_Row;
+
+ -- Modify the row with the new data.
+ v_Row.enmPendingCmd := a_enmNewCmd;
+ IF v_idStrComment IS NOT NULL THEN
+ v_Row.idStrComment := v_idStrComment;
+ END IF;
+ v_Row.tsEffective := v_Row.tsExpire;
+ r_tsEffective := v_Row.tsExpire;
+ v_Row.tsExpire := 'infinity'::TIMESTAMP;
+
+ -- Get a new generation ID.
+ SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+ r_idGenTestBox := v_Row.idGenTestBox;
+
+ -- Insert the modified row.
+ INSERT INTO TestBoxes VALUES (v_Row.*);
+ END;
+$$ LANGUAGE plpgsql;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testbox.py b/src/VBox/ValidationKit/testmanager/core/testbox.py
new file mode 100755
index 00000000..6686ca3b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testbox.py
@@ -0,0 +1,1286 @@
+# -*- coding: utf-8 -*-
+# $Id: testbox.py $
+
+"""
+Test Manager - TestBox.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import copy;
+import sys;
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core import db;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMInFligthCollision, \
+ TMInvalidData, TMTooManyRows, TMRowNotFound, \
+ ChangeLogEntry, AttributeChangeEntry, AttributeChangeEntryPre;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestBoxInSchedGroupData(ModelDataBase):
+ """
+ TestBox in SchedGroup data.
+ """
+
+ ksParam_idTestBox = 'TestBoxInSchedGroup_idTestBox';
+ ksParam_idSchedGroup = 'TestBoxInSchedGroup_idSchedGroup';
+ ksParam_tsEffective = 'TestBoxInSchedGroup_tsEffective';
+ ksParam_tsExpire = 'TestBoxInSchedGroup_tsExpire';
+ ksParam_uidAuthor = 'TestBoxInSchedGroup_uidAuthor';
+ ksParam_iSchedPriority = 'TestBoxInSchedGroup_iSchedPriority';
+
+ kasAllowNullAttributes = [ 'tsEffective', 'tsExpire', 'uidAuthor', ]
+
+ kiMin_iSchedPriority = 0;
+ kiMax_iSchedPriority = 32;
+
+ kcDbColumns = 6;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+ self.idTestBox = None;
+ self.idSchedGroup = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.iSchedPriority = 16;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Expecting the result from a query like this:
+ SELECT * FROM TestBoxesInSchedGroups
+ """
+ if aoRow is None:
+ raise TMRowNotFound('TestBox/SchedGroup not found.');
+
+ self.idTestBox = aoRow[0];
+ self.idSchedGroup = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ self.iSchedPriority = aoRow[5];
+
+ return self;
+
+class TestBoxInSchedGroupDataEx(TestBoxInSchedGroupData):
+ """
+ Extended version of TestBoxInSchedGroupData that contains the scheduling group.
+ """
+
+ def __init__(self):
+ TestBoxInSchedGroupData.__init__(self);
+ self.oSchedGroup = None # type: SchedGroupData
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Extended version of initFromDbRow that fills in the rest from the database.
+ """
+ from testmanager.core.schedgroup import SchedGroupData;
+ self.initFromDbRow(aoRow);
+ self.oSchedGroup = SchedGroupData().initFromDbWithId(oDb, self.idSchedGroup, tsNow, sPeriodBack);
+ return self;
+
+class TestBoxDataForSchedGroup(TestBoxInSchedGroupData):
+ """
+ Extended version of TestBoxInSchedGroupData that adds the testbox data (if available).
+ Used by TestBoxLogic.fetchForSchedGroup
+ """
+
+ def __init__(self):
+ TestBoxInSchedGroupData.__init__(self);
+ self.oTestBox = None # type: TestBoxData
+
+ def initFromDbRow(self, aoRow):
+ """
+ The row is: TestBoxesInSchedGroups.*, TestBoxesWithStrings.*
+ """
+ TestBoxInSchedGroupData.initFromDbRow(self, aoRow);
+ if aoRow[self.kcDbColumns]:
+ self.oTestBox = TestBoxData().initFromDbRow(aoRow[self.kcDbColumns:]);
+ else:
+ self.oTestBox = None;
+ return self;
+
+ def getDataAttributes(self):
+ asAttributes = TestBoxInSchedGroupData.getDataAttributes(self);
+ asAttributes.remove('oTestBox');
+ return asAttributes;
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ dErrors = TestBoxInSchedGroupData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+ if self.ksParam_idTestBox not in dErrors:
+ self.oTestBox = TestBoxData();
+ try:
+ self.oTestBox.initFromDbWithId(oDb, self.idTestBox);
+ except Exception as oXcpt:
+ self.oTestBox = TestBoxData()
+ dErrors[self.ksParam_idTestBox] = str(oXcpt);
+ return dErrors;
+
+
+# pylint: disable=invalid-name
+class TestBoxData(ModelDataBase): # pylint: disable=too-many-instance-attributes
+ """
+ TestBox Data.
+ """
+
+ ## LomKind_T
+ ksLomKind_None = 'none';
+ ksLomKind_ILOM = 'ilom';
+ ksLomKind_ELOM = 'elom';
+ ksLomKind_AppleXserveLom = 'apple-xserver-lom';
+ kasLomKindValues = [ ksLomKind_None, ksLomKind_ILOM, ksLomKind_ELOM, ksLomKind_AppleXserveLom];
+ kaoLomKindDescs = \
+ [
+ ( ksLomKind_None, 'None', ''),
+ ( ksLomKind_ILOM, 'ILOM', ''),
+ ( ksLomKind_ELOM, 'ELOM', ''),
+ ( ksLomKind_AppleXserveLom, 'Apple Xserve LOM', ''),
+ ];
+
+
+ ## TestBoxCmd_T
+ ksTestBoxCmd_None = 'none';
+ ksTestBoxCmd_Abort = 'abort';
+ ksTestBoxCmd_Reboot = 'reboot';
+ ksTestBoxCmd_Upgrade = 'upgrade';
+ ksTestBoxCmd_UpgradeAndReboot = 'upgrade-and-reboot';
+ ksTestBoxCmd_Special = 'special';
+ kasTestBoxCmdValues = [ ksTestBoxCmd_None, ksTestBoxCmd_Abort, ksTestBoxCmd_Reboot, ksTestBoxCmd_Upgrade,
+ ksTestBoxCmd_UpgradeAndReboot, ksTestBoxCmd_Special];
+ kaoTestBoxCmdDescs = \
+ [
+ ( ksTestBoxCmd_None, 'None', ''),
+ ( ksTestBoxCmd_Abort, 'Abort current test', ''),
+ ( ksTestBoxCmd_Reboot, 'Reboot TestBox', ''),
+ ( ksTestBoxCmd_Upgrade, 'Upgrade TestBox Script', ''),
+ ( ksTestBoxCmd_UpgradeAndReboot, 'Upgrade TestBox Script and reboot', ''),
+ ( ksTestBoxCmd_Special, 'Special (reserved)', ''),
+ ];
+
+
+ ksIdAttr = 'idTestBox';
+ ksIdGenAttr = 'idGenTestBox';
+
+ ksParam_idTestBox = 'TestBox_idTestBox';
+ ksParam_tsEffective = 'TestBox_tsEffective';
+ ksParam_tsExpire = 'TestBox_tsExpire';
+ ksParam_uidAuthor = 'TestBox_uidAuthor';
+ ksParam_idGenTestBox = 'TestBox_idGenTestBox';
+ ksParam_ip = 'TestBox_ip';
+ ksParam_uuidSystem = 'TestBox_uuidSystem';
+ ksParam_sName = 'TestBox_sName';
+ ksParam_sDescription = 'TestBox_sDescription';
+ ksParam_fEnabled = 'TestBox_fEnabled';
+ ksParam_enmLomKind = 'TestBox_enmLomKind';
+ ksParam_ipLom = 'TestBox_ipLom';
+ ksParam_pctScaleTimeout = 'TestBox_pctScaleTimeout';
+ ksParam_sComment = 'TestBox_sComment';
+ ksParam_sOs = 'TestBox_sOs';
+ ksParam_sOsVersion = 'TestBox_sOsVersion';
+ ksParam_sCpuVendor = 'TestBox_sCpuVendor';
+ ksParam_sCpuArch = 'TestBox_sCpuArch';
+ ksParam_sCpuName = 'TestBox_sCpuName';
+ ksParam_lCpuRevision = 'TestBox_lCpuRevision';
+ ksParam_cCpus = 'TestBox_cCpus';
+ ksParam_fCpuHwVirt = 'TestBox_fCpuHwVirt';
+ ksParam_fCpuNestedPaging = 'TestBox_fCpuNestedPaging';
+ ksParam_fCpu64BitGuest = 'TestBox_fCpu64BitGuest';
+ ksParam_fChipsetIoMmu = 'TestBox_fChipsetIoMmu';
+ ksParam_fRawMode = 'TestBox_fRawMode';
+ ksParam_cMbMemory = 'TestBox_cMbMemory';
+ ksParam_cMbScratch = 'TestBox_cMbScratch';
+ ksParam_sReport = 'TestBox_sReport';
+ ksParam_iTestBoxScriptRev = 'TestBox_iTestBoxScriptRev';
+ ksParam_iPythonHexVersion = 'TestBox_iPythonHexVersion';
+ ksParam_enmPendingCmd = 'TestBox_enmPendingCmd';
+
+ kasInternalAttributes = [ 'idStrDescription', 'idStrComment', 'idStrOs', 'idStrOsVersion', 'idStrCpuVendor',
+ 'idStrCpuArch', 'idStrCpuName', 'idStrReport', ];
+ kasMachineSettableOnly = [ 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
+ 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'fRawMode',
+ 'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion', ];
+ kasAllowNullAttributes = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
+ 'ipLom', 'sComment', ] + kasMachineSettableOnly + kasInternalAttributes;
+
+ kasValidValues_enmLomKind = kasLomKindValues;
+ kasValidValues_enmPendingCmd = kasTestBoxCmdValues;
+ kiMin_pctScaleTimeout = 11;
+ kiMax_pctScaleTimeout = 19999;
+ kcchMax_sReport = 65535;
+
+ kcDbColumns = 40; # including the 7 string joins columns
+
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestBox = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.idGenTestBox = None;
+ self.ip = None;
+ self.uuidSystem = None;
+ self.sName = None;
+ self.idStrDescription = None;
+ self.fEnabled = False;
+ self.enmLomKind = self.ksLomKind_None;
+ self.ipLom = None;
+ self.pctScaleTimeout = 100;
+ self.idStrComment = None;
+ self.idStrOs = None;
+ self.idStrOsVersion = None;
+ self.idStrCpuVendor = None;
+ self.idStrCpuArch = None;
+ self.idStrCpuName = None;
+ self.lCpuRevision = None;
+ self.cCpus = 1;
+ self.fCpuHwVirt = False;
+ self.fCpuNestedPaging = False;
+ self.fCpu64BitGuest = False;
+ self.fChipsetIoMmu = False;
+ self.fRawMode = None;
+ self.cMbMemory = 1;
+ self.cMbScratch = 0;
+ self.idStrReport = None;
+ self.iTestBoxScriptRev = 0;
+ self.iPythonHexVersion = 0;
+ self.enmPendingCmd = self.ksTestBoxCmd_None;
+ # String table values.
+ self.sDescription = None;
+ self.sComment = None;
+ self.sOs = None;
+ self.sOsVersion = None;
+ self.sCpuVendor = None;
+ self.sCpuArch = None;
+ self.sCpuName = None;
+ self.sReport = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Internal worker for initFromDbWithId and initFromDbWithGenId as well as
+ from TestBoxLogic. Expecting the result from a query like this:
+ SELECT TestBoxesWithStrings.* FROM TestBoxesWithStrings
+ """
+ if aoRow is None:
+ raise TMRowNotFound('TestBox not found.');
+
+ self.idTestBox = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.idGenTestBox = aoRow[4];
+ self.ip = aoRow[5];
+ self.uuidSystem = aoRow[6];
+ self.sName = aoRow[7];
+ self.idStrDescription = aoRow[8];
+ self.fEnabled = aoRow[9];
+ self.enmLomKind = aoRow[10];
+ self.ipLom = aoRow[11];
+ self.pctScaleTimeout = aoRow[12];
+ self.idStrComment = aoRow[13];
+ self.idStrOs = aoRow[14];
+ self.idStrOsVersion = aoRow[15];
+ self.idStrCpuVendor = aoRow[16];
+ self.idStrCpuArch = aoRow[17];
+ self.idStrCpuName = aoRow[18];
+ self.lCpuRevision = aoRow[19];
+ self.cCpus = aoRow[20];
+ self.fCpuHwVirt = aoRow[21];
+ self.fCpuNestedPaging = aoRow[22];
+ self.fCpu64BitGuest = aoRow[23];
+ self.fChipsetIoMmu = aoRow[24];
+ self.fRawMode = aoRow[25];
+ self.cMbMemory = aoRow[26];
+ self.cMbScratch = aoRow[27];
+ self.idStrReport = aoRow[28];
+ self.iTestBoxScriptRev = aoRow[29];
+ self.iPythonHexVersion = aoRow[30];
+ self.enmPendingCmd = aoRow[31];
+
+ # String table values.
+ if len(aoRow) > 32:
+ self.sDescription = aoRow[32];
+ self.sComment = aoRow[33];
+ self.sOs = aoRow[34];
+ self.sOsVersion = aoRow[35];
+ self.sCpuVendor = aoRow[36];
+ self.sCpuArch = aoRow[37];
+ self.sCpuName = aoRow[38];
+ self.sReport = aoRow[39];
+
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE idTestBox = %s\n'
+ , ( idTestBox, ), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestBox=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestBox, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
+ """
+ Initialize the object from the database.
+ """
+ _ = tsNow; # Only useful for extended data classes.
+ oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE idGenTestBox = %s\n'
+ , (idGenTestBox, ) );
+ return self.initFromDbRow(oDb.fetchOne());
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ # Override to do extra ipLom checks.
+ dErrors = ModelDataBase._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+ if self.ksParam_ipLom not in dErrors \
+ and self.ksParam_enmLomKind not in dErrors \
+ and self.enmLomKind != self.ksLomKind_None \
+ and self.ipLom is None:
+ dErrors[self.ksParam_ipLom] = 'Light-out-management IP is mandatory and a LOM is selected.'
+ return dErrors;
+
+ @staticmethod
+ def formatPythonVersionEx(iPythonHexVersion):
+ """ Unbuttons the version number and formats it as a version string. """
+ if iPythonHexVersion is None:
+ return 'N/A';
+ return 'v%d.%d.%d.%d' \
+ % ( iPythonHexVersion >> 24,
+ (iPythonHexVersion >> 16) & 0xff,
+ (iPythonHexVersion >> 8) & 0xff,
+ iPythonHexVersion & 0xff);
+
+ def formatPythonVersion(self):
+ """ Unbuttons the version number and formats it as a version string. """
+ return self.formatPythonVersionEx(self.iPythonHexVersion);
+
+
+ @staticmethod
+ def getCpuFamilyEx(lCpuRevision):
+ """ Returns the CPU family for a x86 or amd64 testboxes."""
+ if lCpuRevision is None:
+ return 0;
+ return (lCpuRevision >> 24 & 0xff);
+
+ def getCpuFamily(self):
+ """ Returns the CPU family for a x86 or amd64 testboxes."""
+ return self.getCpuFamilyEx(self.lCpuRevision);
+
+ @staticmethod
+ def getCpuModelEx(lCpuRevision):
+ """ Returns the CPU model for a x86 or amd64 testboxes."""
+ if lCpuRevision is None:
+ return 0;
+ return (lCpuRevision >> 8 & 0xffff);
+
+ def getCpuModel(self):
+ """ Returns the CPU model for a x86 or amd64 testboxes."""
+ return self.getCpuModelEx(self.lCpuRevision);
+
+ @staticmethod
+ def getCpuSteppingEx(lCpuRevision):
+ """ Returns the CPU stepping for a x86 or amd64 testboxes."""
+ if lCpuRevision is None:
+ return 0;
+ return (lCpuRevision & 0xff);
+
+ def getCpuStepping(self):
+ """ Returns the CPU stepping for a x86 or amd64 testboxes."""
+ return self.getCpuSteppingEx(self.lCpuRevision);
+
+
+ # The following is a translation of the g_aenmIntelFamily06 array in CPUMR3CpuId.cpp:
+ kdIntelFamily06 = {
+ 0x00: 'P6',
+ 0x01: 'P6',
+ 0x03: 'P6_II',
+ 0x05: 'P6_II',
+ 0x06: 'P6_II',
+ 0x07: 'P6_III',
+ 0x08: 'P6_III',
+ 0x09: 'P6_M_Banias',
+ 0x0a: 'P6_III',
+ 0x0b: 'P6_III',
+ 0x0d: 'P6_M_Dothan',
+ 0x0e: 'Core_Yonah',
+ 0x0f: 'Core2_Merom',
+ 0x15: 'P6_M_Dothan',
+ 0x16: 'Core2_Merom',
+ 0x17: 'Core2_Penryn',
+ 0x1a: 'Core7_Nehalem',
+ 0x1c: 'Atom_Bonnell',
+ 0x1d: 'Core2_Penryn',
+ 0x1e: 'Core7_Nehalem',
+ 0x1f: 'Core7_Nehalem',
+ 0x25: 'Core7_Westmere',
+ 0x26: 'Atom_Lincroft',
+ 0x27: 'Atom_Saltwell',
+ 0x2a: 'Core7_SandyBridge',
+ 0x2c: 'Core7_Westmere',
+ 0x2d: 'Core7_SandyBridge',
+ 0x2e: 'Core7_Nehalem',
+ 0x2f: 'Core7_Westmere',
+ 0x35: 'Atom_Saltwell',
+ 0x36: 'Atom_Saltwell',
+ 0x37: 'Atom_Silvermont',
+ 0x3a: 'Core7_IvyBridge',
+ 0x3c: 'Core7_Haswell',
+ 0x3d: 'Core7_Broadwell',
+ 0x3e: 'Core7_IvyBridge',
+ 0x3f: 'Core7_Haswell',
+ 0x45: 'Core7_Haswell',
+ 0x46: 'Core7_Haswell',
+ 0x47: 'Core7_Broadwell',
+ 0x4a: 'Atom_Silvermont',
+ 0x4c: 'Atom_Airmount',
+ 0x4d: 'Atom_Silvermont',
+ 0x4e: 'Core7_Skylake',
+ 0x4f: 'Core7_Broadwell',
+ 0x55: 'Core7_Skylake',
+ 0x56: 'Core7_Broadwell',
+ 0x5a: 'Atom_Silvermont',
+ 0x5c: 'Atom_Goldmont',
+ 0x5d: 'Atom_Silvermont',
+ 0x5e: 'Core7_Skylake',
+ 0x66: 'Core7_Cannonlake',
+ };
+ # Also from CPUMR3CpuId.cpp, but the switch.
+ kdIntelFamily15 = {
+ 0x00: 'NB_Willamette',
+ 0x01: 'NB_Willamette',
+ 0x02: 'NB_Northwood',
+ 0x03: 'NB_Prescott',
+ 0x04: 'NB_Prescott2M',
+ 0x05: 'NB_Unknown',
+ 0x06: 'NB_CedarMill',
+ 0x07: 'NB_Gallatin',
+ };
+
+ @staticmethod
+ def queryCpuMicroarchEx(lCpuRevision, sCpuVendor):
+ """ Try guess the microarch name for the cpu. Returns None if we cannot. """
+ if lCpuRevision is None or sCpuVendor is None:
+ return None;
+ uFam = TestBoxData.getCpuFamilyEx(lCpuRevision);
+ uMod = TestBoxData.getCpuModelEx(lCpuRevision);
+ if sCpuVendor == 'GenuineIntel':
+ if uFam == 6:
+ return TestBoxData.kdIntelFamily06.get(uMod, None);
+ if uFam == 15:
+ return TestBoxData.kdIntelFamily15.get(uMod, None);
+ elif sCpuVendor == 'AuthenticAMD':
+ if uFam == 0xf:
+ if uMod < 0x10: return 'K8_130nm';
+ if 0x60 <= uMod < 0x80: return 'K8_65nm';
+ if uMod >= 0x40: return 'K8_90nm_AMDV';
+ if uMod in [0x21, 0x23, 0x2b, 0x37, 0x3f]: return 'K8_90nm_DualCore';
+ return 'AMD_K8_90nm';
+ if uFam == 0x10: return 'K10';
+ if uFam == 0x11: return 'K10_Lion';
+ if uFam == 0x12: return 'K10_Llano';
+ if uFam == 0x14: return 'Bobcat';
+ if uFam == 0x15:
+ if uMod <= 0x01: return 'Bulldozer';
+ if uMod in [0x02, 0x10, 0x13]: return 'Piledriver';
+ return None;
+ if uFam == 0x16:
+ return 'Jaguar';
+ elif sCpuVendor == 'CentaurHauls':
+ if uFam == 0x05:
+ if uMod == 0x01: return 'Centaur_C6';
+ if uMod == 0x04: return 'Centaur_C6';
+ if uMod == 0x08: return 'Centaur_C2';
+ if uMod == 0x09: return 'Centaur_C3';
+ if uFam == 0x06:
+ if uMod == 0x05: return 'VIA_C3_M2';
+ if uMod == 0x06: return 'VIA_C3_C5A';
+ if uMod == 0x07: return 'VIA_C3_C5B' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5C';
+ if uMod == 0x08: return 'VIA_C3_C5N';
+ if uMod == 0x09: return 'VIA_C3_C5XL' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5P';
+ if uMod == 0x0a: return 'VIA_C7_C5J';
+ if uMod == 0x0f: return 'VIA_Isaiah';
+ elif sCpuVendor == ' Shanghai ':
+ if uFam == 0x07:
+ if uMod == 0x0b: return 'Shanghai_KX-5000';
+ return None;
+
+ def queryCpuMicroarch(self):
+ """ Try guess the microarch name for the cpu. Returns None if we cannot. """
+ return self.queryCpuMicroarchEx(self.lCpuRevision, self.sCpuVendor);
+
+ @staticmethod
+ def getPrettyCpuVersionEx(lCpuRevision, sCpuVendor):
+ """ Pretty formatting of the family/model/stepping with microarch optimizations. """
+ if lCpuRevision is None or sCpuVendor is None:
+ return u'<none>';
+ sMarch = TestBoxData.queryCpuMicroarchEx(lCpuRevision, sCpuVendor);
+ if sMarch is not None:
+ return '%s %02x:%x' \
+ % (sMarch, TestBoxData.getCpuModelEx(lCpuRevision), TestBoxData.getCpuSteppingEx(lCpuRevision));
+ return 'fam%02X m%02X s%02X' \
+ % ( TestBoxData.getCpuFamilyEx(lCpuRevision), TestBoxData.getCpuModelEx(lCpuRevision),
+ TestBoxData.getCpuSteppingEx(lCpuRevision));
+
+ def getPrettyCpuVersion(self):
+ """ Pretty formatting of the family/model/stepping with microarch optimizations. """
+ return self.getPrettyCpuVersionEx(self.lCpuRevision, self.sCpuVendor);
+
+ def getArchBitString(self):
+ """ Returns 32-bit, 64-bit, <none>, or sCpuArch. """
+ if self.sCpuArch is None:
+ return '<none>';
+ if self.sCpuArch in [ 'x86',]:
+ return '32-bit';
+ if self.sCpuArch in [ 'amd64',]:
+ return '64-bit';
+ return self.sCpuArch;
+
+ def getPrettyCpuVendor(self):
+ """ Pretty vendor name."""
+ if self.sCpuVendor is None:
+ return '<none>';
+ if self.sCpuVendor == 'GenuineIntel': return 'Intel';
+ if self.sCpuVendor == 'AuthenticAMD': return 'AMD';
+ if self.sCpuVendor == 'CentaurHauls': return 'VIA';
+ if self.sCpuVendor == ' Shanghai ': return 'Shanghai';
+ return self.sCpuVendor;
+
+
+class TestBoxDataEx(TestBoxData):
+ """
+ TestBox data.
+ """
+
+ ksParam_aoInSchedGroups = 'TestBox_aoInSchedGroups';
+
+ # Use [] instead of None.
+ kasAltArrayNull = [ 'aoInSchedGroups', ];
+
+ ## Helper parameter containing the comma separated list with the IDs of
+ # potential members found in the parameters.
+ ksParam_aidSchedGroups = 'TestBoxDataEx_aidSchedGroups';
+
+ def __init__(self):
+ TestBoxData.__init__(self);
+ self.aoInSchedGroups = [] # type: list[TestBoxInSchedGroupData]
+
+ def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Worker shared by the initFromDb* methods.
+ Returns self. Raises exception if no row or database error.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM TestBoxesInSchedGroups\n'
+ 'WHERE idTestBox = %s\n'
+ , (self.idTestBox,), tsNow, sPeriodBack)
+ + 'ORDER BY idSchedGroup\n' );
+ self.aoInSchedGroups = [];
+ for aoRow in oDb.fetchAll():
+ self.aoInSchedGroups.append(TestBoxInSchedGroupDataEx().initFromDbRowEx(aoRow, oDb, tsNow, sPeriodBack));
+ return self;
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
+ """
+ Reinitialize from a SELECT * FROM TestBoxesWithStrings row. Will query the
+ necessary additional data from oDb using tsNow.
+ Returns self. Raises exception if no row or database error.
+ """
+ TestBoxData.initFromDbRow(self, aoRow);
+ return self._initExtraMembersFromDb(oDb, tsNow);
+
+ def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ TestBoxData.initFromDbWithId(self, oDb, idTestBox, tsNow, sPeriodBack);
+ return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
+
+ def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
+ """
+ Initialize the object from the database.
+ """
+ TestBoxData.initFromDbWithGenId(self, oDb, idGenTestBox);
+ if tsNow is None and not oDb.isTsInfinity(self.tsExpire):
+ tsNow = self.tsEffective;
+ return self._initExtraMembersFromDb(oDb, tsNow);
+
+ def getAttributeParamNullValues(self, sAttr): # Necessary?
+ if sAttr in ['aoInSchedGroups', ]:
+ return [[], ''];
+ return TestBoxData.getAttributeParamNullValues(self, sAttr);
+
+ def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
+ """
+ For dealing with the in-scheduling-group list.
+ """
+ if sAttr != 'aoInSchedGroups':
+ return TestBoxData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
+
+ aoNewValues = [];
+ aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = []);
+ asIds = oDisp.getStringParam(self.ksParam_aidSchedGroups, sDefault = '').split(',');
+ for idSchedGroup in asIds:
+ try: idSchedGroup = int(idSchedGroup);
+ except: pass;
+ oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestBoxDataEx.ksParam_aoInSchedGroups, idSchedGroup,))
+ oMember = TestBoxInSchedGroupData().initFromParams(oDispWrapper, fStrict = False);
+ if idSchedGroup in aidSelected:
+ aoNewValues.append(oMember);
+ return aoNewValues;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb): # pylint: disable=too-many-locals
+ """
+ Validate special arrays and requirement expressions.
+
+ Some special needs for the in-scheduling-group list.
+ """
+ if sAttr != 'aoInSchedGroups':
+ return TestBoxData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ asErrors = [];
+ aoNewValues = [];
+
+ # Note! We'll be returning an error dictionary instead of an string here.
+ dErrors = {};
+
+ # HACK ALERT! idTestBox might not have been validated and converted yet, but we need detect
+ # adding so we can ignore idTestBox being NIL when validating group memberships.
+ ## @todo make base.py pass us the ksValidateFor_Xxxx value.
+ fIsAdding = bool(self.idTestBox in [ None, -1, '-1', 'None', '' ])
+
+ for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
+ oInSchedGroup = copy.copy(oInSchedGroup);
+ oInSchedGroup.idTestBox = self.idTestBox;
+ if fIsAdding:
+ dCurErrors = oInSchedGroup.validateAndConvertEx(['idTestBox',] + oInSchedGroup.kasAllowNullAttributes,
+ oDb, ModelDataBase.ksValidateFor_Add);
+ else:
+ dCurErrors = oInSchedGroup.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
+ if not dCurErrors:
+ pass; ## @todo figure out the ID?
+ else:
+ asErrors = [];
+ for sKey in dCurErrors:
+ asErrors.append('%s: %s' % (sKey[len('TestBoxInSchedGroup_'):],
+ dCurErrors[sKey] + ('{%s}' % self.idTestBox)))
+ dErrors[iInGrp] = '<br>\n'.join(asErrors)
+ aoNewValues.append(oInSchedGroup);
+
+ for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
+ for iInGrp2 in xrange(iInGrp + 1, len(self.aoInSchedGroups)):
+ if self.aoInSchedGroups[iInGrp2].idSchedGroup == oInSchedGroup.idSchedGroup:
+ sMsg = 'Duplicate scheduling group #%s".' % (oInSchedGroup.idSchedGroup,);
+ if iInGrp in dErrors: dErrors[iInGrp] += '<br>\n' + sMsg;
+ else: dErrors[iInGrp] = sMsg;
+ if iInGrp2 in dErrors: dErrors[iInGrp2] += '<br>\n' + sMsg;
+ else: dErrors[iInGrp2] = sMsg;
+ break;
+
+ return (aoNewValues, dErrors if dErrors else None);
+
+
+class TestBoxLogic(ModelLogicBase):
+ """
+ TestBox logic.
+ """
+
+ kiSortColumn_sName = 1;
+ kiSortColumn_sOs = 2;
+ kiSortColumn_sOsVersion = 3;
+ kiSortColumn_sCpuVendor = 4;
+ kiSortColumn_sCpuArch = 5;
+ kiSortColumn_lCpuRevision = 6;
+ kiSortColumn_cCpus = 7;
+ kiSortColumn_cMbMemory = 8;
+ kiSortColumn_cMbScratch = 9;
+ kiSortColumn_fCpuNestedPaging = 10;
+ kiSortColumn_iTestBoxScriptRev = 11;
+ kiSortColumn_iPythonHexVersion = 12;
+ kiSortColumn_enmPendingCmd = 13;
+ kiSortColumn_fEnabled = 14;
+ kiSortColumn_enmState = 15;
+ kiSortColumn_tsUpdated = 16;
+ kcMaxSortColumns = 17;
+ kdSortColumnMap = {
+ 0: 'TestBoxesWithStrings.sName',
+ kiSortColumn_sName: "regexp_replace(TestBoxesWithStrings.sName,'[0-9]*', '', 'g'), " \
+ "RIGHT(CONCAT(regexp_replace(TestBoxesWithStrings.sName,'[^0-9]*','', 'g'),'0'),8)::int",
+ -kiSortColumn_sName: "regexp_replace(TestBoxesWithStrings.sName,'[0-9]*', '', 'g') DESC, " \
+ "RIGHT(CONCAT(regexp_replace(TestBoxesWithStrings.sName,'[^0-9]*','', 'g'),'0'),8)::int DESC",
+ kiSortColumn_sOs: 'TestBoxesWithStrings.sOs',
+ -kiSortColumn_sOs: 'TestBoxesWithStrings.sOs DESC',
+ kiSortColumn_sOsVersion: 'TestBoxesWithStrings.sOsVersion',
+ -kiSortColumn_sOsVersion: 'TestBoxesWithStrings.sOsVersion DESC',
+ kiSortColumn_sCpuVendor: 'TestBoxesWithStrings.sCpuVendor',
+ -kiSortColumn_sCpuVendor: 'TestBoxesWithStrings.sCpuVendor DESC',
+ kiSortColumn_sCpuArch: 'TestBoxesWithStrings.sCpuArch',
+ -kiSortColumn_sCpuArch: 'TestBoxesWithStrings.sCpuArch DESC',
+ kiSortColumn_lCpuRevision: 'TestBoxesWithStrings.lCpuRevision',
+ -kiSortColumn_lCpuRevision: 'TestBoxesWithStrings.lCpuRevision DESC',
+ kiSortColumn_cCpus: 'TestBoxesWithStrings.cCpus',
+ -kiSortColumn_cCpus: 'TestBoxesWithStrings.cCpus DESC',
+ kiSortColumn_cMbMemory: 'TestBoxesWithStrings.cMbMemory',
+ -kiSortColumn_cMbMemory: 'TestBoxesWithStrings.cMbMemory DESC',
+ kiSortColumn_cMbScratch: 'TestBoxesWithStrings.cMbScratch',
+ -kiSortColumn_cMbScratch: 'TestBoxesWithStrings.cMbScratch DESC',
+ kiSortColumn_fCpuNestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging',
+ -kiSortColumn_fCpuNestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging DESC',
+ kiSortColumn_iTestBoxScriptRev: 'TestBoxesWithStrings.iTestBoxScriptRev',
+ -kiSortColumn_iTestBoxScriptRev: 'TestBoxesWithStrings.iTestBoxScriptRev DESC',
+ kiSortColumn_iPythonHexVersion: 'TestBoxesWithStrings.iPythonHexVersion',
+ -kiSortColumn_iPythonHexVersion: 'TestBoxesWithStrings.iPythonHexVersion DESC',
+ kiSortColumn_enmPendingCmd: 'TestBoxesWithStrings.enmPendingCmd',
+ -kiSortColumn_enmPendingCmd: 'TestBoxesWithStrings.enmPendingCmd DESC',
+ kiSortColumn_fEnabled: 'TestBoxesWithStrings.fEnabled',
+ -kiSortColumn_fEnabled: 'TestBoxesWithStrings.fEnabled DESC',
+ kiSortColumn_enmState: 'TestBoxStatuses.enmState',
+ -kiSortColumn_enmState: 'TestBoxStatuses.enmState DESC',
+ kiSortColumn_tsUpdated: 'TestBoxStatuses.tsUpdated',
+ -kiSortColumn_tsUpdated: 'TestBoxStatuses.tsUpdated DESC',
+ };
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+ self.dCache = None;
+
+ def tryFetchTestBoxByUuid(self, sTestBoxUuid):
+ """
+ Tries to fetch a testbox by its UUID alone.
+ """
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE uuidSystem = %s\n'
+ ' AND tsExpire = \'infinity\'::timestamp\n'
+ 'ORDER BY tsEffective DESC\n',
+ (sTestBoxUuid,));
+ if self._oDb.getRowCount() == 0:
+ return None;
+ if self._oDb.getRowCount() != 1:
+ raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
+ oData = TestBoxData();
+ oData.initFromDbRow(self._oDb.fetchOne());
+ return oData;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches testboxes for listing.
+
+ Returns an array (list) of TestBoxDataForListing items, empty list if none.
+ The TestBoxDataForListing instances are just TestBoxData with two extra
+ members, an extra oStatus member that is either None or a TestBoxStatusData
+ instance, and a member tsCurrent holding CURRENT_TIMESTAMP.
+
+ Raises exception on error.
+ """
+ class TestBoxDataForListing(TestBoxDataEx):
+ """ We add two members for the listing. """
+ def __init__(self):
+ TestBoxDataEx.__init__(self);
+ self.tsCurrent = None; # CURRENT_TIMESTAMP
+ self.oStatus = None # type: TestBoxStatusData
+
+ from testmanager.core.testboxstatus import TestBoxStatusData;
+
+ if not aiSortColumns:
+ aiSortColumns = [self.kiSortColumn_sName,];
+
+ if tsNow is None:
+ self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
+ ' TestBoxStatuses.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ ' LEFT OUTER JOIN TestBoxStatuses\n'
+ ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
+ 'WHERE TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
+ ' TestBoxStatuses.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ ' LEFT OUTER JOIN TestBoxStatuses\n'
+ ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = [];
+ for aoOne in self._oDb.fetchAll():
+ oTestBox = TestBoxDataForListing().initFromDbRowEx(aoOne, self._oDb, tsNow);
+ oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();
+ if aoOne[TestBoxData.kcDbColumns] is not None:
+ oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[TestBoxData.kcDbColumns:]);
+ aoRows.append(oTestBox);
+ return aoRows;
+
+ def fetchForSchedGroup(self, idSchedGroup, tsNow, aiSortColumns = None):
+ """
+ Fetches testboxes for listing.
+
+ Returns an array (list) of TestBoxDataForSchedGroup items, empty list if none.
+
+ Raises exception on error.
+ """
+ if not aiSortColumns:
+ aiSortColumns = [self.kiSortColumn_sName,];
+ asSortColumns = [self.kdSortColumnMap[i] for i in aiSortColumns];
+ asSortColumns.append('TestBoxesInSchedGroups.idTestBox');
+
+ if tsNow is None:
+ self._oDb.execute('''
+SELECT TestBoxesInSchedGroups.*,
+ TestBoxesWithStrings.*
+FROM TestBoxesInSchedGroups
+ LEFT OUTER JOIN TestBoxesWithStrings
+ ON TestBoxesWithStrings.idTestBox = TestBoxesInSchedGroups.idTestBox
+ AND TestBoxesWithStrings.tsExpire = 'infinity'::TIMESTAMP
+WHERE TestBoxesInSchedGroups.idSchedGroup = %s
+ AND TestBoxesInSchedGroups.tsExpire = 'infinity'::TIMESTAMP
+ORDER BY ''' + ', '.join(asSortColumns), (idSchedGroup, ));
+ else:
+ self._oDb.execute('''
+SELECT TestBoxesInSchedGroups.*,
+ TestBoxesWithStrings.*
+FROM TestBoxesInSchedGroups
+ LEFT OUTER JOIN TestBoxesWithStrings
+ ON TestBoxesWithStrings.idTestBox = TestBoxesInSchedGroups.idTestBox
+ AND TestBoxesWithStrings.tsExpire > %s
+ AND TestBoxesWithStrings.tsEffective <= %s
+WHERE TestBoxesInSchedGroups.idSchedGroup = %s
+ AND TestBoxesInSchedGroups.tsExpire > %s
+ AND TestBoxesInSchedGroups.tsEffective <= %s
+ORDER BY ''' + ', '.join(asSortColumns), (tsNow, tsNow, idSchedGroup, tsNow, tsNow, ));
+
+ aoRows = [];
+ for aoOne in self._oDb.fetchAll():
+ aoRows.append(TestBoxDataForSchedGroup().initFromDbRow(aoOne));
+ return aoRows;
+
+ def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
+ """
+ Fetches change log entries for a testbox.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+
+ ## @todo calc changes to scheduler group!
+
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE TestBoxesWithStrings.tsEffective <= %s\n'
+ ' AND TestBoxesWithStrings.idTestBox = %s\n'
+ 'ORDER BY TestBoxesWithStrings.tsExpire DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, idTestBox, cMaxRows + 1, iStart,));
+
+ aoRows = [];
+ for aoDbRow in self._oDb.fetchAll():
+ aoRows.append(TestBoxData().initFromDbRow(aoDbRow));
+
+ # Calculate the changes.
+ aoEntries = [];
+ for i in xrange(0, len(aoRows) - 1):
+ oNew = aoRows[i];
+ oOld = aoRows[i + 1];
+ aoChanges = [];
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ if sAttr == 'sReport':
+ aoChanges.append(AttributeChangeEntryPre(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+ else:
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
+
+ # If we're at the end of the log, add the initial entry.
+ if len(aoRows) <= cMaxRows and aoRows:
+ oNew = aoRows[-1];
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
+
+ UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
+ return (aoEntries, len(aoRows) > cMaxRows);
+
+ def _validateAndConvertData(self, oData, enmValidateFor):
+ # type: (TestBoxDataEx, str) -> None
+ """
+ Helper for addEntry and editEntry that validates the scheduling group IDs in
+ addtion to what's covered by the default validateAndConvert of the data object.
+
+ Raises exception on invalid input.
+ """
+ dDataErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
+ if dDataErrors:
+ raise TMInvalidData('TestBoxLogic.addEntry: %s' % (dDataErrors,));
+ if isinstance(oData, TestBoxDataEx):
+ if oData.aoInSchedGroups:
+ sSchedGrps = ', '.join('(%s)' % oCur.idSchedGroup for oCur in oData.aoInSchedGroups);
+ self._oDb.execute('SELECT SchedGroupIDs.idSchedGroup\n'
+ 'FROM (VALUES ' + sSchedGrps + ' ) AS SchedGroupIDs(idSchedGroup)\n'
+ ' LEFT OUTER JOIN SchedGroups\n'
+ ' ON SchedGroupIDs.idSchedGroup = SchedGroups.idSchedGroup\n'
+ ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'WHERE SchedGroups.idSchedGroup IS NULL\n');
+ aaoRows = self._oDb.fetchAll();
+ if aaoRows:
+ raise TMInvalidData('TestBoxLogic.addEntry missing scheduling groups: %s'
+ % (', '.join(str(aoRow[0]) for aoRow in aaoRows),));
+ return None;
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ # type: (TestBoxDataEx, int, bool) -> (int, int, datetime.datetime)
+ """
+ Creates a testbox in the database.
+ Returns the testbox ID, testbox generation ID and effective timestamp
+ of the created testbox on success. Throws error on failure.
+ """
+
+ #
+ # Validate. Extra work because of missing foreign key (due to history).
+ #
+ self._validateAndConvertData(oData, oData.ksValidateFor_Add);
+
+ #
+ # Do it.
+ #
+ self._oDb.callProc('TestBoxLogic_addEntry'
+ , ( uidAuthor,
+ oData.ip, # Should we allow setting the IP?
+ oData.uuidSystem,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled,
+ oData.enmLomKind,
+ oData.ipLom,
+ oData.pctScaleTimeout,
+ oData.sComment,
+ oData.enmPendingCmd, ) );
+ (idTestBox, idGenTestBox, tsEffective) = self._oDb.fetchOne();
+
+ for oInSchedGrp in oData.aoInSchedGroups:
+ self._oDb.callProc('TestBoxLogic_addGroupEntry',
+ ( uidAuthor, idTestBox, oInSchedGrp.idSchedGroup, oInSchedGrp.iSchedPriority,) );
+
+ self._oDb.maybeCommit(fCommit);
+ return (idTestBox, idGenTestBox, tsEffective);
+
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Data edit update, web UI is the primary user.
+
+ oData is either TestBoxDataEx or TestBoxData. The latter is for enabling
+ Returns the new generation ID and effective date.
+ """
+
+ #
+ # Validate.
+ #
+ self._validateAndConvertData(oData, oData.ksValidateFor_Edit);
+
+ #
+ # Get current data.
+ #
+ oOldData = TestBoxDataEx().initFromDbWithId(self._oDb, oData.idTestBox);
+
+ #
+ # Do it.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoInSchedGroups', ]
+ + TestBoxData.kasMachineSettableOnly ):
+ self._oDb.callProc('TestBoxLogic_editEntry'
+ , ( uidAuthor,
+ oData.idTestBox,
+ oData.ip, # Should we allow setting the IP?
+ oData.uuidSystem,
+ oData.sName,
+ oData.sDescription,
+ oData.fEnabled,
+ oData.enmLomKind,
+ oData.ipLom,
+ oData.pctScaleTimeout,
+ oData.sComment,
+ oData.enmPendingCmd, ));
+ (idGenTestBox, tsEffective) = self._oDb.fetchOne();
+ else:
+ idGenTestBox = oOldData.idGenTestBox;
+ tsEffective = oOldData.tsEffective;
+
+ if isinstance(oData, TestBoxDataEx):
+ # Calc in-group changes.
+ aoRemoved = list(oOldData.aoInSchedGroups);
+ aoNew = [];
+ aoUpdated = [];
+ for oNewInGroup in oData.aoInSchedGroups:
+ oOldInGroup = None;
+ for iCur, oCur in enumerate(aoRemoved):
+ if oCur.idSchedGroup == oNewInGroup.idSchedGroup:
+ oOldInGroup = aoRemoved.pop(iCur);
+ break;
+ if oOldInGroup is None:
+ aoNew.append(oNewInGroup);
+ elif oNewInGroup.iSchedPriority != oOldInGroup.iSchedPriority:
+ aoUpdated.append(oNewInGroup);
+
+ # Remove in-groups.
+ for oInGroup in aoRemoved:
+ self._oDb.callProc('TestBoxLogic_removeGroupEntry', (uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, ));
+
+ # Add new ones.
+ for oInGroup in aoNew:
+ self._oDb.callProc('TestBoxLogic_addGroupEntry',
+ ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
+
+ # Edit existing ones.
+ for oInGroup in aoUpdated:
+ self._oDb.callProc('TestBoxLogic_editGroupEntry',
+ ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
+ else:
+ assert isinstance(oData, TestBoxData);
+
+ self._oDb.maybeCommit(fCommit);
+ return (idGenTestBox, tsEffective);
+
+
+ def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False):
+ """
+ Delete test box and scheduling group associations.
+ """
+ self._oDb.callProc('TestBoxLogic_removeEntry'
+ , ( uidAuthor, idTestBox, fCascade,));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=too-many-arguments,too-many-locals
+ sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
+ fChipsetIoMmu, fRawMode, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
+ """
+ Update the testbox attributes automatically on behalf of the testbox script.
+ Returns the new generation id on success, raises an exception on failure.
+ """
+ _ = idGenTestBox;
+ self._oDb.callProc('TestBoxLogic_updateOnSignOn'
+ , ( idTestBox,
+ sTestBoxAddr,
+ sOs,
+ sOsVersion,
+ sCpuVendor,
+ sCpuArch,
+ sCpuName,
+ lCpuRevision,
+ cCpus,
+ fCpuHwVirt,
+ fCpuNestedPaging,
+ fCpu64BitGuest,
+ fChipsetIoMmu,
+ fRawMode,
+ cMbMemory,
+ cMbScratch,
+ sReport,
+ iTestBoxScriptRev,
+ iPythonHexVersion,));
+ return self._oDb.fetchOne()[0];
+
+
+ def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None):
+ """
+ Sets or resets the pending command on a testbox.
+ Returns (idGenTestBox, tsEffective) of the new row.
+ """
+ ## @todo throw TMInFligthCollision again...
+ self._oDb.callProc('TestBoxLogic_setCommand'
+ , ( uidAuthor, idTestBox, sOldCommand, sNewCommand, sComment,));
+ aoRow = self._oDb.fetchOne();
+ self._oDb.maybeCommit(fCommit);
+ return (aoRow[0], aoRow[1]);
+
+
+ def getAll(self):
+ """
+ Retrieve list of all registered Test Box records from DB.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE tsExpire=\'infinity\'::timestamp\n'
+ 'ORDER BY sName')
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestBoxData().initFromDbRow(aoRow))
+ return aoRet
+
+
+ def cachedLookup(self, idTestBox):
+ # type: (int) -> TestBoxDataEx
+ """
+ Looks up the most recent TestBoxData object for idTestBox via
+ an object cache.
+
+ Returns a shared TestBoxDataEx object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('TestBoxData');
+ oEntry = self.dCache.get(idTestBox, None);
+ if oEntry is None:
+ fNeedNow = False;
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE idTestBox = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestBox, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings\n'
+ 'WHERE idTestBox = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idTestBox, ));
+ fNeedNow = True;
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ if not fNeedNow:
+ oEntry = TestBoxDataEx().initFromDbRowEx(aaoRow, self._oDb);
+ else:
+ oEntry = TestBoxDataEx().initFromDbRow(aaoRow);
+ oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow = db.dbTimestampMinusOneTick(oEntry.tsExpire));
+ self.dCache[idTestBox] = oEntry;
+ return oEntry;
+
+
+
+ #
+ # The virtual test sheriff interface.
+ #
+
+ def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
+ """
+ Checks if the testbox has been rebooted in the specified time period.
+
+ This does not include already pending reboots, though under some
+ circumstances it may. These being the test box entry being edited for
+ other reasons.
+
+ Returns True / False.
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('SELECT COUNT(idTestBox)\n'
+ 'FROM TestBoxes\n'
+ 'WHERE idTestBox = %s\n'
+ ' AND tsExpire < %s\n'
+ ' AND tsExpire >= %s - interval \'%s hours\'\n'
+ ' AND enmPendingCmd IN (%s, %s)\n'
+ , ( idTestBox, tsNow, tsNow, cHoursBack,
+ TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
+ return self._oDb.fetchOne()[0] > 0;
+
+
+ def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
+ """
+ Issues a reboot command for the given test box.
+ Return True on succes, False on in-flight collision.
+ May raise DB exception on other trouble.
+ """
+ try:
+ self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
+ uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment);
+ except TMInFligthCollision:
+ return False;
+ return True;
+
+
+ def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
+ """
+ Disables the given test box.
+
+ Raises exception on trouble, without rollback.
+ """
+ oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
+ if oTestBox.fEnabled:
+ oTestBox.fEnabled = False;
+ if sComment is not None:
+ oTestBox.sComment = sComment;
+ self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
+ return None;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestBoxDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestBoxData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py b/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py
new file mode 100755
index 00000000..b131aa88
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py
@@ -0,0 +1,954 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxcontroller.py $
+
+"""
+Test Manager Core - Web Server Abstraction Base Class.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import re;
+import os;
+import string; # pylint: disable=deprecated-module
+import sys;
+import uuid;
+
+# Validation Kit imports.
+from common import constants;
+from testmanager import config;
+from testmanager.core import coreconsts;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.base import TMExceptionBase;
+from testmanager.core.globalresource import GlobalResourceLogic;
+from testmanager.core.testboxstatus import TestBoxStatusData, TestBoxStatusLogic;
+from testmanager.core.testbox import TestBoxData, TestBoxLogic;
+from testmanager.core.testresults import TestResultLogic, TestResultFileData;
+from testmanager.core.testset import TestSetData, TestSetLogic;
+from testmanager.core.systemlog import SystemLogData, SystemLogLogic;
+from testmanager.core.schedulerbase import SchedulerBase;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestBoxControllerException(TMExceptionBase):
+ """
+ Exception class for TestBoxController.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TestBoxController(object): # pylint: disable=too-few-public-methods
+ """
+ TestBox Controller class.
+ """
+
+ ## Applicable testbox commands to an idle TestBox.
+ kasIdleCmds = [TestBoxData.ksTestBoxCmd_Reboot,
+ TestBoxData.ksTestBoxCmd_Upgrade,
+ TestBoxData.ksTestBoxCmd_UpgradeAndReboot,
+ TestBoxData.ksTestBoxCmd_Special];
+ ## Applicable testbox commands to a busy TestBox.
+ kasBusyCmds = [TestBoxData.ksTestBoxCmd_Abort, TestBoxData.ksTestBoxCmd_Reboot];
+ ## Commands that can be ACK'ed.
+ kasAckableCmds = [constants.tbresp.CMD_EXEC, constants.tbresp.CMD_ABORT, constants.tbresp.CMD_REBOOT,
+ constants.tbresp.CMD_UPGRADE, constants.tbresp.CMD_UPGRADE_AND_REBOOT, constants.tbresp.CMD_SPECIAL];
+ ## Commands that can be NACK'ed or NOTSUP'ed.
+ kasNackableCmds = kasAckableCmds + [kasAckableCmds, constants.tbresp.CMD_IDLE, constants.tbresp.CMD_WAIT];
+
+ ## Mapping from TestBoxCmd_T to TestBoxState_T
+ kdCmdToState = \
+ { \
+ TestBoxData.ksTestBoxCmd_Abort: None,
+ TestBoxData.ksTestBoxCmd_Reboot: TestBoxStatusData.ksTestBoxState_Rebooting,
+ TestBoxData.ksTestBoxCmd_Upgrade: TestBoxStatusData.ksTestBoxState_Upgrading,
+ TestBoxData.ksTestBoxCmd_UpgradeAndReboot: TestBoxStatusData.ksTestBoxState_UpgradingAndRebooting,
+ TestBoxData.ksTestBoxCmd_Special: TestBoxStatusData.ksTestBoxState_DoingSpecialCmd,
+ };
+
+ ## Mapping from TestBoxCmd_T to TestBox responses commands.
+ kdCmdToTbRespCmd = \
+ {
+ TestBoxData.ksTestBoxCmd_Abort: constants.tbresp.CMD_ABORT,
+ TestBoxData.ksTestBoxCmd_Reboot: constants.tbresp.CMD_REBOOT,
+ TestBoxData.ksTestBoxCmd_Upgrade: constants.tbresp.CMD_UPGRADE,
+ TestBoxData.ksTestBoxCmd_UpgradeAndReboot: constants.tbresp.CMD_UPGRADE_AND_REBOOT,
+ TestBoxData.ksTestBoxCmd_Special: constants.tbresp.CMD_SPECIAL,
+ };
+
+ ## Mapping from TestBox responses to TestBoxCmd_T commands.
+ kdTbRespCmdToCmd = \
+ {
+ constants.tbresp.CMD_IDLE: None,
+ constants.tbresp.CMD_WAIT: None,
+ constants.tbresp.CMD_EXEC: None,
+ constants.tbresp.CMD_ABORT: TestBoxData.ksTestBoxCmd_Abort,
+ constants.tbresp.CMD_REBOOT: TestBoxData.ksTestBoxCmd_Reboot,
+ constants.tbresp.CMD_UPGRADE: TestBoxData.ksTestBoxCmd_Upgrade,
+ constants.tbresp.CMD_UPGRADE_AND_REBOOT: TestBoxData.ksTestBoxCmd_UpgradeAndReboot,
+ constants.tbresp.CMD_SPECIAL: TestBoxData.ksTestBoxCmd_Special,
+ };
+
+
+ ## The path to the upgrade zip, relative WebServerGlueBase.getBaseUrl().
+ ksUpgradeZip = 'htdocs/upgrade/VBoxTestBoxScript.zip';
+
+ ## Valid TestBox result values.
+ kasValidResults = list(constants.result.g_kasValidResults);
+ ## Mapping TestBox result values to TestStatus_T values.
+ kadTbResultToStatus = \
+ {
+ constants.result.PASSED: TestSetData.ksTestStatus_Success,
+ constants.result.SKIPPED: TestSetData.ksTestStatus_Skipped,
+ constants.result.ABORTED: TestSetData.ksTestStatus_Aborted,
+ constants.result.BAD_TESTBOX: TestSetData.ksTestStatus_BadTestBox,
+ constants.result.FAILED: TestSetData.ksTestStatus_Failure,
+ constants.result.TIMED_OUT: TestSetData.ksTestStatus_TimedOut,
+ constants.result.REBOOTED: TestSetData.ksTestStatus_Rebooted,
+ };
+
+
+ def __init__(self, oSrvGlue):
+ """
+ Won't raise exceptions.
+ """
+ self._oSrvGlue = oSrvGlue;
+ self._sAction = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._idTestBox = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._sTestBoxUuid = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._sTestBoxAddr = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._idTestSet = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._dParams = None; # _getStandardParams / dispatchRequest sets this later on.
+ self._asCheckedParams = [];
+ self._dActions = \
+ { \
+ constants.tbreq.SIGNON : self._actionSignOn,
+ constants.tbreq.REQUEST_COMMAND_BUSY: self._actionRequestCommandBusy,
+ constants.tbreq.REQUEST_COMMAND_IDLE: self._actionRequestCommandIdle,
+ constants.tbreq.COMMAND_ACK : self._actionCommandAck,
+ constants.tbreq.COMMAND_NACK : self._actionCommandNack,
+ constants.tbreq.COMMAND_NOTSUP : self._actionCommandNotSup,
+ constants.tbreq.LOG_MAIN : self._actionLogMain,
+ constants.tbreq.UPLOAD : self._actionUpload,
+ constants.tbreq.XML_RESULTS : self._actionXmlResults,
+ constants.tbreq.EXEC_COMPLETED : self._actionExecCompleted,
+ };
+
+ def _getStringParam(self, sName, asValidValues = None, fStrip = False, sDefValue = None):
+ """
+ Gets a string parameter (stripped).
+
+ Raises exception if not found and no default is provided, or if the
+ value isn't found in asValidValues.
+ """
+ if sName not in self._dParams:
+ if sDefValue is None:
+ raise TestBoxControllerException('%s parameter %s is missing' % (self._sAction, sName));
+ return sDefValue;
+ sValue = self._dParams[sName];
+ if fStrip:
+ sValue = sValue.strip();
+
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if asValidValues is not None and sValue not in asValidValues:
+ raise TestBoxControllerException('%s parameter %s value "%s" not in %s ' \
+ % (self._sAction, sName, sValue, asValidValues));
+ return sValue;
+
+ def _getBoolParam(self, sName, fDefValue = None):
+ """
+ Gets a boolean parameter.
+
+ Raises exception if not found and no default is provided, or if not a
+ valid boolean.
+ """
+ sValue = self._getStringParam(sName, [ 'True', 'true', '1', 'False', 'false', '0'], sDefValue = str(fDefValue));
+ return sValue in ('True', 'true', '1',);
+
+ def _getIntParam(self, sName, iMin = None, iMax = None):
+ """
+ Gets a string parameter.
+ Raises exception if not found, not a valid integer, or if the value
+ isn't in the range defined by iMin and iMax.
+ """
+ sValue = self._getStringParam(sName);
+ try:
+ iValue = int(sValue, 0);
+ except:
+ raise TestBoxControllerException('%s parameter %s value "%s" cannot be convert to an integer' \
+ % (self._sAction, sName, sValue));
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise TestBoxControllerException('%s parameter %s value %d is out of range [%s..%s]' \
+ % (self._sAction, sName, iValue, iMin, iMax));
+ return iValue;
+
+ def _getLongParam(self, sName, lMin = None, lMax = None, lDefValue = None):
+ """
+ Gets a string parameter.
+ Raises exception if not found, not a valid long integer, or if the value
+ isn't in the range defined by lMin and lMax.
+ """
+ sValue = self._getStringParam(sName, sDefValue = (str(lDefValue) if lDefValue is not None else None));
+ try:
+ lValue = long(sValue, 0);
+ except Exception as oXcpt:
+ raise TestBoxControllerException('%s parameter %s value "%s" cannot be convert to an integer (%s)' \
+ % (self._sAction, sName, sValue, oXcpt));
+
+ if (lMin is not None and lValue < lMin) \
+ or (lMax is not None and lValue > lMax):
+ raise TestBoxControllerException('%s parameter %s value %d is out of range [%s..%s]' \
+ % (self._sAction, sName, lValue, lMin, lMax));
+ return lValue;
+
+ def _checkForUnknownParameters(self):
+ """
+ Check if we've handled all parameters, raises exception if anything
+ unknown was found.
+ """
+
+ if len(self._asCheckedParams) != len(self._dParams):
+ sUnknownParams = '';
+ for sKey in self._dParams:
+ if sKey not in self._asCheckedParams:
+ sUnknownParams += ' ' + sKey + '=' + self._dParams[sKey];
+ raise TestBoxControllerException('Unknown parameters: ' + sUnknownParams);
+
+ return True;
+
+ def _writeResponse(self, dParams):
+ """
+ Makes a reply to the testbox script.
+ Will raise exception on failure.
+ """
+ self._oSrvGlue.writeParams(dParams);
+ self._oSrvGlue.flush();
+ return True;
+
+ def _resultResponse(self, sResultValue):
+ """
+ Makes a simple reply to the testbox script.
+ Will raise exception on failure.
+ """
+ return self._writeResponse({constants.tbresp.ALL_PARAM_RESULT: sResultValue});
+
+
+ def _idleResponse(self):
+ """
+ Makes an IDLE reply to the testbox script.
+ Will raise exception on failure.
+ """
+ return self._writeResponse({ constants.tbresp.ALL_PARAM_RESULT: constants.tbresp.CMD_IDLE });
+
+ def _cleanupOldTest(self, oDb, oStatusData):
+ """
+ Cleans up any old test set that may be left behind and changes the
+ state to 'idle'. See scenario #9:
+ file://../../docs/AutomaticTestingRevamp.html#cleaning-up-abandoned-testcase
+
+ Note. oStatusData.enmState is set to idle, but tsUpdated is not changed.
+ """
+
+ # Cleanup any abandoned test.
+ if oStatusData.idTestSet is not None:
+ SystemLogLogic(oDb).addEntry(SystemLogData.ksEvent_TestSetAbandoned,
+ "idTestSet=%u idTestBox=%u enmState=%s %s"
+ % (oStatusData.idTestSet, oStatusData.idTestBox,
+ oStatusData.enmState, self._sAction),
+ fCommit = False);
+ TestSetLogic(oDb).completeAsAbandoned(oStatusData.idTestSet, fCommit = False);
+ GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
+
+ # Change to idle status
+ if oStatusData.enmState != TestBoxStatusData.ksTestBoxState_Idle:
+ TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
+ oStatusData.tsUpdated = oDb.getCurrentTimestamp();
+ oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle;
+
+ # Commit.
+ oDb.commit();
+
+ return True;
+
+ def _connectToDbAndValidateTb(self, asValidStates = None):
+ """
+ Connects to the database and validates the testbox.
+
+ Returns (TMDatabaseConnection, TestBoxStatusData, TestBoxData) on success.
+ Returns (None, None, None) on failure after sending the box an appropriate response.
+ May raise exception on DB error.
+ """
+ oDb = TMDatabaseConnection(self._oSrvGlue.dprint);
+ oLogic = TestBoxStatusLogic(oDb);
+ (oStatusData, oTestBoxData) = oLogic.tryFetchStatusAndConfig(self._idTestBox, self._sTestBoxUuid, self._sTestBoxAddr);
+ if oStatusData is None:
+ self._resultResponse(constants.tbresp.STATUS_DEAD);
+ elif asValidStates is not None and oStatusData.enmState not in asValidStates:
+ self._resultResponse(constants.tbresp.STATUS_NACK);
+ elif self._idTestSet is not None and self._idTestSet != oStatusData.idTestSet:
+ self._resultResponse(constants.tbresp.STATUS_NACK);
+ else:
+ return (oDb, oStatusData, oTestBoxData);
+ return (None, None, None);
+
+ def writeToMainLog(self, oTestSet, sText, fIgnoreSizeCheck = False):
+ """ Writes the text to the main log file. """
+
+ # Calc the file name and open the file.
+ sFile = os.path.join(config.g_ksFileAreaRootDir, oTestSet.sBaseFilename + '-main.log');
+ if not os.path.exists(os.path.dirname(sFile)):
+ os.makedirs(os.path.dirname(sFile), 0o755);
+
+ with open(sFile, 'ab') as oFile:
+ # Check the size.
+ fSizeOk = True;
+ if not fIgnoreSizeCheck:
+ oStat = os.fstat(oFile.fileno());
+ fSizeOk = oStat.st_size / (1024 * 1024) < config.g_kcMbMaxMainLog;
+
+ # Write the text.
+ if fSizeOk:
+ if sys.version_info[0] >= 3:
+ oFile.write(bytes(sText, 'utf-8'));
+ else:
+ oFile.write(sText);
+
+ return fSizeOk;
+
+ def _actionSignOn(self): # pylint: disable=too-many-locals
+ """ Implement sign-on """
+
+ #
+ # Validate parameters (raises exception on failure).
+ #
+ sOs = self._getStringParam(constants.tbreq.SIGNON_PARAM_OS, coreconsts.g_kasOses);
+ sOsVersion = self._getStringParam(constants.tbreq.SIGNON_PARAM_OS_VERSION);
+ sCpuVendor = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_VENDOR);
+ sCpuArch = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_ARCH, coreconsts.g_kasCpuArches);
+ sCpuName = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_NAME, fStrip = True, sDefValue = ''); # new
+ lCpuRevision = self._getLongParam( constants.tbreq.SIGNON_PARAM_CPU_REVISION, lMin = 0, lDefValue = 0); # new
+ cCpus = self._getIntParam( constants.tbreq.SIGNON_PARAM_CPU_COUNT, 1, 16384);
+ fCpuHwVirt = self._getBoolParam( constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT);
+ fCpuNestedPaging = self._getBoolParam( constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING);
+ fCpu64BitGuest = self._getBoolParam( constants.tbreq.SIGNON_PARAM_HAS_64_BIT_GUEST, fDefValue = True);
+ fChipsetIoMmu = self._getBoolParam( constants.tbreq.SIGNON_PARAM_HAS_IOMMU);
+ fRawMode = self._getBoolParam( constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE, fDefValue = None);
+ cMbMemory = self._getLongParam( constants.tbreq.SIGNON_PARAM_MEM_SIZE, 8, 1073741823); # 8MB..1PB
+ cMbScratch = self._getLongParam( constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE, 0, 1073741823); # 0..1PB
+ sReport = self._getStringParam(constants.tbreq.SIGNON_PARAM_REPORT, fStrip = True, sDefValue = ''); # new
+ iTestBoxScriptRev = self._getIntParam( constants.tbreq.SIGNON_PARAM_SCRIPT_REV, 1, 100000000);
+ iPythonHexVersion = self._getIntParam( constants.tbreq.SIGNON_PARAM_PYTHON_VERSION, 0x020300f0, 0x030f00f0);
+ self._checkForUnknownParameters();
+
+ # Null conversions for new parameters.
+ if not sReport:
+ sReport = None;
+ if not sCpuName:
+ sCpuName = None;
+ if lCpuRevision <= 0:
+ lCpuRevision = None;
+
+ #
+ # Connect to the database and validate the testbox.
+ #
+ oDb = TMDatabaseConnection(self._oSrvGlue.dprint);
+ oTestBoxLogic = TestBoxLogic(oDb);
+ oTestBox = oTestBoxLogic.tryFetchTestBoxByUuid(self._sTestBoxUuid);
+ if oTestBox is None:
+ oSystemLogLogic = SystemLogLogic(oDb);
+ oSystemLogLogic.addEntry(SystemLogData.ksEvent_TestBoxUnknown,
+ 'addr=%s uuid=%s os=%s %d cpus' \
+ % (self._sTestBoxAddr, self._sTestBoxUuid, sOs, cCpus),
+ 24, fCommit = True);
+ return self._resultResponse(constants.tbresp.STATUS_NACK);
+
+ #
+ # Update the row in TestBoxes if something changed.
+ #
+ if oTestBox.cMbScratch is not None and oTestBox.cMbScratch != 0:
+ cPctScratchDiff = (cMbScratch - oTestBox.cMbScratch) * 100 / oTestBox.cMbScratch;
+ else:
+ cPctScratchDiff = 100;
+
+ # pylint: disable=too-many-boolean-expressions
+ if self._sTestBoxAddr != oTestBox.ip \
+ or sOs != oTestBox.sOs \
+ or sOsVersion != oTestBox.sOsVersion \
+ or sCpuVendor != oTestBox.sCpuVendor \
+ or sCpuArch != oTestBox.sCpuArch \
+ or sCpuName != oTestBox.sCpuName \
+ or lCpuRevision != oTestBox.lCpuRevision \
+ or cCpus != oTestBox.cCpus \
+ or fCpuHwVirt != oTestBox.fCpuHwVirt \
+ or fCpuNestedPaging != oTestBox.fCpuNestedPaging \
+ or fCpu64BitGuest != oTestBox.fCpu64BitGuest \
+ or fChipsetIoMmu != oTestBox.fChipsetIoMmu \
+ or fRawMode != oTestBox.fRawMode \
+ or cMbMemory != oTestBox.cMbMemory \
+ or abs(cPctScratchDiff) >= min(4 + cMbScratch / 10240, 12) \
+ or sReport != oTestBox.sReport \
+ or iTestBoxScriptRev != oTestBox.iTestBoxScriptRev \
+ or iPythonHexVersion != oTestBox.iPythonHexVersion:
+ oTestBoxLogic.updateOnSignOn(oTestBox.idTestBox,
+ oTestBox.idGenTestBox,
+ sTestBoxAddr = self._sTestBoxAddr,
+ sOs = sOs,
+ sOsVersion = sOsVersion,
+ sCpuVendor = sCpuVendor,
+ sCpuArch = sCpuArch,
+ sCpuName = sCpuName,
+ lCpuRevision = lCpuRevision,
+ cCpus = cCpus,
+ fCpuHwVirt = fCpuHwVirt,
+ fCpuNestedPaging = fCpuNestedPaging,
+ fCpu64BitGuest = fCpu64BitGuest,
+ fChipsetIoMmu = fChipsetIoMmu,
+ fRawMode = fRawMode,
+ cMbMemory = cMbMemory,
+ cMbScratch = cMbScratch,
+ sReport = sReport,
+ iTestBoxScriptRev = iTestBoxScriptRev,
+ iPythonHexVersion = iPythonHexVersion);
+
+ #
+ # Update the testbox status, making sure there is a status.
+ #
+ oStatusLogic = TestBoxStatusLogic(oDb);
+ oStatusData = oStatusLogic.tryFetchStatus(oTestBox.idTestBox);
+ if oStatusData is not None:
+ self._cleanupOldTest(oDb, oStatusData);
+ else:
+ oStatusLogic.insertIdleStatus(oTestBox.idTestBox, oTestBox.idGenTestBox, fCommit = True);
+
+ #
+ # ACK the request.
+ #
+ dResponse = \
+ {
+ constants.tbresp.ALL_PARAM_RESULT: constants.tbresp.STATUS_ACK,
+ constants.tbresp.SIGNON_PARAM_ID: oTestBox.idTestBox,
+ constants.tbresp.SIGNON_PARAM_NAME: oTestBox.sName,
+ }
+ return self._writeResponse(dResponse);
+
+ def _doGangCleanup(self, oDb, oStatusData):
+ """
+ _doRequestCommand worker for handling a box in gang-cleanup.
+ This will check if all testboxes has completed their run, pretending to
+ be busy until that happens. Once all are completed, resources will be
+ freed and the testbox returns to idle state (we update oStatusData).
+ """
+ oStatusLogic = TestBoxStatusLogic(oDb)
+ oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
+ if oStatusLogic.isWholeGangDoneTesting(oTestSet.idTestSetGangLeader):
+ oDb.begin();
+
+ GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
+ TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
+
+ oStatusData.tsUpdated = oDb.getCurrentTimestamp();
+ oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle;
+
+ oDb.commit();
+ return None;
+
+ def _doGangGatheringTimedOut(self, oDb, oStatusData):
+ """
+ _doRequestCommand worker for handling a box in gang-gathering-timed-out state.
+ This will do clean-ups similar to _cleanupOldTest and update the state likewise.
+ """
+ oDb.begin();
+
+ TestSetLogic(oDb).completeAsGangGatheringTimeout(oStatusData.idTestSet, fCommit = False);
+ GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
+ TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
+
+ oStatusData.tsUpdated = oDb.getCurrentTimestamp();
+ oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle;
+
+ oDb.commit();
+ return None;
+
+ def _doGangGathering(self, oDb, oStatusData):
+ """
+ _doRequestCommand worker for handling a box in gang-gathering state.
+ This only checks for timeout. It will update the oStatusData if a
+ timeout is detected, so that the box will be idle upon return.
+ """
+ oStatusLogic = TestBoxStatusLogic(oDb);
+ if oStatusLogic.timeSinceLastChangeInSecs(oStatusData) > config.g_kcSecGangGathering \
+ and SchedulerBase.tryCancelGangGathering(oDb, oStatusData): # <-- Updates oStatusData.
+ self._doGangGatheringTimedOut(oDb, oStatusData);
+ return None;
+
+ def _doRequestCommand(self, fIdle):
+ """
+ Common code for handling command request.
+ """
+
+ (oDb, oStatusData, oTestBoxData) = self._connectToDbAndValidateTb();
+ if oDb is None:
+ return False;
+
+ #
+ # Status clean up.
+ #
+ # Only when BUSY will the TestBox Script request and execute commands
+ # concurrently. So, it must be idle when sending REQUEST_COMMAND_IDLE.
+ #
+ if fIdle:
+ if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGathering:
+ self._doGangGathering(oDb, oStatusData);
+ elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut:
+ self._doGangGatheringTimedOut(oDb, oStatusData);
+ elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangTesting:
+ dResponse = SchedulerBase.composeExecResponse(oDb, oTestBoxData.idTestBox, self._oSrvGlue.getBaseUrl());
+ if dResponse is not None:
+ return dResponse;
+ elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangCleanup:
+ self._doGangCleanup(oDb, oStatusData);
+ elif oStatusData.enmState != TestBoxStatusData.ksTestBoxState_Idle: # (includes ksTestBoxState_GangGatheringTimedOut)
+ self._cleanupOldTest(oDb, oStatusData);
+
+ #
+ # Check for pending command.
+ #
+ if oTestBoxData.enmPendingCmd != TestBoxData.ksTestBoxCmd_None:
+ asValidCmds = TestBoxController.kasIdleCmds if fIdle else TestBoxController.kasBusyCmds;
+ if oTestBoxData.enmPendingCmd in asValidCmds:
+ dResponse = { constants.tbresp.ALL_PARAM_RESULT: TestBoxController.kdCmdToTbRespCmd[oTestBoxData.enmPendingCmd] };
+ if oTestBoxData.enmPendingCmd in [TestBoxData.ksTestBoxCmd_Upgrade, TestBoxData.ksTestBoxCmd_UpgradeAndReboot]:
+ dResponse[constants.tbresp.UPGRADE_PARAM_URL] = self._oSrvGlue.getBaseUrl() + TestBoxController.ksUpgradeZip;
+ return self._writeResponse(dResponse);
+
+ if oTestBoxData.enmPendingCmd == TestBoxData.ksTestBoxCmd_Abort and fIdle:
+ TestBoxLogic(oDb).setCommand(self._idTestBox, sOldCommand = oTestBoxData.enmPendingCmd,
+ sNewCommand = TestBoxData.ksTestBoxCmd_None, fCommit = True);
+
+ #
+ # If doing gang stuff, return 'CMD_WAIT'.
+ #
+ ## @todo r=bird: Why is GangTesting included here? Figure out when testing gang testing.
+ if oStatusData.enmState in [TestBoxStatusData.ksTestBoxState_GangGathering,
+ TestBoxStatusData.ksTestBoxState_GangTesting,
+ TestBoxStatusData.ksTestBoxState_GangCleanup]:
+ return self._resultResponse(constants.tbresp.CMD_WAIT);
+
+ #
+ # If idling and enabled try schedule a new task.
+ #
+ if fIdle \
+ and oTestBoxData.fEnabled \
+ and not TestSetLogic(oDb).isTestBoxExecutingTooRapidly(oTestBoxData.idTestBox) \
+ and oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Idle: # (paranoia)
+ dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, oStatusData.iWorkItem, self._oSrvGlue.getBaseUrl());
+ if dResponse is not None:
+ return self._writeResponse(dResponse);
+
+ #
+ # Touch the status row every couple of mins so we can tell that the box is alive.
+ #
+ oStatusLogic = TestBoxStatusLogic(oDb);
+ if oStatusData.enmState != TestBoxStatusData.ksTestBoxState_GangGathering \
+ and oStatusLogic.timeSinceLastChangeInSecs(oStatusData) >= TestBoxStatusLogic.kcSecIdleTouchStatus:
+ oStatusLogic.touchStatus(oTestBoxData.idTestBox, fCommit = True);
+
+ return self._idleResponse();
+
+ def _actionRequestCommandBusy(self):
+ """ Implement request for command. """
+ self._checkForUnknownParameters();
+ return self._doRequestCommand(False);
+
+ def _actionRequestCommandIdle(self):
+ """ Implement request for command. """
+ self._checkForUnknownParameters();
+ return self._doRequestCommand(True);
+
+ def _doCommandAckNck(self, sCmd):
+ """ Implements ACK, NACK and NACK(ENOTSUP). """
+
+ (oDb, _, _) = self._connectToDbAndValidateTb();
+ if oDb is None:
+ return False;
+
+ #
+ # If the command maps to a TestBoxCmd_T value, it means we have to
+ # check and update TestBoxes. If it's an ACK, the testbox status will
+ # need updating as well.
+ #
+ sPendingCmd = TestBoxController.kdTbRespCmdToCmd[sCmd];
+ if sPendingCmd is not None:
+ oTestBoxLogic = TestBoxLogic(oDb)
+ oTestBoxLogic.setCommand(self._idTestBox, sOldCommand = sPendingCmd,
+ sNewCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False);
+
+ if self._sAction == constants.tbreq.COMMAND_ACK \
+ and TestBoxController.kdCmdToState[sPendingCmd] is not None:
+ oStatusLogic = TestBoxStatusLogic(oDb);
+ oStatusLogic.updateState(self._idTestBox, TestBoxController.kdCmdToState[sPendingCmd], fCommit = False);
+
+ # Commit the two updates.
+ oDb.commit();
+
+ #
+ # Log NACKs.
+ #
+ if self._sAction != constants.tbreq.COMMAND_ACK:
+ oSysLogLogic = SystemLogLogic(oDb);
+ oSysLogLogic.addEntry(SystemLogData.ksEvent_CmdNacked,
+ 'idTestBox=%s sCmd=%s' % (self._idTestBox, sPendingCmd),
+ 24, fCommit = True);
+
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+ def _actionCommandAck(self):
+ """ Implement command ACK'ing """
+ sCmd = self._getStringParam(constants.tbreq.COMMAND_ACK_PARAM_CMD_NAME, TestBoxController.kasAckableCmds);
+ self._checkForUnknownParameters();
+ return self._doCommandAckNck(sCmd);
+
+ def _actionCommandNack(self):
+ """ Implement command NACK'ing """
+ sCmd = self._getStringParam(constants.tbreq.COMMAND_ACK_PARAM_CMD_NAME, TestBoxController.kasNackableCmds);
+ self._checkForUnknownParameters();
+ return self._doCommandAckNck(sCmd);
+
+ def _actionCommandNotSup(self):
+ """ Implement command NACK(ENOTSUP)'ing """
+ sCmd = self._getStringParam(constants.tbreq.COMMAND_ACK_PARAM_CMD_NAME, TestBoxController.kasNackableCmds);
+ self._checkForUnknownParameters();
+ return self._doCommandAckNck(sCmd);
+
+ def _actionLogMain(self):
+ """ Implement submitting log entries to the main log file. """
+ #
+ # Parameter validation.
+ #
+ sBody = self._getStringParam(constants.tbreq.LOG_PARAM_BODY, fStrip = False);
+ if not sBody:
+ return self._resultResponse(constants.tbresp.STATUS_NACK);
+ self._checkForUnknownParameters();
+
+ (oDb, oStatusData, _) = self._connectToDbAndValidateTb([TestBoxStatusData.ksTestBoxState_Testing,
+ TestBoxStatusData.ksTestBoxState_GangTesting]);
+ if oStatusData is None:
+ return False;
+
+ #
+ # Write the text to the log file.
+ #
+ oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
+ self.writeToMainLog(oTestSet, sBody);
+ ## @todo Overflow is a hanging offence, need to note it and fail whatever is going on...
+
+ # Done.
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+ def _actionUpload(self):
+ """ Implement uploading of files. """
+ #
+ # Parameter validation.
+ #
+ sName = self._getStringParam(constants.tbreq.UPLOAD_PARAM_NAME);
+ sMime = self._getStringParam(constants.tbreq.UPLOAD_PARAM_MIME);
+ sKind = self._getStringParam(constants.tbreq.UPLOAD_PARAM_KIND);
+ sDesc = self._getStringParam(constants.tbreq.UPLOAD_PARAM_DESC);
+ self._checkForUnknownParameters();
+
+ (oDb, oStatusData, _) = self._connectToDbAndValidateTb([TestBoxStatusData.ksTestBoxState_Testing,
+ TestBoxStatusData.ksTestBoxState_GangTesting]);
+ if oStatusData is None:
+ return False;
+
+ if len(sName) > 128 or len(sName) < 3:
+ raise TestBoxControllerException('Invalid file name "%s"' % (sName,));
+ if re.match(r'^[a-zA-Z0-9_\-(){}#@+,.=]*$', sName) is None:
+ raise TestBoxControllerException('Invalid file name "%s"' % (sName,));
+
+ if sMime not in [ 'text/plain', #'text/html', 'text/xml',
+ 'application/octet-stream',
+ 'image/png', #'image/gif', 'image/jpeg',
+ 'video/webm', #'video/mpeg', 'video/mpeg4-generic',
+ ]:
+ raise TestBoxControllerException('Invalid MIME type "%s"' % (sMime,));
+
+ if sKind not in TestResultFileData.kasKinds:
+ raise TestBoxControllerException('Invalid kind "%s"' % (sKind,));
+
+ if len(sDesc) > 256:
+ raise TestBoxControllerException('Invalid description "%s"' % (sDesc,));
+ if not set(sDesc).issubset(set(string.printable)):
+ raise TestBoxControllerException('Invalid description "%s"' % (sDesc,));
+
+ if ('application/octet-stream', {}) != self._oSrvGlue.getContentType():
+ raise TestBoxControllerException('Unexpected content type: %s; %s' % self._oSrvGlue.getContentType());
+
+ cbFile = self._oSrvGlue.getContentLength();
+ if cbFile <= 0:
+ raise TestBoxControllerException('File "%s" is empty or negative in size (%s)' % (sName, cbFile));
+ if (cbFile + 1048575) / 1048576 > config.g_kcMbMaxUploadSingle:
+ raise TestBoxControllerException('File "%s" is too big %u bytes (max %u MiB)'
+ % (sName, cbFile, config.g_kcMbMaxUploadSingle,));
+
+ #
+ # Write the text to the log file.
+ #
+ oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
+ oDstFile = TestSetLogic(oDb).createFile(oTestSet, sName = sName, sMime = sMime, sKind = sKind, sDesc = sDesc,
+ cbFile = cbFile, fCommit = True);
+
+ offFile = 0;
+ oSrcFile = self._oSrvGlue.getBodyIoStreamBinary();
+ while offFile < cbFile:
+ cbToRead = cbFile - offFile;
+ if cbToRead > 256*1024:
+ cbToRead = 256*1024;
+ offFile += cbToRead;
+
+ abBuf = oSrcFile.read(cbToRead);
+ oDstFile.write(abBuf); # pylint: disable=maybe-no-member
+ del abBuf;
+
+ oDstFile.close(); # pylint: disable=maybe-no-member
+
+ # Done.
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+ def _actionXmlResults(self):
+ """ Implement submitting "XML" like test result stream. """
+ #
+ # Parameter validation.
+ #
+ sXml = self._getStringParam(constants.tbreq.XML_RESULT_PARAM_BODY, fStrip = False);
+ self._checkForUnknownParameters();
+ if not sXml: # Used for link check by vboxinstaller.py on Windows.
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+ (oDb, oStatusData, _) = self._connectToDbAndValidateTb([TestBoxStatusData.ksTestBoxState_Testing,
+ TestBoxStatusData.ksTestBoxState_GangTesting]);
+ if oStatusData is None:
+ return False;
+
+ #
+ # Process the XML.
+ #
+ (sError, fUnforgivable) = TestResultLogic(oDb).processXmlStream(sXml, self._idTestSet);
+ if sError is not None:
+ oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
+ self.writeToMainLog(oTestSet, '\n!!XML error: %s\n%s\n\n' % (sError, sXml,));
+ if fUnforgivable:
+ return self._resultResponse(constants.tbresp.STATUS_NACK);
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+
+ def _actionExecCompleted(self):
+ """
+ Implement EXEC completion.
+
+ Because the action is request by the worker thread of the testbox
+ script we cannot pass pending commands back to it like originally
+ planned. So, we just complete the test set and update the status.
+ """
+ #
+ # Parameter validation.
+ #
+ sStatus = self._getStringParam(constants.tbreq.EXEC_COMPLETED_PARAM_RESULT, TestBoxController.kasValidResults);
+ self._checkForUnknownParameters();
+
+ (oDb, oStatusData, _) = self._connectToDbAndValidateTb([TestBoxStatusData.ksTestBoxState_Testing,
+ TestBoxStatusData.ksTestBoxState_GangTesting]);
+ if oStatusData is None:
+ return False;
+
+ #
+ # Complete the status.
+ #
+ oDb.rollback();
+ oDb.begin();
+ oTestSetLogic = TestSetLogic(oDb);
+ idTestSetGangLeader = oTestSetLogic.complete(oStatusData.idTestSet, self.kadTbResultToStatus[sStatus], fCommit = False);
+
+ oStatusLogic = TestBoxStatusLogic(oDb);
+ if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Testing:
+ assert idTestSetGangLeader is None;
+ GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox);
+ oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
+ else:
+ assert idTestSetGangLeader is not None;
+ oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_GangCleanup, oStatusData.idTestSet,
+ fCommit = False);
+ if oStatusLogic.isWholeGangDoneTesting(idTestSetGangLeader):
+ GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox);
+ oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
+
+ oDb.commit();
+ return self._resultResponse(constants.tbresp.STATUS_ACK);
+
+
+
+ def _getStandardParams(self, dParams):
+ """
+ Gets the standard parameters and validates them.
+
+ The parameters are returned as a tuple: sAction, idTestBox, sTestBoxUuid.
+ Note! the sTextBoxId can be None if it's a SIGNON request.
+
+ Raises TestBoxControllerException on invalid input.
+ """
+ #
+ # Get the action parameter and validate it.
+ #
+ if constants.tbreq.ALL_PARAM_ACTION not in dParams:
+ raise TestBoxControllerException('No "%s" parameter in request (params: %s)' \
+ % (constants.tbreq.ALL_PARAM_ACTION, dParams,));
+ sAction = dParams[constants.tbreq.ALL_PARAM_ACTION];
+
+ if sAction not in self._dActions:
+ raise TestBoxControllerException('Unknown action "%s" in request (params: %s; action: %s)' \
+ % (sAction, dParams, self._dActions));
+
+ #
+ # TestBox UUID.
+ #
+ if constants.tbreq.ALL_PARAM_TESTBOX_UUID not in dParams:
+ raise TestBoxControllerException('No "%s" parameter in request (params: %s)' \
+ % (constants.tbreq.ALL_PARAM_TESTBOX_UUID, dParams,));
+ sTestBoxUuid = dParams[constants.tbreq.ALL_PARAM_TESTBOX_UUID];
+ try:
+ sTestBoxUuid = str(uuid.UUID(sTestBoxUuid));
+ except Exception as oXcpt:
+ raise TestBoxControllerException('Invalid %s parameter value "%s": %s ' \
+ % (constants.tbreq.ALL_PARAM_TESTBOX_UUID, sTestBoxUuid, oXcpt));
+ if sTestBoxUuid == '00000000-0000-0000-0000-000000000000':
+ raise TestBoxControllerException('Invalid %s parameter value "%s": NULL UUID not allowed.' \
+ % (constants.tbreq.ALL_PARAM_TESTBOX_UUID, sTestBoxUuid));
+
+ #
+ # TestBox ID.
+ #
+ if constants.tbreq.ALL_PARAM_TESTBOX_ID in dParams:
+ sTestBoxId = dParams[constants.tbreq.ALL_PARAM_TESTBOX_ID];
+ try:
+ idTestBox = int(sTestBoxId);
+ if idTestBox <= 0 or idTestBox >= 0x7fffffff:
+ raise Exception;
+ except:
+ raise TestBoxControllerException('Bad value for "%s": "%s"' \
+ % (constants.tbreq.ALL_PARAM_TESTBOX_ID, sTestBoxId));
+ elif sAction == constants.tbreq.SIGNON:
+ idTestBox = None;
+ else:
+ raise TestBoxControllerException('No "%s" parameter in request (params: %s)' \
+ % (constants.tbreq.ALL_PARAM_TESTBOX_ID, dParams,));
+
+ #
+ # Test Set ID.
+ #
+ if constants.tbreq.RESULT_PARAM_TEST_SET_ID in dParams:
+ sTestSetId = dParams[constants.tbreq.RESULT_PARAM_TEST_SET_ID];
+ try:
+ idTestSet = int(sTestSetId);
+ if idTestSet <= 0 or idTestSet >= 0x7fffffff:
+ raise Exception;
+ except:
+ raise TestBoxControllerException('Bad value for "%s": "%s"' \
+ % (constants.tbreq.RESULT_PARAM_TEST_SET_ID, sTestSetId));
+ elif sAction not in [ constants.tbreq.XML_RESULTS, ]: ## More later.
+ idTestSet = None;
+ else:
+ raise TestBoxControllerException('No "%s" parameter in request (params: %s)' \
+ % (constants.tbreq.RESULT_PARAM_TEST_SET_ID, dParams,));
+
+ #
+ # The testbox address.
+ #
+ sTestBoxAddr = self._oSrvGlue.getClientAddr();
+ if sTestBoxAddr is None or sTestBoxAddr.strip() == '':
+ raise TestBoxControllerException('Invalid client address "%s"' % (sTestBoxAddr,));
+
+ #
+ # Update the list of checked parameters.
+ #
+ self._asCheckedParams.extend([constants.tbreq.ALL_PARAM_TESTBOX_UUID, constants.tbreq.ALL_PARAM_ACTION]);
+ if idTestBox is not None:
+ self._asCheckedParams.append(constants.tbreq.ALL_PARAM_TESTBOX_ID);
+ if idTestSet is not None:
+ self._asCheckedParams.append(constants.tbreq.RESULT_PARAM_TEST_SET_ID);
+
+ return (sAction, idTestBox, sTestBoxUuid, sTestBoxAddr, idTestSet);
+
+ def dispatchRequest(self):
+ """
+ Dispatches the incoming request.
+
+ Will raise TestBoxControllerException on failure.
+ """
+
+ #
+ # Must be a POST request.
+ #
+ try:
+ sMethod = self._oSrvGlue.getMethod();
+ except Exception as oXcpt:
+ raise TestBoxControllerException('Error retriving request method: %s' % (oXcpt,));
+ if sMethod != 'POST':
+ raise TestBoxControllerException('Error expected POST request not "%s"' % (sMethod,));
+
+ #
+ # Get the parameters and checks for duplicates.
+ #
+ try:
+ dParams = self._oSrvGlue.getParameters();
+ except Exception as oXcpt:
+ raise TestBoxControllerException('Error retriving parameters: %s' % (oXcpt,));
+ for sKey in dParams.keys():
+ if len(dParams[sKey]) > 1:
+ raise TestBoxControllerException('Parameter "%s" is given multiple times: %s' % (sKey, dParams[sKey]));
+ dParams[sKey] = dParams[sKey][0];
+ self._dParams = dParams;
+
+ #
+ # Get+validate the standard action parameters and dispatch the request.
+ #
+ (self._sAction, self._idTestBox, self._sTestBoxUuid, self._sTestBoxAddr, self._idTestSet) = \
+ self._getStandardParams(dParams);
+ return self._dActions[self._sAction]();
diff --git a/src/VBox/ValidationKit/testmanager/core/testboxstatus.py b/src/VBox/ValidationKit/testmanager/core/testboxstatus.py
new file mode 100755
index 00000000..86c6041f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testboxstatus.py
@@ -0,0 +1,317 @@
+# -*- coding: utf-8 -*-
+# $Id: testboxstatus.py $
+
+"""
+Test Manager - TestBoxStatus.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMTooManyRows, TMRowNotFound;
+from testmanager.core.testbox import TestBoxData;
+
+
+class TestBoxStatusData(ModelDataBase):
+ """
+ TestBoxStatus Data.
+ """
+
+ ## @name TestBoxState_T
+ # @{
+ ksTestBoxState_Idle = 'idle';
+ ksTestBoxState_Testing = 'testing';
+ ksTestBoxState_GangGathering = 'gang-gathering';
+ ksTestBoxState_GangGatheringTimedOut = 'gang-gathering-timedout';
+ ksTestBoxState_GangTesting = 'gang-testing';
+ ksTestBoxState_GangCleanup = 'gang-cleanup';
+ ksTestBoxState_Rebooting = 'rebooting';
+ ksTestBoxState_Upgrading = 'upgrading';
+ ksTestBoxState_UpgradingAndRebooting = 'upgrading-and-rebooting';
+ ksTestBoxState_DoingSpecialCmd = 'doing-special-cmd';
+ ## @}
+
+ ksParam_idTestBox = 'TestBoxStatus_idTestBox';
+ ksParam_idGenTestBox = 'TestBoxStatus_idGenTestBox'
+ ksParam_tsUpdated = 'TestBoxStatus_tsUpdated';
+ ksParam_enmState = 'TestBoxStatus_enmState';
+ ksParam_idTestSet = 'TestBoxStatus_idTestSet';
+ ksParam_iWorkItem = 'TestBoxStatus_iWorkItem';
+
+ kasAllowNullAttributes = ['idTestSet', ];
+ kasValidValues_enmState = \
+ [
+ ksTestBoxState_Idle, ksTestBoxState_Testing, ksTestBoxState_GangGathering,
+ ksTestBoxState_GangGatheringTimedOut, ksTestBoxState_GangTesting, ksTestBoxState_GangCleanup,
+ ksTestBoxState_Rebooting, ksTestBoxState_Upgrading, ksTestBoxState_UpgradingAndRebooting,
+ ksTestBoxState_DoingSpecialCmd,
+ ];
+
+ kcDbColumns = 6;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestBox = None;
+ self.idGenTestBox = None;
+ self.tsUpdated = None;
+ self.enmState = self.ksTestBoxState_Idle;
+ self.idTestSet = None;
+ self.iWorkItem = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Internal worker for initFromDbWithId and initFromDbWithGenId as well as
+ TestBoxStatusLogic.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('TestBoxStatus not found.');
+
+ self.idTestBox = aoRow[0];
+ self.idGenTestBox = aoRow[1];
+ self.tsUpdated = aoRow[2];
+ self.enmState = aoRow[3];
+ self.idTestSet = aoRow[4];
+ self.iWorkItem = aoRow[5];
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestBox):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute('SELECT *\n'
+ 'FROM TestBoxStatuses\n'
+ 'WHERE idTestBox = %s\n'
+ , (idTestBox, ) );
+ return self.initFromDbRow(oDb.fetchOne());
+
+ def initFromDbWithGenId(self, oDb, idGenTestBox):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute('SELECT *\n'
+ 'FROM TestBoxStatuses\n'
+ 'WHERE idGenTestBox = %s\n'
+ , (idGenTestBox, ) );
+ return self.initFromDbRow(oDb.fetchOne());
+
+
+class TestBoxStatusLogic(ModelLogicBase):
+ """
+ TestBoxStatus logic.
+ """
+
+ ## The number of seconds between each time to call touchStatus() when
+ # returning CMD_IDLE.
+ kcSecIdleTouchStatus = 120;
+
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+
+
+ def tryFetchStatus(self, idTestBox):
+ """
+ Attempts to fetch the status of the given testbox.
+
+ Returns a TestBoxStatusData object on success.
+ Returns None if no status was found.
+ Raises exception on other errors.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestBoxStatuses\n'
+ 'WHERE idTestBox = %s\n',
+ (idTestBox,));
+ if self._oDb.getRowCount() == 0:
+ return None;
+ oStatus = TestBoxStatusData();
+ return oStatus.initFromDbRow(self._oDb.fetchOne());
+
+ def tryFetchStatusAndConfig(self, idTestBox, sTestBoxUuid, sTestBoxAddr):
+ """
+ Tries to fetch the testbox status and current testbox config.
+
+ Returns (TestBoxStatusData, TestBoxData) on success, (None, None) if
+ not found. May throw an exception on database error.
+ """
+ self._oDb.execute('SELECT TestBoxStatuses.*,\n'
+ ' TestBoxesWithStrings.*\n'
+ 'FROM TestBoxStatuses,\n'
+ ' TestBoxesWithStrings\n'
+ 'WHERE TestBoxStatuses.idTestBox = %s\n'
+ ' AND TestBoxesWithStrings.idTestBox = %s\n'
+ ' AND TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestBoxesWithStrings.uuidSystem = %s\n'
+ ' AND TestBoxesWithStrings.ip = %s\n'
+ , ( idTestBox,
+ idTestBox,
+ sTestBoxUuid,
+ sTestBoxAddr,) );
+ cRows = self._oDb.getRowCount();
+ if cRows != 1:
+ if cRows != 0:
+ raise TMTooManyRows('tryFetchStatusForCommandReq got %s rows for idTestBox=%s' % (cRows, idTestBox));
+ return (None, None);
+ aoRow = self._oDb.fetchOne();
+ return (TestBoxStatusData().initFromDbRow(aoRow[:TestBoxStatusData.kcDbColumns]),
+ TestBoxData().initFromDbRow(aoRow[TestBoxStatusData.kcDbColumns:]));
+
+
+ def insertIdleStatus(self, idTestBox, idGenTestBox, fCommit = False):
+ """
+ Inserts an idle status for the specified testbox.
+ """
+ self._oDb.execute('INSERT INTO TestBoxStatuses (\n'
+ ' idTestBox,\n'
+ ' idGenTestBox,\n'
+ ' enmState,\n'
+ ' idTestSet,\n'
+ ' iWorkItem)\n'
+ 'VALUES ( %s,\n'
+ ' %s,\n'
+ ' \'idle\'::TestBoxState_T,\n'
+ ' NULL,\n'
+ ' 0)\n'
+ , (idTestBox, idGenTestBox) );
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def touchStatus(self, idTestBox, fCommit = False):
+ """
+ Touches the testbox status row, i.e. sets tsUpdated to the current time.
+ """
+ self._oDb.execute('UPDATE TestBoxStatuses\n'
+ 'SET tsUpdated = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestBox = %s\n'
+ , (idTestBox,));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def updateState(self, idTestBox, sNewState, idTestSet = None, fCommit = False):
+ """
+ Updates the testbox state.
+ """
+ self._oDb.execute('UPDATE TestBoxStatuses\n'
+ 'SET enmState = %s,\n'
+ ' idTestSet = %s,\n'
+ ' tsUpdated = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestBox = %s\n',
+ (sNewState, idTestSet, idTestBox));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def updateGangStatus(self, idTestSetGangLeader, sNewState, fCommit = False):
+ """
+ Update the state of all members of a gang.
+ """
+ self._oDb.execute('UPDATE TestBoxStatuses\n'
+ 'SET enmState = %s,\n'
+ ' tsUpdated = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestBox IN (SELECT idTestBox\n'
+ ' FROM TestSets\n'
+ ' WHERE idTestSetGangLeader = %s)\n'
+ , (sNewState, idTestSetGangLeader,) );
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def updateWorkItem(self, idTestBox, iWorkItem, fCommit = False):
+ """
+ Updates the testbox state.
+ """
+ self._oDb.execute('UPDATE TestBoxStatuses\n'
+ 'SET iWorkItem = %s\n'
+ 'WHERE idTestBox = %s\n'
+ , ( iWorkItem, idTestBox,));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def isWholeGangDoneTesting(self, idTestSetGangLeader):
+ """
+ Checks if the whole gang is done testing.
+ """
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM TestBoxStatuses, TestSets\n'
+ 'WHERE TestBoxStatuses.idTestSet = TestSets.idTestSet\n'
+ ' AND TestSets.idTestSetGangLeader = %s\n'
+ ' AND TestBoxStatuses.enmState IN (%s, %s)\n'
+ , ( idTestSetGangLeader,
+ TestBoxStatusData.ksTestBoxState_GangGathering,
+ TestBoxStatusData.ksTestBoxState_GangTesting));
+ return self._oDb.fetchOne()[0] == 0;
+
+ def isTheWholeGangThere(self, idTestSetGangLeader):
+ """
+ Checks if the whole gang is done testing.
+ """
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM TestBoxStatuses, TestSets\n'
+ 'WHERE TestBoxStatuses.idTestSet = TestSets.idTestSet\n'
+ ' AND TestSets.idTestSetGangLeader = %s\n'
+ ' AND TestBoxStatuses.enmState IN (%s, %s)\n'
+ , ( idTestSetGangLeader,
+ TestBoxStatusData.ksTestBoxState_GangGathering,
+ TestBoxStatusData.ksTestBoxState_GangTesting));
+ return self._oDb.fetchOne()[0] == 0;
+
+ def timeSinceLastChangeInSecs(self, oStatusData):
+ """
+ Figures the time since the last status change.
+ """
+ tsNow = self._oDb.getCurrentTimestamp();
+ oDelta = tsNow - oStatusData.tsUpdated;
+ return oDelta.seconds + oDelta.days * 24 * 3600;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestBoxStatusDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestBoxStatusData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testcase.pgsql b/src/VBox/ValidationKit/testmanager/core/testcase.pgsql
new file mode 100644
index 00000000..8d32d1c9
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testcase.pgsql
@@ -0,0 +1,275 @@
+-- $Id: testcase.pgsql $
+--- @file
+-- VBox Test Manager Database Stored Procedures - TestCases.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+DROP FUNCTION IF EXISTS add_testcase(INTEGER, TEXT, TEXT, BOOLEAN, INTEGER, TEXT, TEXT);
+DROP FUNCTION IF EXISTS edit_testcase(INTEGER, INTEGER, TEXT, TEXT, BOOLEAN, INTEGER, TEXT, TEXT);
+DROP FUNCTION IF EXISTS del_testcase(INTEGER);
+DROP FUNCTION IF EXISTS TestCaseLogic_delEntry(INTEGER, INTEGER);
+DROP FUNCTION IF EXISTS TestCaseLogic_addEntry(a_uidAuthor INTEGER, a_sName TEXT, a_sDescription TEXT,
+ a_fEnabled BOOL, a_cSecTimeout INTEGER, a_sTestBoxReqExpr TEXT,
+ a_sBuildReqExpr TEXT, a_sBaseCmd TEXT, a_sTestSuiteZips TEXT);
+DROP FUNCTION IF EXISTS TestCaseLogic_editEntry(a_uidAuthor INTEGER, a_idTestCase INTEGER, a_sName TEXT, a_sDescription TEXT,
+ a_fEnabled BOOL, a_cSecTimeout INTEGER, a_sTestBoxReqExpr TEXT,
+ a_sBuildReqExpr TEXT, a_sBaseCmd TEXT, a_sTestSuiteZips TEXT);
+
+---
+-- Checks if the test case name is unique, ignoring a_idTestCaseIgnore.
+-- Raises exception if duplicates are found.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestCaseLogic_checkUniqueName(a_sName TEXT, a_idTestCaseIgnore INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ SELECT COUNT(*) INTO v_cRows
+ FROM TestCases
+ WHERE sName = a_sName
+ AND tsExpire = 'infinity'::TIMESTAMP
+ AND idTestCase <> a_idTestCaseIgnore;
+ IF v_cRows <> 0 THEN
+ RAISE EXCEPTION 'Duplicate test case name "%" (% times)', a_sName, v_cRows;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+---
+-- Check that the test case exists.
+-- Raises exception if it doesn't.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestCaseLogic_checkExists(a_idTestCase INTEGER) RETURNS VOID AS $$
+ BEGIN
+ IF NOT EXISTS( SELECT *
+ FROM TestCases
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP ) THEN
+ RAISE EXCEPTION 'Test case with ID % does not currently exist', a_idTestCase;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Historize a row.
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestCaseLogic_historizeEntry(a_idTestCase INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cUpdatedRows INTEGER;
+ BEGIN
+ UPDATE TestCases
+ SET tsExpire = a_tsExpire
+ WHERE idTestcase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
+ IF v_cUpdatedRows <> 1 THEN
+ IF v_cUpdatedRows = 0 THEN
+ RAISE EXCEPTION 'Test case ID % does not currently exist', a_idTestCase;
+ END IF;
+ RAISE EXCEPTION 'Integrity error in TestCases: % current rows with idTestCase=%d', v_cUpdatedRows, a_idTestCase;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE function TestCaseLogic_addEntry(a_uidAuthor INTEGER, a_sName TEXT, a_sDescription TEXT,
+ a_fEnabled BOOL, a_cSecTimeout INTEGER, a_sTestBoxReqExpr TEXT,
+ a_sBuildReqExpr TEXT, a_sBaseCmd TEXT, a_sTestSuiteZips TEXT,
+ a_sComment TEXT)
+ RETURNS INTEGER AS $$
+ DECLARE
+ v_idTestCase INTEGER;
+ BEGIN
+ PERFORM TestCaseLogic_checkUniqueName(a_sName, -1);
+
+ INSERT INTO TestCases (uidAuthor, sName, sDescription, fEnabled, cSecTimeout,
+ sTestBoxReqExpr, sBuildReqExpr, sBaseCmd, sTestSuiteZips, sComment)
+ VALUES (a_uidAuthor, a_sName, a_sDescription, a_fEnabled, a_cSecTimeout,
+ a_sTestBoxReqExpr, a_sBuildReqExpr, a_sBaseCmd, a_sTestSuiteZips, a_sComment)
+ RETURNING idTestcase INTO v_idTestCase;
+ RETURN v_idTestCase;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE function TestCaseLogic_editEntry(a_uidAuthor INTEGER, a_idTestCase INTEGER, a_sName TEXT, a_sDescription TEXT,
+ a_fEnabled BOOL, a_cSecTimeout INTEGER, a_sTestBoxReqExpr TEXT,
+ a_sBuildReqExpr TEXT, a_sBaseCmd TEXT, a_sTestSuiteZips TEXT,
+ a_sComment TEXT)
+ RETURNS INTEGER AS $$
+ DECLARE
+ v_idGenTestCase INTEGER;
+ BEGIN
+ PERFORM TestCaseLogic_checkExists(a_idTestCase);
+ PERFORM TestCaseLogic_checkUniqueName(a_sName, a_idTestCase);
+
+ PERFORM TestCaseLogic_historizeEntry(a_idTestCase, CURRENT_TIMESTAMP);
+ INSERT INTO TestCases (idTestCase, uidAuthor, sName, sDescription, fEnabled, cSecTimeout,
+ sTestBoxReqExpr, sBuildReqExpr, sBaseCmd, sTestSuiteZips, sComment)
+ VALUES (a_idTestCase, a_uidAuthor, a_sName, a_sDescription, a_fEnabled, a_cSecTimeout,
+ a_sTestBoxReqExpr, a_sBuildReqExpr, a_sBaseCmd, a_sTestSuiteZips, a_sComment)
+ RETURNING idGenTestCase INTO v_idGenTestCase;
+ RETURN v_idGenTestCase;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION TestCaseLogic_delEntry(a_uidAuthor INTEGER, a_idTestCase INTEGER, a_fCascade BOOLEAN)
+ RETURNS VOID AS $$
+ DECLARE
+ v_Row TestCases%ROWTYPE;
+ v_tsEffective TIMESTAMP WITH TIME ZONE;
+ v_Rec RECORD;
+ v_sErrors TEXT;
+ BEGIN
+ --
+ -- Check preconditions.
+ --
+ IF a_fCascade <> TRUE THEN
+ IF EXISTS( SELECT *
+ FROM TestCaseDeps
+ WHERE idTestCasePreReq = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP ) THEN
+ v_sErrors := '';
+ FOR v_Rec IN
+ SELECT TestCases.idTestCase AS idTestCase,
+ TestCases.sName AS sName
+ FROM TestCaseDeps, TestCases
+ WHERE TestCaseDeps.idTestCasePreReq = a_idTestCase
+ AND TestCaseDeps.tsExpire = 'infinity'::TIMESTAMP
+ AND TestCases.idTestCase = TestCaseDeps.idTestCase
+ AND TestCases.tsExpire = 'infinity'::TIMESTAMP
+ LOOP
+ IF v_sErrors <> '' THEN
+ v_sErrors := v_sErrors || ', ';
+ END IF;
+ v_sErrors := v_sErrors || v_Rec.sName || ' (idTestCase=' || v_Rec.idTestCase || ')';
+ END LOOP;
+ RAISE EXCEPTION 'Other test cases depends on test case with ID %: % ', a_idTestCase, v_sErrors;
+ END IF;
+
+ IF EXISTS( SELECT *
+ FROM TestGroupMembers
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP ) THEN
+ v_sErrors := '';
+ FOR v_Rec IN
+ SELECT TestGroups.idTestGroup AS idTestGroup,
+ TestGroups.sName AS sName
+ FROM TestGroupMembers, TestGroups
+ WHERE TestGroupMembers.idTestCase = a_idTestCase
+ AND TestGroupMembers.tsExpire = 'infinity'::TIMESTAMP
+ AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup
+ AND TestGroups.tsExpire = 'infinity'::TIMESTAMP
+ LOOP
+ IF v_sErrors <> '' THEN
+ v_sErrors := v_sErrors || ', ';
+ END IF;
+ v_sErrors := v_sErrors || v_Rec.sName || ' (idTestGroup=' || v_Rec.idTestGroup || ')';
+ END LOOP;
+ RAISE EXCEPTION 'Test case with ID % is member of the following test group(s): % ', a_idTestCase, v_sErrors;
+ END IF;
+ END IF;
+
+ --
+ -- To preserve the information about who deleted the record, we try to
+ -- add a dummy record which expires immediately. I say try because of
+ -- the primary key, we must let the new record be valid for 1 us. :-(
+ --
+ SELECT * INTO STRICT v_Row
+ FROM TestCases
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
+ IF v_Row.tsEffective < v_tsEffective THEN
+ PERFORM TestCaseLogic_historizeEntry(a_idTestCase, v_tsEffective);
+ v_Row.tsEffective := v_tsEffective;
+ v_Row.tsExpire := CURRENT_TIMESTAMP;
+ v_Row.uidAuthor := a_uidAuthor;
+ SELECT NEXTVAL('TestCaseGenIdSeq') INTO v_Row.idGenTestCase;
+ INSERT INTO TestCases VALUES (v_Row.*);
+ ELSE
+ PERFORM TestCaseLogic_historizeEntry(a_idTestCase, CURRENT_TIMESTAMP);
+ END IF;
+
+ --
+ -- Delete arguments, test case dependencies and resource dependencies.
+ -- (We don't bother recording who deleted the records here since it's
+ -- a lot of work and sufficiently covered in the TestCases table.)
+ --
+ UPDATE TestCaseArgs
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ UPDATE TestCaseDeps
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ UPDATE TestCaseGlobalRsrcDeps
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ IF a_fCascade = TRUE THEN
+ UPDATE TestCaseDeps
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestCasePreReq = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ UPDATE TestGroupMembers
+ SET tsExpire = CURRENT_TIMESTAMP
+ WHERE idTestCase = a_idTestCase
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ END IF;
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'Test case with ID % does not currently exist', a_idTestCase;
+ WHEN TOO_MANY_ROWS THEN
+ RAISE EXCEPTION 'Integrity error in TestCases: Too many current rows for %', a_idTestCase;
+ END;
+$$ LANGUAGE plpgsql;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testcase.py b/src/VBox/ValidationKit/testmanager/core/testcase.py
new file mode 100755
index 00000000..b2820ff2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testcase.py
@@ -0,0 +1,1467 @@
+# -*- coding: utf-8 -*-
+# $Id: testcase.py $
+# pylint: disable=too-many-lines
+
+"""
+Test Manager - Test Case.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import copy;
+import sys;
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
+ TMInvalidData, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry;
+from testmanager.core.globalresource import GlobalResourceData;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+
+class TestCaseGlobalRsrcDepData(ModelDataBase):
+ """
+ Test case dependency on a global resource - data.
+ """
+
+ ksParam_idTestCase = 'TestCaseDependency_idTestCase';
+ ksParam_idGlobalRsrc = 'TestCaseDependency_idGlobalRsrc';
+ ksParam_tsEffective = 'TestCaseDependency_tsEffective';
+ ksParam_tsExpire = 'TestCaseDependency_tsExpire';
+ ksParam_uidAuthor = 'TestCaseDependency_uidAuthor';
+
+ kasAllowNullAttributes = ['idTestSet', ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestCase = None;
+ self.idGlobalRsrc = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestCaseDeps row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test case not found.');
+
+ self.idTestCase = aoRow[0];
+ self.idGlobalRsrc = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ return self;
+
+
+class TestCaseGlobalRsrcDepLogic(ModelLogicBase):
+ """
+ Test case dependency on a global resources - logic.
+ """
+
+ def getTestCaseDeps(self, idTestCase, tsNow = None):
+ """
+ Returns an array of (TestCaseGlobalRsrcDepData, GlobalResourceData)
+ with the global resources required by idTestCase.
+ Returns empty array if none found. Raises exception on database error.
+
+ Note! Maybe a bit overkill...
+ """
+ ## @todo This code isn't entirely kosher... Should use a DataEx with a oGlobalRsrc = GlobalResourceData().
+ if tsNow is not None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseGlobalRsrcDeps, GlobalResources\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire > %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsEffective <= %s\n'
+ ' AND GlobalResources.idGlobalRsrc = TestCaseGlobalRsrcDeps.idGlobalRsrc\n'
+ ' AND GlobalResources.tsExpire > %s\n'
+ ' AND GlobalResources.tsEffective <= %s\n'
+ , (idTestCase, tsNow, tsNow, tsNow, tsNow) );
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseGlobalRsrcDeps, GlobalResources\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND GlobalResources.idGlobalRsrc = TestCaseGlobalRsrcDeps.idGlobalRsrc\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND GlobalResources.tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase,))
+ aaoRows = self._oDb.fetchAll();
+ aoRet = []
+ for aoRow in aaoRows:
+ oItem = [TestCaseDependencyData().initFromDbRow(aoRow),
+ GlobalResourceData().initFromDbRow(aoRow[5:])];
+ aoRet.append(oItem);
+
+ return aoRet
+
+ def getTestCaseDepsIds(self, idTestCase, tsNow = None):
+ """
+ Returns an array of global resources that idTestCase require.
+ Returns empty array if none found. Raises exception on database error.
+ """
+ if tsNow is not None:
+ self._oDb.execute('SELECT idGlobalRsrc\n'
+ 'FROM TestCaseGlobalRsrcDeps\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire > %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsEffective <= %s\n'
+ , (idTestCase, tsNow, tsNow, ) );
+ else:
+ self._oDb.execute('SELECT idGlobalRsrc\n'
+ 'FROM TestCaseGlobalRsrcDeps\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase,))
+ aidGlobalRsrcs = []
+ for aoRow in self._oDb.fetchAll():
+ aidGlobalRsrcs.append(aoRow[0]);
+ return aidGlobalRsrcs;
+
+
+ def getDepGlobalResourceData(self, idTestCase, tsNow = None):
+ """
+ Returns an array of objects of type GlobalResourceData on which the
+ specified test case depends on.
+ """
+ if tsNow is None :
+ self._oDb.execute('SELECT GlobalResources.*\n'
+ 'FROM TestCaseGlobalRsrcDeps, GlobalResources\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND GlobalResources.idGlobalRsrc = TestCaseGlobalRsrcDeps.idGlobalRsrc\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND GlobalResources.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY GlobalResources.idGlobalRsrc\n'
+ , (idTestCase,))
+ else:
+ self._oDb.execute('SELECT GlobalResources.*\n'
+ 'FROM TestCaseGlobalRsrcDeps, GlobalResources\n'
+ 'WHERE TestCaseGlobalRsrcDeps.idTestCase = %s\n'
+ ' AND GlobalResources.idGlobalRsrc = TestCaseGlobalRsrcDeps.idGlobalRsrc\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire > %s\n'
+ ' AND TestCaseGlobalRsrcDeps.tsExpire <= %s\n'
+ ' AND GlobalResources.tsExpire > %s\n'
+ ' AND GlobalResources.tsEffective <= %s\n'
+ 'ORDER BY GlobalResources.idGlobalRsrc\n'
+ , (idTestCase, tsNow, tsNow, tsNow, tsNow));
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(GlobalResourceData().initFromDbRow(aoRow));
+
+ return aoRet
+
+
+class TestCaseDependencyData(ModelDataBase):
+ """
+ Test case dependency data
+ """
+
+ ksParam_idTestCase = 'TestCaseDependency_idTestCase';
+ ksParam_idTestCasePreReq = 'TestCaseDependency_idTestCasePreReq';
+ ksParam_tsEffective = 'TestCaseDependency_tsEffective';
+ ksParam_tsExpire = 'TestCaseDependency_tsExpire';
+ ksParam_uidAuthor = 'TestCaseDependency_uidAuthor';
+
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestCase = None;
+ self.idTestCasePreReq = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestCaseDeps row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test case not found.');
+
+ self.idTestCase = aoRow[0];
+ self.idTestCasePreReq = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ return self;
+
+ def initFromParams(self, oDisp, fStrict=True):
+ """
+ Initialize the object from parameters.
+ The input is not validated at all, except that all parameters must be
+ present when fStrict is True.
+ Note! Returns parameter NULL values, not database ones.
+ """
+
+ self.convertToParamNull();
+ fn = oDisp.getStringParam; # Shorter...
+
+ self.idTestCase = fn(self.ksParam_idTestCase, None, None if fStrict else self.idTestCase);
+ self.idTestCasePreReq = fn(self.ksParam_idTestCasePreReq, None, None if fStrict else self.idTestCasePreReq);
+ self.tsEffective = fn(self.ksParam_tsEffective, None, None if fStrict else self.tsEffective);
+ self.tsExpire = fn(self.ksParam_tsExpire, None, None if fStrict else self.tsExpire);
+ self.uidAuthor = fn(self.ksParam_uidAuthor, None, None if fStrict else self.uidAuthor);
+
+ return True
+
+ def validateAndConvert(self, oDb = None, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ """
+ Validates the input and converts valid fields to their right type.
+ Returns a dictionary with per field reports, only invalid fields will
+ be returned, so an empty dictionary means that the data is valid.
+
+ The dictionary keys are ksParam_*.
+ """
+ dErrors = {}
+
+ self.idTestCase = self._validateInt( dErrors, self.ksParam_idTestCase, self.idTestCase);
+ self.idTestCasePreReq = self._validateInt( dErrors, self.ksParam_idTestCasePreReq, self.idTestCasePreReq);
+ self.tsEffective = self._validateTs( dErrors, self.ksParam_tsEffective, self.tsEffective);
+ self.tsExpire = self._validateTs( dErrors, self.ksParam_tsExpire, self.tsExpire);
+ self.uidAuthor = self._validateInt( dErrors, self.ksParam_uidAuthor, self.uidAuthor);
+
+ _ = oDb;
+ _ = enmValidateFor;
+ return dErrors
+
+ def convertFromParamNull(self):
+ """
+ Converts from parameter NULL values to database NULL values (None).
+ """
+ if self.idTestCase in [-1, '']: self.idTestCase = None;
+ if self.idTestCasePreReq in [-1, '']: self.idTestCasePreReq = None;
+ if self.tsEffective == '': self.tsEffective = None;
+ if self.tsExpire == '': self.tsExpire = None;
+ if self.uidAuthor in [-1, '']: self.uidAuthor = None;
+ return True;
+
+ def convertToParamNull(self):
+ """
+ Converts from database NULL values (None) to special values we can
+ pass thru parameters list.
+ """
+ if self.idTestCase is None: self.idTestCase = -1;
+ if self.idTestCasePreReq is None: self.idTestCasePreReq = -1;
+ if self.tsEffective is None: self.tsEffective = '';
+ if self.tsExpire is None: self.tsExpire = '';
+ if self.uidAuthor is None: self.uidAuthor = -1;
+ return True;
+
+ def isEqual(self, oOther):
+ """ Compares two instances. """
+ return self.idTestCase == oOther.idTestCase \
+ and self.idTestCasePreReq == oOther.idTestCasePreReq \
+ and self.tsEffective == oOther.tsEffective \
+ and self.tsExpire == oOther.tsExpire \
+ and self.uidAuthor == oOther.uidAuthor;
+
+ def getTestCasePreReqIds(self, aTestCaseDependencyData):
+ """
+ Get list of Test Case IDs which current
+ Test Case depends on
+ """
+ if not aTestCaseDependencyData:
+ return []
+
+ aoRet = []
+ for oTestCaseDependencyData in aTestCaseDependencyData:
+ aoRet.append(oTestCaseDependencyData.idTestCasePreReq)
+
+ return aoRet
+
+class TestCaseDependencyLogic(ModelLogicBase):
+ """Test case dependency management logic"""
+
+ def getTestCaseDeps(self, idTestCase, tsEffective = None):
+ """
+ Returns an array of TestCaseDependencyData with the prerequisites of
+ idTestCase.
+ Returns empty array if none found. Raises exception on database error.
+ """
+ if tsEffective is not None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ , (idTestCase, tsEffective, tsEffective, ) );
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase, ) );
+ aaoRows = self._oDb.fetchAll();
+ aoRet = [];
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseDependencyData().initFromDbRow(aoRow));
+
+ return aoRet
+
+ def getTestCaseDepsIds(self, idTestCase, tsNow = None):
+ """
+ Returns an array of test case IDs of the prerequisites of idTestCase.
+ Returns empty array if none found. Raises exception on database error.
+ """
+ if tsNow is not None:
+ self._oDb.execute('SELECT idTestCase\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ , (idTestCase, tsNow, tsNow, ) );
+ else:
+ self._oDb.execute('SELECT idTestCase\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase, ) );
+ aidPreReqs = [];
+ for aoRow in self._oDb.fetchAll():
+ aidPreReqs.append(aoRow[0]);
+ return aidPreReqs;
+
+
+ def getDepTestCaseData(self, idTestCase, tsNow = None):
+ """
+ Returns an array of objects of type TestCaseData2 on which
+ specified test case depends on
+ """
+ if tsNow is None:
+ self._oDb.execute('SELECT TestCases.*\n'
+ 'FROM TestCases, TestCaseDeps\n'
+ 'WHERE TestCaseDeps.idTestCase = %s\n'
+ ' AND TestCaseDeps.idTestCasePreReq = TestCases.idTestCase\n'
+ ' AND TestCaseDeps.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY TestCases.idTestCase\n'
+ , (idTestCase, ) );
+ else:
+ self._oDb.execute('SELECT TestCases.*\n'
+ 'FROM TestCases, TestCaseDeps\n'
+ 'WHERE TestCaseDeps.idTestCase = %s\n'
+ ' AND TestCaseDeps.idTestCasePreReq = TestCases.idTestCase\n'
+ ' AND TestCaseDeps.tsExpire > %s\n'
+ ' AND TestCaseDeps.tsEffective <= %s\n'
+ ' AND TestCases.tsExpire > %s\n'
+ ' AND TestCases.tsEffective <= %s\n'
+ 'ORDER BY TestCases.idTestCase\n'
+ , (idTestCase, tsNow, tsNow, tsNow, tsNow, ) );
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseData().initFromDbRow(aoRow));
+
+ return aoRet
+
+ def getApplicableDepTestCaseData(self, idTestCase):
+ """
+ Returns an array of objects of type TestCaseData on which
+ specified test case might depends on (all test
+ cases except the specified one and those testcases which are
+ depend on idTestCase)
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE idTestCase <> %s\n'
+ ' AND idTestCase NOT IN (SELECT idTestCase\n'
+ ' FROM TestCaseDeps\n'
+ ' WHERE idTestCasePreReq=%s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP)\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase, idTestCase) )
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseData().initFromDbRow(aoRow));
+
+ return aoRet
+
+class TestCaseData(ModelDataBase):
+ """
+ Test case data
+ """
+
+ ksIdAttr = 'idTestCase';
+ ksIdGenAttr = 'idGenTestCase';
+
+ ksParam_idTestCase = 'TestCase_idTestCase'
+ ksParam_tsEffective = 'TestCase_tsEffective'
+ ksParam_tsExpire = 'TestCase_tsExpire'
+ ksParam_uidAuthor = 'TestCase_uidAuthor'
+ ksParam_idGenTestCase = 'TestCase_idGenTestCase'
+ ksParam_sName = 'TestCase_sName'
+ ksParam_sDescription = 'TestCase_sDescription'
+ ksParam_fEnabled = 'TestCase_fEnabled'
+ ksParam_cSecTimeout = 'TestCase_cSecTimeout'
+ ksParam_sTestBoxReqExpr = 'TestCase_sTestBoxReqExpr';
+ ksParam_sBuildReqExpr = 'TestCase_sBuildReqExpr';
+ ksParam_sBaseCmd = 'TestCase_sBaseCmd'
+ ksParam_sValidationKitZips = 'TestCase_sValidationKitZips'
+ ksParam_sComment = 'TestCase_sComment'
+
+ kasAllowNullAttributes = [ 'idTestCase', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestCase', 'sDescription',
+ 'sTestBoxReqExpr', 'sBuildReqExpr', 'sValidationKitZips', 'sComment' ];
+
+ kcDbColumns = 14;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestCase = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.idGenTestCase = None;
+ self.sName = None;
+ self.sDescription = None;
+ self.fEnabled = False;
+ self.cSecTimeout = 10; # Init with minimum timeout value
+ self.sTestBoxReqExpr = None;
+ self.sBuildReqExpr = None;
+ self.sBaseCmd = None;
+ self.sValidationKitZips = None;
+ self.sComment = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestCases row.
+ Returns self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test case not found.');
+
+ self.idTestCase = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.idGenTestCase = aoRow[4];
+ self.sName = aoRow[5];
+ self.sDescription = aoRow[6];
+ self.fEnabled = aoRow[7];
+ self.cSecTimeout = aoRow[8];
+ self.sTestBoxReqExpr = aoRow[9];
+ self.sBuildReqExpr = aoRow[10];
+ self.sBaseCmd = aoRow[11];
+ self.sValidationKitZips = aoRow[12];
+ self.sComment = aoRow[13];
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestCase, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE idTestCase = %s\n'
+ , ( idTestCase,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestCase=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestCase, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def initFromDbWithGenId(self, oDb, idGenTestCase, tsNow = None):
+ """
+ Initialize the object from the database.
+ """
+ _ = tsNow; # For relevant for the TestCaseDataEx version only.
+ oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE idGenTestCase = %s\n'
+ , (idGenTestCase, ) );
+ return self.initFromDbRow(oDb.fetchOne());
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ if sAttr == 'cSecTimeout' and oValue not in aoNilValues: # Allow human readable interval formats.
+ return utils.parseIntervalSeconds(oValue);
+
+ (oValue, sError) = ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+ if sError is None:
+ if sAttr == 'sTestBoxReqExpr':
+ sError = TestCaseData.validateTestBoxReqExpr(oValue);
+ elif sAttr == 'sBuildReqExpr':
+ sError = TestCaseData.validateBuildReqExpr(oValue);
+ elif sAttr == 'sBaseCmd':
+ _, sError = TestCaseData.validateStr(oValue, fAllowUnicodeSymbols=False);
+ return (oValue, sError);
+
+
+ #
+ # Misc.
+ #
+
+ def needValidationKitBit(self):
+ """
+ Predicate method for checking whether a validation kit build is required.
+ """
+ return self.sValidationKitZips is None \
+ or self.sValidationKitZips.find('@VALIDATIONKIT_ZIP@') >= 0;
+
+ def matchesTestBoxProps(self, oTestBoxData):
+ """
+ Checks if the all of the testbox related test requirements matches the
+ given testbox.
+
+ Returns True or False according to the expression, None on exception or
+ non-boolean expression result.
+ """
+ return TestCaseData.matchesTestBoxPropsEx(oTestBoxData, self.sTestBoxReqExpr);
+
+ def matchesBuildProps(self, oBuildDataEx):
+ """
+ Checks if the all of the build related test requirements matches the
+ given build.
+
+ Returns True or False according to the expression, None on exception or
+ non-boolean expression result.
+ """
+ return TestCaseData.matchesBuildPropsEx(oBuildDataEx, self.sBuildReqExpr);
+
+
+ #
+ # Expression validation code shared with TestCaseArgsDataEx.
+ #
+ @staticmethod
+ def _safelyEvalExpr(sExpr, dLocals, fMayRaiseXcpt = False):
+ """
+ Safely evaluate requirment expression given a set of locals.
+
+ Returns True or False according to the expression. If the expression
+ causes an exception to be raised or does not return a boolean result,
+ None will be returned.
+ """
+ if sExpr is None or sExpr == '':
+ return True;
+
+ dGlobals = \
+ {
+ '__builtins__': None,
+ 'long': long,
+ 'int': int,
+ 'bool': bool,
+ 'True': True,
+ 'False': False,
+ 'len': len,
+ 'isinstance': isinstance,
+ 'type': type,
+ 'dict': dict,
+ 'dir': dir,
+ 'list': list,
+ 'versionCompare': utils.versionCompare,
+ };
+
+ try:
+ fRc = eval(sExpr, dGlobals, dLocals);
+ except:
+ if fMayRaiseXcpt:
+ raise;
+ return None;
+
+ if not isinstance(fRc, bool):
+ if fMayRaiseXcpt:
+ raise Exception('not a boolean result: "%s" - %s' % (fRc, type(fRc)) );
+ return None;
+
+ return fRc;
+
+ @staticmethod
+ def _safelyValidateReqExpr(sExpr, adLocals):
+ """
+ Validates a requirement expression using the given sets of locals,
+ returning None on success and an error string on failure.
+ """
+ for dLocals in adLocals:
+ try:
+ TestCaseData._safelyEvalExpr(sExpr, dLocals, True);
+ except Exception as oXcpt:
+ return str(oXcpt);
+ return None;
+
+ @staticmethod
+ def validateTestBoxReqExpr(sExpr):
+ """
+ Validates a testbox expression, returning None on success and an error
+ string on failure.
+ """
+ adTestBoxes = \
+ [
+ {
+ 'sOs': 'win',
+ 'sOsVersion': '3.1',
+ 'sCpuVendor': 'VirtualBox',
+ 'sCpuArch': 'x86',
+ 'cCpus': 1,
+ 'fCpuHwVirt': False,
+ 'fCpuNestedPaging': False,
+ 'fCpu64BitGuest': False,
+ 'fChipsetIoMmu': False,
+ 'fRawMode': False,
+ 'cMbMemory': 985034,
+ 'cMbScratch': 1234089,
+ 'iTestBoxScriptRev': 1,
+ 'sName': 'emanon',
+ 'uuidSystem': '8FF81BE5-3901-4AB1-8A65-B48D511C0321',
+ },
+ {
+ 'sOs': 'linux',
+ 'sOsVersion': '3.1',
+ 'sCpuVendor': 'VirtualBox',
+ 'sCpuArch': 'amd64',
+ 'cCpus': 8191,
+ 'fCpuHwVirt': True,
+ 'fCpuNestedPaging': True,
+ 'fCpu64BitGuest': True,
+ 'fChipsetIoMmu': True,
+ 'fRawMode': True,
+ 'cMbMemory': 9999999999,
+ 'cMbScratch': 9999999999999,
+ 'iTestBoxScriptRev': 9999999,
+ 'sName': 'emanon',
+ 'uuidSystem': '00000000-0000-0000-0000-000000000000',
+ },
+ ];
+ return TestCaseData._safelyValidateReqExpr(sExpr, adTestBoxes);
+
+ @staticmethod
+ def matchesTestBoxPropsEx(oTestBoxData, sExpr):
+ """ Worker for TestCaseData.matchesTestBoxProps and TestCaseArgsDataEx.matchesTestBoxProps. """
+ if sExpr is None:
+ return True;
+ dLocals = \
+ {
+ 'sOs': oTestBoxData.sOs,
+ 'sOsVersion': oTestBoxData.sOsVersion,
+ 'sCpuVendor': oTestBoxData.sCpuVendor,
+ 'sCpuArch': oTestBoxData.sCpuArch,
+ 'iCpuFamily': oTestBoxData.getCpuFamily(),
+ 'iCpuModel': oTestBoxData.getCpuModel(),
+ 'cCpus': oTestBoxData.cCpus,
+ 'fCpuHwVirt': oTestBoxData.fCpuHwVirt,
+ 'fCpuNestedPaging': oTestBoxData.fCpuNestedPaging,
+ 'fCpu64BitGuest': oTestBoxData.fCpu64BitGuest,
+ 'fChipsetIoMmu': oTestBoxData.fChipsetIoMmu,
+ 'fRawMode': oTestBoxData.fRawMode,
+ 'cMbMemory': oTestBoxData.cMbMemory,
+ 'cMbScratch': oTestBoxData.cMbScratch,
+ 'iTestBoxScriptRev': oTestBoxData.iTestBoxScriptRev,
+ 'iPythonHexVersion': oTestBoxData.iPythonHexVersion,
+ 'sName': oTestBoxData.sName,
+ 'uuidSystem': oTestBoxData.uuidSystem,
+ };
+ return TestCaseData._safelyEvalExpr(sExpr, dLocals);
+
+ @staticmethod
+ def validateBuildReqExpr(sExpr):
+ """
+ Validates a testbox expression, returning None on success and an error
+ string on failure.
+ """
+ adBuilds = \
+ [
+ {
+ 'sProduct': 'VirtualBox',
+ 'sBranch': 'trunk',
+ 'sType': 'release',
+ 'asOsArches': ['win.amd64', 'win.x86'],
+ 'sVersion': '1.0',
+ 'iRevision': 1234,
+ 'uidAuthor': None,
+ 'idBuild': 953,
+ },
+ {
+ 'sProduct': 'VirtualBox',
+ 'sBranch': 'VBox-4.1',
+ 'sType': 'release',
+ 'asOsArches': ['linux.x86',],
+ 'sVersion': '4.2.15',
+ 'iRevision': 89876,
+ 'uidAuthor': None,
+ 'idBuild': 945689,
+ },
+ {
+ 'sProduct': 'VirtualBox',
+ 'sBranch': 'VBox-4.1',
+ 'sType': 'strict',
+ 'asOsArches': ['solaris.x86', 'solaris.amd64',],
+ 'sVersion': '4.3.0_RC3',
+ 'iRevision': 97939,
+ 'uidAuthor': 33,
+ 'idBuild': 9456893,
+ },
+ ];
+ return TestCaseData._safelyValidateReqExpr(sExpr, adBuilds);
+
+ @staticmethod
+ def matchesBuildPropsEx(oBuildDataEx, sExpr):
+ """
+ Checks if the all of the build related test requirements matches the
+ given build.
+ """
+ if sExpr is None:
+ return True;
+ dLocals = \
+ {
+ 'sProduct': oBuildDataEx.oCat.sProduct,
+ 'sBranch': oBuildDataEx.oCat.sBranch,
+ 'sType': oBuildDataEx.oCat.sType,
+ 'asOsArches': oBuildDataEx.oCat.asOsArches,
+ 'sVersion': oBuildDataEx.sVersion,
+ 'iRevision': oBuildDataEx.iRevision,
+ 'uidAuthor': oBuildDataEx.uidAuthor,
+ 'idBuild': oBuildDataEx.idBuild,
+ };
+ return TestCaseData._safelyEvalExpr(sExpr, dLocals);
+
+
+
+
+class TestCaseDataEx(TestCaseData):
+ """
+ Test case data.
+ """
+
+ ksParam_aoTestCaseArgs = 'TestCase_aoTestCaseArgs';
+ ksParam_aoDepTestCases = 'TestCase_aoDepTestCases';
+ ksParam_aoDepGlobalResources = 'TestCase_aoDepGlobalResources';
+
+ # Use [] instead of None.
+ kasAltArrayNull = [ 'aoTestCaseArgs', 'aoDepTestCases', 'aoDepGlobalResources' ];
+
+
+ def __init__(self):
+ TestCaseData.__init__(self);
+
+ # List of objects of type TestCaseData (or TestCaseDataEx, we don't
+ # care) on which current Test Case depends.
+ self.aoDepTestCases = [];
+
+ # List of objects of type GlobalResourceData on which current Test Case depends.
+ self.aoDepGlobalResources = [];
+
+ # List of objects of type TestCaseArgsData.
+ self.aoTestCaseArgs = [];
+
+ def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Worker shared by the initFromDb* methods.
+ Returns self. Raises exception if no row or database error.
+ """
+ _ = sPeriodBack; ## @todo sPeriodBack
+ from testmanager.core.testcaseargs import TestCaseArgsLogic;
+ self.aoDepTestCases = TestCaseDependencyLogic(oDb).getDepTestCaseData(self.idTestCase, tsNow);
+ self.aoDepGlobalResources = TestCaseGlobalRsrcDepLogic(oDb).getDepGlobalResourceData(self.idTestCase, tsNow);
+ self.aoTestCaseArgs = TestCaseArgsLogic(oDb).getTestCaseArgs(self.idTestCase, tsNow);
+ # Note! The above arrays are sorted by their relvant IDs for fetchForChangeLog's sake.
+ return self;
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
+ """
+ Reinitialize from a SELECT * FROM TestCases row. Will query the
+ necessary additional data from oDb using tsNow.
+ Returns self. Raises exception if no row or database error.
+ """
+ TestCaseData.initFromDbRow(self, aoRow);
+ return self._initExtraMembersFromDb(oDb, tsNow);
+
+ def initFromDbWithId(self, oDb, idTestCase, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ TestCaseData.initFromDbWithId(self, oDb, idTestCase, tsNow, sPeriodBack);
+ return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
+
+ def initFromDbWithGenId(self, oDb, idGenTestCase, tsNow = None):
+ """
+ Initialize the object from the database.
+ """
+ TestCaseData.initFromDbWithGenId(self, oDb, idGenTestCase);
+ if tsNow is None and not oDb.isTsInfinity(self.tsExpire):
+ tsNow = self.tsEffective;
+ return self._initExtraMembersFromDb(oDb, tsNow);
+
+ def getAttributeParamNullValues(self, sAttr):
+ if sAttr in ['aoDepTestCases', 'aoDepGlobalResources', 'aoTestCaseArgs']:
+ return [[], ''];
+ return TestCaseData.getAttributeParamNullValues(self, sAttr);
+
+ def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
+ """For dealing with the arrays."""
+ if sAttr not in ['aoDepTestCases', 'aoDepGlobalResources', 'aoTestCaseArgs']:
+ return TestCaseData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
+
+ aoNewValues = [];
+ if sAttr == 'aoDepTestCases':
+ for idTestCase in oDisp.getListOfIntParams(sParam, 1, 0x7ffffffe, []):
+ oDep = TestCaseData();
+ oDep.idTestCase = str(idTestCase);
+ aoNewValues.append(oDep);
+
+ elif sAttr == 'aoDepGlobalResources':
+ for idGlobalRsrc in oDisp.getListOfIntParams(sParam, 1, 0x7ffffffe, []):
+ oGlobalRsrc = GlobalResourceData();
+ oGlobalRsrc.idGlobalRsrc = str(idGlobalRsrc);
+ aoNewValues.append(oGlobalRsrc);
+
+ elif sAttr == 'aoTestCaseArgs':
+ from testmanager.core.testcaseargs import TestCaseArgsData;
+ for sArgKey in oDisp.getStringParam(TestCaseDataEx.ksParam_aoTestCaseArgs, sDefault = '').split(','):
+ oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestCaseDataEx.ksParam_aoTestCaseArgs, sArgKey,))
+ aoNewValues.append(TestCaseArgsData().initFromParams(oDispWrapper, fStrict = False));
+ return aoNewValues;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb): # pylint: disable=too-many-locals
+ """
+ Validate special arrays and requirement expressions.
+
+ For the two dependency arrays we have to supply missing bits by
+ looking them up in the database. In the argument variation case we
+ need to validate each item.
+ """
+ if sAttr not in ['aoDepTestCases', 'aoDepGlobalResources', 'aoTestCaseArgs']:
+ return TestCaseData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ asErrors = [];
+ aoNewValues = [];
+ if sAttr == 'aoDepTestCases':
+ for oTestCase in self.aoDepTestCases:
+ if utils.isString(oTestCase.idTestCase): # Stored as string convertParamToAttribute.
+ oTestCase = copy.copy(oTestCase);
+ try:
+ oTestCase.idTestCase = int(oTestCase.idTestCase);
+ oTestCase.initFromDbWithId(oDb, oTestCase.idTestCase);
+ except Exception as oXcpt:
+ asErrors.append('Test case dependency #%s: %s' % (oTestCase.idTestCase, oXcpt));
+ aoNewValues.append(oTestCase);
+
+ elif sAttr == 'aoDepGlobalResources':
+ for oGlobalRsrc in self.aoDepGlobalResources:
+ if utils.isString(oGlobalRsrc.idGlobalRsrc): # Stored as string convertParamToAttribute.
+ oGlobalRsrc = copy.copy(oGlobalRsrc);
+ try:
+ oGlobalRsrc.idTestCase = int(oGlobalRsrc.idGlobalRsrc);
+ oGlobalRsrc.initFromDbWithId(oDb, oGlobalRsrc.idGlobalRsrc);
+ except Exception as oXcpt:
+ asErrors.append('Resource dependency #%s: %s' % (oGlobalRsrc.idGlobalRsrc, oXcpt));
+ aoNewValues.append(oGlobalRsrc);
+
+ else:
+ assert sAttr == 'aoTestCaseArgs';
+ if not self.aoTestCaseArgs:
+ return (None, 'The testcase requires at least one argument variation to be valid.');
+
+ # Note! We'll be returning an error dictionary instead of an string here.
+ dErrors = {};
+
+ for iVar, oVar in enumerate(self.aoTestCaseArgs):
+ oVar = copy.copy(oVar);
+ oVar.idTestCase = self.idTestCase;
+ dCurErrors = oVar.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
+ if not dCurErrors:
+ pass; ## @todo figure out the ID?
+ else:
+ asErrors = [];
+ for sKey in dCurErrors:
+ asErrors.append('%s: %s' % (sKey[len('TestCaseArgs_'):], dCurErrors[sKey]));
+ dErrors[iVar] = '<br>\n'.join(asErrors)
+ aoNewValues.append(oVar);
+
+ for iVar, oVar in enumerate(self.aoTestCaseArgs):
+ sArgs = oVar.sArgs;
+ for iVar2 in range(iVar + 1, len(self.aoTestCaseArgs)):
+ if self.aoTestCaseArgs[iVar2].sArgs == sArgs:
+ sMsg = 'Duplicate argument variation "%s".' % (sArgs);
+ if iVar in dErrors: dErrors[iVar] += '<br>\n' + sMsg;
+ else: dErrors[iVar] = sMsg;
+ if iVar2 in dErrors: dErrors[iVar2] += '<br>\n' + sMsg;
+ else: dErrors[iVar2] = sMsg;
+ break;
+
+ return (aoNewValues, dErrors if dErrors else None);
+
+ return (aoNewValues, None if not asErrors else ' <br>'.join(asErrors));
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ dErrors = TestCaseData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+
+ # Validate dependencies a wee bit for paranoid reasons. The scheduler
+ # queue generation code does the real validation here!
+ if not dErrors and self.idTestCase is not None:
+ for oDep in self.aoDepTestCases:
+ if oDep.idTestCase == self.idTestCase:
+ if self.ksParam_aoDepTestCases in dErrors:
+ dErrors[self.ksParam_aoDepTestCases] += ' Depending on itself!';
+ else:
+ dErrors[self.ksParam_aoDepTestCases] = 'Depending on itself!';
+ return dErrors;
+
+
+
+
+
+class TestCaseLogic(ModelLogicBase):
+ """
+ Test case management logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def getAll(self):
+ """
+ Fetches all test case records from DB (TestCaseData).
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idTestCase ASC;')
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = [];
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseData().initFromDbRow(aoRow))
+ return aoRet
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches test cases.
+
+ Returns an array (list) of TestCaseDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sName ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart, ));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sName ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart, ));
+
+ aoRows = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(TestCaseDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
+ return aoRows;
+
+ def fetchForChangeLog(self, idTestCase, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
+ """
+ Fetches change log entries for a testbox.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ # 1. Get a list of the relevant change times.
+ self._oDb.execute('( SELECT tsEffective, uidAuthor FROM TestCases WHERE idTestCase = %s AND tsEffective <= %s )\n'
+ 'UNION\n'
+ '( SELECT tsEffective, uidAuthor FROM TestCaseArgs WHERE idTestCase = %s AND tsEffective <= %s )\n'
+ 'UNION\n'
+ '( SELECT tsEffective, uidAuthor FROM TestCaseDeps WHERE idTestCase = %s AND tsEffective <= %s )\n'
+ 'UNION\n'
+ '( SELECT tsEffective, uidAuthor FROM TestCaseGlobalRsrcDeps \n' \
+ ' WHERE idTestCase = %s AND tsEffective <= %s )\n'
+ 'ORDER BY tsEffective DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idTestCase, tsNow,
+ idTestCase, tsNow,
+ idTestCase, tsNow,
+ idTestCase, tsNow,
+ cMaxRows + 1, iStart, ));
+ aaoChanges = self._oDb.fetchAll();
+
+ # 2. Collect data sets for each of those points.
+ # (Doing it the lazy + inefficient way for now.)
+ aoRows = [];
+ for aoChange in aaoChanges:
+ aoRows.append(TestCaseDataEx().initFromDbWithId(self._oDb, idTestCase, aoChange[0]));
+
+ # 3. Calculate the changes.
+ aoEntries = [];
+ for i in range(0, len(aoRows) - 1):
+ oNew = aoRows[i];
+ oOld = aoRows[i + 1];
+ (tsEffective, uidAuthor) = aaoChanges[i];
+ (tsExpire, _) = aaoChanges[i - 1] if i > 0 else (oNew.tsExpire, None)
+ assert self._oDb.isTsInfinity(tsEffective) != self._oDb.isTsInfinity(tsExpire) or tsEffective < tsExpire, \
+ '%s vs %s' % (tsEffective, tsExpire);
+
+ aoChanges = [];
+
+ # The testcase object.
+ if oNew.tsEffective != oOld.tsEffective:
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', \
+ 'aoTestCaseArgs', 'aoDepTestCases', 'aoDepGlobalResources']:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+
+ # The argument variations.
+ iChildOld = 0;
+ for oChildNew in oNew.aoTestCaseArgs:
+ # Locate the old entry, emitting removed markers for old items we have to skip.
+ while iChildOld < len(oOld.aoTestCaseArgs) \
+ and oOld.aoTestCaseArgs[iChildOld].idTestCaseArgs < oChildNew.idTestCaseArgs:
+ oChildOld = oOld.aoTestCaseArgs[iChildOld];
+ aoChanges.append(AttributeChangeEntry('Variation #%s' % (oChildOld.idTestCaseArgs,),
+ None, oChildOld, 'Removed', str(oChildOld)));
+ iChildOld += 1;
+
+ if iChildOld < len(oOld.aoTestCaseArgs) \
+ and oOld.aoTestCaseArgs[iChildOld].idTestCaseArgs == oChildNew.idTestCaseArgs:
+ oChildOld = oOld.aoTestCaseArgs[iChildOld];
+ if oChildNew.tsEffective != oChildOld.tsEffective:
+ for sAttr in oChildNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestCase', ]:
+ oOldAttr = getattr(oChildOld, sAttr);
+ oNewAttr = getattr(oChildNew, sAttr);
+ if oOldAttr != oNewAttr:
+ aoChanges.append(AttributeChangeEntry('Variation[#%s].%s'
+ % (oChildOld.idTestCaseArgs, sAttr,),
+ oNewAttr, oOldAttr,
+ str(oNewAttr), str(oOldAttr)));
+ iChildOld += 1;
+ else:
+ aoChanges.append(AttributeChangeEntry('Variation #%s' % (oChildNew.idTestCaseArgs,),
+ oChildNew, None,
+ str(oChildNew), 'Did not exist'));
+
+ # The testcase dependencies.
+ iChildOld = 0;
+ for oChildNew in oNew.aoDepTestCases:
+ # Locate the old entry, emitting removed markers for old items we have to skip.
+ while iChildOld < len(oOld.aoDepTestCases) \
+ and oOld.aoDepTestCases[iChildOld].idTestCase < oChildNew.idTestCase:
+ oChildOld = oOld.aoDepTestCases[iChildOld];
+ aoChanges.append(AttributeChangeEntry('Dependency #%s' % (oChildOld.idTestCase,),
+ None, oChildOld, 'Removed',
+ '%s (#%u)' % (oChildOld.sName, oChildOld.idTestCase,)));
+ iChildOld += 1;
+ if iChildOld < len(oOld.aoDepTestCases) \
+ and oOld.aoDepTestCases[iChildOld].idTestCase == oChildNew.idTestCase:
+ iChildOld += 1;
+ else:
+ aoChanges.append(AttributeChangeEntry('Dependency #%s' % (oChildNew.idTestCase,),
+ oChildNew, None,
+ '%s (#%u)' % (oChildNew.sName, oChildNew.idTestCase,),
+ 'Did not exist'));
+
+ # The global resource dependencies.
+ iChildOld = 0;
+ for oChildNew in oNew.aoDepGlobalResources:
+ # Locate the old entry, emitting removed markers for old items we have to skip.
+ while iChildOld < len(oOld.aoDepGlobalResources) \
+ and oOld.aoDepGlobalResources[iChildOld].idGlobalRsrc < oChildNew.idGlobalRsrc:
+ oChildOld = oOld.aoDepGlobalResources[iChildOld];
+ aoChanges.append(AttributeChangeEntry('Global Resource #%s' % (oChildOld.idGlobalRsrc,),
+ None, oChildOld, 'Removed',
+ '%s (#%u)' % (oChildOld.sName, oChildOld.idGlobalRsrc,)));
+ iChildOld += 1;
+ if iChildOld < len(oOld.aoDepGlobalResources) \
+ and oOld.aoDepGlobalResources[iChildOld].idGlobalRsrc == oChildNew.idGlobalRsrc:
+ iChildOld += 1;
+ else:
+ aoChanges.append(AttributeChangeEntry('Global Resource #%s' % (oChildNew.idGlobalRsrc,),
+ oChildNew, None,
+ '%s (#%u)' % (oChildNew.sName, oChildNew.idGlobalRsrc,),
+ 'Did not exist'));
+
+ # Done.
+ aoEntries.append(ChangeLogEntry(uidAuthor, None, tsEffective, tsExpire, oNew, oOld, aoChanges));
+
+ # If we're at the end of the log, add the initial entry.
+ if len(aoRows) <= cMaxRows and aoRows:
+ oNew = aoRows[-1];
+ aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None,
+ aaoChanges[-1][0], aaoChanges[-2][0] if len(aaoChanges) > 1 else oNew.tsExpire,
+ oNew, None, []));
+
+ return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aoRows) > cMaxRows);
+
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add a new testcase to the DB.
+ """
+
+ #
+ # Validate the input first.
+ #
+ assert isinstance(oData, TestCaseDataEx);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dErrors:
+ raise TMInvalidData('Invalid input data: %s' % (dErrors,));
+
+ #
+ # Add the testcase.
+ #
+ self._oDb.callProc('TestCaseLogic_addEntry',
+ ( uidAuthor, oData.sName, oData.sDescription, oData.fEnabled, oData.cSecTimeout,
+ oData.sTestBoxReqExpr, oData.sBuildReqExpr, oData.sBaseCmd, oData.sValidationKitZips,
+ oData.sComment ));
+ oData.idTestCase = self._oDb.fetchOne()[0];
+
+ # Add testcase dependencies.
+ for oDep in oData.aoDepTestCases:
+ self._oDb.execute('INSERT INTO TestCaseDeps (idTestCase, idTestCasePreReq, uidAuthor) VALUES (%s, %s, %s)'
+ , (oData.idTestCase, oDep.idTestCase, uidAuthor))
+
+ # Add global resource dependencies.
+ for oDep in oData.aoDepGlobalResources:
+ self._oDb.execute('INSERT INTO TestCaseGlobalRsrcDeps (idTestCase, idGlobalRsrc, uidAuthor) VALUES (%s, %s, %s)'
+ , (oData.idTestCase, oDep.idGlobalRsrc, uidAuthor))
+
+ # Set Test Case Arguments variations
+ for oVar in oData.aoTestCaseArgs:
+ self._oDb.execute('INSERT INTO TestCaseArgs (\n'
+ ' idTestCase, uidAuthor, sArgs, cSecTimeout,\n'
+ ' sTestBoxReqExpr, sBuildReqExpr, cGangMembers, sSubName)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
+ , ( oData.idTestCase, uidAuthor, oVar.sArgs, oVar.cSecTimeout,
+ oVar.sTestBoxReqExpr, oVar.sBuildReqExpr, oVar.cGangMembers, oVar.sSubName, ));
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False): # pylint: disable=too-many-locals
+ """
+ Edit a testcase entry (extended).
+ Caller is expected to rollback the database transactions on exception.
+ """
+
+ #
+ # Validate the input.
+ #
+ assert isinstance(oData, TestCaseDataEx);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('Invalid input data: %s' % (dErrors,));
+
+ #
+ # Did anything change? If not return straight away.
+ #
+ oOldDataEx = TestCaseDataEx().initFromDbWithId(self._oDb, oData.idTestCase);
+ if oOldDataEx.isEqual(oData):
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ #
+ # Make the necessary changes.
+ #
+
+ # The test case itself.
+ if not TestCaseData().initFromOther(oOldDataEx).isEqual(oData):
+ self._oDb.callProc('TestCaseLogic_editEntry', ( uidAuthor, oData.idTestCase, oData.sName, oData.sDescription,
+ oData.fEnabled, oData.cSecTimeout, oData.sTestBoxReqExpr,
+ oData.sBuildReqExpr, oData.sBaseCmd, oData.sValidationKitZips,
+ oData.sComment ));
+ oData.idGenTestCase = self._oDb.fetchOne()[0];
+
+ #
+ # Its dependencies on other testcases.
+ #
+ aidNewDeps = [oDep.idTestCase for oDep in oData.aoDepTestCases];
+ aidOldDeps = [oDep.idTestCase for oDep in oOldDataEx.aoDepTestCases];
+
+ sQuery = self._oDb.formatBindArgs('UPDATE TestCaseDeps\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::timestamp\n'
+ , (oData.idTestCase,));
+ asKeepers = [];
+ for idDep in aidOldDeps:
+ if idDep in aidNewDeps:
+ asKeepers.append(str(idDep));
+ if asKeepers:
+ sQuery += ' AND idTestCasePreReq NOT IN (' + ', '.join(asKeepers) + ')\n';
+ self._oDb.execute(sQuery);
+
+ for idDep in aidNewDeps:
+ if idDep not in aidOldDeps:
+ self._oDb.execute('INSERT INTO TestCaseDeps (idTestCase, idTestCasePreReq, uidAuthor)\n'
+ 'VALUES (%s, %s, %s)\n'
+ , (oData.idTestCase, idDep, uidAuthor) );
+
+ #
+ # Its dependencies on global resources.
+ #
+ aidNewDeps = [oDep.idGlobalRsrc for oDep in oData.aoDepGlobalResources];
+ aidOldDeps = [oDep.idGlobalRsrc for oDep in oOldDataEx.aoDepGlobalResources];
+
+ sQuery = self._oDb.formatBindArgs('UPDATE TestCaseGlobalRsrcDeps\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::timestamp\n'
+ , (oData.idTestCase,));
+ asKeepers = [];
+ for idDep in aidOldDeps:
+ if idDep in aidNewDeps:
+ asKeepers.append(str(idDep));
+ if asKeepers:
+ sQuery = ' AND idGlobalRsrc NOT IN (' + ', '.join(asKeepers) + ')\n';
+ self._oDb.execute(sQuery);
+
+ for idDep in aidNewDeps:
+ if idDep not in aidOldDeps:
+ self._oDb.execute('INSERT INTO TestCaseGlobalRsrcDeps (idTestCase, idGlobalRsrc, uidAuthor)\n'
+ 'VALUES (%s, %s, %s)\n'
+ , (oData.idTestCase, idDep, uidAuthor) );
+
+ #
+ # Update Test Case Args
+ # Note! Primary key is idTestCase, tsExpire, sArgs.
+ #
+
+ # Historize rows that have been removed.
+ sQuery = self._oDb.formatBindArgs('UPDATE TestCaseArgs\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP'
+ , (oData.idTestCase, ));
+ for oNewVar in oData.aoTestCaseArgs:
+ asKeepers.append(self._oDb.formatBindArgs('%s', (oNewVar.sArgs,)));
+ if asKeepers:
+ sQuery += ' AND sArgs NOT IN (' + ', '.join(asKeepers) + ')\n';
+ self._oDb.execute(sQuery);
+
+ # Add new TestCaseArgs records if necessary, reusing old IDs when possible.
+ from testmanager.core.testcaseargs import TestCaseArgsData;
+ for oNewVar in oData.aoTestCaseArgs:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND sArgs = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (oData.idTestCase, oNewVar.sArgs,));
+ aoRow = self._oDb.fetchOne();
+ if aoRow is None:
+ # New
+ self._oDb.execute('INSERT INTO TestCaseArgs (\n'
+ ' idTestCase, uidAuthor, sArgs, cSecTimeout,\n'
+ ' sTestBoxReqExpr, sBuildReqExpr, cGangMembers, sSubName)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
+ , ( oData.idTestCase, uidAuthor, oNewVar.sArgs, oNewVar.cSecTimeout,
+ oNewVar.sTestBoxReqExpr, oNewVar.sBuildReqExpr, oNewVar.cGangMembers, oNewVar.sSubName));
+ else:
+ oCurVar = TestCaseArgsData().initFromDbRow(aoRow);
+ if self._oDb.isTsInfinity(oCurVar.tsExpire):
+ # Existing current entry, updated if changed.
+ if oNewVar.cSecTimeout == oCurVar.cSecTimeout \
+ and oNewVar.sTestBoxReqExpr == oCurVar.sTestBoxReqExpr \
+ and oNewVar.sBuildReqExpr == oCurVar.sBuildReqExpr \
+ and oNewVar.cGangMembers == oCurVar.cGangMembers \
+ and oNewVar.sSubName == oCurVar.sSubName:
+ oNewVar.idTestCaseArgs = oCurVar.idTestCaseArgs;
+ oNewVar.idGenTestCaseArgs = oCurVar.idGenTestCaseArgs;
+ continue; # Unchanged.
+ self._oDb.execute('UPDATE TestCaseArgs SET tsExpire = CURRENT_TIMESTAMP WHERE idGenTestCaseArgs = %s\n'
+ , (oCurVar.idGenTestCaseArgs, ));
+ else:
+ # Existing old entry, re-use the ID.
+ pass;
+ self._oDb.execute('INSERT INTO TestCaseArgs (\n'
+ ' idTestCaseArgs, idTestCase, uidAuthor, sArgs, cSecTimeout,\n'
+ ' sTestBoxReqExpr, sBuildReqExpr, cGangMembers, sSubName)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)\n'
+ 'RETURNING idGenTestCaseArgs\n'
+ , ( oCurVar.idTestCaseArgs, oData.idTestCase, uidAuthor, oNewVar.sArgs, oNewVar.cSecTimeout,
+ oNewVar.sTestBoxReqExpr, oNewVar.sBuildReqExpr, oNewVar.cGangMembers, oNewVar.sSubName));
+ oNewVar.idGenTestCaseArgs = self._oDb.fetchOne()[0];
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, idTestCase, fCascade = False, fCommit = False):
+ """ Deletes the test case if possible. """
+ self._oDb.callProc('TestCaseLogic_delEntry', (uidAuthor, idTestCase, fCascade));
+ self._oDb.maybeCommit(fCommit);
+ return True
+
+
+ def getTestCasePreReqIds(self, idTestCase, tsEffective = None, cMax = None):
+ """
+ Returns an array of prerequisite testcases (IDs) for the given testcase.
+ May raise exception on database error or if the result exceeds cMax.
+ """
+ if tsEffective is None:
+ self._oDb.execute('SELECT idTestCasePreReq\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idTestCasePreReq\n'
+ , (idTestCase,) );
+ else:
+ self._oDb.execute('SELECT idTestCasePreReq\n'
+ 'FROM TestCaseDeps\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idTestCasePreReq\n'
+ , (idTestCase, tsEffective, tsEffective) );
+
+
+ if cMax is not None and self._oDb.getRowCount() > cMax:
+ raise TMExceptionBase('Too many prerequisites for testcase %s: %s, max %s'
+ % (idTestCase, cMax, self._oDb.getRowCount(),));
+
+ aidPreReqs = [];
+ for aoRow in self._oDb.fetchAll():
+ aidPreReqs.append(aoRow[0]);
+ return aidPreReqs;
+
+
+ def cachedLookup(self, idTestCase):
+ """
+ Looks up the most recent TestCaseDataEx object for idTestCase
+ via an object cache.
+
+ Returns a shared TestCaseDataEx object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('TestCaseDataEx');
+ oEntry = self.dCache.get(idTestCase, None);
+ if oEntry is None:
+ fNeedTsNow = False;
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCase, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCases\n'
+ 'WHERE idTestCase = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idTestCase, ));
+ fNeedTsNow = True;
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestCase));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = TestCaseDataEx();
+ tsNow = oEntry.initFromDbRow(aaoRow).tsEffective if fNeedTsNow else None;
+ oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow);
+ self.dCache[idTestCase] = oEntry;
+ return oEntry;
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestCaseGlobalRsrcDepDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestCaseGlobalRsrcDepData(),];
+
+class TestCaseDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestCaseData(),];
+
+ def testEmptyExpr(self):
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr(None), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr(''), None);
+
+ def testSimpleExpr(self):
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('cMbMemory > 10'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('cMbScratch < 10'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('fChipsetIoMmu'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('fChipsetIoMmu is True'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('fChipsetIoMmu is False'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('fChipsetIoMmu is None'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('isinstance(fChipsetIoMmu, bool)'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('isinstance(iTestBoxScriptRev, int)'), None);
+ self.assertEqual(TestCaseData.validateTestBoxReqExpr('isinstance(cMbScratch, long)'), None);
+
+ def testBadExpr(self):
+ self.assertNotEqual(TestCaseData.validateTestBoxReqExpr('this is an bad expression, surely it must be'), None);
+ self.assertNotEqual(TestCaseData.validateTestBoxReqExpr('x = 1 + 1'), None);
+ self.assertNotEqual(TestCaseData.validateTestBoxReqExpr('__import__(\'os\').unlink(\'/tmp/no/such/file\')'), None);
+ self.assertNotEqual(TestCaseData.validateTestBoxReqExpr('print "foobar"'), None);
+
+class TestCaseDataExTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestCaseDataEx(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testcaseargs.py b/src/VBox/ValidationKit/testmanager/core/testcaseargs.py
new file mode 100755
index 00000000..e63df090
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testcaseargs.py
@@ -0,0 +1,416 @@
+# -*- coding: utf-8 -*-
+# $Id: testcaseargs.py $
+
+"""
+Test Manager - Test Case Arguments Variations.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+import sys;
+
+# Validation Kit imports.
+from common import utils;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
+ TMRowNotFound;
+from testmanager.core.testcase import TestCaseData, TestCaseDependencyLogic, TestCaseGlobalRsrcDepLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestCaseArgsData(ModelDataBase):
+ """
+ Test case argument variation.
+ """
+
+ ksIdAttr = 'idTestCaseArgs';
+ ksIdGenAttr = 'idGenTestCaseArgs';
+
+ ksParam_idTestCase = 'TestCaseArgs_idTestCase';
+ ksParam_idTestCaseArgs = 'TestCaseArgs_idTestCaseArgs';
+ ksParam_tsEffective = 'TestCaseArgs_tsEffective';
+ ksParam_tsExpire = 'TestCaseArgs_tsExpire';
+ ksParam_uidAuthor = 'TestCaseArgs_uidAuthor';
+ ksParam_idGenTestCaseArgs = 'TestCaseArgs_idGenTestCaseArgs';
+ ksParam_sArgs = 'TestCaseArgs_sArgs';
+ ksParam_cSecTimeout = 'TestCaseArgs_cSecTimeout';
+ ksParam_sTestBoxReqExpr = 'TestCaseArgs_sTestBoxReqExpr';
+ ksParam_sBuildReqExpr = 'TestCaseArgs_sBuildReqExpr';
+ ksParam_cGangMembers = 'TestCaseArgs_cGangMembers';
+ ksParam_sSubName = 'TestCaseArgs_sSubName';
+
+ kcDbColumns = 12;
+
+ kasAllowNullAttributes = [ 'idTestCase', 'idTestCaseArgs', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestCaseArgs',
+ 'cSecTimeout', 'sTestBoxReqExpr', 'sBuildReqExpr', 'sSubName', ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestCase = None;
+ self.idTestCaseArgs = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.idGenTestCaseArgs = None;
+ self.sArgs = '';
+ self.cSecTimeout = None;
+ self.sTestBoxReqExpr = None;
+ self.sBuildReqExpr = None;
+ self.cGangMembers = 1;
+ self.sSubName = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM TestCaseArgs row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('TestBoxStatus not found.');
+
+ self.idTestCase = aoRow[0];
+ self.idTestCaseArgs = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ self.idGenTestCaseArgs = aoRow[5];
+ self.sArgs = aoRow[6];
+ self.cSecTimeout = aoRow[7];
+ self.sTestBoxReqExpr = aoRow[8];
+ self.sBuildReqExpr = aoRow[9];
+ self.cGangMembers = aoRow[10];
+ self.sSubName = aoRow[11];
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestCaseArgs, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCaseArgs = %s\n'
+ , ( idTestCaseArgs,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestCaseArgs=%s not found (tsNow=%s sPeriodBack=%s)'
+ % (idTestCaseArgs, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def initFromDbWithGenId(self, oDb, idGenTestCaseArgs):
+ """
+ Initialize from the database, given the generation ID of a row.
+ """
+ oDb.execute('SELECT * FROM TestCaseArgs WHERE idGenTestCaseArgs = %s', (idGenTestCaseArgs,));
+ return self.initFromDbRow(oDb.fetchOne());
+
+ def initFromValues(self, sArgs, cSecTimeout = None, sTestBoxReqExpr = None, sBuildReqExpr = None, # pylint: disable=too-many-arguments
+ cGangMembers = 1, idTestCase = None, idTestCaseArgs = None, tsEffective = None, tsExpire = None,
+ uidAuthor = None, idGenTestCaseArgs = None, sSubName = None):
+ """
+ Reinitialize from values.
+ Returns self.
+ """
+ self.idTestCase = idTestCase;
+ self.idTestCaseArgs = idTestCaseArgs;
+ self.tsEffective = tsEffective;
+ self.tsExpire = tsExpire;
+ self.uidAuthor = uidAuthor;
+ self.idGenTestCaseArgs = idGenTestCaseArgs;
+ self.sArgs = sArgs;
+ self.cSecTimeout = utils.parseIntervalSeconds(cSecTimeout);
+ self.sTestBoxReqExpr = sTestBoxReqExpr;
+ self.sBuildReqExpr = sBuildReqExpr;
+ self.cGangMembers = cGangMembers;
+ self.sSubName = sSubName;
+ return self;
+
+ def getAttributeParamNullValues(self, sAttr):
+ aoNilValues = ModelDataBase.getAttributeParamNullValues(self, sAttr);
+ if sAttr == 'cSecTimeout':
+ aoNilValues.insert(0, ''); # Prettier NULL value for cSecTimeout.
+ elif sAttr == 'sArgs':
+ aoNilValues = []; # No NULL value here, thank you.
+ return aoNilValues;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ if sAttr == 'cSecTimeout' and oValue not in aoNilValues: # Allow human readable interval formats.
+ return utils.parseIntervalSeconds(oValue);
+
+ (oValue, sError) = ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+ if sError is None:
+ if sAttr == 'sTestBoxReqExpr':
+ sError = TestCaseData.validateTestBoxReqExpr(oValue);
+ elif sAttr == 'sBuildReqExpr':
+ sError = TestCaseData.validateBuildReqExpr(oValue);
+ return (oValue, sError);
+
+
+
+
+class TestCaseArgsDataEx(TestCaseArgsData):
+ """
+ Complete data set.
+ """
+
+ def __init__(self):
+ TestCaseArgsData.__init__(self);
+ self.oTestCase = None;
+ self.aoTestCasePreReqs = [];
+ self.aoGlobalRsrc = [];
+
+ def initFromDbRow(self, aoRow):
+ raise TMExceptionBase('Do not call me: %s' % (aoRow,))
+
+ def initFromDbRowEx(self, aoRow, oDb, tsConfigEff = None, tsRsrcEff = None):
+ """
+ Extended version of initFromDbRow that fills in the rest from the database.
+ """
+ TestCaseArgsData.initFromDbRow(self, aoRow);
+
+ if tsConfigEff is None: tsConfigEff = oDb.getCurrentTimestamp();
+ if tsRsrcEff is None: tsRsrcEff = oDb.getCurrentTimestamp();
+
+ self.oTestCase = TestCaseData().initFromDbWithId(oDb, self.idTestCase, tsConfigEff);
+ self.aoTestCasePreReqs = TestCaseDependencyLogic(oDb).getTestCaseDeps(self.idTestCase, tsConfigEff);
+ self.aoGlobalRsrc = TestCaseGlobalRsrcDepLogic(oDb).getTestCaseDeps(self.idTestCase, tsRsrcEff);
+
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestCaseArgs, tsNow = None, sPeriodBack = None):
+ _ = oDb; _ = idTestCaseArgs; _ = tsNow; _ = sPeriodBack;
+ raise TMExceptionBase('Not supported.');
+
+ def initFromDbWithGenId(self, oDb, idGenTestCaseArgs):
+ _ = oDb; _ = idGenTestCaseArgs;
+ raise TMExceptionBase('Use initFromDbWithGenIdEx...');
+
+ def initFromDbWithGenIdEx(self, oDb, idGenTestCaseArgs, tsConfigEff = None, tsRsrcEff = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ oDb.execute('SELECT *, CURRENT_TIMESTAMP FROM TestCaseArgs WHERE idGenTestCaseArgs = %s', (idGenTestCaseArgs,));
+ aoRow = oDb.fetchOne();
+ return self.initFromDbRowEx(aoRow, oDb, tsConfigEff, tsRsrcEff);
+
+ def convertFromParamNull(self):
+ raise TMExceptionBase('Not implemented');
+
+ def convertToParamNull(self):
+ raise TMExceptionBase('Not implemented');
+
+ def isEqual(self, oOther):
+ raise TMExceptionBase('Not implemented');
+
+ def matchesTestBoxProps(self, oTestBoxData):
+ """
+ Checks if the all of the testbox related test requirements matches the
+ given testbox.
+
+ Returns True or False according to the expression, None on exception or
+ non-boolean expression result.
+ """
+ return TestCaseData.matchesTestBoxPropsEx(oTestBoxData, self.oTestCase.sTestBoxReqExpr) \
+ and TestCaseData.matchesTestBoxPropsEx(oTestBoxData, self.sTestBoxReqExpr);
+
+ def matchesBuildProps(self, oBuildDataEx):
+ """
+ Checks if the all of the build related test requirements matches the
+ given build.
+
+ Returns True or False according to the expression, None on exception or
+ non-boolean expression result.
+ """
+ return TestCaseData.matchesBuildPropsEx(oBuildDataEx, self.oTestCase.sBuildReqExpr) \
+ and TestCaseData.matchesBuildPropsEx(oBuildDataEx, self.sBuildReqExpr);
+
+
+class TestCaseArgsLogic(ModelLogicBase):
+ """
+ TestCaseArgs database logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+ self.dCache = None;
+
+
+ def areResourcesFree(self, oDataEx):
+ """
+ Checks if all global resources are currently still in existance and free.
+ Returns True/False. May raise exception on database error.
+ """
+
+ # Create a set of global resource IDs.
+ if not oDataEx.aoGlobalRsrc:
+ return True;
+ asIdRsrcs = [str(oDep.idGlobalRsrc) for oDep, _ in oDataEx.aoGlobalRsrc];
+
+ # A record in the resource status table means it's allocated.
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM GlobalResourceStatuses\n'
+ 'WHERE GlobalResourceStatuses.idGlobalRsrc IN (' + ', '.join(asIdRsrcs) + ')\n');
+ if self._oDb.fetchOne()[0] == 0:
+ # Check for disabled or deleted resources (we cannot allocate them).
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM GlobalResources\n'
+ 'WHERE GlobalResources.idGlobalRsrc IN (' + ', '.join(asIdRsrcs) + ')\n'
+ ' AND GlobalResources.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND GlobalResources.fEnabled = TRUE\n');
+ if self._oDb.fetchOne()[0] == len(oDataEx.aoGlobalRsrc):
+ return True;
+ return False;
+
+ def getAll(self):
+ """Get list of objects of type TestCaseArgsData"""
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP')
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseArgsData().initFromDbRow(aoRow))
+
+ return aoRet
+
+ def getTestCaseArgs(self, idTestCase, tsNow = None, aiWhiteList = None):
+ """Get list of testcase's arguments variations"""
+ if aiWhiteList is None:
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY TestCaseArgs.idTestCaseArgs\n'
+ , (idTestCase,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY TestCaseArgs.idTestCaseArgs\n'
+ , (idTestCase, tsNow, tsNow));
+ else:
+ sWhiteList = ','.join((str(x) for x in aiWhiteList));
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND idTestCaseArgs IN (' + sWhiteList + ')\n'
+ 'ORDER BY TestCaseArgs.idTestCaseArgs\n'
+ , (idTestCase,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCase = %s\n'
+ ' AND tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ ' AND idTestCaseArgs IN (' + sWhiteList + ')\n'
+ 'ORDER BY TestCaseArgs.idTestCaseArgs\n'
+ , (idTestCase, tsNow, tsNow));
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestCaseArgsData().initFromDbRow(aoRow))
+
+ return aoRet
+
+ def addTestCaseArgs(self, oTestCaseArgsData):
+ """Add Test Case Args record into DB"""
+ pass; # pylint: disable=unnecessary-pass
+
+ def cachedLookup(self, idTestCaseArgs):
+ """
+ Looks up the most recent TestCaseArgsDataEx object for idTestCaseArg
+ via in an object cache.
+
+ Returns a shared TestCaseArgDataEx object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('TestCaseArgsDataEx');
+ oEntry = self.dCache.get(idTestCaseArgs, None);
+ if oEntry is None:
+ fNeedTsNow = False;
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCaseArgs = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestCaseArgs, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestCaseArgs\n'
+ 'WHERE idTestCaseArgs = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idTestCaseArgs, ));
+ fNeedTsNow = True;
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestCaseArgs));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = TestCaseArgsDataEx();
+ tsNow = TestCaseArgsData().initFromDbRow(aaoRow).tsEffective if fNeedTsNow else None;
+ oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow, tsNow);
+ self.dCache[idTestCaseArgs] = oEntry;
+ return oEntry;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestCaseArgsDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestCaseArgsData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testgroup.py b/src/VBox/ValidationKit/testmanager/core/testgroup.py
new file mode 100755
index 00000000..9a648b0d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testgroup.py
@@ -0,0 +1,771 @@
+# -*- coding: utf-8 -*-
+# $Id: testgroup.py $
+
+"""
+Test Manager - Test groups management.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMRowInUse, \
+ TMTooManyRows, TMInvalidData, TMRowNotFound, TMRowAlreadyExists;
+from testmanager.core.testcase import TestCaseData, TestCaseDataEx;
+
+
+class TestGroupMemberData(ModelDataBase):
+ """Representation of a test group member database row."""
+
+ ksParam_idTestGroup = 'TestGroupMember_idTestGroup';
+ ksParam_idTestCase = 'TestGroupMember_idTestCase';
+ ksParam_tsEffective = 'TestGroupMember_tsEffective';
+ ksParam_tsExpire = 'TestGroupMember_tsExpire';
+ ksParam_uidAuthor = 'TestGroupMember_uidAuthor';
+ ksParam_iSchedPriority = 'TestGroupMember_iSchedPriority';
+ ksParam_aidTestCaseArgs = 'TestGroupMember_aidTestCaseArgs';
+
+ kasAllowNullAttributes = ['idTestGroup', 'idTestCase', 'tsEffective', 'tsExpire', 'uidAuthor', 'aidTestCaseArgs' ];
+ kiMin_iSchedPriority = 0;
+ kiMax_iSchedPriority = 31;
+
+ kcDbColumns = 7;
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestGroup = None;
+ self.idTestCase = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.iSchedPriority = 16;
+ self.aidTestCaseArgs = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestCaseGroupMembers.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test group member not found.')
+
+ self.idTestGroup = aoRow[0];
+ self.idTestCase = aoRow[1];
+ self.tsEffective = aoRow[2];
+ self.tsExpire = aoRow[3];
+ self.uidAuthor = aoRow[4];
+ self.iSchedPriority = aoRow[5];
+ self.aidTestCaseArgs = aoRow[6];
+ return self
+
+
+ def getAttributeParamNullValues(self, sAttr):
+ # Arrays default to [] as NULL currently. That doesn't work for us.
+ if sAttr == 'aidTestCaseArgs':
+ aoNilValues = [None, '-1'];
+ else:
+ aoNilValues = ModelDataBase.getAttributeParamNullValues(self, sAttr);
+ return aoNilValues;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ if sAttr != 'aidTestCaseArgs':
+ return ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ # -1 is a special value, which when present make the whole thing NULL (None).
+ (aidVariations, sError) = self.validateListOfInts(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
+ iMin = -1, iMax = 0x7ffffffe);
+ if sError is None:
+ if aidVariations is None:
+ pass;
+ elif -1 in aidVariations:
+ aidVariations = None;
+ elif 0 in aidVariations:
+ sError = 'Invalid test case varation ID #0.';
+ else:
+ aidVariations = sorted(aidVariations);
+ return (aidVariations, sError);
+
+
+
+class TestGroupMemberDataEx(TestGroupMemberData):
+ """Extended representation of a test group member."""
+
+ def __init__(self):
+ """Extend parent class"""
+ TestGroupMemberData.__init__(self)
+ self.oTestCase = None; # TestCaseDataEx.
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
+ """
+ Reinitialize from a SELECT * FROM TestGroupMembers, TestCases row.
+ Will query the necessary additional data from oDb using tsNow.
+
+ Returns self. Raises exception if no row or database error.
+ """
+ TestGroupMemberData.initFromDbRow(self, aoRow);
+ self.oTestCase = TestCaseDataEx();
+ self.oTestCase.initFromDbRowEx(aoRow[TestGroupMemberData.kcDbColumns:], oDb, tsNow);
+ return self;
+
+ def initFromParams(self, oDisp, fStrict = True):
+ self.oTestCase = None;
+ return TestGroupMemberData.initFromParams(self, oDisp, fStrict);
+
+ def getDataAttributes(self):
+ asAttributes = TestGroupMemberData.getDataAttributes(self);
+ asAttributes.remove('oTestCase');
+ return asAttributes;
+
+ def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
+ dErrors = TestGroupMemberData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
+ if self.ksParam_idTestCase not in dErrors:
+ self.oTestCase = TestCaseDataEx()
+ try:
+ self.oTestCase.initFromDbWithId(oDb, self.idTestCase);
+ except Exception as oXcpt:
+ self.oTestCase = TestCaseDataEx()
+ dErrors[self.ksParam_idTestCase] = str(oXcpt);
+ return dErrors;
+
+
+class TestGroupMemberData2(TestCaseData):
+ """Special representation of a Test Group Member item"""
+
+ def __init__(self):
+ """Extend parent class"""
+ TestCaseData.__init__(self)
+ self.idTestGroup = None
+ self.aidTestCaseArgs = []
+
+ def initFromDbRowEx(self, aoRow):
+ """
+ Reinitialize from this query:
+
+ SELECT TestCases.*,
+ TestGroupMembers.idTestGroup,
+ TestGroupMembers.aidTestCaseArgs
+ FROM TestCases, TestGroupMembers
+ WHERE TestCases.idTestCase = TestGroupMembers.idTestCase
+
+ Represents complete test group member (test case) info.
+ Returns object of type TestGroupMemberData2. Raises exception if no row.
+ """
+ TestCaseData.initFromDbRow(self, aoRow);
+ self.idTestGroup = aoRow[-2]
+ self.aidTestCaseArgs = aoRow[-1]
+ return self;
+
+
+class TestGroupData(ModelDataBase):
+ """
+ Test group data.
+ """
+
+ ksIdAttr = 'idTestGroup';
+
+ ksParam_idTestGroup = 'TestGroup_idTestGroup'
+ ksParam_tsEffective = 'TestGroup_tsEffective'
+ ksParam_tsExpire = 'TestGroup_tsExpire'
+ ksParam_uidAuthor = 'TestGroup_uidAuthor'
+ ksParam_sName = 'TestGroup_sName'
+ ksParam_sDescription = 'TestGroup_sDescription'
+ ksParam_sComment = 'TestGroup_sComment'
+
+ kasAllowNullAttributes = ['idTestGroup', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription', 'sComment' ];
+
+ kcDbColumns = 7;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestGroup = None
+ self.tsEffective = None
+ self.tsExpire = None
+ self.uidAuthor = None
+ self.sName = None
+ self.sDescription = None
+ self.sComment = None
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestGroups row.
+ Returns object of type TestGroupData. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test group not found.')
+
+ self.idTestGroup = aoRow[0]
+ self.tsEffective = aoRow[1]
+ self.tsExpire = aoRow[2]
+ self.uidAuthor = aoRow[3]
+ self.sName = aoRow[4]
+ self.sDescription = aoRow[5]
+ self.sComment = aoRow[6]
+ return self
+
+ def initFromDbWithId(self, oDb, idTestGroup, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE idTestGroup = %s\n'
+ , ( idTestGroup,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestGroup=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestGroup, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+
+class TestGroupDataEx(TestGroupData):
+ """
+ Extended test group data.
+ """
+
+ ksParam_aoMembers = 'TestGroupDataEx_aoMembers';
+ kasAltArrayNull = [ 'aoMembers', ];
+
+ ## Helper parameter containing the comma separated list with the IDs of
+ # potential members found in the parameters.
+ ksParam_aidTestCases = 'TestGroupDataEx_aidTestCases';
+
+
+ def __init__(self):
+ TestGroupData.__init__(self);
+ self.aoMembers = []; # TestGroupMemberDataEx.
+
+ def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Worker shared by the initFromDb* methods.
+ Returns self. Raises exception if no row or database error.
+ """
+ self.aoMembers = [];
+ _ = sPeriodBack; ## @todo sPeriodBack
+
+ if tsNow is None:
+ oDb.execute('SELECT TestGroupMembers.*, TestCases.*\n'
+ 'FROM TestGroupMembers\n'
+ 'LEFT OUTER JOIN TestCases ON (\n'
+ ' TestGroupMembers.idTestCase = TestCases.idTestCase\n'
+ ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP)\n'
+ 'WHERE TestGroupMembers.idTestGroup = %s\n'
+ ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY TestCases.sName, TestCases.idTestCase\n'
+ , (self.idTestGroup,));
+ else:
+ oDb.execute('SELECT TestGroupMembers.*, TestCases.*\n'
+ 'FROM TestGroupMembers\n'
+ 'LEFT OUTER JOIN TestCases ON (\n'
+ ' TestGroupMembers.idTestCase = TestCases.idTestCase\n'
+ ' AND TestCases.tsExpire > %s\n'
+ ' AND TestCases.tsEffective <= %s)\n'
+ 'WHERE TestGroupMembers.idTestGroup = %s\n'
+ ' AND TestGroupMembers.tsExpire > %s\n'
+ ' AND TestGroupMembers.tsEffective <= %s\n'
+ 'ORDER BY TestCases.sName, TestCases.idTestCase\n'
+ , (tsNow, tsNow, self.idTestGroup, tsNow, tsNow));
+
+ for aoRow in oDb.fetchAll():
+ self.aoMembers.append(TestGroupMemberDataEx().initFromDbRowEx(aoRow, oDb, tsNow));
+ return self;
+
+ def initFromDbRowEx(self, aoRow, oDb, tsNow = None, sPeriodBack = None):
+ """
+ Reinitialize from a SELECT * FROM TestGroups row. Will query the
+ necessary additional data from oDb using tsNow.
+ Returns self. Raises exception if no row or database error.
+ """
+ TestGroupData.initFromDbRow(self, aoRow);
+ return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
+
+ def initFromDbWithId(self, oDb, idTestGroup, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ TestGroupData.initFromDbWithId(self, oDb, idTestGroup, tsNow, sPeriodBack);
+ return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
+
+
+ def getAttributeParamNullValues(self, sAttr):
+ if sAttr != 'aoMembers':
+ return TestGroupData.getAttributeParamNullValues(self, sAttr);
+ return ['', [], None];
+
+ def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
+ if sAttr != 'aoMembers':
+ return TestGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
+
+ aoNewValue = [];
+ aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
+ sIds = oDisp.getStringParam(self.ksParam_aidTestCases, sDefault = '');
+ for idTestCase in sIds.split(','):
+ try: idTestCase = int(idTestCase);
+ except: pass;
+ oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestGroupDataEx.ksParam_aoMembers, idTestCase,))
+ oMember = TestGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
+ if idTestCase in aidSelected:
+ aoNewValue.append(oMember);
+ return aoNewValue;
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ if sAttr != 'aoMembers':
+ return TestGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+ asErrors = [];
+ aoNewMembers = [];
+ for oOldMember in oValue:
+ oNewMember = TestGroupMemberDataEx().initFromOther(oOldMember);
+ aoNewMembers.append(oNewMember);
+
+ dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
+ if dErrors:
+ asErrors.append(str(dErrors));
+
+ if not asErrors:
+ for i, _ in enumerate(aoNewMembers):
+ idTestCase = aoNewMembers[i];
+ for j in range(i + 1, len(aoNewMembers)):
+ if aoNewMembers[j].idTestCase == idTestCase:
+ asErrors.append('Duplicate testcase #%d!' % (idTestCase, ));
+ break;
+
+ return (aoNewMembers, None if not asErrors else '<br>\n'.join(asErrors));
+
+
+class TestGroupLogic(ModelLogicBase):
+ """
+ Test case management logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches test groups.
+
+ Returns an array (list) of TestGroupDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sName ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sName ASC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(TestGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
+ return aoRet;
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Adds a testgroup to the database.
+ """
+
+ #
+ # Validate inputs.
+ #
+ assert isinstance(oData, TestGroupDataEx);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dErrors:
+ raise TMInvalidData('addEntry invalid input: %s' % (dErrors,));
+ self._assertUniq(oData, None);
+
+ #
+ # Do the job.
+ #
+ self._oDb.execute('INSERT INTO TestGroups (uidAuthor, sName, sDescription, sComment)\n'
+ 'VALUES (%s, %s, %s, %s)\n'
+ 'RETURNING idTestGroup\n'
+ , ( uidAuthor,
+ oData.sName,
+ oData.sDescription,
+ oData.sComment ));
+ idTestGroup = self._oDb.fetchOne()[0];
+ oData.idTestGroup = idTestGroup;
+
+ for oMember in oData.aoMembers:
+ oMember.idTestGroup = idTestGroup;
+ self._insertTestGroupMember(uidAuthor, oMember)
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a test group.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, TestGroupDataEx);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+ self._assertUniq(oData, oData.idTestGroup);
+
+ oOldData = TestGroupDataEx().initFromDbWithId(self._oDb, oData.idTestGroup);
+
+ #
+ # Update the data that needs updating.
+ #
+
+ if not oData.isEqualEx(oOldData, [ 'aoMembers', 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeTestGroup(oData.idTestGroup);
+ self._oDb.execute('INSERT INTO TestGroups\n'
+ ' (uidAuthor, idTestGroup, sName, sDescription, sComment)\n'
+ 'VALUES (%s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ oData.idTestGroup,
+ oData.sName,
+ oData.sDescription,
+ oData.sComment ));
+
+ # Create a lookup dictionary for old entries.
+ dOld = {};
+ for oOld in oOldData.aoMembers:
+ dOld[oOld.idTestCase] = oOld;
+ assert len(dOld) == len(oOldData.aoMembers);
+
+ # Add new members, updated existing ones.
+ dNew = {};
+ for oNewMember in oData.aoMembers:
+ oNewMember.idTestGroup = oData.idTestGroup;
+ if oNewMember.idTestCase in dNew:
+ raise TMRowAlreadyExists('Duplicate test group member: idTestCase=%d (%s / %s)'
+ % (oNewMember.idTestCase, oNewMember, dNew[oNewMember.idTestCase],));
+ dNew[oNewMember.idTestCase] = oNewMember;
+
+ oOldMember = dOld.get(oNewMember.idTestCase, None);
+ if oOldMember is not None:
+ if oNewMember.isEqualEx(oOldMember, [ 'uidAuthor', 'tsEffective', 'tsExpire' ]):
+ continue; # Skip, nothing changed.
+ self._historizeTestGroupMember(oData.idTestGroup, oNewMember.idTestCase);
+ self._insertTestGroupMember(uidAuthor, oNewMember);
+
+ # Expire members that have been removed.
+ sQuery = self._oDb.formatBindArgs('UPDATE TestGroupMembers\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( oData.idTestGroup, ));
+ if dNew:
+ sQuery += ' AND idTestCase NOT IN (%s)' % (', '.join([str(iKey) for iKey in dNew]),);
+ self._oDb.execute(sQuery);
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, idTestGroup, fCascade = False, fCommit = False):
+ """
+ Deletes a test group.
+ """
+ _ = uidAuthor; ## @todo record uidAuthor.
+
+ #
+ # Cascade.
+ #
+ if fCascade is not True:
+ self._oDb.execute('SELECT SchedGroups.idSchedGroup, SchedGroups.sName\n'
+ 'FROM SchedGroupMembers, SchedGroups\n'
+ 'WHERE SchedGroupMembers.idTestGroup = %s\n'
+ ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND SchedGroups.idSchedGroup = SchedGroupMembers.idSchedGroup\n'
+ ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( idTestGroup, ));
+ aoGroups = self._oDb.fetchAll();
+ if aoGroups:
+ asGroups = ['%s (#%d)' % (sName, idSchedGroup) for idSchedGroup, sName in aoGroups];
+ raise TMRowInUse('Test group #%d is member of one or more scheduling groups: %s'
+ % (idTestGroup, ', '.join(asGroups),));
+ else:
+ self._oDb.execute('UPDATE SchedGroupMembers\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( idTestGroup, ));
+
+ #
+ # Remove the group.
+ #
+ self._oDb.execute('UPDATE TestGroupMembers\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestGroup,))
+ self._oDb.execute('UPDATE TestGroups\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestGroup,))
+
+ self._oDb.maybeCommit(fCommit)
+ return True;
+
+ def cachedLookup(self, idTestGroup):
+ """
+ Looks up the most recent TestGroupDataEx object for idTestGroup
+ via an object cache.
+
+ Returns a shared TestGroupDataEx object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('TestGroupDataEx');
+ oEntry = self.dCache.get(idTestGroup, None);
+ if oEntry is None:
+ fNeedTsNow = False;
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestGroup, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE idTestGroup = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (idTestGroup, ));
+ fNeedTsNow = True;
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestGroup));
+
+ if self._oDb.getRowCount() == 1:
+ aaoRow = self._oDb.fetchOne();
+ oEntry = TestGroupDataEx();
+ tsNow = oEntry.initFromDbRow(aaoRow).tsEffective if fNeedTsNow else None;
+ oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow);
+ self.dCache[idTestGroup] = oEntry;
+ return oEntry;
+
+
+ #
+ # Other methods.
+ #
+
+ def fetchOrderedByName(self, tsNow = None):
+ """
+ Return list of objects of type TestGroupData ordered by name.
+ May raise exception on database error.
+ """
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sName ASC\n');
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sName ASC\n'
+ , (tsNow, tsNow,));
+ aoRet = []
+ for _ in range(self._oDb.getRowCount()):
+ aoRet.append(TestGroupData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRet;
+
+ def getMembers(self, idTestGroup):
+ """
+ Fetches all test case records from DB which are
+ belong to current Test Group.
+ Returns list of objects of type TestGroupMemberData2 (!).
+ """
+ self._oDb.execute('SELECT TestCases.*,\n'
+ ' TestGroupMembers.idTestGroup,\n'
+ ' TestGroupMembers.aidTestCaseArgs\n'
+ 'FROM TestCases, TestGroupMembers\n'
+ 'WHERE TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestGroupMembers.idTestCase = TestCases.idTestCase\n'
+ ' AND TestGroupMembers.idTestGroup = %s\n'
+ 'ORDER BY TestCases.idTestCase ASC;',
+ (idTestGroup,))
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestGroupMemberData2().initFromDbRowEx(aoRow))
+
+ return aoRet
+
+ def getAll(self, tsNow=None):
+ """Return list of objects of type TestGroupData"""
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY idTestGroup ASC;')
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY idTestGroup ASC;',
+ (tsNow, tsNow))
+
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestGroupData().initFromDbRow(aoRow))
+
+ return aoRet
+
+ def getById(self, idTestGroup, tsNow=None):
+ """Get Test Group data by its ID"""
+
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idTestGroup = %s\n'
+ 'ORDER BY idTestGroup ASC;'
+ , (idTestGroup,))
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestGroups\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ ' AND idTestGroup = %s\n'
+ 'ORDER BY idTestGroup ASC;'
+ , (tsNow, tsNow, idTestGroup))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise TMTooManyRows('Found more than one test groups with the same credentials. Database structure is corrupted.')
+ try:
+ return TestGroupData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+ #
+ # Helpers.
+ #
+
+ def _assertUniq(self, oData, idTestGroupIgnore):
+ """ Checks that the test group name is unique, raises exception if it isn't. """
+ self._oDb.execute('SELECT idTestGroup\n'
+ 'FROM TestGroups\n'
+ 'WHERE sName = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ + ('' if idTestGroupIgnore is None else ' AND idTestGroup <> %d\n' % (idTestGroupIgnore,))
+ , ( oData.sName, ))
+ if self._oDb.getRowCount() > 0:
+ raise TMRowAlreadyExists('A Test group with name "%s" already exist.' % (oData.sName,));
+ return True;
+
+ def _historizeTestGroup(self, idTestGroup):
+ """ Historize Test Group record. """
+ self._oDb.execute('UPDATE TestGroups\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( idTestGroup, ));
+ return True;
+
+ def _historizeTestGroupMember(self, idTestGroup, idTestCase):
+ """ Historize Test Group Member record. """
+ self._oDb.execute('UPDATE TestGroupMembers\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestGroup = %s\n'
+ ' AND idTestCase = %s\n'
+ ' AND tsExpire = \'infinity\'::timestamp\n'
+ , (idTestGroup, idTestCase,));
+ return True;
+
+ def _insertTestGroupMember(self, uidAuthor, oMember):
+ """ Inserts a test group member. """
+ self._oDb.execute('INSERT INTO TestGroupMembers\n'
+ ' (uidAuthor, idTestGroup, idTestCase, iSchedPriority, aidTestCaseArgs)\n'
+ 'VALUES (%s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ oMember.idTestGroup,
+ oMember.idTestCase,
+ oMember.iSchedPriority,
+ oMember.aidTestCaseArgs, ));
+ return True;
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestGroupMemberDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestGroupMemberData(),];
+
+class TestGroupDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestGroupData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testresultfailures.py b/src/VBox/ValidationKit/testmanager/core/testresultfailures.py
new file mode 100755
index 00000000..b94ff7d5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testresultfailures.py
@@ -0,0 +1,529 @@
+# -*- coding: utf-8 -*-
+# $Id: testresultfailures.py $
+# pylint: disable=too-many-lines
+
+## @todo Rename this file to testresult.py!
+
+"""
+Test Manager - Test result failures.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import sys;
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelLogicBase, ModelDataBaseTestCase, TMInvalidData, TMRowNotFound, \
+ TMRowAlreadyExists, ChangeLogEntry, AttributeChangeEntry;
+from testmanager.core.failurereason import FailureReasonData;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestResultFailureData(ModelDataBase):
+ """
+ Test result failure reason data.
+ """
+
+ ksIdAttr = 'idTestResult';
+ kfIdAttrIsForForeign = True; # Modifies the 'add' validation.
+
+ ksParam_idTestResult = 'TestResultFailure_idTestResult';
+ ksParam_tsEffective = 'TestResultFailure_tsEffective';
+ ksParam_tsExpire = 'TestResultFailure_tsExpire';
+ ksParam_uidAuthor = 'TestResultFailure_uidAuthor';
+ ksParam_idTestSet = 'TestResultFailure_idTestSet';
+ ksParam_idFailureReason = 'TestResultFailure_idFailureReason';
+ ksParam_sComment = 'TestResultFailure_sComment';
+
+ kasAllowNullAttributes = ['tsEffective', 'tsExpire', 'uidAuthor', 'sComment', 'idTestSet' ];
+
+ kcDbColumns = 7;
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+ self.idTestResult = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.idTestSet = None;
+ self.idFailureReason = None;
+ self.sComment = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestResultFailures.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result file record not found.')
+
+ self.idTestResult = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.idTestSet = aoRow[4];
+ self.idFailureReason = aoRow[5];
+ self.sComment = aoRow[6];
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestResult, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM TestResultFailures\n'
+ 'WHERE idTestResult = %s\n'
+ , ( idTestResult,), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestResult=%s not found (tsNow=%s, sPeriodBack=%s)' % (idTestResult, tsNow, sPeriodBack));
+ assert len(aoRow) == self.kcDbColumns;
+ return self.initFromDbRow(aoRow);
+
+ def initFromValues(self, idTestResult, idFailureReason, uidAuthor,
+ tsExpire = None, tsEffective = None, idTestSet = None, sComment = None):
+ """
+ Initialize from values.
+ """
+ self.idTestResult = idTestResult;
+ self.tsEffective = tsEffective;
+ self.tsExpire = tsExpire;
+ self.uidAuthor = uidAuthor;
+ self.idTestSet = idTestSet;
+ self.idFailureReason = idFailureReason;
+ self.sComment = sComment;
+ return self;
+
+
+
+class TestResultFailureDataEx(TestResultFailureData):
+ """
+ Extends TestResultFailureData by resolving reasons and user.
+ """
+
+ def __init__(self):
+ TestResultFailureData.__init__(self);
+ self.oFailureReason = None;
+ self.oAuthor = None;
+
+ def initFromDbRowEx(self, aoRow, oFailureReasonLogic, oUserAccountLogic):
+ """
+ Reinitialize from a SELECT * FROM TestResultFailures.
+ Return self. Raises exception if no row.
+ """
+ self.initFromDbRow(aoRow);
+ self.oFailureReason = oFailureReasonLogic.cachedLookup(self.idFailureReason);
+ self.oAuthor = oUserAccountLogic.cachedLookup(self.uidAuthor);
+ return self;
+
+
+class TestResultListingData(ModelDataBase): # pylint: disable=too-many-instance-attributes
+ """
+ Test case result data representation for table listing
+ """
+
+ def __init__(self):
+ """Initialize"""
+ ModelDataBase.__init__(self)
+
+ self.idTestSet = None
+
+ self.idBuildCategory = None;
+ self.sProduct = None
+ self.sRepository = None;
+ self.sBranch = None
+ self.sType = None
+ self.idBuild = None;
+ self.sVersion = None;
+ self.iRevision = None
+
+ self.sOs = None;
+ self.sOsVersion = None;
+ self.sArch = None;
+ self.sCpuVendor = None;
+ self.sCpuName = None;
+ self.cCpus = None;
+ self.fCpuHwVirt = None;
+ self.fCpuNestedPaging = None;
+ self.fCpu64BitGuest = None;
+ self.idTestBox = None
+ self.sTestBoxName = None
+
+ self.tsCreated = None
+ self.tsElapsed = None
+ self.enmStatus = None
+ self.cErrors = None;
+
+ self.idTestCase = None
+ self.sTestCaseName = None
+ self.sBaseCmd = None
+ self.sArgs = None
+ self.sSubName = None;
+
+ self.idBuildTestSuite = None;
+ self.iRevisionTestSuite = None;
+
+ self.oFailureReason = None;
+ self.oFailureReasonAssigner = None;
+ self.tsFailureReasonAssigned = None;
+ self.sFailureReasonComment = None;
+
+ def initFromDbRowEx(self, aoRow, oFailureReasonLogic, oUserAccountLogic):
+ """
+ Reinitialize from a database query.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result record not found.')
+
+ self.idTestSet = aoRow[0];
+
+ self.idBuildCategory = aoRow[1];
+ self.sProduct = aoRow[2];
+ self.sRepository = aoRow[3];
+ self.sBranch = aoRow[4];
+ self.sType = aoRow[5];
+ self.idBuild = aoRow[6];
+ self.sVersion = aoRow[7];
+ self.iRevision = aoRow[8];
+
+ self.sOs = aoRow[9];
+ self.sOsVersion = aoRow[10];
+ self.sArch = aoRow[11];
+ self.sCpuVendor = aoRow[12];
+ self.sCpuName = aoRow[13];
+ self.cCpus = aoRow[14];
+ self.fCpuHwVirt = aoRow[15];
+ self.fCpuNestedPaging = aoRow[16];
+ self.fCpu64BitGuest = aoRow[17];
+ self.idTestBox = aoRow[18];
+ self.sTestBoxName = aoRow[19];
+
+ self.tsCreated = aoRow[20];
+ self.tsElapsed = aoRow[21];
+ self.enmStatus = aoRow[22];
+ self.cErrors = aoRow[23];
+
+ self.idTestCase = aoRow[24];
+ self.sTestCaseName = aoRow[25];
+ self.sBaseCmd = aoRow[26];
+ self.sArgs = aoRow[27];
+ self.sSubName = aoRow[28];
+
+ self.idBuildTestSuite = aoRow[29];
+ self.iRevisionTestSuite = aoRow[30];
+
+ self.oFailureReason = None;
+ if aoRow[31] is not None:
+ self.oFailureReason = oFailureReasonLogic.cachedLookup(aoRow[31]);
+ self.oFailureReasonAssigner = None;
+ if aoRow[32] is not None:
+ self.oFailureReasonAssigner = oUserAccountLogic.cachedLookup(aoRow[32]);
+ self.tsFailureReasonAssigned = aoRow[33];
+ self.sFailureReasonComment = aoRow[34];
+
+ return self
+
+
+
+class TestResultFailureLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Test result failure reason logic.
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+
+ def fetchForChangeLog(self, idTestResult, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
+ """
+ Fetches change log entries for a failure reason.
+
+ Returns an array of ChangeLogEntry instance and an indicator whether
+ there are more entries.
+ Raises exception on error.
+ """
+
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ # 1. Get a list of the changes from both TestResultFailures and assoicated
+ # FailureReasons. The latter is useful since the failure reason
+ # description may evolve along side the invidiual failure analysis.
+ self._oDb.execute('( SELECT trf.tsEffective AS tsEffectiveChangeLog,\n'
+ ' trf.uidAuthor AS uidAuthorChangeLog,\n'
+ ' trf.*,\n'
+ ' fr.*\n'
+ ' FROM TestResultFailures trf,\n'
+ ' FailureReasons fr\n'
+ ' WHERE trf.idTestResult = %s\n'
+ ' AND trf.tsEffective <= %s\n'
+ ' AND trf.idFailureReason = fr.idFailureReason\n'
+ ' AND fr.tsEffective <= trf.tsEffective\n'
+ ' AND fr.tsExpire > trf.tsEffective\n'
+ ')\n'
+ 'UNION\n'
+ '( SELECT fr.tsEffective AS tsEffectiveChangeLog,\n'
+ ' fr.uidAuthor AS uidAuthorChangeLog,\n'
+ ' trf.*,\n'
+ ' fr.*\n'
+ ' FROM TestResultFailures trf,\n'
+ ' FailureReasons fr\n'
+ ' WHERE trf.idTestResult = %s\n'
+ ' AND trf.tsEffective <= %s\n'
+ ' AND trf.idFailureReason = fr.idFailureReason\n'
+ ' AND fr.tsEffective > trf.tsEffective\n'
+ ' AND fr.tsEffective < trf.tsExpire\n'
+ ')\n'
+ 'ORDER BY tsEffectiveChangeLog DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , ( idTestResult, tsNow, idTestResult, tsNow, cMaxRows + 1, iStart, ));
+
+ aaoRows = [];
+ for aoChange in self._oDb.fetchAll():
+ oTrf = TestResultFailureDataEx().initFromDbRow(aoChange[2:]);
+ oFr = FailureReasonData().initFromDbRow(aoChange[(2+TestResultFailureData.kcDbColumns):]);
+ oTrf.oFailureReason = oFr;
+ aaoRows.append([aoChange[0], aoChange[1], oTrf, oFr]);
+
+ # 2. Calculate the changes.
+ oFailureCategoryLogic = None;
+ aoEntries = [];
+ for i in xrange(0, len(aaoRows) - 1):
+ aoNew = aaoRows[i];
+ aoOld = aaoRows[i + 1];
+
+ aoChanges = [];
+ oNew = aoNew[2];
+ oOld = aoOld[2];
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', 'oFailureReason', 'oAuthor' ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ if sAttr == 'idFailureReason':
+ oNewAttr = '%s (%s)' % (oNewAttr, oNew.oFailureReason.sShort, );
+ oOldAttr = '%s (%s)' % (oOldAttr, oOld.oFailureReason.sShort, );
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+ if oOld.idFailureReason == oNew.idFailureReason:
+ oNew = aoNew[3];
+ oOld = aoOld[3];
+ for sAttr in oNew.getDataAttributes():
+ if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
+ oOldAttr = getattr(oOld, sAttr);
+ oNewAttr = getattr(oNew, sAttr);
+ if oOldAttr != oNewAttr:
+ if sAttr == 'idFailureCategory':
+ if oFailureCategoryLogic is None:
+ from testmanager.core.failurecategory import FailureCategoryLogic;
+ oFailureCategoryLogic = FailureCategoryLogic(self._oDb);
+ oCat = oFailureCategoryLogic.cachedLookup(oNewAttr);
+ if oCat is not None:
+ oNewAttr = '%s (%s)' % (oNewAttr, oCat.sShort, );
+ oCat = oFailureCategoryLogic.cachedLookup(oOldAttr);
+ if oCat is not None:
+ oOldAttr = '%s (%s)' % (oOldAttr, oCat.sShort, );
+ aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
+
+
+ tsExpire = aaoRows[i - 1][0] if i > 0 else aoNew[2].tsExpire;
+ aoEntries.append(ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], aoOld[2], aoChanges));
+
+ # If we're at the end of the log, add the initial entry.
+ if len(aaoRows) <= cMaxRows and aaoRows:
+ aoNew = aaoRows[-1];
+ tsExpire = aaoRows[-1 - 1][0] if len(aaoRows) > 1 else aoNew[2].tsExpire;
+ aoEntries.append(ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], None, []));
+
+ return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aaoRows) > cMaxRows);
+
+
+ def getById(self, idTestResult):
+ """Get Test result failure reason data by idTestResult"""
+
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestResultFailures\n'
+ 'WHERE tsExpire = \'infinity\'::timestamp\n'
+ ' AND idTestResult = %s;', (idTestResult,))
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise self._oDb.integrityException(
+ 'Found more than one failure reasons with the same credentials. Database structure is corrupted.')
+ try:
+ return TestResultFailureData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add a test result failure reason record.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, TestResultFailureData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_AddForeignId);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ # Check if it exist first (we're adding, not editing, collisions not allowed).
+ oOldData = self.getById(oData.idTestResult);
+ if oOldData is not None:
+ raise TMRowAlreadyExists('TestResult %d already have a failure reason associated with it:'
+ '%s\n'
+ 'Perhaps someone else beat you to it? Or did you try resubmit?'
+ % (oData.idTestResult, oOldData));
+ oData = self._resolveSetTestIdIfMissing(oData);
+
+ #
+ # Add record.
+ #
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modifies a test result failure reason.
+ """
+
+ #
+ # Validate inputs and read in the old(/current) data.
+ #
+ assert isinstance(oData, TestResultFailureData);
+ dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
+ if dErrors:
+ raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
+
+ oOldData = self.getById(oData.idTestResult)
+ oData.idTestSet = oOldData.idTestSet;
+
+ #
+ # Update the data that needs updating.
+ #
+ if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', ]):
+ self._historizeEntry(oData.idTestResult);
+ self._readdEntry(uidAuthor, oData);
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def removeEntry(self, uidAuthor, idTestResult, fCascade = False, fCommit = False):
+ """
+ Deletes a test result failure reason.
+ """
+ _ = fCascade; # Not applicable.
+
+ oData = self.getById(idTestResult)
+ (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
+ if oData.tsEffective not in (tsCur, tsCurMinusOne):
+ self._historizeEntry(idTestResult, tsCurMinusOne);
+ self._readdEntry(uidAuthor, oData, tsCurMinusOne);
+ self._historizeEntry(idTestResult);
+ self._oDb.execute('UPDATE TestResultFailures\n'
+ 'SET tsExpire = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestResult = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (idTestResult,));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ #
+ # Helpers.
+ #
+
+ def _readdEntry(self, uidAuthor, oData, tsEffective = None):
+ """
+ Re-adds the TestResultFailure entry. Used by addEntry, editEntry and removeEntry.
+ """
+ if tsEffective is None:
+ tsEffective = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('INSERT INTO TestResultFailures (\n'
+ ' uidAuthor,\n'
+ ' tsEffective,\n'
+ ' idTestResult,\n'
+ ' idTestSet,\n'
+ ' idFailureReason,\n'
+ ' sComment)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s)\n'
+ , ( uidAuthor,
+ tsEffective,
+ oData.idTestResult,
+ oData.idTestSet,
+ oData.idFailureReason,
+ oData.sComment,) );
+ return True;
+
+
+ def _historizeEntry(self, idTestResult, tsExpire = None):
+ """ Historizes the current entry. """
+ if tsExpire is None:
+ tsExpire = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('UPDATE TestResultFailures\n'
+ 'SET tsExpire = %s\n'
+ 'WHERE idTestResult = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (tsExpire, idTestResult,));
+ return True;
+
+
+ def _resolveSetTestIdIfMissing(self, oData):
+ """ Resolve any missing idTestSet reference (it's a duplicate for speed efficiency). """
+ if oData.idTestSet is None and oData.idTestResult is not None:
+ self._oDb.execute('SELECT idTestSet FROM TestResults WHERE idTestResult = %s', (oData.idTestResult,));
+ oData.idTestSet = self._oDb.fetchOne()[0];
+ return oData;
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestResultFailureDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestResultFailureData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testresults.py b/src/VBox/ValidationKit/testmanager/core/testresults.py
new file mode 100755
index 00000000..58f056d2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testresults.py
@@ -0,0 +1,2926 @@
+# -*- coding: utf-8 -*-
+# $Id: testresults.py $
+# pylint: disable=too-many-lines
+
+## @todo Rename this file to testresult.py!
+
+"""
+Test Manager - Fetch test results.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import sys;
+import unittest;
+
+# Validation Kit imports.
+from common import constants;
+from testmanager import config;
+from testmanager.core.base import ModelDataBase, ModelLogicBase, ModelDataBaseTestCase, ModelFilterBase, \
+ FilterCriterion, FilterCriterionValueAndDescription, \
+ TMExceptionBase, TMTooManyRows, TMRowNotFound;
+from testmanager.core.testgroup import TestGroupData;
+from testmanager.core.build import BuildDataEx, BuildCategoryData;
+from testmanager.core.failurereason import FailureReasonLogic;
+from testmanager.core.testbox import TestBoxData, TestBoxLogic;
+from testmanager.core.testcase import TestCaseData;
+from testmanager.core.schedgroup import SchedGroupData, SchedGroupLogic;
+from testmanager.core.systemlog import SystemLogData, SystemLogLogic;
+from testmanager.core.testresultfailures import TestResultFailureDataEx;
+from testmanager.core.useraccount import UserAccountLogic;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class TestResultData(ModelDataBase):
+ """
+ Test case execution result data
+ """
+
+ ## @name TestStatus_T
+ # @{
+ ksTestStatus_Running = 'running';
+ ksTestStatus_Success = 'success';
+ ksTestStatus_Skipped = 'skipped';
+ ksTestStatus_BadTestBox = 'bad-testbox';
+ ksTestStatus_Aborted = 'aborted';
+ ksTestStatus_Failure = 'failure';
+ ksTestStatus_TimedOut = 'timed-out';
+ ksTestStatus_Rebooted = 'rebooted';
+ ## @}
+
+ ## List of relatively harmless (to testgroup/case) statuses.
+ kasHarmlessTestStatuses = [ ksTestStatus_Skipped, ksTestStatus_BadTestBox, ksTestStatus_Aborted, ];
+ ## List of bad statuses.
+ kasBadTestStatuses = [ ksTestStatus_Failure, ksTestStatus_TimedOut, ksTestStatus_Rebooted, ];
+
+
+ ksIdAttr = 'idTestResult';
+
+ ksParam_idTestResult = 'TestResultData_idTestResult';
+ ksParam_idTestResultParent = 'TestResultData_idTestResultParent';
+ ksParam_idTestSet = 'TestResultData_idTestSet';
+ ksParam_tsCreated = 'TestResultData_tsCreated';
+ ksParam_tsElapsed = 'TestResultData_tsElapsed';
+ ksParam_idStrName = 'TestResultData_idStrName';
+ ksParam_cErrors = 'TestResultData_cErrors';
+ ksParam_enmStatus = 'TestResultData_enmStatus';
+ ksParam_iNestingDepth = 'TestResultData_iNestingDepth';
+ kasValidValues_enmStatus = [
+ ksTestStatus_Running,
+ ksTestStatus_Success,
+ ksTestStatus_Skipped,
+ ksTestStatus_BadTestBox,
+ ksTestStatus_Aborted,
+ ksTestStatus_Failure,
+ ksTestStatus_TimedOut,
+ ksTestStatus_Rebooted
+ ];
+
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+ self.idTestResult = None
+ self.idTestResultParent = None
+ self.idTestSet = None
+ self.tsCreated = None
+ self.tsElapsed = None
+ self.idStrName = None
+ self.cErrors = 0;
+ self.enmStatus = None
+ self.iNestingDepth = None
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestResults.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result record not found.')
+
+ self.idTestResult = aoRow[0]
+ self.idTestResultParent = aoRow[1]
+ self.idTestSet = aoRow[2]
+ self.tsCreated = aoRow[3]
+ self.tsElapsed = aoRow[4]
+ self.idStrName = aoRow[5]
+ self.cErrors = aoRow[6]
+ self.enmStatus = aoRow[7]
+ self.iNestingDepth = aoRow[8]
+ return self;
+
+ def initFromDbWithId(self, oDb, idTestResult, tsNow = None, sPeriodBack = None):
+ """
+ Initialize from the database, given the ID of a row.
+ """
+ _ = tsNow;
+ _ = sPeriodBack;
+ oDb.execute('SELECT *\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestResult = %s\n'
+ , ( idTestResult,));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestResult=%s not found' % (idTestResult,));
+ return self.initFromDbRow(aoRow);
+
+ def isFailure(self):
+ """ Check if it's a real failure. """
+ return self.enmStatus in self.kasBadTestStatuses;
+
+
+class TestResultDataEx(TestResultData):
+ """
+ Extended test result data class.
+
+ This is intended for use as a node in a result tree. This is not intended
+ for serialization to parameters or vice versa. Use TestResultLogic to
+ construct the tree.
+ """
+
+ def __init__(self):
+ TestResultData.__init__(self)
+ self.sName = None; # idStrName resolved.
+ self.oParent = None; # idTestResultParent within the tree.
+
+ self.aoChildren = []; # TestResultDataEx;
+ self.aoValues = []; # TestResultValueDataEx;
+ self.aoMsgs = []; # TestResultMsgDataEx;
+ self.aoFiles = []; # TestResultFileDataEx;
+ self.oReason = None; # TestResultReasonDataEx;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Initialize from a query like this:
+ SELECT TestResults.*, TestResultStrTab.sValue
+ FROM TestResults, TestResultStrTab
+ WHERE TestResultStrTab.idStr = TestResults.idStrName
+
+ Note! The caller is expected to fetch children, values, failure
+ details, and files.
+ """
+ self.sName = None;
+ self.oParent = None;
+ self.aoChildren = [];
+ self.aoValues = [];
+ self.aoMsgs = [];
+ self.aoFiles = [];
+ self.oReason = None;
+
+ TestResultData.initFromDbRow(self, aoRow);
+
+ self.sName = aoRow[9];
+ return self;
+
+ def deepCountErrorContributers(self):
+ """
+ Counts how many test result instances actually contributed to cErrors.
+ """
+
+ # Check each child (if any).
+ cChanges = 0;
+ cChildErrors = 0;
+ for oChild in self.aoChildren:
+ if oChild.cErrors > 0:
+ cChildErrors += oChild.cErrors;
+ cChanges += oChild.deepCountErrorContributers();
+
+ # Did we contribute as well?
+ if self.cErrors > cChildErrors:
+ cChanges += 1;
+ return cChanges;
+
+ def getListOfFailures(self):
+ """
+ Get a list of test results instances actually contributing to cErrors.
+
+ Returns a list of TestResultDataEx instances from this tree. (shared!)
+ """
+ # Check each child (if any).
+ aoRet = [];
+ cChildErrors = 0;
+ for oChild in self.aoChildren:
+ if oChild.cErrors > 0:
+ cChildErrors += oChild.cErrors;
+ aoRet.extend(oChild.getListOfFailures());
+
+ # Did we contribute as well?
+ if self.cErrors > cChildErrors:
+ aoRet.append(self);
+
+ return aoRet;
+
+ def getListOfLogFilesByKind(self, asKinds):
+ """
+ Get a list of test results instances actually contributing to cErrors.
+
+ Returns a list of TestResultFileDataEx instances from this tree. (shared!)
+ """
+ aoRet = [];
+
+ # Check the children first.
+ for oChild in self.aoChildren:
+ aoRet.extend(oChild.getListOfLogFilesByKind(asKinds));
+
+ # Check our own files next.
+ for oFile in self.aoFiles:
+ if oFile.sKind in asKinds:
+ aoRet.append(oFile);
+
+ return aoRet;
+
+ def getFullName(self):
+ """ Constructs the full name of this test result. """
+ if self.oParent is None:
+ return self.sName;
+ return self.oParent.getFullName() + ' / ' + self.sName;
+
+
+
+class TestResultValueData(ModelDataBase):
+ """
+ Test result value data.
+ """
+
+ ksIdAttr = 'idTestResultValue';
+
+ ksParam_idTestResultValue = 'TestResultValue_idTestResultValue';
+ ksParam_idTestResult = 'TestResultValue_idTestResult';
+ ksParam_idTestSet = 'TestResultValue_idTestSet';
+ ksParam_tsCreated = 'TestResultValue_tsCreated';
+ ksParam_idStrName = 'TestResultValue_idStrName';
+ ksParam_lValue = 'TestResultValue_lValue';
+ ksParam_iUnit = 'TestResultValue_iUnit';
+
+ kasAllowNullAttributes = [ 'idTestSet', ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+ self.idTestResultValue = None;
+ self.idTestResult = None;
+ self.idTestSet = None;
+ self.tsCreated = None;
+ self.idStrName = None;
+ self.lValue = None;
+ self.iUnit = 0;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestResultValues.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result value record not found.')
+
+ self.idTestResultValue = aoRow[0];
+ self.idTestResult = aoRow[1];
+ self.idTestSet = aoRow[2];
+ self.tsCreated = aoRow[3];
+ self.idStrName = aoRow[4];
+ self.lValue = aoRow[5];
+ self.iUnit = aoRow[6];
+ return self;
+
+
+class TestResultValueDataEx(TestResultValueData):
+ """
+ Extends TestResultValue by resolving the value name and unit string.
+ """
+
+ def __init__(self):
+ TestResultValueData.__init__(self)
+ self.sName = None;
+ self.sUnit = '';
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a query like this:
+ SELECT TestResultValues.*, TestResultStrTab.sValue
+ FROM TestResultValues, TestResultStrTab
+ WHERE TestResultStrTab.idStr = TestResultValues.idStrName
+
+ Return self. Raises exception if no row.
+ """
+ TestResultValueData.initFromDbRow(self, aoRow);
+ self.sName = aoRow[7];
+ if self.iUnit < len(constants.valueunit.g_asNames):
+ self.sUnit = constants.valueunit.g_asNames[self.iUnit];
+ else:
+ self.sUnit = '<%d>' % (self.iUnit,);
+ return self;
+
+class TestResultMsgData(ModelDataBase):
+ """
+ Test result message data.
+ """
+
+ ksIdAttr = 'idTestResultMsg';
+
+ ksParam_idTestResultMsg = 'TestResultValue_idTestResultMsg';
+ ksParam_idTestResult = 'TestResultValue_idTestResult';
+ ksParam_idTestSet = 'TestResultValue_idTestSet';
+ ksParam_tsCreated = 'TestResultValue_tsCreated';
+ ksParam_idStrMsg = 'TestResultValue_idStrMsg';
+ ksParam_enmLevel = 'TestResultValue_enmLevel';
+
+ kasAllowNullAttributes = [ 'idTestSet', ];
+
+ kcDbColumns = 6
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+ self.idTestResultMsg = None;
+ self.idTestResult = None;
+ self.idTestSet = None;
+ self.tsCreated = None;
+ self.idStrMsg = None;
+ self.enmLevel = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestResultMsgs.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result value record not found.')
+
+ self.idTestResultMsg = aoRow[0];
+ self.idTestResult = aoRow[1];
+ self.idTestSet = aoRow[2];
+ self.tsCreated = aoRow[3];
+ self.idStrMsg = aoRow[4];
+ self.enmLevel = aoRow[5];
+ return self;
+
+class TestResultMsgDataEx(TestResultMsgData):
+ """
+ Extends TestResultMsg by resolving the message string.
+ """
+
+ def __init__(self):
+ TestResultMsgData.__init__(self)
+ self.sMsg = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a query like this:
+ SELECT TestResultMsg.*, TestResultStrTab.sValue
+ FROM TestResultMsg, TestResultStrTab
+ WHERE TestResultStrTab.idStr = TestResultMsgs.idStrName
+
+ Return self. Raises exception if no row.
+ """
+ TestResultMsgData.initFromDbRow(self, aoRow);
+ self.sMsg = aoRow[self.kcDbColumns];
+ return self;
+
+
+class TestResultFileData(ModelDataBase):
+ """
+ Test result message data.
+ """
+
+ ksIdAttr = 'idTestResultFile';
+
+ ksParam_idTestResultFile = 'TestResultFile_idTestResultFile';
+ ksParam_idTestResult = 'TestResultFile_idTestResult';
+ ksParam_tsCreated = 'TestResultFile_tsCreated';
+ ksParam_idStrFile = 'TestResultFile_idStrFile';
+ ksParam_idStrDescription = 'TestResultFile_idStrDescription';
+ ksParam_idStrKind = 'TestResultFile_idStrKind';
+ ksParam_idStrMime = 'TestResultFile_idStrMime';
+
+ ## @name Kind of files.
+ ## @{
+ ksKind_LogReleaseVm = 'log/release/vm';
+ ksKind_LogDebugVm = 'log/debug/vm';
+ ksKind_LogReleaseSvc = 'log/release/svc';
+ ksKind_LogDebugSvc = 'log/debug/svc';
+ ksKind_LogReleaseClient = 'log/release/client';
+ ksKind_LogDebugClient = 'log/debug/client';
+ ksKind_LogInstaller = 'log/installer';
+ ksKind_LogUninstaller = 'log/uninstaller';
+ ksKind_LogGuestKernel = 'log/guest/kernel';
+ ksKind_ProcessReportVm = 'process/report/vm';
+ ksKind_CrashReportVm = 'crash/report/vm';
+ ksKind_CrashDumpVm = 'crash/dump/vm';
+ ksKind_CrashReportSvc = 'crash/report/svc';
+ ksKind_CrashDumpSvc = 'crash/dump/svc';
+ ksKind_CrashReportClient = 'crash/report/client';
+ ksKind_CrashDumpClient = 'crash/dump/client';
+ ksKind_InfoCollection = 'info/collection';
+ ksKind_InfoVgaText = 'info/vgatext';
+ ksKind_MiscOther = 'misc/other';
+ ksKind_ScreenshotFailure = 'screenshot/failure';
+ ksKind_ScreenshotSuccesss = 'screenshot/success';
+ ksKind_ScreenRecordingFailure = 'screenrecording/failure';
+ ksKind_ScreenRecordingSuccess = 'screenrecording/success';
+ ## @}
+
+ kasKinds = [
+ ksKind_LogReleaseVm,
+ ksKind_LogDebugVm,
+ ksKind_LogReleaseSvc,
+ ksKind_LogDebugSvc,
+ ksKind_LogReleaseClient,
+ ksKind_LogDebugClient,
+ ksKind_LogInstaller,
+ ksKind_LogUninstaller,
+ ksKind_LogGuestKernel,
+ ksKind_ProcessReportVm,
+ ksKind_CrashReportVm,
+ ksKind_CrashDumpVm,
+ ksKind_CrashReportSvc,
+ ksKind_CrashDumpSvc,
+ ksKind_CrashReportClient,
+ ksKind_CrashDumpClient,
+ ksKind_InfoCollection,
+ ksKind_InfoVgaText,
+ ksKind_MiscOther,
+ ksKind_ScreenshotFailure,
+ ksKind_ScreenshotSuccesss,
+ ksKind_ScreenRecordingFailure,
+ ksKind_ScreenRecordingSuccess,
+ ];
+
+ kasAllowNullAttributes = [ 'idTestSet', ];
+
+ kcDbColumns = 8
+
+ def __init__(self):
+ ModelDataBase.__init__(self)
+ self.idTestResultFile = None;
+ self.idTestResult = None;
+ self.idTestSet = None;
+ self.tsCreated = None;
+ self.idStrFile = None;
+ self.idStrDescription = None;
+ self.idStrKind = None;
+ self.idStrMime = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a SELECT * FROM TestResultFiles.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result file record not found.')
+
+ self.idTestResultFile = aoRow[0];
+ self.idTestResult = aoRow[1];
+ self.idTestSet = aoRow[2];
+ self.tsCreated = aoRow[3];
+ self.idStrFile = aoRow[4];
+ self.idStrDescription = aoRow[5];
+ self.idStrKind = aoRow[6];
+ self.idStrMime = aoRow[7];
+ return self;
+
+class TestResultFileDataEx(TestResultFileData):
+ """
+ Extends TestResultFile by resolving the strings.
+ """
+
+ def __init__(self):
+ TestResultFileData.__init__(self)
+ self.sFile = None;
+ self.sDescription = None;
+ self.sKind = None;
+ self.sMime = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Reinitialize from a query like this:
+ SELECT TestResultFiles.*,
+ StrTabFile.sValue AS sFile,
+ StrTabDesc.sValue AS sDescription
+ StrTabKind.sValue AS sKind,
+ StrTabMime.sValue AS sMime,
+ FROM ...
+
+ Return self. Raises exception if no row.
+ """
+ TestResultFileData.initFromDbRow(self, aoRow);
+ self.sFile = aoRow[self.kcDbColumns];
+ self.sDescription = aoRow[self.kcDbColumns + 1];
+ self.sKind = aoRow[self.kcDbColumns + 2];
+ self.sMime = aoRow[self.kcDbColumns + 3];
+ return self;
+
+ def initFakeMainLog(self, oTestSet):
+ """
+ Reinitializes to represent the main.log object (not in DB).
+
+ Returns self.
+ """
+ self.idTestResultFile = 0;
+ self.idTestResult = oTestSet.idTestResult;
+ self.tsCreated = oTestSet.tsCreated;
+ self.idStrFile = None;
+ self.idStrDescription = None;
+ self.idStrKind = None;
+ self.idStrMime = None;
+
+ self.sFile = 'main.log';
+ self.sDescription = '';
+ self.sKind = 'log/main';
+ self.sMime = 'text/plain';
+ return self;
+
+ def isProbablyUtf8Encoded(self):
+ """
+ Checks if the file is likely to be UTF-8 encoded.
+ """
+ if self.sMime in [ 'text/plain', 'text/html' ]:
+ return True;
+ return False;
+
+ def getMimeWithEncoding(self):
+ """
+ Gets the MIME type with encoding if likely to be UTF-8.
+ """
+ if self.isProbablyUtf8Encoded():
+ return '%s; charset=utf-8' % (self.sMime,);
+ return self.sMime;
+
+
+
+class TestResultListingData(ModelDataBase): # pylint: disable=too-many-instance-attributes
+ """
+ Test case result data representation for table listing
+ """
+
+ class FailureReasonListingData(object):
+ """ Failure reason listing data """
+ def __init__(self):
+ self.oFailureReason = None;
+ self.oFailureReasonAssigner = None;
+ self.tsFailureReasonAssigned = None;
+ self.sFailureReasonComment = None;
+
+ def __init__(self):
+ """Initialize"""
+ ModelDataBase.__init__(self)
+
+ self.idTestSet = None
+
+ self.idBuildCategory = None;
+ self.sProduct = None
+ self.sRepository = None;
+ self.sBranch = None
+ self.sType = None
+ self.idBuild = None;
+ self.sVersion = None;
+ self.iRevision = None
+
+ self.sOs = None;
+ self.sOsVersion = None;
+ self.sArch = None;
+ self.sCpuVendor = None;
+ self.sCpuName = None;
+ self.cCpus = None;
+ self.fCpuHwVirt = None;
+ self.fCpuNestedPaging = None;
+ self.fCpu64BitGuest = None;
+ self.idTestBox = None
+ self.sTestBoxName = None
+
+ self.tsCreated = None
+ self.tsElapsed = None
+ self.enmStatus = None
+ self.cErrors = None;
+
+ self.idTestCase = None
+ self.sTestCaseName = None
+ self.sBaseCmd = None
+ self.sArgs = None
+ self.sSubName = None;
+
+ self.idBuildTestSuite = None;
+ self.iRevisionTestSuite = None;
+
+ self.aoFailureReasons = [];
+
+ def initFromDbRowEx(self, aoRow, oFailureReasonLogic, oUserAccountLogic):
+ """
+ Reinitialize from a database query.
+ Return self. Raises exception if no row.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('Test result record not found.')
+
+ self.idTestSet = aoRow[0];
+
+ self.idBuildCategory = aoRow[1];
+ self.sProduct = aoRow[2];
+ self.sRepository = aoRow[3];
+ self.sBranch = aoRow[4];
+ self.sType = aoRow[5];
+ self.idBuild = aoRow[6];
+ self.sVersion = aoRow[7];
+ self.iRevision = aoRow[8];
+
+ self.sOs = aoRow[9];
+ self.sOsVersion = aoRow[10];
+ self.sArch = aoRow[11];
+ self.sCpuVendor = aoRow[12];
+ self.sCpuName = aoRow[13];
+ self.cCpus = aoRow[14];
+ self.fCpuHwVirt = aoRow[15];
+ self.fCpuNestedPaging = aoRow[16];
+ self.fCpu64BitGuest = aoRow[17];
+ self.idTestBox = aoRow[18];
+ self.sTestBoxName = aoRow[19];
+
+ self.tsCreated = aoRow[20];
+ self.tsElapsed = aoRow[21];
+ self.enmStatus = aoRow[22];
+ self.cErrors = aoRow[23];
+
+ self.idTestCase = aoRow[24];
+ self.sTestCaseName = aoRow[25];
+ self.sBaseCmd = aoRow[26];
+ self.sArgs = aoRow[27];
+ self.sSubName = aoRow[28];
+
+ self.idBuildTestSuite = aoRow[29];
+ self.iRevisionTestSuite = aoRow[30];
+
+ self.aoFailureReasons = [];
+ for i, _ in enumerate(aoRow[31]):
+ if aoRow[31][i] is not None \
+ or aoRow[32][i] is not None \
+ or aoRow[33][i] is not None \
+ or aoRow[34][i] is not None:
+ oReason = self.FailureReasonListingData();
+ if aoRow[31][i] is not None:
+ oReason.oFailureReason = oFailureReasonLogic.cachedLookup(aoRow[31][i]);
+ if aoRow[32][i] is not None:
+ oReason.oFailureReasonAssigner = oUserAccountLogic.cachedLookup(aoRow[32][i]);
+ oReason.tsFailureReasonAssigned = aoRow[33][i];
+ oReason.sFailureReasonComment = aoRow[34][i];
+ self.aoFailureReasons.append(oReason);
+
+ return self
+
+
+class TestResultHangingOffence(TMExceptionBase):
+ """Hanging offence committed by test case."""
+ pass; # pylint: disable=unnecessary-pass
+
+
+class TestResultFilter(ModelFilterBase):
+ """
+ Test result filter.
+ """
+
+ kiTestStatus = 0;
+ kiErrorCounts = 1;
+ kiBranches = 2;
+ kiBuildTypes = 3;
+ kiRevisions = 4;
+ kiRevisionRange = 5;
+ kiFailReasons = 6;
+ kiTestCases = 7;
+ kiTestCaseMisc = 8;
+ kiTestBoxes = 9;
+ kiOses = 10;
+ kiCpuArches = 11;
+ kiCpuVendors = 12;
+ kiCpuCounts = 13;
+ kiMemory = 14;
+ kiTestboxMisc = 15;
+ kiPythonVersions = 16;
+ kiSchedGroups = 17;
+
+ ## Misc test case / variation name filters.
+ ## Presented in table order. The first sub element is the presistent ID.
+ kaTcMisc = (
+ ( 1, 'x86', ),
+ ( 2, 'amd64', ),
+ ( 3, 'uni', ),
+ ( 4, 'smp', ),
+ ( 5, 'raw', ),
+ ( 6, 'hw', ),
+ ( 7, 'np', ),
+ ( 8, 'Install', ),
+ ( 20, 'UInstall', ), # NB. out of order.
+ ( 9, 'Benchmark', ),
+ ( 18, 'smoke', ), # NB. out of order.
+ ( 19, 'unit', ), # NB. out of order.
+ ( 10, 'USB', ),
+ ( 11, 'Debian', ),
+ ( 12, 'Fedora', ),
+ ( 13, 'Oracle', ),
+ ( 14, 'RHEL', ),
+ ( 15, 'SUSE', ),
+ ( 16, 'Ubuntu', ),
+ ( 17, 'Win', ),
+ );
+
+ kiTbMisc_NestedPaging = 0;
+ kiTbMisc_NoNestedPaging = 1;
+ kiTbMisc_RawMode = 2;
+ kiTbMisc_NoRawMode = 3;
+ kiTbMisc_64BitGuest = 4;
+ kiTbMisc_No64BitGuest = 5;
+ kiTbMisc_HwVirt = 6;
+ kiTbMisc_NoHwVirt = 7;
+ kiTbMisc_IoMmu = 8;
+ kiTbMisc_NoIoMmu = 9;
+
+ def __init__(self):
+ ModelFilterBase.__init__(self);
+
+ # Test statuses
+ oCrit = FilterCriterion('Test statuses', sVarNm = 'ts', sType = FilterCriterion.ksType_String,
+ sTable = 'TestSets', sColumn = 'enmStatus');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiTestStatus] is oCrit;
+
+ # Error counts
+ oCrit = FilterCriterion('Error counts', sVarNm = 'ec', sTable = 'TestResults', sColumn = 'cErrors');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiErrorCounts] is oCrit;
+
+ # Branches
+ oCrit = FilterCriterion('Branches', sVarNm = 'br', sType = FilterCriterion.ksType_String,
+ sTable = 'BuildCategories', sColumn = 'sBranch');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiBranches] is oCrit;
+
+ # Build types
+ oCrit = FilterCriterion('Build types', sVarNm = 'bt', sType = FilterCriterion.ksType_String,
+ sTable = 'BuildCategories', sColumn = 'sType');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiBuildTypes] is oCrit;
+
+ # Revisions
+ oCrit = FilterCriterion('Revisions', sVarNm = 'rv', sTable = 'Builds', sColumn = 'iRevision');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiRevisions] is oCrit;
+
+ # Revision Range
+ oCrit = FilterCriterion('Revision Range', sVarNm = 'rr', sType = FilterCriterion.ksType_Ranges,
+ sKind = FilterCriterion.ksKind_ElementOfOrNot, sTable = 'Builds', sColumn = 'iRevision');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiRevisionRange] is oCrit;
+
+ # Failure reasons
+ oCrit = FilterCriterion('Failure reasons', sVarNm = 'fr', sType = FilterCriterion.ksType_UIntNil,
+ sTable = 'TestResultFailures', sColumn = 'idFailureReason');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiFailReasons] is oCrit;
+
+ # Test cases and variations.
+ oCrit = FilterCriterion('Test case / var', sVarNm = 'tc', sTable = 'TestSets', sColumn = 'idTestCase',
+ oSub = FilterCriterion('Test variations', sVarNm = 'tv',
+ sTable = 'TestSets', sColumn = 'idTestCaseArgs'));
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiTestCases] is oCrit;
+
+ # Special test case and varation name sub string matching.
+ oCrit = FilterCriterion('Test case name', sVarNm = 'cm', sKind = FilterCriterion.ksKind_Special,
+ asTables = ('TestCases', 'TestCaseArgs'));
+ oCrit.aoPossible = [
+ FilterCriterionValueAndDescription(aoCur[0], 'Include %s' % (aoCur[1],)) for aoCur in self.kaTcMisc
+ ];
+ oCrit.aoPossible.extend([
+ FilterCriterionValueAndDescription(aoCur[0] + 32, 'Exclude %s' % (aoCur[1],)) for aoCur in self.kaTcMisc
+ ]);
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiTestCaseMisc] is oCrit;
+
+ # Testboxes
+ oCrit = FilterCriterion('Testboxes', sVarNm = 'tb', sTable = 'TestSets', sColumn = 'idTestBox');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiTestBoxes] is oCrit;
+
+ # Testbox OS and OS version.
+ oCrit = FilterCriterion('OS / version', sVarNm = 'os', sTable = 'TestBoxesWithStrings', sColumn = 'idStrOs',
+ oSub = FilterCriterion('OS Versions', sVarNm = 'ov',
+ sTable = 'TestBoxesWithStrings', sColumn = 'idStrOsVersion'));
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiOses] is oCrit;
+
+ # Testbox CPU architectures.
+ oCrit = FilterCriterion('CPU arches', sVarNm = 'ca', sTable = 'TestBoxesWithStrings', sColumn = 'idStrCpuArch');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiCpuArches] is oCrit;
+
+ # Testbox CPU vendors and revisions.
+ oCrit = FilterCriterion('CPU vendor / rev', sVarNm = 'cv', sTable = 'TestBoxesWithStrings', sColumn = 'idStrCpuVendor',
+ oSub = FilterCriterion('CPU revisions', sVarNm = 'cr',
+ sTable = 'TestBoxesWithStrings', sColumn = 'lCpuRevision'));
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiCpuVendors] is oCrit;
+
+ # Testbox CPU (thread) count
+ oCrit = FilterCriterion('CPU counts', sVarNm = 'cc', sTable = 'TestBoxesWithStrings', sColumn = 'cCpus');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiCpuCounts] is oCrit;
+
+ # Testbox memory sizes.
+ oCrit = FilterCriterion('Memory', sVarNm = 'mb', sTable = 'TestBoxesWithStrings', sColumn = 'cMbMemory');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiMemory] is oCrit;
+
+ # Testbox features.
+ oCrit = FilterCriterion('Testbox features', sVarNm = 'tm', sKind = FilterCriterion.ksKind_Special,
+ sTable = 'TestBoxesWithStrings');
+ oCrit.aoPossible = [
+ FilterCriterionValueAndDescription(self.kiTbMisc_NestedPaging, "req nested paging"),
+ FilterCriterionValueAndDescription(self.kiTbMisc_NoNestedPaging, "w/o nested paging"),
+ #FilterCriterionValueAndDescription(self.kiTbMisc_RawMode, "req raw-mode"), - not implemented yet.
+ #FilterCriterionValueAndDescription(self.kiTbMisc_NoRawMode, "w/o raw-mode"), - not implemented yet.
+ FilterCriterionValueAndDescription(self.kiTbMisc_64BitGuest, "req 64-bit guests"),
+ FilterCriterionValueAndDescription(self.kiTbMisc_No64BitGuest, "w/o 64-bit guests"),
+ FilterCriterionValueAndDescription(self.kiTbMisc_HwVirt, "req VT-x / AMD-V"),
+ FilterCriterionValueAndDescription(self.kiTbMisc_NoHwVirt, "w/o VT-x / AMD-V"),
+ #FilterCriterionValueAndDescription(self.kiTbMisc_IoMmu, "req I/O MMU"), - not implemented yet.
+ #FilterCriterionValueAndDescription(self.kiTbMisc_NoIoMmu, "w/o I/O MMU"), - not implemented yet.
+ ];
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiTestboxMisc] is oCrit;
+
+ # Testbox python versions.
+ oCrit = FilterCriterion('Python', sVarNm = 'py', sTable = 'TestBoxesWithStrings', sColumn = 'iPythonHexVersion');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiPythonVersions] is oCrit;
+
+ # Scheduling groups.
+ oCrit = FilterCriterion('Sched groups', sVarNm = 'sg', sTable = 'TestSets', sColumn = 'idSchedGroup');
+ self.aCriteria.append(oCrit);
+ assert self.aCriteria[self.kiSchedGroups] is oCrit;
+
+
+ kdTbMiscConditions = {
+ kiTbMisc_NestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging IS TRUE',
+ kiTbMisc_NoNestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging IS FALSE',
+ kiTbMisc_RawMode: 'TestBoxesWithStrings.fRawMode IS TRUE',
+ kiTbMisc_NoRawMode: 'TestBoxesWithStrings.fRawMode IS NOT TRUE',
+ kiTbMisc_64BitGuest: 'TestBoxesWithStrings.fCpu64BitGuest IS TRUE',
+ kiTbMisc_No64BitGuest: 'TestBoxesWithStrings.fCpu64BitGuest IS FALSE',
+ kiTbMisc_HwVirt: 'TestBoxesWithStrings.fCpuHwVirt IS TRUE',
+ kiTbMisc_NoHwVirt: 'TestBoxesWithStrings.fCpuHwVirt IS FALSE',
+ kiTbMisc_IoMmu: 'TestBoxesWithStrings.fChipsetIoMmu IS TRUE',
+ kiTbMisc_NoIoMmu: 'TestBoxesWithStrings.fChipsetIoMmu IS FALSE',
+ };
+
+ def _getWhereWorker(self, iCrit, oCrit, sExtraIndent, iOmit):
+ """ Formats one - main or sub. """
+ sQuery = '';
+ if oCrit.sState == FilterCriterion.ksState_Selected and iCrit != iOmit:
+ if iCrit == self.kiTestCaseMisc:
+ for iValue, sLike in self.kaTcMisc:
+ if iValue in oCrit.aoSelected: sNot = '';
+ elif iValue + 32 in oCrit.aoSelected: sNot = 'NOT ';
+ else: continue;
+ sQuery += '%s AND %s (' % (sExtraIndent, sNot,);
+ if len(sLike) <= 3: # do word matching for small substrings (hw, np, smp, uni, ++).
+ sQuery += 'TestCases.sName ~ \'.*\\y%s\\y.*\' ' \
+ 'OR COALESCE(TestCaseArgs.sSubName, \'\') ~ \'.*\\y%s\\y.*\')\n' \
+ % ( sLike, sLike,);
+ else:
+ sQuery += 'TestCases.sName LIKE \'%%%s%%\' ' \
+ 'OR COALESCE(TestCaseArgs.sSubName, \'\') LIKE \'%%%s%%\')\n' \
+ % ( sLike, sLike,);
+ elif iCrit == self.kiTestboxMisc:
+ dConditions = self.kdTbMiscConditions;
+ for iValue in oCrit.aoSelected:
+ if iValue in dConditions:
+ sQuery += '%s AND %s\n' % (sExtraIndent, dConditions[iValue],);
+ elif oCrit.sType == FilterCriterion.ksType_Ranges:
+ assert not oCrit.aoPossible;
+ if oCrit.aoSelected:
+ asConditions = [];
+ for tRange in oCrit.aoSelected:
+ if tRange[0] == tRange[1]:
+ asConditions.append('%s.%s = %s' % (oCrit.asTables[0], oCrit.sColumn, tRange[0]));
+ elif tRange[1] is None: # 9999-
+ asConditions.append('%s.%s >= %s' % (oCrit.asTables[0], oCrit.sColumn, tRange[0]));
+ elif tRange[0] is None: # -9999
+ asConditions.append('%s.%s <= %s' % (oCrit.asTables[0], oCrit.sColumn, tRange[1]));
+ else:
+ asConditions.append('%s.%s BETWEEN %s AND %s' % (oCrit.asTables[0], oCrit.sColumn,
+ tRange[0], tRange[1]));
+ if not oCrit.fInverted:
+ sQuery += '%s AND (%s)\n' % (sExtraIndent, ' OR '.join(asConditions));
+ else:
+ sQuery += '%s AND NOT (%s)\n' % (sExtraIndent, ' OR '.join(asConditions));
+ else:
+ assert len(oCrit.asTables) == 1;
+ sQuery += '%s AND (' % (sExtraIndent,);
+
+ if oCrit.sType != FilterCriterion.ksType_UIntNil or max(oCrit.aoSelected) != -1:
+ if iCrit == self.kiMemory:
+ sQuery += '(%s.%s / 1024)' % (oCrit.asTables[0], oCrit.sColumn,);
+ else:
+ sQuery += '%s.%s' % (oCrit.asTables[0], oCrit.sColumn,);
+ if not oCrit.fInverted:
+ sQuery += ' IN (';
+ else:
+ sQuery += ' NOT IN (';
+ if oCrit.sType == FilterCriterion.ksType_String:
+ sQuery += ', '.join('\'%s\'' % (sValue,) for sValue in oCrit.aoSelected) + ')';
+ else:
+ sQuery += ', '.join(str(iValue) for iValue in oCrit.aoSelected if iValue != -1) + ')';
+
+ if oCrit.sType == FilterCriterion.ksType_UIntNil \
+ and -1 in oCrit.aoSelected:
+ if sQuery[-1] != '(': sQuery += ' OR ';
+ sQuery += '%s.%s IS NULL' % (oCrit.asTables[0], oCrit.sColumn,);
+
+ if iCrit == self.kiFailReasons:
+ if oCrit.fInverted:
+ sQuery += '%s OR TestResultFailures.idFailureReason IS NULL\n' % (sExtraIndent,);
+ else:
+ sQuery += '%s AND TestSets.enmStatus >= \'failure\'::TestStatus_T\n' % (sExtraIndent,);
+ sQuery += ')\n';
+ if oCrit.oSub is not None:
+ sQuery += self._getWhereWorker(iCrit | (((iCrit >> 8) + 1) << 8), oCrit.oSub, sExtraIndent, iOmit);
+ return sQuery;
+
+ def getWhereConditions(self, sExtraIndent = '', iOmit = -1):
+ """
+ Construct the WHERE conditions for the filter, optionally omitting one
+ criterion.
+ """
+ sQuery = '';
+ for iCrit, oCrit in enumerate(self.aCriteria):
+ sQuery += self._getWhereWorker(iCrit, oCrit, sExtraIndent, iOmit);
+ return sQuery;
+
+ def getTableJoins(self, sExtraIndent = '', iOmit = -1, dOmitTables = None):
+ """
+ Construct the WHERE conditions for the filter, optionally omitting one
+ criterion.
+ """
+ afDone = { 'TestSets': True, };
+ if dOmitTables is not None:
+ afDone.update(dOmitTables);
+
+ sQuery = '';
+ for iCrit, oCrit in enumerate(self.aCriteria):
+ if oCrit.sState == FilterCriterion.ksState_Selected \
+ and iCrit != iOmit:
+ for sTable in oCrit.asTables:
+ if sTable not in afDone:
+ afDone[sTable] = True;
+ if sTable == 'Builds':
+ sQuery += '%sINNER JOIN Builds\n' \
+ '%s ON Builds.idBuild = TestSets.idBuild\n' \
+ '%s AND Builds.tsExpire > TestSets.tsCreated\n' \
+ '%s AND Builds.tsEffective <= TestSets.tsCreated\n' \
+ % ( sExtraIndent, sExtraIndent, sExtraIndent, sExtraIndent, );
+ elif sTable == 'BuildCategories':
+ sQuery += '%sINNER JOIN BuildCategories\n' \
+ '%s ON BuildCategories.idBuildCategory = TestSets.idBuildCategory\n' \
+ % ( sExtraIndent, sExtraIndent, );
+ elif sTable == 'TestBoxesWithStrings':
+ sQuery += '%sLEFT OUTER JOIN TestBoxesWithStrings\n' \
+ '%s ON TestBoxesWithStrings.idGenTestBox = TestSets.idGenTestBox\n' \
+ % ( sExtraIndent, sExtraIndent, );
+ elif sTable == 'TestCases':
+ sQuery += '%sINNER JOIN TestCases\n' \
+ '%s ON TestCases.idGenTestCase = TestSets.idGenTestCase\n' \
+ % ( sExtraIndent, sExtraIndent, );
+ elif sTable == 'TestCaseArgs':
+ sQuery += '%sINNER JOIN TestCaseArgs\n' \
+ '%s ON TestCaseArgs.idGenTestCaseArgs = TestSets.idGenTestCaseArgs\n' \
+ % ( sExtraIndent, sExtraIndent, );
+ elif sTable == 'TestResults':
+ sQuery += '%sINNER JOIN TestResults\n' \
+ '%s ON TestResults.idTestResult = TestSets.idTestResult\n' \
+ % ( sExtraIndent, sExtraIndent, );
+ elif sTable == 'TestResultFailures':
+ sQuery += '%sLEFT OUTER JOIN TestResultFailures\n' \
+ '%s ON TestResultFailures.idTestSet = TestSets.idTestSet\n' \
+ '%s AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n' \
+ % ( sExtraIndent, sExtraIndent, sExtraIndent, );
+ else:
+ assert False, sTable;
+ return sQuery;
+
+ def isJoiningWithTable(self, sTable):
+ """ Checks whether getTableJoins already joins with TestResultFailures. """
+ for oCrit in self.aCriteria:
+ if oCrit.sState == FilterCriterion.ksState_Selected and sTable in oCrit.asTables:
+ return True;
+ return False
+
+
+
+class TestResultLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ Results grouped by scheduling group.
+ """
+
+ #
+ # Result grinding for displaying in the WUI.
+ #
+
+ ksResultsGroupingTypeNone = 'ResultsGroupingTypeNone';
+ ksResultsGroupingTypeTestGroup = 'ResultsGroupingTypeTestGroup';
+ ksResultsGroupingTypeBuildCat = 'ResultsGroupingTypeBuildCat';
+ ksResultsGroupingTypeBuildRev = 'ResultsGroupingTypeBuildRev';
+ ksResultsGroupingTypeTestBox = 'ResultsGroupingTypeTestBox';
+ ksResultsGroupingTypeTestCase = 'ResultsGroupingTypeTestCase';
+ ksResultsGroupingTypeOS = 'ResultsGroupingTypeOS';
+ ksResultsGroupingTypeArch = 'ResultsGroupingTypeArch';
+ ksResultsGroupingTypeSchedGroup = 'ResultsGroupingTypeSchedGroup';
+
+ ## @name Result sorting options.
+ ## @{
+ ksResultsSortByRunningAndStart = 'ResultsSortByRunningAndStart'; ##< Default
+ ksResultsSortByBuildRevision = 'ResultsSortByBuildRevision';
+ ksResultsSortByTestBoxName = 'ResultsSortByTestBoxName';
+ ksResultsSortByTestBoxOs = 'ResultsSortByTestBoxOs';
+ ksResultsSortByTestBoxOsVersion = 'ResultsSortByTestBoxOsVersion';
+ ksResultsSortByTestBoxOsArch = 'ResultsSortByTestBoxOsArch';
+ ksResultsSortByTestBoxArch = 'ResultsSortByTestBoxArch';
+ ksResultsSortByTestBoxCpuVendor = 'ResultsSortByTestBoxCpuVendor';
+ ksResultsSortByTestBoxCpuName = 'ResultsSortByTestBoxCpuName';
+ ksResultsSortByTestBoxCpuRev = 'ResultsSortByTestBoxCpuRev';
+ ksResultsSortByTestBoxCpuFeatures = 'ResultsSortByTestBoxCpuFeatures';
+ ksResultsSortByTestCaseName = 'ResultsSortByTestCaseName';
+ ksResultsSortByFailureReason = 'ResultsSortByFailureReason';
+ kasResultsSortBy = {
+ ksResultsSortByRunningAndStart,
+ ksResultsSortByBuildRevision,
+ ksResultsSortByTestBoxName,
+ ksResultsSortByTestBoxOs,
+ ksResultsSortByTestBoxOsVersion,
+ ksResultsSortByTestBoxOsArch,
+ ksResultsSortByTestBoxArch,
+ ksResultsSortByTestBoxCpuVendor,
+ ksResultsSortByTestBoxCpuName,
+ ksResultsSortByTestBoxCpuRev,
+ ksResultsSortByTestBoxCpuFeatures,
+ ksResultsSortByTestCaseName,
+ ksResultsSortByFailureReason,
+ };
+ ## Used by the WUI for generating the drop down.
+ kaasResultsSortByTitles = (
+ ( ksResultsSortByRunningAndStart, 'Running & Start TS' ),
+ ( ksResultsSortByBuildRevision, 'Build Revision' ),
+ ( ksResultsSortByTestBoxName, 'TestBox Name' ),
+ ( ksResultsSortByTestBoxOs, 'O/S' ),
+ ( ksResultsSortByTestBoxOsVersion, 'O/S Version' ),
+ ( ksResultsSortByTestBoxOsArch, 'O/S & Architecture' ),
+ ( ksResultsSortByTestBoxArch, 'Architecture' ),
+ ( ksResultsSortByTestBoxCpuVendor, 'CPU Vendor' ),
+ ( ksResultsSortByTestBoxCpuName, 'CPU Vendor & Name' ),
+ ( ksResultsSortByTestBoxCpuRev, 'CPU Vendor & Revision' ),
+ ( ksResultsSortByTestBoxCpuFeatures, 'CPU Features' ),
+ ( ksResultsSortByTestCaseName, 'Test Case Name' ),
+ ( ksResultsSortByFailureReason, 'Failure Reason' ),
+ );
+ ## @}
+
+ ## Default sort by map.
+ kdResultSortByMap = {
+ ksResultsSortByRunningAndStart: ( (), None, None, '', '' ),
+ ksResultsSortByBuildRevision: (
+ # Sorting tables.
+ ('Builds',),
+ # Sorting table join(s).
+ ' AND TestSets.idBuild = Builds.idBuild'
+ ' AND Builds.tsExpire >= TestSets.tsCreated'
+ ' AND Builds.tsEffective <= TestSets.tsCreated',
+ # Start of ORDER BY statement.
+ ' Builds.iRevision DESC',
+ # Extra columns to fetch for the above ORDER BY to work in a SELECT DISTINCT statement.
+ '',
+ # Columns for the GROUP BY
+ ''),
+ ksResultsSortByTestBoxName: (
+ ('TestBoxes',),
+ ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
+ ' TestBoxes.sName DESC',
+ '', '' ),
+ ksResultsSortByTestBoxOsArch: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sCpuArch',
+ '', '' ),
+ ksResultsSortByTestBoxOs: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sOs',
+ '', '' ),
+ ksResultsSortByTestBoxOsVersion: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sOsVersion DESC',
+ '', '' ),
+ ksResultsSortByTestBoxArch: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sCpuArch',
+ '', '' ),
+ ksResultsSortByTestBoxCpuVendor: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sCpuVendor',
+ '', '' ),
+ ksResultsSortByTestBoxCpuName: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.sCpuName',
+ '', '' ),
+ ksResultsSortByTestBoxCpuRev: (
+ ('TestBoxesWithStrings',),
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+ ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.lCpuRevision DESC',
+ ', TestBoxesWithStrings.lCpuRevision',
+ ', TestBoxesWithStrings.lCpuRevision' ),
+ ksResultsSortByTestBoxCpuFeatures: (
+ ('TestBoxes',),
+ ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
+ ' TestBoxes.fCpuHwVirt DESC, TestBoxes.fCpuNestedPaging DESC, TestBoxes.fCpu64BitGuest DESC, TestBoxes.cCpus DESC',
+ '',
+ '' ),
+ ksResultsSortByTestCaseName: (
+ ('TestCases',),
+ ' AND TestSets.idGenTestCase = TestCases.idGenTestCase',
+ ' TestCases.sName',
+ '', '' ),
+ ksResultsSortByFailureReason: (
+ (), '',
+ 'asSortByFailureReason ASC',
+ ', array_agg(FailureReasons.sShort ORDER BY TestResultFailures.idTestResult) AS asSortByFailureReason',
+ '' ),
+ };
+
+ kdResultGroupingMap = {
+ ksResultsGroupingTypeNone: (
+ # Grouping tables;
+ (),
+ # Grouping field;
+ None,
+ # Grouping where addition.
+ None,
+ # Sort by overrides.
+ {},
+ ),
+ ksResultsGroupingTypeTestGroup: ('', 'TestSets.idTestGroup', None, {},),
+ ksResultsGroupingTypeTestBox: ('', 'TestSets.idTestBox', None, {},),
+ ksResultsGroupingTypeTestCase: ('', 'TestSets.idTestCase', None, {},),
+ ksResultsGroupingTypeOS: (
+ ('TestBoxes',),
+ 'TestBoxes.idStrOs',
+ ' AND TestBoxes.idGenTestBox = TestSets.idGenTestBox',
+ {},
+ ),
+ ksResultsGroupingTypeArch: (
+ ('TestBoxes',),
+ 'TestBoxes.idStrCpuArch',
+ ' AND TestBoxes.idGenTestBox = TestSets.idGenTestBox',
+ {},
+ ),
+ ksResultsGroupingTypeBuildCat: ('', 'TestSets.idBuildCategory', None, {},),
+ ksResultsGroupingTypeBuildRev: (
+ ('Builds',),
+ 'Builds.iRevision',
+ ' AND Builds.idBuild = TestSets.idBuild'
+ ' AND Builds.tsExpire > TestSets.tsCreated'
+ ' AND Builds.tsEffective <= TestSets.tsCreated',
+ { ksResultsSortByBuildRevision: ( (), None, ' Builds.iRevision DESC' ), }
+ ),
+ ksResultsGroupingTypeSchedGroup: ( '', 'TestSets.idSchedGroup', None, {},),
+ };
+
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.oFailureReasonLogic = None;
+ self.oUserAccountLogic = None;
+
+ def _getTimePeriodQueryPart(self, tsNow, sInterval, sExtraIndent = ''):
+ """
+ Get part of SQL query responsible for SELECT data within
+ specified period of time.
+ """
+ assert sInterval is not None; # too many rows.
+
+ cMonthsMourningPeriod = 2; # Stop reminding everyone about testboxes after 2 months. (May also speed up the query.)
+ if tsNow is None:
+ sRet = '(TestSets.tsDone IS NULL OR TestSets.tsDone >= (CURRENT_TIMESTAMP - \'%s\'::interval))\n' \
+ '%s AND TestSets.tsCreated >= (CURRENT_TIMESTAMP - \'%s\'::interval - \'%u months\'::interval)\n' \
+ % ( sInterval,
+ sExtraIndent, sInterval, cMonthsMourningPeriod);
+ else:
+ sTsNow = '\'%s\'::TIMESTAMP' % (tsNow,); # It's actually a string already. duh.
+ sRet = 'TestSets.tsCreated <= %s\n' \
+ '%s AND TestSets.tsCreated >= (%s - \'%s\'::interval - \'%u months\'::interval)\n' \
+ '%s AND (TestSets.tsDone IS NULL OR TestSets.tsDone >= (%s - \'%s\'::interval))\n' \
+ % ( sTsNow,
+ sExtraIndent, sTsNow, sInterval, cMonthsMourningPeriod,
+ sExtraIndent, sTsNow, sInterval );
+ return sRet
+
+ def fetchResultsForListing(self, iStart, cMaxRows, tsNow, sInterval, oFilter, enmResultSortBy, # pylint: disable=too-many-arguments
+ enmResultsGroupingType, iResultsGroupingValue, fOnlyFailures, fOnlyNeedingReason):
+ """
+ Fetches TestResults table content.
+
+ If @param enmResultsGroupingType and @param iResultsGroupingValue
+ are not None, then resulting (returned) list contains only records
+ that match specified @param enmResultsGroupingType.
+
+ If @param enmResultsGroupingType is None, then
+ @param iResultsGroupingValue is ignored.
+
+ Returns an array (list) of TestResultData items, empty list if none.
+ Raises exception on error.
+ """
+
+ _ = oFilter;
+
+ #
+ # Get SQL query parameters
+ #
+ if enmResultsGroupingType is None or enmResultsGroupingType not in self.kdResultGroupingMap:
+ raise TMExceptionBase('Unknown grouping type');
+ if enmResultSortBy is None or enmResultSortBy not in self.kasResultsSortBy:
+ raise TMExceptionBase('Unknown sorting');
+ asGroupingTables, sGroupingField, sGroupingCondition, dSortOverrides = self.kdResultGroupingMap[enmResultsGroupingType];
+ if enmResultSortBy in dSortOverrides:
+ asSortTables, sSortWhere, sSortOrderBy, sSortColumns, sSortGroupBy = dSortOverrides[enmResultSortBy];
+ else:
+ asSortTables, sSortWhere, sSortOrderBy, sSortColumns, sSortGroupBy = self.kdResultSortByMap[enmResultSortBy];
+
+ #
+ # Construct the query.
+ #
+ sQuery = 'SELECT DISTINCT TestSets.idTestSet,\n' \
+ ' BuildCategories.idBuildCategory,\n' \
+ ' BuildCategories.sProduct,\n' \
+ ' BuildCategories.sRepository,\n' \
+ ' BuildCategories.sBranch,\n' \
+ ' BuildCategories.sType,\n' \
+ ' Builds.idBuild,\n' \
+ ' Builds.sVersion,\n' \
+ ' Builds.iRevision,\n' \
+ ' TestBoxesWithStrings.sOs,\n' \
+ ' TestBoxesWithStrings.sOsVersion,\n' \
+ ' TestBoxesWithStrings.sCpuArch,\n' \
+ ' TestBoxesWithStrings.sCpuVendor,\n' \
+ ' TestBoxesWithStrings.sCpuName,\n' \
+ ' TestBoxesWithStrings.cCpus,\n' \
+ ' TestBoxesWithStrings.fCpuHwVirt,\n' \
+ ' TestBoxesWithStrings.fCpuNestedPaging,\n' \
+ ' TestBoxesWithStrings.fCpu64BitGuest,\n' \
+ ' TestBoxesWithStrings.idTestBox,\n' \
+ ' TestBoxesWithStrings.sName,\n' \
+ ' TestResults.tsCreated,\n' \
+ ' COALESCE(TestResults.tsElapsed, CURRENT_TIMESTAMP - TestResults.tsCreated) AS tsElapsedTestResult,\n' \
+ ' TestSets.enmStatus,\n' \
+ ' TestResults.cErrors,\n' \
+ ' TestCases.idTestCase,\n' \
+ ' TestCases.sName,\n' \
+ ' TestCases.sBaseCmd,\n' \
+ ' TestCaseArgs.sArgs,\n' \
+ ' TestCaseArgs.sSubName,\n' \
+ ' TestSuiteBits.idBuild AS idBuildTestSuite,\n' \
+ ' TestSuiteBits.iRevision AS iRevisionTestSuite,\n' \
+ ' array_agg(TestResultFailures.idFailureReason ORDER BY TestResultFailures.idTestResult),\n' \
+ ' array_agg(TestResultFailures.uidAuthor ORDER BY TestResultFailures.idTestResult),\n' \
+ ' array_agg(TestResultFailures.tsEffective ORDER BY TestResultFailures.idTestResult),\n' \
+ ' array_agg(TestResultFailures.sComment ORDER BY TestResultFailures.idTestResult),\n' \
+ ' (TestSets.tsDone IS NULL) SortRunningFirst' + sSortColumns + '\n' \
+ 'FROM ( SELECT TestSets.idTestSet AS idTestSet,\n' \
+ ' TestSets.tsDone AS tsDone,\n' \
+ ' TestSets.tsCreated AS tsCreated,\n' \
+ ' TestSets.enmStatus AS enmStatus,\n' \
+ ' TestSets.idBuild AS idBuild,\n' \
+ ' TestSets.idBuildTestSuite AS idBuildTestSuite,\n' \
+ ' TestSets.idGenTestBox AS idGenTestBox,\n' \
+ ' TestSets.idGenTestCase AS idGenTestCase,\n' \
+ ' TestSets.idGenTestCaseArgs AS idGenTestCaseArgs\n' \
+ ' FROM TestSets\n';
+ sQuery += oFilter.getTableJoins(' ');
+ if fOnlyNeedingReason and not oFilter.isJoiningWithTable('TestResultFailures'):
+ sQuery += '\n' \
+ ' LEFT OUTER JOIN TestResultFailures\n' \
+ ' ON TestSets.idTestSet = TestResultFailures.idTestSet\n' \
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP';
+ for asTables in [asGroupingTables, asSortTables]:
+ for sTable in asTables:
+ if not oFilter.isJoiningWithTable(sTable):
+ sQuery = sQuery[:-1] + ',\n ' + sTable + '\n';
+
+ sQuery += ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sInterval, ' ') + \
+ oFilter.getWhereConditions(' ');
+ if fOnlyFailures or fOnlyNeedingReason:
+ sQuery += ' AND TestSets.enmStatus != \'success\'::TestStatus_T\n' \
+ ' AND TestSets.enmStatus != \'running\'::TestStatus_T\n';
+ if fOnlyNeedingReason:
+ sQuery += ' AND TestResultFailures.idTestSet IS NULL\n';
+ if sGroupingField is not None:
+ sQuery += ' AND %s = %d\n' % (sGroupingField, iResultsGroupingValue,);
+ if sGroupingCondition is not None:
+ sQuery += sGroupingCondition.replace(' AND ', ' AND ');
+ if sSortWhere is not None:
+ sQuery += sSortWhere.replace(' AND ', ' AND ');
+ sQuery += ' ORDER BY ';
+ if sSortOrderBy is not None and sSortOrderBy.find('FailureReason') < 0:
+ sQuery += sSortOrderBy + ',\n ';
+ sQuery += '(TestSets.tsDone IS NULL) DESC, TestSets.idTestSet DESC\n' \
+ ' LIMIT %s OFFSET %s\n' % (cMaxRows, iStart,);
+
+ # Note! INNER JOIN TestBoxesWithStrings performs miserable compared to LEFT OUTER JOIN. Doesn't matter for the result
+ # because TestSets.idGenTestBox is a foreign key and unique in TestBoxes. So, let's do what ever is faster.
+ sQuery += ' ) AS TestSets\n' \
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n' \
+ ' ON TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox' \
+ ' LEFT OUTER JOIN Builds AS TestSuiteBits\n' \
+ ' ON TestSuiteBits.idBuild = TestSets.idBuildTestSuite\n' \
+ ' AND TestSuiteBits.tsExpire > TestSets.tsCreated\n' \
+ ' AND TestSuiteBits.tsEffective <= TestSets.tsCreated\n' \
+ ' LEFT OUTER JOIN TestResultFailures\n' \
+ ' ON TestSets.idTestSet = TestResultFailures.idTestSet\n' \
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP';
+ if sSortOrderBy is not None and sSortOrderBy.find('FailureReason') >= 0:
+ sQuery += '\n' \
+ ' LEFT OUTER JOIN FailureReasons\n' \
+ ' ON TestResultFailures.idFailureReason = FailureReasons.idFailureReason\n' \
+ ' AND FailureReasons.tsExpire = \'infinity\'::TIMESTAMP';
+ sQuery += ',\n' \
+ ' BuildCategories,\n' \
+ ' Builds,\n' \
+ ' TestResults,\n' \
+ ' TestCases,\n' \
+ ' TestCaseArgs\n';
+ sQuery += 'WHERE TestSets.idTestSet = TestResults.idTestSet\n' \
+ ' AND TestResults.idTestResultParent is NULL\n' \
+ ' AND TestSets.idBuild = Builds.idBuild\n' \
+ ' AND Builds.tsExpire > TestSets.tsCreated\n' \
+ ' AND Builds.tsEffective <= TestSets.tsCreated\n' \
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n' \
+ ' AND TestSets.idGenTestCase = TestCases.idGenTestCase\n' \
+ ' AND TestSets.idGenTestCaseArgs = TestCaseArgs.idGenTestCaseArgs\n';
+ sQuery += 'GROUP BY TestSets.idTestSet,\n' \
+ ' BuildCategories.idBuildCategory,\n' \
+ ' BuildCategories.sProduct,\n' \
+ ' BuildCategories.sRepository,\n' \
+ ' BuildCategories.sBranch,\n' \
+ ' BuildCategories.sType,\n' \
+ ' Builds.idBuild,\n' \
+ ' Builds.sVersion,\n' \
+ ' Builds.iRevision,\n' \
+ ' TestBoxesWithStrings.sOs,\n' \
+ ' TestBoxesWithStrings.sOsVersion,\n' \
+ ' TestBoxesWithStrings.sCpuArch,\n' \
+ ' TestBoxesWithStrings.sCpuVendor,\n' \
+ ' TestBoxesWithStrings.sCpuName,\n' \
+ ' TestBoxesWithStrings.cCpus,\n' \
+ ' TestBoxesWithStrings.fCpuHwVirt,\n' \
+ ' TestBoxesWithStrings.fCpuNestedPaging,\n' \
+ ' TestBoxesWithStrings.fCpu64BitGuest,\n' \
+ ' TestBoxesWithStrings.idTestBox,\n' \
+ ' TestBoxesWithStrings.sName,\n' \
+ ' TestResults.tsCreated,\n' \
+ ' tsElapsedTestResult,\n' \
+ ' TestSets.enmStatus,\n' \
+ ' TestResults.cErrors,\n' \
+ ' TestCases.idTestCase,\n' \
+ ' TestCases.sName,\n' \
+ ' TestCases.sBaseCmd,\n' \
+ ' TestCaseArgs.sArgs,\n' \
+ ' TestCaseArgs.sSubName,\n' \
+ ' TestSuiteBits.idBuild,\n' \
+ ' TestSuiteBits.iRevision,\n' \
+ ' SortRunningFirst' + sSortGroupBy + '\n';
+ sQuery += 'ORDER BY ';
+ if sSortOrderBy is not None:
+ sQuery += sSortOrderBy.replace('TestBoxes.', 'TestBoxesWithStrings.') + ',\n ';
+ sQuery += '(TestSets.tsDone IS NULL) DESC, TestSets.idTestSet DESC\n';
+
+ #
+ # Execute the query and return the wrapped results.
+ #
+ self._oDb.execute(sQuery);
+
+ if self.oFailureReasonLogic is None:
+ self.oFailureReasonLogic = FailureReasonLogic(self._oDb);
+ if self.oUserAccountLogic is None:
+ self.oUserAccountLogic = UserAccountLogic(self._oDb);
+
+ aoRows = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRows.append(TestResultListingData().initFromDbRowEx(aoRow, self.oFailureReasonLogic, self.oUserAccountLogic));
+
+ return aoRows
+
+
+ def fetchTimestampsForLogViewer(self, idTestSet):
+ """
+ Returns an ordered list with all the test result timestamps, both start
+ and end.
+
+ The log viewer create anchors in the log text so we can jump directly to
+ the log lines relevant for a test event.
+ """
+ self._oDb.execute('(\n'
+ 'SELECT tsCreated\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ') UNION (\n'
+ 'SELECT tsCreated + tsElapsed\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND tsElapsed IS NOT NULL\n'
+ ') UNION (\n'
+ 'SELECT TestResultFiles.tsCreated\n'
+ 'FROM TestResultFiles\n'
+ 'WHERE idTestSet = %s\n'
+ ') UNION (\n'
+ 'SELECT tsCreated\n'
+ 'FROM TestResultValues\n'
+ 'WHERE idTestSet = %s\n'
+ ') UNION (\n'
+ 'SELECT TestResultMsgs.tsCreated\n'
+ 'FROM TestResultMsgs\n'
+ 'WHERE idTestSet = %s\n'
+ ') ORDER by 1'
+ , ( idTestSet, idTestSet, idTestSet, idTestSet, idTestSet, ));
+ return [aoRow[0] for aoRow in self._oDb.fetchAll()];
+
+
+ def getEntriesCount(self, tsNow, sInterval, oFilter, enmResultsGroupingType, iResultsGroupingValue,
+ fOnlyFailures, fOnlyNeedingReason):
+ """
+ Get number of table records.
+
+ If @param enmResultsGroupingType and @param iResultsGroupingValue
+ are not None, then we count only only those records
+ that match specified @param enmResultsGroupingType.
+
+ If @param enmResultsGroupingType is None, then
+ @param iResultsGroupingValue is ignored.
+ """
+ _ = oFilter;
+
+ #
+ # Get SQL query parameters
+ #
+ if enmResultsGroupingType is None:
+ raise TMExceptionBase('Unknown grouping type')
+
+ if enmResultsGroupingType not in self.kdResultGroupingMap:
+ raise TMExceptionBase('Unknown grouping type')
+ asGroupingTables, sGroupingField, sGroupingCondition, _ = self.kdResultGroupingMap[enmResultsGroupingType];
+
+ #
+ # Construct the query.
+ #
+ sQuery = 'SELECT COUNT(TestSets.idTestSet)\n' \
+ 'FROM TestSets\n';
+ sQuery += oFilter.getTableJoins();
+ if fOnlyNeedingReason and not oFilter.isJoiningWithTable('TestResultFailures'):
+ sQuery += ' LEFT OUTER JOIN TestResultFailures\n' \
+ ' ON TestSets.idTestSet = TestResultFailures.idTestSet\n' \
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n';
+ for sTable in asGroupingTables:
+ if not oFilter.isJoiningWithTable(sTable):
+ sQuery = sQuery[:-1] + ',\n ' + sTable + '\n';
+ sQuery += 'WHERE ' + self._getTimePeriodQueryPart(tsNow, sInterval) + \
+ oFilter.getWhereConditions();
+ if fOnlyFailures or fOnlyNeedingReason:
+ sQuery += ' AND TestSets.enmStatus != \'success\'::TestStatus_T\n' \
+ ' AND TestSets.enmStatus != \'running\'::TestStatus_T\n';
+ if fOnlyNeedingReason:
+ sQuery += ' AND TestResultFailures.idTestSet IS NULL\n';
+ if sGroupingField is not None:
+ sQuery += ' AND %s = %d\n' % (sGroupingField, iResultsGroupingValue,);
+ if sGroupingCondition is not None:
+ sQuery += sGroupingCondition.replace(' AND ', ' AND ');
+
+ #
+ # Execute the query and return the result.
+ #
+ self._oDb.execute(sQuery)
+ return self._oDb.fetchOne()[0]
+
+ def getTestGroups(self, tsNow, sPeriod):
+ """
+ Get list of uniq TestGroupData objects which
+ found in all test results.
+ """
+
+ self._oDb.execute('SELECT DISTINCT TestGroups.*\n'
+ 'FROM TestGroups, TestSets\n'
+ 'WHERE TestSets.idTestGroup = TestGroups.idTestGroup\n'
+ ' AND TestGroups.tsExpire > TestSets.tsCreated\n'
+ ' AND TestGroups.tsEffective <= TestSets.tsCreated'
+ ' AND ' + self._getTimePeriodQueryPart(tsNow, sPeriod))
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(TestGroupData().initFromDbRow(aoRow))
+ return aoRet
+
+ def getBuilds(self, tsNow, sPeriod):
+ """
+ Get list of uniq BuildDataEx objects which
+ found in all test results.
+ """
+
+ self._oDb.execute('SELECT DISTINCT Builds.*, BuildCategories.*\n'
+ 'FROM Builds, BuildCategories, TestSets\n'
+ 'WHERE TestSets.idBuild = Builds.idBuild\n'
+ ' AND Builds.idBuildCategory = BuildCategories.idBuildCategory\n'
+ ' AND Builds.tsExpire > TestSets.tsCreated\n'
+ ' AND Builds.tsEffective <= TestSets.tsCreated'
+ ' AND ' + self._getTimePeriodQueryPart(tsNow, sPeriod))
+ aaoRows = self._oDb.fetchAll()
+ aoRet = []
+ for aoRow in aaoRows:
+ aoRet.append(BuildDataEx().initFromDbRow(aoRow))
+ return aoRet
+
+ def getTestBoxes(self, tsNow, sPeriod):
+ """
+ Get list of uniq TestBoxData objects which
+ found in all test results.
+ """
+ # Note! INNER JOIN TestBoxesWithStrings performs miserable compared to LEFT OUTER JOIN. Doesn't matter for the result
+ # because TestSets.idGenTestBox is a foreign key and unique in TestBoxes. So, let's do what ever is faster.
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM ( SELECT idTestBox AS idTestBox,\n'
+ ' MAX(idGenTestBox) AS idGenTestBox\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' GROUP BY idTestBox\n'
+ ' ) AS TestBoxIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxIDs.idGenTestBox\n'
+ 'ORDER BY TestBoxesWithStrings.sName\n' );
+ aoRet = []
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(TestBoxData().initFromDbRow(aoRow));
+ return aoRet
+
+ def getTestCases(self, tsNow, sPeriod):
+ """
+ Get a list of unique TestCaseData objects which is appears in the test
+ specified result period.
+ """
+
+ # Using LEFT OUTER JOIN instead of INNER JOIN in case it performs better, doesn't matter for the result.
+ self._oDb.execute('SELECT TestCases.*\n'
+ 'FROM ( SELECT idTestCase AS idTestCase,\n'
+ ' MAX(idGenTestCase) AS idGenTestCase\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' GROUP BY idTestCase\n'
+ ' ) AS TestCasesIDs\n'
+ ' LEFT OUTER JOIN TestCases ON TestCases.idGenTestCase = TestCasesIDs.idGenTestCase\n'
+ 'ORDER BY TestCases.sName\n' );
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(TestCaseData().initFromDbRow(aoRow));
+ return aoRet
+
+ def getOSes(self, tsNow, sPeriod):
+ """
+ Get a list of [idStrOs, sOs] tuples of the OSes that appears in the specified result period.
+ """
+
+ # Note! INNER JOIN TestBoxesWithStrings performs miserable compared to LEFT OUTER JOIN. Doesn't matter for the result
+ # because TestSets.idGenTestBox is a foreign key and unique in TestBoxes. So, let's do what ever is faster.
+ self._oDb.execute('SELECT DISTINCT TestBoxesWithStrings.idStrOs, TestBoxesWithStrings.sOs\n'
+ 'FROM ( SELECT idTestBox AS idTestBox,\n'
+ ' MAX(idGenTestBox) AS idGenTestBox\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' GROUP BY idTestBox\n'
+ ' ) AS TestBoxIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxIDs.idGenTestBox\n'
+ 'ORDER BY TestBoxesWithStrings.sOs\n' );
+ return self._oDb.fetchAll();
+
+ def getArchitectures(self, tsNow, sPeriod):
+ """
+ Get a list of [idStrCpuArch, sCpuArch] tuples of the architecutres
+ that appears in the specified result period.
+ """
+
+ # Note! INNER JOIN TestBoxesWithStrings performs miserable compared to LEFT OUTER JOIN. Doesn't matter for the result
+ # because TestSets.idGenTestBox is a foreign key and unique in TestBoxes. So, let's do what ever is faster.
+ self._oDb.execute('SELECT DISTINCT TestBoxesWithStrings.idStrCpuArch, TestBoxesWithStrings.sCpuArch\n'
+ 'FROM ( SELECT idTestBox AS idTestBox,\n'
+ ' MAX(idGenTestBox) AS idGenTestBox\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' GROUP BY idTestBox\n'
+ ' ) AS TestBoxIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxIDs.idGenTestBox\n'
+ 'ORDER BY TestBoxesWithStrings.sCpuArch\n' );
+ return self._oDb.fetchAll();
+
+ def getBuildCategories(self, tsNow, sPeriod):
+ """
+ Get a list of BuildCategoryData that appears in the specified result period.
+ """
+
+ self._oDb.execute('SELECT DISTINCT BuildCategories.*\n'
+ 'FROM ( SELECT DISTINCT idBuildCategory AS idBuildCategory\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' ) AS BuildCategoryIDs\n'
+ ' LEFT OUTER JOIN BuildCategories\n'
+ ' ON BuildCategories.idBuildCategory = BuildCategoryIDs.idBuildCategory\n'
+ 'ORDER BY BuildCategories.sProduct, BuildCategories.sBranch, BuildCategories.sType\n');
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(BuildCategoryData().initFromDbRow(aoRow));
+ return aoRet;
+
+ def getSchedGroups(self, tsNow, sPeriod):
+ """
+ Get list of uniq SchedGroupData objects which
+ found in all test results.
+ """
+
+ self._oDb.execute('SELECT SchedGroups.*\n'
+ 'FROM ( SELECT idSchedGroup,\n'
+ ' MAX(TestSets.tsCreated) AS tsNow\n'
+ ' FROM TestSets\n'
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' GROUP BY idSchedGroup\n'
+ ' ) AS SchedGroupIDs\n'
+ ' INNER JOIN SchedGroups\n'
+ ' ON SchedGroups.idSchedGroup = SchedGroupIDs.idSchedGroup\n'
+ ' AND SchedGroups.tsExpire > SchedGroupIDs.tsNow\n'
+ ' AND SchedGroups.tsEffective <= SchedGroupIDs.tsNow\n'
+ 'ORDER BY SchedGroups.sName\n' );
+ aoRet = []
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(SchedGroupData().initFromDbRow(aoRow));
+ return aoRet
+
+ def getById(self, idTestResult):
+ """
+ Get build record by its id
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestResult = %s\n',
+ (idTestResult,))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise TMTooManyRows('Found more than one test result with the same credentials. Database structure is corrupted.')
+ try:
+ return TestResultData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+ def fetchPossibleFilterOptions(self, oFilter, tsNow, sPeriod, oReportModel = None):
+ """
+ Fetches the available filter criteria, given the current filtering.
+
+ Returns oFilter.
+ """
+ assert isinstance(oFilter, TestResultFilter);
+
+ # Hack to avoid lot's of conditionals or duplicate this code.
+ if oReportModel is None:
+ class DummyReportModel(object):
+ """ Dummy """
+ def getExtraSubjectTables(self):
+ """ Dummy """
+ return [];
+ def getExtraSubjectWhereExpr(self):
+ """ Dummy """
+ return '';
+ oReportModel = DummyReportModel();
+
+ def workerDoFetch(oMissingLogicType, sNameAttr = 'sName', fIdIsName = False, idxHover = -1,
+ idNull = -1, sNullDesc = '<NULL>'):
+ """ Does the tedious result fetching and handling of missing bits. """
+ dLeft = { oValue: 1 for oValue in oCrit.aoSelected };
+ oCrit.aoPossible = [];
+ for aoRow in self._oDb.fetchAll():
+ oCrit.aoPossible.append(FilterCriterionValueAndDescription(aoRow[0] if aoRow[0] is not None else idNull,
+ aoRow[1] if aoRow[1] is not None else sNullDesc,
+ aoRow[2],
+ aoRow[idxHover] if idxHover >= 0 else None));
+ if aoRow[0] in dLeft:
+ del dLeft[aoRow[0]];
+ if dLeft:
+ if fIdIsName:
+ for idMissing in dLeft:
+ oCrit.aoPossible.append(FilterCriterionValueAndDescription(idMissing, str(idMissing),
+ fIrrelevant = True));
+ else:
+ oMissingLogic = oMissingLogicType(self._oDb);
+ for idMissing in dLeft:
+ oMissing = oMissingLogic.cachedLookup(idMissing);
+ if oMissing is not None:
+ oCrit.aoPossible.append(FilterCriterionValueAndDescription(idMissing,
+ getattr(oMissing, sNameAttr),
+ fIrrelevant = True));
+
+ def workerDoFetchNested():
+ """ Does the tedious result fetching and handling of missing bits. """
+ oCrit.aoPossible = [];
+ oCrit.oSub.aoPossible = [];
+ dLeft = { oValue: 1 for oValue in oCrit.aoSelected };
+ dSubLeft = { oValue: 1 for oValue in oCrit.oSub.aoSelected };
+ oMain = None;
+ for aoRow in self._oDb.fetchAll():
+ if oMain is None or oMain.oValue != aoRow[0]:
+ oMain = FilterCriterionValueAndDescription(aoRow[0], aoRow[1], 0);
+ oCrit.aoPossible.append(oMain);
+ if aoRow[0] in dLeft:
+ del dLeft[aoRow[0]];
+ oCurSub = FilterCriterionValueAndDescription(aoRow[2], aoRow[3], aoRow[4]);
+ oCrit.oSub.aoPossible.append(oCurSub);
+ if aoRow[2] in dSubLeft:
+ del dSubLeft[aoRow[2]];
+
+ oMain.aoSubs.append(oCurSub);
+ oMain.cTimes += aoRow[4];
+
+ if dLeft:
+ pass; ## @todo
+
+ # Statuses.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiTestStatus];
+ self._oDb.execute('SELECT TestSets.enmStatus, TestSets.enmStatus, COUNT(TestSets.idTestSet)\n'
+ 'FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiTestStatus) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ 'WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod) +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiTestStatus) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ 'GROUP BY TestSets.enmStatus\n'
+ 'ORDER BY TestSets.enmStatus\n');
+ workerDoFetch(None, fIdIsName = True);
+
+ # Scheduling groups (see getSchedGroups).
+ oCrit = oFilter.aCriteria[TestResultFilter.kiSchedGroups];
+ self._oDb.execute('SELECT SchedGroups.idSchedGroup, SchedGroups.sName, SchedGroupIDs.cTimes\n'
+ 'FROM ( SELECT TestSets.idSchedGroup,\n'
+ ' MAX(TestSets.tsCreated) AS tsNow,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiSchedGroups) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiSchedGroups) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idSchedGroup\n'
+ ' ) AS SchedGroupIDs\n'
+ ' INNER JOIN SchedGroups\n'
+ ' ON SchedGroups.idSchedGroup = SchedGroupIDs.idSchedGroup\n'
+ ' AND SchedGroups.tsExpire > SchedGroupIDs.tsNow\n'
+ ' AND SchedGroups.tsEffective <= SchedGroupIDs.tsNow\n'
+ 'ORDER BY SchedGroups.sName\n' );
+ workerDoFetch(SchedGroupLogic);
+
+ # Testboxes (see getTestBoxes).
+ oCrit = oFilter.aCriteria[TestResultFilter.kiTestBoxes];
+ self._oDb.execute('SELECT TestBoxesWithStrings.idTestBox,\n'
+ ' TestBoxesWithStrings.sName,\n'
+ ' TestBoxIDs.cTimes\n'
+ 'FROM ( SELECT TestSets.idTestBox AS idTestBox,\n'
+ ' MAX(TestSets.idGenTestBox) AS idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiTestBoxes) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiTestBoxes) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idTestBox\n'
+ ' ) AS TestBoxIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxIDs.idGenTestBox\n'
+ 'ORDER BY TestBoxesWithStrings.sName\n' );
+ workerDoFetch(TestBoxLogic);
+
+ # Testbox OSes and versions.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiOses];
+ self._oDb.execute('SELECT TestBoxesWithStrings.idStrOs,\n'
+ ' TestBoxesWithStrings.sOs,\n'
+ ' TestBoxesWithStrings.idStrOsVersion,\n'
+ ' TestBoxesWithStrings.sOsVersion,\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiOses) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiOses) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox\n'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.idStrOs,\n'
+ ' TestBoxesWithStrings.sOs,\n'
+ ' TestBoxesWithStrings.idStrOsVersion,\n'
+ ' TestBoxesWithStrings.sOsVersion\n'
+ 'ORDER BY TestBoxesWithStrings.sOs,\n'
+ ' TestBoxesWithStrings.sOs = \'win\' AND TestBoxesWithStrings.sOsVersion = \'10\' DESC,\n'
+ ' TestBoxesWithStrings.sOsVersion DESC\n'
+ );
+ workerDoFetchNested();
+
+ # Testbox CPU(/OS) architectures.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiCpuArches];
+ self._oDb.execute('SELECT TestBoxesWithStrings.idStrCpuArch,\n'
+ ' TestBoxesWithStrings.sCpuArch,\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiCpuArches) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuArches) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox\n'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.idStrCpuArch, TestBoxesWithStrings.sCpuArch\n'
+ 'ORDER BY TestBoxesWithStrings.sCpuArch\n' );
+ workerDoFetch(None, fIdIsName = True);
+
+ # Testbox CPU revisions.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiCpuVendors];
+ self._oDb.execute('SELECT TestBoxesWithStrings.idStrCpuVendor,\n'
+ ' TestBoxesWithStrings.sCpuVendor,\n'
+ ' TestBoxesWithStrings.lCpuRevision,\n'
+ ' TestBoxesWithStrings.sCpuVendor,\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiCpuVendors) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuVendors) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.idStrCpuVendor,\n'
+ ' TestBoxesWithStrings.sCpuVendor,\n'
+ ' TestBoxesWithStrings.lCpuRevision,\n'
+ ' TestBoxesWithStrings.sCpuVendor\n'
+ 'ORDER BY TestBoxesWithStrings.sCpuVendor DESC,\n'
+ ' TestBoxesWithStrings.sCpuVendor = \'GenuineIntel\'\n'
+ ' AND (TestBoxesWithStrings.lCpuRevision >> 24) = 15,\n' # P4 at the bottom is a start...
+ ' TestBoxesWithStrings.lCpuRevision DESC\n'
+ );
+ workerDoFetchNested();
+ for oCur in oCrit.oSub.aoPossible:
+ oCur.sDesc = TestBoxData.getPrettyCpuVersionEx(oCur.oValue, oCur.sDesc).replace('_', ' ');
+
+ # Testbox CPU core/thread counts.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiCpuCounts];
+ self._oDb.execute('SELECT TestBoxesWithStrings.cCpus,\n'
+ ' CAST(TestBoxesWithStrings.cCpus AS TEXT),\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiCpuCounts) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuCounts) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.cCpus\n'
+ 'ORDER BY TestBoxesWithStrings.cCpus\n' );
+ workerDoFetch(None, fIdIsName = True);
+
+ # Testbox memory.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiMemory];
+ self._oDb.execute('SELECT TestBoxesWithStrings.cMbMemory / 1024,\n'
+ ' NULL,\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiMemory) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiMemory) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.cMbMemory / 1024\n'
+ 'ORDER BY 1\n' );
+ workerDoFetch(None, fIdIsName = True);
+ for oCur in oCrit.aoPossible:
+ oCur.sDesc = '%u GB' % (oCur.oValue,);
+
+ # Testbox python versions .
+ oCrit = oFilter.aCriteria[TestResultFilter.kiPythonVersions];
+ self._oDb.execute('SELECT TestBoxesWithStrings.iPythonHexVersion,\n'
+ ' NULL,\n'
+ ' SUM(TestBoxGenIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idGenTestBox AS idGenTestBox,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiPythonVersions) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiPythonVersions) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idGenTestBox\n'
+ ' ) AS TestBoxGenIDs\n'
+ ' LEFT OUTER JOIN TestBoxesWithStrings\n'
+ ' ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+ 'GROUP BY TestBoxesWithStrings.iPythonHexVersion\n'
+ 'ORDER BY TestBoxesWithStrings.iPythonHexVersion\n' );
+ workerDoFetch(None, fIdIsName = True);
+ for oCur in oCrit.aoPossible:
+ oCur.sDesc = TestBoxData.formatPythonVersionEx(oCur.oValue); # pylint: disable=redefined-variable-type
+
+ # Testcase with variation.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiTestCases];
+ self._oDb.execute('SELECT TestCaseArgsIDs.idTestCase,\n'
+ ' TestCases.sName,\n'
+ ' TestCaseArgsIDs.idTestCaseArgs,\n'
+ ' CASE WHEN TestCaseArgs.sSubName IS NULL OR TestCaseArgs.sSubName = \'\' THEN\n'
+ ' CONCAT(\'/ #\', TestCaseArgs.idTestCaseArgs)\n'
+ ' ELSE\n'
+ ' TestCaseArgs.sSubName\n'
+ ' END,'
+ ' TestCaseArgsIDs.cTimes\n'
+ 'FROM ( SELECT TestSets.idTestCase AS idTestCase,\n'
+ ' TestSets.idTestCaseArgs AS idTestCaseArgs,\n'
+ ' MAX(TestSets.idGenTestCase) AS idGenTestCase,\n'
+ ' MAX(TestSets.idGenTestCaseArgs) AS idGenTestCaseArgs,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiTestCases) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiTestCases) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idTestCase, TestSets.idTestCaseArgs\n'
+ ' ) AS TestCaseArgsIDs\n'
+ ' LEFT OUTER JOIN TestCases ON TestCases.idGenTestCase = TestCaseArgsIDs.idGenTestCase\n'
+ ' LEFT OUTER JOIN TestCaseArgs\n'
+ ' ON TestCaseArgs.idGenTestCaseArgs = TestCaseArgsIDs.idGenTestCaseArgs\n'
+ 'ORDER BY TestCases.sName, 4\n' );
+ workerDoFetchNested();
+
+ # Build revisions.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiRevisions];
+ self._oDb.execute('SELECT Builds.iRevision, CONCAT(\'r\', Builds.iRevision), SUM(BuildIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idBuild AS idBuild,\n'
+ ' MAX(TestSets.tsCreated) AS tsNow,\n'
+ ' COUNT(TestSets.idBuild) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiRevisions) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiRevisions) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idBuild\n'
+ ' ) AS BuildIDs\n'
+ ' INNER JOIN Builds\n'
+ ' ON Builds.idBuild = BuildIDs.idBuild\n'
+ ' AND Builds.tsExpire > BuildIDs.tsNow\n'
+ ' AND Builds.tsEffective <= BuildIDs.tsNow\n'
+ 'GROUP BY Builds.iRevision\n'
+ 'ORDER BY Builds.iRevision DESC\n' );
+ workerDoFetch(None, fIdIsName = True);
+
+ # Build branches.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiBranches];
+ self._oDb.execute('SELECT BuildCategories.sBranch, BuildCategories.sBranch, SUM(BuildCategoryIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idBuildCategory,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiBranches) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiBranches) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idBuildCategory\n'
+ ' ) AS BuildCategoryIDs\n'
+ ' INNER JOIN BuildCategories\n'
+ ' ON BuildCategories.idBuildCategory = BuildCategoryIDs.idBuildCategory\n'
+ 'GROUP BY BuildCategories.sBranch\n'
+ 'ORDER BY BuildCategories.sBranch DESC\n' );
+ workerDoFetch(None, fIdIsName = True);
+
+ # Build types.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiBuildTypes];
+ self._oDb.execute('SELECT BuildCategories.sType, BuildCategories.sType, SUM(BuildCategoryIDs.cTimes)\n'
+ 'FROM ( SELECT TestSets.idBuildCategory,\n'
+ ' COUNT(TestSets.idTestSet) AS cTimes\n'
+ ' FROM TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiBuildTypes) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiBuildTypes) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestSets.idBuildCategory\n'
+ ' ) AS BuildCategoryIDs\n'
+ ' INNER JOIN BuildCategories\n'
+ ' ON BuildCategories.idBuildCategory = BuildCategoryIDs.idBuildCategory\n'
+ 'GROUP BY BuildCategories.sType\n'
+ 'ORDER BY BuildCategories.sType DESC\n' );
+ workerDoFetch(None, fIdIsName = True);
+
+ # Failure reasons.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiFailReasons];
+ self._oDb.execute('SELECT FailureReasons.idFailureReason, FailureReasons.sShort, FailureReasonIDs.cTimes\n'
+ 'FROM ( SELECT TestResultFailures.idFailureReason,\n'
+ ' COUNT(TestSets.idTestSet) as cTimes\n'
+ ' FROM TestSets\n'
+ ' LEFT OUTER JOIN TestResultFailures\n'
+ ' ON TestResultFailures.idTestSet = TestSets.idTestSet\n'
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n' +
+ oFilter.getTableJoins(iOmit = TestResultFilter.kiFailReasons) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ ' AND TestSets.enmStatus >= \'failure\'::TestStatus_T\n' +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiFailReasons) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' GROUP BY TestResultFailures.idFailureReason\n'
+ ' ) AS FailureReasonIDs\n'
+ ' LEFT OUTER JOIN FailureReasons\n'
+ ' ON FailureReasons.idFailureReason = FailureReasonIDs.idFailureReason\n'
+ ' AND FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY FailureReasons.idFailureReason IS NULL DESC,\n'
+ ' FailureReasons.sShort\n' );
+ workerDoFetch(FailureReasonLogic, 'sShort', sNullDesc = 'Not given');
+
+ # Error counts.
+ oCrit = oFilter.aCriteria[TestResultFilter.kiErrorCounts];
+ self._oDb.execute('SELECT TestResults.cErrors, CAST(TestResults.cErrors AS TEXT), COUNT(TestResults.idTestResult)\n'
+ 'FROM ( SELECT TestSets.idTestResult AS idTestResult\n'
+ ' FROM TestSets\n' +
+ oFilter.getTableJoins(iOmit = TestResultFilter.kiFailReasons) +
+ ''.join(' , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+ ' WHERE ' + self._getTimePeriodQueryPart(tsNow, sPeriod, ' ') +
+ oFilter.getWhereConditions(iOmit = TestResultFilter.kiFailReasons) +
+ oReportModel.getExtraSubjectWhereExpr() +
+ ' ) AS TestSetIDs\n'
+ ' INNER JOIN TestResults\n'
+ ' ON TestResults.idTestResult = TestSetIDs.idTestResult\n'
+ 'GROUP BY TestResults.cErrors\n'
+ 'ORDER BY TestResults.cErrors\n');
+
+ workerDoFetch(None, fIdIsName = True);
+
+ return oFilter;
+
+
+ #
+ # Details view and interface.
+ #
+
+ def fetchResultTree(self, idTestSet, cMaxDepth = None):
+ """
+ Fetches the result tree for the given test set.
+
+ Returns a tree of TestResultDataEx nodes.
+ Raises exception on invalid input and database issues.
+ """
+ # Depth first, i.e. just like the XML added them.
+ ## @todo this still isn't performing extremely well, consider optimizations.
+ sQuery = self._oDb.formatBindArgs(
+ 'SELECT TestResults.*,\n'
+ ' TestResultStrTab.sValue,\n'
+ ' EXISTS ( SELECT idTestResultValue\n'
+ ' FROM TestResultValues\n'
+ ' WHERE TestResultValues.idTestResult = TestResults.idTestResult ) AS fHasValues,\n'
+ ' EXISTS ( SELECT idTestResultMsg\n'
+ ' FROM TestResultMsgs\n'
+ ' WHERE TestResultMsgs.idTestResult = TestResults.idTestResult ) AS fHasMsgs,\n'
+ ' EXISTS ( SELECT idTestResultFile\n'
+ ' FROM TestResultFiles\n'
+ ' WHERE TestResultFiles.idTestResult = TestResults.idTestResult ) AS fHasFiles,\n'
+ ' EXISTS ( SELECT idTestResult\n'
+ ' FROM TestResultFailures\n'
+ ' WHERE TestResultFailures.idTestResult = TestResults.idTestResult ) AS fHasReasons\n'
+ 'FROM TestResults, TestResultStrTab\n'
+ 'WHERE TestResults.idTestSet = %s\n'
+ ' AND TestResults.idStrName = TestResultStrTab.idStr\n'
+ , ( idTestSet, ));
+ if cMaxDepth is not None:
+ sQuery += self._oDb.formatBindArgs(' AND TestResults.iNestingDepth <= %s\n', (cMaxDepth,));
+ sQuery += 'ORDER BY idTestResult ASC\n'
+
+ self._oDb.execute(sQuery);
+ cRows = self._oDb.getRowCount();
+ if cRows > 65536:
+ raise TMTooManyRows('Too many rows returned for idTestSet=%d: %d' % (idTestSet, cRows,));
+
+ aaoRows = self._oDb.fetchAll();
+ if not aaoRows:
+ raise TMRowNotFound('No test results for idTestSet=%d.' % (idTestSet,));
+
+ # Set up the root node first.
+ aoRow = aaoRows[0];
+ oRoot = TestResultDataEx().initFromDbRow(aoRow);
+ if oRoot.idTestResultParent is not None:
+ raise self._oDb.integrityException('The root TestResult (#%s) has a parent (#%s)!'
+ % (oRoot.idTestResult, oRoot.idTestResultParent));
+ self._fetchResultTreeNodeExtras(oRoot, aoRow[-4], aoRow[-3], aoRow[-2], aoRow[-1]);
+
+ # The children (if any).
+ dLookup = { oRoot.idTestResult: oRoot };
+ oParent = oRoot;
+ for iRow in range(1, len(aaoRows)):
+ aoRow = aaoRows[iRow];
+ oCur = TestResultDataEx().initFromDbRow(aoRow);
+ self._fetchResultTreeNodeExtras(oCur, aoRow[-4], aoRow[-3], aoRow[-2], aoRow[-1]);
+
+ # Figure out and vet the parent.
+ if oParent.idTestResult != oCur.idTestResultParent:
+ oParent = dLookup.get(oCur.idTestResultParent, None);
+ if oParent is None:
+ raise self._oDb.integrityException('TestResult #%d is orphaned from its parent #%s.'
+ % (oCur.idTestResult, oCur.idTestResultParent,));
+ if oParent.iNestingDepth + 1 != oCur.iNestingDepth:
+ raise self._oDb.integrityException('TestResult #%d has incorrect nesting depth (%d instead of %d)'
+ % (oCur.idTestResult, oCur.iNestingDepth, oParent.iNestingDepth + 1,));
+
+ # Link it up.
+ oCur.oParent = oParent;
+ oParent.aoChildren.append(oCur);
+ dLookup[oCur.idTestResult] = oCur;
+
+ return (oRoot, dLookup);
+
+ def _fetchResultTreeNodeExtras(self, oCurNode, fHasValues, fHasMsgs, fHasFiles, fHasReasons):
+ """
+ fetchResultTree worker that fetches values, message and files for the
+ specified node.
+ """
+ assert(oCurNode.aoValues == []);
+ assert(oCurNode.aoMsgs == []);
+ assert(oCurNode.aoFiles == []);
+ assert(oCurNode.oReason is None);
+
+ if fHasValues:
+ self._oDb.execute('SELECT TestResultValues.*,\n'
+ ' TestResultStrTab.sValue\n'
+ 'FROM TestResultValues, TestResultStrTab\n'
+ 'WHERE TestResultValues.idTestResult = %s\n'
+ ' AND TestResultValues.idStrName = TestResultStrTab.idStr\n'
+ 'ORDER BY idTestResultValue ASC\n'
+ , ( oCurNode.idTestResult, ));
+ for aoRow in self._oDb.fetchAll():
+ oCurNode.aoValues.append(TestResultValueDataEx().initFromDbRow(aoRow));
+
+ if fHasMsgs:
+ self._oDb.execute('SELECT TestResultMsgs.*,\n'
+ ' TestResultStrTab.sValue\n'
+ 'FROM TestResultMsgs, TestResultStrTab\n'
+ 'WHERE TestResultMsgs.idTestResult = %s\n'
+ ' AND TestResultMsgs.idStrMsg = TestResultStrTab.idStr\n'
+ 'ORDER BY idTestResultMsg ASC\n'
+ , ( oCurNode.idTestResult, ));
+ for aoRow in self._oDb.fetchAll():
+ oCurNode.aoMsgs.append(TestResultMsgDataEx().initFromDbRow(aoRow));
+
+ if fHasFiles:
+ self._oDb.execute('SELECT TestResultFiles.*,\n'
+ ' StrTabFile.sValue AS sFile,\n'
+ ' StrTabDesc.sValue AS sDescription,\n'
+ ' StrTabKind.sValue AS sKind,\n'
+ ' StrTabMime.sValue AS sMime\n'
+ 'FROM TestResultFiles,\n'
+ ' TestResultStrTab AS StrTabFile,\n'
+ ' TestResultStrTab AS StrTabDesc,\n'
+ ' TestResultStrTab AS StrTabKind,\n'
+ ' TestResultStrTab AS StrTabMime\n'
+ 'WHERE TestResultFiles.idTestResult = %s\n'
+ ' AND TestResultFiles.idStrFile = StrTabFile.idStr\n'
+ ' AND TestResultFiles.idStrDescription = StrTabDesc.idStr\n'
+ ' AND TestResultFiles.idStrKind = StrTabKind.idStr\n'
+ ' AND TestResultFiles.idStrMime = StrTabMime.idStr\n'
+ 'ORDER BY idTestResultFile ASC\n'
+ , ( oCurNode.idTestResult, ));
+ for aoRow in self._oDb.fetchAll():
+ oCurNode.aoFiles.append(TestResultFileDataEx().initFromDbRow(aoRow));
+
+ if fHasReasons:
+ if self.oFailureReasonLogic is None:
+ self.oFailureReasonLogic = FailureReasonLogic(self._oDb);
+ if self.oUserAccountLogic is None:
+ self.oUserAccountLogic = UserAccountLogic(self._oDb);
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestResultFailures\n'
+ 'WHERE idTestResult = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , ( oCurNode.idTestResult, ));
+ if self._oDb.getRowCount() > 0:
+ oCurNode.oReason = TestResultFailureDataEx().initFromDbRowEx(self._oDb.fetchOne(), self.oFailureReasonLogic,
+ self.oUserAccountLogic);
+
+ return True;
+
+
+
+ #
+ # TestBoxController interface(s).
+ #
+
+ def _inhumeTestResults(self, aoStack, idTestSet, sError):
+ """
+ The test produces too much output, kill and bury it.
+
+ Note! We leave the test set open, only the test result records are
+ completed. Thus, _getResultStack will return an empty stack and
+ cause XML processing to fail immediately, while we can still
+ record when it actually completed in the test set the normal way.
+ """
+ self._oDb.dprint('** _inhumeTestResults: idTestSet=%d\n%s' % (idTestSet, self._stringifyStack(aoStack),));
+
+ #
+ # First add a message.
+ #
+ self._newFailureDetails(aoStack[0].idTestResult, idTestSet, sError, None);
+
+ #
+ # The complete all open test results.
+ #
+ for oTestResult in aoStack:
+ oTestResult.cErrors += 1;
+ self._completeTestResults(oTestResult, None, TestResultData.ksTestStatus_Failure, oTestResult.cErrors);
+
+ # A bit of paranoia.
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET cErrors = cErrors + 1,\n'
+ ' enmStatus = \'failure\'::TestStatus_T,\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ , ( idTestSet, ));
+ self._oDb.commit();
+
+ return None;
+
+ def strTabString(self, sString, fCommit = False):
+ """
+ Gets the string table id for the given string, adding it if new.
+
+ Note! A copy of this code is also in TestSetLogic.
+ """
+ ## @todo move this and make a stored procedure for it.
+ self._oDb.execute('SELECT idStr\n'
+ 'FROM TestResultStrTab\n'
+ 'WHERE sValue = %s'
+ , (sString,));
+ if self._oDb.getRowCount() == 0:
+ self._oDb.execute('INSERT INTO TestResultStrTab (sValue)\n'
+ 'VALUES (%s)\n'
+ 'RETURNING idStr\n'
+ , (sString,));
+ if fCommit:
+ self._oDb.commit();
+ return self._oDb.fetchOne()[0];
+
+ @staticmethod
+ def _stringifyStack(aoStack):
+ """Returns a string rep of the stack."""
+ sRet = '';
+ for i, _ in enumerate(aoStack):
+ sRet += 'aoStack[%d]=%s\n' % (i, aoStack[i]);
+ return sRet;
+
+ def _getResultStack(self, idTestSet):
+ """
+ Gets the current stack of result sets.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ 'ORDER BY idTestResult DESC'
+ , ( idTestSet, ));
+ aoStack = [];
+ for aoRow in self._oDb.fetchAll():
+ aoStack.append(TestResultData().initFromDbRow(aoRow));
+
+ for i, _ in enumerate(aoStack):
+ assert aoStack[i].iNestingDepth == len(aoStack) - i - 1, self._stringifyStack(aoStack);
+
+ return aoStack;
+
+ def _newTestResult(self, idTestResultParent, idTestSet, iNestingDepth, tsCreated, sName, dCounts, fCommit = False):
+ """
+ Creates a new test result.
+ Returns the TestResultData object for the new record.
+ May raise exception on database error.
+ """
+ assert idTestResultParent is not None;
+ assert idTestResultParent > 1;
+
+ #
+ # This isn't necessarily very efficient, but it's necessary to prevent
+ # a wild test or testbox from filling up the database.
+ #
+ sCountName = 'cTestResults';
+ if sCountName not in dCounts:
+ self._oDb.execute('SELECT COUNT(idTestResult)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ , ( idTestSet,));
+ dCounts[sCountName] = self._oDb.fetchOne()[0];
+ dCounts[sCountName] += 1;
+ if dCounts[sCountName] > config.g_kcMaxTestResultsPerTS:
+ raise TestResultHangingOffence('Too many sub-tests in total!');
+
+ sCountName = 'cTestResultsIn%d' % (idTestResultParent,);
+ if sCountName not in dCounts:
+ self._oDb.execute('SELECT COUNT(idTestResult)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestResultParent = %s\n'
+ , ( idTestResultParent,));
+ dCounts[sCountName] = self._oDb.fetchOne()[0];
+ dCounts[sCountName] += 1;
+ if dCounts[sCountName] > config.g_kcMaxTestResultsPerTR:
+ raise TestResultHangingOffence('Too many immediate sub-tests!');
+
+ # This is also a hanging offence.
+ if iNestingDepth > config.g_kcMaxTestResultDepth:
+ raise TestResultHangingOffence('To deep sub-test nesting!');
+
+ # Ditto.
+ if len(sName) > config.g_kcchMaxTestResultName:
+ raise TestResultHangingOffence('Test name is too long: %d chars - "%s"' % (len(sName), sName));
+
+ #
+ # Within bounds, do the job.
+ #
+ idStrName = self.strTabString(sName, fCommit);
+ self._oDb.execute('INSERT INTO TestResults (\n'
+ ' idTestResultParent,\n'
+ ' idTestSet,\n'
+ ' tsCreated,\n'
+ ' idStrName,\n'
+ ' iNestingDepth )\n'
+ 'VALUES (%s, %s, TIMESTAMP WITH TIME ZONE %s, %s, %s)\n'
+ 'RETURNING *\n'
+ , ( idTestResultParent, idTestSet, tsCreated, idStrName, iNestingDepth) )
+ oData = TestResultData().initFromDbRow(self._oDb.fetchOne());
+
+ self._oDb.maybeCommit(fCommit);
+ return oData;
+
+ def _newTestValue(self, idTestResult, idTestSet, sName, lValue, sUnit, dCounts, tsCreated = None, fCommit = False):
+ """
+ Creates a test value.
+ May raise exception on database error.
+ """
+
+ #
+ # Bounds checking.
+ #
+ sCountName = 'cTestValues';
+ if sCountName not in dCounts:
+ self._oDb.execute('SELECT COUNT(idTestResultValue)\n'
+ 'FROM TestResultValues, TestResults\n'
+ 'WHERE TestResultValues.idTestResult = TestResults.idTestResult\n'
+ ' AND TestResults.idTestSet = %s\n'
+ , ( idTestSet,));
+ dCounts[sCountName] = self._oDb.fetchOne()[0];
+ dCounts[sCountName] += 1;
+ if dCounts[sCountName] > config.g_kcMaxTestValuesPerTS:
+ raise TestResultHangingOffence('Too many values in total!');
+
+ sCountName = 'cTestValuesIn%d' % (idTestResult,);
+ if sCountName not in dCounts:
+ self._oDb.execute('SELECT COUNT(idTestResultValue)\n'
+ 'FROM TestResultValues\n'
+ 'WHERE idTestResult = %s\n'
+ , ( idTestResult,));
+ dCounts[sCountName] = self._oDb.fetchOne()[0];
+ dCounts[sCountName] += 1;
+ if dCounts[sCountName] > config.g_kcMaxTestValuesPerTR:
+ raise TestResultHangingOffence('Too many immediate values for one test result!');
+
+ if len(sName) > config.g_kcchMaxTestValueName:
+ raise TestResultHangingOffence('Value name is too long: %d chars - "%s"' % (len(sName), sName));
+
+ #
+ # Do the job.
+ #
+ iUnit = constants.valueunit.g_kdNameToConst.get(sUnit, constants.valueunit.NONE);
+
+ idStrName = self.strTabString(sName, fCommit);
+ if tsCreated is None:
+ self._oDb.execute('INSERT INTO TestResultValues (\n'
+ ' idTestResult,\n'
+ ' idTestSet,\n'
+ ' idStrName,\n'
+ ' lValue,\n'
+ ' iUnit)\n'
+ 'VALUES ( %s, %s, %s, %s, %s )\n'
+ , ( idTestResult, idTestSet, idStrName, lValue, iUnit,) );
+ else:
+ self._oDb.execute('INSERT INTO TestResultValues (\n'
+ ' idTestResult,\n'
+ ' idTestSet,\n'
+ ' tsCreated,\n'
+ ' idStrName,\n'
+ ' lValue,\n'
+ ' iUnit)\n'
+ 'VALUES ( %s, %s, TIMESTAMP WITH TIME ZONE %s, %s, %s, %s )\n'
+ , ( idTestResult, idTestSet, tsCreated, idStrName, lValue, iUnit,) );
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def _newFailureDetails(self, idTestResult, idTestSet, sText, dCounts, tsCreated = None, fCommit = False):
+ """
+ Creates a record detailing cause of failure.
+ May raise exception on database error.
+ """
+
+ #
+ # Overflow protection.
+ #
+ if dCounts is not None:
+ sCountName = 'cTestMsgsIn%d' % (idTestResult,);
+ if sCountName not in dCounts:
+ self._oDb.execute('SELECT COUNT(idTestResultMsg)\n'
+ 'FROM TestResultMsgs\n'
+ 'WHERE idTestResult = %s\n'
+ , ( idTestResult,));
+ dCounts[sCountName] = self._oDb.fetchOne()[0];
+ dCounts[sCountName] += 1;
+ if dCounts[sCountName] > config.g_kcMaxTestMsgsPerTR:
+ raise TestResultHangingOffence('Too many messages under for one test result!');
+
+ if len(sText) > config.g_kcchMaxTestMsg:
+ raise TestResultHangingOffence('Failure details message is too long: %d chars - "%s"' % (len(sText), sText));
+
+ #
+ # Do the job.
+ #
+ idStrMsg = self.strTabString(sText, fCommit);
+ if tsCreated is None:
+ self._oDb.execute('INSERT INTO TestResultMsgs (\n'
+ ' idTestResult,\n'
+ ' idTestSet,\n'
+ ' idStrMsg,\n'
+ ' enmLevel)\n'
+ 'VALUES ( %s, %s, %s, %s)\n'
+ , ( idTestResult, idTestSet, idStrMsg, 'failure',) );
+ else:
+ self._oDb.execute('INSERT INTO TestResultMsgs (\n'
+ ' idTestResult,\n'
+ ' idTestSet,\n'
+ ' tsCreated,\n'
+ ' idStrMsg,\n'
+ ' enmLevel)\n'
+ 'VALUES ( %s, %s, TIMESTAMP WITH TIME ZONE %s, %s, %s)\n'
+ , ( idTestResult, idTestSet, tsCreated, idStrMsg, 'failure',) );
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+
+ def _completeTestResults(self, oTestResult, tsDone, enmStatus, cErrors = 0, fCommit = False):
+ """
+ Completes a test result. Updates the oTestResult object.
+ May raise exception on database error.
+ """
+ self._oDb.dprint('** _completeTestResults: cErrors=%s tsDone=%s enmStatus=%s oTestResults=\n%s'
+ % (cErrors, tsDone, enmStatus, oTestResult,));
+
+ #
+ # Sanity check: No open sub tests (aoStack should make sure about this!).
+ #
+ self._oDb.execute('SELECT COUNT(idTestResult)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestResultParent = %s\n'
+ ' AND enmStatus = %s\n'
+ , ( oTestResult.idTestResult, TestResultData.ksTestStatus_Running,));
+ cOpenSubTest = self._oDb.fetchOne()[0];
+ assert cOpenSubTest == 0, 'cOpenSubTest=%d - %s' % (cOpenSubTest, oTestResult,);
+ assert oTestResult.enmStatus == TestResultData.ksTestStatus_Running;
+
+ #
+ # Make sure the reporter isn't lying about successes or error counts.
+ #
+ self._oDb.execute('SELECT COALESCE(SUM(cErrors), 0)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestResultParent = %s\n'
+ , ( oTestResult.idTestResult, ));
+ cMinErrors = self._oDb.fetchOne()[0] + oTestResult.cErrors;
+ cErrors = max(cErrors, cMinErrors);
+ if cErrors > 0 and enmStatus == TestResultData.ksTestStatus_Success:
+ enmStatus = TestResultData.ksTestStatus_Failure
+
+ #
+ # Do the update.
+ #
+ if tsDone is None:
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET cErrors = %s,\n'
+ ' enmStatus = %s,\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated\n'
+ 'WHERE idTestResult = %s\n'
+ 'RETURNING tsElapsed'
+ , ( cErrors, enmStatus, oTestResult.idTestResult,) );
+ else:
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET cErrors = %s,\n'
+ ' enmStatus = %s,\n'
+ ' tsElapsed = TIMESTAMP WITH TIME ZONE %s - tsCreated\n'
+ 'WHERE idTestResult = %s\n'
+ 'RETURNING tsElapsed'
+ , ( cErrors, enmStatus, tsDone, oTestResult.idTestResult,) );
+
+ oTestResult.tsElapsed = self._oDb.fetchOne()[0];
+ oTestResult.enmStatus = enmStatus;
+ oTestResult.cErrors = cErrors;
+
+ self._oDb.maybeCommit(fCommit);
+ return None;
+
+ def _doPopHint(self, aoStack, cStackEntries, dCounts, idTestSet):
+ """ Executes a PopHint. """
+ assert cStackEntries >= 0;
+ while len(aoStack) > cStackEntries:
+ if aoStack[0].enmStatus == TestResultData.ksTestStatus_Running:
+ self._newFailureDetails(aoStack[0].idTestResult, idTestSet, 'XML error: Missing </Test>', dCounts);
+ self._completeTestResults(aoStack[0], tsDone = None, cErrors = 1,
+ enmStatus = TestResultData.ksTestStatus_Failure, fCommit = True);
+ aoStack.pop(0);
+ return True;
+
+
+ @staticmethod
+ def _validateElement(sName, dAttribs, fClosed):
+ """
+ Validates an element and its attributes.
+ """
+
+ #
+ # Validate attributes by name.
+ #
+
+ # Validate integer attributes.
+ for sAttr in [ 'errors', 'testdepth' ]:
+ if sAttr in dAttribs:
+ try:
+ _ = int(dAttribs[sAttr]);
+ except:
+ return 'Element %s has an invalid %s attribute value: %s.' % (sName, sAttr, dAttribs[sAttr],);
+
+ # Validate long attributes.
+ for sAttr in [ 'value', ]:
+ if sAttr in dAttribs:
+ try:
+ _ = long(dAttribs[sAttr]); # pylint: disable=redefined-variable-type
+ except:
+ return 'Element %s has an invalid %s attribute value: %s.' % (sName, sAttr, dAttribs[sAttr],);
+
+ # Validate string attributes.
+ for sAttr in [ 'name', 'text' ]: # 'unit' can be zero length.
+ if sAttr in dAttribs and not dAttribs[sAttr]:
+ return 'Element %s has an empty %s attribute value.' % (sName, sAttr,);
+
+ # Validate the timestamp attribute.
+ if 'timestamp' in dAttribs:
+ (dAttribs['timestamp'], sError) = ModelDataBase.validateTs(dAttribs['timestamp'], fAllowNull = False);
+ if sError is not None:
+ return 'Element %s has an invalid timestamp ("%s"): %s' % (sName, dAttribs['timestamp'], sError,);
+
+
+ #
+ # Check that attributes that are required are present.
+ # We ignore extra attributes.
+ #
+ dElementAttribs = \
+ {
+ 'Test': [ 'timestamp', 'name', ],
+ 'Value': [ 'timestamp', 'name', 'unit', 'value', ],
+ 'FailureDetails': [ 'timestamp', 'text', ],
+ 'Passed': [ 'timestamp', ],
+ 'Skipped': [ 'timestamp', ],
+ 'Failed': [ 'timestamp', 'errors', ],
+ 'TimedOut': [ 'timestamp', 'errors', ],
+ 'End': [ 'timestamp', ],
+ 'PushHint': [ 'testdepth', ],
+ 'PopHint': [ 'testdepth', ],
+ };
+ if sName not in dElementAttribs:
+ return 'Unknown element "%s".' % (sName,);
+ for sAttr in dElementAttribs[sName]:
+ if sAttr not in dAttribs:
+ return 'Element %s requires attribute "%s".' % (sName, sAttr);
+
+ #
+ # Only the Test element can (and must) remain open.
+ #
+ if sName == 'Test' and fClosed:
+ return '<Test/> is not allowed.';
+ if sName != 'Test' and not fClosed:
+ return 'All elements except <Test> must be closed.';
+
+ return None;
+
+ @staticmethod
+ def _parseElement(sElement):
+ """
+ Parses an element.
+
+ """
+ #
+ # Element level bits.
+ #
+ sName = sElement.split()[0];
+ sElement = sElement[len(sName):];
+
+ fClosed = sElement[-1] == '/';
+ if fClosed:
+ sElement = sElement[:-1];
+
+ #
+ # Attributes.
+ #
+ sError = None;
+ dAttribs = {};
+ sElement = sElement.strip();
+ while sElement:
+ # Extract attribute name.
+ off = sElement.find('=');
+ if off < 0 or not sElement[:off].isalnum():
+ sError = 'Attributes shall have alpha numberical names and have values.';
+ break;
+ sAttr = sElement[:off];
+
+ # Extract attribute value.
+ if off + 2 >= len(sElement) or sElement[off + 1] != '"':
+ sError = 'Attribute (%s) value is missing or not in double quotes.' % (sAttr,);
+ break;
+ off += 2;
+ offEndQuote = sElement.find('"', off);
+ if offEndQuote < 0:
+ sError = 'Attribute (%s) value is missing end quotation mark.' % (sAttr,);
+ break;
+ sValue = sElement[off:offEndQuote];
+
+ # Check for duplicates.
+ if sAttr in dAttribs:
+ sError = 'Attribute "%s" appears more than once.' % (sAttr,);
+ break;
+
+ # Unescape the value.
+ sValue = sValue.replace('&lt;', '<');
+ sValue = sValue.replace('&gt;', '>');
+ sValue = sValue.replace('&apos;', '\'');
+ sValue = sValue.replace('&quot;', '"');
+ sValue = sValue.replace('&#xA;', '\n');
+ sValue = sValue.replace('&#xD;', '\r');
+ sValue = sValue.replace('&amp;', '&'); # last
+
+ # Done.
+ dAttribs[sAttr] = sValue;
+
+ # advance
+ sElement = sElement[offEndQuote + 1:];
+ sElement = sElement.lstrip();
+
+ #
+ # Validate the element before we return.
+ #
+ if sError is None:
+ sError = TestResultLogic._validateElement(sName, dAttribs, fClosed);
+
+ return (sName, dAttribs, sError)
+
+ def _handleElement(self, sName, dAttribs, idTestSet, aoStack, aaiHints, dCounts):
+ """
+ Worker for processXmlStream that handles one element.
+
+ Returns None on success, error string on bad XML or similar.
+ Raises exception on hanging offence and on database error.
+ """
+ if sName == 'Test':
+ iNestingDepth = aoStack[0].iNestingDepth + 1 if aoStack else 0;
+ aoStack.insert(0, self._newTestResult(idTestResultParent = aoStack[0].idTestResult, idTestSet = idTestSet,
+ tsCreated = dAttribs['timestamp'], sName = dAttribs['name'],
+ iNestingDepth = iNestingDepth, dCounts = dCounts, fCommit = True) );
+
+ elif sName == 'Value':
+ self._newTestValue(idTestResult = aoStack[0].idTestResult, idTestSet = idTestSet, tsCreated = dAttribs['timestamp'],
+ sName = dAttribs['name'], sUnit = dAttribs['unit'], lValue = long(dAttribs['value']),
+ dCounts = dCounts, fCommit = True);
+
+ elif sName == 'FailureDetails':
+ self._newFailureDetails(idTestResult = aoStack[0].idTestResult, idTestSet = idTestSet,
+ tsCreated = dAttribs['timestamp'], sText = dAttribs['text'], dCounts = dCounts,
+ fCommit = True);
+
+ elif sName == 'Passed':
+ self._completeTestResults(aoStack[0], tsDone = dAttribs['timestamp'],
+ enmStatus = TestResultData.ksTestStatus_Success, fCommit = True);
+
+ elif sName == 'Skipped':
+ self._completeTestResults(aoStack[0], tsDone = dAttribs['timestamp'],
+ enmStatus = TestResultData.ksTestStatus_Skipped, fCommit = True);
+
+ elif sName == 'Failed':
+ self._completeTestResults(aoStack[0], tsDone = dAttribs['timestamp'], cErrors = int(dAttribs['errors']),
+ enmStatus = TestResultData.ksTestStatus_Failure, fCommit = True);
+
+ elif sName == 'TimedOut':
+ self._completeTestResults(aoStack[0], tsDone = dAttribs['timestamp'], cErrors = int(dAttribs['errors']),
+ enmStatus = TestResultData.ksTestStatus_TimedOut, fCommit = True);
+
+ elif sName == 'End':
+ self._completeTestResults(aoStack[0], tsDone = dAttribs['timestamp'],
+ cErrors = int(dAttribs.get('errors', '1')),
+ enmStatus = TestResultData.ksTestStatus_Success, fCommit = True);
+
+ elif sName == 'PushHint':
+ if len(aaiHints) > 1:
+ return 'PushHint cannot be nested.'
+
+ aaiHints.insert(0, [len(aoStack), int(dAttribs['testdepth'])]);
+
+ elif sName == 'PopHint':
+ if not aaiHints:
+ return 'No hint to pop.'
+
+ iDesiredTestDepth = int(dAttribs['testdepth']);
+ cStackEntries, iTestDepth = aaiHints.pop(0);
+ self._doPopHint(aoStack, cStackEntries, dCounts, idTestSet); # Fake the necessary '<End/></Test>' tags.
+ if iDesiredTestDepth != iTestDepth:
+ return 'PopHint tag has different testdepth: %d, on stack %d.' % (iDesiredTestDepth, iTestDepth);
+ else:
+ return 'Unexpected element "%s".' % (sName,);
+ return None;
+
+
+ def processXmlStream(self, sXml, idTestSet):
+ """
+ Processes the "XML" stream section given in sXml.
+
+ The sXml isn't a complete XML document, even should we save up all sXml
+ for a given set, they may not form a complete and well formed XML
+ document since the test may be aborted, abend or simply be buggy. We
+ therefore do our own parsing and treat the XML tags as commands more
+ than anything else.
+
+ Returns (sError, fUnforgivable), where sError is None on success.
+ May raise database exception.
+ """
+ aoStack = self._getResultStack(idTestSet); # [0] == top; [-1] == bottom.
+ if not aoStack:
+ return ('No open results', True);
+ self._oDb.dprint('** processXmlStream len(aoStack)=%s' % (len(aoStack),));
+ #self._oDb.dprint('processXmlStream: %s' % (self._stringifyStack(aoStack),));
+ #self._oDb.dprint('processXmlStream: sXml=%s' % (sXml,));
+
+ dCounts = {};
+ aaiHints = [];
+ sError = None;
+
+ fExpectCloseTest = False;
+ sXml = sXml.strip();
+ while sXml:
+ if sXml.startswith('</Test>'): # Only closing tag.
+ offNext = len('</Test>');
+ if len(aoStack) <= 1:
+ sError = 'Trying to close the top test results.'
+ break;
+ # ASSUMES that we've just seen an <End/>, <Passed/>, <Failed/>,
+ # <TimedOut/> or <Skipped/> tag earlier in this call!
+ if aoStack[0].enmStatus == TestResultData.ksTestStatus_Running or not fExpectCloseTest:
+ sError = 'Missing <End/>, <Passed/>, <Failed/>, <TimedOut/> or <Skipped/> tag.';
+ break;
+ aoStack.pop(0);
+ fExpectCloseTest = False;
+
+ elif fExpectCloseTest:
+ sError = 'Expected </Test>.'
+ break;
+
+ elif sXml.startswith('<?xml '): # Ignore (included files).
+ offNext = sXml.find('?>');
+ if offNext < 0:
+ sError = 'Unterminated <?xml ?> element.';
+ break;
+ offNext += 2;
+
+ elif sXml[0] == '<':
+ # Parse and check the tag.
+ if not sXml[1].isalpha():
+ sError = 'Malformed element.';
+ break;
+ offNext = sXml.find('>')
+ if offNext < 0:
+ sError = 'Unterminated element.';
+ break;
+ (sName, dAttribs, sError) = self._parseElement(sXml[1:offNext]);
+ offNext += 1;
+ if sError is not None:
+ break;
+
+ # Handle it.
+ try:
+ sError = self._handleElement(sName, dAttribs, idTestSet, aoStack, aaiHints, dCounts);
+ except TestResultHangingOffence as oXcpt:
+ self._inhumeTestResults(aoStack, idTestSet, str(oXcpt));
+ return (str(oXcpt), True);
+
+
+ fExpectCloseTest = sName in [ 'End', 'Passed', 'Failed', 'TimedOut', 'Skipped', ];
+ else:
+ sError = 'Unexpected content.';
+ break;
+
+ # Advance.
+ sXml = sXml[offNext:];
+ sXml = sXml.lstrip();
+
+ #
+ # Post processing checks.
+ #
+ if sError is None and fExpectCloseTest:
+ sError = 'Expected </Test> before the end of the XML section.'
+ elif sError is None and aaiHints:
+ sError = 'Expected </PopHint> before the end of the XML section.'
+ if aaiHints:
+ self._doPopHint(aoStack, aaiHints[-1][0], dCounts, idTestSet);
+
+ #
+ # Log the error.
+ #
+ if sError is not None:
+ SystemLogLogic(self._oDb).addEntry(SystemLogData.ksEvent_XmlResultMalformed,
+ 'idTestSet=%s idTestResult=%s XML="%s" %s'
+ % ( idTestSet,
+ aoStack[0].idTestResult if aoStack else -1,
+ sXml[:min(len(sXml), 30)],
+ sError, ),
+ cHoursRepeat = 6, fCommit = True);
+ return (sError, False);
+
+
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestResultDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestResultData(),];
+
+class TestResultValueDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestResultValueData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/testset.py b/src/VBox/ValidationKit/testmanager/core/testset.py
new file mode 100755
index 00000000..e9e408fe
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/testset.py
@@ -0,0 +1,869 @@
+# -*- coding: utf-8 -*-
+# $Id: testset.py $
+
+"""
+Test Manager - TestSet.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os;
+import zipfile;
+import unittest;
+
+# Validation Kit imports.
+from common import utils;
+from testmanager import config;
+from testmanager.core import db;
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, \
+ TMExceptionBase, TMTooManyRows, TMRowNotFound;
+from testmanager.core.testbox import TestBoxData;
+from testmanager.core.testresults import TestResultFileDataEx;
+
+
+class TestSetData(ModelDataBase):
+ """
+ TestSet Data.
+ """
+
+ ## @name TestStatus_T
+ # @{
+ ksTestStatus_Running = 'running';
+ ksTestStatus_Success = 'success';
+ ksTestStatus_Skipped = 'skipped';
+ ksTestStatus_BadTestBox = 'bad-testbox';
+ ksTestStatus_Aborted = 'aborted';
+ ksTestStatus_Failure = 'failure';
+ ksTestStatus_TimedOut = 'timed-out';
+ ksTestStatus_Rebooted = 'rebooted';
+ ## @}
+
+ ## List of relatively harmless (to testgroup/case) statuses.
+ kasHarmlessTestStatuses = [ ksTestStatus_Skipped, ksTestStatus_BadTestBox, ksTestStatus_Aborted, ];
+ ## List of bad statuses.
+ kasBadTestStatuses = [ ksTestStatus_Failure, ksTestStatus_TimedOut, ksTestStatus_Rebooted, ];
+
+ ksIdAttr = 'idTestSet';
+
+ ksParam_idTestSet = 'TestSet_idTestSet';
+ ksParam_tsConfig = 'TestSet_tsConfig';
+ ksParam_tsCreated = 'TestSet_tsCreated';
+ ksParam_tsDone = 'TestSet_tsDone';
+ ksParam_enmStatus = 'TestSet_enmStatus';
+ ksParam_idBuild = 'TestSet_idBuild';
+ ksParam_idBuildCategory = 'TestSet_idBuildCategory';
+ ksParam_idBuildTestSuite = 'TestSet_idBuildTestSuite';
+ ksParam_idGenTestBox = 'TestSet_idGenTestBox';
+ ksParam_idTestBox = 'TestSet_idTestBox';
+ ksParam_idSchedGroup = 'TestSet_idSchedGroup';
+ ksParam_idTestGroup = 'TestSet_idTestGroup';
+ ksParam_idGenTestCase = 'TestSet_idGenTestCase';
+ ksParam_idTestCase = 'TestSet_idTestCase';
+ ksParam_idGenTestCaseArgs = 'TestSet_idGenTestCaseArgs';
+ ksParam_idTestCaseArgs = 'TestSet_idTestCaseArgs';
+ ksParam_idTestResult = 'TestSet_idTestResult';
+ ksParam_sBaseFilename = 'TestSet_sBaseFilename';
+ ksParam_iGangMemberNo = 'TestSet_iGangMemberNo';
+ ksParam_idTestSetGangLeader = 'TestSet_idTestSetGangLeader';
+
+ kasAllowNullAttributes = [ 'tsDone', 'idBuildTestSuite', 'idTestSetGangLeader' ];
+ kasValidValues_enmStatus = [
+ ksTestStatus_Running,
+ ksTestStatus_Success,
+ ksTestStatus_Skipped,
+ ksTestStatus_BadTestBox,
+ ksTestStatus_Aborted,
+ ksTestStatus_Failure,
+ ksTestStatus_TimedOut,
+ ksTestStatus_Rebooted,
+ ];
+ kiMin_iGangMemberNo = 0;
+ kiMax_iGangMemberNo = 1023;
+
+
+ kcDbColumns = 20;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.idTestSet = None;
+ self.tsConfig = None;
+ self.tsCreated = None;
+ self.tsDone = None;
+ self.enmStatus = 'running';
+ self.idBuild = None;
+ self.idBuildCategory = None;
+ self.idBuildTestSuite = None;
+ self.idGenTestBox = None;
+ self.idTestBox = None;
+ self.idSchedGroup = None;
+ self.idTestGroup = None;
+ self.idGenTestCase = None;
+ self.idTestCase = None;
+ self.idGenTestCaseArgs = None;
+ self.idTestCaseArgs = None;
+ self.idTestResult = None;
+ self.sBaseFilename = None;
+ self.iGangMemberNo = 0;
+ self.idTestSetGangLeader = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Internal worker for initFromDbWithId and initFromDbWithGenId as well as
+ TestBoxSetLogic.
+ """
+
+ if aoRow is None:
+ raise TMRowNotFound('TestSet not found.');
+
+ self.idTestSet = aoRow[0];
+ self.tsConfig = aoRow[1];
+ self.tsCreated = aoRow[2];
+ self.tsDone = aoRow[3];
+ self.enmStatus = aoRow[4];
+ self.idBuild = aoRow[5];
+ self.idBuildCategory = aoRow[6];
+ self.idBuildTestSuite = aoRow[7];
+ self.idGenTestBox = aoRow[8];
+ self.idTestBox = aoRow[9];
+ self.idSchedGroup = aoRow[10];
+ self.idTestGroup = aoRow[11];
+ self.idGenTestCase = aoRow[12];
+ self.idTestCase = aoRow[13];
+ self.idGenTestCaseArgs = aoRow[14];
+ self.idTestCaseArgs = aoRow[15];
+ self.idTestResult = aoRow[16];
+ self.sBaseFilename = aoRow[17];
+ self.iGangMemberNo = aoRow[18];
+ self.idTestSetGangLeader = aoRow[19];
+ return self;
+
+
+ def initFromDbWithId(self, oDb, idTestSet):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute('SELECT *\n'
+ 'FROM TestSets\n'
+ 'WHERE idTestSet = %s\n'
+ , (idTestSet, ) );
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('idTestSet=%s not found' % (idTestSet,));
+ return self.initFromDbRow(aoRow);
+
+
+ def openFile(self, sFilename, sMode = 'rb'):
+ """
+ Opens a file.
+
+ Returns (oFile, cbFile, fIsStream) on success.
+ Returns (None, sErrorMsg, None) on failure.
+ Will not raise exceptions, unless the class instance is invalid.
+ """
+ assert sMode in [ 'rb', 'r', 'rU' ];
+
+ # Try raw file first.
+ sFile1 = os.path.join(config.g_ksFileAreaRootDir, self.sBaseFilename + '-' + sFilename);
+ try:
+ oFile = open(sFile1, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+ return (oFile, os.fstat(oFile.fileno()).st_size, False);
+ except Exception as oXcpt1:
+ # Try the zip archive next.
+ sFile2 = os.path.join(config.g_ksZipFileAreaRootDir, self.sBaseFilename + '.zip');
+ try:
+ oZipFile = zipfile.ZipFile(sFile2, 'r'); # pylint: disable=consider-using-with
+ oFile = oZipFile.open(sFilename, sMode if sMode != 'rb' else 'r'); # pylint: disable=consider-using-with
+ cbFile = oZipFile.getinfo(sFilename).file_size;
+ return (oFile, cbFile, True);
+ except Exception as oXcpt2:
+ # Construct a meaningful error message.
+ try:
+ if os.path.exists(sFile1):
+ return (None, 'Error opening "%s": %s' % (sFile1, oXcpt1), None);
+ if not os.path.exists(sFile2):
+ return (None, 'File "%s" not found. [%s, %s]' % (sFilename, sFile1, sFile2,), None);
+ return (None, 'Error opening "%s" inside "%s": %s' % (sFilename, sFile2, oXcpt2), None);
+ except Exception as oXcpt3:
+ return (None, 'OMG! %s; %s; %s' % (oXcpt1, oXcpt2, oXcpt3,), None);
+ return (None, 'Code not reachable!', None);
+
+ def createFile(self, sFilename, sMode = 'wb'):
+ """
+ Creates a new file.
+
+ Returns oFile on success.
+ Returns sErrorMsg on failure.
+ """
+ assert sMode in [ 'wb', 'w', 'wU' ];
+
+ # Try raw file first.
+ sFile1 = os.path.join(config.g_ksFileAreaRootDir, self.sBaseFilename + '-' + sFilename);
+ try:
+ if not os.path.exists(os.path.dirname(sFile1)):
+ os.makedirs(os.path.dirname(sFile1), 0o755);
+ oFile = open(sFile1, sMode); # pylint: disable=consider-using-with,unspecified-encoding
+ except Exception as oXcpt1:
+ return str(oXcpt1);
+ return oFile;
+
+ @staticmethod
+ def findLogOffsetForTimestamp(sLogContent, tsTimestamp, offStart = 0, fAfter = False):
+ """
+ Log parsing utility function for finding the offset for the given timestamp.
+
+ We ASSUME the log lines are prefixed with UTC timestamps on the format
+ '09:43:55.789353'.
+
+ Return index into the sLogContent string, 0 if not found.
+ """
+ # Turn tsTimestamp into a string compatible with what we expect to find in the log.
+ oTsZulu = db.dbTimestampToZuluDatetime(tsTimestamp);
+ sWantedTs = oTsZulu.strftime('%H:%M:%S.%f');
+ assert len(sWantedTs) == 15;
+
+ # Now loop thru the string, line by line.
+ offRet = offStart;
+ off = offStart;
+ while True:
+ sThisTs = sLogContent[off : off + 15];
+ if len(sThisTs) >= 15 \
+ and sThisTs[2] == ':' \
+ and sThisTs[5] == ':' \
+ and sThisTs[8] == '.' \
+ and sThisTs[14] in '0123456789':
+ if sThisTs < sWantedTs:
+ offRet = off;
+ elif sThisTs == sWantedTs:
+ if not fAfter:
+ return off;
+ offRet = off;
+ else:
+ if fAfter:
+ offRet = off;
+ break;
+
+ # next line.
+ off = sLogContent.find('\n', off);
+ if off < 0:
+ if fAfter:
+ offRet = len(sLogContent);
+ break;
+ off += 1;
+
+ return offRet;
+
+ @staticmethod
+ def extractLogSection(sLogContent, tsStart, tsLast):
+ """
+ Returns log section from tsStart to tsLast (or all if we cannot make sense of it).
+ """
+ offStart = TestSetData.findLogOffsetForTimestamp(sLogContent, tsStart);
+ offEnd = TestSetData.findLogOffsetForTimestamp(sLogContent, tsLast, offStart, fAfter = True);
+ return sLogContent[offStart : offEnd];
+
+ @staticmethod
+ def extractLogSectionElapsed(sLogContent, tsStart, tsElapsed):
+ """
+ Returns log section from tsStart and tsElapsed forward (or all if we cannot make sense of it).
+ """
+ tsStart = db.dbTimestampToZuluDatetime(tsStart);
+ tsLast = tsStart + tsElapsed;
+ return TestSetData.extractLogSection(sLogContent, tsStart, tsLast);
+
+
+
+class TestSetLogic(ModelLogicBase):
+ """
+ TestSet logic.
+ """
+
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb);
+
+
+ def tryFetch(self, idTestSet):
+ """
+ Attempts to fetch a test set.
+
+ Returns a TestSetData object on success.
+ Returns None if no status was found.
+ Raises exception on other errors.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestSets\n'
+ 'WHERE idTestSet = %s\n',
+ (idTestSet,));
+ if self._oDb.getRowCount() == 0:
+ return None;
+ oData = TestSetData();
+ return oData.initFromDbRow(self._oDb.fetchOne());
+
+ def strTabString(self, sString, fCommit = False):
+ """
+ Gets the string table id for the given string, adding it if new.
+ """
+ ## @todo move this and make a stored procedure for it.
+ self._oDb.execute('SELECT idStr\n'
+ 'FROM TestResultStrTab\n'
+ 'WHERE sValue = %s'
+ , (sString,));
+ if self._oDb.getRowCount() == 0:
+ self._oDb.execute('INSERT INTO TestResultStrTab (sValue)\n'
+ 'VALUES (%s)\n'
+ 'RETURNING idStr\n'
+ , (sString,));
+ if fCommit:
+ self._oDb.commit();
+ return self._oDb.fetchOne()[0];
+
+ def complete(self, idTestSet, sStatus, fCommit = False):
+ """
+ Completes the testset.
+ Returns the test set ID of the gang leader, None if no gang involvement.
+ Raises exceptions on database errors and invalid input.
+ """
+
+ assert sStatus != TestSetData.ksTestStatus_Running;
+
+ #
+ # Get the basic test set data and check if there is anything to do here.
+ #
+ oData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ if oData.enmStatus != TestSetData.ksTestStatus_Running:
+ raise TMExceptionBase('TestSet %s is already completed as %s.' % (idTestSet, oData.enmStatus));
+ if oData.idTestResult is None:
+ raise self._oDb.integrityException('idTestResult is NULL for TestSet %u' % (idTestSet,));
+
+ #
+ # Close open sub test results, count these as errors.
+ # Note! No need to propagate error counts here. Only one tree line will
+ # have open sets, and it will go all the way to the root.
+ #
+ self._oDb.execute('SELECT idTestResult\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = %s\n'
+ ' AND idTestResult <> %s\n'
+ 'ORDER BY idTestResult DESC\n'
+ , (idTestSet, TestSetData.ksTestStatus_Running, oData.idTestResult));
+ aaoRows = self._oDb.fetchAll();
+ if aaoRows:
+ idStr = self.strTabString('Unclosed test result', fCommit = fCommit);
+ for aoRow in aaoRows:
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET enmStatus = \'failure\',\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated,\n'
+ ' cErrors = cErrors + 1\n'
+ 'WHERE idTestResult = %s\n'
+ , (aoRow[0],));
+ self._oDb.execute('INSERT INTO TestResultMsgs (idTestResult, idTestSet, idStrMsg, enmLevel)\n'
+ 'VALUES ( %s, %s, %s, \'failure\'::TestResultMsgLevel_T)\n'
+ , (aoRow[0], idTestSet, idStr,));
+
+ #
+ # If it's a success result, check it against error counters.
+ #
+ if sStatus not in TestSetData.kasBadTestStatuses:
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND cErrors > 0\n'
+ , (idTestSet,));
+ cErrors = self._oDb.fetchOne()[0];
+ if cErrors > 0:
+ sStatus = TestSetData.ksTestStatus_Failure;
+
+ #
+ # If it's an pure 'failure', check for timeouts and propagate it.
+ #
+ if sStatus == TestSetData.ksTestStatus_Failure:
+ self._oDb.execute('SELECT COUNT(*)\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = %s\n'
+ , ( idTestSet, TestSetData.ksTestStatus_TimedOut, ));
+ if self._oDb.fetchOne()[0] > 0:
+ sStatus = TestSetData.ksTestStatus_TimedOut;
+
+ #
+ # Complete the top level test result and then the test set.
+ #
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET cErrors = (SELECT COALESCE(SUM(cErrors), 0)\n'
+ ' FROM TestResults\n'
+ ' WHERE idTestResultParent = %s)\n'
+ 'WHERE idTestResult = %s\n'
+ 'RETURNING cErrors\n'
+ , (oData.idTestResult, oData.idTestResult));
+ cErrors = self._oDb.fetchOne()[0];
+ if cErrors == 0 and sStatus in TestSetData.kasBadTestStatuses:
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET cErrors = 1\n'
+ 'WHERE idTestResult = %s\n'
+ , (oData.idTestResult,));
+ elif cErrors > 0 and sStatus not in TestSetData.kasBadTestStatuses:
+ sStatus = TestSetData.ksTestStatus_Failure; # Impossible.
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET enmStatus = %s,\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated\n'
+ 'WHERE idTestResult = %s\n'
+ , (sStatus, oData.idTestResult,));
+
+ self._oDb.execute('UPDATE TestSets\n'
+ 'SET enmStatus = %s,\n'
+ ' tsDone = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestSet = %s\n'
+ , (sStatus, idTestSet,));
+
+ self._oDb.maybeCommit(fCommit);
+ return oData.idTestSetGangLeader;
+
+ def completeAsAbandoned(self, idTestSet, fCommit = False):
+ """
+ Completes the testset as abandoned if necessary.
+
+ See scenario #9:
+ file://../../docs/AutomaticTestingRevamp.html#cleaning-up-abandond-testcase
+
+ Returns True if successfully completed as abandond, False if it's already
+ completed, and raises exceptions under exceptional circumstances.
+ """
+
+ #
+ # Get the basic test set data and check if there is anything to do here.
+ #
+ oData = self.tryFetch(idTestSet);
+ if oData is None:
+ return False;
+ if oData.enmStatus != TestSetData.ksTestStatus_Running:
+ return False;
+
+ if oData.idTestResult is not None:
+ #
+ # Clean up test results, adding a message why they failed.
+ #
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET enmStatus = \'failure\',\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated,\n'
+ ' cErrors = cErrors + 1\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ , (idTestSet,));
+
+ idStr = self.strTabString('The test was abandond by the testbox', fCommit = fCommit);
+ self._oDb.execute('INSERT INTO TestResultMsgs (idTestResult, idTestSet, idStrMsg, enmLevel)\n'
+ 'VALUES ( %s, %s, %s, \'failure\'::TestResultMsgLevel_T)\n'
+ , (oData.idTestResult, idTestSet, idStr,));
+
+ #
+ # Complete the testset.
+ #
+ self._oDb.execute('UPDATE TestSets\n'
+ 'SET enmStatus = \'failure\',\n'
+ ' tsDone = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ , (idTestSet,));
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def completeAsGangGatheringTimeout(self, idTestSet, fCommit = False):
+ """
+ Completes the testset with a gang-gathering timeout.
+ Raises exceptions on database errors and invalid input.
+ """
+ #
+ # Get the basic test set data and check if there is anything to do here.
+ #
+ oData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ if oData.enmStatus != TestSetData.ksTestStatus_Running:
+ raise TMExceptionBase('TestSet %s is already completed as %s.' % (idTestSet, oData.enmStatus));
+ if oData.idTestResult is None:
+ raise self._oDb.integrityException('idTestResult is NULL for TestSet %u' % (idTestSet,));
+
+ #
+ # Complete the top level test result and then the test set.
+ #
+ self._oDb.execute('UPDATE TestResults\n'
+ 'SET enmStatus = \'failure\',\n'
+ ' tsElapsed = CURRENT_TIMESTAMP - tsCreated,\n'
+ ' cErrors = cErrors + 1\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ , (idTestSet,));
+
+ idStr = self.strTabString('Gang gathering timed out', fCommit = fCommit);
+ self._oDb.execute('INSERT INTO TestResultMsgs (idTestResult, idTestSet, idStrMsg, enmLevel)\n'
+ 'VALUES ( %s, %s, %s, \'failure\'::TestResultMsgLevel_T)\n'
+ , (oData.idTestResult, idTestSet, idStr,));
+
+ self._oDb.execute('UPDATE TestSets\n'
+ 'SET enmStatus = \'failure\',\n'
+ ' tsDone = CURRENT_TIMESTAMP\n'
+ 'WHERE idTestSet = %s\n'
+ , (idTestSet,));
+
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def createFile(self, oTestSet, sName, sMime, sKind, sDesc, cbFile, fCommit = False): # pylint: disable=too-many-locals
+ """
+ Creates a file and associating with the current test result record in
+ the test set.
+
+ Returns file object that the file content can be written to.
+ Raises exception on database error, I/O errors, if there are too many
+ files in the test set or if they take up too much disk space.
+
+ The caller (testboxdisp.py) is expected to do basic input validation,
+ so we skip that and get on with the bits only we can do.
+ """
+
+ #
+ # Furhter input and limit checks.
+ #
+ if oTestSet.enmStatus != TestSetData.ksTestStatus_Running:
+ raise TMExceptionBase('Cannot create files on a test set with status "%s".' % (oTestSet.enmStatus,));
+
+ self._oDb.execute('SELECT TestResultStrTab.sValue\n'
+ 'FROM TestResultFiles,\n'
+ ' TestResults,\n'
+ ' TestResultStrTab\n'
+ 'WHERE TestResults.idTestSet = %s\n'
+ ' AND TestResultFiles.idTestResult = TestResults.idTestResult\n'
+ ' AND TestResultStrTab.idStr = TestResultFiles.idStrFile\n'
+ , ( oTestSet.idTestSet,));
+ if self._oDb.getRowCount() + 1 > config.g_kcMaxUploads:
+ raise TMExceptionBase('Uploaded too many files already (%d).' % (self._oDb.getRowCount(),));
+
+ dFiles = {}
+ cbTotalFiles = 0;
+ for aoRow in self._oDb.fetchAll():
+ dFiles[aoRow[0].lower()] = 1; # For determining a unique filename further down.
+ sFile = os.path.join(config.g_ksFileAreaRootDir, oTestSet.sBaseFilename + '-' + aoRow[0]);
+ try:
+ cbTotalFiles += os.path.getsize(sFile);
+ except:
+ cbTotalFiles += config.g_kcMbMaxUploadSingle * 1048576;
+ if (cbTotalFiles + cbFile + 1048575) / 1048576 > config.g_kcMbMaxUploadTotal:
+ raise TMExceptionBase('Will exceed total upload limit: %u bytes + %u bytes > %s MiB.' \
+ % (cbTotalFiles, cbFile, config.g_kcMbMaxUploadTotal));
+
+ #
+ # Create a new file.
+ #
+ self._oDb.execute('SELECT idTestResult\n'
+ 'FROM TestResults\n'
+ 'WHERE idTestSet = %s\n'
+ ' AND enmStatus = \'running\'::TestStatus_T\n'
+ 'ORDER BY idTestResult DESC\n'
+ 'LIMIT 1\n'
+ % ( oTestSet.idTestSet, ));
+ if self._oDb.getRowCount() < 1:
+ raise TMExceptionBase('No open test results - someone committed a capital offence or we ran into a race.');
+ idTestResult = self._oDb.fetchOne()[0];
+
+ if sName.lower() in dFiles:
+ # Note! There is in theory a race here, but that's something the
+ # test driver doing parallel upload with non-unique names
+ # should worry about. The TD should always avoid this path.
+ sOrgName = sName;
+ for i in range(2, config.g_kcMaxUploads + 6):
+ sName = '%s-%s' % (i, sName,);
+ if sName not in dFiles:
+ break;
+ sName = None;
+ if sName is None:
+ raise TMExceptionBase('Failed to find unique name for %s.' % (sOrgName,));
+
+ self._oDb.execute('INSERT INTO TestResultFiles(idTestResult, idTestSet, idStrFile, idStrDescription,\n'
+ ' idStrKind, idStrMime)\n'
+ 'VALUES (%s, %s, %s, %s, %s, %s)\n'
+ , ( idTestResult,
+ oTestSet.idTestSet,
+ self.strTabString(sName),
+ self.strTabString(sDesc),
+ self.strTabString(sKind),
+ self.strTabString(sMime),
+ ));
+
+ oFile = oTestSet.createFile(sName, 'wb');
+ if utils.isString(oFile):
+ raise TMExceptionBase('Error creating "%s": %s' % (sName, oFile));
+ self._oDb.maybeCommit(fCommit);
+ return oFile;
+
+ def getGang(self, idTestSetGangLeader):
+ """
+ Returns an array of TestBoxData object representing the gang for the given testset.
+ """
+ self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+ 'FROM TestBoxesWithStrings,\n'
+ ' TestSets'
+ 'WHERE TestSets.idTestSetGangLeader = %s\n'
+ ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox\n'
+ 'ORDER BY iGangMemberNo ASC\n'
+ , ( idTestSetGangLeader,));
+ aaoRows = self._oDb.fetchAll();
+ aoTestBoxes = [];
+ for aoRow in aaoRows:
+ aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow));
+ return aoTestBoxes;
+
+ def getFile(self, idTestSet, idTestResultFile):
+ """
+ Gets the TestResultFileEx corresponding to idTestResultFile.
+
+ Raises an exception if the file wasn't found, doesn't belong to
+ idTestSet, and on DB error.
+ """
+ self._oDb.execute('SELECT TestResultFiles.*,\n'
+ ' StrTabFile.sValue AS sFile,\n'
+ ' StrTabDesc.sValue AS sDescription,\n'
+ ' StrTabKind.sValue AS sKind,\n'
+ ' StrTabMime.sValue AS sMime\n'
+ 'FROM TestResultFiles,\n'
+ ' TestResultStrTab AS StrTabFile,\n'
+ ' TestResultStrTab AS StrTabDesc,\n'
+ ' TestResultStrTab AS StrTabKind,\n'
+ ' TestResultStrTab AS StrTabMime,\n'
+ ' TestResults\n'
+ 'WHERE TestResultFiles.idTestResultFile = %s\n'
+ ' AND TestResultFiles.idStrFile = StrTabFile.idStr\n'
+ ' AND TestResultFiles.idStrDescription = StrTabDesc.idStr\n'
+ ' AND TestResultFiles.idStrKind = StrTabKind.idStr\n'
+ ' AND TestResultFiles.idStrMime = StrTabMime.idStr\n'
+ ' AND TestResults.idTestResult = TestResultFiles.idTestResult\n'
+ ' AND TestResults.idTestSet = %s\n'
+ , ( idTestResultFile, idTestSet, ));
+ return TestResultFileDataEx().initFromDbRow(self._oDb.fetchOne());
+
+
+ def getById(self, idTestSet):
+ """
+ Get TestSet table record by its id
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestSets\n'
+ 'WHERE idTestSet=%s\n',
+ (idTestSet,))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise TMTooManyRows('Found more than one test sets with the same credentials. Database structure is corrupted.')
+ try:
+ return TestSetData().initFromDbRow(aRows[0])
+ except IndexError:
+ return None
+
+
+ def fetchOrphaned(self):
+ """
+ Returns a list of TestSetData objects of orphaned test sets.
+
+ A test set is orphaned if tsDone is NULL and the testbox has created
+ one or more newer testsets.
+ """
+
+ self._oDb.execute('SELECT TestSets.*\n'
+ 'FROM TestSets,\n'
+ ' (SELECT idTestSet, idTestBox FROM TestSets WHERE tsDone is NULL) AS t\n'
+ 'WHERE TestSets.idTestSet = t.idTestSet\n'
+ ' AND EXISTS(SELECT 1 FROM TestSets st\n'
+ ' WHERE st.idTestBox = t.idTestBox AND st.idTestSet > t.idTestSet)\n'
+ ' AND NOT EXISTS(SELECT 1 FROM TestBoxStatuses tbs\n'
+ ' WHERE tbs.idTestBox = t.idTestBox AND tbs.idTestSet = t.idTestSet)\n'
+ 'ORDER by TestSets.idTestBox, TestSets.idTestSet'
+ );
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(TestSetData().initFromDbRow(aoRow));
+ return aoRet;
+
+ def fetchByAge(self, tsNow = None, cHoursBack = 24):
+ """
+ Returns a list of TestSetData objects of a given time period (default is 24 hours).
+
+ Returns None if no testsets stored,
+ Returns an empty list if no testsets found with given criteria.
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+
+ if self._oDb.getRowCount() == 0:
+ return None;
+
+ self._oDb.execute('(SELECT *\n'
+ ' FROM TestSets\n'
+ ' WHERE tsDone <= %s\n'
+ ' AND tsDone > (%s - interval \'%s hours\')\n'
+ ')\n'
+ , ( tsNow, tsNow, cHoursBack, ));
+
+ aoRet = [];
+ for aoRow in self._oDb.fetchAll():
+ aoRet.append(TestSetData().initFromDbRow(aoRow));
+ return aoRet;
+
+ def isTestBoxExecutingTooRapidly(self, idTestBox): ## s/To/Too/
+ """
+ Checks whether the specified test box is executing tests too rapidly.
+
+ The parameters defining too rapid execution are defined in config.py.
+
+ Returns True if it does, False if it doesn't.
+ May raise database problems.
+ """
+
+ self._oDb.execute('(\n'
+ 'SELECT tsCreated\n'
+ 'FROM TestSets\n'
+ 'WHERE idTestBox = %s\n'
+ ' AND tsCreated >= (CURRENT_TIMESTAMP - interval \'%s seconds\')\n'
+ ') UNION (\n'
+ 'SELECT tsCreated\n'
+ 'FROM TestSets\n'
+ 'WHERE idTestBox = %s\n'
+ ' AND tsCreated >= (CURRENT_TIMESTAMP - interval \'%s seconds\')\n'
+ ' AND enmStatus >= \'failure\'\n'
+ ')'
+ , ( idTestBox, config.g_kcSecMinSinceLastTask,
+ idTestBox, config.g_kcSecMinSinceLastFailedTask, ));
+ return self._oDb.getRowCount() > 0;
+
+
+ #
+ # The virtual test sheriff interface.
+ #
+
+ def fetchBadTestBoxIds(self, cHoursBack = 2, tsNow = None, aidFailureReasons = None):
+ """
+ Fetches a list of test box IDs which returned bad-testbox statuses in the
+ given period (tsDone).
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+ if aidFailureReasons is None:
+ aidFailureReasons = [ -1, ];
+ self._oDb.execute('(SELECT idTestBox\n'
+ ' FROM TestSets\n'
+ ' WHERE TestSets.enmStatus = \'bad-testbox\'\n'
+ ' AND tsDone <= %s\n'
+ ' AND tsDone > (%s - interval \'%s hours\')\n'
+ ') UNION (\n'
+ ' SELECT TestSets.idTestBox\n'
+ ' FROM TestSets,\n'
+ ' TestResultFailures\n'
+ ' WHERE TestSets.tsDone <= %s\n'
+ ' AND TestSets.tsDone > (%s - interval \'%s hours\')\n'
+ ' AND TestSets.enmStatus >= \'failure\'::TestStatus_T\n'
+ ' AND TestSets.idTestSet = TestResultFailures.idTestSet\n'
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND TestResultFailures.idFailureReason IN ('
+ + ', '.join([str(i) for i in aidFailureReasons]) + ')\n'
+ ')\n'
+ , ( tsNow, tsNow, cHoursBack,
+ tsNow, tsNow, cHoursBack, ));
+ return [aoRow[0] for aoRow in self._oDb.fetchAll()];
+
+ def fetchSetsForTestBox(self, idTestBox, cHoursBack = 2, tsNow = None):
+ """
+ Fetches the TestSet rows for idTestBox for the given period (tsDone), w/o running ones.
+
+ Returns list of TestSetData sorted by tsDone in descending order.
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('SELECT *\n'
+ 'FROM TestSets\n'
+ 'WHERE TestSets.idTestBox = %s\n'
+ ' AND tsDone IS NOT NULL\n'
+ ' AND tsDone <= %s\n'
+ ' AND tsDone > (%s - interval \'%s hours\')\n'
+ 'ORDER by tsDone DESC\n'
+ , ( idTestBox, tsNow, tsNow, cHoursBack,));
+ return self._dbRowsToModelDataList(TestSetData);
+
+ def fetchFailedSetsWithoutReason(self, cHoursBack = 2, tsNow = None):
+ """
+ Fetches the TestSet failure rows without any currently (CURRENT_TIMESTAMP
+ not tsNow) assigned failure reason.
+
+ Returns list of TestSetData sorted by tsDone in descending order.
+
+ Note! Includes bad-testbox sets too as it can be useful to analyze these
+ too even if we normally count them in the 'skipped' category.
+ """
+ if tsNow is None:
+ tsNow = self._oDb.getCurrentTimestamp();
+ self._oDb.execute('SELECT TestSets.*\n'
+ 'FROM TestSets\n'
+ ' LEFT OUTER JOIN TestResultFailures\n'
+ ' ON TestResultFailures.idTestSet = TestSets.idTestSet\n'
+ ' AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'WHERE TestSets.tsDone IS NOT NULL\n'
+ ' AND TestSets.enmStatus IN ( %s, %s, %s, %s )\n'
+ ' AND TestSets.tsDone <= %s\n'
+ ' AND TestSets.tsDone > (%s - interval \'%s hours\')\n'
+ ' AND TestResultFailures.idTestSet IS NULL\n'
+ 'ORDER by tsDone DESC\n'
+ , ( TestSetData.ksTestStatus_Failure, TestSetData.ksTestStatus_TimedOut,
+ TestSetData.ksTestStatus_Rebooted, TestSetData.ksTestStatus_BadTestBox,
+ tsNow,
+ tsNow, cHoursBack,));
+ return self._dbRowsToModelDataList(TestSetData);
+
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class TestSetDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [TestSetData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
diff --git a/src/VBox/ValidationKit/testmanager/core/useraccount.pgsql b/src/VBox/ValidationKit/testmanager/core/useraccount.pgsql
new file mode 100644
index 00000000..1abd8b71
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/useraccount.pgsql
@@ -0,0 +1,178 @@
+-- $Id: useraccount.pgsql $
+--- @file
+-- VBox Test Manager Database Stored Procedures - UserAccounts.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+---
+-- Checks if the user name and login name are unique, ignoring a_uidIgnore.
+-- Raises exception if duplicates are found.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION UserAccountLogic_checkUniqueUser(a_sUsername TEXT, a_sLoginName TEXT, a_uidIgnore INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ -- sUserName
+ SELECT COUNT(*) INTO v_cRows
+ FROM Users
+ WHERE sUsername = a_sUsername
+ AND tsExpire = 'infinity'::TIMESTAMP
+ AND uid <> a_uidIgnore;
+ IF v_cRows <> 0 THEN
+ RAISE EXCEPTION 'Duplicate user name "%" (% times)', a_sUsername, v_cRows;
+ END IF;
+
+ -- sLoginName
+ SELECT COUNT(*) INTO v_cRows
+ FROM Users
+ WHERE sLoginName = a_sLoginName
+ AND tsExpire = 'infinity'::TIMESTAMP
+ AND uid <> a_uidIgnore;
+ IF v_cRows <> 0 THEN
+ RAISE EXCEPTION 'Duplicate login name "%" (% times)', a_sUsername, v_cRows;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+---
+-- Check that the user account exists.
+-- Raises exception if it doesn't.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION UserAccountLogic_checkExists(a_uid INTEGER) RETURNS VOID AS $$
+ DECLARE
+ v_cUpdatedRows INTEGER;
+ BEGIN
+ IF NOT EXISTS( SELECT *
+ FROM Users
+ WHERE uid = a_uid
+ AND tsExpire = 'infinity'::TIMESTAMP ) THEN
+ RAISE EXCEPTION 'User with ID % does not currently exist', a_uid;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+---
+-- Historize a row.
+-- @internal
+--
+CREATE OR REPLACE FUNCTION UserAccountLogic_historizeEntry(a_uid INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cUpdatedRows INTEGER;
+ BEGIN
+ UPDATE Users
+ SET tsExpire = a_tsExpire
+ WHERE uid = a_uid
+ AND tsExpire = 'infinity'::TIMESTAMP;
+ GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
+ IF v_cUpdatedRows <> 1 THEN
+ IF v_cUpdatedRows = 0 THEN
+ RAISE EXCEPTION 'User with ID % does not currently exist', a_uid;
+ END IF;
+ RAISE EXCEPTION 'Integrity error in UserAccounts: % current rows with uid=%d', v_cUpdatedRows, a_uid;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Adds a new user.
+--
+CREATE OR REPLACE FUNCTION UserAccountLogic_addEntry(a_uidAuthor INTEGER, a_sUsername TEXT, a_sEmail TEXT, a_sFullName TEXT,
+ a_sLoginName TEXT, a_fReadOnly BOOLEAN)
+ RETURNS VOID AS $$
+ DECLARE
+ v_cRows INTEGER;
+ BEGIN
+ PERFORM UserAccountLogic_checkUniqueUser(a_sUsername, a_sLoginName, -1);
+ INSERT INTO Users(uidAuthor, sUsername, sEmail, sFullName, sLoginName)
+ VALUES (a_uidAuthor, a_sUsername, a_sEmail, a_sFullName, a_sLoginName);
+ END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION UserAccountLogic_editEntry(a_uidAuthor INTEGER, a_uid INTEGER, a_sUsername TEXT, a_sEmail TEXT,
+ a_sFullName TEXT, a_sLoginName TEXT, a_fReadOnly BOOLEAN)
+ RETURNS VOID AS $$
+ BEGIN
+ PERFORM UserAccountLogic_checkExists(a_uid);
+ PERFORM UserAccountLogic_checkUniqueUser(a_sUsername, a_sLoginName, a_uid);
+
+ PERFORM UserAccountLogic_historizeEntry(a_uid, CURRENT_TIMESTAMP);
+ INSERT INTO Users (uid, uidAuthor, sUsername, sEmail, sFullName, sLoginName, fReadOnly)
+ VALUES (a_uid, a_uidAuthor, a_sUsername, a_sEmail, a_sFullName, a_sLoginName, a_fReadOnly);
+ END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION UserAccountLogic_delEntry(a_uidAuthor INTEGER, a_uid INTEGER) RETURNS VOID AS $$
+ DECLARE
+ v_Row Users%ROWTYPE;
+ v_tsEffective TIMESTAMP WITH TIME ZONE;
+ BEGIN
+ --
+ -- To preserve the information about who deleted the record, we try to
+ -- add a dummy record which expires immediately. I say try because of
+ -- the primary key, we must let the new record be valid for 1 us. :-(
+ --
+
+ SELECT * INTO STRICT v_Row
+ FROM Users
+ WHERE uid = a_uid
+ AND tsExpire = 'infinity'::TIMESTAMP;
+
+ v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
+ IF v_Row.tsEffective < v_tsEffective THEN
+ PERFORM UserAccountLogic_historizeEntry(a_uid, v_tsEffective);
+ v_Row.tsEffective = v_tsEffective;
+ v_Row.tsExpire = CURRENT_TIMESTAMP;
+ INSERT INTO Users VALUES (v_Row.*);
+ ELSE
+ PERFORM UserAccountLogic_historizeEntry(a_uid, CURRENT_TIMESTAMP);
+ END IF;
+
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'User with ID % does not currently exist', a_uid;
+ WHEN TOO_MANY_ROWS THEN
+ RAISE EXCEPTION 'Integrity error in UserAccounts: Too many current rows for %', a_uid;
+ END;
+$$ LANGUAGE plpgsql;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/useraccount.py b/src/VBox/ValidationKit/testmanager/core/useraccount.py
new file mode 100755
index 00000000..6c3f3f51
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/useraccount.py
@@ -0,0 +1,302 @@
+# -*- coding: utf-8 -*-
+# $Id: useraccount.py $
+
+"""
+Test Manager - User DB records management.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.base import ModelDataBase, ModelLogicBase, ModelDataBaseTestCase, TMTooManyRows, TMRowNotFound;
+
+
+class UserAccountData(ModelDataBase):
+ """
+ User account data
+ """
+
+ ksIdAttr = 'uid';
+
+ ksParam_uid = 'UserAccount_uid'
+ ksParam_tsExpire = 'UserAccount_tsExpire'
+ ksParam_tsEffective = 'UserAccount_tsEffective'
+ ksParam_uidAuthor = 'UserAccount_uidAuthor'
+ ksParam_sLoginName = 'UserAccount_sLoginName'
+ ksParam_sUsername = 'UserAccount_sUsername'
+ ksParam_sEmail = 'UserAccount_sEmail'
+ ksParam_sFullName = 'UserAccount_sFullName'
+ ksParam_fReadOnly = 'UserAccount_fReadOnly'
+
+ kasAllowNullAttributes = ['uid', 'tsEffective', 'tsExpire', 'uidAuthor'];
+
+
+ def __init__(self):
+ """Init parameters"""
+ ModelDataBase.__init__(self);
+ self.uid = None;
+ self.tsEffective = None;
+ self.tsExpire = None;
+ self.uidAuthor = None;
+ self.sUsername = None;
+ self.sEmail = None;
+ self.sFullName = None;
+ self.sLoginName = None;
+ self.fReadOnly = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Init from database table row
+ Returns self. Raises exception of the row is None.
+ """
+ if aoRow is None:
+ raise TMRowNotFound('User not found.');
+
+ self.uid = aoRow[0];
+ self.tsEffective = aoRow[1];
+ self.tsExpire = aoRow[2];
+ self.uidAuthor = aoRow[3];
+ self.sUsername = aoRow[4];
+ self.sEmail = aoRow[5];
+ self.sFullName = aoRow[6];
+ self.sLoginName = aoRow[7];
+ self.fReadOnly = aoRow[8];
+ return self;
+
+ def initFromDbWithId(self, oDb, uid, tsNow = None, sPeriodBack = None):
+ """
+ Initialize the object from the database.
+ """
+ oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
+ 'SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE uid = %s\n'
+ , ( uid, ), tsNow, sPeriodBack));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMRowNotFound('uid=%s not found (tsNow=%s sPeriodBack=%s)' % (uid, tsNow, sPeriodBack,));
+ return self.initFromDbRow(aoRow);
+
+ def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
+ # Custom handling of the email field.
+ if sAttr == 'sEmail':
+ return ModelDataBase.validateEmail(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
+
+ # Automatically lowercase the login name if we're supposed to do case
+ # insensitive matching. (The feature assumes lower case in DB.)
+ if sAttr == 'sLoginName' and oValue is not None and config.g_kfLoginNameCaseInsensitive:
+ oValue = oValue.lower();
+
+ return ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
+
+
+class UserAccountLogic(ModelLogicBase):
+ """
+ User account logic (for the Users table).
+ """
+
+ def __init__(self, oDb):
+ ModelLogicBase.__init__(self, oDb)
+ self.dCache = None;
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches user accounts.
+
+ Returns an array (list) of UserAccountData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = aiSortColumns;
+ if tsNow is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ 'ORDER BY sUsername DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+ else:
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE tsExpire > %s\n'
+ ' AND tsEffective <= %s\n'
+ 'ORDER BY sUsername DESC\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (tsNow, tsNow, cMaxRows, iStart,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(UserAccountData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+ def addEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Add user account entry to the DB.
+ """
+ self._oDb.callProc('UserAccountLogic_addEntry',
+ (uidAuthor, oData.sUsername, oData.sEmail, oData.sFullName, oData.sLoginName, oData.fReadOnly));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def editEntry(self, oData, uidAuthor, fCommit = False):
+ """
+ Modify user account.
+ """
+ self._oDb.callProc('UserAccountLogic_editEntry',
+ ( uidAuthor, oData.uid, oData.sUsername, oData.sEmail,
+ oData.sFullName, oData.sLoginName, oData.fReadOnly));
+ self._oDb.maybeCommit(fCommit);
+ return True;
+
+ def removeEntry(self, uidAuthor, uid, fCascade = False, fCommit = False):
+ """
+ Delete user account
+ """
+ self._oDb.callProc('UserAccountLogic_delEntry', (uidAuthor, uid));
+ self._oDb.maybeCommit(fCommit);
+ _ = fCascade;
+ return True;
+
+ def _getByField(self, sField, sValue):
+ """
+ Get user account record by its field value
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
+ ' AND ' + sField + ' = %s'
+ , (sValue,))
+
+ aRows = self._oDb.fetchAll()
+ if len(aRows) not in (0, 1):
+ raise TMTooManyRows('Found more than one user account with the same credentials. Database structure is corrupted.')
+
+ try:
+ return aRows[0]
+ except IndexError:
+ return []
+
+ def getById(self, idUserId):
+ """
+ Get user account information by ID.
+ """
+ return self._getByField('uid', idUserId)
+
+ def tryFetchAccountByLoginName(self, sLoginName):
+ """
+ Try get user account information by login name.
+
+ Returns UserAccountData if found, None if not.
+ Raises exception on DB error.
+ """
+ if config.g_kfLoginNameCaseInsensitive:
+ sLoginName = sLoginName.lower();
+
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE sLoginName = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (sLoginName, ));
+ if self._oDb.getRowCount() != 1:
+ if self._oDb.getRowCount() != 0:
+ raise self._oDb.integrityException('%u rows in Users with sLoginName="%s"'
+ % (self._oDb.getRowCount(), sLoginName));
+ return None;
+ return UserAccountData().initFromDbRow(self._oDb.fetchOne());
+
+ def cachedLookup(self, uid):
+ """
+ Looks up the current UserAccountData object for uid via an object cache.
+
+ Returns a shared UserAccountData object. None if not found.
+ Raises exception on DB error.
+ """
+ if self.dCache is None:
+ self.dCache = self._oDb.getCache('UserAccount');
+
+ oUser = self.dCache.get(uid, None);
+ if oUser is None:
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE uid = %s\n'
+ ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
+ , (uid, ));
+ if self._oDb.getRowCount() == 0:
+ # Maybe it was deleted, try get the last entry.
+ self._oDb.execute('SELECT *\n'
+ 'FROM Users\n'
+ 'WHERE uid = %s\n'
+ 'ORDER BY tsExpire DESC\n'
+ 'LIMIT 1\n'
+ , (uid, ));
+ elif self._oDb.getRowCount() > 1:
+ raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), uid));
+
+ if self._oDb.getRowCount() == 1:
+ oUser = UserAccountData().initFromDbRow(self._oDb.fetchOne());
+ self.dCache[uid] = oUser;
+ return oUser;
+
+ def resolveChangeLogAuthors(self, aoEntries):
+ """
+ Given an array of ChangeLogEntry instances, set sAuthor to whatever
+ uidAuthor resolves to.
+
+ Returns aoEntries.
+ Raises exception on DB error.
+ """
+ for oEntry in aoEntries:
+ oUser = self.cachedLookup(oEntry.uidAuthor)
+ if oUser is not None:
+ oEntry.sAuthor = oUser.sUsername;
+ return aoEntries;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class UserAccountDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [UserAccountData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/vcsbugreference.py b/src/VBox/ValidationKit/testmanager/core/vcsbugreference.py
new file mode 100755
index 00000000..9ae9c941
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/vcsbugreference.py
@@ -0,0 +1,251 @@
+# -*- coding: utf-8 -*-
+# $Id: vcsbugreference.py $
+
+"""
+Test Manager - VcsBugReferences
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase;
+
+
+class VcsBugReferenceData(ModelDataBase):
+ """
+ A version control system (VCS) bug tracker reference (commit message tag).
+ """
+
+ #kasIdAttr = ['sRepository','iRevision', 'sBugTracker', 'iBugNo'];
+
+ ksParam_sRepository = 'VcsBugReference_sRepository';
+ ksParam_iRevision = 'VcsBugReference_iRevision';
+ ksParam_sBugTracker = 'VcsBugReference_sBugTracker';
+ ksParam_lBugNo = 'VcsBugReference_lBugNo';
+
+ kasAllowNullAttributes = [ ];
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.sRepository = None;
+ self.iRevision = None;
+ self.sBugTracker = None;
+ self.lBugNo = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM VcsBugReferences row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMExceptionBase('VcsBugReference not found.');
+
+ self.sRepository = aoRow[0];
+ self.iRevision = aoRow[1];
+ self.sBugTracker = aoRow[2];
+ self.lBugNo = aoRow[3];
+ return self;
+
+ def initFromValues(self, sRepository, iRevision, sBugTracker, lBugNo):
+ """
+ Reinitializes form a set of values.
+ return self.
+ """
+ self.sRepository = sRepository;
+ self.iRevision = iRevision;
+ self.sBugTracker = sBugTracker;
+ self.lBugNo = lBugNo;
+ return self;
+
+
+class VcsBugReferenceDataEx(VcsBugReferenceData):
+ """
+ Extended version of VcsBugReferenceData that includes the commit details.
+ """
+ def __init__(self):
+ VcsBugReferenceData.__init__(self);
+ self.tsCreated = None;
+ self.sAuthor = None;
+ self.sMessage = None;
+
+ def initFromDbRow(self, aoRow):
+ VcsBugReferenceData.initFromDbRow(self, aoRow);
+ self.tsCreated = aoRow[4];
+ self.sAuthor = aoRow[5];
+ self.sMessage = aoRow[6];
+ return self;
+
+
+class VcsBugReferenceLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ VCS revision <-> bug tracker references database logic.
+ """
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches VCS revisions for listing.
+
+ Returns an array (list) of VcsBugReferenceData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = tsNow; _ = aiSortColumns;
+ self._oDb.execute('''
+SELECT *
+FROM VcsBugReferences
+ORDER BY sRepository, iRevision, sBugTracker, lBugNo
+LIMIT %s OFFSET %s
+''', (cMaxRows, iStart,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(VcsBugReferenceData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+ def exists(self, oData):
+ """
+ Checks if the data is already present in the DB.
+ Returns True / False.
+ Raises exception on input and database errors.
+ """
+ self._oDb.execute('''
+SELECT COUNT(*)
+FROM VcsBugReferences
+WHERE sRepository = %s
+ AND iRevision = %s
+ AND sBugTracker = %s
+ AND lBugNo = %s
+''', ( oData.sRepository, oData.iRevision, oData.sBugTracker, oData.lBugNo));
+ cRows = self._oDb.fetchOne()[0];
+ if cRows < 0 or cRows > 1:
+ raise TMExceptionBase('VcsBugReferences has a primary key problem: %u duplicates' % (cRows,));
+ return cRows != 0;
+
+
+ #
+ # Other methods.
+ #
+
+ def addVcsBugReference(self, oData, fCommit = False):
+ """
+ Adds (or updates) a tree revision record.
+ Raises exception on input and database errors.
+ """
+
+ # Check VcsBugReferenceData before do anything
+ dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dDataErrors:
+ raise TMExceptionBase('Invalid data passed to addVcsBugReference(): %s' % (dDataErrors,));
+
+ # Does it already exist?
+ if not self.exists(oData):
+ # New row.
+ self._oDb.execute('INSERT INTO VcsBugReferences (sRepository, iRevision, sBugTracker, lBugNo)\n'
+ 'VALUES (%s, %s, %s, %s)\n'
+ , ( oData.sRepository,
+ oData.iRevision,
+ oData.sBugTracker,
+ oData.lBugNo,
+ ));
+
+ self._oDb.maybeCommit(fCommit);
+ return oData;
+
+ def getLastRevision(self, sRepository):
+ """
+ Get the last known revision number for the given repository, returns 0
+ if the repository is not known to us:
+ """
+ self._oDb.execute('''
+SELECT iRevision
+FROM VcsBugReferences
+WHERE sRepository = %s
+ORDER BY iRevision DESC
+LIMIT 1
+''', ( sRepository, ));
+ if self._oDb.getRowCount() == 0:
+ return 0;
+ return self._oDb.fetchOne()[0];
+
+ def fetchForBug(self, sBugTracker, lBugNo):
+ """
+ Fetches VCS revisions for a bug.
+
+ Returns an array (list) of VcsBugReferenceDataEx items, empty list if none.
+ Raises exception on error.
+ """
+ self._oDb.execute('''
+SELECT VcsBugReferences.*,
+ VcsRevisions.tsCreated,
+ VcsRevisions.sAuthor,
+ VcsRevisions.sMessage
+FROM VcsBugReferences
+LEFT OUTER JOIN VcsRevisions ON ( VcsRevisions.sRepository = VcsBugReferences.sRepository
+ AND VcsRevisions.iRevision = VcsBugReferences.iRevision )
+WHERE sBugTracker = %s
+ AND lBugNo = %s
+ORDER BY VcsRevisions.tsCreated, VcsBugReferences.sRepository, VcsBugReferences.iRevision
+''', (sBugTracker, lBugNo,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(VcsBugReferenceDataEx().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class VcsBugReferenceDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [VcsBugReferenceData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py b/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py
new file mode 100755
index 00000000..ea841d11
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 -*-
+# $Id: vcsrevisions.py $
+
+"""
+Test Manager - VcsRevisions
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import unittest;
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase;
+
+
+class VcsRevisionData(ModelDataBase):
+ """
+ A version control system (VCS) revision.
+ """
+
+ #kasIdAttr = ['sRepository',iRevision];
+
+ ksParam_sRepository = 'VcsRevision_sRepository';
+ ksParam_iRevision = 'VcsRevision_iRevision';
+ ksParam_tsCreated = 'VcsRevision_tsCreated';
+ ksParam_sAuthor = 'VcsRevision_sAuthor';
+ ksParam_sMessage = 'VcsRevision_sMessage';
+
+ kasAllowNullAttributes = [ ];
+ kfAllowUnicode_sMessage = True;
+ kcchMax_sMessage = 8192;
+
+ def __init__(self):
+ ModelDataBase.__init__(self);
+
+ #
+ # Initialize with defaults.
+ # See the database for explanations of each of these fields.
+ #
+ self.sRepository = None;
+ self.iRevision = None;
+ self.tsCreated = None;
+ self.sAuthor = None;
+ self.sMessage = None;
+
+ def initFromDbRow(self, aoRow):
+ """
+ Re-initializes the object from a SELECT * FROM VcsRevisions row.
+ Returns self. Raises exception if aoRow is None.
+ """
+ if aoRow is None:
+ raise TMExceptionBase('VcsRevision not found.');
+
+ self.sRepository = aoRow[0];
+ self.iRevision = aoRow[1];
+ self.tsCreated = aoRow[2];
+ self.sAuthor = aoRow[3];
+ self.sMessage = aoRow[4];
+ return self;
+
+ def initFromDbWithRepoAndRev(self, oDb, sRepository, iRevision):
+ """
+ Initialize from the database, given the tree and revision of a row.
+ """
+ oDb.execute('SELECT * FROM VcsRevisions WHERE sRepository = %s AND iRevision = %u', (sRepository, iRevision,));
+ aoRow = oDb.fetchOne()
+ if aoRow is None:
+ raise TMExceptionBase('sRepository = %s iRevision = %u not found' % (sRepository, iRevision, ));
+ return self.initFromDbRow(aoRow);
+
+ def initFromValues(self, sRepository, iRevision, tsCreated, sAuthor, sMessage):
+ """
+ Reinitializes form a set of values.
+ return self.
+ """
+ self.sRepository = sRepository;
+ self.iRevision = iRevision;
+ self.tsCreated = tsCreated;
+ self.sAuthor = sAuthor;
+ self.sMessage = sMessage;
+ return self;
+
+
+class VcsRevisionLogic(ModelLogicBase): # pylint: disable=too-few-public-methods
+ """
+ VCS revisions database logic.
+ """
+
+ #
+ # Standard methods.
+ #
+
+ def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
+ """
+ Fetches VCS revisions for listing.
+
+ Returns an array (list) of VcsRevisionData items, empty list if none.
+ Raises exception on error.
+ """
+ _ = tsNow; _ = aiSortColumns;
+ self._oDb.execute('SELECT *\n'
+ 'FROM VcsRevisions\n'
+ 'ORDER BY tsCreated, sRepository, iRevision\n'
+ 'LIMIT %s OFFSET %s\n'
+ , (cMaxRows, iStart,));
+
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(VcsRevisionData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+ def tryFetch(self, sRepository, iRevision):
+ """
+ Tries to fetch the specified tree revision record.
+ Returns VcsRevisionData instance if found, None if not found.
+ Raises exception on input and database errors.
+ """
+ self._oDb.execute('SELECT * FROM VcsRevisions WHERE sRepository = %s AND iRevision = %s',
+ ( sRepository, iRevision, ));
+ aaoRows = self._oDb.fetchAll();
+ if len(aaoRows) == 1:
+ return VcsRevisionData().initFromDbRow(aaoRows[0]);
+ if aaoRows:
+ raise TMExceptionBase('VcsRevisions has a primary key problem: %u duplicates' % (len(aaoRows),));
+ return None
+
+
+ #
+ # Other methods.
+ #
+
+ def addVcsRevision(self, oData, fCommit = False):
+ """
+ Adds (or updates) a tree revision record.
+ Raises exception on input and database errors.
+ """
+
+ # Check VcsRevisionData before do anything
+ dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
+ if dDataErrors:
+ raise TMExceptionBase('Invalid data passed to addVcsRevision(): %s' % (dDataErrors,));
+
+ # Does it already exist?
+ oOldData = self.tryFetch(oData.sRepository, oData.iRevision);
+ if oOldData is None:
+ # New row.
+ self._oDb.execute('INSERT INTO VcsRevisions (sRepository, iRevision, tsCreated, sAuthor, sMessage)\n'
+ 'VALUES (%s, %s, %s, %s, %s)\n'
+ , ( oData.sRepository,
+ oData.iRevision,
+ oData.tsCreated,
+ oData.sAuthor,
+ oData.sMessage,
+ ));
+ elif not oOldData.isEqual(oData):
+ # Update old row.
+ self._oDb.execute('UPDATE VcsRevisions\n'
+ ' SET tsCreated = %s,\n'
+ ' sAuthor = %s,\n'
+ ' sMessage = %s\n'
+ 'WHERE sRepository = %s\n'
+ ' AND iRevision = %s'
+ , ( oData.tsCreated,
+ oData.sAuthor,
+ oData.sMessage,
+ oData.sRepository,
+ oData.iRevision,
+ ));
+
+ self._oDb.maybeCommit(fCommit);
+ return oData;
+
+ def getLastRevision(self, sRepository):
+ """
+ Get the last known revision number for the given repository, returns 0
+ if the repository is not known to us:
+ """
+ self._oDb.execute('SELECT iRevision\n'
+ 'FROM VcsRevisions\n'
+ 'WHERE sRepository = %s\n'
+ 'ORDER BY iRevision DESC\n'
+ 'LIMIT 1\n'
+ , ( sRepository, ));
+ if self._oDb.getRowCount() == 0:
+ return 0;
+ return self._oDb.fetchOne()[0];
+
+ def fetchTimeline(self, sRepository, iRevision, cEntriesBack):
+ """
+ Fetches a VCS timeline portion for a repository.
+
+ Returns an array (list) of VcsRevisionData items, empty list if none.
+ Raises exception on error.
+ """
+ self._oDb.execute('SELECT *\n'
+ 'FROM VcsRevisions\n'
+ 'WHERE sRepository = %s\n'
+ ' AND iRevision > %s\n'
+ ' AND iRevision <= %s\n'
+ 'ORDER BY iRevision DESC\n'
+ 'LIMIT %s\n'
+ , ( sRepository, iRevision - cEntriesBack*2 + 1, iRevision, cEntriesBack));
+ aoRows = [];
+ for _ in range(self._oDb.getRowCount()):
+ aoRows.append(VcsRevisionData().initFromDbRow(self._oDb.fetchOne()));
+ return aoRows;
+
+
+#
+# Unit testing.
+#
+
+# pylint: disable=missing-docstring
+class VcsRevisionDataTestCase(ModelDataBaseTestCase):
+ def setUp(self):
+ self.aoSamples = [VcsRevisionData(),];
+
+if __name__ == '__main__':
+ unittest.main();
+ # not reached.
+
diff --git a/src/VBox/ValidationKit/testmanager/core/webservergluebase.py b/src/VBox/ValidationKit/testmanager/core/webservergluebase.py
new file mode 100755
index 00000000..a36e1af7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/webservergluebase.py
@@ -0,0 +1,717 @@
+# -*- coding: utf-8 -*-
+# $Id: webservergluebase.py $
+
+"""
+Test Manager Core - Web Server Abstraction Base Class.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import cgitb
+import codecs;
+import os
+import sys
+
+# Validation Kit imports.
+from common import webutils, utils;
+from testmanager import config;
+
+
+class WebServerGlueException(Exception):
+ """
+ For exceptions raised by glue code.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class WebServerGlueBase(object):
+ """
+ Web server interface abstraction and some HTML utils.
+ """
+
+ ## Enables more debug output.
+ kfDebugInfoEnabled = True;
+
+ ## The maximum number of characters to cache.
+ kcchMaxCached = 65536;
+
+ ## Special getUserName return value.
+ ksUnknownUser = 'Unknown User';
+
+ ## HTTP status codes and their messages.
+ kdStatusMsgs = {
+ 100: 'Continue',
+ 101: 'Switching Protocols',
+ 102: 'Processing',
+ 103: 'Early Hints',
+ 200: 'OK',
+ 201: 'Created',
+ 202: 'Accepted',
+ 203: 'Non-Authoritative Information',
+ 204: 'No Content',
+ 205: 'Reset Content',
+ 206: 'Partial Content',
+ 207: 'Multi-Status',
+ 208: 'Already Reported',
+ 226: 'IM Used',
+ 300: 'Multiple Choices',
+ 301: 'Moved Permantently',
+ 302: 'Found',
+ 303: 'See Other',
+ 304: 'Not Modified',
+ 305: 'Use Proxy',
+ 306: 'Switch Proxy',
+ 307: 'Temporary Redirect',
+ 308: 'Permanent Redirect',
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 402: 'Payment Required',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 405: 'Method Not Allowed',
+ 406: 'Not Acceptable',
+ 407: 'Proxy Authentication Required',
+ 408: 'Request Timeout',
+ 409: 'Conflict',
+ 410: 'Gone',
+ 411: 'Length Required',
+ 412: 'Precondition Failed',
+ 413: 'Payload Too Large',
+ 414: 'URI Too Long',
+ 415: 'Unsupported Media Type',
+ 416: 'Range Not Satisfiable',
+ 417: 'Expectation Failed',
+ 418: 'I\'m a teapot',
+ 421: 'Misdirection Request',
+ 422: 'Unprocessable Entity',
+ 423: 'Locked',
+ 424: 'Failed Dependency',
+ 425: 'Too Early',
+ 426: 'Upgrade Required',
+ 428: 'Precondition Required',
+ 429: 'Too Many Requests',
+ 431: 'Request Header Fields Too Large',
+ 451: 'Unavailable For Legal Reasons',
+ 500: 'Internal Server Error',
+ 501: 'Not Implemented',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Timeout',
+ 505: 'HTTP Version Not Supported',
+ 506: 'Variant Also Negotiates',
+ 507: 'Insufficient Storage',
+ 508: 'Loop Detected',
+ 510: 'Not Extended',
+ 511: 'Network Authentication Required',
+ };
+
+
+ def __init__(self, sValidationKitDir, fHtmlDebugOutput = True):
+ self._sValidationKitDir = sValidationKitDir;
+
+ # Debug
+ self.tsStart = utils.timestampNano();
+ self._fHtmlDebugOutput = fHtmlDebugOutput; # For trace
+ self._oDbgFile = sys.stderr;
+ if config.g_ksSrvGlueDebugLogDst is not None and config.g_kfSrvGlueDebug is True:
+ self._oDbgFile = open(config.g_ksSrvGlueDebugLogDst, 'a'); # pylint: disable=consider-using-with,unspecified-encoding
+ if config.g_kfSrvGlueCgiDumpArgs:
+ self._oDbgFile.write('Arguments: %s\nEnvironment:\n' % (sys.argv,));
+ if config.g_kfSrvGlueCgiDumpEnv:
+ for sVar in sorted(os.environ):
+ self._oDbgFile.write(' %s=\'%s\' \\\n' % (sVar, os.environ[sVar],));
+
+ self._afnDebugInfo = [];
+
+ # HTTP header.
+ self._fHeaderWrittenOut = False;
+ self._dHeaderFields = \
+ { \
+ 'Content-Type': 'text/html; charset=utf-8',
+ };
+
+ # Body.
+ self._sBodyType = None;
+ self._dParams = {};
+ self._sHtmlBody = '';
+ self._cchCached = 0;
+ self._cchBodyWrittenOut = 0;
+
+ # Output.
+ if sys.version_info[0] >= 3:
+ self.oOutputRaw = sys.stdout.detach(); # pylint: disable=no-member
+ sys.stdout = None; # Prevents flush_std_files() from complaining on stderr during sys.exit().
+ else:
+ self.oOutputRaw = sys.stdout;
+ self.oOutputText = codecs.getwriter('utf-8')(self.oOutputRaw);
+
+
+ #
+ # Get stuff.
+ #
+
+ def getParameters(self):
+ """
+ Returns a dictionary with the query parameters.
+
+ The parameter name is the key, the values are given as lists. If a
+ parameter is given more than once, the value is appended to the
+ existing dictionary entry.
+ """
+ return {};
+
+ def getClientAddr(self):
+ """
+ Returns the client address, as a string.
+ """
+ raise WebServerGlueException('getClientAddr is not implemented');
+
+ def getMethod(self):
+ """
+ Gets the HTTP request method.
+ """
+ return 'POST';
+
+ def getLoginName(self):
+ """
+ Gets login name provided by Apache.
+ Returns kUnknownUser if not logged on.
+ """
+ return WebServerGlueBase.ksUnknownUser;
+
+ def getUrlScheme(self):
+ """
+ Gets scheme name (aka. access protocol) from request URL, i.e. 'http' or 'https'.
+ See also urlparse.scheme.
+ """
+ return 'http';
+
+ def getUrlNetLoc(self):
+ """
+ Gets the network location (server host name / ip) from the request URL.
+ See also urlparse.netloc.
+ """
+ raise WebServerGlueException('getUrlNetLoc is not implemented');
+
+ def getUrlPath(self):
+ """
+ Gets the hirarchical path (relative to server) from the request URL.
+ See also urlparse.path.
+ Note! This includes the leading slash.
+ """
+ raise WebServerGlueException('getUrlPath is not implemented');
+
+ def getUrlBasePath(self):
+ """
+ Gets the hirarchical base path (relative to server) from the request URL.
+ Note! This includes both a leading an trailing slash.
+ """
+ sPath = self.getUrlPath(); # virtual method # pylint: disable=assignment-from-no-return
+ iLastSlash = sPath.rfind('/');
+ if iLastSlash >= 0:
+ sPath = sPath[:iLastSlash];
+ sPath = sPath.rstrip('/');
+ return sPath + '/';
+
+ def getUrl(self):
+ """
+ Gets the URL being accessed, sans parameters.
+ For instance this will return, "http://localhost/testmanager/admin.cgi"
+ when "http://localhost/testmanager/admin.cgi?blah=blah" is being access.
+ """
+ return '%s://%s%s' % (self.getUrlScheme(), self.getUrlNetLoc(), self.getUrlPath());
+
+ def getBaseUrl(self):
+ """
+ Gets the base URL (with trailing slash).
+ For instance this will return, "http://localhost/testmanager/" when
+ "http://localhost/testmanager/admin.cgi?blah=blah" is being access.
+ """
+ return '%s://%s%s' % (self.getUrlScheme(), self.getUrlNetLoc(), self.getUrlBasePath());
+
+ def getUserAgent(self):
+ """
+ Gets the User-Agent field of the HTTP header, returning empty string
+ if not present.
+ """
+ return '';
+
+ def getContentType(self):
+ """
+ Gets the Content-Type field of the HTTP header, parsed into a type
+ string and a dictionary.
+ """
+ return ('text/html', {});
+
+ def getContentLength(self):
+ """
+ Gets the content length.
+ Returns int.
+ """
+ return 0;
+
+ def getBodyIoStream(self):
+ """
+ Returns file object for reading the HTML body.
+ """
+ raise WebServerGlueException('getUrlPath is not implemented');
+
+ def getBodyIoStreamBinary(self):
+ """
+ Returns file object for reading the binary HTML body.
+ """
+ raise WebServerGlueException('getBodyIoStreamBinary is not implemented');
+
+ #
+ # Output stuff.
+ #
+
+ def _writeHeader(self, sHeaderLine):
+ """
+ Worker function which child classes can override.
+ """
+ sys.stderr.write('_writeHeader: cch=%s "%s..."\n' % (len(sHeaderLine), sHeaderLine[0:10],))
+ self.oOutputText.write(sHeaderLine);
+ return True;
+
+ def flushHeader(self):
+ """
+ Flushes the HTTP header.
+ """
+ if self._fHeaderWrittenOut is False:
+ for sKey, sValue in self._dHeaderFields.items():
+ self._writeHeader('%s: %s\n' % (sKey, sValue,));
+ self._fHeaderWrittenOut = True;
+ self._writeHeader('\n'); # End of header indicator.
+ return None;
+
+ def setHeaderField(self, sField, sValue):
+ """
+ Sets a header field.
+ """
+ assert self._fHeaderWrittenOut is False;
+ self._dHeaderFields[sField] = sValue;
+ return True;
+
+ def setRedirect(self, sLocation, iCode = 302):
+ """
+ Sets up redirection of the page.
+ Raises an exception if called too late.
+ """
+ if self._fHeaderWrittenOut is True:
+ raise WebServerGlueException('setRedirect called after the header was written');
+ if iCode != 302:
+ raise WebServerGlueException('Redirection code %d is not supported' % (iCode,));
+
+ self.setHeaderField('Location', sLocation);
+ self.setHeaderField('Status', '302 Found');
+ return True;
+
+ def setStatus(self, iStatus, sMsg = None):
+ """ Sets the status code. """
+ if not sMsg:
+ sMsg = self.kdStatusMsgs[iStatus];
+ return self.setHeaderField('Status', '%u %s' % (iStatus, sMsg));
+
+ def setContentType(self, sType):
+ """ Sets the content type header field. """
+ return self.setHeaderField('Content-Type', sType);
+
+ def _writeWorker(self, sChunkOfHtml):
+ """
+ Worker function which child classes can override.
+ """
+ sys.stderr.write('_writeWorker: cch=%s "%s..."\n' % (len(sChunkOfHtml), sChunkOfHtml[0:10],))
+ self.oOutputText.write(sChunkOfHtml);
+ return True;
+
+ def write(self, sChunkOfHtml):
+ """
+ Writes chunk of HTML, making sure the HTTP header is flushed first.
+ """
+ if self._sBodyType is None:
+ self._sBodyType = 'html';
+ elif self._sBodyType != 'html':
+ raise WebServerGlueException('Cannot use writeParameter when body type is "%s"' % (self._sBodyType, ));
+
+ self._sHtmlBody += sChunkOfHtml;
+ self._cchCached += len(sChunkOfHtml);
+
+ if self._cchCached > self.kcchMaxCached:
+ self.flush();
+ return True;
+
+ def writeRaw(self, abChunk):
+ """
+ Writes a raw chunk the document. Can be binary or any encoding.
+ No caching.
+ """
+ if self._sBodyType is None:
+ self._sBodyType = 'raw';
+ elif self._sBodyType != 'raw':
+ raise WebServerGlueException('Cannot use writeRaw when body type is "%s"' % (self._sBodyType, ));
+
+ self.flushHeader();
+ if self._cchCached > 0:
+ self.flush();
+
+ sys.stderr.write('writeRaw: cb=%s\n' % (len(abChunk),))
+ self.oOutputRaw.write(abChunk);
+ return True;
+
+ def writeParams(self, dParams):
+ """
+ Writes one or more reply parameters in a form style response. The names
+ and values in dParams are unencoded, this method takes care of that.
+
+ Note! This automatically changes the content type to
+ 'application/x-www-form-urlencoded', if the header hasn't been flushed
+ already.
+ """
+ if self._sBodyType is None:
+ if not self._fHeaderWrittenOut:
+ self.setHeaderField('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
+ elif self._dHeaderFields['Content-Type'] != 'application/x-www-form-urlencoded; charset=utf-8':
+ raise WebServerGlueException('Cannot use writeParams when content-type is "%s"' % \
+ (self._dHeaderFields['Content-Type'],));
+ self._sBodyType = 'form';
+
+ elif self._sBodyType != 'form':
+ raise WebServerGlueException('Cannot use writeParams when body type is "%s"' % (self._sBodyType, ));
+
+ for sKey in dParams:
+ sValue = str(dParams[sKey]);
+ self._dParams[sKey] = sValue;
+ self._cchCached += len(sKey) + len(sValue);
+
+ if self._cchCached > self.kcchMaxCached:
+ self.flush();
+
+ return True;
+
+ def flush(self):
+ """
+ Flush the output.
+ """
+ self.flushHeader();
+
+ if self._sBodyType == 'form':
+ sBody = webutils.encodeUrlParams(self._dParams);
+ self._writeWorker(sBody);
+
+ self._dParams = {};
+ self._cchBodyWrittenOut += self._cchCached;
+
+ elif self._sBodyType == 'html':
+ self._writeWorker(self._sHtmlBody);
+
+ self._sHtmlBody = '';
+ self._cchBodyWrittenOut += self._cchCached;
+
+ self._cchCached = 0;
+ return None;
+
+ #
+ # Paths.
+ #
+
+ def pathTmWebUI(self):
+ """
+ Gets the path to the TM 'webui' directory.
+ """
+ return os.path.join(self._sValidationKitDir, 'testmanager', 'webui');
+
+ #
+ # Error stuff & Debugging.
+ #
+
+ def errorLog(self, sError, aXcptInfo, sLogFile):
+ """
+ Writes the error to a log file.
+ """
+ # Easy solution for log file size: Only one report.
+ try: os.unlink(sLogFile);
+ except: pass;
+
+ # Try write the log file.
+ fRc = True;
+ fSaved = self._fHtmlDebugOutput;
+
+ try:
+ with open(sLogFile, 'w') as oFile: # pylint: disable=unspecified-encoding
+ oFile.write(sError + '\n\n');
+ if aXcptInfo[0] is not None:
+ oFile.write(' B a c k t r a c e\n');
+ oFile.write('===================\n');
+ oFile.write(cgitb.text(aXcptInfo, 5));
+ oFile.write('\n\n');
+
+ oFile.write(' D e b u g I n f o\n');
+ oFile.write('=====================\n\n');
+ self._fHtmlDebugOutput = False;
+ self.debugDumpStuff(oFile.write);
+ except:
+ fRc = False;
+
+ self._fHtmlDebugOutput = fSaved;
+ return fRc;
+
+ def errorPage(self, sError, aXcptInfo, sLogFile = None):
+ """
+ Displays a page with an error message.
+ """
+ if sLogFile is not None:
+ self.errorLog(sError, aXcptInfo, sLogFile);
+
+ # Reset buffering, hoping that nothing was flushed yet.
+ self._sBodyType = None;
+ self._sHtmlBody = '';
+ self._cchCached = 0;
+ if not self._fHeaderWrittenOut:
+ if self._fHtmlDebugOutput:
+ self.setHeaderField('Content-Type', 'text/html; charset=utf-8');
+ else:
+ self.setHeaderField('Content-Type', 'text/plain; charset=utf-8');
+
+ # Write the error page.
+ if self._fHtmlDebugOutput:
+ self.write('<html><head><title>Test Manage Error</title></head>\n' +
+ '<body><h1>Test Manager Error:</h1>\n' +
+ '<p>' + sError + '</p>\n');
+ else:
+ self.write(' Test Manage Error\n'
+ '===================\n'
+ '\n'
+ '' + sError + '\n\n');
+
+ if aXcptInfo[0] is not None:
+ if self._fHtmlDebugOutput:
+ self.write('<h1>Backtrace:</h1>\n');
+ self.write(cgitb.html(aXcptInfo, 5));
+ else:
+ self.write('Backtrace\n'
+ '---------\n'
+ '\n');
+ self.write(cgitb.text(aXcptInfo, 5));
+ self.write('\n\n');
+
+ if self.kfDebugInfoEnabled:
+ if self._fHtmlDebugOutput:
+ self.write('<h1>Debug Info:</h1>\n');
+ else:
+ self.write('Debug Info\n'
+ '----------\n'
+ '\n');
+ self.debugDumpStuff();
+
+ for fn in self._afnDebugInfo:
+ try:
+ fn(self, self._fHtmlDebugOutput);
+ except Exception as oXcpt:
+ self.write('\nDebug info callback %s raised exception: %s\n' % (fn, oXcpt));
+
+ if self._fHtmlDebugOutput:
+ self.write('</body></html>');
+
+ self.flush();
+
+ def debugInfoPage(self, fnWrite = None):
+ """
+ Dumps useful debug info.
+ """
+ if fnWrite is None:
+ fnWrite = self.write;
+
+ fnWrite('<html><head><title>Test Manage Debug Info</title></head>\n<body>\n');
+ self.debugDumpStuff(fnWrite = fnWrite);
+ fnWrite('</body></html>');
+ self.flush();
+
+ def debugDumpDict(self, sName, dDict, fSorted = True, fnWrite = None):
+ """
+ Dumps dictionary.
+ """
+ if fnWrite is None:
+ fnWrite = self.write;
+
+ asKeys = list(dDict.keys());
+ if fSorted:
+ asKeys.sort();
+
+ if self._fHtmlDebugOutput:
+ fnWrite('<h2>%s</h2>\n'
+ '<table border="1"><tr><th>name</th><th>value</th></tr>\n' % (sName,));
+ for sKey in asKeys:
+ fnWrite(' <tr><td>' + webutils.escapeElem(sKey) + '</td><td>' \
+ + webutils.escapeElem(str(dDict.get(sKey))) \
+ + '</td></tr>\n');
+ fnWrite('</table>\n');
+ else:
+ for i in range(len(sName) - 1):
+ fnWrite('%s ' % (sName[i],));
+ fnWrite('%s\n\n' % (sName[-1],));
+
+ fnWrite('%28s Value\n' % ('Name',));
+ fnWrite('------------------------------------------------------------------------\n');
+ for sKey in asKeys:
+ fnWrite('%28s: %s\n' % (sKey, dDict.get(sKey),));
+ fnWrite('\n');
+
+ return True;
+
+ def debugDumpList(self, sName, aoStuff, fnWrite = None):
+ """
+ Dumps array.
+ """
+ if fnWrite is None:
+ fnWrite = self.write;
+
+ if self._fHtmlDebugOutput:
+ fnWrite('<h2>%s</h2>\n'
+ '<table border="1"><tr><th>index</th><th>value</th></tr>\n' % (sName,));
+ for i, _ in enumerate(aoStuff):
+ fnWrite(' <tr><td>' + str(i) + '</td><td>' + webutils.escapeElem(str(aoStuff[i])) + '</td></tr>\n');
+ fnWrite('</table>\n');
+ else:
+ for ch in sName[:-1]:
+ fnWrite('%s ' % (ch,));
+ fnWrite('%s\n\n' % (sName[-1],));
+
+ fnWrite('Index Value\n');
+ fnWrite('------------------------------------------------------------------------\n');
+ for i, oStuff in enumerate(aoStuff):
+ fnWrite('%5u %s\n' % (i, str(oStuff)));
+ fnWrite('\n');
+
+ return True;
+
+ def debugDumpParameters(self, fnWrite):
+ """ Dumps request parameters. """
+ if fnWrite is None:
+ fnWrite = self.write;
+
+ try:
+ dParams = self.getParameters();
+ return self.debugDumpDict('Parameters', dParams);
+ except Exception as oXcpt:
+ if self._fHtmlDebugOutput:
+ fnWrite('<p>Exception %s while retriving parameters.</p>\n' % (oXcpt,))
+ else:
+ fnWrite('Exception %s while retriving parameters.\n' % (oXcpt,))
+ return False;
+
+ def debugDumpEnv(self, fnWrite = None):
+ """ Dumps os.environ. """
+ return self.debugDumpDict('Environment (os.environ)', os.environ, fnWrite = fnWrite);
+
+ def debugDumpArgv(self, fnWrite = None):
+ """ Dumps sys.argv. """
+ return self.debugDumpList('Arguments (sys.argv)', sys.argv, fnWrite = fnWrite);
+
+ def debugDumpPython(self, fnWrite = None):
+ """
+ Dump python info.
+ """
+ dInfo = {};
+ dInfo['sys.version'] = sys.version;
+ dInfo['sys.hexversion'] = sys.hexversion;
+ dInfo['sys.api_version'] = sys.api_version;
+ if hasattr(sys, 'subversion'):
+ dInfo['sys.subversion'] = sys.subversion; # pylint: disable=no-member
+ dInfo['sys.platform'] = sys.platform;
+ dInfo['sys.executable'] = sys.executable;
+ dInfo['sys.copyright'] = sys.copyright;
+ dInfo['sys.byteorder'] = sys.byteorder;
+ dInfo['sys.exec_prefix'] = sys.exec_prefix;
+ dInfo['sys.prefix'] = sys.prefix;
+ dInfo['sys.path'] = sys.path;
+ dInfo['sys.builtin_module_names'] = sys.builtin_module_names;
+ dInfo['sys.flags'] = sys.flags;
+
+ return self.debugDumpDict('Python Info', dInfo, fnWrite = fnWrite);
+
+
+ def debugDumpStuff(self, fnWrite = None):
+ """
+ Dumps stuff to the error page and debug info page.
+ Should be extended by child classes when possible.
+ """
+ self.debugDumpParameters(fnWrite);
+ self.debugDumpEnv(fnWrite);
+ self.debugDumpArgv(fnWrite);
+ self.debugDumpPython(fnWrite);
+ return True;
+
+ def dprint(self, sMessage):
+ """
+ Prints to debug log (usually apache error log).
+ """
+ if config.g_kfSrvGlueDebug is True:
+ if config.g_kfSrvGlueDebugTS is False:
+ self._oDbgFile.write(sMessage);
+ if not sMessage.endswith('\n'):
+ self._oDbgFile.write('\n');
+ else:
+ tsNow = utils.timestampMilli();
+ tsReq = tsNow - (self.tsStart / 1000000);
+ iPid = os.getpid();
+ for sLine in sMessage.split('\n'):
+ self._oDbgFile.write('%s/%03u,pid=%04x: %s\n' % (tsNow, tsReq, iPid, sLine,));
+
+ return True;
+
+ def registerDebugInfoCallback(self, fnDebugInfo):
+ """
+ Registers a debug info method for calling when the error page is shown.
+
+ The fnDebugInfo function takes two parameters. The first is this
+ object, the second is a boolean indicating html (True) or text (False)
+ output. The return value is ignored.
+ """
+ if self.kfDebugInfoEnabled:
+ self._afnDebugInfo.append(fnDebugInfo);
+ return True;
+
+ def unregisterDebugInfoCallback(self, fnDebugInfo):
+ """
+ Unregisters a debug info method previously registered by
+ registerDebugInfoCallback.
+ """
+ if self.kfDebugInfoEnabled:
+ try: self._afnDebugInfo.remove(fnDebugInfo);
+ except: pass;
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/core/webservergluecgi.py b/src/VBox/ValidationKit/testmanager/core/webservergluecgi.py
new file mode 100755
index 00000000..e530c7ec
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/core/webservergluecgi.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+# $Id: webservergluecgi.py $
+
+"""
+Test Manager Core - Web Server Abstraction Base Class.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import cgitb;
+import os;
+import sys;
+import cgi;
+
+# Validation Kit imports.
+from testmanager.core.webservergluebase import WebServerGlueBase;
+from testmanager import config;
+
+
+class WebServerGlueCgi(WebServerGlueBase):
+ """
+ CGI glue.
+ """
+ def __init__(self, sValidationKitDir, fHtmlOutput=True):
+ WebServerGlueBase.__init__(self, sValidationKitDir, fHtmlOutput);
+
+ if config.g_kfSrvGlueCgiTb is True:
+ cgitb.enable(format=('html' if fHtmlOutput else 'text'));
+
+ def getParameters(self):
+ return cgi.parse(keep_blank_values=True);
+
+ def getClientAddr(self):
+ return os.environ.get('REMOTE_ADDR');
+
+ def getMethod(self):
+ return os.environ.get('REQUEST_METHOD', 'POST');
+
+ def getLoginName(self):
+ return os.environ.get('REMOTE_USER', WebServerGlueBase.ksUnknownUser);
+
+ def getUrlScheme(self):
+ return 'https' if 'HTTPS' in os.environ else 'http';
+
+ def getUrlNetLoc(self):
+ return os.environ['HTTP_HOST'];
+
+ def getUrlPath(self):
+ return os.environ['REQUEST_URI'];
+
+ def getUserAgent(self):
+ return os.getenv('HTTP_USER_AGENT', '');
+
+ def getContentType(self):
+ return cgi.parse_header(os.environ.get('CONTENT_TYPE', 'text/html'));
+
+ def getContentLength(self):
+ return int(os.environ.get('CONTENT_LENGTH', 0));
+
+ def getBodyIoStream(self):
+ return sys.stdin;
+
+ def getBodyIoStreamBinary(self):
+ # Python 3: sys.stdin.read() returns a string. To get untranslated
+ # binary data we use the sys.stdin.buffer object instead.
+ return getattr(sys.stdin, 'buffer', sys.stdin);
+
diff --git a/src/VBox/ValidationKit/testmanager/db/Makefile.kmk b/src/VBox/ValidationKit/testmanager/db/Makefile.kmk
new file mode 100644
index 00000000..194c01a2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/Makefile.kmk
@@ -0,0 +1,98 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Makefile for generating .html from .txt.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+
+# Need proper shell on windows.
+DEPTH = ../../../../..
+ifneq ($(wildcard $(DEPTH)/Config.kmk),)
+ include $(KBUILD_PATH)/header.kmk
+else
+ VBOX_BLD_PYTHON ?= python
+endif
+
+
+GENERATED_FILES = TestManagerDatabaseComments.pgsql
+PSQL := $(firstword $(which $(foreach pgver, 16 15 14 13 12 10 11 95 94 93 92,psql$(pgver)) ) psql)
+ifeq ($(PSQL_DB_HOST),)
+ PSQL_DB_HOST := localhost # Use localhost if nothing else is set.
+endif
+ifeq ($(PSQL_DB_PORT),)
+ PSQL_DB_PORT := 5432 # Same for the port; use the default.
+endif
+ifeq ($(PSQL_DB_USER),)
+ PSQL_DB_USER := postgres
+endif
+PSQL_OPTS = --user=$(PSQL_DB_USER) --set=ON_ERROR_STOP=1 --host=$(PSQL_DB_HOST) --port=$(PSQL_DB_PORT)
+
+all: $(GENERATED_FILES)
+
+clean:
+ kmk_builtin_rm -f -- $(GENERATED_FILES)
+
+
+TestManagerDatabaseComments.pgsql: TestManagerDatabaseInit.pgsql gen-sql-comments.py
+ LC_ALL=C $(VBOX_BLD_PYTHON) gen-sql-comments.py $< > $@
+
+
+load-testmanager-db: \
+ TestManagerDatabaseInit.pgsql \
+ TestManagerDatabaseComments.pgsql \
+ ../core/useraccount.pgsql \
+ ../core/testcase.pgsql \
+ ../core/testbox.pgsql \
+ ../core/globalresource.pgsql
+ @kmk_builtin_echo "Creating testmanager database: For script verification only!"
+ $(PSQL) $(PSQL_OPTS) -f TestManagerDatabaseInit.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f TestManagerDatabaseComments.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/useraccount.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testcase.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testbox.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/globalresource.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f TestManagerDatabaseDefaultUserAccounts.pgsql
+
+reload-testmanager-db-functions: \
+ ../core/useraccount.pgsql \
+ ../core/testcase.pgsql \
+ ../core/testbox.pgsql \
+ ../core/globalresource.pgsql
+ @kmk_builtin_echo "Reloading testmanager database functions"
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/useraccount.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testcase.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testbox.pgsql
+ $(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/globalresource.pgsql
+
+# Only for prettier graphs:
+# $(PSQL) $(PSQL_OPTS) -d testmanager -f TestManagerDatabaseForeignKeyErHacks.pgsql
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase.dmd b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase.dmd
new file mode 100644
index 00000000..cf35f3fb
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase.dmd
@@ -0,0 +1,8 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<OSDM_Design class="oracle.dbtools.crest.model.design.Design" name="TestManagerDatabase" id="99299876-6D97-026B-55F9-DF582D334681" version="3.5">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<capitalNames>false</capitalNames>
+<designId>99299876-6D97-026B-55F9-DF582D334681</designId>
+</OSDM_Design> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/DataTypes.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/DataTypes.xml
new file mode 100644
index 00000000..9b86b6dd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/DataTypes.xml
@@ -0,0 +1,15 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<DataTypesDesign class="oracle.dbtools.crest.model.design.datatypes.DataTypesDesign" name="DataTypes" id="E0EE53BE-07B1-7CE9-B0DA-5D939EA4A3C9" mainViewID="E9476B45-3C62-EE27-4705-6F1EFAD11B74">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<shouldBeOpen>false</shouldBeOpen>
+<collectionOfRefsPrefix>array_ref_</collectionOfRefsPrefix>
+<collectionPrefix>array_</collectionPrefix>
+<defaultArrayLimit>10</defaultArrayLimit>
+<defaultCollectionType_Kind>ARRAY</defaultCollectionType_Kind>
+<defaultCollectionType_Suffix>_Array</defaultCollectionType_Suffix>
+<embeddedStructuredTypePrefix>inst_</embeddedStructuredTypePrefix>
+<referencePrefix>ref_</referencePrefix>
+<useRoleInAssociationEndAsName>true</useRoleInAssociationEndAsName>
+</DataTypesDesign> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/47E390DE-0671-C4B1-8428-0F45CBEE18F8.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/47E390DE-0671-C4B1-8428-0F45CBEE18F8.xml
new file mode 100644
index 00000000..e274e153
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/47E390DE-0671-C4B1-8428-0F45CBEE18F8.xml
@@ -0,0 +1,37 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<StructuredType class="oracle.dbtools.crest.model.design.datatypes.StructuredType" name="SDO_GEOMETRY" id="47E390DE-0671-C4B1-8428-0F45CBEE18F8" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<visible>false</visible>
+<predefined>true</predefined>
+<final>false</final>
+<instantiable>true</instantiable>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16777056</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Method</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Instantiable</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Mandatory</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+</fonts>
+</StructuredType> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1.xml
new file mode 100644
index 00000000..5ff7c301
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/structuredtype/seg_0/F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1.xml
@@ -0,0 +1,37 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<StructuredType class="oracle.dbtools.crest.model.design.datatypes.StructuredType" name="XMLTYPE" id="F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<visible>false</visible>
+<predefined>true</predefined>
+<final>false</final>
+<instantiable>true</instantiable>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16777056</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Method</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Instantiable</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Mandatory</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+</fonts>
+</StructuredType> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/subviews/E9476B45-3C62-EE27-4705-6F1EFAD11B74.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/subviews/E9476B45-3C62-EE27-4705-6F1EFAD11B74.xml
new file mode 100644
index 00000000..d09e8a61
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/datatypes/subviews/E9476B45-3C62-EE27-4705-6F1EFAD11B74.xml
@@ -0,0 +1,21 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.datatypes.DPVDataTypes" id="E9476B45-3C62-EE27-4705-6F1EFAD11B74">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.datatypes.TVStructuredType" oid="47E390DE-0671-C4B1-8428-0F45CBEE18F8" otype="StructuredType" vid="48CB0B19-5276-2CC9-FC10-6C17D5E5FAC6">
+<bounds x="20" y="20" width="100" height="100"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.datatypes.TVStructuredType" oid="F72C39E0-D1CA-8821-2AD7-A1E95A37D3D1" otype="StructuredType" vid="5CEA75E2-B53E-AD72-1C75-F8820307529C">
+<bounds x="20" y="20" width="100" height="100"/>
+</OView>
+</objectViews>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultRDBMSSites.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultRDBMSSites.xml
new file mode 100644
index 00000000..07122f85
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultRDBMSSites.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<sites version="1.0">
+ <site name="Oracle Database 11g" type="9" oid="32076570-2523-435C-2E92-BF29817DFF70" pathid="1" />
+ <site name="Oracle Database 10g" type="8" oid="D9582E4E-79E2-319F-387A-2ED963CB9D32" pathid="2" />
+ <site name="Oracle9i" type="7" oid="9807C1FA-0550-772D-1F14-16B19CA63681" pathid="3" />
+ <site name="SQL Server 2005" type="5" oid="B0943E51-0387-1F2A-CED9-5FB738BA5A0C" pathid="4" />
+ <site name="SQL Server 2000" type="4" oid="3424E3DB-6FE1-14EB-9311-F76EF3096E76" pathid="5" />
+ <site name="DB2/390 8" type="1" oid="CC7FDCE5-F5A5-F2C0-C9A7-0C07C92C898D" pathid="6" />
+ <site name="DB2/390 7" type="0" oid="26535E02-9B31-3EDE-24D5-4E3188C99288" pathid="7" />
+ <site name="DB2/UDB 8.1" type="3" oid="2BAE410E-5CEB-5134-8F33-CCB20E003569" pathid="8" />
+ <site name="DB2/UDB 7.1" type="2" oid="BA6252DC-29CE-184D-7701-48F55E3954D4" pathid="9" />
+</sites> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultdomains.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultdomains.xml
new file mode 100644
index 00000000..d858b5c4
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/defaultdomains.xml
@@ -0,0 +1,13 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<DomainFile class="oracle.dbtools.crest.model.design.DomainFileWrapper" fileName="defaultdomains">
+ <domains>
+ <Domain class="oracle.dbtools.crest.model.design.Domain" name="Unknown" id="DOM3000004">
+ <createdBy>bird</createdBy>
+ <createdTime>2012-08-20 21:58:45 UTC</createdTime>
+ <ownerDesignName>System</ownerDesignName>
+ <avTSortOrder>0</avTSortOrder>
+ <fileName>defaultdomains</fileName>
+ <logicalDatatype>LOGDT017</logicalDatatype>
+ </Domain>
+ </domains>
+</DomainFile> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dl_settings.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dl_settings.xml
new file mode 100644
index 00000000..6c426008
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dl_settings.xml
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<settings>
+ <logical_type_for_domain_presentation value="false" />
+ <automatic_pk_generation value="false" />
+ <automatic_uk_generation value="false" />
+ <automatic_fk_generation value="false" />
+ <substitution_patterns>
+ </substitution_patterns>
+ <classification_types>
+ <type name="Fact" color="-7482" prefix="" id="1" />
+ <type name="Dimension" color="-1781507" prefix="" id="2" />
+ <type name="Logging" color="-1776412" prefix="" id="3" />
+ <type name="Summary" color="-3148598" prefix="" id="4" />
+ <type name="Temporary" color="-1" prefix="" id="5" />
+ </classification_types>
+ <default_fonts_and_colors>
+ <fc_object classname="Entity" background="-5971457" foreground="-16776961">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Attribute" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-16744448" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="PK Element" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="FK Element" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="UK Element" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Not Null" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Key" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Logical View" background="-25750" foreground="-16776961">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Attribute" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-16744448" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Table" background="-76" foreground="-16776961">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Column" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-16744448" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="PK Element" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="FK Element" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="UK Element" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Not Null" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Key" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Relational View" background="-6881386" foreground="-16776961">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Column" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-16744448" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Structured Type" background="-7537956" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Attribute" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-16777056" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Method" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Not Instantiable" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Mandatory" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Cube" background="-7482" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Fact Entities" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Measure Type" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Measure" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Function" font_color="-16777056" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Formula" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Child to Parent Attributes" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Dimension" background="-16713196" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Level" background="-1781507" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Level Entity" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Type" font_color="-16776961" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Attribute" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Function" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Process" background="-106" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Process Number" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Transformation Task" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="External Agent" background="-5570646" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Information Store" background="-10170881" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16776961" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Number" font_color="-1" font_name="Dialog" font_size="10" font_style="1"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="In-Out Parameters" background="-328966" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16777216" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Parameters" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ <font_object fo_type="Datatype" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Transformation" background="-43" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16777216" font_name="Dialog" font_size="10" font_style="1"/>
+ <font_object fo_type="Process Number" font_color="-65536" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Note" background="-4144960" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Title" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Label" background="-1" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Text" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ <fc_object classname="Legend" background="-1" foreground="-16777216">
+ <fonts>
+ <font_object fo_type="Text" font_color="-16777216" font_name="Dialog" font_size="10" font_style="0"/>
+ </fonts>
+ </fc_object>
+ </default_fonts_and_colors>
+ <default_line_widths_and_colors>
+ <lwc_object classname="Logical Relation" color="-16777216" width="1">
+ </lwc_object>
+ <lwc_object classname="Logical Inheritance" color="-65536" width="1">
+ </lwc_object>
+ <lwc_object classname="Relational Foreign Key" color="-16777216" width="1">
+ </lwc_object>
+ <lwc_object classname="Type Substitution" color="-16725996" width="1">
+ </lwc_object>
+ <lwc_object classname="Datatype Reference" color="-16776961" width="1">
+ </lwc_object>
+ <lwc_object classname="Datatype Inheritance" color="-65536" width="1">
+ </lwc_object>
+ <lwc_object classname="Multidimentional Link" color="-16776961" width="1">
+ </lwc_object>
+ <lwc_object classname="Multidimensional Hierarchy" color="-16725996" width="1">
+ </lwc_object>
+ <lwc_object classname="Process Flow" color="-65536" width="1">
+ </lwc_object>
+ </default_line_widths_and_colors>
+ <naming_standard_rules>
+ <logical>
+ <separator value= "Title Case" char=" "/>
+ <entity>
+ </entity>
+ <attribute>
+ </attribute>
+ </logical>
+ <relational>
+ <separator value= "_" abbreviated_only="false"/>
+ <table>
+ </table>
+ <column>
+ </column>
+ </relational>
+ <domains>
+ <separator value= " "/>
+ <domain>
+ </domain>
+ </domains>
+ <constraints>
+ <pk value="{table}_PK"/>
+ <fk value="{child}_{parent}_FK"/>
+ <ck value="{table}_CK"/>
+ <un value="{table}_{column}_UN"/>
+ <idx value="{table}_{column}_IDX"/>
+ <colck value="CK_{table}_{column}"/>
+ <column_foreign_key value="{ref table}_{ref column}"/>
+ <ui value="{entity} PK"/>
+ <relation_attribute value="{ref entity}_{ref attribute}"/>
+ </constraints>
+ <glossaries>
+ </glossaries>
+ </naming_standard_rules>
+<comparemapping>
+</comparemapping>
+ <engineering_params>
+ <delete_without_origin value="false"/>
+ <engineer_coordinates value="true"/>
+ <engineer_generated value="false"/>
+ <show_engineering_intree value="false"/>
+ <apply_naming_std value="false"/>
+ <use_pref_abbreviation value="true"/>
+ <upload_directory value=""/>
+ <date_format value="YYYY/MM/DD HH24:MI:SS"/>
+ <timestamp_format value="YYYY/MM/DD HH24:MI:SS.FF"/>
+ <timestamp_tz_format value="YYYY/MM/DD HH24:MI:SS.FFTZH:TZM"/>
+ </engineering_params>
+ <eng_compare show_sel_prop_only="true" not_apply_for_new_objects="true" exclude_from_tree="false">
+ <entity_table>
+ <property name="Name" selected="true"/>
+ <property name="Short Name / Abbreviation" selected="true"/>
+ <property name="Comment" selected="true"/>
+ <property name="Comment in RDBMS" selected="true"/>
+ <property name="Notes" selected="true"/>
+ <property name="Temporary Table Scope" selected="true"/>
+ <property name="Table Type" selected="true"/>
+ <property name="Structured Type" selected="true"/>
+ <property name="Type Substitution (Super-Type Object)" selected="true"/>
+ <property name="Min Volumes" selected="true"/>
+ <property name="Expected Volumes" selected="true"/>
+ <property name="Max Volumes" selected="true"/>
+ <property name="Growth Percent" selected="true"/>
+ <property name="Growth Type" selected="true"/>
+ <property name="Normal Form" selected="true"/>
+ <property name="Adequately Normalized" selected="true"/>
+ </entity_table>
+ <attribute_column>
+ <property name="Name" selected="true"/>
+ <property name="Data Type" selected="true"/>
+ <property name="Data Type Kind" selected="true"/>
+ <property name="Mandatory" selected="true"/>
+ <property name="Default Value" selected="true"/>
+ <property name="Check Constraint Name" selected="true"/>
+ <property name="Use Domain Constraint" selected="true"/>
+ <property name="Check Constraint" selected="true"/>
+ <property name="Range Constraint" selected="true"/>
+ <property name="LOV Constraint" selected="true"/>
+ <property name="Comment" selected="true"/>
+ <property name="Comment in RDBMS" selected="true"/>
+ <property name="Notes" selected="true"/>
+ <property name="Source Type" selected="true"/>
+ <property name="Formula Description" selected="true"/>
+ <property name="Type Substitution" selected="true"/>
+ <property name="Scope" selected="true"/>
+ </attribute_column>
+ <key_index>
+ <property name="Name" selected="true"/>
+ <property name="Comment" selected="true"/>
+ <property name="Comment in RDBMS" selected="true"/>
+ <property name="Notes" selected="true"/>
+ <property name="Primary Key" selected="true"/>
+ <property name="Attributes/Columns" selected="true"/>
+ </key_index>
+ <relation_fk>
+ <property name="Name" selected="true"/>
+ <property name="Delete Rule" selected="true"/>
+ <property name="Comment" selected="true"/>
+ <property name="Comment in RDBMS" selected="true"/>
+ <property name="Notes" selected="true"/>
+ </relation_fk>
+ <entityview_view>
+ <property name="Name" selected="true"/>
+ <property name="Comment" selected="true"/>
+ <property name="Comment in RDBMS" selected="true"/>
+ <property name="Notes" selected="true"/>
+ <property name="Structured Type" selected="true"/>
+ <property name="Where" selected="true"/>
+ <property name="Having" selected="true"/>
+ <property name="User Defined SQL" selected="true"/>
+ </entityview_view>
+ </eng_compare>
+ <naming_options>
+ <model_options objectid="B082B14A-BEA8-D8A7-D661-197F34766ED3">
+ <naming_option class_name="oracle.dbtools.crest.model.design.relational.Table" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.relational.Column" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.relational.TableView" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.constraint.TableLevelConstraint" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.relational.FKIndexAssociation" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.relational.Index" max_name_length="30" case_type="2" valid_characters="" all_valid="true" />
+ </model_options>
+ <model_options objectid="E3665D68-35D3-8757-63ED-30AEFB972A2C">
+ <naming_option class_name="oracle.dbtools.crest.model.design.logical.Entity" max_name_length="254" case_type="2" valid_characters="[a-z][A-Z][0-9]" all_valid="false" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.logical.Attribute" max_name_length="254" case_type="2" valid_characters="[a-z][A-Z][0-9] " all_valid="false" />
+ <naming_option class_name="oracle.dbtools.crest.model.design.logical.EntityView" max_name_length="254" case_type="2" valid_characters="[a-z][A-Z][0-9] " all_valid="false" />
+ </model_options>
+ </naming_options>
+ <merge_conflicts>
+ </merge_conflicts>
+ <deleted_files>
+ </deleted_files>
+</settings> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dr_custom_scripts.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dr_custom_scripts.xml
new file mode 100644
index 00000000..3580f0f0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/dr_custom_scripts.xml
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<dr_custom_scripts>
+ <scr id="D36CE536-D575-BE5C-625F-23DE23913C6B" name="Complex rule - check comments demo" object="Table" engine="Mozilla Rhino" type="Warning" var="table" library="my first library" method="checkcomments" purpose="validation" >
+ <script>
+ <![CDATA[var ruleMessage;
+var errType;
+var table;
+function checkcomments(object){
+ result = true;
+ ruleMessage="";
+ if(table.getCommentInRDBMS().equals("")){
+ ruleMessage="no comments in RDBMS defined";
+ errType="Problem:";
+ result = false;
+ }
+ if(table.getComment().equals("")){
+ if(ruleMessage.equals("")){
+ ruleMessage="no comments defined";
+ }else{
+ ruleMessage= ruleMessage +" , no comments defined";
+ }
+ errType="Error";
+ return false;
+ }
+ return result;
+}]]>
+ </script>
+ </scr>
+ <scr id="0BAA564F-AB5F-D776-2E4F-31FDB3047F69" name="Tables to lower case - Rhino" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ name = table.getName().toLowerCase();
+ table.setName(name);
+ columns = table.getElements();
+ size = table.getElementsCollection().size();
+ for (var i = 0; i < size; i++) {
+ column = columns[i];
+ cname = column.getName().toLowerCase();
+ column.setName(cname);
+ }
+ table.setDirty(true);
+ keys = table.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ key = keys[i];
+ if(!key.isFK()){
+ kname = key.getName().toLowerCase();
+ key.setName(kname);
+ }else{
+ kname = key.getFKAssociation().getName().toLowerCase();
+ key.getFKAssociation().setName(kname);
+ key.getFKAssociation().setDirty(true);
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="B673F271-4836-DD48-15AC-487DDECCAF49" name="Tables to upper case - JRuby" object="relational" engine="JSR 223 JRuby Engine" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[tables =$model.getTableSet().toArray()
+for t in 0..tables.length-1
+ table = tables[t]
+ name = table.getName().upcase
+ table.setName(name)
+ columns = table.getElements()
+ size = table.getElementsCollection().size()-1
+ for i in 0..size
+ column = columns[i]
+ cname = column.getName().upcase
+ column.setName(cname)
+ end
+ keys = table.getKeys()
+ for i in 0..keys.length-1
+ key = keys[i]
+ kname = key.getName().upcase
+ key.setName(kname)
+ end
+end]]>
+ </script>
+ </scr>
+ <scr id="3E7C4F9E-9FCB-56C7-086F-F976F9A66384" name="Tables to upper case - JRuby - library usage" object="relational" engine="JSR 223 JRuby Engine" type="" var="model" library="Jruby lib" method="tables_up" purpose="transformation" >
+ <script>
+ <![CDATA[def tables_up(model)
+tables = model.getTableSet().toArray()
+for t in 0..tables.length-1
+ table = tables[t]
+ name = table.getName().upcase
+ table.setName(name)
+ columns = table.getElements()
+ size = table.getElementsCollection().size()-1
+ for i in 0..size
+ column = columns[i]
+ cname = column.getName().upcase
+ column.setName(cname)
+ end
+ keys = table.getKeys()
+ for i in 0..keys.length-1
+ key = keys[i]
+ kname = key.getName().upcase
+ key.setName(kname)
+ end
+end
+return true
+end]]>
+ </script>
+ </scr>
+ <scr id="E60A5A28-BB9B-3787-10E7-259DF900B9E6" name="Table abbreviation to column" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ abbr = table.getAbbreviation()+"_";
+ if(!"_".equals(abbr)){
+ columns = table.getElements();
+ for (var i = 0; i < columns.length; i++) {
+ column = columns[i];
+ cname = column.getName();
+ if(!cname.startsWith(abbr)){
+ column.setName(abbr+cname);
+ }
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="9BE4E26C-36D8-A92C-ADEA-F183327DC239" name="Remove Table abbr from column" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ abbr = table.getAbbreviation()+"_";
+ count = table.getAbbreviation().length()+1;
+ if(!"_".equals(abbr)){
+ columns = table.getElements();
+ for (var i = 0; i < columns.length; i++) {
+ column = columns[i];
+ cname = column.getName();
+ if(cname.startsWith(abbr)){
+ column.setName(cname.substring(count));
+ table.setDirty(true);
+ }
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="23BE8827-D732-72B0-C6E6-266EFE116EDD" name="Table template" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[var t_name = "table_template";
+var p_name = "ctemplateID";
+template = model.getTableSet().getByName(t_name);
+if(template!=null){
+ tcolumns = template.getElements();
+ tables = model.getTableSet().toArray();
+ for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ // compare name ignoring the case
+ if(!table.getName().equalsIgnoreCase(t_name)){
+ for (var i = 0; i < tcolumns.length; i++) {
+ column = tcolumns[i];
+ col = table.getColumnByProperty(p_name,column.getObjectID());
+ if(col==null){
+ col = table.createColumn();
+ }
+ column.copy(col);
+ //set property after copy otherwise it'll be cleared
+ col.setProperty(p_name,column.getObjectID());
+ table.setDirty(true);
+ }
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="5A8A151A-13FD-4B0A-E233-E3C5126BA02C" name="Tables to upper case - Rhino" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ name = table.getName().toUpperCase();
+ table.setName(name);
+ columns = table.getElements();
+ size = table.getElementsCollection().size();
+ for (var i = 0; i < size; i++) {
+ column = columns[i];
+ cname = column.getName().toUpperCase();
+ column.setName(cname);
+ }
+ table.setDirty(true);
+ keys = table.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ key = keys[i];
+ if(!key.isFK()){
+ kname = key.getName().toUpperCase();
+ key.setName(kname);
+ }else{
+ kname = key.getFKAssociation().getName().toUpperCase();
+ key.getFKAssociation().setName(kname);
+ key.getFKAssociation().setDirty(true);
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="0528C35C-F29B-E7BB-57AC-37BA2780A98D" name="Table template - uses column name" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[// version without usage of dynamic properties, columns are found by column name
+// this allow reuse of already existing columns
+var t_name = "table_template";
+template = model.getTableSet().getByName(t_name);
+if(template!=null){
+ tcolumns = template.getElements();
+ tables = model.getTableSet().toArray();
+ for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ // compare name ignoring the case
+ if(!table.getName().equalsIgnoreCase(t_name)){
+ for (var i = 0; i < tcolumns.length; i++) {
+ column = tcolumns[i];
+ col = table.getElementByName(column.getName());
+ if(col==null){
+ col = table.createColumn();
+ }
+ column.copy(col);
+ table.setDirty(true);
+ }
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="6279C414-90DD-A52B-4CEB-8D49AB31DC10" name="Copy Comments to Comments in RDBMS" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[max_length = 4000;
+function copyComments(object){
+ if(object.getCommentInRDBMS().equals("")){
+ if(!object.getComment().equals("")){
+ if(object.getComment().length()>max_length){
+ object.setCommentInRDBMS(object.getComment().substring(0, max_length));
+ }else{
+ object.setCommentInRDBMS(object.getComment());
+ }
+ object.setDirty(true);
+ }
+ }
+}
+
+tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t]
+ copyComments(table);
+ columns = table.getElements();
+ size = table.getElementsCollection().size();
+ for (var i = 0; i < columns.length; i++) {
+ column = columns[i];
+ copyComments(column);
+ }
+ keys = table.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ key = keys[i];
+ if(!key.isFK()){
+ copyComments(key);
+ }else{
+ copyComments(key.getFKAssociation());
+ }
+ }
+}]]>
+ </script>
+ </scr>
+ <scr id="7C4EDFC0-26EA-859C-DBD9-AC9345DEAF98" name="Create index on FK" object="relational" engine="Mozilla Rhino" type="" var="model" library="" method="" purpose="transformation" >
+ <script>
+ <![CDATA[function getIndex(tab,cols){
+ keys = tab.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ index = keys[i];
+ if(!(index.isPK() || index.isUnique()) && !index.isFK() && index.isIndexForColumns(cols)){
+ return index
+ }
+ }
+ return null;
+}
+
+tables = model.getTableSet().toArray();
+for (var t = 0; t<tables.length;t++){
+ table = tables[t];
+ indexes = table.getKeys();
+ for (var i = 0; i < indexes.length; i++) {
+ index = indexes[i];
+ if(index.isFK()){
+ columns = index.getColumns();
+ if(columns.length>0){
+ newIndex = getIndex(table,columns);
+ if(newIndex==null){
+ newIndex = table.createIndex()
+ table.setDirty(true);
+ for (var k = 0; k < columns.length; k++){
+ newIndex.add(columns[k]);
+ }
+ }
+ }
+ }
+ }
+}]]>
+ </script>
+ </scr>
+
+ <lib id="B310E434-78AE-6AED-EA94-6808B0262483" name="my first library" engine="Mozilla Rhino" methods="checkcomments" >
+ <script>
+ <![CDATA[var ruleMessage;
+var errType;
+var table;
+function checkcomments(object){
+ result = true;
+ ruleMessage="";
+ if(table.getCommentInRDBMS().equals("")){
+ ruleMessage="no comments in RDBMS defined";
+ errType="Problem:";
+ result = false;
+ }
+ if(table.getComment().equals("")){
+ if(ruleMessage.equals("")){
+ ruleMessage="no comments defined";
+ }else{
+ ruleMessage= ruleMessage +" , no comments defined";
+ }
+ errType="Error";
+ return false;
+ }
+ return result;
+}]]>
+ </script>
+ </lib>
+ <lib id="2518F33A-DE50-9E1D-7216-DD2A0FD6B84C" name="Jruby lib" engine="JRuby Engine" methods="tables_up" >
+ <script>
+ <![CDATA[def tables_up(model)
+tables = model.getTableSet().toArray()
+for t in 0..tables.length-1
+ table = tables[t]
+ name = table.getName().upcase
+ table.setName(name)
+ columns = table.getElements()
+ size = table.getElementsCollection().size()-1
+ for i in 0..size
+ column = columns[i]
+ cname = column.getName().upcase
+ column.setName(cname)
+ end
+ keys = table.getKeys()
+ for i in 0..keys.length-1
+ key = keys[i]
+ kname = key.getName().upcase
+ key.setName(kname)
+ end
+end
+return true
+end]]>
+ </script>
+ </lib>
+</dr_custom_scripts> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/Logical.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/Logical.xml
new file mode 100644
index 00000000..0403a605
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/Logical.xml
@@ -0,0 +1,7 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<LogicalDesign class="oracle.dbtools.crest.model.design.logical.LogicalDesign" name="Logical" id="E3665D68-35D3-8757-63ED-30AEFB972A2C" mainViewID="AFCEF013-4CF2-4A5A-79A3-31521C1CA20A">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<shouldBeOpen>false</shouldBeOpen>
+</LogicalDesign> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/16464F5A-64BE-D2ED-91E0-BCBD0AA34680.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/16464F5A-64BE-D2ED-91E0-BCBD0AA34680.xml
new file mode 100644
index 00000000..6904bb54
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/16464F5A-64BE-D2ED-91E0-BCBD0AA34680.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="16464F5A-64BE-D2ED-91E0-BCBD0AA34680" directorySegmentName="seg_0" name="TestResults">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:11:26 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/1BEAB532-23CA-8628-0C97-7CAD39119A4E.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/1BEAB532-23CA-8628-0C97-7CAD39119A4E.xml
new file mode 100644
index 00000000..9f54c6cd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/1BEAB532-23CA-8628-0C97-7CAD39119A4E.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="1BEAB532-23CA-8628-0C97-7CAD39119A4E" directorySegmentName="seg_0" name="TestCaseArgs">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:38:18 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/24150FB1-B00F-4F69-6F77-49ECB58F0F66.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/24150FB1-B00F-4F69-6F77-49ECB58F0F66.xml
new file mode 100644
index 00000000..3a02553a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/24150FB1-B00F-4F69-6F77-49ECB58F0F66.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="24150FB1-B00F-4F69-6F77-49ECB58F0F66" directorySegmentName="seg_0" name="BuildSources">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:54:55 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/28DD93CF-D058-7343-CD47-E9B435E1AC16.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/28DD93CF-D058-7343-CD47-E9B435E1AC16.xml
new file mode 100644
index 00000000..3a9992c9
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/28DD93CF-D058-7343-CD47-E9B435E1AC16.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="28DD93CF-D058-7343-CD47-E9B435E1AC16" directorySegmentName="seg_0" name="TestResultFiles">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:12:51 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/2F6ACC6D-3D17-537D-8ADF-F8424395B345.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/2F6ACC6D-3D17-537D-8ADF-F8424395B345.xml
new file mode 100644
index 00000000..4ea40fc7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/2F6ACC6D-3D17-537D-8ADF-F8424395B345.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="2F6ACC6D-3D17-537D-8ADF-F8424395B345" directorySegmentName="seg_0" name="GlobalRsrcStatuses">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:17:42 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39.xml
new file mode 100644
index 00000000..e3300354
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39" directorySegmentName="seg_0" name="FailureReasons">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:47:11 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4579B792-2F35-D72A-1A3B-C7E53C41A766.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4579B792-2F35-D72A-1A3B-C7E53C41A766.xml
new file mode 100644
index 00000000..e35d8bc0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4579B792-2F35-D72A-1A3B-C7E53C41A766.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="4579B792-2F35-D72A-1A3B-C7E53C41A766" directorySegmentName="seg_0" name="TestResultMsgs">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:13:03 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4D937E7C-3A28-E52D-89C0-EC8804C62367.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4D937E7C-3A28-E52D-89C0-EC8804C62367.xml
new file mode 100644
index 00000000..7b2d1d01
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/4D937E7C-3A28-E52D-89C0-EC8804C62367.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="4D937E7C-3A28-E52D-89C0-EC8804C62367" directorySegmentName="seg_0" name="FailureCategories">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:47:19 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/504221DA-1B57-4EAD-39DB-40FD553E9FA2.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/504221DA-1B57-4EAD-39DB-40FD553E9FA2.xml
new file mode 100644
index 00000000..e536867c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/504221DA-1B57-4EAD-39DB-40FD553E9FA2.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="504221DA-1B57-4EAD-39DB-40FD553E9FA2" directorySegmentName="seg_0" name="Builds">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:52:15 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/6A886CEE-579B-48FF-63F6-0FB03393FBF6.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/6A886CEE-579B-48FF-63F6-0FB03393FBF6.xml
new file mode 100644
index 00000000..20424c7c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/6A886CEE-579B-48FF-63F6-0FB03393FBF6.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="6A886CEE-579B-48FF-63F6-0FB03393FBF6" directorySegmentName="seg_0" name="SchedGroups">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:16:15 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/7AE36CC1-A030-63E5-6EF3-72FCD04815EE.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/7AE36CC1-A030-63E5-6EF3-72FCD04815EE.xml
new file mode 100644
index 00000000..9475385d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/7AE36CC1-A030-63E5-6EF3-72FCD04815EE.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="7AE36CC1-A030-63E5-6EF3-72FCD04815EE" directorySegmentName="seg_0" name="TestBoxes">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:34:30 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90367AFB-BA2D-A918-46B9-1E5DE53ACC48.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90367AFB-BA2D-A918-46B9-1E5DE53ACC48.xml
new file mode 100644
index 00000000..96b815e1
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90367AFB-BA2D-A918-46B9-1E5DE53ACC48.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="90367AFB-BA2D-A918-46B9-1E5DE53ACC48" directorySegmentName="seg_0" name="BuildBlacklist">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:59:31 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90F477EE-35D6-21A7-B693-E5724FB07476.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90F477EE-35D6-21A7-B693-E5724FB07476.xml
new file mode 100644
index 00000000..6bcf734c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/90F477EE-35D6-21A7-B693-E5724FB07476.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="90F477EE-35D6-21A7-B693-E5724FB07476" directorySegmentName="seg_0" name="TestSets">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:11:20 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/9F78B73C-056D-DDEF-8C50-A9DA76B9E724.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/9F78B73C-056D-DDEF-8C50-A9DA76B9E724.xml
new file mode 100644
index 00000000..d672b27e
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/9F78B73C-056D-DDEF-8C50-A9DA76B9E724.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="9F78B73C-056D-DDEF-8C50-A9DA76B9E724" directorySegmentName="seg_0" name="BuildTypes">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:52:32 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A352A20F-310D-E285-FBC9-90DD0DA7BB9B.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A352A20F-310D-E285-FBC9-90DD0DA7BB9B.xml
new file mode 100644
index 00000000..301a3f28
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A352A20F-310D-E285-FBC9-90DD0DA7BB9B.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="A352A20F-310D-E285-FBC9-90DD0DA7BB9B" directorySegmentName="seg_0" name="TestBoxStatuses">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:09:55 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A6A5F317-479C-A0DD-CAAE-9DCB56B29D40.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A6A5F317-479C-A0DD-CAAE-9DCB56B29D40.xml
new file mode 100644
index 00000000..a6f31387
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/A6A5F317-479C-A0DD-CAAE-9DCB56B29D40.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="A6A5F317-479C-A0DD-CAAE-9DCB56B29D40" directorySegmentName="seg_0" name="RequirementSets">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:14:04 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B36A186B-CDB3-7851-8C38-12EA8D50EAEB.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B36A186B-CDB3-7851-8C38-12EA8D50EAEB.xml
new file mode 100644
index 00000000..7e22bcc2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B36A186B-CDB3-7851-8C38-12EA8D50EAEB.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="B36A186B-CDB3-7851-8C38-12EA8D50EAEB" directorySegmentName="seg_0" name="RequirementsNum">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:14:37 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B82DAF9A-6F99-5CF6-4D99-A391BAD66192.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B82DAF9A-6F99-5CF6-4D99-A391BAD66192.xml
new file mode 100644
index 00000000..aa84dcf3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/B82DAF9A-6F99-5CF6-4D99-A391BAD66192.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="B82DAF9A-6F99-5CF6-4D99-A391BAD66192" directorySegmentName="seg_0" name="TestCases">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:34:30 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C332E3D7-638B-6CA8-24BF-383CA8659A3A.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C332E3D7-638B-6CA8-24BF-383CA8659A3A.xml
new file mode 100644
index 00000000..f093d805
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C332E3D7-638B-6CA8-24BF-383CA8659A3A.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="C332E3D7-638B-6CA8-24BF-383CA8659A3A" directorySegmentName="seg_0" name="SchedQueues">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:09:44 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C79482B8-771B-FAD8-0337-163E3A45003A.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C79482B8-771B-FAD8-0337-163E3A45003A.xml
new file mode 100644
index 00000000..3550b18c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/C79482B8-771B-FAD8-0337-163E3A45003A.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="C79482B8-771B-FAD8-0337-163E3A45003A" directorySegmentName="seg_0" name="GlobalResources">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:13:16 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/D09E0DE5-99D6-2991-032A-A8A124F6ACBA.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/D09E0DE5-99D6-2991-032A-A8A124F6ACBA.xml
new file mode 100644
index 00000000..1e10ffb7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/D09E0DE5-99D6-2991-032A-A8A124F6ACBA.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="D09E0DE5-99D6-2991-032A-A8A124F6ACBA" directorySegmentName="seg_0" name="TestResultValues">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:11:32 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DCC79294-5434-1DED-298C-6473DEE59FBA.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DCC79294-5434-1DED-298C-6473DEE59FBA.xml
new file mode 100644
index 00000000..7891dab7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DCC79294-5434-1DED-298C-6473DEE59FBA.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="DCC79294-5434-1DED-298C-6473DEE59FBA" directorySegmentName="seg_0" name="TestResultFailures">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:46:51 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DE366053-6F7A-7F42-ABA3-00E583098C37.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DE366053-6F7A-7F42-ABA3-00E583098C37.xml
new file mode 100644
index 00000000..145b2c76
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/DE366053-6F7A-7F42-ABA3-00E583098C37.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="DE366053-6F7A-7F42-ABA3-00E583098C37" directorySegmentName="seg_0" name="TestGroups">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:34:30 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/E93BBF08-067B-A665-39F3-CF488A6547B2.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/E93BBF08-067B-A665-39F3-CF488A6547B2.xml
new file mode 100644
index 00000000..c8632bf7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/entity/seg_0/E93BBF08-067B-A665-39F3-CF488A6547B2.xml
@@ -0,0 +1,52 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Entity class="oracle.dbtools.crest.model.design.logical.Entity" id="E93BBF08-067B-A665-39F3-CF488A6547B2" directorySegmentName="seg_0" name="RequirementsText">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:14:21 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<adequatelyNormalized>NO</adequatelyNormalized>
+<expectedVolumes>0</expectedVolumes>
+<fwdEngineeringStrategyName>Single Table</fwdEngineeringStrategyName>
+<growthPercent>0</growthPercent>
+<growthType>Year</growthType>
+<maxVolumes>9999999</maxVolumes>
+<minVolumes>0</minVolumes>
+<normalForm>Third</normalForm>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<fontStyle>1</fontStyle>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Attribute</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Datatype</foType>
+<colorRGB>-16744448</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>PK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>FK Element</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>UK Element</foType>
+<colorRGB>-16776961</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Not Null</foType>
+<colorRGB>-65536</colorRGB>
+</FontObject>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Key</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Entity> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/876CB767-80BA-6C8E-AACA-F1CCC95C445E.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/876CB767-80BA-6C8E-AACA-F1CCC95C445E.xml
new file mode 100644
index 00000000..31ddc417
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/876CB767-80BA-6C8E-AACA-F1CCC95C445E.xml
@@ -0,0 +1,16 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Note class="oracle.dbtools.crest.model.design.Note" name="Note_1" id="876CB767-80BA-6C8E-AACA-F1CCC95C445E" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:43:49 UTC</createdTime>
+<comment>Priority, scheduling time, and testgroup dependencies are associated with SchedGroup membership.</comment>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Note> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/D487AFDC-4027-F824-EA29-5C6D0ABB9E1E.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/D487AFDC-4027-F824-EA29-5C6D0ABB9E1E.xml
new file mode 100644
index 00000000..9152a7c6
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/note/seg_0/D487AFDC-4027-F824-EA29-5C6D0ABB9E1E.xml
@@ -0,0 +1,16 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Note class="oracle.dbtools.crest.model.design.Note" name="Note_3" id="D487AFDC-4027-F824-EA29-5C6D0ABB9E1E" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:57:21 UTC</createdTime>
+<comment>Testsuite and build sources.</comment>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<fonts>
+<FontObject class="oracle.dbtools.crest.model.design.FontObjectWr">
+<foType>Title</foType>
+<colorRGB>-16777216</colorRGB>
+</FontObject>
+</fonts>
+</Note> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/01537211-CCFB-0A1E-B43B-E8C641B69471.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/01537211-CCFB-0A1E-B43B-E8C641B69471.xml
new file mode 100644
index 00000000..e8b317cd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/01537211-CCFB-0A1E-B43B-E8C641B69471.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="WhichTestcaseArgs" id="01537211-CCFB-0A1E-B43B-E8C641B69471" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:57:18 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>90F477EE-35D6-21A7-B693-E5724FB07476</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>1BEAB532-23CA-8628-0C97-7CAD39119A4E</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/02096BBB-0795-1759-1E26-2877BE36BB59.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/02096BBB-0795-1759-1E26-2877BE36BB59.xml
new file mode 100644
index 00000000..48df0a07
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/02096BBB-0795-1759-1E26-2877BE36BB59.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="NestedTestResults" id="02096BBB-0795-1759-1E26-2877BE36BB59" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:16:26 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/0CCF1DE3-7916-9054-BEA6-C601FF564DB2.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/0CCF1DE3-7916-9054-BEA6-C601FF564DB2.xml
new file mode 100644
index 00000000..e5304e1d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/0CCF1DE3-7916-9054-BEA6-C601FF564DB2.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestBoxGrouping" id="0CCF1DE3-7916-9054-BEA6-C601FF564DB2" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:35:28 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>true</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>7AE36CC1-A030-63E5-6EF3-72FCD04815EE</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>6A886CEE-579B-48FF-63F6-0FB03393FBF6</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/10867E70-94CE-FDAF-6B6E-2742D3A49E57.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/10867E70-94CE-FDAF-6B6E-2742D3A49E57.xml
new file mode 100644
index 00000000..ed642271
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/10867E70-94CE-FDAF-6B6E-2742D3A49E57.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="ReasonForBlacklisting" id="10867E70-94CE-FDAF-6B6E-2742D3A49E57" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:56:22 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>90367AFB-BA2D-A918-46B9-1E5DE53ACC48</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/11710A55-6423-1904-841A-C7D2AB8CEEBF.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/11710A55-6423-1904-841A-C7D2AB8CEEBF.xml
new file mode 100644
index 00000000..4c37ff79
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/11710A55-6423-1904-841A-C7D2AB8CEEBF.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestResultValues\" id="11710A55-6423-1904-841A-C7D2AB8CEEBF" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:17:15 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>D09E0DE5-99D6-2991-032A-A8A124F6ACBA</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/1C189437-742B-B999-C955-7754C8ADB089.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/1C189437-742B-B999-C955-7754C8ADB089.xml
new file mode 100644
index 00000000..ee340833
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/1C189437-742B-B999-C955-7754C8ADB089.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="SchedTestGroupMembership" id="1C189437-742B-B999-C955-7754C8ADB089" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:46:08 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>6A886CEE-579B-48FF-63F6-0FB03393FBF6</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>DE366053-6F7A-7F42-ABA3-00E583098C37</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/34733942-1305-4CA1-47EB-ACE724B04E69.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/34733942-1305-4CA1-47EB-ACE724B04E69.xml
new file mode 100644
index 00000000..bde14e2c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/34733942-1305-4CA1-47EB-ACE724B04E69.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestResultFiles" id="34733942-1305-4CA1-47EB-ACE724B04E69" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:16:58 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>28DD93CF-D058-7343-CD47-E9B435E1AC16</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3563C940-E524-7F96-7AE0-DAC3C1C17AFC.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3563C940-E524-7F96-7AE0-DAC3C1C17AFC.xml
new file mode 100644
index 00000000..0d924eae
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3563C940-E524-7F96-7AE0-DAC3C1C17AFC.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestedBuild" id="3563C940-E524-7F96-7AE0-DAC3C1C17AFC" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 10:14:03 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>true</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>504221DA-1B57-4EAD-39DB-40FD553E9FA2</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>90F477EE-35D6-21A7-B693-E5724FB07476</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C.xml
new file mode 100644
index 00000000..f0a22501
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="BuildSource" id="3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:55:43 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>24150FB1-B00F-4F69-6F77-49ECB58F0F66</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>6A886CEE-579B-48FF-63F6-0FB03393FBF6</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3B7C8913-EB6A-47B1-27D0-E2C85EE9048B.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3B7C8913-EB6A-47B1-27D0-E2C85EE9048B.xml
new file mode 100644
index 00000000..9a95a66a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/3B7C8913-EB6A-47B1-27D0-E2C85EE9048B.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="NumericalRequirement" id="3B7C8913-EB6A-47B1-27D0-E2C85EE9048B" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:41:40 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>true</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>A6A5F317-479C-A0DD-CAAE-9DCB56B29D40</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>B36A186B-CDB3-7851-8C38-12EA8D50EAEB</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/518CE489-97B4-C05C-07A2-E3DBF14EE267.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/518CE489-97B4-C05C-07A2-E3DBF14EE267.xml
new file mode 100644
index 00000000..7987194b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/518CE489-97B4-C05C-07A2-E3DBF14EE267.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestResultFailureReason" id="518CE489-97B4-C05C-07A2-E3DBF14EE267" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:58:35 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>DCC79294-5434-1DED-298C-6473DEE59FBA</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/68A0C3E1-0FA1-8414-A361-33B08A8EDB39.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/68A0C3E1-0FA1-8414-A361-33B08A8EDB39.xml
new file mode 100644
index 00000000..bf2200dc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/68A0C3E1-0FA1-8414-A361-33B08A8EDB39.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="FailureRegardingTestResult" id="68A0C3E1-0FA1-8414-A361-33B08A8EDB39" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:48:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>DCC79294-5434-1DED-298C-6473DEE59FBA</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7497D76B-781B-3BDD-D797-FFBDB974F772.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7497D76B-781B-3BDD-D797-FFBDB974F772.xml
new file mode 100644
index 00000000..43673229
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7497D76B-781B-3BDD-D797-FFBDB974F772.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="GlobalResourceDependencies" id="7497D76B-781B-3BDD-D797-FFBDB974F772" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:42:25 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>C79482B8-771B-FAD8-0337-163E3A45003A</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635.xml
new file mode 100644
index 00000000..dd75d4cb
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestResultMessages" id="7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:17:23 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>4579B792-2F35-D72A-1A3B-C7E53C41A766</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/89A83E25-364B-6B73-0613-FEAD875EF9FB.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/89A83E25-364B-6B73-0613-FEAD875EF9FB.xml
new file mode 100644
index 00000000..e8a4730c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/89A83E25-364B-6B73-0613-FEAD875EF9FB.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestcaseArguments" id="89A83E25-364B-6B73-0613-FEAD875EF9FB" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:40:39 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>1BEAB532-23CA-8628-0C97-7CAD39119A4E</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2.xml
new file mode 100644
index 00000000..9d086559
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="WhatToRun" id="8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:41:56 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>C332E3D7-638B-6CA8-24BF-383CA8659A3A</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>1BEAB532-23CA-8628-0C97-7CAD39119A4E</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51.xml
new file mode 100644
index 00000000..b50ed32a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="WhichResource" id="9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:52:20 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>2F6ACC6D-3D17-537D-8ADF-F8424395B345</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>C79482B8-771B-FAD8-0337-163E3A45003A</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/A182A65A-47AE-5D00-9A30-BC20AB050BF2.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/A182A65A-47AE-5D00-9A30-BC20AB050BF2.xml
new file mode 100644
index 00000000..b29652bd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/A182A65A-47AE-5D00-9A30-BC20AB050BF2.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestSetResult" id="A182A65A-47AE-5D00-9A30-BC20AB050BF2" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:15:48 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>90F477EE-35D6-21A7-B693-E5724FB07476</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>16464F5A-64BE-D2ED-91E0-BCBD0AA34680</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B346381F-48FE-E495-01A7-E22EC26AEE8A.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B346381F-48FE-E495-01A7-E22EC26AEE8A.xml
new file mode 100644
index 00000000..ba60f398
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B346381F-48FE-E495-01A7-E22EC26AEE8A.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestGroupMember" id="B346381F-48FE-E495-01A7-E22EC26AEE8A" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:37:24 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>DE366053-6F7A-7F42-ABA3-00E583098C37</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B3596116-540F-6397-ECE4-58A386644E15.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B3596116-540F-6397-ECE4-58A386644E15.xml
new file mode 100644
index 00000000..d4f9edd8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/B3596116-540F-6397-ECE4-58A386644E15.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestcaseDependencies" id="B3596116-540F-6397-ECE4-58A386644E15" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:39:51 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/BAD8EC05-6F14-4E38-366C-B4B660C6F38A.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/BAD8EC05-6F14-4E38-366C-B4B660C6F38A.xml
new file mode 100644
index 00000000..da1e2a8f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/BAD8EC05-6F14-4E38-366C-B4B660C6F38A.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="InFailureCategory" id="BAD8EC05-6F14-4E38-366C-B4B660C6F38A" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 11:57:18 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>true</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>4D937E7C-3A28-E52D-89C0-EC8804C62367</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE.xml
new file mode 100644
index 00000000..d75c9a0a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="WhichTestBox" id="C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:59:42 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>90F477EE-35D6-21A7-B693-E5724FB07476</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>7AE36CC1-A030-63E5-6EF3-72FCD04815EE</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/CCD38E11-8557-EB34-2651-07EB29E83FA6.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/CCD38E11-8557-EB34-2651-07EB29E83FA6.xml
new file mode 100644
index 00000000..bf216b5d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/CCD38E11-8557-EB34-2651-07EB29E83FA6.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestSuiteSource" id="CCD38E11-8557-EB34-2651-07EB29E83FA6" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:56:11 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>24150FB1-B00F-4F69-6F77-49ECB58F0F66</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>6A886CEE-579B-48FF-63F6-0FB03393FBF6</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E2A47942-ED55-E81D-4C71-9A134C49C147.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E2A47942-ED55-E81D-4C71-9A134C49C147.xml
new file mode 100644
index 00000000..5164076c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E2A47942-ED55-E81D-4C71-9A134C49C147.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestBox" id="E2A47942-ED55-E81D-4C71-9A134C49C147" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:43:14 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>false</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>7AE36CC1-A030-63E5-6EF3-72FCD04815EE</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>A352A20F-310D-E285-FBC9-90DD0DA7BB9B</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E4FE88E9-EE21-B43B-B0FE-A153E38246F9.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E4FE88E9-EE21-B43B-B0FE-A153E38246F9.xml
new file mode 100644
index 00000000..fc0ec020
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E4FE88E9-EE21-B43B-B0FE-A153E38246F9.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TestcaseRequirements" id="E4FE88E9-EE21-B43B-B0FE-A153E38246F9" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:38:38 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>*</sourceCardinality>
+<sourceEntity>B82DAF9A-6F99-5CF6-4D99-A391BAD66192</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>A6A5F317-479C-A0DD-CAAE-9DCB56B29D40</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E62AE7DF-49EE-9280-B328-A867CBD273AE.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E62AE7DF-49EE-9280-B328-A867CBD273AE.xml
new file mode 100644
index 00000000..3121966f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E62AE7DF-49EE-9280-B328-A867CBD273AE.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="CurrentTestSet" id="E62AE7DF-49EE-9280-B328-A867CBD273AE" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:48:53 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>A352A20F-310D-E285-FBC9-90DD0DA7BB9B</sourceEntity>
+<targetCardinalityString>1</targetCardinalityString>
+<targetEntity>90F477EE-35D6-21A7-B693-E5724FB07476</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E74406B5-20F1-4323-DC99-6E45982CB606.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E74406B5-20F1-4323-DC99-6E45982CB606.xml
new file mode 100644
index 00000000..498ce1fb
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/E74406B5-20F1-4323-DC99-6E45982CB606.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="TextRequirements" id="E74406B5-20F1-4323-DC99-6E45982CB606" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:41:57 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>true</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>A6A5F317-479C-A0DD-CAAE-9DCB56B29D40</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>E93BBF08-067B-A665-39F3-CF488A6547B2</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EC4EB506-3DBE-7F36-6451-F31920EDAB52.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EC4EB506-3DBE-7F36-6451-F31920EDAB52.xml
new file mode 100644
index 00000000..18840e25
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EC4EB506-3DBE-7F36-6451-F31920EDAB52.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="AllocatedBy" id="EC4EB506-3DBE-7F36-6451-F31920EDAB52" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:44:47 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>true</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>7AE36CC1-A030-63E5-6EF3-72FCD04815EE</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>2F6ACC6D-3D17-537D-8ADF-F8424395B345</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880.xml
new file mode 100644
index 00000000..6fcc7e2b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/relation/seg_0/EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880.xml
@@ -0,0 +1,17 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Relation class="oracle.dbtools.crest.model.design.logical.Relation" name="BuildToType" id="EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880" directorySegmentName="seg_0">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:53:25 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<engineerTo>
+<item key="B082B14A-BEA8-D8A7-D661-197F34766ED3" value="true"/>
+</engineerTo>
+<identifying>false</identifying>
+<optionalSource>true</optionalSource>
+<optionalTarget>false</optionalTarget>
+<sourceCardinality>1</sourceCardinality>
+<sourceEntity>9F78B73C-056D-DDEF-8C50-A9DA76B9E724</sourceEntity>
+<targetCardinalityString>*</targetCardinalityString>
+<targetEntity>504221DA-1B57-4EAD-39DB-40FD553E9FA2</targetEntity>
+<transferable>true</transferable>
+</Relation> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/016BA1CF-6EA4-9CA4-CDF7-3AAA507EF6EF.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/016BA1CF-6EA4-9CA4-CDF7-3AAA507EF6EF.xml
new file mode 100644
index 00000000..e947c03a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/016BA1CF-6EA4-9CA4-CDF7-3AAA507EF6EF.xml
@@ -0,0 +1,40 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogicalSubView" name="Failure Tracking" id="016BA1CF-6EA4-9CA4-CDF7-3AAA507EF6EF">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-22 12:01:22 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39" otype="Entity" vid="D1B4D1DF-E3AB-F84A-F479-87FB68F0A2D2">
+<bounds x="1270" y="448" width="151" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="4D937E7C-3A28-E52D-89C0-EC8804C62367" otype="Entity" vid="37DED3CC-443D-FC8B-A30D-07BF0D742C62">
+<bounds x="1270" y="522" width="152" height="43"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="DCC79294-5434-1DED-298C-6473DEE59FBA" otype="Entity" vid="95A5D57E-9986-0942-BCE8-4B9F5F46AE30">
+<bounds x="1087" y="460" width="157" height="51"/>
+</OView>
+</objectViews>
+<connectors>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="518CE489-97B4-C05C-07A2-E3DBF14EE267" otype="Relation" vid_source="95A5D57E-9986-0942-BCE8-4B9F5F46AE30" vid_target="D1B4D1DF-E3AB-F84A-F479-87FB68F0A2D2">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1244" y="474"/>
+<point x="1270" y="474"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="BAD8EC05-6F14-4E38-366C-B4B660C6F38A" otype="Relation" vid_source="D1B4D1DF-E3AB-F84A-F479-87FB68F0A2D2" vid_target="37DED3CC-443D-FC8B-A30D-07BF0D742C62">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1345" y="489"/>
+<point x="1345" y="522"/>
+</points>
+</Connector>
+</connectors>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/32D718B4-250F-95DC-37F0-C0A817F69020.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/32D718B4-250F-95DC-37F0-C0A817F69020.xml
new file mode 100644
index 00000000..6493425b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/32D718B4-250F-95DC-37F0-C0A817F69020.xml
@@ -0,0 +1,70 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogicalSubView" name="Outputs" id="32D718B4-250F-95DC-37F0-C0A817F69020">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:19:53 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="16464F5A-64BE-D2ED-91E0-BCBD0AA34680" otype="Entity" vid="636E76B2-6F21-38E5-BF29-D4C078AC8F61">
+<bounds x="1014" y="625" width="121" height="102"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="28DD93CF-D058-7343-CD47-E9B435E1AC16" otype="Entity" vid="89BDF7A8-D79D-A869-BE57-BD2E1C2B290C">
+<bounds x="1190" y="610" width="131" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="4579B792-2F35-D72A-1A3B-C7E53C41A766" otype="Entity" vid="D72D72DA-F9C0-CE9C-E6A6-7A44DA7656DC">
+<bounds x="1190" y="710" width="131" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="90F477EE-35D6-21A7-B693-E5724FB07476" otype="Entity" vid="0A09F0EB-AF09-D080-F1B5-EC4E3693C1C5">
+<bounds x="824" y="652" width="141" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="D09E0DE5-99D6-2991-032A-A8A124F6ACBA" otype="Entity" vid="239CADB1-5F1D-1286-1C79-0DCD91157E84">
+<bounds x="1190" y="662" width="131" height="39"/>
+</OView>
+</objectViews>
+<connectors>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="02096BBB-0795-1759-1E26-2877BE36BB59" otype="Relation" vid_source="636E76B2-6F21-38E5-BF29-D4C078AC8F61" vid_target="636E76B2-6F21-38E5-BF29-D4C078AC8F61">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="676"/>
+<point x="1150" y="676"/>
+<point x="1150" y="742"/>
+<point x="1074" y="742"/>
+<point x="1074" y="727"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="11710A55-6423-1904-841A-C7D2AB8CEEBF" otype="Relation" vid_source="636E76B2-6F21-38E5-BF29-D4C078AC8F61" vid_target="239CADB1-5F1D-1286-1C79-0DCD91157E84">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="691"/>
+<point x="1190" y="691"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="34733942-1305-4CA1-47EB-ACE724B04E69" otype="Relation" vid_source="636E76B2-6F21-38E5-BF29-D4C078AC8F61" vid_target="89BDF7A8-D79D-A869-BE57-BD2E1C2B290C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="638"/>
+<point x="1190" y="638"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635" otype="Relation" vid_source="636E76B2-6F21-38E5-BF29-D4C078AC8F61" vid_target="D72D72DA-F9C0-CE9C-E6A6-7A44DA7656DC">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="718"/>
+<point x="1190" y="718"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="A182A65A-47AE-5D00-9A30-BC20AB050BF2" otype="Relation" vid_source="0A09F0EB-AF09-D080-F1B5-EC4E3693C1C5" vid_target="636E76B2-6F21-38E5-BF29-D4C078AC8F61">
+<lineWidth>1</lineWidth>
+<points>
+<point x="965" y="677"/>
+<point x="1014" y="677"/>
+</points>
+</Connector>
+</connectors>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/571DBBAF-CDDA-1C46-4220-D1319C0EEC00.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/571DBBAF-CDDA-1C46-4220-D1319C0EEC00.xml
new file mode 100644
index 00000000..25df5afc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/571DBBAF-CDDA-1C46-4220-D1319C0EEC00.xml
@@ -0,0 +1,24 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogicalSubView" name="Persistent Test Manager Data" id="571DBBAF-CDDA-1C46-4220-D1319C0EEC00">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:19:18 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="2F6ACC6D-3D17-537D-8ADF-F8424395B345" otype="Entity" vid="B4E5F358-5BC8-9B06-4A13-EDF705ED9089">
+<bounds x="110" y="570" width="151" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="A352A20F-310D-E285-FBC9-90DD0DA7BB9B" otype="Entity" vid="8747577F-8999-3CBF-1376-1DD291702774">
+<bounds x="300" y="570" width="151" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="C332E3D7-638B-6CA8-24BF-383CA8659A3A" otype="Entity" vid="F053C992-CB30-88B3-66FF-F4E522C60155">
+<bounds x="499" y="570" width="136" height="61"/>
+</OView>
+</objectViews>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/65FA5BA0-CC9C-C108-BB1B-AC9E13F5BC83.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/65FA5BA0-CC9C-C108-BB1B-AC9E13F5BC83.xml
new file mode 100644
index 00000000..c248a58e
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/65FA5BA0-CC9C-C108-BB1B-AC9E13F5BC83.xml
@@ -0,0 +1,127 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogicalSubView" name="Configuration" id="65FA5BA0-CC9C-C108-BB1B-AC9E13F5BC83">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 08:58:45 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="1BEAB532-23CA-8628-0C97-7CAD39119A4E" otype="Entity" vid="459DD9CF-0825-0BAE-7BBA-FADAA3B895BB">
+<bounds x="680" y="419" width="161" height="52"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="24150FB1-B00F-4F69-6F77-49ECB58F0F66" otype="Entity" vid="398E8687-F10E-D31E-DD4E-EA0A6A7868A3">
+<bounds x="273" y="96" width="138" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="6A886CEE-579B-48FF-63F6-0FB03393FBF6" otype="Entity" vid="E301FF23-DE18-19FB-9A6A-9F170D26B939">
+<bounds x="180" y="250" width="131" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="7AE36CC1-A030-63E5-6EF3-72FCD04815EE" otype="Entity" vid="B06DA0BE-1DA3-3AB7-06CD-E7EA9FDC0B3E">
+<bounds x="101" y="95" width="131" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="A6A5F317-479C-A0DD-CAAE-9DCB56B29D40" otype="Entity" vid="49F6288A-70A0-788D-3FEE-BE0053D8D44C">
+<bounds x="680" y="130" width="161" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="B36A186B-CDB3-7851-8C38-12EA8D50EAEB" otype="Entity" vid="9E4B525D-2B00-0B76-39EE-0C0F74693333">
+<bounds x="600" y="30" width="141" height="31"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="B82DAF9A-6F99-5CF6-4D99-A391BAD66192" otype="Entity" vid="2C49F347-32B8-CA7C-2646-4F16FDDA087E">
+<bounds x="680" y="250" width="161" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="C79482B8-771B-FAD8-0337-163E3A45003A" otype="Entity" vid="8FAC087B-6133-162A-207B-3FAFB7B41E98">
+<bounds x="908" y="250" width="153" height="31"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="DE366053-6F7A-7F42-ABA3-00E583098C37" otype="Entity" vid="61150DED-91F4-1AE3-BD02-4EDC4CC0D98F">
+<bounds x="430" y="250" width="131" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="E93BBF08-067B-A665-39F3-CF488A6547B2" otype="Entity" vid="C41DA40C-A50A-BDCC-4DA0-2DCA7874C1A2">
+<bounds x="789" y="30" width="132" height="31"/>
+</OView>
+</objectViews>
+<connectors>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="0CCF1DE3-7916-9054-BEA6-C601FF564DB2" otype="Relation" vid_source="B06DA0BE-1DA3-3AB7-06CD-E7EA9FDC0B3E" vid_target="E301FF23-DE18-19FB-9A6A-9F170D26B939">
+<lineWidth>1</lineWidth>
+<points>
+<point x="206" y="156"/>
+<point x="206" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="1C189437-742B-B999-C955-7754C8ADB089" otype="Relation" vid_source="E301FF23-DE18-19FB-9A6A-9F170D26B939" vid_target="61150DED-91F4-1AE3-BD02-4EDC4CC0D98F">
+<lineWidth>1</lineWidth>
+<points>
+<point x="311" y="285"/>
+<point x="430" y="285"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C" otype="Relation" vid_source="398E8687-F10E-D31E-DD4E-EA0A6A7868A3" vid_target="E301FF23-DE18-19FB-9A6A-9F170D26B939">
+<lineWidth>1</lineWidth>
+<points>
+<point x="292" y="157"/>
+<point x="292" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="3B7C8913-EB6A-47B1-27D0-E2C85EE9048B" otype="Relation" vid_source="49F6288A-70A0-788D-3FEE-BE0053D8D44C" vid_target="9E4B525D-2B00-0B76-39EE-0C0F74693333">
+<lineWidth>1</lineWidth>
+<points>
+<point x="710" y="130"/>
+<point x="710" y="61"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="7497D76B-781B-3BDD-D797-FFBDB974F772" otype="Relation" vid_source="2C49F347-32B8-CA7C-2646-4F16FDDA087E" vid_target="8FAC087B-6133-162A-207B-3FAFB7B41E98">
+<lineWidth>1</lineWidth>
+<points>
+<point x="841" y="265"/>
+<point x="908" y="265"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="89A83E25-364B-6B73-0613-FEAD875EF9FB" otype="Relation" vid_source="2C49F347-32B8-CA7C-2646-4F16FDDA087E" vid_target="459DD9CF-0825-0BAE-7BBA-FADAA3B895BB">
+<lineWidth>1</lineWidth>
+<points>
+<point x="760" y="321"/>
+<point x="760" y="419"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="B346381F-48FE-E495-01A7-E22EC26AEE8A" otype="Relation" vid_source="2C49F347-32B8-CA7C-2646-4F16FDDA087E" vid_target="61150DED-91F4-1AE3-BD02-4EDC4CC0D98F">
+<lineWidth>1</lineWidth>
+<points>
+<point x="680" y="285"/>
+<point x="561" y="285"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="B3596116-540F-6397-ECE4-58A386644E15" otype="Relation" vid_source="2C49F347-32B8-CA7C-2646-4F16FDDA087E" vid_target="2C49F347-32B8-CA7C-2646-4F16FDDA087E">
+<lineWidth>1</lineWidth>
+<points>
+<point x="841" y="285"/>
+<point x="856" y="285"/>
+<point x="856" y="336"/>
+<point x="760" y="336"/>
+<point x="760" y="321"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="CCD38E11-8557-EB34-2651-07EB29E83FA6" otype="Relation" vid_source="398E8687-F10E-D31E-DD4E-EA0A6A7868A3" vid_target="E301FF23-DE18-19FB-9A6A-9F170D26B939">
+<lineWidth>1</lineWidth>
+<points>
+<point x="302" y="157"/>
+<point x="302" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E4FE88E9-EE21-B43B-B0FE-A153E38246F9" otype="Relation" vid_source="2C49F347-32B8-CA7C-2646-4F16FDDA087E" vid_target="49F6288A-70A0-788D-3FEE-BE0053D8D44C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="760" y="250"/>
+<point x="760" y="171"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E74406B5-20F1-4323-DC99-6E45982CB606" otype="Relation" vid_source="49F6288A-70A0-788D-3FEE-BE0053D8D44C" vid_target="C41DA40C-A50A-BDCC-4DA0-2DCA7874C1A2">
+<lineWidth>1</lineWidth>
+<points>
+<point x="815" y="130"/>
+<point x="815" y="61"/>
+</points>
+</Connector>
+</connectors>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/AFCEF013-4CF2-4A5A-79A3-31521C1CA20A.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/AFCEF013-4CF2-4A5A-79A3-31521C1CA20A.xml
new file mode 100644
index 00000000..14a7566f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/AFCEF013-4CF2-4A5A-79A3-31521C1CA20A.xml
@@ -0,0 +1,306 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogical" name="Logical" id="AFCEF013-4CF2-4A5A-79A3-31521C1CA20A">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:02:17 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>true</showLabels>
+<showGrid>true</showGrid>
+<diagramColor>-1</diagramColor>
+<legendPosX>265</legendPosX>
+<legendPosY>490</legendPosY>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="16464F5A-64BE-D2ED-91E0-BCBD0AA34680" otype="Entity" vid="5B100733-B921-D478-15B5-3BE9A7747A87">
+<bounds x="1014" y="625" width="121" height="102"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="1BEAB532-23CA-8628-0C97-7CAD39119A4E" otype="Entity" vid="62F579AD-F97F-1F92-7C5F-525AE1A2F26C">
+<bounds x="680" y="419" width="161" height="52"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="24150FB1-B00F-4F69-6F77-49ECB58F0F66" otype="Entity" vid="B3D29C8C-8482-D7AF-BE58-122AB07FB853">
+<bounds x="273" y="96" width="138" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="28DD93CF-D058-7343-CD47-E9B435E1AC16" otype="Entity" vid="ABB72A58-23E7-DF85-4B01-74F467F60284">
+<bounds x="1190" y="610" width="131" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="2F6ACC6D-3D17-537D-8ADF-F8424395B345" otype="Entity" vid="40AB3AA2-7D9F-7BA7-AB96-050F27CF81AB">
+<bounds x="110" y="570" width="151" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="44FFF5E9-0C2F-7BAC-B5B7-73CA3A230B39" otype="Entity" vid="BE78445F-B005-8F1A-E390-120DCC587063">
+<bounds x="1270" y="448" width="151" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="4579B792-2F35-D72A-1A3B-C7E53C41A766" otype="Entity" vid="BA629852-B837-F348-59DD-12899B260C79">
+<bounds x="1190" y="710" width="131" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="4D937E7C-3A28-E52D-89C0-EC8804C62367" otype="Entity" vid="109E2A3F-B942-1D32-CB1C-4F60260ACF5C">
+<bounds x="1270" y="522" width="152" height="43"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="504221DA-1B57-4EAD-39DB-40FD553E9FA2" otype="Entity" vid="F4CED71A-65B7-151C-3ADC-26F25043F168">
+<bounds x="1092" y="301" width="151" height="70"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="6A886CEE-579B-48FF-63F6-0FB03393FBF6" otype="Entity" vid="81A8E233-0690-CBFE-6102-F71A991903FC">
+<bounds x="180" y="250" width="131" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="7AE36CC1-A030-63E5-6EF3-72FCD04815EE" otype="Entity" vid="C8DAF849-7026-3615-7FC8-4397BFC6CA14">
+<bounds x="101" y="95" width="131" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.TVNote" oid="876CB767-80BA-6C8E-AACA-F1CCC95C445E" otype="Note" vid="593FF096-DB74-2562-91B0-A4F1423FEBA7">
+<bounds x="292" y="336" width="149" height="61"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="90367AFB-BA2D-A918-46B9-1E5DE53ACC48" otype="Entity" vid="5A1E3970-E7C2-5B4A-B4FC-A4224370E349">
+<bounds x="1270" y="300" width="145" height="72"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="90F477EE-35D6-21A7-B693-E5724FB07476" otype="Entity" vid="B6946DC3-6424-2A37-D668-5BD36839859C">
+<bounds x="824" y="652" width="141" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="9F78B73C-056D-DDEF-8C50-A9DA76B9E724" otype="Entity" vid="EEE8DCBD-05DB-E390-AE27-14DFF3B0DD56">
+<bounds x="1091" y="205" width="151" height="63"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="A352A20F-310D-E285-FBC9-90DD0DA7BB9B" otype="Entity" vid="27BF1041-8402-6396-1A77-2223122117A1">
+<bounds x="292" y="570" width="148" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="A6A5F317-479C-A0DD-CAAE-9DCB56B29D40" otype="Entity" vid="AB9AED98-F420-DDD6-02BA-ABA20D05AFB3">
+<bounds x="680" y="130" width="161" height="41"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="B36A186B-CDB3-7851-8C38-12EA8D50EAEB" otype="Entity" vid="8B654282-58D6-084A-69E2-3C8D7E390802">
+<bounds x="600" y="30" width="141" height="31"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="B82DAF9A-6F99-5CF6-4D99-A391BAD66192" otype="Entity" vid="2F2EDF15-4992-FE58-E928-D09AF0373D9E">
+<bounds x="680" y="250" width="161" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="C332E3D7-638B-6CA8-24BF-383CA8659A3A" otype="Entity" vid="03B42717-C78B-007E-11B3-EEA11AABA415">
+<bounds x="472" y="570" width="136" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="C79482B8-771B-FAD8-0337-163E3A45003A" otype="Entity" vid="8D1A1E0A-0651-0364-F81D-EC5D599DF29A">
+<bounds x="909" y="251" width="132" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="D09E0DE5-99D6-2991-032A-A8A124F6ACBA" otype="Entity" vid="2446BDB4-EEEF-A6B8-6F46-4C1208EDECC2">
+<bounds x="1190" y="662" width="131" height="39"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.TVNote" oid="D487AFDC-4027-F824-EA29-5C6D0ABB9E1E" otype="Note" vid="583B257A-5AD8-026F-84FF-AB3956387595">
+<bounds x="322" y="179" width="89" height="40"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="DCC79294-5434-1DED-298C-6473DEE59FBA" otype="Entity" vid="8689850E-1426-9DCF-EF62-4753AFEE7BE6">
+<bounds x="1087" y="460" width="157" height="51"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="DE366053-6F7A-7F42-ABA3-00E583098C37" otype="Entity" vid="CAF127DE-45F6-6BCE-8FAB-7BAE679347E1">
+<bounds x="430" y="250" width="131" height="71"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="E93BBF08-067B-A665-39F3-CF488A6547B2" otype="Entity" vid="2862D2B6-5340-9024-1DF2-E4408EA96B6E">
+<bounds x="789" y="30" width="132" height="31"/>
+</OView>
+</objectViews>
+<connectors>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="01537211-CCFB-0A1E-B43B-E8C641B69471" otype="Relation" vid_source="B6946DC3-6424-2A37-D668-5BD36839859C" vid_target="62F579AD-F97F-1F92-7C5F-525AE1A2F26C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="832" y="652"/>
+<point x="832" y="471"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="02096BBB-0795-1759-1E26-2877BE36BB59" otype="Relation" vid_source="5B100733-B921-D478-15B5-3BE9A7747A87" vid_target="5B100733-B921-D478-15B5-3BE9A7747A87">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="676"/>
+<point x="1150" y="676"/>
+<point x="1150" y="742"/>
+<point x="1074" y="742"/>
+<point x="1074" y="727"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="0CCF1DE3-7916-9054-BEA6-C601FF564DB2" otype="Relation" vid_source="C8DAF849-7026-3615-7FC8-4397BFC6CA14" vid_target="81A8E233-0690-CBFE-6102-F71A991903FC">
+<lineWidth>1</lineWidth>
+<points>
+<point x="206" y="156"/>
+<point x="206" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="10867E70-94CE-FDAF-6B6E-2742D3A49E57" otype="Relation" vid_source="5A1E3970-E7C2-5B4A-B4FC-A4224370E349" vid_target="BE78445F-B005-8F1A-E390-120DCC587063">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1342" y="372"/>
+<point x="1342" y="448"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="11710A55-6423-1904-841A-C7D2AB8CEEBF" otype="Relation" vid_source="5B100733-B921-D478-15B5-3BE9A7747A87" vid_target="2446BDB4-EEEF-A6B8-6F46-4C1208EDECC2">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="690"/>
+<point x="1190" y="690"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="1C189437-742B-B999-C955-7754C8ADB089" otype="Relation" vid_source="81A8E233-0690-CBFE-6102-F71A991903FC" vid_target="CAF127DE-45F6-6BCE-8FAB-7BAE679347E1">
+<lineWidth>1</lineWidth>
+<points>
+<point x="311" y="285"/>
+<point x="430" y="285"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="34733942-1305-4CA1-47EB-ACE724B04E69" otype="Relation" vid_source="5B100733-B921-D478-15B5-3BE9A7747A87" vid_target="ABB72A58-23E7-DF85-4B01-74F467F60284">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="638"/>
+<point x="1190" y="638"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="3563C940-E524-7F96-7AE0-DAC3C1C17AFC" otype="Relation" vid_source="F4CED71A-65B7-151C-3ADC-26F25043F168" vid_target="B6946DC3-6424-2A37-D668-5BD36839859C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1167" y="371"/>
+<point x="894" y="652"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="3983F50A-EBB9-E4DE-1958-60EA4EDD6D6C" otype="Relation" vid_source="B3D29C8C-8482-D7AF-BE58-122AB07FB853" vid_target="81A8E233-0690-CBFE-6102-F71A991903FC">
+<lineWidth>1</lineWidth>
+<points>
+<point x="300" y="157"/>
+<point x="300" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="3B7C8913-EB6A-47B1-27D0-E2C85EE9048B" otype="Relation" vid_source="AB9AED98-F420-DDD6-02BA-ABA20D05AFB3" vid_target="8B654282-58D6-084A-69E2-3C8D7E390802">
+<lineWidth>1</lineWidth>
+<points>
+<point x="710" y="130"/>
+<point x="710" y="61"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="518CE489-97B4-C05C-07A2-E3DBF14EE267" otype="Relation" vid_source="8689850E-1426-9DCF-EF62-4753AFEE7BE6" vid_target="BE78445F-B005-8F1A-E390-120DCC587063">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1244" y="474"/>
+<point x="1270" y="474"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="68A0C3E1-0FA1-8414-A361-33B08A8EDB39" otype="Relation" vid_source="8689850E-1426-9DCF-EF62-4753AFEE7BE6" vid_target="5B100733-B921-D478-15B5-3BE9A7747A87">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1111" y="511"/>
+<point x="1111" y="625"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="7497D76B-781B-3BDD-D797-FFBDB974F772" otype="Relation" vid_source="2F2EDF15-4992-FE58-E928-D09AF0373D9E" vid_target="8D1A1E0A-0651-0364-F81D-EC5D599DF29A">
+<lineWidth>1</lineWidth>
+<points>
+<point x="841" y="266"/>
+<point x="909" y="266"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="7DA9DD83-A52E-CA1E-FCBF-FC9CE71AF635" otype="Relation" vid_source="5B100733-B921-D478-15B5-3BE9A7747A87" vid_target="BA629852-B837-F348-59DD-12899B260C79">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1135" y="718"/>
+<point x="1190" y="718"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="89A83E25-364B-6B73-0613-FEAD875EF9FB" otype="Relation" vid_source="2F2EDF15-4992-FE58-E928-D09AF0373D9E" vid_target="62F579AD-F97F-1F92-7C5F-525AE1A2F26C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="750" y="321"/>
+<point x="750" y="419"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="8E5018CC-34E3-9AFC-D6D1-31E2BC4E9FE2" otype="Relation" vid_source="03B42717-C78B-007E-11B3-EEA11AABA415" vid_target="62F579AD-F97F-1F92-7C5F-525AE1A2F26C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="540" y="570"/>
+<point x="760" y="471"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="9B1FE0CF-B2AD-EED0-22FC-461A7D46DE51" otype="Relation" vid_source="40AB3AA2-7D9F-7BA7-AB96-050F27CF81AB" vid_target="8D1A1E0A-0651-0364-F81D-EC5D599DF29A">
+<lineWidth>1</lineWidth>
+<points>
+<point x="185" y="570"/>
+<point x="985" y="302"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="A182A65A-47AE-5D00-9A30-BC20AB050BF2" otype="Relation" vid_source="B6946DC3-6424-2A37-D668-5BD36839859C" vid_target="5B100733-B921-D478-15B5-3BE9A7747A87">
+<lineWidth>1</lineWidth>
+<points>
+<point x="965" y="677"/>
+<point x="1014" y="677"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="B346381F-48FE-E495-01A7-E22EC26AEE8A" otype="Relation" vid_source="2F2EDF15-4992-FE58-E928-D09AF0373D9E" vid_target="CAF127DE-45F6-6BCE-8FAB-7BAE679347E1">
+<lineWidth>1</lineWidth>
+<points>
+<point x="680" y="285"/>
+<point x="561" y="285"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="B3596116-540F-6397-ECE4-58A386644E15" otype="Relation" vid_source="2F2EDF15-4992-FE58-E928-D09AF0373D9E" vid_target="2F2EDF15-4992-FE58-E928-D09AF0373D9E">
+<lineWidth>1</lineWidth>
+<points>
+<point x="841" y="285"/>
+<point x="856" y="285"/>
+<point x="856" y="336"/>
+<point x="760" y="336"/>
+<point x="760" y="321"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="BAD8EC05-6F14-4E38-366C-B4B660C6F38A" otype="Relation" vid_source="BE78445F-B005-8F1A-E390-120DCC587063" vid_target="109E2A3F-B942-1D32-CB1C-4F60260ACF5C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1345" y="489"/>
+<point x="1345" y="522"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="C5B67DD4-FA4F-EF9F-1FF5-0445D51B32EE" otype="Relation" vid_source="B6946DC3-6424-2A37-D668-5BD36839859C" vid_target="C8DAF849-7026-3615-7FC8-4397BFC6CA14">
+<lineWidth>1</lineWidth>
+<points>
+<point x="894" y="652"/>
+<point x="166" y="156"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="CCD38E11-8557-EB34-2651-07EB29E83FA6" otype="Relation" vid_source="B3D29C8C-8482-D7AF-BE58-122AB07FB853" vid_target="81A8E233-0690-CBFE-6102-F71A991903FC">
+<lineWidth>1</lineWidth>
+<points>
+<point x="280" y="157"/>
+<point x="280" y="250"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E2A47942-ED55-E81D-4C71-9A134C49C147" otype="Relation" vid_source="C8DAF849-7026-3615-7FC8-4397BFC6CA14" vid_target="27BF1041-8402-6396-1A77-2223122117A1">
+<lineWidth>1</lineWidth>
+<points>
+<point x="166" y="156"/>
+<point x="330" y="570"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E4FE88E9-EE21-B43B-B0FE-A153E38246F9" otype="Relation" vid_source="2F2EDF15-4992-FE58-E928-D09AF0373D9E" vid_target="AB9AED98-F420-DDD6-02BA-ABA20D05AFB3">
+<lineWidth>1</lineWidth>
+<points>
+<point x="760" y="250"/>
+<point x="760" y="171"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E62AE7DF-49EE-9280-B328-A867CBD273AE" otype="Relation" vid_source="27BF1041-8402-6396-1A77-2223122117A1" vid_target="B6946DC3-6424-2A37-D668-5BD36839859C">
+<lineWidth>1</lineWidth>
+<points>
+<point x="360" y="621"/>
+<point x="824" y="677"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="E74406B5-20F1-4323-DC99-6E45982CB606" otype="Relation" vid_source="AB9AED98-F420-DDD6-02BA-ABA20D05AFB3" vid_target="2862D2B6-5340-9024-1DF2-E4408EA96B6E">
+<lineWidth>1</lineWidth>
+<points>
+<point x="815" y="130"/>
+<point x="815" y="61"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="EC4EB506-3DBE-7F36-6451-F31920EDAB52" otype="Relation" vid_source="C8DAF849-7026-3615-7FC8-4397BFC6CA14" vid_target="40AB3AA2-7D9F-7BA7-AB96-050F27CF81AB">
+<lineWidth>1</lineWidth>
+<points>
+<point x="130" y="156"/>
+<point x="130" y="570"/>
+</points>
+</Connector>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880" otype="Relation" vid_source="EEE8DCBD-05DB-E390-AE27-14DFF3B0DD56" vid_target="F4CED71A-65B7-151C-3ADC-26F25043F168">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1166" y="243"/>
+<point x="1167" y="301"/>
+</points>
+</Connector>
+</connectors>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/F936BE6D-7A74-1B57-7564-41C1E13B973B.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/F936BE6D-7A74-1B57-7564-41C1E13B973B.xml
new file mode 100644
index 00000000..bcc0009f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/logical/subviews/F936BE6D-7A74-1B57-7564-41C1E13B973B.xml
@@ -0,0 +1,33 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.logical.DPVLogicalSubView" name="Inputs" id="F936BE6D-7A74-1B57-7564-41C1E13B973B">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-21 09:08:50 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+<objectViews>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="504221DA-1B57-4EAD-39DB-40FD553E9FA2" otype="Entity" vid="EA3885E3-FEE4-031B-1751-1C6351610836">
+<bounds x="1091" y="476" width="151" height="70"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="90367AFB-BA2D-A918-46B9-1E5DE53ACC48" otype="Entity" vid="86784B28-925D-6EAF-24D8-27DE22A0A93B">
+<bounds x="1090" y="376" width="151" height="68"/>
+</OView>
+<OView class="oracle.dbtools.crest.swingui.logical.TVEntity" oid="9F78B73C-056D-DDEF-8C50-A9DA76B9E724" otype="Entity" vid="1B62E962-0DFC-D5AE-0AC4-33E14F65E825">
+<bounds x="1297" y="477" width="151" height="71"/>
+</OView>
+</objectViews>
+<connectors>
+<Connector class="oracle.dbtools.crest.swingui.logical.TVRelation" oid="EE1D98EF-6AEA-2790-D9B9-DBC2ED21D880" otype="Relation" vid_source="1B62E962-0DFC-D5AE-0AC4-33E14F65E825" vid_target="EA3885E3-FEE4-031B-1751-1C6351610836">
+<lineWidth>1</lineWidth>
+<points>
+<point x="1297" y="511"/>
+<point x="1242" y="511"/>
+</points>
+</Connector>
+</connectors>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap.xml
new file mode 100644
index 00000000..6811f63f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap.xml
@@ -0,0 +1,3 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<ExtendedMap class="oracle.dbtools.crest.model.xtdmapping.ExtendedMap">
+</ExtendedMap> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap_RMB082B14A-BEA8-D8A7-D661-197F34766ED3.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap_RMB082B14A-BEA8-D8A7-D661-197F34766ED3.xml
new file mode 100644
index 00000000..7ea5df08
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/mapping/ExtendedMap_RMB082B14A-BEA8-D8A7-D661-197F34766ED3.xml
@@ -0,0 +1,3 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<RMExtendedMap class="oracle.dbtools.crest.model.xtdmapping.RMExtendedMap">
+</RMExtendedMap> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rdbms/TestManagerDatabase_RDBMSSites.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rdbms/TestManagerDatabase_RDBMSSites.xml
new file mode 100644
index 00000000..e0c5dad0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rdbms/TestManagerDatabase_RDBMSSites.xml
@@ -0,0 +1,2 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<metadatadoc version="2.0"/> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3.xml
new file mode 100644
index 00000000..76bdad85
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3.xml
@@ -0,0 +1,8 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<relationalModel class="oracle.dbtools.crest.model.design.relational.RelationalDesign" name="Relational_1" id="B082B14A-BEA8-D8A7-D661-197F34766ED3" mainViewID="6CEC5843-B4DD-D9B0-54D4-2845569D5E9F">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 21:58:45 UTC</createdTime>
+<ownerDesignName>TestManagerDatabase</ownerDesignName>
+<shouldBeOpen>false</shouldBeOpen>
+<selectedRDBMSSite>32076570-2523-435C-2E92-BF29817DFF70</selectedRDBMSSite>
+</relationalModel> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3/subviews/6CEC5843-B4DD-D9B0-54D4-2845569D5E9F.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3/subviews/6CEC5843-B4DD-D9B0-54D4-2845569D5E9F.xml
new file mode 100644
index 00000000..44b040be
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/rel/B082B14A-197F34766ED3/subviews/6CEC5843-B4DD-D9B0-54D4-2845569D5E9F.xml
@@ -0,0 +1,13 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<Diagram class="oracle.dbtools.crest.swingui.relational.DPVRelational" name="Relational_1" id="6CEC5843-B4DD-D9B0-54D4-2845569D5E9F">
+<createdBy>bird</createdBy>
+<createdTime>2012-08-20 22:02:17 UTC</createdTime>
+<autoRoute>false</autoRoute>
+<boxInbox>true</boxInbox>
+<showLegend>false</showLegend>
+<showLabels>false</showLabels>
+<showGrid>false</showGrid>
+<diagramColor>-1</diagramColor>
+<display>false</display>
+<notation>0</notation>
+</Diagram> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/types.xml b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/types.xml
new file mode 100644
index 00000000..64fa7ab8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabase/types.xml
@@ -0,0 +1,933 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<logtypes>
+ <logicaltype name="Audio" objectid="LOGDT005">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">BINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">BINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="BFile" objectid="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10034">
+ <mapping rdbms="Oracle Database 11g">BFILE</mapping>
+ <mapping rdbms="Oracle Database 10g">BFILE</mapping>
+ <mapping rdbms="Oracle9i">BFILE</mapping>
+ <mapping rdbms="SQL Server 2005">VARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DATALINK</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DATALINK</mapping>
+ </logicaltype>
+ <logicaltype name="BIGINT" objectid="LOGDT027">
+ <mapping rdbms="Oracle Database 11g">INTEGER</mapping>
+ <mapping rdbms="Oracle Database 10g">INTEGER</mapping>
+ <mapping rdbms="Oracle9i">INTEGER</mapping>
+ <mapping rdbms="SQL Server 2005">BIGINT</mapping>
+ <mapping rdbms="SQL Server 2000">BIGINT</mapping>
+ <mapping rdbms="DB2/390 8">INTEGER</mapping>
+ <mapping rdbms="DB2/390 7">INTEGER</mapping>
+ <mapping rdbms="DB2/UDB 8.1">INTEGER</mapping>
+ <mapping rdbms="DB2/UDB 7.1">INTEGER</mapping>
+ </logicaltype>
+ <logicaltype name="BINARY" objectid="LOGDT033">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">BINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">BINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="BINARY DOUBLE" objectid="LOGDT056">
+ <mapping rdbms="Oracle Database 11g">BINARY_DOUBLE</mapping>
+ <mapping rdbms="Oracle Database 10g">BINARY_DOUBLE</mapping>
+ <mapping rdbms="Oracle9i">NUMBER</mapping>
+ <mapping rdbms="SQL Server 2005">FLOAT</mapping>
+ <mapping rdbms="SQL Server 2000">FLOAT</mapping>
+ <mapping rdbms="DB2/390 8">DOUBLE</mapping>
+ <mapping rdbms="DB2/390 7">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DOUBLE</mapping>
+ </logicaltype>
+ <logicaltype name="BINARY FLOAT" objectid="LOGDT055">
+ <mapping rdbms="Oracle Database 11g">BINARY_FLOAT</mapping>
+ <mapping rdbms="Oracle Database 10g">BINARY_FLOAT</mapping>
+ <mapping rdbms="Oracle9i">NUMBER</mapping>
+ <mapping rdbms="SQL Server 2005">REAL</mapping>
+ <mapping rdbms="SQL Server 2000">REAL</mapping>
+ <mapping rdbms="DB2/390 8">REAL</mapping>
+ <mapping rdbms="DB2/390 7">REAL</mapping>
+ <mapping rdbms="DB2/UDB 8.1">REAL</mapping>
+ <mapping rdbms="DB2/UDB 7.1">REAL</mapping>
+ </logicaltype>
+ <logicaltype name="BIT" objectid="LOGDT034">
+ <mapping rdbms="Oracle Database 11g">CHAR</mapping>
+ <mapping rdbms="Oracle Database 10g">CHAR</mapping>
+ <mapping rdbms="Oracle9i">CHAR</mapping>
+ <mapping rdbms="SQL Server 2005">BIT</mapping>
+ <mapping rdbms="SQL Server 2000">BIT</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="BLOB" objectid="LOGDT029">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">IMAGE</mapping>
+ <mapping rdbms="SQL Server 2000">IMAGE</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="Boolean" objectid="LOGDT006">
+ <mapping rdbms="Oracle Database 11g">CHAR</mapping>
+ <mapping rdbms="Oracle Database 10g">CHAR</mapping>
+ <mapping rdbms="Oracle9i">CHAR</mapping>
+ <mapping rdbms="SQL Server 2005">BIT</mapping>
+ <mapping rdbms="SQL Server 2000">BIT</mapping>
+ <mapping rdbms="DB2/390 8">CHAR</mapping>
+ <mapping rdbms="DB2/390 7">CHAR</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR</mapping>
+ </logicaltype>
+ <logicaltype name="CHAR" objectid="LOGDT025">
+ <mapping rdbms="Oracle Database 11g">CHAR, size</mapping>
+ <mapping rdbms="Oracle Database 10g">CHAR, size</mapping>
+ <mapping rdbms="Oracle9i">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="CLOB" objectid="LOGDT028">
+ <mapping rdbms="Oracle Database 11g">CLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">CLOB</mapping>
+ <mapping rdbms="Oracle9i">CLOB</mapping>
+ <mapping rdbms="SQL Server 2005" size_default_value="max">VARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">TEXT</mapping>
+ <mapping rdbms="DB2/390 8">CLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="DATALINK" objectid="LOGDT030">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">BINARY</mapping>
+ <mapping rdbms="SQL Server 2000">BINARY</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DATALINK</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DATALINK</mapping>
+ </logicaltype>
+ <logicaltype name="DBURIType" objectid="LOGDT054">
+ <mapping rdbms="Oracle Database 11g">DBURITYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">DBURITYPE</mapping>
+ <mapping rdbms="Oracle9i">DBURITYPE</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DATALINK</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DATALINK</mapping>
+ </logicaltype>
+ <logicaltype name="DECIMAL" objectid="LOGDT026">
+ <mapping rdbms="Oracle Database 11g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle Database 10g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle9i">NUMBER, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2005">DECIMAL, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2000">DECIMAL, precision, scale</mapping>
+ <mapping rdbms="DB2/390 8">DECIMAL, precision, scale</mapping>
+ <mapping rdbms="DB2/390 7">DECIMAL, precision, scale</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DECIMAL, precision, scale</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DECIMAL, precision, scale</mapping>
+ </logicaltype>
+ <logicaltype name="DOUBLE" objectid="LOGDT020">
+ <mapping rdbms="Oracle Database 11g">NUMBER</mapping>
+ <mapping rdbms="Oracle Database 10g">NUMBER</mapping>
+ <mapping rdbms="Oracle9i">NUMBER</mapping>
+ <mapping rdbms="SQL Server 2005">BIGINT</mapping>
+ <mapping rdbms="SQL Server 2000">BIGINT</mapping>
+ <mapping rdbms="DB2/390 8">DOUBLE</mapping>
+ <mapping rdbms="DB2/390 7">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DOUBLE</mapping>
+ </logicaltype>
+ <logicaltype name="Date" objectid="LOGDT007">
+ <mapping rdbms="Oracle Database 11g">DATE</mapping>
+ <mapping rdbms="Oracle Database 10g">DATE</mapping>
+ <mapping rdbms="Oracle9i">DATE</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">DATE</mapping>
+ <mapping rdbms="DB2/390 7">DATE</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DATE</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DATE</mapping>
+ </logicaltype>
+ <logicaltype name="Datetime" objectid="LOGDT008">
+ <mapping rdbms="Oracle Database 11g">DATE</mapping>
+ <mapping rdbms="Oracle Database 10g">DATE</mapping>
+ <mapping rdbms="Oracle9i">DATE</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/390 7">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIMESTAMP</mapping>
+ </logicaltype>
+ <logicaltype name="FLOAT" objectid="LOGDT021">
+ <mapping rdbms="Oracle Database 11g">FLOAT, precision</mapping>
+ <mapping rdbms="Oracle Database 10g">FLOAT, precision</mapping>
+ <mapping rdbms="Oracle9i">FLOAT, precision</mapping>
+ <mapping rdbms="SQL Server 2005">FLOAT, precision</mapping>
+ <mapping rdbms="SQL Server 2000">FLOAT, precision</mapping>
+ <mapping rdbms="DB2/390 8">FLOAT, precision</mapping>
+ <mapping rdbms="DB2/390 7">FLOAT, precision</mapping>
+ <mapping rdbms="DB2/UDB 8.1">FLOAT, precision</mapping>
+ <mapping rdbms="DB2/UDB 7.1">FLOAT, precision</mapping>
+ </logicaltype>
+ <logicaltype name="GRAPHIC" objectid="LOGDT031">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">BINARY</mapping>
+ <mapping rdbms="SQL Server 2000">BINARY</mapping>
+ <mapping rdbms="DB2/390 8">GRAPHIC, size</mapping>
+ <mapping rdbms="DB2/390 7">GRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">GRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">GRAPHIC, size</mapping>
+ </logicaltype>
+ <logicaltype name="HTTPURIType" objectid="LOGDT052">
+ <mapping rdbms="Oracle Database 11g">HTTPURITYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">HTTPURITYPE</mapping>
+ <mapping rdbms="Oracle9i">HTTPURITYPE</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="INTERVAL DAY TO SECOND" objectid="LOGDT049">
+ <mapping rdbms="Oracle Database 11g">INTERVAL DAY TO SECOND, precision, scale</mapping>
+ <mapping rdbms="Oracle Database 10g">INTERVAL DAY TO SECOND, precision, scale</mapping>
+ <mapping rdbms="Oracle9i">INTERVAL DAY TO SECOND, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="INTERVAL YEAR TO MONTH" objectid="LOGDT048">
+ <mapping rdbms="Oracle Database 11g">INTERVAL YEAR TO MONTH, precision</mapping>
+ <mapping rdbms="Oracle Database 10g">INTERVAL YEAR TO MONTH, precision</mapping>
+ <mapping rdbms="Oracle9i">INTERVAL YEAR TO MONTH, precision</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="Image" objectid="LOGDT010">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">IMAGE</mapping>
+ <mapping rdbms="SQL Server 2000">IMAGE</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="Integer" objectid="LOGDT011">
+ <mapping rdbms="Oracle Database 11g">INTEGER</mapping>
+ <mapping rdbms="Oracle Database 10g">INTEGER</mapping>
+ <mapping rdbms="Oracle9i">INTEGER</mapping>
+ <mapping rdbms="SQL Server 2005">INTEGER</mapping>
+ <mapping rdbms="SQL Server 2000">INTEGER</mapping>
+ <mapping rdbms="DB2/390 8">INTEGER</mapping>
+ <mapping rdbms="DB2/390 7">INTEGER</mapping>
+ <mapping rdbms="DB2/UDB 8.1">INTEGER</mapping>
+ <mapping rdbms="DB2/UDB 7.1">INTEGER</mapping>
+ </logicaltype>
+ <logicaltype name="Long Char" objectid="LogDes-1768A872-F385-FDBA-D95E-0CB63F5908E2@LOGDT10045">
+ <mapping rdbms="Oracle Database 11g">LONG</mapping>
+ <mapping rdbms="Oracle Database 10g">LONG</mapping>
+ <mapping rdbms="Oracle9i">LONG</mapping>
+ <mapping rdbms="SQL Server 2005">VARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="Long_Raw" objectid="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036">
+ <mapping rdbms="Oracle Database 11g">LONG RAW</mapping>
+ <mapping rdbms="Oracle Database 10g">LONG RAW</mapping>
+ <mapping rdbms="Oracle9i">LONG RAW</mapping>
+ <mapping rdbms="SQL Server 2005">VARBINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARBINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="MONEY" objectid="LOGDT043">
+ <mapping rdbms="Oracle Database 11g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle Database 10g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle9i">NUMBER, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2005">MONEY</mapping>
+ <mapping rdbms="SQL Server 2000">MONEY</mapping>
+ <mapping rdbms="DB2/390 8">DOUBLE</mapping>
+ <mapping rdbms="DB2/390 7">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 8.1">DOUBLE</mapping>
+ <mapping rdbms="DB2/UDB 7.1">DOUBLE</mapping>
+ </logicaltype>
+ <logicaltype name="NCHAR" objectid="LOGDT035">
+ <mapping rdbms="Oracle Database 11g">NCHAR, size</mapping>
+ <mapping rdbms="Oracle Database 10g">NCHAR, size</mapping>
+ <mapping rdbms="Oracle9i">NCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2005">NCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">NCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="NClob" objectid="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10035">
+ <mapping rdbms="Oracle Database 11g">NCLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">NCLOB</mapping>
+ <mapping rdbms="Oracle9i">NCLOB</mapping>
+ <mapping rdbms="SQL Server 2005">NTEXT</mapping>
+ <mapping rdbms="SQL Server 2000">NTEXT</mapping>
+ <mapping rdbms="DB2/390 8">CLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="NTEXT" objectid="LOGDT036">
+ <mapping rdbms="Oracle Database 11g">NCLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">NCLOB</mapping>
+ <mapping rdbms="Oracle9i">NCLOB</mapping>
+ <mapping rdbms="SQL Server 2005">NTEXT</mapping>
+ <mapping rdbms="SQL Server 2000">NTEXT</mapping>
+ <mapping rdbms="DB2/390 8">CLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="NUMERIC" objectid="LOGDT019">
+ <mapping rdbms="Oracle Database 11g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle Database 10g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle9i">NUMBER, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2005">NUMERIC, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2000">NUMERIC, precision, scale</mapping>
+ <mapping rdbms="DB2/390 8">NUMERIC, precision, scale</mapping>
+ <mapping rdbms="DB2/390 7">NUMERIC, precision, scale</mapping>
+ <mapping rdbms="DB2/UDB 8.1">NUMERIC, precision, scale</mapping>
+ <mapping rdbms="DB2/UDB 7.1">NUMERIC, precision, scale</mapping>
+ </logicaltype>
+ <logicaltype name="NVARCHAR" objectid="LOGDT037">
+ <mapping rdbms="Oracle Database 11g">NVARCHAR2, size</mapping>
+ <mapping rdbms="Oracle Database 10g">NVARCHAR2, size</mapping>
+ <mapping rdbms="Oracle9i">NVARCHAR2, size</mapping>
+ <mapping rdbms="SQL Server 2005">NVARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">NVARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARCHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="ORDAUDIO" objectid="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10005">
+ <mapping rdbms="Oracle Database 11g">ORDSYS.ORDAudio</mapping>
+ <mapping rdbms="Oracle Database 10g">ORDSYS.ORDAudio</mapping>
+ <mapping rdbms="Oracle9i">ORDSYS.ORDAudio</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="ORDDOC" objectid="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10009">
+ <mapping rdbms="Oracle Database 11g">ORDSYS.ORDDoc</mapping>
+ <mapping rdbms="Oracle Database 10g">ORDSYS.ORDDoc</mapping>
+ <mapping rdbms="Oracle9i">ORDSYS.ORDDoc</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="ORDIMAGE" objectid="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10006">
+ <mapping rdbms="Oracle Database 11g">ORDSYS.ORDImage</mapping>
+ <mapping rdbms="Oracle Database 10g">ORDSYS.ORDImage</mapping>
+ <mapping rdbms="Oracle9i">ORDSYS.ORDImage</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="ORDIMAGE_SIGNATURE" objectid="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10007">
+ <mapping rdbms="Oracle Database 11g">ORDSYS.ORDImageSignature</mapping>
+ <mapping rdbms="Oracle Database 10g">ORDSYS.ORDImageSignature</mapping>
+ <mapping rdbms="Oracle9i">ORDSYS.ORDImageSignature</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="ORDVIDEO" objectid="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10008">
+ <mapping rdbms="Oracle Database 11g">ORDSYS.ORDVideo</mapping>
+ <mapping rdbms="Oracle Database 10g">ORDSYS.ORDVideo</mapping>
+ <mapping rdbms="Oracle9i">ORDSYS.ORDVideo</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="REAL" objectid="LOGDT022">
+ <mapping rdbms="Oracle Database 11g">REAL</mapping>
+ <mapping rdbms="Oracle Database 10g">REAL</mapping>
+ <mapping rdbms="Oracle9i">REAL</mapping>
+ <mapping rdbms="SQL Server 2005">REAL, precision</mapping>
+ <mapping rdbms="SQL Server 2000">REAL, precision</mapping>
+ <mapping rdbms="DB2/390 8">REAL</mapping>
+ <mapping rdbms="DB2/390 7">REAL</mapping>
+ <mapping rdbms="DB2/UDB 8.1">REAL</mapping>
+ <mapping rdbms="DB2/UDB 7.1">REAL</mapping>
+ </logicaltype>
+ <logicaltype name="ROWID" objectid="LOGDT032">
+ <mapping rdbms="Oracle Database 11g">ROWID</mapping>
+ <mapping rdbms="Oracle Database 10g">ROWID</mapping>
+ <mapping rdbms="Oracle9i">ROWID</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">ROWID</mapping>
+ <mapping rdbms="DB2/390 7">ROWID</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="Raw" objectid="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10040">
+ <mapping rdbms="Oracle Database 11g">RAW, size</mapping>
+ <mapping rdbms="Oracle Database 10g">RAW, size</mapping>
+ <mapping rdbms="Oracle9i">RAW, size</mapping>
+ <mapping rdbms="SQL Server 2005">VARBINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARBINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/390 7">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARGRAPHIC, size</mapping>
+ </logicaltype>
+ <logicaltype name="SMALLDATETIME" objectid="LOGDT038">
+ <mapping rdbms="Oracle Database 11g">DATE</mapping>
+ <mapping rdbms="Oracle Database 10g">DATE</mapping>
+ <mapping rdbms="Oracle9i">DATE</mapping>
+ <mapping rdbms="SQL Server 2005">SMALLDATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">SMALLDATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/390 7">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIMESTAMP</mapping>
+ </logicaltype>
+ <logicaltype name="SMALLINT" objectid="LOGDT018">
+ <mapping rdbms="Oracle Database 11g">SMALLINT</mapping>
+ <mapping rdbms="Oracle Database 10g">SMALLINT</mapping>
+ <mapping rdbms="Oracle9i">SMALLINT</mapping>
+ <mapping rdbms="SQL Server 2005">SMALLINT</mapping>
+ <mapping rdbms="SQL Server 2000">SMALLINT</mapping>
+ <mapping rdbms="DB2/390 8">SMALLINT</mapping>
+ <mapping rdbms="DB2/390 7">SMALLINT</mapping>
+ <mapping rdbms="DB2/UDB 8.1">SMALLINT</mapping>
+ <mapping rdbms="DB2/UDB 7.1">SMALLINT</mapping>
+ </logicaltype>
+ <logicaltype name="SMALLMONEY" objectid="LOGDT044">
+ <mapping rdbms="Oracle Database 11g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle Database 10g">NUMBER, precision, scale</mapping>
+ <mapping rdbms="Oracle9i">NUMBER, precision, scale</mapping>
+ <mapping rdbms="SQL Server 2005">SMALLMONEY</mapping>
+ <mapping rdbms="SQL Server 2000">SMALLMONEY</mapping>
+ <mapping rdbms="DB2/390 8">REAL</mapping>
+ <mapping rdbms="DB2/390 7">REAL</mapping>
+ <mapping rdbms="DB2/UDB 8.1">REAL</mapping>
+ <mapping rdbms="DB2/UDB 7.1">REAL</mapping>
+ </logicaltype>
+ <logicaltype name="SQL_VARIANT" objectid="LOGDT045">
+ <mapping rdbms="Oracle Database 11g">SYS.ANYDATA</mapping>
+ <mapping rdbms="Oracle Database 10g">SYS.ANYDATA</mapping>
+ <mapping rdbms="Oracle9i">SYS.ANYDATA</mapping>
+ <mapping rdbms="SQL Server 2005">SQL_VARIANT</mapping>
+ <mapping rdbms="SQL Server 2000">SQL_VARIANT</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="SYSNAME" objectid="LOGDT039">
+ <mapping rdbms="Oracle Database 11g">VARCHAR2, size</mapping>
+ <mapping rdbms="Oracle Database 10g">VARCHAR2, size</mapping>
+ <mapping rdbms="Oracle9i">VARCHAR2, size</mapping>
+ <mapping rdbms="SQL Server 2005">SYSNAME</mapping>
+ <mapping rdbms="SQL Server 2000">SYSNAME</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARCHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="SYS_ANYDATA" objectid="LogDes-F046B719-7D91-3873-3302-38C441683842@LOGDT10010">
+ <mapping rdbms="Oracle Database 11g">SYS.ANYDATA</mapping>
+ <mapping rdbms="Oracle Database 10g">SYS.ANYDATA</mapping>
+ <mapping rdbms="Oracle9i">SYS.ANYDATA</mapping>
+ <mapping rdbms="SQL Server 2005">SQL_VARIANT</mapping>
+ <mapping rdbms="SQL Server 2000">SQL_VARIANT</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="SYS_ANYDATASET" objectid="LogDes-22E251EB-9F6C-8137-56B2-DD4B87DC1E33@LOGDT10030">
+ <mapping rdbms="Oracle Database 11g">SYS.ANYDATASET</mapping>
+ <mapping rdbms="Oracle Database 10g">SYS.ANYDATASET</mapping>
+ <mapping rdbms="Oracle9i">SYS.ANYDATASET</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="SYS_ANYTYPE" objectid="LogDes-F046B719-7D91-3873-3302-38C441683842@LOGDT10011">
+ <mapping rdbms="Oracle Database 11g">SYS.ANYTYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">SYS.ANYTYPE</mapping>
+ <mapping rdbms="Oracle9i">SYS.ANYTYPE</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <logicaltype name="TEXT" objectid="LOGDT040">
+ <mapping rdbms="Oracle Database 11g">CLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">CLOB</mapping>
+ <mapping rdbms="Oracle9i">CLOB</mapping>
+ <mapping rdbms="SQL Server 2005">TEXT</mapping>
+ <mapping rdbms="SQL Server 2000">TEXT</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARCHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="TIMESTAMP WITH LOCAL TIME ZONE" objectid="LOGDT047">
+ <mapping rdbms="Oracle Database 11g">TIMESTAMP WITH LOCAL TIME ZONE, precision</mapping>
+ <mapping rdbms="Oracle Database 10g">TIMESTAMP WITH LOCAL TIME ZONE, precision</mapping>
+ <mapping rdbms="Oracle9i">TIMESTAMP WITH LOCAL TIME ZONE, precision</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/390 7">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIMESTAMP</mapping>
+ </logicaltype>
+ <logicaltype name="TIMESTAMP WITH TIME ZONE" objectid="LOGDT046">
+ <mapping rdbms="Oracle Database 11g">TIMESTAMP WITH TIME ZONE, precision</mapping>
+ <mapping rdbms="Oracle Database 10g">TIMESTAMP WITH TIME ZONE, precision</mapping>
+ <mapping rdbms="Oracle9i">TIMESTAMP WITH TIME ZONE, precision</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/390 7">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIMESTAMP</mapping>
+ </logicaltype>
+ <logicaltype name="TINYINT" objectid="LOGDT042">
+ <mapping rdbms="Oracle Database 11g">SMALLINT</mapping>
+ <mapping rdbms="Oracle Database 10g">SMALLINT</mapping>
+ <mapping rdbms="Oracle9i">SMALLINT</mapping>
+ <mapping rdbms="SQL Server 2005">TINYINT</mapping>
+ <mapping rdbms="SQL Server 2000">TINYINT</mapping>
+ <mapping rdbms="DB2/390 8">SMALLINT</mapping>
+ <mapping rdbms="DB2/390 7">SMALLINT</mapping>
+ <mapping rdbms="DB2/UDB 8.1">SMALLINT</mapping>
+ <mapping rdbms="DB2/UDB 7.1">SMALLINT</mapping>
+ </logicaltype>
+ <logicaltype name="Time" objectid="LOGDT014">
+ <mapping rdbms="Oracle Database 11g">DATE</mapping>
+ <mapping rdbms="Oracle Database 10g">DATE</mapping>
+ <mapping rdbms="Oracle9i">DATE</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIME</mapping>
+ <mapping rdbms="DB2/390 7">TIME</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIME</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIME</mapping>
+ </logicaltype>
+ <logicaltype name="Timestamp" objectid="LOGDT015">
+ <mapping rdbms="Oracle Database 11g">TIMESTAMP, precision</mapping>
+ <mapping rdbms="Oracle Database 10g">TIMESTAMP, precision</mapping>
+ <mapping rdbms="Oracle9i">TIMESTAMP, precision</mapping>
+ <mapping rdbms="SQL Server 2005">DATETIME</mapping>
+ <mapping rdbms="SQL Server 2000">DATETIME</mapping>
+ <mapping rdbms="DB2/390 8">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/390 7">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 8.1">TIMESTAMP</mapping>
+ <mapping rdbms="DB2/UDB 7.1">TIMESTAMP</mapping>
+ </logicaltype>
+ <logicaltype name="UNIQUEIDENTIFIER" objectid="LOGDT057">
+ <mapping rdbms="Oracle Database 11g">CHAR, size</mapping>
+ <mapping rdbms="Oracle Database 10g">CHAR, size</mapping>
+ <mapping rdbms="Oracle9i">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2005">UNIQUEIDENTIFIER</mapping>
+ <mapping rdbms="SQL Server 2000">UNIQUEIDENTIFIER</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="URIType" objectid="LOGDT051">
+ <mapping rdbms="Oracle Database 11g">URITYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">URITYPE</mapping>
+ <mapping rdbms="Oracle9i">URITYPE</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="URowID" objectid="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10041">
+ <mapping rdbms="Oracle Database 11g">UROWID, size</mapping>
+ <mapping rdbms="Oracle Database 10g">UROWID, size</mapping>
+ <mapping rdbms="Oracle9i">UROWID, size</mapping>
+ <mapping rdbms="SQL Server 2005">VARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARCHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="VARBINARY" objectid="LOGDT041">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">VARBINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARBINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">BLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="VARCHAR" objectid="LOGDT024">
+ <mapping rdbms="Oracle Database 11g">VARCHAR2, size</mapping>
+ <mapping rdbms="Oracle Database 10g">VARCHAR2, size</mapping>
+ <mapping rdbms="Oracle9i">VARCHAR2, size</mapping>
+ <mapping rdbms="SQL Server 2005">VARCHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARCHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARCHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="VARGRAPHIC" objectid="LOGDT023">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">VARBINARY, size</mapping>
+ <mapping rdbms="SQL Server 2000">VARBINARY, size</mapping>
+ <mapping rdbms="DB2/390 8">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/390 7">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">VARGRAPHIC, size</mapping>
+ </logicaltype>
+ <logicaltype name="Video" objectid="LOGDT016">
+ <mapping rdbms="Oracle Database 11g">BLOB</mapping>
+ <mapping rdbms="Oracle Database 10g">BLOB</mapping>
+ <mapping rdbms="Oracle9i">BLOB</mapping>
+ <mapping rdbms="SQL Server 2005">IMAGE</mapping>
+ <mapping rdbms="SQL Server 2000">IMAGE</mapping>
+ <mapping rdbms="DB2/390 8">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/390 7">VARGRAPHIC, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">BLOB</mapping>
+ <mapping rdbms="DB2/UDB 7.1">BLOB</mapping>
+ </logicaltype>
+ <logicaltype name="XDBURIType" objectid="LOGDT053">
+ <mapping rdbms="Oracle Database 11g">XDBURITYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">XDBURITYPE</mapping>
+ <mapping rdbms="Oracle9i">XDBURITYPE</mapping>
+ <mapping rdbms="SQL Server 2005">CHAR, size</mapping>
+ <mapping rdbms="SQL Server 2000">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 8">CHAR, size</mapping>
+ <mapping rdbms="DB2/390 7">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">CHAR, size</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CHAR, size</mapping>
+ </logicaltype>
+ <logicaltype name="XMLType" objectid="LOGDT050">
+ <mapping rdbms="Oracle Database 11g">XMLTYPE</mapping>
+ <mapping rdbms="Oracle Database 10g">XMLTYPE</mapping>
+ <mapping rdbms="Oracle9i">XMLTYPE</mapping>
+ <mapping rdbms="SQL Server 2005">XML</mapping>
+ <mapping rdbms="SQL Server 2000">TEXT</mapping>
+ <mapping rdbms="DB2/390 8">CLOB, size</mapping>
+ <mapping rdbms="DB2/390 7">CLOB, size</mapping>
+ <mapping rdbms="DB2/UDB 8.1">XML</mapping>
+ <mapping rdbms="DB2/UDB 7.1">CLOB, size</mapping>
+ </logicaltype>
+ <logicaltype name="unknown" objectid="LOGDT017" default="true">
+ <mapping rdbms="Oracle Database 11g">UNKNOWN</mapping>
+ <mapping rdbms="Oracle Database 10g">UNKNOWN</mapping>
+ <mapping rdbms="Oracle9i">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2005">UNKNOWN</mapping>
+ <mapping rdbms="SQL Server 2000">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 8">UNKNOWN</mapping>
+ <mapping rdbms="DB2/390 7">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 8.1">UNKNOWN</mapping>
+ <mapping rdbms="DB2/UDB 7.1">UNKNOWN</mapping>
+ </logicaltype>
+ <native_to_logical_mappings>
+ <mappings_for_RDBMS_type rdbms_type="Oracle Database 11g">
+ <mapping native_type="BFILE" logicaltype="BFile" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10034" />
+ <mapping native_type="BINARY_DOUBLE" logicaltype="BINARY DOUBLE" log_type_id="LOGDT056" />
+ <mapping native_type="BINARY_FLOAT" logicaltype="BINARY FLOAT" log_type_id="LOGDT055" />
+ <mapping native_type="BLOB" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHAR VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CHARACTER" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHARACTER VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="LONG" logicaltype="Long Char" log_type_id="LogDes-1768A872-F385-FDBA-D95E-0CB63F5908E2@LOGDT10045" />
+ <mapping native_type="LONG RAW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONG ROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONGROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="NATIONAL CHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NATIONAL CHARACTER" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHARACTER VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NCHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCLOB" logicaltype="NClob" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10035" />
+ <mapping native_type="NUMBER" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="RAW" logicaltype="Raw" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10040" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="ROWID" logicaltype="ROWID" log_type_id="LOGDT032" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="UROWID" logicaltype="URowID" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10041" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="VARCHAR2" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="Oracle Database 10g">
+ <mapping native_type="BFILE" logicaltype="BFile" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10034" />
+ <mapping native_type="BINARY_DOUBLE" logicaltype="BINARY DOUBLE" log_type_id="LOGDT056" />
+ <mapping native_type="BINARY_FLOAT" logicaltype="BINARY FLOAT" log_type_id="LOGDT055" />
+ <mapping native_type="BLOB" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHAR VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CHARACTER" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHARACTER VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="LONG" logicaltype="Long Char" log_type_id="LogDes-1768A872-F385-FDBA-D95E-0CB63F5908E2@LOGDT10045" />
+ <mapping native_type="LONG RAW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONG ROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONGROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="NATIONAL CHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NATIONAL CHARACTER" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHARACTER VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NCHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCLOB" logicaltype="NClob" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10035" />
+ <mapping native_type="NUMBER" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="RAW" logicaltype="Raw" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10040" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="ROWID" logicaltype="ROWID" log_type_id="LOGDT032" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="UROWID" logicaltype="URowID" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10041" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="VARCHAR2" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="Oracle9i">
+ <mapping native_type="BFILE" logicaltype="BFile" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10034" />
+ <mapping native_type="BLOB" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHAR VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CHARACTER" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHARACTER VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DBURITYPE" logicaltype="DBURIType" log_type_id="LOGDT054" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="HTTPURITYPE" logicaltype="HTTPURIType" log_type_id="LOGDT052" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="INTERVAL DAY TO SECOND" logicaltype="INTERVAL DAY TO SECOND" log_type_id="LOGDT049" />
+ <mapping native_type="INTERVAL YEAR TO MONTH" logicaltype="INTERVAL YEAR TO MONTH" log_type_id="LOGDT048" />
+ <mapping native_type="LONG" logicaltype="Long Char" log_type_id="LogDes-1768A872-F385-FDBA-D95E-0CB63F5908E2@LOGDT10045" />
+ <mapping native_type="LONG RAW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONG ROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="LONGROW" logicaltype="Long_Raw" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10036" />
+ <mapping native_type="NATIONAL CHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NATIONAL CHARACTER" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NATIONAL CHARACTER VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NCHAR VARYING" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="NCLOB" logicaltype="NClob" log_type_id="LogDes-7DD553FD-11E8-61FA-399D-2E531FB621D0@LOGDT10035" />
+ <mapping native_type="NUMBER" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="NVARCHAR2" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="ORDSYS.ORDAudio" logicaltype="ORDAUDIO" log_type_id="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10005" />
+ <mapping native_type="ORDSYS.ORDDoc" logicaltype="ORDDOC" log_type_id="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10009" />
+ <mapping native_type="ORDSYS.ORDImage" logicaltype="ORDIMAGE" log_type_id="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10006" />
+ <mapping native_type="ORDSYS.ORDImageSignature" logicaltype="ORDIMAGE_SIGNATURE" log_type_id="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10007" />
+ <mapping native_type="ORDSYS.ORDVideo" logicaltype="ORDVIDEO" log_type_id="LogDes-4972B6D2-6F93-8AE5-6E24-3599E65A7CFE@LOGDT10008" />
+ <mapping native_type="RAW" logicaltype="Raw" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10040" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="ROWID" logicaltype="ROWID" log_type_id="LOGDT032" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="SYS.ANYDATA" logicaltype="SYS_ANYDATA" log_type_id="LogDes-F046B719-7D91-3873-3302-38C441683842@LOGDT10010" />
+ <mapping native_type="SYS.ANYDATASET" logicaltype="SYS_ANYDATASET" log_type_id="LogDes-22E251EB-9F6C-8137-56B2-DD4B87DC1E33@LOGDT10030" />
+ <mapping native_type="SYS.ANYTYPE" logicaltype="SYS_ANYTYPE" log_type_id="LogDes-F046B719-7D91-3873-3302-38C441683842@LOGDT10011" />
+ <mapping native_type="TIMESTAMP" logicaltype="Timestamp" log_type_id="LOGDT015" />
+ <mapping native_type="TIMESTAMP WITH LOCAL TIME ZONE" logicaltype="TIMESTAMP WITH LOCAL TIME ZONE" log_type_id="LOGDT047" />
+ <mapping native_type="TIMESTAMP WITH TIME ZONE" logicaltype="TIMESTAMP WITH TIME ZONE" log_type_id="LOGDT046" />
+ <mapping native_type="URITYPE" logicaltype="URIType" log_type_id="LOGDT051" />
+ <mapping native_type="UROWID" logicaltype="URowID" log_type_id="LogDes-4BABEC65-108B-2A3C-F7C4-84AC47D292B0@LOGDT10041" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="VARCHAR2" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="XDBURITYPE" logicaltype="XDBURIType" log_type_id="LOGDT053" />
+ <mapping native_type="XMLTYPE" logicaltype="XMLType" log_type_id="LOGDT050" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="SQL Server 2005">
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="XML" logicaltype="XMLType" log_type_id="LOGDT050" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="SQL Server 2000">
+ <mapping native_type="BIGINT" logicaltype="BIGINT" log_type_id="LOGDT027" />
+ <mapping native_type="BINARY" logicaltype="BINARY" log_type_id="LOGDT033" />
+ <mapping native_type="BIT" logicaltype="BIT" log_type_id="LOGDT034" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DATETIME" logicaltype="Datetime" log_type_id="LOGDT008" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="IMAGE" logicaltype="Image" log_type_id="LOGDT010" />
+ <mapping native_type="INT" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="MONEY" logicaltype="MONEY" log_type_id="LOGDT043" />
+ <mapping native_type="NCHAR" logicaltype="NCHAR" log_type_id="LOGDT035" />
+ <mapping native_type="NTEXT" logicaltype="NTEXT" log_type_id="LOGDT036" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="NVARCHAR" logicaltype="NVARCHAR" log_type_id="LOGDT037" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="ROWID" logicaltype="ROWID" log_type_id="LOGDT032" />
+ <mapping native_type="SMALLDATETIME" logicaltype="SMALLDATETIME" log_type_id="LOGDT038" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="SMALLMONEY" logicaltype="SMALLMONEY" log_type_id="LOGDT044" />
+ <mapping native_type="SQL_VARIANT" logicaltype="SQL_VARIANT" log_type_id="LOGDT045" />
+ <mapping native_type="SYSNAME" logicaltype="SYSNAME" log_type_id="LOGDT039" />
+ <mapping native_type="TEXT" logicaltype="TEXT" log_type_id="LOGDT040" />
+ <mapping native_type="TIMESTAMP" logicaltype="Timestamp" log_type_id="LOGDT015" />
+ <mapping native_type="TINYINT" logicaltype="TINYINT" log_type_id="LOGDT042" />
+ <mapping native_type="UNIQUEIDENTIFIER" logicaltype="UNIQUEIDENTIFIER" log_type_id="LOGDT057" />
+ <mapping native_type="VARBINARY" logicaltype="VARBINARY" log_type_id="LOGDT041" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="DB2/390 8">
+ <mapping native_type="GRAPHIC" logicaltype="GRAPHIC" log_type_id="LOGDT031" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="DB2/390 7">
+ <mapping native_type="BINARY LARGE OBJECT" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="BLOB" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHAR LARGE OBJECT" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="CHAR VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CHARACTER" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHARACTER LARGE OBJECT" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="CHARACTER VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DBCLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="GRAPHIC" logicaltype="GRAPHIC" log_type_id="LOGDT031" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="LONG VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="LONG VARGRAPHIC" logicaltype="VARGRAPHIC" log_type_id="LOGDT023" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="ROWID" logicaltype="ROWID" log_type_id="LOGDT032" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="TIME" logicaltype="Time" log_type_id="LOGDT014" />
+ <mapping native_type="TIMESTAMP" logicaltype="Timestamp" log_type_id="LOGDT015" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="VARGRAPHIC" logicaltype="VARGRAPHIC" log_type_id="LOGDT023" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="DB2/UDB 8.1">
+ <mapping native_type="GRAPHIC" logicaltype="GRAPHIC" log_type_id="LOGDT031" />
+ <mapping native_type="XML" logicaltype="XMLType" log_type_id="LOGDT050" />
+ </mappings_for_RDBMS_type>
+ <mappings_for_RDBMS_type rdbms_type="DB2/UDB 7.1">
+ <mapping native_type="BIGINT" logicaltype="BIGINT" log_type_id="LOGDT027" />
+ <mapping native_type="BLOB" logicaltype="BLOB" log_type_id="LOGDT029" />
+ <mapping native_type="CHAR" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHAR VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CHARACTER" logicaltype="CHAR" log_type_id="LOGDT025" />
+ <mapping native_type="CHARACTER VARYING" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="CLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DATALINK" logicaltype="DATALINK" log_type_id="LOGDT030" />
+ <mapping native_type="DATE" logicaltype="Date" log_type_id="LOGDT007" />
+ <mapping native_type="DBCLOB" logicaltype="CLOB" log_type_id="LOGDT028" />
+ <mapping native_type="DECIMAL" logicaltype="DECIMAL" log_type_id="LOGDT026" />
+ <mapping native_type="DOUBLE" logicaltype="DOUBLE" log_type_id="LOGDT020" />
+ <mapping native_type="FLOAT" logicaltype="FLOAT" log_type_id="LOGDT021" />
+ <mapping native_type="GRAPHIC" logicaltype="GRAPHIC" log_type_id="LOGDT031" />
+ <mapping native_type="INTEGER" logicaltype="Integer" log_type_id="LOGDT011" />
+ <mapping native_type="LONG VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="LONG VARGRAPHIC" logicaltype="VARGRAPHIC" log_type_id="LOGDT023" />
+ <mapping native_type="NUMERIC" logicaltype="NUMERIC" log_type_id="LOGDT019" />
+ <mapping native_type="REAL" logicaltype="REAL" log_type_id="LOGDT022" />
+ <mapping native_type="SMALLINT" logicaltype="SMALLINT" log_type_id="LOGDT018" />
+ <mapping native_type="TIME" logicaltype="Time" log_type_id="LOGDT014" />
+ <mapping native_type="TIMESTAMP" logicaltype="Timestamp" log_type_id="LOGDT015" />
+ <mapping native_type="VARCHAR" logicaltype="VARCHAR" log_type_id="LOGDT024" />
+ <mapping native_type="VARGRAPHIC" logicaltype="VARGRAPHIC" log_type_id="LOGDT023" />
+ </mappings_for_RDBMS_type>
+ </native_to_logical_mappings>
+ <ud_native_db_types />
+</logtypes> \ No newline at end of file
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseComments.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseComments.pgsql
new file mode 100644
index 00000000..75d3e053
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseComments.pgsql
@@ -0,0 +1,1193 @@
+-- $Id: TestManagerDatabaseComments.pgsql $
+--- @file
+-- Autogenerated from TestManagerDatabaseInit.pgsql. Do not edit!
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+COMMENT ON COLUMN SystemLog.tsCreated IS
+ 'When this was logged.';
+
+COMMENT ON COLUMN SystemLog.sEvent IS
+ 'The event type.
+This is a 8 character string identifier so that we don''t need to change
+some enum type everytime we introduce a new event type.';
+
+COMMENT ON COLUMN SystemLog.sLogText IS
+ 'The log text.';
+
+COMMENT ON TABLE Users IS
+ 'Test manager users.
+
+This is mainly for doing simple access checks before permitting access to
+the test manager. This needs to be coordinated with
+apache/ldap/Oracle-Single-Sign-On.
+
+The main purpose, though, is for tracing who changed the test config and
+analysis data.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp.';
+
+COMMENT ON COLUMN Users.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN Users.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN Users.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN Users.sUsername IS
+ 'User name.';
+
+COMMENT ON COLUMN Users.sEmail IS
+ 'The email address of the user.';
+
+COMMENT ON COLUMN Users.sFullName IS
+ 'The full name.';
+
+COMMENT ON COLUMN Users.sLoginName IS
+ 'The login name used by apache.';
+
+COMMENT ON COLUMN Users.fReadOnly IS
+ 'Read access only.';
+
+COMMENT ON TABLE GlobalResources IS
+ 'Global resource configuration.
+
+For example an iSCSI target.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp.';
+
+COMMENT ON COLUMN GlobalResources.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN GlobalResources.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN GlobalResources.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN GlobalResources.sName IS
+ 'The name of the resource.';
+
+COMMENT ON COLUMN GlobalResources.sDescription IS
+ 'Optional resource description.';
+
+COMMENT ON COLUMN GlobalResources.fEnabled IS
+ 'Indicates whether this resource is currently enabled (online).';
+
+COMMENT ON TABLE BuildSources IS
+ 'Build sources.
+
+This is used by a scheduling group to select builds and the default
+Validation Kit from the Builds table.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp.
+
+@todo Any better way of representing this so we could more easily
+ join/whatever when searching for builds?';
+
+COMMENT ON COLUMN BuildSources.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN BuildSources.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN BuildSources.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN BuildSources.sName IS
+ 'The name of the build source.';
+
+COMMENT ON COLUMN BuildSources.sDescription IS
+ 'Description.';
+
+COMMENT ON COLUMN BuildSources.sProduct IS
+ 'Which product.
+ASSUME that it is okay to limit a build source to a single product.';
+
+COMMENT ON COLUMN BuildSources.sBranch IS
+ 'Which branch.
+ASSUME that it is okay to limit a build source to a branch.';
+
+COMMENT ON COLUMN BuildSources.asTypes IS
+ 'Build types to include, all matches if NULL.
+@todo Weighting the types would be nice in a later version.';
+
+COMMENT ON COLUMN BuildSources.asOsArches IS
+ 'Array of the ''sOs.sCpuArch'' to match, all matches if NULL.
+See KBUILD_OSES in kBuild for a list of standard target OSes, and
+KBUILD_ARCHES for a list of standard architectures.
+
+@remarks See marks on ''os-agnostic'' and ''noarch'' in BuildCategories.';
+
+COMMENT ON COLUMN BuildSources.iFirstRevision IS
+ 'The first subversion tree revision to match, no lower limit if NULL.';
+
+COMMENT ON COLUMN BuildSources.iLastRevision IS
+ 'The last subversion tree revision to match, no upper limit if NULL.';
+
+COMMENT ON COLUMN BuildSources.cSecMaxAge IS
+ 'The maximum age of the builds in seconds, unlimited if NULL.';
+
+COMMENT ON TABLE TestCases IS
+ 'Test case configuration.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp.';
+
+COMMENT ON COLUMN TestCases.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestCases.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestCases.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestCases.sName IS
+ 'The name of the test case.';
+
+COMMENT ON COLUMN TestCases.sDescription IS
+ 'Optional test case description.';
+
+COMMENT ON COLUMN TestCases.fEnabled IS
+ 'Indicates whether this test case is currently enabled.';
+
+COMMENT ON COLUMN TestCases.cSecTimeout IS
+ 'Default test case timeout given in seconds.';
+
+COMMENT ON COLUMN TestCases.sTestBoxReqExpr IS
+ 'Default TestBox requirement expression (python boolean expression).
+All the scheduler properties are available for use with the same names
+as in that table.
+If NULL everything matches.';
+
+COMMENT ON COLUMN TestCases.sBuildReqExpr IS
+ 'Default build requirement expression (python boolean expression).
+The following build properties are available: sProduct, sBranch,
+sType, asOsArches, sVersion, iRevision, uidAuthor and idBuild.
+If NULL everything matches.';
+
+COMMENT ON COLUMN TestCases.sBaseCmd IS
+ 'The base command.
+String suitable for executing in bourne shell with space as separator
+(IFS). References to @BUILD_BINARIES@ will be replaced WITH the content
+of the Builds(sBinaries) field.';
+
+COMMENT ON COLUMN TestCases.sTestSuiteZips IS
+ 'Comma separated list of test suite zips (or tars) that the testbox will
+need to download and expand prior to testing.
+If NULL the current test suite of the scheduling group will be used (the
+scheduling group will have an optional test suite build queue associated
+with it). The current test suite can also be referenced by
+@VALIDATIONKIT_ZIP@ in case more downloads are required. Files may also be
+uploaded to the test manager download area, in which case the
+@DOWNLOAD_BASE_URL@ prefix can be used to refer to this area.';
+
+COMMENT ON TABLE TestCaseArgs IS
+ 'Test case argument list variations.
+
+For example, we have a test case that does a set of tests on a virtual
+machine. To get better code/feature coverage of this testcase we wish to
+run it with different guest hardware configuration. The test case may do
+the same stuff, but the guest OS as well as the VMM may react differently to
+the hardware configurations and uncover issues in the VMM, device emulation
+or other places.
+
+Typical hardware variations are:
+ - guest memory size (RAM),
+ - guest video memory size (VRAM),
+ - virtual CPUs / cores / threads,
+ - virtual chipset
+ - virtual network interface card (NIC)
+ - USB 1.1, USB 2.0, no USB
+
+The TM web UI will help the user create a reasonable set of permutations
+of these parameters, the user specifies a maximum and the TM uses certain
+rules together with random selection to generate the desired number. The
+UI will also help suggest fitting testbox requirements according to the
+RAM/VRAM sizes and the virtual CPU counts. The user may then make
+adjustments to the suggestions before commit them.
+
+Alternatively, the user may also enter all the permutations without any
+help from the UI.
+
+Note! All test cases has at least one entry in this table, even if it is
+empty, because testbox requirements are specified thru this.
+
+Querying the valid parameter lists for a testase this way:
+ SELECT * ... WHERE idTestCase = TestCases.idTestCase
+ AND tsExpire > <when>
+ AND tsEffective <= <when>;
+
+Querying the valid parameter list for the latest generation can be
+simplified by just checking tsExpire date:
+ SELECT * ... WHERE idTestCase = TestCases.idTestCase
+ AND tsExpire == TIMESTAMP WITH TIME ZONE ''infinity'';
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp.';
+
+COMMENT ON COLUMN TestCaseArgs.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestCaseArgs.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestCaseArgs.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestCaseArgs.sArgs IS
+ 'The additional arguments.
+String suitable for bourne shell style argument parsing with space as
+separator (IFS). References to @BUILD_BINARIES@ will be replaced with
+the content of the Builds(sBinaries) field.';
+
+COMMENT ON COLUMN TestCaseArgs.cSecTimeout IS
+ 'Optional test case timeout given in seconds.
+If NULL, the TestCases.cSecTimeout field is used instead.';
+
+COMMENT ON COLUMN TestCaseArgs.sTestBoxReqExpr IS
+ 'Additional TestBox requirement expression (python boolean expression).
+All the scheduler properties are available for use with the same names
+as in that table. This is checked after first checking the requirements
+in the TestCases.sTestBoxReqExpr field.';
+
+COMMENT ON COLUMN TestCaseArgs.sBuildReqExpr IS
+ 'Additional build requirement expression (python boolean expression).
+The following build properties are available: sProduct, sBranch,
+sType, asOsArches, sVersion, iRevision, uidAuthor and idBuild. This is
+checked after first checking the requirements in the
+TestCases.sBuildReqExpr field.';
+
+COMMENT ON COLUMN TestCaseArgs.cGangMembers IS
+ 'Number of testboxes required (gang scheduling).';
+
+COMMENT ON COLUMN TestCaseArgs.sSubName IS
+ 'Optional variation sub-name.';
+
+COMMENT ON INDEX TestCaseArgsLookupIdx IS
+ 'The arguments are part of the primary key for several reasons.
+No duplicate argument lists (makes no sense - if you want to prioritize
+argument lists, we add that explicitly). This may hopefully enable us
+to more easily check coverage later on, even when the test case is
+reconfigured with more/less permutations.';
+
+COMMENT ON TABLE TestCaseDeps IS
+ 'Test case dependencies (N:M)
+
+This effect build selection. The build must have passed all runs of the
+given prerequisite testcase (idTestCasePreReq) and executed at a minimum one
+argument list variation.
+
+This should also affect scheduling order, if possible at least one
+prerequisite testcase variation should be place before the specific testcase
+in the scheduling queue.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestCaseDeps.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestCaseDeps.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestCaseDeps.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON TABLE TestCaseGlobalRsrcDeps IS
+ 'Test case dependencies on global resources (N:M)
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestCaseGlobalRsrcDeps.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestCaseGlobalRsrcDeps.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestCaseGlobalRsrcDeps.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON TABLE TestGroups IS
+ 'Test Group - A collection of test cases.
+
+This is for simplifying test configuration by working with a few groups
+instead of a herd of individual testcases. It may also be used for creating
+test suites for certain areas (like guest additions) or tasks (like
+performance measurements).
+
+A test case can be member of any number of test groups.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestGroups.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestGroups.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestGroups.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestGroups.sName IS
+ 'The name of the scheduling group.';
+
+COMMENT ON COLUMN TestGroups.sDescription IS
+ 'Optional group description.';
+
+COMMENT ON TABLE TestGroupMembers IS
+ 'The N:M relationship between test case configurations and test groups.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestGroupMembers.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestGroupMembers.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestGroupMembers.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestGroupMembers.iSchedPriority IS
+ 'Test case scheduling priority.
+Higher number causes the test case to be run more frequently.
+@sa SchedGroupMembers.iSchedPriority, TestBoxesInSchedGroups.iSchedPriority
+@todo Not sure we want to keep this...';
+
+COMMENT ON TABLE SchedGroups IS
+ 'Scheduling group (aka. testbox partitioning) configuration.
+
+A testbox is associated with exactly one scheduling group. This association
+can be changed, of course. If we (want to) retire a group which still has
+testboxes associated with it, these will be moved to the ''default'' group.
+
+The TM web UI will make sure that a testbox is always in a group and that
+the default group cannot be deleted.
+
+A scheduling group combines several things:
+ - A selection of builds to test (via idBuildSrc).
+ - A collection of test groups to test with (via SchedGroupMembers).
+ - A set of testboxes to test on (via TestBoxes.idSchedGroup).
+
+In additions there is an optional source of fresh test suite builds (think
+VBoxTestSuite) as well as scheduling options.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN SchedGroups.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN SchedGroups.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN SchedGroups.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)
+@note This is NULL for the default group.';
+
+COMMENT ON COLUMN SchedGroups.sName IS
+ 'The name of the scheduling group.';
+
+COMMENT ON COLUMN SchedGroups.sDescription IS
+ 'Optional group description.';
+
+COMMENT ON COLUMN SchedGroups.fEnabled IS
+ 'Indicates whether this group is currently enabled.';
+
+COMMENT ON COLUMN SchedGroups.enmScheduler IS
+ 'The scheduler to use.
+This is for when we later desire different scheduling that the best
+effort stuff provided by the initial implementation.';
+
+COMMENT ON COLUMN SchedGroups.sComment IS
+ 'The Validation Kit build source (@VALIDATIONKIT_ZIP@).
+Non-unique foreign key: BuildSources(idBuildSrc)';
+
+COMMENT ON TABLE SchedGroupMembers IS
+ 'N:M relationship between scheduling groups and test groups.
+
+Several scheduling parameters are associated with this relationship.
+
+The test group dependency (idTestGroupPreReq) can be used in the same way as
+TestCaseDeps.idTestCasePreReq, only here on test group level. This means it
+affects the build selection. The builds needs to have passed all test runs
+the prerequisite test group and done at least one argument variation of each
+test case in it.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN SchedGroupMembers.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN SchedGroupMembers.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN SchedGroupMembers.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN SchedGroupMembers.iSchedPriority IS
+ 'The scheduling priority of the test group.
+Higher number causes the test case to be run more frequently.
+@sa TestGroupMembers.iSchedPriority, TestBoxesInSchedGroups.iSchedPriority';
+
+COMMENT ON COLUMN SchedGroupMembers.bmHourlySchedule IS
+ 'When during the week this group is allowed to start running, NULL means
+there are no constraints.
+Each bit in the bitstring represents one hour, with bit 0 indicating the
+midnight hour on a monday.';
+
+COMMENT ON TABLE TestBoxStrTab IS
+ 'String table for the test boxes.
+
+This is a string cache for all string members in TestBoxes except the name.
+The rational is to avoid duplicating large strings like sReport when the
+testbox reports a new cMbScratch value or the box when the test sheriff
+sends a reboot command or similar.
+
+At the time this table was introduced, we had 400558 TestBoxes rows, where
+the SUM(LENGTH(sReport)) was 993MB. There were really just 1066 distinct
+sReport values, with a total length of 0x3 MB.
+
+Nothing is ever deleted from this table.
+
+@note Should use a stored procedure to query/insert a string.
+
+
+TestBox stats prior to conversion:
+ SELECT COUNT(*) FROM TestBoxes: 400558 rows
+ SELECT pg_total_relation_size(''TestBoxes''): 740794368 bytes (706 MB)
+ Average row cost: 740794368 / 400558 = 1849 bytes/row
+
+After conversion:
+ SELECT COUNT(*) FROM TestBoxes: 400558 rows
+ SELECT pg_total_relation_size(''TestBoxes''): 144375808 bytes (138 MB)
+ SELECT COUNT(idStr) FROM TestBoxStrTab: 1292 rows
+ SELECT pg_total_relation_size(''TestBoxStrTab''): 5709824 bytes (5.5 MB)
+ (144375808 + 5709824) / 740794368 = 20 %
+ Average row cost boxes: 144375808 / 400558 = 360 bytes/row
+ Average row cost strings: 5709824 / 1292 = 4420 bytes/row';
+
+COMMENT ON COLUMN TestBoxStrTab.sValue IS
+ 'The string value.';
+
+COMMENT ON COLUMN TestBoxStrTab.tsCreated IS
+ 'Creation time stamp.';
+
+COMMENT ON TYPE TestBoxCmd_T IS
+ 'Testbox commands.';
+
+COMMENT ON TYPE LomKind_T IS
+ 'The kind of lights out management on a testbox.';
+
+COMMENT ON TABLE TestBoxes IS
+ 'Testbox configurations.
+
+The testboxes are identified by IP and the system UUID if available. Should
+the IP change, the testbox will be refused at sign on and the testbox
+sheriff will have to update it''s IP.
+
+@todo Implement the UUID stuff. Get it from DMI, UEFI or whereever.
+ Mismatching needs to be logged somewhere...
+
+To query the currently valid configuration:
+ SELECT ... WHERE id = idTestBox AND tsExpire = TIMESTAMP WITH TIME ZONE ''infinity'';
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestBoxes.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestBoxes.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestBoxes.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+When modified automatically by the testbox, NULL is used.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestBoxes.uuidSystem IS
+ 'The system or firmware UUID.
+This uniquely identifies the testbox when talking to the server. After
+SIGNON though, the testbox will also provide idTestBox and ip to
+establish its identity beyond doubt.';
+
+COMMENT ON COLUMN TestBoxes.sName IS
+ 'The testbox name.
+Usually similar to the DNS name.';
+
+COMMENT ON COLUMN TestBoxes.fEnabled IS
+ 'Indicates whether this testbox is enabled.
+A testbox gets disabled when we''re doing maintenance, debugging a issue
+that happens only on that testbox, or some similar stuff. This is an
+alternative to deleting the testbox.';
+
+COMMENT ON COLUMN TestBoxes.enmLomKind IS
+ 'The kind of lights-out-management.';
+
+COMMENT ON COLUMN TestBoxes.lCpuRevision IS
+ 'Number identifying the CPU family/model/stepping/whatever.
+For x86 and AMD64 type CPUs, this will on the following format:
+ (EffFamily << 24) | (EffModel << 8) | Stepping.';
+
+COMMENT ON COLUMN TestBoxes.cCpus IS
+ 'Number of CPUs, CPU cores and CPU threads.';
+
+COMMENT ON COLUMN TestBoxes.fCpuHwVirt IS
+ 'Set if capable of hardware virtualization.';
+
+COMMENT ON COLUMN TestBoxes.fCpuNestedPaging IS
+ 'Set if capable of nested paging.';
+
+COMMENT ON COLUMN TestBoxes.fCpu64BitGuest IS
+ 'Set if CPU capable of 64-bit (VBox) guests.';
+
+COMMENT ON COLUMN TestBoxes.fChipsetIoMmu IS
+ 'Set if chipset with usable IOMMU (VT-d / AMD-Vi).';
+
+COMMENT ON COLUMN TestBoxes.fRawMode IS
+ 'Set if the test box does raw-mode tests.';
+
+COMMENT ON COLUMN TestBoxes.cMbMemory IS
+ 'The (approximate) memory size in megabytes (rounded down to nearest 4 MB).';
+
+COMMENT ON COLUMN TestBoxes.cMbScratch IS
+ 'The amount of scratch space in megabytes (rounded down to nearest 64 MB).';
+
+COMMENT ON COLUMN TestBoxes.iTestBoxScriptRev IS
+ 'The testbox script revision number, serves the purpose of a version number.
+Probably good to have when scheduling upgrades as well for status purposes.';
+
+COMMENT ON COLUMN TestBoxes.iPythonHexVersion IS
+ 'The python sys.hexversion (layed out as of 2.7).
+Good to know which python versions we need to support.';
+
+COMMENT ON COLUMN TestBoxes.enmPendingCmd IS
+ 'Pending command.
+@note We put it here instead of in TestBoxStatuses to get history.';
+
+COMMENT ON INDEX TestBoxesUuidIdx IS
+ 'Nested paging requires hardware virtualization.';
+
+COMMENT ON TABLE TestBoxesInSchedGroups IS
+ 'N:M relationship between test boxes and scheduling groups.
+
+We associate a priority with this relationship.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestBoxesInSchedGroups.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestBoxesInSchedGroups.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestBoxesInSchedGroups.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestBoxesInSchedGroups.iSchedPriority IS
+ 'The scheduling priority of the scheduling group for the test box.
+Higher number causes the scheduling group to be serviced more frequently.
+@sa TestGroupMembers.iSchedPriority, SchedGroups.iSchedPriority';
+
+COMMENT ON TABLE FailureCategories IS
+ 'Failure categories.
+
+This is for organizing the failure reasons.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN FailureCategories.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN FailureCategories.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN FailureCategories.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN FailureCategories.sShort IS
+ 'The short category description.
+For combo boxes and other selection lists.';
+
+COMMENT ON COLUMN FailureCategories.sFull IS
+ 'Full description
+For cursor-over-poppups for instance.';
+
+COMMENT ON TABLE FailureReasons IS
+ 'Failure reasons.
+
+When analysing a test failure, the testbox sheriff will try assign a fitting
+reason for the failure. This table is here to help the sheriff in his/hers
+job as well as developers looking checking if their changes affected the
+test results in any way.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN FailureReasons.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN FailureReasons.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN FailureReasons.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN FailureReasons.sShort IS
+ 'The short failure description.
+For combo boxes and other selection lists.';
+
+COMMENT ON COLUMN FailureReasons.sFull IS
+ 'Full failure description.';
+
+COMMENT ON COLUMN FailureReasons.iTicket IS
+ 'Ticket number in the primary bugtracker.';
+
+COMMENT ON COLUMN FailureReasons.asUrls IS
+ 'Other URLs to reports or discussions of the observed symptoms.';
+
+COMMENT ON TABLE TestResultFailures IS
+ 'This is for tracking/discussing test result failures.
+
+The rational for putting this is a separate table is that we need history on
+this while TestResults does not.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN TestResultFailures.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN TestResultFailures.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN TestResultFailures.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN TestResultFailures.sComment IS
+ 'Optional comment.';
+
+COMMENT ON TABLE BuildBlacklist IS
+ 'Table used to blacklist sets of builds.
+
+The best usage example is a VMM developer realizing that a change causes the
+host to panic, hang, or otherwise misbehave. To prevent the testbox sheriff
+from repeatedly having to reboot testboxes, the builds gets blacklisted
+until there is a working build again. This may mean adding an open ended
+blacklist spec and then updating it with the final revision number once the
+fix has been committed.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.
+
+@todo Would be nice if we could replace the text strings below with a set of
+ BuildCategories, or sore it in any other way which would enable us to
+ do a negative join with build category... The way it is specified
+ now, it looks like we have to open a cursor of prospecitve builds and
+ filter then thru this table one by one.
+
+ Any better representation is welcome, but this is low prioirty for
+ now, as it''s relatively easy to change this later one.';
+
+COMMENT ON COLUMN BuildBlacklist.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN BuildBlacklist.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN BuildBlacklist.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)';
+
+COMMENT ON COLUMN BuildBlacklist.sProduct IS
+ 'Which product.
+ASSUME that it is okay to limit a blacklisting to a single product.';
+
+COMMENT ON COLUMN BuildBlacklist.sBranch IS
+ 'Which branch.
+ASSUME that it is okay to limit a blacklisting to a branch.';
+
+COMMENT ON COLUMN BuildBlacklist.asTypes IS
+ 'Build types to include, all matches if NULL.';
+
+COMMENT ON COLUMN BuildBlacklist.asOsArches IS
+ 'Array of the ''sOs.sCpuArch'' to match, all matches if NULL.
+See KBUILD_OSES in kBuild for a list of standard target OSes, and
+KBUILD_ARCHES for a list of standard architectures.
+
+@remarks See marks on ''os-agnostic'' and ''noarch'' in BuildCategories.';
+
+COMMENT ON COLUMN BuildBlacklist.iFirstRevision IS
+ 'The first subversion tree revision to blacklist.';
+
+COMMENT ON COLUMN BuildBlacklist.iLastRevision IS
+ 'The last subversion tree revision to blacklist, no upper limit if NULL.';
+
+COMMENT ON TABLE BuildCategories IS
+ 'Build categories.
+
+The purpose of this table is saving space in the Builds table and hopefully
+speed things up when selecting builds as well (compared to selecting on 4
+text fields in the much larger Builds table).
+
+Insert only table, no update, no delete. History is not needed.';
+
+COMMENT ON COLUMN BuildCategories.sProduct IS
+ 'Product.
+The product name. For instance ''VBox'' or ''VBoxTestSuite''.';
+
+COMMENT ON COLUMN BuildCategories.sRepository IS
+ 'The version control repository name.';
+
+COMMENT ON COLUMN BuildCategories.sBranch IS
+ 'The branch name (in the version control system).';
+
+COMMENT ON COLUMN BuildCategories.sType IS
+ 'The build type.
+See KBUILD_BLD_TYPES in kBuild for a list of standard build types.';
+
+COMMENT ON COLUMN BuildCategories.asOsArches IS
+ 'Array of the ''sOs.sCpuArch'' supported by the build.
+See KBUILD_OSES in kBuild for a list of standard target OSes, and
+KBUILD_ARCHES for a list of standard architectures.
+
+@remarks ''os-agnostic'' is used if the build doesn''t really target any
+ specific OS or if it targets all applicable OSes.
+ ''noarch'' is used if the build is architecture independent or if
+ all applicable architectures are handled.
+ Thus, ''os-agnostic.noarch'' will run on all build boxes.
+
+@note The array shall be sorted ascendingly to prevent unnecessary duplicates!';
+
+COMMENT ON TABLE Builds IS
+ 'The builds table contains builds from the tinderboxes and oaccasionally from
+developers.
+
+The tinderbox side could be fed by a batch job enumerating the build output
+directories every so often, looking for new builds. Or we could query them
+from the tinderbox database. Yet another alternative is making the
+tinderbox server or client side software inform us about all new builds.
+
+The developer builds are entered manually thru the TM web UI. They are used
+for subjecting new code to some larger scale testing before commiting,
+enabling, or merging a private branch.
+
+The builds are being selected from this table by the via the build source
+specification that SchedGroups.idBuildSrc and
+SchedGroups.idBuildSrcTestSuite links to.
+
+@remarks This table stores history. Never update or delete anything. The
+ equivalent of deleting is done by setting the ''tsExpire'' field to
+ current_timestamp. To select the currently valid entries use
+ tsExpire = TIMESTAMP WITH TIME ZONE ''infinity''.';
+
+COMMENT ON COLUMN Builds.tsCreated IS
+ 'When this build was created or entered into the database.
+This remains unchanged';
+
+COMMENT ON COLUMN Builds.tsEffective IS
+ 'When this row starts taking effect (inclusive).';
+
+COMMENT ON COLUMN Builds.tsExpire IS
+ 'When this row stops being tsEffective (exclusive).';
+
+COMMENT ON COLUMN Builds.uidAuthor IS
+ 'The user id of the one who created/modified this entry.
+Non-unique foreign key: Users(uid)
+@note This is NULL if added by a batch job / tinderbox.';
+
+COMMENT ON COLUMN Builds.iRevision IS
+ 'The subversion tree revision of the build.';
+
+COMMENT ON COLUMN Builds.sVersion IS
+ 'The product version number (suitable for RTStrVersionCompare).';
+
+COMMENT ON COLUMN Builds.sLogUrl IS
+ 'The link to the tinderbox log of this build.';
+
+COMMENT ON COLUMN Builds.sBinaries IS
+ 'Comma separated list of binaries.
+The binaries have paths relative to the TESTBOX_PATH_BUILDS or full URLs.';
+
+COMMENT ON COLUMN Builds.fBinariesDeleted IS
+ 'Set when the binaries gets deleted by the build quota script.';
+
+COMMENT ON TABLE VcsRevisions IS
+ 'This table is for translating build revisions into commit details.
+
+For graphs and test results, it would be useful to translate revisions into
+dates and maybe provide commit message and the committer.
+
+Data is entered exclusively thru one or more batch jobs, so no internal
+authorship needed. Also, since we''re mirroring data from external sources
+here, the batch job is allowed to update/replace existing records.
+
+@todo We we could collect more info from the version control systems, if we
+ believe it''s useful and can be presented in a reasonable manner.
+ Getting a list of affected files would be simple (requires
+ a separate table with a M:1 relationship to this table), or try
+ associate a commit to a branch.';
+
+COMMENT ON COLUMN VcsRevisions.sRepository IS
+ 'The version control tree name.';
+
+COMMENT ON COLUMN VcsRevisions.iRevision IS
+ 'The version control tree revision number.';
+
+COMMENT ON COLUMN VcsRevisions.tsCreated IS
+ 'When the revision was created (committed).';
+
+COMMENT ON COLUMN VcsRevisions.sAuthor IS
+ 'The name of the committer.
+@note Not to be confused with uidAuthor and test manager users.';
+
+COMMENT ON COLUMN VcsRevisions.sMessage IS
+ 'The commit message.';
+
+COMMENT ON TABLE TestResultStrTab IS
+ 'String table for the test results.
+
+This is a string cache for value names, test names and possible more, that
+is frequently repated in the test results record for each test run. The
+purpose is not only to save space, but to make datamining queries faster by
+giving them integer fields to work on instead of text fields. There may
+possibly be some benefits on INSERT as well as there are only integer
+indexes.
+
+Nothing is ever deleted from this table.
+
+@note Should use a stored procedure to query/insert a string.';
+
+COMMENT ON COLUMN TestResultStrTab.sValue IS
+ 'The string value.';
+
+COMMENT ON COLUMN TestResultStrTab.tsCreated IS
+ 'Creation time stamp.';
+
+COMMENT ON TYPE TestStatus_T IS
+ 'The status of a test (set / result).';
+
+COMMENT ON TABLE TestResults IS
+ 'Test results - a recursive bundle of joy!
+
+A test case will be created when the testdriver calls reporter.testStart and
+concluded with reporter.testDone. The testdriver (or it subordinates) can
+use these methods to create nested test results. For IPRT based test cases,
+RTTestCreate, RTTestInitAndCreate and RTTestSub will both create new test
+result records, where as RTTestSubDone, RTTestSummaryAndDestroy and
+RTTestDestroy will conclude records.
+
+By concluding is meant updating the status. When the test driver reports
+success, we check it against reported results. (paranoia strikes again!)
+
+Nothing is ever deleted from this table.
+
+@note As seen below, several other tables associate data with a
+ test result, and the top most test result is referenced by the
+ test set.';
+
+COMMENT ON COLUMN TestResults.tsCreated IS
+ 'Creation time stamp. This may also be the timestamp of when the test started.';
+
+COMMENT ON COLUMN TestResults.tsElapsed IS
+ 'The elapsed time for this test.
+This is either reported by the directly (with some sanity checking) or
+calculated (current_timestamp - created_ts).
+@todo maybe use a nanosecond field here, check with what';
+
+COMMENT ON COLUMN TestResults.cErrors IS
+ 'The error count.';
+
+COMMENT ON COLUMN TestResults.enmStatus IS
+ 'The test status.';
+
+COMMENT ON COLUMN TestResults.iNestingDepth IS
+ 'Nesting depth.';
+
+COMMENT ON TABLE TestResultValues IS
+ 'Test result values.
+
+A testdriver or subordinate may report a test value via
+reporter.testValue(), while IPRT based test will use RTTestValue and
+associates.
+
+This is an insert only table, no deletes, no updates.';
+
+COMMENT ON COLUMN TestResultValues.tsCreated IS
+ 'Creation time stamp.';
+
+COMMENT ON COLUMN TestResultValues.lValue IS
+ 'The value.';
+
+COMMENT ON COLUMN TestResultValues.iUnit IS
+ 'The unit.
+@todo This is currently not defined properly. Will fix/correlate this
+ with the other places we use unit (IPRT/testdriver/VMMDev).';
+
+COMMENT ON TABLE TestResultFiles IS
+ 'Test result files.
+
+A testdriver or subordinate may report a file by using
+reporter.addFile() or reporter.addLogFile().
+
+The files stored here as well as the primary log file will be processed by a
+batch job and compressed if considered compressable. Thus, TM will look for
+files with a .gz/.bz2 suffix first and then without a suffix.
+
+This is an insert only table, no deletes, no updates.';
+
+COMMENT ON COLUMN TestResultFiles.tsCreated IS
+ 'Creation time stamp.';
+
+COMMENT ON INDEX TestResultFilesIdx IS
+ 'The mime type for the file.
+For instance: ''text/plain'',
+ ''image/png'',
+ ''video/webm'',
+ ''text/xml''';
+
+COMMENT ON TABLE TestResultMsgs IS
+ 'Test result message.
+
+A testdriver or subordinate may report a message via the sDetails parameter
+of the reporter.testFailure() method, while IPRT test cases will use
+RTTestFailed, RTTestPrintf and their friends. For RTTestPrintf, we will
+ignore the more verbose message levels since these can also be found in one
+of the logs.
+
+This is an insert only table, no deletes, no updates.';
+
+COMMENT ON COLUMN TestResultMsgs.tsCreated IS
+ 'Creation time stamp.';
+
+COMMENT ON COLUMN TestResultMsgs.enmLevel IS
+ 'The message level.';
+
+COMMENT ON TABLE TestSets IS
+ 'Test sets / Test case runs.
+
+This is where we collect data about test runs.
+
+@todo Not entirely sure where the ''test set'' term came from. Consider
+ finding something more appropriate.';
+
+COMMENT ON COLUMN TestSets.tsConfig IS
+ 'The test config timestamp, used when reading test config.';
+
+COMMENT ON COLUMN TestSets.tsCreated IS
+ 'When this test set was scheduled.
+idGenTestBox is valid at this point.';
+
+COMMENT ON COLUMN TestSets.tsDone IS
+ 'When this test completed, i.e. testing stopped. This should only be set once.';
+
+COMMENT ON COLUMN TestSets.enmStatus IS
+ 'The current status.';
+
+COMMENT ON COLUMN TestSets.sBaseFilename IS
+ 'The base filename used for storing files related to this test set.
+This is a path relative to wherever TM is dumping log files. In order
+to not become a file system test case, we will try not to put too many
+hundred thousand files in a directory. A simple first approach would
+be to just use the current date (tsCreated) like this:
+ TM_FILE_DIR/year/month/day/TestSets.idTestSet
+
+The primary log file for the test is this name suffixed by ''.log''.
+
+The files in the testresultfile table gets their full names like this:
+ TM_FILE_DIR/sBaseFilename-testresultfile.id-TestResultStrTab(testresultfile.idStrFilename)
+
+@remarks We store this explicitly in case we change the directly layout
+ at some later point.';
+
+COMMENT ON COLUMN TestSets.iGangMemberNo IS
+ 'The gang member number number, 0 is the leader.';
+
+COMMENT ON INDEX TestSetsGangIdx IS
+ 'The test set of the gang leader, NULL if no gang involved.
+@note This is set by the gang leader as well, so that we can find all
+ gang members by WHERE idTestSetGangLeader = :id.';
+
+COMMENT ON INDEX TestSetsDoneCreatedBuildCatIdx IS
+ 'The TestSetsDoneCreatedBuildCatIdx is for testbox results, graph options and such.';
+
+COMMENT ON INDEX TestSetsGraphBoxIdx IS
+ 'For graphs.';
+
+COMMENT ON TYPE TestBoxState_T IS
+ 'TestBox state.
+
+@todo Consider drawing a state diagram for this.';
+
+COMMENT ON TABLE TestBoxStatuses IS
+ 'Testbox status table.
+
+History is not planned on this table.';
+
+COMMENT ON COLUMN TestBoxStatuses.tsUpdated IS
+ 'When this status was last updated.
+This is updated everytime the testbox talks to the test manager, thus it
+can easily be used to find testboxes which has stopped responding.
+
+This is used for timeout calculation during gang-gathering, so in that
+scenario it won''t be updated until the gang is gathered or we time out.';
+
+COMMENT ON COLUMN TestBoxStatuses.enmState IS
+ 'The current state.';
+
+COMMENT ON COLUMN TestBoxStatuses.iWorkItem IS
+ 'Interal work item number.
+This is used to pick and prioritize between multiple scheduling groups.';
+
+COMMENT ON TABLE GlobalResourceStatuses IS
+ 'Global resource status, tracks which test set resources are allocated by.
+
+History is not planned on this table.';
+
+COMMENT ON COLUMN GlobalResourceStatuses.tsAllocated IS
+ 'When the allocation took place.';
+
+COMMENT ON TABLE SchedQueues IS
+ 'Scheduler queue.
+
+The queues are currently associated with a scheduling group, it could
+alternative be changed to hook on to a testbox instead. It depends on what
+kind of scheduling method we prefer. The former method aims at test case
+thruput, making sacrifices in the hardware distribution area. The latter is
+more like the old buildbox style testing, making sure that each test case is
+executed on each testbox.
+
+When there are configuration changes, TM will regenerate the scheduling
+queue for the affected scheduling groups. We do not concern ourselves with
+trying to continue at the approximately same queue position, we simply take
+it from the top.
+
+When a testbox ask for work, we will open a cursor on the queue and take the
+first test in the queue that can be executed on that testbox. The test will
+be moved to the end of the queue (getting a new item_id).
+
+If a test is manually changed to the head of the queue, the item will get a
+item_id which is 1 lower than the head of the queue. Unless someone does
+this a couple of billion times, we shouldn''t have any trouble running out of
+number space. :-)
+
+Manually moving a test to the end of the queue is easy, just get a new
+''item_id''.
+
+History is not planned on this table.';
+
+COMMENT ON COLUMN SchedQueues.bmHourlySchedule IS
+ 'The scheduling time constraints (see SchedGroupMembers.bmHourlySchedule).';
+
+COMMENT ON COLUMN SchedQueues.tsConfig IS
+ 'When the queue entry was created and for which config is valid.
+This is the timestamp that should be used when reading config info.';
+
+COMMENT ON COLUMN SchedQueues.tsLastScheduled IS
+ 'When this status was last scheduled.
+This is set to current_timestamp when moving the entry to the end of the
+queue. It''s initial value is unix-epoch. Not entirely sure if it''s
+useful beyond introspection and non-unique foreign key hacking.';
+
+COMMENT ON COLUMN SchedQueues.cMissingGangMembers IS
+ 'The number of gang members still missing.
+
+This saves calculating the number of missing members via selects like:
+ SELECT COUNT(*) FROM TestSets WHERE idTestSetGangLeader = :idGang;
+and
+ SELECT cGangMembers FROM TestCaseArgs WHERE idGenTestCaseArgs = :idTest;
+to figure out whether to remain in ''gather-gang''::TestBoxState_T.';
+
+COMMENT ON INDEX SchedQueuesItemIdx IS
+ 'The number of times this has been considered for scheduling.
+cConsidered SMALLINT DEFAULT 0 NOT NULL,';
+
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseDefaultUserAccounts.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseDefaultUserAccounts.pgsql
new file mode 100644
index 00000000..41198b4f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseDefaultUserAccounts.pgsql
@@ -0,0 +1,43 @@
+-- $Id: TestManagerDatabaseDefaultUserAccounts.pgsql $
+--- @file
+-- VBox Test Manager default user account records creation script.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+-- Add record for user 'admin'
+INSERT INTO Users (sUsername, sEmail, sFullName, sLoginName)
+ VALUES ('root', 'admin@example.org', 'Administrator', 'admin');
+
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks.pgsql
new file mode 100644
index 00000000..1430169c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks.pgsql
@@ -0,0 +1,90 @@
+-- $Id: TestManagerDatabaseForeignKeyErHacks.pgsql $
+--- @file
+-- VBox Test Manager Database Addendum that adds non-unique foreign keys.
+--
+-- This is for getting better visualization in reverse engeering ER tools,
+-- it is not for production databases.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager
+
+ALTER TABLE TestCaseArgs
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idTestCase, tsExpire) REFERENCES TestCases(idTestCase, tsExpire) MATCH FULL;
+
+ALTER TABLE TestcaseDeps
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idTestCase, tsExpire) REFERENCES TestCases(idTestCase, tsExpire) MATCH FULL;
+ALTER TABLE TestcaseDeps
+ ADD CONSTRAINT non_unique_fk2 FOREIGN KEY (idTestCasePreReq,tsExpire) REFERENCES TestCases(idTestCase, tsExpire) MATCH FULL;
+
+ALTER TABLE TestCaseGlobalRsrcDeps
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idTestCase, tsExpire) REFERENCES TestCases(idTestCase, tsExpire) MATCH FULL;
+ALTER TABLE TestCaseGlobalRsrcDeps
+ ADD CONSTRAINT non_unique_fk2 FOREIGN KEY (idGlobalRsrc, tsExpire) REFERENCES GlobalResources(idGlobalRsrc, tsExpire) MATCH FULL;
+
+ALTER TABLE TestGroupMembers
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idTestGroup, tsExpire) REFERENCES TestGroups(idTestGroup, tsExpire) MATCH FULL;
+ALTER TABLE TestGroupMembers
+ ADD CONSTRAINT non_unique_fk2 FOREIGN KEY (idTestCase, tsExpire) REFERENCES TestCases(idTestCase, tsExpire) MATCH FULL;
+
+ALTER TABLE SchedGroups
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idBuildSrc, tsExpire) REFERENCES BuildSources(idBuildSrc, tsExpire) MATCH SIMPLE;
+ALTER TABLE SchedGroups
+ ADD CONSTRAINT non_unique_fk2 FOREIGN KEY (idBuildSrcTestSuite, tsExpire) REFERENCES BuildSources(idBuildSrc, tsExpire) MATCH SIMPLE;
+
+ALTER TABLE SchedGroupMembers
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idSchedGroup, tsExpire) REFERENCES SchedGroups(idSchedGroup, tsExpire) MATCH FULL;
+ALTER TABLE SchedGroupMembers
+ ADD CONSTRAINT non_unique_fk2 FOREIGN KEY (idTestGroup, tsExpire) REFERENCES TestGroups(idTestGroup, tsExpire) MATCH FULL;
+ALTER TABLE SchedGroupMembers
+ ADD CONSTRAINT non_unique_fk3 FOREIGN KEY (idTestGroupPreReq, tsExpire) REFERENCES TestGroups(idTestGroup, tsExpire) MATCH FULL;
+
+ALTER TABLE TestBoxes
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idSchedGroup, tsExpire) REFERENCES SchedGroups(idSchedGroup, tsExpire) MATCH FULL;
+
+ALTER TABLE FailureReasons
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idFailureCategory, tsExpire) REFERENCES FailureCategories(idFailureCategory, tsExpire) MATCH FULL;
+
+ALTER TABLE TestResultFailures
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idFailureReason, tsExpire) REFERENCES FailureReasons(idFailureReason, tsExpire) MATCH FULL;
+
+ALTER TABLE BuildBlacklist
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idFailureReason, tsExpire) REFERENCES FailureReasons(idFailureReason, tsExpire) MATCH FULL;
+
+ALTER TABLE GlobalResourceStatuses
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idGlobalRsrc, tsAllocated) REFERENCES GlobalResources(idGlobalRsrc, tsExpire) MATCH FULL;
+
+ALTER TABLE SchedQueues
+ ADD CONSTRAINT non_unique_fk1 FOREIGN KEY (idSchedGroup, tsLastScheduled) REFERENCES SchedGroups(idSchedGroup, tsExpire) MATCH FULL;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks2.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks2.pgsql
new file mode 100644
index 00000000..12686d81
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseForeignKeyErHacks2.pgsql
@@ -0,0 +1,77 @@
+-- $Id: TestManagerDatabaseForeignKeyErHacks2.pgsql $
+--- @file
+-- VBox Test Manager Database Addendum that adds non-unique foreign keys to Users.
+--
+-- This is for getting better visualization in reverse engeering ER tools,
+-- it is not for production databases.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\set ON_ERROR_STOP 1
+\connect testmanager
+
+ALTER TABLE GlobalResources
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE BuildSources
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE RequirementSets
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsCreated) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestCases
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestCaseArgs
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestcaseDeps
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestCaseGlobalRsrcDeps
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestGroups
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestGroupMembers
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE SchedGroups
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH SIMPLE;
+ALTER TABLE SchedGroupMembers
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestBoxes
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE FailureCategories
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE FailureReasons
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE TestResultFailures
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE BuildBlacklist
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsExpire) REFERENCES Users(uid, tsExpire) MATCH FULL;
+ALTER TABLE Builds
+ ADD CONSTRAINT non_unique_fk9 FOREIGN KEY (uidAuthor, tsCreated) REFERENCES Users(uid, tsExpire) MATCH FULL;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql
new file mode 100644
index 00000000..10e93ff2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql
@@ -0,0 +1,1950 @@
+-- $Id: TestManagerDatabaseInit.pgsql $
+--- @file
+-- VBox Test Manager Database Creation script.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Declaimer:
+--
+-- The guys working on this design are not database experts, web
+-- programming experts or similar, rather we are low level guys
+-- who's main job is x86 & AMD64 virtualization. So, please don't
+-- be too hard on us. :-)
+--
+--
+
+
+-- D R O P D A T A B A S E t e s t m a n a g e r - - you do this now.
+\set ON_ERROR_STOP 1
+CREATE DATABASE testmanager;
+\connect testmanager;
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- S y s t e m
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+---
+-- Log table for a few important events.
+--
+-- Currently, two events are planned to be logged:
+-- - Sign on of an unknown testbox, including the IP and System UUID.
+-- This will be restricted to one entry per 24h or something like that:
+-- SELECT COUNT(*)
+-- FROM SystemLog
+-- WHERE tsCreated >= (current_timestamp - interval '24 hours')
+-- AND sEvent = 'TBoxUnkn'
+-- AND sLogText = :sNewLogText;
+-- - When cleaning up an abandoned testcase (scenario #9), log which
+-- testbox abandoned which testset.
+--
+-- The Web UI will have some way of displaying the log.
+--
+-- A batch job should regularly clean out old log messages, like for instance
+-- > 64 days.
+--
+CREATE TABLE SystemLog (
+ --- When this was logged.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The event type.
+ -- This is a 8 character string identifier so that we don't need to change
+ -- some enum type everytime we introduce a new event type.
+ sEvent CHAR(8) NOT NULL,
+ --- The log text.
+ sLogText text NOT NULL,
+
+ PRIMARY KEY (tsCreated, sEvent)
+);
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- C o n f i g u r a t i o n
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+--- @table Users
+-- Test manager users.
+--
+-- This is mainly for doing simple access checks before permitting access to
+-- the test manager. This needs to be coordinated with
+-- apache/ldap/Oracle-Single-Sign-On.
+--
+-- The main purpose, though, is for tracing who changed the test config and
+-- analysis data.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp.
+--
+CREATE SEQUENCE UserIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE Users (
+ --- The user id.
+ uid INTEGER DEFAULT NEXTVAL('UserIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER DEFAULT NULL,
+ --- User name.
+ sUsername text NOT NULL,
+ --- The email address of the user.
+ sEmail text NOT NULL,
+ --- The full name.
+ sFullName text NOT NULL,
+ --- The login name used by apache.
+ sLoginName text NOT NULL,
+ --- Read access only.
+ fReadOnly BOOLEAN NOT NULL DEFAULT FALSE,
+
+ PRIMARY KEY (uid, tsExpire)
+);
+CREATE INDEX UsersLoginNameIdx ON Users (sLoginName, tsExpire DESC);
+
+
+--- @table GlobalResources
+-- Global resource configuration.
+--
+-- For example an iSCSI target.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp.
+--
+CREATE SEQUENCE GlobalResourceIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE GlobalResources (
+ --- The global resource ID.
+ -- This stays the same thru updates.
+ idGlobalRsrc INTEGER DEFAULT NEXTVAL('GlobalResourceIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- The name of the resource.
+ sName text NOT NULL,
+ --- Optional resource description.
+ sDescription text,
+ --- Indicates whether this resource is currently enabled (online).
+ fEnabled boolean DEFAULT FALSE NOT NULL,
+
+ PRIMARY KEY (idGlobalRsrc, tsExpire)
+);
+
+
+--- @table BuildSources
+-- Build sources.
+--
+-- This is used by a scheduling group to select builds and the default
+-- Validation Kit from the Builds table.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp.
+--
+-- @todo Any better way of representing this so we could more easily
+-- join/whatever when searching for builds?
+--
+CREATE SEQUENCE BuildSourceIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE BuildSources (
+ --- The build source identifier.
+ -- This stays constant over time.
+ idBuildSrc INTEGER DEFAULT NEXTVAL('BuildSourceIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The name of the build source.
+ sName TEXT NOT NULL,
+ --- Description.
+ sDescription TEXT DEFAULT NULL,
+
+ --- Which product.
+ -- ASSUME that it is okay to limit a build source to a single product.
+ sProduct text NOT NULL,
+ --- Which branch.
+ -- ASSUME that it is okay to limit a build source to a branch.
+ sBranch text NOT NULL,
+
+ --- Build types to include, all matches if NULL.
+ -- @todo Weighting the types would be nice in a later version.
+ asTypes text ARRAY DEFAULT NULL,
+ --- Array of the 'sOs.sCpuArch' to match, all matches if NULL.
+ -- See KBUILD_OSES in kBuild for a list of standard target OSes, and
+ -- KBUILD_ARCHES for a list of standard architectures.
+ --
+ -- @remarks See marks on 'os-agnostic' and 'noarch' in BuildCategories.
+ asOsArches text ARRAY DEFAULT NULL,
+
+ --- The first subversion tree revision to match, no lower limit if NULL.
+ iFirstRevision INTEGER DEFAULT NULL,
+ --- The last subversion tree revision to match, no upper limit if NULL.
+ iLastRevision INTEGER DEFAULT NULL,
+
+ --- The maximum age of the builds in seconds, unlimited if NULL.
+ cSecMaxAge INTEGER DEFAULT NULL,
+
+ PRIMARY KEY (idBuildSrc, tsExpire)
+);
+
+
+--- @table TestCases
+-- Test case configuration.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp.
+--
+CREATE SEQUENCE TestCaseIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE SEQUENCE TestCaseGenIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestCases (
+ --- The fixed test case ID.
+ -- This is assigned when the test case is created and will never change.
+ idTestCase INTEGER DEFAULT NEXTVAL('TestCaseIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- Generation ID for this row, a truly unique identifier.
+ -- This is primarily for referencing by TestSets.
+ idGenTestCase INTEGER UNIQUE DEFAULT NEXTVAL('TestCaseGenIdSeq') NOT NULL,
+
+ --- The name of the test case.
+ sName TEXT NOT NULL,
+ --- Optional test case description.
+ sDescription TEXT DEFAULT NULL,
+ --- Indicates whether this test case is currently enabled.
+ fEnabled BOOLEAN DEFAULT FALSE NOT NULL,
+ --- Default test case timeout given in seconds.
+ cSecTimeout INTEGER NOT NULL CHECK (cSecTimeout > 0),
+ --- Default TestBox requirement expression (python boolean expression).
+ -- All the scheduler properties are available for use with the same names
+ -- as in that table.
+ -- If NULL everything matches.
+ sTestBoxReqExpr TEXT DEFAULT NULL,
+ --- Default build requirement expression (python boolean expression).
+ -- The following build properties are available: sProduct, sBranch,
+ -- sType, asOsArches, sVersion, iRevision, uidAuthor and idBuild.
+ -- If NULL everything matches.
+ sBuildReqExpr TEXT DEFAULT NULL,
+
+ --- The base command.
+ -- String suitable for executing in bourne shell with space as separator
+ -- (IFS). References to @BUILD_BINARIES@ will be replaced WITH the content
+ -- of the Builds(sBinaries) field.
+ sBaseCmd TEXT NOT NULL,
+
+ --- Comma separated list of test suite zips (or tars) that the testbox will
+ -- need to download and expand prior to testing.
+ -- If NULL the current test suite of the scheduling group will be used (the
+ -- scheduling group will have an optional test suite build queue associated
+ -- with it). The current test suite can also be referenced by
+ -- @VALIDATIONKIT_ZIP@ in case more downloads are required. Files may also be
+ -- uploaded to the test manager download area, in which case the
+ -- @DOWNLOAD_BASE_URL@ prefix can be used to refer to this area.
+ sTestSuiteZips TEXT DEFAULT NULL,
+
+ -- Comment regarding a change or something.
+ sComment TEXT DEFAULT NULL,
+
+ PRIMARY KEY (idTestCase, tsExpire)
+);
+
+
+--- @table TestCaseArgs
+-- Test case argument list variations.
+--
+-- For example, we have a test case that does a set of tests on a virtual
+-- machine. To get better code/feature coverage of this testcase we wish to
+-- run it with different guest hardware configuration. The test case may do
+-- the same stuff, but the guest OS as well as the VMM may react differently to
+-- the hardware configurations and uncover issues in the VMM, device emulation
+-- or other places.
+--
+-- Typical hardware variations are:
+-- - guest memory size (RAM),
+-- - guest video memory size (VRAM),
+-- - virtual CPUs / cores / threads,
+-- - virtual chipset
+-- - virtual network interface card (NIC)
+-- - USB 1.1, USB 2.0, no USB
+--
+-- The TM web UI will help the user create a reasonable set of permutations
+-- of these parameters, the user specifies a maximum and the TM uses certain
+-- rules together with random selection to generate the desired number. The
+-- UI will also help suggest fitting testbox requirements according to the
+-- RAM/VRAM sizes and the virtual CPU counts. The user may then make
+-- adjustments to the suggestions before commit them.
+--
+-- Alternatively, the user may also enter all the permutations without any
+-- help from the UI.
+--
+-- Note! All test cases has at least one entry in this table, even if it is
+-- empty, because testbox requirements are specified thru this.
+--
+-- Querying the valid parameter lists for a testase this way:
+-- SELECT * ... WHERE idTestCase = TestCases.idTestCase
+-- AND tsExpire > <when>
+-- AND tsEffective <= <when>;
+--
+-- Querying the valid parameter list for the latest generation can be
+-- simplified by just checking tsExpire date:
+-- SELECT * ... WHERE idTestCase = TestCases.idTestCase
+-- AND tsExpire == TIMESTAMP WITH TIME ZONE 'infinity';
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp.
+--
+CREATE SEQUENCE TestCaseArgsIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE SEQUENCE TestCaseArgsGenIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestCaseArgs (
+ --- The test case ID.
+ -- Non-unique foreign key: TestCases(idTestCase).
+ idTestCase INTEGER NOT NULL,
+ --- The testcase argument variation ID (fixed).
+ -- This is primarily for TestGroupMembers.aidTestCaseArgs.
+ idTestCaseArgs INTEGER DEFAULT NEXTVAL('TestCaseArgsIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- Generation ID for this row.
+ -- This is primarily for efficient referencing by TestSets and SchedQueues.
+ idGenTestCaseArgs INTEGER UNIQUE DEFAULT NEXTVAL('TestCaseArgsGenIdSeq') NOT NULL,
+
+ --- The additional arguments.
+ -- String suitable for bourne shell style argument parsing with space as
+ -- separator (IFS). References to @BUILD_BINARIES@ will be replaced with
+ -- the content of the Builds(sBinaries) field.
+ sArgs TEXT NOT NULL,
+ --- Optional test case timeout given in seconds.
+ -- If NULL, the TestCases.cSecTimeout field is used instead.
+ cSecTimeout INTEGER DEFAULT NULL CHECK (cSecTimeout IS NULL OR cSecTimeout > 0),
+ --- Additional TestBox requirement expression (python boolean expression).
+ -- All the scheduler properties are available for use with the same names
+ -- as in that table. This is checked after first checking the requirements
+ -- in the TestCases.sTestBoxReqExpr field.
+ sTestBoxReqExpr TEXT DEFAULT NULL,
+ --- Additional build requirement expression (python boolean expression).
+ -- The following build properties are available: sProduct, sBranch,
+ -- sType, asOsArches, sVersion, iRevision, uidAuthor and idBuild. This is
+ -- checked after first checking the requirements in the
+ -- TestCases.sBuildReqExpr field.
+ sBuildReqExpr TEXT DEFAULT NULL,
+ --- Number of testboxes required (gang scheduling).
+ cGangMembers SMALLINT DEFAULT 1 NOT NULL CHECK (cGangMembers > 0 AND cGangMembers < 1024),
+ --- Optional variation sub-name.
+ sSubName TEXT DEFAULT NULL,
+
+ --- The arguments are part of the primary key for several reasons.
+ -- No duplicate argument lists (makes no sense - if you want to prioritize
+ -- argument lists, we add that explicitly). This may hopefully enable us
+ -- to more easily check coverage later on, even when the test case is
+ -- reconfigured with more/less permutations.
+ PRIMARY KEY (idTestCase, tsExpire, sArgs)
+);
+CREATE INDEX TestCaseArgsLookupIdx ON TestCaseArgs (idTestCase, tsExpire DESC, tsEffective ASC);
+
+
+--- @table TestCaseDeps
+-- Test case dependencies (N:M)
+--
+-- This effect build selection. The build must have passed all runs of the
+-- given prerequisite testcase (idTestCasePreReq) and executed at a minimum one
+-- argument list variation.
+--
+-- This should also affect scheduling order, if possible at least one
+-- prerequisite testcase variation should be place before the specific testcase
+-- in the scheduling queue.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE TestCaseDeps (
+ --- The test case that depends on someone.
+ -- Non-unique foreign key: TestCases(idTestCase).
+ idTestCase INTEGER NOT NULL,
+ --- The prerequisite test case ID.
+ -- Non-unique foreign key: TestCases(idTestCase).
+ idTestCasePreReq INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ PRIMARY KEY (idTestCase, idTestCasePreReq, tsExpire)
+);
+
+
+--- @table TestCaseGlobalRsrcDeps
+-- Test case dependencies on global resources (N:M)
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE TestCaseGlobalRsrcDeps (
+ --- The test case that depends on someone.
+ -- Non-unique foreign key: TestCases(idTestCase).
+ idTestCase INTEGER NOT NULL,
+ --- The prerequisite resource ID.
+ -- Non-unique foreign key: GlobalResources(idGlobalRsrc).
+ idGlobalRsrc INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ PRIMARY KEY (idTestCase, idGlobalRsrc, tsExpire)
+);
+
+
+--- @table TestGroups
+-- Test Group - A collection of test cases.
+--
+-- This is for simplifying test configuration by working with a few groups
+-- instead of a herd of individual testcases. It may also be used for creating
+-- test suites for certain areas (like guest additions) or tasks (like
+-- performance measurements).
+--
+-- A test case can be member of any number of test groups.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE SEQUENCE TestGroupIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestGroups (
+ --- The fixed scheduling group ID.
+ -- This is assigned when the group is created and will never change.
+ idTestGroup INTEGER DEFAULT NEXTVAL('TestGroupIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The name of the scheduling group.
+ sName TEXT NOT NULL,
+ --- Optional group description.
+ sDescription TEXT,
+ -- Comment regarding a change or something.
+ sComment TEXT DEFAULT NULL,
+
+ PRIMARY KEY (idTestGroup, tsExpire)
+);
+CREATE INDEX TestGroups_id_index ON TestGroups (idTestGroup, tsExpire DESC, tsEffective ASC);
+
+
+--- @table TestGroupMembers
+-- The N:M relationship between test case configurations and test groups.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE TestGroupMembers (
+ --- The group ID.
+ -- Non-unique foreign key: TestGroups(idTestGroup).
+ idTestGroup INTEGER NOT NULL,
+ --- The test case ID.
+ -- Non-unique foreign key: TestCases(idTestCase).
+ idTestCase INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- Test case scheduling priority.
+ -- Higher number causes the test case to be run more frequently.
+ -- @sa SchedGroupMembers.iSchedPriority, TestBoxesInSchedGroups.iSchedPriority
+ -- @todo Not sure we want to keep this...
+ iSchedPriority INTEGER DEFAULT 16 CHECK (iSchedPriority >= 0 AND iSchedPriority < 32) NOT NULL,
+
+ --- Limit the memberships to the given argument variations.
+ -- Non-unique foreign key: TestCaseArgs(idTestCase, idTestCaseArgs).
+ aidTestCaseArgs INTEGER ARRAY DEFAULT NULL,
+
+ PRIMARY KEY (idTestGroup, idTestCase, tsExpire)
+);
+
+
+--- @table SchedGroups
+-- Scheduling group (aka. testbox partitioning) configuration.
+--
+-- A testbox is associated with exactly one scheduling group. This association
+-- can be changed, of course. If we (want to) retire a group which still has
+-- testboxes associated with it, these will be moved to the 'default' group.
+--
+-- The TM web UI will make sure that a testbox is always in a group and that
+-- the default group cannot be deleted.
+--
+-- A scheduling group combines several things:
+-- - A selection of builds to test (via idBuildSrc).
+-- - A collection of test groups to test with (via SchedGroupMembers).
+-- - A set of testboxes to test on (via TestBoxes.idSchedGroup).
+--
+-- In additions there is an optional source of fresh test suite builds (think
+-- VBoxTestSuite) as well as scheduling options.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TYPE Scheduler_T AS ENUM (
+ 'bestEffortContinousItegration',
+ 'reserved'
+);
+CREATE SEQUENCE SchedGroupIdSeq
+ START 2
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE SchedGroups (
+ --- The fixed scheduling group ID.
+ -- This is assigned when the group is created and will never change.
+ idSchedGroup INTEGER DEFAULT NEXTVAL('SchedGroupIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ -- @note This is NULL for the default group.
+ uidAuthor INTEGER DEFAULT NULL,
+
+ --- The name of the scheduling group.
+ sName TEXT NOT NULL,
+ --- Optional group description.
+ sDescription TEXT,
+ --- Indicates whether this group is currently enabled.
+ fEnabled boolean NOT NULL,
+ --- The scheduler to use.
+ -- This is for when we later desire different scheduling that the best
+ -- effort stuff provided by the initial implementation.
+ enmScheduler Scheduler_T DEFAULT 'bestEffortContinousItegration'::Scheduler_T NOT NULL,
+ --- The build source.
+ -- Non-unique foreign key: BuildSources(idBuildSrc)
+ idBuildSrc INTEGER DEFAULT NULL,
+ --- The Validation Kit build source (@VALIDATIONKIT_ZIP@).
+ -- Non-unique foreign key: BuildSources(idBuildSrc)
+ idBuildSrcTestSuite INTEGER DEFAULT NULL,
+ -- Comment regarding a change or something.
+ sComment TEXT DEFAULT NULL,
+
+ PRIMARY KEY (idSchedGroup, tsExpire)
+);
+
+-- Special default group.
+INSERT INTO SchedGroups (idSchedGroup, tsEffective, tsExpire, sName, sDescription, fEnabled)
+ VALUES (1, TIMESTAMP WITH TIME ZONE 'epoch', TIMESTAMP WITH TIME ZONE 'infinity', 'default', 'default group', FALSE);
+
+
+--- @table SchedGroupMembers
+-- N:M relationship between scheduling groups and test groups.
+--
+-- Several scheduling parameters are associated with this relationship.
+--
+-- The test group dependency (idTestGroupPreReq) can be used in the same way as
+-- TestCaseDeps.idTestCasePreReq, only here on test group level. This means it
+-- affects the build selection. The builds needs to have passed all test runs
+-- the prerequisite test group and done at least one argument variation of each
+-- test case in it.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE SchedGroupMembers (
+ --- Scheduling ID.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup).
+ idSchedGroup INTEGER NOT NULL,
+ --- Testgroup ID.
+ -- Non-unique foreign key: TestGroups(idTestGroup).
+ idTestGroup INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The scheduling priority of the test group.
+ -- Higher number causes the test case to be run more frequently.
+ -- @sa TestGroupMembers.iSchedPriority, TestBoxesInSchedGroups.iSchedPriority
+ iSchedPriority INTEGER DEFAULT 16 CHECK (iSchedPriority >= 0 AND iSchedPriority < 32) NOT NULL,
+ --- When during the week this group is allowed to start running, NULL means
+ -- there are no constraints.
+ -- Each bit in the bitstring represents one hour, with bit 0 indicating the
+ -- midnight hour on a monday.
+ bmHourlySchedule bit(168) DEFAULT NULL,
+ --- Optional test group dependency.
+ -- Non-unique foreign key: TestGroups(idTestGroup).
+ -- This is for requiring that a build has been subject to smoke tests
+ -- before bothering to subject it to longer tests.
+ -- @todo Not entirely sure this should be here, but I'm not so keen on yet
+ -- another table as the only use case is smoketests.
+ idTestGroupPreReq INTEGER DEFAULT NULL,
+
+ PRIMARY KEY (idSchedGroup, idTestGroup, tsExpire)
+);
+
+
+--- @table TestBoxStrTab
+-- String table for the test boxes.
+--
+-- This is a string cache for all string members in TestBoxes except the name.
+-- The rational is to avoid duplicating large strings like sReport when the
+-- testbox reports a new cMbScratch value or the box when the test sheriff
+-- sends a reboot command or similar.
+--
+-- At the time this table was introduced, we had 400558 TestBoxes rows, where
+-- the SUM(LENGTH(sReport)) was 993MB. There were really just 1066 distinct
+-- sReport values, with a total length of 0x3 MB.
+--
+-- Nothing is ever deleted from this table.
+--
+-- @note Should use a stored procedure to query/insert a string.
+--
+--
+-- TestBox stats prior to conversion:
+-- SELECT COUNT(*) FROM TestBoxes: 400558 rows
+-- SELECT pg_total_relation_size('TestBoxes'): 740794368 bytes (706 MB)
+-- Average row cost: 740794368 / 400558 = 1849 bytes/row
+--
+-- After conversion:
+-- SELECT COUNT(*) FROM TestBoxes: 400558 rows
+-- SELECT pg_total_relation_size('TestBoxes'): 144375808 bytes (138 MB)
+-- SELECT COUNT(idStr) FROM TestBoxStrTab: 1292 rows
+-- SELECT pg_total_relation_size('TestBoxStrTab'): 5709824 bytes (5.5 MB)
+-- (144375808 + 5709824) / 740794368 = 20 %
+-- Average row cost boxes: 144375808 / 400558 = 360 bytes/row
+-- Average row cost strings: 5709824 / 1292 = 4420 bytes/row
+--
+CREATE SEQUENCE TestBoxStrTabIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestBoxStrTab (
+ --- The ID of this string.
+ idStr INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestBoxStrTabIdSeq'),
+ --- The string value.
+ sValue text NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL
+);
+-- Note! Must use hash index as the sReport strings are too long for regular indexing.
+CREATE INDEX TestBoxStrTabNameIdx ON TestBoxStrTab USING hash (sValue);
+
+--- Empty string with ID 0.
+INSERT INTO TestBoxStrTab (idStr, sValue) VALUES (0, '');
+
+
+--- @type TestBoxCmd_T
+-- Testbox commands.
+CREATE TYPE TestBoxCmd_T AS ENUM (
+ 'none',
+ 'abort',
+ 'reboot', --< This implies abort. Status changes when reaching 'idle'.
+ 'upgrade', --< This is only handled when asking for work.
+ 'upgrade-and-reboot', --< Ditto.
+ 'special' --< Similar to upgrade, reserved for the future.
+);
+
+
+--- @type LomKind_T
+-- The kind of lights out management on a testbox.
+CREATE TYPE LomKind_T AS ENUM (
+ 'none',
+ 'ilom',
+ 'elom',
+ 'apple-xserve-lom'
+);
+
+
+--- @table TestBoxes
+-- Testbox configurations.
+--
+-- The testboxes are identified by IP and the system UUID if available. Should
+-- the IP change, the testbox will be refused at sign on and the testbox
+-- sheriff will have to update it's IP.
+--
+-- @todo Implement the UUID stuff. Get it from DMI, UEFI or whereever.
+-- Mismatching needs to be logged somewhere...
+--
+-- To query the currently valid configuration:
+-- SELECT ... WHERE id = idTestBox AND tsExpire = TIMESTAMP WITH TIME ZONE 'infinity';
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE SEQUENCE TestBoxIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE SEQUENCE TestBoxGenIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestBoxes (
+ --- The fixed testbox ID.
+ -- This is assigned when the testbox is created and will never change.
+ idTestBox INTEGER DEFAULT NEXTVAL('TestBoxIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- When modified automatically by the testbox, NULL is used.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER DEFAULT NULL,
+ --- Generation ID for this row.
+ -- This is primarily for referencing by TestSets.
+ idGenTestBox INTEGER UNIQUE DEFAULT NEXTVAL('TestBoxGenIdSeq') NOT NULL,
+
+ --- The testbox IP.
+ -- This is from the webserver point of view and automatically updated on
+ -- SIGNON. The test setup doesn't permit for IP addresses to change while
+ -- the testbox is operational, because this will break gang tests.
+ ip inet NOT NULL,
+ --- The system or firmware UUID.
+ -- This uniquely identifies the testbox when talking to the server. After
+ -- SIGNON though, the testbox will also provide idTestBox and ip to
+ -- establish its identity beyond doubt.
+ uuidSystem uuid NOT NULL,
+ --- The testbox name.
+ -- Usually similar to the DNS name.
+ sName text NOT NULL,
+ --- Optional testbox description.
+ -- Intended for describing the box as well as making other relevant notes.
+ idStrDescription INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+
+ --- Indicates whether this testbox is enabled.
+ -- A testbox gets disabled when we're doing maintenance, debugging a issue
+ -- that happens only on that testbox, or some similar stuff. This is an
+ -- alternative to deleting the testbox.
+ fEnabled BOOLEAN DEFAULT NULL,
+
+ --- The kind of lights-out-management.
+ enmLomKind LomKind_T DEFAULT 'none'::LomKind_T NOT NULL,
+ --- The IP adress of the lights-out-management.
+ -- This can be NULL if enmLomKind is 'none', otherwise it must contain a valid address.
+ ipLom inet DEFAULT NULL,
+
+ --- Timeout scale factor, given as a percent.
+ -- This is a crude adjustment of the test case timeout for slower hardware.
+ pctScaleTimeout smallint DEFAULT 100 NOT NULL CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
+
+ --- Change comment or similar.
+ idStrComment INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+
+ --- @name Scheduling properties (reported by testbox script).
+ -- @{
+ --- Same abbrieviations as kBuild, see KBUILD_OSES.
+ idStrOs INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Informational, no fixed format.
+ idStrOsVersion INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
+ idStrCpuVendor INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
+ idStrCpuArch INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- The CPU name if available.
+ idStrCpuName INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Number identifying the CPU family/model/stepping/whatever.
+ -- For x86 and AMD64 type CPUs, this will on the following format:
+ -- (EffFamily << 24) | (EffModel << 8) | Stepping.
+ lCpuRevision bigint DEFAULT NULL,
+ --- Number of CPUs, CPU cores and CPU threads.
+ cCpus smallint DEFAULT NULL CHECK (cCpus IS NULL OR cCpus > 0),
+ --- Set if capable of hardware virtualization.
+ fCpuHwVirt boolean DEFAULT NULL,
+ --- Set if capable of nested paging.
+ fCpuNestedPaging boolean DEFAULT NULL,
+ --- Set if CPU capable of 64-bit (VBox) guests.
+ fCpu64BitGuest boolean DEFAULT NULL,
+ --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
+ fChipsetIoMmu boolean DEFAULT NULL,
+ --- Set if the test box does raw-mode tests.
+ fRawMode boolean DEFAULT NULL,
+ --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
+ cMbMemory bigint DEFAULT NULL CHECK (cMbMemory IS NULL OR cMbMemory > 0),
+ --- The amount of scratch space in megabytes (rounded down to nearest 64 MB).
+ cMbScratch bigint DEFAULT NULL CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
+ --- Free form hardware and software report field.
+ idStrReport INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- @}
+
+ --- The testbox script revision number, serves the purpose of a version number.
+ -- Probably good to have when scheduling upgrades as well for status purposes.
+ iTestBoxScriptRev INTEGER DEFAULT 0 NOT NULL,
+ --- The python sys.hexversion (layed out as of 2.7).
+ -- Good to know which python versions we need to support.
+ iPythonHexVersion INTEGER DEFAULT NULL,
+
+ --- Pending command.
+ -- @note We put it here instead of in TestBoxStatuses to get history.
+ enmPendingCmd TestBoxCmd_T DEFAULT 'none'::TestBoxCmd_T NOT NULL,
+
+ PRIMARY KEY (idTestBox, tsExpire),
+
+ --- Nested paging requires hardware virtualization.
+ CHECK (fCpuNestedPaging IS NULL OR (fCpuNestedPaging <> TRUE OR fCpuHwVirt = TRUE))
+);
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire DESC);
+CREATE INDEX TestBoxesExpireEffectiveIdx ON TestBoxes (tsExpire DESC, tsEffective ASC);
+
+
+--
+-- Create a view for TestBoxes where the strings are resolved.
+--
+CREATE VIEW TestBoxesWithStrings AS
+ SELECT TestBoxes.*,
+ Str1.sValue AS sDescription,
+ Str2.sValue AS sComment,
+ Str3.sValue AS sOs,
+ Str4.sValue AS sOsVersion,
+ Str5.sValue AS sCpuVendor,
+ Str6.sValue AS sCpuArch,
+ Str7.sValue AS sCpuName,
+ Str8.sValue AS sReport
+ FROM TestBoxes
+ LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment = Str2.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs = Str3.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion = Str4.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor = Str5.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch = Str6.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName = Str7.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport = Str8.idStr;
+
+
+--- @table TestBoxesInSchedGroups
+-- N:M relationship between test boxes and scheduling groups.
+--
+-- We associate a priority with this relationship.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE TestBoxesInSchedGroups (
+ --- TestBox ID.
+ -- Non-unique foreign key: TestBoxes(idTestBox).
+ idTestBox INTEGER NOT NULL,
+ --- Scheduling ID.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup).
+ idSchedGroup INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The scheduling priority of the scheduling group for the test box.
+ -- Higher number causes the scheduling group to be serviced more frequently.
+ -- @sa TestGroupMembers.iSchedPriority, SchedGroups.iSchedPriority
+ iSchedPriority INTEGER DEFAULT 16 CHECK (iSchedPriority >= 0 AND iSchedPriority < 32) NOT NULL,
+
+ PRIMARY KEY (idTestBox, idSchedGroup, tsExpire)
+);
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- F a i l u r e T r a c k i n g
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+
+--- @table FailureCategories
+-- Failure categories.
+--
+-- This is for organizing the failure reasons.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE SEQUENCE FailureCategoryIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE FailureCategories (
+ --- The identifier of this failure category (once assigned, it will never change).
+ idFailureCategory INTEGER DEFAULT NEXTVAL('FailureCategoryIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- The short category description.
+ -- For combo boxes and other selection lists.
+ sShort text NOT NULL,
+ --- Full description
+ -- For cursor-over-poppups for instance.
+ sFull text NOT NULL,
+
+ PRIMARY KEY (idFailureCategory, tsExpire)
+);
+
+
+--- @table FailureReasons
+-- Failure reasons.
+--
+-- When analysing a test failure, the testbox sheriff will try assign a fitting
+-- reason for the failure. This table is here to help the sheriff in his/hers
+-- job as well as developers looking checking if their changes affected the
+-- test results in any way.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE SEQUENCE FailureReasonIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE FailureReasons (
+ --- The identifier of this failure reason (once assigned, it will never change).
+ idFailureReason INTEGER DEFAULT NEXTVAL('FailureReasonIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The failure category this reason belongs to.
+ -- Non-unique foreign key: FailureCategories(idFailureCategory)
+ idFailureCategory INTEGER NOT NULL,
+ --- The short failure description.
+ -- For combo boxes and other selection lists.
+ sShort text NOT NULL,
+ --- Full failure description.
+ sFull text NOT NULL,
+ --- Ticket number in the primary bugtracker.
+ iTicket INTEGER DEFAULT NULL,
+ --- Other URLs to reports or discussions of the observed symptoms.
+ asUrls text ARRAY DEFAULT NULL,
+
+ PRIMARY KEY (idFailureReason, tsExpire)
+);
+CREATE INDEX FailureReasonsCategoryIdx ON FailureReasons (idFailureCategory, idFailureReason);
+
+
+
+--- @table TestResultFailures
+-- This is for tracking/discussing test result failures.
+--
+-- The rational for putting this is a separate table is that we need history on
+-- this while TestResults does not.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE TABLE TestResultFailures (
+ --- The test result we're disucssing.
+ -- @note The foreign key is declared after TestResults (further down).
+ idTestResult INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- The testsest this result is a part of.
+ -- This is mainly an aid for bypassing the enormous TestResults table.
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+
+ --- The suggested failure reason.
+ -- Non-unique foreign key: FailureReasons(idFailureReason)
+ idFailureReason INTEGER NOT NULL,
+ --- Optional comment.
+ sComment text DEFAULT NULL,
+
+ PRIMARY KEY (idTestResult, tsExpire)
+);
+CREATE INDEX TestResultFailureIdx ON TestResultFailures (idTestSet, tsExpire DESC, tsEffective ASC);
+CREATE INDEX TestResultFailureIdx2 ON TestResultFailures (idTestResult, tsExpire DESC, tsEffective ASC);
+CREATE INDEX TestResultFailureIdx3 ON TestResultFailures (idFailureReason, idTestResult, tsExpire DESC, tsEffective ASC);
+
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- T e s t I n p u t
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+
+--- @table BuildBlacklist
+-- Table used to blacklist sets of builds.
+--
+-- The best usage example is a VMM developer realizing that a change causes the
+-- host to panic, hang, or otherwise misbehave. To prevent the testbox sheriff
+-- from repeatedly having to reboot testboxes, the builds gets blacklisted
+-- until there is a working build again. This may mean adding an open ended
+-- blacklist spec and then updating it with the final revision number once the
+-- fix has been committed.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+-- @todo Would be nice if we could replace the text strings below with a set of
+-- BuildCategories, or sore it in any other way which would enable us to
+-- do a negative join with build category... The way it is specified
+-- now, it looks like we have to open a cursor of prospecitve builds and
+-- filter then thru this table one by one.
+--
+-- Any better representation is welcome, but this is low prioirty for
+-- now, as it's relatively easy to change this later one.
+--
+CREATE SEQUENCE BuildBlacklistIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE BuildBlacklist (
+ --- The blacklist entry id.
+ -- This stays constant over time.
+ idBlacklisting INTEGER DEFAULT NEXTVAL('BuildBlacklistIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The reason for the blacklisting.
+ -- Non-unique foreign key: FailureReasons(idFailureReason)
+ idFailureReason INTEGER NOT NULL,
+
+ --- Which product.
+ -- ASSUME that it is okay to limit a blacklisting to a single product.
+ sProduct text NOT NULL,
+ --- Which branch.
+ -- ASSUME that it is okay to limit a blacklisting to a branch.
+ sBranch text NOT NULL,
+
+ --- Build types to include, all matches if NULL.
+ asTypes text ARRAY DEFAULT NULL,
+ --- Array of the 'sOs.sCpuArch' to match, all matches if NULL.
+ -- See KBUILD_OSES in kBuild for a list of standard target OSes, and
+ -- KBUILD_ARCHES for a list of standard architectures.
+ --
+ -- @remarks See marks on 'os-agnostic' and 'noarch' in BuildCategories.
+ asOsArches text ARRAY DEFAULT NULL,
+
+ --- The first subversion tree revision to blacklist.
+ iFirstRevision INTEGER NOT NULL,
+ --- The last subversion tree revision to blacklist, no upper limit if NULL.
+ iLastRevision INTEGER NOT NULL,
+
+ PRIMARY KEY (idBlacklisting, tsExpire)
+);
+CREATE INDEX BuildBlacklistIdx ON BuildBlacklist (iLastRevision DESC, iFirstRevision ASC, sProduct, sBranch,
+ tsExpire DESC, tsEffective ASC);
+
+--- @table BuildCategories
+-- Build categories.
+--
+-- The purpose of this table is saving space in the Builds table and hopefully
+-- speed things up when selecting builds as well (compared to selecting on 4
+-- text fields in the much larger Builds table).
+--
+-- Insert only table, no update, no delete. History is not needed.
+--
+CREATE SEQUENCE BuildCategoryIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE BuildCategories (
+ --- The build type identifier.
+ idBuildCategory INTEGER PRIMARY KEY DEFAULT NEXTVAL('BuildCategoryIdSeq') NOT NULL,
+ --- Product.
+ -- The product name. For instance 'VBox' or 'VBoxTestSuite'.
+ sProduct TEXT NOT NULL,
+ --- The version control repository name.
+ sRepository TEXT NOT NULL,
+ --- The branch name (in the version control system).
+ sBranch TEXT NOT NULL,
+ --- The build type.
+ -- See KBUILD_BLD_TYPES in kBuild for a list of standard build types.
+ sType TEXT NOT NULL,
+ --- Array of the 'sOs.sCpuArch' supported by the build.
+ -- See KBUILD_OSES in kBuild for a list of standard target OSes, and
+ -- KBUILD_ARCHES for a list of standard architectures.
+ --
+ -- @remarks 'os-agnostic' is used if the build doesn't really target any
+ -- specific OS or if it targets all applicable OSes.
+ -- 'noarch' is used if the build is architecture independent or if
+ -- all applicable architectures are handled.
+ -- Thus, 'os-agnostic.noarch' will run on all build boxes.
+ --
+ -- @note The array shall be sorted ascendingly to prevent unnecessary duplicates!
+ --
+ asOsArches TEXT ARRAY NOT NULL,
+
+ UNIQUE (sProduct, sRepository, sBranch, sType, asOsArches)
+);
+
+
+--- @table Builds
+-- The builds table contains builds from the tinderboxes and oaccasionally from
+-- developers.
+--
+-- The tinderbox side could be fed by a batch job enumerating the build output
+-- directories every so often, looking for new builds. Or we could query them
+-- from the tinderbox database. Yet another alternative is making the
+-- tinderbox server or client side software inform us about all new builds.
+--
+-- The developer builds are entered manually thru the TM web UI. They are used
+-- for subjecting new code to some larger scale testing before commiting,
+-- enabling, or merging a private branch.
+--
+-- The builds are being selected from this table by the via the build source
+-- specification that SchedGroups.idBuildSrc and
+-- SchedGroups.idBuildSrcTestSuite links to.
+--
+-- @remarks This table stores history. Never update or delete anything. The
+-- equivalent of deleting is done by setting the 'tsExpire' field to
+-- current_timestamp. To select the currently valid entries use
+-- tsExpire = TIMESTAMP WITH TIME ZONE 'infinity'.
+--
+CREATE SEQUENCE BuildIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE Builds (
+ --- The build identifier.
+ -- This remains unchanged
+ idBuild INTEGER DEFAULT NEXTVAL('BuildIdSeq') NOT NULL,
+ --- When this build was created or entered into the database.
+ -- This remains unchanged
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ -- @note This is NULL if added by a batch job / tinderbox.
+ uidAuthor INTEGER DEFAULT NULL,
+ --- The build category.
+ idBuildCategory INTEGER REFERENCES BuildCategories(idBuildCategory) NOT NULL,
+ --- The subversion tree revision of the build.
+ iRevision INTEGER NOT NULL,
+ --- The product version number (suitable for RTStrVersionCompare).
+ sVersion TEXT NOT NULL,
+ --- The link to the tinderbox log of this build.
+ sLogUrl TEXT,
+ --- Comma separated list of binaries.
+ -- The binaries have paths relative to the TESTBOX_PATH_BUILDS or full URLs.
+ sBinaries TEXT NOT NULL,
+ --- Set when the binaries gets deleted by the build quota script.
+ fBinariesDeleted BOOLEAN DEFAULT FALSE NOT NULL,
+
+ UNIQUE (idBuild, tsExpire)
+);
+CREATE INDEX BuildsLookupIdx ON Builds (idBuildCategory, iRevision);
+
+
+--- @table VcsRevisions
+-- This table is for translating build revisions into commit details.
+--
+-- For graphs and test results, it would be useful to translate revisions into
+-- dates and maybe provide commit message and the committer.
+--
+-- Data is entered exclusively thru one or more batch jobs, so no internal
+-- authorship needed. Also, since we're mirroring data from external sources
+-- here, the batch job is allowed to update/replace existing records.
+--
+-- @todo We we could collect more info from the version control systems, if we
+-- believe it's useful and can be presented in a reasonable manner.
+-- Getting a list of affected files would be simple (requires
+-- a separate table with a M:1 relationship to this table), or try
+-- associate a commit to a branch.
+--
+CREATE TABLE VcsRevisions (
+ --- The version control tree name.
+ sRepository TEXT NOT NULL,
+ --- The version control tree revision number.
+ iRevision INTEGER NOT NULL,
+ --- When the revision was created (committed).
+ tsCreated TIMESTAMP WITH TIME ZONE NOT NULL,
+ --- The name of the committer.
+ -- @note Not to be confused with uidAuthor and test manager users.
+ sAuthor TEXT,
+ --- The commit message.
+ sMessage TEXT,
+
+ UNIQUE (sRepository, iRevision)
+);
+CREATE INDEX VcsRevisionsByDate ON VcsRevisions (tsCreated DESC);
+
+
+--- @table VcsBugReferences
+-- This is for relating commits to a bug and vice versa.
+--
+-- This feature isn't so much for the test manager as a cheap way of extending
+-- bug trackers without VCS integration. We just need to parse the commit
+-- messages when inserting them into the VcsRevisions table.
+--
+-- Same input, updating and history considerations as VcsRevisions.
+--
+CREATE TABLE VcsBugReferences (
+ --- The version control tree name.
+ sRepository TEXT NOT NULL,
+ --- The version control tree revision number.
+ iRevision INTEGER NOT NULL,
+ --- The bug tracker identifier - see g_kdBugTrackers in config.py.
+ sBugTracker CHAR(4) NOT NULL,
+ --- The bug number in the bug tracker.
+ lBugNo BIGINT NOT NULL,
+
+ UNIQUE (sRepository, iRevision, sBugTracker, lBugNo)
+);
+CREATE INDEX VcsBugReferencesLookupIdx ON VcsBugReferences (sBugTracker, lBugNo);
+
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- T e s t R e s u l t s
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+
+--- @table TestResultStrTab
+-- String table for the test results.
+--
+-- This is a string cache for value names, test names and possible more, that
+-- is frequently repated in the test results record for each test run. The
+-- purpose is not only to save space, but to make datamining queries faster by
+-- giving them integer fields to work on instead of text fields. There may
+-- possibly be some benefits on INSERT as well as there are only integer
+-- indexes.
+--
+-- Nothing is ever deleted from this table.
+--
+-- @note Should use a stored procedure to query/insert a string.
+--
+CREATE SEQUENCE TestResultStrTabIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestResultStrTab (
+ --- The ID of this string.
+ idStr INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultStrTabIdSeq'),
+ --- The string value.
+ sValue text NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL
+);
+CREATE UNIQUE INDEX TestResultStrTabNameIdx ON TestResultStrTab (sValue);
+
+--- Empty string with ID 0.
+INSERT INTO TestResultStrTab (idStr, sValue) VALUES (0, '');
+
+
+--- @type TestStatus_T
+-- The status of a test (set / result).
+--
+CREATE TYPE TestStatus_T AS ENUM (
+ -- Initial status:
+ 'running',
+ -- Final statuses:
+ 'success',
+ -- Final status: Test didn't fail as such, it was something else.
+ 'skipped',
+ 'bad-testbox',
+ 'aborted',
+ -- Final status: Test failed.
+ 'failure',
+ 'timed-out',
+ 'rebooted'
+);
+
+
+--- @table TestResults
+-- Test results - a recursive bundle of joy!
+--
+-- A test case will be created when the testdriver calls reporter.testStart and
+-- concluded with reporter.testDone. The testdriver (or it subordinates) can
+-- use these methods to create nested test results. For IPRT based test cases,
+-- RTTestCreate, RTTestInitAndCreate and RTTestSub will both create new test
+-- result records, where as RTTestSubDone, RTTestSummaryAndDestroy and
+-- RTTestDestroy will conclude records.
+--
+-- By concluding is meant updating the status. When the test driver reports
+-- success, we check it against reported results. (paranoia strikes again!)
+--
+-- Nothing is ever deleted from this table.
+--
+-- @note As seen below, several other tables associate data with a
+-- test result, and the top most test result is referenced by the
+-- test set.
+--
+CREATE SEQUENCE TestResultIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestResults (
+ --- The ID of this test result.
+ idTestResult INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultIdSeq'),
+ --- The parent test result.
+ -- This is NULL for the top test result.
+ idTestResultParent INTEGER REFERENCES TestResults(idTestResult),
+ --- The test set this result is a part of.
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp. This may also be the timestamp of when the test started.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The elapsed time for this test.
+ -- This is either reported by the directly (with some sanity checking) or
+ -- calculated (current_timestamp - created_ts).
+ -- @todo maybe use a nanosecond field here, check with what
+ tsElapsed interval DEFAULT NULL,
+ --- The test name.
+ idStrName INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The error count.
+ cErrors INTEGER DEFAULT 0 NOT NULL,
+ --- The test status.
+ enmStatus TestStatus_T DEFAULT 'running'::TestStatus_T NOT NULL,
+ --- Nesting depth.
+ iNestingDepth smallint NOT NULL CHECK (iNestingDepth >= 0 AND iNestingDepth < 16),
+ -- Make sure errors and status match up.
+ CONSTRAINT CheckStatusMatchesErrors
+ CHECK ( (cErrors > 0 AND enmStatus IN ('running'::TestStatus_T,
+ 'failure'::TestStatus_T, 'timed-out'::TestStatus_T, 'rebooted'::TestStatus_T ))
+ OR (cErrors = 0 AND enmStatus IN ('running'::TestStatus_T, 'success'::TestStatus_T,
+ 'skipped'::TestStatus_T, 'aborted'::TestStatus_T, 'bad-testbox'::TestStatus_T))
+ ),
+ -- The following is for the TestResultFailures foreign key.
+ -- Note! This was added with the name TestResults_idTestResult_idTestSet_key in the tmdb-r16 update script.
+ UNIQUE (idTestResult, idTestSet)
+);
+
+CREATE INDEX TestResultsSetIdx ON TestResults (idTestSet, idStrName, idTestResult);
+CREATE INDEX TestResultsParentIdx ON TestResults (idTestResultParent);
+-- The TestResultsNameIdx and TestResultsNameIdx2 are for speeding up the result graph & reporting code.
+CREATE INDEX TestResultsNameIdx ON TestResults (idStrName, tsCreated DESC);
+CREATE INDEX TestResultsNameIdx2 ON TestResults (idTestResult, idStrName);
+
+ALTER TABLE TestResultFailures ADD CONSTRAINT TestResultFailures_idTestResult_idTestSet_fkey
+ FOREIGN KEY (idTestResult, idTestSet) REFERENCES TestResults(idTestResult, idTestSet) MATCH FULL;
+
+
+--- @table TestResultValues
+-- Test result values.
+--
+-- A testdriver or subordinate may report a test value via
+-- reporter.testValue(), while IPRT based test will use RTTestValue and
+-- associates.
+--
+-- This is an insert only table, no deletes, no updates.
+--
+CREATE SEQUENCE TestResultValueIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestResultValues (
+ --- The ID of this value.
+ idTestResultValue INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultValueIdSeq'),
+ --- The test result it was reported within.
+ idTestResult INTEGER REFERENCES TestResults(idTestResult) NOT NULL,
+ --- The test set this value is a part of (for avoiding joining thru TestResults).
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The name.
+ idStrName INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The value.
+ lValue bigint NOT NULL,
+ --- The unit.
+ -- @todo This is currently not defined properly. Will fix/correlate this
+ -- with the other places we use unit (IPRT/testdriver/VMMDev).
+ iUnit smallint NOT NULL CHECK (iUnit >= 0 AND iUnit < 1024)
+);
+
+CREATE INDEX TestResultValuesIdx ON TestResultValues(idTestResult);
+-- The TestResultValuesGraphIdx is for speeding up the result graph & reporting code.
+CREATE INDEX TestResultValuesGraphIdx ON TestResultValues(idStrName, tsCreated);
+-- The TestResultValuesLogIdx is for speeding up the log viewer.
+CREATE INDEX TestResultValuesLogIdx ON TestResultValues(idTestSet, tsCreated);
+
+
+--- @table TestResultFiles
+-- Test result files.
+--
+-- A testdriver or subordinate may report a file by using
+-- reporter.addFile() or reporter.addLogFile().
+--
+-- The files stored here as well as the primary log file will be processed by a
+-- batch job and compressed if considered compressable. Thus, TM will look for
+-- files with a .gz/.bz2 suffix first and then without a suffix.
+--
+-- This is an insert only table, no deletes, no updates.
+--
+CREATE SEQUENCE TestResultFileId
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestResultFiles (
+ --- The ID of this file.
+ idTestResultFile INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultFileId'),
+ --- The test result it was reported within.
+ idTestResult INTEGER REFERENCES TestResults(idTestResult) NOT NULL,
+ --- The test set this file is a part of (for avoiding joining thru TestResults).
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The filename relative to TestSets(sBaseFilename) + '-'.
+ -- The set of valid filename characters should be very limited so that no
+ -- file system issues can occure either on the TM side or the user when
+ -- loading the files. Tests trying to use other characters will fail.
+ -- Valid character regular expession: '^[a-zA-Z0-9_-(){}#@+,.=]*$'
+ idStrFile INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The description.
+ idStrDescription INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The kind of file.
+ -- For instance: 'log/release/vm',
+ -- 'screenshot/failure',
+ -- 'screencapture/failure',
+ -- 'xmllog/somestuff'
+ idStrKind INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The mime type for the file.
+ -- For instance: 'text/plain',
+ -- 'image/png',
+ -- 'video/webm',
+ -- 'text/xml'
+ idStrMime INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL
+);
+
+CREATE INDEX TestResultFilesIdx ON TestResultFiles(idTestResult);
+CREATE INDEX TestResultFilesIdx2 ON TestResultFiles(idTestSet, tsCreated DESC);
+
+
+--- @table TestResultMsgs
+-- Test result message.
+--
+-- A testdriver or subordinate may report a message via the sDetails parameter
+-- of the reporter.testFailure() method, while IPRT test cases will use
+-- RTTestFailed, RTTestPrintf and their friends. For RTTestPrintf, we will
+-- ignore the more verbose message levels since these can also be found in one
+-- of the logs.
+--
+-- This is an insert only table, no deletes, no updates.
+--
+CREATE TYPE TestResultMsgLevel_T AS ENUM (
+ 'failure',
+ 'info'
+);
+CREATE SEQUENCE TestResultMsgIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestResultMsgs (
+ --- The ID of this file.
+ idTestResultMsg INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultMsgIdSeq'),
+ --- The test result it was reported within.
+ idTestResult INTEGER REFERENCES TestResults(idTestResult) NOT NULL,
+ --- The test set this file is a part of (for avoiding joining thru TestResults).
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The message string.
+ idStrMsg INTEGER REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The message level.
+ enmLevel TestResultMsgLevel_T NOT NULL
+);
+
+CREATE INDEX TestResultMsgsIdx ON TestResultMsgs(idTestResult);
+CREATE INDEX TestResultMsgsIdx2 ON TestResultMsgs(idTestSet, tsCreated DESC);
+
+
+--- @table TestSets
+-- Test sets / Test case runs.
+--
+-- This is where we collect data about test runs.
+--
+-- @todo Not entirely sure where the 'test set' term came from. Consider
+-- finding something more appropriate.
+--
+CREATE SEQUENCE TestSetIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestSets (
+ --- The ID of this test set.
+ idTestSet INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestSetIdSeq') NOT NULL,
+
+ --- The test config timestamp, used when reading test config.
+ tsConfig TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test set was scheduled.
+ -- idGenTestBox is valid at this point.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test completed, i.e. testing stopped. This should only be set once.
+ tsDone TIMESTAMP WITH TIME ZONE DEFAULT NULL,
+ --- The current status.
+ enmStatus TestStatus_T DEFAULT 'running'::TestStatus_T NOT NULL,
+
+ --- The build we're testing.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuild INTEGER NOT NULL,
+ --- The build category of idBuild when the test started.
+ -- This is for speeding up graph data collection, i.e. avoid idBuild
+ -- the WHERE part of the selection.
+ idBuildCategory INTEGER REFERENCES BuildCategories(idBuildCategory) NOT NULL,
+ --- The test suite build we're using to do the testing.
+ -- This is NULL if the test suite zip wasn't referred or if a test suite
+ -- build source wasn't configured.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuildTestSuite INTEGER DEFAULT NULL,
+
+ --- The exact testbox configuration.
+ idGenTestBox INTEGER REFERENCES TestBoxes(idGenTestBox) NOT NULL,
+ --- The testbox ID for joining with (valid: tsStarted).
+ -- Non-unique foreign key: TestBoxes(idTestBox)
+ idTestBox INTEGER NOT NULL,
+ --- The scheduling group ID the test was scheduled thru (valid: tsStarted).
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ idSchedGroup INTEGER NOT NULL,
+
+ --- The testgroup (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestGroup)
+ -- Note! This also gives the member ship entry, since a testcase can only
+ -- have one membership per test group.
+ idTestGroup INTEGER NOT NULL,
+
+ --- The exact test case config we executed in this test run.
+ idGenTestCase INTEGER REFERENCES TestCases(idGenTestCase) NOT NULL,
+ --- The test case ID for joining with (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestCase)
+ idTestCase INTEGER NOT NULL,
+
+ --- The arguments (and requirements++) we executed this test case with.
+ idGenTestCaseArgs INTEGER REFERENCES TestCaseArgs(idGenTestCaseArgs) NOT NULL,
+ --- The argument variation ID (valid: tsConfig).
+ -- Non-unique foreign key: TestCaseArgs(idTestCaseArgs)
+ idTestCaseArgs INTEGER NOT NULL,
+
+ --- The root of the test result tree.
+ -- @note This will only be NULL early in the transaction setting up the testset.
+ -- @note If the test reports more than one top level test result, we'll
+ -- fail the whole test run and let the test developer fix it.
+ idTestResult INTEGER REFERENCES TestResults(idTestResult) DEFAULT NULL,
+
+ --- The base filename used for storing files related to this test set.
+ -- This is a path relative to wherever TM is dumping log files. In order
+ -- to not become a file system test case, we will try not to put too many
+ -- hundred thousand files in a directory. A simple first approach would
+ -- be to just use the current date (tsCreated) like this:
+ -- TM_FILE_DIR/year/month/day/TestSets.idTestSet
+ --
+ -- The primary log file for the test is this name suffixed by '.log'.
+ --
+ -- The files in the testresultfile table gets their full names like this:
+ -- TM_FILE_DIR/sBaseFilename-testresultfile.id-TestResultStrTab(testresultfile.idStrFilename)
+ --
+ -- @remarks We store this explicitly in case we change the directly layout
+ -- at some later point.
+ sBaseFilename text UNIQUE NOT NULL,
+
+ --- The gang member number number, 0 is the leader.
+ iGangMemberNo SMALLINT DEFAULT 0 NOT NULL CHECK (iGangMemberNo >= 0 AND iGangMemberNo < 1024),
+ --- The test set of the gang leader, NULL if no gang involved.
+ -- @note This is set by the gang leader as well, so that we can find all
+ -- gang members by WHERE idTestSetGangLeader = :id.
+ idTestSetGangLeader INTEGER REFERENCES TestSets(idTestSet) DEFAULT NULL
+
+);
+CREATE INDEX TestSetsGangIdx ON TestSets (idTestSetGangLeader);
+CREATE INDEX TestSetsBoxIdx ON TestSets (idTestBox, idTestResult);
+CREATE INDEX TestSetsBuildIdx ON TestSets (idBuild, idTestResult);
+CREATE INDEX TestSetsTestCaseIdx ON TestSets (idTestCase, idTestResult);
+CREATE INDEX TestSetsTestVarIdx ON TestSets (idTestCaseArgs, idTestResult);
+--- The TestSetsDoneCreatedBuildCatIdx is for testbox results, graph options and such.
+CREATE INDEX TestSetsDoneCreatedBuildCatIdx ON TestSets (tsDone DESC NULLS FIRST, tsCreated ASC, idBuildCategory);
+--- For graphs.
+CREATE INDEX TestSetsGraphBoxIdx ON TestSets (idTestBox, tsCreated DESC, tsDone ASC NULLS LAST, idBuildCategory, idTestCase);
+
+ALTER TABLE TestResults ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultFiles ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultMsgs ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultFailures ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+--
+-- T e s t M a n g e r P e r s i s t e n t S t o r a g e
+--
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+--- @type TestBoxState_T
+-- TestBox state.
+--
+-- @todo Consider drawing a state diagram for this.
+--
+CREATE TYPE TestBoxState_T AS ENUM (
+ --- Nothing to do.
+ -- Prev: testing, gang-cleanup, rebooting, upgrading,
+ -- upgrading-and-rebooting, doing-special-cmd.
+ -- Next: testing, gang-gathering, rebooting, upgrading,
+ -- upgrading-and-rebooting, doing-special-cmd.
+ 'idle',
+ --- Executing a test.
+ -- Prev: idle
+ -- Next: idle
+ 'testing',
+
+ -- Gang scheduling statuses:
+ --- The gathering of a gang.
+ -- Prev: idle
+ -- Next: gang-gathering-timedout, gang-testing
+ 'gang-gathering',
+ --- The gathering timed out, the testbox needs to cleanup and move on.
+ -- Prev: gang-gathering
+ -- Next: idle
+ -- This is set on all gathered members by the testbox who triggers the
+ -- timeout.
+ 'gang-gathering-timedout',
+ --- The gang scheduling equivalent of 'testing'.
+ -- Prev: gang-gathering
+ -- Next: gang-cleanup
+ 'gang-testing',
+ --- Waiting for the other gang members to stop testing so that cleanups
+ -- can be performed and members safely rescheduled.
+ -- Prev: gang-testing
+ -- Next: idle
+ --
+ -- There are two resource clean up issues being targeted here:
+ -- 1. Global resources will be allocated by the leader when he enters the
+ -- 'gang-gathering' state. If the leader quits and frees the resource
+ -- while someone is still using it, bad things will happen. Imagine a
+ -- global resource without any access checks and relies exclusivly on
+ -- the TM doing its job.
+ -- 2. TestBox resource accessed by other gang members may also be used in
+ -- other tests. Should a gang member leave early and embark on a
+ -- testcase using the same resources, bad things will happen. Example:
+ -- Live migration. One partner leaves early because it detected some
+ -- fatal failure, the other one is still trying to connect to him.
+ -- The testbox is scheduled again on the same live migration testcase,
+ -- only with different arguments (VM config), it will try migrate using
+ -- the same TCP ports. Confusion ensues.
+ --
+ -- To figure out whether to remain in this status because someone is
+ -- still testing:
+ -- SELECT COUNT(*) FROM TestBoxStatuses, TestSets
+ -- WHERE TestSets.idTestSetGangLeader = :idGangLeader
+ -- AND TestSets.idTestBox = TestBoxStatuses.idTestBox
+ -- AND TestSets.idTestSet = TestBoxStatuses.idTestSet
+ -- AND TestBoxStatuses.enmState = 'gang-testing'::TestBoxState_T;
+ 'gang-cleanup',
+
+ -- Command related statuses (all command status changes comes from 'idle'
+ -- and goes back to 'idle'):
+ 'rebooting',
+ 'upgrading',
+ 'upgrading-and-rebooting',
+ 'doing-special-cmd'
+);
+
+--- @table TestBoxStatuses
+-- Testbox status table.
+--
+-- History is not planned on this table.
+--
+CREATE TABLE TestBoxStatuses (
+ --- The testbox.
+ idTestBox INTEGER PRIMARY KEY NOT NULL,
+ --- The testbox generation ID.
+ idGenTestBox INTEGER REFERENCES TestBoxes(idGenTestBox) NOT NULL,
+ --- When this status was last updated.
+ -- This is updated everytime the testbox talks to the test manager, thus it
+ -- can easily be used to find testboxes which has stopped responding.
+ --
+ -- This is used for timeout calculation during gang-gathering, so in that
+ -- scenario it won't be updated until the gang is gathered or we time out.
+ tsUpdated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The current state.
+ enmState TestBoxState_T DEFAULT 'idle'::TestBoxState_T NOT NULL,
+ --- Reference to the test set
+ idTestSet INTEGER REFERENCES TestSets(idTestSet),
+ --- Interal work item number.
+ -- This is used to pick and prioritize between multiple scheduling groups.
+ iWorkItem INTEGER DEFAULT 0 NOT NULL
+);
+
+
+--- @table GlobalResourceStatuses
+-- Global resource status, tracks which test set resources are allocated by.
+--
+-- History is not planned on this table.
+--
+CREATE TABLE GlobalResourceStatuses (
+ --- The resource ID.
+ -- Non-unique foreign key: GlobalResources(idGlobalRsrc).
+ idGlobalRsrc INTEGER PRIMARY KEY NOT NULL,
+ --- The resource owner.
+ -- @note This is going thru testboxstatus to be able to use the testbox ID
+ -- as a foreign key.
+ idTestBox INTEGER REFERENCES TestBoxStatuses(idTestBox) NOT NULL,
+ --- When the allocation took place.
+ tsAllocated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL
+);
+
+
+--- @table SchedQueues
+-- Scheduler queue.
+--
+-- The queues are currently associated with a scheduling group, it could
+-- alternative be changed to hook on to a testbox instead. It depends on what
+-- kind of scheduling method we prefer. The former method aims at test case
+-- thruput, making sacrifices in the hardware distribution area. The latter is
+-- more like the old buildbox style testing, making sure that each test case is
+-- executed on each testbox.
+--
+-- When there are configuration changes, TM will regenerate the scheduling
+-- queue for the affected scheduling groups. We do not concern ourselves with
+-- trying to continue at the approximately same queue position, we simply take
+-- it from the top.
+--
+-- When a testbox ask for work, we will open a cursor on the queue and take the
+-- first test in the queue that can be executed on that testbox. The test will
+-- be moved to the end of the queue (getting a new item_id).
+--
+-- If a test is manually changed to the head of the queue, the item will get a
+-- item_id which is 1 lower than the head of the queue. Unless someone does
+-- this a couple of billion times, we shouldn't have any trouble running out of
+-- number space. :-)
+--
+-- Manually moving a test to the end of the queue is easy, just get a new
+-- 'item_id'.
+--
+-- History is not planned on this table.
+--
+CREATE SEQUENCE SchedQueueItemIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE SchedQueues (
+ --- The scheduling queue (one queue per scheduling group).
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ idSchedGroup INTEGER NOT NULL,
+ --- The scheduler queue entry ID.
+ -- Lower numbers means early queue position.
+ idItem INTEGER DEFAULT NEXTVAL('SchedQueueItemIdSeq') NOT NULL,
+ --- The queue offset.
+ -- This is used for repositining the queue when recreating it. It can also
+ -- be used to figure out how jumbled the queue gets after real life has had
+ -- it's effect on it.
+ offQueue INTEGER NOT NULL,
+ --- The test case argument variation to execute.
+ idGenTestCaseArgs INTEGER REFERENCES TestCaseArgs(idGenTestCaseArgs) NOT NULL,
+ --- The relevant testgroup.
+ -- Non-unique foreign key: TestGroups(idTestGroup).
+ idTestGroup INTEGER NOT NULL,
+ --- Aggregated test group dependencies (NULL if none).
+ -- Non-unique foreign key: TestGroups(idTestGroup).
+ -- See also comments on SchedGroupMembers.idTestGroupPreReq.
+ aidTestGroupPreReqs INTEGER ARRAY DEFAULT NULL,
+ --- The scheduling time constraints (see SchedGroupMembers.bmHourlySchedule).
+ bmHourlySchedule bit(168) DEFAULT NULL,
+ --- When the queue entry was created and for which config is valid.
+ -- This is the timestamp that should be used when reading config info.
+ tsConfig TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this status was last scheduled.
+ -- This is set to current_timestamp when moving the entry to the end of the
+ -- queue. It's initial value is unix-epoch. Not entirely sure if it's
+ -- useful beyond introspection and non-unique foreign key hacking.
+ tsLastScheduled TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'epoch' NOT NULL,
+
+ --- This is used in gang scheduling.
+ idTestSetGangLeader INTEGER REFERENCES TestSets(idTestSet) DEFAULT NULL UNIQUE,
+ --- The number of gang members still missing.
+ --
+ -- This saves calculating the number of missing members via selects like:
+ -- SELECT COUNT(*) FROM TestSets WHERE idTestSetGangLeader = :idGang;
+ -- and
+ -- SELECT cGangMembers FROM TestCaseArgs WHERE idGenTestCaseArgs = :idTest;
+ -- to figure out whether to remain in 'gather-gang'::TestBoxState_T.
+ --
+ cMissingGangMembers smallint DEFAULT 1 NOT NULL,
+
+ --- @todo
+ --- The number of times this has been considered for scheduling.
+ -- cConsidered SMALLINT DEFAULT 0 NOT NULL,
+
+ PRIMARY KEY (idSchedGroup, idItem)
+);
+CREATE INDEX SchedQueuesItemIdx ON SchedQueues(idItem);
+CREATE INDEX SchedQueuesSchedGroupIdx ON SchedQueues(idSchedGroup);
+
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseMap.png b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseMap.png
new file mode 100644
index 00000000..861a407d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseMap.png
Binary files differ
diff --git a/src/VBox/ValidationKit/testmanager/db/TestManagerVBoxPilot-1.pgsql b/src/VBox/ValidationKit/testmanager/db/TestManagerVBoxPilot-1.pgsql
new file mode 100644
index 00000000..bdff3bc4
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/TestManagerVBoxPilot-1.pgsql
@@ -0,0 +1,101 @@
+-- $Id: TestManagerVBoxPilot-1.pgsql $
+--- @file
+-- VBox Test Manager - Setup for the 1st VBox Pilot.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+BEGIN WORK;
+
+--
+-- The user we assign all the changes too.
+--
+INSERT INTO Users (sUsername, sEmail, sFullName, sLoginName)
+ VALUES ('vbox-pilot-config', 'pilot1@example.org', 'VBox Pilot Configurator', 'vbox-pilot-config');
+\set idUserQuery '(SELECT uid FROM Users WHERE sUsername = \'vbox-pilot-config\')'
+
+--
+-- Configure a scheduling group with build sources.
+--
+INSERT INTO BuildSources (uidAuthor, sName, sProduct, sBranch, asTypes, asOsArches)
+ VALUES (:idUserQuery, 'VBox trunk builds', 'VirtualBox', 'trunk', ARRAY['release', 'strict'], NULL);
+
+INSERT INTO BuildSources (uidAuthor, sName, sProduct, sBranch, asTypes, asOsArches)
+ VALUES (:idUserQuery, 'VBox TestSuite trunk builds', 'VBox TestSuite', 'trunk', ARRAY['release'], NULL);
+
+INSERT INTO SchedGroups (sName, sDescription, fEnabled, idBuildSrc, idBuildSrcTestSuite)
+ VALUES ('VirtualBox Trunk', NULL, TRUE,
+ (SELECT idBuildSrc FROM BuildSources WHERE sName = 'VBox trunk builds'),
+ (SELECT idBuildSrc FROM BuildSources WHERE sName = 'VBox TestSuite trunk builds') );
+\set idSchedGroupQuery '(SELECT idSchedGroup FROM SchedGroups WHERE sName = \'VirtualBox Trunk\')'
+
+--
+-- Configure three test groups.
+--
+INSERT INTO TestGroups (uidAuthor, sName)
+ VALUES (:idUserQuery, 'VBox smoketests');
+\set idGrpSmokeQuery '(SELECT idTestGroup FROM TestGroups WHERE sName = \'VBox smoketests\')'
+INSERT INTO SchedGroupMembers (idSchedGroup, idTestGroup, uidAuthor, idTestGroupPreReq)
+ VALUES (:idSchedGroupQuery, :idGrpSmokeQuery, :idUserQuery, NULL);
+
+INSERT INTO TestGroups (uidAuthor, sName)
+ VALUES (:idUserQuery, 'VBox general');
+\set idGrpGeneralQuery '(SELECT idTestGroup FROM TestGroups WHERE sName = \'VBox general\')'
+INSERT INTO SchedGroupMembers (idSchedGroup, idTestGroup, uidAuthor, idTestGroupPreReq)
+ VALUES (:idSchedGroupQuery, :idGrpGeneralQuery, :idUserQuery, :idGrpSmokeQuery);
+
+INSERT INTO TestGroups (uidAuthor, sName)
+ VALUES (:idUserQuery, 'VBox benchmarks');
+\set idGrpBenchmarksQuery '(SELECT idTestGroup FROM TestGroups WHERE sName = \'VBox benchmarks\')'
+INSERT INTO SchedGroupMembers (idSchedGroup, idTestGroup, uidAuthor, idTestGroupPreReq)
+ VALUES (:idSchedGroupQuery, :idGrpBenchmarksQuery, :idUserQuery, :idGrpGeneralQuery);
+
+
+--
+-- Testcases
+--
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (:idUserQuery, 'VBox install', TRUE, 600,
+ 'validationkit/testdriver/vboxinstaller.py --vbox-build @BUILD_BINARIES@ @ACTION@ -- testdriver/base.py @ACTION@',
+ '@VALIDATIONKIT_ZIP@');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'VBox install'), :idUserQuery, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES (:idGrpSmokeQuery, (SELECT idTestCase FROM TestCases WHERE sName = 'VBox install'), :idUserQuery);
+
+COMMIT WORK;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/gen-sql-comments.py b/src/VBox/ValidationKit/testmanager/db/gen-sql-comments.py
new file mode 100755
index 00000000..2bdc239c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/gen-sql-comments.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: gen-sql-comments.py $
+
+"""
+Converts doxygen style comments in SQL script to COMMENT ON statements.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+
+import sys;
+import re;
+
+
+def errorMsg(sMsg):
+ sys.stderr.write('error: %s\n' % (sMsg,));
+ return 1;
+
+class SqlDox(object):
+ """
+ Class for parsing relevant comments out of a pgsql file
+ and emit COMMENT ON statements from it.
+ """
+
+ def __init__(self, oFile, sFilename):
+ self.oFile = oFile;
+ self.sFilename = sFilename;
+ self.iLine = 0; # The current input line number.
+ self.sComment = None; # The current comment.
+ self.fCommentComplete = False; # Indicates that the comment has ended.
+ self.sCommentSqlObj = None; # SQL object indicated by the comment (@table).
+ self.sOuterSqlObj = None; # Like 'table yyyy' or 'type zzzz'.
+ self.sPrevSqlObj = None; # Like 'table xxxx'.
+
+
+ def error(self, sMsg):
+ return errorMsg('%s(%d): %s' % (self.sFilename, self.iLine, sMsg,));
+
+ def dprint(self, sMsg):
+ sys.stderr.write('debug: %s\n' % (sMsg,));
+ return True;
+
+ def resetComment(self):
+ self.sComment = None;
+ self.fCommentComplete = False;
+ self.sCommentSqlObj = None;
+
+ def quoteSqlString(self, s):
+ return s.replace("'", "''");
+
+ def commitComment2(self, sSqlObj):
+ if self.sComment is not None and sSqlObj is not None:
+ print("COMMENT ON %s IS\n '%s';\n" % (sSqlObj, self.quoteSqlString(self.sComment.strip())));
+ self.resetComment();
+ return True;
+
+ def commitComment(self):
+ return self.commitComment2(self.sCommentSqlObj);
+
+ def process(self):
+ for sLine in self.oFile:
+ self.iLine += 1;
+
+ sLine = sLine.strip();
+ self.dprint('line %d: %s\n' % (self.iLine, sLine));
+ if sLine.startswith('--'):
+ if sLine.startswith('--- '):
+ #
+ # New comment.
+ # The first list may have a @table, @type or similar that we're interested in.
+ #
+ self.commitComment();
+
+ sLine = sLine.lstrip('- ');
+ if sLine.startswith('@table '):
+ self.sCommentSqlObj = 'TABLE ' + (sLine[7:]).rstrip();
+ self.sComment = '';
+ elif sLine.startswith('@type '):
+ self.sCommentSqlObj = 'TYPE ' + (sLine[6:]).rstrip();
+ self.sComment = '';
+ elif sLine.startswith('@todo') \
+ or sLine.startswith('@file') \
+ or sLine.startswith('@page') \
+ or sLine.startswith('@name') \
+ or sLine.startswith('@{') \
+ or sLine.startswith('@}'):
+ # Ignore.
+ pass;
+ elif sLine.startswith('@'):
+ return self.error('Unknown tag: %s' % (sLine,));
+ else:
+ self.sComment = sLine;
+
+ elif (sLine.startswith('-- ') or sLine == '--') \
+ and self.sComment is not None and self.fCommentComplete is False:
+ #
+ # Append line to comment.
+ #
+ if sLine == '--':
+ sLine = '';
+ else:
+ sLine = (sLine[3:]);
+ if self.sComment == '':
+ self.sComment = sLine;
+ else:
+ self.sComment += "\n" + sLine;
+
+ elif sLine.startswith('--< '):
+ #
+ # Comment that starts on the same line as the object it describes.
+ #
+ sLine = (sLine[4:]).rstrip();
+ # => Later/never.
+ else:
+ #
+ # Not a comment that interests us. So, complete any open
+ # comment and commit it if we know which SQL object it
+ # applies to.
+ #
+ self.fCommentComplete = True;
+ if self.sCommentSqlObj is not None:
+ self.commitComment();
+ else:
+ #
+ # Not a comment. As above, we complete and optionally commit
+ # any open comment.
+ #
+ self.fCommentComplete = True;
+ if self.sCommentSqlObj is not None:
+ self.commitComment();
+
+ #
+ # Check for SQL (very fuzzy and bad).
+ #
+ asWords = sLine.split(' ');
+ if len(asWords) >= 3 \
+ and asWords[0] == 'CREATE':
+ # CREATE statement.
+ sType = asWords[1];
+ sName = asWords[2];
+ if sType == 'UNIQUE' and sName == 'INDEX' and len(asWords) >= 4:
+ sType = asWords[2];
+ sName = asWords[3];
+ if sType in ('TABLE', 'TYPE', 'INDEX', 'VIEW'):
+ self.sOuterSqlObj = sType + ' ' + sName;
+ self.sPrevSqlObj = self.sOuterSqlObj;
+ self.dprint('%s' % (self.sOuterSqlObj,));
+ self.commitComment2(self.sOuterSqlObj);
+ elif len(asWords) >= 1 \
+ and self.sOuterSqlObj is not None \
+ and self.sOuterSqlObj.startswith('TABLE ') \
+ and re.search("^(as|al|bm|c|enm|f|i|l|s|ts|uid|uuid)[A-Z][a-zA-Z0-9]*$", asWords[0]) is not None:
+ # Possibly a column name.
+ self.sPrevSqlObj = 'COLUMN ' + self.sOuterSqlObj[6:] + '.' + asWords[0];
+ self.dprint('column? %s' % (self.sPrevSqlObj));
+ self.commitComment2(self.sPrevSqlObj);
+
+ #
+ # Check for semicolon.
+ #
+ if sLine.find(");") >= 0:
+ self.sOuterSqlObj = None;
+
+ return 0;
+
+
+def usage():
+ sys.stderr.write('usage: gen-sql-comments.py <filename.pgsql>\n'
+ '\n'
+ 'The output goes to stdout.\n');
+ return 0;
+
+
+def main(asArgs):
+ # Parse the argument. :-)
+ sInput = None;
+ if (len(asArgs) != 2):
+ sys.stderr.write('syntax error: expected exactly 1 argument, a psql file\n');
+ usage();
+ return 2;
+ sInput = asArgs[1];
+
+ # Do the job, outputting to standard output.
+ try:
+ oFile = open(sInput, 'r');
+ except:
+ return errorMsg("failed to open '%s' for reading" % (sInput,));
+
+ # header.
+ print("-- $" "Id" "$");
+ print("--- @file");
+ print("-- Autogenerated from %s. Do not edit!" % (sInput,));
+ print("--");
+ print("");
+ for sLine in __copyright__.split('\n'):
+ if len(sLine) > 0:
+ print("-- %s" % (sLine,));
+ else:
+ print("--");
+ print("");
+ print("");
+ me = SqlDox(oFile, sInput);
+ return me.process();
+
+sys.exit(main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/testmanager/db/partial-db-dump.py b/src/VBox/ValidationKit/testmanager/db/partial-db-dump.py
new file mode 100755
index 00000000..6676de47
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/partial-db-dump.py
@@ -0,0 +1,392 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: partial-db-dump.py $
+# pylint: disable=line-too-long
+
+"""
+Utility for dumping the last X days of data.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports
+import sys;
+import os;
+import zipfile;
+from optparse import OptionParser;
+import xml.etree.ElementTree as ET;
+
+# Add Test Manager's modules path
+g_ksTestManagerDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksTestManagerDir);
+
+# Test Manager imports
+from testmanager.core.db import TMDatabaseConnection;
+from common import utils;
+
+
+class PartialDbDump(object): # pylint: disable=too-few-public-methods
+ """
+ Dumps or loads the last X days of database data.
+
+ This is a useful tool when hacking on the test manager locally. You can get
+ a small sample from the last few days from the production test manager server
+ without spending hours dumping, downloading, and loading the whole database
+ (because it is gigantic).
+
+ """
+
+ def __init__(self):
+ """
+ Parse command line.
+ """
+
+ oParser = OptionParser()
+ oParser.add_option('-q', '--quiet', dest = 'fQuiet', action = 'store_true',
+ help = 'Quiet execution');
+ oParser.add_option('-f', '--filename', dest = 'sFilename', metavar = '<filename>',
+ default = 'partial-db-dump.zip', help = 'The name of the partial database zip file to write/load.');
+
+ oParser.add_option('-t', '--tmp-file', dest = 'sTempFile', metavar = '<temp-file>',
+ default = '/tmp/tm-partial-db-dump.pgtxt',
+ help = 'Name of temporary file for duping tables. Must be absolute');
+ oParser.add_option('--days-to-dump', dest = 'cDays', metavar = '<days>', type = 'int', default = 14,
+ help = 'How many days to dump (counting backward from current date).');
+ oParser.add_option('--load-dump-into-database', dest = 'fLoadDumpIntoDatabase', action = 'store_true',
+ default = False, help = 'For loading instead of dumping.');
+ oParser.add_option('--store', dest = 'fStore', action = 'store_true',
+ default = False, help = 'Do not compress the zip file.');
+
+ (self.oConfig, _) = oParser.parse_args();
+
+
+ ##
+ # Tables dumped in full because they're either needed in full or they normally
+ # aren't large enough to bother reducing.
+ kasTablesToDumpInFull = [
+ 'Users',
+ 'BuildBlacklist',
+ 'BuildCategories',
+ 'BuildSources',
+ 'FailureCategories',
+ 'FailureReasons',
+ 'GlobalResources',
+ 'Testcases',
+ 'TestcaseArgs',
+ 'TestcaseDeps',
+ 'TestcaseGlobalRsrcDeps',
+ 'TestGroups',
+ 'TestGroupMembers',
+ 'SchedGroups',
+ 'SchedGroupMembers', # ?
+ 'TestBoxesInSchedGroups', # ?
+ 'SchedQueues',
+ 'TestResultStrTab', # 36K rows, never mind complicated then.
+ ];
+
+ ##
+ # Tables where we only dump partial info (the TestResult* tables are rather
+ # gigantic).
+ kasTablesToPartiallyDump = [
+ 'TestBoxes', # 2016-05-25: ca. 641 MB
+ 'TestSets', # 2016-05-25: ca. 525 MB
+ 'TestResults', # 2016-05-25: ca. 13 GB
+ 'TestResultFiles', # 2016-05-25: ca. 87 MB
+ 'TestResultMsgs', # 2016-05-25: ca. 29 MB
+ 'TestResultValues', # 2016-05-25: ca. 3728 MB
+ 'TestResultFailures',
+ 'Builds',
+ 'TestBoxStrTab',
+ 'SystemLog',
+ 'VcsRevisions',
+ ];
+
+ def _doCopyTo(self, sTable, oZipFile, oDb, sSql, aoArgs = None):
+ """ Does one COPY TO job. """
+ print('Dumping %s...' % (sTable,));
+
+ if aoArgs is not None:
+ sSql = oDb.formatBindArgs(sSql, aoArgs);
+
+ oFile = open(self.oConfig.sTempFile, 'w');
+ oDb.copyExpert(sSql, oFile);
+ cRows = oDb.getRowCount();
+ oFile.close();
+ print('... %s rows.' % (cRows,));
+
+ oZipFile.write(self.oConfig.sTempFile, sTable);
+ return True;
+
+ def _doDump(self, oDb):
+ """ Does the dumping of the database. """
+
+ enmCompression = zipfile.ZIP_DEFLATED;
+ if self.oConfig.fStore:
+ enmCompression = zipfile.ZIP_STORED;
+ oZipFile = zipfile.ZipFile(self.oConfig.sFilename, 'w', enmCompression);
+
+ oDb.begin();
+
+ # Dumping full tables is simple.
+ for sTable in self.kasTablesToDumpInFull:
+ self._doCopyTo(sTable, oZipFile, oDb, 'COPY ' + sTable + ' TO STDOUT WITH (FORMAT TEXT)');
+
+ # Figure out how far back we need to go.
+ oDb.execute('SELECT CURRENT_TIMESTAMP - INTERVAL \'%s days\'' % (self.oConfig.cDays,));
+ tsEffective = oDb.fetchOne()[0];
+ oDb.execute('SELECT CURRENT_TIMESTAMP - INTERVAL \'%s days\'' % (self.oConfig.cDays + 2,));
+ tsEffectiveSafe = oDb.fetchOne()[0];
+ print('Going back to: %s (safe: %s)' % (tsEffective, tsEffectiveSafe));
+
+ # We dump test boxes back to the safe timestamp because the test sets may
+ # use slightly dated test box references and we don't wish to have dangling
+ # references when loading.
+ for sTable in [ 'TestBoxes', ]:
+ self._doCopyTo(sTable, oZipFile, oDb,
+ 'COPY (SELECT * FROM ' + sTable + ' WHERE tsExpire >= %s) TO STDOUT WITH (FORMAT TEXT)',
+ (tsEffectiveSafe,));
+
+ # The test results needs to start with test sets and then dump everything
+ # releated to them. So, figure the lowest (oldest) test set ID we'll be
+ # dumping first.
+ oDb.execute('SELECT idTestSet FROM TestSets WHERE tsCreated >= %s', (tsEffective, ));
+ idFirstTestSet = 0;
+ if oDb.getRowCount() > 0:
+ idFirstTestSet = oDb.fetchOne()[0];
+ print('First test set ID: %s' % (idFirstTestSet,));
+
+ oDb.execute('SELECT MAX(idTestSet) FROM TestSets WHERE tsCreated >= %s', (tsEffective, ));
+ idLastTestSet = 0;
+ if oDb.getRowCount() > 0:
+ idLastTestSet = oDb.fetchOne()[0];
+ print('Last test set ID: %s' % (idLastTestSet,));
+
+ oDb.execute('SELECT MAX(idTestResult) FROM TestResults WHERE tsCreated >= %s', (tsEffective, ));
+ idLastTestResult = 0;
+ if oDb.getRowCount() > 0:
+ idLastTestResult = oDb.fetchOne()[0];
+ print('Last test result ID: %s' % (idLastTestResult,));
+
+ # Tables with idTestSet member.
+ for sTable in [ 'TestSets', 'TestResults', 'TestResultValues' ]:
+ self._doCopyTo(sTable, oZipFile, oDb,
+ 'COPY (SELECT *\n'
+ ' FROM ' + sTable + '\n'
+ ' WHERE idTestSet >= %s\n'
+ ' AND idTestSet <= %s\n'
+ ' AND idTestResult <= %s\n'
+ ') TO STDOUT WITH (FORMAT TEXT)'
+ , ( idFirstTestSet, idLastTestSet, idLastTestResult,));
+
+ # Tables where we have to go via TestResult.
+ for sTable in [ 'TestResultFiles', 'TestResultMsgs', 'TestResultFailures' ]:
+ self._doCopyTo(sTable, oZipFile, oDb,
+ 'COPY (SELECT it.*\n'
+ ' FROM ' + sTable + ' it, TestResults tr\n'
+ ' WHERE tr.idTestSet >= %s\n'
+ ' AND tr.idTestSet <= %s\n'
+ ' AND tr.idTestResult <= %s\n'
+ ' AND tr.tsCreated >= %s\n' # performance hack.
+ ' AND it.idTestResult = tr.idTestResult\n'
+ ') TO STDOUT WITH (FORMAT TEXT)'
+ , ( idFirstTestSet, idLastTestSet, idLastTestResult, tsEffective,));
+
+ # Tables which goes exclusively by tsCreated using tsEffectiveSafe.
+ for sTable in [ 'SystemLog', 'VcsRevisions' ]:
+ self._doCopyTo(sTable, oZipFile, oDb,
+ 'COPY (SELECT * FROM ' + sTable + ' WHERE tsCreated >= %s) TO STDOUT WITH (FORMAT TEXT)',
+ (tsEffectiveSafe,));
+
+ # The builds table.
+ oDb.execute('SELECT MIN(idBuild), MIN(idBuildTestSuite) FROM TestSets WHERE idTestSet >= %s', (idFirstTestSet,));
+ idFirstBuild = 0;
+ if oDb.getRowCount() > 0:
+ idFirstBuild = min(oDb.fetchOne());
+ print('First build ID: %s' % (idFirstBuild,));
+ for sTable in [ 'Builds', ]:
+ self._doCopyTo(sTable, oZipFile, oDb,
+ 'COPY (SELECT * FROM ' + sTable + ' WHERE idBuild >= %s) TO STDOUT WITH (FORMAT TEXT)',
+ (idFirstBuild,));
+
+ # The test box string table.
+ self._doCopyTo('TestBoxStrTab', oZipFile, oDb, '''
+COPY (SELECT * FROM TestBoxStrTab WHERE idStr IN (
+ ( SELECT 0
+ ) UNION ( SELECT idStrComment FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrCpuArch FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrCpuName FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrCpuVendor FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrDescription FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrOS FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrOsVersion FROM TestBoxes WHERE tsExpire >= %s
+ ) UNION ( SELECT idStrReport FROM TestBoxes WHERE tsExpire >= %s
+ ) ) ) TO STDOUT WITH (FORMAT TEXT)
+''', (tsEffectiveSafe, tsEffectiveSafe, tsEffectiveSafe, tsEffectiveSafe,
+ tsEffectiveSafe, tsEffectiveSafe, tsEffectiveSafe, tsEffectiveSafe,));
+
+ oZipFile.close();
+ print('Done!');
+ return 0;
+
+ def _doLoad(self, oDb):
+ """ Does the loading of the dumped data into the database. """
+
+ try:
+ oZipFile = zipfile.ZipFile(self.oConfig.sFilename, 'r');
+ except:
+ print('error: Dump file "%s" cannot be opened! Use "-f <file>" to specify a file.' % (self.oConfig.sFilename,));
+ return 1;
+
+ asTablesInLoadOrder = [
+ 'Users',
+ 'BuildBlacklist',
+ 'BuildCategories',
+ 'BuildSources',
+ 'FailureCategories',
+ 'FailureReasons',
+ 'GlobalResources',
+ 'Testcases',
+ 'TestcaseArgs',
+ 'TestcaseDeps',
+ 'TestcaseGlobalRsrcDeps',
+ 'TestGroups',
+ 'TestGroupMembers',
+ 'SchedGroups',
+ 'TestBoxStrTab',
+ 'TestBoxes',
+ 'SchedGroupMembers',
+ 'TestBoxesInSchedGroups',
+ 'SchedQueues',
+ 'Builds',
+ 'SystemLog',
+ 'VcsRevisions',
+ 'TestResultStrTab',
+ 'TestSets',
+ 'TestResults',
+ 'TestResultFiles',
+ 'TestResultMsgs',
+ 'TestResultValues',
+ 'TestResultFailures',
+ ];
+ assert len(asTablesInLoadOrder) == len(self.kasTablesToDumpInFull) + len(self.kasTablesToPartiallyDump);
+
+ oDb.begin();
+ oDb.execute('SET CONSTRAINTS ALL DEFERRED;');
+
+ print('Checking if the database looks empty...\n');
+ for sTable in asTablesInLoadOrder + [ 'TestBoxStatuses', 'GlobalResourceStatuses' ]:
+ oDb.execute('SELECT COUNT(*) FROM ' + sTable);
+ cRows = oDb.fetchOne()[0];
+ cMaxRows = 0;
+ if sTable in [ 'SchedGroups', 'TestBoxStrTab', 'TestResultStrTab', 'Users' ]: cMaxRows = 1;
+ if cRows > cMaxRows:
+ print('error: Table %s has %u rows which is more than %u - refusing to delete and load.'
+ % (sTable, cRows, cMaxRows,));
+ print('info: Please drop and recreate the database before loading!');
+ return 1;
+
+ print('Dropping default table content...\n');
+ for sTable in [ 'SchedGroups', 'TestBoxStrTab', 'TestResultStrTab', 'Users']:
+ oDb.execute('DELETE FROM ' + sTable);
+
+ oDb.execute('ALTER TABLE TestSets DROP CONSTRAINT IF EXISTS TestSets_idTestResult_fkey');
+
+ for sTable in asTablesInLoadOrder:
+ print('Loading %s...' % (sTable,));
+ oFile = oZipFile.open(sTable);
+ oDb.copyExpert('COPY ' + sTable + ' FROM STDIN WITH (FORMAT TEXT)', oFile);
+ cRows = oDb.getRowCount();
+ print('... %s rows.' % (cRows,));
+
+ oDb.execute('ALTER TABLE TestSets ADD FOREIGN KEY (idTestResult) REFERENCES TestResults(idTestResult)');
+ oDb.commit();
+
+ # Correct sequences.
+ atSequences = [
+ ( 'UserIdSeq', 'Users', 'uid' ),
+ ( 'GlobalResourceIdSeq', 'GlobalResources', 'idGlobalRsrc' ),
+ ( 'BuildSourceIdSeq', 'BuildSources', 'idBuildSrc' ),
+ ( 'TestCaseIdSeq', 'TestCases', 'idTestCase' ),
+ ( 'TestCaseGenIdSeq', 'TestCases', 'idGenTestCase' ),
+ ( 'TestCaseArgsIdSeq', 'TestCaseArgs', 'idTestCaseArgs' ),
+ ( 'TestCaseArgsGenIdSeq', 'TestCaseArgs', 'idGenTestCaseArgs' ),
+ ( 'TestGroupIdSeq', 'TestGroups', 'idTestGroup' ),
+ ( 'SchedGroupIdSeq', 'SchedGroups', 'idSchedGroup' ),
+ ( 'TestBoxStrTabIdSeq', 'TestBoxStrTab', 'idStr' ),
+ ( 'TestBoxIdSeq', 'TestBoxes', 'idTestBox' ),
+ ( 'TestBoxGenIdSeq', 'TestBoxes', 'idGenTestBox' ),
+ ( 'FailureCategoryIdSeq', 'FailureCategories', 'idFailureCategory' ),
+ ( 'FailureReasonIdSeq', 'FailureReasons', 'idFailureReason' ),
+ ( 'BuildBlacklistIdSeq', 'BuildBlacklist', 'idBlacklisting' ),
+ ( 'BuildCategoryIdSeq', 'BuildCategories', 'idBuildCategory' ),
+ ( 'BuildIdSeq', 'Builds', 'idBuild' ),
+ ( 'TestResultStrTabIdSeq', 'TestResultStrTab', 'idStr' ),
+ ( 'TestResultIdSeq', 'TestResults', 'idTestResult' ),
+ ( 'TestResultValueIdSeq', 'TestResultValues', 'idTestResultValue' ),
+ ( 'TestResultFileId', 'TestResultFiles', 'idTestResultFile' ),
+ ( 'TestResultMsgIdSeq', 'TestResultMsgs', 'idTestResultMsg' ),
+ ( 'TestSetIdSeq', 'TestSets', 'idTestSet' ),
+ ( 'SchedQueueItemIdSeq', 'SchedQueues', 'idItem' ),
+ ];
+ for (sSeq, sTab, sCol) in atSequences:
+ oDb.execute('SELECT MAX(%s) FROM %s' % (sCol, sTab,));
+ idMax = oDb.fetchOne()[0];
+ print('%s: idMax=%s' % (sSeq, idMax));
+ if idMax is not None:
+ oDb.execute('SELECT setval(\'%s\', %s)' % (sSeq, idMax));
+
+ # Last step.
+ print('Analyzing...');
+ oDb.execute('ANALYZE');
+ oDb.commit();
+
+ print('Done!');
+ return 0;
+
+ def main(self):
+ """
+ Main function.
+ """
+ oDb = TMDatabaseConnection();
+
+ if self.oConfig.fLoadDumpIntoDatabase is not True:
+ rc = self._doDump(oDb);
+ else:
+ rc = self._doLoad(oDb);
+
+ oDb.close();
+ return 0;
+
+if __name__ == '__main__':
+ sys.exit(PartialDbDump().main());
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r01-builds-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r01-builds-1.pgsql
new file mode 100644
index 00000000..f8ab331d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r01-builds-1.pgsql
@@ -0,0 +1,91 @@
+-- $Id: tmdb-r01-builds-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Changed Builds to be historized.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+DROP TABLE OldBuilds;
+DROP TABLE NewBuilds;
+DROP INDEX BuildsLookupIdx;
+
+\set ON_ERROR_STOP 1
+
+--
+-- idBuild won't be unique, so it cannot be used directly as a foreign key
+-- by TestSets.
+--
+ALTER TABLE TestSets
+ DROP CONSTRAINT TestSets_idBuild_fkey;
+ALTER TABLE TestSets
+ DROP CONSTRAINT TestSets_idBuildTestSuite_fkey;
+
+
+--
+-- Create the table, filling it with the current Builds content.
+--
+CREATE TABLE NewBuilds (
+ idBuild INTEGER DEFAULT NEXTVAL('BuildIdSeq') NOT NULL,
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ uidAuthor INTEGER DEFAULT NULL,
+ idBuildCategory INTEGER REFERENCES BuildCategories(idBuildCategory) NOT NULL,
+ iRevision INTEGER NOT NULL,
+ sVersion TEXT NOT NULL,
+ sLogUrl TEXT,
+ sBinaries TEXT NOT NULL,
+ fBinariesDeleted BOOLEAN DEFAULT FALSE NOT NULL,
+ UNIQUE (idBuild, tsExpire)
+);
+
+INSERT INTO NewBuilds (idBuild, tsCreated, tsEffective, uidAuthor, idBuildCategory, iRevision, sVersion, sLogUrl, sBinaries)
+ SELECT idBuild, tsCreated, tsCreated, uidAuthor, idBuildCategory, iRevision, sVersion, sLogUrl, sBinaries
+ FROM Builds;
+COMMIT;
+
+-- Switch the tables.
+ALTER TABLE Builds RENAME TO OldBuilds;
+ALTER TABLE NewBuilds RENAME TO Builds;
+COMMIT;
+
+-- Finally index the table.
+CREATE INDEX BuildsLookupIdx ON Builds (idBuildCategory, iRevision);
+COMMIT;
+
+DROP TABLE OldBuilds;
+COMMIT;
+
+-- Fix implicit index name.
+ALTER INDEX newbuilds_idbuild_tsexpire_key RENAME TO builds_idbuild_tsexpire_key;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r02-testboxes-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r02-testboxes-1.pgsql
new file mode 100644
index 00000000..2cd75da0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r02-testboxes-1.pgsql
@@ -0,0 +1,194 @@
+-- $Id: tmdb-r02-testboxes-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds fCpu64BitGuest to TestBoxes
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+DROP TABLE OldTestBoxes;
+DROP TABLE NewTestBoxes;
+
+\d TestBoxes;
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+
+DROP INDEX TestBoxesUuidIdx;
+
+--
+-- Rename the original table, drop constrains and foreign key references so we
+-- get the right name automatic when creating the new one.
+--
+ALTER TABLE TestBoxes RENAME TO OldTestBoxes;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_ccpus_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbmemory_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbscratch_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pctscaletimeout_check;
+
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT TestBoxStatuses_idGenTestBox_fkey;
+ALTER TABLE TestSets DROP CONSTRAINT TestSets_idGenTestBox_fkey;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pkey;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_idgentestbox_key;
+
+--
+-- Create the new table, filling it with the current TestBoxes content.
+--
+CREATE TABLE TestBoxes (
+ --- The fixed testbox ID.
+ -- This is assigned when the testbox is created and will never change.
+ idTestBox INTEGER DEFAULT NEXTVAL('TestBoxIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- When modified automatically by the testbox, NULL is used.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER DEFAULT NULL,
+ --- Generation ID for this row.
+ -- This is primarily for referencing by TestSets.
+ idGenTestBox INTEGER UNIQUE DEFAULT NEXTVAL('TestBoxGenIdSeq') NOT NULL,
+
+ --- The testbox IP.
+ -- This is from the webserver point of view and automatically updated on
+ -- SIGNON. The test setup doesn't permit for IP addresses to change while
+ -- the testbox is operational, because this will break gang tests.
+ ip inet NOT NULL,
+ --- The system or firmware UUID.
+ -- This uniquely identifies the testbox when talking to the server. After
+ -- SIGNON though, the testbox will also provide idTestBox and ip to
+ -- establish its identity beyond doubt.
+ uuidSystem uuid NOT NULL,
+ --- The testbox name.
+ -- Usually similar to the DNS name.
+ sName text NOT NULL,
+ --- Optional testbox description.
+ -- Intended for describing the box as well as making other relevant notes.
+ sDescription text DEFAULT NULL,
+
+ --- Reference to the scheduling group that this testbox is a member of.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ -- A testbox is always part of a group, the default one nothing else.
+ idSchedGroup INTEGER DEFAULT 1 NOT NULL,
+
+ --- Indicates whether this testbox is enabled.
+ -- A testbox gets disabled when we're doing maintenance, debugging a issue
+ -- that happens only on that testbox, or some similar stuff. This is an
+ -- alternative to deleting the testbox.
+ fEnabled BOOLEAN DEFAULT NULL,
+
+ --- The kind of lights-out-management.
+ enmLomKind LomKind_T DEFAULT 'none'::LomKind_T NOT NULL,
+ --- The IP adress of the lights-out-management.
+ -- This can be NULL if enmLomKind is 'none', otherwise it must contain a valid address.
+ ipLom inet DEFAULT NULL,
+
+ --- Timeout scale factor, given as a percent.
+ -- This is a crude adjustment of the test case timeout for slower hardware.
+ pctScaleTimeout smallint DEFAULT 100 NOT NULL CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
+
+ --- @name Scheduling properties (reported by testbox script).
+ -- @{
+ --- Same abbrieviations as kBuild, see KBUILD_OSES.
+ sOs text DEFAULT NULL,
+ --- Informational, no fixed format.
+ sOsVersion text DEFAULT NULL,
+ --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
+ sCpuVendor text DEFAULT NULL,
+ --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
+ sCpuArch text DEFAULT NULL,
+ --- Number of CPUs, CPU cores and CPU threads.
+ cCpus smallint DEFAULT NULL CHECK (cCpus IS NULL OR cCpus > 0),
+ --- Set if capable of hardware virtualization.
+ fCpuHwVirt boolean DEFAULT NULL,
+ --- Set if capable of nested paging.
+ fCpuNestedPaging boolean DEFAULT NULL,
+ --- Set if CPU capable of 64-bit (VBox) guests.
+ fCpu64BitGuest boolean DEFAULT NULL,
+ --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
+ fChipsetIoMmu boolean DEFAULT NULL,
+ --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
+ cMbMemory bigint DEFAULT NULL CHECK (cMbMemory IS NULL OR cMbMemory > 0),
+ --- The amount of scratch space in megabytes (rounded down to nearest 64 MB).
+ cMbScratch bigint DEFAULT NULL CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
+ --- @}
+
+ --- The testbox script revision number, serves the purpose of a version number.
+ -- Probably good to have when scheduling upgrades as well for status purposes.
+ iTestBoxScriptRev INTEGER DEFAULT 0 NOT NULL,
+ --- The python sys.hexversion (layed out as of 2.7).
+ -- Good to know which python versions we need to support.
+ iPythonHexVersion INTEGER DEFAULT NULL,
+
+ --- Pending command.
+ -- @note We put it here instead of in TestBoxStatuses to get history.
+ enmPendingCmd TestBoxCmd_T DEFAULT 'none'::TestBoxCmd_T NOT NULL,
+
+ PRIMARY KEY (idTestBox, tsExpire),
+
+ --- Nested paging requires hardware virtualization.
+ CHECK (fCpuNestedPaging IS NULL OR (fCpuNestedPaging <> TRUE OR fCpuHwVirt = TRUE))
+);
+
+
+INSERT INTO TestBoxes ( idTestBox, tsEffective, tsExpire, uidAuthor, idGenTestBox, ip, uuidSystem, sName, sDescription,
+ idSchedGroup, fEnabled, enmLomKind, ipLom, pctScaleTimeout, sOs, sOsVersion, sCpuVendor, sCpuArch, cCpus, fCpuHwVirt,
+ fCpuNestedPaging, fCpu64BitGuest, fChipsetIoMmu, cMbMemory, cMbScratch, iTestBoxScriptRev, iPythonHexVersion,
+ enmPendingCmd )
+ SELECT idTestBox, tsEffective, tsExpire, uidAuthor, idGenTestBox, ip, uuidSystem, sName, sDescription,
+ idSchedGroup, fEnabled, enmLomKind, ipLom, pctScaleTimeout, sOs, sOsVersion, sCpuVendor, sCpuArch, cCpus, fCpuHwVirt,
+ fCpuNestedPaging, TRUE, fChipsetIoMmu, cMbMemory, cMbScratch, iTestBoxScriptRev, iPythonHexVersion,
+ enmPendingCmd
+ FROM OldTestBoxes;
+
+-- Add index.
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire);
+
+-- Restore foreign key references to the table.
+ALTER TABLE TestBoxStatuses ADD CONSTRAINT TestBoxStatuses_idGenTestBox_fkey FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets ADD CONSTRAINT TestSets_idGenTestBox_fkey FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+
+-- Drop the old table.
+DROP TABLE OldTestBoxes;
+
+COMMIT;
+
+\d TestBoxes;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r03-teststatus-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r03-teststatus-1.pgsql
new file mode 100644
index 00000000..2eed9f3c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r03-teststatus-1.pgsql
@@ -0,0 +1,48 @@
+-- $Id: tmdb-r03-teststatus-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds 'bad-testbox', 'aborted', and 'timeout' to TestStatus_T.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 1
+
+\dT+ TestStatus_T
+
+ALTER TYPE TestStatus_T ADD VALUE 'bad-testbox' BEFORE 'failure';
+ALTER TYPE TestStatus_T ADD VALUE 'aborted' BEFORE 'failure';
+ALTER TYPE TestStatus_T ADD VALUE 'timed-out' AFTER 'failure';
+
+\dT+ TestStatus_T
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r04-teststatus-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r04-teststatus-2.pgsql
new file mode 100644
index 00000000..fa82d36b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r04-teststatus-2.pgsql
@@ -0,0 +1,46 @@
+-- $Id: tmdb-r04-teststatus-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds 'rebooted' to TestStatus_T.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 1
+
+\dT+ TestStatus_T
+
+ALTER TYPE TestStatus_T ADD VALUE 'rebooted' AFTER 'timed-out';
+
+\dT+ TestStatus_T
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r05-teststatus-3.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r05-teststatus-3.pgsql
new file mode 100644
index 00000000..87656ca9
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r05-teststatus-3.pgsql
@@ -0,0 +1,54 @@
+-- $Id: tmdb-r05-teststatus-3.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds 'rebooted' to TestStatus_T.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResults
+
+ALTER TABLE TestResults
+ DROP CONSTRAINT CheckStatusMatchesErrors;
+ALTER TABLE TestResults
+ ADD CONSTRAINT CheckStatusMatchesErrors
+ CHECK ( (cErrors > 0 AND enmStatus IN ('running'::TestStatus_T,
+ 'failure'::TestStatus_T, 'timed-out'::TestStatus_T, 'rebooted'::TestStatus_T ))
+ OR (cErrors = 0 AND enmStatus IN ('running'::TestStatus_T, 'success'::TestStatus_T,
+ 'skipped'::TestStatus_T, 'aborted'::TestStatus_T, 'bad-testbox'::TestStatus_T))
+ );
+COMMIT;
+\d+ TestResults
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r06-buildsources-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r06-buildsources-1.pgsql
new file mode 100644
index 00000000..8b4213c0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r06-buildsources-1.pgsql
@@ -0,0 +1,46 @@
+-- $Id: tmdb-r06-buildsources-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds cMaxSecondsOld to BuildSources.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 1
+
+\d+ buildsources
+
+ALTER TABLE BuildSources ADD COLUMN cSecMaxAge INTEGER DEFAULT NULL;
+
+\d+ buildsources
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r07-testresults-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r07-testresults-1.pgsql
new file mode 100644
index 00000000..5ab20bc2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r07-testresults-1.pgsql
@@ -0,0 +1,47 @@
+-- $Id: tmdb-r07-testresults-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an index to TestResults.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResults
+
+CREATE INDEX TestResultsNameIdx ON TestResults (idStrName, idTestResult, tsCreated);
+COMMIT;
+
+\d+ TestResults
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r08-testresultvalues-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r08-testresultvalues-1.pgsql
new file mode 100644
index 00000000..5d963664
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r08-testresultvalues-1.pgsql
@@ -0,0 +1,47 @@
+-- $Id: tmdb-r08-testresultvalues-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an index to TestResultValues.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResultValues
+
+CREATE INDEX TestResultValuesNameIdx ON TestResultValues (idStrName, tsCreated);
+COMMIT;
+
+\d+ TestResultValues
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r09-testsets-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r09-testsets-1.pgsql
new file mode 100644
index 00000000..5f002dc4
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r09-testsets-1.pgsql
@@ -0,0 +1,48 @@
+-- $Id: tmdb-r09-testsets-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds two indexes to TestSets.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestSets
+
+CREATE INDEX TestSetsCreated ON TestSets (tsCreated);
+CREATE INDEX TestSetsDone ON TestSets (tsDone);
+COMMIT;
+
+\d+ TestSets
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r10-testresultvalues-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r10-testresultvalues-2.pgsql
new file mode 100644
index 00000000..ceb4a429
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r10-testresultvalues-2.pgsql
@@ -0,0 +1,111 @@
+-- $Id: tmdb-r10-testresultvalues-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an idTestSet to TestResultValues.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE NewTestResultValues;
+
+--
+-- Drop all indexes (might already be dropped).
+--
+DROP INDEX TestResultValuesIdx;
+DROP INDEX TestResultValuesNameIdx;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResultValues;
+
+--
+-- Create the new version of the table and filling with the content of the old.
+--
+CREATE TABLE NewTestResultValues (
+ --- The ID of this value.
+ idTestResultValue INTEGER DEFAULT NEXTVAL('TestResultValueIdSeq'), -- PRIMARY KEY
+ --- The test result it was reported within.
+ idTestResult INTEGER NOT NULL, -- REFERENCES TestResults(idTestResult) NOT NULL,
+ --- The test result it was reported within.
+ idTestSet INTEGER NOT NULL, -- REFERENCES TestSets(idTestSet) NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The name.
+ idStrName INTEGER NOT NULL, -- REFERENCES TestResultStrTab(idStr) NOT NULL,
+ --- The value.
+ lValue bigint NOT NULL,
+ --- The unit.
+ -- @todo This is currently not defined properly. Will fix/correlate this
+ -- with the other places we use unit (IPRT/testdriver/VMMDev).
+ iUnit smallint NOT NULL --CHECK (iUnit >= 0 AND iUnit < 1024)
+);
+COMMIT;
+\d+ NewTestResultValues
+
+-- Note! Using left out join here to speed up things (no hashing).
+SELECT COUNT(*) FROM TestResultValues a LEFT OUTER JOIN TestResults b ON (a.idTestResult = b.idTestResult);
+SELECT COUNT(*) FROM TestResultValues;
+
+INSERT INTO NewTestResultValues (idTestResultValue, idTestResult, idTestSet, tsCreated, idStrName, lValue, iUnit)
+ SELECT a.idTestResultValue, a.idTestResult, b.idTestSet, a.tsCreated, a.idStrName, a.lValue, a.iUnit
+ FROM TestResultValues a LEFT OUTER JOIN TestResults b ON (a.idTestResult = b.idTestResult);
+COMMIT;
+SELECT COUNT(*) FROM NewTestResultValues;
+
+-- Switch the tables.
+ALTER TABLE TestResultValues RENAME TO OldTestResultValues;
+ALTER TABLE NewTestResultValues RENAME TO TestResultValues;
+COMMIT;
+
+-- Index the table.
+CREATE INDEX TestResultValuesIdx ON TestResultValues(idTestResult);
+CREATE INDEX TestResultValuesNameIdx ON TestResultValues(idStrName, tsCreated);
+COMMIT;
+
+-- Drop the old table.
+DROP TABLE OldTestResultValues;
+COMMIT;
+
+-- Add the constraints constraint.
+ALTER TABLE TestResultValues ADD CONSTRAINT TestResultValues_iUnit_Check CHECK (iUnit >= 0 AND iUnit < 1024);
+ALTER TABLE TestResultValues ADD PRIMARY KEY (idTestResultValue);
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idStrName) REFERENCES TestResultstrtab(idStr);
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idTestResult) REFERENCES TestResults(idTestResult);
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet);
+COMMIT;
+
+\d+ TestResultValues;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r11-testsets-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r11-testsets-2.pgsql
new file mode 100644
index 00000000..1df2807f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r11-testsets-2.pgsql
@@ -0,0 +1,214 @@
+-- $Id: tmdb-r11-testsets-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an idBuildCategories to TestSets.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Drop all indexes (might already be dropped).
+--
+DROP INDEX TestSetsGangIdx;
+DROP INDEX TestSetsBoxIdx;
+DROP INDEX TestSetsBuildIdx;
+DROP INDEX TestSetsTestCaseIdx;
+DROP INDEX TestSetsTestVarIdx;
+DROP INDEX TestSetsCreated;
+DROP INDEX TestSetsDone;
+
+--
+-- Drop foreign keys on this table.
+--
+ALTER TABLE SchedQueues DROP CONSTRAINT SchedQueues_idTestSetGangLeader_fkey;
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT TestBoxStatuses_idTestSet_fkey;
+ALTER TABLE TestResults DROP CONSTRAINT idTestSetFk; -- old name
+ALTER TABLE TestResults DROP CONSTRAINT TestResults_idTestSet_fkey;
+ALTER TABLE TestResultValues DROP CONSTRAINT TestResultValues_idTestSet_fkey;
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE NewTestSets;
+DROP TABLE OldTestSets;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestSets;
+
+--
+-- Create the new version of the table and filling with the content of the old.
+--
+CREATE TABLE NewTestSets (
+ --- The ID of this test set.
+ idTestSet INTEGER DEFAULT NEXTVAL('TestSetIdSeq') NOT NULL, -- PRIMARY KEY
+
+ --- The test config timestamp, used when reading test config.
+ tsConfig TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test set was scheduled.
+ -- idGenTestBox is valid at this point.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test completed, i.e. testing stopped. This should only be set once.
+ tsDone TIMESTAMP WITH TIME ZONE DEFAULT NULL,
+ --- The current status.
+ enmStatus TestStatus_T DEFAULT 'running'::TestStatus_T NOT NULL,
+
+ --- The build we're testing.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuild INTEGER NOT NULL,
+ --- The build category of idBuild when the test started.
+ -- This is for speeding up graph data collection, i.e. avoid idBuild
+ -- the WHERE part of the selection.
+ idBuildCategory INTEGER , -- NOT NULL REFERENCES BuildCategories(idBuildCategory)
+ --- The test suite build we're using to do the testing.
+ -- This is NULL if the test suite zip wasn't referred or if a test suite
+ -- build source wasn't configured.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuildTestSuite INTEGER DEFAULT NULL,
+
+ --- The exact testbox configuration.
+ idGenTestBox INTEGER NOT NULL, -- REFERENCES TestBoxes(idGenTestBox)
+ --- The testbox ID for joining with (valid: tsStarted).
+ -- Non-unique foreign key: TestBoxes(idTestBox)
+ idTestBox INTEGER NOT NULL,
+
+ --- The testgroup (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestGroup)
+ -- Note! This also gives the member ship entry, since a testcase can only
+ -- have one membership per test group.
+ idTestGroup INTEGER NOT NULL,
+
+ --- The exact test case config we executed in this test run.
+ idGenTestCase INTEGER NOT NULL, -- REFERENCES TestCases(idGenTestCase)
+ --- The test case ID for joining with (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestCase)
+ idTestCase INTEGER NOT NULL,
+
+ --- The arguments (and requirements++) we executed this test case with.
+ idGenTestCaseArgs INTEGER NOT NULL, -- REFERENCES TestCaseArgs(idGenTestCaseArgs)
+ --- The argument variation ID (valid: tsConfig).
+ -- Non-unique foreign key: TestCaseArgs(idTestCaseArgs)
+ idTestCaseArgs INTEGER NOT NULL,
+
+ --- The root of the test result tree.
+ -- @note This will only be NULL early in the transaction setting up the testset.
+ -- @note If the test reports more than one top level test result, we'll
+ -- fail the whole test run and let the test developer fix it.
+ idTestResult INTEGER DEFAULT NULL, -- REFERENCES TestResults(idTestResult)
+
+ --- The base filename used for storing files related to this test set.
+ -- This is a path relative to wherever TM is dumping log files. In order
+ -- to not become a file system test case, we will try not to put too many
+ -- hundred thousand files in a directory. A simple first approach would
+ -- be to just use the current date (tsCreated) like this:
+ -- TM_FILE_DIR/year/month/day/TestSets.idTestSet
+ --
+ -- The primary log file for the test is this name suffixed by '.log'.
+ --
+ -- The files in the testresultfile table gets their full names like this:
+ -- TM_FILE_DIR/sBaseFilename-testresultfile.id-TestResultStrTab(testresultfile.idStrFilename)
+ --
+ -- @remarks We store this explicitly in case we change the directly layout
+ -- at some later point.
+ sBaseFilename text UNIQUE NOT NULL,
+
+ --- The gang member number number, 0 is the leader.
+ iGangMemberNo SMALLINT DEFAULT 0 NOT NULL, --CHECK (iGangMemberNo >= 0 AND iGangMemberNo < 1024),
+ --- The test set of the gang leader, NULL if no gang involved.
+ -- @note This is set by the gang leader as well, so that we can find all
+ -- gang members by WHERE idTestSetGangLeader = :id.
+ idTestSetGangLeader INTEGER DEFAULT NULL -- REFERENCES TestSets(idTestSet)
+
+);
+COMMIT;
+\d+ NewTestSets
+
+-- Note! Using left out join here to speed up things (no hashing).
+SELECT COUNT(*) FROM TestSets a LEFT OUTER JOIN Builds b ON (a.idBuild = b.idBuild AND b.tsExpire = 'infinity'::TIMESTAMP);
+SELECT COUNT(*) FROM TestSets;
+
+INSERT INTO NewTestSets (idTestSet, tsConfig, tsCreated, tsDone, enmStatus, idBuild, idBuildCategory, idBuildTestSuite,
+ idGenTestBox, idTestBox, idTestGroup, idGenTestCase, idTestCase, idGenTestCaseArgs, idTestCaseArgs,
+ idTestResult, sBaseFilename, iGangMemberNo, idTestSetGangLeader )
+ SELECT a.idTestSet, a.tsConfig, a.tsCreated, tsDone, a.enmStatus, a.idBuild, b.idBuildCategory, a.idBuildTestSuite,
+ a.idGenTestBox, a.idTestBox, a.idTestGroup, a.idGenTestCase, a.idTestCase, a.idGenTestCaseArgs, a.idTestCaseArgs,
+ a.idTestResult, a.sBaseFilename, a.iGangMemberNo, a.idTestSetGangLeader
+ FROM TestSets a LEFT OUTER JOIN Builds b ON (a.idBuild = b.idBuild AND b.tsExpire = 'infinity'::TIMESTAMP);
+COMMIT;
+SELECT COUNT(*) FROM NewTestSets;
+
+-- Note! 2-3 builds are missing from the Builds table, so fudge it.
+UPDATE NewTestSets
+ SET idBuildCategory = 1
+ WHERE idBuildCategory IS NULL;
+
+-- Switch the tables.
+ALTER TABLE TestSets RENAME TO OldTestSets;
+ALTER TABLE NewTestSets RENAME TO TestSets;
+COMMIT;
+
+-- Index the table.
+CREATE INDEX TestSetsGangIdx ON TestSets (idTestSetGangLeader);
+CREATE INDEX TestSetsBoxIdx ON TestSets (idTestBox, idTestResult);
+CREATE INDEX TestSetsBuildIdx ON TestSets (idBuild, idTestResult);
+CREATE INDEX TestSetsTestCaseIdx ON TestSets (idTestCase, idTestResult);
+CREATE INDEX TestSetsTestVarIdx ON TestSets (idTestCaseArgs, idTestResult);
+CREATE INDEX TestSetsCreated ON TestSets (tsCreated);
+CREATE INDEX TestSetsDone ON TestSets (tsDone);
+COMMIT;
+
+-- Drop the old table.
+DROP TABLE OldTestSets;
+COMMIT;
+
+-- Add the constraints constraint.
+ALTER TABLE TestSets ADD CONSTRAINT TestSets_iGangMemberNo_Check CHECK (iGangMemberNo >= 0 AND iGangMemberNo < 1024);
+ALTER TABLE TestSets ADD PRIMARY KEY (idTestSet);
+ALTER TABLE TestSets ADD FOREIGN KEY (idBuildCategory) REFERENCES BuildCategories(idBuildCategory);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestCase) REFERENCES TestCases(idGenTestCase);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestCaseArgs) REFERENCES TestCaseArgs(idGenTestCaseArgs);
+ALTER TABLE TestSets ADD FOREIGN KEY (idTestResult) REFERENCES TestResults(idTestResult);
+ALTER TABLE TestSets ADD FOREIGN KEY (idTestSetGangLeader) REFERENCES TestSets(idTestSet);
+COMMIT;
+
+-- Restore foreign keys.
+LOCK TABLE SchedQueues, TestBoxStatuses, TestResults, TestResultValues IN EXCLUSIVE MODE;
+ALTER TABLE SchedQueues ADD FOREIGN KEY (idTestSetGangLeader) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestBoxStatuses ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResults ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+COMMIT;
+
+\d+ TestSets;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r12-testresultvalues-3-testsets-3.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r12-testresultvalues-3-testsets-3.pgsql
new file mode 100644
index 00000000..57f48dea
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r12-testresultvalues-3-testsets-3.pgsql
@@ -0,0 +1,58 @@
+-- $Id: tmdb-r12-testresultvalues-3-testsets-3.pgsql $
+--- @file
+-- VBox Test Manager Database - Graph related optimizations for TestResultValues and TestSets.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResultValues
+
+-- Rename index to better show it's purpose.
+ALTER INDEX TestResultValuesNameIdx RENAME TO TestResultValuesGraphIdx;
+COMMIT;
+
+-- Combine the tsCreated and tsDone indexes.
+DROP INDEX TestSetsCreated;
+DROP INDEX TestSetsDone;
+CREATE INDEX TestSetsCreatedDoneIdx ON TestSets (tsCreated, tsDone);
+COMMIT;
+
+-- Create index for graph.
+CREATE INDEX TestSetsGraphBoxIdx ON TestSets (idTestBox, tsCreated, tsDone, idBuildCategory, idTestCase);
+COMMIT;
+
+\d+ TestResultValues
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r13-buildcategories-1-vcsrevisions-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r13-buildcategories-1-vcsrevisions-1.pgsql
new file mode 100644
index 00000000..0dfdd8c7
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r13-buildcategories-1-vcsrevisions-1.pgsql
@@ -0,0 +1,134 @@
+-- $Id: tmdb-r13-buildcategories-1-vcsrevisions-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an sRepository to Builds and creates a new VcsRepositories table.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE NewBuildCategories;
+DROP TABLE OldBuildCategories;
+
+--
+-- Drop foreign keys on this table.
+--
+ALTER TABLE Builds DROP CONSTRAINT NewBuilds_idBuildCategory_fkey;
+ALTER TABLE Builds DROP CONSTRAINT Builds_idBuildCategory_fkey;
+ALTER TABLE TestSets DROP CONSTRAINT TestSets_idBuildCategory_fkey;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ BuildCategories;
+
+--
+-- Create the new version of the table and filling with the content of the old.
+--
+CREATE TABLE NewBuildCategories (
+ --- The build type identifier.
+ idBuildCategory INTEGER PRIMARY KEY DEFAULT NEXTVAL('BuildCategoryIdSeq') NOT NULL,
+ --- Product.
+ -- The product name. For instance 'VBox' or 'VBoxTestSuite'.
+ sProduct TEXT NOT NULL,
+ --- The version control repository name.
+ sRepository TEXT NOT NULL,
+ --- The branch name (in the version control system).
+ sBranch TEXT NOT NULL,
+ --- The build type.
+ -- See KBUILD_BLD_TYPES in kBuild for a list of standard build types.
+ sType TEXT NOT NULL,
+ --- Array of the 'sOs.sCpuArch' supported by the build.
+ -- See KBUILD_OSES in kBuild for a list of standard target OSes, and
+ -- KBUILD_ARCHES for a list of standard architectures.
+ --
+ -- @remarks 'os-agnostic' is used if the build doesn't really target any
+ -- specific OS or if it targets all applicable OSes.
+ -- 'noarch' is used if the build is architecture independent or if
+ -- all applicable architectures are handled.
+ -- Thus, 'os-agnostic.noarch' will run on all build boxes.
+ --
+ -- @note The array shall be sorted ascendingly to prevent unnecessary duplicates!
+ --
+ asOsArches TEXT ARRAY NOT NULL,
+
+ UNIQUE (sProduct, sRepository, sBranch, sType, asOsArches)
+);
+COMMIT;
+\d+ NewBuildCategories
+
+INSERT INTO NewBuildCategories (idBuildCategory, sProduct, sRepository, sBranch, sType, asOsArches)
+ SELECT idBuildCategory, sProduct, 'vbox', sBranch, sType, asOsArches
+ FROM BuildCategories
+COMMIT;
+
+-- Switch the tables.
+ALTER TABLE BuildCategories RENAME TO OldBuildCategories;
+ALTER TABLE NewBuildCategories RENAME TO BuildCategories;
+COMMIT;
+
+-- Drop the old table.
+DROP TABLE OldBuildCategories;
+COMMIT;
+
+-- Restore foreign keys.
+LOCK TABLE Builds, TestSets;
+ALTER TABLE Builds ADD FOREIGN KEY (idBuildCategory) REFERENCES BuildCategories(idBuildCategory);
+ALTER TABLE TestSets ADD FOREIGN KEY (idBuildCategory) REFERENCES BuildCategories(idBuildCategory);
+COMMIT;
+
+\d+ BuildCategories;
+
+
+--
+-- Create the new VcsRevisions table.
+--
+CREATE TABLE VcsRevisions (
+ --- The version control tree name.
+ sRepository TEXT NOT NULL,
+ --- The version control tree revision number.
+ iRevision INTEGER NOT NULL,
+ --- When the revision was created (committed).
+ tsCreated TIMESTAMP WITH TIME ZONE NOT NULL,
+ --- The name of the committer.
+ -- @note Not to be confused with uidAuthor and test manager users.
+ sAuthor TEXT,
+ --- The commit message.
+ sMessage TEXT,
+
+ UNIQUE (sRepository, iRevision)
+);
+COMMIT;
+\d+ VcsRevisions;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r14-testboxes-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r14-testboxes-2.pgsql
new file mode 100644
index 00000000..784398b5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r14-testboxes-2.pgsql
@@ -0,0 +1,201 @@
+-- $Id: tmdb-r14-testboxes-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds sCpuName, lCpuRevision and sReport to TestBoxes.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+DROP TABLE OldTestBoxes;
+DROP TABLE NewTestBoxes;
+
+\d TestBoxes;
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+
+DROP INDEX TestBoxesUuidIdx;
+
+--
+-- Rename the original table, drop constrains and foreign key references so we
+-- get the right name automatic when creating the new one.
+--
+ALTER TABLE TestBoxes RENAME TO OldTestBoxes;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_ccpus_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbmemory_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbscratch_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pctscaletimeout_check;
+
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT TestBoxStatuses_idGenTestBox_fkey;
+ALTER TABLE TestSets DROP CONSTRAINT TestSets_idGenTestBox_fkey;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pkey;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_idgentestbox_key;
+
+--
+-- Create the new table, filling it with the current TestBoxes content.
+--
+CREATE TABLE TestBoxes (
+ --- The fixed testbox ID.
+ -- This is assigned when the testbox is created and will never change.
+ idTestBox INTEGER DEFAULT NEXTVAL('TestBoxIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- When modified automatically by the testbox, NULL is used.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER DEFAULT NULL,
+ --- Generation ID for this row.
+ -- This is primarily for referencing by TestSets.
+ idGenTestBox INTEGER UNIQUE DEFAULT NEXTVAL('TestBoxGenIdSeq') NOT NULL,
+
+ --- The testbox IP.
+ -- This is from the webserver point of view and automatically updated on
+ -- SIGNON. The test setup doesn't permit for IP addresses to change while
+ -- the testbox is operational, because this will break gang tests.
+ ip inet NOT NULL,
+ --- The system or firmware UUID.
+ -- This uniquely identifies the testbox when talking to the server. After
+ -- SIGNON though, the testbox will also provide idTestBox and ip to
+ -- establish its identity beyond doubt.
+ uuidSystem uuid NOT NULL,
+ --- The testbox name.
+ -- Usually similar to the DNS name.
+ sName text NOT NULL,
+ --- Optional testbox description.
+ -- Intended for describing the box as well as making other relevant notes.
+ sDescription text DEFAULT NULL,
+
+ --- Reference to the scheduling group that this testbox is a member of.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ -- A testbox is always part of a group, the default one nothing else.
+ idSchedGroup INTEGER DEFAULT 1 NOT NULL,
+
+ --- Indicates whether this testbox is enabled.
+ -- A testbox gets disabled when we're doing maintenance, debugging a issue
+ -- that happens only on that testbox, or some similar stuff. This is an
+ -- alternative to deleting the testbox.
+ fEnabled BOOLEAN DEFAULT NULL,
+
+ --- The kind of lights-out-management.
+ enmLomKind LomKind_T DEFAULT 'none'::LomKind_T NOT NULL,
+ --- The IP adress of the lights-out-management.
+ -- This can be NULL if enmLomKind is 'none', otherwise it must contain a valid address.
+ ipLom inet DEFAULT NULL,
+
+ --- Timeout scale factor, given as a percent.
+ -- This is a crude adjustment of the test case timeout for slower hardware.
+ pctScaleTimeout smallint DEFAULT 100 NOT NULL CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
+
+ --- @name Scheduling properties (reported by testbox script).
+ -- @{
+ --- Same abbrieviations as kBuild, see KBUILD_OSES.
+ sOs text DEFAULT NULL,
+ --- Informational, no fixed format.
+ sOsVersion text DEFAULT NULL,
+ --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
+ sCpuVendor text DEFAULT NULL,
+ --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
+ sCpuArch text DEFAULT NULL,
+ --- The CPU name if available.
+ sCpuName text DEFAULT NULL,
+ --- Number identifying the CPU family/model/stepping/whatever.
+ -- For x86 and AMD64 type CPUs, this will on the following format:
+ -- (EffFamily << 24) | (EffModel << 8) | Stepping.
+ lCpuRevision bigint DEFAULT NULL,
+ --- Number of CPUs, CPU cores and CPU threads.
+ cCpus smallint DEFAULT NULL CHECK (cCpus IS NULL OR cCpus > 0),
+ --- Set if capable of hardware virtualization.
+ fCpuHwVirt boolean DEFAULT NULL,
+ --- Set if capable of nested paging.
+ fCpuNestedPaging boolean DEFAULT NULL,
+ --- Set if CPU capable of 64-bit (VBox) guests.
+ fCpu64BitGuest boolean DEFAULT NULL,
+ --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
+ fChipsetIoMmu boolean DEFAULT NULL,
+ --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
+ cMbMemory bigint DEFAULT NULL CHECK (cMbMemory IS NULL OR cMbMemory > 0),
+ --- The amount of scratch space in megabytes (rounded down to nearest 64 MB).
+ cMbScratch bigint DEFAULT NULL CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
+ --- Free form hardware and software report field.
+ sReport text DEFAULT NULL,
+ --- @}
+
+ --- The testbox script revision number, serves the purpose of a version number.
+ -- Probably good to have when scheduling upgrades as well for status purposes.
+ iTestBoxScriptRev INTEGER DEFAULT 0 NOT NULL,
+ --- The python sys.hexversion (layed out as of 2.7).
+ -- Good to know which python versions we need to support.
+ iPythonHexVersion INTEGER DEFAULT NULL,
+
+ --- Pending command.
+ -- @note We put it here instead of in TestBoxStatuses to get history.
+ enmPendingCmd TestBoxCmd_T DEFAULT 'none'::TestBoxCmd_T NOT NULL,
+
+ PRIMARY KEY (idTestBox, tsExpire),
+
+ --- Nested paging requires hardware virtualization.
+ CHECK (fCpuNestedPaging IS NULL OR (fCpuNestedPaging <> TRUE OR fCpuHwVirt = TRUE))
+);
+
+INSERT INTO TestBoxes ( idTestBox, tsEffective, tsExpire, uidAuthor, idGenTestBox, ip, uuidSystem, sName, sDescription,
+ idSchedGroup, fEnabled, enmLomKind, ipLom, pctScaleTimeout, sOs, sOsVersion, sCpuVendor, sCpuArch, sCpuName,
+ lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest, fChipsetIoMmu, cMbMemory, cMbScratch, sReport,
+ iTestBoxScriptRev, iPythonHexVersion, enmPendingCmd )
+ SELECT idTestBox, tsEffective, tsExpire, uidAuthor, idGenTestBox, ip, uuidSystem, sName, sDescription,
+ idSchedGroup, fEnabled, enmLomKind, ipLom, pctScaleTimeout, sOs, sOsVersion, sCpuVendor, sCpuArch, NULL,
+ NULL, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest, fChipsetIoMmu, cMbMemory, cMbScratch, NULL,
+ iTestBoxScriptRev, iPythonHexVersion, enmPendingCmd
+ FROM OldTestBoxes;
+
+-- Add index.
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire);
+
+-- Restore foreign key references to the table.
+ALTER TABLE TestBoxStatuses ADD CONSTRAINT TestBoxStatuses_idGenTestBox_fkey FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets ADD CONSTRAINT TestSets_idGenTestBox_fkey FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+
+-- Drop the old table.
+DROP TABLE OldTestBoxes;
+
+COMMIT;
+
+\d TestBoxes;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r15-index-sorting.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r15-index-sorting.pgsql
new file mode 100644
index 00000000..b95dff80
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r15-index-sorting.pgsql
@@ -0,0 +1,108 @@
+-- $Id: tmdb-r15-index-sorting.pgsql $
+--- @file
+-- VBox Test Manager Database - Index tuning effort.
+--
+
+--
+-- Copyright (C) 2015-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+--
+-- Reordered, modified and new indexes.
+--
+\d UsersLoginNameIdx;
+DROP INDEX UsersLoginNameIdx;
+CREATE INDEX UsersLoginNameIdx ON Users (sLoginName, tsExpire DESC);
+\d UsersLoginNameIdx;
+ANALYZE VERBOSE Users;
+
+
+\d TestCaseArgsLookupIdx;
+DROP INDEX TestCaseArgsLookupIdx;
+CREATE INDEX TestCaseArgsLookupIdx ON TestCaseArgs (idTestCase, tsExpire DESC, tsEffective ASC);
+\d TestCaseArgsLookupIdx;
+ANALYZE VERBOSE TestCaseArgs;
+
+
+\d TestGroups_id_index;
+DROP INDEX TestGroups_id_index;
+CREATE INDEX TestGroups_id_index ON TestGroups (idTestGroup, tsExpire DESC, tsEffective ASC);
+\d TestGroups_id_index;
+ANALYZE VERBOSE TestGroups;
+
+
+\d TestBoxesUuidIdx;
+DROP INDEX TestBoxesUuidIdx;
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire DESC);
+\d TestBoxesUuidIdx;
+DROP INDEX IF EXISTS TestBoxesExpireEffectiveIdx;
+CREATE INDEX TestBoxesExpireEffectiveIdx ON TestBoxes (tsExpire DESC, tsEffective ASC);
+\d TestBoxesExpireEffectiveIdx;
+ANALYZE VERBOSE TestBoxes;
+
+
+DROP INDEX IF EXISTS BuildBlacklistIdx;
+CREATE INDEX BuildBlacklistIdx ON BuildBlacklist (iLastRevision DESC, iFirstRevision ASC, sProduct, sBranch,
+ tsExpire DESC, tsEffective ASC);
+\d BuildBlacklist;
+ANALYZE VERBOSE BuildBlacklist;
+
+
+\d TestResultsNameIdx;
+DROP INDEX TestResultsNameIdx;
+CREATE INDEX TestResultsNameIdx ON TestResults (idStrName, tsCreated DESC);
+\d TestResultsNameIdx;
+DROP INDEX IF EXISTS TestResultsNameIdx2;
+CREATE INDEX TestResultsNameIdx2 ON TestResults (idTestResult, idStrName);
+\d TestResultsNameIdx2;
+ANALYZE VERBOSE TestResults;
+
+
+\d TestSetsCreatedDoneIdx;
+DROP INDEX TestSetsCreatedDoneIdx;
+DROP INDEX IF EXISTS TestSetsDoneCreatedBuildCatIdx;
+CREATE INDEX TestSetsDoneCreatedBuildCatIdx ON TestSets (tsDone DESC NULLS FIRST, tsCreated ASC, idBuildCategory);
+\d TestSetsDoneCreatedBuildCatIdx;
+\d TestSetsGraphBoxIdx;
+DROP INDEX TestSetsGraphBoxIdx;
+CREATE INDEX TestSetsGraphBoxIdx ON TestSets (idTestBox, tsCreated DESC, tsDone ASC NULLS LAST, idBuildCategory, idTestCase);
+\d TestSetsGraphBoxIdx;
+ANALYZE VERBOSE TestSets;
+
+
+DROP INDEX IF EXISTS SchedQueuesItemIdx;
+CREATE INDEX SchedQueuesItemIdx ON SchedQueues(idItem);
+\d SchedQueuesItemIdx;
+DROP INDEX IF EXISTS SchedQueuesSchedGroupIdx;
+CREATE INDEX SchedQueuesSchedGroupIdx ON SchedQueues(idSchedGroup);
+\d SchedQueuesSchedGroupIdx;
+ANALYZE VERBOSE SchedQueues;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r16-testcaseargs-1-testresultfailures-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r16-testcaseargs-1-testresultfailures-1.pgsql
new file mode 100644
index 00000000..108da811
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r16-testcaseargs-1-testresultfailures-1.pgsql
@@ -0,0 +1,122 @@
+-- $Id: tmdb-r16-testcaseargs-1-testresultfailures-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds sName to TestCaseArgs, idTestSet
+-- to TestResultFailures and add some indexes to the latter as well.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+DROP TABLE OldTestCaseArgs;
+DROP TABLE NewTestCaseArgs;
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestCaseArgs IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResultFailures IN ACCESS EXCLUSIVE MODE;
+
+--
+-- TestCaseArgs is simple and we can use ALTER TABLE for a change.
+--
+\d TestCaseArgs;
+ALTER TABLE TestCaseArgs ADD COLUMN sSubName text DEFAULT NULL;
+\d TestCaseArgs;
+
+
+--
+-- Rename the original table, drop constrains and foreign key references so we
+-- get the right name automatic when creating the new one.
+--
+\d TestResultFailures;
+ALTER TABLE TestResultFailures DROP CONSTRAINT idTestResultFk;
+ALTER TABLE TestResultFailures RENAME TO OldTestResultFailures;
+
+DROP INDEX IF EXISTS TestResultFailureIdx;
+DROP INDEX IF EXISTS TestResultFailureIdx2;
+DROP INDEX IF EXISTS TestResultFailureIdx3;
+
+
+CREATE TABLE TestResultFailures (
+ --- The test result we're disucssing.
+ -- @note The foreign key is declared after TestResults (further down).
+ idTestResult INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+ --- The testsest this result is a part of.
+ -- This is mainly an aid for bypassing the enormous TestResults table.
+ -- Note! This is a foreign key, but we have to add it after TestSets has
+ -- been created, see further down.
+ idTestSet INTEGER NOT NULL,
+
+ --- The suggested failure reason.
+ -- Non-unique foreign key: FailureReasons(idFailureReason)
+ idFailureReason INTEGER NOT NULL,
+ --- Optional comment.
+ sComment text DEFAULT NULL,
+
+ PRIMARY KEY (idTestResult, tsExpire)
+);
+
+INSERT INTO TestResultFailures ( idTestResult, tsEffective, tsExpire, uidAuthor, idTestSet, idFailureReason, sComment )
+ SELECT o.idTestResult, o.tsEffective, o.tsExpire, o.uidAuthor, tr.idTestSet, o.idFailureReason, sComment
+ FROM OldTestResultFailures o,
+ TestResults tr
+ WHERE o.idTestResult = tr.idTestResult;
+
+-- Add unique constraint to TestResult for our new foreign key.
+ALTER TABLE TestResults ADD CONSTRAINT TestResults_idTestResult_idTestSet_key UNIQUE (idTestResult, idTestSet);
+
+-- Restore foreign key.
+ALTER TABLE TestResultFailures ADD CONSTRAINT TestResultFailures_idTestResult_idTestSet_fkey
+ FOREIGN KEY (idTestResult, idTestSet) REFERENCES TestResults(idTestResult, idTestSet) MATCH FULL;
+
+-- Add new indexes.
+CREATE INDEX TestResultFailureIdx ON TestResultFailures (idTestSet, tsExpire DESC, tsEffective ASC);
+CREATE INDEX TestResultFailureIdx2 ON TestResultFailures (idTestResult, tsExpire DESC, tsEffective ASC);
+CREATE INDEX TestResultFailureIdx3 ON TestResultFailures (idFailureReason, idTestResult, tsExpire DESC, tsEffective ASC);
+
+-- Drop the old table.
+DROP TABLE OldTestResultFailures;
+
+COMMIT;
+
+\d TestResultFailures;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r17-testresultvalues-4.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r17-testresultvalues-4.pgsql
new file mode 100644
index 00000000..d97954b6
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r17-testresultvalues-4.pgsql
@@ -0,0 +1,48 @@
+-- $Id: tmdb-r17-testresultvalues-4.pgsql $
+--- @file
+-- VBox Test Manager Database - Log viewer related optimizations for TestResultValues.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+\d+ TestResultValues
+
+-- Create index for the log viewer
+CREATE INDEX TestResultValuesLogIdx ON TestResultValues(idTestSet, tsCreated);
+COMMIT;
+
+\d+ TestResultValues
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r18-testresultfiles-1-testresultmsgs-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r18-testresultfiles-1-testresultmsgs-1.pgsql
new file mode 100644
index 00000000..88788273
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r18-testresultfiles-1-testresultmsgs-1.pgsql
@@ -0,0 +1,171 @@
+-- $Id: tmdb-r18-testresultfiles-1-testresultmsgs-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an idTestSet to TestResultFiles and TestResultMsgs.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS NewTestResultFiles;
+DROP TABLE IF EXISTS OldTestResultFiles;
+DROP TABLE IF EXISTS NewTestResultMsgs;
+DROP TABLE IF EXISTS OldTestResultMsgs;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+
+--
+-- Rename the original table, drop constrains and foreign key references so we
+-- get the right name automatic when creating the new one.
+--
+\d+ TestResultFiles;
+ALTER TABLE TestResultFiles RENAME TO OldTestResultFiles;
+
+DROP INDEX IF EXISTS TestResultFilesIdx;
+DROP INDEX IF EXISTS TestResultFilesIdx2;
+
+--
+-- Create the new version of the table and filling with the content of the old.
+--
+CREATE TABLE TestResultFiles (
+ --- The ID of this file.
+ idTestResultFile INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultFileId'),
+ --- The test result it was reported within.
+ idTestResult INTEGER NOT NULL,
+ --- The test set this file is a part of (for avoiding joining thru TestResults).
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The filename relative to TestSets(sBaseFilename) + '-'.
+ -- The set of valid filename characters should be very limited so that no
+ -- file system issues can occure either on the TM side or the user when
+ -- loading the files. Tests trying to use other characters will fail.
+ -- Valid character regular expession: '^[a-zA-Z0-9_-(){}#@+,.=]*$'
+ idStrFile INTEGER NOT NULL,
+ --- The description.
+ idStrDescription INTEGER NOT NULL,
+ --- The kind of file.
+ -- For instance: 'log/release/vm',
+ -- 'screenshot/failure',
+ -- 'screencapture/failure',
+ -- 'xmllog/somestuff'
+ idStrKind INTEGER NOT NULL,
+ --- The mime type for the file.
+ -- For instance: 'text/plain',
+ -- 'image/png',
+ -- 'video/webm',
+ -- 'text/xml'
+ idStrMime INTEGER NOT NULL
+);
+
+INSERT INTO TestResultFiles ( idTestResultFile, idTestResult, idTestSet, tsCreated, idStrFile, idStrDescription,
+ idStrKind, idStrMime)
+ SELECT o.idTestResultFile, o.idTestResult, tr.idTestSet, o.tsCreated, o.idStrFile, o.idStrDescription,
+ o.idStrKind, o.idStrMime
+ FROM OldTestResultFiles o,
+ TestResults tr
+ WHERE o.idTestResult = tr.idTestResult;
+
+-- Add new indexes.
+CREATE INDEX TestResultFilesIdx ON TestResultFiles(idTestResult);
+CREATE INDEX TestResultFilesIdx2 ON TestResultFiles(idTestSet, tsCreated DESC);
+
+-- Restore foreign keys.
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idTestResult_fkey FOREIGN KEY(idTestResult) REFERENCES TestResults(idTestResult);
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idTestSet_fkey FOREIGN KEY(idTestSet) REFERENCES TestSets(idTestSet);
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idStrFile_fkey FOREIGN KEY(idStrFile) REFERENCES TestResultStrTab(idStr);
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idStrDescription_fkey FOREIGN KEY(idStrDescription) REFERENCES TestResultStrTab(idStr);
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idStrKind_fkey FOREIGN KEY(idStrKind) REFERENCES TestResultStrTab(idStr);
+ALTER TABLE TestResultFiles ADD CONSTRAINT TestResultFiles_idStrMime_fkey FOREIGN KEY(idStrMime) REFERENCES TestResultStrTab(idStr);
+
+\d TestResultFiles;
+
+
+--
+-- Rename the original table, drop constrains and foreign key references so we
+-- get the right name automatic when creating the new one.
+--
+\d+ TestResultMsgs;
+ALTER TABLE TestResultMsgs RENAME TO OldTestResultMsgs;
+
+DROP INDEX IF EXISTS TestResultMsgsIdx;
+DROP INDEX IF EXISTS TestResultMsgsIdx2;
+
+--
+-- Create the new version of the table and filling with the content of the old.
+--
+CREATE TABLE TestResultMsgs (
+ --- The ID of this file.
+ idTestResultMsg INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestResultMsgIdSeq'),
+ --- The test result it was reported within.
+ idTestResult INTEGER NOT NULL,
+ --- The test set this file is a part of (for avoiding joining thru TestResults).
+ idTestSet INTEGER NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- The message string.
+ idStrMsg INTEGER NOT NULL,
+ --- The message level.
+ enmLevel TestResultMsgLevel_T NOT NULL
+);
+
+INSERT INTO TestResultMsgs ( idTestResultMsg, idTestResult, idTestSet, tsCreated, idStrMsg, enmLevel)
+ SELECT o.idTestResultMsg, o.idTestResult, tr.idTestSet, o.tsCreated, o.idStrMsg, o.enmLevel
+ FROM OldTestResultMsgs o,
+ TestResults tr
+ WHERE o.idTestResult = tr.idTestResult;
+
+-- Add new indexes.
+CREATE INDEX TestResultMsgsIdx ON TestResultMsgs(idTestResult);
+CREATE INDEX TestResultMsgsIdx2 ON TestResultMsgs(idTestSet, tsCreated DESC);
+
+-- Restore foreign keys.
+ALTER TABLE TestResultMsgs ADD CONSTRAINT TestResultMsgs_idTestResult_fkey FOREIGN KEY(idTestResult) REFERENCES TestResults(idTestResult);
+ALTER TABLE TestResultMsgs ADD CONSTRAINT TestResultMsgs_idTestSet_fkey FOREIGN KEY(idTestSet) REFERENCES TestSets(idTestSet);
+ALTER TABLE TestResultMsgs ADD CONSTRAINT TestResultMsgs_idStrMsg_fkey FOREIGN KEY(idStrMsg) REFERENCES TestResultStrTab(idStr);
+
+
+\d TestResultMsgs;
+
+
+--
+-- Drop the old tables and commit.
+--
+DROP TABLE OldTestResultFiles;
+DROP TABLE OldTestResultMsgs;
+
+COMMIT;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql
new file mode 100644
index 00000000..26228a9d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql
@@ -0,0 +1,356 @@
+-- $Id: tmdb-r19-testboxes-3.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds sComment and fRawMode to TestBoxes and
+-- moves the strings to separate table.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS OldTestBoxes;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+-- Sanity check that we haven't already run this script.
+SELECT 'done conversion already?', COUNT(sReport) FROM TestBoxes WHERE tsExpire = 'infinity'::TIMESTAMP;
+
+-- Total grid lock.
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroupMembers IN ACCESS EXCLUSIVE MODE;
+
+\d+ TestBoxes;
+
+--
+-- Rename the table, drop foreign keys refering to it, and drop constrains
+-- within the table itself. The latter is mostly for naming and we do it
+-- up front in case the database we're running against has different names
+-- due to previous conversions.
+--
+ALTER TABLE TestBoxes RENAME TO OldTestBoxes;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_ccpus_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbmemory_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_cmbscratch_check;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pctscaletimeout_check;
+
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT TestBoxStatuses_idGenTestBox_fkey;
+ALTER TABLE TestSets DROP CONSTRAINT TestSets_idGenTestBox_fkey;
+
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_pkey;
+ALTER TABLE OldTestBoxes DROP CONSTRAINT testboxes_idgentestbox_key;
+
+DROP INDEX IF EXISTS TestBoxesUuidIdx;
+DROP INDEX IF EXISTS TestBoxesExpireEffectiveIdx;
+
+-- This output should be free of index, constraints and references from other tables.
+\d+ OldTestBoxes;
+
+--
+-- Create the two new tables before starting data migration (don't want to spend time
+-- on converting strings just to find a typo in the TestBoxes create table syntax).
+--
+CREATE SEQUENCE TestBoxStrTabIdSeq
+ START 1
+ INCREMENT BY 1
+ NO MAXVALUE
+ NO MINVALUE
+ CACHE 1;
+CREATE TABLE TestBoxStrTab (
+ --- The ID of this string.
+ idStr INTEGER PRIMARY KEY DEFAULT NEXTVAL('TestBoxStrTabIdSeq'),
+ --- The string value.
+ sValue text NOT NULL,
+ --- Creation time stamp.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL
+);
+
+CREATE TABLE TestBoxes (
+ --- The fixed testbox ID.
+ -- This is assigned when the testbox is created and will never change.
+ idTestBox INTEGER DEFAULT NEXTVAL('TestBoxIdSeq') NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- When modified automatically by the testbox, NULL is used.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER DEFAULT NULL,
+ --- Generation ID for this row.
+ -- This is primarily for referencing by TestSets.
+ idGenTestBox INTEGER UNIQUE DEFAULT NEXTVAL('TestBoxGenIdSeq') NOT NULL,
+
+ --- The testbox IP.
+ -- This is from the webserver point of view and automatically updated on
+ -- SIGNON. The test setup doesn't permit for IP addresses to change while
+ -- the testbox is operational, because this will break gang tests.
+ ip inet NOT NULL,
+ --- The system or firmware UUID.
+ -- This uniquely identifies the testbox when talking to the server. After
+ -- SIGNON though, the testbox will also provide idTestBox and ip to
+ -- establish its identity beyond doubt.
+ uuidSystem uuid NOT NULL,
+ --- The testbox name.
+ -- Usually similar to the DNS name.
+ sName text NOT NULL,
+ --- Optional testbox description.
+ -- Intended for describing the box as well as making other relevant notes.
+ idStrDescription INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+
+ --- Reference to the scheduling group that this testbox is a member of.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ -- A testbox is always part of a group, the default one nothing else.
+ idSchedGroup INTEGER DEFAULT 1 NOT NULL,
+
+ --- Indicates whether this testbox is enabled.
+ -- A testbox gets disabled when we're doing maintenance, debugging a issue
+ -- that happens only on that testbox, or some similar stuff. This is an
+ -- alternative to deleting the testbox.
+ fEnabled BOOLEAN DEFAULT NULL,
+
+ --- The kind of lights-out-management.
+ enmLomKind LomKind_T DEFAULT 'none'::LomKind_T NOT NULL,
+ --- The IP adress of the lights-out-management.
+ -- This can be NULL if enmLomKind is 'none', otherwise it must contain a valid address.
+ ipLom inet DEFAULT NULL,
+
+ --- Timeout scale factor, given as a percent.
+ -- This is a crude adjustment of the test case timeout for slower hardware.
+ pctScaleTimeout smallint DEFAULT 100 NOT NULL CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
+
+ --- Change comment or similar.
+ idStrComment INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+
+ --- @name Scheduling properties (reported by testbox script).
+ -- @{
+ --- Same abbrieviations as kBuild, see KBUILD_OSES.
+ idStrOs INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Informational, no fixed format.
+ idStrOsVersion INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
+ idStrCpuVendor INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
+ idStrCpuArch INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- The CPU name if available.
+ idStrCpuName INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- Number identifying the CPU family/model/stepping/whatever.
+ -- For x86 and AMD64 type CPUs, this will on the following format:
+ -- (EffFamily << 24) | (EffModel << 8) | Stepping.
+ lCpuRevision bigint DEFAULT NULL,
+ --- Number of CPUs, CPU cores and CPU threads.
+ cCpus smallint DEFAULT NULL CHECK (cCpus IS NULL OR cCpus > 0),
+ --- Set if capable of hardware virtualization.
+ fCpuHwVirt boolean DEFAULT NULL,
+ --- Set if capable of nested paging.
+ fCpuNestedPaging boolean DEFAULT NULL,
+ --- Set if CPU capable of 64-bit (VBox) guests.
+ fCpu64BitGuest boolean DEFAULT NULL,
+ --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
+ fChipsetIoMmu boolean DEFAULT NULL,
+ --- Set if the test box does raw-mode tests.
+ fRawMode boolean DEFAULT NULL,
+ --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
+ cMbMemory bigint DEFAULT NULL CHECK (cMbMemory IS NULL OR cMbMemory > 0),
+ --- The amount of scratch space in megabytes (rounded down to nearest 64 MB).
+ cMbScratch bigint DEFAULT NULL CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
+ --- Free form hardware and software report field.
+ idStrReport INTEGER REFERENCES TestBoxStrTab(idStr) DEFAULT NULL,
+ --- @}
+
+ --- The testbox script revision number, serves the purpose of a version number.
+ -- Probably good to have when scheduling upgrades as well for status purposes.
+ iTestBoxScriptRev INTEGER DEFAULT 0 NOT NULL,
+ --- The python sys.hexversion (layed out as of 2.7).
+ -- Good to know which python versions we need to support.
+ iPythonHexVersion INTEGER DEFAULT NULL,
+
+ --- Pending command.
+ -- @note We put it here instead of in TestBoxStatuses to get history.
+ enmPendingCmd TestBoxCmd_T DEFAULT 'none'::TestBoxCmd_T NOT NULL,
+
+ PRIMARY KEY (idTestBox, tsExpire),
+
+ --- Nested paging requires hardware virtualization.
+ CHECK (fCpuNestedPaging IS NULL OR (fCpuNestedPaging <> TRUE OR fCpuHwVirt = TRUE))
+);
+
+-- Convenience view that simplifies querying a lot.
+CREATE VIEW TestBoxesWithStrings AS
+ SELECT TestBoxes.*,
+ Str1.sValue AS sDescription,
+ Str2.sValue AS sComment,
+ Str3.sValue AS sOs,
+ Str4.sValue AS sOsVersion,
+ Str5.sValue AS sCpuVendor,
+ Str6.sValue AS sCpuArch,
+ Str7.sValue AS sCpuName,
+ Str8.sValue AS sReport
+ FROM TestBoxes
+ LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment = Str2.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs = Str3.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion = Str4.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor = Str5.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch = Str6.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName = Str7.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport = Str8.idStr;
+
+
+--
+-- Populate the string table.
+--
+
+--- Empty string with ID 0.
+INSERT INTO TestBoxStrTab (idStr, sValue) VALUES (0, '');
+
+INSERT INTO TestBoxStrTab (sValue)
+( SELECT DISTINCT sDescription FROM OldTestBoxes WHERE sDescription IS NOT NULL
+) UNION ( SELECT DISTINCT sOs FROM OldTestBoxes WHERE sOs IS NOT NULL
+) UNION ( SELECT DISTINCT sOsVersion FROM OldTestBoxes WHERE sOsVersion IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuVendor FROM OldTestBoxes WHERE sCpuVendor IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuArch FROM OldTestBoxes WHERE sCpuArch IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuName FROM OldTestBoxes WHERE sCpuName IS NOT NULL
+) UNION ( SELECT DISTINCT sReport FROM OldTestBoxes WHERE sReport IS NOT NULL );
+
+-- Index and analyze the string table as we'll be using it a lot below already.
+CREATE INDEX TestBoxStrTabNameIdx ON TestBoxStrTab USING hash (sValue);
+ANALYZE VERBOSE TestBoxStrTab;
+
+SELECT MAX(idStr) FROM TestBoxStrTab;
+SELECT pg_total_relation_size('TestBoxStrTab');
+
+
+--
+-- Populate the test box table.
+--
+
+INSERT INTO TestBoxes (
+ idTestBox, -- 0
+ tsEffective, -- 1
+ tsExpire, -- 2
+ uidAuthor, -- 3
+ idGenTestBox, -- 4
+ ip, -- 5
+ uuidSystem, -- 6
+ sName, -- 7
+ idStrDescription, -- 8
+ idSchedGroup, -- 9
+ fEnabled, -- 10
+ enmLomKind, -- 11
+ ipLom, -- 12
+ pctScaleTimeout, -- 13
+ idStrComment, -- 14
+ idStrOs, -- 15
+ idStrOsVersion, -- 16
+ idStrCpuVendor, -- 17
+ idStrCpuArch, -- 18
+ idStrCpuName, -- 19
+ lCpuRevision, -- 20
+ cCpus, -- 21
+ fCpuHwVirt, -- 22
+ fCpuNestedPaging, -- 23
+ fCpu64BitGuest, -- 24
+ fChipsetIoMmu, -- 25
+ fRawMode, -- 26
+ cMbMemory, -- 27
+ cMbScratch, -- 28
+ idStrReport, -- 29
+ iTestBoxScriptRev, -- 30
+ iPythonHexVersion, -- 31
+ enmPendingCmd -- 32
+ )
+SELECT idTestBox,
+ tsEffective,
+ tsExpire,
+ uidAuthor,
+ idGenTestBox,
+ ip,
+ uuidSystem,
+ sName,
+ st1.idStr,
+ idSchedGroup,
+ fEnabled,
+ enmLomKind,
+ ipLom,
+ pctScaleTimeout,
+ NULL,
+ st2.idStr,
+ st3.idStr,
+ st4.idStr,
+ st5.idStr,
+ st6.idStr,
+ lCpuRevision,
+ cCpus,
+ fCpuHwVirt,
+ fCpuNestedPaging,
+ fCpu64BitGuest,
+ fChipsetIoMmu,
+ NULL,
+ cMbMemory,
+ cMbScratch,
+ st7.idStr,
+ iTestBoxScriptRev,
+ iPythonHexVersion,
+ enmPendingCmd
+FROM OldTestBoxes
+ LEFT OUTER JOIN TestBoxStrTab st1 ON sDescription = st1.sValue
+ LEFT OUTER JOIN TestBoxStrTab st2 ON sOs = st2.sValue
+ LEFT OUTER JOIN TestBoxStrTab st3 ON sOsVersion = st3.sValue
+ LEFT OUTER JOIN TestBoxStrTab st4 ON sCpuVendor = st4.sValue
+ LEFT OUTER JOIN TestBoxStrTab st5 ON sCpuArch = st5.sValue
+ LEFT OUTER JOIN TestBoxStrTab st6 ON sCpuName = st6.sValue
+ LEFT OUTER JOIN TestBoxStrTab st7 ON sReport = st7.sValue;
+
+-- Restore indexes.
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire DESC);
+CREATE INDEX TestBoxesExpireEffectiveIdx ON TestBoxes (tsExpire DESC, tsEffective ASC);
+
+-- Restore foreign key references to the table.
+ALTER TABLE TestBoxStatuses ADD CONSTRAINT TestBoxStatuses_idGenTestBox_fkey
+ FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets ADD CONSTRAINT TestSets_idGenTestBox_fkey
+ FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+
+-- Drop the old table.
+DROP TABLE OldTestBoxes;
+
+COMMIT;
+
+\d TestBoxes;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r20-testcases-1-testgroups-1-schedgroups-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r20-testcases-1-testgroups-1-schedgroups-1.pgsql
new file mode 100644
index 00000000..ac40ebb8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r20-testcases-1-testgroups-1-schedgroups-1.pgsql
@@ -0,0 +1,67 @@
+-- $Id: tmdb-r20-testcases-1-testgroups-1-schedgroups-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds sComment to TestCases, TestGroups
+-- and SchedGroups.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestCases IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestGroups IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroups IN ACCESS EXCLUSIVE MODE;
+
+--
+-- All the changes are rather simple and we'll just add the sComment column last.
+--
+\d TestCases;
+\d TestGroups;
+\d SchedGroups;
+
+ALTER TABLE TestCases ADD COLUMN sComment TEXT DEFAULT NULL;
+ALTER TABLE TestGroups ADD COLUMN sComment TEXT DEFAULT NULL;
+ALTER TABLE SchedGroups ADD COLUMN sComment TEXT DEFAULT NULL;
+
+\d TestCases;
+\d TestGroups;
+\d SchedGroups;
+
+\prompt "Update python files while everything is locked. Hurry!" dummy
+
+COMMIT;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r21-testsets-4.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r21-testsets-4.pgsql
new file mode 100644
index 00000000..13a57854
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r21-testsets-4.pgsql
@@ -0,0 +1,290 @@
+-- $Id: tmdb-r21-testsets-4.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds an idSchedGroup to TestSets in
+-- preparation for testboxes belonging to multiple scheduling queues.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS OldTestSets;
+
+--
+-- Die on error from now on.
+--
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+
+-- Total grid lock (don't want to deadlock below).
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResults IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResultFailures IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResultFiles IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResultMsgs IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestResultValues IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroups IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedQueues IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroupMembers IN ACCESS EXCLUSIVE MODE;
+
+\d+ TestSets;
+
+--
+-- Rename the table, drop foreign keys refering to it, and drop constrains
+-- within the table itself. The latter is mostly for naming and we do it
+-- up front in case the database we're running against has different names
+-- due to previous conversions.
+--
+ALTER TABLE TestSets RENAME TO OldTestSets;
+
+ALTER TABLE TestResultFailures DROP CONSTRAINT IF EXISTS idtestsetfk;
+ALTER TABLE TestResultFailures DROP CONSTRAINT IF EXISTS TestResultFailures_idTestSet_fkey;
+ALTER TABLE SchedQueues DROP CONSTRAINT IF EXISTS SchedQueues_idTestSetGangLeader_fkey;
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT IF EXISTS TestBoxStatuses_idTestSet_fkey;
+ALTER TABLE TestResultFiles DROP CONSTRAINT IF EXISTS TestResultFiles_idTestSet_fkey;
+ALTER TABLE TestResultMsgs DROP CONSTRAINT IF EXISTS TestResultMsgs_idTestSet_fkey;
+ALTER TABLE TestResults DROP CONSTRAINT IF EXISTS TestResults_idTestSet_fkey;
+ALTER TABLE TestResultValues DROP CONSTRAINT IF EXISTS TestResultValues_idTestSet_fkey;
+ALTER TABLE TestResultValues DROP CONSTRAINT IF EXISTS TestResultValues_idTestSet_fkey1;
+
+ALTER TABLE OldTestSets DROP CONSTRAINT testsets_igangmemberno_check;
+
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idBuildCategory_fkey;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idGenTestBox_fkey;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idGenTestCase_fkey;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idGenTestCaseArgs_fkey;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idTestResult_fkey;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_idTestSetGangLeader_fkey;
+
+ALTER TABLE OldTestSets DROP CONSTRAINT IF EXISTS TestSets_sBaseFilename_key;
+ALTER TABLE OldTestSets DROP CONSTRAINT IF EXISTS NewTestSets_sBaseFilename_key;
+ALTER TABLE OldTestSets DROP CONSTRAINT TestSets_pkey;
+
+DROP INDEX IF EXISTS TestSetsGangIdx;
+DROP INDEX IF EXISTS TestSetsBoxIdx;
+DROP INDEX IF EXISTS TestSetsBuildIdx;
+DROP INDEX IF EXISTS TestSetsTestCaseIdx;
+DROP INDEX IF EXISTS TestSetsTestVarIdx;
+DROP INDEX IF EXISTS TestSetsDoneCreatedBuildCatIdx;
+DROP INDEX IF EXISTS TestSetsGraphBoxIdx;
+
+
+-- This output should be free of indexes, constraints and references from other tables.
+\d+ OldTestSets;
+
+\prompt "Is the above table completely free of indexes, constraints and references? Ctrl-C if not." dummy
+
+--
+-- Create the new table (no foreign keys).
+--
+CREATE TABLE TestSets (
+ --- The ID of this test set.
+ idTestSet INTEGER DEFAULT NEXTVAL('TestSetIdSeq') NOT NULL,
+
+ --- The test config timestamp, used when reading test config.
+ tsConfig TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test set was scheduled.
+ -- idGenTestBox is valid at this point.
+ tsCreated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ --- When this test completed, i.e. testing stopped. This should only be set once.
+ tsDone TIMESTAMP WITH TIME ZONE DEFAULT NULL,
+ --- The current status.
+ enmStatus TestStatus_T DEFAULT 'running'::TestStatus_T NOT NULL,
+
+ --- The build we're testing.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuild INTEGER NOT NULL,
+ --- The build category of idBuild when the test started.
+ -- This is for speeding up graph data collection, i.e. avoid idBuild
+ -- the WHERE part of the selection.
+ idBuildCategory INTEGER NOT NULL,
+ --- The test suite build we're using to do the testing.
+ -- This is NULL if the test suite zip wasn't referred or if a test suite
+ -- build source wasn't configured.
+ -- Non-unique foreign key: Builds(idBuild)
+ idBuildTestSuite INTEGER DEFAULT NULL,
+
+ --- The exact testbox configuration.
+ idGenTestBox INTEGER NOT NULL,
+ --- The testbox ID for joining with (valid: tsStarted).
+ -- Non-unique foreign key: TestBoxes(idTestBox)
+ idTestBox INTEGER NOT NULL,
+ --- The scheduling group ID the test was scheduled thru (valid: tsStarted).
+ -- Non-unique foreign key: SchedGroups(idSchedGroup)
+ idSchedGroup INTEGER NOT NULL,
+
+ --- The testgroup (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestGroup)
+ -- Note! This also gives the member ship entry, since a testcase can only
+ -- have one membership per test group.
+ idTestGroup INTEGER NOT NULL,
+
+ --- The exact test case config we executed in this test run.
+ idGenTestCase INTEGER NOT NULL,
+ --- The test case ID for joining with (valid: tsConfig).
+ -- Non-unique foreign key: TestBoxes(idTestCase)
+ idTestCase INTEGER NOT NULL,
+
+ --- The arguments (and requirements++) we executed this test case with.
+ idGenTestCaseArgs INTEGER NOT NULL,
+ --- The argument variation ID (valid: tsConfig).
+ -- Non-unique foreign key: TestCaseArgs(idTestCaseArgs)
+ idTestCaseArgs INTEGER NOT NULL,
+
+ --- The root of the test result tree.
+ -- @note This will only be NULL early in the transaction setting up the testset.
+ -- @note If the test reports more than one top level test result, we'll
+ -- fail the whole test run and let the test developer fix it.
+ idTestResult INTEGER DEFAULT NULL,
+
+ --- The base filename used for storing files related to this test set.
+ -- This is a path relative to wherever TM is dumping log files. In order
+ -- to not become a file system test case, we will try not to put too many
+ -- hundred thousand files in a directory. A simple first approach would
+ -- be to just use the current date (tsCreated) like this:
+ -- TM_FILE_DIR/year/month/day/TestSets.idTestSet
+ --
+ -- The primary log file for the test is this name suffixed by '.log'.
+ --
+ -- The files in the testresultfile table gets their full names like this:
+ -- TM_FILE_DIR/sBaseFilename-testresultfile.id-TestResultStrTab(testresultfile.idStrFilename)
+ --
+ -- @remarks We store this explicitly in case we change the directly layout
+ -- at some later point.
+ sBaseFilename text NOT NULL,
+
+ --- The gang member number number, 0 is the leader.
+ iGangMemberNo SMALLINT DEFAULT 0 NOT NULL, -- CHECK (iGangMemberNo >= 0 AND iGangMemberNo < 1024),
+ --- The test set of the gang leader, NULL if no gang involved.
+ -- @note This is set by the gang leader as well, so that we can find all
+ -- gang members by WHERE idTestSetGangLeader = :id.
+ idTestSetGangLeader INTEGER DEFAULT NULL
+
+);
+
+-- Convert the data.
+INSERT INTO TestSets (
+ idTestSet,
+ tsConfig,
+ tsCreated,
+ tsDone,
+ enmStatus,
+ idBuild,
+ idBuildCategory,
+ idBuildTestSuite,
+ idGenTestBox,
+ idTestBox,
+ idSchedGroup,
+ idTestGroup,
+ idGenTestCase,
+ idTestCase,
+ idGenTestCaseArgs,
+ idTestCaseArgs,
+ idTestResult,
+ sBaseFilename,
+ iGangMemberNo,
+ idTestSetGangLeader
+ )
+SELECT OldTestSets.idTestSet,
+ OldTestSets.tsConfig,
+ OldTestSets.tsCreated,
+ OldTestSets.tsDone,
+ OldTestSets.enmStatus,
+ OldTestSets.idBuild,
+ OldTestSets.idBuildCategory,
+ OldTestSets.idBuildTestSuite,
+ OldTestSets.idGenTestBox,
+ OldTestSets.idTestBox,
+ TestBoxes.idSchedGroup,
+ OldTestSets.idTestGroup,
+ OldTestSets.idGenTestCase,
+ OldTestSets.idTestCase,
+ OldTestSets.idGenTestCaseArgs,
+ OldTestSets.idTestCaseArgs,
+ OldTestSets.idTestResult,
+ OldTestSets.sBaseFilename,
+ OldTestSets.iGangMemberNo,
+ OldTestSets.idTestSetGangLeader
+FROM OldTestSets
+ INNER JOIN TestBoxes
+ ON OldTestSets.idGenTestBox = TestBoxes.idGenTestBox;
+
+-- Restore the primary key and unique constraints.
+ALTER TABLE TestSets ADD PRIMARY KEY (idTestSet);
+ALTER TABLE TestSets ADD UNIQUE (sBaseFilename);
+
+-- Restore check constraints.
+ALTER TABLE TestSets ADD CONSTRAINT TestSets_iGangMemberNo_Check CHECK (iGangMemberNo >= 0 AND iGangMemberNo < 1024);
+
+-- Restore foreign keys in the table.
+ALTER TABLE TestSets ADD FOREIGN KEY (idBuildCategory) REFERENCES BuildCategories(idBuildCategory);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestCase) REFERENCES TestCases(idGenTestCase);
+ALTER TABLE TestSets ADD FOREIGN KEY (idGenTestCaseArgs) REFERENCES TestCaseArgs(idGenTestCaseArgs);
+ALTER TABLE TestSets ADD FOREIGN KEY (idTestResult) REFERENCES TestResults(idTestResult);
+ALTER TABLE TestSets ADD FOREIGN KEY (idTestSetGangLeader) REFERENCES TestSets(idTestSet);
+
+-- Restore indexes.
+CREATE INDEX TestSetsGangIdx ON TestSets (idTestSetGangLeader);
+CREATE INDEX TestSetsBoxIdx ON TestSets (idTestBox, idTestResult);
+CREATE INDEX TestSetsBuildIdx ON TestSets (idBuild, idTestResult);
+CREATE INDEX TestSetsTestCaseIdx ON TestSets (idTestCase, idTestResult);
+CREATE INDEX TestSetsTestVarIdx ON TestSets (idTestCaseArgs, idTestResult);
+CREATE INDEX TestSetsDoneCreatedBuildCatIdx ON TestSets (tsDone DESC NULLS FIRST, tsCreated ASC, idBuildCategory);
+CREATE INDEX TestSetsGraphBoxIdx ON TestSets (idTestBox, tsCreated DESC, tsDone ASC NULLS LAST, idBuildCategory, idTestCase);
+
+-- Restore foreign key references to the table.
+ALTER TABLE TestResults ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultValues ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultFiles ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultMsgs ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE TestResultFailures ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+
+ALTER TABLE TestBoxStatuses ADD FOREIGN KEY (idTestSet) REFERENCES TestSets(idTestSet) MATCH FULL;
+ALTER TABLE SchedQueues ADD FOREIGN KEY (idTestSetGangLeader) REFERENCES TestSets(idTestSet) MATCH FULL;
+
+-- Drop the old table.
+DROP TABLE OldTestSets;
+
+\prompt "Update python files while everything is locked. Hurry!" dummy
+
+-- Grant access to the new table.
+GRANT ALL PRIVILEGES ON TABLE TestSets TO testmanager;
+
+COMMIT;
+
+\d TestSets;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r22-testboxes-3-teststatus-4-testboxinschedgroups-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r22-testboxes-3-teststatus-4-testboxinschedgroups-1.pgsql
new file mode 100644
index 00000000..8d7d7df0
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r22-testboxes-3-teststatus-4-testboxinschedgroups-1.pgsql
@@ -0,0 +1,181 @@
+-- $Id: tmdb-r22-testboxes-3-teststatus-4-testboxinschedgroups-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Turns idSchedGroup column in TestBoxes
+-- into an N:M relationship with a priority via the new table
+-- TestBoxesInSchedGroups. Adds an internal scheduling table index to
+-- TestBoxStatuses to implement testboxes switching between groups.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS OldTestBoxes;
+
+--
+-- Die on error from now on.
+--
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+
+-- Total grid lock.
+LOCK TABLE TestBoxStatuses IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroups IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroupMembers IN ACCESS EXCLUSIVE MODE;
+
+\d+ TestBoxes;
+
+--
+-- We'll only be doing simple alterations so, no need to drop constraints
+-- and stuff like we usually do first.
+--
+
+--
+-- Create the new table and populate it.
+--
+
+CREATE TABLE TestBoxesInSchedGroups (
+ --- TestBox ID.
+ -- Non-unique foreign key: TestBoxes(idTestBox).
+ idTestBox INTEGER NOT NULL,
+ --- Scheduling ID.
+ -- Non-unique foreign key: SchedGroups(idSchedGroup).
+ idSchedGroup INTEGER NOT NULL,
+ --- When this row starts taking effect (inclusive).
+ tsEffective TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
+ --- When this row stops being tsEffective (exclusive).
+ tsExpire TIMESTAMP WITH TIME ZONE DEFAULT TIMESTAMP WITH TIME ZONE 'infinity' NOT NULL,
+ --- The user id of the one who created/modified this entry.
+ -- Non-unique foreign key: Users(uid)
+ uidAuthor INTEGER NOT NULL,
+
+ --- The scheduling priority of the scheduling group for the test box.
+ -- Higher number causes the scheduling group to be serviced more frequently.
+ -- @sa TestGroupMembers.iSchedPriority, SchedGroups.iSchedPriority
+ iSchedPriority INTEGER DEFAULT 16 CHECK (iSchedPriority >= 0 AND iSchedPriority < 32) NOT NULL,
+
+ PRIMARY KEY (idTestBox, idSchedGroup, tsExpire)
+);
+
+GRANT ALL PRIVILEGES ON TABLE TestBoxesInSchedGroups TO testmanager;
+
+CREATE OR REPLACE FUNCTION TestBoxesInSchedGroups_ConvertedOneBox(a_idTestBox INTEGER)
+ RETURNS VOID AS $$
+ DECLARE
+ v_Row RECORD;
+ v_idSchedGroup INTEGER;
+ v_uidAuthor INTEGER;
+ v_tsEffective TIMESTAMP WITH TIME ZONE;
+ v_tsExpire TIMESTAMP WITH TIME ZONE;
+ BEGIN
+ FOR v_Row IN
+ SELECT idTestBox,
+ idSchedGroup,
+ tsEffective,
+ tsExpire,
+ uidAuthor
+ FROM TestBoxes
+ WHERE idTestBox = a_idTestBox
+ ORDER BY tsEffective, tsExpire
+ LOOP
+ IF v_idSchedGroup IS NOT NULL THEN
+ IF (v_idSchedGroup != v_Row.idSchedGroup) OR (v_Row.tsEffective <> v_tsExpire) THEN
+ INSERT INTO TestBoxesInSchedGroups (idTestBox, idSchedGroup, tsEffective, tsExpire, uidAuthor)
+ VALUES (a_idTestBox, v_idSchedGroup, v_tsEffective, v_tsExpire, v_uidAuthor);
+ v_idSchedGroup := NULL;
+ END IF;
+ END IF;
+
+ IF v_idSchedGroup IS NULL THEN
+ v_idSchedGroup := v_Row.idSchedGroup;
+ v_tsEffective := v_Row.tsEffective;
+ END IF;
+ IF v_Row.uidAuthor IS NOT NULL THEN
+ v_uidAuthor := v_Row.uidAuthor;
+ END IF;
+ v_tsExpire := v_Row.tsExpire;
+ END LOOP;
+
+ IF v_idSchedGroup != -1 THEN
+ INSERT INTO TestBoxesInSchedGroups (idTestBox, idSchedGroup, tsEffective, tsExpire, uidAuthor)
+ VALUES (a_idTestBox, v_idSchedGroup, v_tsEffective, v_tsExpire, v_uidAuthor);
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
+SELECT TestBoxesInSchedGroups_ConvertedOneBox(TestBoxIDs.idTestBox)
+FROM ( SELECT DISTINCT idTestBox FROM TestBoxes ) AS TestBoxIDs;
+
+DROP FUNCTION TestBoxesInSchedGroups_ConvertedOneBox(INTEGER);
+
+--
+-- Do the other two modifications.
+--
+ALTER TABLE TestBoxStatuses ADD COLUMN iWorkItem INTEGER DEFAULT 0 NOT NULL;
+
+DROP VIEW TestBoxesWithStrings;
+ALTER TABLE TestBoxes DROP COLUMN idSchedGroup;
+CREATE VIEW TestBoxesWithStrings AS
+ SELECT TestBoxes.*,
+ Str1.sValue AS sDescription,
+ Str2.sValue AS sComment,
+ Str3.sValue AS sOs,
+ Str4.sValue AS sOsVersion,
+ Str5.sValue AS sCpuVendor,
+ Str6.sValue AS sCpuArch,
+ Str7.sValue AS sCpuName,
+ Str8.sValue AS sReport
+ FROM TestBoxes
+ LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment = Str2.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs = Str3.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion = Str4.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor = Str5.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch = Str6.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName = Str7.idStr
+ LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport = Str8.idStr;
+
+GRANT ALL PRIVILEGES ON TABLE TestBoxesWithStrings TO testmanager;
+
+\prompt "Update python files while everything is locked. Hurry!" dummy
+
+COMMIT;
+
+\d TestBoxesInSchedGroups;
+\d TestBoxStatuses;
+\d TestBoxes;
+ANALYZE VERBOSE TestBoxesInSchedGroups;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r23-users-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r23-users-2.pgsql
new file mode 100644
index 00000000..7a919da3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r23-users-2.pgsql
@@ -0,0 +1,60 @@
+-- $Id: tmdb-r23-users-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Adds fReadOnly column to Users.
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS OldTestBoxes;
+
+--
+-- Die on error from now on.
+--
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+
+
+-- This change can be implemented using ALTER TABLE. Yeah!
+\d+ Users;
+
+ALTER TABLE Users
+ ADD COLUMN fReadOnly BOOLEAN NOT NULL DEFAULT FALSE;
+
+COMMIT;
+
+\d Users;
+ANALYZE VERBOSE Users;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r24-vcsbugreferences-1.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r24-vcsbugreferences-1.pgsql
new file mode 100644
index 00000000..0bb92ed4
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r24-vcsbugreferences-1.pgsql
@@ -0,0 +1,59 @@
+-- $Id: tmdb-r24-vcsbugreferences-1.pgsql $
+--- @file
+-- VBox Test Manager Database - Creates a new VcsBugReferences table.
+--
+
+--
+-- Copyright (C) 2020-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+--
+-- Create the new VcsBugReferences table.
+--
+CREATE TABLE VcsBugReferences (
+ --- The version control tree name.
+ sRepository TEXT NOT NULL,
+ --- The version control tree revision number.
+ iRevision INTEGER NOT NULL,
+ --- The bug tracker identifier - see g_kdBugTrackers in config.py.
+ sBugTracker CHAR(4) NOT NULL,
+ --- The bug number in the bug tracker.
+ lBugNo BIGINT NOT NULL,
+
+ UNIQUE (sRepository, iRevision, sBugTracker, lBugNo)
+);
+CREATE INDEX VcsBugReferencesLookupIdx ON VcsBugReferences (sBugTracker, lBugNo);
+COMMIT;
+\d+ VcsBugReferences;
+
diff --git a/src/VBox/ValidationKit/testmanager/db/tmdb-r25-vcsrevisions-2.pgsql b/src/VBox/ValidationKit/testmanager/db/tmdb-r25-vcsrevisions-2.pgsql
new file mode 100644
index 00000000..f0c4e2bd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/db/tmdb-r25-vcsrevisions-2.pgsql
@@ -0,0 +1,45 @@
+-- $Id: tmdb-r25-vcsrevisions-2.pgsql $
+--- @file
+-- VBox Test Manager Database - Creates a new index on VcsRevisions
+--
+
+--
+-- Copyright (C) 2013-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+--
+-- Die on error from now on.
+--
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 1
+
+
+CREATE INDEX VcsRevisionsByDate ON VcsRevisions (tsCreated DESC);
+
diff --git a/src/VBox/ValidationKit/testmanager/debug/Makefile.kmk b/src/VBox/ValidationKit/testmanager/debug/Makefile.kmk
new file mode 100644
index 00000000..74d882cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/debug/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/debug/__init__.py b/src/VBox/ValidationKit/testmanager/debug/__init__.py
new file mode 100644
index 00000000..1a97eacf
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/debug/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Test Manager - Debug Utilities.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/testmanager/debug/add_testbox.pgsql b/src/VBox/ValidationKit/testmanager/debug/add_testbox.pgsql
new file mode 100644
index 00000000..faf303c2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/debug/add_testbox.pgsql
@@ -0,0 +1,76 @@
+-- $Id: add_testbox.pgsql $
+--- @file
+-- Test data.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+\connect testmanager;
+
+INSERT INTO users (sUsername, sNickname, sFullName, sLoginName)
+ VALUES ('testmanager', 'testmanager', 'testmanager', 'testmanager');
+
+INSERT INTO testboxes (uidAuthor,
+ ip,
+ uuidSystem,
+ sName,
+ fEnabled,
+ sOs,
+ sOsVersion,
+ sCpuVendor,
+ sCpuArch,
+ cCpus,
+ fCpuHwVirt,
+ fCpuNestedPaging,
+ fCpu64BitGuest,
+ fChipsetIoMmu,
+ cMbMemory,
+ cMbScratch)
+
+ VALUES (1,
+ '127.0.0.1',
+ '9394c36a-cb2f-3c7f-b987-9f54a4519bbc',
+ 'localhost',
+ TRUE,
+ 'LINUX',
+ '1.0',
+ 'Intel',
+ 'AMD64',
+ 2,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ 1024,
+ 1024);
+
diff --git a/src/VBox/ValidationKit/testmanager/debug/cgiprofiling.py b/src/VBox/ValidationKit/testmanager/debug/cgiprofiling.py
new file mode 100755
index 00000000..be049e41
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/debug/cgiprofiling.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: cgiprofiling.py $
+
+"""
+Debug - CGI Profiling.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+def profileIt(fnMain, sAppendToElement = 'main', sSort = 'time'):
+ """
+ Profiles a main() type function call (no parameters, returns int) and
+ outputs a hacky HTML section.
+ """
+
+ #
+ # Execute it.
+ #
+ import cProfile;
+ oProfiler = cProfile.Profile();
+ rc = oProfiler.runcall(fnMain);
+
+ #
+ # Output HTML to stdout (CGI assumption).
+ #
+ print('<div id="debug2"><br>\n' # Lazy BR-layouting!!
+ ' <h2>Profiler Output</h2>\n'
+ ' <pre>');
+ try:
+ oProfiler.print_stats(sort = sSort);
+ except Exception as oXcpt:
+ print('<p><pre>%s</pre></p>\n' % (oXcpt,));
+ else:
+ print('</pre>\n');
+ oProfiler = None;
+ print('</div>\n');
+
+ #
+ # Trick to move the section in under the SQL trace.
+ #
+ print('<script lang="script/javascript">\n'
+ 'var oMain = document.getElementById(\'%s\');\n'
+ 'if (oMain) {\n'
+ ' oMain.appendChild(document.getElementById(\'debug2\'));\n'
+ '}\n'
+ '</script>\n'
+ % (sAppendToElement, ) );
+
+ return rc;
+
diff --git a/src/VBox/ValidationKit/testmanager/debug/functions.pgsql b/src/VBox/ValidationKit/testmanager/debug/functions.pgsql
new file mode 100644
index 00000000..00854e91
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/debug/functions.pgsql
@@ -0,0 +1,82 @@
+-- $Id: functions.pgsql $
+--- @file
+-- ?????????????????????????
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+\connect testmanager;
+
+DROP FUNCTION authenticate_testbox(inet, uuid);
+DROP FUNCTION testbox_status_set(integer, TestBoxState_T);
+
+-- Authenticate Test Box record by IP and UUID and set its state to IDLE
+-- Args: IP, UUID
+CREATE OR REPLACE FUNCTION authenticate_testbox(inet, uuid) RETURNS testboxes AS $$
+ DECLARE
+ _ip ALIAS FOR $1;
+ _uuidSystem ALIAS FOR $2;
+ _box TestBoxes;
+ BEGIN
+ -- Find Test Box record
+ SELECT *
+ FROM testboxes
+ WHERE ip=_ip AND uuidSystem=_uuidSystem INTO _box;
+ IF FOUND THEN
+ -- Update Test Box status if exists
+ UPDATE TestBoxStatuses SET enmState='idle' WHERE idTestBox=_box.idTestBox;
+ IF NOT FOUND THEN
+ -- Otherwise, add new record to TestBoxStatuses table
+ INSERT
+ INTO TestBoxStatuses(idTestBox, idGenTestBox, enmState)
+ VALUES (_box.idTestBox, _box.idGenTestBox, 'idle');
+ END IF;
+ END IF;
+ return _box;
+ END;
+$$ LANGUAGE plpgsql;
+
+-- Set Test Box status and make sure if it has been set
+-- Args: Test Box ID, new status
+CREATE OR REPLACE FUNCTION testbox_status_set(integer, TestBoxState_T) RETURNS VOID AS $$
+ DECLARE
+ _box ALIAS FOR $1;
+ _status ALIAS FOR $2;
+ BEGIN
+ -- Update Test Box status if exists
+ UPDATE TestBoxStatuses SET enmState=_status WHERE idTestBox=_box;
+ IF NOT FOUND THEN
+ RAISE EXCEPTION 'Test Box (#%) was not found in database', _box;
+ END IF;
+ END;
+$$ LANGUAGE plpgsql;
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/Makefile.kup b/src/VBox/ValidationKit/testmanager/htdocs/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/Makefile.kup
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/css/common.css b/src/VBox/ValidationKit/testmanager/htdocs/css/common.css
new file mode 100644
index 00000000..9ccf7a54
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/css/common.css
@@ -0,0 +1,1183 @@
+/* $Id: common.css $ */
+/** @file
+ * Test Manager - Common CSS.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+@charset "UTF-8";
+
+/*
+ * Basic HTML elements.
+ */
+* {
+ margin: 0;
+ padding: 0;
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ background: #f9f9f9 repeat-y center;
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 0.8em;
+ color: #2f2f2f;
+}
+
+p, ul, ol {
+ margin-top: 0;
+}
+
+div {
+ margin: 0;
+ padding: 0;
+}
+
+h1, h2, h3 {
+ margin: 0px 0 10px 0;
+ padding: 0;
+ font-weight: normal;
+ color: #2f2f2f;
+ line-height: 180%;
+}
+h1 {
+ font-size: 2.4em;
+}
+h2 {
+ font-size: 2.0em;
+}
+h3 {
+ font-size: 1.5em;
+}
+
+dl {
+ margin-bottom: 10px;
+}
+
+
+/*
+ * Misc class stuff.
+ */
+.clear {
+ clear: both;
+}
+
+.left {
+ float: left;
+}
+
+.right {
+ float: right;
+}
+
+
+
+/*
+ * The general layout.
+ *
+ * Note! Not quite sure if something like this will work well everywhere...
+ * Will get back to that when the logic and content is all there, not
+ * worth wasting more time on CSS now.
+ */
+
+html, body {
+ height: 100%;
+}
+
+#wrap {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+#head-wrap {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 74px; /**< header + top-menu. */
+ width: 100%;
+ background: #f9f9f9;
+}
+
+#logo {
+ width: 42px;
+ height: 46px;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: auto;
+ /* Center the image in both directions. */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ justify-content: flex-end;
+}
+
+#logo img {
+ height: 36px;
+ width: 36px;
+}
+
+#header {
+ position: fixed;
+ width: 100%; /** @todo this is too wide, darn! */
+ height: 46px;
+ left: 42px;
+ top: 0;
+ right: 0;
+ bottom: auto;
+ margin-top: 0px;
+ margin-left: 0px;
+ text-align: left;
+ /* Center the h1 child vertically: */
+ display: flex;
+ align-items: center;
+}
+
+#login {
+ position: absolute;
+ top: 0;
+ left: auto;
+ right: 2px;
+ bottom: auto;
+ height: auto;
+}
+
+#top-menu {
+ position: fixed;
+ padding: 0px;
+ width: 99%;
+ height: auto;
+ max-height: 22px;
+ top: 46px;
+ left: 0px;
+ right: 0px;
+ bottom: auto;
+}
+
+body.tm-wide-side-menu #side-menu-wrap {
+ width: 300px;
+}
+#side-menu-wrap {
+ position: fixed;
+ top: 0px;
+ left: 0;
+ right: auto;
+ bottom: auto;
+
+ width: 164px;
+ height: 100vh;
+ min-height: 100vh;
+ max-height: 100vh;
+
+ display: flex;
+}
+
+#side-menu {
+ margin-top: 46px;
+ margin-top: 70px;
+ padding-top: 6px
+ height: auto;
+ max-height: 100%;
+ width: 95%;
+ width: calc(100% - 8px); /* CSS3 */
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+
+#side-menu-body {
+ display: block;
+ max-height: 100%;
+ overflow: auto;
+}
+
+body.tm-wide-side-menu #main {
+ margin-left: 300px;
+}
+#main {
+ height: 100%;
+ margin-top: 74px; /**< header + top-menu + padding. */
+ margin-left: 164px;
+ padding-left: 2px;
+ padding-right: 2px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
+
+/*
+ * Header and logo specifics.
+ */
+#header h1 {
+ margin-left: 8px;
+ margin-top: 0px;
+ margin-right: 0px;
+ margin-bottom: 0px;
+ font-weight: bold;
+ font-size: 2.2em;
+ font-family: Times New, Times, serif;
+}
+
+#login p {
+ line-height: 100%;
+}
+
+
+/*
+ * Navigation menus (common).
+ */
+#top-menu, #side-menu {
+ font-weight: bold;
+ font-size: 1em;
+ font-family: Arial, Helvetica, sans-serif;
+ background-color: #c0d0e0;
+ padding: 2px 2px 2px 2px;
+}
+
+#top-menu.tm-top-menu-wo-side {
+ border-radius: 12px;
+}
+#top-menu {
+ border-radius: 12px 12px 12px 0px;
+}
+
+#side-menu {
+ border-radius: 0px 0px 12px 12px;
+}
+
+#head-wrap {
+ line-height: 180%;
+}
+
+#top-menu ul li a, #side-menu ul li a {
+ text-decoration: none;
+ color: #000000;
+ font-weight: bold;
+ font-size: 1em;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+#top-menu a:hover, #top-menu .current_page_item a, #side-menu a:hover, #side-menu .current_page_item a {
+ text-decoration: none;
+ color: #b23c1c;
+}
+
+
+/*
+ * Navigation in on the left side.
+ */
+
+
+/* Side menu: */
+#side-menu {
+ /* margin-top and padding-top are set up in layout !*/
+ margin-right: 3px;
+ margin-left: 3px;
+ margin-bottom: 3px;
+}
+
+#side-menu p {
+ margin-right: 3px;
+ margin-left: 3px;
+}
+
+#side-menu ul {
+ list-style: none;
+ margin-left: 3px;
+ margin-right: 3px;
+}
+
+#side-menu li {
+ padding-top: 0.3em;
+ padding-bottom: 0.3em;
+ line-height: 1.0em;
+ text-align: left;
+}
+
+#side-menu .subheader_item {
+ font-style: italic;
+ font-size: 1.1em;
+ text-decoration: underline;
+}
+
+.subheader_item:not(:first-child) {
+ margin-top: 0.5em;
+}
+
+/* The following is for the element of / not element of checkbox, supplying text and hiding the actual box. */
+input.tm-side-filter-union-input {
+ display: none;
+}
+input.tm-side-filter-union-input + label {
+ vertical-align: middle;
+}
+input.tm-side-filter-union-input[type=checkbox]:checked + label::after {
+ content: '∉'; /* U+2209: not an element of. */
+}
+input.tm-side-filter-union-input[type=checkbox] + label::after {
+ content: '∈'; /* U+2208: element of. */
+}
+
+/* Webkit: Pretty scroll bars on the menu body as well as inside filter criteria. */
+#side-menu ::-webkit-scrollbar {
+ width: 8px;
+}
+#side-menu ::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+}
+#side-menu ::-webkit-scrollbar-thumb {
+ -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.5);
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ background: rgba(112, 128, 144, 0.9);
+}
+#side-menu ::-webkit-scrollbar-thumb:window-inactive {
+ background: rgba(112, 128, 144, 0.7);
+}
+
+/* Filters: */
+.tm-side-filter-title-buttons {
+ float: right;
+}
+body.tm-wide-side-menu .tm-side-filter-title-buttons input {
+ display: none;
+}
+.tm-side-filter-title-buttons input {
+ display: inline;
+}
+.tm-side-filter-title-buttons input {
+ font-size: 0.6em;
+}
+.tm-side-filter-dt-buttons input {
+ font-size: 0.6em;
+}
+body.tm-wide-side-menu .tm-side-filter-dt-buttons input[type=submit] {
+ display: inline;
+}
+.tm-side-filter-dt-buttons input[type=submit] {
+ display: none;
+}
+.tm-side-filter-dt-buttons {
+ float: right;
+}
+
+#side-filters p:first-child {
+ margin-top: 0.5em;
+ font-style: italic;
+ font-size: 1.1em;
+ text-decoration: underline;
+}
+
+#side-filters dd.sf-collapsible {
+ display: block;
+}
+
+#side-filters dd.sf-expandable {
+ display: none;
+}
+
+#side-filters a {
+ text-decoration: none;
+ color: #000000;
+}
+
+#side-filters dt {
+ margin-top: 0.4em;
+}
+
+#side-filters dd {
+ font-size: 0.82em;
+ font-family: "Arial Narrow", Arial, sans-serif;
+ font-weight: normal;
+ clear: both; /* cancel .tm-side-filter-dt-buttons */
+}
+
+#side-filters li, #side-filters input[type=checkbox], #side-filters p {
+ line-height: 0.9em;
+ vertical-align: text-bottom;
+}
+
+#side-filters input[type=checkbox] {
+ margin-right: 0.20em;
+ width: 1.0em;
+ height: 1.0em;
+}
+@supports(-moz-appearance:meterbar) {
+ #side-filters input[type=checkbox] {
+ /* not currently used */
+ }
+}
+@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { /* IE 10+ specific tweaks */
+ #side-filters input[type=checkbox] {
+ width: 1.1em;
+ height: 1.1em;
+ }
+}
+
+#side-filters dd > ul {
+ max-height: 22em;
+ overflow: auto;
+}
+
+#side-filters ul ul {
+ margin-left: 1.4em;
+}
+
+#side-filters li {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ overflow-wrap: break-word;
+}
+
+ul.sf-checkbox-collapsible {
+ display: block;
+}
+
+ul.sf-checkbox-expandable {
+ display: none;
+}
+
+.side-filter-irrelevant {
+ font-style: italic;
+ font-weight: normal;
+}
+.side-filter-count {
+ font-size: smaller;
+ vertical-align: text-top;
+}
+
+/* Footer: */
+#side-footer {
+ width: 100%;
+ margin-left: 2px;
+ margin-right: 2px;
+ margin-top: 1em;
+ padding-top: 1em;
+ padding-bottom: 0.8em;
+ border-top: thin white ridge;
+}
+
+#side-footer p {
+ margin-left: 3px;
+ margin-right: 3px;
+ margin-bottom: 0.5em;
+ font-family: Times New, Times, serif;
+ font-size: 0.86em;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1.2em;
+ text-align: center;
+}
+
+
+/*
+ * Navigation in the header.
+ */
+#top-menu {
+ margin-right: 3px; /* same as #side-menu! */
+ margin-left: 3px;
+}
+
+#top-menu ul li a {
+ padding: .1em 1em;
+}
+
+#top-menu ul li {
+ display: inline;
+}
+
+#top-menu ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ list-style-type: none;
+ text-align: center;
+}
+
+#top-menu a {
+ border: none;
+}
+
+#top-menu .current_page_item a {
+}
+
+/*
+ * Time navigation forms on a line with some padding between them.
+ */
+.tmtimenav form {
+ display: inline-block;
+}
+
+.tmtimenav form + form {
+ padding-left: 0.6em;
+}
+
+/*
+ * Items per page and next.
+ */
+.tmnextanditemsperpage form {
+ display: inline-block;
+ padding-left: 1em;
+}
+
+/*
+ * Error message (typically a paragraph in the body).
+ */
+.tmerrormsg {
+ color: #ff0000;
+ white-space: pre;
+ font-family: Monospace, "Lucida Console", "Courier New", "Courier";
+ display: block;
+ border: 1px solid;
+ margin: 1em;
+ padding: 0.6em;
+}
+
+
+/*
+ * Generic odd/even row and sub-row attribs.
+ */
+.tmeven {
+ background-color: #ececec;
+}
+
+.tmodd {
+ background-color: #fcfcfc;
+}
+
+/** @todo adjust the sub row colors (see change logs for examples). */
+.tmeveneven {
+ background-color: #d8e0f8;
+}
+
+.tmevenodd {
+ background-color: #e8f0ff;
+}
+
+.tmoddeven {
+ background-color: #d8e0f8;
+}
+
+.tmoddodd {
+ background-color: #e8f0ff;
+}
+
+/*
+ * Multi color row/item coloring, 0..7.
+ */
+.tmshade0 { background-color: #ececec; }
+.tmshade1 { background-color: #fbfbfb; }
+.tmshade2 { background-color: #e4e4e4; }
+.tmshade3 { background-color: #f4f4f4; }
+.tmshade4 { background-color: #e0e0e0; }
+.tmshade5 { background-color: #f0f0f0; }
+.tmshade6 { background-color: #dcdcdc; }
+.tmshade7 { background-color: #fdfdfd; }
+
+
+/*
+ * Generic thead class (first-child doesn't work for multiple header rows).
+ */
+.tmheader {
+ background-color: #d0d0d0;
+ color: black;
+}
+
+/*
+ * Generic class for div elements wrapping pre inside a table. This prevents
+ * the <pre> from taking up way more screen space that available.
+ */
+.tdpre {
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+}
+.tdpre pre {
+ overflow: auto;
+}
+
+
+/*
+ * A typical table.
+ */
+/* table.tmtable th {
+ background-color: #d0d0d0;
+ color: black;
+} */
+
+table.tmtable caption {
+ text-align: left;
+}
+
+table.tmtable {
+ width: 100%;
+ border-spacing: 0px;
+}
+
+table.tmtable th {
+ font-size: 1.3em;
+ text-align: center;
+}
+
+table.tmtable, table.tmtable tr, table.tmtable td, table.tmtable th {
+ vertical-align: top;
+}
+
+table.tmtable {
+ border-left: 1px solid black;
+ border-top: 1px solid black;
+ border-right: none;
+ border-bottom: none;
+}
+
+table.tmtable td, table.tmtable th {
+ border-left: none;
+ border-top: none;
+ border-right: 1px solid black;
+ border-bottom: 1px solid black;
+}
+
+table.tmtable td {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+table.tmtable th {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 6px;
+ padding-bottom: 6px;
+}
+
+.tmtable td {
+}
+
+tr.tmseparator td {
+ border-bottom: 2px solid black;
+ font-size: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+
+
+/*
+ * Table placed inside of a big table used to display *all* stuff of a category.
+ */
+
+table.tminnertbl tr:nth-child(odd) {
+ background-color: #e8e8e8;
+}
+table.tminnertbl tr:nth-child(even) {
+ background-color: #f8f8f8;
+}
+table.tminnertbl tr:first-child {
+ background-color: #d0d0d0;
+ color: black;
+}
+
+table.tminnertbl {
+ border-style: dashed;
+ border-spacing: 1px;
+ border-width: 1px;
+ border-color: gray;
+ border-collapse: separate;
+}
+
+table.tminnertbl th, table.tminnertbl td {
+ font-size: 1em;
+ text-align: center;
+ border-style: none;
+ padding: 1px;
+ border-width: 1px;
+ border-color: #FFFFF0;
+}
+
+/*
+ * Table placed inside a form.
+ */
+table.tmformtbl {
+ border-style: none;
+ border-spacing: 1px;
+ border-width: 1px;
+ border-collapse: separate;
+}
+
+table.tmformtbl th, table.tmformtbl td {
+ font-size: 1em;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+ padding-bottom: 1px;
+ padding-top: 1px;
+ border-width: 1px;
+}
+
+table.tmformtbl th, table.tmformtbl thead {
+ background-color: #d0d0d0;
+ font-size: 1em;
+ font-weight: bold;
+}
+
+table.tmformtbl tr.tmodd {
+ background: #e2e2e2;
+}
+
+table.tmformtblschedgroupmembers tr td:nth-child(3),
+table.tmformtblschedgroupmembers tr td:nth-child(4) {
+ text-align: center;
+}
+
+
+/*
+ * Change log table (used with tmtable).
+ */
+table.tmchangelog > tbody {
+ font-size: 1em;
+}
+
+table.tmchangelog tr.tmodd td:nth-child(1),
+table.tmchangelog tr.tmeven td:nth-child(1),
+table.tmchangelog tr.tmodd td:nth-child(2),
+table.tmchangelog tr.tmeven td:nth-child(2) {
+ min-width: 5em;
+ max-width: 10em; /* futile */
+}
+
+table.tmchangelog tr.tmeven {
+ background-color: #e8f0ff;
+}
+
+table.tmchangelog tr.tmodd {
+ background-color: #d8e0f8;
+}
+
+table.tmchangelog tr.tmoddeven, table.tmchangelog tr.tmeveneven {
+ background-color: #fcfcfc;
+}
+
+table.tmchangelog tr.tmoddodd, table.tmchangelog tr.tmevenodd {
+ background-color: #ececec;
+}
+
+table.tmchangelog tr.tmoddeven, table.tmchangelog tr.tmeveneven, table.tmchangelog tr.tmoddodd, table.tmchangelog tr.tmevenodd {
+ font-size: 0.86em;
+}
+
+.tmsyschlogattr {
+ font-size: 0.80em;
+}
+
+.tmsyschlogspacer {
+ width: 0.8em;
+}
+
+td.tmsyschlogspacer:not(:last-child) {
+ width: 1.8em;
+ border-bottom: 0px solid green !important;
+}
+
+.tmsyschlogevent {
+ border-bottom: 0px solid green !important;
+}
+
+.tmsyschlogspacerrowabove {
+ height: 0.22em;
+}
+
+.tmsyschlogspacerrowbelow {
+ height: 0.80em;
+}
+
+
+/*
+ * Elements to be shows on *Show All* pages.
+ */
+
+ul.tmshowall {
+ margin-left: 15px;
+ margin-right: 15px;
+}
+
+li.tmshowall {
+ margin-left: 5px;
+ margin-right: 5px;
+}
+
+
+/*
+ * List navigation table
+ */
+table.tmlistnavtab {
+ width: 100%;
+}
+
+table.tmlistnavtab tr td:nth-child(1) {
+ text-align: left;
+}
+
+table.tmlistnavtab tr td:nth-child(2) {
+ text-align: right;
+}
+
+
+/*
+ * A typical form.
+ *
+ * Note! This _has_ to be redone. It sucks for the wide fields and such.
+ */
+.tmform ul {
+ list-style: none;
+ list-style-type: none;
+}
+
+.tmform li {
+ line-height: 160%;
+}
+
+
+.tmform-field {
+ display: block;
+ clear: both;
+}
+
+.tmform-field label {
+ float: left;
+ text-align: right;
+ width: 20%;
+ min-width: 10em;
+ max-width: 16em;
+ padding-right: 0.9em;
+}
+
+.tmform-error-desc {
+ display: block;
+ color: #ff0000;
+ font-style: italic;
+}
+
+.tmform-button {
+ float: left;
+ padding-top: 0.8em;
+}
+
+.tmform-field input {
+}
+
+.tmform-field-tiny-int input {
+ width: 2em;
+}
+
+.tmform-field-int input {
+ width: 6em;
+}
+
+.tmform-field-long input {
+ width: 9em;
+}
+
+.tmform-field-submit input {
+}
+
+.tmform-field-string input {
+ width: 24em;
+}
+
+.tmform-field-subname input {
+ width: 10em;
+}
+
+.tmform-field-timestamp input {
+ width: 20em;
+}
+
+.tmform-field-uuid input {
+ width: 24em;
+}
+
+.tmform-field-wide input {
+ width: 78%;
+ overflow: hidden;
+}
+
+.tmform-field-wide100 input {
+ width: 100%;
+ overflow: hidden;
+}
+
+.tmform-field-list {
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
+.tmform-checkboxes-container {
+ padding: 3px;
+ overflow: auto;
+ border: 1px dotted #cccccc;
+}
+
+.tmform-checkbox-holder {
+ float: left;
+ min-width: 20em;
+}
+
+#tmform-checkbox-list-os-arches .tmform-checkbox-holder {
+ min-width: 11em;
+}
+
+#tmform-checkbox-list-build-types .tmform-checkbox-holder {
+ min-width: 6em;
+}
+
+.tmform-input-readonly {
+ background: #ADD8EF;
+ color: #ffffff;
+}
+
+/* (Test case argument variation.) */
+
+table.tmform-innertbl {
+ border-style: none;
+ border-spacing: 1px;
+ border-width: 1px;
+ border-collapse: separate;
+ width: 78%;
+}
+
+table.tmform-innertbl caption {
+ text-align: left;
+}
+
+table.tmform-innertbl th, table.tmform-innertbl td {
+ font-size: 1em;
+ text-align: center;
+ border-style: none;
+ /* padding-top: 1px;*/
+ /*padding-bottom: 1px;*/
+ padding-left: 2px;
+ padding-right: 2px;
+ border-width: 1px;
+ border-color: #FFFFF0;
+ background-color: #f9f9f9;
+}
+
+.tmform-inntertbl-td-wide input {
+ width: 100%;
+ overflow: hidden;
+}
+
+.tmform-inntertbl-td-wide {
+ width: 100%;
+}
+
+
+/*
+ * The test case argument variation table.
+ */
+table.tmform-testcasevars {
+ border-style: none;
+ border-spacing: 0px;
+ border-width: 0px;
+ border-collapse: collapse;
+ width: 78%;
+}
+
+table.tmform-testcasevars tbody {
+ border-style: solid;
+ border-spacing: 1px;
+ border-width: 1px;
+ margin: 2px;
+}
+
+table.tmform-testcasevars td {
+ padding-right: 3px;
+ padding-left: 3px;
+}
+
+table.tmform-testcasevars td:first-child, table.tmform-testcasevars td:nth-child(3) {
+ width: 8em;
+ text-align: right;
+}
+table.tmform-testcasevars td:nth-child(5) {
+ width: 4em;
+ text-align: left;
+}
+
+
+.tmform-testcasevars caption {
+ text-align: left;
+}
+
+tr.tmform-testcasevars-first-row td {
+ padding-top: 0px;
+ padding-bottom: 0px;
+ background-color: #e3e3ec;
+}
+
+.tmform-testcasevars-inner-row td {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
+
+tr.tmform-testcasevars-final-row td {
+ padding-top: 0px;
+ padding-bottom: 1px;
+}
+
+td.tmform-testcasevars-stupid-border-column {
+ /* Stupid hack. */
+ min-width: 2px;
+ width: 0.1%;
+}
+
+
+
+/*
+ * Log viewer.
+ */
+.tmlog a[href] {
+ background-color: #e0e0e0;
+ padding-left: 0.8em;
+ padding-right: 0.8em;
+}
+
+.tmlog pre {
+ background-color: #000000;
+ color: #00ff00;
+ font-family: "Monospace", "Lucida Console", "Courier New", "Courier";
+}
+
+
+/*
+ * Debug SQL traceback.
+ */
+#debug, #debug h1, #debug h2, #debug h3,
+#debug2, #debug2 h1, #debug2 h2, #debug2 h3 {
+ color: #00009f;
+}
+
+table.tmsqltable {
+ border-collapse: collapse;
+}
+
+table.tmsqltable, table.tmsqltable tr, table.tmsqltable td, table.tmsqltable th {
+ border: 1px solid;
+ vertical-align: middle;
+ padding: 0.1ex 0.5ex;
+}
+
+table.tmsqltable pre {
+ text-align: left;
+}
+
+table.tmsqltable tr td {
+ text-align: left;
+}
+
+table.tmsqltable tr td:nth-child(1),
+table.tmsqltable tr td:nth-child(2),
+table.tmsqltable tr td:nth-child(3),
+table.tmsqltable tr td:nth-child(4) {
+ text-align: right;
+}
+
+
+
+/*
+ * Various more or less common span classes.
+ */
+.tmspan-offline {
+ color: #f08020;
+ font-size: 0.75em;
+}
+
+.tmspan-online {
+ font-size: 0.75em;
+}
+
+.tmspan-name, .tmspan-osarch {
+ font-weight: bold;
+}
+
+.tmspan-osver1 {
+ font-style: italic;
+}
+
+.tmspan-osver2 {
+ font-style: normal;
+}
+
+
+/*
+ * Subversion tooltip.
+ */
+.tmvcstooltip {
+ padding: 0px;
+ min-width: 50em;
+ overflow: hidden;
+ border: 0px none;
+}
+
+.tmvcstooltip iframe {
+ padding: 0px;
+ margin: 0px;
+ border: 0px none;
+ width: 100%;
+ //overflow: auto;
+ overflow: hidden;
+}
+
+.tmvcstooltipnew {
+ padding: 0px;
+ min-width: 50em;
+ overflow: hidden;
+ border: 0px none;
+ background-color: #f9f9f9;
+}
+
+
+/*
+ * Workaround for flickering tooltips in the column bar graphs (see
+ * https://github.com/google/google-visualization-issues/issues/2162).
+ */
+.google-visualization-tooltip {
+ pointer-events: none;
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/css/details.css b/src/VBox/ValidationKit/testmanager/htdocs/css/details.css
new file mode 100644
index 00000000..1ae05671
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/css/details.css
@@ -0,0 +1,216 @@
+/* $Id: details.css $ */
+/** @file
+ * Test Manager - Test Details CSS.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*
+ * The test details page has no side menu, so adjust the top-menu and main
+ * sections so they start at the left border.
+ */
+
+#top-menu, #main {
+ left: 0;
+}
+#main {
+ margin-left: 0px;
+}
+
+.tmtbl-events {
+
+}
+
+.tmstatusrow-failure, .tmstatusrow-timed-out, .tmstatusrow-rebooted {
+ color: #e80000;
+}
+
+.tmstatusrow-skipped, .tmstatusrow-aborted, .tmstatusrow-bad-testbox {
+ color: #0000f0;
+}
+
+
+/*
+ * Test results.
+ */
+
+/*
+ * Details table on the individual test result page.
+ */
+table.tmtbl-testresult-details {
+ border-style: dashed;
+ border-spacing: 1px;
+ border-width: 1px;
+ border-color: gray;
+ border-collapse: separate;
+}
+
+table.tmtbl-testresult-details caption {
+ text-align: left;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+table.tmtbl-testresult-details td, table.tmtbl-testresult-details th {
+ font-size: 1em;
+ border-style: none;
+ padding-bottom: 3px;
+ padding-top: 3px;
+ padding-left: 2px;
+ padding-right: 2px;
+ border-width: 1px;
+}
+
+table.tmtbl-testresult-details th {
+ text-align: left;
+}
+
+.tmtbl-result-details-caption {
+ font-size: 1.2em;
+ font-weight: bold;
+ text-align: center;
+ background-color: #c0d0e0;
+}
+
+.tmtbl-result-details-subcaption {
+ text-align: center;
+}
+
+
+/*
+ * Event log on the individual test result page.
+ */
+.tmtbl-events td {
+ padding-bottom: 1px;
+ padding-top: 1px;
+ padding-left: 1px;
+ padding-right: 1px;
+ vertical-align: top;
+}
+
+.tmtbl-events th {
+ font-size: 1.3em;
+ text-align: center;
+}
+
+table.tmtbl-events, table.tmtbl-events tr, table.tmtbl-events td, table.tmtbl-events th {
+ border-collapse: collapse;
+}
+
+tr.tmtbl-events-leaf {
+}
+
+tr.tmtbl-events-first {
+ border-top: 1px dotted;
+}
+
+tr.tmtbl-events-value {
+}
+
+tr.tmtbl-events-final {
+ border-bottom: 1px dotted;
+}
+
+
+tr.tmtbl-events-lvl0 td {
+ padding-top: 8px;
+ padding-bottom: 8px;
+}
+
+tr.tmtbl-events-lvl1 td {
+ padding-top: 6px;
+ padding-bottom: 6px;
+}
+
+tr.tmtbl-events-lvl2 td {
+ padding-top: 4px;
+ padding-bottom: 4px;
+}
+
+tr.tmtbl-events-lvl3 td {
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
+tr.tmtbl-events-lvl4 td {
+ padding-top: 1px;
+ padding-bottom: 1px;
+}
+
+tr.tmtbl-events-lvl5 td,
+tr.tmtbl-events-lvl6 td,
+tr.tmtbl-events-lvl7 td,
+tr.tmtbl-events-lvl8 td,
+tr.tmtbl-events-lvl9 td,
+tr.tmtbl-events-lvl10 td {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
+
+td.tmtbl-events-number {
+ text-align: right;
+}
+
+td.tmtbl-events-number, td.tmtbl-events-unit {
+}
+
+tr.tmtbl-events-value td:nth-child(3),
+tr.tmtbl-events-file td:nth-child(3),
+tr.tmtbl-events-message td:nth-child(3) {
+ padding-left: 2em;
+}
+
+tr.tmtbl-events-value td:nth-child(3),
+tr.tmtbl-events-message td:nth-child(3) {
+ font-style: italic;
+}
+
+
+/*
+ * Status coloring. (move to common.css?)
+ */
+.tmspan-status-success {
+ color: green;
+}
+.tmspan-status-skipped {
+ color: blue;
+}
+.tmspan-status-failure {
+ color: red;
+}
+.tmspan-status-success, .tmspan-status-skipped, .tmspan-status-failure {
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/css/graphwiz.css b/src/VBox/ValidationKit/testmanager/htdocs/css/graphwiz.css
new file mode 100644
index 00000000..2354bfc1
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/css/graphwiz.css
@@ -0,0 +1,237 @@
+/* $Id: graphwiz.css $ */
+/** @file
+ * Test Manager - Graph Wizard CSS.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*
+ * The graph wizard page currently has no side menu, so adjust the top-menu
+ * and main sections so they start at the left border.
+ */
+
+#main {
+ margin-left: 0;
+}
+
+.tmtbl-events {
+
+}
+
+/*
+ * Let the top navigation and end selection inputs look alike.
+ */
+#graphwiz-nav, #graphwiz-end-selection {
+ background-color: #c0cbd6;
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ margin-left: 1px;
+ margin-right: 1px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+ width: 100%;
+}
+
+
+/*
+ * Navigation and it's inputs.
+ */
+
+#graphwiz-nav {
+ min-height: 4.2em;
+}
+
+#graphwiz-top-1, #graphwiz-top-2 {
+ clear: both;
+}
+
+#graphwiz-time, #graphwiz-top-options-1, #graphwiz-top-submit, #graphwiz-top-options-2 {
+ display: block;
+}
+
+#graphwiz-time, #graphwiz-top-submit {
+ margin-left: 1em;
+ margin-right: 2em;
+ float: left;
+}
+
+#graphwiz-top-options-1, #graphwiz-top-options-2 {
+ margin-left: 2em;
+ margin-right: 1em;
+ float: right;
+}
+
+.graphwiz-pixel-input, .graphwiz-dpi-input, .graphwiz-time-input, .graphwiz-period-input {
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+
+.graphwiz-pixel-input {
+ width: 3em;
+ text-align: right
+}
+
+.graphwiz-dpi-input {
+ width: 2em;
+ text-align: right
+}
+
+.graphwiz-time-input {
+ width: 18em;
+ text-align: left
+}
+
+.graphwiz-period-input {
+ width: 4em;
+ text-align: right
+}
+
+.graphwiz-maxerrorbar-input {
+ width: 2em;
+ text-align: right;
+}
+
+.graphwiz-fontsize-input {
+ width: 2em;
+ text-align: right;
+}
+
+.graphwiz-maxpergraph-input {
+ width: 2em;
+ text-align: right;
+}
+
+/*
+ * The graphs.
+ */
+#graphwiz-graphs {
+ margin-top: 0.5em;
+}
+
+.graphwiz-collection {
+ margin-top: 1em;
+ background-color: #f0f0f0;
+ padding-bottom: 1em;
+}
+
+.graphwiz-src-select {
+ margin-left: 0.2em;
+ margin-right: 0.2em;
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+ padding-left: 0.3em;
+ padding-top: 0.3em;
+ padding-bottom: 0.3em;
+ padding-right: 0.3em;
+ font-size: 1.4em;
+}
+
+.graphwiz-graph {
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+.graphwiz-graph svg {
+ width: 100%;
+}
+
+/*
+ * Table data.
+ */
+table.graphwiz-tab {
+ width: auto;
+}
+
+.graphwiz-tab td {
+ text-align: right;
+}
+
+/*
+ * The end selection.
+ */
+#graphwiz-end-selection {
+ margin-top: 1em;
+}
+
+.graphwiz-end-selection-group {
+ clear: both;
+ display: block;
+}
+
+.graphwiz-end-selection-group li {
+ display: block;
+ width: 25%;
+ float: left;
+}
+
+#graphwiz-buildcategories li, #graphwiz-testcase-variations li {
+ width: 50%;
+}
+
+.graphwiz-end-selection-group label {
+ margin-left: 0.3em;
+ vertical-align: middle;
+}
+
+.graphwiz-end-selection-group input {
+ vertical-align: middle;
+}
+
+.graphwiz-end-selection-group h3 {
+ font-size: 1.2em;
+ font-style: italic;
+ font-weight: bold;
+ margin-bottom: 0.26em;
+}
+
+#graphwiz-buildcategories h3, #graphwiz-testcase-variations h3, #graphwiz-end-submit {
+ padding-top: 1em;
+}
+
+#graphwiz-end-submit {
+ clear: both;
+ display: block;
+}
+
+
+
+/*
+ * Tool tip tables.
+ */
+table.graphwiz-tt td:nth-child(1) {
+ font-weight: bold;
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css b/src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css
new file mode 100644
index 00000000..cb90ae0f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/css/tooltip.css
@@ -0,0 +1,132 @@
+/* $Id: tooltip.css $ */
+/** @file
+ * Test Manager - Tooltip content (via iframe).
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/*
+ * Form the main divs in template-tooltip.html.
+ */
+.tooltip-main {
+ width: 100%;
+}
+
+.tooltip-inner {
+ clear: both;
+ border: 2px solid black;
+ padding-left: 2px;
+ padding-right: 2px;
+ padding-top: 2px;
+ padding-bottom: 2px
+}
+
+/*
+ * Timeline tooltip.
+ */
+.tmtimelinetooltip {
+ font-size: 1em;
+}
+
+/*
+ * Relative stuff that could also be used for a non-tooltip VCS timeline.
+ */
+.tmvcstimeline-highlighted {
+ background: #f0f0f0;
+}
+
+.tmvcstimeline h2 {
+ clear: both;
+ font-size: 120%;
+ background: #e8e8e8;
+ border-bottom: 1px solid #c8c8c8;
+ border-radius: 3px;
+ margin-left: 0.2em;
+ margin-right: 0.2em;
+ margin-top: 0.2em;
+ margin-bottom: 0.4em;
+ padding-left: 0.2em;
+ padding-right: 0.2em;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+}
+
+.tmvcstimeline dl {
+ margin-left: 0.8em;
+ margin-right: 0.2em;
+ margin-top: 0.2em;
+ margin-bottom: 0.8em;
+}
+
+.tmvcstimeline dt {
+ font-size: 118%;
+ padding-left: 0.2em;
+ margin-top: 0.1em;
+ margin-bottom: 0.0em;
+}
+
+.tmvcstimeline dt, .tmvcstimeline :link, .tmvcstimeline :link:visited, .tmvcstimeline :link:hover {
+ color: black;
+ text-decoration: none;
+}
+
+.tmvcstimeline :link:hover {
+ border: 1px dotted black;
+}
+
+.tmvcstimeline-time {
+ font-size: 88%;
+ margin-right: 0.2em;
+}
+
+.tmvcstimeline-time, .tmvcstimeline-author {
+ color: #5858a0;
+}
+
+.tmvcstimeline-rev {
+ color: #0000ee;
+}
+
+.tmvcstimeline dd {
+ padding-left: 2em;
+ margin-top: 0.0em;
+ margin-bottom: 0.4em;
+ color: #424250;
+}
+
+/* This helps highlighting the revision we're showing the tooltip for. */
+.tmvcstimeline-highlight, .tmvcstimeline :target, .tmvcstimeline :target + dd {
+ background-color: #d8e8ff;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox.svg b/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox.svg
new file mode 100644
index 00000000..2369828b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox.svg
@@ -0,0 +1,806 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
+<g>
+ <rect fill="none" width="256" height="256"/>
+ <g>
+
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-180.1821" y1="784.8389" x2="-56.9487" y2="784.8389" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#0A2B4D"/>
+ <stop offset="0.0423" style="stop-color:#7DA5DC"/>
+ <stop offset="0.0441" style="stop-color:#83A9DE"/>
+ <stop offset="0.0553" style="stop-color:#A1BFE8"/>
+ <stop offset="0.067" style="stop-color:#B9D0F0"/>
+ <stop offset="0.0794" style="stop-color:#CADCF6"/>
+ <stop offset="0.093" style="stop-color:#D4E4F9"/>
+ <stop offset="0.1099" style="stop-color:#D7E6FA"/>
+ <stop offset="0.254" style="stop-color:#D0DCFA"/>
+ <stop offset="0.42" style="stop-color:#85A0C8"/>
+ <stop offset="0.57" style="stop-color:#2E4573"/>
+ <stop offset="0.6978" style="stop-color:#14335E"/>
+ <stop offset="0.7692" style="stop-color:#1C3866"/>
+ <stop offset="0.8462" style="stop-color:#4F73AA"/>
+ <stop offset="0.9153" style="stop-color:#6487B9"/>
+ <stop offset="1" style="stop-color:#1A3B61"/>
+ </linearGradient>
+ <path fill="url(#SVGID_1_)" d="M129.117,141.783c33.426,0,60.732,23.482,60.732,52.32c0,1.838,0,4.744,0,6.578
+ c0,28.838-27.308,51.803-60.732,51.803c-33.503,0-60.811-22.965-60.811-51.803c0-1.834,0-4.74,0-6.578
+ C68.307,165.266,95.614,141.783,129.117,141.783z"/>
+
+ <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-150.8486" y1="822.7695" x2="-84.4476" y2="740.7712" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#E1E6FA"/>
+ <stop offset="0.07" style="stop-color:#B4C3E1"/>
+ <stop offset="0.11" style="stop-color:#8293B8"/>
+ <stop offset="0.1551" style="stop-color:#2D4173"/>
+ <stop offset="0.2888" style="stop-color:#1B2F61"/>
+ <stop offset="0.5" style="stop-color:#14285A"/>
+ <stop offset="0.7" style="stop-color:#14285A"/>
+ <stop offset="0.8663" style="stop-color:#213970"/>
+ <stop offset="1" style="stop-color:#4164A5"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_2_)" cx="129.041" cy="194.065" rx="57.889" ry="49.1"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638" id="SVGID_3_">
+ <g filter="url(#Adobe_OpacityMaskFilter)">
+
+ <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-85.9404" y1="823.8486" x2="-149.3573" y2="739.6917" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#CCCCCC"/>
+ <stop offset="0.15" style="stop-color:#000000"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_4_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ </g>
+ </mask>
+
+ <radialGradient id="SVGID_5_" cx="-148.8311" cy="824.0654" r="100.2425" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#DCE6FA"/>
+ <stop offset="0.4" style="stop-color:#AAC3EB"/>
+ <stop offset="1" style="stop-color:#AAC3EB"/>
+ </radialGradient>
+ <ellipse mask="url(#SVGID_3_)" fill="url(#SVGID_5_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_1_" filterUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638" id="SVGID_6_">
+ <g filter="url(#Adobe_OpacityMaskFilter_1_)">
+
+ <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-117.6489" y1="831.0889" x2="-117.6489" y2="732.4512" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#999999"/>
+ <stop offset="0.05" style="stop-color:#000000"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_7_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ </g>
+ </mask>
+
+ <radialGradient id="SVGID_8_" cx="-148.8311" cy="824.0654" r="100.2425" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#DCE6FA"/>
+ <stop offset="0.4" style="stop-color:#AAC3EB"/>
+ <stop offset="1" style="stop-color:#AAC3EB"/>
+ </radialGradient>
+ <ellipse mask="url(#SVGID_6_)" fill="url(#SVGID_8_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_2_" filterUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638" id="SVGID_9_">
+ <g filter="url(#Adobe_OpacityMaskFilter_2_)">
+
+ <linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="-100.396" y1="829.1719" x2="-134.902" y2="734.3676" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#595959"/>
+ <stop offset="0.0535" style="stop-color:#282828"/>
+ <stop offset="0.1" style="stop-color:#000000"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_10_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ </g>
+ </mask>
+
+ <radialGradient id="SVGID_11_" cx="-148.8311" cy="824.0654" r="100.2425" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#DCE6FA"/>
+ <stop offset="0.4" style="stop-color:#AAC3EB"/>
+ <stop offset="1" style="stop-color:#AAC3EB"/>
+ </radialGradient>
+ <ellipse mask="url(#SVGID_9_)" fill="url(#SVGID_11_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_3_" filterUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="70.895" y="144.746" width="116.292" height="98.638" id="SVGID_12_">
+ <g filter="url(#Adobe_OpacityMaskFilter_3_)">
+
+ <linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="-134.9023" y1="829.1719" x2="-100.3965" y2="734.368" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="0.05" style="stop-color:#1A1A1A"/>
+ <stop offset="0.09" style="stop-color:#000000"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_13_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+ </g>
+ </mask>
+
+ <radialGradient id="SVGID_14_" cx="-148.8311" cy="824.0654" r="100.2425" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#DCE6FA"/>
+ <stop offset="0.4" style="stop-color:#AAC3EB"/>
+ <stop offset="1" style="stop-color:#AAC3EB"/>
+ </radialGradient>
+ <ellipse mask="url(#SVGID_12_)" fill="url(#SVGID_14_)" cx="129.041" cy="194.065" rx="58.146" ry="49.319"/>
+
+ <linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="-178.3818" y1="813.666" x2="-56.8384" y2="813.666" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.0055" style="stop-color:#3C5A78"/>
+ <stop offset="0.033" style="stop-color:#6E8CBE"/>
+ <stop offset="0.123" style="stop-color:#A5BEE1"/>
+ <stop offset="0.25" style="stop-color:#96AFD2"/>
+ <stop offset="0.52" style="stop-color:#3C5A87"/>
+ <stop offset="0.72" style="stop-color:#2B486D"/>
+ <stop offset="0.9" style="stop-color:#466491"/>
+ <stop offset="1" style="stop-color:#3C5082"/>
+ </linearGradient>
+ <path fill="url(#SVGID_15_)" d="M189.852,198.923c0,0.61,0,1.222,0,1.759c0,28.838-27.309,52.318-60.733,52.318
+ c-33.501,0-60.81-23.48-60.81-52.318c0-0.537,0-1.146,0-1.759c0,28.761,27.308,52.243,60.81,52.243
+ C162.543,251.166,189.852,227.684,189.852,198.923z"/>
+
+ <linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="-178.1328" y1="776.4775" x2="-57.1652" y2="787.0609" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.0053" style="stop-color:#9BBEE1"/>
+ <stop offset="0.0802" style="stop-color:#D2E6FA"/>
+ <stop offset="0.1337" style="stop-color:#F0F5FF"/>
+ <stop offset="0.1979" style="stop-color:#F0F5FF"/>
+ <stop offset="0.35" style="stop-color:#DEE6F0"/>
+ <stop offset="0.55" style="stop-color:#AFBEDC"/>
+ <stop offset="0.7" style="stop-color:#96AFD7"/>
+ <stop offset="0.9091" style="stop-color:#A0B4DC"/>
+ <stop offset="1" style="stop-color:#6487AF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_16_)" d="M129.041,141.693c-33.563,0-60.771,23.449-60.771,52.371s27.208,52.371,60.771,52.371
+ c33.564,0,60.771-23.449,60.771-52.371C189.813,165.145,162.604,141.693,129.041,141.693z M129.041,243.165
+ c-31.971,0-57.887-21.983-57.887-49.101c0-27.115,25.916-49.104,57.887-49.104c31.973,0,57.889,21.986,57.889,49.104
+ S161.014,243.165,129.041,243.165z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_4_" filterUnits="userSpaceOnUse" x="68.27" y="141.693" width="121.542" height="104.742">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="68.27" y="141.693" width="121.542" height="104.742" id="SVGID_17_">
+ <g filter="url(#Adobe_OpacityMaskFilter_4_)">
+
+ <linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="-117.6489" y1="834.1406" x2="-117.6489" y2="729.4004" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.35" style="stop-color:#000000"/>
+ <stop offset="0.6" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_18_)" cx="129.041" cy="194.065" rx="60.771" ry="52.37"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_17_)" fill="#142355" d="M129.041,141.693c-33.563,0-60.771,23.449-60.771,52.371
+ s27.208,52.371,60.771,52.371c33.564,0,60.771-23.449,60.771-52.371C189.813,165.145,162.604,141.693,129.041,141.693z
+ M129.041,243.165c-31.971,0-57.887-21.983-57.887-49.101c0-27.115,25.916-49.104,57.887-49.104
+ c31.973,0,57.889,21.986,57.889,49.104S161.014,243.165,129.041,243.165z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_5_" filterUnits="userSpaceOnUse" x="68.27" y="141.693" width="121.542" height="104.742">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="68.27" y="141.693" width="121.542" height="104.742" id="SVGID_19_">
+ <g filter="url(#Adobe_OpacityMaskFilter_5_)">
+
+ <linearGradient id="SVGID_20_" gradientUnits="userSpaceOnUse" x1="-135.9253" y1="831.9824" x2="-99.3735" y2="731.5574" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.3" style="stop-color:#000000"/>
+ <stop offset="1" style="stop-color:#CCCCCC"/>
+ </linearGradient>
+ <ellipse fill="url(#SVGID_20_)" cx="129.041" cy="194.065" rx="60.771" ry="52.37"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_19_)" fill="#142355" d="M129.041,141.693c-33.563,0-60.771,23.449-60.771,52.371
+ s27.208,52.371,60.771,52.371c33.564,0,60.771-23.449,60.771-52.371C189.813,165.145,162.604,141.693,129.041,141.693z
+ M129.041,243.165c-31.971,0-57.887-21.983-57.887-49.101c0-27.115,25.916-49.104,57.887-49.104
+ c31.973,0,57.889,21.986,57.889,49.104S161.014,243.165,129.041,243.165z"/>
+
+ <radialGradient id="SVGID_21_" cx="-242.0352" cy="-534.5098" r="111.9119" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="1" style="stop-color:#C3D2F0"/>
+ </radialGradient>
+ <path fill="url(#SVGID_21_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699l-92.826,71.046
+ c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045c-1.157-0.889-1.897-2.246-2.011-3.7
+ L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393
+ C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+
+ <linearGradient id="SVGID_22_" gradientUnits="userSpaceOnUse" x1="-121.1333" y1="710.2393" x2="-217.5764" y2="629.314" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="0.9" style="stop-color:#F0F5FF"/>
+ <stop offset="0.96" style="stop-color:#DCE6F8"/>
+ <stop offset="0.99" style="stop-color:#C3D2F0"/>
+ </linearGradient>
+ <path fill="url(#SVGID_22_)" d="M22.205,49.877c-1.1,1.812,2.384,3.694,2.678,3.872l99.186,60.164
+ c1.081,0.655,1.748,1.842,1.752,3.109l4.361-0.001c0.004-1.266,0.672-2.454,1.752-3.109l-1.959-3.612
+ c-0.578,0.348-1.229,0.521-1.882,0.521c-0.652,0-1.305-0.173-1.883-0.521L26.503,50.528c-0.292-0.175-0.46-0.499-0.439-0.837
+ C26.063,49.691,23.283,48.1,22.205,49.877z"/>
+
+ <linearGradient id="SVGID_23_" gradientUnits="userSpaceOnUse" x1="-106.9102" y1="716.4375" x2="-29.1729" y2="623.7938" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="0.6" style="stop-color:#F5FAFF"/>
+ <stop offset="0.92" style="stop-color:#E1E6FA"/>
+ <stop offset="0.97" style="stop-color:#CFDAF4"/>
+ <stop offset="1" style="stop-color:#C0CFEE"/>
+ </linearGradient>
+ <path fill="url(#SVGID_23_)" d="M233.797,49.876c1.1,1.812-2.385,3.694-2.678,3.872l-99.188,60.163
+ c-1.08,0.655-1.748,1.842-1.752,3.109l-4.36-0.001c-0.005-1.266-0.672-2.454-1.752-3.109l1.958-3.612
+ c0.579,0.348,1.231,0.521,1.882,0.521c0.652,0,1.304-0.172,1.882-0.521l99.707-59.771c0.291-0.175,0.461-0.499,0.438-0.837
+ C229.938,49.69,232.719,48.099,233.797,49.876z"/>
+
+ <linearGradient id="SVGID_24_" gradientUnits="userSpaceOnUse" x1="-118.689" y1="698.002" x2="-118.689" y2="824.7695" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="0.9" style="stop-color:#DCE6FF"/>
+ <stop offset="1" style="stop-color:#C2D4F0"/>
+ </linearGradient>
+ <path fill="url(#SVGID_24_)" d="M128,237.064c-2.653,0-2.922-3.642-2.922-3.642c0.138,0,0.278-0.03,0.405-0.096
+ c0.31-0.155,0.507-0.474,0.507-0.819l-0.17-115.49c-0.005-1.267-0.672-2.454-1.752-3.109l2.142-3.612
+ c0.579,0.348,1.231,0.52,1.883,0.52c0.65,0,1.304-0.172,1.882-0.52l1.959,3.611c-1.082,0.655-1.748,1.843-1.752,3.109
+ l-0.17,115.49c0,0.349,0.194,0.665,0.506,0.819c0.127,0.063,0.268,0.095,0.403,0.095C130.922,233.423,130.654,237.064,128,237.064
+ z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_6_" filterUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064" id="SVGID_25_">
+ <g filter="url(#Adobe_OpacityMaskFilter_6_)">
+
+ <linearGradient id="SVGID_26_" gradientUnits="userSpaceOnUse" x1="-225.665" y1="639.6367" x2="-23.757" y2="755.2705" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.78" style="stop-color:#000000"/>
+ <stop offset="0.8" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_26_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699l-92.826,71.046
+ c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045c-1.157-0.889-1.897-2.246-2.011-3.7
+ L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393
+ C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ </g>
+ </mask>
+
+ <linearGradient id="SVGID_27_" gradientUnits="userSpaceOnUse" x1="-213.625" y1="755.2715" x2="-11.7157" y2="639.637" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.2" style="stop-color:#C3D2F0"/>
+ <stop offset="0.22" style="stop-color:#7DA0D2"/>
+ <stop offset="0.2674" style="stop-color:#6E91CD"/>
+ <stop offset="0.7" style="stop-color:#7396D2"/>
+ <stop offset="1" style="stop-color:#5A78B4"/>
+ </linearGradient>
+ <path mask="url(#SVGID_25_)" fill="url(#SVGID_27_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699
+ l-92.826,71.046c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045
+ c-1.157-0.889-1.897-2.246-2.011-3.7L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433
+ c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_7_" filterUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064" id="SVGID_28_">
+ <g filter="url(#Adobe_OpacityMaskFilter_7_)">
+
+ <linearGradient id="SVGID_29_" gradientUnits="userSpaceOnUse" x1="-213.522" y1="755.417" x2="-12.0263" y2="639.0833" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0.2" style="stop-color:#FFFFFF"/>
+ <stop offset="0.22" style="stop-color:#000000"/>
+ </linearGradient>
+ <path fill="url(#SVGID_29_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699l-92.826,71.046
+ c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045c-1.157-0.889-1.897-2.246-2.011-3.7
+ L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393
+ C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ </g>
+ </mask>
+
+ <linearGradient id="SVGID_30_" gradientUnits="userSpaceOnUse" x1="-225.354" y1="639.084" x2="-23.8602" y2="755.4165" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#B9C8DC"/>
+ <stop offset="0.3" style="stop-color:#D7D5EB"/>
+ <stop offset="0.5" style="stop-color:#D7DCEB"/>
+ <stop offset="0.78" style="stop-color:#DCDCEB"/>
+ <stop offset="0.8" style="stop-color:#C3D2F0"/>
+ </linearGradient>
+ <path mask="url(#SVGID_28_)" fill="url(#SVGID_30_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699
+ l-92.826,71.046c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045
+ c-1.157-0.889-1.897-2.246-2.011-3.7L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433
+ c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_8_" filterUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="21.454" y="3" width="213.092" height="234.064" id="SVGID_31_">
+ <g filter="url(#Adobe_OpacityMaskFilter_8_)">
+
+ <radialGradient id="SVGID_32_" cx="-214.9419" cy="621.7891" r="31.5227" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#595959"/>
+ <stop offset="1" style="stop-color:#000000"/>
+ </radialGradient>
+ <path fill="url(#SVGID_32_)" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699l-92.826,71.046
+ c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045c-1.157-0.889-1.897-2.246-2.011-3.7
+ L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393
+ C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_31_)" fill="#E1F5FF" d="M234.529,52.965l-8.549,108.286c-0.113,1.454-0.854,2.812-2.012,3.699
+ l-92.826,71.046c-0.933,0.714-2.037,1.068-3.144,1.068c-1.105,0-2.21-0.354-3.14-1.068l-92.832-71.045
+ c-1.157-0.889-1.897-2.246-2.011-3.7L21.47,52.965c-0.173-2.195,1.062-4.258,3.078-5.141L125.929,3.433
+ c1.318-0.577,2.827-0.577,4.147,0l101.375,44.393C233.467,48.708,234.703,50.771,234.529,52.965z"/>
+ <g>
+
+ <radialGradient id="SVGID_33_" cx="-118.6724" cy="642.2432" r="64.9423" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#19416E"/>
+ <stop offset="1" style="stop-color:#0A2D64"/>
+ </radialGradient>
+ <path fill="url(#SVGID_33_)" d="M209.039,50.425l-79.438,46.227c-0.477,0.275-1.002,0.412-1.528,0.412
+ c-0.527,0-1.053-0.136-1.528-0.412L46.999,50.426c-0.25-0.145-0.398-0.417-0.384-0.705c0.014-0.288,0.188-0.545,0.449-0.666
+ l79.834-36.784c0.746-0.346,1.604-0.346,2.354,0l79.721,36.783c0.262,0.122,0.435,0.377,0.447,0.667
+ C209.436,50.008,209.287,50.28,209.039,50.425z"/>
+
+ <radialGradient id="SVGID_34_" cx="-172.187" cy="727.8721" r="56.6285" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#19416E"/>
+ <stop offset="1" style="stop-color:#0A2D64"/>
+ </radialGradient>
+ <path fill="url(#SVGID_34_)" d="M114.98,123.905l0.871,83.976c0.002,0.327-0.178,0.628-0.471,0.775
+ c-0.123,0.063-0.257,0.096-0.391,0.096c-0.18,0-0.361-0.057-0.512-0.169l-74.515-54.917c-0.674-0.497-1.095-1.257-1.153-2.092
+ l-5.652-79.07c-0.024-0.324,0.138-0.634,0.417-0.8c0.277-0.166,0.626-0.162,0.902,0.008l79.063,49.616
+ C114.428,121.886,114.969,122.855,114.98,123.905z"/>
+
+ <radialGradient id="SVGID_35_" cx="-1001.7246" cy="782.7354" r="61.9365" gradientTransform="matrix(-0.9143 0 0 0.9143 -734.343 -575.489)" gradientUnits="userSpaceOnUse">
+ <stop offset="0" style="stop-color:#19416E"/>
+ <stop offset="1" style="stop-color:#0A2D64"/>
+ </radialGradient>
+ <path fill="url(#SVGID_35_)" d="M141.057,123.904l-0.871,83.977c-0.002,0.326,0.179,0.627,0.472,0.775
+ c0.122,0.063,0.258,0.095,0.391,0.095c0.181,0,0.361-0.058,0.513-0.168l74.516-54.917c0.674-0.498,1.096-1.257,1.152-2.093
+ l5.651-79.07c0.022-0.324-0.139-0.634-0.416-0.8c-0.276-0.166-0.627-0.163-0.901,0.008L142.5,121.327
+ C141.609,121.885,141.068,122.854,141.057,123.904z"/>
+ </g>
+
+ <linearGradient id="SVGID_36_" gradientUnits="userSpaceOnUse" x1="-199.5181" y1="619.627" x2="-37.8291" y2="619.627" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#234B82"/>
+ <stop offset="0.489" style="stop-color:#3C5A8C"/>
+ <stop offset="0.5112" style="stop-color:#E6F0FF"/>
+ <stop offset="0.7697" style="stop-color:#FFFFFF"/>
+ <stop offset="1" style="stop-color:#F0F5FF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_36_)" d="M208.004,51.354c0.525-0.314,0.885-0.917,0.855-1.54c-0.027-0.646-0.422-1.267-1.006-1.537
+ l-78.097-36.02c-0.534-0.248-1.11-0.371-1.687-0.371s-1.153,0.123-1.688,0.373l-78.2,36.018c-0.586,0.27-1.01,0.89-1.01,1.525
+ c0,0.621,0.328,1.245,0.863,1.557l1.186,0.6c-0.243-0.141-0.387-0.405-0.373-0.686c0.014-0.279,0.182-0.53,0.437-0.647
+ l77.641-35.774c0.726-0.335,1.561-0.335,2.289,0l77.533,35.772c0.254,0.119,0.422,0.367,0.436,0.649
+ c0.015,0.279-0.131,0.544-0.371,0.685L208.004,51.354z"/>
+
+ <linearGradient id="SVGID_37_" gradientUnits="userSpaceOnUse" x1="-114.0732" y1="786.165" x2="-6.8749" y2="678.9667" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#AFC3D7"/>
+ <stop offset="0.2033" style="stop-color:#BED2E6"/>
+ <stop offset="0.5879" style="stop-color:#BED7EB"/>
+ <stop offset="0.6154" style="stop-color:#3C5A8C"/>
+ <stop offset="1" style="stop-color:#234B82"/>
+ </linearGradient>
+ <path fill="url(#SVGID_37_)" d="M140.545,205.538l-0.002,0.062c-0.002,0.713,0.405,1.371,1.039,1.696
+ c0.271,0.142,0.572,0.212,0.871,0.212c0.393,0,0.795-0.121,1.137-0.375l71.861-52.972c0.945-0.697,1.549-1.777,1.627-2.957
+ l5.451-76.295c0.003-0.051,0.006-0.087,0.006-0.139c-0.003-0.675-0.354-1.291-0.93-1.635c-0.299-0.178-0.64-0.271-0.979-0.271
+ c-0.346,0-0.701,0.094-1.021,0.296l-0.818,0.53c0.266-0.164,0.6-0.167,0.865-0.008c0.266,0.159,0.424,0.457,0.398,0.767
+ l-5.425,75.887c-0.058,0.803-0.461,1.53-1.106,2.008l-71.517,52.707c-0.146,0.108-0.317,0.161-0.489,0.161
+ c-0.13,0-0.259-0.03-0.375-0.091c-0.281-0.143-0.455-0.43-0.453-0.744L140.545,205.538z"/>
+
+ <linearGradient id="SVGID_38_" gradientUnits="userSpaceOnUse" x1="-230.4624" y1="678.9688" x2="-123.2632" y2="786.1679" gradientTransform="matrix(1 0 0 1 246.6899 -587.7051)">
+ <stop offset="0" style="stop-color:#234B82"/>
+ <stop offset="0.3846" style="stop-color:#3C5A8C"/>
+ <stop offset="0.4045" style="stop-color:#F0F5FF"/>
+ <stop offset="0.691" style="stop-color:#FFFFFF"/>
+ <stop offset="1" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_38_)" d="M115.354,204.379c0.003,0.314-0.171,0.604-0.451,0.744c-0.119,0.061-0.247,0.091-0.376,0.091
+ c-0.173,0-0.346-0.053-0.49-0.161L42.52,152.346c-0.646-0.478-1.05-1.205-1.106-2.008l-5.425-75.887
+ c-0.023-0.311,0.133-0.608,0.4-0.767c0.266-0.159,0.601-0.156,0.865,0.008l-0.818-0.53c-0.318-0.203-0.675-0.296-1.02-0.296
+ c-0.341,0-0.681,0.092-0.98,0.271c-0.576,0.344-0.926,0.96-0.928,1.635c0,0.052,0.002,0.087,0.004,0.139l5.452,76.295
+ c0.079,1.18,0.682,2.26,1.628,2.957l71.861,52.972c0.341,0.254,0.743,0.375,1.137,0.375c0.298,0,0.599-0.07,0.871-0.212
+ c0.633-0.325,1.04-0.983,1.038-1.696l-0.002-0.062L115.354,204.379z"/>
+
+ <linearGradient id="SVGID_39_" gradientUnits="userSpaceOnUse" x1="-242.0366" y1="-525.2964" x2="-242.0366" y2="-418.9517" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0" style="stop-color:#FFFFFF"/>
+ <stop offset="1" style="stop-color:#BEDCFA"/>
+ </linearGradient>
+ <path fill="url(#SVGID_39_)" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837C229.912,49.351,229.705,49.051,229.395,48.914
+ z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539
+ c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0
+ l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_9_" filterUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344" id="SVGID_40_">
+ <g filter="url(#Adobe_OpacityMaskFilter_9_)">
+
+ <linearGradient id="SVGID_41_" gradientUnits="userSpaceOnUse" x1="-318.0947" y1="-509.2944" x2="-164.9766" y2="-420.8915" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.6" style="stop-color:#000000"/>
+ <stop offset="1" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_41_)" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837
+ C229.912,49.351,229.705,49.051,229.395,48.914z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4
+ c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647
+ l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649
+ C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_40_)" fill="#C3DCFA" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837C229.912,49.351,229.705,49.051,229.395,48.914
+ z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539
+ c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0
+ l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_10_" filterUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344" id="SVGID_42_">
+ <g filter="url(#Adobe_OpacityMaskFilter_10_)">
+
+ <linearGradient id="SVGID_43_" gradientUnits="userSpaceOnUse" x1="-284.3638" y1="-514.6699" x2="-199.7078" y2="-413.781" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0" style="stop-color:#7A7A7A"/>
+ <stop offset="0.3048" style="stop-color:#000000"/>
+ </linearGradient>
+ <path fill="url(#SVGID_43_)" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837
+ C229.912,49.351,229.705,49.051,229.395,48.914z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4
+ c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647
+ l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649
+ C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ </g>
+ </mask>
+
+ <linearGradient id="SVGID_44_" gradientUnits="userSpaceOnUse" x1="-268.7026" y1="-510.4141" x2="-216.3228" y2="-419.6894" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.0053" style="stop-color:#FFF5F0"/>
+ <stop offset="0.25" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <path mask="url(#SVGID_42_)" fill="url(#SVGID_44_)" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0
+ L26.605,48.914c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771
+ c0.578,0.348,1.231,0.52,1.883,0.52c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837
+ C229.912,49.351,229.705,49.051,229.395,48.914z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4
+ c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647
+ l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649
+ C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_11_" filterUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="26.061" y="4.475" width="203.875" height="106.344" id="SVGID_45_">
+ <g filter="url(#Adobe_OpacityMaskFilter_11_)">
+
+ <linearGradient id="SVGID_46_" gradientUnits="userSpaceOnUse" x1="-302.0186" y1="-413.8936" x2="-182.0531" y2="-514.5565" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0" style="stop-color:#141414"/>
+ <stop offset="0.25" style="stop-color:#000000"/>
+ </linearGradient>
+ <path fill="url(#SVGID_46_)" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837
+ C229.912,49.351,229.705,49.051,229.395,48.914z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4
+ c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647
+ l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649
+ C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_45_)" fill="#2B388F" d="M229.395,48.914l-99.83-44.127c-0.938-0.417-2.003-0.417-2.947,0L26.605,48.914
+ c-0.312,0.138-0.521,0.437-0.542,0.777c-0.021,0.339,0.147,0.662,0.439,0.837l99.707,59.771c0.578,0.348,1.231,0.52,1.883,0.52
+ c0.652,0,1.303-0.172,1.883-0.52l99.52-59.771c0.293-0.175,0.463-0.499,0.439-0.837C229.912,49.351,229.705,49.051,229.395,48.914
+ z M206.813,50.538l-77.256,44.957c-0.461,0.267-0.976,0.4-1.486,0.4c-0.513,0-1.024-0.132-1.486-0.4L49.224,50.539
+ c-0.243-0.141-0.387-0.405-0.373-0.686c0.013-0.279,0.182-0.53,0.437-0.647l77.641-35.774c0.726-0.335,1.561-0.335,2.29,0
+ l77.531,35.772c0.256,0.119,0.422,0.367,0.438,0.649C207.199,50.133,207.057,50.397,206.813,50.538z"/>
+
+ <linearGradient id="SVGID_47_" gradientUnits="userSpaceOnUse" x1="-321.6987" y1="-603.7334" x2="-268.8858" y2="-512.2586" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.0053" style="stop-color:#E6E1E1"/>
+ <stop offset="0.5" style="stop-color:#E3E0DC"/>
+ <stop offset="1" style="stop-color:#EEE9E1"/>
+ </linearGradient>
+ <path fill="url(#SVGID_47_)" d="M125.82,117.02c-0.005-1.267-0.672-2.453-1.752-3.109L24.881,53.748
+ c-0.294-0.178-0.658-0.178-0.951,0.001c-0.292,0.179-0.459,0.505-0.432,0.847l8.144,106.359c0.079,1.038,0.599,1.994,1.429,2.624
+ l91.455,69.657c0.161,0.123,0.357,0.188,0.552,0.188c0.138,0,0.278-0.031,0.405-0.095c0.311-0.155,0.508-0.474,0.507-0.819
+ L125.82,117.02z M113.735,205.9c-0.118,0.061-0.247,0.091-0.375,0.091c-0.174,0-0.347-0.054-0.492-0.161l-71.516-52.707
+ c-0.646-0.479-1.051-1.206-1.107-2.008L34.82,75.227c-0.023-0.311,0.133-0.608,0.4-0.768c0.266-0.159,0.601-0.156,0.865,0.008
+ l75.882,47.62c0.853,0.535,1.372,1.466,1.384,2.473l0.835,80.597C114.19,205.471,114.018,205.758,113.735,205.9z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_12_" filterUnits="userSpaceOnUse" x="23.495" y="53.614" width="102.495" height="179.81">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="23.495" y="53.614" width="102.495" height="179.81" id="SVGID_48_">
+ <g filter="url(#Adobe_OpacityMaskFilter_12_)">
+
+ <linearGradient id="SVGID_49_" gradientUnits="userSpaceOnUse" x1="-218.2163" y1="-602.4976" x2="-372.3692" y2="-513.4973" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.2" style="stop-color:#000000"/>
+ <stop offset="0.9" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <path fill="url(#SVGID_49_)" d="M125.82,117.02c-0.005-1.267-0.672-2.453-1.752-3.109L24.881,53.748
+ c-0.294-0.178-0.658-0.178-0.951,0.001c-0.292,0.179-0.459,0.505-0.432,0.847l8.144,106.359
+ c0.079,1.038,0.599,1.994,1.429,2.624l91.455,69.657c0.161,0.123,0.357,0.188,0.552,0.188c0.138,0,0.278-0.031,0.405-0.095
+ c0.311-0.155,0.508-0.474,0.507-0.819L125.82,117.02z M113.735,205.9c-0.118,0.061-0.247,0.091-0.375,0.091
+ c-0.174,0-0.347-0.054-0.492-0.161l-71.516-52.707c-0.646-0.479-1.051-1.206-1.107-2.008L34.82,75.227
+ c-0.023-0.311,0.133-0.608,0.4-0.768c0.266-0.159,0.601-0.156,0.865,0.008l75.882,47.62c0.853,0.535,1.372,1.466,1.384,2.473
+ l0.835,80.597C114.19,205.471,114.018,205.758,113.735,205.9z"/>
+ </g>
+ </mask>
+
+ <linearGradient id="SVGID_50_" gradientUnits="userSpaceOnUse" x1="-225.4541" y1="-631.8145" x2="-355.862" y2="-476.4005" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0" style="stop-color:#E6D2F0"/>
+ <stop offset="1" style="stop-color:#C3C8DC"/>
+ </linearGradient>
+ <path mask="url(#SVGID_48_)" fill="url(#SVGID_50_)" d="M125.82,117.02c-0.005-1.267-0.672-2.453-1.752-3.109L24.881,53.748
+ c-0.294-0.178-0.658-0.178-0.951,0.001c-0.292,0.179-0.459,0.505-0.432,0.847l8.144,106.359c0.079,1.038,0.599,1.994,1.429,2.624
+ l91.455,69.657c0.161,0.123,0.357,0.188,0.552,0.188c0.138,0,0.278-0.031,0.405-0.095c0.311-0.155,0.508-0.474,0.507-0.819
+ L125.82,117.02z M113.735,205.9c-0.118,0.061-0.247,0.091-0.375,0.091c-0.174,0-0.347-0.054-0.492-0.161l-71.516-52.707
+ c-0.646-0.479-1.051-1.206-1.107-2.008L34.82,75.227c-0.023-0.311,0.133-0.608,0.4-0.768c0.266-0.159,0.601-0.156,0.865,0.008
+ l75.882,47.62c0.853,0.535,1.372,1.466,1.384,2.473l0.835,80.597C114.19,205.471,114.018,205.758,113.735,205.9z"/>
+
+ <linearGradient id="SVGID_51_" gradientUnits="userSpaceOnUse" x1="-162.3706" y1="-603.7349" x2="-215.1844" y2="-512.2589" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0" style="stop-color:#7896BE"/>
+ <stop offset="1" style="stop-color:#4164A5"/>
+ </linearGradient>
+ <path fill="url(#SVGID_51_)" d="M232.068,53.75c-0.293-0.18-0.654-0.18-0.951-0.001l-99.186,60.162
+ c-1.08,0.655-1.748,1.843-1.752,3.109l-0.17,115.49c0,0.348,0.196,0.665,0.506,0.819c0.129,0.063,0.268,0.095,0.405,0.095
+ c0.195,0,0.392-0.063,0.554-0.188l91.457-69.657c0.828-0.629,1.348-1.585,1.428-2.623l8.145-106.359
+ C232.527,54.254,232.361,53.93,232.068,53.75z M215.791,151.115c-0.057,0.802-0.459,1.529-1.104,2.008l-71.52,52.707
+ c-0.145,0.107-0.318,0.161-0.491,0.161c-0.128,0-0.257-0.03-0.375-0.091c-0.28-0.145-0.454-0.433-0.45-0.744l0.834-80.597
+ c0.014-1.007,0.531-1.938,1.383-2.473l75.886-47.62c0.264-0.165,0.599-0.167,0.862-0.008c0.269,0.159,0.425,0.456,0.4,0.768
+ L215.791,151.115z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_13_" filterUnits="userSpaceOnUse" x="130.01" y="53.615" width="102.496" height="179.809">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="130.01" y="53.615" width="102.496" height="179.809" id="SVGID_52_">
+ <g filter="url(#Adobe_OpacityMaskFilter_13_)">
+
+ <linearGradient id="SVGID_53_" gradientUnits="userSpaceOnUse" x1="-148.5908" y1="-581.1997" x2="-238.9791" y2="-529.0141" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.0053" style="stop-color:#808080"/>
+ <stop offset="0.6" style="stop-color:#000000"/>
+ </linearGradient>
+ <path fill="url(#SVGID_53_)" d="M232.068,53.75c-0.293-0.18-0.654-0.18-0.951-0.001l-99.186,60.162
+ c-1.08,0.655-1.748,1.843-1.752,3.109l-0.17,115.49c0,0.348,0.196,0.665,0.506,0.819c0.129,0.063,0.268,0.095,0.405,0.095
+ c0.195,0,0.392-0.063,0.554-0.188l91.457-69.657c0.828-0.629,1.348-1.585,1.428-2.623l8.145-106.359
+ C232.527,54.254,232.361,53.93,232.068,53.75z M215.791,151.115c-0.057,0.802-0.459,1.529-1.104,2.008l-71.52,52.707
+ c-0.145,0.107-0.318,0.161-0.491,0.161c-0.128,0-0.257-0.03-0.375-0.091c-0.28-0.145-0.454-0.433-0.45-0.744l0.834-80.597
+ c0.014-1.007,0.531-1.938,1.383-2.473l75.886-47.62c0.264-0.165,0.599-0.167,0.862-0.008c0.269,0.159,0.425,0.456,0.4,0.768
+ L215.791,151.115z"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_52_)" fill="#78A0E6" d="M232.068,53.75c-0.293-0.18-0.654-0.18-0.951-0.001l-99.186,60.162
+ c-1.08,0.655-1.748,1.843-1.752,3.109l-0.17,115.49c0,0.348,0.196,0.665,0.506,0.819c0.129,0.063,0.268,0.095,0.405,0.095
+ c0.195,0,0.392-0.063,0.554-0.188l91.457-69.657c0.828-0.629,1.348-1.585,1.428-2.623l8.145-106.359
+ C232.527,54.254,232.361,53.93,232.068,53.75z M215.791,151.115c-0.057,0.802-0.459,1.529-1.104,2.008l-71.52,52.707
+ c-0.145,0.107-0.318,0.161-0.491,0.161c-0.128,0-0.257-0.03-0.375-0.091c-0.28-0.145-0.454-0.433-0.45-0.744l0.834-80.597
+ c0.014-1.007,0.531-1.938,1.383-2.473l75.886-47.62c0.264-0.165,0.599-0.167,0.862-0.008c0.269,0.159,0.425,0.456,0.4,0.768
+ L215.791,151.115z"/>
+ <defs>
+ <filter id="Adobe_OpacityMaskFilter_14_" filterUnits="userSpaceOnUse" x="130.01" y="53.615" width="102.496" height="179.809">
+ <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
+ </filter>
+ </defs>
+ <mask maskUnits="userSpaceOnUse" x="130.01" y="53.615" width="102.496" height="179.809" id="SVGID_54_">
+ <g filter="url(#Adobe_OpacityMaskFilter_14_)">
+
+ <linearGradient id="SVGID_55_" gradientUnits="userSpaceOnUse" x1="-210.332" y1="-652.9912" x2="-176.5425" y2="-461.3605" gradientTransform="matrix(1 0 0 -1 370.0347 -414.4775)">
+ <stop offset="0.0053" style="stop-color:#808080"/>
+ <stop offset="0.4" style="stop-color:#000000"/>
+ </linearGradient>
+ <path fill="url(#SVGID_55_)" d="M232.068,53.75c-0.293-0.18-0.654-0.18-0.951-0.001l-99.186,60.162
+ c-1.08,0.655-1.748,1.843-1.752,3.109l-0.17,115.49c0,0.348,0.196,0.665,0.506,0.819c0.129,0.063,0.268,0.095,0.405,0.095
+ c0.195,0,0.392-0.063,0.554-0.188l91.457-69.657c0.828-0.629,1.348-1.585,1.428-2.623l8.145-106.359
+ C232.527,54.254,232.361,53.93,232.068,53.75z M215.791,151.115c-0.057,0.802-0.459,1.529-1.104,2.008l-71.52,52.707
+ c-0.145,0.107-0.318,0.161-0.491,0.161c-0.128,0-0.257-0.03-0.375-0.091c-0.28-0.145-0.454-0.433-0.45-0.744l0.834-80.597
+ c0.014-1.007,0.531-1.938,1.383-2.473l75.886-47.62c0.264-0.165,0.599-0.167,0.862-0.008c0.269,0.159,0.425,0.456,0.4,0.768
+ L215.791,151.115z"/>
+ </g>
+ </mask>
+ <path mask="url(#SVGID_54_)" fill="#7896BE" d="M232.068,53.75c-0.293-0.18-0.654-0.18-0.951-0.001l-99.186,60.162
+ c-1.08,0.655-1.748,1.843-1.752,3.109l-0.17,115.49c0,0.348,0.196,0.665,0.506,0.819c0.129,0.063,0.268,0.095,0.405,0.095
+ c0.195,0,0.392-0.063,0.554-0.188l91.457-69.657c0.828-0.629,1.348-1.585,1.428-2.623l8.145-106.359
+ C232.527,54.254,232.361,53.93,232.068,53.75z M215.791,151.115c-0.057,0.802-0.459,1.529-1.104,2.008l-71.52,52.707
+ c-0.145,0.107-0.318,0.161-0.491,0.161c-0.128,0-0.257-0.03-0.375-0.091c-0.28-0.145-0.454-0.433-0.45-0.744l0.834-80.597
+ c0.014-1.007,0.531-1.938,1.383-2.473l75.886-47.62c0.264-0.165,0.599-0.167,0.862-0.008c0.269,0.159,0.425,0.456,0.4,0.768
+ L215.791,151.115z"/>
+ <g id="text_3_">
+ <g>
+ <path fill="#FFFFFF" d="M49.103,122.684c-0.36-0.256-0.717-0.627-1.054-1.077c-0.337-0.45-0.654-0.981-0.936-1.557
+ c-0.281-0.577-0.526-1.199-0.719-1.834c-0.192-0.634-0.332-1.281-0.403-1.907c-0.07-0.625-0.064-1.169,0.006-1.617
+ c0.07-0.449,0.206-0.8,0.394-1.043c0.189-0.242,0.432-0.373,0.718-0.38c0.285-0.008,0.613,0.109,0.973,0.367l4.473,3.189
+ c0.36,0.256,0.718,0.628,1.056,1.08c0.337,0.452,0.657,0.984,0.939,1.562c0.282,0.579,0.528,1.203,0.721,1.84
+ c0.193,0.636,0.334,1.285,0.404,1.911c0.07,0.626,0.064,1.169-0.007,1.617c-0.071,0.447-0.208,0.797-0.398,1.037
+ c-0.19,0.24-0.435,0.369-0.721,0.375c-0.287,0.005-0.616-0.113-0.976-0.371L49.103,122.684 M52.382,114.425l-4.682-3.337
+ c-0.555-0.397-1.063-0.577-1.504-0.567c-0.441,0.012-0.817,0.216-1.109,0.591c-0.293,0.374-0.502,0.919-0.611,1.611
+ c-0.109,0.693-0.117,1.533-0.008,2.5c0.108,0.963,0.325,1.963,0.622,2.943c0.298,0.98,0.677,1.943,1.112,2.835
+ c0.435,0.891,0.926,1.712,1.447,2.409c0.521,0.697,1.073,1.271,1.628,1.667l4.68,3.342c0.557,0.397,1.065,0.581,1.506,0.57
+ c0.442-0.009,0.819-0.212,1.112-0.583c0.293-0.372,0.503-0.915,0.613-1.604c0.11-0.69,0.119-1.529,0.01-2.493
+ c-0.108-0.968-0.324-1.97-0.623-2.954c-0.298-0.983-0.678-1.949-1.115-2.844c-0.435-0.895-0.927-1.717-1.449-2.416
+ C53.492,115.396,52.938,114.821,52.382,114.425 M64.026,122.726l-6.789-4.84l1.575,13.998l1.548,1.105l-1.295-11.521
+ l5.131,3.659c0.18,0.129,0.36,0.314,0.528,0.54c0.169,0.225,0.328,0.491,0.469,0.778c0.141,0.289,0.262,0.6,0.359,0.917
+ c0.095,0.317,0.165,0.642,0.2,0.955c0.034,0.313,0.032,0.585-0.002,0.811c-0.036,0.226-0.103,0.402-0.197,0.522
+ c-0.095,0.123-0.216,0.188-0.359,0.192c-0.143,0.003-0.308-0.055-0.489-0.184l-4.374-3.13l5.418,10.305l2.253,1.607
+ l-3.628-6.758l0.713,0.51c0.376,0.269,0.72,0.393,1.02,0.386c0.299-0.006,0.555-0.142,0.753-0.394
+ c0.199-0.252,0.342-0.619,0.417-1.086c0.075-0.469,0.082-1.035,0.008-1.689c-0.073-0.653-0.219-1.329-0.42-1.994
+ c-0.203-0.666-0.459-1.32-0.755-1.925c-0.295-0.606-0.628-1.164-0.981-1.637C64.775,123.383,64.401,122.994,64.026,122.726
+ M87.361,139.361l-5.595-3.986c-0.558-0.398-1.068-0.581-1.511-0.57c-0.444,0.01-0.823,0.213-1.118,0.588
+ c-0.295,0.373-0.507,0.918-0.619,1.611c-0.111,0.691-0.122,1.536-0.013,2.503c0.109,0.966,0.325,1.968,0.624,2.95
+ c0.298,0.982,0.679,1.949,1.116,2.844c0.438,0.896,0.931,1.719,1.454,2.417c0.523,0.699,1.077,1.274,1.635,1.673l4.802,3.43
+ l0.645-1.813l-5.616-4.009c-0.362-0.258-0.721-0.63-1.059-1.083c-0.339-0.452-0.659-0.984-0.942-1.563
+ c-0.283-0.58-0.529-1.205-0.723-1.84c-0.193-0.639-0.333-1.285-0.403-1.912c-0.07-0.627-0.063-1.173,0.009-1.621
+ c0.072-0.447,0.209-0.802,0.4-1.041c0.192-0.242,0.438-0.373,0.725-0.379c0.288-0.008,0.618,0.112,0.98,0.371l4.571,3.26
+ L87.361,139.361 M89.758,141.07l-1.557-1.109l1.415,12.684c0.01,0.088,0.024,0.177,0.043,0.268
+ c0.018,0.092,0.041,0.184,0.068,0.273c0.028,0.092,0.06,0.186,0.097,0.278c0.037,0.093,0.078,0.187,0.124,0.279
+ c0.044,0.089,0.089,0.174,0.138,0.256c0.048,0.079,0.099,0.153,0.15,0.224c0.052,0.067,0.105,0.131,0.16,0.187
+ c0.054,0.057,0.108,0.103,0.164,0.146l7.112,5.076l0.64-1.816l-7.267-5.188L89.758,141.07 M106.35,152.898l-5.617-4.004
+ c-0.561-0.399-1.073-0.586-1.519-0.576c-0.447,0.011-0.826,0.214-1.123,0.589c-0.297,0.373-0.508,0.919-0.62,1.612
+ c-0.112,0.694-0.123,1.535-0.015,2.506c0.107,0.969,0.324,1.971,0.622,2.953c0.298,0.983,0.679,1.953,1.116,2.849
+ c0.438,0.896,0.932,1.719,1.457,2.421c0.525,0.701,1.081,1.279,1.642,1.679l4.817,3.44l0.643-1.818l-5.633-4.021
+ c-0.298-0.213-0.596-0.506-0.882-0.855c-0.285-0.354-0.561-0.762-0.815-1.215c-0.254-0.451-0.488-0.941-0.691-1.455
+ c-0.203-0.512-0.374-1.046-0.506-1.582l6.679,4.766l0.644-1.817l-7.597-5.419c0.025-0.42,0.098-0.776,0.212-1.063
+ c0.113-0.286,0.268-0.5,0.457-0.635c0.19-0.135,0.412-0.19,0.664-0.161c0.25,0.031,0.529,0.15,0.828,0.362l4.584,3.271
+ L106.35,152.898 M72.648,128.752c-0.1-0.07-0.194-0.119-0.282-0.145c-0.087-0.027-0.169-0.032-0.242-0.018
+ c-0.075,0.016-0.14,0.053-0.198,0.107c-0.058,0.057-0.107,0.132-0.146,0.227l-3.404,9.787l1.829,1.307l2.79-8.179l2.878,7.683
+ l-4.148-2.959l1.189,3.117l4.199,2.998l1.145,3.088l1.831,1.308l-6.404-16.808c-0.068-0.17-0.142-0.33-0.222-0.483
+ c-0.08-0.152-0.166-0.294-0.254-0.425s-0.181-0.248-0.275-0.352C72.838,128.906,72.743,128.82,72.648,128.752"/>
+ </g>
+ <path fill="#FFFFFF" d="M127.647,21.402L64.145,50.701l63.045,36.036l64.158-36.036L127.647,21.402z M127.253,81.504
+ L72.909,50.768l35.185-16.416l8.502,3.661L94.557,59.727l37.996-15.501l-13.538,15.697l24.135-10.007l-12.231,15.239
+ l12.886,7.128L127.253,81.504z M136.936,63.977l18.508-23.021l-23.217,9.484l12.947-15.172L110.25,49.59l12.425-12.426
+ l-10.397-4.773l15.369-7.194l55.001,25.571l-34.4,19.096L136.936,63.977z"/>
+ <g>
+ <path fill="#FFFFFF" d="M155.916,147.47l-1.908,1.384c-0.084,0.539-0.174,1.1-0.269,1.681c-0.099,0.577-0.198,1.18-0.306,1.803
+ c-0.105,0.622-0.222,1.267-0.338,1.929c-0.121,0.664-0.244,1.351-0.373,2.055c-0.134,0.705-0.263,1.388-0.388,2.045
+ c-0.123,0.658-0.246,1.295-0.364,1.908c-0.119,0.612-0.236,1.203-0.351,1.771c-0.111,0.565-0.224,1.112-0.33,1.634
+ c-0.06-0.505-0.114-1.013-0.17-1.523c-0.058-0.514-0.108-1.027-0.162-1.551c-0.053-0.52-0.104-1.045-0.151-1.574
+ c-0.052-0.529-0.099-1.063-0.144-1.604c-0.047-0.541-0.093-1.068-0.133-1.583c-0.039-0.515-0.074-1.015-0.109-1.502
+ c-0.03-0.489-0.062-0.963-0.088-1.424c-0.026-0.463-0.051-0.914-0.069-1.351l-2.175,1.576c0.046,0.646,0.097,1.284,0.146,1.92
+ c0.054,0.633,0.106,1.26,0.162,1.883c0.058,0.623,0.117,1.238,0.179,1.85c0.063,0.609,0.127,1.217,0.194,1.818
+ c0.064,0.599,0.138,1.205,0.209,1.816c0.068,0.612,0.146,1.229,0.228,1.854c0.078,0.625,0.16,1.255,0.246,1.888
+ c0.086,0.637,0.174,1.273,0.267,1.922l2.434-1.775c0.186-0.83,0.365-1.653,0.541-2.469c0.182-0.815,0.354-1.623,0.524-2.426
+ c0.169-0.8,0.337-1.593,0.499-2.375c0.161-0.783,0.319-1.563,0.475-2.33c0.154-0.771,0.309-1.537,0.457-2.308
+ c0.146-0.771,0.293-1.539,0.438-2.31c0.142-0.771,0.281-1.541,0.422-2.313C155.646,149.02,155.783,148.244,155.916,147.47"/>
+ <path fill="#FFFFFF" d="M159.123,149.271l-1.807,1.311l-0.031,0.525l-0.771,12.022l1.805-1.317L159.123,149.271 M158.57,144.455
+ c-0.094,0.064-0.178,0.135-0.256,0.208c-0.08,0.071-0.15,0.149-0.221,0.229c-0.063,0.08-0.125,0.164-0.181,0.25
+ c-0.056,0.088-0.103,0.18-0.146,0.272c-0.041,0.094-0.08,0.194-0.113,0.305c-0.032,0.11-0.063,0.229-0.09,0.354
+ s-0.049,0.259-0.063,0.4c-0.021,0.144-0.032,0.291-0.045,0.451c-0.009,0.162-0.015,0.311-0.015,0.442
+ c0,0.131,0.007,0.25,0.019,0.35c0.013,0.101,0.026,0.188,0.052,0.259c0.021,0.07,0.051,0.127,0.084,0.166
+ c0.032,0.039,0.071,0.065,0.12,0.078c0.05,0.012,0.104,0.012,0.166-0.002c0.062-0.015,0.134-0.042,0.209-0.082
+ c0.076-0.041,0.16-0.094,0.25-0.162c0.095-0.063,0.181-0.135,0.259-0.209c0.08-0.072,0.151-0.149,0.221-0.229
+ c0.066-0.081,0.129-0.164,0.182-0.25c0.055-0.086,0.104-0.178,0.145-0.27c0.041-0.096,0.08-0.197,0.113-0.31
+ c0.031-0.11,0.063-0.229,0.09-0.358c0.025-0.127,0.051-0.267,0.068-0.41c0.016-0.146,0.029-0.299,0.043-0.461
+ c0.01-0.164,0.014-0.311,0.014-0.44c0-0.132-0.008-0.248-0.018-0.349c-0.012-0.102-0.025-0.188-0.047-0.254
+ c-0.021-0.068-0.051-0.123-0.082-0.16c-0.031-0.039-0.072-0.064-0.121-0.076c-0.047-0.012-0.104-0.012-0.166,0.005
+ c-0.063,0.015-0.133,0.043-0.211,0.085C158.75,144.333,158.664,144.386,158.57,144.455"/>
+ <path fill="#FFFFFF" d="M165.184,144.634c-0.069,0.052-0.143,0.113-0.209,0.187c-0.067,0.07-0.139,0.154-0.203,0.248
+ c-0.069,0.095-0.137,0.197-0.203,0.313c-0.063,0.116-0.129,0.242-0.192,0.378c-0.063,0.137-0.135,0.289-0.211,0.463
+ c-0.074,0.174-0.153,0.366-0.24,0.577c-0.086,0.211-0.178,0.443-0.274,0.695c-0.099,0.251-0.199,0.521-0.31,0.813l-0.125-2.01
+ l-1.358,0.987l-0.808,12.532l1.813-1.32l0.516-8.008c0.078-0.208,0.151-0.404,0.223-0.588c0.072-0.184,0.141-0.354,0.203-0.514
+ c0.064-0.158,0.127-0.306,0.184-0.438c0.06-0.137,0.111-0.26,0.162-0.369c0.05-0.105,0.099-0.207,0.146-0.296
+ c0.051-0.089,0.096-0.168,0.145-0.239c0.046-0.068,0.091-0.131,0.136-0.182c0.043-0.053,0.088-0.094,0.131-0.123
+ c0.021-0.016,0.041-0.028,0.065-0.042c0.021-0.015,0.047-0.029,0.07-0.042c0.025-0.014,0.053-0.024,0.08-0.037
+ c0.025-0.014,0.057-0.023,0.086-0.036c0.031-0.008,0.063-0.019,0.092-0.022c0.027-0.012,0.06-0.021,0.092-0.027
+ c0.029-0.008,0.062-0.018,0.091-0.024c0.03-0.008,0.063-0.015,0.094-0.021l0.418-3.135c-0.034,0.011-0.067,0.021-0.101,0.029
+ c-0.03,0.012-0.063,0.021-0.09,0.031c-0.03,0.01-0.061,0.021-0.084,0.033c-0.026,0.011-0.053,0.021-0.076,0.033
+ c-0.022,0.012-0.049,0.021-0.069,0.033s-0.045,0.024-0.067,0.036c-0.021,0.015-0.041,0.026-0.063,0.04
+ C165.223,144.607,165.201,144.621,165.184,144.634"/>
+ <path fill="#FFFFFF" d="M169.717,137.949l-1.076,0.779c-0.07,0.399-0.145,0.791-0.221,1.172c-0.074,0.38-0.154,0.75-0.234,1.11
+ c-0.082,0.361-0.164,0.714-0.252,1.058c-0.084,0.342-0.176,0.676-0.268,0.998l-0.922,0.67l-0.143,2.169l0.91-0.662l-0.435,6.753
+ c-0.022,0.335-0.035,0.639-0.035,0.914c-0.002,0.273,0.006,0.52,0.024,0.736c0.019,0.215,0.05,0.401,0.084,0.563
+ c0.041,0.16,0.089,0.289,0.146,0.393c0.059,0.103,0.133,0.176,0.217,0.225c0.084,0.045,0.182,0.064,0.291,0.056
+ c0.109-0.007,0.23-0.043,0.365-0.104c0.139-0.063,0.283-0.152,0.445-0.271c0.074-0.053,0.15-0.112,0.227-0.177
+ c0.074-0.063,0.152-0.13,0.229-0.204c0.073-0.069,0.153-0.146,0.229-0.229c0.076-0.08,0.156-0.166,0.234-0.254
+ c0.08-0.088,0.154-0.179,0.23-0.271c0.073-0.09,0.146-0.183,0.217-0.274c0.07-0.093,0.139-0.188,0.203-0.281
+ c0.063-0.096,0.129-0.188,0.19-0.286l0.022-1.794c-0.043,0.044-0.086,0.087-0.125,0.125c-0.043,0.039-0.084,0.078-0.123,0.115
+ c-0.041,0.039-0.08,0.074-0.118,0.108c-0.037,0.036-0.078,0.067-0.115,0.101c-0.037,0.03-0.074,0.063-0.108,0.09
+ c-0.041,0.029-0.078,0.062-0.113,0.091c-0.037,0.026-0.074,0.057-0.111,0.087c-0.037,0.025-0.074,0.054-0.111,0.082
+ c-0.055,0.04-0.106,0.068-0.154,0.087c-0.047,0.021-0.092,0.024-0.131,0.021c-0.039-0.008-0.076-0.021-0.104-0.049
+ c-0.033-0.029-0.062-0.068-0.084-0.119s-0.043-0.106-0.062-0.173c-0.014-0.063-0.026-0.137-0.033-0.214
+ c-0.012-0.078-0.014-0.164-0.016-0.258c0-0.094,0.004-0.193,0.01-0.301l0.429-6.604l1.522-1.108l0.142-2.164l-1.523,1.104
+ L169.717,137.949"/>
+ <path fill="#FFFFFF" d="M178.785,134.994l-1.791,1.302l-0.615,9.468c-0.111,0.174-0.225,0.338-0.334,0.487
+ c-0.107,0.149-0.215,0.287-0.32,0.409c-0.104,0.125-0.209,0.234-0.309,0.333c-0.104,0.098-0.201,0.182-0.299,0.252
+ c-0.076,0.057-0.147,0.093-0.213,0.11c-0.063,0.018-0.121,0.014-0.174-0.008s-0.099-0.063-0.14-0.123
+ c-0.04-0.062-0.073-0.142-0.103-0.238c-0.026-0.104-0.049-0.235-0.063-0.402c-0.015-0.167-0.023-0.367-0.025-0.6
+ c-0.002-0.233,0.002-0.498,0.014-0.797s0.027-0.631,0.054-0.996l0.456-6.396l-1.797,1.306c-0.01,0.138-0.021,0.313-0.037,0.531
+ c-0.014,0.219-0.031,0.479-0.055,0.776c-0.021,0.3-0.047,0.64-0.076,1.02c-0.025,0.382-0.061,0.804-0.096,1.265
+ c-0.033,0.462-0.063,0.882-0.092,1.26c-0.025,0.379-0.053,0.716-0.073,1.013c-0.021,0.298-0.04,0.552-0.054,0.767
+ c-0.019,0.215-0.027,0.389-0.037,0.521c-0.031,0.512-0.053,0.979-0.063,1.402c-0.008,0.421-0.002,0.799,0.017,1.131
+ c0.016,0.332,0.045,0.62,0.088,0.863c0.043,0.242,0.1,0.438,0.166,0.595c0.065,0.153,0.146,0.272,0.238,0.356
+ c0.094,0.086,0.196,0.135,0.313,0.149c0.118,0.019,0.245-0.004,0.389-0.056c0.144-0.053,0.298-0.142,0.463-0.264
+ c0.091-0.063,0.181-0.14,0.271-0.224c0.091-0.085,0.185-0.183,0.278-0.285c0.094-0.104,0.188-0.222,0.287-0.348
+ c0.096-0.126,0.194-0.263,0.295-0.409c0.102-0.146,0.196-0.296,0.297-0.452c0.096-0.151,0.188-0.313,0.285-0.479
+ c0.092-0.162,0.186-0.33,0.276-0.504c0.093-0.172,0.185-0.352,0.272-0.533l0.104,1.271l1.389-1.014L178.785,134.994"/>
+ <path fill="#FFFFFF" d="M182.768,141.832c-0.063,0.05-0.127,0.087-0.188,0.115c-0.057,0.027-0.113,0.042-0.162,0.048
+ c-0.053,0.005-0.1,0-0.139-0.017c-0.043-0.019-0.078-0.047-0.113-0.086s-0.063-0.086-0.084-0.145
+ c-0.021-0.061-0.043-0.128-0.055-0.207c-0.014-0.08-0.021-0.168-0.023-0.27c-0.002-0.1,0-0.209,0.008-0.326
+ c0.01-0.143,0.025-0.28,0.051-0.418c0.021-0.137,0.049-0.27,0.086-0.399c0.035-0.131,0.078-0.26,0.125-0.386
+ c0.052-0.126,0.105-0.251,0.17-0.374c0.063-0.123,0.138-0.246,0.224-0.367c0.082-0.119,0.183-0.237,0.285-0.354
+ c0.106-0.115,0.226-0.23,0.354-0.344s0.268-0.224,0.416-0.334l0.521-0.382l-0.166,2.56c-0.059,0.104-0.113,0.203-0.17,0.3
+ c-0.053,0.095-0.109,0.186-0.162,0.271c-0.059,0.085-0.107,0.167-0.162,0.244c-0.052,0.077-0.104,0.146-0.154,0.215
+ c-0.053,0.067-0.104,0.133-0.158,0.191c-0.053,0.063-0.106,0.117-0.162,0.174c-0.055,0.054-0.108,0.104-0.166,0.154
+ C182.885,141.745,182.826,141.791,182.768,141.832 M184.02,130.961c-0.102,0.071-0.199,0.148-0.305,0.232
+ c-0.102,0.084-0.203,0.173-0.311,0.269c-0.105,0.097-0.213,0.196-0.322,0.306c-0.107,0.105-0.219,0.222-0.33,0.34
+ c-0.111,0.117-0.229,0.248-0.354,0.394s-0.254,0.301-0.39,0.472c-0.137,0.172-0.278,0.354-0.426,0.553
+ c-0.147,0.194-0.304,0.406-0.461,0.632l0.067,1.792c0.095-0.117,0.188-0.229,0.277-0.338c0.092-0.108,0.18-0.213,0.27-0.313
+ c0.089-0.1,0.173-0.196,0.257-0.288c0.084-0.091,0.161-0.179,0.241-0.261c0.08-0.083,0.158-0.164,0.24-0.242
+ c0.08-0.079,0.164-0.153,0.246-0.228c0.084-0.071,0.168-0.146,0.254-0.213c0.086-0.068,0.174-0.135,0.262-0.198
+ c0.099-0.072,0.191-0.132,0.281-0.181c0.084-0.047,0.168-0.082,0.246-0.104c0.076-0.022,0.148-0.033,0.215-0.031
+ c0.067,0.002,0.127,0.017,0.185,0.044c0.055,0.026,0.103,0.068,0.142,0.127c0.036,0.058,0.069,0.133,0.092,0.226
+ c0.023,0.092,0.035,0.198,0.041,0.323c0.008,0.123,0.006,0.266-0.006,0.422l-0.056,0.845l-0.483,0.353
+ c-0.295,0.215-0.57,0.439-0.832,0.676c-0.256,0.236-0.5,0.481-0.724,0.74c-0.224,0.258-0.43,0.524-0.618,0.805
+ c-0.187,0.277-0.355,0.565-0.513,0.867c-0.151,0.301-0.287,0.604-0.406,0.907c-0.12,0.306-0.223,0.611-0.312,0.919
+ c-0.086,0.309-0.153,0.615-0.209,0.926c-0.055,0.312-0.092,0.623-0.11,0.935c-0.039,0.601-0.028,1.093,0.031,1.479
+ c0.057,0.385,0.166,0.662,0.321,0.832c0.156,0.17,0.365,0.233,0.621,0.189c0.252-0.043,0.558-0.193,0.908-0.452
+ c0.098-0.067,0.189-0.147,0.287-0.235c0.094-0.088,0.188-0.187,0.283-0.293c0.098-0.106,0.189-0.225,0.284-0.349
+ c0.099-0.125,0.19-0.259,0.288-0.401c0.096-0.146,0.188-0.293,0.281-0.445c0.092-0.15,0.181-0.305,0.268-0.463
+ c0.086-0.157,0.17-0.318,0.252-0.481c0.082-0.164,0.16-0.33,0.236-0.498l0.104,1.269l1.311-0.957l0.566-8.646
+ c0.021-0.337,0.029-0.645,0.021-0.921c-0.002-0.276-0.021-0.524-0.053-0.742c-0.035-0.217-0.08-0.403-0.14-0.561
+ c-0.062-0.157-0.133-0.283-0.22-0.38c-0.086-0.099-0.19-0.161-0.309-0.195c-0.119-0.033-0.254-0.036-0.404-0.005
+ c-0.149,0.028-0.317,0.092-0.5,0.185C184.432,130.682,184.232,130.807,184.02,130.961"/>
+ <path fill="#FFFFFF" d="M191.021,121.232l-1.761,1.275l-0.987,15.075c-0.019,0.257-0.023,0.488-0.023,0.693
+ s0.011,0.387,0.025,0.543c0.02,0.156,0.045,0.287,0.078,0.393s0.074,0.188,0.127,0.246c0.051,0.057,0.108,0.093,0.18,0.113
+ c0.068,0.02,0.146,0.021,0.234,0.004c0.088-0.016,0.184-0.053,0.289-0.105c0.104-0.052,0.221-0.125,0.344-0.215
+ c0.104-0.077,0.212-0.162,0.32-0.256c0.106-0.094,0.219-0.196,0.33-0.307c0.112-0.11,0.227-0.23,0.342-0.358
+ c0.116-0.128,0.229-0.265,0.352-0.409l0.027-1.81l-0.426,0.332c-0.046,0.032-0.082,0.056-0.119,0.069s-0.07,0.018-0.099,0.012
+ c-0.028-0.004-0.053-0.019-0.073-0.041c-0.021-0.022-0.041-0.055-0.056-0.094c-0.015-0.041-0.022-0.099-0.03-0.168
+ c-0.007-0.07-0.013-0.156-0.015-0.259c-0.002-0.101,0-0.217,0.006-0.347c0.002-0.131,0.009-0.277,0.021-0.438L191.021,121.232"
+ />
+ <path fill="#FFFFFF" d="M195.492,132.34l-0.818,0.523l0.33-5.009l0.77-0.559c0.14-0.101,0.269-0.181,0.39-0.24
+ c0.121-0.06,0.229-0.098,0.332-0.114c0.101-0.017,0.19-0.013,0.271,0.012c0.084,0.024,0.152,0.072,0.215,0.139
+ c0.062,0.069,0.115,0.148,0.158,0.243c0.041,0.094,0.076,0.201,0.104,0.322c0.023,0.12,0.041,0.254,0.047,0.402
+ c0.006,0.149,0.006,0.31-0.006,0.485c-0.016,0.205-0.035,0.403-0.063,0.597c-0.029,0.192-0.063,0.379-0.107,0.563
+ c-0.045,0.185-0.094,0.36-0.153,0.534c-0.058,0.173-0.119,0.342-0.194,0.506c-0.07,0.164-0.151,0.318-0.242,0.469
+ c-0.088,0.148-0.187,0.289-0.293,0.422c-0.105,0.135-0.219,0.26-0.342,0.376C195.764,132.129,195.633,132.238,195.492,132.34
+ M195.164,125.433l0.277-4.249l0.852-0.618c0.098-0.069,0.186-0.122,0.27-0.158c0.082-0.036,0.158-0.056,0.228-0.06
+ c0.069-0.004,0.133,0.008,0.188,0.037c0.059,0.028,0.106,0.071,0.149,0.131c0.041,0.062,0.078,0.134,0.104,0.219
+ c0.029,0.085,0.056,0.18,0.068,0.287c0.018,0.107,0.027,0.226,0.029,0.355c0.004,0.129,0,0.27-0.012,0.423
+ c-0.01,0.155-0.025,0.309-0.051,0.461c-0.023,0.153-0.058,0.305-0.095,0.457c-0.038,0.15-0.084,0.301-0.137,0.45
+ c-0.054,0.149-0.115,0.298-0.183,0.445c-0.067,0.146-0.143,0.286-0.222,0.418c-0.081,0.132-0.167,0.257-0.261,0.374
+ c-0.094,0.116-0.191,0.225-0.299,0.326c-0.105,0.101-0.219,0.195-0.338,0.282L195.164,125.433 M196.744,117.89l-2.961,2.146
+ l-1.102,16.726l2.411-1.762c0.334-0.243,0.646-0.501,0.938-0.773c0.292-0.271,0.563-0.559,0.813-0.858
+ c0.25-0.302,0.479-0.617,0.688-0.948c0.206-0.331,0.395-0.676,0.561-1.037c0.164-0.357,0.313-0.72,0.443-1.086
+ c0.131-0.363,0.244-0.732,0.34-1.105c0.097-0.373,0.174-0.75,0.233-1.129c0.062-0.379,0.104-0.763,0.13-1.15
+ c0.014-0.209,0.018-0.409,0.01-0.598c-0.008-0.19-0.026-0.368-0.06-0.537c-0.026-0.168-0.067-0.326-0.119-0.474
+ c-0.053-0.147-0.114-0.285-0.188-0.412c-0.069-0.127-0.149-0.23-0.233-0.312c-0.086-0.082-0.176-0.14-0.275-0.176
+ c-0.1-0.036-0.201-0.05-0.313-0.04c-0.111,0.009-0.229,0.042-0.353,0.097c0.119-0.177,0.229-0.354,0.334-0.531
+ c0.104-0.177,0.201-0.353,0.291-0.53c0.09-0.177,0.172-0.354,0.248-0.533c0.075-0.177,0.146-0.356,0.207-0.535
+ c0.062-0.176,0.117-0.36,0.164-0.551c0.053-0.19,0.096-0.388,0.135-0.592c0.037-0.203,0.069-0.413,0.101-0.63
+ c0.024-0.217,0.045-0.438,0.063-0.668c0.021-0.313,0.023-0.598,0.016-0.856c-0.014-0.258-0.037-0.487-0.08-0.69
+ c-0.041-0.203-0.1-0.377-0.172-0.526c-0.074-0.148-0.164-0.269-0.268-0.362c-0.104-0.092-0.225-0.15-0.359-0.179
+ c-0.137-0.027-0.287-0.024-0.453,0.012s-0.348,0.104-0.545,0.205C197.188,117.591,196.973,117.724,196.744,117.89"/>
+ <path fill="#FFFFFF" d="M203.377,126.843c-0.111,0.082-0.217,0.139-0.311,0.17c-0.096,0.033-0.181,0.041-0.26,0.024
+ c-0.076-0.017-0.146-0.058-0.207-0.124c-0.063-0.066-0.115-0.157-0.16-0.273c-0.043-0.117-0.078-0.271-0.104-0.46
+ c-0.025-0.19-0.043-0.417-0.053-0.681c-0.008-0.263-0.008-0.564,0.002-0.902c0.01-0.337,0.027-0.712,0.057-1.124
+ c0.021-0.324,0.047-0.631,0.08-0.92c0.031-0.29,0.07-0.562,0.113-0.817c0.045-0.255,0.096-0.495,0.15-0.717
+ c0.057-0.221,0.115-0.425,0.184-0.614c0.064-0.188,0.137-0.358,0.213-0.517c0.076-0.159,0.158-0.305,0.246-0.438
+ c0.086-0.131,0.176-0.249,0.271-0.354c0.1-0.105,0.198-0.196,0.309-0.274c0.104-0.075,0.197-0.127,0.287-0.154
+ c0.092-0.026,0.172-0.028,0.246-0.005c0.072,0.023,0.139,0.071,0.197,0.145c0.057,0.072,0.106,0.17,0.149,0.293
+ c0.043,0.124,0.076,0.282,0.103,0.477c0.022,0.195,0.041,0.426,0.047,0.693c0.01,0.267,0.008,0.569-0.002,0.908
+ c-0.011,0.339-0.027,0.713-0.056,1.125c-0.022,0.323-0.049,0.629-0.081,0.916c-0.031,0.288-0.07,0.558-0.113,0.81
+ c-0.043,0.251-0.09,0.485-0.146,0.7c-0.054,0.217-0.113,0.414-0.179,0.593c-0.063,0.182-0.135,0.35-0.209,0.503
+ c-0.071,0.154-0.149,0.293-0.231,0.419c-0.082,0.125-0.17,0.239-0.26,0.338C203.57,126.685,203.477,126.771,203.377,126.843
+ M204.135,116.359c-0.248,0.18-0.484,0.385-0.709,0.616c-0.229,0.231-0.443,0.488-0.646,0.771
+ c-0.204,0.284-0.397,0.593-0.581,0.929c-0.185,0.335-0.357,0.698-0.521,1.087c-0.161,0.386-0.308,0.792-0.438,1.22
+ c-0.13,0.428-0.244,0.876-0.345,1.345c-0.1,0.469-0.186,0.958-0.25,1.469c-0.065,0.511-0.121,1.041-0.156,1.592
+ c-0.034,0.547-0.053,1.044-0.053,1.49c0,0.447,0.019,0.845,0.056,1.191c0.036,0.347,0.092,0.644,0.165,0.892
+ c0.074,0.247,0.164,0.444,0.275,0.594c0.109,0.146,0.238,0.254,0.383,0.318c0.146,0.066,0.308,0.09,0.484,0.074
+ c0.18-0.016,0.375-0.074,0.59-0.172c0.213-0.1,0.441-0.238,0.689-0.418c0.25-0.185,0.488-0.394,0.717-0.627
+ c0.229-0.234,0.447-0.498,0.652-0.785c0.207-0.287,0.402-0.6,0.59-0.94c0.186-0.34,0.359-0.707,0.523-1.101
+ c0.164-0.393,0.313-0.806,0.444-1.237c0.136-0.433,0.248-0.884,0.351-1.355c0.1-0.472,0.184-0.962,0.25-1.472
+ c0.067-0.511,0.123-1.041,0.158-1.59c0.034-0.516,0.049-0.986,0.047-1.412c-0.004-0.426-0.023-0.807-0.064-1.143
+ c-0.039-0.335-0.1-0.625-0.178-0.871c-0.076-0.246-0.174-0.445-0.287-0.6c-0.113-0.155-0.244-0.27-0.393-0.341
+ c-0.146-0.073-0.31-0.103-0.488-0.09c-0.18,0.01-0.375,0.063-0.584,0.157C204.604,116.047,204.377,116.183,204.135,116.359"/>
+ <path fill="#FFFFFF" d="M213.489,109.8l-1.823,1.325c-0.043,0.158-0.084,0.317-0.127,0.478c-0.043,0.16-0.084,0.321-0.129,0.482
+ c-0.043,0.162-0.088,0.324-0.133,0.487c-0.047,0.162-0.094,0.326-0.139,0.49c-0.046,0.163-0.091,0.322-0.138,0.478
+ c-0.045,0.156-0.09,0.31-0.135,0.458c-0.046,0.148-0.091,0.292-0.136,0.433s-0.086,0.277-0.129,0.409l-0.664-2.463l-2.035,1.479
+ l1.427,4.602l-2.521,7.942l1.875-1.37c0.053-0.206,0.105-0.413,0.16-0.619c0.053-0.207,0.107-0.414,0.162-0.621
+ c0.059-0.208,0.113-0.417,0.17-0.626c0.061-0.209,0.117-0.419,0.178-0.631c0.06-0.211,0.117-0.419,0.177-0.623
+ c0.059-0.204,0.116-0.404,0.176-0.601c0.06-0.196,0.116-0.389,0.174-0.579c0.058-0.189,0.115-0.375,0.172-0.558l0.857,3.234
+ l2.096-1.531l-1.696-5.256L213.489,109.8"/>
+ </g>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox_64px.png b/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox_64px.png
new file mode 100644
index 00000000..d8849bdd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/images/VirtualBox_64px.png
Binary files differ
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/images/tmfavicon.ico b/src/VBox/ValidationKit/testmanager/htdocs/images/tmfavicon.ico
new file mode 100644
index 00000000..72f7032d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/images/tmfavicon.ico
Binary files differ
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup b/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/common.js b/src/VBox/ValidationKit/testmanager/htdocs/js/common.js
new file mode 100644
index 00000000..52c4179c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/js/common.js
@@ -0,0 +1,1926 @@
+/* $Id: common.js $ */
+/** @file
+ * Common JavaScript functions
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Same as WuiDispatcherBase.ksParamRedirectTo. */
+var g_ksParamRedirectTo = 'RedirectTo';
+
+/** Days of the week in Date() style with Sunday first. */
+var g_kasDaysOfTheWeek = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ];
+
+
+/**
+ * Detects the firefox browser.
+ */
+function isBrowserFirefox()
+{
+ return typeof InstallTrigger !== 'undefined';
+}
+
+/**
+ * Detects the google chrome browser.
+ * @note Might be confused with edge chromium
+ */
+function isBrowserChrome()
+{
+ var oChrome = window.chrome;
+ if (!oChrome)
+ return false;
+ return !!oChrome.runtime || !oChrome.webstore;
+}
+
+/**
+ * Detects the chromium-based edge browser.
+ */
+function isBrowserEdgeChromium()
+{
+ if (!isBrowserChrome())
+ return false;
+ return navigation.userAgent.indexOf('Edg') >= 0
+}
+
+/**
+ * Detects the chromium-based edge browser.
+ */
+function isBrowserInternetExplorer()
+{
+ /* documentMode is an IE only property. Values are 5,7,8,9,10 or 11
+ according to google results. */
+ if (typeof document.documentMode !== 'undefined')
+ {
+ if (document.documentMode)
+ return true;
+ }
+ /* IE only conditional compiling feature. Here, the 'true || ' part
+ will be included in the if when executing in IE: */
+ if (/*@cc_on true || @*/false)
+ return true;
+ return false;
+}
+
+/**
+ * Detects the safari browser (v3+).
+ */
+function isBrowserSafari()
+{
+ /* Check if window.HTMLElement is a function named 'HTMLElementConstructor()'?
+ Should work for older safari versions. */
+ var sStr = window.HTMLElement.toString();
+ if (/constructor/i.test(sStr))
+ return true;
+
+ /* Check the class name of window.safari.pushNotification. This works for current. */
+ var oSafari = window['safari'];
+ if (oSafari)
+ {
+ if (typeof oSafari !== 'undefined')
+ {
+ var oPushNotify = oSafari.pushNotification;
+ if (oPushNotify)
+ {
+ sStr = oPushNotify.toString();
+ if (/\[object Safari.*Notification\]/.test(sStr))
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Checks if the given value is a decimal integer value.
+ *
+ * @returns true if it is, false if it's isn't.
+ * @param sValue The value to inspect.
+ */
+function isInteger(sValue)
+{
+ if (typeof sValue != 'undefined')
+ {
+ var intRegex = /^\d+$/;
+ if (intRegex.test(sValue))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Checks if @a oMemmber is present in aoArray.
+ *
+ * @returns true/false.
+ * @param aoArray The array to check.
+ * @param oMember The member to check for.
+ */
+function isMemberOfArray(aoArray, oMember)
+{
+ var i;
+ for (i = 0; i < aoArray.length; i++)
+ if (aoArray[i] == oMember)
+ return true;
+ return false;
+}
+
+/**
+ * Parses a typical ISO timestamp, returing a Date object, reasonably
+ * forgiving, but will throw weird indexing/conversion errors if the input
+ * is malformed.
+ *
+ * @returns Date object.
+ * @param sTs The timestamp to parse.
+ * @sa parseIsoTimestamp() in utils.py.
+ */
+function parseIsoTimestamp(sTs)
+{
+ /* YYYY-MM-DD */
+ var iYear = parseInt(sTs.substring(0, 4), 10);
+ console.assert(sTs.charAt(4) == '-');
+ var iMonth = parseInt(sTs.substring(5, 7), 10);
+ console.assert(sTs.charAt(7) == '-');
+ var iDay = parseInt(sTs.substring(8, 10), 10);
+
+ /* Skip separator */
+ var sTime = sTs.substring(10);
+ while ('Tt \t\n\r'.includes(sTime.charAt(0))) {
+ sTime = sTime.substring(1);
+ }
+
+ /* HH:MM[:SS[.fraction] */
+ var iHour = parseInt(sTime.substring(0, 2), 10);
+ console.assert(sTime.charAt(2) == ':');
+ var iMin = parseInt(sTime.substring(3, 5), 10);
+ var iSec = 0;
+ var iMicroseconds = 0;
+ var offTime = 5;
+ if (sTime.charAt(5) == ':')
+ {
+ iSec = parseInt(sTime.substring(6, 8), 10);
+
+ /* Fraction? */
+ offTime = 8;
+ if (offTime < sTime.length && '.,'.includes(sTime.charAt(offTime)))
+ {
+ offTime += 1;
+ var cchFraction = 0;
+ while (offTime + cchFraction < sTime.length && '0123456789'.includes(sTime.charAt(offTime + cchFraction)))
+ cchFraction += 1;
+ if (cchFraction > 0)
+ {
+ iMicroseconds = parseInt(sTime.substring(offTime, offTime + cchFraction), 10);
+ offTime += cchFraction;
+ while (cchFraction < 6)
+ {
+ iMicroseconds *= 10;
+ cchFraction += 1;
+ }
+ while (cchFraction > 6)
+ {
+ iMicroseconds = iMicroseconds / 10;
+ cchFraction -= 1;
+ }
+ }
+ }
+ }
+ var iMilliseconds = (iMicroseconds + 499) / 1000;
+
+ /* Naive? */
+ var oDate = new Date(Date.UTC(iYear, iMonth - 1, iDay, iHour, iMin, iSec, iMilliseconds));
+ if (offTime >= sTime.length)
+ return oDate;
+
+ /* Zulu? */
+ if (offTime >= sTime.length || 'Zz'.includes(sTime.charAt(offTime)))
+ return oDate;
+
+ /* Some kind of offset afterwards. */
+ var chSign = sTime.charAt(offTime);
+ if ('+-'.includes(chSign))
+ {
+ offTime += 1;
+ var cMinTz = parseInt(sTime.substring(offTime, offTime + 2), 10) * 60;
+ offTime += 2;
+ if (offTime < sTime.length && sTime.charAt(offTime) == ':')
+ offTime += 1;
+ if (offTime + 2 <= sTime.length)
+ {
+ cMinTz += parseInt(sTime.substring(offTime, offTime + 2), 10);
+ offTime += 2;
+ }
+ console.assert(offTime == sTime.length);
+ if (chSign == '-')
+ cMinTz = -cMinTz;
+
+ return new Date(oDate.getTime() - cMinTz * 60000);
+ }
+ console.assert(false);
+ return oDate;
+}
+
+/**
+ * @param oDate Date object.
+ */
+function formatTimeHHMM(oDate, fNbsp)
+{
+ var sTime = oDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit'} );
+ if (fNbsp === true)
+ sTime = sTime.replace(' ', '\u00a0');
+
+ /* Workaround for single digit hours in firefox with en_US (minutes works fine): */
+ var iHours = oDate.getHours();
+ if ((iHours % 12) < 10)
+ {
+ var ch1 = sTime.substr(0, 1);
+ var ch2 = sTime.substr(1, 1);
+ if ( ch1 == (iHours % 12).toString()
+ && !(ch2 >= '0' && ch2 <= '9'))
+ sTime = '0' + sTime;
+ }
+ return sTime;
+}
+
+/**
+ * Escapes special characters to HTML-safe sequences, for element use.
+ *
+ * @returns Escaped string suitable for HTML.
+ * @param sText Plain text to escape.
+ */
+function escapeElem(sText)
+{
+ sText = sText.replace(/&/g, '&amp;');
+ sText = sText.replace(/>/g, '&lt;');
+ return sText.replace(/</g, '&gt;');
+}
+
+/**
+ * Escapes special characters to HTML-safe sequences, for double quoted
+ * attribute use.
+ *
+ * @returns Escaped string suitable for HTML.
+ * @param sText Plain text to escape.
+ */
+function escapeAttr(sText)
+{
+ sText = sText.replace(/&/g, '&amp;');
+ sText = sText.replace(/</g, '&lt;');
+ sText = sText.replace(/>/g, '&gt;');
+ return sText.replace(/"/g, '&quot;');
+}
+
+/**
+ * Removes the element with the specified ID.
+ */
+function removeHtmlNode(sContainerId)
+{
+ var oElement = document.getElementById(sContainerId);
+ if (oElement)
+ {
+ oElement.parentNode.removeChild(oElement);
+ }
+}
+
+/**
+ * Sets the value of the element with id @a sInputId to the keys of aoItems
+ * (comma separated).
+ */
+function setElementValueToKeyList(sInputId, aoItems)
+{
+ var sKey;
+ var oElement = document.getElementById(sInputId);
+ oElement.value = '';
+
+ for (sKey in aoItems)
+ {
+ if (oElement.value.length > 0)
+ {
+ oElement.value += ',';
+ }
+
+ oElement.value += sKey;
+ }
+}
+
+/**
+ * Get the Window.devicePixelRatio in a safe way.
+ *
+ * @returns Floating point ratio. 1.0 means it's a 1:1 ratio.
+ */
+function getDevicePixelRatio()
+{
+ var fpRatio = 1.0;
+ if (window.devicePixelRatio)
+ {
+ fpRatio = window.devicePixelRatio;
+ if (fpRatio < 0.5 || fpRatio > 10.0)
+ fpRatio = 1.0;
+ }
+ return fpRatio;
+}
+
+/**
+ * Tries to figure out the DPI of the device in the X direction.
+ *
+ * @returns DPI on success, null on failure.
+ */
+function getDeviceXDotsPerInch()
+{
+ if (window.deviceXDPI && window.deviceXDPI > 48 && window.deviceXDPI < 2048)
+ {
+ return window.deviceXDPI;
+ }
+ else if (window.devicePixelRatio && window.devicePixelRatio >= 0.5 && window.devicePixelRatio <= 10.0)
+ {
+ cDotsPerInch = Math.round(96 * window.devicePixelRatio);
+ }
+ else
+ {
+ cDotsPerInch = null;
+ }
+ return cDotsPerInch;
+}
+
+/**
+ * Gets the width of the given element (downscaled).
+ *
+ * Useful when using the element to figure the size of a image
+ * or similar.
+ *
+ * @returns Number of pixels. null if oElement is bad.
+ * @param oElement The element (not ID).
+ */
+function getElementWidth(oElement)
+{
+ if (oElement && oElement.offsetWidth)
+ return oElement.offsetWidth;
+ return null;
+}
+
+/** By element ID version of getElementWidth. */
+function getElementWidthById(sElementId)
+{
+ return getElementWidth(document.getElementById(sElementId));
+}
+
+/**
+ * Gets the real unscaled width of the given element.
+ *
+ * Useful when using the element to figure the size of a image
+ * or similar.
+ *
+ * @returns Number of screen pixels. null if oElement is bad.
+ * @param oElement The element (not ID).
+ */
+function getUnscaledElementWidth(oElement)
+{
+ if (oElement && oElement.offsetWidth)
+ return Math.round(oElement.offsetWidth * getDevicePixelRatio());
+ return null;
+}
+
+/** By element ID version of getUnscaledElementWidth. */
+function getUnscaledElementWidthById(sElementId)
+{
+ return getUnscaledElementWidth(document.getElementById(sElementId));
+}
+
+/**
+ * Gets the part of the URL needed for a RedirectTo parameter.
+ *
+ * @returns URL string.
+ */
+function getCurrentBrowerUrlPartForRedirectTo()
+{
+ var sWhere = window.location.href;
+ var offTmp;
+ var offPathKeep;
+
+ /* Find the end of that URL 'path' component. */
+ var offPathEnd = sWhere.indexOf('?');
+ if (offPathEnd < 0)
+ offPathEnd = sWhere.indexOf('#');
+ if (offPathEnd < 0)
+ offPathEnd = sWhere.length;
+
+ /* Go backwards from the end of the and find the start of the last component. */
+ offPathKeep = sWhere.lastIndexOf("/", offPathEnd);
+ offTmp = sWhere.lastIndexOf(":", offPathEnd);
+ if (offPathKeep < offTmp)
+ offPathKeep = offTmp;
+ offTmp = sWhere.lastIndexOf("\\", offPathEnd);
+ if (offPathKeep < offTmp)
+ offPathKeep = offTmp;
+
+ return sWhere.substring(offPathKeep + 1);
+}
+
+/**
+ * Adds the given sorting options to the URL and reloads.
+ *
+ * This will preserve previous sorting columns except for those
+ * given in @a aiColumns.
+ *
+ * @param sParam Sorting parameter.
+ * @param aiColumns Array of sorting columns.
+ */
+function ahrefActionSortByColumns(sParam, aiColumns)
+{
+ var sWhere = window.location.href;
+
+ var offHash = sWhere.indexOf('#');
+ if (offHash < 0)
+ offHash = sWhere.length;
+
+ var offQm = sWhere.indexOf('?');
+ if (offQm > offHash)
+ offQm = -1;
+
+ var sNew = '';
+ if (offQm > 0)
+ sNew = sWhere.substring(0, offQm);
+
+ sNew += '?' + sParam + '=' + aiColumns[0];
+ var i;
+ for (i = 1; i < aiColumns.length; i++)
+ sNew += '&' + sParam + '=' + aiColumns[i];
+
+ if (offQm >= 0 && offQm + 1 < offHash)
+ {
+ var sArgs = '&' + sWhere.substring(offQm + 1, offHash);
+ var off = 0;
+ while (off < sArgs.length)
+ {
+ var offMatch = sArgs.indexOf('&' + sParam + '=', off);
+ if (offMatch >= 0)
+ {
+ if (off < offMatch)
+ sNew += sArgs.substring(off, offMatch);
+
+ var offValue = offMatch + 1 + sParam.length + 1;
+ offEnd = sArgs.indexOf('&', offValue);
+ if (offEnd < offValue)
+ offEnd = sArgs.length;
+
+ var iColumn = parseInt(sArgs.substring(offValue, offEnd));
+ if (!isMemberOfArray(aiColumns, iColumn) && !isMemberOfArray(aiColumns, -iColumn))
+ sNew += sArgs.substring(offMatch, offEnd);
+
+ off = offEnd;
+ }
+ else
+ {
+ sNew += sArgs.substring(off);
+ break;
+ }
+ }
+ }
+
+ if (offHash < sWhere.length)
+ sNew = sWhere.substr(offHash);
+
+ window.location.href = sNew;
+}
+
+/**
+ * Sets the value of an input field element (give by ID).
+ *
+ * @returns Returns success indicator (true/false).
+ * @param sFieldId The field ID (required for updating).
+ * @param sValue The field value.
+ */
+function setInputFieldValue(sFieldId, sValue)
+{
+ var oInputElement = document.getElementById(sFieldId);
+ if (oInputElement)
+ {
+ oInputElement.value = sValue;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Adds a hidden input field to a form.
+ *
+ * @returns The new input field element.
+ * @param oFormElement The form to append it to.
+ * @param sName The field name.
+ * @param sValue The field value.
+ * @param sFieldId The field ID (optional).
+ */
+function addHiddenInputFieldToForm(oFormElement, sName, sValue, sFieldId)
+{
+ var oNew = document.createElement('input');
+ oNew.type = 'hidden';
+ oNew.name = sName;
+ oNew.value = sValue;
+ if (sFieldId)
+ oNew.id = sFieldId;
+ oFormElement.appendChild(oNew);
+ return oNew;
+}
+
+/** By element ID version of addHiddenInputFieldToForm. */
+function addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId)
+{
+ return addHiddenInputFieldToForm(document.getElementById(sFormId), sName, sValue, sFieldId);
+}
+
+/**
+ * Adds or updates a hidden input field to/on a form.
+ *
+ * @returns The new input field element.
+ * @param sFormId The ID of the form to amend.
+ * @param sName The field name.
+ * @param sValue The field value.
+ * @param sFieldId The field ID (required for updating).
+ */
+function addUpdateHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId)
+{
+ var oInputElement = null;
+ if (sFieldId)
+ {
+ oInputElement = document.getElementById(sFieldId);
+ }
+ if (oInputElement)
+ {
+ oInputElement.name = sName;
+ oInputElement.value = sValue;
+ }
+ else
+ {
+ oInputElement = addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId);
+ }
+ return oInputElement;
+}
+
+/**
+ * Adds a width and a dpi input to the given form element if possible to
+ * determine the values.
+ *
+ * This is normally employed in an onlick hook, but then you must specify IDs or
+ * the browser may end up adding it several times.
+ *
+ * @param sFormId The ID of the form to amend.
+ * @param sWidthSrcId The ID of the element to calculate the width
+ * value from.
+ * @param sWidthName The name of the width value.
+ * @param sDpiName The name of the dpi value.
+ */
+function addDynamicGraphInputs(sFormId, sWidthSrcId, sWidthName, sDpiName)
+{
+ var cx = getUnscaledElementWidthById(sWidthSrcId);
+ var cDotsPerInch = getDeviceXDotsPerInch();
+
+ if (cx)
+ {
+ addUpdateHiddenInputFieldToFormById(sFormId, sWidthName, cx, sFormId + '-' + sWidthName + '-id');
+ }
+
+ if (cDotsPerInch)
+ {
+ addUpdateHiddenInputFieldToFormById(sFormId, sDpiName, cDotsPerInch, sFormId + '-' + sDpiName + '-id');
+ }
+
+}
+
+/**
+ * Adds the RedirecTo field with the current URL to the form.
+ *
+ * This is a 'onsubmit' action.
+ *
+ * @returns Returns success indicator (true/false).
+ * @param oForm The form being submitted.
+ */
+function addRedirectToInputFieldWithCurrentUrl(oForm)
+{
+ /* Constant used here is duplicated in WuiDispatcherBase.ksParamRedirectTo */
+ return addHiddenInputFieldToForm(oForm, 'RedirectTo', getCurrentBrowerUrlPartForRedirectTo(), null);
+}
+
+/**
+ * Adds the RedirecTo parameter to the href of the given anchor.
+ *
+ * This is a 'onclick' action.
+ *
+ * @returns Returns success indicator (true/false).
+ * @param oAnchor The anchor element being clicked on.
+ */
+function addRedirectToAnchorHref(oAnchor)
+{
+ var sRedirectToParam = g_ksParamRedirectTo + '=' + encodeURIComponent(getCurrentBrowerUrlPartForRedirectTo());
+ var sHref = oAnchor.href;
+ if (sHref.indexOf(sRedirectToParam) < 0)
+ {
+ var sHash;
+ var offHash = sHref.indexOf('#');
+ if (offHash >= 0)
+ sHash = sHref.substring(offHash);
+ else
+ {
+ sHash = '';
+ offHash = sHref.length;
+ }
+ sHref = sHref.substring(0, offHash)
+ if (sHref.indexOf('?') >= 0)
+ sHref += '&';
+ else
+ sHref += '?';
+ sHref += sRedirectToParam;
+ sHref += sHash;
+ oAnchor.href = sHref;
+ }
+ return true;
+}
+
+
+
+/**
+ * Clears one input element.
+ *
+ * @param oInput The input to clear.
+ */
+function resetInput(oInput)
+{
+ switch (oInput.type)
+ {
+ case 'checkbox':
+ case 'radio':
+ oInput.checked = false;
+ break;
+
+ case 'text':
+ oInput.value = 0;
+ break;
+ }
+}
+
+
+/**
+ * Clears a form.
+ *
+ * @param sIdForm The ID of the form
+ */
+function clearForm(sIdForm)
+{
+ var oForm = document.getElementById(sIdForm);
+ if (oForm)
+ {
+ var aoInputs = oForm.getElementsByTagName('INPUT');
+ var i;
+ for (i = 0; i < aoInputs.length; i++)
+ resetInput(aoInputs[i])
+
+ /* HTML5 allows inputs outside <form>, so scan the document. */
+ aoInputs = document.getElementsByTagName('INPUT');
+ for (i = 0; i < aoInputs.length; i++)
+ if (aoInputs.hasOwnProperty("form"))
+ if (aoInputs.form == sIdForm)
+ resetInput(aoInputs[i])
+ }
+
+ return true;
+}
+
+
+/**
+ * Used by the time navigation to update the hidden efficient date field when
+ * either of the date or time fields changes.
+ *
+ * @param oForm The form.
+ */
+function timeNavigationUpdateHiddenEffDate(oForm, sIdSuffix)
+{
+ var sDate = document.getElementById('EffDate' + sIdSuffix).value;
+ var sTime = document.getElementById('EffTime' + sIdSuffix).value;
+
+ var oField = document.getElementById('EffDateTime' + sIdSuffix);
+ oField.value = sDate + 'T' + sTime + '.00Z';
+}
+
+
+/** @name Collapsible / Expandable items
+ * @{
+ */
+
+
+/**
+ * Toggles the collapsible / expandable state of a parent DD and DT uncle.
+ *
+ * @returns true
+ * @param oAnchor The anchor object.
+ */
+function toggleCollapsibleDtDd(oAnchor)
+{
+ var oParent = oAnchor.parentElement;
+ var sClass = oParent.className;
+
+ /* Find the DD sibling tag */
+ var oDdElement = oParent.nextSibling;
+ while (oDdElement != null && oDdElement.tagName != 'DD')
+ oDdElement = oDdElement.nextSibling;
+
+ /* Determin the new class and arrow char. */
+ var sNewClass;
+ var sNewChar;
+ if ( sClass.substr(-11) == 'collapsible')
+ {
+ sNewClass = sClass.substr(0, sClass.length - 11) + 'expandable';
+ sNewChar = '\u25B6'; /* black right-pointing triangle */
+ }
+ else if (sClass.substr(-10) == 'expandable')
+ {
+ sNewClass = sClass.substr(0, sClass.length - 10) + 'collapsible';
+ sNewChar = '\u25BC'; /* black down-pointing triangle */
+ }
+ else
+ {
+ console.log('toggleCollapsibleParent: Invalid class: ' + sClass);
+ return true;
+ }
+
+ /* Update the parent (DT) class and anchor text. */
+ oParent.className = sNewClass;
+ oAnchor.firstChild.textContent = sNewChar + oAnchor.firstChild.textContent.substr(1);
+
+ /* Update the uncle (DD) class. */
+ if (oDdElement)
+ oDdElement.className = sNewClass;
+ return true;
+}
+
+/**
+ * Shows/hides a sub-category UL according to checkbox status.
+ *
+ * The checkbox is expected to be within a label element or something.
+ *
+ * @returns true
+ * @param oInput The input checkbox.
+ */
+function toggleCollapsibleCheckbox(oInput)
+{
+ var oParent = oInput.parentElement;
+
+ /* Find the UL sibling element. */
+ var oUlElement = oParent.nextSibling;
+ while (oUlElement != null && oUlElement.tagName != 'UL')
+ oUlElement = oUlElement.nextSibling;
+
+ /* Change the visibility. */
+ if (oInput.checked)
+ oUlElement.className = oUlElement.className.replace('expandable', 'collapsible');
+ else
+ {
+ oUlElement.className = oUlElement.className.replace('collapsible', 'expandable');
+
+ /* Make sure all sub-checkboxes are now unchecked. */
+ var aoSubInputs = oUlElement.getElementsByTagName('input');
+ var i;
+ for (i = 0; i < aoSubInputs.length; i++)
+ aoSubInputs[i].checked = false;
+ }
+ return true;
+}
+
+/**
+ * Toggles the sidebar size so filters can more easily manipulated.
+ */
+function toggleSidebarSize()
+{
+ var sLinkText;
+ if (document.body.className != 'tm-wide-side-menu')
+ {
+ document.body.className = 'tm-wide-side-menu';
+ sLinkText = '\u00ab\u00ab';
+ }
+ else
+ {
+ document.body.className = '';
+ sLinkText = '\u00bb\u00bb';
+ }
+
+ var aoToggleLink = document.getElementsByClassName('tm-sidebar-size-link');
+ var i;
+ for (i = 0; i < aoToggleLink.length; i++)
+ if ( aoToggleLink[i].textContent.indexOf('\u00bb') >= 0
+ || aoToggleLink[i].textContent.indexOf('\u00ab') >= 0)
+ aoToggleLink[i].textContent = sLinkText;
+}
+
+/** @} */
+
+
+/** @name Custom Tooltips
+ * @{
+ */
+
+/** Enables non-iframe tooltip code. */
+var g_fNewTooltips = true;
+
+/** Where we keep tooltip elements when not displayed. */
+var g_dTooltips = {};
+var g_oCurrentTooltip = null;
+var g_idTooltipShowTimer = null;
+var g_idTooltipHideTimer = null;
+var g_cTooltipSvnRevisions = 12;
+
+/**
+ * Cancel showing/replacing/repositing a tooltip.
+ */
+function tooltipResetShowTimer()
+{
+ if (g_idTooltipShowTimer)
+ {
+ clearTimeout(g_idTooltipShowTimer);
+ g_idTooltipShowTimer = null;
+ }
+}
+
+/**
+ * Cancel hiding of the current tooltip.
+ */
+function tooltipResetHideTimer()
+{
+ if (g_idTooltipHideTimer)
+ {
+ clearTimeout(g_idTooltipHideTimer);
+ g_idTooltipHideTimer = null;
+ }
+}
+
+/**
+ * Really hide the tooltip.
+ */
+function tooltipReallyHide()
+{
+ if (g_oCurrentTooltip)
+ {
+ //console.log('tooltipReallyHide: ' + g_oCurrentTooltip);
+ g_oCurrentTooltip.oElm.style.display = 'none';
+ g_oCurrentTooltip = null;
+ }
+}
+
+/**
+ * Schedule the tooltip for hiding.
+ */
+function tooltipHide()
+{
+ function tooltipDelayedHide()
+ {
+ tooltipResetHideTimer();
+ tooltipReallyHide();
+ }
+
+ /*
+ * Cancel any pending show and schedule hiding if necessary.
+ */
+ tooltipResetShowTimer();
+ if (g_oCurrentTooltip && !g_idTooltipHideTimer)
+ {
+ g_idTooltipHideTimer = setTimeout(tooltipDelayedHide, 700);
+ }
+
+ return true;
+}
+
+/**
+ * Function that is repositions the tooltip when it's shown.
+ *
+ * Used directly, via onload, and hackish timers to catch all browsers and
+ * whatnot.
+ *
+ * Will set several tooltip member variables related to position and space.
+ */
+function tooltipRepositionOnLoad()
+{
+ //console.log('tooltipRepositionOnLoad');
+ if (g_oCurrentTooltip)
+ {
+ var oRelToRect = g_oCurrentTooltip.oRelToRect;
+ var cxNeeded = g_oCurrentTooltip.oElm.offsetWidth + 8;
+ var cyNeeded = g_oCurrentTooltip.oElm.offsetHeight + 8;
+
+ var cyWindow = window.innerHeight;
+ var yScroll = window.pageYOffset || document.documentElement.scrollTop;
+ var yScrollBottom = yScroll + cyWindow;
+ var cxWindow = window.innerWidth;
+ var xScroll = window.pageXOffset || document.documentElement.scrollLeft;
+ var xScrollRight = xScroll + cxWindow;
+
+ var cyAbove = Math.max(oRelToRect.top, 0);
+ var cyBelow = Math.max(cyWindow - oRelToRect.bottom, 0);
+ var cxLeft = Math.max(oRelToRect.left, 0);
+ var cxRight = Math.max(cxWindow - oRelToRect.right, 0);
+
+ var xPos;
+ var yPos;
+
+ //console.log('tooltipRepositionOnLoad: rect: x,y=' + oRelToRect.x + ',' + oRelToRect.y
+ // + ' cx,cy=' + oRelToRect.width + ',' + oRelToRect.height + ' top=' + oRelToRect.top
+ // + ' bottom=' + oRelToRect.bottom + ' left=' + oRelToRect.left + ' right=' + oRelToRect.right);
+ //console.log('tooltipRepositionOnLoad: yScroll=' + yScroll + ' yScrollBottom=' + yScrollBottom);
+ //console.log('tooltipRepositionOnLoad: cyAbove=' + cyAbove + ' cyBelow=' + cyBelow + ' cyNeeded=' + cyNeeded);
+ //console.log('tooltipRepositionOnLoad: xScroll=' + xScroll + ' xScrollRight=' + xScrollRight);
+ //console.log('tooltipRepositionOnLoad: cxLeft=' + cxLeft + ' cxRight=' + cxRight + ' cxNeeded=' + cxNeeded);
+
+ /*
+ * Decide where to put the thing.
+ */
+ if (cyNeeded < cyBelow)
+ {
+ yPos = yScroll + oRelToRect.top;
+ g_oCurrentTooltip.cyMax = cyBelow;
+ //console.log('tooltipRepositionOnLoad: #1');
+ }
+ else if (cyBelow >= cyAbove)
+ {
+ yPos = yScrollBottom - cyNeeded;
+ g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
+ //console.log('tooltipRepositionOnLoad: #2');
+ }
+ else
+ {
+ yPos = yScroll + oRelToRect.bottom - cyNeeded;
+ g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
+ //console.log('tooltipRepositionOnLoad: #3');
+ }
+ if (yPos < yScroll)
+ {
+ yPos = yScroll;
+ g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
+ //console.log('tooltipRepositionOnLoad: #4');
+ }
+ g_oCurrentTooltip.yPos = yPos;
+ g_oCurrentTooltip.yScroll = yScroll;
+ g_oCurrentTooltip.cyMaxUp = yPos - yScroll;
+ //console.log('tooltipRepositionOnLoad: yPos=' + yPos + ' yScroll=' + yScroll + ' cyMaxUp=' + g_oCurrentTooltip.cyMaxUp);
+
+ if (cxNeeded < cxRight)
+ {
+ xPos = xScroll + oRelToRect.right;
+ g_oCurrentTooltip.cxMax = cxRight;
+ //console.log('tooltipRepositionOnLoad: #5');
+ }
+ else
+ {
+ xPos = xScroll + oRelToRect.left - cxNeeded;
+ if (xPos < xScroll)
+ xPos = xScroll;
+ g_oCurrentTooltip.cxMax = cxNeeded;
+ //console.log('tooltipRepositionOnLoad: #6');
+ }
+ g_oCurrentTooltip.xPos = xPos;
+ g_oCurrentTooltip.xScroll = xScroll;
+ //console.log('tooltipRepositionOnLoad: xPos=' + xPos + ' xScroll=' + xScroll);
+
+ g_oCurrentTooltip.oElm.style.top = yPos + 'px';
+ g_oCurrentTooltip.oElm.style.left = xPos + 'px';
+ }
+ return true;
+}
+
+
+/**
+ * Really show the tooltip.
+ *
+ * @param oTooltip The tooltip object.
+ * @param oRelTo What to put the tooltip adjecent to.
+ */
+function tooltipReallyShow(oTooltip, oRelTo)
+{
+ var oRect;
+
+ tooltipResetShowTimer();
+ tooltipResetHideTimer();
+
+ if (g_oCurrentTooltip == oTooltip)
+ {
+ //console.log('moving tooltip');
+ }
+ else if (g_oCurrentTooltip)
+ {
+ //console.log('removing current tooltip and showing new');
+ tooltipReallyHide();
+ }
+ else
+ {
+ //console.log('showing tooltip');
+ }
+
+ //oTooltip.oElm.setAttribute('style', 'display: block; position: absolute;');
+ oTooltip.oElm.style.position = 'absolute';
+ oTooltip.oElm.style.display = 'block';
+ oRect = oRelTo.getBoundingClientRect();
+ oTooltip.oRelToRect = oRect;
+
+ g_oCurrentTooltip = oTooltip;
+
+ /*
+ * Do repositioning (again).
+ */
+ tooltipRepositionOnLoad();
+}
+
+/**
+ * Tooltip onmouseenter handler .
+ */
+function tooltipElementOnMouseEnter()
+{
+ /*console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]);
+ console.log('ENT: currentTarget='+arguments[0].currentTarget+' id='+arguments[0].currentTarget.id+' class='+arguments[0].currentTarget.className); */
+ tooltipResetShowTimer();
+ tooltipResetHideTimer();
+ return true;
+}
+
+/**
+ * Tooltip onmouseout handler.
+ *
+ * @remarks We only use this and onmouseenter for one tooltip element (iframe
+ * for svn, because chrome is sending onmouseout events after
+ * onmouseneter for the next element, which would confuse this simple
+ * code.
+ */
+function tooltipElementOnMouseOut()
+{
+ var oEvt = arguments[0];
+ /*console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+oEvt);
+ console.log('OUT: currentTarget='+oEvt.currentTarget+' id='+oEvt.currentTarget.id+' class='+oEvt.currentTarget.className);*/
+
+ /* Ignore the event if leaving to a child element. */
+ var oElm = oEvt.toElement || oEvt.relatedTarget;
+ if (oElm != this && oElm)
+ {
+ for (;;)
+ {
+ oElm = oElm.parentNode;
+ if (!oElm || oElm == window)
+ break;
+ if (oElm == this)
+ {
+ console.log('OUT: was to child! - ignore');
+ return false;
+ }
+ }
+ }
+
+ tooltipHide();
+ return true;
+}
+
+/**
+ * iframe.onload hook that repositions and resizes the tooltip.
+ *
+ * This is a little hacky and we're calling it one or three times too many to
+ * work around various browser differences too.
+ */
+function svnHistoryTooltipOldOnLoad()
+{
+ //console.log('svnHistoryTooltipOldOnLoad');
+
+ /*
+ * Resize the tooltip to better fit the content.
+ */
+ tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */
+ if (g_oCurrentTooltip && g_oCurrentTooltip.oIFrame.contentWindow)
+ {
+ var oIFrameElement = g_oCurrentTooltip.oIFrame;
+ var cxSpace = Math.max(oIFrameElement.offsetLeft * 2, 0); /* simplified */
+ var cySpace = Math.max(oIFrameElement.offsetTop * 2, 0); /* simplified */
+ var cxNeeded = oIFrameElement.contentWindow.document.body.scrollWidth + cxSpace;
+ var cyNeeded = oIFrameElement.contentWindow.document.body.scrollHeight + cySpace;
+ var cx = Math.min(cxNeeded, g_oCurrentTooltip.cxMax);
+ var cy;
+
+ g_oCurrentTooltip.oElm.width = cx + 'px';
+ oIFrameElement.width = (cx - cxSpace) + 'px';
+ if (cx >= cxNeeded)
+ {
+ //console.log('svnHistoryTooltipOldOnLoad: overflowX -> hidden');
+ oIFrameElement.style.overflowX = 'hidden';
+ }
+ else
+ {
+ oIFrameElement.style.overflowX = 'scroll';
+ }
+
+ cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
+ if (cyNeeded > g_oCurrentTooltip.cyMax && g_oCurrentTooltip.cyMaxUp > 0)
+ {
+ var cyMove = Math.min(cyNeeded - g_oCurrentTooltip.cyMax, g_oCurrentTooltip.cyMaxUp);
+ g_oCurrentTooltip.cyMax += cyMove;
+ g_oCurrentTooltip.yPos -= cyMove;
+ g_oCurrentTooltip.oElm.style.top = g_oCurrentTooltip.yPos + 'px';
+ cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
+ }
+
+ g_oCurrentTooltip.oElm.height = cy + 'px';
+ oIFrameElement.height = (cy - cySpace) + 'px';
+ if (cy >= cyNeeded)
+ {
+ //console.log('svnHistoryTooltipOldOnLoad: overflowY -> hidden');
+ oIFrameElement.style.overflowY = 'hidden';
+ }
+ else
+ {
+ oIFrameElement.style.overflowY = 'scroll';
+ }
+
+ //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy);
+ //console.log('oIFrameElement.offsetTop='+oIFrameElement.offsetTop);
+ //console.log('svnHistoryTooltipOldOnLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax);
+
+ tooltipRepositionOnLoad();
+ }
+ return true;
+}
+
+/**
+ * iframe.onload hook that repositions and resizes the tooltip.
+ *
+ * This is a little hacky and we're calling it one or three times too many to
+ * work around various browser differences too.
+ */
+function svnHistoryTooltipNewOnLoad()
+{
+ //console.log('svnHistoryTooltipNewOnLoad');
+
+ /*
+ * Resize the tooltip to better fit the content.
+ */
+ tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */
+ oTooltip = g_oCurrentTooltip;
+ if (oTooltip)
+ {
+ var oElmInner = oTooltip.oInnerElm;
+ var cxSpace = Math.max(oElmInner.offsetLeft * 2, 0); /* simplified */
+ var cySpace = Math.max(oElmInner.offsetTop * 2, 0); /* simplified */
+ var cxNeeded = oElmInner.scrollWidth + cxSpace;
+ var cyNeeded = oElmInner.scrollHeight + cySpace;
+ var cx = Math.min(cxNeeded, oTooltip.cxMax);
+
+ oTooltip.oElm.width = cx + 'px';
+ oElmInner.width = (cx - cxSpace) + 'px';
+ if (cx >= cxNeeded)
+ {
+ //console.log('svnHistoryTooltipNewOnLoad: overflowX -> hidden');
+ oElmInner.style.overflowX = 'hidden';
+ }
+ else
+ {
+ oElmInner.style.overflowX = 'scroll';
+ }
+
+ var cy = Math.min(cyNeeded, oTooltip.cyMax);
+ if (cyNeeded > oTooltip.cyMax && oTooltip.cyMaxUp > 0)
+ {
+ var cyMove = Math.min(cyNeeded - oTooltip.cyMax, oTooltip.cyMaxUp);
+ oTooltip.cyMax += cyMove;
+ oTooltip.yPos -= cyMove;
+ oTooltip.oElm.style.top = oTooltip.yPos + 'px';
+ cy = Math.min(cyNeeded, oTooltip.cyMax);
+ }
+
+ oTooltip.oElm.height = cy + 'px';
+ oElmInner.height = (cy - cySpace) + 'px';
+ if (cy >= cyNeeded)
+ {
+ //console.log('svnHistoryTooltipNewOnLoad: overflowY -> hidden');
+ oElmInner.style.overflowY = 'hidden';
+ }
+ else
+ {
+ oElmInner.style.overflowY = 'scroll';
+ }
+
+ //console.log('cyNeeded='+cyNeeded+' cyMax='+oTooltip.cyMax+' cySpace='+cySpace+' cy='+cy);
+ //console.log('oElmInner.offsetTop='+oElmInner.offsetTop);
+ //console.log('svnHistoryTooltipNewOnLoad: cx='+cx+'cxMax='+oTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+oTooltip.cyMax);
+
+ tooltipRepositionOnLoad();
+ }
+ return true;
+}
+
+
+function svnHistoryTooltipNewOnReadState(oTooltip, oRestReq, oParent)
+{
+ /*console.log('svnHistoryTooltipNewOnReadState: status=' + oRestReq.status + ' readyState=' + oRestReq.readyState);*/
+ if (oRestReq.readyState != oRestReq.DONE)
+ {
+ oTooltip.oInnerElm.innerHTML = '<p>Loading ...(' + oRestReq.readyState + ')</p>';
+ return true;
+ }
+
+ /*
+ * Check the result and translate it to a javascript object (oResp).
+ */
+ var oResp = null;
+ var sHtml;
+ if (oRestReq.status != 200)
+ {
+ console.log('svnHistoryTooltipNewOnReadState: status=' + oRestReq.status);
+ sHtml = '<p>error: status=' + oRestReq.status + '</p>';
+ }
+ else
+ {
+ try
+ {
+ oResp = JSON.parse(oRestReq.responseText);
+ }
+ catch (oEx)
+ {
+ console.log('JSON.parse threw: ' + oEx.toString());
+ console.log(oRestReq.responseText);
+ sHtml = '<p>error: JSON.parse threw: ' + oEx.toString() + '</p>';
+ }
+ }
+
+ /*
+ * Generate the HTML.
+ *
+ * Note! Make sure the highlighting code in svnHistoryTooltipNewDelayedShow
+ * continues to work after modifying this code.
+ */
+ if (oResp)
+ {
+ sHtml = '<div class="tmvcstimeline tmvcstimelinetooltip">\n';
+
+ var aoCommits = oResp.aoCommits;
+ var cCommits = oResp.aoCommits.length;
+ var iCurDay = null;
+ var i;
+ for (i = 0; i < cCommits; i++)
+ {
+ var oCommit = aoCommits[i];
+ var tsCreated = parseIsoTimestamp(oCommit.tsCreated);
+ var iCommitDay = Math.floor((tsCreated.getTime() + tsCreated.getTimezoneOffset()) / (24 * 60 * 60 * 1000));
+ if (iCurDay === null || iCurDay != iCommitDay)
+ {
+ if (iCurDay !== null)
+ sHtml += ' </dl>\n';
+ iCurDay = iCommitDay;
+ sHtml += ' <h2>' + tsCreated.toISOString().split('T')[0] + ' ' + g_kasDaysOfTheWeek[tsCreated.getDay()] + '</h2>\n';
+ sHtml += ' <dl>\n';
+ }
+ Date
+
+ var sHighligh = '';
+ if (oCommit.iRevision == oTooltip.iRevision)
+ sHighligh += ' class="tmvcstimeline-highlight"';
+
+ sHtml += ' <dt id="r' + oCommit.iRevision + '"' + sHighligh + '>';
+ sHtml += '<a href="' + oResp.sTracChangesetUrlFmt.replace('%(iRevision)s', oCommit.iRevision.toString());
+ sHtml += '" target="_blank">';
+ sHtml += '<span class="tmvcstimeline-time">' + escapeElem(formatTimeHHMM(tsCreated, true)) + '</span>'
+ sHtml += ' Changeset <span class="tmvcstimeline-rev">[' + oCommit.iRevision + ']</span>';
+ sHtml += ' by <span class="tmvcstimeline-author">' + escapeElem(oCommit.sAuthor) + '</span>';
+ sHtml += '</a></dt>\n';
+ sHtml += ' <dd' + sHighligh + '>' + escapeElem(oCommit.sMessage) + '</dd>\n';
+ }
+
+ if (iCurDay !== null)
+ sHtml += ' </dl>\n';
+ sHtml += '</div>';
+ }
+
+ /*console.log('svnHistoryTooltipNewOnReadState: sHtml=' + sHtml);*/
+ oTooltip.oInnerElm.innerHTML = sHtml;
+
+ tooltipReallyShow(oTooltip, oParent);
+ svnHistoryTooltipNewOnLoad();
+}
+
+/**
+ * Calculates the last revision to get when showing a tooltip for @a iRevision.
+ *
+ * A tooltip covers several change log entries, both to limit the number of
+ * tooltips to load and to give context. The exact number is defined by
+ * g_cTooltipSvnRevisions.
+ *
+ * @returns Last revision in a tooltip.
+ * @param iRevision The revision number.
+ */
+function svnHistoryTooltipCalcLastRevision(iRevision)
+{
+ var iFirstRev = Math.floor(iRevision / g_cTooltipSvnRevisions) * g_cTooltipSvnRevisions;
+ return iFirstRev + g_cTooltipSvnRevisions - 1;
+}
+
+/**
+ * Calculates a unique ID for the tooltip element.
+ *
+ * This is also used as dictionary index.
+ *
+ * @returns tooltip ID value (string).
+ * @param sRepository The repository name.
+ * @param iRevision The revision number.
+ */
+function svnHistoryTooltipCalcId(sRepository, iRevision)
+{
+ return 'svnHistoryTooltip_' + sRepository + '_' + svnHistoryTooltipCalcLastRevision(iRevision);
+}
+
+/**
+ * The onmouseenter event handler for creating the tooltip.
+ *
+ * @param oEvt The event.
+ * @param sRepository The repository name.
+ * @param iRevision The revision number.
+ * @param sUrlPrefix URL prefix for non-testmanager use.
+ *
+ * @remarks onmouseout must be set to call tooltipHide.
+ */
+function svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, sUrlPrefix)
+{
+ var sKey = svnHistoryTooltipCalcId(sRepository, iRevision);
+ var oParent = oEvt.currentTarget;
+ //console.log('svnHistoryTooltipShow ' + sRepository);
+
+ function svnHistoryTooltipOldDelayedShow()
+ {
+ var sSrc;
+
+ var oTooltip = g_dTooltips[sKey];
+ //console.log('svnHistoryTooltipOldDelayedShow ' + sRepository + ' ' + oTooltip);
+ if (!oTooltip)
+ {
+ /*
+ * Create a new tooltip element.
+ */
+ //console.log('creating ' + sKey);
+ oTooltip = {};
+ oTooltip.oElm = document.createElement('div');
+ oTooltip.oElm.setAttribute('id', sKey);
+ oTooltip.oElm.className = 'tmvcstooltip';
+ //oTooltip.oElm.setAttribute('style', 'display:none; position: absolute;');
+ oTooltip.oElm.style.display = 'none'; /* Note! Must stay hidden till loaded, or parent jumps with #rXXXX.*/
+ oTooltip.oElm.style.position = 'absolute';
+ oTooltip.oElm.style.zIndex = 6001;
+ oTooltip.xPos = 0;
+ oTooltip.yPos = 0;
+ oTooltip.cxMax = 0;
+ oTooltip.cyMax = 0;
+ oTooltip.cyMaxUp = 0;
+ oTooltip.xScroll = 0;
+ oTooltip.yScroll = 0;
+ oTooltip.iRevision = iRevision; /**< For :target/highlighting */
+
+ var oIFrameElement = document.createElement('iframe');
+ oIFrameElement.setAttribute('id', sKey + '_iframe');
+ oIFrameElement.style.position = 'relative';
+ oIFrameElement.onmouseenter = tooltipElementOnMouseEnter;
+ //oIFrameElement.onmouseout = tooltipElementOnMouseOut;
+ oTooltip.oElm.appendChild(oIFrameElement);
+ oTooltip.oIFrame = oIFrameElement;
+ g_dTooltips[sKey] = oTooltip;
+
+ document.body.appendChild(oTooltip.oElm);
+
+ oIFrameElement.onload = function() { /* A slight delay here to give time for #rXXXX scrolling before we show it. */
+ setTimeout(function(){
+ /*console.log('iframe/onload');*/
+ tooltipReallyShow(oTooltip, oParent);
+ svnHistoryTooltipOldOnLoad();
+ }, isBrowserInternetExplorer() ? 256 : 128);
+ };
+
+ var sUrl = sUrlPrefix + 'index.py?Action=VcsHistoryTooltip&repo=' + sRepository
+ + '&rev=' + svnHistoryTooltipCalcLastRevision(iRevision)
+ + '&cEntries=' + g_cTooltipSvnRevisions
+ + '#r' + iRevision;
+ oIFrameElement.src = sUrl;
+ }
+ else
+ {
+ /*
+ * Show the existing one, possibly with different :target/highlighting.
+ */
+ if (oTooltip.iRevision != iRevision)
+ {
+ //console.log('Changing revision ' + oTooltip.iRevision + ' -> ' + iRevision);
+ oTooltip.oIFrame.contentWindow.location.hash = '#r' + iRevision;
+ if (!isBrowserFirefox()) /* Chrome updates stuff like expected; Firefox OTOH doesn't change anything. */
+ {
+ setTimeout(function() { /* Slight delay to make sure it scrolls before it's shown. */
+ tooltipReallyShow(oTooltip, oParent);
+ svnHistoryTooltipOldOnLoad();
+ }, isBrowserInternetExplorer() ? 256 : 64);
+ }
+ else
+ oTooltip.oIFrame.contentWindow.location.reload();
+ }
+ else
+ {
+ tooltipReallyShow(oTooltip, oParent);
+ svnHistoryTooltipOldOnLoad();
+ }
+ }
+ }
+
+ function svnHistoryTooltipNewDelayedShow()
+ {
+ var sSrc;
+
+ var oTooltip = g_dTooltips[sKey];
+ /*console.log('svnHistoryTooltipNewDelayedShow: ' + sRepository + ' ' + oTooltip);*/
+ if (!oTooltip)
+ {
+ /*
+ * Create a new tooltip element.
+ */
+ /*console.log('creating ' + sKey);*/
+
+ var oElm = document.createElement('div');
+ oElm.setAttribute('id', sKey);
+ oElm.className = 'tmvcstooltipnew';
+ //oElm.setAttribute('style', 'display:none; position: absolute;');
+ oElm.style.display = 'none'; /* Note! Must stay hidden till loaded, or parent jumps with #rXXXX.*/
+ oElm.style.position = 'absolute';
+ oElm.style.zIndex = 6001;
+ oElm.onmouseenter = tooltipElementOnMouseEnter;
+ oElm.onmouseout = tooltipElementOnMouseOut;
+
+ var oInnerElm = document.createElement('div');
+ oInnerElm.className = 'tooltip-inner';
+ oElm.appendChild(oInnerElm);
+
+ oTooltip = {};
+ oTooltip.oElm = oElm;
+ oTooltip.oInnerElm = oInnerElm;
+ oTooltip.xPos = 0;
+ oTooltip.yPos = 0;
+ oTooltip.cxMax = 0;
+ oTooltip.cyMax = 0;
+ oTooltip.cyMaxUp = 0;
+ oTooltip.xScroll = 0;
+ oTooltip.yScroll = 0;
+ oTooltip.iRevision = iRevision; /**< For :target/highlighting */
+
+ oRestReq = new XMLHttpRequest();
+ oRestReq.onreadystatechange = function() { svnHistoryTooltipNewOnReadState(oTooltip, this, oParent); }
+ oRestReq.open('GET', sUrlPrefix + 'rest.py?sPath=vcs/changelog/' + sRepository
+ + '/' + svnHistoryTooltipCalcLastRevision(iRevision) + '/' + g_cTooltipSvnRevisions);
+ oRestReq.setRequestHeader('Content-type', 'application/json');
+
+ document.body.appendChild(oTooltip.oElm);
+ g_dTooltips[sKey] = oTooltip;
+
+ oRestReq.send('');
+ }
+ else
+ {
+ /*
+ * Show the existing one, possibly with different highlighting.
+ * Note! Update this code when changing svnHistoryTooltipNewOnReadState.
+ */
+ if (oTooltip.iRevision != iRevision)
+ {
+ //console.log('Changing revision ' + oTooltip.iRevision + ' -> ' + iRevision);
+ var oElmTimelineDiv = oTooltip.oInnerElm.firstElementChild;
+ var i;
+ for (i = 0; i < oElmTimelineDiv.children.length; i++)
+ {
+ var oElm = oElmTimelineDiv.children[i];
+ //console.log('oElm='+oElm+' id='+oElm.id+' nodeName='+oElm.nodeName);
+ if (oElm.nodeName == 'DL')
+ {
+ var iCurRev = iRevision - 64;
+ var j;
+ for (j = 0; i < oElm.children.length; i++)
+ {
+ var oDlSubElm = oElm.children[i];
+ //console.log(' oDlSubElm='+oDlSubElm+' id='+oDlSubElm.id+' nodeName='+oDlSubElm.nodeName+' className='+oDlSubElm.className);
+ if (oDlSubElm.id.length > 2)
+ iCurRev = parseInt(oDlSubElm.id.substring(1), 10);
+ if (iCurRev == iRevision)
+ oDlSubElm.className = 'tmvcstimeline-highlight';
+ else
+ oDlSubElm.className = '';
+ }
+ }
+ }
+ oTooltip.iRevision = iRevision;
+ }
+
+ tooltipReallyShow(oTooltip, oParent);
+ svnHistoryTooltipNewOnLoad();
+ }
+ }
+
+
+ /*
+ * Delay the change (in case the mouse moves on).
+ */
+ tooltipResetShowTimer();
+ if (g_fNewTooltips)
+ g_idTooltipShowTimer = setTimeout(svnHistoryTooltipNewDelayedShow, 512);
+ else
+ g_idTooltipShowTimer = setTimeout(svnHistoryTooltipOldDelayedShow, 512);
+}
+
+/**
+ * The onmouseenter event handler for creating the tooltip.
+ *
+ * @param oEvt The event.
+ * @param sRepository The repository name.
+ * @param iRevision The revision number.
+ *
+ * @remarks onmouseout must be set to call tooltipHide.
+ */
+function svnHistoryTooltipShow(oEvt, sRepository, iRevision)
+{
+ return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, '');
+}
+
+/** @} */
+
+
+/** @name Debugging and Introspection
+ * @{
+ */
+
+/**
+ * Python-like dir() implementation.
+ *
+ * @returns Array of names associated with oObj.
+ * @param oObj The object under inspection. If not specified we'll
+ * look at the window object.
+ */
+function pythonlikeDir(oObj, fDeep)
+{
+ var aRet = [];
+ var dTmp = {};
+
+ if (!oObj)
+ {
+ oObj = window;
+ }
+
+ for (var oCur = oObj; oCur; oCur = Object.getPrototypeOf(oCur))
+ {
+ var aThis = Object.getOwnPropertyNames(oCur);
+ for (var i = 0; i < aThis.length; i++)
+ {
+ if (!(aThis[i] in dTmp))
+ {
+ dTmp[aThis[i]] = 1;
+ aRet.push(aThis[i]);
+ }
+ }
+ }
+
+ return aRet;
+}
+
+
+/**
+ * Python-like dir() implementation, shallow version.
+ *
+ * @returns Array of names associated with oObj.
+ * @param oObj The object under inspection. If not specified we'll
+ * look at the window object.
+ */
+function pythonlikeShallowDir(oObj, fDeep)
+{
+ var aRet = [];
+ var dTmp = {};
+
+ if (oObj)
+ {
+ for (var i in oObj)
+ {
+ aRet.push(i);
+ }
+ }
+
+ return aRet;
+}
+
+
+
+function dbgGetObjType(oObj)
+{
+ var sType = typeof oObj;
+ if (sType == "object" && oObj !== null)
+ {
+ if (oObj.constructor && oObj.constructor.name)
+ {
+ sType = oObj.constructor.name;
+ }
+ else
+ {
+ var fnToString = Object.prototype.toString;
+ var sTmp = fnToString.call(oObj);
+ if (sTmp.indexOf('[object ') === 0)
+ {
+ sType = sTmp.substring(8, sTmp.length);
+ }
+ }
+ }
+ return sType;
+}
+
+
+/**
+ * Dumps the given object to the console.
+ *
+ * @param oObj The object under inspection.
+ * @param sPrefix What to prefix the log output with.
+ */
+function dbgDumpObj(oObj, sName, sPrefix)
+{
+ var aMembers;
+ var sType;
+
+ /*
+ * Defaults
+ */
+ if (!oObj)
+ {
+ oObj = window;
+ }
+
+ if (!sPrefix)
+ {
+ if (sName)
+ {
+ sPrefix = sName + ':';
+ }
+ else
+ {
+ sPrefix = 'dbgDumpObj:';
+ }
+ }
+
+ if (!sName)
+ {
+ sName = '';
+ }
+
+ /*
+ * The object itself.
+ */
+ sPrefix = sPrefix + ' ';
+ console.log(sPrefix + sName + ' ' + dbgGetObjType(oObj));
+
+ /*
+ * The members.
+ */
+ sPrefix = sPrefix + ' ';
+ aMembers = pythonlikeDir(oObj);
+ for (i = 0; i < aMembers.length; i++)
+ {
+ console.log(sPrefix + aMembers[i]);
+ }
+
+ return true;
+}
+
+function dbgDumpObjWorker(sType, sName, oObj, sPrefix)
+{
+ var sRet;
+ switch (sType)
+ {
+ case 'function':
+ {
+ sRet = sPrefix + 'function ' + sName + '()' + '\n';
+ break;
+ }
+
+ case 'object':
+ {
+ sRet = sPrefix + 'var ' + sName + '(' + dbgGetObjType(oObj) + ') =';
+ if (oObj !== null)
+ {
+ sRet += '\n';
+ }
+ else
+ {
+ sRet += ' null\n';
+ }
+ break;
+ }
+
+ case 'string':
+ {
+ sRet = sPrefix + 'var ' + sName + '(string, ' + oObj.length + ')';
+ if (oObj.length < 80)
+ {
+ sRet += ' = "' + oObj + '"\n';
+ }
+ else
+ {
+ sRet += '\n';
+ }
+ break;
+ }
+
+ case 'Oops!':
+ sRet = sPrefix + sName + '(??)\n';
+ break;
+
+ default:
+ sRet = sPrefix + 'var ' + sName + '(' + sType + ')\n';
+ break;
+ }
+ return sRet;
+}
+
+
+function dbgObjInArray(aoObjs, oObj)
+{
+ var i = aoObjs.length;
+ while (i > 0)
+ {
+ i--;
+ if (aoObjs[i] === oObj)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+function dbgDumpObjTreeWorker(oObj, sPrefix, aParentObjs, cMaxDepth)
+{
+ var sRet = '';
+ var aMembers = pythonlikeShallowDir(oObj);
+ var i;
+
+ for (i = 0; i < aMembers.length; i++)
+ {
+ //var sName = i;
+ var sName = aMembers[i];
+ var oMember;
+ var sType;
+ var oEx;
+
+ try
+ {
+ oMember = oObj[sName];
+ sType = typeof oObj[sName];
+ }
+ catch (oEx)
+ {
+ oMember = null;
+ sType = 'Oops!';
+ }
+
+ //sRet += '[' + i + '/' + aMembers.length + ']';
+ sRet += dbgDumpObjWorker(sType, sName, oMember, sPrefix);
+
+ if ( sType == 'object'
+ && oObj !== null)
+ {
+
+ if (dbgObjInArray(aParentObjs, oMember))
+ {
+ sRet += sPrefix + '! parent recursion\n';
+ }
+ else if ( sName == 'previousSibling'
+ || sName == 'previousElement'
+ || sName == 'lastChild'
+ || sName == 'firstElementChild'
+ || sName == 'lastElementChild'
+ || sName == 'nextElementSibling'
+ || sName == 'prevElementSibling'
+ || sName == 'parentElement'
+ || sName == 'ownerDocument')
+ {
+ sRet += sPrefix + '! potentially dangerous element name\n';
+ }
+ else if (aParentObjs.length >= cMaxDepth)
+ {
+ sRet = sRet.substring(0, sRet.length - 1);
+ sRet += ' <too deep>!\n';
+ }
+ else
+ {
+
+ aParentObjs.push(oMember);
+ if (i + 1 < aMembers.length)
+ {
+ sRet += dbgDumpObjTreeWorker(oMember, sPrefix + '| ', aParentObjs, cMaxDepth);
+ }
+ else
+ {
+ sRet += dbgDumpObjTreeWorker(oMember, sPrefix.substring(0, sPrefix.length - 2) + ' | ', aParentObjs, cMaxDepth);
+ }
+ aParentObjs.pop();
+ }
+ }
+ }
+ return sRet;
+}
+
+/**
+ * Dumps the given object and all it's subobjects to the console.
+ *
+ * @returns String dump of the object.
+ * @param oObj The object under inspection.
+ * @param sName The object name (optional).
+ * @param sPrefix What to prefix the log output with (optional).
+ * @param cMaxDepth The max depth, optional.
+ */
+function dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth)
+{
+ var sType;
+ var sRet;
+ var oEx;
+
+ /*
+ * Defaults
+ */
+ if (!sPrefix)
+ {
+ sPrefix = '';
+ }
+
+ if (!sName)
+ {
+ sName = '??';
+ }
+
+ if (!cMaxDepth)
+ {
+ cMaxDepth = 2;
+ }
+
+ /*
+ * The object itself.
+ */
+ try
+ {
+ sType = typeof oObj;
+ }
+ catch (oEx)
+ {
+ sType = 'Oops!';
+ }
+ sRet = dbgDumpObjWorker(sType, sName, oObj, sPrefix);
+ if (sType == 'object' && oObj !== null)
+ {
+ var aParentObjs = Array();
+ aParentObjs.push(oObj);
+ sRet += dbgDumpObjTreeWorker(oObj, sPrefix + '| ', aParentObjs, cMaxDepth);
+ }
+
+ return sRet;
+}
+
+function dbgLogString(sLongString)
+{
+ var aStrings = sLongString.split("\n");
+ var i;
+ for (i = 0; i < aStrings.length; i++)
+ {
+ console.log(aStrings[i]);
+ }
+ console.log('dbgLogString - end - ' + aStrings.length + '/' + sLongString.length);
+ return true;
+}
+
+function dbgLogObjTree(oObj, sName, sPrefix, cMaxDepth)
+{
+ return dbgLogString(dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth));
+}
+
+/** @} */
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js b/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js
new file mode 100644
index 00000000..90e1163d
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js
@@ -0,0 +1,126 @@
+/* $Id: graphwiz.js $ */
+/** @file
+ * JavaScript functions for the Graph Wizard.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The previous width of the div element that we measure. */
+var g_cxPreviousWidth = 0;
+
+
+/**
+ * onload function that sets g_cxPreviousWidth to the width of @a sWidthSrcId.
+ *
+ * @returns true.
+ * @param sWidthSrcId The ID of the element which width we should measure.
+ */
+function graphwizOnLoadRememberWidth(sWidthSrcId)
+{
+ var cx = getUnscaledElementWidthById(sWidthSrcId);
+ if (cx)
+ {
+ g_cxPreviousWidth = cx;
+ }
+ return true;
+}
+
+
+/**
+ * onresize callback function that scales the given graph width input field
+ * value according to the resized element.
+ *
+ * @returns true.
+ * @param sWidthSrcId The ID of the element which width we should measure
+ * the resize effect on.
+ * @param sWidthInputId The ID of the input field which values should be
+ * scaled.
+ *
+ * @remarks Since we're likely to get several resize calls as part of one user
+ * resize operation, we're likely to suffer from some rounding
+ * artifacts. So, should the user abort or undo the resizing, the
+ * width value is unlikely to be restored to the exact value it had
+ * prior to the resizing.
+ */
+function graphwizOnResizeRecalcWidth(sWidthSrcId, sWidthInputId)
+{
+ var cx = getUnscaledElementWidthById(sWidthSrcId);
+ if (cx)
+ {
+ var oElement = document.getElementById(sWidthInputId);
+ if (oElement && g_cxPreviousWidth)
+ {
+ var cxOld = oElement.value;
+ if (isInteger(cxOld))
+ {
+ var fpRatio = cxOld / g_cxPreviousWidth;
+ oElement.value = Math.round(cx * fpRatio);
+ }
+ }
+ g_cxPreviousWidth = cx;
+ }
+
+ return true;
+}
+
+/**
+ * Fills thegraph size (cx, cy) and dpi fields with default values.
+ *
+ * @returns false (for onclick).
+ * @param sWidthSrcId The ID of the element which width we should measure.
+ * @param sWidthInputId The ID of the graph width field (cx).
+ * @param sHeightInputId The ID of the graph height field (cy).
+ * @param sDpiInputId The ID of the graph DPI field.
+ */
+function graphwizSetDefaultSizeValues(sWidthSrcId, sWidthInputId, sHeightInputId, sDpiInputId)
+{
+ var cx = getUnscaledElementWidthById(sWidthSrcId);
+ var cDotsPerInch = getDeviceXDotsPerInch();
+
+ if (cx)
+ {
+ setInputFieldValue(sWidthInputId, cx);
+ setInputFieldValue(sHeightInputId, Math.round(cx * 5 / 16)); /* See wuimain.py. */
+ }
+
+ if (cDotsPerInch)
+ {
+ setInputFieldValue(sDpiInputId, cDotsPerInch);
+ }
+
+ return false;
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/vcsrevisions.js b/src/VBox/ValidationKit/testmanager/htdocs/js/vcsrevisions.js
new file mode 100644
index 00000000..f7b7de7c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/htdocs/js/vcsrevisions.js
@@ -0,0 +1,237 @@
+/* $Id: vcsrevisions.js $ */
+/** @file
+ * Common JavaScript functions
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/**
+ * @internal.
+ */
+function vcsRevisionFormatDate(tsDate)
+{
+ /*return tsDate.toLocaleDateString();*/
+ return tsDate.toISOString().split('T')[0];
+}
+
+/**
+ * @internal.
+ */
+function vcsRevisionFormatTime(tsDate)
+{
+ return formatTimeHHMM(tsDate, true /*fNbsp*/);
+}
+
+/**
+ * Called 'onclick' for the link/button used to show the detailed VCS
+ * revisions.
+ * @internal.
+ */
+function vcsRevisionShowDetails(oElmSource)
+{
+ document.getElementById('vcsrevisions-detailed').style.display = 'block';
+ document.getElementById('vcsrevisions-brief').style.display = 'none';
+ oElmSource.style.display = 'none';
+ return false;
+}
+
+/**
+ * Called when we've got the revision data.
+ * @internal
+ */
+function vcsRevisionsRender(sTestMgr, oElmDst, sBugTracker, oRestReq, sUrl)
+{
+ console.log('vcsRevisionsRender: status=' + oRestReq.status + ' readyState=' + oRestReq.readyState + ' url=' + sUrl);
+ if (oRestReq.readyState != oRestReq.DONE)
+ {
+ oElmDst.innerHTML = '<p>' + oRestReq.readyState + '</p>';
+ return true;
+ }
+
+
+ /*
+ * Check the result and translate it to a javascript object (oResp).
+ */
+ var oResp = null;
+ var sHtml;
+ if (oRestReq.status != 200)
+ {
+ /** @todo figure why this doesn't work (sPath to something random). */
+ var sMsg = oRestReq.getResponseHeader('tm-error-message');
+ console.log('vcsRevisionsRender: status=' + oRestReq.status + ' readyState=' + oRestReq.readyState + ' url=' + sUrl + ' msg=' + sMsg);
+ sHtml = '<p>error: status=' + oRestReq.status + 'readyState=' + oRestReq.readyState + ' url=' + sUrl;
+ if (sMsg)
+ sHtml += ' msg=' + sMsg;
+ sHtml += '</p>';
+ }
+ else
+ {
+ try
+ {
+ oResp = JSON.parse(oRestReq.responseText);
+ }
+ catch (oEx)
+ {
+ console.log('JSON.parse threw: ' + oEx.toString());
+ console.log(oRestReq.responseText);
+ sHtml = '<p>error: JSON.parse threw: ' + oEx.toString() + '</p>';
+ }
+ }
+
+ /*
+ * Do the rendering.
+ */
+ if (oResp)
+ {
+ if (oResp.cCommits == 0)
+ {
+ sHtml = '<p>None.</p>';
+ }
+ else
+ {
+ var aoCommits = oResp.aoCommits;
+ var cCommits = oResp.aoCommits.length;
+ var i;
+
+ sHtml = '';
+ /*sHtml = '<a href="#" onclick="return vcsRevisionShowDetails(this);" class="vcsrevisions-show-details">Show full VCS details...</a>\n';*/
+ /*sHtml = '<button onclick="vcsRevisionShowDetails(this);" class="vcsrevisions-show-details">Show full VCS details...</button>\n';*/
+
+ /* Brief view (the default): */
+ sHtml += '<p id="vcsrevisions-brief">';
+ for (i = 0; i < cCommits; i++)
+ {
+ var oCommit = aoCommits[i];
+ var sUrl = oResp.sTracChangesetUrlFmt.replace('%(sRepository)s', oCommit.sRepository).replace('%(iRevision)s', oCommit.iRevision.toString());
+ var sTitle = oCommit.sAuthor + ': ' + oCommit.sMessage;
+ sHtml += ' <a href="' + escapeElem(sUrl) + '" title="' + escapeElem(sTitle) + '">r' + oCommit.iRevision + '</a> \n';
+ }
+ sHtml += '</p>';
+ sHtml += '<a href="#" onclick="return vcsRevisionShowDetails(this);" class="vcsrevisions-show-details-bottom">Show full VCS details...</a>\n';
+
+ /* Details view: */
+ sHtml += '<div id="vcsrevisions-detailed" style="display:none;">\n';
+ var iCurDay = null;
+ if (0)
+ {
+ /* Changelog variant: */
+ for (i = 0; i < cCommits; i++)
+ {
+ var oCommit = aoCommits[i];
+ var tsCreated = parseIsoTimestamp(oCommit.tsCreated);
+ var sUrl = oResp.sTracChangesetUrlFmt.replace('%(sRepository)s', oCommit.sRepository).replace('%(iRevision)s', oCommit.iRevision.toString());
+ var iCommitDay = Math.floor((tsCreated.getTime() + tsCreated.getTimezoneOffset()) / (24 * 60 * 60 * 1000));
+ if (iCurDay === null || iCurDay != iCommitDay)
+ {
+ if (iCurDay !== null)
+ sHtml += ' </dl>\n';
+ iCurDay = iCommitDay;
+ sHtml += ' <h3>' + vcsRevisionFormatDate(tsCreated) + ' ' + g_kasDaysOfTheWeek[tsCreated.getDay()] + '</h3>\n';
+ sHtml += ' <dl>\n';
+ }
+
+ sHtml += ' <dt id="r' + oCommit.iRevision + '">';
+ sHtml += '<a href="' + oResp.sTracChangesetUrlFmt.replace('%(iRevision)s', oCommit.iRevision.toString()) + '">';
+ /*sHtml += '<span class="vcsrevisions-time">' + escapeElem(vcsRevisionFormatTime(tsCreated)) + '</span>'
+ sHtml += ' Changeset <span class="vcsrevisions-rev">r' + oCommit.iRevision + '</span>';
+ sHtml += ' by <span class="vcsrevisions-author">' + escapeElem(oCommit.sAuthor) + '</span>'; */
+ sHtml += '<span class="vcsrevisions-time">' + escapeElem(vcsRevisionFormatTime(tsCreated)) + '</span>';
+ sHtml += ' - <span class="vcsrevisions-rev">r' + oCommit.iRevision + '</span>';
+ sHtml += ' - <span class="vcsrevisions-author">' + escapeElem(oCommit.sAuthor) + '</span>';
+ sHtml += '</a></dt>\n';
+ sHtml += ' <dd>' + escapeElem(oCommit.sMessage) + '</dd>\n';
+ }
+
+ if (iCurDay !== null)
+ sHtml += ' </dl>\n';
+ }
+ else
+ { /* TABLE variant: */
+ sHtml += '<table class="vcsrevisions-table">';
+ var iAlt = 0;
+ for (i = 0; i < cCommits; i++)
+ {
+ var oCommit = aoCommits[i];
+ var tsCreated = parseIsoTimestamp(oCommit.tsCreated);
+ var sUrl = oResp.sTracChangesetUrlFmt.replace('%(sRepository)s', oCommit.sRepository).replace('%(iRevision)s', oCommit.iRevision.toString());
+ var iCommitDay = Math.floor((tsCreated.getTime() + tsCreated.getTimezoneOffset()) / (24 * 60 * 60 * 1000));
+ if (iCurDay === null || iCurDay != iCommitDay)
+ {
+ iCurDay = iCommitDay;
+ sHtml += '<tr id="r' + oCommit.iRevision + '"><td colspan="4" class="vcsrevisions-tab-date">';
+ sHtml += vcsRevisionFormatDate(tsCreated) + ' ' + g_kasDaysOfTheWeek[tsCreated.getDay()];
+ sHtml += '</td></tr>\n';
+ sHtml += '<tr>';
+ iAlt = 0;
+ }
+ else
+ sHtml += '<tr id="r' + oCommit.iRevision + '">';
+ var sAltCls = '';
+ var sAltClsStmt = '';
+ iAlt += 1;
+ if (iAlt & 1)
+ {
+ sAltCls = ' alt';
+ sAltClsStmt = ' class="alt"';
+ }
+ sHtml += '<td class="vcsrevisions-tab-time'+sAltCls+'"><a href="' + sUrl + '">'
+ + escapeElem(vcsRevisionFormatTime(tsCreated)) + '</a></td>';
+ sHtml += '<td'+sAltClsStmt+'><a href="' + sUrl + '" class="vcsrevisions-rev' + sAltCls + '">r'
+ + oCommit.iRevision + '</a></td>';
+ sHtml += '<td'+sAltClsStmt+'><a href="' + sUrl + '" class="vcsrevisions-author' + sAltCls + '">'
+ + escapeElem(oCommit.sAuthor) + '<a></td>';
+ sHtml += '<td'+sAltClsStmt+'>' + escapeElem(oCommit.sMessage) + '</td></tr>\n';
+ }
+ sHtml += '</table>\n';
+ }
+ sHtml += '</div>\n';
+ }
+ }
+
+ oElmDst.innerHTML = sHtml;
+}
+
+/** Called by the xtracker bugdetails page. */
+function VcsRevisionsLoad(sTestMgr, oElmDst, sBugTracker, lBugNo)
+{
+ oElmDst.innerHTML = '<p>Loading VCS revisions...</p>';
+
+ var sUrl = sTestMgr + 'rest.py?sPath=vcs/bugreferences/' + sBugTracker + '/' + lBugNo;
+ var oRestReq = new XMLHttpRequest();
+ oRestReq.onreadystatechange = function() { vcsRevisionsRender(sTestMgr, oElmDst, sBugTracker, this, sUrl); }
+ oRestReq.open('GET', sUrl);
+ oRestReq.withCredentials = true;
+ /*oRestReq.setRequestHeader('Content-type', 'application/json'); - Causes CORS trouble. */
+ oRestReq.send();
+}
+
diff --git a/src/VBox/ValidationKit/testmanager/misc/Makefile.kmk b/src/VBox/ValidationKit/testmanager/misc/Makefile.kmk
new file mode 100644
index 00000000..74d882cc
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/misc/Makefile.kmk
@@ -0,0 +1,46 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/misc/htpasswd-logout b/src/VBox/ValidationKit/testmanager/misc/htpasswd-logout
new file mode 100644
index 00000000..8a36998b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/misc/htpasswd-logout
@@ -0,0 +1 @@
+logout:$apr1$OqiMc/Uv$XylAjnIPla7gb57UMW0TK.
diff --git a/src/VBox/ValidationKit/testmanager/misc/htpasswd-sample b/src/VBox/ValidationKit/testmanager/misc/htpasswd-sample
new file mode 100644
index 00000000..6b6c1b33
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/misc/htpasswd-sample
@@ -0,0 +1,2 @@
+admin:ZXHvyrLs.vCmw
+test:ClO2uu6/D7jDg
diff --git a/src/VBox/ValidationKit/testmanager/readme.txt b/src/VBox/ValidationKit/testmanager/readme.txt
new file mode 100644
index 00000000..7211c7b6
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/readme.txt
@@ -0,0 +1,125 @@
+$Id: readme.txt $
+
+Directory descriptions:
+ ./ The Test Manager.
+ ./batch/ Batch scripts to be run via cron.
+ ./cgi/ CGI scripts (we'll use standard CGI at first).
+ ./core/ The core Test Manager logic (model).
+ ./htdocs/ Files to be served directly by the web server.
+ ./htdocs/css/ Style sheets.
+ ./htdocs/images/ Graphics.
+ ./webui/ The Web User Interface (WUI) bits. (Not sure if we will
+ do model-view-controller stuff, though. Time will show.)
+
+I. Running a Test Manager instance with Docker:
+
+ - This way should be preferred to get a local Test Manager instance running
+ and is NOT meant for production use!
+
+ - Install docker-ce and docker-compose on your Linux host (not tested on
+ Windows yet). Your user must be able to run the Docker CLI (see Docker documentation).
+
+ - Type "kmk" to get the containers built, "kmk start|stop" to start/stop them
+ respectively. To start over, use "kmk clean". For having a peek into the container
+ logs, use "kmk logs".
+
+ To administrate / develop the database, an Adminer instance is running at
+ http://localhost:8080
+
+ To access the actual Test Manager instance, go to http://localhost:8080/testmanager/
+
+ - There are two ways of doing development with this setup:
+
+ a. The Test Manager source is stored inside a separate data volume called
+ "docker_vbox-testmgr-web". The source will be checked out automatically on
+ container initialization. Development then can take part within that data
+ container. The initialization script will automatically pull the sources
+ from the public OSE tree, so make sure this is what you want!
+
+ b. Edit the (hidden) .env file in this directory and change VBOX_TESTMGR_DATA
+ to point to your checked out VBox root, e.g. VBOX_TESTMGR_DATA=/path/to/VBox/trunk
+
+
+II. Steps for manually setting up a local Test Manager instance for development:
+
+ - Install apache, postgresql, python, psycopg2 (python) and pylint.
+
+ - Create the database by executing 'kmk load-testmanager-db' in
+ the './db/' subdirectory. The default psql parameters there
+ requies pg_hba.conf to specify 'trust' instead of 'peer' as the
+ authentication method for local connections.
+
+ - Use ./db/partial-db-dump.py on the production system to extract a
+ partial database dump (last 14 days).
+
+ - Use ./db/partial-db-dump.py with the --load-dump-into-database
+ parameter on the development box to load the dump.
+
+ - Configure apache using the ./apache-template-2.4.conf (see top of
+ file for details), for example:
+
+ Define TestManagerRootDir "/mnt/scratch/vbox/svn/trunk/src/VBox/ValidationKit/testmanager"
+ Define VBoxBuildOutputDir "/tmp"
+ Include "${TestManagerRootDir}/apache-template-2.4.conf"
+
+ Make sure to enable cgi (a2enmod cgi && systemctl restart apache2).
+
+ - Default htpasswd file has users a user 'admin' with password 'admin' and a
+ 'test' user with password 'test'. This isn't going to get you far if
+ you've loaded something from the production server as there is typically
+ no 'admin' user in the 'Users' table there. So, you will need to add your
+ user and a throwaway password to 'misc/htpasswd-sample' using the htpasswd
+ utility.
+
+ - Try http://localhost/testmanager/ in a browser and see if it works.
+
+
+III. OS X version of the above manual setup using MacPorts:
+
+ - sudo ports install apache2 postgresql12 postgresql12-server py38-psycopg2 py38-pylint
+ sudo port select --set python python38
+ sudo port select --set python3 python38
+ sudo port select --set pylint pylint38
+
+ Note! Replace the python 38 with the most recent one you want to use. Same
+ for the 12 in relation to postgresql.
+
+ - Do what the postgresql12-server notes says, at the time of writing:
+ sudo mkdir -p /opt/local/var/db/postgresql12/defaultdb
+ sudo chown postgres:postgres /opt/local/var/db/postgresql12/defaultdb
+ sudo su postgres -c 'cd /opt/local/var/db/postgresql12 && /opt/local/lib/postgresql12/bin/initdb -D /opt/local/var/db/postgresql12/defaultdb'
+ sudo port load postgresql12-server
+
+ Note! The postgresql12-server's config is 'trust' already, so no need to
+ edit /opt/local/var/db/postgresql12/defaultdb/pg_hba.conf there. If
+ you use a different version, please check it.
+
+ - kmk load-testmanager-db
+
+ - Creating and loading a partial database dump as detailed above.
+
+ - Configure apache:
+ - sudo joe /opt/local/etc/apache2/httpd.conf:
+ - Uncomment the line "LoadModule cgi_module...".
+ - At the end of the file add (edit paths):
+ Define TestManagerRootDir "/Users/bird/coding/vbox/svn/trunk/src/VBox/ValidationKit/testmanager"
+ Define VBoxBuildOutputDir "/tmp"
+ Include "${TestManagerRootDir}/apache-template-2.4.conf"
+ - Test the config:
+ /opt/local/sbin/apachectl -t
+ - So apache will find the right python add the following to
+ /opt/local/sbin/envvars:
+ PATH=/opt/local/bin:/opt/local/sbin:$PATH
+ export PATH
+ - Load the apache service (or reload it):
+ sudo port load apache2
+ - Give apache access to read everything under TestManagerRootDir:
+ chmod -R a:rX /Users/bird/coding/vbox/svn/trunk/src/VBox/ValidationKit/testmanager
+ MYDIR=/Users/bird/coding/vbox/svn/trunk/src/VBox/ValidationKit; while [ '!' "$MYDIR" '<' "$HOME" ]; do \
+ chmod a+x "$MYDIR"; MYDIR=`dirname $MYDIR`; done
+
+ - Fix htpasswd file as detailed above and try the url (also above).
+
+
+N.B. For developing tests (../tests/), setting up a local test manager will be
+ a complete waste of time. Just run the test drivers locally.
diff --git a/src/VBox/ValidationKit/testmanager/selftest/st1-load.pgsql b/src/VBox/ValidationKit/testmanager/selftest/st1-load.pgsql
new file mode 100644
index 00000000..af4d5ace
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/selftest/st1-load.pgsql
@@ -0,0 +1,164 @@
+-- $Id: st1-load.pgsql $
+--- @file
+-- VBox Test Manager - Self Test #1 Database Load File.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+BEGIN WORK;
+
+
+INSERT INTO Users (uid, sUsername, sEmail, sFullName, sLoginName)
+ VALUES (1112223331, 'st1', 'st1@example.org', 'self test #1', 'st1');
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test1', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest1.py', '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test1'), 1112223331, '');
+
+INSERT INTO TestGroups (uidAuthor, sName)
+ VALUES (1112223331, 'st1-testgroup');
+
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test1'),
+ 1112223331);
+
+INSERT INTO BuildSources (uidAuthor, sName, sProduct, sBranch, asTypes, asOsArches)
+ VALUES (1112223331, 'st1-src', 'st1', 'trunk',
+ ARRAY['release', 'strict'],
+ ARRAY['win.x86', 'linux.noarch', 'solaris.amd64', 'os-agnostic.sparc64', 'os-agnostic.noarch']);
+
+INSERT INTO BuildCategories (sProduct, sBranch, sType, asOsArches)
+ VALUES ('st1', 'trunk', 'release', ARRAY['os-agnostic.noarch']);
+
+INSERT INTO Builds (uidAuthor, idBuildCategory, iRevision, sVersion, sBinaries)
+ VALUES (1112223331,
+ (SELECT idBuildCategory FROM BuildCategories WHERE sProduct = 'st1' AND sBranch = 'trunk'),
+ 1234, '1.0', '');
+
+INSERT INTO SchedGroups (uidAuthor, sName, sDescription, fEnabled, idBuildSrc)
+ VALUES (1112223331, 'st1-group', 'test test #1', TRUE,
+ (SELECT idBuildSrc FROM BuildSources WHERE sName = 'st1-src') );
+
+INSERT INTO SchedGroupMembers (idSchedGroup, idTestGroup, uidAuthor)
+ VALUES ((SELECT idSchedGroup FROM SchedGroups WHERE sName = 'st1-group'),
+ (SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ 1112223331);
+
+
+-- The second test
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test2', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest2.py', '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test2'), 1112223331, '');
+
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test2'),
+ 1112223331);
+
+-- The third test
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test3', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest3.py', '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test3'), 1112223331, '');
+
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test3'),
+ 1112223331);
+
+-- The fourth thru eight tests
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test4-neg', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest4.py --test immediate-sub-tests',
+ '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test4-neg'), 1112223331, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test4-neg'),
+ 1112223331);
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test5-neg', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest4.py --test total-sub-tests',
+ '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test5-neg'), 1112223331, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test5-neg'),
+ 1112223331);
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test6-neg', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest4.py --test immediate-values',
+ '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test6-neg'), 1112223331, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test6-neg'),
+ 1112223331);
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test7-neg', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest4.py --test total-values',
+ '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test7-neg'), 1112223331, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test7-neg'),
+ 1112223331);
+
+INSERT INTO TestCases (uidAuthor, sName, fEnabled, cSecTimeout, sBaseCmd, sTestSuiteZips)
+ VALUES (1112223331, 'st1-test8-neg', TRUE, 3600, 'validationkit/tests/selftests/tdSelfTest4.py --test immediate-messages',
+ '@DOWNLOAD_BASE_URL@/VBoxValidationKit.zip');
+INSERT INTO TestCaseArgs (idTestCase, uidAuthor, sArgs)
+ VALUES ((SELECT idTestCase FROM TestCases WHERE sName = 'st1-test8-neg'), 1112223331, '');
+INSERT INTO TestGroupMembers (idTestGroup, idTestCase, uidAuthor)
+ VALUES ((SELECT idTestGroup FROM TestGroups WHERE sName = 'st1-testgroup'),
+ (SELECT idTestCase FROM TestCases WHERE sName = 'st1-test8-neg'),
+ 1112223331);
+
+COMMIT WORK;
+
diff --git a/src/VBox/ValidationKit/testmanager/selftest/st1-unload.pgsql b/src/VBox/ValidationKit/testmanager/selftest/st1-unload.pgsql
new file mode 100644
index 00000000..5fe797c3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/selftest/st1-unload.pgsql
@@ -0,0 +1,87 @@
+-- $Id: st1-unload.pgsql $
+--- @file
+-- VBox Test Manager - Self Test #1 Database Unload File.
+--
+
+--
+-- Copyright (C) 2012-2023 Oracle and/or its affiliates.
+--
+-- This file is part of VirtualBox base platform packages, as
+-- available from https://www.virtualbox.org.
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation, in version 3 of the
+-- License.
+--
+-- This program is distributed in the hope that it will be useful, but
+-- WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+-- General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, see <https://www.gnu.org/licenses>.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+-- in the VirtualBox distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+-- SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+--
+
+
+
+\set ON_ERROR_STOP 1
+\connect testmanager;
+
+BEGIN WORK;
+
+DELETE FROM TestBoxStatuses;
+DELETE FROM SchedQueues;
+
+DELETE FROM SchedGroupMembers WHERE uidAuthor = 1112223331;
+UPDATE TestBoxes SET idSchedGroup = 1 WHERE idSchedGroup IN ( SELECT idSchedGroup FROM SchedGroups WHERE uidAuthor = 1112223331 );
+DELETE FROM SchedGroups WHERE uidAuthor = 1112223331 OR sName = 'st1-group';
+
+UPDATE TestSets SET idTestResult = NULL
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases WHERE uidAuthor = 1112223331 );
+
+DELETE FROM TestResultValues
+ WHERE idTestResult IN ( SELECT idTestResult FROM TestResults
+ WHERE idTestSet IN ( SELECT idTestSet FROM TestSets
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases
+ WHERE uidAuthor = 1112223331 ) ) );
+DELETE FROM TestResultFiles
+ WHERE idTestResult IN ( SELECT idTestResult FROM TestResults
+ WHERE idTestSet IN ( SELECT idTestSet FROM TestSets
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases
+ WHERE uidAuthor = 1112223331 ) ) );
+DELETE FROM TestResultMsgs
+ WHERE idTestResult IN ( SELECT idTestResult FROM TestResults
+ WHERE idTestSet IN ( SELECT idTestSet FROM TestSets
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases
+ WHERE uidAuthor = 1112223331 ) ) );
+DELETE FROM TestResults
+ WHERE idTestSet IN ( SELECT idTestSet FROM TestSets
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases WHERE uidAuthor = 1112223331 ) );
+DELETE FROM TestSets
+ WHERE idTestCase IN ( SELECT idTestCase FROM TestCases WHERE uidAuthor = 1112223331 );
+
+DELETE FROM TestCases WHERE uidAuthor = 1112223331;
+DELETE FROM TestCaseArgs WHERE uidAuthor = 1112223331;
+DELETE FROM TestGroups WHERE uidAuthor = 1112223331 OR sName = 'st1-testgroup';
+DELETE FROM TestGroupMembers WHERE uidAuthor = 1112223331;
+
+DELETE FROM BuildSources WHERE uidAuthor = 1112223331;
+DELETE FROM Builds WHERE uidAuthor = 1112223331;
+DELETE FROM BuildCategories WHERE sProduct = 'st1';
+
+DELETE FROM Users WHERE uid = 1112223331;
+
+COMMIT WORK;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/Makefile.kmk b/src/VBox/ValidationKit/testmanager/webui/Makefile.kmk
new file mode 100644
index 00000000..5a9b58bd
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/Makefile.kmk
@@ -0,0 +1,47 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py)
+VBOX_VALIDATIONKIT_PYUNITTEST_EXCLUDE += $(PATH_SUB_CURRENT)/wuihlpgraphmatplotlib.py
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+$(evalcall def_vbox_validationkit_process_js_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/__init__.py b/src/VBox/ValidationKit/testmanager/webui/__init__.py
new file mode 100644
index 00000000..d00f5436
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+TestBox Script - WUI Presentation.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/template-details.html b/src/VBox/ValidationKit/testmanager/webui/template-details.html
new file mode 100644
index 00000000..e7e16c0f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/template-details.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="content-language" content="en" />
+ <meta name="language" content="en" />
+ <link href="htdocs/images/tmfavicon.ico" rel="shortcut icon" type="image/x-icon" />
+ <link href="htdocs/images/tmfavicon.ico" rel="icon" type="image/x-icon" />
+ <link href="htdocs/css/common.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/tooltip.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/details.css" rel="stylesheet" type="text/css" media="screen" />
+ <script type="text/javascript" src="htdocs/js/common.js"></script>
+ <title>@@PAGE_TITLE@@</title>
+ </head>
+
+ <body>
+ <div id="wrap">
+ <div id="head-wrap">
+ <div id="logo">
+ <img alt ="VirtualBox" src="htdocs/images/VirtualBox.svg">
+ </div>
+ <div id="header">
+ <h1>@@PAGE_TITLE@@</h1>
+ </div>
+ <div id="top-menu" class="tm-top-menu-wo-side">
+ <ul>
+ @@TOP_MENU_ITEMS@@
+ </ul>
+ </div>
+ <div id="login">
+ <p><small>
+ Logged in as <b>@@USER_NAME@@</b>@@LOG_OUT@@
+ </small></p>
+ </div>
+ </div>
+
+ <div id="main">
+ @@PAGE_BODY@@
+
+ @@DEBUG@@
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/template-graphwiz.html b/src/VBox/ValidationKit/testmanager/webui/template-graphwiz.html
new file mode 100644
index 00000000..4e1dc0c8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/template-graphwiz.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="content-language" content="en" />
+ <meta name="language" content="en" />
+ <link href="htdocs/images/tmfavicon.ico" rel="shortcut icon" type="image/x-icon" />
+ <link href="htdocs/images/tmfavicon.ico" rel="icon" type="image/x-icon" />
+ <link href="htdocs/css/common.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/tooltip.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/graphwiz.css" rel="stylesheet" type="text/css" media="screen" />
+ <script type="text/javascript" src="htdocs/js/common.js"></script>
+ <script type="text/javascript" src="htdocs/js/graphwiz.js"></script>
+ <title>@@PAGE_TITLE@@</title>
+ </head>
+
+ <body>
+ <div id="wrap">
+ <div id="head-wrap">
+ <div id="logo">
+ <img alt ="VirtualBox" src="htdocs/images/VirtualBox.svg">
+ </div>
+ <div id="header">
+ <h1>@@PAGE_TITLE@@</h1>
+ </div>
+ <div id="top-menu" class="tm-top-menu-wo-side">
+ <ul>
+ @@TOP_MENU_ITEMS@@
+ </ul>
+ </div>
+ <div id="login">
+ <p><small>
+ Logged in as <b>@@USER_NAME@@</b>@@LOG_OUT@@
+ </small></p>
+ </div>
+ </div>
+
+ <div id="main">
+ @@PAGE_BODY@@
+
+ @@DEBUG@@
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/template-tooltip.html b/src/VBox/ValidationKit/testmanager/webui/template-tooltip.html
new file mode 100644
index 00000000..7aa95d71
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/template-tooltip.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="content-language" content="en" />
+ <meta name="language" content="en" />
+ <link href="htdocs/css/common.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/tooltip.css" rel="stylesheet" type="text/css" media="screen" />
+ <title>@@PAGE_TITLE@@</title>
+</head>
+
+<body scroll="no">
+<div id="tooltip" class="tooltip-main">
+<div id="tooltip-inner" class="tooltip-inner">
+@@PAGE_BODY@@
+</div>
+</div>
+</body>
+</html>
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/template.html b/src/VBox/ValidationKit/testmanager/webui/template.html
new file mode 100644
index 00000000..6480c20b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/template.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="content-language" content="en" />
+ <meta name="language" content="en" />
+ <link href="htdocs/images/tmfavicon.ico" rel="shortcut icon" />
+ <link href="htdocs/images/tmfavicon.ico" rel="icon" type="image/x-icon" />
+ <link href="htdocs/css/common.css" rel="stylesheet" type="text/css" media="screen" />
+ <link href="htdocs/css/tooltip.css" rel="stylesheet" type="text/css" media="screen" />
+ <script type="text/javascript" src="htdocs/js/common.js"></script>
+ <title>@@PAGE_TITLE@@</title>
+ </head>
+
+ <body>
+ <div id="wrap">
+ <div id="head-wrap">
+ <div id="logo">
+ <img alt ="VirtualBox" src="htdocs/images/VirtualBox.svg">
+ </div>
+ <div id="header">
+ <h1>@@PAGE_TITLE@@</h1>
+ </div>
+ <div id="login">
+ <p><small>
+ Logged in as <b>@@USER_NAME@@</b>@@LOG_OUT@@
+ </small></p>
+ </div>
+ <div id="top-menu">
+ <ul>
+ @@TOP_MENU_ITEMS@@
+ </ul>
+ </div>
+ </div>
+
+ <div id="side-menu-wrap">
+ <div id="side-menu">
+ <div id="side-menu-body">
+ <form id="side-menu-form" @@SIDE_MENU_FORM_ATTRS@@>
+ <ul>
+ @@SIDE_MENU_ITEMS@@
+ </ul>
+ @@SIDE_FILTER_CONTROL@@
+ </form>
+ </div>
+ <!-- justify-content: space-between -->
+ <div id="side-footer">
+ <p>
+ VBox Test Manager<br/>@@TESTMANAGER_VERSION@@r@@TESTMANAGER_REVISION@@
+ </p>
+ <p>Copyright &copy; 2012-2023 Oracle Corporation</p>
+ </div>
+ </div>
+ </div>
+
+ <div id="main">
+ @@PAGE_BODY@@
+
+ @@DEBUG@@
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py b/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py
new file mode 100755
index 00000000..ef5c7669
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py
@@ -0,0 +1,1270 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadmin.py $
+
+"""
+Test Manager Core - WUI - Admin Main page.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import cgitb;
+import sys;
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager import config;
+from testmanager.webui.wuibase import WuiDispatcherBase, WuiException
+
+
+class WuiAdmin(WuiDispatcherBase):
+ """
+ WUI Admin main page.
+ """
+
+ ## The name of the script.
+ ksScriptName = 'admin.py'
+
+ ## Number of days back.
+ ksParamDaysBack = 'cDaysBack';
+
+ ## @name Actions
+ ## @{
+ ksActionSystemLogList = 'SystemLogList'
+ ksActionSystemChangelogList = 'SystemChangelogList'
+ ksActionSystemDbDump = 'SystemDbDump'
+ ksActionSystemDbDumpDownload = 'SystemDbDumpDownload'
+
+ ksActionUserList = 'UserList'
+ ksActionUserAdd = 'UserAdd'
+ ksActionUserAddPost = 'UserAddPost'
+ ksActionUserEdit = 'UserEdit'
+ ksActionUserEditPost = 'UserEditPost'
+ ksActionUserDelPost = 'UserDelPost'
+ ksActionUserDetails = 'UserDetails'
+
+ ksActionTestBoxList = 'TestBoxList'
+ ksActionTestBoxListPost = 'TestBoxListPost'
+ ksActionTestBoxAdd = 'TestBoxAdd'
+ ksActionTestBoxAddPost = 'TestBoxAddPost'
+ ksActionTestBoxEdit = 'TestBoxEdit'
+ ksActionTestBoxEditPost = 'TestBoxEditPost'
+ ksActionTestBoxDetails = 'TestBoxDetails'
+ ksActionTestBoxRemovePost = 'TestBoxRemove'
+ ksActionTestBoxesRegenQueues = 'TestBoxesRegenQueues';
+
+ ksActionTestCaseList = 'TestCaseList'
+ ksActionTestCaseAdd = 'TestCaseAdd'
+ ksActionTestCaseAddPost = 'TestCaseAddPost'
+ ksActionTestCaseClone = 'TestCaseClone'
+ ksActionTestCaseDetails = 'TestCaseDetails'
+ ksActionTestCaseEdit = 'TestCaseEdit'
+ ksActionTestCaseEditPost = 'TestCaseEditPost'
+ ksActionTestCaseDoRemove = 'TestCaseDoRemove'
+
+ ksActionGlobalRsrcShowAll = 'GlobalRsrcShowAll'
+ ksActionGlobalRsrcShowAdd = 'GlobalRsrcShowAdd'
+ ksActionGlobalRsrcShowEdit = 'GlobalRsrcShowEdit'
+ ksActionGlobalRsrcAdd = 'GlobalRsrcAddPost'
+ ksActionGlobalRsrcEdit = 'GlobalRsrcEditPost'
+ ksActionGlobalRsrcDel = 'GlobalRsrcDelPost'
+
+ ksActionBuildList = 'BuildList'
+ ksActionBuildAdd = 'BuildAdd'
+ ksActionBuildAddPost = 'BuildAddPost'
+ ksActionBuildClone = 'BuildClone'
+ ksActionBuildDetails = 'BuildDetails'
+ ksActionBuildDoRemove = 'BuildDoRemove'
+ ksActionBuildEdit = 'BuildEdit'
+ ksActionBuildEditPost = 'BuildEditPost'
+
+ ksActionBuildBlacklist = 'BuildBlacklist';
+ ksActionBuildBlacklistAdd = 'BuildBlacklistAdd';
+ ksActionBuildBlacklistAddPost = 'BuildBlacklistAddPost';
+ ksActionBuildBlacklistClone = 'BuildBlacklistClone';
+ ksActionBuildBlacklistDetails = 'BuildBlacklistDetails';
+ ksActionBuildBlacklistDoRemove = 'BuildBlacklistDoRemove';
+ ksActionBuildBlacklistEdit = 'BuildBlacklistEdit';
+ ksActionBuildBlacklistEditPost = 'BuildBlacklistEditPost';
+
+ ksActionFailureCategoryList = 'FailureCategoryList';
+ ksActionFailureCategoryAdd = 'FailureCategoryAdd';
+ ksActionFailureCategoryAddPost = 'FailureCategoryAddPost';
+ ksActionFailureCategoryDetails = 'FailureCategoryDetails';
+ ksActionFailureCategoryDoRemove = 'FailureCategoryDoRemove';
+ ksActionFailureCategoryEdit = 'FailureCategoryEdit';
+ ksActionFailureCategoryEditPost = 'FailureCategoryEditPost';
+
+ ksActionFailureReasonList = 'FailureReasonList'
+ ksActionFailureReasonAdd = 'FailureReasonAdd'
+ ksActionFailureReasonAddPost = 'FailureReasonAddPost'
+ ksActionFailureReasonDetails = 'FailureReasonDetails'
+ ksActionFailureReasonDoRemove = 'FailureReasonDoRemove'
+ ksActionFailureReasonEdit = 'FailureReasonEdit'
+ ksActionFailureReasonEditPost = 'FailureReasonEditPost'
+
+ ksActionBuildSrcList = 'BuildSrcList'
+ ksActionBuildSrcAdd = 'BuildSrcAdd'
+ ksActionBuildSrcAddPost = 'BuildSrcAddPost'
+ ksActionBuildSrcClone = 'BuildSrcClone'
+ ksActionBuildSrcDetails = 'BuildSrcDetails'
+ ksActionBuildSrcEdit = 'BuildSrcEdit'
+ ksActionBuildSrcEditPost = 'BuildSrcEditPost'
+ ksActionBuildSrcDoRemove = 'BuildSrcDoRemove'
+
+ ksActionBuildCategoryList = 'BuildCategoryList'
+ ksActionBuildCategoryAdd = 'BuildCategoryAdd'
+ ksActionBuildCategoryAddPost = 'BuildCategoryAddPost'
+ ksActionBuildCategoryClone = 'BuildCategoryClone';
+ ksActionBuildCategoryDetails = 'BuildCategoryDetails';
+ ksActionBuildCategoryDoRemove = 'BuildCategoryDoRemove';
+
+ ksActionTestGroupList = 'TestGroupList'
+ ksActionTestGroupAdd = 'TestGroupAdd'
+ ksActionTestGroupAddPost = 'TestGroupAddPost'
+ ksActionTestGroupClone = 'TestGroupClone'
+ ksActionTestGroupDetails = 'TestGroupDetails'
+ ksActionTestGroupDoRemove = 'TestGroupDoRemove'
+ ksActionTestGroupEdit = 'TestGroupEdit'
+ ksActionTestGroupEditPost = 'TestGroupEditPost'
+ ksActionTestCfgRegenQueues = 'TestCfgRegenQueues'
+
+ ksActionSchedGroupList = 'SchedGroupList'
+ ksActionSchedGroupAdd = 'SchedGroupAdd';
+ ksActionSchedGroupAddPost = 'SchedGroupAddPost';
+ ksActionSchedGroupClone = 'SchedGroupClone';
+ ksActionSchedGroupDetails = 'SchedGroupDetails';
+ ksActionSchedGroupDoRemove = 'SchedGroupDel';
+ ksActionSchedGroupEdit = 'SchedGroupEdit';
+ ksActionSchedGroupEditPost = 'SchedGroupEditPost';
+ ksActionSchedQueueList = 'SchedQueueList';
+ ## @}
+
+ def __init__(self, oSrvGlue): # pylint: disable=too-many-locals,too-many-statements
+ WuiDispatcherBase.__init__(self, oSrvGlue, self.ksScriptName);
+ self._sTemplate = 'template.html';
+
+
+ #
+ # System actions.
+ #
+ self._dDispatch[self.ksActionSystemChangelogList] = self._actionSystemChangelogList;
+ self._dDispatch[self.ksActionSystemLogList] = self._actionSystemLogList;
+ self._dDispatch[self.ksActionSystemDbDump] = self._actionSystemDbDump;
+ self._dDispatch[self.ksActionSystemDbDumpDownload] = self._actionSystemDbDumpDownload;
+
+ #
+ # User Account actions.
+ #
+ self._dDispatch[self.ksActionUserList] = self._actionUserList;
+ self._dDispatch[self.ksActionUserAdd] = self._actionUserAdd;
+ self._dDispatch[self.ksActionUserEdit] = self._actionUserEdit;
+ self._dDispatch[self.ksActionUserAddPost] = self._actionUserAddPost;
+ self._dDispatch[self.ksActionUserEditPost] = self._actionUserEditPost;
+ self._dDispatch[self.ksActionUserDetails] = self._actionUserDetails;
+ self._dDispatch[self.ksActionUserDelPost] = self._actionUserDelPost;
+
+ #
+ # TestBox actions.
+ #
+ self._dDispatch[self.ksActionTestBoxList] = self._actionTestBoxList;
+ self._dDispatch[self.ksActionTestBoxListPost] = self._actionTestBoxListPost;
+ self._dDispatch[self.ksActionTestBoxAdd] = self._actionTestBoxAdd;
+ self._dDispatch[self.ksActionTestBoxAddPost] = self._actionTestBoxAddPost;
+ self._dDispatch[self.ksActionTestBoxDetails] = self._actionTestBoxDetails;
+ self._dDispatch[self.ksActionTestBoxEdit] = self._actionTestBoxEdit;
+ self._dDispatch[self.ksActionTestBoxEditPost] = self._actionTestBoxEditPost;
+ self._dDispatch[self.ksActionTestBoxRemovePost] = self._actionTestBoxRemovePost;
+ self._dDispatch[self.ksActionTestBoxesRegenQueues] = self._actionRegenQueuesCommon;
+
+ #
+ # Test Case actions.
+ #
+ self._dDispatch[self.ksActionTestCaseList] = self._actionTestCaseList;
+ self._dDispatch[self.ksActionTestCaseAdd] = self._actionTestCaseAdd;
+ self._dDispatch[self.ksActionTestCaseAddPost] = self._actionTestCaseAddPost;
+ self._dDispatch[self.ksActionTestCaseClone] = self._actionTestCaseClone;
+ self._dDispatch[self.ksActionTestCaseDetails] = self._actionTestCaseDetails;
+ self._dDispatch[self.ksActionTestCaseEdit] = self._actionTestCaseEdit;
+ self._dDispatch[self.ksActionTestCaseEditPost] = self._actionTestCaseEditPost;
+ self._dDispatch[self.ksActionTestCaseDoRemove] = self._actionTestCaseDoRemove;
+
+ #
+ # Global Resource actions
+ #
+ self._dDispatch[self.ksActionGlobalRsrcShowAll] = self._actionGlobalRsrcShowAll;
+ self._dDispatch[self.ksActionGlobalRsrcShowAdd] = self._actionGlobalRsrcShowAdd;
+ self._dDispatch[self.ksActionGlobalRsrcShowEdit] = self._actionGlobalRsrcShowEdit;
+ self._dDispatch[self.ksActionGlobalRsrcAdd] = self._actionGlobalRsrcAdd;
+ self._dDispatch[self.ksActionGlobalRsrcEdit] = self._actionGlobalRsrcEdit;
+ self._dDispatch[self.ksActionGlobalRsrcDel] = self._actionGlobalRsrcDel;
+
+ #
+ # Build Source actions
+ #
+ self._dDispatch[self.ksActionBuildSrcList] = self._actionBuildSrcList;
+ self._dDispatch[self.ksActionBuildSrcAdd] = self._actionBuildSrcAdd;
+ self._dDispatch[self.ksActionBuildSrcAddPost] = self._actionBuildSrcAddPost;
+ self._dDispatch[self.ksActionBuildSrcClone] = self._actionBuildSrcClone;
+ self._dDispatch[self.ksActionBuildSrcDetails] = self._actionBuildSrcDetails;
+ self._dDispatch[self.ksActionBuildSrcDoRemove] = self._actionBuildSrcDoRemove;
+ self._dDispatch[self.ksActionBuildSrcEdit] = self._actionBuildSrcEdit;
+ self._dDispatch[self.ksActionBuildSrcEditPost] = self._actionBuildSrcEditPost;
+
+ #
+ # Build actions
+ #
+ self._dDispatch[self.ksActionBuildList] = self._actionBuildList;
+ self._dDispatch[self.ksActionBuildAdd] = self._actionBuildAdd;
+ self._dDispatch[self.ksActionBuildAddPost] = self._actionBuildAddPost;
+ self._dDispatch[self.ksActionBuildClone] = self._actionBuildClone;
+ self._dDispatch[self.ksActionBuildDetails] = self._actionBuildDetails;
+ self._dDispatch[self.ksActionBuildDoRemove] = self._actionBuildDoRemove;
+ self._dDispatch[self.ksActionBuildEdit] = self._actionBuildEdit;
+ self._dDispatch[self.ksActionBuildEditPost] = self._actionBuildEditPost;
+
+ #
+ # Build Black List actions
+ #
+ self._dDispatch[self.ksActionBuildBlacklist] = self._actionBuildBlacklist;
+ self._dDispatch[self.ksActionBuildBlacklistAdd] = self._actionBuildBlacklistAdd;
+ self._dDispatch[self.ksActionBuildBlacklistAddPost] = self._actionBuildBlacklistAddPost;
+ self._dDispatch[self.ksActionBuildBlacklistClone] = self._actionBuildBlacklistClone;
+ self._dDispatch[self.ksActionBuildBlacklistDetails] = self._actionBuildBlacklistDetails;
+ self._dDispatch[self.ksActionBuildBlacklistDoRemove] = self._actionBuildBlacklistDoRemove;
+ self._dDispatch[self.ksActionBuildBlacklistEdit] = self._actionBuildBlacklistEdit;
+ self._dDispatch[self.ksActionBuildBlacklistEditPost] = self._actionBuildBlacklistEditPost;
+
+ #
+ # Failure Category actions
+ #
+ self._dDispatch[self.ksActionFailureCategoryList] = self._actionFailureCategoryList;
+ self._dDispatch[self.ksActionFailureCategoryAdd] = self._actionFailureCategoryAdd;
+ self._dDispatch[self.ksActionFailureCategoryAddPost] = self._actionFailureCategoryAddPost;
+ self._dDispatch[self.ksActionFailureCategoryDetails] = self._actionFailureCategoryDetails;
+ self._dDispatch[self.ksActionFailureCategoryDoRemove] = self._actionFailureCategoryDoRemove;
+ self._dDispatch[self.ksActionFailureCategoryEdit] = self._actionFailureCategoryEdit;
+ self._dDispatch[self.ksActionFailureCategoryEditPost] = self._actionFailureCategoryEditPost;
+
+ #
+ # Failure Reason actions
+ #
+ self._dDispatch[self.ksActionFailureReasonList] = self._actionFailureReasonList;
+ self._dDispatch[self.ksActionFailureReasonAdd] = self._actionFailureReasonAdd;
+ self._dDispatch[self.ksActionFailureReasonAddPost] = self._actionFailureReasonAddPost;
+ self._dDispatch[self.ksActionFailureReasonDetails] = self._actionFailureReasonDetails;
+ self._dDispatch[self.ksActionFailureReasonDoRemove] = self._actionFailureReasonDoRemove;
+ self._dDispatch[self.ksActionFailureReasonEdit] = self._actionFailureReasonEdit;
+ self._dDispatch[self.ksActionFailureReasonEditPost] = self._actionFailureReasonEditPost;
+
+ #
+ # Build Category actions
+ #
+ self._dDispatch[self.ksActionBuildCategoryList] = self._actionBuildCategoryList;
+ self._dDispatch[self.ksActionBuildCategoryAdd] = self._actionBuildCategoryAdd;
+ self._dDispatch[self.ksActionBuildCategoryAddPost] = self._actionBuildCategoryAddPost;
+ self._dDispatch[self.ksActionBuildCategoryClone] = self._actionBuildCategoryClone;
+ self._dDispatch[self.ksActionBuildCategoryDetails] = self._actionBuildCategoryDetails;
+ self._dDispatch[self.ksActionBuildCategoryDoRemove] = self._actionBuildCategoryDoRemove;
+
+ #
+ # Test Group actions
+ #
+ self._dDispatch[self.ksActionTestGroupList] = self._actionTestGroupList;
+ self._dDispatch[self.ksActionTestGroupAdd] = self._actionTestGroupAdd;
+ self._dDispatch[self.ksActionTestGroupAddPost] = self._actionTestGroupAddPost;
+ self._dDispatch[self.ksActionTestGroupClone] = self._actionTestGroupClone;
+ self._dDispatch[self.ksActionTestGroupDetails] = self._actionTestGroupDetails;
+ self._dDispatch[self.ksActionTestGroupEdit] = self._actionTestGroupEdit;
+ self._dDispatch[self.ksActionTestGroupEditPost] = self._actionTestGroupEditPost;
+ self._dDispatch[self.ksActionTestGroupDoRemove] = self._actionTestGroupDoRemove;
+ self._dDispatch[self.ksActionTestCfgRegenQueues] = self._actionRegenQueuesCommon;
+
+ #
+ # Scheduling Group and Queue actions
+ #
+ self._dDispatch[self.ksActionSchedGroupList] = self._actionSchedGroupList;
+ self._dDispatch[self.ksActionSchedGroupAdd] = self._actionSchedGroupAdd;
+ self._dDispatch[self.ksActionSchedGroupClone] = self._actionSchedGroupClone;
+ self._dDispatch[self.ksActionSchedGroupDetails] = self._actionSchedGroupDetails;
+ self._dDispatch[self.ksActionSchedGroupEdit] = self._actionSchedGroupEdit;
+ self._dDispatch[self.ksActionSchedGroupAddPost] = self._actionSchedGroupAddPost;
+ self._dDispatch[self.ksActionSchedGroupEditPost] = self._actionSchedGroupEditPost;
+ self._dDispatch[self.ksActionSchedGroupDoRemove] = self._actionSchedGroupDoRemove;
+ self._dDispatch[self.ksActionSchedQueueList] = self._actionSchedQueueList;
+
+
+ #
+ # Menus
+ #
+ self._aaoMenus = \
+ [
+ [
+ 'Builds', self._sActionUrlBase + self.ksActionBuildList,
+ [
+ [ 'Builds', self._sActionUrlBase + self.ksActionBuildList, False ],
+ [ 'Blacklist', self._sActionUrlBase + self.ksActionBuildBlacklist, False ],
+ [ 'Build sources', self._sActionUrlBase + self.ksActionBuildSrcList, False ],
+ [ 'Build categories', self._sActionUrlBase + self.ksActionBuildCategoryList, False ],
+ [ 'New build', self._sActionUrlBase + self.ksActionBuildAdd, True ],
+ [ 'New blacklisting', self._sActionUrlBase + self.ksActionBuildBlacklistAdd, True ],
+ [ 'New build source', self._sActionUrlBase + self.ksActionBuildSrcAdd, True ],
+ [ 'New build category', self._sActionUrlBase + self.ksActionBuildCategoryAdd, True ],
+ ]
+ ],
+ [
+ 'Failure Reasons', self._sActionUrlBase + self.ksActionFailureReasonList,
+ [
+ [ 'Failure categories', self._sActionUrlBase + self.ksActionFailureCategoryList, False ],
+ [ 'Failure reasons', self._sActionUrlBase + self.ksActionFailureReasonList, False ],
+ [ 'New failure category', self._sActionUrlBase + self.ksActionFailureCategoryAdd, True ],
+ [ 'New failure reason', self._sActionUrlBase + self.ksActionFailureReasonAdd, True ],
+ ]
+ ],
+ [
+ 'System', self._sActionUrlBase + self.ksActionSystemChangelogList,
+ [
+ [ 'Changelog', self._sActionUrlBase + self.ksActionSystemChangelogList, False ],
+ [ 'System log', self._sActionUrlBase + self.ksActionSystemLogList, False ],
+ [ 'Partial DB Dump', self._sActionUrlBase + self.ksActionSystemDbDump, False ],
+ [ 'User accounts', self._sActionUrlBase + self.ksActionUserList, False ],
+ [ 'New user', self._sActionUrlBase + self.ksActionUserAdd, True ],
+ ]
+ ],
+ [
+ 'Testboxes', self._sActionUrlBase + self.ksActionTestBoxList,
+ [
+ [ 'Testboxes', self._sActionUrlBase + self.ksActionTestBoxList, False ],
+ [ 'Scheduling groups', self._sActionUrlBase + self.ksActionSchedGroupList, False ],
+ [ 'New testbox', self._sActionUrlBase + self.ksActionTestBoxAdd, True ],
+ [ 'New scheduling group', self._sActionUrlBase + self.ksActionSchedGroupAdd, True ],
+ [ 'View scheduling queues', self._sActionUrlBase + self.ksActionSchedQueueList, False ],
+ [ 'Regenerate all scheduling queues', self._sActionUrlBase + self.ksActionTestBoxesRegenQueues, True ],
+ ]
+ ],
+ [
+ 'Test Config', self._sActionUrlBase + self.ksActionTestGroupList,
+ [
+ [ 'Test cases', self._sActionUrlBase + self.ksActionTestCaseList, False ],
+ [ 'Test groups', self._sActionUrlBase + self.ksActionTestGroupList, False ],
+ [ 'Global resources', self._sActionUrlBase + self.ksActionGlobalRsrcShowAll, False ],
+ [ 'New test case', self._sActionUrlBase + self.ksActionTestCaseAdd, True ],
+ [ 'New test group', self._sActionUrlBase + self.ksActionTestGroupAdd, True ],
+ [ 'New global resource', self._sActionUrlBase + self.ksActionGlobalRsrcShowAdd, True ],
+ [ 'Regenerate all scheduling queues', self._sActionUrlBase + self.ksActionTestCfgRegenQueues, True ],
+ ]
+ ],
+ [
+ '> Test Results', 'index.py?' + webutils.encodeUrlParams(self._dDbgParams), []
+ ],
+ ];
+
+
+ def _actionDefault(self):
+ """Show the default admin page."""
+ self._sAction = self.ksActionTestBoxList;
+ from testmanager.core.testbox import TestBoxLogic;
+ from testmanager.webui.wuiadmintestbox import WuiTestBoxList;
+ return self._actionGenericListing(TestBoxLogic, WuiTestBoxList);
+
+ def _actionGenericDoDelOld(self, oCoreObjectLogic, sCoreObjectIdFieldName, sRedirectAction):
+ """
+ Delete entry (using oLogicType.remove).
+
+ @param oCoreObjectLogic A *Logic class
+
+ @param sCoreObjectIdFieldName Name of HTTP POST variable that
+ contains object ID information
+
+ @param sRedirectAction An action for redirect user to
+ in case of operation success
+ """
+ iCoreDataObjectId = self.getStringParam(sCoreObjectIdFieldName) # STRING?!?!
+ self._checkForUnknownParameters()
+
+ try:
+ self._sPageTitle = None
+ self._sPageBody = None
+ self._sRedirectTo = self._sActionUrlBase + sRedirectAction
+ return oCoreObjectLogic(self._oDb).remove(self._oCurUser.uid, iCoreDataObjectId)
+ except Exception as oXcpt:
+ self._oDb.rollback();
+ self._sPageTitle = 'Unable to delete record'
+ self._sPageBody = str(oXcpt);
+ if config.g_kfDebugDbXcpt:
+ self._sPageBody += cgitb.html(sys.exc_info());
+ self._sRedirectTo = None
+
+ return False
+
+
+ #
+ # System Category.
+ #
+
+ # System wide changelog actions.
+
+ def _actionSystemChangelogList(self):
+ """ Action handler. """
+ from testmanager.core.systemchangelog import SystemChangelogLogic;
+ from testmanager.webui.wuiadminsystemchangelog import WuiAdminSystemChangelogList;
+
+ tsEffective = self.getEffectiveDateParam();
+ cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 384);
+ iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0);
+ cDaysBack = self.getIntParam(self.ksParamDaysBack, iMin = 1, iMax = 366, iDefault = 14);
+ self._checkForUnknownParameters();
+
+ aoEntries = SystemChangelogLogic(self._oDb).fetchForListingEx(iPage * cItemsPerPage, cItemsPerPage + 1,
+ tsEffective, cDaysBack);
+ oContent = WuiAdminSystemChangelogList(aoEntries, iPage, cItemsPerPage, tsEffective,
+ cDaysBack = cDaysBack, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ return True;
+
+ # System Log actions.
+
+ def _actionSystemLogList(self):
+ """ Action wrapper. """
+ from testmanager.core.systemlog import SystemLogLogic;
+ from testmanager.webui.wuiadminsystemlog import WuiAdminSystemLogList;
+ return self._actionGenericListing(SystemLogLogic, WuiAdminSystemLogList)
+
+ def _actionSystemDbDump(self):
+ """ Action handler. """
+ from testmanager.webui.wuiadminsystemdbdump import WuiAdminSystemDbDumpForm;
+
+ cDaysBack = self.getIntParam(self.ksParamDaysBack, iMin = config.g_kcTmDbDumpMinDays,
+ iMax = config.g_kcTmDbDumpMaxDays, iDefault = config.g_kcTmDbDumpDefaultDays);
+ self._checkForUnknownParameters();
+
+ oContent = WuiAdminSystemDbDumpForm(cDaysBack, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.showForm();
+ return True;
+
+ def _actionSystemDbDumpDownload(self):
+ """ Action handler. """
+ import datetime;
+ import os;
+
+ cDaysBack = self.getIntParam(self.ksParamDaysBack, iMin = config.g_kcTmDbDumpMinDays,
+ iMax = config.g_kcTmDbDumpMaxDays, iDefault = config.g_kcTmDbDumpDefaultDays);
+ self._checkForUnknownParameters();
+
+ #
+ # Generate the dump.
+ #
+ # We generate a file name that's unique to a user is smart enough to only
+ # issue one of these requests at the time. This also makes sure we won't
+ # waste too much space should this code get interrupted and rerun.
+ #
+ oFile = None;
+ oNow = datetime.datetime.utcnow();
+ sOutFile = config.g_ksTmDbDumpOutFileTmpl % (self._oCurUser.uid,);
+ sTmpFile = config.g_ksTmDbDumpTmpFileTmpl % (self._oCurUser.uid,);
+ sScript = os.path.join(config.g_ksTestManagerDir, 'db', 'partial-db-dump.py');
+ try:
+ (iExitCode, sStdOut, sStdErr) = utils.processOutputUnchecked([ sScript,
+ '--days-to-dump', str(cDaysBack),
+ '-f', sOutFile,
+ '-t', sTmpFile,
+ ]);
+ if iExitCode != 0:
+ raise Exception('iExitCode=%s\n--- stderr ---\n%s\n--- stdout ---\n%s' % (iExitCode, sStdOut, sStdErr,));
+
+ #
+ # Open and send the dump.
+ #
+ oFile = open(sOutFile, 'rb'); # pylint: disable=consider-using-with
+ cbFile = os.fstat(oFile.fileno()).st_size;
+
+ self._oSrvGlue.setHeaderField('Content-Type', 'application/zip');
+ self._oSrvGlue.setHeaderField('Content-Disposition',
+ oNow.strftime('attachment; filename="partial-db-dump-%Y-%m-%dT%H-%M-%S.zip"'));
+ self._oSrvGlue.setHeaderField('Content-Length', str(cbFile));
+
+ while True:
+ abChunk = oFile.read(262144);
+ if not abChunk:
+ break;
+ self._oSrvGlue.writeRaw(abChunk);
+
+ finally:
+ # Delete the file to save space.
+ if oFile:
+ try: oFile.close();
+ except: pass;
+ utils.noxcptDeleteFile(sOutFile);
+ utils.noxcptDeleteFile(sTmpFile);
+ return self.ksDispatchRcAllDone;
+
+
+ # User Account actions.
+
+ def _actionUserList(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountLogic;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccountList;
+ return self._actionGenericListing(UserAccountLogic, WuiUserAccountList)
+
+ def _actionUserAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccount;
+ return self._actionGenericFormAdd(UserAccountData, WuiUserAccount)
+
+ def _actionUserDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData, UserAccountLogic;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccount;
+ return self._actionGenericFormDetails(UserAccountData, UserAccountLogic, WuiUserAccount, 'uid');
+
+ def _actionUserEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccount;
+ return self._actionGenericFormEdit(UserAccountData, WuiUserAccount, UserAccountData.ksParam_uid);
+
+ def _actionUserAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData, UserAccountLogic;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccount;
+ return self._actionGenericFormAddPost(UserAccountData, UserAccountLogic, WuiUserAccount, self.ksActionUserList)
+
+ def _actionUserEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData, UserAccountLogic;
+ from testmanager.webui.wuiadminuseraccount import WuiUserAccount;
+ return self._actionGenericFormEditPost(UserAccountData, UserAccountLogic, WuiUserAccount, self.ksActionUserList)
+
+ def _actionUserDelPost(self):
+ """ Action wrapper. """
+ from testmanager.core.useraccount import UserAccountData, UserAccountLogic;
+ return self._actionGenericDoRemove(UserAccountLogic, UserAccountData.ksParam_uid, self.ksActionUserList)
+
+
+ #
+ # TestBox & Scheduling Category.
+ #
+
+ def _actionTestBoxList(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxLogic
+ from testmanager.webui.wuiadmintestbox import WuiTestBoxList;
+ return self._actionGenericListing(TestBoxLogic, WuiTestBoxList);
+
+ def _actionTestBoxListPost(self):
+ """Actions on a list of testboxes."""
+ from testmanager.core.testbox import TestBoxData, TestBoxLogic
+ from testmanager.webui.wuiadmintestbox import WuiTestBoxList;
+
+ # Parameters.
+ aidTestBoxes = self.getListOfIntParams(TestBoxData.ksParam_idTestBox, iMin = 1, aiDefaults = []);
+ sListAction = self.getStringParam(self.ksParamListAction);
+ if sListAction in [asDesc[0] for asDesc in WuiTestBoxList.kasTestBoxActionDescs]:
+ idAction = None;
+ else:
+ asActionPrefixes = [ 'setgroup-', ];
+ i = 0;
+ while i < len(asActionPrefixes) and not sListAction.startswith(asActionPrefixes[i]):
+ i += 1;
+ if i >= len(asActionPrefixes):
+ raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
+ idAction = sListAction[len(asActionPrefixes[i]):];
+ if not idAction.isdigit():
+ raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
+ idAction = int(idAction);
+ sListAction = sListAction[:len(asActionPrefixes[i]) - 1];
+ self._checkForUnknownParameters();
+
+
+ # Take action.
+ if sListAction == 'none':
+ pass;
+ else:
+ oLogic = TestBoxLogic(self._oDb);
+ aoTestBoxes = []
+ for idTestBox in aidTestBoxes:
+ aoTestBoxes.append(TestBoxData().initFromDbWithId(self._oDb, idTestBox));
+
+ if sListAction in [ 'enable', 'disable' ]:
+ fEnable = sListAction == 'enable';
+ for oTestBox in aoTestBoxes:
+ if oTestBox.fEnabled != fEnable:
+ oTestBox.fEnabled = fEnable;
+ oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
+ else:
+ for oTestBox in aoTestBoxes:
+ if oTestBox.enmPendingCmd != sListAction:
+ oLogic.setCommand(oTestBox.idTestBox, oTestBox.enmPendingCmd, sListAction, self._oCurUser.uid,
+ fCommit = False);
+ self._oDb.commit();
+
+ # Re-display the list.
+ self._sPageTitle = None;
+ self._sPageBody = None;
+ self._sRedirectTo = self._sActionUrlBase + self.ksActionTestBoxList;
+ return True;
+
+ def _actionTestBoxAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxDataEx;
+ from testmanager.webui.wuiadmintestbox import WuiTestBox;
+ return self._actionGenericFormAdd(TestBoxDataEx, WuiTestBox);
+
+ def _actionTestBoxAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic;
+ from testmanager.webui.wuiadmintestbox import WuiTestBox;
+ return self._actionGenericFormAddPost(TestBoxDataEx, TestBoxLogic, WuiTestBox, self.ksActionTestBoxList);
+
+ def _actionTestBoxDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic;
+ from testmanager.webui.wuiadmintestbox import WuiTestBox;
+ return self._actionGenericFormDetails(TestBoxDataEx, TestBoxLogic, WuiTestBox, 'idTestBox', 'idGenTestBox');
+
+ def _actionTestBoxEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxDataEx;
+ from testmanager.webui.wuiadmintestbox import WuiTestBox;
+ return self._actionGenericFormEdit(TestBoxDataEx, WuiTestBox, TestBoxDataEx.ksParam_idTestBox);
+
+ def _actionTestBoxEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic;
+ from testmanager.webui.wuiadmintestbox import WuiTestBox;
+ return self._actionGenericFormEditPost(TestBoxDataEx, TestBoxLogic,WuiTestBox, self.ksActionTestBoxList);
+
+ def _actionTestBoxRemovePost(self):
+ """ Action wrapper. """
+ from testmanager.core.testbox import TestBoxData, TestBoxLogic;
+ return self._actionGenericDoRemove(TestBoxLogic, TestBoxData.ksParam_idTestBox, self.ksActionTestBoxList);
+
+
+ # Scheduling Group actions
+
+ def _actionSchedGroupList(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupLogic;
+ from testmanager.webui.wuiadminschedgroup import WuiAdminSchedGroupList;
+ return self._actionGenericListing(SchedGroupLogic, WuiAdminSchedGroupList);
+
+ def _actionSchedGroupAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormAdd(SchedGroupDataEx, WuiSchedGroup);
+
+ def _actionSchedGroupClone(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormClone( SchedGroupDataEx, WuiSchedGroup, 'idSchedGroup');
+
+ def _actionSchedGroupDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx, SchedGroupLogic;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormDetails(SchedGroupDataEx, SchedGroupLogic, WuiSchedGroup, 'idSchedGroup');
+
+ def _actionSchedGroupEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormEdit(SchedGroupDataEx, WuiSchedGroup, SchedGroupDataEx.ksParam_idSchedGroup);
+
+ def _actionSchedGroupAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx, SchedGroupLogic;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormAddPost(SchedGroupDataEx, SchedGroupLogic, WuiSchedGroup, self.ksActionSchedGroupList);
+
+ def _actionSchedGroupEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupDataEx, SchedGroupLogic;
+ from testmanager.webui.wuiadminschedgroup import WuiSchedGroup;
+ return self._actionGenericFormEditPost(SchedGroupDataEx, SchedGroupLogic, WuiSchedGroup, self.ksActionSchedGroupList);
+
+ def _actionSchedGroupDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.schedgroup import SchedGroupData, SchedGroupLogic;
+ return self._actionGenericDoRemove(SchedGroupLogic, SchedGroupData.ksParam_idSchedGroup, self.ksActionSchedGroupList)
+
+ def _actionSchedQueueList(self):
+ """ Action wrapper. """
+ from testmanager.core.schedqueue import SchedQueueLogic;
+ from testmanager.webui.wuiadminschedqueue import WuiAdminSchedQueueList;
+ return self._actionGenericListing(SchedQueueLogic, WuiAdminSchedQueueList);
+
+ def _actionRegenQueuesCommon(self):
+ """
+ Common code for ksActionTestBoxesRegenQueues and ksActionTestCfgRegenQueues.
+
+ Too lazy to put this in some separate place right now.
+ """
+ from testmanager.core.schedgroup import SchedGroupLogic;
+ from testmanager.core.schedulerbase import SchedulerBase;
+
+ self._checkForUnknownParameters();
+ ## @todo should also be changed to a POST with a confirmation dialog preceeding it.
+
+ self._sPageTitle = 'Regenerate All Scheduling Queues';
+ if not self.isReadOnlyUser():
+ self._sPageBody = '';
+ aoGroups = SchedGroupLogic(self._oDb).getAll();
+ for oGroup in aoGroups:
+ self._sPageBody += '<h3>%s (ID %#d)</h3>' % (webutils.escapeElem(oGroup.sName), oGroup.idSchedGroup);
+ try:
+ (aoErrors, asMessages) = SchedulerBase.recreateQueue(self._oDb, self._oCurUser.uid, oGroup.idSchedGroup, 2);
+ except Exception as oXcpt:
+ self._oDb.rollback();
+ self._sPageBody += '<p>SchedulerBase.recreateQueue threw an exception: %s</p>' \
+ % (webutils.escapeElem(str(oXcpt)),);
+ self._sPageBody += cgitb.html(sys.exc_info());
+ else:
+ if not aoErrors:
+ self._sPageBody += '<p>Successfully regenerated.</p>';
+ else:
+ for oError in aoErrors:
+ if oError[1] is None:
+ self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),);
+ ## @todo links.
+ #elif isinstance(oError[1], TestGroupData):
+ # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),);
+ #elif isinstance(oError[1], TestGroupCase):
+ # self._sPageBody += '<p>%s.</p>' % (webutils.escapeElem(oError[0]),);
+ else:
+ self._sPageBody += '<p>%s. [Cannot link to %s]</p>' \
+ % (webutils.escapeElem(oError[0]), webutils.escapeElem(str(oError[1])),);
+ for sMsg in asMessages:
+ self._sPageBody += '<p>%s<p>\n' % (webutils.escapeElem(sMsg),);
+
+ # Remove leftovers from deleted scheduling groups.
+ self._sPageBody += '<h3>Cleanups</h3>\n';
+ cOrphans = SchedulerBase.cleanUpOrphanedQueues(self._oDb);
+ self._sPageBody += '<p>Removed %s orphaned (deleted) queue%s.<p>\n' % (cOrphans, '' if cOrphans == 1 else 's', );
+ else:
+ self._sPageBody = webutils.escapeElem('%s is a read only user and may not regenerate the scheduling queues!'
+ % (self._oCurUser.sUsername,));
+ return True;
+
+
+
+ #
+ # Test Config Category.
+ #
+
+ # Test Cases
+
+ def _actionTestCaseList(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseLogic;
+ from testmanager.webui.wuiadmintestcase import WuiTestCaseList;
+ return self._actionGenericListing(TestCaseLogic, WuiTestCaseList);
+
+ def _actionTestCaseAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormAdd(TestCaseDataEx, WuiTestCase);
+
+ def _actionTestCaseAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx, TestCaseLogic;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormAddPost(TestCaseDataEx, TestCaseLogic, WuiTestCase, self.ksActionTestCaseList);
+
+ def _actionTestCaseClone(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormClone( TestCaseDataEx, WuiTestCase, 'idTestCase', 'idGenTestCase');
+
+ def _actionTestCaseDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx, TestCaseLogic;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormDetails(TestCaseDataEx, TestCaseLogic, WuiTestCase, 'idTestCase', 'idGenTestCase');
+
+ def _actionTestCaseEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormEdit(TestCaseDataEx, WuiTestCase, TestCaseDataEx.ksParam_idTestCase);
+
+ def _actionTestCaseEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseDataEx, TestCaseLogic;
+ from testmanager.webui.wuiadmintestcase import WuiTestCase;
+ return self._actionGenericFormEditPost(TestCaseDataEx, TestCaseLogic, WuiTestCase, self.ksActionTestCaseList);
+
+ def _actionTestCaseDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.testcase import TestCaseData, TestCaseLogic;
+ return self._actionGenericDoRemove(TestCaseLogic, TestCaseData.ksParam_idTestCase, self.ksActionTestCaseList);
+
+ # Test Group actions
+
+ def _actionTestGroupList(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupLogic;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroupList;
+ return self._actionGenericListing(TestGroupLogic, WuiTestGroupList);
+ def _actionTestGroupAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormAdd(TestGroupDataEx, WuiTestGroup);
+ def _actionTestGroupAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx, TestGroupLogic;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormAddPost(TestGroupDataEx, TestGroupLogic, WuiTestGroup, self.ksActionTestGroupList);
+ def _actionTestGroupClone(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormClone(TestGroupDataEx, WuiTestGroup, 'idTestGroup');
+ def _actionTestGroupDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx, TestGroupLogic;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormDetails(TestGroupDataEx, TestGroupLogic, WuiTestGroup, 'idTestGroup');
+ def _actionTestGroupEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormEdit(TestGroupDataEx, WuiTestGroup, TestGroupDataEx.ksParam_idTestGroup);
+ def _actionTestGroupEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx, TestGroupLogic;
+ from testmanager.webui.wuiadmintestgroup import WuiTestGroup;
+ return self._actionGenericFormEditPost(TestGroupDataEx, TestGroupLogic, WuiTestGroup, self.ksActionTestGroupList);
+ def _actionTestGroupDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.testgroup import TestGroupDataEx, TestGroupLogic;
+ return self._actionGenericDoRemove(TestGroupLogic, TestGroupDataEx.ksParam_idTestGroup, self.ksActionTestGroupList)
+
+
+ # Global Resources
+
+ def _actionGlobalRsrcShowAll(self):
+ """ Action wrapper. """
+ from testmanager.core.globalresource import GlobalResourceLogic;
+ from testmanager.webui.wuiadminglobalrsrc import WuiGlobalResourceList;
+ return self._actionGenericListing(GlobalResourceLogic, WuiGlobalResourceList);
+
+ def _actionGlobalRsrcShowAdd(self):
+ """ Action wrapper. """
+ return self._actionGlobalRsrcShowAddEdit(WuiAdmin.ksActionGlobalRsrcAdd);
+
+ def _actionGlobalRsrcShowEdit(self):
+ """ Action wrapper. """
+ return self._actionGlobalRsrcShowAddEdit(WuiAdmin.ksActionGlobalRsrcEdit);
+
+ def _actionGlobalRsrcAdd(self):
+ """ Action wrapper. """
+ return self._actionGlobalRsrcAddEdit(WuiAdmin.ksActionGlobalRsrcAdd);
+
+ def _actionGlobalRsrcEdit(self):
+ """ Action wrapper. """
+ return self._actionGlobalRsrcAddEdit(WuiAdmin.ksActionGlobalRsrcEdit);
+
+ def _actionGlobalRsrcDel(self):
+ """ Action wrapper. """
+ from testmanager.core.globalresource import GlobalResourceData, GlobalResourceLogic;
+ return self._actionGenericDoDelOld(GlobalResourceLogic, GlobalResourceData.ksParam_idGlobalRsrc,
+ self.ksActionGlobalRsrcShowAll);
+
+ def _actionGlobalRsrcShowAddEdit(self, sAction): # pylint: disable=invalid-name
+ """Show Global Resource creation or edit dialog"""
+ from testmanager.core.globalresource import GlobalResourceLogic, GlobalResourceData;
+ from testmanager.webui.wuiadminglobalrsrc import WuiGlobalResource;
+
+ oGlobalResourceLogic = GlobalResourceLogic(self._oDb)
+ if sAction == WuiAdmin.ksActionGlobalRsrcEdit:
+ idGlobalRsrc = self.getIntParam(GlobalResourceData.ksParam_idGlobalRsrc, iDefault = -1)
+ oData = oGlobalResourceLogic.getById(idGlobalRsrc)
+ else:
+ oData = GlobalResourceData()
+ oData.convertToParamNull()
+
+ self._checkForUnknownParameters()
+
+ oContent = WuiGlobalResource(oData)
+ (self._sPageTitle, self._sPageBody) = oContent.showAddModifyPage(sAction)
+
+ return True
+
+ def _actionGlobalRsrcAddEdit(self, sAction):
+ """Add or modify Global Resource record"""
+ from testmanager.core.globalresource import GlobalResourceLogic, GlobalResourceData;
+ from testmanager.webui.wuiadminglobalrsrc import WuiGlobalResource;
+
+ oData = GlobalResourceData()
+ oData.initFromParams(self, fStrict=True)
+
+ self._checkForUnknownParameters()
+
+ if self._oSrvGlue.getMethod() != 'POST':
+ raise WuiException('Expected "POST" request, got "%s"' % (self._oSrvGlue.getMethod(),))
+
+ oGlobalResourceLogic = GlobalResourceLogic(self._oDb)
+ dErrors = oData.validateAndConvert(self._oDb);
+ if not dErrors:
+ if sAction == WuiAdmin.ksActionGlobalRsrcAdd:
+ oGlobalResourceLogic.addGlobalResource(self._oCurUser.uid, oData)
+ elif sAction == WuiAdmin.ksActionGlobalRsrcEdit:
+ idGlobalRsrc = self.getStringParam(GlobalResourceData.ksParam_idGlobalRsrc)
+ oGlobalResourceLogic.editGlobalResource(self._oCurUser.uid, idGlobalRsrc, oData)
+ else:
+ raise WuiException('Invalid parameter.')
+ self._sPageTitle = None;
+ self._sPageBody = None;
+ self._sRedirectTo = self._sActionUrlBase + self.ksActionGlobalRsrcShowAll;
+ else:
+ oContent = WuiGlobalResource(oData)
+ (self._sPageTitle, self._sPageBody) = oContent.showAddModifyPage(sAction, dErrors=dErrors)
+
+ return True
+
+
+ #
+ # Build Source actions
+ #
+
+ def _actionBuildSrcList(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceLogic;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrcList;
+ return self._actionGenericListing(BuildSourceLogic, WuiAdminBuildSrcList);
+
+ def _actionBuildSrcAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormAdd(BuildSourceData, WuiAdminBuildSrc);
+
+ def _actionBuildSrcAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormAddPost(BuildSourceData, BuildSourceLogic, WuiAdminBuildSrc, self.ksActionBuildSrcList);
+
+ def _actionBuildSrcClone(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormClone( BuildSourceData, WuiAdminBuildSrc, 'idBuildSrc');
+
+ def _actionBuildSrcDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormDetails(BuildSourceData, BuildSourceLogic, WuiAdminBuildSrc, 'idBuildSrc');
+
+ def _actionBuildSrcDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+ return self._actionGenericDoRemove(BuildSourceLogic, BuildSourceData.ksParam_idBuildSrc, self.ksActionBuildSrcList);
+
+ def _actionBuildSrcEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormEdit(BuildSourceData, WuiAdminBuildSrc, BuildSourceData.ksParam_idBuildSrc);
+
+ def _actionBuildSrcEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+ from testmanager.webui.wuiadminbuildsource import WuiAdminBuildSrc;
+ return self._actionGenericFormEditPost(BuildSourceData, BuildSourceLogic, WuiAdminBuildSrc, self.ksActionBuildSrcList);
+
+
+ #
+ # Build actions
+ #
+ def _actionBuildList(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildLogic;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuildList;
+ return self._actionGenericListing(BuildLogic, WuiAdminBuildList);
+
+ def _actionBuildAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormAdd(BuildData, WuiAdminBuild);
+
+ def _actionBuildAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData, BuildLogic;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormAddPost(BuildData, BuildLogic, WuiAdminBuild, self.ksActionBuildList);
+
+ def _actionBuildClone(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormClone( BuildData, WuiAdminBuild, 'idBuild');
+
+ def _actionBuildDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData, BuildLogic;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormDetails(BuildData, BuildLogic, WuiAdminBuild, 'idBuild');
+
+ def _actionBuildDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData, BuildLogic;
+ return self._actionGenericDoRemove(BuildLogic, BuildData.ksParam_idBuild, self.ksActionBuildList);
+
+ def _actionBuildEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormEdit(BuildData, WuiAdminBuild, BuildData.ksParam_idBuild);
+
+ def _actionBuildEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildData, BuildLogic;
+ from testmanager.webui.wuiadminbuild import WuiAdminBuild;
+ return self._actionGenericFormEditPost(BuildData, BuildLogic, WuiAdminBuild, self.ksActionBuildList)
+
+
+ #
+ # Build Category actions
+ #
+ def _actionBuildCategoryList(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryLogic;
+ from testmanager.webui.wuiadminbuildcategory import WuiAdminBuildCatList;
+ return self._actionGenericListing(BuildCategoryLogic, WuiAdminBuildCatList);
+
+ def _actionBuildCategoryAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryData;
+ from testmanager.webui.wuiadminbuildcategory import WuiAdminBuildCat;
+ return self._actionGenericFormAdd(BuildCategoryData, WuiAdminBuildCat);
+
+ def _actionBuildCategoryAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryData, BuildCategoryLogic;
+ from testmanager.webui.wuiadminbuildcategory import WuiAdminBuildCat;
+ return self._actionGenericFormAddPost(BuildCategoryData, BuildCategoryLogic, WuiAdminBuildCat,
+ self.ksActionBuildCategoryList);
+
+ def _actionBuildCategoryClone(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryData;
+ from testmanager.webui.wuiadminbuildcategory import WuiAdminBuildCat;
+ return self._actionGenericFormClone(BuildCategoryData, WuiAdminBuildCat, 'idBuildCategory');
+
+ def _actionBuildCategoryDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryData, BuildCategoryLogic;
+ from testmanager.webui.wuiadminbuildcategory import WuiAdminBuildCat;
+ return self._actionGenericFormDetails(BuildCategoryData, BuildCategoryLogic, WuiAdminBuildCat, 'idBuildCategory');
+
+ def _actionBuildCategoryDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.build import BuildCategoryData, BuildCategoryLogic;
+ return self._actionGenericDoRemove(BuildCategoryLogic, BuildCategoryData.ksParam_idBuildCategory,
+ self.ksActionBuildCategoryList)
+
+
+ #
+ # Build Black List actions
+ #
+ def _actionBuildBlacklist(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistLogic;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminListOfBlacklistItems;
+ return self._actionGenericListing(BuildBlacklistLogic, WuiAdminListOfBlacklistItems);
+
+ def _actionBuildBlacklistAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormAdd(BuildBlacklistData, WuiAdminBuildBlacklist);
+
+ def _actionBuildBlacklistAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData, BuildBlacklistLogic;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormAddPost(BuildBlacklistData, BuildBlacklistLogic,
+ WuiAdminBuildBlacklist, self.ksActionBuildBlacklist);
+
+ def _actionBuildBlacklistClone(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormClone(BuildBlacklistData, WuiAdminBuildBlacklist, 'idBlacklisting');
+
+ def _actionBuildBlacklistDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData, BuildBlacklistLogic;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormDetails(BuildBlacklistData, BuildBlacklistLogic, WuiAdminBuildBlacklist, 'idBlacklisting');
+
+ def _actionBuildBlacklistDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData, BuildBlacklistLogic;
+ return self._actionGenericDoRemove(BuildBlacklistLogic, BuildBlacklistData.ksParam_idBlacklisting,
+ self.ksActionBuildBlacklist);
+
+ def _actionBuildBlacklistEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormEdit(BuildBlacklistData, WuiAdminBuildBlacklist, BuildBlacklistData.ksParam_idBlacklisting);
+
+ def _actionBuildBlacklistEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.buildblacklist import BuildBlacklistData, BuildBlacklistLogic;
+ from testmanager.webui.wuiadminbuildblacklist import WuiAdminBuildBlacklist;
+ return self._actionGenericFormEditPost(BuildBlacklistData, BuildBlacklistLogic, WuiAdminBuildBlacklist,
+ self.ksActionBuildBlacklist)
+
+
+ #
+ # Failure Category actions
+ #
+ def _actionFailureCategoryList(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryLogic;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategoryList;
+ return self._actionGenericListing(FailureCategoryLogic, WuiFailureCategoryList);
+
+ def _actionFailureCategoryAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategory;
+ return self._actionGenericFormAdd(FailureCategoryData, WuiFailureCategory);
+
+ def _actionFailureCategoryAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData, FailureCategoryLogic;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategory;
+ return self._actionGenericFormAddPost(FailureCategoryData, FailureCategoryLogic, WuiFailureCategory,
+ self.ksActionFailureCategoryList)
+
+ def _actionFailureCategoryDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData, FailureCategoryLogic;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategory;
+ return self._actionGenericFormDetails(FailureCategoryData, FailureCategoryLogic, WuiFailureCategory);
+
+
+ def _actionFailureCategoryDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData, FailureCategoryLogic;
+ return self._actionGenericDoRemove(FailureCategoryLogic, FailureCategoryData.ksParam_idFailureCategory,
+ self.ksActionFailureCategoryList);
+
+ def _actionFailureCategoryEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategory;
+ return self._actionGenericFormEdit(FailureCategoryData, WuiFailureCategory,
+ FailureCategoryData.ksParam_idFailureCategory);
+
+ def _actionFailureCategoryEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.failurecategory import FailureCategoryData, FailureCategoryLogic;
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureCategory;
+ return self._actionGenericFormEditPost(FailureCategoryData, FailureCategoryLogic, WuiFailureCategory,
+ self.ksActionFailureCategoryList);
+
+ #
+ # Failure Reason actions
+ #
+ def _actionFailureReasonList(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonLogic;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReasonList;
+ return self._actionGenericListing(FailureReasonLogic, WuiAdminFailureReasonList)
+
+ def _actionFailureReasonAdd(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReason;
+ return self._actionGenericFormAdd(FailureReasonData, WuiAdminFailureReason);
+
+ def _actionFailureReasonAddPost(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData, FailureReasonLogic;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReason;
+ return self._actionGenericFormAddPost(FailureReasonData, FailureReasonLogic, WuiAdminFailureReason,
+ self.ksActionFailureReasonList);
+
+ def _actionFailureReasonDetails(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData, FailureReasonLogic;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReason;
+ return self._actionGenericFormDetails(FailureReasonData, FailureReasonLogic, WuiAdminFailureReason);
+
+ def _actionFailureReasonDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData, FailureReasonLogic;
+ return self._actionGenericDoRemove(FailureReasonLogic, FailureReasonData.ksParam_idFailureReason,
+ self.ksActionFailureReasonList);
+
+ def _actionFailureReasonEdit(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReason;
+ return self._actionGenericFormEdit(FailureReasonData, WuiAdminFailureReason);
+
+
+ def _actionFailureReasonEditPost(self):
+ """ Action wrapper. """
+ from testmanager.core.failurereason import FailureReasonData, FailureReasonLogic;
+ from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReason;
+ return self._actionGenericFormEditPost(FailureReasonData, FailureReasonLogic, WuiAdminFailureReason,
+ self.ksActionFailureReasonList)
+
+
+ #
+ # Overrides.
+ #
+
+ def _generatePage(self):
+ """Override parent handler in order to change page titte"""
+ if self._sPageTitle is not None:
+ self._sPageTitle = 'Test Manager Admin - ' + self._sPageTitle
+
+ return WuiDispatcherBase._generatePage(self)
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminbuild.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuild.py
new file mode 100755
index 00000000..17944ec3
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuild.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminbuild.py $
+
+"""
+Test Manager WUI - Builds.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink, WuiBuildLogLink, \
+ WuiSvnLinkWithTooltip;
+from testmanager.core.build import BuildData, BuildCategoryLogic;
+from testmanager.core.buildblacklist import BuildBlacklistData;
+from testmanager.core.db import isDbTimestampInfinity;
+
+
+class WuiAdminBuild(WuiFormContentBase):
+ """
+ WUI Build HTML content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add Build'
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Modify Build - #%s' % (oData.idBuild,);
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Build - #%s' % (oData.idBuild,);
+ WuiFormContentBase.__init__(self, oData, sMode, 'Build', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO (BuildData.ksParam_idBuild, oData.idBuild, 'Build ID')
+ oForm.addTimestampRO(BuildData.ksParam_tsCreated, oData.tsCreated, 'Created')
+ oForm.addTimestampRO(BuildData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(BuildData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (BuildData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+
+ oForm.addComboBox (BuildData.ksParam_idBuildCategory, oData.idBuildCategory, 'Build category',
+ BuildCategoryLogic(self._oDisp.getDb()).fetchForCombo());
+
+ oForm.addInt (BuildData.ksParam_iRevision, oData.iRevision, 'Revision')
+ oForm.addText (BuildData.ksParam_sVersion, oData.sVersion, 'Version')
+ oForm.addWideText (BuildData.ksParam_sLogUrl, oData.sLogUrl, 'Log URL')
+ oForm.addWideText (BuildData.ksParam_sBinaries, oData.sBinaries, 'Binaries')
+ oForm.addCheckBox (BuildData.ksParam_fBinariesDeleted, oData.fBinariesDeleted, 'Binaries deleted')
+
+ oForm.addSubmit()
+ return True;
+
+
+class WuiAdminBuildList(WuiListContentBase):
+ """
+ WUI Admin Build List Content Generator.
+ """
+
+ ksResultsSortByOs_Darwin = ''
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Builds', sId = 'builds', fnDPrint = fnDPrint, oDisp = oDisp,
+ aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = ['ID', 'Product', 'Branch', 'Version',
+ 'Type', 'OS(es)', 'Author', 'Added',
+ 'Files', 'Action' ];
+ self._asColumnAttribs = ['align="right"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ '', 'align="center"'];
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry];
+
+ aoActions = [];
+ if oEntry.sLogUrl is not None:
+ aoActions.append(WuiBuildLogLink(oEntry.sLogUrl, 'Build Log'));
+
+ dParams = { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistAdd,
+ BuildBlacklistData.ksParam_sProduct: oEntry.oCat.sProduct,
+ BuildBlacklistData.ksParam_sBranch: oEntry.oCat.sBranch,
+ BuildBlacklistData.ksParam_asTypes: oEntry.oCat.sType,
+ BuildBlacklistData.ksParam_asOsArches: oEntry.oCat.asOsArches,
+ BuildBlacklistData.ksParam_iFirstRevision: oEntry.iRevision,
+ BuildBlacklistData.ksParam_iLastRevision: oEntry.iRevision }
+
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Blacklist', WuiAdmin.ksScriptName, dParams),
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDetails,
+ BuildData.ksParam_idBuild: oEntry.idBuild,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }),
+ WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildClone,
+ BuildData.ksParam_idBuild: oEntry.idBuild,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }),
+ ];
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildEdit,
+ BuildData.ksParam_idBuild: oEntry.idBuild }),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDoRemove,
+ BuildData.ksParam_idBuild: oEntry.idBuild },
+ sConfirm = 'Are you sure you want to remove build #%d?' % (oEntry.idBuild,) ),
+ ];
+
+ return [ oEntry.idBuild,
+ oEntry.oCat.sProduct,
+ oEntry.oCat.sBranch,
+ WuiSvnLinkWithTooltip(oEntry.iRevision, oEntry.oCat.sRepository,
+ sName = '%s r%s' % (oEntry.sVersion, oEntry.iRevision,)),
+ oEntry.oCat.sType,
+ ' '.join(oEntry.oCat.asOsArches),
+ 'batch' if oEntry.uidAuthor is None else oEntry.uidAuthor,
+ self.formatTsShort(oEntry.tsCreated),
+ oEntry.sBinaries if not oEntry.fBinariesDeleted else '<Deleted>',
+ aoActions,
+ ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildblacklist.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildblacklist.py
new file mode 100755
index 00000000..d265259b
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildblacklist.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminbuildblacklist.py $
+
+"""
+Test Manager WUI - Build Blacklist.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.webui.wuibase import WuiException
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink
+from testmanager.core.buildblacklist import BuildBlacklistData
+from testmanager.core.failurereason import FailureReasonLogic
+from testmanager.core.db import TMDatabaseConnection
+from testmanager.core import coreconsts
+
+
+class WuiAdminBuildBlacklist(WuiFormContentBase):
+ """
+ WUI Build Black List Form.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ """
+ Prepare & initialize parent
+ """
+
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add Build Blacklist Entry'
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit Build Blacklist Entry'
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Build Black';
+ WuiFormContentBase.__init__(self, oData, sMode, 'BuildBlacklist', oDisp, sTitle);
+
+ #
+ # Additional data.
+ #
+ self.asTypes = coreconsts.g_kasBuildTypesAll
+ self.asOsArches = coreconsts.g_kasOsDotCpusAll
+
+ def _populateForm(self, oForm, oData):
+ """
+ Construct an HTML form
+ """
+
+ aoFailureReasons = FailureReasonLogic(self._oDisp.getDb()).fetchForCombo()
+ if not aoFailureReasons:
+ from testmanager.webui.wuiadmin import WuiAdmin
+ raise WuiException('Please <a href="%s?%s=%s">add</a> some Failure Reasons first.'
+ % (WuiAdmin.ksScriptName, WuiAdmin.ksParamAction, WuiAdmin.ksActionFailureReasonAdd));
+
+ asTypes = self.getListOfItems(self.asTypes, oData.asTypes)
+ asOsArches = self.getListOfItems(self.asOsArches, oData.asOsArches)
+
+ oForm.addIntRO (BuildBlacklistData.ksParam_idBlacklisting, oData.idBlacklisting, 'Blacklist item ID')
+ oForm.addTimestampRO(BuildBlacklistData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(BuildBlacklistData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (BuildBlacklistData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+
+ oForm.addComboBox (BuildBlacklistData.ksParam_idFailureReason, oData.idFailureReason, 'Failure Reason',
+ aoFailureReasons)
+
+ oForm.addText (BuildBlacklistData.ksParam_sProduct, oData.sProduct, 'Product')
+ oForm.addText (BuildBlacklistData.ksParam_sBranch, oData.sBranch, 'Branch')
+ oForm.addListOfTypes(BuildBlacklistData.ksParam_asTypes, asTypes, 'Build types')
+ oForm.addListOfOsArches(BuildBlacklistData.ksParam_asOsArches, asOsArches, 'Target architectures')
+ oForm.addInt (BuildBlacklistData.ksParam_iFirstRevision, oData.iFirstRevision, 'First revision')
+ oForm.addInt (BuildBlacklistData.ksParam_iLastRevision, oData.iLastRevision, 'Last revision (incl)')
+
+ oForm.addSubmit();
+
+ return True;
+
+
+class WuiAdminListOfBlacklistItems(WuiListContentBase):
+ """
+ WUI Admin Build Blacklist Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Build Blacklist', sId = 'buildsBlacklist',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = ['ID', 'Failure Reason',
+ 'Product', 'Branch', 'Type',
+ 'OS(es)', 'First Revision', 'Last Revision',
+ 'Actions' ]
+ self._asColumnAttribs = ['align="right"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"' ]
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry]
+
+ sShortFailReason = FailureReasonLogic(TMDatabaseConnection()).getById(oEntry.idFailureReason).sShort
+
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDetails,
+ BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Edit', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistEdit,
+ BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting }),
+ WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistClone,
+ BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting,
+ WuiAdmin.ksParamEffectiveDate: oEntry.tsEffective, }),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildBlacklistDoRemove,
+ BuildBlacklistData.ksParam_idBlacklisting: oEntry.idBlacklisting },
+ sConfirm = 'Are you sure you want to remove black list entry #%d?' % (oEntry.idBlacklisting,)),
+ ];
+
+ return [ oEntry.idBlacklisting,
+ sShortFailReason,
+ oEntry.sProduct,
+ oEntry.sBranch,
+ oEntry.asTypes,
+ oEntry.asOsArches,
+ oEntry.iFirstRevision,
+ oEntry.iLastRevision,
+ aoActions
+ ];
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildcategory.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildcategory.py
new file mode 100755
index 00000000..87d76fba
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildcategory.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminbuildcategory.py $
+
+"""
+Test Manager WUI - Build categories.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from common import webutils;
+from testmanager.webui.wuicontentbase import WuiListContentBase, WuiFormContentBase, WuiRawHtml, WuiTmLink;
+from testmanager.core.build import BuildCategoryData
+from testmanager.core import coreconsts;
+
+
+class WuiAdminBuildCatList(WuiListContentBase):
+ """
+ WUI Build Category List Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Build Categories', sId = 'buildcategories',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = ([ 'ID', 'Product', 'Repository', 'Branch', 'Build Type', 'OS/Architectures', 'Actions' ]);
+ self._asColumnAttribs = (['align="right"', '', '', '', '', 'align="center"' ]);
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ oEntry = self._aoEntries[iEntry];
+
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryDetails,
+ BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryClone,
+ BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }),
+ WuiTmLink('Try Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildCategoryDoRemove,
+ BuildCategoryData.ksParam_idBuildCategory: oEntry.idBuildCategory, }),
+ ];
+
+ sHtml = '<ul class="tmshowall">\n';
+ for sOsArch in oEntry.asOsArches:
+ sHtml += ' <li class="tmshowall">%s</li>\n' % (webutils.escapeElem(sOsArch),);
+ sHtml += '</ul>\n'
+
+ return [ oEntry.idBuildCategory,
+ oEntry.sRepository,
+ oEntry.sProduct,
+ oEntry.sBranch,
+ oEntry.sType,
+ WuiRawHtml(sHtml),
+ aoActions,
+ ];
+
+
+class WuiAdminBuildCat(WuiFormContentBase):
+ """
+ WUI Build Category Form Content Generator.
+ """
+ def __init__(self, oData, sMode, oDisp):
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Create Build Category';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ assert False, 'not possible'
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Build Category- %s' % (oData.idBuildCategory,);
+ WuiFormContentBase.__init__(self, oData, sMode, 'BuildCategory', oDisp, sTitle, fEditable = False);
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO( BuildCategoryData.ksParam_idBuildCategory, oData.idBuildCategory, 'Build Category ID')
+ oForm.addText( BuildCategoryData.ksParam_sRepository, oData.sRepository, 'VCS repository name');
+ oForm.addText( BuildCategoryData.ksParam_sProduct, oData.sProduct, 'Product name')
+ oForm.addText( BuildCategoryData.ksParam_sBranch, oData.sBranch, 'Branch name')
+ oForm.addText( BuildCategoryData.ksParam_sType, oData.sType, 'Build type')
+
+ aoOsArches = [[sOsArch, sOsArch in oData.asOsArches, sOsArch] for sOsArch in coreconsts.g_kasOsDotCpusAll];
+ oForm.addListOfOsArches(BuildCategoryData.ksParam_asOsArches, aoOsArches, 'Target architectures');
+
+ if self._sMode != WuiFormContentBase.ksMode_Show:
+ oForm.addSubmit();
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildsource.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildsource.py
new file mode 100755
index 00000000..52d05f07
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminbuildsource.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminbuildsource.py $
+
+"""
+Test Manager WUI - Build Sources.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink, WuiRawHtml;
+from testmanager.core import coreconsts;
+from testmanager.core.db import isDbTimestampInfinity;
+from testmanager.core.buildsource import BuildSourceData;
+
+
+class WuiAdminBuildSrc(WuiFormContentBase):
+ """
+ WUI Build Sources HTML content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ assert isinstance(oData, BuildSourceData);
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'New Build Source';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit Build Source - %s (#%s)' % (oData.sName, oData.idBuildSrc,);
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Build Source - %s (#%s)' % (oData.sName, oData.idBuildSrc,);
+ WuiFormContentBase.__init__(self, oData, sMode, 'BuildSrc', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO (BuildSourceData.ksParam_idBuildSrc, oData.idBuildSrc, 'Build Source item ID')
+ oForm.addTimestampRO(BuildSourceData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(BuildSourceData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (BuildSourceData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+ oForm.addText (BuildSourceData.ksParam_sName, oData.sName, 'Name')
+ oForm.addText (BuildSourceData.ksParam_sDescription, oData.sDescription, 'Description')
+ oForm.addText (BuildSourceData.ksParam_sProduct, oData.sProduct, 'Product')
+ oForm.addText (BuildSourceData.ksParam_sBranch, oData.sBranch, 'Branch')
+ asTypes = self.getListOfItems(coreconsts.g_kasBuildTypesAll, oData.asTypes);
+ oForm.addListOfTypes(BuildSourceData.ksParam_asTypes, asTypes, 'Build types')
+ asOsArches = self.getListOfItems(coreconsts.g_kasOsDotCpusAll, oData.asOsArches);
+ oForm.addListOfOsArches(BuildSourceData.ksParam_asOsArches, asOsArches, 'Target architectures')
+ oForm.addInt (BuildSourceData.ksParam_iFirstRevision, oData.iFirstRevision, 'Starting from revision')
+ oForm.addInt (BuildSourceData.ksParam_iLastRevision, oData.iLastRevision, 'Ending by revision')
+ oForm.addLong (BuildSourceData.ksParam_cSecMaxAge,
+ utils.formatIntervalSeconds2(oData.cSecMaxAge) if oData.cSecMaxAge not in [-1, '', None] else '',
+ 'Max age in seconds');
+ oForm.addSubmit();
+ return True;
+
+class WuiAdminBuildSrcList(WuiListContentBase):
+ """
+ WUI Build Source content generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Registered Build Sources', sId = 'build sources',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = ['ID', 'Name', 'Description', 'Product',
+ 'Branch', 'Build Types', 'OS/ARCH', 'First Revision', 'Last Revision', 'Max Age',
+ 'Actions' ];
+ self._asColumnAttribs = ['align="center"', 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="left"', 'align="left"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"' ];
+
+ def _getSubList(self, aList):
+ """
+ Convert pythonic list into HTML list
+ """
+ if aList not in (None, []):
+ sHtml = ' <ul class="tmshowall">\n'
+ for sTmp in aList:
+ sHtml += ' <li class="tmshowall">%s</a></li>\n' % (webutils.escapeElem(sTmp),);
+ sHtml += ' </ul>\n';
+ else:
+ sHtml = '<ul class="tmshowall"><li class="tmshowall">Any</li></ul>\n';
+
+ return WuiRawHtml(sHtml);
+
+ def _formatListEntry(self, iEntry):
+ """
+ Format *show all* table entry
+ """
+
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry]
+
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDetails,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcClone,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }),
+ ];
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcEdit,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc } ),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDoRemove,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc },
+ sConfirm = 'Are you sure you want to remove build source #%d?' % (oEntry.idBuildSrc,) )
+ ];
+
+ return [ oEntry.idBuildSrc,
+ oEntry.sName,
+ oEntry.sDescription,
+ oEntry.sProduct,
+ oEntry.sBranch,
+ self._getSubList(oEntry.asTypes),
+ self._getSubList(oEntry.asOsArches),
+ oEntry.iFirstRevision,
+ oEntry.iLastRevision,
+ utils.formatIntervalSeconds2(oEntry.cSecMaxAge) if oEntry.cSecMaxAge is not None else None,
+ aoActions,
+ ]
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurecategory.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurecategory.py
new file mode 100755
index 00000000..d02931de
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurecategory.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminfailurecategory.py $
+
+"""
+Test Manager WUI - Failure Categories Web content generator.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiContentBase, WuiListContentBase, WuiTmLink;
+from testmanager.webui.wuiadminfailurereason import WuiAdminFailureReasonList;
+from testmanager.core.failurecategory import FailureCategoryData;
+from testmanager.core.failurereason import FailureReasonLogic;
+
+
+class WuiFailureReasonCategoryLink(WuiTmLink):
+ """ Link to a failure category. """
+ def __init__(self, idFailureCategory, sName = WuiContentBase.ksShortDetailsLink, sTitle = None, fBracketed = None):
+ if fBracketed is None:
+ fBracketed = len(sName) > 2;
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ WuiTmLink.__init__(self, sName = sName,
+ sUrlBase = WuiAdmin.ksScriptName,
+ dParams = { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDetails,
+ FailureCategoryData.ksParam_idFailureCategory: idFailureCategory, },
+ fBracketed = fBracketed,
+ sTitle = sTitle);
+ self.idFailureCategory = idFailureCategory;
+
+
+
+class WuiFailureCategory(WuiFormContentBase):
+ """
+ WUI Failure Category HTML content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ """
+ Prepare & initialize parent
+ """
+
+ sTitle = 'Failure Category';
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add ' + sTitle;
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit ' + sTitle;
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+
+ WuiFormContentBase.__init__(self, oData, sMode, 'FailureCategory', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+ """
+ Construct an HTML form
+ """
+
+ oForm.addIntRO (FailureCategoryData.ksParam_idFailureCategory, oData.idFailureCategory, 'Failure Category ID')
+ oForm.addTimestampRO(FailureCategoryData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(FailureCategoryData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (FailureCategoryData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+ oForm.addText (FailureCategoryData.ksParam_sShort, oData.sShort, 'Short Description')
+ oForm.addText (FailureCategoryData.ksParam_sFull, oData.sFull, 'Full Description')
+
+ oForm.addSubmit()
+
+ return True;
+
+ def _generatePostFormContent(self, oData):
+ """
+ Adds a table with the category members below the form.
+ """
+ if oData.idFailureCategory is not None and oData.idFailureCategory >= 0:
+ oLogic = FailureReasonLogic(self._oDisp.getDb());
+ tsNow = self._oDisp.getNow();
+ cMax = 4096;
+ aoEntries = oLogic.fetchForListingInCategory(0, cMax, tsNow, oData.idFailureCategory)
+ if aoEntries:
+ oList = WuiAdminFailureReasonList(aoEntries, 0, cMax, tsNow, fnDPrint = None, oDisp = self._oDisp);
+ return [ [ 'Members', oList.show(fShowNavigation = False)[1]], ];
+ return [];
+
+
+
+class WuiFailureCategoryList(WuiListContentBase):
+ """
+ WUI Admin Failure Category Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Failure Categories', sId = 'failureCategories',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = ['ID', 'Short Description', 'Full Description', 'Actions' ]
+ self._asColumnAttribs = ['align="right"', 'align="center"', 'align="center"', 'align="center"']
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry]
+
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDetails,
+ FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryEdit,
+ FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory }),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureCategoryDoRemove,
+ FailureCategoryData.ksParam_idFailureCategory: oEntry.idFailureCategory },
+ sConfirm = 'Do you really want to remove failure cateogry #%d?' % (oEntry.idFailureCategory,)),
+ ]
+
+ return [ oEntry.idFailureCategory,
+ oEntry.sShort,
+ oEntry.sFull,
+ aoActions,
+ ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurereason.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurereason.py
new file mode 100755
index 00000000..5fa76227
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminfailurereason.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminfailurereason.py $
+
+"""
+Test Manager WUI - Failure Reasons Web content generator.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.webui.wuibase import WuiException
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiContentBase, WuiTmLink;
+from testmanager.core.failurereason import FailureReasonData;
+from testmanager.core.failurecategory import FailureCategoryLogic;
+from testmanager.core.db import TMDatabaseConnection;
+
+
+
+class WuiFailureReasonDetailsLink(WuiTmLink):
+ """ Short link to a failure reason. """
+ def __init__(self, idFailureReason, sName = WuiContentBase.ksShortDetailsLink, sTitle = None, fBracketed = None):
+ if fBracketed is None:
+ fBracketed = len(sName) > 2;
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ WuiTmLink.__init__(self, sName = sName,
+ sUrlBase = WuiAdmin.ksScriptName,
+ dParams = { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDetails,
+ FailureReasonData.ksParam_idFailureReason: idFailureReason, },
+ fBracketed = fBracketed);
+ self.idFailureReason = idFailureReason;
+
+
+
+class WuiFailureReasonAddLink(WuiTmLink):
+ """ Link for adding a failure reason. """
+ def __init__(self, sName = WuiContentBase.ksShortAddLink, sTitle = None, fBracketed = None):
+ if fBracketed is None:
+ fBracketed = len(sName) > 2;
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ WuiTmLink.__init__(self, sName = sName,
+ sUrlBase = WuiAdmin.ksScriptName,
+ dParams = { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonAdd, },
+ fBracketed = fBracketed);
+
+
+
+class WuiAdminFailureReason(WuiFormContentBase):
+ """
+ WUI Failure Reason HTML content generator.
+ """
+
+ def __init__(self, oFailureReasonData, sMode, oDisp):
+ """
+ Prepare & initialize parent
+ """
+
+ sTitle = 'Failure Reason';
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add' + sTitle;
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit' + sTitle;
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+
+ WuiFormContentBase.__init__(self, oFailureReasonData, sMode, 'FailureReason', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+ """
+ Construct an HTML form
+ """
+
+ aoFailureCategories = FailureCategoryLogic(TMDatabaseConnection()).getFailureCategoriesForCombo()
+ if not aoFailureCategories:
+ from testmanager.webui.wuiadmin import WuiAdmin
+ sExceptionMsg = 'Please <a href="%s?%s=%s">add</a> Failure Category first.' % \
+ (WuiAdmin.ksScriptName, WuiAdmin.ksParamAction, WuiAdmin.ksActionFailureCategoryAdd)
+
+ raise WuiException(sExceptionMsg)
+
+ oForm.addIntRO (FailureReasonData.ksParam_idFailureReason, oData.idFailureReason, 'Failure Reason ID')
+ oForm.addTimestampRO (FailureReasonData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO (FailureReasonData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (FailureReasonData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+
+ oForm.addComboBox (FailureReasonData.ksParam_idFailureCategory, oData.idFailureCategory, 'Failure Category',
+ aoFailureCategories)
+
+ oForm.addText (FailureReasonData.ksParam_sShort, oData.sShort, 'Short Description')
+ oForm.addText (FailureReasonData.ksParam_sFull, oData.sFull, 'Full Description')
+ oForm.addInt (FailureReasonData.ksParam_iTicket, oData.iTicket, 'Ticket Number')
+ oForm.addMultilineText(FailureReasonData.ksParam_asUrls, oData.asUrls, 'Other URLs to reports '
+ 'or discussions of the '
+ 'observed symptoms')
+ oForm.addSubmit()
+
+ return True
+
+
+class WuiAdminFailureReasonList(WuiListContentBase):
+ """
+ WUI Admin Failure Reasons Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Failure Reasons', sId = 'failureReasons',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = ['ID', 'Category', 'Short Description',
+ 'Full Description', 'Ticket', 'External References', 'Actions' ]
+
+ self._asColumnAttribs = ['align="right"', 'align="center"', 'align="center"',
+ 'align="center"',' align="center"', 'align="center"', 'align="center"']
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin
+ from testmanager.webui.wuiadminfailurecategory import WuiFailureReasonCategoryLink;
+ oEntry = self._aoEntries[iEntry]
+
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDetails,
+ FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonEdit,
+ FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason } ),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDoRemove,
+ FailureReasonData.ksParam_idFailureReason: oEntry.idFailureReason },
+ sConfirm = 'Are you sure you want to remove failure reason #%d?' % (oEntry.idFailureReason,)),
+ ];
+
+ return [ oEntry.idFailureReason,
+ WuiFailureReasonCategoryLink(oEntry.idFailureCategory, sName = oEntry.oCategory.sShort, fBracketed = False),
+ oEntry.sShort,
+ oEntry.sFull,
+ oEntry.iTicket,
+ oEntry.asUrls,
+ aoActions,
+ ]
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminglobalrsrc.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminglobalrsrc.py
new file mode 100755
index 00000000..b748ea2f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminglobalrsrc.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminglobalrsrc.py $
+
+"""
+Test Manager WUI - Global resources.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from testmanager.webui.wuibase import WuiException
+from testmanager.webui.wuicontentbase import WuiContentBase
+from testmanager.webui.wuihlpform import WuiHlpForm
+from testmanager.core.globalresource import GlobalResourceData
+from testmanager.webui.wuicontentbase import WuiListContentBase, WuiTmLink
+
+
+class WuiGlobalResource(WuiContentBase):
+ """
+ WUI global resources content generator.
+ """
+
+ def __init__(self, oData, fnDPrint = None):
+ """
+ Do necessary initializations
+ """
+ WuiContentBase.__init__(self, fnDPrint)
+ self._oData = oData
+
+ def showAddModifyPage(self, sAction, dErrors = None):
+ """
+ Render add global resource HTML form.
+ """
+ from testmanager.webui.wuiadmin import WuiAdmin
+
+ sFormActionUrl = '%s?%s=%s' % (WuiAdmin.ksScriptName,
+ WuiAdmin.ksParamAction, sAction)
+ if sAction == WuiAdmin.ksActionGlobalRsrcAdd:
+ sTitle = 'Add Global Resource'
+ elif sAction == WuiAdmin.ksActionGlobalRsrcEdit:
+ sTitle = 'Modify Global Resource'
+ sFormActionUrl += '&%s=%s' % (GlobalResourceData.ksParam_idGlobalRsrc, self._oData.idGlobalRsrc)
+ else:
+ raise WuiException('Invalid paraemter "%s"' % (sAction,))
+
+ oForm = WuiHlpForm('globalresourceform',
+ sFormActionUrl,
+ dErrors if dErrors is not None else {})
+
+ if sAction == WuiAdmin.ksActionGlobalRsrcAdd:
+ oForm.addIntRO (GlobalResourceData.ksParam_idGlobalRsrc, self._oData.idGlobalRsrc, 'Global Resource ID')
+ oForm.addTimestampRO(GlobalResourceData.ksParam_tsEffective, self._oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(GlobalResourceData.ksParam_tsExpire, self._oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (GlobalResourceData.ksParam_uidAuthor, self._oData.uidAuthor, 'Changed by UID')
+ oForm.addText (GlobalResourceData.ksParam_sName, self._oData.sName, 'Name')
+ oForm.addText (GlobalResourceData.ksParam_sDescription, self._oData.sDescription, 'Description')
+ oForm.addCheckBox (GlobalResourceData.ksParam_fEnabled, self._oData.fEnabled, 'Enabled')
+
+ oForm.addSubmit('Submit')
+
+ return (sTitle, oForm.finalize())
+
+
+class WuiGlobalResourceList(WuiListContentBase):
+ """
+ WUI Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Global Resources', sId = 'globalResources',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = ['ID', 'Name', 'Description', 'Enabled', 'Actions' ]
+ self._asColumnAttribs = ['align="right"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"']
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry]
+
+ aoActions = [ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcShowEdit,
+ GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc }),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionGlobalRsrcDel,
+ GlobalResourceData.ksParam_idGlobalRsrc: oEntry.idGlobalRsrc },
+ sConfirm = 'Are you sure you want to remove global resource #%d?' % (oEntry.idGlobalRsrc,)),
+ ];
+
+ return [ oEntry.idGlobalRsrc,
+ oEntry.sName,
+ oEntry.sDescription,
+ oEntry.fEnabled,
+ aoActions, ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminschedgroup.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminschedgroup.py
new file mode 100755
index 00000000..e7a43717
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminschedgroup.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminschedgroup.py $
+
+"""
+Test Manager WUI - Scheduling groups.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.buildsource import BuildSourceData, BuildSourceLogic;
+from testmanager.core.db import isDbTimestampInfinity;
+from testmanager.core.schedgroup import SchedGroupData, SchedGroupDataEx;
+from testmanager.core.testgroup import TestGroupData, TestGroupLogic;
+from testmanager.core.testbox import TestBoxLogic;
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink, WuiRawHtml;
+from testmanager.webui.wuiadmintestbox import WuiTestBoxDetailsLink;
+
+
+class WuiSchedGroup(WuiFormContentBase):
+ """
+ WUI Scheduling Groups HTML content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ assert isinstance(oData, SchedGroupData);
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'New Scheduling Group';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit Scheduling Group'
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Scheduling Group';
+ WuiFormContentBase.__init__(self, oData, sMode, 'SchedGroup', oDisp, sTitle);
+
+ # Read additional bits form the DB, unless we're in
+ if sMode != WuiFormContentBase.ksMode_Show:
+ self._aoAllRelevantTestGroups = TestGroupLogic(oDisp.getDb()).getAll();
+ self._aoAllRelevantTestBoxes = TestBoxLogic(oDisp.getDb()).getAll();
+ else:
+ self._aoAllRelevantTestGroups = [oMember.oTestGroup for oMember in oData.aoMembers];
+ self._aoAllRelevantTestBoxes = [oMember.oTestBox for oMember in oData.aoTestBoxes];
+
+ def _populateForm(self, oForm, oData): # type: (WuiHlpForm, SchedGroupDataEx) -> bool
+ """
+ Construct an HTML form
+ """
+
+ oForm.addIntRO( SchedGroupData.ksParam_idSchedGroup, oData.idSchedGroup, 'ID')
+ oForm.addTimestampRO(SchedGroupData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(SchedGroupData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO( SchedGroupData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+ oForm.addText( SchedGroupData.ksParam_sName, oData.sName, 'Name')
+ oForm.addText( SchedGroupData.ksParam_sDescription, oData.sDescription, 'Description')
+ oForm.addCheckBox( SchedGroupData.ksParam_fEnabled, oData.fEnabled, 'Enabled')
+
+ oForm.addComboBox( SchedGroupData.ksParam_enmScheduler, oData.enmScheduler, 'Scheduler type',
+ SchedGroupData.kasSchedulerDesc)
+
+ aoBuildSrcIds = BuildSourceLogic(self._oDisp.getDb()).fetchForCombo();
+ oForm.addComboBox( SchedGroupData.ksParam_idBuildSrc, oData.idBuildSrc, 'Build source', aoBuildSrcIds);
+ oForm.addComboBox( SchedGroupData.ksParam_idBuildSrcTestSuite,
+ oData.idBuildSrcTestSuite, 'Test suite', aoBuildSrcIds);
+
+ oForm.addListOfSchedGroupMembers(SchedGroupDataEx.ksParam_aoMembers,
+ oData.aoMembers, self._aoAllRelevantTestGroups, 'Test groups',
+ oData.idSchedGroup, fReadOnly = self._sMode == WuiFormContentBase.ksMode_Show);
+
+ oForm.addListOfSchedGroupBoxes(SchedGroupDataEx.ksParam_aoTestBoxes,
+ oData.aoTestBoxes, self._aoAllRelevantTestBoxes, 'Test boxes',
+ oData.idSchedGroup, fReadOnly = self._sMode == WuiFormContentBase.ksMode_Show);
+
+ oForm.addMultilineText(SchedGroupData.ksParam_sComment, oData.sComment, 'Comment');
+ oForm.addSubmit()
+
+ return True;
+
+class WuiAdminSchedGroupList(WuiListContentBase):
+ """
+ Content generator for the schedule group listing.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Registered Scheduling Groups', sId = 'schedgroups',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._asColumnHeaders = [
+ 'ID', 'Name', 'Enabled', 'Scheduler Type',
+ 'Build Source', 'Validation Kit Source', 'Test Groups', 'TestBoxes', 'Note', 'Actions',
+ ];
+
+ self._asColumnAttribs = [
+ 'align="right"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', '', '', 'align="center"', 'align="center"',
+ ];
+
+ def _formatListEntry(self, iEntry):
+ """
+ Format *show all* table entry
+ """
+ from testmanager.webui.wuiadmin import WuiAdmin
+ oEntry = self._aoEntries[iEntry] # type: SchedGroupDataEx
+
+ oBuildSrc = None;
+ if oEntry.idBuildSrc is not None:
+ oBuildSrc = WuiTmLink(oEntry.oBuildSrc.sName if oEntry.oBuildSrc else str(oEntry.idBuildSrc),
+ WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDetails,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrc, });
+
+ oValidationKitSrc = None;
+ if oEntry.idBuildSrcTestSuite is not None:
+ oValidationKitSrc = WuiTmLink(oEntry.oBuildSrcValidationKit.sName if oEntry.oBuildSrcValidationKit
+ else str(oEntry.idBuildSrcTestSuite),
+ WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildSrcDetails,
+ BuildSourceData.ksParam_idBuildSrc: oEntry.idBuildSrcTestSuite, });
+
+ # Test groups
+ aoMembers = [];
+ for oMember in oEntry.aoMembers:
+ aoMembers.append(WuiTmLink(oMember.oTestGroup.sName, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupDetails,
+ TestGroupData.ksParam_idTestGroup: oMember.idTestGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, },
+ sTitle = '#%s' % (oMember.idTestGroup,) if oMember.oTestGroup.sDescription is None
+ else '#%s - %s' % (oMember.idTestGroup, oMember.oTestGroup.sDescription,) ));
+
+ # Test boxes.
+ aoTestBoxes = [];
+ for oRelation in oEntry.aoTestBoxes:
+ oTestBox = oRelation.oTestBox;
+ if oTestBox:
+ aoTestBoxes.append(WuiTestBoxDetailsLink(oTestBox, fBracketed = True, tsNow = self._tsEffectiveDate));
+ else:
+ aoTestBoxes.append(WuiRawHtml('#%s' % (oRelation.idTestBox,)));
+
+ # Actions
+ aoActions = [ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupDetails,
+ SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
+ SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup } ));
+ aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupClone,
+ SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ));
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupDoRemove,
+ SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup },
+ sConfirm = 'Are you sure you want to remove scheduling group #%d?'
+ % (oEntry.idSchedGroup,)));
+
+ return [
+ oEntry.idSchedGroup,
+ oEntry.sName,
+ oEntry.fEnabled,
+ oEntry.enmScheduler,
+ oBuildSrc,
+ oValidationKitSrc,
+ aoMembers,
+ aoTestBoxes,
+ self._formatCommentCell(oEntry.sComment),
+ aoActions,
+ ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminschedqueue.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminschedqueue.py
new file mode 100755
index 00000000..88a3475a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminschedqueue.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+# "$Id: wuiadminschedqueue.py $"
+
+"""
+Test Manager WUI - Admin - Scheduling Queue.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports
+from testmanager.webui.wuicontentbase import WuiListContentBase
+
+
+class WuiAdminSchedQueueList(WuiListContentBase):
+ """
+ WUI Scheduling Queue Content Generator.
+ """
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ tsEffective = None; # Not relevant, no history on the scheduling queue.
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'Scheduling Queue',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns,
+ fTimeNavigation = False);
+ self._asColumnHeaders = [
+ 'Last Run', 'Scheduling Group', 'Test Group', 'Test Case', 'Config State', 'Item ID'
+ ];
+ self._asColumnAttribs = [
+ 'align="center"', 'align="center"', 'align="center"', 'align="center"', 'align="center"', 'align="center"'
+ ];
+ self._iPrevPerSchedGroupRowNumber = 0;
+
+ def _formatListEntry(self, iEntry):
+ oEntry = self._aoEntries[iEntry] # type: SchedQueueEntry
+ sState = 'up-to-date' if oEntry.fUpToDate else 'outdated';
+ return [ oEntry.tsLastScheduled, oEntry.sSchedGroup, oEntry.sTestGroup, oEntry.sTestCase, sState, oEntry.idItem ];
+
+ def _formatListEntryHtml(self, iEntry):
+ sHtml = WuiListContentBase._formatListEntryHtml(self, iEntry);
+
+ # Insert separator row?
+ if iEntry < len(self._aoEntries):
+ oEntry = self._aoEntries[iEntry] # type: SchedQueueEntry
+ if oEntry.iPerSchedGroupRowNumber != self._iPrevPerSchedGroupRowNumber:
+ if iEntry > 0 and iEntry + 1 < min(len(self._aoEntries), self._cItemsPerPage):
+ sHtml += '<tr class="tmseparator"><td colspan=%s> </td></tr>\n' % (len(self._asColumnHeaders),);
+ self._iPrevPerSchedGroupRowNumber = oEntry.iPerSchedGroupRowNumber;
+ return sHtml;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemchangelog.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemchangelog.py
new file mode 100755
index 00000000..94057524
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemchangelog.py
@@ -0,0 +1,447 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminsystemchangelog.py $
+
+"""
+Test Manager WUI - Admin - System changelog.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+from common import webutils;
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiListContentBase, WuiHtmlKeeper, WuiAdminLink, \
+ WuiMainLink, WuiElementText, WuiHtmlBase;
+
+from testmanager.core.base import AttributeChangeEntryPre;
+from testmanager.core.buildblacklist import BuildBlacklistLogic, BuildBlacklistData;
+from testmanager.core.build import BuildLogic, BuildData;
+from testmanager.core.buildsource import BuildSourceLogic, BuildSourceData;
+from testmanager.core.globalresource import GlobalResourceLogic, GlobalResourceData;
+from testmanager.core.failurecategory import FailureCategoryLogic, FailureCategoryData;
+from testmanager.core.failurereason import FailureReasonLogic, FailureReasonData;
+from testmanager.core.systemlog import SystemLogData;
+from testmanager.core.systemchangelog import SystemChangelogLogic;
+from testmanager.core.schedgroup import SchedGroupLogic, SchedGroupData;
+from testmanager.core.testbox import TestBoxLogic, TestBoxData;
+from testmanager.core.testcase import TestCaseLogic, TestCaseData;
+from testmanager.core.testgroup import TestGroupLogic, TestGroupData;
+from testmanager.core.testset import TestSetData;
+from testmanager.core.useraccount import UserAccountLogic, UserAccountData;
+
+
+class WuiAdminSystemChangelogList(WuiListContentBase):
+ """
+ WUI System Changelog Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ];
+ self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ];
+ self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb());
+ self._oBuildLogic = BuildLogic(oDisp.getDb());
+ self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb());
+ self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb());
+ self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb());
+ self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb());
+ self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb());
+ self._oTestBoxLogic = TestBoxLogic(oDisp.getDb());
+ self._oTestCaseLogic = TestCaseLogic(oDisp.getDb());
+ self._oTestGroupLogic = TestGroupLogic(oDisp.getDb());
+ self._oUserAccountLogic = UserAccountLogic(oDisp.getDb());
+ self._sPrevDate = '';
+ _ = cDaysBack;
+
+ # oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+ def _createBlacklistingDetailsLink(self, idBlacklisting, tsEffective):
+ """ Creates a link to the build source details. """
+ oBlacklisting = self._oBuildBlacklistLogic.cachedLookup(idBlacklisting);
+ if oBlacklisting is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink('Blacklisting #%u' % (oBlacklisting.idBlacklisting,),
+ WuiAdmin.ksActionBuildBlacklistDetails, tsEffective,
+ { BuildBlacklistData.ksParam_idBlacklisting: oBlacklisting.idBlacklisting },
+ fBracketed = False);
+ return WuiElementText('[blacklisting #%u not found]' % (idBlacklisting,));
+
+ def _createBuildDetailsLink(self, idBuild, tsEffective):
+ """ Creates a link to the build details. """
+ oBuild = self._oBuildLogic.cachedLookup(idBuild);
+ if oBuild is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink('%s %sr%u' % ( oBuild.oCat.sProduct, oBuild.sVersion, oBuild.iRevision),
+ WuiAdmin.ksActionBuildDetails, tsEffective,
+ { BuildData.ksParam_idBuild: oBuild.idBuild },
+ fBracketed = False,
+ sTitle = 'build #%u for %s, type %s'
+ % (oBuild.idBuild, ' & '.join(oBuild.oCat.asOsArches), oBuild.oCat.sType));
+ return WuiElementText('[build #%u not found]' % (idBuild,));
+
+ def _createBuildSourceDetailsLink(self, idBuildSrc, tsEffective):
+ """ Creates a link to the build source details. """
+ oBuildSource = self._oBuildSourceLogic.cachedLookup(idBuildSrc);
+ if oBuildSource is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oBuildSource.sName, WuiAdmin.ksActionBuildSrcDetails, tsEffective,
+ { BuildSourceData.ksParam_idBuildSrc: oBuildSource.idBuildSrc },
+ fBracketed = False,
+ sTitle = 'Build source #%u' % (oBuildSource.idBuildSrc,));
+ return WuiElementText('[build source #%u not found]' % (idBuildSrc,));
+
+ def _createFailureCategoryDetailsLink(self, idFailureCategory, tsEffective):
+ """ Creates a link to the failure category details. """
+ oFailureCategory = self._oFailureCategoryLogic.cachedLookup(idFailureCategory);
+ if oFailureCategory is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oFailureCategory.sShort, WuiAdmin.ksActionFailureCategoryDetails, tsEffective,
+ { FailureCategoryData.ksParam_idFailureCategory: oFailureCategory.idFailureCategory },
+ fBracketed = False,
+ sTitle = 'Failure category #%u' % (oFailureCategory.idFailureCategory,));
+ return WuiElementText('[failure category #%u not found]' % (idFailureCategory,));
+
+ def _createFailureReasonDetailsLink(self, idFailureReason, tsEffective):
+ """ Creates a link to the failure reason details. """
+ oFailureReason = self._oFailureReasonLogic.cachedLookup(idFailureReason);
+ if oFailureReason is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oFailureReason.sShort, WuiAdmin.ksActionFailureReasonDetails, tsEffective,
+ { FailureReasonData.ksParam_idFailureReason: oFailureReason.idFailureReason },
+ fBracketed = False,
+ sTitle = 'Failure reason #%u, category %s'
+ % (oFailureReason.idFailureReason, oFailureReason.oCategory.sShort));
+ return WuiElementText('[failure reason #%u not found]' % (idFailureReason,));
+
+ def _createGlobalResourceDetailsLink(self, idGlobalRsrc, tsEffective):
+ """ Creates a link to the global resource details. """
+ oGlobalResource = self._oGlobalResourceLogic.cachedLookup(idGlobalRsrc);
+ if oGlobalResource is not None:
+ return WuiAdminLink(oGlobalResource.sName, '@todo', tsEffective,
+ { GlobalResourceData.ksParam_idGlobalRsrc: oGlobalResource.idGlobalRsrc },
+ fBracketed = False,
+ sTitle = 'Global resource #%u' % (oGlobalResource.idGlobalRsrc,));
+ return WuiElementText('[global resource #%u not found]' % (idGlobalRsrc,));
+
+ def _createSchedGroupDetailsLink(self, idSchedGroup, tsEffective):
+ """ Creates a link to the scheduling group details. """
+ oSchedGroup = self._oSchedGroupLogic.cachedLookup(idSchedGroup);
+ if oSchedGroup is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oSchedGroup.sName, WuiAdmin.ksActionSchedGroupDetails, tsEffective,
+ { SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup },
+ fBracketed = False,
+ sTitle = 'Scheduling group #%u' % (oSchedGroup.idSchedGroup,));
+ return WuiElementText('[scheduling group #%u not found]' % (idSchedGroup,));
+
+ def _createTestBoxDetailsLink(self, idTestBox, tsEffective):
+ """ Creates a link to the testbox details. """
+ oTestBox = self._oTestBoxLogic.cachedLookup(idTestBox);
+ if oTestBox is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oTestBox.sName, WuiAdmin.ksActionTestBoxDetails, tsEffective,
+ { TestBoxData.ksParam_idTestBox: oTestBox.idTestBox },
+ fBracketed = False, sTitle = 'Testbox #%u' % (oTestBox.idTestBox,));
+ return WuiElementText('[testbox #%u not found]' % (idTestBox,));
+
+ def _createTestCaseDetailsLink(self, idTestCase, tsEffective):
+ """ Creates a link to the test case details. """
+ oTestCase = self._oTestCaseLogic.cachedLookup(idTestCase);
+ if oTestCase is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oTestCase.sName, WuiAdmin.ksActionTestCaseDetails, tsEffective,
+ { TestCaseData.ksParam_idTestCase: oTestCase.idTestCase },
+ fBracketed = False, sTitle = 'Test case #%u' % (oTestCase.idTestCase,));
+ return WuiElementText('[test case #%u not found]' % (idTestCase,));
+
+ def _createTestGroupDetailsLink(self, idTestGroup, tsEffective):
+ """ Creates a link to the test group details. """
+ oTestGroup = self._oTestGroupLogic.cachedLookup(idTestGroup);
+ if oTestGroup is not None:
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ return WuiAdminLink(oTestGroup.sName, WuiAdmin.ksActionTestGroupDetails, tsEffective,
+ { TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup },
+ fBracketed = False, sTitle = 'Test group #%u' % (oTestGroup.idTestGroup,));
+ return WuiElementText('[test group #%u not found]' % (idTestGroup,));
+
+ def _createTestSetResultsDetailsLink(self, idTestSet, tsEffective):
+ """ Creates a link to the test set results. """
+ _ = tsEffective;
+ from testmanager.webui.wuimain import WuiMain;
+ return WuiMainLink('test set #%u' % idTestSet, WuiMain.ksActionTestSetDetails,
+ { TestSetData.ksParam_idTestSet: idTestSet }, fBracketed = False);
+
+ def _createTestSetDetailsLinkByResult(self, idTestResult, tsEffective):
+ """ Creates a link to the test set results. """
+ _ = tsEffective;
+ from testmanager.webui.wuimain import WuiMain;
+ return WuiMainLink('test result #%u' % idTestResult, WuiMain.ksActionTestSetDetailsFromResult,
+ { TestSetData.ksParam_idTestResult: idTestResult }, fBracketed = False);
+
+ def _createUserAccountDetailsLink(self, uid, tsEffective):
+ """ Creates a link to the user account details. """
+ oUser = self._oUserAccountLogic.cachedLookup(uid);
+ if oUser is not None:
+ return WuiAdminLink(oUser.sUsername, '@todo', tsEffective, { UserAccountData.ksParam_uid: oUser.uid },
+ fBracketed = False, sTitle = '%s (#%u)' % (oUser.sFullName, oUser.uid));
+ return WuiElementText('[user #%u not found]' % (uid,));
+
+ def _formatDescGeneric(self, sDesc, oEntry):
+ """
+ Generically format system log the description.
+ """
+ oRet = WuiHtmlKeeper();
+ asWords = sDesc.split();
+ for sWord in asWords:
+ offEqual = sWord.find('=');
+ if offEqual > 0:
+ sKey = sWord[:offEqual];
+ try: idValue = int(sWord[offEqual+1:].rstrip('.,'));
+ except: pass;
+ else:
+ if sKey == 'idTestSet':
+ oRet.append(self._createTestSetResultsDetailsLink(idValue, oEntry.tsEffective));
+ continue;
+ if sKey == 'idTestBox':
+ oRet.append(self._createTestBoxDetailsLink(idValue, oEntry.tsEffective));
+ continue;
+ if sKey == 'idSchedGroup':
+ oRet.append(self._createSchedGroupDetailsLink(idValue, oEntry.tsEffective));
+ continue;
+
+ oRet.append(WuiElementText(sWord));
+ return oRet;
+
+ def _formatListEntryHtml(self, iEntry): # pylint: disable=too-many-statements
+ """
+ Overridden parent method.
+ """
+ oEntry = self._aoEntries[iEntry];
+ sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven';
+ sHtml = u'';
+
+ #
+ # Format the timestamp.
+ #
+ sDate = self.formatTsShort(oEntry.tsEffective);
+ if sDate[:10] != self._sPrevDate:
+ self._sPrevDate = sDate[:10];
+ sHtml += ' <tr class="%s tmdaterow" align="left"><td colspan="7">%s</td></tr>\n' % (sRowClass, sDate[:10],);
+ sDate = sDate[11:]
+
+ #
+ # System log events.
+ # pylint: disable=redefined-variable-type
+ #
+ aoChanges = None;
+ if oEntry.sEvent == SystemLogData.ksEvent_CmdNacked:
+ sEvent = 'Command not acknowleged';
+ oDetails = oEntry.sDesc;
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown:
+ sEvent = 'Unknown testbox';
+ oDetails = oEntry.sDesc;
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_TestSetAbandoned:
+ sEvent = 'Abandoned ' if oEntry.sDesc.startswith('idTestSet') else 'Abandoned test set';
+ oDetails = self._formatDescGeneric(oEntry.sDesc, oEntry);
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown:
+ sEvent = 'Unknown user account';
+ oDetails = oEntry.sDesc;
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_XmlResultMalformed:
+ sEvent = 'Malformed XML result';
+ oDetails = oEntry.sDesc;
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_SchedQueueRecreate:
+ sEvent = 'Recreating scheduling queue';
+ asWords = oEntry.sDesc.split();
+ if len(asWords) > 3 and asWords[0] == 'User' and asWords[1][0] == '#':
+ try: idAuthor = int(asWords[1][1:]);
+ except: pass;
+ else:
+ oEntry.oAuthor = self._oUserAccountLogic.cachedLookup(idAuthor);
+ if oEntry.oAuthor is not None:
+ i = 2;
+ if asWords[i] == 'recreated': i += 1;
+ oEntry.sDesc = ' '.join(asWords[i:]);
+ oDetails = self._formatDescGeneric(oEntry.sDesc.replace('sched queue #', 'for scheduling group idSchedGroup='),
+ oEntry);
+ #
+ # System changelog events.
+ #
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Blacklisting:
+ sEvent = 'Modified blacklisting';
+ oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Build:
+ sEvent = 'Modified build';
+ oDetails = self._createBuildDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_BuildSource:
+ sEvent = 'Modified build source';
+ oDetails = self._createBuildSourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_GlobalRsrc:
+ sEvent = 'Modified global resource';
+ oDetails = self._createGlobalResourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureCategory:
+ sEvent = 'Modified failure category';
+ oDetails = self._createFailureCategoryDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+ (aoChanges, _) = self._oFailureCategoryLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureReason:
+ sEvent = 'Modified failure reason';
+ oDetails = self._createFailureReasonDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+ (aoChanges, _) = self._oFailureReasonLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_SchedGroup:
+ sEvent = 'Modified scheduling group';
+ oDetails = self._createSchedGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestBox:
+ sEvent = 'Modified testbox';
+ oDetails = self._createTestBoxDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+ (aoChanges, _) = self._oTestBoxLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestCase:
+ sEvent = 'Modified test case';
+ oDetails = self._createTestCaseDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+ (aoChanges, _) = self._oTestCaseLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestGroup:
+ sEvent = 'Modified test group';
+ oDetails = self._createTestGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestResult:
+ sEvent = 'Modified test failure reason';
+ oDetails = self._createTestSetDetailsLinkByResult(oEntry.idWhat, oEntry.tsEffective);
+
+ elif oEntry.sEvent == SystemChangelogLogic.ksWhat_User:
+ sEvent = 'Modified user account';
+ oDetails = self._createUserAccountDetailsLink(oEntry.idWhat, oEntry.tsEffective);
+
+ else:
+ sEvent = '%s(%s)' % (oEntry.sEvent, oEntry.idWhat,);
+ oDetails = '!Unknown event!' + (oEntry.sDesc if oEntry.sDesc else '');
+
+ #
+ # Do the formatting.
+ #
+
+ if aoChanges:
+ oChangeEntry = aoChanges[0];
+ cAttribsChanged = len(oChangeEntry.aoChanges) + 1;
+ if oChangeEntry.oOldRaw is None and sEvent.startswith('Modified '):
+ sEvent = 'Created ' + sEvent[9:];
+
+ else:
+ oChangeEntry = None;
+ cAttribsChanged = -1;
+
+ sHtml += u' <tr class="%s">\n' \
+ u' <td rowspan="%d" align="center" >%s</td>\n' \
+ u' <td rowspan="%d" align="center" >%s</td>\n' \
+ u' <td colspan="5" class="%s%s">%s %s</td>\n' \
+ u' </tr>\n' \
+ % ( sRowClass,
+ 1 + cAttribsChanged + 1, sDate,
+ 1 + cAttribsChanged + 1, webutils.escapeElem(oEntry.oAuthor.sUsername if oEntry.oAuthor is not None else ''),
+ sRowClass, ' tmsyschlogevent' if oChangeEntry is not None else '', webutils.escapeElem(sEvent),
+ oDetails.toHtml() if isinstance(oDetails, WuiHtmlBase) else oDetails,
+ );
+
+ if oChangeEntry is not None:
+ sHtml += u' <tr class="%s tmsyschlogspacerrowabove">\n' \
+ u' <td xrowspan="%d" style="border-right: 0px; border-bottom: 0px;"></td>\n' \
+ u' <td colspan="3" style="border-right: 0px;"></td>\n' \
+ u' <td rowspan="%d" class="%s tmsyschlogspacer"></td>\n' \
+ u' </tr>\n' \
+ % (sRowClass, cAttribsChanged + 1, cAttribsChanged + 1, sRowClass);
+ for j, oChange in enumerate(oChangeEntry.aoChanges):
+ fLastRow = j + 1 == len(oChangeEntry.aoChanges);
+ sHtml += u' <tr class="%s%s tmsyschlogattr%s">\n' \
+ % ( sRowClass, 'odd' if j & 1 else 'even', ' tmsyschlogattrfinal' if fLastRow else '',);
+ if j == 0:
+ sHtml += u' <td class="%s tmsyschlogspacer" rowspan="%d"></td>\n' % (sRowClass, cAttribsChanged - 1,);
+
+ if isinstance(oChange, AttributeChangeEntryPre):
+ sHtml += u' <td class="%s%s">%s</td>\n' \
+ u' <td><div class="tdpre"><pre>%s</pre></div></td>\n' \
+ u' <td class="%s%s"><div class="tdpre"><pre>%s</pre></div></td>\n' \
+ % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
+ webutils.escapeElem(oChange.sAttr),
+ webutils.escapeElem(oChange.sOldText),
+ ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
+ webutils.escapeElem(oChange.sNewText), );
+ else:
+ sHtml += u' <td class="%s%s">%s</td>\n' \
+ u' <td>%s</td>\n' \
+ u' <td class="%s%s">%s</td>\n' \
+ % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
+ webutils.escapeElem(oChange.sAttr),
+ webutils.escapeElem(oChange.sOldText),
+ ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
+ webutils.escapeElem(oChange.sNewText), );
+ sHtml += u' </tr>\n';
+
+ if oChangeEntry is not None:
+ sHtml += u' <tr class="%s tmsyschlogspacerrowbelow "><td colspan="5"></td></tr>\n\n' % (sRowClass,);
+ return sHtml;
+
+
+ def _generateTableHeaders(self):
+ """
+ Overridden parent method.
+ """
+
+ sHtml = u'<thead class="tmheader">\n' \
+ u' <tr>\n' \
+ u' <th rowspan="2">When</th>\n' \
+ u' <th rowspan="2">Who</th>\n' \
+ u' <th colspan="5">Event</th>\n' \
+ u' </tr>\n' \
+ u' <tr>\n' \
+ u' <th style="border-right: 0px;"></th>\n' \
+ u' <th>Attribute</th>\n' \
+ u' <th>Old</th>\n' \
+ u' <th style="border-right: 0px;">New</th>\n' \
+ u' <th></th>\n' \
+ u' </tr>\n' \
+ u'</thead>\n';
+ return sHtml;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemdbdump.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemdbdump.py
new file mode 100755
index 00000000..b0e6f99c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemdbdump.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminsystemdbdump.py $
+
+"""
+Test Manager WUI - System DB - Partial Dumping
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.core.base import ModelDataBase;
+from testmanager.webui.wuicontentbase import WuiFormContentBase;
+
+
+class WuiAdminSystemDbDumpForm(WuiFormContentBase):
+ """
+ WUI Partial DB Dump HTML content generator.
+ """
+
+ def __init__(self, cDaysBack, oDisp):
+ WuiFormContentBase.__init__(self, ModelDataBase(),
+ WuiFormContentBase.ksMode_Edit, 'DbDump', oDisp, 'Partial DB Dump',
+ sSubmitAction = oDisp.ksActionSystemDbDumpDownload);
+ self._cDaysBack = cDaysBack;
+
+ def _generateTopRowFormActions(self, oData):
+ _ = oData;
+ return [];
+
+ def _populateForm(self, oForm, oData): # type: (WuiHlpForm, SchedGroupDataEx) -> bool
+ """
+ Construct an HTML form
+ """
+ _ = oData;
+
+ oForm.addInt(self._oDisp.ksParamDaysBack, self._cDaysBack, 'How many days back to dump');
+ oForm.addSubmit('Produce & Download');
+
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemlog.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemlog.py
new file mode 100755
index 00000000..6b3b7a45
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemlog.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminsystemlog.py $
+
+"""
+Test Manager WUI - Admin - System Log.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiListContentBase, WuiTmLink;
+from testmanager.core.testbox import TestBoxData;
+from testmanager.core.systemlog import SystemLogData;
+from testmanager.core.useraccount import UserAccountData;
+
+
+class WuiAdminSystemLogList(WuiListContentBase):
+ """
+ WUI System Log Content Generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Log',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = ['Date', 'Event', 'Message', 'Action'];
+ self._asColumnAttribs = ['', '', '', 'align="center"'];
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ oEntry = self._aoEntries[iEntry];
+
+ oAction = None
+
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ if oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown \
+ and oEntry.sLogText.find('addr=') >= 0 \
+ and oEntry.sLogText.find('uuid=') >= 0:
+ sUuid = (oEntry.sLogText[(oEntry.sLogText.find('uuid=') + 5):])[:36];
+ sAddr = (oEntry.sLogText[(oEntry.sLogText.find('addr=') + 5):]).split(' ')[0];
+ oAction = WuiTmLink('Add TestBox', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxAdd,
+ TestBoxData.ksParam_uuidSystem: sUuid,
+ TestBoxData.ksParam_ip: sAddr });
+
+ elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown:
+ sUserName = oEntry.sLogText[oEntry.sLogText.find('(') + 1:
+ oEntry.sLogText.find(')')]
+ oAction = WuiTmLink('Add User', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserAdd,
+ UserAccountData.ksParam_sLoginName: sUserName });
+
+ return [oEntry.tsCreated, oEntry.sEvent, oEntry.sLogText, oAction];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
new file mode 100755
index 00000000..a06e670c
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
@@ -0,0 +1,490 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadmintestbox.py $
+
+"""
+Test Manager WUI - TestBox.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import socket;
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager.webui.wuicontentbase import WuiContentBase, WuiListContentWithActionBase, WuiFormContentBase, WuiLinkBase, \
+ WuiSvnLink, WuiTmLink, WuiSpanText, WuiRawHtml;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.schedgroup import SchedGroupLogic, SchedGroupData;
+from testmanager.core.testbox import TestBoxData, TestBoxDataEx, TestBoxLogic;
+from testmanager.core.testset import TestSetData;
+from testmanager.core.db import isDbTimestampInfinity;
+
+
+
+class WuiTestBoxDetailsLinkById(WuiTmLink):
+ """ Test box details link by ID. """
+
+ def __init__(self, idTestBox, sName = WuiContentBase.ksShortDetailsLink, fBracketed = False, tsNow = None, sTitle = None):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ dParams = {
+ WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
+ TestBoxData.ksParam_idTestBox: idTestBox,
+ };
+ if tsNow is not None:
+ dParams[WuiAdmin.ksParamEffectiveDate] = tsNow; ## ??
+ WuiTmLink.__init__(self, sName, WuiAdmin.ksScriptName, dParams, fBracketed = fBracketed, sTitle = sTitle);
+ self.idTestBox = idTestBox;
+
+
+class WuiTestBoxDetailsLink(WuiTestBoxDetailsLinkById):
+ """ Test box details link by TestBoxData instance. """
+
+ def __init__(self, oTestBox, sName = None, fBracketed = False, tsNow = None): # (TestBoxData, str, bool, Any) -> None
+ WuiTestBoxDetailsLinkById.__init__(self, oTestBox.idTestBox,
+ sName if sName else oTestBox.sName,
+ fBracketed = fBracketed,
+ tsNow = tsNow,
+ sTitle = self.formatTitleText(oTestBox));
+ self.oTestBox = oTestBox;
+
+ @staticmethod
+ def formatTitleText(oTestBox): # (TestBoxData) -> str
+ """
+ Formats the title text for a TestBoxData object.
+ """
+
+ # Note! Somewhat similar code is found in testresults.py
+
+ #
+ # Collect field/value tuples.
+ #
+ aasTestBoxTitle = [
+ (u'Identifier:', '#%u' % (oTestBox.idTestBox,),),
+ (u'Name:', oTestBox.sName,),
+ ];
+ if oTestBox.sCpuVendor:
+ aasTestBoxTitle.append((u'CPU\u00a0vendor:', oTestBox.sCpuVendor, ));
+ if oTestBox.sCpuName:
+ aasTestBoxTitle.append((u'CPU\u00a0name:', u'\u00a0'.join(oTestBox.sCpuName.split()),));
+ if oTestBox.cCpus:
+ aasTestBoxTitle.append((u'CPU\u00a0threads:', u'%s' % ( oTestBox.cCpus, ),));
+
+ asFeatures = [];
+ if oTestBox.fCpuHwVirt is True:
+ if oTestBox.sCpuVendor is None:
+ asFeatures.append(u'HW\u2011Virt');
+ elif oTestBox.sCpuVendor in ['AuthenticAMD',]:
+ asFeatures.append(u'HW\u2011Virt(AMD\u2011V)');
+ else:
+ asFeatures.append(u'HW\u2011Virt(VT\u2011x)');
+ if oTestBox.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
+ if oTestBox.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
+ if oTestBox.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
+ aasTestBoxTitle.append((u'CPU\u00a0features:', u',\u00a0'.join(asFeatures),));
+
+ if oTestBox.cMbMemory:
+ aasTestBoxTitle.append((u'System\u00a0RAM:', u'%s MiB' % ( oTestBox.cMbMemory, ),));
+ if oTestBox.sOs:
+ aasTestBoxTitle.append((u'OS:', oTestBox.sOs, ));
+ if oTestBox.sCpuArch:
+ aasTestBoxTitle.append((u'OS\u00a0arch:', oTestBox.sCpuArch,));
+ if oTestBox.sOsVersion:
+ aasTestBoxTitle.append((u'OS\u00a0version:', u'\u00a0'.join(oTestBox.sOsVersion.split()),));
+ if oTestBox.ip:
+ aasTestBoxTitle.append((u'IP\u00a0address:', u'%s' % ( oTestBox.ip, ),));
+
+ #
+ # Do a guestimation of the max field name width and pad short
+ # names when constructing the title text lines.
+ #
+ cchMaxWidth = 0;
+ for sEntry, _ in aasTestBoxTitle:
+ cchMaxWidth = max(WuiTestBoxDetailsLink.estimateStringWidth(sEntry), cchMaxWidth);
+ asTestBoxTitle = [];
+ for sEntry, sValue in aasTestBoxTitle:
+ asTestBoxTitle.append(u'%s%s\t\t%s'
+ % (sEntry, WuiTestBoxDetailsLink.getStringWidthPadding(sEntry, cchMaxWidth), sValue));
+
+ return u'\n'.join(asTestBoxTitle);
+
+
+class WuiTestBoxDetailsLinkShort(WuiTestBoxDetailsLink):
+ """ Test box details link by TestBoxData instance, but with ksShortDetailsLink as default name. """
+
+ def __init__(self, oTestBox, sName = WuiContentBase.ksShortDetailsLink, fBracketed = False,
+ tsNow = None): # (TestBoxData, str, bool, Any) -> None
+ WuiTestBoxDetailsLink.__init__(self, oTestBox, sName = sName, fBracketed = fBracketed, tsNow = tsNow);
+
+
+class WuiTestBox(WuiFormContentBase):
+ """
+ WUI TestBox Form Content Generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Create TextBox';
+ if oData.uuidSystem is not None and len(oData.uuidSystem) > 10:
+ sTitle += ' - ' + oData.uuidSystem;
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit TestBox - %s (#%s)' % (oData.sName, oData.idTestBox);
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'TestBox - %s (#%s)' % (oData.sName, oData.idTestBox);
+ WuiFormContentBase.__init__(self, oData, sMode, 'TestBox', oDisp, sTitle);
+
+ # Try enter sName as hostname (no domain) when creating the testbox.
+ if sMode == WuiFormContentBase.ksMode_Add \
+ and self._oData.sName in [None, ''] \
+ and self._oData.ip not in [None, '']:
+ try:
+ (self._oData.sName, _, _) = socket.gethostbyaddr(self._oData.ip);
+ except:
+ pass;
+ offDot = self._oData.sName.find('.');
+ if offDot > 0:
+ self._oData.sName = self._oData.sName[:offDot];
+
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO( TestBoxData.ksParam_idTestBox, oData.idTestBox, 'TestBox ID');
+ oForm.addIntRO( TestBoxData.ksParam_idGenTestBox, oData.idGenTestBox, 'TestBox generation ID');
+ oForm.addTimestampRO(TestBoxData.ksParam_tsEffective, oData.tsEffective, 'Last changed');
+ oForm.addTimestampRO(TestBoxData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)');
+ oForm.addIntRO( TestBoxData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID');
+
+ oForm.addText( TestBoxData.ksParam_ip, oData.ip, 'TestBox IP Address'); ## make read only??
+ oForm.addUuid( TestBoxData.ksParam_uuidSystem, oData.uuidSystem, 'TestBox System/Firmware UUID');
+ oForm.addText( TestBoxData.ksParam_sName, oData.sName, 'TestBox Name');
+ oForm.addText( TestBoxData.ksParam_sDescription, oData.sDescription, 'TestBox Description');
+ oForm.addCheckBox( TestBoxData.ksParam_fEnabled, oData.fEnabled, 'Enabled');
+ oForm.addComboBox( TestBoxData.ksParam_enmLomKind, oData.enmLomKind, 'Lights-out-management',
+ TestBoxData.kaoLomKindDescs);
+ oForm.addText( TestBoxData.ksParam_ipLom, oData.ipLom, 'Lights-out-management IP Address');
+ oForm.addInt( TestBoxData.ksParam_pctScaleTimeout, oData.pctScaleTimeout, 'Timeout scale factor (%)');
+
+ oForm.addListOfSchedGroupsForTestBox(TestBoxDataEx.ksParam_aoInSchedGroups,
+ oData.aoInSchedGroups,
+ SchedGroupLogic(TMDatabaseConnection()).fetchOrderedByName(),
+ 'Scheduling Group', oData.idTestBox);
+ # Command, comment and submit button.
+ if self._sMode == WuiFormContentBase.ksMode_Edit:
+ oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command',
+ TestBoxData.kaoTestBoxCmdDescs);
+ else:
+ oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command',
+ TestBoxData.kaoTestBoxCmdDescs);
+ oForm.addMultilineText(TestBoxData.ksParam_sComment, oData.sComment, 'Comment');
+ if self._sMode != WuiFormContentBase.ksMode_Show:
+ oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox');
+
+ return True;
+
+
+ def _generatePostFormContent(self, oData):
+ from testmanager.webui.wuihlpform import WuiHlpForm;
+
+ oForm = WuiHlpForm('testbox-machine-settable', '', fReadOnly = True);
+ oForm.addTextRO( TestBoxData.ksParam_sOs, oData.sOs, 'TestBox OS');
+ oForm.addTextRO( TestBoxData.ksParam_sOsVersion, oData.sOsVersion, 'TestBox OS version');
+ oForm.addTextRO( TestBoxData.ksParam_sCpuArch, oData.sCpuArch, 'TestBox OS kernel architecture');
+ oForm.addTextRO( TestBoxData.ksParam_sCpuVendor, oData.sCpuVendor, 'TestBox CPU vendor');
+ oForm.addTextRO( TestBoxData.ksParam_sCpuName, oData.sCpuName, 'TestBox CPU name');
+ if oData.lCpuRevision:
+ oForm.addTextRO( TestBoxData.ksParam_lCpuRevision, '%#x' % (oData.lCpuRevision,), 'TestBox CPU revision',
+ sPostHtml = ' (family=%#x model=%#x stepping=%#x)'
+ % (oData.getCpuFamily(), oData.getCpuModel(), oData.getCpuStepping(),),
+ sSubClass = 'long');
+ else:
+ oForm.addLongRO( TestBoxData.ksParam_lCpuRevision, oData.lCpuRevision, 'TestBox CPU revision');
+ oForm.addIntRO( TestBoxData.ksParam_cCpus, oData.cCpus, 'Number of CPUs, cores and threads');
+ oForm.addCheckBoxRO( TestBoxData.ksParam_fCpuHwVirt, oData.fCpuHwVirt, 'VT-x or AMD-V supported');
+ oForm.addCheckBoxRO( TestBoxData.ksParam_fCpuNestedPaging, oData.fCpuNestedPaging, 'Nested paging supported');
+ oForm.addCheckBoxRO( TestBoxData.ksParam_fCpu64BitGuest, oData.fCpu64BitGuest, '64-bit guest supported');
+ oForm.addCheckBoxRO( TestBoxData.ksParam_fChipsetIoMmu, oData.fChipsetIoMmu, 'I/O MMU supported');
+ oForm.addMultilineTextRO(TestBoxData.ksParam_sReport, oData.sReport, 'Hardware/software report');
+ oForm.addLongRO( TestBoxData.ksParam_cMbMemory, oData.cMbMemory, 'Installed RAM size (MB)');
+ oForm.addLongRO( TestBoxData.ksParam_cMbScratch, oData.cMbScratch, 'Available scratch space (MB)');
+ oForm.addIntRO( TestBoxData.ksParam_iTestBoxScriptRev, oData.iTestBoxScriptRev,
+ 'TestBox Script SVN revision');
+ sHexVer = oData.formatPythonVersion();
+ oForm.addIntRO( TestBoxData.ksParam_iPythonHexVersion, oData.iPythonHexVersion,
+ 'Python version (hex)', sPostHtml = webutils.escapeElem(sHexVer));
+ return [('Machine Only Settables', oForm.finalize()),];
+
+
+
+class WuiTestBoxList(WuiListContentWithActionBase):
+ """
+ WUI TestBox List Content Generator.
+ """
+
+ ## Descriptors for the combo box.
+ kasTestBoxActionDescs = \
+ [ \
+ [ 'none', 'Select an action...', '' ],
+ [ 'enable', 'Enable', '' ],
+ [ 'disable', 'Disable', '' ],
+ TestBoxData.kaoTestBoxCmdDescs[1],
+ TestBoxData.kaoTestBoxCmdDescs[2],
+ TestBoxData.kaoTestBoxCmdDescs[3],
+ TestBoxData.kaoTestBoxCmdDescs[4],
+ TestBoxData.kaoTestBoxCmdDescs[5],
+ ];
+
+ ## Boxes which doesn't report in for more than 15 min are considered dead.
+ kcSecMaxStatusDeltaAlive = 15*60
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ # type: (list[TestBoxDataForListing], int, int, datetime.datetime, ignore, WuiAdmin) -> None
+ WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp,
+ aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders.extend([ 'Name', 'LOM', 'Status', 'Cmd',
+ 'Note', 'Script', 'Python', 'Group',
+ 'OS', 'CPU', 'Features', 'CPUs', 'RAM', 'Scratch',
+ 'Actions' ]);
+ self._asColumnAttribs.extend([ 'align="center"', 'align="center"', 'align="center"', 'align="center"'
+ 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ '', '', '', 'align="left"', 'align="right"', 'align="right"', 'align="right"',
+ 'align="center"' ]);
+ self._aaiColumnSorting.extend([
+ (TestBoxLogic.kiSortColumn_sName,),
+ None, # LOM
+ (-TestBoxLogic.kiSortColumn_fEnabled, TestBoxLogic.kiSortColumn_enmState, -TestBoxLogic.kiSortColumn_tsUpdated,),
+ (TestBoxLogic.kiSortColumn_enmPendingCmd,),
+ None, # Note
+ (TestBoxLogic.kiSortColumn_iTestBoxScriptRev,),
+ (TestBoxLogic.kiSortColumn_iPythonHexVersion,),
+ None, # Group
+ (TestBoxLogic.kiSortColumn_sOs, TestBoxLogic.kiSortColumn_sOsVersion, TestBoxLogic.kiSortColumn_sCpuArch,),
+ (TestBoxLogic.kiSortColumn_sCpuVendor, TestBoxLogic.kiSortColumn_lCpuRevision,),
+ (TestBoxLogic.kiSortColumn_fCpuNestedPaging,),
+ (TestBoxLogic.kiSortColumn_cCpus,),
+ (TestBoxLogic.kiSortColumn_cMbMemory,),
+ (TestBoxLogic.kiSortColumn_cMbScratch,),
+ None, # Actions
+ ]);
+ assert len(self._aaiColumnSorting) == len(self._asColumnHeaders);
+ self._aoActions = list(self.kasTestBoxActionDescs);
+ self._sAction = oDisp.ksActionTestBoxListPost;
+ self._sCheckboxName = TestBoxData.ksParam_idTestBox;
+
+ def show(self, fShowNavigation = True):
+ """ Adds some stats at the bottom of the page """
+ (sTitle, sBody) = super(WuiTestBoxList, self).show(fShowNavigation);
+
+ # Count boxes in interesting states.
+ if self._aoEntries:
+ cActive = 0;
+ cDead = 0;
+ for oTestBox in self._aoEntries:
+ if oTestBox.oStatus is not None:
+ oDelta = oTestBox.tsCurrent - oTestBox.oStatus.tsUpdated;
+ if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
+ if oTestBox.fEnabled:
+ cActive += 1;
+ else:
+ cDead += 1;
+ else:
+ cDead += 1;
+ sBody += '<div id="testboxsummary"><p>\n' \
+ '%s testboxes of which %s are active and %s dead' \
+ '</p></div>\n' \
+ % (len(self._aoEntries), cActive, cDead,)
+ return (sTitle, sBody);
+
+ def _formatListEntry(self, iEntry): # pylint: disable=too-many-locals
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ oEntry = self._aoEntries[iEntry];
+
+ # Lights outs managment.
+ if oEntry.enmLomKind == TestBoxData.ksLomKind_ILOM:
+ aoLom = [ WuiLinkBase('ILOM', 'https://%s/' % (oEntry.ipLom,), fBracketed = False), ];
+ elif oEntry.enmLomKind == TestBoxData.ksLomKind_ELOM:
+ aoLom = [ WuiLinkBase('ELOM', 'http://%s/' % (oEntry.ipLom,), fBracketed = False), ];
+ elif oEntry.enmLomKind == TestBoxData.ksLomKind_AppleXserveLom:
+ aoLom = [ 'Apple LOM' ];
+ elif oEntry.enmLomKind == TestBoxData.ksLomKind_None:
+ aoLom = [ 'none' ];
+ else:
+ aoLom = [ 'Unexpected enmLomKind value "%s"' % (oEntry.enmLomKind,) ];
+ if oEntry.ipLom is not None:
+ if oEntry.enmLomKind in [ TestBoxData.ksLomKind_ILOM, TestBoxData.ksLomKind_ELOM ]:
+ aoLom += [ WuiLinkBase('(ssh)', 'ssh://%s' % (oEntry.ipLom,), fBracketed = False) ];
+ aoLom += [ WuiRawHtml('<br>'), '%s' % (oEntry.ipLom,) ];
+
+ # State and Last seen.
+ if oEntry.oStatus is None:
+ oSeen = WuiSpanText('tmspan-offline', 'Never');
+ oState = '';
+ else:
+ oDelta = oEntry.tsCurrent - oEntry.oStatus.tsUpdated;
+ if oDelta.days <= 0 and oDelta.seconds <= self.kcSecMaxStatusDeltaAlive:
+ oSeen = WuiSpanText('tmspan-online', u'%s\u00a0s\u00a0ago' % (oDelta.days * 24 * 3600 + oDelta.seconds,));
+ else:
+ oSeen = WuiSpanText('tmspan-offline', u'%s' % (self.formatTsShort(oEntry.oStatus.tsUpdated),));
+
+ if oEntry.oStatus.idTestSet is None:
+ oState = str(oEntry.oStatus.enmState);
+ else:
+ from testmanager.webui.wuimain import WuiMain;
+ oState = WuiTmLink(oEntry.oStatus.enmState, WuiMain.ksScriptName, # pylint: disable=redefined-variable-type
+ { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
+ TestSetData.ksParam_idTestSet: oEntry.oStatus.idTestSet, },
+ sTitle = '#%u' % (oEntry.oStatus.idTestSet,),
+ fBracketed = False);
+ # Comment
+ oComment = self._formatCommentCell(oEntry.sComment);
+
+ # Group links.
+ aoGroups = [];
+ for oInGroup in oEntry.aoInSchedGroups:
+ oSchedGroup = oInGroup.oSchedGroup;
+ aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
+ SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, },
+ sTitle = '#%u' % (oSchedGroup.idSchedGroup,),
+ fBracketed = len(oEntry.aoInSchedGroups) > 1));
+
+ # Reformat the OS version to take less space.
+ aoOs = [ 'N/A' ];
+ if oEntry.sOs is not None and oEntry.sOsVersion is not None and oEntry.sCpuArch:
+ sOsVersion = oEntry.sOsVersion;
+ if sOsVersion[0] not in [ 'v', 'V', 'r', 'R'] \
+ and sOsVersion[0].isdigit() \
+ and sOsVersion.find('.') in range(4) \
+ and oEntry.sOs in [ 'linux', 'solaris', 'darwin', ]:
+ sOsVersion = 'v' + sOsVersion;
+
+ sVer1 = sOsVersion;
+ sVer2 = None;
+ if oEntry.sOs in ('linux', 'darwin'):
+ iSep = sOsVersion.find(' / ');
+ if iSep > 0:
+ sVer1 = sOsVersion[:iSep].strip();
+ sVer2 = sOsVersion[iSep + 3:].strip();
+ sVer2 = sVer2.replace('Red Hat Enterprise Linux Server', 'RHEL');
+ sVer2 = sVer2.replace('Oracle Linux Server', 'OL');
+ elif oEntry.sOs == 'solaris':
+ iSep = sOsVersion.find(' (');
+ if iSep > 0 and sOsVersion[-1] == ')':
+ sVer1 = sOsVersion[:iSep].strip();
+ sVer2 = sOsVersion[iSep + 2:-1].strip();
+ elif oEntry.sOs == 'win':
+ iSep = sOsVersion.find('build');
+ if iSep > 0:
+ sVer1 = sOsVersion[:iSep].strip();
+ sVer2 = 'B' + sOsVersion[iSep + 1:].strip();
+ aoOs = [
+ WuiSpanText('tmspan-osarch', u'%s.%s' % (oEntry.sOs, oEntry.sCpuArch,)),
+ WuiSpanText('tmspan-osver1', sVer1.replace('-', u'\u2011'),),
+ ];
+ if sVer2 is not None:
+ aoOs += [ WuiRawHtml('<br>'), WuiSpanText('tmspan-osver2', sVer2.replace('-', u'\u2011')), ];
+
+ # Format the CPU revision.
+ oCpu = None;
+ if oEntry.lCpuRevision is not None and oEntry.sCpuVendor is not None and oEntry.sCpuName is not None:
+ oCpu = [
+ u'%s (fam:%xh\u00a0m:%xh\u00a0s:%xh)'
+ % (oEntry.sCpuVendor, oEntry.getCpuFamily(), oEntry.getCpuModel(), oEntry.getCpuStepping(),),
+ WuiRawHtml('<br>'),
+ oEntry.sCpuName,
+ ];
+ else:
+ oCpu = [];
+ if oEntry.sCpuVendor is not None:
+ oCpu.append(oEntry.sCpuVendor);
+ if oEntry.lCpuRevision is not None:
+ oCpu.append('%#x' % (oEntry.lCpuRevision,));
+ if oEntry.sCpuName is not None:
+ oCpu.append(oEntry.sCpuName);
+
+ # Stuff cpu vendor and cpu/box features into one field.
+ asFeatures = []
+ if oEntry.fCpuHwVirt is True: asFeatures.append(u'HW\u2011Virt');
+ if oEntry.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
+ if oEntry.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
+ if oEntry.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
+ sFeatures = u' '.join(asFeatures) if asFeatures else u'';
+
+ # Collection applicable actions.
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
+ TestBoxData.ksParam_idTestBox: oEntry.idTestBox,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, } ),
+ ]
+
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions += [
+ WuiTmLink('Edit', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxEdit,
+ TestBoxData.ksParam_idTestBox: oEntry.idTestBox, } ),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxRemovePost,
+ TestBoxData.ksParam_idTestBox: oEntry.idTestBox },
+ sConfirm = 'Are you sure that you want to remove %s (%s)?' % (oEntry.sName, oEntry.ip) ),
+ ]
+
+ if oEntry.sOs not in [ 'win', 'os2', ] and oEntry.ip is not None:
+ aoActions.append(WuiLinkBase('ssh', 'ssh://vbox@%s' % (oEntry.ip,),));
+
+ return [ self._getCheckBoxColumn(iEntry, oEntry.idTestBox),
+ [ WuiSpanText('tmspan-name', oEntry.sName), WuiRawHtml('<br>'), '%s' % (oEntry.ip,),],
+ aoLom,
+ [
+ '' if oEntry.fEnabled else 'disabled / ',
+ oState,
+ WuiRawHtml('<br>'),
+ oSeen,
+ ],
+ oEntry.enmPendingCmd,
+ oComment,
+ WuiSvnLink(oEntry.iTestBoxScriptRev),
+ oEntry.formatPythonVersion(),
+ aoGroups,
+ aoOs,
+ oCpu,
+ sFeatures,
+ oEntry.cCpus if oEntry.cCpus is not None else 'N/A',
+ utils.formatNumberNbsp(oEntry.cMbMemory) + u'\u00a0MB' if oEntry.cMbMemory is not None else 'N/A',
+ utils.formatNumberNbsp(oEntry.cMbScratch) + u'\u00a0MB' if oEntry.cMbScratch is not None else 'N/A',
+ aoActions,
+ ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadmintestcase.py b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestcase.py
new file mode 100755
index 00000000..19fa86c9
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestcase.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadmintestcase.py $
+
+"""
+Test Manager WUI - Test Cases.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiContentBase, WuiTmLink, WuiRawHtml;
+from testmanager.core.db import isDbTimestampInfinity;
+from testmanager.core.testcase import TestCaseDataEx, TestCaseData, TestCaseDependencyLogic;
+from testmanager.core.globalresource import GlobalResourceData, GlobalResourceLogic;
+
+
+
+class WuiTestCaseDetailsLink(WuiTmLink):
+ """ Test case details link by ID. """
+
+ def __init__(self, idTestCase, sName = WuiContentBase.ksShortDetailsLink, fBracketed = False, tsNow = None):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ dParams = {
+ WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDetails,
+ TestCaseData.ksParam_idTestCase: idTestCase,
+ };
+ if tsNow is not None:
+ dParams[WuiAdmin.ksParamEffectiveDate] = tsNow; ## ??
+ WuiTmLink.__init__(self, sName, WuiAdmin.ksScriptName, dParams, fBracketed = fBracketed);
+ self.idTestCase = idTestCase;
+
+
+class WuiTestCaseList(WuiListContentBase):
+ """
+ WUI test case list content generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, sTitle = 'Test Cases',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = \
+ [
+ 'Name', 'Active', 'Timeout', 'Base Command / Variations', 'Validation Kit Files',
+ 'Test Case Prereqs', 'Global Rsrces', 'Note', 'Actions'
+ ];
+ self._asColumnAttribs = \
+ [
+ '', '', 'align="center"', '', '',
+ 'valign="top"', 'valign="top"', 'align="center"', 'align="center"'
+ ];
+
+ def _formatListEntry(self, iEntry):
+ oEntry = self._aoEntries[iEntry];
+ from testmanager.webui.wuiadmin import WuiAdmin;
+
+ aoRet = \
+ [
+ oEntry.sName.replace('-', u'\u2011'),
+ 'Enabled' if oEntry.fEnabled else 'Disabled',
+ utils.formatIntervalSeconds(oEntry.cSecTimeout),
+ ];
+
+ # Base command and variations.
+ fNoGang = True;
+ fNoSubName = True;
+ fAllDefaultTimeouts = True;
+ for oVar in oEntry.aoTestCaseArgs:
+ if fNoSubName and oVar.sSubName is not None and oVar.sSubName.strip():
+ fNoSubName = False;
+ if oVar.cGangMembers > 1:
+ fNoGang = False;
+ if oVar.cSecTimeout is not None:
+ fAllDefaultTimeouts = False;
+
+ sHtml = ' <table class="tminnertbl" width=100%>\n' \
+ ' <tr>\n' \
+ ' ';
+ if not fNoSubName:
+ sHtml += '<th class="tmtcasubname">Sub-name</th>';
+ if not fNoGang:
+ sHtml += '<th class="tmtcagangsize">Gang Size</th>';
+ if not fAllDefaultTimeouts:
+ sHtml += '<th class="tmtcatimeout">Timeout</th>';
+ sHtml += '<th>Additional Arguments</b></th>\n' \
+ ' </tr>\n'
+ for oTmp in oEntry.aoTestCaseArgs:
+ sHtml += '<tr>';
+ if not fNoSubName:
+ sHtml += '<td>%s</td>' % (webutils.escapeElem(oTmp.sSubName) if oTmp.sSubName is not None else '');
+ if not fNoGang:
+ sHtml += '<td>%d</td>' % (oTmp.cGangMembers,)
+ if not fAllDefaultTimeouts:
+ sHtml += '<td>%s</td>' \
+ % (utils.formatIntervalSeconds(oTmp.cSecTimeout) if oTmp.cSecTimeout is not None else 'Default',)
+ sHtml += u'<td>%s</td></tr>' \
+ % ( webutils.escapeElem(oTmp.sArgs.replace('-', u'\u2011')) if oTmp.sArgs else u'\u2011',);
+ sHtml += '</tr>\n';
+ sHtml += ' </table>'
+
+ aoRet.append([oEntry.sBaseCmd.replace('-', u'\u2011'), WuiRawHtml(sHtml)]);
+
+ # Next.
+ aoRet += [ oEntry.sValidationKitZips if oEntry.sValidationKitZips is not None else '', ];
+
+ # Show dependency on other testcases
+ if oEntry.aoDepTestCases not in (None, []):
+ sHtml = ' <ul class="tmshowall">\n'
+ for sTmp in oEntry.aoDepTestCases:
+ sHtml += ' <li class="tmshowall"><a href="%s?%s=%s&%s=%s">%s</a></li>\n' \
+ % (WuiAdmin.ksScriptName,
+ WuiAdmin.ksParamAction, WuiAdmin.ksActionTestCaseEdit,
+ TestCaseData.ksParam_idTestCase, sTmp.idTestCase,
+ sTmp.sName)
+ sHtml += ' </ul>\n'
+ else:
+ sHtml = '<ul class="tmshowall"><li class="tmshowall">None</li></ul>\n'
+ aoRet.append(WuiRawHtml(sHtml));
+
+ # Show dependency on global resources
+ if oEntry.aoDepGlobalResources not in (None, []):
+ sHtml = ' <ul class="tmshowall">\n'
+ for sTmp in oEntry.aoDepGlobalResources:
+ sHtml += ' <li class="tmshowall"><a href="%s?%s=%s&%s=%s">%s</a></li>\n' \
+ % (WuiAdmin.ksScriptName,
+ WuiAdmin.ksParamAction, WuiAdmin.ksActionGlobalRsrcShowEdit,
+ GlobalResourceData.ksParam_idGlobalRsrc, sTmp.idGlobalRsrc,
+ sTmp.sName)
+ sHtml += ' </ul>\n'
+ else:
+ sHtml = '<ul class="tmshowall"><li class="tmshowall">None</li></ul>\n'
+ aoRet.append(WuiRawHtml(sHtml));
+
+ # Comment (note).
+ aoRet.append(self._formatCommentCell(oEntry.sComment));
+
+ # Show actions that can be taken.
+ aoActions = [ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDetails,
+ TestCaseData.ksParam_idGenTestCase: oEntry.idGenTestCase }), ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseEdit,
+ TestCaseData.ksParam_idTestCase: oEntry.idTestCase }));
+ aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseClone,
+ TestCaseData.ksParam_idGenTestCase: oEntry.idGenTestCase }));
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDoRemove,
+ TestCaseData.ksParam_idTestCase: oEntry.idTestCase },
+ sConfirm = 'Are you sure you want to remove test case #%d?' % (oEntry.idTestCase,)));
+ aoRet.append(aoActions);
+
+ return aoRet;
+
+
+class WuiTestCase(WuiFormContentBase):
+ """
+ WUI user account content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ assert isinstance(oData, TestCaseDataEx);
+
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'New Test Case';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Edit Test Case - %s (#%s)' % (oData.sName, oData.idTestCase);
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Test Case - %s (#%s)' % (oData.sName, oData.idTestCase);
+ WuiFormContentBase.__init__(self, oData, sMode, 'TestCase', oDisp, sTitle);
+
+ # Read additional bits form the DB.
+ oDepLogic = TestCaseDependencyLogic(oDisp.getDb());
+ self._aoAllTestCases = oDepLogic.getApplicableDepTestCaseData(-1 if oData.idTestCase is None else oData.idTestCase);
+ self._aoAllGlobalRsrcs = GlobalResourceLogic(oDisp.getDb()).getAll();
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO (TestCaseData.ksParam_idTestCase, oData.idTestCase, 'Test Case ID')
+ oForm.addTimestampRO(TestCaseData.ksParam_tsEffective, oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO(TestCaseData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (TestCaseData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID')
+ oForm.addIntRO (TestCaseData.ksParam_idGenTestCase, oData.idGenTestCase, 'Test Case generation ID')
+ oForm.addText (TestCaseData.ksParam_sName, oData.sName, 'Name')
+ oForm.addText (TestCaseData.ksParam_sDescription, oData.sDescription, 'Description')
+ oForm.addCheckBox (TestCaseData.ksParam_fEnabled, oData.fEnabled, 'Enabled')
+ oForm.addLong (TestCaseData.ksParam_cSecTimeout,
+ utils.formatIntervalSeconds2(oData.cSecTimeout), 'Default timeout')
+ oForm.addWideText (TestCaseData.ksParam_sTestBoxReqExpr, oData.sTestBoxReqExpr, 'TestBox requirements (python)');
+ oForm.addWideText (TestCaseData.ksParam_sBuildReqExpr, oData.sBuildReqExpr, 'Build requirement (python)');
+ oForm.addWideText (TestCaseData.ksParam_sBaseCmd, oData.sBaseCmd, 'Base command')
+ oForm.addText (TestCaseData.ksParam_sValidationKitZips, oData.sValidationKitZips, 'Test suite files')
+
+ oForm.addListOfTestCaseArgs(TestCaseDataEx.ksParam_aoTestCaseArgs, oData.aoTestCaseArgs, 'Argument variations')
+
+ aoTestCaseDeps = [];
+ for oTestCase in self._aoAllTestCases:
+ if oTestCase.idTestCase == oData.idTestCase:
+ continue;
+ fSelected = False;
+ for oDep in oData.aoDepTestCases:
+ if oDep.idTestCase == oTestCase.idTestCase:
+ fSelected = True;
+ break;
+ aoTestCaseDeps.append([oTestCase.idTestCase, fSelected, oTestCase.sName]);
+ oForm.addListOfTestCases(TestCaseDataEx.ksParam_aoDepTestCases, aoTestCaseDeps, 'Depends on test cases')
+
+ aoGlobalResrcDeps = [];
+ for oGlobalRsrc in self._aoAllGlobalRsrcs:
+ fSelected = False;
+ for oDep in oData.aoDepGlobalResources:
+ if oDep.idGlobalRsrc == oGlobalRsrc.idGlobalRsrc:
+ fSelected = True;
+ break;
+ aoGlobalResrcDeps.append([oGlobalRsrc.idGlobalRsrc, fSelected, oGlobalRsrc.sName]);
+ oForm.addListOfResources(TestCaseDataEx.ksParam_aoDepGlobalResources, aoGlobalResrcDeps, 'Depends on resources')
+
+ oForm.addMultilineText(TestCaseDataEx.ksParam_sComment, oData.sComment, 'Comment');
+
+ oForm.addSubmit();
+
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadmintestgroup.py b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestgroup.py
new file mode 100755
index 00000000..8954afe9
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadmintestgroup.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadmintestgroup.py $
+
+"""
+Test Manager WUI - Test Groups.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink, WuiRawHtml;
+from testmanager.core.db import isDbTimestampInfinity;
+from testmanager.core.testgroup import TestGroupData, TestGroupDataEx;
+from testmanager.core.testcase import TestCaseData, TestCaseLogic;
+
+
+class WuiTestGroup(WuiFormContentBase):
+ """
+ WUI test group content generator.
+ """
+
+ def __init__(self, oData, sMode, oDisp):
+ assert isinstance(oData, TestGroupDataEx);
+
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add Test Group';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Modify Test Group';
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Test Group';
+ WuiFormContentBase.__init__(self, oData, sMode, 'TestGroup', oDisp, sTitle);
+
+ #
+ # Fetch additional data.
+ #
+ if sMode in [WuiFormContentBase.ksMode_Add, WuiFormContentBase.ksMode_Edit]:
+ self.aoAllTestCases = TestCaseLogic(oDisp.getDb()).fetchForListing(0, 0x7fff, None);
+ else:
+ self.aoAllTestCases = [oMember.oTestCase for oMember in oData.aoMembers];
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO (TestGroupData.ksParam_idTestGroup, self._oData.idTestGroup, 'Test Group ID')
+ oForm.addTimestampRO (TestGroupData.ksParam_tsEffective, self._oData.tsEffective, 'Last changed')
+ oForm.addTimestampRO (TestGroupData.ksParam_tsExpire, self._oData.tsExpire, 'Expires (excl)')
+ oForm.addIntRO (TestGroupData.ksParam_uidAuthor, self._oData.uidAuthor, 'Changed by UID')
+ oForm.addText (TestGroupData.ksParam_sName, self._oData.sName, 'Name')
+ oForm.addText (TestGroupData.ksParam_sDescription, self._oData.sDescription, 'Description')
+
+ oForm.addListOfTestGroupMembers(TestGroupDataEx.ksParam_aoMembers,
+ oData.aoMembers, self.aoAllTestCases, 'Test Case List',
+ fReadOnly = self._sMode == WuiFormContentBase.ksMode_Show);
+
+ oForm.addMultilineText (TestGroupData.ksParam_sComment, self._oData.sComment, 'Comment');
+ oForm.addSubmit();
+ return True;
+
+
+class WuiTestGroupList(WuiListContentBase):
+ """
+ WUI test group list content generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ assert not aoEntries or isinstance(aoEntries[0], TestGroupDataEx)
+
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, sTitle = 'Test Groups',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = [ 'ID', 'Name', 'Description', 'Test Cases', 'Note', 'Actions' ];
+ self._asColumnAttribs = [ 'align="right"', '', '', '', 'align="center"', 'align="center"' ];
+
+
+ def _formatListEntry(self, iEntry):
+ oEntry = self._aoEntries[iEntry];
+ from testmanager.webui.wuiadmin import WuiAdmin;
+
+ #
+ # Test case list.
+ #
+ sHtml = '';
+ if oEntry.aoMembers:
+ for oMember in oEntry.aoMembers:
+ sHtml += '<dl>\n' \
+ ' <dd><strong>%s</strong> (priority: %d) %s %s</dd>\n' \
+ % ( webutils.escapeElem(oMember.oTestCase.sName),
+ oMember.iSchedPriority,
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDetails,
+ TestCaseData.ksParam_idGenTestCase: oMember.oTestCase.idGenTestCase, } ).toHtml(),
+ WuiTmLink('Edit', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseEdit,
+ TestCaseData.ksParam_idTestCase: oMember.oTestCase.idTestCase, } ).toHtml()
+ if isDbTimestampInfinity(oMember.oTestCase.tsExpire)
+ and self._oDisp is not None
+ and not self._oDisp.isReadOnlyUser() else '',
+ );
+
+ sHtml += ' <dt>\n';
+
+ fNoGang = True;
+ for oVar in oMember.oTestCase.aoTestCaseArgs:
+ if oVar.cGangMembers > 1:
+ fNoGang = False
+ break;
+
+ sHtml += ' <table class="tminnertbl" width="100%">\n'
+ if fNoGang:
+ sHtml += ' <tr><th>Timeout</th><th>Arguments</th></tr>\n';
+ else:
+ sHtml += ' <tr><th>Gang Size</th><th>Timeout</th><th style="text-align:left;">Arguments</th></tr>\n';
+
+ cArgsIncluded = 0;
+ for oVar in oMember.oTestCase.aoTestCaseArgs:
+ if oMember.aidTestCaseArgs is None or oVar.idTestCaseArgs in oMember.aidTestCaseArgs:
+ cArgsIncluded += 1;
+ if fNoGang:
+ sHtml += ' <tr>';
+ else:
+ sHtml += ' <tr><td>%s</td>' % (oVar.cGangMembers,);
+ sHtml += '<td>%s</td><td>%s</td></tr>\n' \
+ % ( utils.formatIntervalSeconds(oMember.oTestCase.cSecTimeout if oVar.cSecTimeout is None
+ else oVar.cSecTimeout),
+ webutils.escapeElem(oVar.sArgs), );
+ if cArgsIncluded == 0:
+ sHtml += ' <tr><td colspan="%u">No arguments selected.</td></tr>\n' % ( 2 if fNoGang else 3, );
+ sHtml += ' </table>\n' \
+ ' </dl>\n';
+ oTestCases = WuiRawHtml(sHtml);
+
+ #
+ # Actions.
+ #
+ aoActions = [ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupDetails,
+ TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }) ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+
+ if isDbTimestampInfinity(oEntry.tsExpire):
+ aoActions.append(WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupEdit,
+ TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup }));
+ aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone,
+ TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }));
+ aoActions.append(WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupDoRemove,
+ TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup },
+ sConfirm = 'Do you really want to remove test group #%d?' % (oEntry.idTestGroup,)));
+ else:
+ aoActions.append(WuiTmLink('Clone', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestGroupClone,
+ TestGroupData.ksParam_idTestGroup: oEntry.idTestGroup,
+ WuiAdmin.ksParamEffectiveDate: self._tsEffectiveDate, }));
+
+
+
+ return [ oEntry.idTestGroup,
+ oEntry.sName,
+ oEntry.sDescription if oEntry.sDescription is not None else '',
+ oTestCases,
+ self._formatCommentCell(oEntry.sComment, cMaxLines = max(3, len(oEntry.aoMembers) * 2)),
+ aoActions ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuiadminuseraccount.py b/src/VBox/ValidationKit/testmanager/webui/wuiadminuseraccount.py
new file mode 100755
index 00000000..30d78cd4
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuiadminuseraccount.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+# $Id: wuiadminuseraccount.py $
+
+"""
+Test Manager WUI - User accounts.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiListContentBase, WuiTmLink;
+from testmanager.core.useraccount import UserAccountData
+
+
+class WuiUserAccount(WuiFormContentBase):
+ """
+ WUI user account content generator.
+ """
+ def __init__(self, oData, sMode, oDisp):
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add User Account';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Modify User Account';
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'User Account';
+ WuiFormContentBase.__init__(self, oData, sMode, 'User', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+ oForm.addIntRO( UserAccountData.ksParam_uid, oData.uid, 'User ID');
+ oForm.addTimestampRO(UserAccountData.ksParam_tsEffective, oData.tsEffective, 'Effective Date');
+ oForm.addTimestampRO(UserAccountData.ksParam_tsExpire, oData.tsExpire, 'Effective Date');
+ oForm.addIntRO( UserAccountData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID');
+ oForm.addText( UserAccountData.ksParam_sLoginName, oData.sLoginName, 'Login name')
+ oForm.addText( UserAccountData.ksParam_sUsername, oData.sUsername, 'User name')
+ oForm.addText( UserAccountData.ksParam_sFullName, oData.sFullName, 'Full name')
+ oForm.addText( UserAccountData.ksParam_sEmail, oData.sEmail, 'E-mail')
+ oForm.addCheckBox( UserAccountData.ksParam_fReadOnly, oData.fReadOnly, 'Only read access')
+ if self._sMode != WuiFormContentBase.ksMode_Show:
+ oForm.addSubmit('Add User' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change User');
+ return True;
+
+
+class WuiUserAccountList(WuiListContentBase):
+ """
+ WUI user account list content generator.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Registered User Accounts', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp,
+ aiSelectedSortColumns = aiSelectedSortColumns);
+ self._asColumnHeaders = ['User ID', 'Name', 'E-mail', 'Full Name', 'Login Name', 'Access', 'Actions'];
+ self._asColumnAttribs = ['align="center"', 'align="center"', 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', ];
+
+ def _formatListEntry(self, iEntry):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ oEntry = self._aoEntries[iEntry];
+ aoActions = [
+ WuiTmLink('Details', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserDetails,
+ UserAccountData.ksParam_uid: oEntry.uid } ),
+ ];
+ if self._oDisp is None or not self._oDisp.isReadOnlyUser():
+ aoActions += [
+ WuiTmLink('Modify', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserEdit,
+ UserAccountData.ksParam_uid: oEntry.uid } ),
+ WuiTmLink('Remove', WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionUserDelPost,
+ UserAccountData.ksParam_uid: oEntry.uid },
+ sConfirm = 'Are you sure you want to remove user #%d?' % (oEntry.uid,)),
+ ];
+
+ return [ oEntry.uid, oEntry.sUsername, oEntry.sEmail, oEntry.sFullName, oEntry.sLoginName,
+ 'read only' if oEntry.fReadOnly else 'full',
+ aoActions, ];
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuibase.py b/src/VBox/ValidationKit/testmanager/webui/wuibase.py
new file mode 100755
index 00000000..feccf20a
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuibase.py
@@ -0,0 +1,1245 @@
+# -*- coding: utf-8 -*-
+# $Id: wuibase.py $
+
+"""
+Test Manager Web-UI - Base Classes.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import os;
+import sys;
+import string;
+
+# Validation Kit imports.
+from common import webutils, utils;
+from testmanager import config;
+from testmanager.core.base import ModelDataBase, ModelLogicBase, TMExceptionBase;
+from testmanager.core.db import TMDatabaseConnection;
+from testmanager.core.systemlog import SystemLogLogic, SystemLogData;
+from testmanager.core.useraccount import UserAccountLogic
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ unicode = str; # pylint: disable=redefined-builtin,invalid-name
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class WuiException(TMExceptionBase):
+ """
+ For exceptions raised by Web UI code.
+ """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class WuiDispatcherBase(object):
+ """
+ Base class for the Web User Interface (WUI) dispatchers.
+
+ The dispatcher class defines the basics of the page (like base template,
+ menu items, action). It is also responsible for parsing requests and
+ dispatching them to action (POST) or/and content generators (GET+POST).
+ The content returned by the generator is merged into the template and sent
+ back to the webserver glue.
+ """
+
+ ## @todo possible that this should all go into presentation.
+
+ ## The action parameter.
+ ksParamAction = 'Action';
+ ## The name of the default action.
+ ksActionDefault = 'default';
+
+ ## The name of the current page number parameter used when displaying lists.
+ ksParamPageNo = 'PageNo';
+ ## The name of the page length (list items) parameter when displaying lists.
+ ksParamItemsPerPage = 'ItemsPerPage';
+
+ ## The name of the effective date (timestamp) parameter.
+ ksParamEffectiveDate = 'EffectiveDate';
+
+ ## The name of the redirect-to (test manager relative url) parameter.
+ ksParamRedirectTo = 'RedirectTo';
+
+ ## The name of the list-action parameter (WuiListContentWithActionBase).
+ ksParamListAction = 'ListAction';
+
+ ## One or more columns to sort by.
+ ksParamSortColumns = 'SortBy';
+
+ ## The name of the change log enabled/disabled parameter.
+ ksParamChangeLogEnabled = 'ChangeLogEnabled';
+ ## The name of the parmaeter indicating the change log page number.
+ ksParamChangeLogPageNo = 'ChangeLogPageNo';
+ ## The name of the parameter indicate number of change log entries per page.
+ ksParamChangeLogEntriesPerPage = 'ChangeLogEntriesPerPage';
+ ## The change log related parameters.
+ kasChangeLogParams = (ksParamChangeLogEnabled, ksParamChangeLogPageNo, ksParamChangeLogEntriesPerPage,);
+
+ ## @name Dispatcher debugging parameters.
+ ## {@
+ ksParamDbgSqlTrace = 'DbgSqlTrace';
+ ksParamDbgSqlExplain = 'DbgSqlExplain';
+ ## List of all debugging parameters.
+ kasDbgParams = (ksParamDbgSqlTrace, ksParamDbgSqlExplain,);
+ ## @}
+
+ ## Special action return code for skipping _generatePage. Useful for
+ # download pages and the like that messes with the HTTP header and more.
+ ksDispatchRcAllDone = 'Done - Page has been rendered already';
+
+
+ def __init__(self, oSrvGlue, sScriptName):
+ self._oSrvGlue = oSrvGlue;
+ self._oDb = TMDatabaseConnection(self.dprint if config.g_kfWebUiSqlDebug else None, oSrvGlue = oSrvGlue);
+ self._tsNow = None; # Set by getEffectiveDateParam.
+ self._asCheckedParams = [];
+ self._dParams = None; # Set by dispatchRequest.
+ self._sAction = None; # Set by dispatchRequest.
+ self._dDispatch = { self.ksActionDefault: self._actionDefault, };
+
+ # Template bits.
+ self._sTemplate = 'template-default.html';
+ self._sPageTitle = '$$TODO$$'; # The page title.
+ self._aaoMenus = []; # List of [sName, sLink, [ [sSideName, sLink], .. ] tuples.
+ self._sPageFilter = ''; # The filter controls (optional).
+ self._sPageBody = '$$TODO$$'; # The body text.
+ self._dSideMenuFormAttrs = {}; # key/value with attributes for the side menu <form> tag.
+ self._sRedirectTo = None;
+ self._sDebug = '';
+
+ # Debugger bits.
+ self._fDbgSqlTrace = False;
+ self._fDbgSqlExplain = False;
+ self._dDbgParams = {};
+ for sKey, sValue in oSrvGlue.getParameters().items():
+ if sKey in self.kasDbgParams:
+ self._dDbgParams[sKey] = sValue;
+ if self._dDbgParams:
+ from testmanager.webui.wuicontentbase import WuiTmLink;
+ WuiTmLink.kdDbgParams = self._dDbgParams;
+
+ # Determine currently logged in user credentials
+ self._oCurUser = UserAccountLogic(self._oDb).tryFetchAccountByLoginName(oSrvGlue.getLoginName());
+
+ # Calc a couple of URL base strings for this dispatcher.
+ self._sUrlBase = sScriptName + '?';
+ if self._dDbgParams:
+ self._sUrlBase += webutils.encodeUrlParams(self._dDbgParams) + '&';
+ self._sActionUrlBase = self._sUrlBase + self.ksParamAction + '=';
+
+
+ def _redirectPage(self):
+ """
+ Redirects the page to the URL given in self._sRedirectTo.
+ """
+ assert self._sRedirectTo;
+ assert self._sPageBody is None;
+ assert self._sPageTitle is None;
+
+ self._oSrvGlue.setRedirect(self._sRedirectTo);
+ return True;
+
+ def _isMenuMatch(self, sMenuUrl, sActionParam):
+ """ Overridable menu matcher. """
+ return sMenuUrl is not None and sMenuUrl.find(sActionParam) > 0;
+
+ def _isSideMenuMatch(self, sSideMenuUrl, sActionParam):
+ """ Overridable side menu matcher. """
+ return sSideMenuUrl is not None and sSideMenuUrl.find(sActionParam) > 0;
+
+ def _generateMenus(self):
+ """
+ Generates the two menus, returning them as (sTopMenuItems, sSideMenuItems).
+ """
+ fReadOnly = self.isReadOnlyUser();
+
+ #
+ # We use the action to locate the side menu.
+ #
+ aasSideMenu = None;
+ for cchAction in range(len(self._sAction), 1, -1):
+ sActionParam = '%s=%s' % (self.ksParamAction, self._sAction[:cchAction]);
+ for aoItem in self._aaoMenus:
+ if self._isMenuMatch(aoItem[1], sActionParam):
+ aasSideMenu = aoItem[2];
+ break;
+ for asSubItem in aoItem[2]:
+ if self._isMenuMatch(asSubItem[1], sActionParam):
+ aasSideMenu = aoItem[2];
+ break;
+ if aasSideMenu is not None:
+ break;
+
+ #
+ # Top menu first.
+ #
+ sTopMenuItems = '';
+ for aoItem in self._aaoMenus:
+ if aasSideMenu is aoItem[2]:
+ sTopMenuItems += '<li class="current_page_item">';
+ else:
+ sTopMenuItems += '<li>';
+ sTopMenuItems += '<a href="' + webutils.escapeAttr(aoItem[1]) + '">' \
+ + webutils.escapeElem(aoItem[0]) + '</a></li>\n';
+
+ #
+ # Side menu (if found).
+ #
+ sActionParam = '%s=%s' % (self.ksParamAction, self._sAction);
+ sSideMenuItems = '';
+ if aasSideMenu is not None:
+ for asSubItem in aasSideMenu:
+ if asSubItem[1] is not None:
+ if not asSubItem[2] or not fReadOnly:
+ if self._isSideMenuMatch(asSubItem[1], sActionParam):
+ sSideMenuItems += '<li class="current_page_item">';
+ else:
+ sSideMenuItems += '<li>';
+ sSideMenuItems += '<a href="' + webutils.escapeAttr(asSubItem[1]) + '">' \
+ + webutils.escapeElem(asSubItem[0]) + '</a></li>\n';
+ else:
+ sSideMenuItems += '<li class="subheader_item">' + webutils.escapeElem(asSubItem[0]) + '</li>';
+ return (sTopMenuItems, sSideMenuItems);
+
+ def _generatePage(self):
+ """
+ Generates the page using _sTemplate, _sPageTitle, _aaoMenus, and _sPageBody.
+ """
+ assert self._sRedirectTo is None;
+
+ #
+ # Build the replacement string dictionary.
+ #
+
+ # Provide basic auth log out for browsers that supports it.
+ sUserAgent = self._oSrvGlue.getUserAgent();
+ if sUserAgent.startswith('Mozilla/') and sUserAgent.find('Firefox') > 0:
+ # Log in as the logout user in the same realm, the browser forgets
+ # the old login and the job is done. (see apache sample conf)
+ sLogOut = ' (<a href="%s://logout:logout@%s%slogout.py">logout</a>)' \
+ % (self._oSrvGlue.getUrlScheme(), self._oSrvGlue.getUrlNetLoc(), self._oSrvGlue.getUrlBasePath());
+ elif sUserAgent.startswith('Mozilla/') and sUserAgent.find('Safari') > 0:
+ # For a 401, causing the browser to forget the old login. Works
+ # with safari as well as the two above. Since safari consider the
+ # above method a phishing attempt and displays a warning to that
+ # effect, which when taken seriously aborts the logout, this method
+ # is preferable, even if it throws logon boxes in the user's face
+ # till he/she/it hits escape, because it always works.
+ sLogOut = ' (<a href="logout2.py">logout</a>)'
+ elif (sUserAgent.startswith('Mozilla/') and sUserAgent.find('MSIE') > 0) \
+ or (sUserAgent.startswith('Mozilla/') and sUserAgent.find('Chrome') > 0):
+ ## There doesn't seem to be any way to make IE really log out
+ # without using a cookie and systematically 401 accesses based on
+ # some logout state associated with it. Not sure how secure that
+ # can be made and we really want to avoid cookies. So, perhaps,
+ # just avoid IE for now. :-)
+ ## Chrome/21.0 doesn't want to log out either.
+ sLogOut = ''
+ else:
+ sLogOut = ''
+
+ # Prep Menus.
+ (sTopMenuItems, sSideMenuItems) = self._generateMenus();
+
+ # The dictionary (max variable length is 28 chars (see further down)).
+ dReplacements = {
+ '@@PAGE_TITLE@@': self._sPageTitle,
+ '@@LOG_OUT@@': sLogOut,
+ '@@TESTMANAGER_VERSION@@': config.g_ksVersion,
+ '@@TESTMANAGER_REVISION@@': config.g_ksRevision,
+ '@@BASE_URL@@': self._oSrvGlue.getBaseUrl(),
+ '@@TOP_MENU_ITEMS@@': sTopMenuItems,
+ '@@SIDE_MENU_ITEMS@@': sSideMenuItems,
+ '@@SIDE_FILTER_CONTROL@@': self._sPageFilter,
+ '@@SIDE_MENU_FORM_ATTRS@@': '',
+ '@@PAGE_BODY@@': self._sPageBody,
+ '@@DEBUG@@': '',
+ };
+
+ # Side menu form attributes.
+ if self._dSideMenuFormAttrs:
+ dReplacements['@@SIDE_MENU_FORM_ATTRS@@'] = ' '.join(['%s="%s"' % (sKey, webutils.escapeAttr(sValue))
+ for sKey, sValue in self._dSideMenuFormAttrs.items()]);
+
+ # Special current user handling.
+ if self._oCurUser is not None:
+ dReplacements['@@USER_NAME@@'] = self._oCurUser.sUsername;
+ else:
+ dReplacements['@@USER_NAME@@'] = 'unauthorized user "' + self._oSrvGlue.getLoginName() + '"';
+
+ # Prep debug section.
+ if self._sDebug == '':
+ if config.g_kfWebUiSqlTrace or self._fDbgSqlTrace or self._fDbgSqlExplain:
+ self._sDebug = '<h3>Processed in %s ns.</h3>\n%s\n' \
+ % ( utils.formatNumber(utils.timestampNano() - self._oSrvGlue.tsStart,),
+ self._oDb.debugHtmlReport(self._oSrvGlue.tsStart));
+ elif config.g_kfWebUiProcessedIn:
+ self._sDebug = '<h3>Processed in %s ns.</h3>\n' \
+ % ( utils.formatNumber(utils.timestampNano() - self._oSrvGlue.tsStart,), );
+ if config.g_kfWebUiDebugPanel:
+ self._sDebug += self._debugRenderPanel();
+ if self._sDebug != '':
+ dReplacements['@@DEBUG@@'] = u'<div id="debug"><br><br><hr/>' \
+ + (utils.toUnicode(self._sDebug, errors='ignore') if isinstance(self._sDebug, str)
+ else self._sDebug) \
+ + u'</div>\n';
+
+ #
+ # Load the template.
+ #
+ with open(os.path.join(self._oSrvGlue.pathTmWebUI(), self._sTemplate)) as oFile: # pylint: disable=unspecified-encoding
+ sTmpl = oFile.read();
+
+ #
+ # Process the template, outputting each part we process.
+ #
+ offStart = 0;
+ offCur = 0;
+ while offCur < len(sTmpl):
+ # Look for a replacement variable.
+ offAtAt = sTmpl.find('@@', offCur);
+ if offAtAt < 0:
+ break;
+ offCur = offAtAt + 2;
+ if sTmpl[offCur] not in string.ascii_uppercase:
+ continue;
+ offEnd = sTmpl.find('@@', offCur, offCur+28);
+ if offEnd <= 0:
+ continue;
+ offCur = offEnd;
+ sReplacement = sTmpl[offAtAt:offEnd+2];
+ if sReplacement in dReplacements:
+ # Got a match! Write out the previous chunk followed by the replacement text.
+ if offStart < offAtAt:
+ self._oSrvGlue.write(sTmpl[offStart:offAtAt]);
+ self._oSrvGlue.write(dReplacements[sReplacement]);
+ # Advance past the replacement point in the template.
+ offCur += 2;
+ offStart = offCur;
+ else:
+ assert False, 'Unknown replacement "%s" at offset %s in %s' % (sReplacement, offAtAt, self._sTemplate );
+
+ # The final chunk.
+ if offStart < len(sTmpl):
+ self._oSrvGlue.write(sTmpl[offStart:]);
+
+ return True;
+
+ #
+ # Interface for WuiContentBase classes.
+ #
+
+ def getUrlNoParams(self):
+ """
+ Returns the base URL without any parameters (no trailing '?' or &).
+ """
+ return self._sUrlBase[:self._sUrlBase.rindex('?')];
+
+ def getUrlBase(self):
+ """
+ Returns the base URL, ending with '?' or '&'.
+ This may already include some debug parameters.
+ """
+ return self._sUrlBase;
+
+ def getParameters(self):
+ """
+ Returns a (shallow) copy of the request parameter dictionary.
+ """
+ return self._dParams.copy();
+
+ def getDb(self):
+ """
+ Returns the database connection.
+ """
+ return self._oDb;
+
+ def getNow(self):
+ """
+ Returns the effective date.
+ """
+ return self._tsNow;
+
+
+ #
+ # Parameter handling.
+ #
+
+ def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
+ """
+ Gets a string parameter.
+ Raises exception if not found and sDefault is None.
+ """
+ if sName in self._dParams:
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+ sValue = self._dParams[sName];
+ if isinstance(sValue, list):
+ raise WuiException('%s parameter "%s" is given multiple times: "%s"' % (self._sAction, sName, sValue));
+ sValue = sValue.strip();
+ elif sDefault is None and fAllowNull is not True:
+ raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,));
+ else:
+ sValue = sDefault;
+
+ if asValidValues is not None and sValue not in asValidValues:
+ raise WuiException('%s parameter %s value "%s" not in %s '
+ % (self._sAction, sName, sValue, asValidValues));
+ return sValue;
+
+ def getBoolParam(self, sName, fDefault = None):
+ """
+ Gets a boolean parameter.
+ Raises exception if not found and fDefault is None, or if not a valid boolean.
+ """
+ sValue = self.getStringParam(sName, [ 'True', 'true', '1', 'False', 'false', '0'],
+ '0' if fDefault is None else str(fDefault));
+ # HACK: Checkboxes doesn't return a value when unchecked, so we always
+ # provide a default when dealing with boolean parameters.
+ return sValue in ('True', 'true', '1',);
+
+ def getIntParam(self, sName, iMin = None, iMax = None, iDefault = None):
+ """
+ Gets a integer parameter.
+ Raises exception if not found and iDefault is None, if not a valid int,
+ or if outside the range defined by iMin and iMax.
+ """
+ if iDefault is not None and sName not in self._dParams:
+ return iDefault;
+
+ sValue = self.getStringParam(sName, None, None if iDefault is None else str(iDefault));
+ try:
+ iValue = int(sValue);
+ except:
+ raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
+ % (self._sAction, sName, sValue));
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sAction, sName, iValue, iMin, iMax));
+ return iValue;
+
+ def getLongParam(self, sName, lMin = None, lMax = None, lDefault = None):
+ """
+ Gets a long integer parameter.
+ Raises exception if not found and lDefault is None, if not a valid long,
+ or if outside the range defined by lMin and lMax.
+ """
+ if lDefault is not None and sName not in self._dParams:
+ return lDefault;
+
+ sValue = self.getStringParam(sName, None, None if lDefault is None else str(lDefault));
+ try:
+ lValue = long(sValue);
+ except:
+ raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
+ % (self._sAction, sName, sValue));
+
+ if (lMin is not None and lValue < lMin) \
+ or (lMax is not None and lValue > lMax):
+ raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sAction, sName, lValue, lMin, lMax));
+ return lValue;
+
+ def getTsParam(self, sName, tsDefault = None, fRequired = True):
+ """
+ Gets a timestamp parameter.
+ Raises exception if not found and fRequired is True.
+ """
+ if fRequired is False and sName not in self._dParams:
+ return tsDefault;
+
+ sValue = self.getStringParam(sName, None, None if tsDefault is None else str(tsDefault));
+ (sValue, sError) = ModelDataBase.validateTs(sValue);
+ if sError is not None:
+ raise WuiException('%s parameter %s value "%s": %s'
+ % (self._sAction, sName, sValue, sError));
+ return sValue;
+
+ def getListOfIntParams(self, sName, iMin = None, iMax = None, aiDefaults = None):
+ """
+ Gets parameter list.
+ Raises exception if not found and aiDefaults is None, or if any of the
+ values are not valid integers or outside the range defined by iMin and iMax.
+ """
+ if sName in self._dParams:
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if isinstance(self._dParams[sName], list):
+ asValues = self._dParams[sName];
+ else:
+ asValues = [self._dParams[sName],];
+ aiValues = [];
+ for sValue in asValues:
+ try:
+ iValue = int(sValue);
+ except:
+ raise WuiException('%s parameter %s value "%s" cannot be convert to an integer'
+ % (self._sAction, sName, sValue));
+
+ if (iMin is not None and iValue < iMin) \
+ or (iMax is not None and iValue > iMax):
+ raise WuiException('%s parameter %s value %d is out of range [%s..%s]'
+ % (self._sAction, sName, iValue, iMin, iMax));
+ aiValues.append(iValue);
+ else:
+ aiValues = aiDefaults;
+
+ return aiValues;
+
+ def getListOfStrParams(self, sName, asDefaults = None):
+ """
+ Gets parameter list.
+ Raises exception if not found and asDefaults is None.
+ """
+ if sName in self._dParams:
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ if isinstance(self._dParams[sName], list):
+ asValues = [str(s).strip() for s in self._dParams[sName]];
+ else:
+ asValues = [str(self._dParams[sName]).strip(), ];
+ elif asDefaults is None:
+ raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,));
+ else:
+ asValues = asDefaults;
+
+ return asValues;
+
+ def getListOfTestCasesParam(self, sName, asDefaults = None): # too many local vars - pylint: disable=too-many-locals
+ """Get list of test cases and their parameters"""
+ if sName in self._dParams:
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName)
+
+ aoListOfTestCases = []
+
+ aiSelectedTestCaseIds = self.getListOfIntParams('%s[asCheckedTestCases]' % sName, aiDefaults=[])
+ aiAllTestCases = self.getListOfIntParams('%s[asAllTestCases]' % sName, aiDefaults=[])
+
+ for idTestCase in aiAllTestCases:
+ aiCheckedTestCaseArgs = \
+ self.getListOfIntParams(
+ '%s[%d][asCheckedTestCaseArgs]' % (sName, idTestCase),
+ aiDefaults=[])
+
+ aiAllTestCaseArgs = \
+ self.getListOfIntParams(
+ '%s[%d][asAllTestCaseArgs]' % (sName, idTestCase),
+ aiDefaults=[])
+
+ oListEntryTestCaseArgs = []
+ for idTestCaseArgs in aiAllTestCaseArgs:
+ fArgsChecked = idTestCaseArgs in aiCheckedTestCaseArgs;
+
+ # Dry run
+ sPrefix = '%s[%d][%d]' % (sName, idTestCase, idTestCaseArgs,);
+ self.getIntParam(sPrefix + '[idTestCaseArgs]', iDefault = -1,)
+
+ sArgs = self.getStringParam(sPrefix + '[sArgs]', sDefault = '')
+ cSecTimeout = self.getStringParam(sPrefix + '[cSecTimeout]', sDefault = '')
+ cGangMembers = self.getStringParam(sPrefix + '[cGangMembers]', sDefault = '')
+ cGangMembers = self.getStringParam(sPrefix + '[cGangMembers]', sDefault = '')
+
+ oListEntryTestCaseArgs.append((fArgsChecked, idTestCaseArgs, sArgs, cSecTimeout, cGangMembers))
+
+ sTestCaseName = self.getStringParam('%s[%d][sName]' % (sName, idTestCase), sDefault='')
+
+ oListEntryTestCase = (
+ idTestCase,
+ idTestCase in aiSelectedTestCaseIds,
+ sTestCaseName,
+ oListEntryTestCaseArgs
+ );
+
+ aoListOfTestCases.append(oListEntryTestCase)
+
+ if not aoListOfTestCases:
+ if asDefaults is None:
+ raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName))
+ aoListOfTestCases = asDefaults
+
+ return aoListOfTestCases
+
+ def getEffectiveDateParam(self, sParamName = None):
+ """
+ Gets the effective date parameter.
+
+ Returns a timestamp suitable for database and url parameters.
+ Returns None if not found or empty.
+
+ The first call with sParamName set to None will set the internal _tsNow
+ value upon successfull return.
+ """
+
+ sName = sParamName if sParamName is not None else WuiDispatcherBase.ksParamEffectiveDate
+
+ if sName not in self._dParams:
+ return None;
+
+ if sName not in self._asCheckedParams:
+ self._asCheckedParams.append(sName);
+
+ sValue = self._dParams[sName];
+ if isinstance(sValue, list):
+ raise WuiException('%s parameter "%s" is given multiple times: %s' % (self._sAction, sName, sValue));
+ sValue = sValue.strip();
+ if sValue == '':
+ return None;
+
+ #
+ # Timestamp, just validate it and return.
+ #
+ if sValue[0] not in ['-', '+']:
+ (sValue, sError) = ModelDataBase.validateTs(sValue);
+ if sError is not None:
+ raise WuiException('%s parameter "%s" ("%s") is invalid: %s' % (self._sAction, sName, sValue, sError));
+ if sParamName is None and self._tsNow is None:
+ self._tsNow = sValue;
+ return sValue;
+
+ #
+ # Relative timestamp. Validate and convert it to a fixed timestamp.
+ #
+ chSign = sValue[0];
+ (sValue, sError) = ModelDataBase.validateTs(sValue[1:], fRelative = True);
+ if sError is not None:
+ raise WuiException('%s parameter "%s" ("%s") is invalid: %s' % (self._sAction, sName, sValue, sError));
+ if sValue[-6] in ['-', '+']:
+ raise WuiException('%s parameter "%s" ("%s") is a relative timestamp but incorrectly includes a time zone.'
+ % (self._sAction, sName, sValue));
+ offTime = 11;
+ if sValue[offTime - 1] != ' ':
+ raise WuiException('%s parameter "%s" ("%s") incorrect format.' % (self._sAction, sName, sValue));
+ sInterval = 'P' + sValue[:(offTime - 1)] + 'T' + sValue[offTime:];
+
+ self._oDb.execute('SELECT CURRENT_TIMESTAMP ' + chSign + ' \'' + sInterval + '\'::INTERVAL');
+ oDate = self._oDb.fetchOne()[0];
+
+ sValue = str(oDate);
+ if sParamName is None and self._tsNow is None:
+ self._tsNow = sValue;
+ return sValue;
+
+ def getRedirectToParameter(self, sDefault = None):
+ """
+ Gets the special redirect to parameter if it exists, will Return default
+ if not, with None being a valid default.
+
+ Makes sure the it doesn't got offsite.
+ Raises exception if invalid.
+ """
+ if sDefault is not None or self.ksParamRedirectTo in self._dParams:
+ sValue = self.getStringParam(self.ksParamRedirectTo, sDefault = sDefault);
+ cch = sValue.find("?");
+ if cch < 0:
+ cch = sValue.find("#");
+ if cch < 0:
+ cch = len(sValue);
+ for ch in (':', '/', '\\', '..'):
+ if sValue.find(ch, 0, cch) >= 0:
+ raise WuiException('Invalid character (%c) in redirect-to url: %s' % (ch, sValue,));
+ else:
+ sValue = None;
+ return sValue;
+
+
+ def _checkForUnknownParameters(self):
+ """
+ Check if we've handled all parameters, raises exception if anything
+ unknown was found.
+ """
+
+ if len(self._asCheckedParams) != len(self._dParams):
+ sUnknownParams = '';
+ for sKey in self._dParams:
+ if sKey not in self._asCheckedParams:
+ sUnknownParams += ' ' + sKey + '=' + str(self._dParams[sKey]);
+ raise WuiException('Unknown parameters: ' + sUnknownParams);
+
+ return True;
+
+ def _assertPostRequest(self):
+ """
+ Makes sure that the request we're dispatching is a POST request.
+ Raises an exception of not.
+ """
+ if self._oSrvGlue.getMethod() != 'POST':
+ raise WuiException('Expected "POST" request, got "%s"' % (self._oSrvGlue.getMethod(),))
+ return True;
+
+ #
+ # Client browser type.
+ #
+
+ ## @name Browser types.
+ ## @{
+ ksBrowserFamily_Unknown = 0;
+ ksBrowserFamily_Gecko = 1;
+ ksBrowserFamily_Webkit = 2;
+ ksBrowserFamily_Trident = 3;
+ ## @}
+
+ ## @name Browser types.
+ ## @{
+ ksBrowserType_FamilyMask = 0xff;
+ ksBrowserType_Unknown = 0;
+ ksBrowserType_Firefox = (1 << 8) | ksBrowserFamily_Gecko;
+ ksBrowserType_Chrome = (2 << 8) | ksBrowserFamily_Webkit;
+ ksBrowserType_Safari = (3 << 8) | ksBrowserFamily_Webkit;
+ ksBrowserType_IE = (4 << 8) | ksBrowserFamily_Trident;
+ ## @}
+
+ def getBrowserType(self):
+ """
+ Gets the browser type.
+ The browser family can be extracted from this using ksBrowserType_FamilyMask.
+ """
+ sAgent = self._oSrvGlue.getUserAgent();
+ if sAgent.find('AppleWebKit/') > 0:
+ if sAgent.find('Chrome/') > 0:
+ return self.ksBrowserType_Chrome;
+ if sAgent.find('Safari/') > 0:
+ return self.ksBrowserType_Safari;
+ return self.ksBrowserType_Unknown | self.ksBrowserFamily_Webkit;
+ if sAgent.find('Gecko/') > 0:
+ if sAgent.find('Firefox/') > 0:
+ return self.ksBrowserType_Firefox;
+ return self.ksBrowserType_Unknown | self.ksBrowserFamily_Gecko;
+ return self.ksBrowserType_Unknown | self.ksBrowserFamily_Unknown;
+
+ def isBrowserGecko(self, sMinVersion = None):
+ """ Returns true if it's a gecko based browser. """
+ if (self.getBrowserType() & self.ksBrowserType_FamilyMask) != self.ksBrowserFamily_Gecko:
+ return False;
+ if sMinVersion is not None:
+ sAgent = self._oSrvGlue.getUserAgent();
+ sVersion = sAgent[sAgent.find('Gecko/')+6:].split()[0];
+ if sVersion < sMinVersion:
+ return False;
+ return True;
+
+ #
+ # User related stuff.
+ #
+
+ def isReadOnlyUser(self):
+ """ Returns true if the logged in user is read-only or if no user is logged in. """
+ return self._oCurUser is None or self._oCurUser.fReadOnly;
+
+
+ #
+ # Debugging
+ #
+
+ def _debugProcessDispatch(self):
+ """
+ Processes any debugging parameters in the request and adds them to
+ _asCheckedParams so they won't cause trouble in the action handler.
+ """
+
+ self._fDbgSqlTrace = self.getBoolParam(self.ksParamDbgSqlTrace, False);
+ self._fDbgSqlExplain = self.getBoolParam(self.ksParamDbgSqlExplain, False);
+
+ if self._fDbgSqlExplain:
+ self._oDb.debugEnableExplain();
+
+ return True;
+
+ def _debugRenderPanel(self):
+ """
+ Renders a simple form for controlling WUI debugging.
+
+ Returns the HTML for it.
+ """
+
+ sHtml = '<div id="debug-panel">\n' \
+ ' <form id="debug-panel-form" method="get" action="#">\n';
+
+ for sKey, oValue in self._dParams.items():
+ if sKey not in self.kasDbgParams:
+ if hasattr(oValue, 'startswith'):
+ sHtml += ' <input type="hidden" name="%s" value="%s"/>\n' \
+ % (webutils.escapeAttr(sKey), webutils.escapeAttrToStr(oValue),);
+ else:
+ for oSubValue in oValue:
+ sHtml += ' <input type="hidden" name="%s" value="%s"/>\n' \
+ % (webutils.escapeAttr(sKey), webutils.escapeAttrToStr(oSubValue),);
+
+ for aoCheckBox in (
+ [self.ksParamDbgSqlTrace, self._fDbgSqlTrace, 'SQL trace'],
+ [self.ksParamDbgSqlExplain, self._fDbgSqlExplain, 'SQL explain'], ):
+ sHtml += ' <input type="checkbox" name="%s" value="1"%s />%s\n' \
+ % (aoCheckBox[0], ' checked' if aoCheckBox[1] else '', aoCheckBox[2]);
+
+ sHtml += ' <button type="submit">Apply</button>\n';
+ sHtml += ' </form>\n' \
+ '</div>\n';
+ return sHtml;
+
+
+ def _debugGetParameters(self):
+ """
+ Gets a dictionary with the debug parameters.
+
+ For use when links are constructed from scratch instead of self._dParams.
+ """
+ return self._dDbgParams;
+
+ #
+ # Dispatching
+ #
+
+ def _actionDefault(self):
+ """The default action handler, always overridden. """
+ raise WuiException('The child class shall override WuiBase.actionDefault().')
+
+ def _actionGenericListing(self, oLogicType, oListContentType):
+ """
+ Generic listing action.
+
+ oLogicType implements fetchForListing.
+ oListContentType is a child of WuiListContentBase.
+ """
+ tsEffective = self.getEffectiveDateParam();
+ cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 384);
+ iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0);
+ aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns,
+ iMin = -getattr(oLogicType, 'kcMaxSortColumns', 0) + 1,
+ iMax = getattr(oLogicType, 'kcMaxSortColumns', 0), aiDefaults = []);
+ aiSortColumns = [];
+ for iSortColumn in aiSortColumnsDup:
+ if iSortColumn not in aiSortColumns:
+ aiSortColumns.append(iSortColumn);
+ self._checkForUnknownParameters();
+
+ ## @todo fetchForListing could be made more useful if it returned a tuple
+ # that includes the total number of entries, thus making paging more user
+ # friendly (known number of pages). So, the return should be:
+ # (aoEntries, cAvailableEntries)
+ #
+ # In addition, we could add a new parameter to include deleted entries,
+ # making it easier to find old deleted testboxes/testcases/whatever and
+ # clone them back to life. The temporal navigation is pretty usless here.
+ #
+ aoEntries = oLogicType(self._oDb).fetchForListing(iPage * cItemsPerPage, cItemsPerPage + 1, tsEffective, aiSortColumns);
+ oContent = oListContentType(aoEntries, iPage, cItemsPerPage, tsEffective,
+ fnDPrint = self._oSrvGlue.dprint, oDisp = self, aiSelectedSortColumns = aiSortColumns);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ return True;
+
+ def _actionGenericFormAdd(self, oDataType, oFormType, sRedirectTo = None):
+ """
+ Generic add something form display request handler.
+
+ oDataType is a ModelDataBase child class.
+ oFormType is a WuiFormContentBase child class.
+ """
+ assert issubclass(oDataType, ModelDataBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ oData = oDataType().initFromParams(oDisp = self, fStrict = False);
+ sRedirectTo = self.getRedirectToParameter(sRedirectTo);
+ self._checkForUnknownParameters();
+
+ oForm = oFormType(oData, oFormType.ksMode_Add, oDisp = self);
+ oForm.setRedirectTo(sRedirectTo);
+ (self._sPageTitle, self._sPageBody) = oForm.showForm();
+ return True
+
+ def _actionGenericFormDetails(self, oDataType, oLogicType, oFormType, sIdAttr = None, sGenIdAttr = None): # pylint: disable=too-many-locals
+ """
+ Generic handler for showing a details form/page.
+
+ oDataType is a ModelDataBase child class.
+ oLogicType may implement fetchForChangeLog.
+ oFormType is a WuiFormContentBase child class.
+ sIdParamName is the name of the ID parameter (not idGen!).
+ """
+ # Input.
+ assert issubclass(oDataType, ModelDataBase);
+ assert issubclass(oLogicType, ModelLogicBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ if sIdAttr is None:
+ sIdAttr = oDataType.ksIdAttr;
+ if sGenIdAttr is None:
+ sGenIdAttr = getattr(oDataType, 'ksGenIdAttr', None);
+
+ # Parameters.
+ idGenObject = -1;
+ if sGenIdAttr is not None:
+ idGenObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sGenIdAttr), 0, 0x7ffffffe, -1);
+ if idGenObject != -1:
+ idObject = tsNow = None;
+ else:
+ idObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sIdAttr), 0, 0x7ffffffe, -1);
+ tsNow = self.getEffectiveDateParam();
+ fChangeLog = self.getBoolParam(WuiDispatcherBase.ksParamChangeLogEnabled, True);
+ iChangeLogPageNo = self.getIntParam(WuiDispatcherBase.ksParamChangeLogPageNo, 0, 9999, 0);
+ cChangeLogEntriesPerPage = self.getIntParam(WuiDispatcherBase.ksParamChangeLogEntriesPerPage, 2, 9999, 4);
+ self._checkForUnknownParameters();
+
+ # Fetch item and display it.
+ if idGenObject == -1:
+ oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow);
+ else:
+ oData = oDataType().initFromDbWithGenId(self._oDb, idGenObject);
+
+ oContent = oFormType(oData, oFormType.ksMode_Show, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.showForm();
+
+ # Add change log if supported.
+ if fChangeLog and hasattr(oLogicType, 'fetchForChangeLog'):
+ (aoEntries, fMore) = oLogicType(self._oDb).fetchForChangeLog(getattr(oData, sIdAttr),
+ iChangeLogPageNo * cChangeLogEntriesPerPage,
+ cChangeLogEntriesPerPage ,
+ tsNow);
+ self._sPageBody += oContent.showChangeLog(aoEntries, fMore, iChangeLogPageNo, cChangeLogEntriesPerPage, tsNow);
+ return True
+
+ def _actionGenericDoRemove(self, oLogicType, sParamId, sRedirAction):
+ """
+ Delete entry (using oLogicType.removeEntry).
+
+ oLogicType is a class that implements addEntry.
+
+ sParamId is the name (ksParam_...) of the HTTP variable hold the ID of
+ the database entry to delete.
+
+ sRedirAction is what action to redirect to on success.
+ """
+ import cgitb;
+
+ idEntry = self.getIntParam(sParamId, iMin = 1, iMax = 0x7ffffffe)
+ fCascade = self.getBoolParam('fCascadeDelete', False);
+ sRedirectTo = self.getRedirectToParameter(self._sActionUrlBase + sRedirAction);
+ self._checkForUnknownParameters()
+
+ try:
+ if self.isReadOnlyUser():
+ raise Exception('"%s" is a read only user!' % (self._oCurUser.sUsername,));
+ self._sPageTitle = None
+ self._sPageBody = None
+ self._sRedirectTo = sRedirectTo;
+ return oLogicType(self._oDb).removeEntry(self._oCurUser.uid, idEntry, fCascade = fCascade, fCommit = True);
+ except Exception as oXcpt:
+ self._oDb.rollback();
+ self._sPageTitle = 'Unable to delete entry';
+ self._sPageBody = str(oXcpt);
+ if config.g_kfDebugDbXcpt:
+ self._sPageBody += cgitb.html(sys.exc_info());
+ self._sRedirectTo = None;
+ return False;
+
+ def _actionGenericFormEdit(self, oDataType, oFormType, sIdParamName = None, sRedirectTo = None):
+ """
+ Generic edit something form display request handler.
+
+ oDataType is a ModelDataBase child class.
+ oFormType is a WuiFormContentBase child class.
+ sIdParamName is the name of the ID parameter (not idGen!).
+ """
+ assert issubclass(oDataType, ModelDataBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ if sIdParamName is None:
+ sIdParamName = getattr(oDataType, 'ksParam_' + oDataType.ksIdAttr);
+ assert len(sIdParamName) > 1;
+
+ tsNow = self.getEffectiveDateParam();
+ idObject = self.getIntParam(sIdParamName, 0, 0x7ffffffe);
+ sRedirectTo = self.getRedirectToParameter(sRedirectTo);
+ self._checkForUnknownParameters();
+ oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow = tsNow);
+
+ oContent = oFormType(oData, oFormType.ksMode_Edit, oDisp = self);
+ oContent.setRedirectTo(sRedirectTo);
+ (self._sPageTitle, self._sPageBody) = oContent.showForm();
+ return True
+
+ def _actionGenericFormEditL(self, oCoreObjectLogic, sCoreObjectIdFieldName, oWuiObjectLogic):
+ """
+ Generic modify something form display request handler.
+
+ @param oCoreObjectLogic A *Logic class
+
+ @param sCoreObjectIdFieldName Name of HTTP POST variable that
+ contains object ID information
+
+ @param oWuiObjectLogic Web interface renderer class
+ """
+
+ iCoreDataObjectId = self.getIntParam(sCoreObjectIdFieldName, 0, 0x7ffffffe, -1)
+ self._checkForUnknownParameters();
+
+ ## @todo r=bird: This will return a None object if the object wasn't found... Crash bang in the content generator
+ # code (that's not logic code btw.).
+ oData = oCoreObjectLogic(self._oDb).getById(iCoreDataObjectId)
+
+ # Instantiate and render the MODIFY dialog form
+ oContent = oWuiObjectLogic(oData, oWuiObjectLogic.ksMode_Edit, oDisp=self)
+
+ (self._sPageTitle, self._sPageBody) = oContent.showForm()
+
+ return True
+
+ def _actionGenericFormClone(self, oDataType, oFormType, sIdAttr, sGenIdAttr = None):
+ """
+ Generic clone something form display request handler.
+
+ oDataType is a ModelDataBase child class.
+ oFormType is a WuiFormContentBase child class.
+ sIdParamName is the name of the ID parameter.
+ sGenIdParamName is the name of the generation ID parameter, None if not applicable.
+ """
+ # Input.
+ assert issubclass(oDataType, ModelDataBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ # Parameters.
+ idGenObject = -1;
+ if sGenIdAttr is not None:
+ idGenObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sGenIdAttr), 0, 0x7ffffffe, -1);
+ if idGenObject != -1:
+ idObject = tsNow = None;
+ else:
+ idObject = self.getIntParam(getattr(oDataType, 'ksParam_' + sIdAttr), 0, 0x7ffffffe, -1);
+ tsNow = self.getEffectiveDateParam();
+ self._checkForUnknownParameters();
+
+ # Fetch data and clear identifying attributes not relevant to the clone.
+ if idGenObject != -1:
+ oData = oDataType().initFromDbWithGenId(self._oDb, idGenObject);
+ else:
+ oData = oDataType().initFromDbWithId(self._oDb, idObject, tsNow);
+
+ setattr(oData, sIdAttr, None);
+ if sGenIdAttr is not None:
+ setattr(oData, sGenIdAttr, None);
+ oData.tsEffective = None;
+ oData.tsExpire = None;
+
+ # Display form.
+ oContent = oFormType(oData, oFormType.ksMode_Add, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.showForm()
+ return True
+
+
+ def _actionGenericFormPost(self, sMode, fnLogicAction, oDataType, oFormType, sRedirectTo, fStrict = True):
+ """
+ Generic POST request handling from a WuiFormContentBase child.
+
+ oDataType is a ModelDataBase child class.
+ oFormType is a WuiFormContentBase child class.
+ fnLogicAction is a method taking a oDataType instance and uidAuthor as arguments.
+ """
+ assert issubclass(oDataType, ModelDataBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ #
+ # Read and validate parameters.
+ #
+ oData = oDataType().initFromParams(oDisp = self, fStrict = fStrict);
+ sRedirectTo = self.getRedirectToParameter(sRedirectTo);
+ self._checkForUnknownParameters();
+ self._assertPostRequest();
+ if sMode == WuiFormContentBase.ksMode_Add and getattr(oData, 'kfIdAttrIsForForeign', False):
+ enmValidateFor = oData.ksValidateFor_AddForeignId;
+ elif sMode == WuiFormContentBase.ksMode_Add:
+ enmValidateFor = oData.ksValidateFor_Add;
+ else:
+ enmValidateFor = oData.ksValidateFor_Edit;
+ dErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
+
+ # Check that the user can do this.
+ sErrorMsg = None;
+ assert self._oCurUser is not None;
+ if self.isReadOnlyUser():
+ sErrorMsg = 'User %s is not allowed to modify anything!' % (self._oCurUser.sUsername,)
+
+ if not dErrors and not sErrorMsg:
+ oData.convertFromParamNull();
+
+ #
+ # Try do the job.
+ #
+ try:
+ fnLogicAction(oData, self._oCurUser.uid, fCommit = True);
+ except Exception as oXcpt:
+ self._oDb.rollback();
+ oForm = oFormType(oData, sMode, oDisp = self);
+ oForm.setRedirectTo(sRedirectTo);
+ sErrorMsg = str(oXcpt) if not config.g_kfDebugDbXcpt else '\n'.join(utils.getXcptInfo(4));
+ (self._sPageTitle, self._sPageBody) = oForm.showForm(sErrorMsg = sErrorMsg);
+ else:
+ #
+ # Worked, redirect to the specified page.
+ #
+ self._sPageTitle = None;
+ self._sPageBody = None;
+ self._sRedirectTo = sRedirectTo;
+ else:
+ oForm = oFormType(oData, sMode, oDisp = self);
+ oForm.setRedirectTo(sRedirectTo);
+ (self._sPageTitle, self._sPageBody) = oForm.showForm(dErrors = dErrors, sErrorMsg = sErrorMsg);
+ return True;
+
+ def _actionGenericFormAddPost(self, oDataType, oLogicType, oFormType, sRedirAction, fStrict=True):
+ """
+ Generic add entry POST request handling from a WuiFormContentBase child.
+
+ oDataType is a ModelDataBase child class.
+ oLogicType is a class that implements addEntry.
+ oFormType is a WuiFormContentBase child class.
+ sRedirAction is what action to redirect to on success.
+ """
+ assert issubclass(oDataType, ModelDataBase);
+ assert issubclass(oLogicType, ModelLogicBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ oLogic = oLogicType(self._oDb);
+ return self._actionGenericFormPost(WuiFormContentBase.ksMode_Add, oLogic.addEntry, oDataType, oFormType,
+ '?' + webutils.encodeUrlParams({self.ksParamAction: sRedirAction}), fStrict=fStrict)
+
+ def _actionGenericFormEditPost(self, oDataType, oLogicType, oFormType, sRedirAction, fStrict = True):
+ """
+ Generic edit POST request handling from a WuiFormContentBase child.
+
+ oDataType is a ModelDataBase child class.
+ oLogicType is a class that implements addEntry.
+ oFormType is a WuiFormContentBase child class.
+ sRedirAction is what action to redirect to on success.
+ """
+ assert issubclass(oDataType, ModelDataBase);
+ assert issubclass(oLogicType, ModelLogicBase);
+ from testmanager.webui.wuicontentbase import WuiFormContentBase;
+ assert issubclass(oFormType, WuiFormContentBase);
+
+ oLogic = oLogicType(self._oDb);
+ return self._actionGenericFormPost(WuiFormContentBase.ksMode_Edit, oLogic.editEntry, oDataType, oFormType,
+ '?' + webutils.encodeUrlParams({self.ksParamAction: sRedirAction}),
+ fStrict = fStrict);
+
+ def _unauthorizedUser(self):
+ """
+ Displays the unauthorized user message (corresponding record is not
+ present in DB).
+ """
+
+ sLoginName = self._oSrvGlue.getLoginName();
+
+ # Report to system log
+ oSystemLogLogic = SystemLogLogic(self._oDb);
+ oSystemLogLogic.addEntry(SystemLogData.ksEvent_UserAccountUnknown,
+ 'Unknown user (%s) attempts to access from %s'
+ % (sLoginName, self._oSrvGlue.getClientAddr()),
+ 24, fCommit = True)
+
+ # Display message.
+ self._sPageTitle = 'User not authorized'
+ self._sPageBody = """
+ <p>Access denied for user <b>%s</b>.
+ Please contact an admin user to set up your access.</p>
+ """ % (sLoginName,)
+ return True;
+
+ def dispatchRequest(self):
+ """
+ Dispatches a request.
+ """
+
+ #
+ # Get the parameters and checks for duplicates.
+ #
+ try:
+ dParams = self._oSrvGlue.getParameters();
+ except Exception as oXcpt:
+ raise WuiException('Error retriving parameters: %s' % (oXcpt,));
+
+ for sKey in dParams.keys():
+
+ # Take care about strings which may contain unicode characters: convert percent-encoded symbols back to unicode.
+ for idxItem, _ in enumerate(dParams[sKey]):
+ dParams[sKey][idxItem] = utils.toUnicode(dParams[sKey][idxItem], 'utf-8');
+
+ if not len(dParams[sKey]) > 1:
+ dParams[sKey] = dParams[sKey][0];
+ self._dParams = dParams;
+
+ #
+ # Figure out the requested action and validate it.
+ #
+ if self.ksParamAction in self._dParams:
+ self._sAction = self._dParams[self.ksParamAction];
+ self._asCheckedParams.append(self.ksParamAction);
+ else:
+ self._sAction = self.ksActionDefault;
+
+ if isinstance(self._sAction, list) or self._sAction not in self._dDispatch:
+ raise WuiException('Unknown action "%s" requested' % (self._sAction,));
+
+ #
+ # Call action handler and generate the page (if necessary).
+ #
+ if self._oCurUser is not None:
+ self._debugProcessDispatch();
+ if self._dDispatch[self._sAction]() is self.ksDispatchRcAllDone:
+ return True;
+ else:
+ self._unauthorizedUser();
+
+ if self._sRedirectTo is None:
+ self._generatePage();
+ else:
+ self._redirectPage();
+ return True;
+
+
+ def dprint(self, sText):
+ """ Debug printing. """
+ if config.g_kfWebUiDebug:
+ self._oSrvGlue.dprint(sText);
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py b/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py
new file mode 100755
index 00000000..c91e6036
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py
@@ -0,0 +1,1290 @@
+# -*- coding: utf-8 -*-
+# $Id: wuicontentbase.py $
+
+"""
+Test Manager Web-UI - Content Base Classes.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard python imports.
+import copy;
+import sys;
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager import config;
+from testmanager.webui.wuibase import WuiDispatcherBase, WuiException;
+from testmanager.webui.wuihlpform import WuiHlpForm;
+from testmanager.core import db;
+from testmanager.core.base import AttributeChangeEntryPre;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ unicode = str; # pylint: disable=redefined-builtin,invalid-name
+
+
+class WuiHtmlBase(object): # pylint: disable=too-few-public-methods
+ """
+ Base class for HTML objects.
+ """
+
+ def __init__(self):
+ """Dummy init to shut up pylint."""
+ pass; # pylint: disable=unnecessary-pass
+
+ def toHtml(self):
+
+ """
+ Must be overridden by sub-classes.
+ """
+ assert False;
+ return '';
+
+ def __str__(self):
+ """ String representation is HTML, simplifying formatting and such. """
+ return self.toHtml();
+
+
+class WuiLinkBase(WuiHtmlBase): # pylint: disable=too-few-public-methods
+ """
+ For passing links from WuiListContentBase._formatListEntry.
+ """
+
+ def __init__(self, sName, sUrlBase, dParams = None, sConfirm = None, sTitle = None,
+ sFragmentId = None, fBracketed = True, sExtraAttrs = ''):
+ WuiHtmlBase.__init__(self);
+ self.sName = sName
+ self.sUrl = sUrlBase
+ self.sConfirm = sConfirm;
+ self.sTitle = sTitle;
+ self.fBracketed = fBracketed;
+ self.sExtraAttrs = sExtraAttrs;
+
+ if dParams:
+ # Do some massaging of None arguments.
+ dParams = dict(dParams);
+ for sKey in dParams:
+ if dParams[sKey] is None:
+ dParams[sKey] = '';
+ self.sUrl += '?' + webutils.encodeUrlParams(dParams);
+
+ if sFragmentId is not None:
+ self.sUrl += '#' + sFragmentId;
+
+ def setBracketed(self, fBracketed):
+ """Changes the bracketing style."""
+ self.fBracketed = fBracketed;
+ return True;
+
+ def toHtml(self):
+ """
+ Returns a simple HTML anchor element.
+ """
+ sExtraAttrs = self.sExtraAttrs;
+ if self.sConfirm is not None:
+ sExtraAttrs += 'onclick=\'return confirm("%s");\' ' % (webutils.escapeAttr(self.sConfirm),);
+ if self.sTitle is not None:
+ sExtraAttrs += 'title="%s" ' % (webutils.escapeAttr(self.sTitle),);
+ if sExtraAttrs and sExtraAttrs[-1] != ' ':
+ sExtraAttrs += ' ';
+
+ sFmt = '[<a %shref="%s">%s</a>]';
+ if not self.fBracketed:
+ sFmt = '<a %shref="%s">%s</a>';
+ return sFmt % (sExtraAttrs, webutils.escapeAttr(self.sUrl), webutils.escapeElem(self.sName));
+
+ @staticmethod
+ def estimateStringWidth(sString):
+ """
+ Takes a string and estimate it's width so the caller can pad with
+ U+2002 before tab in a title text. This is very very rough.
+ """
+ cchWidth = 0;
+ for ch in sString:
+ if ch.isupper() or ch in u'wm\u2007\u2003\u2001\u3000':
+ cchWidth += 2;
+ else:
+ cchWidth += 1;
+ return cchWidth;
+
+ @staticmethod
+ def getStringWidthPaddingEx(cchWidth, cchMaxWidth):
+ """ Works with estiamteStringWidth(). """
+ if cchWidth + 2 <= cchMaxWidth:
+ return u'\u2002' * ((cchMaxWidth - cchWidth) * 2 // 3)
+ return u'';
+
+ @staticmethod
+ def getStringWidthPadding(sString, cchMaxWidth):
+ """ Works with estiamteStringWidth(). """
+ return WuiLinkBase.getStringWidthPaddingEx(WuiLinkBase.estimateStringWidth(sString), cchMaxWidth);
+
+ @staticmethod
+ def padStringToWidth(sString, cchMaxWidth):
+ """ Works with estimateStringWidth. """
+ cchWidth = WuiLinkBase.estimateStringWidth(sString);
+ if cchWidth < cchMaxWidth:
+ return sString + WuiLinkBase.getStringWidthPaddingEx(cchWidth, cchMaxWidth);
+ return sString;
+
+
+class WuiTmLink(WuiLinkBase): # pylint: disable=too-few-public-methods
+ """ Local link to the test manager. """
+
+ kdDbgParams = [];
+
+ def __init__(self, sName, sUrlBase, dParams = None, sConfirm = None, sTitle = None,
+ sFragmentId = None, fBracketed = True):
+
+ # Add debug parameters if necessary.
+ if self.kdDbgParams:
+ if not dParams:
+ dParams = dict(self.kdDbgParams);
+ else:
+ dParams = dict(dParams);
+ for sKey in self.kdDbgParams:
+ if sKey not in dParams:
+ dParams[sKey] = self.kdDbgParams[sKey];
+
+ WuiLinkBase.__init__(self, sName, sUrlBase, dParams, sConfirm, sTitle, sFragmentId, fBracketed);
+
+
+class WuiAdminLink(WuiTmLink): # pylint: disable=too-few-public-methods
+ """ Local link to the test manager's admin portion. """
+
+ def __init__(self, sName, sAction, tsEffectiveDate = None, dParams = None, sConfirm = None, sTitle = None,
+ sFragmentId = None, fBracketed = True):
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ if not dParams:
+ dParams = {};
+ else:
+ dParams = dict(dParams);
+ if sAction is not None:
+ dParams[WuiAdmin.ksParamAction] = sAction;
+ if tsEffectiveDate is not None:
+ dParams[WuiAdmin.ksParamEffectiveDate] = tsEffectiveDate;
+ WuiTmLink.__init__(self, sName, WuiAdmin.ksScriptName, dParams = dParams, sConfirm = sConfirm, sTitle = sTitle,
+ sFragmentId = sFragmentId, fBracketed = fBracketed);
+
+class WuiMainLink(WuiTmLink): # pylint: disable=too-few-public-methods
+ """ Local link to the test manager's main portion. """
+
+ def __init__(self, sName, sAction, dParams = None, sConfirm = None, sTitle = None, sFragmentId = None, fBracketed = True):
+ if not dParams:
+ dParams = {};
+ else:
+ dParams = dict(dParams);
+ from testmanager.webui.wuimain import WuiMain;
+ if sAction is not None:
+ dParams[WuiMain.ksParamAction] = sAction;
+ WuiTmLink.__init__(self, sName, WuiMain.ksScriptName, dParams = dParams, sConfirm = sConfirm, sTitle = sTitle,
+ sFragmentId = sFragmentId, fBracketed = fBracketed);
+
+class WuiSvnLink(WuiLinkBase): # pylint: disable=too-few-public-methods
+ """
+ For linking to a SVN revision.
+ """
+ def __init__(self, iRevision, sName = None, fBracketed = True, sExtraAttrs = ''):
+ if sName is None:
+ sName = 'r%s' % (iRevision,);
+ WuiLinkBase.__init__(self, sName, config.g_ksTracLogUrlPrefix, { 'rev': iRevision,},
+ fBracketed = fBracketed, sExtraAttrs = sExtraAttrs);
+
+class WuiSvnLinkWithTooltip(WuiSvnLink): # pylint: disable=too-few-public-methods
+ """
+ For linking to a SVN revision with changelog tooltip.
+ """
+ def __init__(self, iRevision, sRepository, sName = None, fBracketed = True):
+ sExtraAttrs = ' onmouseover="return svnHistoryTooltipShow(event,\'%s\',%s);" onmouseout="return tooltipHide();"' \
+ % ( sRepository, iRevision, );
+ WuiSvnLink.__init__(self, iRevision, sName = sName, fBracketed = fBracketed, sExtraAttrs = sExtraAttrs);
+
+class WuiBuildLogLink(WuiLinkBase):
+ """
+ For linking to a build log.
+ """
+ def __init__(self, sUrl, sName = None, fBracketed = True):
+ assert sUrl;
+ if sName is None:
+ sName = 'Build log';
+ if not webutils.hasSchema(sUrl):
+ WuiLinkBase.__init__(self, sName, config.g_ksBuildLogUrlPrefix + sUrl, fBracketed = fBracketed);
+ else:
+ WuiLinkBase.__init__(self, sName, sUrl, fBracketed = fBracketed);
+
+class WuiRawHtml(WuiHtmlBase): # pylint: disable=too-few-public-methods
+ """
+ For passing raw html from WuiListContentBase._formatListEntry.
+ """
+ def __init__(self, sHtml):
+ self.sHtml = sHtml;
+ WuiHtmlBase.__init__(self);
+
+ def toHtml(self):
+ return self.sHtml;
+
+class WuiHtmlKeeper(WuiHtmlBase): # pylint: disable=too-few-public-methods
+ """
+ For keeping a list of elements, concatenating their toHtml output together.
+ """
+ def __init__(self, aoInitial = None, sSep = ' '):
+ WuiHtmlBase.__init__(self);
+ self.sSep = sSep;
+ self.aoKept = [];
+ if aoInitial is not None:
+ if isinstance(aoInitial, WuiHtmlBase):
+ self.aoKept.append(aoInitial);
+ else:
+ self.aoKept.extend(aoInitial);
+
+ def append(self, oObject):
+ """ Appends one objects. """
+ self.aoKept.append(oObject);
+
+ def extend(self, aoObjects):
+ """ Appends a list of objects. """
+ self.aoKept.extend(aoObjects);
+
+ def toHtml(self):
+ return self.sSep.join(oObj.toHtml() for oObj in self.aoKept);
+
+class WuiSpanText(WuiRawHtml): # pylint: disable=too-few-public-methods
+ """
+ Outputs the given text within a span of the given CSS class.
+ """
+ def __init__(self, sSpanClass, sText, sTitle = None):
+ if sTitle is None:
+ WuiRawHtml.__init__(self,
+ u'<span class="%s">%s</span>'
+ % ( webutils.escapeAttr(sSpanClass), webutils.escapeElem(sText),));
+ else:
+ WuiRawHtml.__init__(self,
+ u'<span class="%s" title="%s">%s</span>'
+ % ( webutils.escapeAttr(sSpanClass), webutils.escapeAttr(sTitle), webutils.escapeElem(sText),));
+
+class WuiElementText(WuiRawHtml): # pylint: disable=too-few-public-methods
+ """
+ Outputs the given element text.
+ """
+ def __init__(self, sText):
+ WuiRawHtml.__init__(self, webutils.escapeElem(sText));
+
+
+class WuiContentBase(object): # pylint: disable=too-few-public-methods
+ """
+ Base for the content classes.
+ """
+
+ ## The text/symbol for a very short add link.
+ ksShortAddLink = u'\u2795'
+ ## HTML hex entity string for ksShortAddLink.
+ ksShortAddLinkHtml = '&#x2795;;'
+ ## The text/symbol for a very short edit link.
+ ksShortEditLink = u'\u270D'
+ ## HTML hex entity string for ksShortDetailsLink.
+ ksShortEditLinkHtml = '&#x270d;'
+ ## The text/symbol for a very short details link.
+ ksShortDetailsLink = u'\U0001f6c8\ufe0e'
+ ## HTML hex entity string for ksShortDetailsLink.
+ ksShortDetailsLinkHtml = '&#x1f6c8;;&#xfe0e;'
+ ## The text/symbol for a very short change log / details / previous page link.
+ ksShortChangeLogLink = u'\u2397'
+ ## HTML hex entity string for ksShortDetailsLink.
+ ksShortChangeLogLinkHtml = '&#x2397;'
+ ## The text/symbol for a very short reports link.
+ ksShortReportLink = u'\U0001f4ca\ufe0e'
+ ## HTML hex entity string for ksShortReportLink.
+ ksShortReportLinkHtml = '&#x1f4ca;&#xfe0e;'
+ ## The text/symbol for a very short test results link.
+ ksShortTestResultsLink = u'\U0001f5d0\ufe0e'
+
+
+ def __init__(self, fnDPrint = None, oDisp = None):
+ self._oDisp = oDisp; # WuiDispatcherBase.
+ self._fnDPrint = fnDPrint;
+ if fnDPrint is None and oDisp is not None:
+ self._fnDPrint = oDisp.dprint;
+
+ def dprint(self, sText):
+ """ Debug printing. """
+ if self._fnDPrint:
+ self._fnDPrint(sText);
+
+ @staticmethod
+ def formatTsShort(oTs):
+ """
+ Formats a timestamp (db rep) into a short form.
+ """
+ oTsZulu = db.dbTimestampToZuluDatetime(oTs);
+ sTs = oTsZulu.strftime('%Y-%m-%d %H:%M:%SZ');
+ return unicode(sTs).replace('-', u'\u2011').replace(' ', u'\u00a0');
+
+ def getNowTs(self):
+ """ Gets a database compatible current timestamp from python. See db.dbTimestampPythonNow(). """
+ return db.dbTimestampPythonNow();
+
+ def formatIntervalShort(self, oInterval):
+ """
+ Formats an interval (db rep) into a short form.
+ """
+ # default formatting for negative intervals.
+ if oInterval.days < 0:
+ return str(oInterval);
+
+ # Figure the hour, min and sec counts.
+ cHours = oInterval.seconds // 3600;
+ cMinutes = (oInterval.seconds % 3600) // 60;
+ cSeconds = oInterval.seconds - cHours * 3600 - cMinutes * 60;
+
+ # Tailor formatting to the interval length.
+ if oInterval.days > 0:
+ if oInterval.days > 1:
+ return '%d days, %d:%02d:%02d' % (oInterval.days, cHours, cMinutes, cSeconds);
+ return '1 day, %d:%02d:%02d' % (cHours, cMinutes, cSeconds);
+ if cMinutes > 0 or cSeconds >= 30 or cHours > 0:
+ return '%d:%02d:%02d' % (cHours, cMinutes, cSeconds);
+ if cSeconds >= 10:
+ return '%d.%ds' % (cSeconds, oInterval.microseconds // 100000);
+ if cSeconds > 0:
+ return '%d.%02ds' % (cSeconds, oInterval.microseconds // 10000);
+ return '%d ms' % (oInterval.microseconds // 1000,);
+
+ @staticmethod
+ def genericPageWalker(iCurItem, cItems, sHrefFmt, cWidth = 11, iBase = 1, sItemName = 'page'):
+ """
+ Generic page walker generator.
+
+ sHrefFmt has three %s sequences:
+ 1. The first is the page number link parameter (0-based).
+ 2. The title text, iBase-based number or text.
+ 3. The link text, iBase-based number or text.
+ """
+
+ # Calc display range.
+ iStart = 0 if iCurItem - cWidth // 2 <= cWidth // 4 else iCurItem - cWidth // 2;
+ iEnd = iStart + cWidth;
+ if iEnd > cItems:
+ iEnd = cItems;
+ if cItems > cWidth:
+ iStart = cItems - cWidth;
+
+ sHtml = u'';
+
+ # Previous page (using << >> because &laquo; and &raquo are too tiny).
+ if iCurItem > 0:
+ sHtml += '%s&nbsp;&nbsp;' % sHrefFmt % (iCurItem - 1, 'previous ' + sItemName, '&lt;&lt;');
+ else:
+ sHtml += '&lt;&lt;&nbsp;&nbsp;';
+
+ # 1 2 3 4...
+ if iStart > 0:
+ sHtml += '%s&nbsp; ... &nbsp;\n' % (sHrefFmt % (0, 'first %s' % (sItemName,), 0 + iBase),);
+
+ sHtml += '&nbsp;\n'.join(sHrefFmt % (i, '%s %d' % (sItemName, i + iBase), i + iBase) if i != iCurItem
+ else unicode(i + iBase)
+ for i in range(iStart, iEnd));
+ if iEnd < cItems:
+ sHtml += '&nbsp; ... &nbsp;%s\n' % (sHrefFmt % (cItems - 1, 'last %s' % (sItemName,), cItems - 1 + iBase));
+
+ # Next page.
+ if iCurItem + 1 < cItems:
+ sHtml += '&nbsp;&nbsp;%s' % sHrefFmt % (iCurItem + 1, 'next ' + sItemName, '&gt;&gt;');
+ else:
+ sHtml += '&nbsp;&nbsp;&gt;&gt;';
+
+ return sHtml;
+
+class WuiSingleContentBase(WuiContentBase): # pylint: disable=too-few-public-methods
+ """
+ Base for the content classes working on a single data object (oData).
+ """
+ def __init__(self, oData, oDisp = None, fnDPrint = None):
+ WuiContentBase.__init__(self, oDisp = oDisp, fnDPrint = fnDPrint);
+ self._oData = oData; # Usually ModelDataBase.
+
+
+class WuiFormContentBase(WuiSingleContentBase): # pylint: disable=too-few-public-methods
+ """
+ Base class for simple input form content classes (single data object).
+ """
+
+ ## @name Form mode.
+ ## @{
+ ksMode_Add = 'add';
+ ksMode_Edit = 'edit';
+ ksMode_Show = 'show';
+ ## @}
+
+ ## Default action mappings.
+ kdSubmitActionMappings = {
+ ksMode_Add: 'AddPost',
+ ksMode_Edit: 'EditPost',
+ };
+
+ def __init__(self, oData, sMode, sCoreName, oDisp, sTitle, sId = None, fEditable = True, sSubmitAction = None):
+ WuiSingleContentBase.__init__(self, copy.copy(oData), oDisp);
+ assert sMode in [self.ksMode_Add, self.ksMode_Edit, self.ksMode_Show];
+ assert len(sTitle) > 1;
+ assert sId is None or sId;
+
+ self._sMode = sMode;
+ self._sCoreName = sCoreName;
+ self._sActionBase = 'ksAction' + sCoreName;
+ self._sTitle = sTitle;
+ self._sId = sId if sId is not None else (type(oData).__name__.lower() + 'form');
+ self._fEditable = fEditable and (oDisp is None or not oDisp.isReadOnlyUser())
+ self._sSubmitAction = sSubmitAction;
+ if sSubmitAction is None and sMode != self.ksMode_Show:
+ self._sSubmitAction = getattr(oDisp, self._sActionBase + self.kdSubmitActionMappings[sMode]);
+ self._sRedirectTo = None;
+
+
+ def _populateForm(self, oForm, oData):
+ """
+ Populates the form. oData has parameter NULL values.
+ This must be reimplemented by the child.
+ """
+ _ = oForm; _ = oData;
+ raise Exception('Reimplement me!');
+
+ def _generatePostFormContent(self, oData):
+ """
+ Generate optional content that comes below the form.
+ Returns a list of tuples, where the first tuple element is the title
+ and the second the content. I.e. similar to show() output.
+ """
+ _ = oData;
+ return [];
+
+ @staticmethod
+ def _calcChangeLogEntryLinks(aoEntries, iEntry):
+ """
+ Returns an array of links to go with the change log entry.
+ """
+ _ = aoEntries; _ = iEntry;
+ ## @todo detect deletion and recreation.
+ ## @todo view details link.
+ ## @todo restore link (need new action)
+ ## @todo clone link.
+ return [];
+
+ @staticmethod
+ def _guessChangeLogEntryDescription(aoEntries, iEntry):
+ """
+ Guesses the action + author that caused the change log entry.
+ Returns descriptive string.
+ """
+ oEntry = aoEntries[iEntry];
+
+ # Figure the author of the change.
+ if oEntry.sAuthor is not None:
+ sAuthor = '%s (#%s)' % (oEntry.sAuthor, oEntry.uidAuthor,);
+ elif oEntry.uidAuthor is not None:
+ sAuthor = '#%d (??)' % (oEntry.uidAuthor,);
+ else:
+ sAuthor = None;
+
+ # Figure the action.
+ if oEntry.oOldRaw is None:
+ if sAuthor is None:
+ return 'Created by batch job.';
+ return 'Created by %s.' % (sAuthor,);
+
+ if sAuthor is None:
+ return 'Automatically updated.'
+ return 'Modified by %s.' % (sAuthor,);
+
+ @staticmethod
+ def formatChangeLogEntry(aoEntries, iEntry, sUrl, dParams):
+ """
+ Formats one change log entry into one or more HTML table rows.
+
+ The sUrl and dParams arguments are used to construct links to historical
+ data using the tsEffective value. If no links wanted, they'll both be None.
+
+ Note! The parameters are given as array + index in case someone wishes
+ to access adjacent entries later in order to generate better
+ change descriptions.
+ """
+ oEntry = aoEntries[iEntry];
+
+ # Turn the effective date into a URL if we can:
+ if sUrl:
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = oEntry.tsEffective;
+ sEffective = WuiLinkBase(WuiFormContentBase.formatTsShort(oEntry.tsEffective), sUrl,
+ dParams, fBracketed = False).toHtml();
+ else:
+ sEffective = webutils.escapeElem(WuiFormContentBase.formatTsShort(oEntry.tsEffective))
+
+ # The primary row.
+ sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven';
+ sContent = ' <tr class="%s">\n' \
+ ' <td rowspan="%d">%s</td>\n' \
+ ' <td rowspan="%d">%s</td>\n' \
+ ' <td colspan="3">%s%s</td>\n' \
+ ' </tr>\n' \
+ % ( sRowClass,
+ len(oEntry.aoChanges) + 1, sEffective,
+ len(oEntry.aoChanges) + 1, webutils.escapeElem(WuiFormContentBase.formatTsShort(oEntry.tsExpire)),
+ WuiFormContentBase._guessChangeLogEntryDescription(aoEntries, iEntry),
+ ' '.join(oLink.toHtml() for oLink in WuiFormContentBase._calcChangeLogEntryLinks(aoEntries, iEntry)),);
+
+ # Additional rows for each changed attribute.
+ j = 0;
+ for oChange in oEntry.aoChanges:
+ if isinstance(oChange, AttributeChangeEntryPre):
+ sContent += ' <tr class="%s%s"><td>%s</td>'\
+ '<td><div class="tdpre">%s%s%s</div></td>' \
+ '<td><div class="tdpre">%s%s%s</div></td></tr>\n' \
+ % ( sRowClass, 'odd' if j & 1 else 'even',
+ webutils.escapeElem(oChange.sAttr),
+ '<pre>' if oChange.sOldText else '',
+ webutils.escapeElem(oChange.sOldText),
+ '</pre>' if oChange.sOldText else '',
+ '<pre>' if oChange.sNewText else '',
+ webutils.escapeElem(oChange.sNewText),
+ '</pre>' if oChange.sNewText else '', );
+ else:
+ sContent += ' <tr class="%s%s"><td>%s</td><td>%s</td><td>%s</td></tr>\n' \
+ % ( sRowClass, 'odd' if j & 1 else 'even',
+ webutils.escapeElem(oChange.sAttr),
+ webutils.escapeElem(oChange.sOldText),
+ webutils.escapeElem(oChange.sNewText), );
+ j += 1;
+
+ return sContent;
+
+ def _showChangeLogNavi(self, fMoreEntries, iPageNo, cEntriesPerPage, tsNow, sWhere):
+ """
+ Returns the HTML for the change log navigator.
+ Note! See also _generateNavigation.
+ """
+ sNavigation = '<div class="tmlistnav-%s">\n' % sWhere;
+ sNavigation += ' <table class="tmlistnavtab">\n' \
+ ' <tr>\n';
+ dParams = self._oDisp.getParameters();
+ dParams[WuiDispatcherBase.ksParamChangeLogEntriesPerPage] = cEntriesPerPage;
+ dParams[WuiDispatcherBase.ksParamChangeLogPageNo] = iPageNo;
+ if tsNow is not None:
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = tsNow;
+
+ # Prev and combo box in one cell. Both inside the form for formatting reasons.
+ sNavigation += ' <td>\n' \
+ ' <form name="ChangeLogEntriesPerPageForm" method="GET">\n'
+
+ # Prev
+ if iPageNo > 0:
+ dParams[WuiDispatcherBase.ksParamChangeLogPageNo] = iPageNo - 1;
+ sNavigation += '<a href="?%s#tmchangelog">Previous</a>\n' \
+ % (webutils.encodeUrlParams(dParams),);
+ dParams[WuiDispatcherBase.ksParamChangeLogPageNo] = iPageNo;
+ else:
+ sNavigation += 'Previous\n';
+
+ # Entries per page selector.
+ del dParams[WuiDispatcherBase.ksParamChangeLogEntriesPerPage];
+ sNavigation += '&nbsp; &nbsp;\n' \
+ ' <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
+ 'this.options[this.selectedIndex].value + \'#tmchangelog\';" ' \
+ 'title="Max change log entries per page">\n' \
+ % (WuiDispatcherBase.ksParamChangeLogEntriesPerPage,
+ webutils.encodeUrlParams(dParams),
+ WuiDispatcherBase.ksParamChangeLogEntriesPerPage);
+ dParams[WuiDispatcherBase.ksParamChangeLogEntriesPerPage] = cEntriesPerPage;
+
+ for iEntriesPerPage in [2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 8192]:
+ sNavigation += ' <option value="%d" %s>%d entries per page</option>\n' \
+ % ( iEntriesPerPage,
+ 'selected="selected"' if iEntriesPerPage == cEntriesPerPage else '',
+ iEntriesPerPage );
+ sNavigation += ' </select>\n';
+
+ # End of cell (and form).
+ sNavigation += ' </form>\n' \
+ ' </td>\n';
+
+ # Next
+ if fMoreEntries:
+ dParams[WuiDispatcherBase.ksParamChangeLogPageNo] = iPageNo + 1;
+ sNavigation += ' <td><a href="?%s#tmchangelog">Next</a></td>\n' \
+ % (webutils.encodeUrlParams(dParams),);
+ else:
+ sNavigation += ' <td>Next</td>\n';
+
+ sNavigation += ' </tr>\n' \
+ ' </table>\n' \
+ '</div>\n';
+ return sNavigation;
+
+ def setRedirectTo(self, sRedirectTo):
+ """
+ For setting the hidden redirect-to field.
+ """
+ self._sRedirectTo = sRedirectTo;
+ return True;
+
+ def showChangeLog(self, aoEntries, fMoreEntries, iPageNo, cEntriesPerPage, tsNow, fShowNavigation = True):
+ """
+ Render the change log, returning raw HTML.
+ aoEntries is an array of ChangeLogEntry.
+ """
+ sContent = '\n' \
+ '<hr>\n' \
+ '<div id="tmchangelog">\n' \
+ ' <h3>Change Log </h3>\n';
+ if fShowNavigation:
+ sContent += self._showChangeLogNavi(fMoreEntries, iPageNo, cEntriesPerPage, tsNow, 'top');
+ sContent += ' <table class="tmtable tmchangelog">\n' \
+ ' <thead class="tmheader">' \
+ ' <tr>' \
+ ' <th rowspan="2">When</th>\n' \
+ ' <th rowspan="2">Expire (excl)</th>\n' \
+ ' <th colspan="3">Changes</th>\n' \
+ ' </tr>\n' \
+ ' <tr>\n' \
+ ' <th>Attribute</th>\n' \
+ ' <th>Old value</th>\n' \
+ ' <th>New value</th>\n' \
+ ' </tr>\n' \
+ ' </thead>\n' \
+ ' <tbody>\n';
+
+ if self._sMode == self.ksMode_Show:
+ sUrl = self._oDisp.getUrlNoParams();
+ dParams = self._oDisp.getParameters();
+ else:
+ sUrl = None;
+ dParams = None;
+
+ for iEntry, _ in enumerate(aoEntries):
+ sContent += self.formatChangeLogEntry(aoEntries, iEntry, sUrl, dParams);
+
+ sContent += ' </tbody>\n' \
+ ' </table>\n';
+ if fShowNavigation and len(aoEntries) >= 8:
+ sContent += self._showChangeLogNavi(fMoreEntries, iPageNo, cEntriesPerPage, tsNow, 'bottom');
+ sContent += '</div>\n\n';
+ return sContent;
+
+ def _generateTopRowFormActions(self, oData):
+ """
+ Returns a list of WuiTmLinks.
+ """
+ aoActions = [];
+ if self._sMode == self.ksMode_Show and self._fEditable:
+ # Remove _idGen and effective date since we're always editing the current data,
+ # and make sure the primary ID is present. Also remove change log stuff.
+ dParams = self._oDisp.getParameters();
+ if hasattr(oData, 'ksIdGenAttr'):
+ sIdGenParam = getattr(oData, 'ksParam_' + oData.ksIdGenAttr);
+ if sIdGenParam in dParams:
+ del dParams[sIdGenParam];
+ for sParam in [ WuiDispatcherBase.ksParamEffectiveDate, ] + list(WuiDispatcherBase.kasChangeLogParams):
+ if sParam in dParams:
+ del dParams[sParam];
+ dParams[getattr(oData, 'ksParam_' + oData.ksIdAttr)] = getattr(oData, oData.ksIdAttr);
+
+ dParams[WuiDispatcherBase.ksParamAction] = getattr(self._oDisp, self._sActionBase + 'Edit');
+ aoActions.append(WuiTmLink('Edit', '', dParams));
+
+ # Add clone operation if available. This uses the same data selection as for showing details. No change log.
+ if hasattr(self._oDisp, self._sActionBase + 'Clone'):
+ dParams = self._oDisp.getParameters();
+ for sParam in WuiDispatcherBase.kasChangeLogParams:
+ if sParam in dParams:
+ del dParams[sParam];
+ dParams[WuiDispatcherBase.ksParamAction] = getattr(self._oDisp, self._sActionBase + 'Clone');
+ aoActions.append(WuiTmLink('Clone', '', dParams));
+
+ elif self._sMode == self.ksMode_Edit:
+ # Details views the details at a given time, so we need either idGen or an effecive date + regular id.
+ dParams = {};
+ if hasattr(oData, 'ksIdGenAttr'):
+ sIdGenParam = getattr(oData, 'ksParam_' + oData.ksIdGenAttr);
+ dParams[sIdGenParam] = getattr(oData, oData.ksIdGenAttr);
+ elif hasattr(oData, 'tsEffective'):
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = oData.tsEffective;
+ dParams[getattr(oData, 'ksParam_' + oData.ksIdAttr)] = getattr(oData, oData.ksIdAttr);
+ dParams[WuiDispatcherBase.ksParamAction] = getattr(self._oDisp, self._sActionBase + 'Details');
+ aoActions.append(WuiTmLink('Details', '', dParams));
+
+ # Add delete operation if available.
+ if hasattr(self._oDisp, self._sActionBase + 'DoRemove'):
+ dParams = self._oDisp.getParameters();
+ dParams[WuiDispatcherBase.ksParamAction] = getattr(self._oDisp, self._sActionBase + 'DoRemove');
+ dParams[getattr(oData, 'ksParam_' + oData.ksIdAttr)] = getattr(oData, oData.ksIdAttr);
+ aoActions.append(WuiTmLink('Delete', '', dParams, sConfirm = "Are you absolutely sure?"));
+
+ return aoActions;
+
+ def showForm(self, dErrors = None, sErrorMsg = None):
+ """
+ Render the form.
+ """
+ oForm = WuiHlpForm(self._sId,
+ '?' + webutils.encodeUrlParams({WuiDispatcherBase.ksParamAction: self._sSubmitAction}),
+ dErrors if dErrors is not None else {},
+ fReadOnly = self._sMode == self.ksMode_Show);
+
+ self._oData.convertToParamNull();
+
+ # If form cannot be constructed due to some reason we
+ # need to show this reason
+ try:
+ self._populateForm(oForm, self._oData);
+ if self._sRedirectTo is not None:
+ oForm.addTextHidden(self._oDisp.ksParamRedirectTo, self._sRedirectTo);
+ except WuiException as oXcpt:
+ sContent = unicode(oXcpt)
+ else:
+ sContent = oForm.finalize();
+
+ # Add any post form content.
+ atPostFormContent = self._generatePostFormContent(self._oData);
+ if atPostFormContent:
+ for iSection, tSection in enumerate(atPostFormContent):
+ (sSectionTitle, sSectionContent) = tSection;
+ sContent += u'<div id="postform-%d" class="tmformpostsection">\n' % (iSection,);
+ if sSectionTitle:
+ sContent += '<h3 class="tmformpostheader">%s</h3>\n' % (webutils.escapeElem(sSectionTitle),);
+ sContent += u' <div id="postform-%d-content" class="tmformpostcontent">\n' % (iSection,);
+ sContent += sSectionContent;
+ sContent += u' </div>\n' \
+ u'</div>\n';
+
+ # Add action to the top.
+ aoActions = self._generateTopRowFormActions(self._oData);
+ if aoActions:
+ sActionLinks = '<p>%s</p>' % (' '.join(unicode(oLink) for oLink in aoActions));
+ sContent = sActionLinks + sContent;
+
+ # Add error info to the top.
+ if sErrorMsg is not None:
+ sContent = '<p class="tmerrormsg">' + webutils.escapeElem(sErrorMsg) + '</p>\n' + sContent;
+
+ return (self._sTitle, sContent);
+
+ def getListOfItems(self, asListItems = tuple(), asSelectedItems = tuple()):
+ """
+ Format generic list which should be used by HTML form
+ """
+ aoRet = []
+ for sListItem in asListItems:
+ fEnabled = sListItem in asSelectedItems;
+ aoRet.append((sListItem, fEnabled, sListItem))
+ return aoRet
+
+
+class WuiListContentBase(WuiContentBase):
+ """
+ Base for the list content classes.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffectiveDate, sTitle, # pylint: disable=too-many-arguments
+ sId = None, fnDPrint = None, oDisp = None, aiSelectedSortColumns = None, fTimeNavigation = True):
+ WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
+ self._aoEntries = aoEntries; ## @todo should replace this with a Logic object and define methods for querying.
+ self._iPage = iPage;
+ self._cItemsPerPage = cItemsPerPage;
+ self._tsEffectiveDate = tsEffectiveDate;
+ self._fTimeNavigation = fTimeNavigation;
+ self._sTitle = sTitle; assert len(sTitle) > 1;
+ if sId is None:
+ sId = sTitle.strip().replace(' ', '').lower();
+ assert sId.strip();
+ self._sId = sId;
+ self._asColumnHeaders = [];
+ self._asColumnAttribs = [];
+ self._aaiColumnSorting = []; ##< list of list of integers
+ self._aiSelectedSortColumns = aiSelectedSortColumns; ##< list of integers
+
+ def _formatCommentCell(self, sComment, cMaxLines = 3, cchMaxLine = 63):
+ """
+ Helper functions for formatting comment cell.
+ Returns None or WuiRawHtml instance.
+ """
+ # Nothing to do for empty comments.
+ if sComment is None:
+ return None;
+ sComment = sComment.strip();
+ if not sComment:
+ return None;
+
+ # Restrict the text if necessary, making the whole text available thru mouse-over.
+ ## @todo this would be better done by java script or smth, so it could automatically adjust to the table size.
+ if len(sComment) > cchMaxLine or sComment.count('\n') >= cMaxLines:
+ sShortHtml = '';
+ for iLine, sLine in enumerate(sComment.split('\n')):
+ if iLine >= cMaxLines:
+ break;
+ if iLine > 0:
+ sShortHtml += '<br>\n';
+ if len(sLine) > cchMaxLine:
+ sShortHtml += webutils.escapeElem(sLine[:(cchMaxLine - 3)]);
+ sShortHtml += '...';
+ else:
+ sShortHtml += webutils.escapeElem(sLine);
+ return WuiRawHtml('<span class="tmcomment" title="%s">%s</span>' % (webutils.escapeAttr(sComment), sShortHtml,));
+
+ return WuiRawHtml('<span class="tmcomment">%s</span>' % (webutils.escapeElem(sComment).replace('\n', '<br>'),));
+
+ def _formatListEntry(self, iEntry):
+ """
+ Formats the specified list entry as a list of column values.
+ Returns HTML for a table row.
+
+ The child class really need to override this!
+ """
+ # ASSUMES ModelDataBase children.
+ asRet = [];
+ for sAttr in self._aoEntries[0].getDataAttributes():
+ asRet.append(getattr(self._aoEntries[iEntry], sAttr));
+ return asRet;
+
+ def _formatListEntryHtml(self, iEntry):
+ """
+ Formats the specified list entry as HTML.
+ Returns HTML for a table row.
+
+ The child class can override this to
+ """
+ if (iEntry + 1) & 1:
+ sRow = u' <tr class="tmodd">\n';
+ else:
+ sRow = u' <tr class="tmeven">\n';
+
+ aoValues = self._formatListEntry(iEntry);
+ assert len(aoValues) == len(self._asColumnHeaders), '%s vs %s' % (len(aoValues), len(self._asColumnHeaders));
+
+ for i, _ in enumerate(aoValues):
+ if i < len(self._asColumnAttribs) and self._asColumnAttribs[i]:
+ sRow += u' <td ' + self._asColumnAttribs[i] + '>';
+ else:
+ sRow += u' <td>';
+
+ if isinstance(aoValues[i], WuiHtmlBase):
+ sRow += aoValues[i].toHtml();
+ elif isinstance(aoValues[i], list):
+ if aoValues[i]:
+ for oElement in aoValues[i]:
+ if isinstance(oElement, WuiHtmlBase):
+ sRow += oElement.toHtml();
+ elif db.isDbTimestamp(oElement):
+ sRow += webutils.escapeElem(self.formatTsShort(oElement));
+ else:
+ sRow += webutils.escapeElem(unicode(oElement));
+ sRow += ' ';
+ elif db.isDbTimestamp(aoValues[i]):
+ sRow += webutils.escapeElem(self.formatTsShort(aoValues[i]));
+ elif db.isDbInterval(aoValues[i]):
+ sRow += webutils.escapeElem(self.formatIntervalShort(aoValues[i]));
+ elif aoValues[i] is not None:
+ sRow += webutils.escapeElem(unicode(aoValues[i]));
+
+ sRow += u'</td>\n';
+
+ return sRow + u' </tr>\n';
+
+ @staticmethod
+ def generateTimeNavigationComboBox(sWhere, dParams, tsEffective):
+ """
+ Generates the HTML for the xxxx ago combo box form.
+ """
+ sNavigation = '<form name="TmTimeNavCombo-%s" method="GET">\n' % (sWhere,);
+ sNavigation += ' <select name="%s" onchange="window.location=' % (WuiDispatcherBase.ksParamEffectiveDate);
+ sNavigation += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), WuiDispatcherBase.ksParamEffectiveDate)
+ sNavigation += 'this.options[this.selectedIndex].value;" title="Effective date">\n';
+
+ aoWayBackPoints = [
+ ('+0000-00-00 00:00:00.00', 'Now', ' title="Present Day. Present Time."'), # lain :)
+
+ ('-0000-00-00 01:00:00.00', '1 hour ago', ''),
+ ('-0000-00-00 02:00:00.00', '2 hours ago', ''),
+ ('-0000-00-00 03:00:00.00', '3 hours ago', ''),
+
+ ('-0000-00-01 00:00:00.00', '1 day ago', ''),
+ ('-0000-00-02 00:00:00.00', '2 days ago', ''),
+ ('-0000-00-03 00:00:00.00', '3 days ago', ''),
+
+ ('-0000-00-07 00:00:00.00', '1 week ago', ''),
+ ('-0000-00-14 00:00:00.00', '2 weeks ago', ''),
+ ('-0000-00-21 00:00:00.00', '3 weeks ago', ''),
+
+ ('-0000-01-00 00:00:00.00', '1 month ago', ''),
+ ('-0000-02-00 00:00:00.00', '2 months ago', ''),
+ ('-0000-03-00 00:00:00.00', '3 months ago', ''),
+ ('-0000-04-00 00:00:00.00', '4 months ago', ''),
+ ('-0000-05-00 00:00:00.00', '5 months ago', ''),
+ ('-0000-06-00 00:00:00.00', 'Half a year ago', ''),
+
+ ('-0001-00-00 00:00:00.00', '1 year ago', ''),
+ ]
+ fSelected = False;
+ for sTimestamp, sWayBackPointCaption, sExtraAttrs in aoWayBackPoints:
+ if sTimestamp == tsEffective:
+ fSelected = True;
+ sNavigation += ' <option value="%s"%s%s>%s</option>\n' \
+ % (webutils.quoteUrl(sTimestamp),
+ ' selected="selected"' if sTimestamp == tsEffective else '',
+ sExtraAttrs, sWayBackPointCaption, );
+ if not fSelected and tsEffective != '':
+ sNavigation += ' <option value="%s" selected>%s</option>\n' \
+ % (webutils.quoteUrl(tsEffective), WuiContentBase.formatTsShort(tsEffective))
+
+ sNavigation += ' </select>\n' \
+ '</form>\n';
+ return sNavigation;
+
+ @staticmethod
+ def generateTimeNavigationDateTime(sWhere, dParams, sNow):
+ """
+ Generates HTML for a form with date + time input fields.
+
+ Note! Modifies dParams!
+ """
+
+ #
+ # Date + time input fields. We use a java script helper to combine the two
+ # into a hidden field as there is no portable datetime input field type.
+ #
+ sNavigation = '<form method="get" action="?" onchange="timeNavigationUpdateHiddenEffDate(this,\'%s\')">' % (sWhere,);
+ if sNow is None:
+ sNow = utils.getIsoTimestamp();
+ else:
+ sNow = utils.normalizeIsoTimestampToZulu(sNow);
+ asSplit = sNow.split('T');
+ sNavigation += ' <input type="date" value="%s" id="EffDate%s"/> ' % (asSplit[0], sWhere, );
+ sNavigation += ' <input type="time" value="%s" id="EffTime%s"/> ' % (asSplit[1][:8], sWhere,);
+ sNavigation += ' <input type="hidden" name="%s" value="%s" id="EffDateTime%s"/>' \
+ % (WuiDispatcherBase.ksParamEffectiveDate, webutils.escapeAttr(sNow), sWhere);
+ for sKey in dParams:
+ sNavigation += ' <input type="hidden" name="%s" value="%s"/>' \
+ % (webutils.escapeAttr(sKey), webutils.escapeAttrToStr(dParams[sKey]));
+ sNavigation += ' <input type="submit" value="Set"/>\n' \
+ '</form>\n';
+ return sNavigation;
+
+ ## @todo move to better place! WuiMain uses it.
+ @staticmethod
+ def generateTimeNavigation(sWhere, dParams, tsEffectiveAbs, sPreamble = '', sPostamble = '', fKeepPageNo = False):
+ """
+ Returns HTML for time navigation.
+
+ Note! Modifies dParams!
+ Note! Views without a need for a timescale just stubs this method.
+ """
+ sNavigation = '<div class="tmtimenav-%s tmtimenav">%s' % (sWhere, sPreamble,);
+
+ #
+ # Prepare the URL parameters.
+ #
+ if WuiDispatcherBase.ksParamPageNo in dParams: # Forget about page No when changing a period
+ del dParams[WuiDispatcherBase.ksParamPageNo]
+ if not fKeepPageNo and WuiDispatcherBase.ksParamEffectiveDate in dParams:
+ tsEffectiveParam = dParams[WuiDispatcherBase.ksParamEffectiveDate];
+ del dParams[WuiDispatcherBase.ksParamEffectiveDate];
+ else:
+ tsEffectiveParam = ''
+
+ #
+ # Generate the individual parts.
+ #
+ sNavigation += WuiListContentBase.generateTimeNavigationDateTime(sWhere, dParams, tsEffectiveAbs);
+ sNavigation += WuiListContentBase.generateTimeNavigationComboBox(sWhere, dParams, tsEffectiveParam);
+
+ sNavigation += '%s</div>' % (sPostamble,);
+ return sNavigation;
+
+ def _generateTimeNavigation(self, sWhere, sPreamble = '', sPostamble = ''):
+ """
+ Returns HTML for time navigation.
+
+ Note! Views without a need for a timescale just stubs this method.
+ """
+ return self.generateTimeNavigation(sWhere, self._oDisp.getParameters(), self._oDisp.getEffectiveDateParam(),
+ sPreamble, sPostamble)
+
+ @staticmethod
+ def generateItemPerPageSelector(sWhere, dParams, cCurItemsPerPage):
+ """
+ Generate HTML code for items per page selector.
+ Note! Modifies dParams!
+ """
+
+ # Drop the current page count parameter.
+ if WuiDispatcherBase.ksParamItemsPerPage in dParams:
+ del dParams[WuiDispatcherBase.ksParamItemsPerPage];
+
+ # Remove the current page number.
+ if WuiDispatcherBase.ksParamPageNo in dParams:
+ del dParams[WuiDispatcherBase.ksParamPageNo];
+
+ sHtmlItemsPerPageSelector = '<form name="TmItemsPerPageForm-%s" method="GET" class="tmitemsperpage-%s tmitemsperpage">\n'\
+ ' <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
+ 'this.options[this.selectedIndex].value;" title="Max items per page">\n' \
+ % (sWhere, WuiDispatcherBase.ksParamItemsPerPage, sWhere,
+ webutils.encodeUrlParams(dParams),
+ WuiDispatcherBase.ksParamItemsPerPage)
+
+ acItemsPerPage = [16, 32, 64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];
+ for cItemsPerPage in acItemsPerPage:
+ sHtmlItemsPerPageSelector += ' <option value="%d" %s>%d per page</option>\n' \
+ % (cItemsPerPage,
+ 'selected="selected"' if cItemsPerPage == cCurItemsPerPage else '',
+ cItemsPerPage)
+ sHtmlItemsPerPageSelector += ' </select>\n' \
+ '</form>\n';
+
+ return sHtmlItemsPerPageSelector
+
+
+ def _generateNavigation(self, sWhere):
+ """
+ Return HTML for navigation.
+ """
+
+ #
+ # ASSUMES the dispatcher/controller code fetches one entry more than
+ # needed to fill the page to indicate further records.
+ #
+ sNavigation = '<div class="tmlistnav-%s">\n' % sWhere;
+ sNavigation += ' <table class="tmlistnavtab">\n' \
+ ' <tr>\n';
+ dParams = self._oDisp.getParameters();
+ dParams[WuiDispatcherBase.ksParamItemsPerPage] = self._cItemsPerPage;
+ dParams[WuiDispatcherBase.ksParamPageNo] = self._iPage;
+ if self._tsEffectiveDate is not None:
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = self._tsEffectiveDate;
+
+ # Prev
+ if self._iPage > 0:
+ dParams[WuiDispatcherBase.ksParamPageNo] = self._iPage - 1;
+ sNavigation += ' <td align="left"><a href="?%s">Previous</a></td>\n' % (webutils.encodeUrlParams(dParams),);
+ else:
+ sNavigation += ' <td></td>\n';
+
+ # Time scale.
+ if self._fTimeNavigation:
+ sNavigation += '<td align="center" class="tmtimenav">';
+ sNavigation += self._generateTimeNavigation(sWhere);
+ sNavigation += '</td>';
+
+ # page count and next.
+ sNavigation += '<td align="right" class="tmnextanditemsperpage">\n';
+
+ if len(self._aoEntries) > self._cItemsPerPage:
+ dParams[WuiDispatcherBase.ksParamPageNo] = self._iPage + 1;
+ sNavigation += ' <a href="?%s">Next</a>\n' % (webutils.encodeUrlParams(dParams),);
+ sNavigation += self.generateItemPerPageSelector(sWhere, dParams, self._cItemsPerPage);
+ sNavigation += '</td>\n';
+ sNavigation += ' </tr>\n' \
+ ' </table>\n' \
+ '</div>\n';
+ return sNavigation;
+
+ def _checkSortingByColumnAscending(self, aiColumns):
+ """
+ Checks if we're sorting by this column.
+
+ Returns 0 if not sorting by this, negative if descending, positive if ascending. The
+ value indicates the priority (nearer to 0 is higher).
+ """
+ if len(aiColumns) <= len(self._aiSelectedSortColumns):
+ aiColumns = list(aiColumns);
+ aiNegColumns = list([-i for i in aiColumns]); # pylint: disable=consider-using-generator
+ i = 0;
+ while i + len(aiColumns) <= len(self._aiSelectedSortColumns):
+ aiSub = list(self._aiSelectedSortColumns[i : i + len(aiColumns)]);
+ if aiSub == aiColumns:
+ return 1 + i;
+ if aiSub == aiNegColumns:
+ return -1 - i;
+ i += 1;
+ return 0;
+
+ def _generateTableHeaders(self):
+ """
+ Generate table headers.
+ Returns raw html string.
+ Overridable.
+ """
+
+ sHtml = ' <thead class="tmheader"><tr>';
+ for iHeader, oHeader in enumerate(self._asColumnHeaders):
+ if isinstance(oHeader, WuiHtmlBase):
+ sHtml += '<th>' + oHeader.toHtml() + '</th>';
+ elif iHeader < len(self._aaiColumnSorting) and self._aaiColumnSorting[iHeader] is not None:
+ sHtml += '<th>'
+ iSorting = self._checkSortingByColumnAscending(self._aaiColumnSorting[iHeader]);
+ if iSorting > 0:
+ sDirection = '&nbsp;&#x25b4;' if iSorting == 1 else '<small>&nbsp;&#x25b5;</small>';
+ sSortParams = ','.join([str(-i) for i in self._aaiColumnSorting[iHeader]]);
+ else:
+ sDirection = '';
+ if iSorting < 0:
+ sDirection = '&nbsp;&#x25be;' if iSorting == -1 else '<small>&nbsp;&#x25bf;</small>'
+ sSortParams = ','.join([str(i) for i in self._aaiColumnSorting[iHeader]]);
+ sHtml += '<a href="javascript:ahrefActionSortByColumns(\'%s\',[%s]);">' \
+ % (WuiDispatcherBase.ksParamSortColumns, sSortParams);
+ sHtml += webutils.escapeElem(oHeader) + '</a>' + sDirection + '</th>';
+ else:
+ sHtml += '<th>' + webutils.escapeElem(oHeader) + '</th>';
+ sHtml += '</tr><thead>\n';
+ return sHtml
+
+ def _generateTable(self):
+ """
+ show worker that just generates the table.
+ """
+
+ #
+ # Create a table.
+ # If no colum headers are provided, fall back on database field
+ # names, ASSUMING that the entries are ModelDataBase children.
+ # Note! the cellspacing is for IE8.
+ #
+ sPageBody = '<table class="tmtable" id="' + self._sId + '" cellspacing="0">\n';
+
+ if not self._asColumnHeaders:
+ self._asColumnHeaders = self._aoEntries[0].getDataAttributes();
+
+ sPageBody += self._generateTableHeaders();
+
+ #
+ # Format the body and close the table.
+ #
+ sPageBody += ' <tbody>\n';
+ for iEntry in range(min(len(self._aoEntries), self._cItemsPerPage)):
+ sPageBody += self._formatListEntryHtml(iEntry);
+ sPageBody += ' </tbody>\n' \
+ '</table>\n';
+ return sPageBody;
+
+ def _composeTitle(self):
+ """Composes the title string (return value)."""
+ sTitle = self._sTitle;
+ if self._iPage != 0:
+ sTitle += ' (page ' + unicode(self._iPage + 1) + ')'
+ if self._tsEffectiveDate is not None:
+ sTitle += ' as per ' + unicode(self.formatTsShort(self._tsEffectiveDate));
+ return sTitle;
+
+
+ def show(self, fShowNavigation = True):
+ """
+ Displays the list.
+ Returns (Title, HTML) on success, raises exception on error.
+ """
+
+ sPageBody = ''
+ if fShowNavigation:
+ sPageBody += self._generateNavigation('top');
+
+ if self._aoEntries:
+ sPageBody += self._generateTable();
+ if fShowNavigation:
+ sPageBody += self._generateNavigation('bottom');
+ else:
+ sPageBody += '<p>No entries.</p>'
+
+ return (self._composeTitle(), sPageBody);
+
+
+class WuiListContentWithActionBase(WuiListContentBase):
+ """
+ Base for the list content with action classes.
+ """
+
+ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffectiveDate, sTitle, # pylint: disable=too-many-arguments
+ sId = None, fnDPrint = None, oDisp = None, aiSelectedSortColumns = None):
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffectiveDate, sTitle, sId = sId,
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+ self._aoActions = None; # List of [ oValue, sText, sHover ] provided by the child class.
+ self._sAction = None; # Set by the child class.
+ self._sCheckboxName = None; # Set by the child class.
+ self._asColumnHeaders = [ WuiRawHtml('<input type="checkbox" onClick="toggle%s(this)">'
+ % ('' if sId is None else sId)), ];
+ self._asColumnAttribs = [ 'align="center"', ];
+ self._aaiColumnSorting = [ None, ];
+
+ def _getCheckBoxColumn(self, iEntry, sValue):
+ """
+ Used by _formatListEntry implementations, returns a WuiRawHtmlBase object.
+ """
+ _ = iEntry;
+ return WuiRawHtml('<input type="checkbox" name="%s" value="%s">'
+ % (webutils.escapeAttr(self._sCheckboxName), webutils.escapeAttr(unicode(sValue))));
+
+ def show(self, fShowNavigation=True):
+ """
+ Displays the list.
+ Returns (Title, HTML) on success, raises exception on error.
+ """
+ assert self._aoActions is not None;
+ assert self._sAction is not None;
+
+ sPageBody = '<script language="JavaScript">\n' \
+ 'function toggle%s(oSource) {\n' \
+ ' aoCheckboxes = document.getElementsByName(\'%s\');\n' \
+ ' for(var i in aoCheckboxes)\n' \
+ ' aoCheckboxes[i].checked = oSource.checked;\n' \
+ '}\n' \
+ '</script>\n' \
+ % ('' if self._sId is None else self._sId, self._sCheckboxName,);
+ if fShowNavigation:
+ sPageBody += self._generateNavigation('top');
+ if self._aoEntries:
+
+ sPageBody += '<form action="?%s" method="post" class="tmlistactionform">\n' \
+ % (webutils.encodeUrlParams({WuiDispatcherBase.ksParamAction: self._sAction,}),);
+ sPageBody += self._generateTable();
+
+ sPageBody += ' <label>Actions</label>\n' \
+ ' <select name="%s" id="%s-action-combo" class="tmlistactionform-combo">\n' \
+ % (webutils.escapeAttr(WuiDispatcherBase.ksParamListAction), webutils.escapeAttr(self._sId),);
+ for oValue, sText, _ in self._aoActions:
+ sPageBody += ' <option value="%s">%s</option>\n' \
+ % (webutils.escapeAttr(unicode(oValue)), webutils.escapeElem(sText), );
+ sPageBody += ' </select>\n';
+ sPageBody += ' <input type="submit"></input>\n';
+ sPageBody += '</form>\n';
+ if fShowNavigation:
+ sPageBody += self._generateNavigation('bottom');
+ else:
+ sPageBody += '<p>No entries.</p>'
+
+ return (self._composeTitle(), sPageBody);
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuigraphwiz.py b/src/VBox/ValidationKit/testmanager/webui/wuigraphwiz.py
new file mode 100755
index 00000000..040b3217
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuigraphwiz.py
@@ -0,0 +1,660 @@
+# -*- coding: utf-8 -*-
+# $Id: wuigraphwiz.py $
+
+"""
+Test Manager WUI - Graph Wizard
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Python imports.
+import functools;
+
+# Validation Kit imports.
+from testmanager.webui.wuimain import WuiMain;
+from testmanager.webui.wuihlpgraph import WuiHlpLineGraphErrorbarY, WuiHlpGraphDataTableEx;
+from testmanager.webui.wuireport import WuiReportBase;
+
+from common import utils, webutils;
+from common import constants;
+
+
+class WuiGraphWiz(WuiReportBase):
+ """Construct a graph for analyzing test results (values) across builds and testboxes."""
+
+ ## @name Series name parts.
+ ## @{
+ kfSeriesName_TestBox = 1;
+ kfSeriesName_Product = 2;
+ kfSeriesName_Branch = 4;
+ kfSeriesName_BuildType = 8;
+ kfSeriesName_OsArchs = 16;
+ kfSeriesName_TestCase = 32;
+ kfSeriesName_TestCaseArgs = 64;
+ kfSeriesName_All = 127;
+ ## @}
+
+
+ def __init__(self, oModel, dParams, fSubReport = False, fnDPrint = None, oDisp = None):
+ WuiReportBase.__init__(self, oModel, dParams, fSubReport = fSubReport, fnDPrint = fnDPrint, oDisp = oDisp);
+
+ # Select graph implementation.
+ if dParams[WuiMain.ksParamGraphWizImpl] == 'charts':
+ from testmanager.webui.wuihlpgraphgooglechart import WuiHlpLineGraphErrorbarY as MyGraph;
+ self.oGraphClass = MyGraph;
+ elif dParams[WuiMain.ksParamGraphWizImpl] == 'matplotlib':
+ from testmanager.webui.wuihlpgraphmatplotlib import WuiHlpLineGraphErrorbarY as MyGraph;
+ self.oGraphClass = MyGraph;
+ else:
+ self.oGraphClass = WuiHlpLineGraphErrorbarY;
+
+
+ #
+ def _figureSeriesNameBits(self, aoSeries):
+ """ Figures out the method (bitmask) to use when naming series. """
+ if len(aoSeries) <= 1:
+ return WuiGraphWiz.kfSeriesName_TestBox;
+
+ # Start with all and drop unnecessary specs one-by-one.
+ fRet = WuiGraphWiz.kfSeriesName_All;
+
+ if [oSrs.idTestBox for oSrs in aoSeries].count(aoSeries[0].idTestBox) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_TestBox;
+
+ if [oSrs.idBuildCategory for oSrs in aoSeries].count(aoSeries[0].idBuildCategory) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_Product;
+ fRet &= ~WuiGraphWiz.kfSeriesName_Branch;
+ fRet &= ~WuiGraphWiz.kfSeriesName_BuildType;
+ fRet &= ~WuiGraphWiz.kfSeriesName_OsArchs;
+ else:
+ if [oSrs.oBuildCategory.sProduct for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sProduct) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_Product;
+ if [oSrs.oBuildCategory.sBranch for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sBranch) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_Branch;
+ if [oSrs.oBuildCategory.sType for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sType) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_BuildType;
+
+ # Complicated.
+ fRet &= ~WuiGraphWiz.kfSeriesName_OsArchs;
+ daTestBoxes = {};
+ for oSeries in aoSeries:
+ if oSeries.idTestBox in daTestBoxes:
+ daTestBoxes[oSeries.idTestBox].append(oSeries);
+ else:
+ daTestBoxes[oSeries.idTestBox] = [oSeries,];
+ for aoSeriesPerTestBox in daTestBoxes.values():
+ if len(aoSeriesPerTestBox) >= 0:
+ asOsArches = aoSeriesPerTestBox[0].oBuildCategory.asOsArches;
+ for i in range(1, len(aoSeriesPerTestBox)):
+ if aoSeriesPerTestBox[i].oBuildCategory.asOsArches != asOsArches:
+ fRet |= WuiGraphWiz.kfSeriesName_OsArchs;
+ break;
+
+ if aoSeries[0].oTestCaseArgs is None:
+ fRet &= ~WuiGraphWiz.kfSeriesName_TestCaseArgs;
+ if [oSrs.idTestCase for oSrs in aoSeries].count(aoSeries[0].idTestCase) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_TestCase;
+ else:
+ fRet &= ~WuiGraphWiz.kfSeriesName_TestCase;
+ if [oSrs.idTestCaseArgs for oSrs in aoSeries].count(aoSeries[0].idTestCaseArgs) == len(aoSeries):
+ fRet &= ~WuiGraphWiz.kfSeriesName_TestCaseArgs;
+
+ return fRet;
+
+ def _getSeriesNameFromBits(self, oSeries, fBits):
+ """ Creates a series name from bits (kfSeriesName_xxx). """
+ assert fBits != 0;
+ sName = '';
+
+ if fBits & WuiGraphWiz.kfSeriesName_Product:
+ if sName: sName += ' / ';
+ sName += oSeries.oBuildCategory.sProduct;
+
+ if fBits & WuiGraphWiz.kfSeriesName_Branch:
+ if sName: sName += ' / ';
+ sName += oSeries.oBuildCategory.sBranch;
+
+ if fBits & WuiGraphWiz.kfSeriesName_BuildType:
+ if sName: sName += ' / ';
+ sName += oSeries.oBuildCategory.sType;
+
+ if fBits & WuiGraphWiz.kfSeriesName_OsArchs:
+ if sName: sName += ' / ';
+ sName += ' & '.join(oSeries.oBuildCategory.asOsArches);
+
+ if fBits & WuiGraphWiz.kfSeriesName_TestCaseArgs:
+ if sName: sName += ' / ';
+ if oSeries.idTestCaseArgs is not None:
+ sName += oSeries.oTestCase.sName + ':#' + str(oSeries.idTestCaseArgs);
+ else:
+ sName += oSeries.oTestCase.sName;
+ elif fBits & WuiGraphWiz.kfSeriesName_TestCase:
+ if sName: sName += ' / ';
+ sName += oSeries.oTestCase.sName;
+
+ if fBits & WuiGraphWiz.kfSeriesName_TestBox:
+ if sName: sName += ' / ';
+ sName += oSeries.oTestBox.sName;
+
+ return sName;
+
+ def _calcGraphName(self, oSeries, fSeriesName, sSampleName):
+ """ Constructs a name for the graph. """
+ fGraphName = ~fSeriesName & ( WuiGraphWiz.kfSeriesName_TestBox
+ | WuiGraphWiz.kfSeriesName_Product
+ | WuiGraphWiz.kfSeriesName_Branch
+ | WuiGraphWiz.kfSeriesName_BuildType
+ );
+ sName = self._getSeriesNameFromBits(oSeries, fGraphName);
+ if sName: sName += ' - ';
+ sName += sSampleName;
+ return sName;
+
+ def _calcSampleName(self, oCollection):
+ """ Constructs a name for a sample source (collection). """
+ if oCollection.sValue is not None:
+ asSampleName = [oCollection.sValue, 'in',];
+ elif oCollection.sType == self._oModel.ksTypeElapsed:
+ asSampleName = ['Elapsed time', 'for', ];
+ elif oCollection.sType == self._oModel.ksTypeResult:
+ asSampleName = ['Error count', 'for',];
+ else:
+ return 'Invalid collection type: "%s"' % (oCollection.sType,);
+
+ sTestName = ', '.join(oCollection.asTests if oCollection.asTests[0] else oCollection.asTests[1:]);
+ if sTestName == '':
+ # Use the testcase name if there is only one for all series.
+ if not oCollection.aoSeries:
+ return asSampleName[0];
+ if len(oCollection.aoSeries) > 1:
+ idTestCase = oCollection.aoSeries[0].idTestCase;
+ for oSeries in oCollection.aoSeries:
+ if oSeries.idTestCase != idTestCase:
+ return asSampleName[0];
+ sTestName = oCollection.aoSeries[0].oTestCase.sName;
+ return ' '.join(asSampleName) + ' ' + sTestName;
+
+
+ def _splitSeries(self, aoSeries):
+ """
+ Splits the data series (ReportGraphModel.DataSeries) into one or more graphs.
+
+ Returns an array of data series arrays.
+ """
+ # Must be at least two series for something to be splittable.
+ if len(aoSeries) <= 1:
+ if not aoSeries:
+ return [];
+ return [aoSeries,];
+
+ # Split on unit.
+ dUnitSeries = {};
+ for oSeries in aoSeries:
+ if oSeries.iUnit not in dUnitSeries:
+ dUnitSeries[oSeries.iUnit] = [];
+ dUnitSeries[oSeries.iUnit].append(oSeries);
+
+ # Sort the per-unit series since the build category was only sorted by ID.
+ for iUnit in dUnitSeries:
+ def mycmp(oSelf, oOther):
+ """ __cmp__ like function. """
+ iCmp = utils.stricmp(oSelf.oBuildCategory.sProduct, oOther.oBuildCategory.sProduct);
+ if iCmp != 0:
+ return iCmp;
+ iCmp = utils.stricmp(oSelf.oBuildCategory.sBranch, oOther.oBuildCategory.sBranch);
+ if iCmp != 0:
+ return iCmp;
+ iCmp = utils.stricmp(oSelf.oBuildCategory.sType, oOther.oBuildCategory.sType);
+ if iCmp != 0:
+ return iCmp;
+ iCmp = utils.stricmp(oSelf.oTestBox.sName, oOther.oTestBox.sName);
+ if iCmp != 0:
+ return iCmp;
+ return 0;
+ dUnitSeries[iUnit] = sorted(dUnitSeries[iUnit], key = functools.cmp_to_key(mycmp));
+
+ # Split the per-unit series up if necessary.
+ cMaxPerGraph = self._dParams[WuiMain.ksParamGraphWizMaxPerGraph];
+ aaoRet = [];
+ for aoUnitSeries in dUnitSeries.values():
+ while len(aoUnitSeries) > cMaxPerGraph:
+ aaoRet.append(aoUnitSeries[:cMaxPerGraph]);
+ aoUnitSeries = aoUnitSeries[cMaxPerGraph:];
+ if aoUnitSeries:
+ aaoRet.append(aoUnitSeries);
+
+ return aaoRet;
+
+ def _configureGraph(self, oGraph):
+ """
+ Configures oGraph according to user parameters and other config settings.
+
+ Returns oGraph.
+ """
+ oGraph.setWidth(self._dParams[WuiMain.ksParamGraphWizWidth])
+ oGraph.setHeight(self._dParams[WuiMain.ksParamGraphWizHeight])
+ oGraph.setDpi(self._dParams[WuiMain.ksParamGraphWizDpi])
+ oGraph.setErrorBarY(self._dParams[WuiMain.ksParamGraphWizErrorBarY]);
+ oGraph.setFontSize(self._dParams[WuiMain.ksParamGraphWizFontSize]);
+ if hasattr(oGraph, 'setXkcdStyle'):
+ oGraph.setXkcdStyle(self._dParams[WuiMain.ksParamGraphWizXkcdStyle]);
+
+ return oGraph;
+
+ def _generateInteractiveForm(self):
+ """
+ Generates the HTML for the interactive form.
+ Returns (sTopOfForm, sEndOfForm)
+ """
+
+ #
+ # The top of the form.
+ #
+ sTop = '<form action="#" method="get" id="graphwiz-form">\n' \
+ ' <input type="hidden" name="%s" value="%s"/>\n' \
+ ' <input type="hidden" name="%s" value="%u"/>\n' \
+ % ( WuiMain.ksParamAction, WuiMain.ksActionGraphWiz,
+ WuiMain.ksParamGraphWizSrcTestSetId, self._dParams[WuiMain.ksParamGraphWizSrcTestSetId],
+ );
+
+ sTop += ' <div id="graphwiz-nav">\n';
+ sTop += ' <script type="text/javascript">\n' \
+ ' window.onresize = function(){ return graphwizOnResizeRecalcWidth("graphwiz-nav", "%s"); }\n' \
+ ' window.onload = function(){ return graphwizOnLoadRememberWidth("graphwiz-nav"); }\n' \
+ ' </script>\n' \
+ % ( WuiMain.ksParamGraphWizWidth, );
+
+ #
+ # Top: First row.
+ #
+ sTop += ' <div id="graphwiz-top-1">\n';
+
+ # time.
+ sNow = self._dParams[WuiMain.ksParamEffectiveDate];
+ if sNow is None: sNow = '';
+ sTop += ' <div id="graphwiz-time">\n';
+ sTop += ' <label for="%s">Starting:</label>\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-time-input"/>\n' \
+ % ( WuiMain.ksParamEffectiveDate,
+ WuiMain.ksParamEffectiveDate, WuiMain.ksParamEffectiveDate, sNow, );
+
+ sTop += ' <input type="hidden" name="%s" value="%u"/>\n' % ( WuiMain.ksParamReportPeriods, 1, );
+ sTop += ' <label for="%s"> Going back:\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-period-input"/>\n' \
+ % ( WuiMain.ksParamReportPeriodInHours,
+ WuiMain.ksParamReportPeriodInHours, WuiMain.ksParamReportPeriodInHours,
+ utils.formatIntervalHours(self._dParams[WuiMain.ksParamReportPeriodInHours]) );
+ sTop += ' </div>\n';
+
+ # Graph options top row.
+ sTop += ' <div id="graphwiz-top-options-1">\n';
+
+ # graph type.
+ sTop += ' <label for="%s">Graph:</label>\n' \
+ ' <select name="%s" id="%s">\n' \
+ % ( WuiMain.ksParamGraphWizImpl, WuiMain.ksParamGraphWizImpl, WuiMain.ksParamGraphWizImpl, );
+ for (sImpl, sDesc) in WuiMain.kaasGraphWizImplCombo:
+ sTop += ' <option value="%s"%s>%s</option>\n' \
+ % (sImpl, ' selected' if sImpl == self._dParams[WuiMain.ksParamGraphWizImpl] else '', sDesc);
+ sTop += ' </select>\n';
+
+ # graph size.
+ sTop += ' <label for="%s">Graph size:</label>\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-pixel-input"> x\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-pixel-input">\n' \
+ ' <label for="%s">Dpi:</label>'\
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-dpi-input">\n' \
+ ' <button type="button" onclick="%s">Defaults</button>\n' \
+ % ( WuiMain.ksParamGraphWizWidth,
+ WuiMain.ksParamGraphWizWidth, WuiMain.ksParamGraphWizWidth, self._dParams[WuiMain.ksParamGraphWizWidth],
+ WuiMain.ksParamGraphWizHeight, WuiMain.ksParamGraphWizHeight, self._dParams[WuiMain.ksParamGraphWizHeight],
+ WuiMain.ksParamGraphWizDpi,
+ WuiMain.ksParamGraphWizDpi, WuiMain.ksParamGraphWizDpi, self._dParams[WuiMain.ksParamGraphWizDpi],
+ webutils.escapeAttr('return graphwizSetDefaultSizeValues("graphwiz-nav", "%s", "%s", "%s");'
+ % ( WuiMain.ksParamGraphWizWidth, WuiMain.ksParamGraphWizHeight,
+ WuiMain.ksParamGraphWizDpi )),
+ );
+
+ sTop += ' </div>\n'; # (options row 1)
+
+ sTop += ' </div>\n'; # (end of row 1)
+
+ #
+ # Top: Second row.
+ #
+ sTop += ' <div id="graphwiz-top-2">\n';
+
+ # Submit
+ sFormButton = '<button type="submit">Refresh</button>\n';
+ sTop += ' <div id="graphwiz-top-submit">' + sFormButton + '</div>\n';
+
+
+ # Options.
+ sTop += ' <div id="graphwiz-top-options-2">\n';
+
+ sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s/>\n' \
+ ' <label for="%s">Tabular data</label>\n' \
+ % ( WuiMain.ksParamGraphWizTabular, WuiMain.ksParamGraphWizTabular,
+ ' checked' if self._dParams[WuiMain.ksParamGraphWizTabular] else '',
+ WuiMain.ksParamGraphWizTabular);
+
+ if hasattr(self.oGraphClass, 'setXkcdStyle'):
+ sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s/>\n' \
+ ' <label for="%s">xkcd-style</label>\n' \
+ % ( WuiMain.ksParamGraphWizXkcdStyle, WuiMain.ksParamGraphWizXkcdStyle,
+ ' checked' if self._dParams[WuiMain.ksParamGraphWizXkcdStyle] else '',
+ WuiMain.ksParamGraphWizXkcdStyle);
+ elif self._dParams[WuiMain.ksParamGraphWizXkcdStyle]:
+ sTop += ' <input type="hidden" name="%s" id="%s" value="1"/>\n' \
+ % ( WuiMain.ksParamGraphWizXkcdStyle, WuiMain.ksParamGraphWizXkcdStyle, );
+
+ if not hasattr(self.oGraphClass, 'kfNoErrorBarsSupport'):
+ sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s title="%s"/>\n' \
+ ' <label for="%s">Error bars,</label>\n' \
+ ' <label for="%s">max: </label>\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-maxerrorbar-input" title="%s"/>\n' \
+ % ( WuiMain.ksParamGraphWizErrorBarY, WuiMain.ksParamGraphWizErrorBarY,
+ ' checked' if self._dParams[WuiMain.ksParamGraphWizErrorBarY] else '',
+ 'Error bars shows some of the max and min results on the Y-axis.',
+ WuiMain.ksParamGraphWizErrorBarY,
+ WuiMain.ksParamGraphWizMaxErrorBarY,
+ WuiMain.ksParamGraphWizMaxErrorBarY, WuiMain.ksParamGraphWizMaxErrorBarY,
+ self._dParams[WuiMain.ksParamGraphWizMaxErrorBarY],
+ 'Maximum number of Y-axis error bar per graph. (Too many makes it unreadable.)'
+ );
+ else:
+ if self._dParams[WuiMain.ksParamGraphWizErrorBarY]:
+ sTop += '<input type="hidden" name="%s" id="%s" value="1">\n' \
+ % ( WuiMain.ksParamGraphWizErrorBarY, WuiMain.ksParamGraphWizErrorBarY, );
+ sTop += '<input type="hidden" name="%s" id="%s" value="%u">\n' \
+ % ( WuiMain.ksParamGraphWizMaxErrorBarY, WuiMain.ksParamGraphWizMaxErrorBarY,
+ self._dParams[WuiMain.ksParamGraphWizMaxErrorBarY], );
+
+ sTop += ' <label for="%s">Font size: </label>\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-fontsize-input"/>\n' \
+ % ( WuiMain.ksParamGraphWizFontSize,
+ WuiMain.ksParamGraphWizFontSize, WuiMain.ksParamGraphWizFontSize,
+ self._dParams[WuiMain.ksParamGraphWizFontSize], );
+
+ sTop += ' <label for="%s">Data series: </label>\n' \
+ ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-maxpergraph-input" title="%s"/>\n' \
+ % ( WuiMain.ksParamGraphWizMaxPerGraph,
+ WuiMain.ksParamGraphWizMaxPerGraph, WuiMain.ksParamGraphWizMaxPerGraph,
+ self._dParams[WuiMain.ksParamGraphWizMaxPerGraph],
+ 'Max data series per graph.' );
+
+ sTop += ' </div>\n'; # (options row 2)
+
+ sTop += ' </div>\n'; # (end of row 2)
+
+ sTop += ' </div>\n'; # end of top.
+
+ #
+ # The end of the page selection.
+ #
+ sEnd = ' <div id="graphwiz-end-selection">\n';
+
+ #
+ # Testbox selection
+ #
+ aidTestBoxes = list(self._dParams[WuiMain.ksParamGraphWizTestBoxIds]);
+ sEnd += ' <div id="graphwiz-testboxes" class="graphwiz-end-selection-group">\n' \
+ ' <h3>TestBox Selection:</h3>\n' \
+ ' <ol class="tmgraph-testboxes">\n';
+
+ # Get a list of eligible testboxes from the DB.
+ for oTestBox in self._oModel.getEligibleTestBoxes():
+ try: aidTestBoxes.remove(oTestBox.idTestBox);
+ except: sChecked = '';
+ else: sChecked = ' checked';
+ sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-tb-%u"%s/>' \
+ '<label for="gw-tb-%u">%s</label></li>\n' \
+ % ( WuiMain.ksParamGraphWizTestBoxIds, oTestBox.idTestBox, oTestBox.idTestBox, sChecked,
+ oTestBox.idTestBox, oTestBox.sName);
+
+ # List testboxes that have been checked in a different period or something.
+ for idTestBox in aidTestBoxes:
+ oTestBox = self._oModel.oCache.getTestBox(idTestBox);
+ sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-tb-%u" checked/>' \
+ '<label for="gw-tb-%u">%s</label></li>\n' \
+ % ( WuiMain.ksParamGraphWizTestBoxIds, oTestBox.idTestBox, oTestBox.idTestBox,
+ oTestBox.idTestBox, oTestBox.sName);
+
+ sEnd += ' </ol>\n' \
+ ' </div>\n';
+
+ #
+ # Build category selection.
+ #
+ aidBuildCategories = list(self._dParams[WuiMain.ksParamGraphWizBuildCatIds]);
+ sEnd += ' <div id="graphwiz-buildcategories" class="graphwiz-end-selection-group">\n' \
+ ' <h3>Build Category Selection:</h3>\n' \
+ ' <ol class="tmgraph-buildcategories">\n';
+ for oBuildCat in self._oModel.getEligibleBuildCategories():
+ try: aidBuildCategories.remove(oBuildCat.idBuildCategory);
+ except: sChecked = '';
+ else: sChecked = ' checked';
+ sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-bc-%u" %s/>' \
+ '<label for="gw-bc-%u">%s / %s / %s / %s</label></li>\n' \
+ % ( WuiMain.ksParamGraphWizBuildCatIds, oBuildCat.idBuildCategory, oBuildCat.idBuildCategory, sChecked,
+ oBuildCat.idBuildCategory,
+ oBuildCat.sProduct, oBuildCat.sBranch, oBuildCat.sType, ' & '.join(oBuildCat.asOsArches) );
+ assert not aidBuildCategories; # SQL should return all currently selected.
+
+ sEnd += ' </ol>\n' \
+ ' </div>\n';
+
+ #
+ # Testcase variations.
+ #
+ sEnd += ' <div id="graphwiz-testcase-variations" class="graphwiz-end-selection-group">\n' \
+ ' <h3>Miscellaneous:</h3>\n' \
+ ' <ol>';
+
+ sEnd += ' <li>\n' \
+ ' <input type="checkbox" id="%s" name="%s" value="1"%s/>\n' \
+ ' <label for="%s">Separate by testcase variation.</label>\n' \
+ ' </li>\n' \
+ % ( WuiMain.ksParamGraphWizSepTestVars, WuiMain.ksParamGraphWizSepTestVars,
+ ' checked' if self._dParams[WuiMain.ksParamGraphWizSepTestVars] else '',
+ WuiMain.ksParamGraphWizSepTestVars );
+
+
+ sEnd += ' <li>\n' \
+ ' <lable for="%s">Test case ID:</label>\n' \
+ ' <input type="text" id="%s" name="%s" value="%s" readonly/>\n' \
+ ' </li>\n' \
+ % ( WuiMain.ksParamGraphWizTestCaseIds,
+ WuiMain.ksParamGraphWizTestCaseIds, WuiMain.ksParamGraphWizTestCaseIds,
+ ','.join([str(i) for i in self._dParams[WuiMain.ksParamGraphWizTestCaseIds]]), );
+
+ sEnd += ' </ol>\n' \
+ ' </div>\n';
+
+ #sEnd += ' <h3>&nbsp;</h3>\n';
+
+ #
+ # Finish up the form.
+ #
+ sEnd += ' <div id="graphwiz-end-submit"><p>' + sFormButton + '</p></div>\n';
+ sEnd += ' </div>\n' \
+ '</form>\n';
+
+ return (sTop, sEnd);
+
+ def generateReportBody(self):
+ fInteractive = not self._fSubReport;
+
+ # Quick mockup.
+ self._sTitle = 'Graph Wizzard';
+
+ sHtml = '';
+ sHtml += '<h2>Incomplete code - no complaints yet, thank you!!</h2>\n';
+
+ #
+ # Create a form for altering the data we're working with.
+ #
+ if fInteractive:
+ (sTopOfForm, sEndOfForm) = self._generateInteractiveForm();
+ sHtml += sTopOfForm;
+ del sTopOfForm;
+
+ #
+ # Emit the graphs. At least one per sample source.
+ #
+ sHtml += ' <div id="graphwiz-graphs">\n';
+ iGraph = 0;
+ aoCollections = self._oModel.fetchGraphData();
+ for iCollection, oCollection in enumerate(aoCollections):
+ # Name the graph and add a checkbox for removing it.
+ sSampleName = self._calcSampleName(oCollection);
+ sHtml += ' <div class="graphwiz-collection" id="graphwiz-source-%u">\n' % (iCollection,);
+ if fInteractive:
+ sHtml += ' <div class="graphwiz-src-select">\n' \
+ ' <input type="checkbox" name="%s" id="%s" value="%s:%s%s" checked class="graphwiz-src-input">\n' \
+ ' <label for="%s">%s</label>\n' \
+ ' </div>\n' \
+ % ( WuiMain.ksParamReportSubjectIds, WuiMain.ksParamReportSubjectIds, oCollection.sType,
+ ':'.join([str(idStr) for idStr in oCollection.aidStrTests]),
+ ':%u' % oCollection.idStrValue if oCollection.idStrValue else '',
+ WuiMain.ksParamReportSubjectIds, sSampleName );
+
+ if oCollection.aoSeries:
+ #
+ # Split the series into sub-graphs as needed and produce SVGs.
+ #
+ aaoSeries = self._splitSeries(oCollection.aoSeries);
+ for aoSeries in aaoSeries:
+ # Gather the data for this graph. (Most big stuff is passed by
+ # reference, so there shouldn't be any large memory penalty for
+ # repacking the data here.)
+ sYUnit = None;
+ if aoSeries[0].iUnit < len(constants.valueunit.g_asNames) and aoSeries[0].iUnit > 0:
+ sYUnit = constants.valueunit.g_asNames[aoSeries[0].iUnit];
+ oData = WuiHlpGraphDataTableEx(sXUnit = 'Build revision', sYUnit = sYUnit);
+
+ fSeriesName = self._figureSeriesNameBits(aoSeries);
+ for oSeries in aoSeries:
+ sSeriesName = self._getSeriesNameFromBits(oSeries, fSeriesName);
+ asHtmlTooltips = None;
+ if len(oSeries.aoRevInfo) == len(oSeries.aiRevisions):
+ asHtmlTooltips = [];
+ for i, oRevInfo in enumerate(oSeries.aoRevInfo):
+ sPlusMinus = '';
+ if oSeries.acSamples[i] > 1:
+ sPlusMinus = ' (+%s/-%s; %u samples)' \
+ % ( utils.formatNumber(oSeries.aiErrorBarAbove[i]),
+ utils.formatNumber(oSeries.aiErrorBarBelow[i]),
+ oSeries.acSamples[i])
+ sTooltip = '<table class=\'graphwiz-tt\'><tr><td>%s:</td><td>%s %s %s</td></tr>'\
+ '<tr><td>Rev:</td><td>r%s</td></tr>' \
+ % ( sSeriesName,
+ utils.formatNumber(oSeries.aiValues[i]),
+ sYUnit, sPlusMinus,
+ oSeries.aiRevisions[i],
+ );
+ if oRevInfo.sAuthor is not None:
+ sMsg = oRevInfo.sMessage[:80].strip();
+ #if sMsg.find('\n') >= 0:
+ # sMsg = sMsg[:sMsg.find('\n')].strip();
+ sTooltip += '<tr><td>Author:</td><td>%s</td></tr>' \
+ '<tr><td>Date:</td><td>%s</td><tr>' \
+ '<tr><td>Message:</td><td>%s%s</td></tr>' \
+ % ( oRevInfo.sAuthor,
+ self.formatTsShort(oRevInfo.tsCreated),
+ sMsg, '...' if len(oRevInfo.sMessage) > len(sMsg) else '');
+ sTooltip += '</table>';
+ asHtmlTooltips.append(sTooltip);
+ oData.addDataSeries(sSeriesName, oSeries.aiRevisions, oSeries.aiValues, asHtmlTooltips,
+ oSeries.aiErrorBarBelow, oSeries.aiErrorBarAbove);
+ # Render the data into a graph.
+ oGraph = self.oGraphClass('tmgraph-%u' % (iGraph,), oData, self._oDisp);
+ self._configureGraph(oGraph);
+
+ oGraph.setTitle(self._calcGraphName(aoSeries[0], fSeriesName, sSampleName));
+ sHtml += ' <div class="graphwiz-graph" id="graphwiz-graph-%u">\n' % (iGraph,);
+ sHtml += oGraph.renderGraph();
+ sHtml += '\n </div>\n';
+ iGraph += 1;
+
+ #
+ # Emit raw tabular data if requested.
+ #
+ if self._dParams[WuiMain.ksParamGraphWizTabular]:
+ sHtml += ' <div class="graphwiz-tab-div" id="graphwiz-tab-%u">\n' \
+ ' <table class="tmtable graphwiz-tab">\n' \
+ % (iCollection, );
+ for aoSeries in aaoSeries:
+ if aoSeries[0].iUnit < len(constants.valueunit.g_asNames) and aoSeries[0].iUnit > 0:
+ sUnit = constants.valueunit.g_asNames[aoSeries[0].iUnit];
+ else:
+ sUnit = str(aoSeries[0].iUnit);
+
+ for iSeries, oSeries in enumerate(aoSeries):
+ sColor = self.oGraphClass.calcSeriesColor(iSeries);
+
+ sHtml += '<thead class="tmheader">\n' \
+ ' <tr class="graphwiz-tab graphwiz-tab-new-series-row">\n' \
+ ' <th colspan="5"><span style="background-color:%s;">&nbsp;&nbsp;</span> %s</th>\n' \
+ ' </tr>\n' \
+ ' <tr class="graphwiz-tab graphwiz-tab-col-hdr-row">\n' \
+ ' <th>Revision</th><th>Value (%s)</th><th>&Delta;max</th><th>&Delta;min</th>' \
+ '<th>Samples</th>\n' \
+ ' </tr>\n' \
+ '</thead>\n' \
+ % ( sColor,
+ self._getSeriesNameFromBits(oSeries, self.kfSeriesName_All & ~self.kfSeriesName_OsArchs),
+ sUnit );
+
+ for i, iRevision in enumerate(oSeries.aiRevisions):
+ sHtml += ' <tr class="%s"><td>r%s</td><td>%s</td><td>+%s</td><td>-%s</td><td>%s</td></tr>\n' \
+ % ( 'tmodd' if i & 1 else 'tmeven',
+ iRevision, oSeries.aiValues[i],
+ oSeries.aiErrorBarAbove[i], oSeries.aiErrorBarBelow[i],
+ oSeries.acSamples[i]);
+ sHtml += ' </table>\n' \
+ ' </div>\n';
+ else:
+ sHtml += '<i>No results.</i>\n';
+ sHtml += ' </div>\n'
+ sHtml += ' </div>\n';
+
+ #
+ # Finish the form.
+ #
+ if fInteractive:
+ sHtml += sEndOfForm;
+
+ return sHtml;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py
new file mode 100755
index 00000000..38015e83
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py
@@ -0,0 +1,1111 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpform.py $
+
+"""
+Test Manager Web-UI - Form Helpers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import copy;
+import sys;
+
+# Validation Kit imports.
+from common import utils;
+from common.webutils import escapeAttr, escapeElem;
+from testmanager import config;
+from testmanager.core.schedgroup import SchedGroupMemberData, SchedGroupDataEx;
+from testmanager.core.testcaseargs import TestCaseArgsData;
+from testmanager.core.testgroup import TestGroupMemberData, TestGroupDataEx;
+from testmanager.core.testbox import TestBoxDataForSchedGroup;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ unicode = str; # pylint: disable=redefined-builtin,invalid-name
+
+
+class WuiHlpForm(object):
+ """
+ Helper for constructing a form.
+ """
+
+ ksItemsList = 'ksItemsList'
+
+ ksOnSubmit_AddReturnToFieldWithCurrentUrl = '+AddReturnToFieldWithCurrentUrl+';
+
+ def __init__(self, sId, sAction, dErrors = None, fReadOnly = False, sOnSubmit = None):
+ self._fFinalized = False;
+ self._fReadOnly = fReadOnly;
+ self._dErrors = dErrors if dErrors is not None else {};
+
+ if sOnSubmit == self.ksOnSubmit_AddReturnToFieldWithCurrentUrl:
+ sOnSubmit = u'return addRedirectToInputFieldWithCurrentUrl(this)';
+ if sOnSubmit is None: sOnSubmit = u'';
+ else: sOnSubmit = u' onsubmit=\"%s\"' % (escapeAttr(sOnSubmit),);
+
+ self._sBody = u'\n' \
+ u'<div id="%s" class="tmform">\n' \
+ u' <form action="%s" method="post"%s>\n' \
+ u' <ul>\n' \
+ % (sId, sAction, sOnSubmit);
+
+ def _add(self, sText):
+ """Internal worker for appending text to the body."""
+ assert not self._fFinalized;
+ if not self._fFinalized:
+ self._sBody += utils.toUnicode(sText, errors='ignore');
+ return True;
+ return False;
+
+ def _escapeErrorText(self, sText):
+ """Escapes error text, preserving some predefined HTML tags."""
+ if sText.find('<br>') >= 0:
+ asParts = sText.split('<br>');
+ for i, _ in enumerate(asParts):
+ asParts[i] = escapeElem(asParts[i].strip());
+ sText = '<br>\n'.join(asParts);
+ else:
+ sText = escapeElem(sText);
+ return sText;
+
+ def _addLabel(self, sName, sLabel, sDivSubClass = 'normal'):
+ """Internal worker for adding a label."""
+ if sName in self._dErrors:
+ sError = self._dErrors[sName];
+ if utils.isString(sError): # List error trick (it's an associative array).
+ return self._add(u' <li>\n'
+ u' <div class="tmform-field"><div class="tmform-field-%s">\n'
+ u' <label for="%s" class="tmform-error-label">%s\n'
+ u' <span class="tmform-error-desc">%s</span>\n'
+ u' </label>\n'
+ % (escapeAttr(sDivSubClass), escapeAttr(sName), escapeElem(sLabel),
+ self._escapeErrorText(sError), ) );
+ return self._add(u' <li>\n'
+ u' <div class="tmform-field"><div class="tmform-field-%s">\n'
+ u' <label for="%s">%s</label>\n'
+ % (escapeAttr(sDivSubClass), escapeAttr(sName), escapeElem(sLabel)) );
+
+
+ def finalize(self):
+ """
+ Finalizes the form and returns the body.
+ """
+ if not self._fFinalized:
+ self._add(u' </ul>\n'
+ u' </form>\n'
+ u'</div>\n'
+ u'<div class="clear"></div>\n' );
+ return self._sBody;
+
+ def addTextHidden(self, sName, sValue, sExtraAttribs = ''):
+ """Adds a hidden text input."""
+ return self._add(u' <div class="tmform-field-hidden">\n'
+ u' <input name="%s" id="%s" type="text" hidden%s value="%s" class="tmform-hidden">\n'
+ u' </div>\n'
+ u' </li>\n'
+ % ( escapeAttr(sName), escapeAttr(sName), sExtraAttribs, escapeElem(str(sValue)) ));
+ #
+ # Non-input stuff.
+ #
+ def addNonText(self, sValue, sLabel, sName = 'non-text', sPostHtml = ''):
+ """Adds a read-only text input."""
+ self._addLabel(sName, sLabel, 'string');
+ if sValue is None: sValue = '';
+ return self._add(u' <p>%s%s</p>\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % (escapeElem(unicode(sValue)), sPostHtml ));
+
+ def addRawHtml(self, sRawHtml, sLabel, sName = 'raw-html'):
+ """Adds a read-only text input."""
+ self._addLabel(sName, sLabel, 'string');
+ self._add(sRawHtml);
+ return self._add(u' </div></div>\n'
+ u' </li>\n');
+
+
+ #
+ # Text input fields.
+ #
+ def addText(self, sName, sValue, sLabel, sSubClass = 'string', sExtraAttribs = '', sPostHtml = ''):
+ """Adds a text input."""
+ if self._fReadOnly:
+ return self.addTextRO(sName, sValue, sLabel, sSubClass, sExtraAttribs);
+ if sSubClass not in ('int', 'long', 'string', 'uuid', 'timestamp', 'wide'): raise Exception(sSubClass);
+ self._addLabel(sName, sLabel, sSubClass);
+ if sValue is None: sValue = '';
+ return self._add(u' <input name="%s" id="%s" type="text"%s value="%s">%s\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % ( escapeAttr(sName), escapeAttr(sName), sExtraAttribs, escapeAttr(unicode(sValue)), sPostHtml ));
+
+ def addTextRO(self, sName, sValue, sLabel, sSubClass = 'string', sExtraAttribs = '', sPostHtml = ''):
+ """Adds a read-only text input."""
+ if sSubClass not in ('int', 'long', 'string', 'uuid', 'timestamp', 'wide'): raise Exception(sSubClass);
+ self._addLabel(sName, sLabel, sSubClass);
+ if sValue is None: sValue = '';
+ return self._add(u' <input name="%s" id="%s" type="text" readonly%s value="%s" class="tmform-input-readonly">'
+ u'%s\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % ( escapeAttr(sName), escapeAttr(sName), sExtraAttribs, escapeAttr(unicode(sValue)), sPostHtml ));
+
+ def addWideText(self, sName, sValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a wide text input."""
+ return self.addText(sName, sValue, sLabel, 'wide', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addWideTextRO(self, sName, sValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a wide read-only text input."""
+ return self.addTextRO(sName, sValue, sLabel, 'wide', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def _adjustMultilineTextAttribs(self, sExtraAttribs, sValue):
+ """ Internal helper for setting good default sizes for textarea based on content."""
+ if sExtraAttribs.find('cols') < 0 and sExtraAttribs.find('width') < 0:
+ sExtraAttribs = 'cols="96%" ' + sExtraAttribs;
+
+ if sExtraAttribs.find('rows') < 0 and sExtraAttribs.find('width') < 0:
+ if sValue is None: sValue = '';
+ else: sValue = sValue.strip();
+
+ cRows = sValue.count('\n') + (not sValue.endswith('\n'));
+ if cRows * 80 < len(sValue):
+ cRows += 2;
+ cRows = max(min(cRows, 16), 2);
+ sExtraAttribs = ('rows="%s" ' % (cRows,)) + sExtraAttribs;
+
+ return sExtraAttribs;
+
+ def addMultilineText(self, sName, sValue, sLabel, sSubClass = 'string', sExtraAttribs = ''):
+ """Adds a multiline text input."""
+ if self._fReadOnly:
+ return self.addMultilineTextRO(sName, sValue, sLabel, sSubClass, sExtraAttribs);
+ if sSubClass not in ('int', 'long', 'string', 'uuid', 'timestamp'): raise Exception(sSubClass)
+ self._addLabel(sName, sLabel, sSubClass)
+ if sValue is None: sValue = '';
+ sNewValue = unicode(sValue) if not isinstance(sValue, list) else '\n'.join(sValue)
+ return self._add(u' <textarea name="%s" id="%s" %s>%s</textarea>\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % ( escapeAttr(sName), escapeAttr(sName), self._adjustMultilineTextAttribs(sExtraAttribs, sNewValue),
+ escapeElem(sNewValue)))
+
+ def addMultilineTextRO(self, sName, sValue, sLabel, sSubClass = 'string', sExtraAttribs = ''):
+ """Adds a multiline read-only text input."""
+ if sSubClass not in ('int', 'long', 'string', 'uuid', 'timestamp'): raise Exception(sSubClass)
+ self._addLabel(sName, sLabel, sSubClass)
+ if sValue is None: sValue = '';
+ sNewValue = unicode(sValue) if not isinstance(sValue, list) else '\n'.join(sValue)
+ return self._add(u' <textarea name="%s" id="%s" readonly %s>%s</textarea>\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % ( escapeAttr(sName), escapeAttr(sName), self._adjustMultilineTextAttribs(sExtraAttribs, sNewValue),
+ escapeElem(sNewValue)))
+
+ def addInt(self, sName, iValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds an integer input."""
+ return self.addText(sName, unicode(iValue), sLabel, 'int', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addIntRO(self, sName, iValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds an integer input."""
+ return self.addTextRO(sName, unicode(iValue), sLabel, 'int', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addLong(self, sName, lValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a long input."""
+ return self.addText(sName, unicode(lValue), sLabel, 'long', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addLongRO(self, sName, lValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a long input."""
+ return self.addTextRO(sName, unicode(lValue), sLabel, 'long', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addUuid(self, sName, uuidValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds an UUID input."""
+ return self.addText(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addUuidRO(self, sName, uuidValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a read-only UUID input."""
+ return self.addTextRO(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs, sPostHtml = sPostHtml);
+
+ def addTimestampRO(self, sName, sTimestamp, sLabel, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a read-only database string timstamp input."""
+ return self.addTextRO(sName, sTimestamp, sLabel, 'timestamp', sExtraAttribs, sPostHtml = sPostHtml);
+
+
+ #
+ # Text areas.
+ #
+
+
+ #
+ # Combo boxes.
+ #
+ def addComboBox(self, sName, sSelected, sLabel, aoOptions, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a combo box."""
+ if self._fReadOnly:
+ return self.addComboBoxRO(sName, sSelected, sLabel, aoOptions, sExtraAttribs, sPostHtml);
+ self._addLabel(sName, sLabel, 'combobox');
+ self._add(' <select name="%s" id="%s" class="tmform-combobox"%s>\n'
+ % (escapeAttr(sName), escapeAttr(sName), sExtraAttribs));
+ sSelected = unicode(sSelected);
+ for iValue, sText, _ in aoOptions:
+ sValue = unicode(iValue);
+ self._add(' <option value="%s"%s>%s</option>\n'
+ % (escapeAttr(sValue), ' selected' if sValue == sSelected else '',
+ escapeElem(sText)));
+ return self._add(u' </select>' + sPostHtml + '\n'
+ u' </div></div>\n'
+ u' </li>\n');
+
+ def addComboBoxRO(self, sName, sSelected, sLabel, aoOptions, sExtraAttribs = '', sPostHtml = ''):
+ """Adds a read-only combo box."""
+ self.addTextHidden(sName, sSelected);
+ self._addLabel(sName, sLabel, 'combobox-readonly');
+ self._add(u' <select name="%s" id="%s" disabled class="tmform-combobox"%s>\n'
+ % (escapeAttr(sName), escapeAttr(sName), sExtraAttribs));
+ sSelected = unicode(sSelected);
+ for iValue, sText, _ in aoOptions:
+ sValue = unicode(iValue);
+ self._add(' <option value="%s"%s>%s</option>\n'
+ % (escapeAttr(sValue), ' selected' if sValue == sSelected else '',
+ escapeElem(sText)));
+ return self._add(u' </select>' + sPostHtml + '\n'
+ u' </div></div>\n'
+ u' </li>\n');
+
+ #
+ # Check boxes.
+ #
+ @staticmethod
+ def _reinterpretBool(fValue):
+ """Reinterprets a value as a boolean type."""
+ if fValue is not type(True):
+ if fValue is None:
+ fValue = False;
+ elif str(fValue) in ('True', 'true', '1'):
+ fValue = True;
+ else:
+ fValue = False;
+ return fValue;
+
+ def addCheckBox(self, sName, fChecked, sLabel, sExtraAttribs = ''):
+ """Adds an check box."""
+ if self._fReadOnly:
+ return self.addCheckBoxRO(sName, fChecked, sLabel, sExtraAttribs);
+ self._addLabel(sName, sLabel, 'checkbox');
+ fChecked = self._reinterpretBool(fChecked);
+ return self._add(u' <input name="%s" id="%s" type="checkbox"%s%s value="1" class="tmform-checkbox">\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % (escapeAttr(sName), escapeAttr(sName), ' checked' if fChecked else '', sExtraAttribs));
+
+ def addCheckBoxRO(self, sName, fChecked, sLabel, sExtraAttribs = ''):
+ """Adds an readonly check box."""
+ self._addLabel(sName, sLabel, 'checkbox');
+ fChecked = self._reinterpretBool(fChecked);
+ # Hack Alert! The onclick and onkeydown are for preventing editing and fake readonly/disabled.
+ return self._add(u' <input name="%s" id="%s" type="checkbox"%s readonly%s value="1" class="readonly"\n'
+ u' onclick="return false" onkeydown="return false">\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % (escapeAttr(sName), escapeAttr(sName), ' checked' if fChecked else '', sExtraAttribs));
+
+ #
+ # List of items to check
+ #
+ def _addList(self, sName, aoRows, sLabel, fUseTable = False, sId = 'dummy', sExtraAttribs = ''):
+ """
+ Adds a list of items to check.
+
+ @param sName Name of HTML form element
+ @param aoRows List of [sValue, fChecked, sName] sub-arrays.
+ @param sLabel Label of HTML form element
+ """
+ fReadOnly = self._fReadOnly; ## @todo add this as a parameter.
+ if fReadOnly:
+ sExtraAttribs += ' readonly onclick="return false" onkeydown="return false"';
+
+ self._addLabel(sName, sLabel, 'list');
+ if not aoRows:
+ return self._add('No items</div></div></li>')
+ sNameEscaped = escapeAttr(sName);
+
+ self._add(' <div class="tmform-checkboxes-container" id="%s">\n' % (escapeAttr(sId),));
+ if fUseTable:
+ self._add(' <table>\n');
+ for asRow in aoRows:
+ assert len(asRow) == 3; # Don't allow sloppy input data!
+ fChecked = self._reinterpretBool(asRow[1])
+ self._add(u' <tr>\n'
+ u' <td><input type="checkbox" name="%s" value="%s"%s%s></td>\n'
+ u' <td>%s</td>\n'
+ u' </tr>\n'
+ % ( sNameEscaped, escapeAttr(unicode(asRow[0])), ' checked' if fChecked else '', sExtraAttribs,
+ escapeElem(unicode(asRow[2])), ));
+ self._add(u' </table>\n');
+ else:
+ for asRow in aoRows:
+ assert len(asRow) == 3; # Don't allow sloppy input data!
+ fChecked = self._reinterpretBool(asRow[1])
+ self._add(u' <div class="tmform-checkbox-holder">'
+ u'<input type="checkbox" name="%s" value="%s"%s%s> %s</input></div>\n'
+ % ( sNameEscaped, escapeAttr(unicode(asRow[0])), ' checked' if fChecked else '', sExtraAttribs,
+ escapeElem(unicode(asRow[2])),));
+ return self._add(u' </div></div></div>\n'
+ u' </li>\n');
+
+
+ def addListOfOsArches(self, sName, aoOsArches, sLabel, sExtraAttribs = ''):
+ """
+ List of checkboxes for OS/ARCH selection.
+ asOsArches is a list of [sValue, fChecked, sName] sub-arrays.
+ """
+ return self._addList(sName, aoOsArches, sLabel, fUseTable = False, sId = 'tmform-checkbox-list-os-arches',
+ sExtraAttribs = sExtraAttribs);
+
+ def addListOfTypes(self, sName, aoTypes, sLabel, sExtraAttribs = ''):
+ """
+ List of checkboxes for build type selection.
+ aoTypes is a list of [sValue, fChecked, sName] sub-arrays.
+ """
+ return self._addList(sName, aoTypes, sLabel, fUseTable = False, sId = 'tmform-checkbox-list-build-types',
+ sExtraAttribs = sExtraAttribs);
+
+ def addListOfTestCases(self, sName, aoTestCases, sLabel, sExtraAttribs = ''):
+ """
+ List of checkboxes for test box (dependency) selection.
+ aoTestCases is a list of [sValue, fChecked, sName] sub-arrays.
+ """
+ return self._addList(sName, aoTestCases, sLabel, fUseTable = False, sId = 'tmform-checkbox-list-testcases',
+ sExtraAttribs = sExtraAttribs);
+
+ def addListOfResources(self, sName, aoTestCases, sLabel, sExtraAttribs = ''):
+ """
+ List of checkboxes for resource selection.
+ aoTestCases is a list of [sValue, fChecked, sName] sub-arrays.
+ """
+ return self._addList(sName, aoTestCases, sLabel, fUseTable = False, sId = 'tmform-checkbox-list-resources',
+ sExtraAttribs = sExtraAttribs);
+
+ def addListOfTestGroups(self, sName, aoTestGroups, sLabel, sExtraAttribs = ''):
+ """
+ List of checkboxes for test group selection.
+ aoTestGroups is a list of [sValue, fChecked, sName] sub-arrays.
+ """
+ return self._addList(sName, aoTestGroups, sLabel, fUseTable = False, sId = 'tmform-checkbox-list-testgroups',
+ sExtraAttribs = sExtraAttribs);
+
+ def addListOfTestCaseArgs(self, sName, aoVariations, sLabel): # pylint: disable=too-many-statements
+ """
+ Adds a list of test case argument variations to the form.
+
+ @param sName Name of HTML form element
+ @param aoVariations List of TestCaseArgsData instances.
+ @param sLabel Label of HTML form element
+ """
+ self._addLabel(sName, sLabel);
+
+ sTableId = u'TestArgsExtendingListRoot';
+ fReadOnly = self._fReadOnly; ## @todo argument?
+ sReadOnlyAttr = u' readonly class="tmform-input-readonly"' if fReadOnly else '';
+
+ sHtml = u'<li>\n'
+
+ #
+ # Define javascript function for extending the list of test case
+ # variations. Doing it here so we can use the python constants. This
+ # also permits multiple argument lists on one page should that ever be
+ # required...
+ #
+ if not fReadOnly:
+ sHtml += u'<script type="text/javascript">\n'
+ sHtml += u'\n';
+ sHtml += u'g_%s_aItems = { %s };\n' % (sName, ', '.join(('%s: 1' % (i,)) for i in range(len(aoVariations))),);
+ sHtml += u'g_%s_cItems = %s;\n' % (sName, len(aoVariations),);
+ sHtml += u'g_%s_iIdMod = %s;\n' % (sName, len(aoVariations) + 32);
+ sHtml += u'\n';
+ sHtml += u'function %s_removeEntry(sId)\n' % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' if (g_%s_cItems > 1)\n' % (sName,);
+ sHtml += u' {\n';
+ sHtml += u' g_%s_cItems--;\n' % (sName,);
+ sHtml += u' delete g_%s_aItems[sId];\n' % (sName,);
+ sHtml += u' setElementValueToKeyList(\'%s\', g_%s_aItems);\n' % (sName, sName);
+ sHtml += u'\n';
+ for iInput in range(8):
+ sHtml += u' removeHtmlNode(\'%s[\' + sId + \'][%s]\');\n' % (sName, iInput,);
+ sHtml += u' }\n';
+ sHtml += u'}\n';
+ sHtml += u'\n';
+ sHtml += u'function %s_extendListEx(sSubName, cGangMembers, cSecTimeout, sArgs, sTestBoxReqExpr, sBuildReqExpr)\n' \
+ % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' var oElement = document.getElementById(\'%s\');\n' % (sTableId,);
+ sHtml += u' var oTBody = document.createElement(\'tbody\');\n';
+ sHtml += u' var sHtml = \'\';\n';
+ sHtml += u' var sId;\n';
+ sHtml += u'\n';
+ sHtml += u' g_%s_iIdMod += 1;\n' % (sName,);
+ sHtml += u' sId = g_%s_iIdMod.toString();\n' % (sName,);
+
+ oVarDefaults = TestCaseArgsData();
+ oVarDefaults.convertToParamNull();
+ sHtml += u'\n';
+ sHtml += u' sHtml += \'<tr class="tmform-testcasevars-first-row">\';\n';
+ sHtml += u' sHtml += \' <td>Sub-Name:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-subname">' \
+ '<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][0]" value="\' + sSubName + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_sSubName, sName,);
+ sHtml += u' sHtml += \' <td>Gang Members:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-tiny-int">' \
+ '<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][0]" value="\' + cGangMembers + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_cGangMembers, sName,);
+ sHtml += u' sHtml += \' <td>Timeout:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-int">' \
+ u'<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][1]" value="\'+ cSecTimeout + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_cSecTimeout, sName,);
+ sHtml += u' sHtml += \' <td><a href="#" onclick="%s_removeEntry(\\\'\' + sId + \'\\\');"> Remove</a></td>\';\n' \
+ % (sName, );
+ sHtml += u' sHtml += \' <td></td>\';\n';
+ sHtml += u' sHtml += \'</tr>\';\n'
+ sHtml += u'\n';
+ sHtml += u' sHtml += \'<tr class="tmform-testcasevars-inner-row">\';\n';
+ sHtml += u' sHtml += \' <td>Arguments:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][2]" value="\' + sArgs + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_sArgs, sName,);
+ sHtml += u' sHtml += \' <td></td>\';\n';
+ sHtml += u' sHtml += \'</tr>\';\n'
+ sHtml += u'\n';
+ sHtml += u' sHtml += \'<tr class="tmform-testcasevars-inner-row">\';\n';
+ sHtml += u' sHtml += \' <td>TestBox Reqs:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][2]" value="\' + sTestBoxReqExpr' \
+ u' + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_sTestBoxReqExpr, sName,);
+ sHtml += u' sHtml += \' <td></td>\';\n';
+ sHtml += u' sHtml += \'</tr>\';\n'
+ sHtml += u'\n';
+ sHtml += u' sHtml += \'<tr class="tmform-testcasevars-final-row">\';\n';
+ sHtml += u' sHtml += \' <td>Build Reqs:</td>\';\n';
+ sHtml += u' sHtml += \' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[\' + sId + \'][%s]" id="%s[\' + sId + \'][2]" value="\' + sBuildReqExpr + \'"></td>\';\n' \
+ % (sName, TestCaseArgsData.ksParam_sBuildReqExpr, sName,);
+ sHtml += u' sHtml += \' <td></td>\';\n';
+ sHtml += u' sHtml += \'</tr>\';\n'
+ sHtml += u'\n';
+ sHtml += u' oTBody.id = \'%s[\' + sId + \'][6]\';\n' % (sName,);
+ sHtml += u' oTBody.innerHTML = sHtml;\n';
+ sHtml += u'\n';
+ sHtml += u' oElement.appendChild(oTBody);\n';
+ sHtml += u'\n';
+ sHtml += u' g_%s_aItems[sId] = 1;\n' % (sName,);
+ sHtml += u' g_%s_cItems++;\n' % (sName,);
+ sHtml += u' setElementValueToKeyList(\'%s\', g_%s_aItems);\n' % (sName, sName);
+ sHtml += u'}\n';
+ sHtml += u'function %s_extendList()\n' % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' %s_extendListEx("%s", "%s", "%s", "%s", "%s", "%s");\n' % (sName,
+ escapeAttr(unicode(oVarDefaults.sSubName)), escapeAttr(unicode(oVarDefaults.cGangMembers)),
+ escapeAttr(unicode(oVarDefaults.cSecTimeout)), escapeAttr(oVarDefaults.sArgs),
+ escapeAttr(oVarDefaults.sTestBoxReqExpr), escapeAttr(oVarDefaults.sBuildReqExpr), );
+ sHtml += u'}\n';
+ if config.g_kfVBoxSpecific:
+ sSecTimeoutDef = escapeAttr(unicode(oVarDefaults.cSecTimeout));
+ sHtml += u'function vbox_%s_add_uni()\n' % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' %s_extendListEx("1-raw", "1", "%s", "--cpu-counts 1 --virt-modes raw", ' \
+ u' "", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("1-hw", "1", "%s", "--cpu-counts 1 --virt-modes hwvirt", ' \
+ u' "fCpuHwVirt is True", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("1-np", "1", "%s", "--cpu-counts 1 --virt-modes hwvirt-np", ' \
+ u' "fCpuNestedPaging is True", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u'}\n';
+ sHtml += u'function vbox_%s_add_uni_amd64()\n' % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' %s_extendListEx("1-hw", "1", "%s", "--cpu-counts 1 --virt-modes hwvirt", ' \
+ u' "fCpuHwVirt is True", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("1-np", "%s", "--cpu-counts 1 --virt-modes hwvirt-np", ' \
+ u' "fCpuNestedPaging is True", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u'}\n';
+ sHtml += u'function vbox_%s_add_smp()\n' % (sName,);
+ sHtml += u'{\n';
+ sHtml += u' %s_extendListEx("2-hw", "1", "%s", "--cpu-counts 2 --virt-modes hwvirt",' \
+ u' "fCpuHwVirt is True and cCpus >= 2", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("2-np", "1", "%s", "--cpu-counts 2 --virt-modes hwvirt-np",' \
+ u' "fCpuNestedPaging is True and cCpus >= 2", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("3-hw", "1", "%s", "--cpu-counts 3 --virt-modes hwvirt",' \
+ u' "fCpuHwVirt is True and cCpus >= 3", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u' %s_extendListEx("4-np", "1", "%s", "--cpu-counts 4 --virt-modes hwvirt-np ",' \
+ u' "fCpuNestedPaging is True and cCpus >= 4", "");\n' % (sName, sSecTimeoutDef);
+ #sHtml += u' %s_extendListEx("6-hw", "1", "%s", "--cpu-counts 6 --virt-modes hwvirt",' \
+ # u' "fCpuHwVirt is True and cCpus >= 6", "");\n' % (sName, sSecTimeoutDef);
+ #sHtml += u' %s_extendListEx("8-np", "1", "%s", "--cpu-counts 8 --virt-modes hwvirt-np",' \
+ # u' "fCpuNestedPaging is True and cCpus >= 8", "");\n' % (sName, sSecTimeoutDef);
+ sHtml += u'}\n';
+ sHtml += u'</script>\n';
+
+
+ #
+ # List current entries.
+ #
+ sHtml += u'<input type="hidden" name="%s" id="%s" value="%s">\n' \
+ % (sName, sName, ','.join(unicode(i) for i in range(len(aoVariations))), );
+ sHtml += u' <table id="%s" class="tmform-testcasevars">\n' % (sTableId,)
+ if not fReadOnly:
+ sHtml += u' <caption>\n' \
+ u' <a href="#" onClick="%s_extendList()">Add</a>\n' % (sName,);
+ if config.g_kfVBoxSpecific:
+ sHtml += u' [<a href="#" onClick="vbox_%s_add_uni()">Single CPU Variations</a>\n' % (sName,);
+ sHtml += u' <a href="#" onClick="vbox_%s_add_uni_amd64()">amd64</a>]\n' % (sName,);
+ sHtml += u' [<a href="#" onClick="vbox_%s_add_smp()">SMP Variations</a>]\n' % (sName,);
+ sHtml += u' </caption>\n';
+
+ dSubErrors = {};
+ if sName in self._dErrors and isinstance(self._dErrors[sName], dict):
+ dSubErrors = self._dErrors[sName];
+
+ for iVar, _ in enumerate(aoVariations):
+ oVar = copy.copy(aoVariations[iVar]);
+ oVar.convertToParamNull();
+
+ sHtml += u'<tbody id="%s[%s][6]">\n' % (sName, iVar,)
+ sHtml += u' <tr class="tmform-testcasevars-first-row">\n' \
+ u' <td>Sub-name:</td>' \
+ u' <td class="tmform-field-subname"><input name="%s[%s][%s]" id="%s[%s][1]" value="%s"%s></td>\n' \
+ u' <td>Gang Members:</td>' \
+ u' <td class="tmform-field-tiny-int"><input name="%s[%s][%s]" id="%s[%s][1]" value="%s"%s></td>\n' \
+ u' <td>Timeout:</td>' \
+ u' <td class="tmform-field-int"><input name="%s[%s][%s]" id="%s[%s][2]" value="%s"%s></td>\n' \
+ % ( sName, iVar, TestCaseArgsData.ksParam_sSubName, sName, iVar, oVar.sSubName, sReadOnlyAttr,
+ sName, iVar, TestCaseArgsData.ksParam_cGangMembers, sName, iVar, oVar.cGangMembers, sReadOnlyAttr,
+ sName, iVar, TestCaseArgsData.ksParam_cSecTimeout, sName, iVar,
+ utils.formatIntervalSeconds2(oVar.cSecTimeout), sReadOnlyAttr, );
+ if not fReadOnly:
+ sHtml += u' <td><a href="#" onclick="%s_removeEntry(\'%s\');">Remove</a></td>\n' \
+ % (sName, iVar);
+ else:
+ sHtml += u' <td></td>\n';
+ sHtml += u' <td class="tmform-testcasevars-stupid-border-column"></td>\n' \
+ u' </tr>\n';
+
+ sHtml += u' <tr class="tmform-testcasevars-inner-row">\n' \
+ u' <td>Arguments:</td>' \
+ u' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[%s][%s]" id="%s[%s][3]" value="%s"%s></td>\n' \
+ u' <td></td>\n' \
+ u' </tr>\n' \
+ % ( sName, iVar, TestCaseArgsData.ksParam_sArgs, sName, iVar, escapeAttr(oVar.sArgs), sReadOnlyAttr)
+
+ sHtml += u' <tr class="tmform-testcasevars-inner-row">\n' \
+ u' <td>TestBox Reqs:</td>' \
+ u' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[%s][%s]" id="%s[%s][4]" value="%s"%s></td>\n' \
+ u' <td></td>\n' \
+ u' </tr>\n' \
+ % ( sName, iVar, TestCaseArgsData.ksParam_sTestBoxReqExpr, sName, iVar,
+ escapeAttr(oVar.sTestBoxReqExpr), sReadOnlyAttr)
+
+ sHtml += u' <tr class="tmform-testcasevars-final-row">\n' \
+ u' <td>Build Reqs:</td>' \
+ u' <td class="tmform-field-wide100" colspan="6">' \
+ u'<input name="%s[%s][%s]" id="%s[%s][5]" value="%s"%s></td>\n' \
+ u' <td></td>\n' \
+ u' </tr>\n' \
+ % ( sName, iVar, TestCaseArgsData.ksParam_sBuildReqExpr, sName, iVar,
+ escapeAttr(oVar.sBuildReqExpr), sReadOnlyAttr)
+
+
+ if iVar in dSubErrors:
+ sHtml += u' <tr><td colspan="4"><p align="left" class="tmform-error-desc">%s</p></td></tr>\n' \
+ % (self._escapeErrorText(dSubErrors[iVar]),);
+
+ sHtml += u'</tbody>\n';
+ sHtml += u' </table>\n'
+ sHtml += u'</li>\n'
+
+ return self._add(sHtml)
+
+ def addListOfTestGroupMembers(self, sName, aoTestGroupMembers, aoAllTestCases, sLabel, # pylint: disable=too-many-locals
+ fReadOnly = True):
+ """
+ For WuiTestGroup.
+ """
+ assert len(aoTestGroupMembers) <= len(aoAllTestCases);
+ self._addLabel(sName, sLabel);
+ if not aoAllTestCases:
+ return self._add('<li>No testcases.</li>\n')
+
+ self._add(u'<input name="%s" type="hidden" value="%s">\n'
+ % ( TestGroupDataEx.ksParam_aidTestCases,
+ ','.join([unicode(oTestCase.idTestCase) for oTestCase in aoAllTestCases]), ));
+
+ self._add(u'<table class="tmformtbl">\n'
+ u' <thead>\n'
+ u' <tr>\n'
+ u' <th rowspan="2"></th>\n'
+ u' <th rowspan="2">Test Case</th>\n'
+ u' <th rowspan="2">All Vars</th>\n'
+ u' <th rowspan="2">Priority [0..31]</th>\n'
+ u' <th colspan="4" align="center">Variations</th>\n'
+ u' </tr>\n'
+ u' <tr>\n'
+ u' <th>Included</th>\n'
+ u' <th>Gang size</th>\n'
+ u' <th>Timeout</th>\n'
+ u' <th>Arguments</th>\n'
+ u' </tr>\n'
+ u' </thead>\n'
+ u' <tbody>\n'
+ );
+
+ if self._fReadOnly:
+ fReadOnly = True;
+ sCheckBoxAttr = ' readonly onclick="return false" onkeydown="return false"' if fReadOnly else '';
+
+ oDefMember = TestGroupMemberData();
+ aoTestGroupMembers = list(aoTestGroupMembers); # Copy it so we can pop.
+ for iTestCase, _ in enumerate(aoAllTestCases):
+ oTestCase = aoAllTestCases[iTestCase];
+
+ # Is it a member?
+ oMember = None;
+ for i, _ in enumerate(aoTestGroupMembers):
+ if aoTestGroupMembers[i].oTestCase.idTestCase == oTestCase.idTestCase:
+ oMember = aoTestGroupMembers.pop(i);
+ break;
+
+ # Start on the rows...
+ sPrefix = u'%s[%d]' % (sName, oTestCase.idTestCase,);
+ self._add(u' <tr class="%s">\n'
+ u' <td rowspan="%d">\n'
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestCase
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
+ u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
+ u' </td>\n'
+ % ( 'tmodd' if iTestCase & 1 else 'tmeven',
+ len(oTestCase.aoTestCaseArgs),
+ sPrefix, TestGroupMemberData.ksParam_idTestCase, oTestCase.idTestCase,
+ sPrefix, TestGroupMemberData.ksParam_idTestGroup, -1 if oMember is None else oMember.idTestGroup,
+ sPrefix, TestGroupMemberData.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire,
+ sPrefix, TestGroupMemberData.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
+ sPrefix, TestGroupMemberData.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor,
+ TestGroupDataEx.ksParam_aoMembers, '' if oMember is None else ' checked', sCheckBoxAttr,
+ oTestCase.idTestCase, oTestCase.idTestCase, escapeElem(oTestCase.sName),
+ ));
+ self._add(u' <td rowspan="%d" align="left">%s</td>\n'
+ % ( len(oTestCase.aoTestCaseArgs), escapeElem(oTestCase.sName), ));
+
+ self._add(u' <td rowspan="%d" title="Include all variations (checked) or choose a set?">\n'
+ u' <input name="%s[%s]" type="checkbox"%s%s value="-1">\n'
+ u' </td>\n'
+ % ( len(oTestCase.aoTestCaseArgs),
+ sPrefix, TestGroupMemberData.ksParam_aidTestCaseArgs,
+ ' checked' if oMember is None or oMember.aidTestCaseArgs is None else '', sCheckBoxAttr, ));
+
+ self._add(u' <td rowspan="%d" align="center">\n'
+ u' <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n'
+ u' </td>\n'
+ % ( len(oTestCase.aoTestCaseArgs),
+ sPrefix, TestGroupMemberData.ksParam_iSchedPriority,
+ (oMember if oMember is not None else oDefMember).iSchedPriority,
+ ' readonly class="tmform-input-readonly"' if fReadOnly else '', ));
+
+ # Argument variations.
+ aidTestCaseArgs = [] if oMember is None or oMember.aidTestCaseArgs is None else oMember.aidTestCaseArgs;
+ for iVar, oVar in enumerate(oTestCase.aoTestCaseArgs):
+ if iVar > 0:
+ self._add(' <tr class="%s">\n' % ('tmodd' if iTestCase & 1 else 'tmeven',));
+ self._add(u' <td align="center">\n'
+ u' <input name="%s[%s]" type="checkbox"%s%s value="%d">'
+ u' </td>\n'
+ % ( sPrefix, TestGroupMemberData.ksParam_aidTestCaseArgs,
+ ' checked' if oVar.idTestCaseArgs in aidTestCaseArgs else '', sCheckBoxAttr, oVar.idTestCaseArgs,
+ ));
+ self._add(u' <td align="center">%s</td>\n'
+ u' <td align="center">%s</td>\n'
+ u' <td align="left">%s</td>\n'
+ % ( oVar.cGangMembers,
+ 'Default' if oVar.cSecTimeout is None else oVar.cSecTimeout,
+ escapeElem(oVar.sArgs) ));
+
+ self._add(u' </tr>\n');
+
+
+
+ if not oTestCase.aoTestCaseArgs:
+ self._add(u' <td></td> <td></td> <td></td> <td></td>\n'
+ u' </tr>\n');
+ return self._add(u' </tbody>\n'
+ u'</table>\n');
+
+ def addListOfSchedGroupMembers(self, sName, aoSchedGroupMembers, aoAllRelevantTestGroups, # pylint: disable=too-many-locals
+ sLabel, idSchedGroup, fReadOnly = True):
+ """
+ For WuiAdminSchedGroup.
+ """
+ if fReadOnly is None or self._fReadOnly:
+ fReadOnly = self._fReadOnly;
+ assert len(aoSchedGroupMembers) <= len(aoAllRelevantTestGroups);
+ self._addLabel(sName, sLabel);
+ if not aoAllRelevantTestGroups:
+ return self._add(u'<li>No test groups.</li>\n')
+
+ self._add(u'<input name="%s" type="hidden" value="%s">\n'
+ % ( SchedGroupDataEx.ksParam_aidTestGroups,
+ ','.join([unicode(oTestGroup.idTestGroup) for oTestGroup in aoAllRelevantTestGroups]), ));
+
+ self._add(u'<table class="tmformtbl tmformtblschedgroupmembers">\n'
+ u' <thead>\n'
+ u' <tr>\n'
+ u' <th></th>\n'
+ u' <th>Test Group</th>\n'
+ u' <th>Priority [0..31]</th>\n'
+ u' <th>Prerequisite Test Group</th>\n'
+ u' <th>Weekly schedule</th>\n'
+ u' </tr>\n'
+ u' </thead>\n'
+ u' <tbody>\n'
+ );
+
+ sCheckBoxAttr = u' readonly onclick="return false" onkeydown="return false"' if fReadOnly else '';
+ sComboBoxAttr = u' disabled' if fReadOnly else '';
+
+ oDefMember = SchedGroupMemberData();
+ aoSchedGroupMembers = list(aoSchedGroupMembers); # Copy it so we can pop.
+ for iTestGroup, _ in enumerate(aoAllRelevantTestGroups):
+ oTestGroup = aoAllRelevantTestGroups[iTestGroup];
+
+ # Is it a member?
+ oMember = None;
+ for i, _ in enumerate(aoSchedGroupMembers):
+ if aoSchedGroupMembers[i].oTestGroup.idTestGroup == oTestGroup.idTestGroup:
+ oMember = aoSchedGroupMembers.pop(i);
+ break;
+
+ # Start on the rows...
+ sPrefix = u'%s[%d]' % (sName, oTestGroup.idTestGroup,);
+ self._add(u' <tr class="%s">\n'
+ u' <td>\n'
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
+ u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
+ u' </td>\n'
+ % ( 'tmodd' if iTestGroup & 1 else 'tmeven',
+ sPrefix, SchedGroupMemberData.ksParam_idTestGroup, oTestGroup.idTestGroup,
+ sPrefix, SchedGroupMemberData.ksParam_idSchedGroup, idSchedGroup,
+ sPrefix, SchedGroupMemberData.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire,
+ sPrefix, SchedGroupMemberData.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
+ sPrefix, SchedGroupMemberData.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor,
+ SchedGroupDataEx.ksParam_aoMembers, '' if oMember is None else ' checked', sCheckBoxAttr,
+ oTestGroup.idTestGroup, oTestGroup.idTestGroup, escapeElem(oTestGroup.sName),
+ ));
+ self._add(u' <td>%s</td>\n' % ( escapeElem(oTestGroup.sName), ));
+
+ self._add(u' <td>\n'
+ u' <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n'
+ u' </td>\n'
+ % ( sPrefix, SchedGroupMemberData.ksParam_iSchedPriority,
+ (oMember if oMember is not None else oDefMember).iSchedPriority,
+ ' readonly class="tmform-input-readonly"' if fReadOnly else '', ));
+
+ self._add(u' <td>\n'
+ u' <select name="%s[%s]" id="%s[%s]" class="tmform-combobox"%s>\n'
+ u' <option value="-1"%s>None</option>\n'
+ % ( sPrefix, SchedGroupMemberData.ksParam_idTestGroupPreReq,
+ sPrefix, SchedGroupMemberData.ksParam_idTestGroupPreReq,
+ sComboBoxAttr,
+ ' selected' if oMember is None or oMember.idTestGroupPreReq is None else '',
+ ));
+ for oTestGroup2 in aoAllRelevantTestGroups:
+ if oTestGroup2 != oTestGroup:
+ fSelected = oMember is not None and oTestGroup2.idTestGroup == oMember.idTestGroupPreReq;
+ self._add(' <option value="%s"%s>%s</option>\n'
+ % ( oTestGroup2.idTestGroup, ' selected' if fSelected else '', escapeElem(oTestGroup2.sName), ));
+ self._add(u' </select>\n'
+ u' </td>\n');
+
+ self._add(u' <td>\n'
+ u' Todo<input name="%s[%s]" type="hidden" value="%s">\n'
+ u' </td>\n'
+ % ( sPrefix, SchedGroupMemberData.ksParam_bmHourlySchedule,
+ '' if oMember is None else oMember.bmHourlySchedule, ));
+
+ self._add(u' </tr>\n');
+ return self._add(u' </tbody>\n'
+ u'</table>\n');
+
+ def addListOfSchedGroupBoxes(self, sName, aoSchedGroupBoxes, # pylint: disable=too-many-locals
+ aoAllRelevantTestBoxes, sLabel, idSchedGroup, fReadOnly = True,
+ fUseTable = False): # (str, list[TestBoxDataEx], list[TestBoxDataEx], str, bool, bool) -> str
+ """
+ For WuiAdminSchedGroup.
+ """
+ if fReadOnly is None or self._fReadOnly:
+ fReadOnly = self._fReadOnly;
+ assert len(aoSchedGroupBoxes) <= len(aoAllRelevantTestBoxes);
+ self._addLabel(sName, sLabel);
+ if not aoAllRelevantTestBoxes:
+ return self._add(u'<li>No test boxes.</li>\n')
+
+ self._add(u'<input name="%s" type="hidden" value="%s">\n'
+ % ( SchedGroupDataEx.ksParam_aidTestBoxes,
+ ','.join([unicode(oTestBox.idTestBox) for oTestBox in aoAllRelevantTestBoxes]), ));
+
+ sCheckBoxAttr = u' readonly onclick="return false" onkeydown="return false"' if fReadOnly else '';
+ oDefMember = TestBoxDataForSchedGroup();
+ aoSchedGroupBoxes = list(aoSchedGroupBoxes); # Copy it so we can pop.
+
+ from testmanager.webui.wuiadmintestbox import WuiTestBoxDetailsLink;
+
+ if not fUseTable:
+ #
+ # Non-table version (see also addListOfOsArches).
+ #
+ self._add(' <div class="tmform-checkboxes-container">\n');
+
+ for iTestBox, oTestBox in enumerate(aoAllRelevantTestBoxes):
+ # Is it a member?
+ oMember = None;
+ for i, _ in enumerate(aoSchedGroupBoxes):
+ if aoSchedGroupBoxes[i].oTestBox and aoSchedGroupBoxes[i].oTestBox.idTestBox == oTestBox.idTestBox:
+ oMember = aoSchedGroupBoxes.pop(i);
+ break;
+
+ # Start on the rows...
+ sPrf = u'%s[%d]' % (sName, oTestBox.idTestBox,);
+ self._add(u' <div class="tmform-checkbox-holder tmshade%u">\n'
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestBox
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
+ u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
+ % ( iTestBox & 7,
+ sPrf, TestBoxDataForSchedGroup.ksParam_idTestBox, oTestBox.idTestBox,
+ sPrf, TestBoxDataForSchedGroup.ksParam_idSchedGroup, idSchedGroup,
+ sPrf, TestBoxDataForSchedGroup.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire,
+ sPrf, TestBoxDataForSchedGroup.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
+ sPrf, TestBoxDataForSchedGroup.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor,
+ SchedGroupDataEx.ksParam_aoTestBoxes, '' if oMember is None else ' checked', sCheckBoxAttr,
+ oTestBox.idTestBox, oTestBox.idTestBox, escapeElem(oTestBox.sName),
+ ));
+
+ self._add(u' <span class="tmform-priority tmform-testbox-priority">'
+ u'<input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s title="%s"></span>\n'
+ % ( sPrf, TestBoxDataForSchedGroup.ksParam_iSchedPriority,
+ (oMember if oMember is not None else oDefMember).iSchedPriority,
+ ' readonly class="tmform-input-readonly"' if fReadOnly else '',
+ escapeAttr("Priority [0..31]. Higher value means run more often.") ));
+
+ self._add(u' <span class="tmform-testbox-name">%s</span>\n'
+ % ( WuiTestBoxDetailsLink(oTestBox, sName = '%s (%s)' % (oTestBox.sName, oTestBox.sOs,)),));
+ self._add(u' </div>\n');
+ return self._add(u' </div></div></div>\n'
+ u' </li>\n');
+
+ #
+ # Table version.
+ #
+ self._add(u'<table class="tmformtbl">\n'
+ u' <thead>\n'
+ u' <tr>\n'
+ u' <th></th>\n'
+ u' <th>Test Box</th>\n'
+ u' <th>Priority [0..31]</th>\n'
+ u' </tr>\n'
+ u' </thead>\n'
+ u' <tbody>\n'
+ );
+
+ for iTestBox, oTestBox in enumerate(aoAllRelevantTestBoxes):
+ # Is it a member?
+ oMember = None;
+ for i, _ in enumerate(aoSchedGroupBoxes):
+ if aoSchedGroupBoxes[i].oTestBox and aoSchedGroupBoxes[i].oTestBox.idTestBox == oTestBox.idTestBox:
+ oMember = aoSchedGroupBoxes.pop(i);
+ break;
+
+ # Start on the rows...
+ sPrefix = u'%s[%d]' % (sName, oTestBox.idTestBox,);
+ self._add(u' <tr class="%s">\n'
+ u' <td>\n'
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestBox
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
+ u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
+ u' </td>\n'
+ % ( 'tmodd' if iTestBox & 1 else 'tmeven',
+ sPrefix, TestBoxDataForSchedGroup.ksParam_idTestBox, oTestBox.idTestBox,
+ sPrefix, TestBoxDataForSchedGroup.ksParam_idSchedGroup, idSchedGroup,
+ sPrefix, TestBoxDataForSchedGroup.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire,
+ sPrefix, TestBoxDataForSchedGroup.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
+ sPrefix, TestBoxDataForSchedGroup.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor,
+ SchedGroupDataEx.ksParam_aoTestBoxes, '' if oMember is None else ' checked', sCheckBoxAttr,
+ oTestBox.idTestBox, oTestBox.idTestBox, escapeElem(oTestBox.sName),
+ ));
+ self._add(u' <td align="left">%s</td>\n' % ( escapeElem(oTestBox.sName), ));
+
+ self._add(u' <td align="center">\n'
+ u' <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n'
+ u' </td>\n'
+ % ( sPrefix,
+ TestBoxDataForSchedGroup.ksParam_iSchedPriority,
+ (oMember if oMember is not None else oDefMember).iSchedPriority,
+ ' readonly class="tmform-input-readonly"' if fReadOnly else '', ));
+
+ self._add(u' </tr>\n');
+ return self._add(u' </tbody>\n'
+ u'</table>\n');
+
+ def addListOfSchedGroupsForTestBox(self, sName, aoInSchedGroups, aoAllSchedGroups, sLabel, # pylint: disable=too-many-locals
+ idTestBox, fReadOnly = None):
+ # type: (str, TestBoxInSchedGroupDataEx, SchedGroupData, str, bool) -> str
+ """
+ For WuiTestGroup.
+ """
+ from testmanager.core.testbox import TestBoxInSchedGroupData, TestBoxDataEx;
+
+ if fReadOnly is None or self._fReadOnly:
+ fReadOnly = self._fReadOnly;
+ assert len(aoInSchedGroups) <= len(aoAllSchedGroups);
+
+ # Only show selected groups in read-only mode.
+ if fReadOnly:
+ aoAllSchedGroups = [oCur.oSchedGroup for oCur in aoInSchedGroups]
+
+ self._addLabel(sName, sLabel);
+ if not aoAllSchedGroups:
+ return self._add('<li>No scheduling groups.</li>\n')
+
+ # Add special parameter with all the scheduling group IDs in the form.
+ self._add(u'<input name="%s" type="hidden" value="%s">\n'
+ % ( TestBoxDataEx.ksParam_aidSchedGroups,
+ ','.join([unicode(oSchedGroup.idSchedGroup) for oSchedGroup in aoAllSchedGroups]), ));
+
+ # Table header.
+ self._add(u'<table class="tmformtbl">\n'
+ u' <thead>\n'
+ u' <tr>\n'
+ u' <th rowspan="2"></th>\n'
+ u' <th rowspan="2">Schedulding Group</th>\n'
+ u' <th rowspan="2">Priority [0..31]</th>\n'
+ u' </tr>\n'
+ u' </thead>\n'
+ u' <tbody>\n'
+ );
+
+ # Table body.
+ if self._fReadOnly:
+ fReadOnly = True;
+ sCheckBoxAttr = ' readonly onclick="return false" onkeydown="return false"' if fReadOnly else '';
+
+ oDefMember = TestBoxInSchedGroupData();
+ aoInSchedGroups = list(aoInSchedGroups); # Copy it so we can pop.
+ for iSchedGroup, oSchedGroup in enumerate(aoAllSchedGroups):
+
+ # Is it a member?
+ oMember = None;
+ for i, _ in enumerate(aoInSchedGroups):
+ if aoInSchedGroups[i].idSchedGroup == oSchedGroup.idSchedGroup:
+ oMember = aoInSchedGroups.pop(i);
+ break;
+
+ # Start on the rows...
+ sPrefix = u'%s[%d]' % (sName, oSchedGroup.idSchedGroup,);
+ self._add(u' <tr class="%s">\n'
+ u' <td>\n'
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestBox
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
+ u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
+ u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
+ u' </td>\n'
+ % ( 'tmodd' if iSchedGroup & 1 else 'tmeven',
+ sPrefix, TestBoxInSchedGroupData.ksParam_idSchedGroup, oSchedGroup.idSchedGroup,
+ sPrefix, TestBoxInSchedGroupData.ksParam_idTestBox, idTestBox,
+ sPrefix, TestBoxInSchedGroupData.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire,
+ sPrefix, TestBoxInSchedGroupData.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
+ sPrefix, TestBoxInSchedGroupData.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor,
+ TestBoxDataEx.ksParam_aoInSchedGroups, '' if oMember is None else ' checked', sCheckBoxAttr,
+ oSchedGroup.idSchedGroup, oSchedGroup.idSchedGroup, escapeElem(oSchedGroup.sName),
+ ));
+ self._add(u' <td align="left">%s</td>\n' % ( escapeElem(oSchedGroup.sName), ));
+
+ self._add(u' <td align="center">\n'
+ u' <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n'
+ u' </td>\n'
+ % ( sPrefix, TestBoxInSchedGroupData.ksParam_iSchedPriority,
+ (oMember if oMember is not None else oDefMember).iSchedPriority,
+ ' readonly class="tmform-input-readonly"' if fReadOnly else '', ));
+ self._add(u' </tr>\n');
+
+ return self._add(u' </tbody>\n'
+ u'</table>\n');
+
+
+ #
+ # Buttons.
+ #
+ def addSubmit(self, sLabel = 'Submit'):
+ """Adds the submit button to the form."""
+ if self._fReadOnly:
+ return True;
+ return self._add(u' <li>\n'
+ u' <br>\n'
+ u' <div class="tmform-field"><div class="tmform-field-submit">\n'
+ u' <label>&nbsp;</label>\n'
+ u' <input type="submit" value="%s">\n'
+ u' </div></div>\n'
+ u' </li>\n'
+ % (escapeElem(sLabel),));
+
+ def addReset(self):
+ """Adds a reset button to the form."""
+ if self._fReadOnly:
+ return True;
+ return self._add(u' <li>\n'
+ u' <div class="tmform-button"><div class="tmform-button-reset">\n'
+ u' <input type="reset" value="%s">\n'
+ u' </div></div>\n'
+ u' </li>\n');
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpgraph.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraph.py
new file mode 100755
index 00000000..ed08bb0f
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraph.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpgraph.py $
+
+"""
+Test Manager Web-UI - Graph Helpers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+class WuiHlpGraphDataTable(object): # pylint: disable=too-few-public-methods
+ """
+ Data table container.
+ """
+
+ class Row(object): # pylint: disable=too-few-public-methods
+ """A row."""
+ def __init__(self, sGroup, aoValues, asValues = None):
+ self.sName = sGroup;
+ self.aoValues = aoValues;
+ if asValues is None:
+ self.asValues = [str(oVal) for oVal in aoValues];
+ else:
+ assert len(asValues) == len(aoValues);
+ self.asValues = asValues;
+
+ def __init__(self, sGroupLable, asMemberLabels):
+ self.aoTable = [ WuiHlpGraphDataTable.Row(sGroupLable, asMemberLabels), ];
+ self.fHasStringValues = False;
+
+ def addRow(self, sGroup, aoValues, asValues = None):
+ """Adds a row to the data table."""
+ if asValues:
+ self.fHasStringValues = True;
+ self.aoTable.append(WuiHlpGraphDataTable.Row(sGroup, aoValues, asValues));
+ return True;
+
+ def getGroupCount(self):
+ """Gets the number of data groups (rows)."""
+ return len(self.aoTable) - 1;
+
+
+class WuiHlpGraphDataTableEx(object): # pylint: disable=too-few-public-methods
+ """
+ Data container for an table/graph with optional error bars on the Y values.
+ """
+
+ class DataSeries(object): # pylint: disable=too-few-public-methods
+ """
+ A data series.
+
+ The aoXValues, aoYValues and aoYErrorBars are parallel arrays, making a
+ series of (X,Y,Y-err-above-delta,Y-err-below-delta) points.
+
+ The error bars are optional.
+ """
+ def __init__(self, sName, aoXValues, aoYValues, asHtmlTooltips = None, aoYErrorBarBelow = None, aoYErrorBarAbove = None):
+ self.sName = sName;
+ self.aoXValues = aoXValues;
+ self.aoYValues = aoYValues;
+ self.asHtmlTooltips = asHtmlTooltips;
+ self.aoYErrorBarBelow = aoYErrorBarBelow;
+ self.aoYErrorBarAbove = aoYErrorBarAbove;
+
+ def __init__(self, sXUnit, sYUnit):
+ self.sXUnit = sXUnit;
+ self.sYUnit = sYUnit;
+ self.aoSeries = [];
+
+ def addDataSeries(self, sName, aoXValues, aoYValues, asHtmlTooltips = None, aoYErrorBarBelow = None, aoYErrorBarAbove = None):
+ """Adds an data series to the table."""
+ self.aoSeries.append(WuiHlpGraphDataTableEx.DataSeries(sName, aoXValues, aoYValues, asHtmlTooltips,
+ aoYErrorBarBelow, aoYErrorBarAbove));
+ return True;
+
+ def getDataSeriesCount(self):
+ """Gets the number of data series."""
+ return len(self.aoSeries);
+
+
+#
+# Dynamically choose implementation.
+#
+if True: # pylint: disable=using-constant-test
+ from testmanager.webui import wuihlpgraphgooglechart as GraphImplementation;
+else:
+ try:
+ import matplotlib; # pylint: disable=unused-import,import-error,import-error,wrong-import-order
+ from testmanager.webui import wuihlpgraphmatplotlib as GraphImplementation; # pylint: disable=ungrouped-imports
+ except:
+ from testmanager.webui import wuihlpgraphsimple as GraphImplementation;
+
+# pylint: disable=invalid-name
+WuiHlpBarGraph = GraphImplementation.WuiHlpBarGraph;
+WuiHlpLineGraph = GraphImplementation.WuiHlpLineGraph;
+WuiHlpLineGraphErrorbarY = GraphImplementation.WuiHlpLineGraphErrorbarY;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphbase.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphbase.py
new file mode 100755
index 00000000..7ab6c6b8
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphbase.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpgraphbase.py $
+
+"""
+Test Manager Web-UI - Graph Helpers - Base Class.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+class WuiHlpGraphBase(object):
+ """
+ Base class for the Graph helpers.
+ """
+
+ ## Set of colors that can be used by child classes to color data series.
+ kasColors = \
+ [
+ '#0000ff', # Blue
+ '#00ff00', # Green
+ '#ff0000', # Red
+ '#000000', # Black
+
+ '#00ffff', # Cyan/Aqua
+ '#ff00ff', # Magenta/Fuchsia
+ '#ffff00', # Yellow
+ '#8b4513', # SaddleBrown
+
+ '#7b68ee', # MediumSlateBlue
+ '#ffc0cb', # Pink
+ '#bdb76b', # DarkKhaki
+ '#008080', # Teal
+
+ '#bc8f8f', # RosyBrown
+ '#000080', # Navy(Blue)
+ '#dc143c', # Crimson
+ '#800080', # Purple
+
+ '#daa520', # Goldenrod
+ '#40e0d0', # Turquoise
+ '#00bfff', # DeepSkyBlue
+ '#c0c0c0', # Silver
+ ];
+
+
+ def __init__(self, sId, oData, oDisp):
+ self._sId = sId;
+ self._oData = oData;
+ self._oDisp = oDisp;
+ # Graph output dimensions.
+ self._cxGraph = 1024;
+ self._cyGraph = 448;
+ self._cDpiGraph = 96;
+ # Other graph attributes
+ self._sTitle = None;
+ self._cPtFont = 8;
+
+ def headerContent(self):
+ """
+ Returns content that goes into the HTML header.
+ """
+ return '';
+
+ def renderGraph(self):
+ """
+ Renders the graph.
+ Returning HTML.
+ """
+ return '<p>renderGraph needs to be overridden by the child class!</p>';
+
+ def setTitle(self, sTitle):
+ """ Sets the graph title. """
+ self._sTitle = sTitle;
+ return True;
+
+ def setWidth(self, cx):
+ """ Sets the graph width. """
+ self._cxGraph = cx;
+ return True;
+
+ def setHeight(self, cy):
+ """ Sets the graph height. """
+ self._cyGraph = cy;
+ return True;
+
+ def setDpi(self, cDotsPerInch):
+ """ Sets the graph DPI. """
+ self._cDpiGraph = cDotsPerInch;
+ return True;
+
+ def setFontSize(self, cPtFont):
+ """ Sets the default font size. """
+ self._cPtFont = cPtFont;
+ return True;
+
+
+ @staticmethod
+ def calcSeriesColor(iSeries):
+ """ Returns a #rrggbb color code for the given series. """
+ return WuiHlpGraphBase.kasColors[iSeries % len(WuiHlpGraphBase.kasColors)];
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphgooglechart.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphgooglechart.py
new file mode 100755
index 00000000..b6861603
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphgooglechart.py
@@ -0,0 +1,376 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpgraphgooglechart.py $
+
+"""
+Test Manager Web-UI - Graph Helpers - Implemented using Google Charts.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from common import utils, webutils;
+from testmanager.webui.wuihlpgraphbase import WuiHlpGraphBase;
+
+
+#*******************************************************************************
+#* Global Variables *
+#*******************************************************************************
+g_cGraphs = 0;
+
+class WuiHlpGraphGoogleChartsBase(WuiHlpGraphBase):
+ """ Base class for the Google Charts graphs. """
+ pass; # pylint: disable=unnecessary-pass
+
+
+class WuiHlpBarGraph(WuiHlpGraphGoogleChartsBase):
+ """
+ Bar graph.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ WuiHlpGraphGoogleChartsBase.__init__(self, sId, oData, oDisp);
+ self.fpMax = None;
+ self.fpMin = 0.0;
+ self.fYInverted = False;
+
+ def setRangeMax(self, fpMax):
+ """ Sets the max range."""
+ self.fpMax = float(fpMax);
+ return None;
+
+ def invertYDirection(self):
+ """ Inverts the direction of the Y-axis direction. """
+ self.fYInverted = True;
+ return None;
+
+ def renderGraph(self):
+ aoTable = self._oData.aoTable # type: WuiHlpGraphDataTable
+
+ # Seems material (google.charts.Bar) cannot change the direction on the Y-axis,
+ # so we cannot get bars growing downwards from the top like we want for the
+ # reports. The classic charts OTOH cannot put X-axis labels on the top, but
+ # we just drop them all together instead, saving a little space.
+ fUseMaterial = False;
+
+ # Unique on load function.
+ global g_cGraphs;
+ iGraph = g_cGraphs;
+ g_cGraphs += 1;
+
+ sHtml = '<div id="%s">\n' % ( self._sId, );
+ sHtml += '<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>\n' \
+ '<script type="text/javascript">\n' \
+ 'google.charts.load("current", { packages: ["corechart", "bar"] });\n' \
+ 'google.setOnLoadCallback(tmDrawBarGraph%u);\n' \
+ 'function tmDrawBarGraph%u()\n' \
+ '{\n' \
+ ' var oGraph;\n' \
+ ' var dGraphOptions = \n' \
+ ' {\n' \
+ ' "title": "%s",\n' \
+ ' "hAxis": {\n' \
+ ' "title": "%s",\n' \
+ ' },\n' \
+ ' "vAxis": {\n' \
+ ' "direction": %s,\n' \
+ ' },\n' \
+ % ( iGraph,
+ iGraph,
+ webutils.escapeAttrJavaScriptStringDQ(self._sTitle) if self._sTitle is not None else '',
+ webutils.escapeAttrJavaScriptStringDQ(aoTable[0].sName) if aoTable and aoTable[0].sName else '',
+ '-1' if self.fYInverted else '1',
+ );
+ if fUseMaterial and self.fYInverted:
+ sHtml += ' "axes": { "x": { 0: { "side": "top" } }, "y": { "0": { "direction": -1, }, }, },\n';
+ sHtml += ' };\n';
+
+ # The data.
+ if self._oData.fHasStringValues and len(aoTable) > 1:
+ sHtml += ' var oData = new google.visualization.DataTable();\n';
+ # Column definitions.
+ sHtml += ' oData.addColumn("string", "%s");\n' \
+ % (webutils.escapeAttrJavaScriptStringDQ(aoTable[0].sName) if aoTable[0].sName else '',);
+ for iValue, oValue in enumerate(aoTable[0].aoValues):
+ oSampleValue = aoTable[1].aoValues[iValue];
+ if utils.isString(oSampleValue):
+ sHtml += ' oData.addColumn("string", "%s");\n' % (webutils.escapeAttrJavaScriptStringDQ(oValue),);
+ else:
+ sHtml += ' oData.addColumn("number", "%s");\n' % (webutils.escapeAttrJavaScriptStringDQ(oValue),);
+ sHtml += ' oData.addColumn({type: "string", role: "annotation"});\n';
+ # The data rows.
+ sHtml += ' oData.addRows([\n';
+ for oRow in aoTable[1:]:
+ if oRow.sName:
+ sRow = ' [ "%s"' % (webutils.escapeAttrJavaScriptStringDQ(oRow.sName),);
+ else:
+ sRow = ' [ null';
+ for iValue, oValue in enumerate(oRow.aoValues):
+ if not utils.isString(oValue):
+ sRow += ', %s' % (oValue,);
+ else:
+ sRow += ', "%s"' % (webutils.escapeAttrJavaScriptStringDQ(oValue),);
+ if oRow.asValues[iValue]:
+ sRow += ', "%s"' % (webutils.escapeAttrJavaScriptStringDQ(oRow.asValues[iValue]),);
+ else:
+ sRow += ', null';
+ sHtml += sRow + '],\n';
+ sHtml += ' ]);\n';
+ else:
+ sHtml += ' var oData = google.visualization.arrayToDataTable([\n';
+ for oRow in aoTable:
+ sRow = ' [ "%s"' % (webutils.escapeAttrJavaScriptStringDQ(oRow.sName),);
+ for oValue in oRow.aoValues:
+ if utils.isString(oValue):
+ sRow += ', "%s"' % (webutils.escapeAttrJavaScriptStringDQ(oValue),);
+ else:
+ sRow += ', %s' % (oValue,);
+ sHtml += sRow + '],\n';
+ sHtml += ' ]);\n';
+
+ # Create and draw.
+ if not fUseMaterial:
+ sHtml += ' oGraph = new google.visualization.ColumnChart(document.getElementById("%s"));\n' \
+ ' oGraph.draw(oData, dGraphOptions);\n' \
+ % ( self._sId, );
+ else:
+ sHtml += ' oGraph = new google.charts.Bar(document.getElementById("%s"));\n' \
+ ' oGraph.draw(oData, google.charts.Bar.convertOptions(dGraphOptions));\n' \
+ % ( self._sId, );
+
+ # clean and return.
+ sHtml += ' oData = null;\n' \
+ ' return true;\n' \
+ '};\n';
+
+ sHtml += '</script>\n' \
+ '</div>\n';
+ return sHtml;
+
+
+class WuiHlpLineGraph(WuiHlpGraphGoogleChartsBase):
+ """
+ Line graph.
+ """
+
+ ## @todo implement error bars.
+ kfNoErrorBarsSupport = True;
+
+ def __init__(self, sId, oData, oDisp = None, fErrorBarY = False):
+ # oData must be a WuiHlpGraphDataTableEx like object.
+ WuiHlpGraphGoogleChartsBase.__init__(self, sId, oData, oDisp);
+ self._cMaxErrorBars = 12;
+ self._fErrorBarY = fErrorBarY;
+
+ def setErrorBarY(self, fEnable):
+ """ Enables or Disables error bars, making this work like a line graph. """
+ self._fErrorBarY = fEnable;
+ return True;
+
+ def renderGraph(self): # pylint: disable=too-many-locals
+ fSlideFilter = True;
+
+ # Tooltips?
+ cTooltips = 0;
+ for oSeries in self._oData.aoSeries:
+ cTooltips += oSeries.asHtmlTooltips is not None;
+
+ # Unique on load function.
+ global g_cGraphs;
+ iGraph = g_cGraphs;
+ g_cGraphs += 1;
+
+ sHtml = '<div id="%s">\n' % ( self._sId, );
+ if fSlideFilter:
+ sHtml += ' <table><tr><td><div id="%s_graph"/></td></tr><tr><td><div id="%s_filter"/></td></tr></table>\n' \
+ % ( self._sId, self._sId, );
+
+ sHtml += '<script type="text/javascript" src="https://www.google.com/jsapi"></script>\n' \
+ '<script type="text/javascript">\n' \
+ 'google.load("visualization", "1.0", { packages: ["corechart"%s] });\n' \
+ 'google.setOnLoadCallback(tmDrawLineGraph%u);\n' \
+ 'function tmDrawLineGraph%u()\n' \
+ '{\n' \
+ ' var fnResize;\n' \
+ ' var fnRedraw;\n' \
+ ' var idRedrawTimer = null;\n' \
+ ' var cxCur = getElementWidthById("%s") - 20;\n' \
+ ' var oGraph;\n' \
+ ' var oData = new google.visualization.DataTable();\n' \
+ ' var fpXYRatio = %u / %u;\n' \
+ ' var dGraphOptions = \n' \
+ ' {\n' \
+ ' "title": "%s",\n' \
+ ' "width": cxCur,\n' \
+ ' "height": Math.round(cxCur / fpXYRatio),\n' \
+ ' "pointSize": 2,\n' \
+ ' "fontSize": %u,\n' \
+ ' "hAxis": { "title": "%s", "minorGridlines": { count: 5 }},\n' \
+ ' "vAxis": { "title": "%s", "minorGridlines": { count: 5 }},\n' \
+ ' "theme": "maximized",\n' \
+ ' "tooltip": { "isHtml": %s }\n' \
+ ' };\n' \
+ % ( ', "controls"' if fSlideFilter else '',
+ iGraph,
+ iGraph,
+ self._sId,
+ self._cxGraph, self._cyGraph,
+ self._sTitle if self._sTitle is not None else '',
+ self._cPtFont * self._cDpiGraph / 72, # fudge
+ self._oData.sXUnit if self._oData.sXUnit else '',
+ self._oData.sYUnit if self._oData.sYUnit else '',
+ 'true' if cTooltips > 0 else 'false',
+ );
+ if fSlideFilter:
+ sHtml += ' var oDashboard = new google.visualization.Dashboard(document.getElementById("%s"));\n' \
+ ' var oSlide = new google.visualization.ControlWrapper({\n' \
+ ' "controlType": "NumberRangeFilter",\n' \
+ ' "containerId": "%s_filter",\n' \
+ ' "options": {\n' \
+ ' "filterColumnIndex": 0,\n' \
+ ' "ui": { "width": getElementWidthById("%s") / 2 }, \n' \
+ ' }\n' \
+ ' });\n' \
+ % ( self._sId,
+ self._sId,
+ self._sId,);
+
+ # Data variables.
+ for iSeries, oSeries in enumerate(self._oData.aoSeries):
+ sHtml += ' var aSeries%u = [\n' % (iSeries,);
+ if oSeries.asHtmlTooltips is None:
+ sHtml += '[%s,%s]' % ( oSeries.aoXValues[0], oSeries.aoYValues[0],);
+ for i in range(1, len(oSeries.aoXValues)):
+ if (i & 16) == 0: sHtml += '\n';
+ sHtml += ',[%s,%s]' % ( oSeries.aoXValues[i], oSeries.aoYValues[i], );
+ else:
+ sHtml += '[%s,%s,"%s"]' \
+ % ( oSeries.aoXValues[0], oSeries.aoYValues[0],
+ webutils.escapeAttrJavaScriptStringDQ(oSeries.asHtmlTooltips[0]),);
+ for i in range(1, len(oSeries.aoXValues)):
+ if (i & 16) == 0: sHtml += '\n';
+ sHtml += ',[%s,%s,"%s"]' \
+ % ( oSeries.aoXValues[i], oSeries.aoYValues[i],
+ webutils.escapeAttrJavaScriptStringDQ(oSeries.asHtmlTooltips[i]),);
+
+ sHtml += '];\n'
+
+ sHtml += ' oData.addColumn("number", "%s");\n' % (self._oData.sXUnit if self._oData.sXUnit else '',);
+ cVColumns = 0;
+ for oSeries in self._oData.aoSeries:
+ sHtml += ' oData.addColumn("number", "%s");\n' % (oSeries.sName,);
+ if oSeries.asHtmlTooltips:
+ sHtml += ' oData.addColumn({"type": "string", "role": "tooltip", "p": {"html": true}});\n';
+ cVColumns += 1;
+ cVColumns += 1;
+ sHtml += 'var i;\n'
+
+ cVColumsDone = 0;
+ for iSeries, oSeries in enumerate(self._oData.aoSeries):
+ sVar = 'aSeries%u' % (iSeries,);
+ sHtml += ' for (i = 0; i < %s.length; i++)\n' \
+ ' {\n' \
+ ' oData.addRow([%s[i][0]%s,%s[i][1]%s%s]);\n' \
+ % ( sVar,
+ sVar,
+ ',null' * cVColumsDone,
+ sVar,
+ '' if oSeries.asHtmlTooltips is None else ',%s[i][2]' % (sVar,),
+ ',null' * (cVColumns - cVColumsDone - 1 - (oSeries.asHtmlTooltips is not None)),
+ );
+ sHtml += ' }\n' \
+ ' %s = null\n' \
+ % (sVar,);
+ cVColumsDone += 1 + (oSeries.asHtmlTooltips is not None);
+
+ # Create and draw.
+ if fSlideFilter:
+ sHtml += ' oGraph = new google.visualization.ChartWrapper({\n' \
+ ' "chartType": "LineChart",\n' \
+ ' "containerId": "%s_graph",\n' \
+ ' "options": dGraphOptions\n' \
+ ' });\n' \
+ ' oDashboard.bind(oSlide, oGraph);\n' \
+ ' oDashboard.draw(oData);\n' \
+ % ( self._sId, );
+ else:
+ sHtml += ' oGraph = new google.visualization.LineChart(document.getElementById("%s"));\n' \
+ ' oGraph.draw(oData, dGraphOptions);\n' \
+ % ( self._sId, );
+
+ # Register a resize handler for redrawing the graph, using a timer to delay it.
+ sHtml += ' fnRedraw = function() {\n' \
+ ' var cxNew = getElementWidthById("%s") - 6;\n' \
+ ' if (Math.abs(cxNew - cxCur) > 8)\n' \
+ ' {\n' \
+ ' cxCur = cxNew;\n' \
+ ' dGraphOptions["width"] = cxNew;\n' \
+ ' dGraphOptions["height"] = Math.round(cxNew / fpXYRatio);\n' \
+ ' oGraph.draw(oData, dGraphOptions);\n' \
+ ' }\n' \
+ ' clearTimeout(idRedrawTimer);\n' \
+ ' idRedrawTimer = null;\n' \
+ ' return true;\n' \
+ ' };\n' \
+ ' fnResize = function() {\n' \
+ ' if (idRedrawTimer != null) { clearTimeout(idRedrawTimer); } \n' \
+ ' idRedrawTimer = setTimeout(fnRedraw, 512);\n' \
+ ' return true;\n' \
+ ' };\n' \
+ ' if (window.attachEvent)\n' \
+ ' { window.attachEvent("onresize", fnResize); }\n' \
+ ' else if (window.addEventListener)\n' \
+ ' { window.addEventListener("resize", fnResize, true); }\n' \
+ % ( self._sId, );
+
+ # clean up what the callbacks don't need.
+ sHtml += ' oData = null;\n' \
+ ' aaaSeries = null;\n';
+
+ # done;
+ sHtml += ' return true;\n' \
+ '};\n';
+
+ sHtml += '</script>\n' \
+ '</div>\n';
+ return sHtml;
+
+
+class WuiHlpLineGraphErrorbarY(WuiHlpLineGraph):
+ """
+ Line graph with an errorbar for the Y axis.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ WuiHlpLineGraph.__init__(self, sId, oData, fErrorBarY = True);
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphmatplotlib.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphmatplotlib.py
new file mode 100755
index 00000000..cb5cf893
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphmatplotlib.py
@@ -0,0 +1,341 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpgraphmatplotlib.py $
+
+"""
+Test Manager Web-UI - Graph Helpers - Implemented using matplotlib.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python Import and extensions installed on the system.
+import re;
+import sys;
+if sys.version_info[0] >= 3:
+ from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+else:
+ from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+
+import matplotlib; # pylint: disable=import-error
+matplotlib.use('Agg'); # Force backend.
+import matplotlib.pyplot; # pylint: disable=import-error
+from numpy import arange as numpy_arange; # pylint: disable=no-name-in-module,import-error,wrong-import-order
+
+# Validation Kit imports.
+from testmanager.webui.wuihlpgraphbase import WuiHlpGraphBase;
+
+
+class WuiHlpGraphMatplotlibBase(WuiHlpGraphBase):
+ """ Base class for the matplotlib graphs. """
+
+ def __init__(self, sId, oData, oDisp):
+ WuiHlpGraphBase.__init__(self, sId, oData, oDisp);
+ self._fXkcdStyle = True;
+
+ def setXkcdStyle(self, fEnabled = True):
+ """ Enables xkcd style graphs for implementations that supports it. """
+ self._fXkcdStyle = fEnabled;
+ return True;
+
+ def _createFigure(self):
+ """
+ Wrapper around matplotlib.pyplot.figure that feeds the figure the
+ basic graph configuration.
+ """
+ if self._fXkcdStyle and matplotlib.__version__ > '1.2.9':
+ matplotlib.pyplot.xkcd(); # pylint: disable=no-member
+ matplotlib.rcParams.update({'font.size': self._cPtFont});
+
+ oFigure = matplotlib.pyplot.figure(figsize = (float(self._cxGraph) / self._cDpiGraph,
+ float(self._cyGraph) / self._cDpiGraph),
+ dpi = self._cDpiGraph);
+ return oFigure;
+
+ def _produceSvg(self, oFigure, fTightLayout = True):
+ """ Creates an SVG string from the given figure. """
+ oOutput = StringIO();
+ if fTightLayout:
+ oFigure.tight_layout();
+ oFigure.savefig(oOutput, format = 'svg');
+
+ if self._oDisp and self._oDisp.isBrowserGecko('20100101'):
+ # This browser will stretch images to fit if no size or width is given.
+ sSubstitute = r'\1 \3 reserveAspectRatio="xMidYMin meet"';
+ else:
+ # Chrome and IE likes to have the sizes as well as the viewBox.
+ sSubstitute = r'\1 \3 reserveAspectRatio="xMidYMin meet" \2 \4';
+ return re.sub(r'(<svg) (height="\d+pt") (version="\d+.\d+" viewBox="\d+ \d+ \d+ \d+") (width="\d+pt")',
+ sSubstitute,
+ oOutput.getvalue().decode('utf8'),
+ count = 1);
+
+class WuiHlpBarGraph(WuiHlpGraphMatplotlibBase):
+ """
+ Bar graph.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
+ self.fpMax = None;
+ self.fpMin = 0.0;
+ self.cxBarWidth = None;
+
+ def setRangeMax(self, fpMax):
+ """ Sets the max range."""
+ self.fpMax = float(fpMax);
+ return None;
+
+ def invertYDirection(self):
+ """ Inverts the direction of the Y-axis direction. """
+ ## @todo self.fYInverted = True;
+ return None;
+
+ def renderGraph(self): # pylint: disable=too-many-locals
+ aoTable = self._oData.aoTable;
+
+ #
+ # Extract/structure the required data.
+ #
+ aoSeries = [];
+ for j in range(len(aoTable[1].aoValues)):
+ aoSeries.append([]);
+ asNames = [];
+ oXRange = numpy_arange(self._oData.getGroupCount());
+ fpMin = self.fpMin;
+ fpMax = self.fpMax;
+ if self.fpMax is None:
+ fpMax = float(aoTable[1].aoValues[0]);
+
+ for i in range(1, len(aoTable)):
+ asNames.append(aoTable[i].sName);
+ for j, oValue in enumerate(aoTable[i].aoValues):
+ fpValue = float(oValue);
+ aoSeries[j].append(fpValue);
+ if fpValue < fpMin:
+ fpMin = fpValue;
+ if fpValue > fpMax:
+ fpMax = fpValue;
+
+ fpMid = fpMin + (fpMax - fpMin) / 2.0;
+
+ if self.cxBarWidth is None:
+ self.cxBarWidth = 1.0 / (len(aoTable[0].asValues) + 1.1);
+
+ # Render the PNG.
+ oFigure = self._createFigure();
+ oSubPlot = oFigure.add_subplot(1, 1, 1);
+
+ aoBars = [];
+ for i, _ in enumerate(aoSeries):
+ sColor = self.calcSeriesColor(i);
+ aoBars.append(oSubPlot.bar(oXRange + self.cxBarWidth * i,
+ aoSeries[i],
+ self.cxBarWidth,
+ color = sColor,
+ align = 'edge'));
+
+ #oSubPlot.set_title('Title')
+ #oSubPlot.set_xlabel('X-axis')
+ #oSubPlot.set_xticks(oXRange + self.cxBarWidth);
+ oSubPlot.set_xticks(oXRange);
+ oLegend = oSubPlot.legend(aoTable[0].asValues, loc = 'best', fancybox = True);
+ oLegend.get_frame().set_alpha(0.5);
+ oSubPlot.set_xticklabels(asNames, ha = "left");
+ #oSubPlot.set_ylabel('Y-axis')
+ oSubPlot.set_yticks(numpy_arange(fpMin, fpMax + (fpMax - fpMin) / 10 * 0, fpMax / 10));
+ oSubPlot.grid(True);
+ fpPadding = (fpMax - fpMin) * 0.02;
+ for i, _ in enumerate(aoBars):
+ aoRects = aoBars[i]
+ for j, _ in enumerate(aoRects):
+ oRect = aoRects[j];
+ fpValue = float(aoTable[j + 1].aoValues[i]);
+ if fpValue <= fpMid:
+ oSubPlot.text(oRect.get_x() + oRect.get_width() / 2.0,
+ oRect.get_height() + fpPadding,
+ aoTable[j + 1].asValues[i],
+ ha = 'center', va = 'bottom', rotation = 'vertical', alpha = 0.6, fontsize = 'small');
+ else:
+ oSubPlot.text(oRect.get_x() + oRect.get_width() / 2.0,
+ oRect.get_height() - fpPadding,
+ aoTable[j + 1].asValues[i],
+ ha = 'center', va = 'top', rotation = 'vertical', alpha = 0.6, fontsize = 'small');
+
+ return self._produceSvg(oFigure);
+
+
+
+
+class WuiHlpLineGraph(WuiHlpGraphMatplotlibBase):
+ """
+ Line graph.
+ """
+
+ def __init__(self, sId, oData, oDisp = None, fErrorBarY = False):
+ # oData must be a WuiHlpGraphDataTableEx like object.
+ WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
+ self._cMaxErrorBars = 12;
+ self._fErrorBarY = fErrorBarY;
+
+ def setErrorBarY(self, fEnable):
+ """ Enables or Disables error bars, making this work like a line graph. """
+ self._fErrorBarY = fEnable;
+ return True;
+
+ def renderGraph(self): # pylint: disable=too-many-locals
+ aoSeries = self._oData.aoSeries;
+
+ oFigure = self._createFigure();
+ oSubPlot = oFigure.add_subplot(1, 1, 1);
+ if self._oData.sYUnit is not None:
+ oSubPlot.set_ylabel(self._oData.sYUnit);
+ if self._oData.sXUnit is not None:
+ oSubPlot.set_xlabel(self._oData.sXUnit);
+
+ cSeriesNames = 0;
+ cYMin = 1000;
+ cYMax = 0;
+ for iSeries, oSeries in enumerate(aoSeries):
+ sColor = self.calcSeriesColor(iSeries);
+ cYMin = min(cYMin, min(oSeries.aoYValues));
+ cYMax = max(cYMax, max(oSeries.aoYValues));
+ if not self._fErrorBarY:
+ oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues, color = sColor);
+ elif len(oSeries.aoXValues) > self._cMaxErrorBars:
+ if matplotlib.__version__ < '1.3.0':
+ oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues, color = sColor);
+ else:
+ oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues,
+ yerr = [oSeries.aoYErrorBarBelow, oSeries.aoYErrorBarAbove],
+ errorevery = len(oSeries.aoXValues) / self._cMaxErrorBars,
+ color = sColor );
+ else:
+ oSubPlot.errorbar(oSeries.aoXValues, oSeries.aoYValues,
+ yerr = [oSeries.aoYErrorBarBelow, oSeries.aoYErrorBarAbove],
+ color = sColor);
+ cSeriesNames += oSeries.sName is not None;
+
+ if cYMin != 0 or cYMax != 0:
+ oSubPlot.set_ylim(bottom = 0);
+
+ if cSeriesNames > 0:
+ oLegend = oSubPlot.legend([oSeries.sName for oSeries in aoSeries], loc = 'best', fancybox = True);
+ oLegend.get_frame().set_alpha(0.5);
+
+ if self._sTitle is not None:
+ oSubPlot.set_title(self._sTitle);
+
+ if self._cxGraph >= 256:
+ oSubPlot.minorticks_on();
+ oSubPlot.grid(True, 'major', axis = 'both');
+ oSubPlot.grid(True, 'both', axis = 'x');
+
+ if True: # pylint: disable=using-constant-test
+ # oSubPlot.axis('off');
+ #oSubPlot.grid(True, 'major', axis = 'none');
+ #oSubPlot.grid(True, 'both', axis = 'none');
+ matplotlib.pyplot.setp(oSubPlot, xticks = [], yticks = []);
+
+ return self._produceSvg(oFigure);
+
+
+class WuiHlpLineGraphErrorbarY(WuiHlpLineGraph):
+ """
+ Line graph with an errorbar for the Y axis.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ WuiHlpLineGraph.__init__(self, sId, oData, fErrorBarY = True);
+
+
+class WuiHlpMiniSuccessRateGraph(WuiHlpGraphMatplotlibBase):
+ """
+ Mini rate graph.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ """
+ oData must be a WuiHlpGraphDataTableEx like object, but only aoSeries,
+ aoSeries[].aoXValues, and aoSeries[].aoYValues will be used. The
+ values are expected to be a percentage, i.e. values between 0 and 100.
+ """
+ WuiHlpGraphMatplotlibBase.__init__(self, sId, oData, oDisp);
+ self.setFontSize(6);
+
+ def renderGraph(self): # pylint: disable=too-many-locals
+ assert len(self._oData.aoSeries) == 1;
+ oSeries = self._oData.aoSeries[0];
+
+ # hacking
+ #self.setWidth(512);
+ #self.setHeight(128);
+ # end
+
+ oFigure = self._createFigure();
+ from mpl_toolkits.axes_grid.axislines import SubplotZero; # pylint: disable=import-error
+ oAxis = SubplotZero(oFigure, 111);
+ oFigure.add_subplot(oAxis);
+
+ # Disable all the normal axis.
+ oAxis.axis['right'].set_visible(False)
+ oAxis.axis['top'].set_visible(False)
+ oAxis.axis['bottom'].set_visible(False)
+ oAxis.axis['left'].set_visible(False)
+
+ # Use the zero axis instead.
+ oAxis.axis['yzero'].set_axisline_style('-|>');
+ oAxis.axis['yzero'].set_visible(True);
+ oAxis.axis['xzero'].set_axisline_style('-|>');
+ oAxis.axis['xzero'].set_visible(True);
+
+ if oSeries.aoYValues[-1] == 100:
+ sColor = 'green';
+ elif oSeries.aoYValues[-1] > 75:
+ sColor = 'yellow';
+ else:
+ sColor = 'red';
+ oAxis.plot(oSeries.aoXValues, oSeries.aoYValues, '.-', color = sColor, linewidth = 3);
+ oAxis.fill_between(oSeries.aoXValues, oSeries.aoYValues, facecolor = sColor, alpha = 0.5)
+
+ oAxis.set_xlim(left = -0.01);
+ oAxis.set_xticklabels([]);
+ oAxis.set_xmargin(1);
+
+ oAxis.set_ylim(bottom = 0, top = 100);
+ oAxis.set_yticks([0, 50, 100]);
+ oAxis.set_ylabel('%');
+ #oAxis.set_yticklabels([]);
+ oAxis.set_yticklabels(['', '%', '']);
+
+ return self._produceSvg(oFigure, False);
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphsimple.py b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphsimple.py
new file mode 100755
index 00000000..9be0b565
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuihlpgraphsimple.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+# $Id: wuihlpgraphsimple.py $
+
+"""
+Test Manager Web-UI - Graph Helpers - Simple/Stub Implementation.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from common.webutils import escapeAttr, escapeElem;
+from testmanager.webui.wuihlpgraphbase import WuiHlpGraphBase;
+
+
+
+class WuiHlpBarGraph(WuiHlpGraphBase):
+ """
+ Bar graph.
+ """
+
+ def __init__(self, sId, oData, oDisp = None):
+ WuiHlpGraphBase.__init__(self, sId, oData, oDisp);
+ self.cxMaxBar = 480;
+ self.fpMax = None;
+ self.fpMin = 0.0;
+
+ def setRangeMax(self, fpMax):
+ """ Sets the max range."""
+ self.fpMax = float(fpMax);
+ return None;
+
+ def invertYDirection(self):
+ """ Not supported. """
+ return None;
+
+ def renderGraph(self):
+ aoTable = self._oData.aoTable;
+ sReport = '<div class="tmbargraph">\n';
+
+ # Figure the range.
+ fpMin = self.fpMin;
+ fpMax = self.fpMax;
+ if self.fpMax is None:
+ fpMax = float(aoTable[1].aoValues[0]);
+ for i in range(1, len(aoTable)):
+ for oValue in aoTable[i].aoValues:
+ fpValue = float(oValue);
+ if fpValue < fpMin:
+ fpMin = fpValue;
+ if fpValue > fpMax:
+ fpMax = fpValue;
+ assert fpMin >= 0;
+
+ # Format the data.
+ sReport += '<table class="tmbargraphl1" border="1" id="%s">\n' % (escapeAttr(self._sId),);
+ for i in range(1, len(aoTable)):
+ oRow = aoTable[i];
+ sReport += ' <tr>\n' \
+ ' <td>%s</td>\n' \
+ ' <td height="100%%" width="%spx">\n' \
+ ' <table class="tmbargraphl2" height="100%%" width="100%%" ' \
+ 'border="0" cellspacing="0" cellpadding="0">\n' \
+ % (escapeElem(oRow.sName), escapeAttr(str(self.cxMaxBar + 2)));
+ for j, oValue in enumerate(oRow.aoValues):
+ cPct = int(float(oValue) * 100 / fpMax);
+ cxBar = int(float(oValue) * self.cxMaxBar / fpMax);
+ sValue = escapeElem(oRow.asValues[j]);
+ sColor = self.kasColors[j % len(self.kasColors)];
+ sInvColor = 'white';
+ if sColor[0] == '#' and len(sColor) == 7:
+ sInvColor = '#%06x' % (~int(sColor[1:],16) & 0xffffff,);
+
+ sReport += ' <tr><td>\n' \
+ ' <table class="tmbargraphl3" height="100%%" border="0" cellspacing="0" cellpadding="0">\n' \
+ ' <tr>\n';
+ if cPct >= 99:
+ sReport += ' <td width="%spx" nowrap bgcolor="%s" align="right" style="color:%s;">' \
+ '%s&nbsp;</td>\n' \
+ % (cxBar, sColor, sInvColor, sValue);
+ elif cPct < 1:
+ sReport += ' <td width="%spx" nowrap style="color:%s;">%s</td>\n' \
+ % (self.cxMaxBar - cxBar, sColor, sValue);
+ elif cPct >= 50:
+ sReport += ' <td width="%spx" nowrap bgcolor="%s" align="right" style="color:%s;">' \
+ '%s&nbsp;</td>\n' \
+ ' <td width="%spx" nowrap><div>&nbsp;</div></td>\n' \
+ % (cxBar, sColor, sInvColor, sValue, self.cxMaxBar - cxBar);
+ else:
+ sReport += ' <td width="%spx" nowrap bgcolor="%s"></td>\n' \
+ ' <td width="%spx" nowrap>&nbsp;%s</td>\n' \
+ % (cxBar, sColor, self.cxMaxBar - cxBar, sValue);
+ sReport += ' </tr>\n' \
+ ' </table>\n' \
+ ' </td></tr>\n'
+ sReport += ' </table>\n' \
+ ' </td>\n' \
+ ' </tr>\n';
+ if i + 1 < len(aoTable) and len(oRow.aoValues) > 1:
+ sReport += ' <tr></tr>\n'
+
+ sReport += '</table>\n';
+
+ sReport += '<div class="tmgraphlegend">\n' \
+ ' <p>Legend:\n';
+ for j, sValue in enumerate(aoTable[0].asValues):
+ sColor = self.kasColors[j % len(self.kasColors)];
+ sReport += ' <font color="%s">&#x25A0; %s</font>\n' % (sColor, escapeElem(sValue),);
+ sReport += ' </p>\n' \
+ '</div>\n';
+
+ sReport += '</div>\n';
+ return sReport;
+
+
+
+
+class WuiHlpLineGraph(WuiHlpGraphBase):
+ """
+ Line graph.
+ """
+
+ def __init__(self, sId, oData, oDisp):
+ WuiHlpGraphBase.__init__(self, sId, oData, oDisp);
+
+
+class WuiHlpLineGraphErrorbarY(WuiHlpLineGraph):
+ """
+ Line graph with an errorbar for the Y axis.
+ """
+
+ pass; # pylint: disable=unnecessary-pass
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py b/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py
new file mode 100755
index 00000000..45a847ed
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py
@@ -0,0 +1,251 @@
+# -*- coding: utf-8 -*-
+# $Id: wuilogviewer.py $
+
+"""
+Test Manager WUI - Log viewer
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from common import webutils;
+from testmanager.core.testset import TestSetData;
+from testmanager.webui.wuicontentbase import WuiContentBase, WuiTmLink;
+from testmanager.webui.wuimain import WuiMain;
+
+
+class WuiLogViewer(WuiContentBase):
+ """Log viewer."""
+
+ def __init__(self, oTestSet, oLogFile, cbChunk, iChunk, aoTimestamps, oDisp = None, fnDPrint = None):
+ WuiContentBase.__init__(self, oDisp = oDisp, fnDPrint = fnDPrint);
+ self._oTestSet = oTestSet;
+ self._oLogFile = oLogFile;
+ self._cbChunk = cbChunk;
+ self._iChunk = iChunk;
+ self._aoTimestamps = aoTimestamps;
+
+ def _generateNavigation(self, cbFile):
+ """Generate the HTML for the log navigation."""
+
+ dParams = {
+ WuiMain.ksParamAction: WuiMain.ksActionViewLog,
+ WuiMain.ksParamLogSetId: self._oTestSet.idTestSet,
+ WuiMain.ksParamLogFileId: self._oLogFile.idTestResultFile,
+ WuiMain.ksParamLogChunkSize: self._cbChunk,
+ WuiMain.ksParamLogChunkNo: self._iChunk,
+ };
+
+ #
+ # The page walker.
+ #
+ dParams2 = dict(dParams);
+ del dParams2[WuiMain.ksParamLogChunkNo];
+ sHrefFmt = '<a href="?%s&%s=%%s" title="%%s">%%s</a>' \
+ % (webutils.encodeUrlParams(dParams2).replace('%', '%%'), WuiMain.ksParamLogChunkNo,);
+ sHtmlWalker = self.genericPageWalker(self._iChunk, (cbFile + self._cbChunk - 1) // self._cbChunk,
+ sHrefFmt, 11, 0, 'chunk');
+
+ #
+ # The chunk size selector.
+ #
+
+ dParams2 = dict(dParams);
+ del dParams2[WuiMain.ksParamLogChunkSize];
+ sHtmlSize = '<form name="ChunkSizeForm" method="GET">\n' \
+ ' Max <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
+ 'this.options[this.selectedIndex].value;" title="Max items per page">\n' \
+ % ( WuiMain.ksParamLogChunkSize, webutils.encodeUrlParams(dParams2), WuiMain.ksParamLogChunkSize,);
+
+ for cbChunk in [ 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152,
+ 4194304, 8388608, 16777216 ]:
+ sHtmlSize += ' <option value="%d" %s>%d bytes</option>\n' \
+ % (cbChunk, 'selected="selected"' if cbChunk == self._cbChunk else '', cbChunk);
+ sHtmlSize += ' </select> per page\n' \
+ '</form>\n'
+
+ #
+ # Download links.
+ #
+ oRawLink = WuiTmLink('View Raw', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
+ WuiMain.ksParamGetFileSetId: self._oTestSet.idTestSet,
+ WuiMain.ksParamGetFileId: self._oLogFile.idTestResultFile,
+ WuiMain.ksParamGetFileDownloadIt: False,
+ },
+ sTitle = '%u MiB' % ((cbFile + 1048576 - 1) // 1048576,) );
+ oDownloadLink = WuiTmLink('Download Log', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
+ WuiMain.ksParamGetFileSetId: self._oTestSet.idTestSet,
+ WuiMain.ksParamGetFileId: self._oLogFile.idTestResultFile,
+ WuiMain.ksParamGetFileDownloadIt: True,
+ },
+ sTitle = '%u MiB' % ((cbFile + 1048576 - 1) // 1048576,) );
+ oTestSetLink = WuiTmLink('Test Set', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
+ TestSetData.ksParam_idTestSet: self._oTestSet.idTestSet,
+ });
+
+
+ #
+ # Combine the elements and return.
+ #
+ return '<div class="tmlogviewernavi">\n' \
+ ' <table width=100%>\n' \
+ ' <tr>\n' \
+ ' <td width=20%>\n' \
+ ' ' + oTestSetLink.toHtml() + '\n' \
+ ' ' + oRawLink.toHtml() + '\n' \
+ ' ' + oDownloadLink.toHtml() + '\n' \
+ ' </td>\n' \
+ ' <td width=60% align=center>' + sHtmlWalker + '</td>' \
+ ' <td width=20% align=right>' + sHtmlSize + '</td>\n' \
+ ' </tr>\n' \
+ ' </table>\n' \
+ '</div>\n';
+
+ def _displayLog(self, oFile, offFile, cbFile, aoTimestamps):
+ """Displays the current section of the log file."""
+ from testmanager.core import db;
+
+ def prepCurTs():
+ """ Formats the current timestamp. """
+ if iCurTs < len(aoTimestamps):
+ oTsZulu = db.dbTimestampToZuluDatetime(aoTimestamps[iCurTs]);
+ return (oTsZulu.strftime('%H:%M:%S.%f'), oTsZulu.strftime('%H_%M_%S_%f'));
+ return ('~~|~~|~~|~~~~~~', '~~|~~|~~|~~~~~~'); # ASCII chars with high values. Limit hits.
+
+ def isCurLineAtOrAfterCurTs():
+ """ Checks if the current line starts with a timestamp that is after the current one. """
+ if len(sLine) >= 15 \
+ and sLine[2] == ':' \
+ and sLine[5] == ':' \
+ and sLine[8] == '.' \
+ and sLine[14] in '0123456789':
+ if sLine[:15] >= sCurTs and iCurTs < len(aoTimestamps):
+ return True;
+ return False;
+
+ # Figure the end offset.
+ offEnd = offFile + self._cbChunk;
+ offEnd = min(offEnd, cbFile);
+
+ #
+ # Here is an annoying thing, we cannot seek in zip file members. So,
+ # since we have to read from the start, we can just as well count line
+ # numbers while we're at it.
+ #
+ iCurTs = 0;
+ (sCurTs, sCurId) = prepCurTs();
+ offCur = 0;
+ iLine = 0;
+ while True:
+ sLine = oFile.readline().decode('utf-8', 'replace');
+ offLine = offCur;
+ iLine += 1;
+ offCur += len(sLine);
+ if offCur >= offFile or not sLine:
+ break;
+ while isCurLineAtOrAfterCurTs():
+ iCurTs += 1;
+ (sCurTs, sCurId) = prepCurTs();
+
+ #
+ # Got to where we wanted, format the chunk.
+ #
+ asLines = ['\n<div class="tmlog">\n<pre>\n', ];
+ while True:
+ # The timestamp IDs.
+ sPrevTs = '';
+ while isCurLineAtOrAfterCurTs():
+ if sPrevTs != sCurTs:
+ asLines.append('<a id="%s"></a>' % (sCurId,));
+ iCurTs += 1;
+ (sCurTs, sCurId) = prepCurTs();
+
+ # The line.
+ asLines.append('<a id="L%d" href="#L%d">%05d</a><a id="O%d"></a>%s\n' \
+ % (iLine, iLine, iLine, offLine, webutils.escapeElem(sLine.rstrip())));
+
+ # next
+ if offCur >= offEnd:
+ break;
+ sLine = oFile.readline().decode('utf-8', 'replace');
+ offLine = offCur;
+ iLine += 1;
+ offCur += len(sLine);
+ if not sLine:
+ break;
+ asLines.append('<pre/></div>\n');
+ return ''.join(asLines);
+
+
+ def show(self):
+ """Shows the log."""
+
+ if self._oLogFile.sDescription not in [ '', None ]:
+ sTitle = '%s - %s' % (self._oLogFile.sFile, self._oLogFile.sDescription);
+ else:
+ sTitle = '%s' % (self._oLogFile.sFile,);
+
+ #
+ # Open the log file. No universal line endings here.
+ #
+ (oFile, oSizeOrError, _) = self._oTestSet.openFile(self._oLogFile.sFile, 'rb');
+ if oFile is None:
+ return (sTitle, '<p>%s</p>\n' % (webutils.escapeElem(oSizeOrError),),);
+ cbFile = oSizeOrError;
+
+ #
+ # Generate the page.
+ #
+
+ # Start with a focus hack.
+ sHtml = '<div id="tmlogoutdiv" tabindex="0">\n' \
+ '<script lang="text/javascript">\n' \
+ 'document.getElementById(\'tmlogoutdiv\').focus();\n' \
+ '</script>\n';
+
+ sNaviHtml = self._generateNavigation(cbFile);
+ sHtml += sNaviHtml;
+
+ offFile = self._iChunk * self._cbChunk;
+ if offFile < cbFile:
+ sHtml += self._displayLog(oFile, offFile, cbFile, self._aoTimestamps);
+ sHtml += sNaviHtml;
+ else:
+ sHtml += '<p>End Of File</p>';
+
+ return (sTitle, sHtml);
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuimain.py b/src/VBox/ValidationKit/testmanager/webui/wuimain.py
new file mode 100755
index 00000000..3f059775
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuimain.py
@@ -0,0 +1,1344 @@
+# -*- coding: utf-8 -*-
+# $Id: wuimain.py $
+
+"""
+Test Manager Core - WUI - The Main page.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core.base import TMExceptionBase, TMTooManyRows;
+from testmanager.webui.wuibase import WuiDispatcherBase, WuiException;
+from testmanager.webui.wuicontentbase import WuiTmLink;
+from common import webutils, utils;
+
+
+
+class WuiMain(WuiDispatcherBase):
+ """
+ WUI Main page.
+
+ Note! All cylic dependency avoiance stuff goes here in the dispatcher code,
+ not in the action specific code. This keeps the uglyness in one place
+ and reduces load time dependencies in the more critical code path.
+ """
+
+ ## The name of the script.
+ ksScriptName = 'index.py'
+
+ ## @name Actions
+ ## @{
+ ksActionResultsUnGrouped = 'ResultsUnGrouped'
+ ksActionResultsGroupedBySchedGroup = 'ResultsGroupedBySchedGroup'
+ ksActionResultsGroupedByTestGroup = 'ResultsGroupedByTestGroup'
+ ksActionResultsGroupedByBuildRev = 'ResultsGroupedByBuildRev'
+ ksActionResultsGroupedByBuildCat = 'ResultsGroupedByBuildCat'
+ ksActionResultsGroupedByTestBox = 'ResultsGroupedByTestBox'
+ ksActionResultsGroupedByTestCase = 'ResultsGroupedByTestCase'
+ ksActionResultsGroupedByOS = 'ResultsGroupedByOS'
+ ksActionResultsGroupedByArch = 'ResultsGroupedByArch'
+ ksActionTestSetDetails = 'TestSetDetails';
+ ksActionTestResultDetails = ksActionTestSetDetails;
+ ksActionTestSetDetailsFromResult = 'TestSetDetailsFromResult'
+ ksActionTestResultFailureDetails = 'TestResultFailureDetails'
+ ksActionTestResultFailureAdd = 'TestResultFailureAdd'
+ ksActionTestResultFailureAddPost = 'TestResultFailureAddPost'
+ ksActionTestResultFailureEdit = 'TestResultFailureEdit'
+ ksActionTestResultFailureEditPost = 'TestResultFailureEditPost'
+ ksActionTestResultFailureDoRemove = 'TestResultFailureDoRemove'
+ ksActionViewLog = 'ViewLog'
+ ksActionGetFile = 'GetFile'
+ ksActionReportSummary = 'ReportSummary';
+ ksActionReportRate = 'ReportRate';
+ ksActionReportTestCaseFailures = 'ReportTestCaseFailures';
+ ksActionReportTestBoxFailures = 'ReportTestBoxFailures';
+ ksActionReportFailureReasons = 'ReportFailureReasons';
+ ksActionGraphWiz = 'GraphWiz';
+ ksActionVcsHistoryTooltip = 'VcsHistoryTooltip'; ##< Hardcoded in common.js.
+ ## @}
+
+ ## @name Standard report parameters
+ ## @{
+ ksParamReportPeriods = 'cPeriods';
+ ksParamReportPeriodInHours = 'cHoursPerPeriod';
+ ksParamReportSubject = 'sSubject';
+ ksParamReportSubjectIds = 'SubjectIds';
+ ## @}
+
+ ## @name Graph Wizard parameters
+ ## Common parameters: ksParamReportPeriods, ksParamReportPeriodInHours, ksParamReportSubjectIds,
+ ## ksParamReportSubject, ksParamEffectivePeriod, and ksParamEffectiveDate.
+ ## @{
+ ksParamGraphWizTestBoxIds = 'aidTestBoxes';
+ ksParamGraphWizBuildCatIds = 'aidBuildCats';
+ ksParamGraphWizTestCaseIds = 'aidTestCases';
+ ksParamGraphWizSepTestVars = 'fSepTestVars';
+ ksParamGraphWizImpl = 'enmImpl';
+ ksParamGraphWizWidth = 'cx';
+ ksParamGraphWizHeight = 'cy';
+ ksParamGraphWizDpi = 'dpi';
+ ksParamGraphWizFontSize = 'cPtFont';
+ ksParamGraphWizErrorBarY = 'fErrorBarY';
+ ksParamGraphWizMaxErrorBarY = 'cMaxErrorBarY';
+ ksParamGraphWizMaxPerGraph = 'cMaxPerGraph';
+ ksParamGraphWizXkcdStyle = 'fXkcdStyle';
+ ksParamGraphWizTabular = 'fTabular';
+ ksParamGraphWizSrcTestSetId = 'idSrcTestSet';
+ ## @}
+
+ ## @name Graph implementations values for ksParamGraphWizImpl.
+ ## @{
+ ksGraphWizImpl_Default = 'default';
+ ksGraphWizImpl_Matplotlib = 'matplotlib';
+ ksGraphWizImpl_Charts = 'charts';
+ kasGraphWizImplValid = [ ksGraphWizImpl_Default, ksGraphWizImpl_Matplotlib, ksGraphWizImpl_Charts];
+ kaasGraphWizImplCombo = [
+ ( ksGraphWizImpl_Default, 'Default' ),
+ ( ksGraphWizImpl_Matplotlib, 'Matplotlib (server)' ),
+ ( ksGraphWizImpl_Charts, 'Google Charts (client)'),
+ ];
+ ## @}
+
+ ## @name Log Viewer parameters.
+ ## @{
+ ksParamLogSetId = 'LogViewer_idTestSet';
+ ksParamLogFileId = 'LogViewer_idFile';
+ ksParamLogChunkSize = 'LogViewer_cbChunk';
+ ksParamLogChunkNo = 'LogViewer_iChunk';
+ ## @}
+
+ ## @name File getter parameters.
+ ## @{
+ ksParamGetFileSetId = 'GetFile_idTestSet';
+ ksParamGetFileId = 'GetFile_idFile';
+ ksParamGetFileDownloadIt = 'GetFile_fDownloadIt';
+ ## @}
+
+ ## @name VCS history parameters.
+ ## @{
+ ksParamVcsHistoryRepository = 'repo';
+ ksParamVcsHistoryRevision = 'rev';
+ ksParamVcsHistoryEntries = 'cEntries';
+ ## @}
+
+ ## @name Test result listing parameters.
+ ## @{
+ ## If this param is specified, then show only results for this member when results grouped by some parameter.
+ ksParamGroupMemberId = 'GroupMemberId'
+ ## Optional parameter for indicating whether to restrict the listing to failures only.
+ ksParamOnlyFailures = 'OnlyFailures';
+ ## The sheriff parameter for getting failures needing a reason or two assigned to them.
+ ksParamOnlyNeedingReason = 'OnlyNeedingReason';
+ ## Result listing sorting.
+ ksParamTestResultsSortBy = 'enmSortBy'
+ ## @}
+
+ ## Effective time period. one of the first column values in kaoResultPeriods.
+ ksParamEffectivePeriod = 'sEffectivePeriod'
+
+ ## Test result period values.
+ kaoResultPeriods = [
+ ( '1 hour', '1 hour', 1 ),
+ ( '2 hours', '2 hours', 2 ),
+ ( '3 hours', '3 hours', 3 ),
+ ( '6 hours', '6 hours', 6 ),
+ ( '12 hours', '12 hours', 12 ),
+
+ ( '1 day', '1 day', 24 ),
+ ( '2 days', '2 days', 48 ),
+ ( '3 days', '3 days', 72 ),
+
+ ( '1 week', '1 week', 168 ),
+ ( '2 weeks', '2 weeks', 336 ),
+ ( '3 weeks', '3 weeks', 504 ),
+
+ ( '1 month', '1 month', 31 * 24 ), # The approx hour count varies with the start date.
+ ( '2 months', '2 months', (31 + 31) * 24 ), # Using maximum values.
+ ( '3 months', '3 months', (31 + 30 + 31) * 24 ),
+
+ ( '6 months', '6 months', (31 + 31 + 30 + 31 + 30 + 31) * 24 ),
+
+ ( '1 year', '1 year', 365 * 24 ),
+ ];
+ ## The default test result period.
+ ksResultPeriodDefault = '6 hours';
+
+
+
+ def __init__(self, oSrvGlue):
+ WuiDispatcherBase.__init__(self, oSrvGlue, self.ksScriptName);
+ self._sTemplate = 'template.html'
+
+ #
+ # Populate the action dispatcher dictionary.
+ # Lambda is forbidden because of readability, speed and reducing number of imports.
+ #
+ self._dDispatch[self.ksActionResultsUnGrouped] = self._actionResultsUnGrouped;
+ self._dDispatch[self.ksActionResultsGroupedByTestGroup] = self._actionResultsGroupedByTestGroup;
+ self._dDispatch[self.ksActionResultsGroupedByBuildRev] = self._actionResultsGroupedByBuildRev;
+ self._dDispatch[self.ksActionResultsGroupedByBuildCat] = self._actionResultsGroupedByBuildCat;
+ self._dDispatch[self.ksActionResultsGroupedByTestBox] = self._actionResultsGroupedByTestBox;
+ self._dDispatch[self.ksActionResultsGroupedByTestCase] = self._actionResultsGroupedByTestCase;
+ self._dDispatch[self.ksActionResultsGroupedByOS] = self._actionResultsGroupedByOS;
+ self._dDispatch[self.ksActionResultsGroupedByArch] = self._actionResultsGroupedByArch;
+ self._dDispatch[self.ksActionResultsGroupedBySchedGroup] = self._actionResultsGroupedBySchedGroup;
+
+ self._dDispatch[self.ksActionTestSetDetails] = self._actionTestSetDetails;
+ self._dDispatch[self.ksActionTestSetDetailsFromResult] = self._actionTestSetDetailsFromResult;
+
+ self._dDispatch[self.ksActionTestResultFailureAdd] = self._actionTestResultFailureAdd;
+ self._dDispatch[self.ksActionTestResultFailureAddPost] = self._actionTestResultFailureAddPost;
+ self._dDispatch[self.ksActionTestResultFailureDetails] = self._actionTestResultFailureDetails;
+ self._dDispatch[self.ksActionTestResultFailureDoRemove] = self._actionTestResultFailureDoRemove;
+ self._dDispatch[self.ksActionTestResultFailureEdit] = self._actionTestResultFailureEdit;
+ self._dDispatch[self.ksActionTestResultFailureEditPost] = self._actionTestResultFailureEditPost;
+
+ self._dDispatch[self.ksActionViewLog] = self._actionViewLog;
+ self._dDispatch[self.ksActionGetFile] = self._actionGetFile;
+
+ self._dDispatch[self.ksActionReportSummary] = self._actionReportSummary;
+ self._dDispatch[self.ksActionReportRate] = self._actionReportRate;
+ self._dDispatch[self.ksActionReportTestCaseFailures] = self._actionReportTestCaseFailures;
+ self._dDispatch[self.ksActionReportFailureReasons] = self._actionReportFailureReasons;
+ self._dDispatch[self.ksActionGraphWiz] = self._actionGraphWiz;
+
+ self._dDispatch[self.ksActionVcsHistoryTooltip] = self._actionVcsHistoryTooltip;
+
+ # Legacy.
+ self._dDispatch['TestResultDetails'] = self._dDispatch[self.ksActionTestSetDetails];
+
+
+ #
+ # Popupate the menus.
+ #
+
+ # Additional URL parameters keeping for time navigation.
+ sExtraTimeNav = ''
+ dCurParams = oSrvGlue.getParameters()
+ if dCurParams is not None:
+ for sExtraParam in [ self.ksParamItemsPerPage, self.ksParamEffectiveDate, self.ksParamEffectivePeriod, ]:
+ if sExtraParam in dCurParams:
+ sExtraTimeNav += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),)
+
+ # Additional URL parameters for reports
+ sExtraReports = '';
+ if dCurParams is not None:
+ for sExtraParam in [ self.ksParamReportPeriods, self.ksParamReportPeriodInHours, self.ksParamEffectiveDate, ]:
+ if sExtraParam in dCurParams:
+ sExtraReports += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),)
+
+ # Shorthand to keep within margins.
+ sActUrlBase = self._sActionUrlBase;
+ sOnlyFailures = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyFailures: True}), sExtraTimeNav, );
+ sSheriff = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyNeedingReason: True}), sExtraTimeNav, );
+
+ self._aaoMenus = \
+ [
+ [
+ 'Sheriff', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff,
+ [
+ [ 'Grouped by', None ],
+ [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff, False ],
+ [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sSheriff, False ],
+ [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sSheriff, False ],
+ [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sSheriff, False ],
+ [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sSheriff, False ],
+ [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sSheriff, False ],
+ [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sSheriff, False ],
+ [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sSheriff, False ],
+ [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sSheriff, False ],
+ ]
+ ],
+ [
+ 'Reports', sActUrlBase + self.ksActionReportSummary,
+ [
+ [ 'Summary', sActUrlBase + self.ksActionReportSummary + sExtraReports, False ],
+ [ 'Success rate', sActUrlBase + self.ksActionReportRate + sExtraReports, False ],
+ [ 'Test case failures', sActUrlBase + self.ksActionReportTestCaseFailures + sExtraReports, False ],
+ [ 'Testbox failures', sActUrlBase + self.ksActionReportTestBoxFailures + sExtraReports, False ],
+ [ 'Failure reasons', sActUrlBase + self.ksActionReportFailureReasons + sExtraReports, False ],
+ ]
+ ],
+ [
+ 'Test Results', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav,
+ [
+ [ 'Grouped by', None ],
+ [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav, False ],
+ [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sExtraTimeNav, False ],
+ [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sExtraTimeNav, False ],
+ [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sExtraTimeNav, False ],
+ [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sExtraTimeNav, False ],
+ [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sExtraTimeNav, False ],
+ [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sExtraTimeNav, False ],
+ [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sExtraTimeNav, False ],
+ [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sExtraTimeNav, False ],
+ ]
+ ],
+ [
+ 'Test Failures', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures,
+ [
+ [ 'Grouped by', None ],
+ [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures, False ],
+ [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sOnlyFailures, False ],
+ [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sOnlyFailures, False ],
+ [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sOnlyFailures, False ],
+ [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sOnlyFailures, False ],
+ [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sOnlyFailures, False ],
+ [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sOnlyFailures, False ],
+ [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sOnlyFailures, False ],
+ [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sOnlyFailures, False ],
+ ]
+ ],
+ [
+ '> Admin', 'admin.py?' + webutils.encodeUrlParams(self._dDbgParams), []
+ ],
+ ];
+
+
+ #
+ # Overriding parent methods.
+ #
+
+ def _generatePage(self):
+ """Override parent handler in order to change page title."""
+ if self._sPageTitle is not None:
+ self._sPageTitle = 'Test Results - ' + self._sPageTitle
+
+ return WuiDispatcherBase._generatePage(self)
+
+ def _actionDefault(self):
+ """Show the default admin page."""
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ self._sAction = self.ksActionResultsUnGrouped
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _isMenuMatch(self, sMenuUrl, sActionParam):
+ if super(WuiMain, self)._isMenuMatch(sMenuUrl, sActionParam):
+ fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False);
+ if fOnlyNeedingReason:
+ return (sMenuUrl.find(self.ksParamOnlyNeedingReason) > 0);
+ fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False);
+ return (sMenuUrl.find(self.ksParamOnlyFailures) > 0) == fOnlyFailures \
+ and sMenuUrl.find(self.ksParamOnlyNeedingReason) < 0;
+ return False;
+
+
+ #
+ # Navigation bar stuff
+ #
+
+ def _generateSortBySelector(self, dParams, sPreamble, sPostamble):
+ """
+ Generate HTML code for the sort by selector.
+ """
+ from testmanager.core.testresults import TestResultLogic;
+
+ if self.ksParamTestResultsSortBy in dParams:
+ enmResultSortBy = dParams[self.ksParamTestResultsSortBy];
+ del dParams[self.ksParamTestResultsSortBy];
+ else:
+ enmResultSortBy = TestResultLogic.ksResultsSortByRunningAndStart;
+
+ sHtmlSortBy = '<form name="TimeForm" method="GET"> Sort by\n';
+ sHtmlSortBy += sPreamble;
+ sHtmlSortBy += '\n <select name="%s" onchange="window.location=' % (self.ksParamTestResultsSortBy,);
+ sHtmlSortBy += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamTestResultsSortBy)
+ sHtmlSortBy += 'this.options[this.selectedIndex].value;" title="Sorting by">\n'
+
+ fSelected = False;
+ for enmCode, sTitle in TestResultLogic.kaasResultsSortByTitles:
+ if enmCode == enmResultSortBy:
+ fSelected = True;
+ sHtmlSortBy += ' <option value="%s"%s>%s</option>\n' \
+ % (enmCode, ' selected="selected"' if enmCode == enmResultSortBy else '', sTitle,);
+ assert fSelected;
+ sHtmlSortBy += ' </select>\n';
+ sHtmlSortBy += sPostamble;
+ sHtmlSortBy += '\n</form>\n'
+ return sHtmlSortBy;
+
+ def _generateStatusSelector(self, dParams, fOnlyFailures):
+ """
+ Generate HTML code for the status code selector. Currently very simple.
+ """
+ dParams[self.ksParamOnlyFailures] = not fOnlyFailures;
+ return WuiTmLink('Show all results' if fOnlyFailures else 'Only show failed tests', '', dParams,
+ fBracketed = False).toHtml();
+
+ def _generateTimeWalker(self, dParams, tsEffective, sCurPeriod):
+ """
+ Generates HTML code for walking back and forth in time.
+ """
+ # Have to do some math here. :-/
+ if tsEffective is None:
+ self._oDb.execute('SELECT CURRENT_TIMESTAMP - \'' + sCurPeriod + '\'::interval');
+ tsNext = None;
+ tsPrev = self._oDb.fetchOne()[0];
+ else:
+ self._oDb.execute('SELECT %s::TIMESTAMP - \'' + sCurPeriod + '\'::interval,\n'
+ ' %s::TIMESTAMP + \'' + sCurPeriod + '\'::interval',
+ (tsEffective, tsEffective,));
+ tsPrev, tsNext = self._oDb.fetchOne();
+
+ # Forget about page No when changing a period
+ if WuiDispatcherBase.ksParamPageNo in dParams:
+ del dParams[WuiDispatcherBase.ksParamPageNo]
+
+ # Format.
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsPrev);
+ sPrev = '<a href="?%s" title="One period earlier">&lt;&lt;</a>&nbsp;&nbsp;' \
+ % (webutils.encodeUrlParams(dParams),);
+
+ if tsNext is not None:
+ dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsNext);
+ sNext = '&nbsp;&nbsp;<a href="?%s" title="One period later">&gt;&gt;</a>' \
+ % (webutils.encodeUrlParams(dParams),);
+ else:
+ sNext = '&nbsp;&nbsp;&gt;&gt;';
+
+ from testmanager.webui.wuicontentbase import WuiListContentBase; ## @todo move to better place.
+ return WuiListContentBase.generateTimeNavigation('top', self.getParameters(), self.getEffectiveDateParam(),
+ sPrev, sNext, False);
+
+ def _generateResultPeriodSelector(self, dParams, sCurPeriod):
+ """
+ Generate HTML code for result period selector.
+ """
+
+ if self.ksParamEffectivePeriod in dParams:
+ del dParams[self.ksParamEffectivePeriod];
+
+ # Forget about page No when changing a period
+ if WuiDispatcherBase.ksParamPageNo in dParams:
+ del dParams[WuiDispatcherBase.ksParamPageNo]
+
+ sHtmlPeriodSelector = '<form name="PeriodForm" method="GET">\n'
+ sHtmlPeriodSelector += ' Period is\n'
+ sHtmlPeriodSelector += ' <select name="%s" onchange="window.location=' % self.ksParamEffectivePeriod
+ sHtmlPeriodSelector += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamEffectivePeriod)
+ sHtmlPeriodSelector += 'this.options[this.selectedIndex].value;">\n'
+
+ for sPeriodValue, sPeriodCaption, _ in self.kaoResultPeriods:
+ sHtmlPeriodSelector += ' <option value="%s"%s>%s</option>\n' \
+ % (webutils.quoteUrl(sPeriodValue),
+ ' selected="selected"' if sPeriodValue == sCurPeriod else '',
+ sPeriodCaption)
+
+ sHtmlPeriodSelector += ' </select>\n' \
+ '</form>\n'
+
+ return sHtmlPeriodSelector
+
+ def _generateGroupContentSelector(self, aoGroupMembers, iCurrentMember, sAltAction):
+ """
+ Generate HTML code for group content selector.
+ """
+
+ dParams = self.getParameters()
+
+ if self.ksParamGroupMemberId in dParams:
+ del dParams[self.ksParamGroupMemberId]
+
+ if sAltAction is not None:
+ if self.ksParamAction in dParams:
+ del dParams[self.ksParamAction];
+ dParams[self.ksParamAction] = sAltAction;
+
+ sHtmlSelector = '<form name="GroupContentForm" method="GET">\n'
+ sHtmlSelector += ' <select name="%s" onchange="window.location=' % self.ksParamGroupMemberId
+ sHtmlSelector += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamGroupMemberId)
+ sHtmlSelector += 'this.options[this.selectedIndex].value;">\n'
+
+ sHtmlSelector += '<option value="-1">All</option>\n'
+
+ for iGroupMemberId, sGroupMemberName in aoGroupMembers:
+ if iGroupMemberId is not None:
+ sHtmlSelector += ' <option value="%s"%s>%s</option>\n' \
+ % (iGroupMemberId,
+ ' selected="selected"' if iGroupMemberId == iCurrentMember else '',
+ sGroupMemberName)
+
+ sHtmlSelector += ' </select>\n' \
+ '</form>\n'
+
+ return sHtmlSelector
+
+ def _generatePagesSelector(self, dParams, cItems, cItemsPerPage, iPage):
+ """
+ Generate HTML code for pages (1, 2, 3 ... N) selector
+ """
+
+ if WuiDispatcherBase.ksParamPageNo in dParams:
+ del dParams[WuiDispatcherBase.ksParamPageNo]
+
+ sHrefPtr = '<a href="?%s&%s=' % (webutils.encodeUrlParams(dParams).replace('%', '%%'),
+ WuiDispatcherBase.ksParamPageNo)
+ sHrefPtr += '%d">%s</a>'
+
+ cNumOfPages = (cItems + cItemsPerPage - 1) // cItemsPerPage;
+ cPagesToDisplay = 10
+ cPagesRangeStart = iPage - cPagesToDisplay // 2 \
+ if not iPage - cPagesToDisplay / 2 < 0 else 0
+ cPagesRangeEnd = cPagesRangeStart + cPagesToDisplay \
+ if not cPagesRangeStart + cPagesToDisplay > cNumOfPages else cNumOfPages
+ # Adjust pages range
+ if cNumOfPages < cPagesToDisplay:
+ cPagesRangeStart = 0
+ cPagesRangeEnd = cNumOfPages
+
+ # 1 2 3 4...
+ sHtmlPager = '&nbsp;\n'.join(sHrefPtr % (x, str(x + 1)) if x != iPage else str(x + 1)
+ for x in range(cPagesRangeStart, cPagesRangeEnd))
+ if cPagesRangeStart > 0:
+ sHtmlPager = '%s&nbsp; ... &nbsp;\n' % (sHrefPtr % (0, str(1))) + sHtmlPager
+ if cPagesRangeEnd < cNumOfPages:
+ sHtmlPager += ' ... %s\n' % (sHrefPtr % (cNumOfPages, str(cNumOfPages + 1)))
+
+ # Prev/Next (using << >> because &laquo; and &raquo are too tiny).
+ if iPage > 0:
+ dParams[WuiDispatcherBase.ksParamPageNo] = iPage - 1
+ sHtmlPager = ('<a title="Previous page" href="?%s">&lt;&lt;</a>&nbsp;&nbsp;\n'
+ % (webutils.encodeUrlParams(dParams), )) \
+ + sHtmlPager;
+ else:
+ sHtmlPager = '&lt;&lt;&nbsp;&nbsp;\n' + sHtmlPager
+
+ if iPage + 1 < cNumOfPages:
+ dParams[WuiDispatcherBase.ksParamPageNo] = iPage + 1
+ sHtmlPager += '\n&nbsp; <a title="Next page" href="?%s">&gt;&gt;</a>\n' % (webutils.encodeUrlParams(dParams),)
+ else:
+ sHtmlPager += '\n&nbsp; &gt;&gt;\n'
+
+ return sHtmlPager
+
+ def _generateItemPerPageSelector(self, dParams, cItemsPerPage):
+ """
+ Generate HTML code for items per page selector
+ Note! Modifies dParams!
+ """
+
+ from testmanager.webui.wuicontentbase import WuiListContentBase; ## @todo move to better place.
+ return WuiListContentBase.generateItemPerPageSelector('top', dParams, cItemsPerPage);
+
+ def _generateResultNavigation(self, cItems, cItemsPerPage, iPage, tsEffective, sCurPeriod, fOnlyFailures,
+ sHtmlMemberSelector):
+ """ Make custom time navigation bar for the results. """
+
+ # Generate the elements.
+ sHtmlStatusSelector = self._generateStatusSelector(self.getParameters(), fOnlyFailures);
+ sHtmlSortBySelector = self._generateSortBySelector(self.getParameters(), '', sHtmlStatusSelector);
+ sHtmlPeriodSelector = self._generateResultPeriodSelector(self.getParameters(), sCurPeriod)
+ sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, sCurPeriod);
+
+ if cItems > 0:
+ sHtmlPager = self._generatePagesSelector(self.getParameters(), cItems, cItemsPerPage, iPage)
+ sHtmlItemsPerPageSelector = self._generateItemPerPageSelector(self.getParameters(), cItemsPerPage)
+ else:
+ sHtmlPager = ''
+ sHtmlItemsPerPageSelector = ''
+
+ # Generate navigation bar
+ sHtml = '<table width=100%>\n' \
+ '<tr>\n' \
+ ' <td width=30%>' + sHtmlMemberSelector + '</td>\n' \
+ ' <td width=40% align=center>' + sHtmlTimeWalker + '</td>' \
+ ' <td width=30% align=right>\n' + sHtmlPeriodSelector + '</td>\n' \
+ '</tr>\n' \
+ '<tr>\n' \
+ ' <td width=30%>' + sHtmlSortBySelector + '</td>\n' \
+ ' <td width=40% align=center>\n' + sHtmlPager + '</td>\n' \
+ ' <td width=30% align=right>\n' + sHtmlItemsPerPageSelector + '</td>\n'\
+ '</tr>\n' \
+ '</table>\n'
+
+ return sHtml
+
+ def _generateReportNavigation(self, tsEffective, cHoursPerPeriod, cPeriods):
+ """ Make time navigation bar for the reports. """
+
+ # The period length selector.
+ dParams = self.getParameters();
+ if WuiMain.ksParamReportPeriodInHours in dParams:
+ del dParams[WuiMain.ksParamReportPeriodInHours];
+ sHtmlPeriodLength = '';
+ sHtmlPeriodLength += '<form name="ReportPeriodInHoursForm" method="GET">\n' \
+ ' Period length <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
+ 'this.options[this.selectedIndex].value;" title="Statistics period length in hours.">\n' \
+ % (WuiMain.ksParamReportPeriodInHours,
+ webutils.encodeUrlParams(dParams),
+ WuiMain.ksParamReportPeriodInHours)
+ for cHours in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 18, 24, 48, 72, 96, 120, 144, 168 ]:
+ sHtmlPeriodLength += ' <option value="%d"%s>%d hour%s</option>\n' \
+ % (cHours, 'selected="selected"' if cHours == cHoursPerPeriod else '', cHours,
+ 's' if cHours > 1 else '');
+ sHtmlPeriodLength += ' </select>\n' \
+ '</form>\n'
+
+ # The period count selector.
+ dParams = self.getParameters();
+ if WuiMain.ksParamReportPeriods in dParams:
+ del dParams[WuiMain.ksParamReportPeriods];
+ sHtmlCountOfPeriods = '';
+ sHtmlCountOfPeriods += '<form name="ReportPeriodsForm" method="GET">\n' \
+ ' Periods <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
+ 'this.options[this.selectedIndex].value;" title="Statistics periods to report.">\n' \
+ % (WuiMain.ksParamReportPeriods,
+ webutils.encodeUrlParams(dParams),
+ WuiMain.ksParamReportPeriods)
+ for cCurPeriods in range(2, 43):
+ sHtmlCountOfPeriods += ' <option value="%d"%s>%d</option>\n' \
+ % (cCurPeriods, 'selected="selected"' if cCurPeriods == cPeriods else '', cCurPeriods);
+ sHtmlCountOfPeriods += ' </select>\n' \
+ '</form>\n'
+
+ # The time walker.
+ sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, '%d hours' % (cHoursPerPeriod));
+
+ # Combine them all.
+ sHtml = '<table width=100%>\n' \
+ ' <tr>\n' \
+ ' <td width=30% align="center">\n' + sHtmlPeriodLength + '</td>\n' \
+ ' <td width=40% align="center">\n' + sHtmlTimeWalker + '</td>' \
+ ' <td width=30% align="center">\n' + sHtmlCountOfPeriods + '</td>\n' \
+ ' </tr>\n' \
+ '</table>\n';
+ return sHtml;
+
+
+ #
+ # The rest of stuff
+ #
+
+ def _actionGroupedResultsListing( #pylint: disable=too-many-locals
+ self,
+ enmResultsGroupingType,
+ oResultsLogicType,
+ oResultFilterType,
+ oResultsListContentType):
+ """
+ Override generic listing action.
+
+ oResultsLogicType implements getEntriesCount, fetchResultsForListing and more.
+ oResultFilterType is a child of ModelFilterBase.
+ oResultsListContentType is a child of WuiListContentBase.
+ """
+ from testmanager.core.testresults import TestResultLogic;
+
+ cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 128);
+ iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0);
+ tsEffective = self.getEffectiveDateParam();
+ iGroupMemberId = self.getIntParam(self.ksParamGroupMemberId, iMin = -1, iMax = 999999, iDefault = -1);
+ fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False);
+ fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False);
+ enmResultSortBy = self.getStringParam(self.ksParamTestResultsSortBy,
+ asValidValues = TestResultLogic.kasResultsSortBy,
+ sDefault = TestResultLogic.ksResultsSortByRunningAndStart);
+ oFilter = oResultFilterType().initFromParams(self);
+
+ # Get testing results period and validate it
+ asValidValues = [x for (x, _, _) in self.kaoResultPeriods]
+ sCurPeriod = self.getStringParam(self.ksParamEffectivePeriod, asValidValues = asValidValues,
+ sDefault = self.ksResultPeriodDefault)
+ assert sCurPeriod != ''; # Impossible!
+
+ self._checkForUnknownParameters()
+
+ #
+ # Fetch the group members.
+ #
+ # If no grouping is selected, we'll fill the grouping combo with
+ # testboxes just to avoid having completely useless combo box.
+ #
+ oTrLogic = TestResultLogic(self._oDb);
+ sAltSelectorAction = None;
+ if enmResultsGroupingType in (TestResultLogic.ksResultsGroupingTypeNone, TestResultLogic.ksResultsGroupingTypeTestBox,):
+ aoTmp = oTrLogic.getTestBoxes(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list({(x.idTestBox, '%s (%s)' % (x.sName, str(x.ip))) for x in aoTmp }),
+ reverse = False, key = lambda asData: asData[1])
+
+ if enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestBox:
+ self._sPageTitle = 'Grouped by Test Box';
+ else:
+ self._sPageTitle = 'Ungrouped results';
+ sAltSelectorAction = self.ksActionResultsGroupedByTestBox;
+ aoGroupMembers.insert(0, [None, None]); # The "All" member.
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestGroup:
+ aoTmp = oTrLogic.getTestGroups(tsNow = tsEffective, sPeriod = sCurPeriod);
+ aoGroupMembers = sorted(list({ (x.idTestGroup, x.sName ) for x in aoTmp }),
+ reverse = False, key = lambda asData: asData[1])
+ self._sPageTitle = 'Grouped by Test Group'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildRev:
+ aoTmp = oTrLogic.getBuilds(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list({ (x.iRevision, '%s.%d' % (x.oCat.sBranch, x.iRevision)) for x in aoTmp }),
+ reverse = True, key = lambda asData: asData[0])
+ self._sPageTitle = 'Grouped by Build'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildCat:
+ aoTmp = oTrLogic.getBuildCategories(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list({ (x.idBuildCategory,
+ '%s / %s / %s / %s' % ( x.sProduct, x.sBranch, ', '.join(x.asOsArches), x.sType) )
+ for x in aoTmp }),
+ reverse = True, key = lambda asData: asData[1]);
+ self._sPageTitle = 'Grouped by Build Category'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestCase:
+ aoTmp = oTrLogic.getTestCases(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list({ (x.idTestCase, '%s' % x.sName) for x in aoTmp }),
+ reverse = False, key = lambda asData: asData[1])
+ self._sPageTitle = 'Grouped by Test Case'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeOS:
+ aoTmp = oTrLogic.getOSes(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
+ self._sPageTitle = 'Grouped by OS'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeArch:
+ aoTmp = oTrLogic.getArchitectures(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
+ self._sPageTitle = 'Grouped by Architecture'
+
+ elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeSchedGroup:
+ aoTmp = oTrLogic.getSchedGroups(tsNow = tsEffective, sPeriod = sCurPeriod)
+ aoGroupMembers = sorted(list({ (x.idSchedGroup, '%s' % x.sName) for x in aoTmp }),
+ reverse = False, key = lambda asData: asData[1])
+ self._sPageTitle = 'Grouped by Scheduling Group'
+
+ else:
+ raise TMExceptionBase('Unknown grouping type')
+
+ _sPageBody = ''
+ oContent = None
+ cEntriesMax = 0
+ _dParams = self.getParameters()
+ oResultLogic = oResultsLogicType(self._oDb);
+ for idMember, sMemberName in aoGroupMembers:
+ #
+ # Count and fetch entries to be displayed.
+ #
+
+ # Skip group members that were not specified.
+ if idMember != iGroupMemberId \
+ and ( (idMember is not None and enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeNone)
+ or (iGroupMemberId > 0 and enmResultsGroupingType != TestResultLogic.ksResultsGroupingTypeNone) ):
+ continue
+
+ cEntries = oResultLogic.getEntriesCount(tsNow = tsEffective,
+ sInterval = sCurPeriod,
+ oFilter = oFilter,
+ enmResultsGroupingType = enmResultsGroupingType,
+ iResultsGroupingValue = idMember,
+ fOnlyFailures = fOnlyFailures,
+ fOnlyNeedingReason = fOnlyNeedingReason);
+ if cEntries == 0: # Do not display empty groups
+ continue
+ aoEntries = oResultLogic.fetchResultsForListing(iPage * cItemsPerPage,
+ cItemsPerPage,
+ tsNow = tsEffective,
+ sInterval = sCurPeriod,
+ oFilter = oFilter,
+ enmResultSortBy = enmResultSortBy,
+ enmResultsGroupingType = enmResultsGroupingType,
+ iResultsGroupingValue = idMember,
+ fOnlyFailures = fOnlyFailures,
+ fOnlyNeedingReason = fOnlyNeedingReason);
+ cEntriesMax = max(cEntriesMax, cEntries)
+
+ #
+ # Format them.
+ #
+ oContent = oResultsListContentType(aoEntries,
+ cEntries,
+ iPage,
+ cItemsPerPage,
+ tsEffective,
+ fnDPrint = self._oSrvGlue.dprint,
+ oDisp = self)
+
+ (_, sHtml) = oContent.show(fShowNavigation = False)
+ if sMemberName is not None:
+ _sPageBody += '<table width=100%><tr><td>'
+
+ _dParams[self.ksParamGroupMemberId] = idMember
+ sLink = WuiTmLink(sMemberName, '', _dParams, fBracketed = False).toHtml()
+
+ _sPageBody += '<h2>%s (%d)</h2></td>' % (sLink, cEntries)
+ _sPageBody += '<td><br></td>'
+ _sPageBody += '</tr></table>'
+ _sPageBody += sHtml
+ _sPageBody += '<br>'
+
+ #
+ # Complete the page by slapping navigation controls at the top and
+ # bottom of it.
+ #
+ sHtmlNavigation = self._generateResultNavigation(cEntriesMax, cItemsPerPage, iPage,
+ tsEffective, sCurPeriod, fOnlyFailures,
+ self._generateGroupContentSelector(aoGroupMembers, iGroupMemberId,
+ sAltSelectorAction));
+ if cEntriesMax > 0:
+ self._sPageBody = sHtmlNavigation + _sPageBody + sHtmlNavigation;
+ else:
+ self._sPageBody = sHtmlNavigation + '<p align="center"><i>No data to display</i></p>\n';
+
+ #
+ # Now, generate a filter control panel for the side bar.
+ #
+ if hasattr(oFilter, 'kiBranches'):
+ oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
+ if hasattr(oFilter, 'kiTestStatus'):
+ oFilter.aCriteria[oFilter.kiTestStatus].fExpanded = True;
+ self._sPageFilter = self._generateResultFilter(oFilter, oResultLogic, tsEffective, sCurPeriod,
+ enmResultsGroupingType = enmResultsGroupingType,
+ aoGroupMembers = aoGroupMembers,
+ fOnlyFailures = fOnlyFailures,
+ fOnlyNeedingReason = fOnlyNeedingReason);
+ return True;
+
+ def _generateResultFilter(self, oFilter, oResultLogic, tsNow, sPeriod, enmResultsGroupingType = None, aoGroupMembers = None,
+ fOnlyFailures = False, fOnlyNeedingReason = False):
+ """
+ Generates the result filter for the left hand side.
+ """
+ _ = enmResultsGroupingType; _ = aoGroupMembers; _ = fOnlyFailures; _ = fOnlyNeedingReason;
+ oResultLogic.fetchPossibleFilterOptions(oFilter, tsNow, sPeriod)
+
+ # Add non-filter parameters as hidden fields so we can use 'GET' and have URLs to bookmark.
+ self._dSideMenuFormAttrs['method'] = 'GET';
+ sHtml = u'';
+ for sKey, oValue in self._oSrvGlue.getParameters().items():
+ if len(sKey) > 3:
+ if hasattr(oValue, 'startswith'):
+ sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
+ % (webutils.escapeAttr(sKey), webutils.escapeAttr(oValue),);
+ else:
+ for oSubValue in oValue:
+ sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
+ % (webutils.escapeAttr(sKey), webutils.escapeAttr(oSubValue),);
+
+ # Generate the filter panel.
+ sHtml += u'<div id="side-filters">\n' \
+ u' <p>Filters' \
+ u' <span class="tm-side-filter-title-buttons"><input type="submit" value="Apply" />\n' \
+ u' <a href="javascript:toggleSidebarSize();" class="tm-sidebar-size-link">&#x00bb;&#x00bb;</a></span></p>\n';
+ sHtml += u' <dl>\n';
+ for oCrit in oFilter.aCriteria:
+ if oCrit.aoPossible or oCrit.sType == oCrit.ksType_Ranges:
+ if ( oCrit.oSub is None \
+ and ( oCrit.sState == oCrit.ksState_Selected \
+ or (len(oCrit.aoPossible) <= 2 and oCrit.sType != oCrit.ksType_Ranges))) \
+ or ( oCrit.oSub is not None \
+ and ( oCrit.sState == oCrit.ksState_Selected \
+ or oCrit.oSub.sState == oCrit.ksState_Selected \
+ or (len(oCrit.aoPossible) <= 2 and len(oCrit.oSub.aoPossible) <= 2))) \
+ or oCrit.fExpanded is True:
+ sClass = 'sf-collapsible';
+ sChar = '&#9660;';
+ else:
+ sClass = 'sf-expandable';
+ sChar = '&#9654;';
+
+ sHtml += u' <dt class="%s"><a href="javascript:void(0)" onclick="toggleCollapsibleDtDd(this);">%s %s</a> ' \
+ % (sClass, sChar, webutils.escapeElem(oCrit.sName),);
+ sHtml += u'<span class="tm-side-filter-dt-buttons">';
+ if oCrit.sInvVarNm is not None:
+ sHtml += u'<input id="sf-union-%s" class="tm-side-filter-union-input" ' \
+ u'name="%s" value="1" type="checkbox"%s />' \
+ u'<label for="sf-union-%s" class="tm-side-filter-union-input"></label>' \
+ % ( oCrit.sInvVarNm, oCrit.sInvVarNm, ' checked' if oCrit.fInverted else '', oCrit.sInvVarNm,);
+ sHtml += u' <input type="submit" value="Apply" />';
+ sHtml += u'</span>';
+ sHtml += u'</dt>\n' \
+ u' <dd class="%s">\n' \
+ u' <ul>\n' \
+ % (sClass);
+
+ if oCrit.sType == oCrit.ksType_Ranges:
+ assert not oCrit.oSub;
+ assert not oCrit.aoPossible;
+ asValues = [];
+ for tRange in oCrit.aoSelected:
+ if tRange[0] == tRange[1]:
+ asValues.append('%s' % (tRange[0],));
+ else:
+ asValues.append('%s-%s' % (tRange[0] if tRange[0] is not None else 'inf',
+ tRange[1] if tRange[1] is not None else 'inf'));
+ sHtml += u' <li title="%s"><input type="text" name="%s" value="%s"/></li>\n' \
+ % ( webutils.escapeAttr('comma separate list of numerical ranges'), oCrit.sVarNm,
+ ', '.join(asValues), );
+ else:
+ for oDesc in oCrit.aoPossible:
+ fChecked = oDesc.oValue in oCrit.aoSelected;
+ sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s%s/>%s%s</label>\n' \
+ % ( ' class="side-filter-irrelevant"' if oDesc.fIrrelevant else '',
+ (' title="%s"' % (webutils.escapeAttr(oDesc.sHover,)) if oDesc.sHover is not None else ''),
+ oCrit.sVarNm,
+ oDesc.oValue,
+ ' checked' if fChecked else '',
+ ' onclick="toggleCollapsibleCheckbox(this);"' if oDesc.aoSubs is not None else '',
+ webutils.escapeElem(oDesc.sDesc),
+ '<span class="side-filter-count"> [%u]</span>' % (oDesc.cTimes) if oDesc.cTimes is not None
+ else '', );
+ if oDesc.aoSubs is not None:
+ sHtml += u' <ul class="sf-checkbox-%s">\n' % ('collapsible' if fChecked else 'expandable', );
+ for oSubDesc in oDesc.aoSubs:
+ fSubChecked = oSubDesc.oValue in oCrit.oSub.aoSelected;
+ sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s/>%s%s</label>\n' \
+ % ( ' class="side-filter-irrelevant"' if oSubDesc.fIrrelevant else '',
+ ' title="%s"' % ( webutils.escapeAttr(oSubDesc.sHover,) if oSubDesc.sHover is not None
+ else ''),
+ oCrit.oSub.sVarNm, oSubDesc.oValue, ' checked' if fSubChecked else '',
+ webutils.escapeElem(oSubDesc.sDesc),
+ '<span class="side-filter-count"> [%u]</span>' % (oSubDesc.cTimes)
+ if oSubDesc.cTimes is not None else '', );
+
+ sHtml += u' </ul>\n';
+ sHtml += u' </li>';
+
+ sHtml += u' </ul>\n' \
+ u' </dd>\n';
+
+ sHtml += u' </dl>\n';
+ sHtml += u' <input type="submit" value="Apply"/>\n';
+ sHtml += u' <input type="reset" value="Reset"/>\n';
+ sHtml += u' <button type="button" onclick="clearForm(\'side-menu-form\');">Clear</button>\n';
+ sHtml += u'</div>\n';
+ return sHtml;
+
+ def _actionResultsUnGrouped(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ #return self._actionResultsListing(TestResultLogic, WuiGroupedResultList)?
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByTestGroup(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestGroup,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByBuildRev(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildRev,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByBuildCat(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildCat,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByTestBox(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestBox,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByTestCase(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestCase,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByOS(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeOS,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedByArch(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeArch,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+ def _actionResultsGroupedBySchedGroup(self):
+ """ Action wrapper. """
+ from testmanager.webui.wuitestresult import WuiGroupedResultList;
+ from testmanager.core.testresults import TestResultLogic, TestResultFilter;
+ return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeSchedGroup,
+ TestResultLogic, TestResultFilter, WuiGroupedResultList);
+
+
+ def _actionTestSetDetailsCommon(self, idTestSet):
+ """Show test case execution result details."""
+ from testmanager.core.build import BuildDataEx;
+ from testmanager.core.testbox import TestBoxData;
+ from testmanager.core.testcase import TestCaseDataEx;
+ from testmanager.core.testcaseargs import TestCaseArgsDataEx;
+ from testmanager.core.testgroup import TestGroupData;
+ from testmanager.core.testresults import TestResultLogic;
+ from testmanager.core.testset import TestSetData;
+ from testmanager.webui.wuitestresult import WuiTestResult;
+
+ self._sTemplate = 'template-details.html';
+ self._checkForUnknownParameters()
+
+ oTestSetData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ try:
+ (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet);
+ except TMTooManyRows:
+ (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet, 2);
+ oBuildDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuild, oTestSetData.tsCreated);
+ try: oBuildValidationKitDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuildTestSuite,
+ oTestSetData.tsCreated);
+ except: oBuildValidationKitDataEx = None;
+ oTestBoxData = TestBoxData().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestBox);
+ oTestGroupData = TestGroupData().initFromDbWithId(self._oDb, ## @todo This bogus time wise. Bad DB design?
+ oTestSetData.idTestGroup, oTestSetData.tsCreated);
+ oTestCaseDataEx = TestCaseDataEx().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestCase,
+ oTestSetData.tsConfig);
+ oTestCaseArgsDataEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTestSetData.idGenTestCaseArgs,
+ oTestSetData.tsConfig);
+
+ oContent = WuiTestResult(oDisp = self, fnDPrint = self._oSrvGlue.dprint);
+ (self._sPageTitle, self._sPageBody) = oContent.showTestCaseResultDetails(oTestResultTree,
+ oTestSetData,
+ oBuildDataEx,
+ oBuildValidationKitDataEx,
+ oTestBoxData,
+ oTestGroupData,
+ oTestCaseDataEx,
+ oTestCaseArgsDataEx);
+ return True
+
+ def _actionTestSetDetails(self):
+ """Show test case execution result details."""
+ from testmanager.core.testset import TestSetData;
+
+ idTestSet = self.getIntParam(TestSetData.ksParam_idTestSet);
+ return self._actionTestSetDetailsCommon(idTestSet);
+
+ def _actionTestSetDetailsFromResult(self):
+ """Show test case execution result details."""
+ from testmanager.core.testresults import TestResultData;
+ from testmanager.core.testset import TestSetData;
+ idTestResult = self.getIntParam(TestSetData.ksParam_idTestResult);
+ oTestResultData = TestResultData().initFromDbWithId(self._oDb, idTestResult);
+ return self._actionTestSetDetailsCommon(oTestResultData.idTestSet);
+
+
+ def _actionTestResultFailureAdd(self):
+ """ Pro forma. """
+ from testmanager.core.testresultfailures import TestResultFailureData;
+ from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
+ return self._actionGenericFormAdd(TestResultFailureData, WuiTestResultFailure);
+
+ def _actionTestResultFailureAddPost(self):
+ """Add test result failure result"""
+ from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
+ from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
+ if self.ksParamRedirectTo not in self._dParams:
+ raise WuiException('Missing parameter ' + self.ksParamRedirectTo);
+
+ return self._actionGenericFormAddPost(TestResultFailureData, TestResultFailureLogic,
+ WuiTestResultFailure, self.ksActionResultsUnGrouped);
+
+ def _actionTestResultFailureDoRemove(self):
+ """ Action wrapper. """
+ from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic;
+ return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult,
+ self.ksActionResultsUnGrouped);
+
+ def _actionTestResultFailureDetails(self):
+ """ Pro forma. """
+ from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
+ from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
+ return self._actionGenericFormDetails(TestResultFailureData, TestResultFailureLogic,
+ WuiTestResultFailure, 'idTestResult');
+
+ def _actionTestResultFailureEdit(self):
+ """ Pro forma. """
+ from testmanager.core.testresultfailures import TestResultFailureData;
+ from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
+ return self._actionGenericFormEdit(TestResultFailureData, WuiTestResultFailure,
+ TestResultFailureData.ksParam_idTestResult);
+
+ def _actionTestResultFailureEditPost(self):
+ """Edit test result failure result"""
+ from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
+ from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
+ return self._actionGenericFormEditPost(TestResultFailureData, TestResultFailureLogic,
+ WuiTestResultFailure, self.ksActionResultsUnGrouped);
+
+ def _actionViewLog(self):
+ """
+ Log viewer action.
+ """
+ from testmanager.core.testresults import TestResultLogic, TestResultFileDataEx;
+ from testmanager.core.testset import TestSetData, TestSetLogic;
+ from testmanager.webui.wuilogviewer import WuiLogViewer;
+
+ self._sTemplate = 'template-details.html'; ## @todo create new template (background color, etc)
+ idTestSet = self.getIntParam(self.ksParamLogSetId, iMin = 1);
+ idLogFile = self.getIntParam(self.ksParamLogFileId, iMin = 0, iDefault = 0);
+ cbChunk = self.getIntParam(self.ksParamLogChunkSize, iMin = 256, iMax = 16777216, iDefault = 1024*1024);
+ iChunk = self.getIntParam(self.ksParamLogChunkNo, iMin = 0,
+ iMax = config.g_kcMbMaxMainLog * 1048576 / cbChunk, iDefault = 0);
+ self._checkForUnknownParameters();
+
+ oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ if idLogFile == 0:
+ oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
+ aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet);
+ else:
+ oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
+ aoTimestamps = [];
+ if oTestFile.sMime not in [ 'text/plain',]:
+ raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,));
+
+ oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps,
+ oDisp = self, fnDPrint = self._oSrvGlue.dprint);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ return True;
+
+ def _actionGetFile(self):
+ """
+ Get file action.
+ """
+ from testmanager.core.testset import TestSetData, TestSetLogic;
+ from testmanager.core.testresults import TestResultFileDataEx;
+
+ idTestSet = self.getIntParam(self.ksParamGetFileSetId, iMin = 1);
+ idFile = self.getIntParam(self.ksParamGetFileId, iMin = 0, iDefault = 0);
+ fDownloadIt = self.getBoolParam(self.ksParamGetFileDownloadIt, fDefault = True);
+ self._checkForUnknownParameters();
+
+ #
+ # Get the file info and open it.
+ #
+ oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
+ if idFile == 0:
+ oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
+ else:
+ oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idFile);
+
+ (oFile, oSizeOrError, _) = oTestSet.openFile(oTestFile.sFile, 'rb');
+ if oFile is None:
+ raise Exception(oSizeOrError);
+
+ #
+ # Send the file.
+ #
+ self._oSrvGlue.setHeaderField('Content-Type', oTestFile.getMimeWithEncoding());
+ if fDownloadIt:
+ self._oSrvGlue.setHeaderField('Content-Disposition', 'attachment; filename="TestSet-%d-%s"'
+ % (idTestSet, oTestFile.sFile,));
+ while True:
+ abChunk = oFile.read(262144);
+ if not abChunk:
+ break;
+ self._oSrvGlue.writeRaw(abChunk);
+ return self.ksDispatchRcAllDone;
+
+ def _actionGenericReport(self, oModelType, oFilterType, oReportType):
+ """
+ Generic report action.
+ oReportType is a child of WuiReportContentBase.
+ oFilterType is a child of ModelFilterBase.
+ oModelType is a child of ReportModelBase.
+ """
+ from testmanager.core.report import ReportModelBase;
+
+ tsEffective = self.getEffectiveDateParam();
+ cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 2, iMax = 99, iDefault = 7);
+ cHoursPerPeriod = self.getIntParam(self.ksParamReportPeriodInHours, iMin = 1, iMax = 168, iDefault = 24);
+ sSubject = self.getStringParam(self.ksParamReportSubject, ReportModelBase.kasSubjects,
+ ReportModelBase.ksSubEverything);
+ if sSubject == ReportModelBase.ksSubEverything:
+ aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, aiDefaults = []);
+ else:
+ aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, iMin = 1);
+ if aidSubjects is None:
+ raise WuiException('Missing parameter %s' % (self.ksParamReportSubjectIds,));
+
+ aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns,
+ iMin = -getattr(oReportType, 'kcMaxSortColumns', cPeriods) + 1,
+ iMax = getattr(oReportType, 'kcMaxSortColumns', cPeriods), aiDefaults = []);
+ aiSortColumns = [];
+ for iSortColumn in aiSortColumnsDup:
+ if iSortColumn not in aiSortColumns:
+ aiSortColumns.append(iSortColumn);
+
+ oFilter = oFilterType().initFromParams(self);
+ self._checkForUnknownParameters();
+
+ dParams = \
+ {
+ self.ksParamEffectiveDate: tsEffective,
+ self.ksParamReportPeriods: cPeriods,
+ self.ksParamReportPeriodInHours: cHoursPerPeriod,
+ self.ksParamReportSubject: sSubject,
+ self.ksParamReportSubjectIds: aidSubjects,
+ };
+ ## @todo oFilter.
+
+ oModel = oModelType(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter);
+ oContent = oReportType(oModel, dParams, fSubReport = False, aiSortColumns = aiSortColumns,
+ fnDPrint = self._oSrvGlue.dprint, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ sNavi = self._generateReportNavigation(tsEffective, cHoursPerPeriod, cPeriods);
+ self._sPageBody = sNavi + self._sPageBody;
+
+ if hasattr(oFilter, 'kiBranches'):
+ oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
+ self._sPageFilter = self._generateResultFilter(oFilter, oModel, tsEffective, '%s hours' % (cHoursPerPeriod * cPeriods,));
+ return True;
+
+ def _actionReportSummary(self):
+ """ Action wrapper. """
+ from testmanager.core.report import ReportLazyModel, ReportFilter;
+ from testmanager.webui.wuireport import WuiReportSummary;
+ return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSummary);
+
+ def _actionReportRate(self):
+ """ Action wrapper. """
+ from testmanager.core.report import ReportLazyModel, ReportFilter;
+ from testmanager.webui.wuireport import WuiReportSuccessRate;
+ return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSuccessRate);
+
+ def _actionReportTestCaseFailures(self):
+ """ Action wrapper. """
+ from testmanager.core.report import ReportLazyModel, ReportFilter;
+ from testmanager.webui.wuireport import WuiReportTestCaseFailures;
+ return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportTestCaseFailures);
+
+ def _actionReportFailureReasons(self):
+ """ Action wrapper. """
+ from testmanager.core.report import ReportLazyModel, ReportFilter;
+ from testmanager.webui.wuireport import WuiReportFailureReasons;
+ return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportFailureReasons);
+
+ def _actionGraphWiz(self):
+ """
+ Graph wizard action.
+ """
+ from testmanager.core.report import ReportModelBase, ReportGraphModel;
+ from testmanager.webui.wuigraphwiz import WuiGraphWiz;
+ self._sTemplate = 'template-graphwiz.html';
+
+ tsEffective = self.getEffectiveDateParam();
+ cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 1, iMax = 1, iDefault = 1); # Not needed yet.
+ sTmp = self.getStringParam(self.ksParamReportPeriodInHours, sDefault = '3 weeks');
+ (cHoursPerPeriod, sError) = utils.parseIntervalHours(sTmp);
+ if sError is not None: raise WuiException(sError);
+ asSubjectIds = self.getListOfStrParams(self.ksParamReportSubjectIds);
+ sSubject = self.getStringParam(self.ksParamReportSubject, [ReportModelBase.ksSubEverything],
+ ReportModelBase.ksSubEverything); # dummy
+ aidTestBoxes = self.getListOfIntParams(self.ksParamGraphWizTestBoxIds, iMin = 1, aiDefaults = []);
+ aidBuildCats = self.getListOfIntParams(self.ksParamGraphWizBuildCatIds, iMin = 1, aiDefaults = []);
+ aidTestCases = self.getListOfIntParams(self.ksParamGraphWizTestCaseIds, iMin = 1, aiDefaults = []);
+ fSepTestVars = self.getBoolParam(self.ksParamGraphWizSepTestVars, fDefault = False);
+
+ enmGraphImpl = self.getStringParam(self.ksParamGraphWizImpl, asValidValues = self.kasGraphWizImplValid,
+ sDefault = self.ksGraphWizImpl_Default);
+ cx = self.getIntParam(self.ksParamGraphWizWidth, iMin = 128, iMax = 8192, iDefault = 1280);
+ cy = self.getIntParam(self.ksParamGraphWizHeight, iMin = 128, iMax = 8192, iDefault = int(cx * 5 / 16) );
+ cDotsPerInch = self.getIntParam(self.ksParamGraphWizDpi, iMin = 64, iMax = 512, iDefault = 96);
+ cPtFont = self.getIntParam(self.ksParamGraphWizFontSize, iMin = 6, iMax = 32, iDefault = 8);
+ fErrorBarY = self.getBoolParam(self.ksParamGraphWizErrorBarY, fDefault = False);
+ cMaxErrorBarY = self.getIntParam(self.ksParamGraphWizMaxErrorBarY, iMin = 8, iMax = 9999999, iDefault = 18);
+ cMaxPerGraph = self.getIntParam(self.ksParamGraphWizMaxPerGraph, iMin = 1, iMax = 24, iDefault = 8);
+ fXkcdStyle = self.getBoolParam(self.ksParamGraphWizXkcdStyle, fDefault = False);
+ fTabular = self.getBoolParam(self.ksParamGraphWizTabular, fDefault = False);
+ idSrcTestSet = self.getIntParam(self.ksParamGraphWizSrcTestSetId, iDefault = None);
+ self._checkForUnknownParameters();
+
+ dParams = \
+ {
+ self.ksParamEffectiveDate: tsEffective,
+ self.ksParamReportPeriods: cPeriods,
+ self.ksParamReportPeriodInHours: cHoursPerPeriod,
+ self.ksParamReportSubject: sSubject,
+ self.ksParamReportSubjectIds: asSubjectIds,
+ self.ksParamGraphWizTestBoxIds: aidTestBoxes,
+ self.ksParamGraphWizBuildCatIds: aidBuildCats,
+ self.ksParamGraphWizTestCaseIds: aidTestCases,
+ self.ksParamGraphWizSepTestVars: fSepTestVars,
+
+ self.ksParamGraphWizImpl: enmGraphImpl,
+ self.ksParamGraphWizWidth: cx,
+ self.ksParamGraphWizHeight: cy,
+ self.ksParamGraphWizDpi: cDotsPerInch,
+ self.ksParamGraphWizFontSize: cPtFont,
+ self.ksParamGraphWizErrorBarY: fErrorBarY,
+ self.ksParamGraphWizMaxErrorBarY: cMaxErrorBarY,
+ self.ksParamGraphWizMaxPerGraph: cMaxPerGraph,
+ self.ksParamGraphWizXkcdStyle: fXkcdStyle,
+ self.ksParamGraphWizTabular: fTabular,
+ self.ksParamGraphWizSrcTestSetId: idSrcTestSet,
+ };
+
+ oModel = ReportGraphModel(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, asSubjectIds,
+ aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars);
+ oContent = WuiGraphWiz(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ return True;
+
+ def _actionVcsHistoryTooltip(self):
+ """
+ Version control system history.
+ """
+ from testmanager.webui.wuivcshistory import WuiVcsHistoryTooltip;
+ from testmanager.core.vcsrevisions import VcsRevisionLogic;
+
+ self._sTemplate = 'template-tooltip.html';
+ iRevision = self.getIntParam(self.ksParamVcsHistoryRevision, iMin = 0, iMax = 999999999);
+ sRepository = self.getStringParam(self.ksParamVcsHistoryRepository);
+ cEntries = self.getIntParam(self.ksParamVcsHistoryEntries, iMin = 1, iMax = 1024, iDefault = 8);
+ self._checkForUnknownParameters();
+
+ aoEntries = VcsRevisionLogic(self._oDb).fetchTimeline(sRepository, iRevision, cEntries);
+ oContent = WuiVcsHistoryTooltip(aoEntries, sRepository, iRevision, cEntries,
+ fnDPrint = self._oSrvGlue.dprint, oDisp = self);
+ (self._sPageTitle, self._sPageBody) = oContent.show();
+ return True;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuireport.py b/src/VBox/ValidationKit/testmanager/webui/wuireport.py
new file mode 100755
index 00000000..f1625176
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuireport.py
@@ -0,0 +1,817 @@
+# -*- coding: utf-8 -*-
+# $Id: wuireport.py $
+
+"""
+Test Manager WUI - Reports.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Validation Kit imports.
+from common import webutils;
+from testmanager.webui.wuicontentbase import WuiContentBase, WuiTmLink, WuiSvnLinkWithTooltip;
+from testmanager.webui.wuihlpgraph import WuiHlpGraphDataTable, WuiHlpBarGraph;
+from testmanager.webui.wuitestresult import WuiTestSetLink, WuiTestResultsForTestCaseLink, WuiTestResultsForTestBoxLink;
+from testmanager.webui.wuiadmintestcase import WuiTestCaseDetailsLink;
+from testmanager.webui.wuiadmintestbox import WuiTestBoxDetailsLinkShort;
+from testmanager.core.report import ReportModelBase, ReportFilter;
+from testmanager.core.testresults import TestResultFilter;
+
+
+class WuiReportSummaryLink(WuiTmLink):
+ """ Generic report summary link. """
+
+ def __init__(self, sSubject, aIdSubjects, sName = WuiContentBase.ksShortReportLink,
+ tsNow = None, cPeriods = None, cHoursPerPeriod = None, fBracketed = False, dExtraParams = None):
+ from testmanager.webui.wuimain import WuiMain;
+ dParams = {
+ WuiMain.ksParamAction: WuiMain.ksActionReportSummary,
+ WuiMain.ksParamReportSubject: sSubject,
+ WuiMain.ksParamReportSubjectIds: aIdSubjects,
+ };
+ if dExtraParams is not None:
+ dParams.update(dExtraParams);
+ if tsNow is not None:
+ dParams[WuiMain.ksParamEffectiveDate] = tsNow;
+ if cPeriods is not None:
+ dParams[WuiMain.ksParamReportPeriods] = cPeriods;
+ if cPeriods is not None:
+ dParams[WuiMain.ksParamReportPeriodInHours] = cHoursPerPeriod;
+ WuiTmLink.__init__(self, sName, WuiMain.ksScriptName, dParams, fBracketed = fBracketed);
+
+
+class WuiReportBase(WuiContentBase):
+ """
+ Base class for the reports.
+ """
+
+ def __init__(self, oModel, dParams, fSubReport = False, aiSortColumns = None, fnDPrint = None, oDisp = None):
+ WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
+ self._oModel = oModel;
+ self._dParams = dParams;
+ self._fSubReport = fSubReport;
+ self._sTitle = None;
+ self._aiSortColumns = aiSortColumns;
+
+ # Additional URL parameters for reports:
+ from testmanager.webui.wuimain import WuiMain;
+ self._dExtraParams = ReportFilter().strainParameters({} if oDisp is None else oDisp.getParameters(),
+ (WuiMain.ksParamReportPeriods,
+ WuiMain.ksParamReportPeriodInHours,
+ WuiMain.ksParamEffectiveDate,));
+ # Additional URL parameters for test results:
+ self._dExtraTestResultsParams = TestResultFilter().strainParameters(oDisp.getParameters(),
+ (WuiMain.ksParamEffectiveDate,));
+ self._dExtraTestResultsParams[WuiMain.ksParamEffectivePeriod] = self.getPeriodForTestResults();
+
+
+ def generateNavigator(self, sWhere):
+ """
+ Generates the navigator (manipulate _dParams).
+ Returns HTML.
+ """
+ assert sWhere in ('top', 'bottom',);
+
+ return '';
+
+ def generateReportBody(self):
+ """
+ This is overridden by the child class to generate the report.
+ Returns HTML.
+ """
+ return '<h3>Must override generateReportBody!</h3>';
+
+ def show(self):
+ """
+ Generate the report.
+ Returns (sTitle, HTML).
+ """
+
+ sTitle = self._sTitle if self._sTitle is not None else type(self).__name__;
+ sReport = self.generateReportBody();
+ if not self._fSubReport:
+ sReport = self.generateNavigator('top') + sReport + self.generateNavigator('bottom');
+ sTitle = self._oModel.sSubject + ' - ' + sTitle; ## @todo add subject to title in a proper way!
+
+ sReport += '\n\n<!-- HEYYOU: sSubject=%s aidSubjects=%s -->\n\n' % (self._oModel.sSubject, self._oModel.aidSubjects);
+ return (sTitle, sReport);
+
+ #
+ # Utility methods
+ #
+
+ def getPeriodForTestResults(self):
+ """
+ Takes the report period length and count and translates it into a
+ reasonable test result period (value).
+ """
+ from testmanager.webui.wuimain import WuiMain;
+ cHours = self._oModel.cPeriods * self._oModel.cHoursPerPeriod;
+ if cHours > 7*24:
+ cHours = cHours // 2;
+ for sPeriodValue, _, cPeriodHours in WuiMain.kaoResultPeriods:
+ sPeriod = sPeriodValue;
+ if cPeriodHours >= cHours:
+ return sPeriod;
+ return sPeriod;
+
+ @staticmethod
+ def fmtPct(cHits, cTotal):
+ """
+ Formats a percent number.
+ Returns a string.
+ """
+ uPct = cHits * 100 // cTotal;
+ if uPct >= 10 and (uPct > 103 or uPct <= 95):
+ return '%s%%' % (uPct,);
+ return '%.1f%%' % (cHits * 100.0 / cTotal,);
+
+ @staticmethod
+ def fmtPctWithHits(cHits, cTotal):
+ """
+ Formats a percent number with total in parentheses.
+ Returns a string.
+ """
+ return '%s (%s)' % (WuiReportBase.fmtPct(cHits, cTotal), cHits);
+
+ @staticmethod
+ def fmtPctWithHitsAndTotal(cHits, cTotal):
+ """
+ Formats a percent number with total in parentheses.
+ Returns a string.
+ """
+ return '%s (%s/%s)' % (WuiReportBase.fmtPct(cHits, cTotal), cHits, cTotal);
+
+
+
+class WuiReportSuccessRate(WuiReportBase):
+ """
+ Generates a report displaying the success rate over time.
+ """
+
+ def generateReportBody(self):
+ self._sTitle = 'Success rate';
+ fTailoredForGoogleCharts = True;
+
+ #
+ # Get the data and check if we have anything in the 'skipped' category.
+ #
+ adPeriods = self._oModel.getSuccessRates();
+
+ cTotalSkipped = 0;
+ for dStatuses in adPeriods:
+ cTotalSkipped += dStatuses[ReportModelBase.ksTestStatus_Skipped];
+
+ #
+ # Output some general stats before the graphs.
+ #
+ cTotalNow = adPeriods[0][ReportModelBase.ksTestStatus_Success];
+ cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Skipped];
+ cSuccessNow = cTotalNow;
+ cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Failure];
+
+ sReport = '<p>Current success rate: ';
+ if cTotalNow > 0:
+ cSkippedNow = adPeriods[0][ReportModelBase.ksTestStatus_Skipped];
+ if cSkippedNow > 0:
+ sReport += '%s (thereof %s skipped)</p>\n' \
+ % (self.fmtPct(cSuccessNow, cTotalNow), self.fmtPct(cSkippedNow, cTotalNow),);
+ else:
+ sReport += '%s (none skipped)</p>\n' % (self.fmtPct(cSuccessNow, cTotalNow),);
+ else:
+ sReport += 'N/A</p>\n'
+
+ #
+ # Create the data table.
+ #
+ if fTailoredForGoogleCharts:
+ if cTotalSkipped > 0:
+ oTable = WuiHlpGraphDataTable(None, [ 'Succeeded', 'Skipped', 'Failed' ]);
+ else:
+ oTable = WuiHlpGraphDataTable(None, [ 'Succeeded', 'Failed' ]);
+ else:
+ if cTotalSkipped > 0:
+ oTable = WuiHlpGraphDataTable('When', [ 'Succeeded', 'Skipped', 'Failed' ]);
+ else:
+ oTable = WuiHlpGraphDataTable('When', [ 'Succeeded', 'Failed' ]);
+
+ for i, dStatuses in enumerate(adPeriods):
+ cSuccesses = dStatuses[ReportModelBase.ksTestStatus_Success];
+ cFailures = dStatuses[ReportModelBase.ksTestStatus_Failure];
+ cSkipped = dStatuses[ReportModelBase.ksTestStatus_Skipped];
+
+ cSuccess = cSuccesses + cSkipped;
+ cTotal = cSuccess + cFailures;
+ sPeriod = self._oModel.getPeriodDesc(i);
+ if fTailoredForGoogleCharts:
+ if cTotalSkipped > 0:
+ oTable.addRow(sPeriod,
+ [ cSuccesses * 100 // cTotal if cTotal else 0,
+ cSkipped * 100 // cTotal if cTotal else 0,
+ cFailures * 100 // cTotal if cTotal else 0, ],
+ [ self.fmtPct(cSuccesses, cTotal) if cSuccesses else None,
+ self.fmtPct(cSkipped, cTotal) if cSkipped else None,
+ self.fmtPct(cFailures, cTotal) if cFailures else None, ]);
+ else:
+ oTable.addRow(sPeriod,
+ [ cSuccesses * 100 // cTotal if cTotal else 0,
+ cFailures * 100 // cTotal if cTotal else 0, ],
+ [ self.fmtPct(cSuccesses, cTotal) if cSuccesses else None,
+ self.fmtPct(cFailures, cTotal) if cFailures else None, ]);
+ elif cTotal > 0:
+ if cTotalSkipped > 0:
+ oTable.addRow(sPeriod,
+ [ cSuccesses * 100 // cTotal,
+ cSkipped * 100 // cTotal,
+ cFailures * 100 // cTotal, ],
+ [ self.fmtPctWithHits(cSuccesses, cTotal),
+ self.fmtPctWithHits(cSkipped, cTotal),
+ self.fmtPctWithHits(cFailures, cTotal), ]);
+ else:
+ oTable.addRow(sPeriod,
+ [ cSuccesses * 100 // cTotal,
+ cFailures * 100 // cTotal, ],
+ [ self.fmtPctWithHits(cSuccesses, cTotal),
+ self.fmtPctWithHits(cFailures, cTotal), ]);
+ elif cTotalSkipped > 0:
+ oTable.addRow(sPeriod, [ 0, 0, 0 ], [ '0%', '0%', '0%' ]);
+ else:
+ oTable.addRow(sPeriod, [ 0, 0 ], [ '0%', '0%' ]);
+
+ #
+ # Render the graph.
+ #
+ oGraph = WuiHlpBarGraph('success-rate', oTable, self._oDisp);
+ oGraph.setRangeMax(100);
+ sReport += oGraph.renderGraph();
+
+ #
+ # Graph with absolute counts.
+ #
+ if fTailoredForGoogleCharts:
+ if cTotalSkipped > 0:
+ oTable = WuiHlpGraphDataTable(None, [ 'Succeeded', 'Skipped', 'Failed' ]);
+ else:
+ oTable = WuiHlpGraphDataTable(None, [ 'Succeeded', 'Failed' ]);
+ for i, dStatuses in enumerate(adPeriods):
+ cSuccesses = dStatuses[ReportModelBase.ksTestStatus_Success];
+ cFailures = dStatuses[ReportModelBase.ksTestStatus_Failure];
+ cSkipped = dStatuses[ReportModelBase.ksTestStatus_Skipped];
+
+ if cTotalSkipped > 0:
+ oTable.addRow(None, #self._oModel.getPeriodDesc(i),
+ [ cSuccesses, cSkipped, cFailures, ],
+ [ str(cSuccesses) if cSuccesses > 0 else None,
+ str(cSkipped) if cSkipped > 0 else None,
+ str(cFailures) if cFailures > 0 else None, ]);
+ else:
+ oTable.addRow(None, #self._oModel.getPeriodDesc(i),
+ [ cSuccesses, cFailures, ],
+ [ str(cSuccesses) if cSuccesses > 0 else None,
+ str(cFailures) if cFailures > 0 else None, ]);
+ oGraph = WuiHlpBarGraph('success-numbers', oTable, self._oDisp);
+ oGraph.invertYDirection();
+ sReport += oGraph.renderGraph();
+
+ return sReport;
+
+
+class WuiReportFailuresBase(WuiReportBase):
+ """
+ Common parent of WuiReportFailureReasons and WuiReportTestCaseFailures.
+ """
+
+ def _splitSeriesIntoMultipleGraphs(self, aidSorted, cMaxSeriesPerGraph = 8):
+ """
+ Splits the ID array into one or more arrays, making sure we don't
+ have too many series per graph.
+ Returns array of ID arrays.
+ """
+ if len(aidSorted) <= cMaxSeriesPerGraph + 2:
+ return [aidSorted,];
+ cGraphs = len(aidSorted) // cMaxSeriesPerGraph + (len(aidSorted) % cMaxSeriesPerGraph != 0);
+ cPerGraph = len(aidSorted) // cGraphs + (len(aidSorted) % cGraphs != 0);
+
+ aaoRet = [];
+ cLeft = len(aidSorted);
+ iSrc = 0;
+ while cLeft > 0:
+ cThis = cPerGraph;
+ if cLeft <= cPerGraph + 2:
+ cThis = cLeft;
+ elif cLeft <= cPerGraph * 2 + 4:
+ cThis = cLeft // 2;
+ aaoRet.append(aidSorted[iSrc : iSrc + cThis]);
+ iSrc += cThis;
+ cLeft -= cThis;
+ return aaoRet;
+
+ def _formatEdgeOccurenceSubject(self, oTransient):
+ """
+ Worker for _formatEdgeOccurence that child classes overrides to format
+ their type of subject data in the best possible way.
+ """
+ _ = oTransient;
+ assert False;
+ return '';
+
+ def _formatEdgeOccurence(self, oTransient):
+ """
+ Helper for formatting the transients.
+ oTransient is of type ReportFailureReasonTransient or ReportTestCaseFailureTransient.
+ """
+ sHtml = u'<li>';
+ if oTransient.fEnter: sHtml += 'Since ';
+ else: sHtml += 'Until ';
+ sHtml += WuiSvnLinkWithTooltip(oTransient.iRevision, oTransient.sRepository, fBracketed = 'False').toHtml();
+ sHtml += u', %s: ' % (WuiTestSetLink(oTransient.idTestSet, self.formatTsShort(oTransient.tsDone),
+ fBracketed = False).toHtml(), )
+ sHtml += self._formatEdgeOccurenceSubject(oTransient);
+ sHtml += u'</li>\n';
+ return sHtml;
+
+ def _generateTransitionList(self, oSet):
+ """
+ Generates the enter and leave lists.
+ """
+ # Skip this if we're looking at builds.
+ if self._oModel.sSubject in [self._oModel.ksSubBuild,] and len(self._oModel.aidSubjects) in [1, 2]:
+ return u'';
+
+ sHtml = u'<h4>Movements:</h4>\n' \
+ u'<ul>\n';
+ if not oSet.aoEnterInfo and not oSet.aoLeaveInfo:
+ sHtml += u'<li>No changes</li>\n';
+ else:
+ for oTransient in oSet.aoEnterInfo:
+ sHtml += self._formatEdgeOccurence(oTransient);
+ for oTransient in oSet.aoLeaveInfo:
+ sHtml += self._formatEdgeOccurence(oTransient);
+ sHtml += u'</ul>\n';
+
+ return sHtml;
+
+
+ def _formatSeriesNameColumnHeadersForTable(self):
+ """ Formats the series name column for the HTML table. """
+ return '<th>Subject Name</th>';
+
+ def _formatSeriesNameForTable(self, oSet, idKey):
+ """ Formats the series name for the HTML table. """
+ _ = oSet;
+ return '<td>%d</td>' % (idKey,);
+
+ def _formatRowValueForTable(self, oRow, oPeriod, cColsPerSeries):
+ """ Formats a row value for the HTML table. """
+ _ = oPeriod;
+ if oRow is None:
+ return u'<td colspan="%d"> </td>' % (cColsPerSeries,);
+ if cColsPerSeries == 2:
+ return u'<td align="right">%u%%</td><td align="center">%u / %u</td>' \
+ % (oRow.cHits * 100 // oRow.cTotal, oRow.cHits, oRow.cTotal);
+ return u'<td align="center">%u</td>' % (oRow.cHits,);
+
+ def _formatSeriesTotalForTable(self, oSet, idKey, cColsPerSeries):
+ """ Formats the totals cell for a data series in the HTML table. """
+ dcTotalPerId = getattr(oSet, 'dcTotalPerId', None);
+ if cColsPerSeries == 2:
+ return u'<td align="right">%u%%</td><td align="center">%u/%u</td>' \
+ % (oSet.dcHitsPerId[idKey] * 100 // dcTotalPerId[idKey], oSet.dcHitsPerId[idKey], dcTotalPerId[idKey]);
+ return u'<td align="center">%u</td>' % (oSet.dcHitsPerId[idKey],);
+
+ def _generateTableForSet(self, oSet, aidSorted = None, iSortColumn = 0,
+ fWithTotals = True, cColsPerSeries = None):
+ """
+ Turns the set into a table.
+
+ Returns raw html.
+ """
+ sHtml = u'<table class="tmtbl-report-set" width="100%%">\n';
+ if cColsPerSeries is None:
+ cColsPerSeries = 2 if hasattr(oSet, 'dcTotalPerId') else 1;
+
+ # Header row.
+ sHtml += u' <tr><thead><th>#</th>';
+ sHtml += self._formatSeriesNameColumnHeadersForTable();
+ for iPeriod, oPeriod in enumerate(reversed(oSet.aoPeriods)):
+ sHtml += u'<th colspan="%d"><a href="javascript:ahrefActionSortByColumns(\'%s\',[%s]);">%s</a>%s</th>' \
+ % ( cColsPerSeries, self._oDisp.ksParamSortColumns, iPeriod, webutils.escapeElem(oPeriod.sDesc),
+ '&#x25bc;' if iPeriod == iSortColumn else '');
+ if fWithTotals:
+ sHtml += u'<th colspan="%d"><a href="javascript:ahrefActionSortByColumns(\'%s\',[%s]);">Total</a>%s</th>' \
+ % ( cColsPerSeries, self._oDisp.ksParamSortColumns, len(oSet.aoPeriods),
+ '&#x25bc;' if iSortColumn == len(oSet.aoPeriods) else '');
+ sHtml += u'</thead></td>\n';
+
+ # Each data series.
+ if aidSorted is None:
+ aidSorted = oSet.dSubjects.keys();
+ sHtml += u' <tbody>\n';
+ for iRow, idKey in enumerate(aidSorted):
+ sHtml += u' <tr class="%s">' % ('tmodd' if iRow & 1 else 'tmeven',);
+ sHtml += u'<td align="left">#%u</td>' % (iRow + 1,);
+ sHtml += self._formatSeriesNameForTable(oSet, idKey);
+ for oPeriod in reversed(oSet.aoPeriods):
+ oRow = oPeriod.dRowsById.get(idKey, None);
+ sHtml += self._formatRowValueForTable(oRow, oPeriod, cColsPerSeries);
+ if fWithTotals:
+ sHtml += self._formatSeriesTotalForTable(oSet, idKey, cColsPerSeries);
+ sHtml += u' </tr>\n';
+ sHtml += u' </tbody>\n';
+ sHtml += u'</table>\n';
+ return sHtml;
+
+
+class WuiReportFailuresWithTotalBase(WuiReportFailuresBase):
+ """
+ For ReportPeriodSetWithTotalBase.
+ """
+
+ def _formatSeriedNameForGraph(self, oSubject):
+ """
+ Format the subject name for the graph.
+ """
+ return str(oSubject);
+
+ def _getSortedIds(self, oSet):
+ """
+ Get default sorted subject IDs and which column.
+ """
+
+ # Figure the sorting column.
+ if self._aiSortColumns is not None \
+ and self._aiSortColumns \
+ and abs(self._aiSortColumns[0]) <= len(oSet.aoPeriods):
+ iSortColumn = abs(self._aiSortColumns[0]);
+ fByTotal = iSortColumn >= len(oSet.aoPeriods); # pylint: disable=unused-variable
+ elif oSet.cMaxTotal < 10:
+ iSortColumn = len(oSet.aoPeriods);
+ else:
+ iSortColumn = 0;
+
+ if iSortColumn >= len(oSet.aoPeriods):
+ # Sort the total.
+ aidSortedRaw = sorted(oSet.dSubjects,
+ key = lambda idKey: oSet.dcHitsPerId[idKey] * 10000 // oSet.dcTotalPerId[idKey],
+ reverse = True);
+ else:
+ # Sort by NOW column.
+ dTmp = {};
+ for idKey in oSet.dSubjects:
+ oRow = oSet.aoPeriods[-1 - iSortColumn].dRowsById.get(idKey, None);
+ if oRow is None: dTmp[idKey] = 0;
+ else: dTmp[idKey] = oRow.cHits * 10000 // max(1, oRow.cTotal);
+ aidSortedRaw = sorted(dTmp, key = lambda idKey: dTmp[idKey], reverse = True);
+ return (aidSortedRaw, iSortColumn);
+
+ def _generateGraph(self, oSet, sIdBase, aidSortedRaw):
+ """
+ Generates graph.
+ """
+ sHtml = u'';
+ fGenerateGraph = len(aidSortedRaw) <= 6 and len(aidSortedRaw) > 0; ## Make this configurable.
+ if fGenerateGraph:
+ # Figure the graph width for all of them.
+ uPctMax = max(oSet.uMaxPct, oSet.cMaxHits * 100 // oSet.cMaxTotal);
+ uPctMax = max(uPctMax + 2, 10);
+
+ for _, aidSorted in enumerate(self._splitSeriesIntoMultipleGraphs(aidSortedRaw, 8)):
+ asNames = [];
+ for idKey in aidSorted:
+ oSubject = oSet.dSubjects[idKey];
+ asNames.append(self._formatSeriedNameForGraph(oSubject));
+
+ oTable = WuiHlpGraphDataTable('Period', asNames);
+
+ for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
+ aiValues = [];
+ asValues = [];
+
+ for idKey in aidSorted:
+ oRow = oPeriod.dRowsById.get(idKey, None);
+ if oRow is not None:
+ aiValues.append(oRow.cHits * 100 // oRow.cTotal);
+ asValues.append(self.fmtPctWithHitsAndTotal(oRow.cHits, oRow.cTotal));
+ else:
+ aiValues.append(0);
+ asValues.append('0');
+
+ oTable.addRow(oPeriod.sDesc, aiValues, asValues);
+
+ if True: # pylint: disable=using-constant-test
+ aiValues = [];
+ asValues = [];
+ for idKey in aidSorted:
+ uPct = oSet.dcHitsPerId[idKey] * 100 // oSet.dcTotalPerId[idKey];
+ aiValues.append(uPct);
+ asValues.append(self.fmtPctWithHitsAndTotal(oSet.dcHitsPerId[idKey], oSet.dcTotalPerId[idKey]));
+ oTable.addRow('Totals', aiValues, asValues);
+
+ oGraph = WuiHlpBarGraph(sIdBase, oTable, self._oDisp);
+ oGraph.setRangeMax(uPctMax);
+ sHtml += '<br>\n';
+ sHtml += oGraph.renderGraph();
+ return sHtml;
+
+
+
+class WuiReportFailureReasons(WuiReportFailuresBase):
+ """
+ Generates a report displaying the failure reasons over time.
+ """
+
+ def _formatEdgeOccurenceSubject(self, oTransient):
+ return u'%s / %s' % ( webutils.escapeElem(oTransient.oReason.oCategory.sShort),
+ webutils.escapeElem(oTransient.oReason.sShort),);
+
+ def _formatSeriesNameColumnHeadersForTable(self):
+ return '<th>Failure Reason</th>';
+
+ def _formatSeriesNameForTable(self, oSet, idKey):
+ oReason = oSet.dSubjects[idKey];
+ sHtml = u'<td>';
+ sHtml += u'%s / %s' % ( webutils.escapeElem(oReason.oCategory.sShort), webutils.escapeElem(oReason.sShort),);
+ sHtml += u'</td>';
+ return sHtml;
+
+
+ def generateReportBody(self):
+ self._sTitle = 'Failure reasons';
+
+ #
+ # Get the data and sort the data series in descending order of badness.
+ #
+ oSet = self._oModel.getFailureReasons();
+ aidSortedRaw = sorted(oSet.dSubjects, key = lambda idReason: oSet.dcHitsPerId[idReason], reverse = True);
+
+ #
+ # Generate table and transition list. These are the most useful ones with the current graph machinery.
+ #
+ sHtml = self._generateTableForSet(oSet, aidSortedRaw, len(oSet.aoPeriods));
+ sHtml += self._generateTransitionList(oSet);
+
+ #
+ # Check if most of the stuff is without any assign reason, if so, skip
+ # that part of the graph so it doesn't offset the interesting bits.
+ #
+ fIncludeWithoutReason = True;
+ for oPeriod in reversed(oSet.aoPeriods):
+ if oPeriod.cWithoutReason > oSet.cMaxHits * 4:
+ fIncludeWithoutReason = False;
+ sHtml += '<p>Warning: Many failures without assigned reason!</p>\n';
+ break;
+
+ #
+ # Generate the graph.
+ #
+ fGenerateGraph = len(aidSortedRaw) <= 9 and len(aidSortedRaw) > 0; ## Make this configurable.
+ if fGenerateGraph:
+ aidSorted = aidSortedRaw;
+
+ asNames = [];
+ for idReason in aidSorted:
+ oReason = oSet.dSubjects[idReason];
+ asNames.append('%s / %s' % (oReason.oCategory.sShort, oReason.sShort,) )
+ if fIncludeWithoutReason:
+ asNames.append('No reason');
+
+ oTable = WuiHlpGraphDataTable('Period', asNames);
+
+ cMax = oSet.cMaxHits;
+ for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
+ aiValues = [];
+
+ for idReason in aidSorted:
+ oRow = oPeriod.dRowsById.get(idReason, None);
+ iValue = oRow.cHits if oRow is not None else 0;
+ aiValues.append(iValue);
+
+ if fIncludeWithoutReason:
+ aiValues.append(oPeriod.cWithoutReason);
+ if oPeriod.cWithoutReason > cMax:
+ cMax = oPeriod.cWithoutReason;
+
+ oTable.addRow(oPeriod.sDesc, aiValues);
+
+ oGraph = WuiHlpBarGraph('failure-reason', oTable, self._oDisp);
+ oGraph.setRangeMax(max(cMax + 1, 3));
+ sHtml += oGraph.renderGraph();
+ return sHtml;
+
+
+class WuiReportTestCaseFailures(WuiReportFailuresWithTotalBase):
+ """
+ Generates a report displaying the failure reasons over time.
+ """
+
+ def _formatEdgeOccurenceSubject(self, oTransient):
+ sHtml = u'%s ' % ( webutils.escapeElem(oTransient.oSubject.sName),);
+ sHtml += WuiTestCaseDetailsLink(oTransient.oSubject.idTestCase, fBracketed = False).toHtml();
+ return sHtml;
+
+ def _formatSeriesNameColumnHeadersForTable(self):
+ return '<th>Test Case</th>';
+
+ def _formatSeriesNameForTable(self, oSet, idKey):
+ oTestCase = oSet.dSubjects[idKey];
+ return u'<td>%s %s %s</td>' % \
+ ( WuiTestResultsForTestCaseLink(idKey, oTestCase.sName, self._dExtraTestResultsParams).toHtml(),
+ WuiTestCaseDetailsLink(oTestCase.idTestCase).toHtml(),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestCase, oTestCase.idTestCase,
+ dExtraParams = self._dExtraParams).toHtml(),);
+
+ def _formatSeriedNameForGraph(self, oSubject):
+ return oSubject.sName;
+
+ def generateReportBody(self):
+ self._sTitle = 'Test Case Failures';
+ oSet = self._oModel.getTestCaseFailures();
+ (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
+
+ sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
+ sHtml += self._generateTransitionList(oSet);
+ sHtml += self._generateGraph(oSet, 'testcase-graph', aidSortedRaw);
+ return sHtml;
+
+
+class WuiReportTestCaseArgsFailures(WuiReportFailuresWithTotalBase):
+ """
+ Generates a report displaying the failure reasons over time.
+ """
+
+ def __init__(self, oModel, dParams, fSubReport = False, aiSortColumns = None, fnDPrint = None, oDisp = None):
+ WuiReportFailuresWithTotalBase.__init__(self, oModel, dParams, fSubReport = fSubReport,
+ aiSortColumns = aiSortColumns, fnDPrint = fnDPrint, oDisp = oDisp);
+ self.oTestCaseCrit = TestResultFilter().aCriteria[TestResultFilter.kiTestCases] # type: FilterCriterion
+
+ @staticmethod
+ def _formatName(oTestCaseArgs):
+ """ Internal helper for formatting the testcase name. """
+ if oTestCaseArgs.sSubName:
+ sName = u'%s / %s' % ( oTestCaseArgs.oTestCase.sName, oTestCaseArgs.sSubName, );
+ else:
+ sName = u'%s / #%u' % ( oTestCaseArgs.oTestCase.sName, oTestCaseArgs.idTestCaseArgs, );
+ return sName;
+
+ def _formatEdgeOccurenceSubject(self, oTransient):
+ sHtml = u'%s ' % ( webutils.escapeElem(self._formatName(oTransient.oSubject)),);
+ sHtml += WuiTestCaseDetailsLink(oTransient.oSubject.idTestCase, fBracketed = False).toHtml();
+ return sHtml;
+
+ def _formatSeriesNameColumnHeadersForTable(self):
+ return '<th>Test Case / Variation</th>';
+
+ def _formatSeriesNameForTable(self, oSet, idKey):
+ oTestCaseArgs = oSet.dSubjects[idKey];
+ sHtml = u'<td>';
+ dParams = dict(self._dExtraTestResultsParams);
+ dParams[self.oTestCaseCrit.sVarNm] = oTestCaseArgs.idTestCase;
+ dParams[self.oTestCaseCrit.oSub.sVarNm] = idKey;
+ sHtml += WuiTestResultsForTestCaseLink(oTestCaseArgs.idTestCase, self._formatName(oTestCaseArgs), dParams).toHtml();
+ sHtml += u' ';
+ sHtml += WuiTestCaseDetailsLink(oTestCaseArgs.idTestCase).toHtml();
+ #sHtml += u' ';
+ #sHtml += WuiReportSummaryLink(ReportModelBase.ksSubTestCaseArgs, oTestCaseArgs.idTestCaseArgs,
+ # sName = self._formatName(oTestCaseArgs), dExtraParams = self._dExtraParams).toHtml();
+ sHtml += u'</td>';
+ return sHtml;
+
+ def _formatSeriedNameForGraph(self, oSubject):
+ return self._formatName(oSubject);
+
+ def generateReportBody(self):
+ self._sTitle = 'Test Case Variation Failures';
+ oSet = self._oModel.getTestCaseVariationFailures();
+ (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
+
+ sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
+ sHtml += self._generateTransitionList(oSet);
+ sHtml += self._generateGraph(oSet, 'testcasearg-graph', aidSortedRaw);
+ return sHtml;
+
+
+
+class WuiReportTestBoxFailures(WuiReportFailuresWithTotalBase):
+ """
+ Generates a report displaying the failure reasons over time.
+ """
+
+ def _formatEdgeOccurenceSubject(self, oTransient):
+ sHtml = u'%s ' % ( webutils.escapeElem(oTransient.oSubject.sName),);
+ sHtml += WuiTestBoxDetailsLinkShort(oTransient.oSubject).toHtml();
+ return sHtml;
+
+ def _formatSeriesNameColumnHeadersForTable(self):
+ return '<th colspan="5">Test Box</th>';
+
+ def _formatSeriesNameForTable(self, oSet, idKey):
+ oTestBox = oSet.dSubjects[idKey];
+ sHtml = u'<td>';
+ sHtml += WuiTestResultsForTestBoxLink(idKey, oTestBox.sName, self._dExtraTestResultsParams).toHtml()
+ sHtml += u' ';
+ sHtml += WuiTestBoxDetailsLinkShort(oTestBox).toHtml();
+ sHtml += u' ';
+ sHtml += WuiReportSummaryLink(ReportModelBase.ksSubTestBox, oTestBox.idTestBox,
+ dExtraParams = self._dExtraParams).toHtml();
+ sHtml += u'</td>';
+ sOsAndVer = '%s %s' % (oTestBox.sOs, oTestBox.sOsVersion.strip(),);
+ if len(sOsAndVer) < 22:
+ sHtml += u'<td>%s</td>' % (webutils.escapeElem(sOsAndVer),);
+ else: # wonder if td.title works..
+ sHtml += u'<td title="%s" width="1%%" style="white-space:nowrap;">%s...</td>' \
+ % (webutils.escapeAttr(sOsAndVer), webutils.escapeElem(sOsAndVer[:20]));
+ sHtml += u'<td>%s</td>' % (webutils.escapeElem(oTestBox.getArchBitString()),);
+ sHtml += u'<td>%s</td>' % (webutils.escapeElem(oTestBox.getPrettyCpuVendor()),);
+ sHtml += u'<td>%s' % (oTestBox.getPrettyCpuVersion(),);
+ if oTestBox.fCpuNestedPaging: sHtml += u', np';
+ elif oTestBox.fCpuHwVirt: sHtml += u', hw';
+ else: sHtml += u', raw';
+ if oTestBox.fCpu64BitGuest: sHtml += u', 64';
+ sHtml += u'</td>';
+ return sHtml;
+
+ def _formatSeriedNameForGraph(self, oSubject):
+ return oSubject.sName;
+
+ def generateReportBody(self):
+ self._sTitle = 'Test Box Failures';
+ oSet = self._oModel.getTestBoxFailures();
+ (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
+
+ sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
+ sHtml += self._generateTransitionList(oSet);
+ sHtml += self._generateGraph(oSet, 'testbox-graph', aidSortedRaw);
+ return sHtml;
+
+
+class WuiReportSummary(WuiReportBase):
+ """
+ Summary report.
+ """
+
+ def generateReportBody(self):
+ self._sTitle = 'Summary';
+ sHtml = '<p>This will display several reports and listings useful to get an overview of %s (id=%s).</p>' \
+ % (self._oModel.sSubject, self._oModel.aidSubjects,);
+
+ aoReports = [];
+
+ aoReports.append(WuiReportSuccessRate( self._oModel, self._dParams, fSubReport = True,
+ aiSortColumns = self._aiSortColumns,
+ fnDPrint = self._fnDPrint, oDisp = self._oDisp));
+ aoReports.append(WuiReportTestCaseFailures(self._oModel, self._dParams, fSubReport = True,
+ aiSortColumns = self._aiSortColumns,
+ fnDPrint = self._fnDPrint, oDisp = self._oDisp));
+ if self._oModel.sSubject == ReportModelBase.ksSubTestCase:
+ aoReports.append(WuiReportTestCaseArgsFailures(self._oModel, self._dParams, fSubReport = True,
+ aiSortColumns = self._aiSortColumns,
+ fnDPrint = self._fnDPrint, oDisp = self._oDisp));
+ aoReports.append(WuiReportTestBoxFailures( self._oModel, self._dParams, fSubReport = True,
+ aiSortColumns = self._aiSortColumns,
+ fnDPrint = self._fnDPrint, oDisp = self._oDisp));
+ aoReports.append(WuiReportFailureReasons( self._oModel, self._dParams, fSubReport = True,
+ aiSortColumns = self._aiSortColumns,
+ fnDPrint = self._fnDPrint, oDisp = self._oDisp));
+
+ for oReport in aoReports:
+ (sTitle, sContent) = oReport.show();
+ sHtml += '<br>'; # drop this layout hack
+ sHtml += '<div>';
+ sHtml += '<h3>%s</h3>\n' % (webutils.escapeElem(sTitle),);
+ sHtml += sContent;
+ sHtml += '</div>';
+
+ return sHtml;
+
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py b/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py
new file mode 100755
index 00000000..1edb03d5
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py
@@ -0,0 +1,965 @@
+# -*- coding: utf-8 -*-
+# $Id: wuitestresult.py $
+
+"""
+Test Manager WUI - Test Results.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Python imports.
+import datetime;
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiContentBase, WuiListContentBase, WuiHtmlBase, WuiTmLink, WuiLinkBase, \
+ WuiSvnLink, WuiSvnLinkWithTooltip, WuiBuildLogLink, WuiRawHtml, \
+ WuiHtmlKeeper;
+from testmanager.webui.wuimain import WuiMain;
+from testmanager.webui.wuihlpform import WuiHlpForm;
+from testmanager.webui.wuiadminfailurereason import WuiFailureReasonAddLink, WuiFailureReasonDetailsLink;
+from testmanager.webui.wuitestresultfailure import WuiTestResultFailureDetailsLink;
+from testmanager.core.failurereason import FailureReasonData, FailureReasonLogic;
+from testmanager.core.report import ReportGraphModel, ReportModelBase;
+from testmanager.core.testbox import TestBoxData;
+from testmanager.core.testcase import TestCaseData;
+from testmanager.core.testset import TestSetData;
+from testmanager.core.testgroup import TestGroupData;
+from testmanager.core.testresultfailures import TestResultFailureData;
+from testmanager.core.build import BuildData;
+from testmanager.core import db;
+from testmanager import config;
+from common import webutils, utils;
+
+
+class WuiTestSetLink(WuiTmLink):
+ """ Test set link. """
+
+ def __init__(self, idTestSet, sName = WuiContentBase.ksShortDetailsLink, fBracketed = False):
+ WuiTmLink.__init__(self, sName, WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
+ TestSetData.ksParam_idTestSet: idTestSet, }, fBracketed = fBracketed);
+ self.idTestSet = idTestSet;
+
+class WuiTestResultsForSomethingLink(WuiTmLink):
+ """ Test results link for a grouping. """
+
+ def __init__(self, sGroupedBy, idGroupMember, sName = WuiContentBase.ksShortTestResultsLink,
+ dExtraParams = None, fBracketed = False):
+ dParams = dict(dExtraParams) if dExtraParams else {};
+ dParams[WuiMain.ksParamAction] = sGroupedBy;
+ dParams[WuiMain.ksParamGroupMemberId] = idGroupMember;
+ WuiTmLink.__init__(self, sName, WuiMain.ksScriptName, dParams, fBracketed = fBracketed);
+
+
+class WuiTestResultsForTestBoxLink(WuiTestResultsForSomethingLink):
+ """ Test results link for a given testbox. """
+
+ def __init__(self, idTestBox, sName = WuiContentBase.ksShortTestResultsLink, dExtraParams = None, fBracketed = False):
+ WuiTestResultsForSomethingLink.__init__(self, WuiMain.ksActionResultsGroupedByTestBox, idTestBox,
+ sName = sName, dExtraParams = dExtraParams, fBracketed = fBracketed);
+
+
+class WuiTestResultsForTestCaseLink(WuiTestResultsForSomethingLink):
+ """ Test results link for a given testcase. """
+
+ def __init__(self, idTestCase, sName = WuiContentBase.ksShortTestResultsLink, dExtraParams = None, fBracketed = False):
+ WuiTestResultsForSomethingLink.__init__(self, WuiMain.ksActionResultsGroupedByTestCase, idTestCase,
+ sName = sName, dExtraParams = dExtraParams, fBracketed = fBracketed);
+
+
+class WuiTestResultsForBuildRevLink(WuiTestResultsForSomethingLink):
+ """ Test results link for a given build revision. """
+
+ def __init__(self, iRevision, sName = WuiContentBase.ksShortTestResultsLink, dExtraParams = None, fBracketed = False):
+ WuiTestResultsForSomethingLink.__init__(self, WuiMain.ksActionResultsGroupedByBuildRev, iRevision,
+ sName = sName, dExtraParams = dExtraParams, fBracketed = fBracketed);
+
+
+class WuiTestResult(WuiContentBase):
+ """Display test case result"""
+
+ def __init__(self, fnDPrint = None, oDisp = None):
+ WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
+
+ # Cyclic import hacks.
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ self.oWuiAdmin = WuiAdmin;
+
+ def _toHtml(self, oObject):
+ """Translate some object to HTML."""
+ if isinstance(oObject, WuiHtmlBase):
+ return oObject.toHtml();
+ if db.isDbTimestamp(oObject):
+ return webutils.escapeElem(self.formatTsShort(oObject));
+ if db.isDbInterval(oObject):
+ return webutils.escapeElem(self.formatIntervalShort(oObject));
+ if utils.isString(oObject):
+ return webutils.escapeElem(oObject);
+ return webutils.escapeElem(str(oObject));
+
+ def _htmlTable(self, aoTableContent):
+ """Generate HTML code for table"""
+ sHtml = u' <table class="tmtbl-testresult-details" width="100%%">\n';
+
+ for aoSubRows in aoTableContent:
+ if not aoSubRows:
+ continue; # Can happen if there is no testsuit.
+ oCaption = aoSubRows[0];
+ sHtml += u' \n' \
+ u' <tr class="tmtbl-result-details-caption">\n' \
+ u' <td colspan="2">%s</td>\n' \
+ u' </tr>\n' \
+ % (self._toHtml(oCaption),);
+
+ iRow = 0;
+ for aoRow in aoSubRows[1:]:
+ iRow += 1;
+ sHtml += u' <tr class="%s">\n' % ('tmodd' if iRow & 1 else 'tmeven',);
+ if len(aoRow) == 1:
+ sHtml += u' <td class="tmtbl-result-details-subcaption" colspan="2">%s</td>\n' \
+ % (self._toHtml(aoRow[0]),);
+ else:
+ sHtml += u' <th scope="row">%s</th>\n' % (webutils.escapeElem(aoRow[0]),);
+ if len(aoRow) > 2:
+ sHtml += u' <td>%s</td>\n' % (aoRow[2](aoRow[1]),);
+ else:
+ sHtml += u' <td>%s</td>\n' % (self._toHtml(aoRow[1]),);
+ sHtml += u' </tr>\n';
+
+ sHtml += u' </table>\n';
+
+ return sHtml
+
+ def _highlightStatus(self, sStatus):
+ """Return sStatus string surrounded by HTML highlight code """
+ sTmp = '<font color=%s><b>%s</b></font>' \
+ % ('red' if sStatus == 'failure' else 'green', webutils.escapeElem(sStatus.upper()))
+ return sTmp
+
+ def _anchorAndAppendBinaries(self, sBinaries, aoRows):
+ """ Formats each binary (if any) into a row with a download link. """
+ if sBinaries is not None:
+ for sBinary in sBinaries.split(','):
+ if not webutils.hasSchema(sBinary):
+ sBinary = config.g_ksBuildBinUrlPrefix + sBinary;
+ aoRows.append([WuiLinkBase(webutils.getFilename(sBinary), sBinary, fBracketed = False),]);
+ return aoRows;
+
+
+ def _formatEventTimestampHtml(self, tsEvent, tsLog, idEvent, oTestSet):
+ """ Formats an event timestamp with a main log link. """
+ tsEvent = db.dbTimestampToZuluDatetime(tsEvent);
+ #sFormattedTimestamp = u'%04u\u2011%02u\u2011%02u\u00a0%02u:%02u:%02uZ' \
+ # % ( tsEvent.year, tsEvent.month, tsEvent.day,
+ # tsEvent.hour, tsEvent.minute, tsEvent.second,);
+ sFormattedTimestamp = u'%02u:%02u:%02uZ' \
+ % ( tsEvent.hour, tsEvent.minute, tsEvent.second,);
+ sTitle = u'#%u - %04u\u2011%02u\u2011%02u\u00a0%02u:%02u:%02u.%06uZ' \
+ % ( idEvent, tsEvent.year, tsEvent.month, tsEvent.day,
+ tsEvent.hour, tsEvent.minute, tsEvent.second, tsEvent.microsecond, );
+ tsLog = db.dbTimestampToZuluDatetime(tsLog);
+ sFragment = u'%02u_%02u_%02u_%06u' % ( tsLog.hour, tsLog.minute, tsLog.second, tsLog.microsecond);
+ return WuiTmLink(sFormattedTimestamp, '',
+ { WuiMain.ksParamAction: WuiMain.ksActionViewLog,
+ WuiMain.ksParamLogSetId: oTestSet.idTestSet, },
+ sFragmentId = sFragment, sTitle = sTitle, fBracketed = False, ).toHtml();
+
+ def _recursivelyGenerateEvents(self, oTestResult, sParentName, sLineage, iRow,
+ iFailure, oTestSet, iDepth): # pylint: disable=too-many-locals
+ """
+ Recursively generate event table rows for the result set.
+
+ oTestResult is an object of the type TestResultDataEx.
+ """
+ # Hack: Replace empty outer test result name with (pretty) command line.
+ if iRow == 1:
+ sName = '';
+ sDisplayName = sParentName;
+ else:
+ sName = oTestResult.sName if sParentName == '' else '%s, %s' % (sParentName, oTestResult.sName,);
+ sDisplayName = webutils.escapeElem(sName);
+
+ # Format error count.
+ sErrCnt = '';
+ if oTestResult.cErrors > 0:
+ sErrCnt = ' (1 error)' if oTestResult.cErrors == 1 else ' (%d errors)' % oTestResult.cErrors;
+
+ # Format bits for adding or editing the failure reason. Level 0 is handled at the top of the page.
+ sChangeReason = '';
+ if oTestResult.cErrors > 0 and iDepth > 0 and self._oDisp is not None and not self._oDisp.isReadOnlyUser():
+ dTmp = {
+ self._oDisp.ksParamAction: self._oDisp.ksActionTestResultFailureAdd if oTestResult.oReason is None else
+ self._oDisp.ksActionTestResultFailureEdit,
+ TestResultFailureData.ksParam_idTestResult: oTestResult.idTestResult,
+ };
+ sChangeReason = ' <a href="?%s" class="tmtbl-edit-reason" onclick="addRedirectToAnchorHref(this)">%s</a> ' \
+ % ( webutils.encodeUrlParams(dTmp), WuiContentBase.ksShortEditLinkHtml );
+
+ # Format the include in graph checkboxes.
+ sLineage += ':%u' % (oTestResult.idStrName,);
+ sResultGraph = '<input type="checkbox" name="%s" value="%s%s" title="Include result in graph."/>' \
+ % (WuiMain.ksParamReportSubjectIds, ReportGraphModel.ksTypeResult, sLineage,);
+ sElapsedGraph = '';
+ if oTestResult.tsElapsed is not None:
+ sElapsedGraph = '<input type="checkbox" name="%s" value="%s%s" title="Include elapsed time in graph."/>' \
+ % ( WuiMain.ksParamReportSubjectIds, ReportGraphModel.ksTypeElapsed, sLineage);
+
+
+ if not oTestResult.aoChildren \
+ and len(oTestResult.aoValues) + len(oTestResult.aoMsgs) + len(oTestResult.aoFiles) == 0:
+ # Leaf - single row.
+ tsEvent = oTestResult.tsCreated;
+ if oTestResult.tsElapsed is not None:
+ tsEvent += oTestResult.tsElapsed;
+ sHtml = ' <tr class="%s tmtbl-events-leaf tmtbl-events-lvl%s tmstatusrow-%s" id="S%u">\n' \
+ ' <td id="E%u">%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td colspan="2"%s>%s%s%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult,
+ oTestResult.idTestResult,
+ self._formatEventTimestampHtml(tsEvent, oTestResult.tsCreated, oTestResult.idTestResult, oTestSet),
+ sElapsedGraph,
+ webutils.escapeElem(self.formatIntervalShort(oTestResult.tsElapsed)) if oTestResult.tsElapsed is not None
+ else '',
+ sDisplayName,
+ ' id="failure-%u"' % (iFailure,) if oTestResult.isFailure() else '',
+ webutils.escapeElem(oTestResult.enmStatus), webutils.escapeElem(sErrCnt),
+ sChangeReason if oTestResult.oReason is None else '',
+ sResultGraph );
+ iRow += 1;
+ else:
+ # Multiple rows.
+ sHtml = ' <tr class="%s tmtbl-events-first tmtbl-events-lvl%s ">\n' \
+ ' <td>%s</td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td colspan="2">%s</td>\n' \
+ ' <td></td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
+ self._formatEventTimestampHtml(oTestResult.tsCreated, oTestResult.tsCreated,
+ oTestResult.idTestResult, oTestSet),
+ sDisplayName,
+ 'running' if oTestResult.tsElapsed is None else '', );
+ iRow += 1;
+
+ # Depth. Check if our error count is just reflecting the one of our children.
+ cErrorsBelow = 0;
+ for oChild in oTestResult.aoChildren:
+ (sChildHtml, iRow, iFailure) = self._recursivelyGenerateEvents(oChild, sName, sLineage,
+ iRow, iFailure, oTestSet, iDepth + 1);
+ sHtml += sChildHtml;
+ cErrorsBelow += oChild.cErrors;
+
+ # Messages.
+ for oMsg in oTestResult.aoMsgs:
+ sHtml += ' <tr class="%s tmtbl-events-message tmtbl-events-lvl%s">\n' \
+ ' <td>%s</td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' <td colspan="3">%s: %s</td>\n' \
+ ' <td></td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
+ self._formatEventTimestampHtml(oMsg.tsCreated, oMsg.tsCreated, oMsg.idTestResultMsg, oTestSet),
+ webutils.escapeElem(oMsg.enmLevel),
+ webutils.escapeElem(oMsg.sMsg), );
+ iRow += 1;
+
+ # Values.
+ for oValue in oTestResult.aoValues:
+ sHtml += ' <tr class="%s tmtbl-events-value tmtbl-events-lvl%s">\n' \
+ ' <td>%s</td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td class="tmtbl-events-number">%s</td>\n' \
+ ' <td class="tmtbl-events-unit">%s</td>\n' \
+ ' <td><input type="checkbox" name="%s" value="%s%s:%u" title="Include value in graph."></td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
+ self._formatEventTimestampHtml(oValue.tsCreated, oValue.tsCreated, oValue.idTestResultValue, oTestSet),
+ webutils.escapeElem(oValue.sName),
+ utils.formatNumber(oValue.lValue).replace(' ', '&nbsp;'),
+ webutils.escapeElem(oValue.sUnit),
+ WuiMain.ksParamReportSubjectIds, ReportGraphModel.ksTypeValue, sLineage, oValue.idStrName, );
+ iRow += 1;
+
+ # Files.
+ for oFile in oTestResult.aoFiles:
+ if oFile.sMime in [ 'text/plain', ]:
+ aoLinks = [
+ WuiTmLink('%s (%s)' % (oFile.sFile, oFile.sKind), '',
+ { self._oDisp.ksParamAction: self._oDisp.ksActionViewLog,
+ self._oDisp.ksParamLogSetId: oTestSet.idTestSet,
+ self._oDisp.ksParamLogFileId: oFile.idTestResultFile, },
+ sTitle = oFile.sDescription),
+ WuiTmLink('View Raw', '',
+ { self._oDisp.ksParamAction: self._oDisp.ksActionGetFile,
+ self._oDisp.ksParamGetFileSetId: oTestSet.idTestSet,
+ self._oDisp.ksParamGetFileId: oFile.idTestResultFile,
+ self._oDisp.ksParamGetFileDownloadIt: False, },
+ sTitle = oFile.sDescription),
+ ]
+ else:
+ aoLinks = [
+ WuiTmLink('%s (%s)' % (oFile.sFile, oFile.sKind), '',
+ { self._oDisp.ksParamAction: self._oDisp.ksActionGetFile,
+ self._oDisp.ksParamGetFileSetId: oTestSet.idTestSet,
+ self._oDisp.ksParamGetFileId: oFile.idTestResultFile,
+ self._oDisp.ksParamGetFileDownloadIt: False, },
+ sTitle = oFile.sDescription),
+ ]
+ aoLinks.append(WuiTmLink('Download', '',
+ { self._oDisp.ksParamAction: self._oDisp.ksActionGetFile,
+ self._oDisp.ksParamGetFileSetId: oTestSet.idTestSet,
+ self._oDisp.ksParamGetFileId: oFile.idTestResultFile,
+ self._oDisp.ksParamGetFileDownloadIt: True, },
+ sTitle = oFile.sDescription));
+
+ sHtml += ' <tr class="%s tmtbl-events-file tmtbl-events-lvl%s">\n' \
+ ' <td>%s</td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' <td></td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
+ self._formatEventTimestampHtml(oFile.tsCreated, oFile.tsCreated, oFile.idTestResultFile, oTestSet),
+ '\n'.join(oLink.toHtml() for oLink in aoLinks),);
+ iRow += 1;
+
+ # Done?
+ if oTestResult.tsElapsed is not None:
+ tsEvent = oTestResult.tsCreated + oTestResult.tsElapsed;
+ sHtml += ' <tr class="%s tmtbl-events-final tmtbl-events-lvl%s tmstatusrow-%s" id="E%d">\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' <td colspan="2"%s>%s%s%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult,
+ self._formatEventTimestampHtml(tsEvent, tsEvent, oTestResult.idTestResult, oTestSet),
+ sElapsedGraph,
+ webutils.escapeElem(self.formatIntervalShort(oTestResult.tsElapsed)),
+ sDisplayName,
+ ' id="failure-%u"' % (iFailure,) if oTestResult.isFailure() else '',
+ webutils.escapeElem(oTestResult.enmStatus), webutils.escapeElem(sErrCnt),
+ sChangeReason if cErrorsBelow < oTestResult.cErrors and oTestResult.oReason is None else '',
+ sResultGraph);
+ iRow += 1;
+
+ # Failure reason.
+ if oTestResult.oReason is not None:
+ sReasonText = '%s / %s' % ( oTestResult.oReason.oFailureReason.oCategory.sShort,
+ oTestResult.oReason.oFailureReason.sShort, );
+ sCommentHtml = '';
+ if oTestResult.oReason.sComment and oTestResult.oReason.sComment.strip():
+ sCommentHtml = '<br>' + webutils.escapeElem(oTestResult.oReason.sComment.strip());
+ sCommentHtml = sCommentHtml.replace('\n', '<br>');
+
+ sDetailedReason = ' <a href="?%s" class="tmtbl-show-reason">%s</a>' \
+ % ( webutils.encodeUrlParams({ self._oDisp.ksParamAction:
+ self._oDisp.ksActionTestResultFailureDetails,
+ TestResultFailureData.ksParam_idTestResult:
+ oTestResult.idTestResult,}),
+ WuiContentBase.ksShortDetailsLinkHtml,);
+
+ sHtml += ' <tr class="%s tmtbl-events-reason tmtbl-events-lvl%s">\n' \
+ ' <td>%s</td>\n' \
+ ' <td colspan="2">%s</td>\n' \
+ ' <td colspan="3">%s%s%s%s</td>\n' \
+ ' <td>%s</td>\n' \
+ ' </tr>\n' \
+ % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
+ webutils.escapeElem(self.formatTsShort(oTestResult.oReason.tsEffective)),
+ oTestResult.oReason.oAuthor.sUsername,
+ webutils.escapeElem(sReasonText), sDetailedReason, sChangeReason,
+ sCommentHtml,
+ 'todo');
+ iRow += 1;
+
+ if oTestResult.isFailure():
+ iFailure += 1;
+
+ return (sHtml, iRow, iFailure);
+
+
+ def _generateMainReason(self, oTestResultTree, oTestSet):
+ """
+ Generates the form for displaying and updating the main failure reason.
+
+ oTestResultTree is an instance TestResultDataEx.
+ oTestSet is an instance of TestSetData.
+
+ """
+ _ = oTestSet;
+ sHtml = ' ';
+
+ if oTestResultTree.isFailure() or oTestResultTree.cErrors > 0:
+ sHtml += ' <h2>Failure Reason:</h2>\n';
+ oData = oTestResultTree.oReason;
+
+ # We need the failure reasons for the combobox.
+ aoFailureReasons = FailureReasonLogic(self._oDisp.getDb()).fetchForCombo('Test Sheriff, you figure out why!');
+ assert aoFailureReasons;
+
+ # For now we'll use the standard form helper.
+ sFormActionUrl = '%s?%s=%s' % ( self._oDisp.ksScriptName, self._oDisp.ksParamAction,
+ WuiMain.ksActionTestResultFailureAddPost if oData is None else
+ WuiMain.ksActionTestResultFailureEditPost )
+ fReadOnly = not self._oDisp or self._oDisp.isReadOnlyUser();
+ oForm = WuiHlpForm('failure-reason', sFormActionUrl,
+ sOnSubmit = WuiHlpForm.ksOnSubmit_AddReturnToFieldWithCurrentUrl, fReadOnly = fReadOnly);
+ oForm.addTextHidden(TestResultFailureData.ksParam_idTestResult, oTestResultTree.idTestResult);
+ oForm.addTextHidden(TestResultFailureData.ksParam_idTestSet, oTestSet.idTestSet);
+ if oData is not None:
+ oForm.addComboBox(TestResultFailureData.ksParam_idFailureReason, oData.idFailureReason, 'Reason',
+ aoFailureReasons,
+ sPostHtml = u' ' + WuiFailureReasonDetailsLink(oData.idFailureReason).toHtml()
+ + (u' ' + WuiFailureReasonAddLink('New', fBracketed = False).toHtml()
+ if not fReadOnly else u''));
+ oForm.addMultilineText(TestResultFailureData.ksParam_sComment, oData.sComment, 'Comment')
+
+ oForm.addNonText(u'%s (%s), %s'
+ % ( oData.oAuthor.sUsername, oData.oAuthor.sUsername,
+ self.formatTsShort(oData.tsEffective),),
+ 'Sheriff',
+ sPostHtml = ' ' + WuiTestResultFailureDetailsLink(oData.idTestResult, "Show Details").toHtml() )
+
+ oForm.addTextHidden(TestResultFailureData.ksParam_tsEffective, oData.tsEffective);
+ oForm.addTextHidden(TestResultFailureData.ksParam_tsExpire, oData.tsExpire);
+ oForm.addTextHidden(TestResultFailureData.ksParam_uidAuthor, oData.uidAuthor);
+ oForm.addSubmit('Change Reason');
+ else:
+ oForm.addComboBox(TestResultFailureData.ksParam_idFailureReason, -1, 'Reason', aoFailureReasons,
+ sPostHtml = ' ' + WuiFailureReasonAddLink('New').toHtml() if not fReadOnly else '');
+ oForm.addMultilineText(TestResultFailureData.ksParam_sComment, '', 'Comment');
+ oForm.addTextHidden(TestResultFailureData.ksParam_tsEffective, '');
+ oForm.addTextHidden(TestResultFailureData.ksParam_tsExpire, '');
+ oForm.addTextHidden(TestResultFailureData.ksParam_uidAuthor, '');
+ oForm.addSubmit('Add Reason');
+
+ sHtml += oForm.finalize();
+ return sHtml;
+
+
+ def showTestCaseResultDetails(self, # pylint: disable=too-many-locals,too-many-statements
+ oTestResultTree,
+ oTestSet,
+ oBuildEx,
+ oValidationKitEx,
+ oTestBox,
+ oTestGroup,
+ oTestCaseEx,
+ oTestVarEx):
+ """Show detailed result"""
+ def getTcDepsHtmlList(aoTestCaseData):
+ """Get HTML <ul> list of Test Case name items"""
+ if aoTestCaseData:
+ sTmp = '<ul>'
+ for oTestCaseData in aoTestCaseData:
+ sTmp += '<li>%s</li>' % (webutils.escapeElem(oTestCaseData.sName),);
+ sTmp += '</ul>'
+ else:
+ sTmp = 'No items'
+ return sTmp
+
+ def getGrDepsHtmlList(aoGlobalResourceData):
+ """Get HTML <ul> list of Global Resource name items"""
+ if aoGlobalResourceData:
+ sTmp = '<ul>'
+ for oGlobalResourceData in aoGlobalResourceData:
+ sTmp += '<li>%s</li>' % (webutils.escapeElem(oGlobalResourceData.sName),);
+ sTmp += '</ul>'
+ else:
+ sTmp = 'No items'
+ return sTmp
+
+
+ asHtml = []
+
+ from testmanager.webui.wuireport import WuiReportSummaryLink;
+ tsReportEffectiveDate = None;
+ if oTestSet.tsDone is not None:
+ tsReportEffectiveDate = oTestSet.tsDone + datetime.timedelta(days = 4);
+ if tsReportEffectiveDate >= self.getNowTs():
+ tsReportEffectiveDate = None;
+
+ # Test result + test set details.
+ aoResultRows = [
+ WuiHtmlKeeper([ WuiTmLink(oTestCaseEx.sName, self.oWuiAdmin.ksScriptName,
+ { self.oWuiAdmin.ksParamAction: self.oWuiAdmin.ksActionTestCaseDetails,
+ TestCaseData.ksParam_idTestCase: oTestCaseEx.idTestCase,
+ self.oWuiAdmin.ksParamEffectiveDate: oTestSet.tsConfig, },
+ fBracketed = False),
+ WuiTestResultsForTestCaseLink(oTestCaseEx.idTestCase),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestCase, oTestCaseEx.idTestCase,
+ tsNow = tsReportEffectiveDate, fBracketed = False),
+ ]),
+ ];
+ if oTestCaseEx.sDescription:
+ aoResultRows.append([oTestCaseEx.sDescription,]);
+ aoResultRows.append([ 'Status:', WuiRawHtml('<span class="tmspan-status-%s">%s</span>'
+ % (oTestResultTree.enmStatus, oTestResultTree.enmStatus,))]);
+ if oTestResultTree.cErrors > 0:
+ aoResultRows.append(( 'Errors:', oTestResultTree.cErrors ));
+ aoResultRows.append([ 'Elapsed:', oTestResultTree.tsElapsed ]);
+ cSecCfgTimeout = oTestCaseEx.cSecTimeout if oTestVarEx.cSecTimeout is None else oTestVarEx.cSecTimeout;
+ cSecEffTimeout = cSecCfgTimeout * oTestBox.pctScaleTimeout / 100;
+ aoResultRows.append([ 'Timeout:',
+ '%s (%s sec)' % (utils.formatIntervalSeconds2(cSecEffTimeout), cSecEffTimeout,) ]);
+ if cSecEffTimeout != cSecCfgTimeout:
+ aoResultRows.append([ 'Cfg Timeout:',
+ '%s (%s sec)' % (utils.formatIntervalSeconds(cSecCfgTimeout), cSecCfgTimeout,) ]);
+ aoResultRows += [
+ ( 'Started:', WuiTmLink(self.formatTsShort(oTestSet.tsCreated), WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionResultsUnGrouped,
+ WuiMain.ksParamEffectiveDate: oTestSet.tsCreated, },
+ fBracketed = False) ),
+ ];
+ if oTestSet.tsDone is not None:
+ aoResultRows += [ ( 'Done:',
+ WuiTmLink(self.formatTsShort(oTestSet.tsDone), WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionResultsUnGrouped,
+ WuiMain.ksParamEffectiveDate: oTestSet.tsDone, },
+ fBracketed = False) ) ];
+ else:
+ aoResultRows += [( 'Done:', 'Still running...')];
+ aoResultRows += [( 'Config:', oTestSet.tsConfig )];
+ if oTestVarEx.cGangMembers > 1:
+ aoResultRows.append([ 'Member No:', '#%s (of %s)' % (oTestSet.iGangMemberNo, oTestVarEx.cGangMembers) ]);
+
+ aoResultRows += [
+ ( 'Test Group:',
+ WuiHtmlKeeper([ WuiTmLink(oTestGroup.sName, self.oWuiAdmin.ksScriptName,
+ { self.oWuiAdmin.ksParamAction: self.oWuiAdmin.ksActionTestGroupDetails,
+ TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup,
+ self.oWuiAdmin.ksParamEffectiveDate: oTestSet.tsConfig, },
+ fBracketed = False),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestGroup, oTestGroup.idTestGroup,
+ tsNow = tsReportEffectiveDate, fBracketed = False),
+ ]), ),
+ ];
+ if oTestVarEx.sTestBoxReqExpr is not None:
+ aoResultRows.append([ 'TestBox reqs:', oTestVarEx.sTestBoxReqExpr ]);
+ elif oTestCaseEx.sTestBoxReqExpr is not None or oTestVarEx.sTestBoxReqExpr is not None:
+ aoResultRows.append([ 'TestBox reqs:', oTestCaseEx.sTestBoxReqExpr ]);
+ if oTestVarEx.sBuildReqExpr is not None:
+ aoResultRows.append([ 'Build reqs:', oTestVarEx.sBuildReqExpr ]);
+ elif oTestCaseEx.sBuildReqExpr is not None or oTestVarEx.sBuildReqExpr is not None:
+ aoResultRows.append([ 'Build reqs:', oTestCaseEx.sBuildReqExpr ]);
+ if oTestCaseEx.sValidationKitZips is not None and oTestCaseEx.sValidationKitZips != '@VALIDATIONKIT_ZIP@':
+ aoResultRows.append([ 'Validation Kit:', oTestCaseEx.sValidationKitZips ]);
+ if oTestCaseEx.aoDepTestCases:
+ aoResultRows.append([ 'Prereq. Test Cases:', oTestCaseEx.aoDepTestCases, getTcDepsHtmlList ]);
+ if oTestCaseEx.aoDepGlobalResources:
+ aoResultRows.append([ 'Global Resources:', oTestCaseEx.aoDepGlobalResources, getGrDepsHtmlList ]);
+
+ # Builds.
+ aoBuildRows = [];
+ if oBuildEx is not None:
+ aoBuildRows += [
+ WuiHtmlKeeper([ WuiTmLink('Build', self.oWuiAdmin.ksScriptName,
+ { self.oWuiAdmin.ksParamAction: self.oWuiAdmin.ksActionBuildDetails,
+ BuildData.ksParam_idBuild: oBuildEx.idBuild,
+ self.oWuiAdmin.ksParamEffectiveDate: oTestSet.tsCreated, },
+ fBracketed = False),
+ WuiTestResultsForBuildRevLink(oBuildEx.iRevision),
+ WuiReportSummaryLink(ReportModelBase.ksSubBuild, oBuildEx.idBuild,
+ tsNow = tsReportEffectiveDate, fBracketed = False), ]),
+ ];
+ self._anchorAndAppendBinaries(oBuildEx.sBinaries, aoBuildRows);
+ aoBuildRows += [
+ ( 'Revision:', WuiSvnLinkWithTooltip(oBuildEx.iRevision, oBuildEx.oCat.sRepository,
+ fBracketed = False) ),
+ ( 'Product:', oBuildEx.oCat.sProduct ),
+ ( 'Branch:', oBuildEx.oCat.sBranch ),
+ ( 'Type:', oBuildEx.oCat.sType ),
+ ( 'Version:', oBuildEx.sVersion ),
+ ( 'Created:', oBuildEx.tsCreated ),
+ ];
+ if oBuildEx.uidAuthor is not None:
+ aoBuildRows += [ ( 'Author ID:', oBuildEx.uidAuthor ), ];
+ if oBuildEx.sLogUrl is not None:
+ aoBuildRows += [ ( 'Log:', WuiBuildLogLink(oBuildEx.sLogUrl, fBracketed = False) ), ];
+
+ aoValidationKitRows = [];
+ if oValidationKitEx is not None:
+ aoValidationKitRows += [
+ WuiTmLink('Validation Kit', self.oWuiAdmin.ksScriptName,
+ { self.oWuiAdmin.ksParamAction: self.oWuiAdmin.ksActionBuildDetails,
+ BuildData.ksParam_idBuild: oValidationKitEx.idBuild,
+ self.oWuiAdmin.ksParamEffectiveDate: oTestSet.tsCreated, },
+ fBracketed = False),
+ ];
+ self._anchorAndAppendBinaries(oValidationKitEx.sBinaries, aoValidationKitRows);
+ aoValidationKitRows += [ ( 'Revision:', WuiSvnLink(oValidationKitEx.iRevision, fBracketed = False) ) ];
+ if oValidationKitEx.oCat.sProduct != 'VBox TestSuite':
+ aoValidationKitRows += [ ( 'Product:', oValidationKitEx.oCat.sProduct ), ];
+ if oValidationKitEx.oCat.sBranch != 'trunk':
+ aoValidationKitRows += [ ( 'Product:', oValidationKitEx.oCat.sBranch ), ];
+ if oValidationKitEx.oCat.sType != 'release':
+ aoValidationKitRows += [ ( 'Type:', oValidationKitEx.oCat.sType), ];
+ if oValidationKitEx.sVersion != '0.0.0':
+ aoValidationKitRows += [ ( 'Version:', oValidationKitEx.sVersion ), ];
+ aoValidationKitRows += [
+ ( 'Created:', oValidationKitEx.tsCreated ),
+ ];
+ if oValidationKitEx.uidAuthor is not None:
+ aoValidationKitRows += [ ( 'Author ID:', oValidationKitEx.uidAuthor ), ];
+ if oValidationKitEx.sLogUrl is not None:
+ aoValidationKitRows += [ ( 'Log:', WuiBuildLogLink(oValidationKitEx.sLogUrl, fBracketed = False) ), ];
+
+ # TestBox.
+ aoTestBoxRows = [
+ WuiHtmlKeeper([ WuiTmLink(oTestBox.sName, self.oWuiAdmin.ksScriptName,
+ { self.oWuiAdmin.ksParamAction: self.oWuiAdmin.ksActionTestBoxDetails,
+ TestBoxData.ksParam_idGenTestBox: oTestSet.idGenTestBox, },
+ fBracketed = False),
+ WuiTestResultsForTestBoxLink(oTestBox.idTestBox),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestBox, oTestSet.idTestBox,
+ tsNow = tsReportEffectiveDate, fBracketed = False),
+ ]),
+ ];
+ if oTestBox.sDescription:
+ aoTestBoxRows.append([oTestBox.sDescription, ]);
+ aoTestBoxRows += [
+ ( 'IP:', oTestBox.ip ),
+ #( 'UUID:', oTestBox.uuidSystem ),
+ #( 'Enabled:', oTestBox.fEnabled ),
+ #( 'Lom Kind:', oTestBox.enmLomKind ),
+ #( 'Lom IP:', oTestBox.ipLom ),
+ ( 'OS/Arch:', '%s.%s' % (oTestBox.sOs, oTestBox.sCpuArch) ),
+ ( 'OS Version:', oTestBox.sOsVersion ),
+ ( 'CPUs:', oTestBox.cCpus ),
+ ];
+ if oTestBox.sCpuName is not None:
+ aoTestBoxRows.append(['CPU Name', oTestBox.sCpuName.replace(' ', ' ')]);
+ if oTestBox.lCpuRevision is not None:
+ sMarch = oTestBox.queryCpuMicroarch();
+ if sMarch is not None:
+ aoTestBoxRows.append( ('CPU Microarch', sMarch) );
+ uFamily = oTestBox.getCpuFamily();
+ uModel = oTestBox.getCpuModel();
+ uStepping = oTestBox.getCpuStepping();
+ aoTestBoxRows += [
+ ( 'CPU Family', '%u (%#x)' % ( uFamily, uFamily, ) ),
+ ( 'CPU Model', '%u (%#x)' % ( uModel, uModel, ) ),
+ ( 'CPU Stepping', '%u (%#x)' % ( uStepping, uStepping, ) ),
+ ];
+ asFeatures = [ oTestBox.sCpuVendor, ];
+ if oTestBox.fCpuHwVirt is True: asFeatures.append(u'HW\u2011Virt');
+ if oTestBox.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
+ if oTestBox.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
+ if oTestBox.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
+ aoTestBoxRows += [
+ ( 'Features:', u' '.join(asFeatures) ),
+ ( 'RAM size:', '%s MB' % (oTestBox.cMbMemory,) ),
+ ( 'Scratch Size:', '%s MB' % (oTestBox.cMbScratch,) ),
+ ( 'Scale Timeout:', '%s%%' % (oTestBox.pctScaleTimeout,) ),
+ ( 'Script Rev:', WuiSvnLink(oTestBox.iTestBoxScriptRev, fBracketed = False) ),
+ ( 'Python:', oTestBox.formatPythonVersion() ),
+ ( 'Pending Command:', oTestBox.enmPendingCmd ),
+ ];
+
+ aoRows = [
+ aoResultRows,
+ aoBuildRows,
+ aoValidationKitRows,
+ aoTestBoxRows,
+ ];
+
+ asHtml.append(self._htmlTable(aoRows));
+
+ #
+ # Convert the tree to a list of events, values, message and files.
+ #
+ sHtmlEvents = '';
+ sHtmlEvents += '<table class="tmtbl-events" id="tmtbl-events" width="100%">\n';
+ sHtmlEvents += ' <tr class="tmheader">\n' \
+ ' <th>When</th>\n' \
+ ' <th></th>\n' \
+ ' <th>Elapsed</th>\n' \
+ ' <th>Event name</th>\n' \
+ ' <th colspan="2">Value (status)</th>' \
+ ' <th></th>\n' \
+ ' </tr>\n';
+ sPrettyCmdLine = '&nbsp;\\<br>&nbsp;&nbsp;&nbsp;&nbsp;\n'.join(webutils.escapeElem(oTestCaseEx.sBaseCmd
+ + ' '
+ + oTestVarEx.sArgs).split() );
+ (sTmp, _, cFailures) = self._recursivelyGenerateEvents(oTestResultTree, sPrettyCmdLine, '', 1, 0, oTestSet, 0);
+ sHtmlEvents += sTmp;
+
+ sHtmlEvents += '</table>\n'
+
+ #
+ # Put it all together.
+ #
+ sHtml = '<table class="tmtbl-testresult-details-base" width="100%">\n';
+ sHtml += ' <tr>\n'
+ sHtml += ' <td valign="top" width="20%%">\n%s\n</td>\n' % ' <br>\n'.join(asHtml);
+
+ sHtml += ' <td valign="top" width="80%" style="padding-left:6px">\n';
+ sHtml += self._generateMainReason(oTestResultTree, oTestSet);
+
+ sHtml += ' <h2>Events:</h2>\n';
+ sHtml += ' <form action="#" method="get" id="graph-form">\n' \
+ ' <input type="hidden" name="%s" value="%s"/>\n' \
+ ' <input type="hidden" name="%s" value="%u"/>\n' \
+ ' <input type="hidden" name="%s" value="%u"/>\n' \
+ ' <input type="hidden" name="%s" value="%u"/>\n' \
+ ' <input type="hidden" name="%s" value="%u"/>\n' \
+ % ( WuiMain.ksParamAction, WuiMain.ksActionGraphWiz,
+ WuiMain.ksParamGraphWizTestBoxIds, oTestBox.idTestBox,
+ WuiMain.ksParamGraphWizBuildCatIds, oBuildEx.idBuildCategory,
+ WuiMain.ksParamGraphWizTestCaseIds, oTestSet.idTestCase,
+ WuiMain.ksParamGraphWizSrcTestSetId, oTestSet.idTestSet,
+ );
+ if oTestSet.tsDone is not None:
+ sHtml += ' <input type="hidden" name="%s" value="%s"/>\n' \
+ % ( WuiMain.ksParamEffectiveDate, oTestSet.tsDone, );
+ sHtml += ' <p>\n';
+ sFormButton = '<button type="submit" onclick="%s">Show graphs</button>' \
+ % ( webutils.escapeAttr('addDynamicGraphInputs("graph-form", "main", "%s", "%s");'
+ % (WuiMain.ksParamGraphWizWidth, WuiMain.ksParamGraphWizDpi, )) );
+ sHtml += ' ' + sFormButton + '\n';
+ sHtml += ' %s %s %s\n' \
+ % ( WuiTmLink('Log File', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionViewLog,
+ WuiMain.ksParamLogSetId: oTestSet.idTestSet,
+ }),
+ WuiTmLink('Raw Log', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
+ WuiMain.ksParamGetFileSetId: oTestSet.idTestSet,
+ WuiMain.ksParamGetFileDownloadIt: False,
+ }),
+ WuiTmLink('Download Log', '',
+ { WuiMain.ksParamAction: WuiMain.ksActionGetFile,
+ WuiMain.ksParamGetFileSetId: oTestSet.idTestSet,
+ WuiMain.ksParamGetFileDownloadIt: True,
+ }),
+ );
+ sHtml += ' </p>\n';
+ if cFailures == 1:
+ sHtml += ' <p>%s</p>\n' % ( WuiTmLink('Jump to failure', '#failure-0'), )
+ elif cFailures > 1:
+ sHtml += ' <p>Jump to failure: ';
+ if cFailures <= 13:
+ for iFailure in range(0, cFailures):
+ sHtml += ' ' + WuiTmLink('#%u' % (iFailure,), '#failure-%u' % (iFailure,)).toHtml();
+ else:
+ for iFailure in range(0, 6):
+ sHtml += ' ' + WuiTmLink('#%u' % (iFailure,), '#failure-%u' % (iFailure,)).toHtml();
+ sHtml += ' ... ';
+ for iFailure in range(cFailures - 6, cFailures):
+ sHtml += ' ' + WuiTmLink('#%u' % (iFailure,), '#failure-%u' % (iFailure,)).toHtml();
+ sHtml += ' </p>\n';
+
+ sHtml += sHtmlEvents;
+ sHtml += ' <p>' + sFormButton + '</p>\n';
+ sHtml += ' </form>\n';
+ sHtml += ' </td>\n';
+
+ sHtml += ' </tr>\n';
+ sHtml += '</table>\n';
+
+ return ('Test Case result details', sHtml)
+
+
+class WuiGroupedResultList(WuiListContentBase):
+ """
+ WUI results content generator.
+ """
+
+ def __init__(self, aoEntries, cEntriesCount, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp,
+ aiSelectedSortColumns = None):
+ """Override initialization"""
+ WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
+ sTitle = 'Ungrouped (%d)' % cEntriesCount, sId = 'results',
+ fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
+
+ self._cEntriesCount = cEntriesCount
+
+ self._asColumnHeaders = [
+ 'Start',
+ 'Product Build',
+ 'Kit',
+ 'Box',
+ 'OS.Arch',
+ 'Test Case',
+ 'Elapsed',
+ 'Result',
+ 'Reason',
+ ];
+ self._asColumnAttribs = ['align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', 'align="center"', 'align="center"',
+ 'align="center"', ];
+
+
+ # Prepare parameter lists.
+ self._dTestBoxLinkParams = self._oDisp.getParameters();
+ self._dTestBoxLinkParams[WuiMain.ksParamAction] = WuiMain.ksActionResultsGroupedByTestBox;
+
+ self._dTestCaseLinkParams = self._oDisp.getParameters();
+ self._dTestCaseLinkParams[WuiMain.ksParamAction] = WuiMain.ksActionResultsGroupedByTestCase;
+
+ self._dRevLinkParams = self._oDisp.getParameters();
+ self._dRevLinkParams[WuiMain.ksParamAction] = WuiMain.ksActionResultsGroupedByBuildRev;
+
+
+
+ def _formatListEntry(self, iEntry):
+ """
+ Format *show all* table entry
+ """
+ oEntry = self._aoEntries[iEntry];
+
+ from testmanager.webui.wuiadmin import WuiAdmin;
+ from testmanager.webui.wuireport import WuiReportSummaryLink;
+
+ oValidationKit = None;
+ if oEntry.idBuildTestSuite is not None:
+ oValidationKit = WuiTmLink('r%s' % (oEntry.iRevisionTestSuite,),
+ WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDetails,
+ BuildData.ksParam_idBuild: oEntry.idBuildTestSuite },
+ fBracketed = False);
+
+ aoTestSetLinks = [];
+ aoTestSetLinks.append(WuiTmLink(oEntry.enmStatus,
+ WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
+ TestSetData.ksParam_idTestSet: oEntry.idTestSet },
+ fBracketed = False));
+ if oEntry.cErrors > 0:
+ aoTestSetLinks.append(WuiRawHtml('-'));
+ aoTestSetLinks.append(WuiTmLink('%d error%s' % (oEntry.cErrors, '' if oEntry.cErrors == 1 else 's', ),
+ WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionTestResultDetails,
+ TestSetData.ksParam_idTestSet: oEntry.idTestSet },
+ sFragmentId = 'failure-0', fBracketed = False));
+
+
+ self._dTestBoxLinkParams[WuiMain.ksParamGroupMemberId] = oEntry.idTestBox;
+ self._dTestCaseLinkParams[WuiMain.ksParamGroupMemberId] = oEntry.idTestCase;
+ self._dRevLinkParams[WuiMain.ksParamGroupMemberId] = oEntry.iRevision;
+
+ sTestBoxTitle = u'';
+ if oEntry.sCpuVendor is not None:
+ sTestBoxTitle += 'CPU vendor:\t%s\n' % ( oEntry.sCpuVendor, );
+ if oEntry.sCpuName is not None:
+ sTestBoxTitle += 'CPU name:\t%s\n' % ( ' '.join(oEntry.sCpuName.split()), );
+ if oEntry.sOsVersion is not None:
+ sTestBoxTitle += 'OS version:\t%s\n' % ( oEntry.sOsVersion, );
+ asFeatures = [];
+ if oEntry.fCpuHwVirt is True:
+ if oEntry.sCpuVendor is None:
+ asFeatures.append(u'HW\u2011Virt');
+ elif oEntry.sCpuVendor in ['AuthenticAMD',]:
+ asFeatures.append(u'HW\u2011Virt(AMD\u2011V)');
+ else:
+ asFeatures.append(u'HW\u2011Virt(VT\u2011x)');
+ if oEntry.fCpuNestedPaging is True: asFeatures.append(u'Nested\u2011Paging');
+ if oEntry.fCpu64BitGuest is True: asFeatures.append(u'64\u2011bit\u2011Guest');
+ #if oEntry.fChipsetIoMmu is True: asFeatures.append(u'I/O\u2011MMU');
+ sTestBoxTitle += u'CPU features:\t' + u', '.join(asFeatures);
+
+ # Testcase
+ if oEntry.sSubName:
+ sTestCaseName = '%s / %s' % (oEntry.sTestCaseName, oEntry.sSubName,);
+ else:
+ sTestCaseName = oEntry.sTestCaseName;
+
+ # Reason:
+ aoReasons = [];
+ for oIt in oEntry.aoFailureReasons:
+ sReasonTitle = 'Reason: \t%s\n' % ( oIt.oFailureReason.sShort, );
+ sReasonTitle += 'Category:\t%s\n' % ( oIt.oFailureReason.oCategory.sShort, );
+ sReasonTitle += 'Assigned:\t%s\n' % ( self.formatTsShort(oIt.tsFailureReasonAssigned), );
+ sReasonTitle += 'By User: \t%s\n' % ( oIt.oFailureReasonAssigner.sUsername, );
+ if oIt.sFailureReasonComment:
+ sReasonTitle += 'Comment: \t%s\n' % ( oIt.sFailureReasonComment, );
+ if oIt.oFailureReason.iTicket is not None and oIt.oFailureReason.iTicket > 0:
+ sReasonTitle += 'xTracker:\t#%s\n' % ( oIt.oFailureReason.iTicket, );
+ for i, sUrl in enumerate(oIt.oFailureReason.asUrls):
+ sUrl = sUrl.strip();
+ if sUrl:
+ sReasonTitle += 'URL#%u: \t%s\n' % ( i, sUrl, );
+ aoReasons.append(WuiTmLink(oIt.oFailureReason.sShort, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionFailureReasonDetails,
+ FailureReasonData.ksParam_idFailureReason: oIt.oFailureReason.idFailureReason },
+ sTitle = sReasonTitle));
+
+ return [
+ oEntry.tsCreated,
+ [ WuiTmLink('%s %s (%s)' % (oEntry.sProduct, oEntry.sVersion, oEntry.sType,),
+ WuiMain.ksScriptName, self._dRevLinkParams, sTitle = '%s' % (oEntry.sBranch,), fBracketed = False),
+ WuiSvnLinkWithTooltip(oEntry.iRevision, 'vbox'), ## @todo add sRepository TestResultListingData
+ WuiTmLink(self.ksShortDetailsLink, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionBuildDetails,
+ BuildData.ksParam_idBuild: oEntry.idBuild },
+ fBracketed = False),
+ ],
+ oValidationKit,
+ [ WuiTmLink(oEntry.sTestBoxName, WuiMain.ksScriptName, self._dTestBoxLinkParams, fBracketed = False,
+ sTitle = sTestBoxTitle),
+ WuiTmLink(self.ksShortDetailsLink, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestBoxDetails,
+ TestBoxData.ksParam_idTestBox: oEntry.idTestBox },
+ fBracketed = False),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestBox, oEntry.idTestBox, fBracketed = False), ],
+ '%s.%s' % (oEntry.sOs, oEntry.sArch),
+ [ WuiTmLink(sTestCaseName, WuiMain.ksScriptName, self._dTestCaseLinkParams, fBracketed = False,
+ sTitle = (oEntry.sBaseCmd + ' ' + oEntry.sArgs) if oEntry.sArgs else oEntry.sBaseCmd),
+ WuiTmLink(self.ksShortDetailsLink, WuiAdmin.ksScriptName,
+ { WuiAdmin.ksParamAction: WuiAdmin.ksActionTestCaseDetails,
+ TestCaseData.ksParam_idTestCase: oEntry.idTestCase },
+ fBracketed = False),
+ WuiReportSummaryLink(ReportModelBase.ksSubTestCase, oEntry.idTestCase, fBracketed = False), ],
+ oEntry.tsElapsed,
+ aoTestSetLinks,
+ aoReasons
+ ];
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuitestresultfailure.py b/src/VBox/ValidationKit/testmanager/webui/wuitestresultfailure.py
new file mode 100755
index 00000000..9ffd5518
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuitestresultfailure.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+# $Id: wuitestresultfailure.py $
+
+"""
+Test Manager WUI - Dummy Test Result Failure Reason Edit Dialog - just for error handling!
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Validation Kit imports.
+from testmanager.webui.wuicontentbase import WuiFormContentBase, WuiContentBase, WuiTmLink;
+from testmanager.webui.wuimain import WuiMain;
+from testmanager.webui.wuiadminfailurereason import WuiFailureReasonDetailsLink, WuiFailureReasonAddLink;
+from testmanager.core.testresultfailures import TestResultFailureData;
+from testmanager.core.testset import TestSetData;
+from testmanager.core.failurereason import FailureReasonLogic;
+
+
+
+class WuiTestResultFailureDetailsLink(WuiTmLink):
+ """ Link for adding a failure reason. """
+ def __init__(self, idTestResult, sName = WuiContentBase.ksShortDetailsLink, sTitle = None, fBracketed = None):
+ if fBracketed is None:
+ fBracketed = len(sName) > 2;
+ WuiTmLink.__init__(self, sName = sName,
+ sUrlBase = WuiMain.ksScriptName,
+ dParams = { WuiMain.ksParamAction: WuiMain.ksActionTestResultFailureDetails,
+ TestResultFailureData.ksParam_idTestResult: idTestResult, },
+ fBracketed = fBracketed,
+ sTitle = sTitle);
+ self.idTestResult = idTestResult;
+
+
+
+class WuiTestResultFailure(WuiFormContentBase):
+ """
+ WUI test result failure error form generator.
+ """
+ def __init__(self, oData, sMode, oDisp):
+ if sMode == WuiFormContentBase.ksMode_Add:
+ sTitle = 'Add Test Result Failure Reason';
+ elif sMode == WuiFormContentBase.ksMode_Edit:
+ sTitle = 'Modify Test Result Failure Reason';
+ else:
+ assert sMode == WuiFormContentBase.ksMode_Show;
+ sTitle = 'Test Result Failure Reason';
+ ## submit access.
+ WuiFormContentBase.__init__(self, oData, sMode, 'TestResultFailure', oDisp, sTitle);
+
+ def _populateForm(self, oForm, oData):
+
+ aoFailureReasons = FailureReasonLogic(self._oDisp.getDb()).fetchForCombo('Todo: Figure out why');
+ sPostHtml = '';
+ if oData.idFailureReason is not None and oData.idFailureReason >= 0:
+ sPostHtml += u' ' + WuiFailureReasonDetailsLink(oData.idFailureReason).toHtml();
+ sPostHtml += u' ' + WuiFailureReasonAddLink('New', fBracketed = False).toHtml();
+ oForm.addComboBox(TestResultFailureData.ksParam_idFailureReason, oData.idFailureReason,
+ 'Reason', aoFailureReasons, sPostHtml = sPostHtml);
+ oForm.addMultilineText(TestResultFailureData.ksParam_sComment, oData.sComment, 'Comment');
+ oForm.addIntRO( TestResultFailureData.ksParam_idTestResult, oData.idTestResult, 'Test Result ID');
+ oForm.addIntRO( TestResultFailureData.ksParam_idTestSet, oData.idTestSet, 'Test Set ID');
+ oForm.addTimestampRO(TestResultFailureData.ksParam_tsEffective, oData.tsEffective, 'Effective Date');
+ oForm.addTimestampRO(TestResultFailureData.ksParam_tsExpire, oData.tsExpire, 'Expire (excl)');
+ oForm.addIntRO( TestResultFailureData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID');
+ if self._sMode != WuiFormContentBase.ksMode_Show:
+ oForm.addSubmit('Add' if self._sMode == WuiFormContentBase.ksMode_Add else 'Modify');
+ return True;
+
+ def _generateTopRowFormActions(self, oData):
+ """
+ We add a way to get back to the test set to the actions.
+ """
+ aoActions = super(WuiTestResultFailure, self)._generateTopRowFormActions(oData);
+ if oData and oData.idTestResult and int(oData.idTestResult) > 0:
+ aoActions.append(WuiTmLink('Associated Test Set', WuiMain.ksScriptName,
+ { WuiMain.ksParamAction: WuiMain.ksActionTestSetDetailsFromResult,
+ TestSetData.ksParam_idTestResult: oData.idTestResult }
+ ));
+ return aoActions;
diff --git a/src/VBox/ValidationKit/testmanager/webui/wuivcshistory.py b/src/VBox/ValidationKit/testmanager/webui/wuivcshistory.py
new file mode 100755
index 00000000..1df9bbb2
--- /dev/null
+++ b/src/VBox/ValidationKit/testmanager/webui/wuivcshistory.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# $Id: wuivcshistory.py $
+
+"""
+Test Manager WUI - VCS history
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Python imports.
+#import datetime;
+
+# Validation Kit imports.
+from testmanager import config;
+from testmanager.core import db;
+from testmanager.webui.wuicontentbase import WuiContentBase;
+from common import webutils;
+
+class WuiVcsHistoryTooltip(WuiContentBase):
+ """
+ WUI VCS history tooltip generator.
+ """
+
+ def __init__(self, aoEntries, sRepository, iRevision, cEntries, fnDPrint, oDisp):
+ """Override initialization"""
+ WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
+ self.aoEntries = aoEntries;
+ self.sRepository = sRepository;
+ self.iRevision = iRevision;
+ self.cEntries = cEntries;
+
+
+ def show(self):
+ """
+ Generates the tooltip.
+ Returns (sTitle, HTML).
+ """
+ sHtml = '<div class="tmvcstimeline tmvcstimelinetooltip">\n';
+
+ oCurDate = None;
+ for oEntry in self.aoEntries:
+ oTsZulu = db.dbTimestampToZuluDatetime(oEntry.tsCreated);
+ if oCurDate is None or oCurDate != oTsZulu.date():
+ if oCurDate is not None:
+ sHtml += ' </dl>\n'
+ oCurDate = oTsZulu.date();
+ sHtml += ' <h2>%s:</h2>\n' \
+ ' <dl>\n' \
+ % (oTsZulu.strftime('%Y-%m-%d'),);
+
+ sEntry = ' <dt id="r%s">' % (oEntry.iRevision, );
+ sEntry += '<a href="%s" target="_blank">' \
+ % ( webutils.escapeAttr(config.g_ksTracChangsetUrlFmt
+ % { 'iRevision': oEntry.iRevision, 'sRepository': oEntry.sRepository,}), );
+
+ sEntry += '<span class="tmvcstimeline-time">%s</span>' % ( oTsZulu.strftime('%H:%MZ'), );
+ sEntry += ' Changeset <span class="tmvcstimeline-rev">[%s]</span>' % ( oEntry.iRevision, );
+ sEntry += ' by <span class="tmvcstimeline-author">%s</span>' % ( webutils.escapeElem(oEntry.sAuthor), );
+ sEntry += '</a>\n';
+ sEntry += '</dt>\n';
+ sEntry += ' <dd>%s</dd>\n' % ( webutils.escapeElem(oEntry.sMessage), );
+
+ sHtml += sEntry;
+
+ if oCurDate is not None:
+ sHtml += ' </dl>\n';
+ sHtml += '</div>\n';
+
+ return ('VCS History Tooltip', sHtml);
+
diff --git a/src/VBox/ValidationKit/tests/Makefile.kmk b/src/VBox/ValidationKit/tests/Makefile.kmk
new file mode 100644
index 00000000..fce3b9bf
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/Makefile.kmk
@@ -0,0 +1,60 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Include sub-makefiles.
+#
+include $(PATH_SUB_CURRENT)/additions/Makefile.kmk
+include $(PATH_SUB_CURRENT)/api/Makefile.kmk
+include $(PATH_SUB_CURRENT)/audio/Makefile.kmk
+include $(PATH_SUB_CURRENT)/autostart/Makefile.kmk
+include $(PATH_SUB_CURRENT)/benchmarks/Makefile.kmk
+include $(PATH_SUB_CURRENT)/cpu/Makefile.kmk
+include $(PATH_SUB_CURRENT)/network/Makefile.kmk
+include $(PATH_SUB_CURRENT)/selftests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/smoketests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/storage/Makefile.kmk
+include $(PATH_SUB_CURRENT)/teleportation/Makefile.kmk
+include $(PATH_SUB_CURRENT)/unittests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/installation/Makefile.kmk
+include $(PATH_SUB_CURRENT)/usb/Makefile.kmk
+include $(PATH_SUB_CURRENT)/serial/Makefile.kmk
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/__init__.py b/src/VBox/ValidationKit/tests/__init__.py
new file mode 100644
index 00000000..00fdf9ea
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Just to make python 2.x happy.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
diff --git a/src/VBox/ValidationKit/tests/additions/Makefile.kmk b/src/VBox/ValidationKit/tests/additions/Makefile.kmk
new file mode 100644
index 00000000..5af122d2
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/Makefile.kmk
@@ -0,0 +1,54 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Additions Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsAdditions
+ValidationKitTestsAdditions_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsAdditions_INST = $(INST_VALIDATIONKIT)tests/additions/
+ValidationKitTestsAdditions_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdAddBasic1.py \
+ $(PATH_SUB_CURRENT)/tdAddGuestCtrl.py \
+ $(PATH_SUB_CURRENT)/tdAddSharedFolders1.py
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsAdditions_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py b/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py
new file mode 100755
index 00000000..572e5048
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py
@@ -0,0 +1,649 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdAddBasic1.py $
+
+"""
+VirtualBox Validation Kit - Additions Basics #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import os;
+import sys;
+import uuid;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+# Sub-test driver imports.
+sys.path.append(os.path.dirname(os.path.abspath(__file__))); # For sub-test drivers.
+from tdAddGuestCtrl import SubTstDrvAddGuestCtrl;
+from tdAddSharedFolders1 import SubTstDrvAddSharedFolders1;
+
+
+class tdAddBasic1(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Additions Basics #1.
+ """
+ ## @todo
+ # - More of the settings stuff can be and need to be generalized!
+ #
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+ self.asTestsDef = ['install', 'guestprops', 'stdguestprops', 'guestcontrol', 'sharedfolders'];
+ self.asTests = self.asTestsDef;
+ self.asRsrcs = None
+ # The file we're going to use as a beacon to wait if the Guest Additions CD-ROM is ready.
+ self.sFileCdWait = '';
+ # Path pointing to the Guest Additions on the (V)ISO file.
+ self.sGstPathGaPrefix = '';
+
+ self.addSubTestDriver(SubTstDrvAddGuestCtrl(self));
+ self.addSubTestDriver(SubTstDrvAddSharedFolders1(self));
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ """ Shows this driver's command line options. """
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAddBasic1 Options:');
+ reporter.log(' --tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --quick');
+ reporter.log(' Same as --virt-modes hwvirt --cpu-counts 1.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--tests" value "%s" is not valid; valid values are: %s'
+ % (s, ' '.join(self.asTestsDef),));
+
+ elif asArgs[iArg] == '--quick':
+ self.parseOption(['--virt-modes', 'hwvirt'], 0);
+ self.parseOption(['--cpu-counts', '1'], 0);
+
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ if self.asRsrcs is None:
+ self.asRsrcs = []
+ for oSubTstDrv in self.aoSubTstDrvs:
+ self.asRsrcs.extend(oSubTstDrv.asRsrcs)
+ self.asRsrcs.extend(self.oTestVmSet.getResourceSet())
+ return self.asRsrcs
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ sGaIso = self.getGuestAdditionsIso();
+
+ # On 6.0 we merge the GAs with the ValidationKit so we can get at FsPerf.
+ #
+ # Note1: Not possible to do a double import as both images an '/OS2' dir.
+ # So, using same dir as with unattended VISOs for the valkit.
+ #
+ # Note2: We need to make sure that we don't change the location of the
+ # ValidationKit bits of the combined VISO, as this will break TXS' (TestExecService)
+ # automatic updating mechanism (uses hardcoded paths, e.g. "{CDROM}/linux/amd64/TestExecService").
+ #
+ ## @todo Find a solution for testing the automatic Guest Additions updates, which also looks at {CDROM}s root.
+ if self.fpApiVer >= 6.0:
+ sGaViso = os.path.join(self.sScratchPath, 'AdditionsAndValKit.viso');
+ ## @todo encode as bash cmd line:
+ sVisoContent = '--iprt-iso-maker-file-marker-bourne-sh %s ' \
+ '--import-iso \'%s\' ' \
+ '--push-iso \'%s\' ' \
+ '/vboxadditions=/ ' \
+ '--pop ' \
+ % (uuid.uuid4(), self.sVBoxValidationKitIso, sGaIso);
+ reporter.log2('Using VISO combining ValKit and GAs "%s": %s' % (sVisoContent, sGaViso));
+ with open(sGaViso, 'w') as oGaViso: # pylint: disable=unspecified-encoding
+ oGaViso.write(sVisoContent);
+ sGaIso = sGaViso;
+
+ self.sGstPathGaPrefix = 'vboxadditions';
+ else:
+ self.sGstPathGaPrefix = '';
+
+
+ reporter.log2('Path to Guest Additions on ISO is "%s"' % self.sGstPathGaPrefix);
+
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = sGaIso);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneCfg(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ fRc = False;
+
+ self.logVmInfo(oVM);
+
+ # We skip Linux Guest Additions testing for VBox < 6.1 for now.
+ fVersionIgnored = oTestVm.isLinux() and self.fpApiVer < 6.1;
+
+ if fVersionIgnored:
+ reporter.log('Skipping testing for "%s" because VBox version %s is ignored' % (oTestVm.sKind, self.fpApiVer,));
+ fRc = True;
+ else:
+ reporter.testStart('Waiting for TXS');
+ if oTestVm.isWindows():
+ self.sFileCdWait = ('%s/VBoxWindowsAdditions.exe' % (self.sGstPathGaPrefix,));
+ elif oTestVm.isLinux():
+ self.sFileCdWait = ('%s/VBoxLinuxAdditions.run' % (self.sGstPathGaPrefix,));
+
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True,
+ cMsCdWait = 5 * 60 * 1000,
+ sFileCdWait = self.sFileCdWait);
+ reporter.testDone();
+
+ if oSession is not None \
+ and oTxsSession is not None:
+ self.addTask(oTxsSession);
+ # Do the testing.
+ fSkip = 'install' not in self.asTests;
+ reporter.testStart('Install');
+ if not fSkip:
+ fRc, oTxsSession = self.testInstallAdditions(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ if not fSkip \
+ and not fRc:
+ reporter.log('Skipping following tests as Guest Additions were not installed successfully');
+ else:
+ fSkip = 'guestprops' not in self.asTests;
+ reporter.testStart('Guest Properties');
+ if not fSkip:
+ fRc = self.testGuestProperties(oSession, oTxsSession, oTestVm) and fRc;
+ reporter.testDone(fSkip);
+
+ fSkip = 'guestcontrol' not in self.asTests;
+ reporter.testStart('Guest Control');
+ if not fSkip:
+ fRc, oTxsSession = self.aoSubTstDrvs[0].testIt(oTestVm, oSession, oTxsSession);
+ reporter.testDone(fSkip);
+
+ fSkip = 'sharedfolders' not in self.asTests;
+ reporter.testStart('Shared Folders');
+ if not fSkip:
+ fRc, oTxsSession = self.aoSubTstDrvs[1].testIt(oTestVm, oSession, oTxsSession);
+ reporter.testDone(fSkip or fRc is None);
+
+ ## @todo Save and restore test.
+
+ ## @todo Reset tests.
+
+ ## @todo Final test: Uninstallation.
+
+ # Download the TxS (Test Execution Service) log. This is not fatal when not being present.
+ if not fRc:
+ self.txsDownloadFiles(oSession, oTxsSession,
+ [ (oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'vbox-txs-release.log'),
+ 'vbox-txs-%s.log' % oTestVm.sVmName) ],
+ fIgnoreErrors = True);
+
+ # Cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+
+ return fRc;
+
+ def testInstallAdditions(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests installing the guest additions
+ """
+ if oTestVm.isWindows():
+ (fRc, oTxsSession) = self.testWindowsInstallAdditions(oSession, oTxsSession, oTestVm);
+ elif oTestVm.isLinux():
+ (fRc, oTxsSession) = self.testLinuxInstallAdditions(oSession, oTxsSession, oTestVm);
+ else:
+ reporter.error('Guest Additions installation not implemented for %s yet! (%s)' %
+ (oTestVm.sKind, oTestVm.sVmName,));
+ fRc = False;
+
+ #
+ # Verify installation of Guest Additions using commmon bits.
+ #
+ if fRc:
+ #
+ # Check if the additions are operational.
+ #
+ try: oGuest = oSession.o.console.guest;
+ except:
+ reporter.errorXcpt('Getting IGuest failed.');
+ return (False, oTxsSession);
+
+ # Wait for the GAs to come up.
+ reporter.testStart('IGuest::additionsRunLevel');
+ fRc = self.testIGuest_additionsRunLevel(oSession, oTestVm, oGuest);
+ reporter.testDone();
+
+ # Check the additionsVersion attribute. It must not be empty.
+ reporter.testStart('IGuest::additionsVersion');
+ fRc = self.testIGuest_additionsVersion(oGuest) and fRc;
+ reporter.testDone();
+
+ # Check Guest Additions facilities
+ reporter.testStart('IGuest::getFacilityStatus');
+ fRc = self.testIGuest_getFacilityStatus(oTestVm, oGuest) and fRc;
+ reporter.testDone();
+
+ # Do a bit of diagnosis on error.
+ if not fRc:
+ if oTestVm.isLinux():
+ reporter.log('Boot log:');
+ sCmdJournalCtl = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'journalctl');
+ oTxsSession.syncExec(sCmdJournalCtl, (sCmdJournalCtl, '-b'), fIgnoreErrors = True);
+ reporter.log('Loaded processes:');
+ sCmdPs = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'ps');
+ oTxsSession.syncExec(sCmdPs, (sCmdPs, '-a', '-u', '-x'), fIgnoreErrors = True);
+ reporter.log('Kernel messages:');
+ sCmdDmesg = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'dmesg');
+ oTxsSession.syncExec(sCmdDmesg, (sCmdDmesg), fIgnoreErrors = True);
+ reporter.log('Loaded modules:');
+ sCmdLsMod = oTestVm.pathJoin(self.getGuestSystemAdminDir(oTestVm), 'lsmod');
+ oTxsSession.syncExec(sCmdLsMod, (sCmdLsMod), fIgnoreErrors = True);
+ elif oTestVm.isWindows() or oTestVm.isOS2():
+ sShell = self.getGuestSystemShell(oTestVm);
+ sShellOpt = '/C' if oTestVm.isWindows() or oTestVm.isOS2() else '-c';
+ reporter.log('Loaded processes:');
+ oTxsSession.syncExec(sShell, (sShell, sShellOpt, "tasklist.exe", "/FO", "CSV"), fIgnoreErrors = True);
+ reporter.log('Listing autostart entries:');
+ oTxsSession.syncExec(sShell, (sShell, sShellOpt, "wmic.exe", "startup", "get"), fIgnoreErrors = True);
+ reporter.log('Listing autostart entries:');
+ oTxsSession.syncExec(sShell, (sShell, sShellOpt, "dir",
+ oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'VBox*')),
+ fIgnoreErrors = True);
+ reporter.log('Downloading logs ...');
+ self.txsDownloadFiles(oSession, oTxsSession,
+ [ ( self.getGuestVBoxTrayClientLogFile(oTestVm),
+ 'ga-vboxtrayclient-%s.log' % (oTestVm.sVmName,),),
+ ( "C:\\Documents and Settings\\All Users\\Application Data\\Microsoft\\Dr Watson\\drwtsn32.log",
+ 'ga-drwatson-%s.log' % (oTestVm.sVmName,), ),
+ ],
+ fIgnoreErrors = True);
+
+ return (fRc, oTxsSession);
+
+ def getGuestVBoxTrayClientLogFile(self, oTestVm):
+ """ Gets the path on the guest for the (release) log file of VBoxTray / VBoxClient. """
+ if oTestVm.isWindows():
+ return oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'VBoxTray.log');
+
+ return oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'VBoxClient.log');
+
+ def setGuestEnvVar(self, oSession, oTxsSession, oTestVm, sName, sValue):
+ """ Sets a system-wide environment variable on the guest. Only supports Windows guests so far. """
+ _ = oSession;
+ if oTestVm.isWindows():
+ sPathRegExe = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'reg.exe');
+ self.txsRunTest(oTxsSession, ('Set env var \"%s\"' % (sName,)),
+ 30 * 1000, sPathRegExe,
+ (sPathRegExe, 'add',
+ '"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"', '/v',
+ sName, '/t', 'REG_EXPAND_SZ', '/d', sValue, '/f'));
+
+ def testWindowsInstallAdditions(self, oSession, oTxsSession, oTestVm):
+ """
+ Installs the Windows guest additions using the test execution service.
+ Since this involves rebooting the guest, we will have to create a new TXS session.
+ """
+
+ # Set system-wide env vars to enable release logging on some applications.
+ self.setGuestEnvVar(oSession, oTxsSession, oTestVm, 'VBOXTRAY_RELEASE_LOG', 'all.e.l.l2.l3.f');
+ self.setGuestEnvVar(oSession, oTxsSession, oTestVm, 'VBOXTRAY_RELEASE_LOG_FLAGS', 'time thread group append');
+ self.setGuestEnvVar(oSession, oTxsSession, oTestVm, 'VBOXTRAY_RELEASE_LOG_DEST',
+ ('file=%s' % (self.getGuestVBoxTrayClientLogFile(oTestVm),)));
+
+ #
+ # Install the public signing key.
+ #
+ if oTestVm.sKind not in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003'):
+ fRc = self.txsRunTest(oTxsSession, 'VBoxCertUtil.exe', 1 * 60 * 1000,
+ '${CDROM}/%s/cert/VBoxCertUtil.exe' % self.sGstPathGaPrefix,
+ ('${CDROM}/%s/cert/VBoxCertUtil.exe' % self.sGstPathGaPrefix, 'add-trusted-publisher',
+ '${CDROM}/%s/cert/vbox-sha1.cer' % self.sGstPathGaPrefix),
+ fCheckSessionStatus = True);
+ if not fRc:
+ reporter.error('Error installing SHA1 certificate');
+ else:
+ fRc = self.txsRunTest(oTxsSession, 'VBoxCertUtil.exe', 1 * 60 * 1000,
+ '${CDROM}/%s/cert/VBoxCertUtil.exe' % self.sGstPathGaPrefix,
+ ('${CDROM}/%s/cert/VBoxCertUtil.exe' % self.sGstPathGaPrefix, 'add-trusted-publisher',
+ '${CDROM}/%s/cert/vbox-sha256.cer' % self.sGstPathGaPrefix), fCheckSessionStatus = True);
+ if not fRc:
+ reporter.error('Error installing SHA256 certificate');
+
+ #
+ # Delete relevant log files.
+ #
+ # Note! On some guests the files in question still can be locked by the OS, so ignore
+ # deletion errors from the guest side (e.g. sharing violations) and just continue.
+ #
+ sWinDir = self.getGuestWinDir(oTestVm);
+ aasLogFiles = [
+ ( oTestVm.pathJoin(sWinDir, 'setupapi.log'), 'ga-setupapi-%s.log' % (oTestVm.sVmName,), ),
+ ( oTestVm.pathJoin(sWinDir, 'setupact.log'), 'ga-setupact-%s.log' % (oTestVm.sVmName,), ),
+ ( oTestVm.pathJoin(sWinDir, 'setuperr.log'), 'ga-setuperr-%s.log' % (oTestVm.sVmName,), ),
+ ];
+
+ # Apply The SetupAPI logging level so that we also get the (most verbose) setupapi.dev.log file.
+ ## @todo !!! HACK ALERT !!! Add the value directly into the testing source image. Later.
+ sRegExe = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'reg.exe');
+ fHaveSetupApiDevLog = self.txsRunTest(oTxsSession, 'Enabling setupapi.dev.log', 30 * 1000,
+ sRegExe,
+ (sRegExe, 'add',
+ '"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup"',
+ '/v', 'LogLevel', '/t', 'REG_DWORD', '/d', '0xFF'),
+ fCheckSessionStatus = True);
+
+ for sGstFile, _ in aasLogFiles:
+ self.txsRmFile(oSession, oTxsSession, sGstFile, 10 * 1000, fIgnoreErrors = True);
+
+ # Enable installing the optional auto-logon modules (VBoxGINA/VBoxCredProv).
+ # Also tell the installer to produce the appropriate log files.
+ sExe = '${CDROM}/%s/VBoxWindowsAdditions.exe' % self.sGstPathGaPrefix;
+ asArgs = [ sExe, '/S', '/l', '/with_autologon' ];
+
+ # Determine if we need to force installing the legacy timestamp CA to make testing succeed.
+ # Note: Don't force installing when the Guest Additions installer should do this automatically,
+ # i.e, only force it for Windows Server 2016 and up.
+ fForceInstallTimeStampCA = False;
+ if self.fpApiVer >= 6.1 \
+ and oTestVm.getNonCanonicalGuestOsType() \
+ in [ 'Windows2016', 'Windows2019', 'Windows2022', 'Windows11' ]:
+ fForceInstallTimeStampCA = True;
+
+ # As we don't have a console command line to parse for the Guest Additions installer (only a message box) and
+ # unknown / unsupported parameters get ignored with silent installs anyway, we safely can add the following parameter(s)
+ # even if older Guest Addition installers might not support those.
+ if fForceInstallTimeStampCA:
+ asArgs.extend([ '/install_timestamp_ca' ]);
+
+ #
+ # Do the actual install.
+ #
+ fRc = self.txsRunTest(oTxsSession, 'VBoxWindowsAdditions.exe', 5 * 60 * 1000,
+ sExe, asArgs, fCheckSessionStatus = True);
+
+ # Add the Windows Guest Additions installer files to the files we want to download
+ # from the guest. Note: There won't be a install_ui.log because of the silent installation.
+ sGuestAddsDir = 'C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\';
+ aasLogFiles.append((sGuestAddsDir + 'install.log', 'ga-install-%s.log' % (oTestVm.sVmName,),));
+ aasLogFiles.append((sGuestAddsDir + 'install_drivers.log', 'ga-install_drivers-%s.log' % (oTestVm.sVmName,),));
+ aasLogFiles.append(('C:\\Windows\\setupapi.log', 'ga-setupapi-%s.log' % (oTestVm.sVmName,),));
+
+ # Note: setupapi.dev.log only is available since Windows 2000.
+ if fHaveSetupApiDevLog:
+ aasLogFiles.append(('C:\\Windows\\setupapi.dev.log', 'ga-setupapi.dev-%s.log' % (oTestVm.sVmName,),));
+
+ #
+ # Download log files.
+ # Ignore errors as all files above might not be present (or in different locations)
+ # on different Windows guests.
+ #
+ self.txsDownloadFiles(oSession, oTxsSession, aasLogFiles, fIgnoreErrors = True);
+
+ #
+ # Reboot the VM and reconnect the TXS session.
+ #
+ if fRc:
+ reporter.testStart('Rebooting guest w/ updated Guest Additions active');
+ (fRc, oTxsSession) = self.txsRebootAndReconnectViaTcp(oSession, oTxsSession, cMsTimeout = 15 * 60 * 1000);
+ if fRc:
+ pass;
+ else:
+ reporter.testFailure('Rebooting and reconnecting to TXS service failed');
+ reporter.testDone();
+ else:
+ reporter.error('Error installing Windows Guest Additions (installer returned with exit code <> 0)')
+
+ return (fRc, oTxsSession);
+
+ def getAdditionsInstallerResult(self, oTxsSession):
+ """
+ Extracts the Guest Additions installer exit code from a run before.
+ Assumes that nothing else has been run on the same TXS session in the meantime.
+ """
+ iRc = 0;
+ (_, sOpcode, abPayload) = oTxsSession.getLastReply();
+ if sOpcode.startswith('PROC NOK '): # Extract process rc
+ iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
+ ## @todo Parse more statuses here.
+ return iRc;
+
+ def testLinuxInstallAdditions(self, oSession, oTxsSession, oTestVm):
+ #
+ # The actual install.
+ # Also tell the installer to produce the appropriate log files.
+ #
+ # Make sure to add "--nox11" to the makeself wrapper in order to not getting any blocking
+ # xterm window spawned.
+ fRc = self.txsRunTest(oTxsSession, 'VBoxLinuxAdditions.run', 30 * 60 * 1000,
+ self.getGuestSystemShell(oTestVm),
+ (self.getGuestSystemShell(oTestVm),
+ '${CDROM}/%s/VBoxLinuxAdditions.run' % self.sGstPathGaPrefix, '--nox11'));
+ if not fRc:
+ iRc = self.getAdditionsInstallerResult(oTxsSession);
+ # Check for rc == 0 just for completeness.
+ if iRc in (0, 2): # Can happen if the GA installer has detected older VBox kernel modules running and needs a reboot.
+ reporter.log('Guest has old(er) VBox kernel modules still running; requires a reboot');
+ fRc = True;
+
+ if not fRc:
+ reporter.error('Installing Linux Additions failed (isSuccess=%s, lastReply=%s, see log file for details)'
+ % (oTxsSession.isSuccess(), oTxsSession.getLastReply()));
+
+ #
+ # Download log files.
+ # Ignore errors as all files above might not be present for whatever reason.
+ #
+ self.txsDownloadFiles(oSession, oTxsSession,
+ [('/var/log/vboxadd-install.log', 'vboxadd-install-%s.log' % oTestVm.sVmName), ],
+ fIgnoreErrors = True);
+
+ # Do the final reboot to get the just installed Guest Additions up and running.
+ if fRc:
+ reporter.testStart('Rebooting guest w/ updated Guest Additions active');
+ (fRc, oTxsSession) = self.txsRebootAndReconnectViaTcp(oSession, oTxsSession, cMsTimeout = 15 * 60 * 1000);
+ if fRc:
+ pass
+ else:
+ reporter.testFailure('Rebooting and reconnecting to TXS service failed');
+ reporter.testDone();
+
+ return (fRc, oTxsSession);
+
+ def testIGuest_additionsRunLevel(self, oSession, oTestVm, oGuest):
+ """
+ Do run level tests.
+ """
+
+ _ = oGuest;
+
+ if oTestVm.isWindows():
+ if oTestVm.isLoggedOntoDesktop():
+ eExpectedRunLevel = vboxcon.AdditionsRunLevelType_Desktop;
+ else:
+ eExpectedRunLevel = vboxcon.AdditionsRunLevelType_Userland;
+ else:
+ ## @todo VBoxClient does not have facility statuses implemented yet.
+ eExpectedRunLevel = vboxcon.AdditionsRunLevelType_Userland;
+
+ return self.waitForGAs(oSession, aenmWaitForRunLevels = [ eExpectedRunLevel ]);
+
+ def testIGuest_additionsVersion(self, oGuest):
+ """
+ Returns False if no version string could be obtained, otherwise True
+ even though errors are logged.
+ """
+ try:
+ sVer = oGuest.additionsVersion;
+ except:
+ reporter.errorXcpt('Getting the additions version failed.');
+ return False;
+ reporter.log('IGuest::additionsVersion="%s"' % (sVer,));
+
+ if sVer.strip() == '':
+ reporter.error('IGuest::additionsVersion is empty.');
+ return False;
+
+ if sVer != sVer.strip():
+ reporter.error('IGuest::additionsVersion is contains spaces: "%s".' % (sVer,));
+
+ asBits = sVer.split('.');
+ if len(asBits) < 3:
+ reporter.error('IGuest::additionsVersion does not contain at least tree dot separated fields: "%s" (%d).'
+ % (sVer, len(asBits)));
+
+ ## @todo verify the format.
+ return True;
+
+ def checkFacilityStatus(self, oGuest, eFacilityType, sDesc, fMustSucceed = True):
+ """
+ Prints the current status of a Guest Additions facility.
+
+ Return success status.
+ """
+
+ fRc = True;
+
+ try:
+ eStatus, tsLastUpdatedMs = oGuest.getFacilityStatus(eFacilityType);
+ except:
+ if fMustSucceed:
+ reporter.errorXcpt('Getting facility status for "%s" failed' % (sDesc,));
+ fRc = False;
+ else:
+ if eStatus == vboxcon.AdditionsFacilityStatus_Inactive:
+ sStatus = "INACTIVE";
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Paused:
+ sStatus = "PAUSED";
+ elif eStatus == vboxcon.AdditionsFacilityStatus_PreInit:
+ sStatus = "PREINIT";
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Init:
+ sStatus = "INIT";
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Active:
+ sStatus = "ACTIVE";
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Terminating:
+ sStatus = "TERMINATING";
+ fRc = not fMustSucceed;
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Terminated:
+ sStatus = "TERMINATED";
+ fRc = not fMustSucceed;
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Failed:
+ sStatus = "FAILED";
+ fRc = not fMustSucceed;
+ elif eStatus == vboxcon.AdditionsFacilityStatus_Unknown:
+ sStatus = "UNKNOWN";
+ fRc = not fMustSucceed;
+ else:
+ sStatus = "???";
+ fRc = not fMustSucceed;
+
+ reporter.log('Guest Additions facility "%s": %s (last updated: %sms)' % (sDesc, sStatus, str(tsLastUpdatedMs)));
+ if fMustSucceed \
+ and not fRc:
+ reporter.error('Guest Additions facility "%s" did not report expected status (is "%s")' % (sDesc, sStatus));
+
+ return fRc;
+
+ def testIGuest_getFacilityStatus(self, oTestVm, oGuest):
+ """
+ Checks Guest Additions facilities for their status.
+
+ Returns success status.
+ """
+
+ reporter.testStart('Status VBoxGuest Driver');
+ fRc = self.checkFacilityStatus(oGuest, vboxcon.AdditionsFacilityType_VBoxGuestDriver, "VBoxGuest Driver");
+ reporter.testDone();
+
+ reporter.testStart('Status VBoxService');
+ fRc = self.checkFacilityStatus(oGuest, vboxcon.AdditionsFacilityType_VBoxService, "VBoxService") and fRc;
+ reporter.testDone();
+
+ if oTestVm.isWindows():
+ if oTestVm.isLoggedOntoDesktop():
+ ## @todo VBoxClient does not have facility statuses implemented yet.
+ reporter.testStart('Status VBoxTray / VBoxClient');
+ fRc = self.checkFacilityStatus(oGuest, vboxcon.AdditionsFacilityType_VBoxTrayClient,
+ "VBoxTray / VBoxClient") and fRc;
+ reporter.testDone();
+ ## @todo Add more.
+
+ return fRc;
+
+ def testGuestProperties(self, oSession, oTxsSession, oTestVm):
+ """
+ Test guest properties.
+ """
+ _ = oSession; _ = oTxsSession; _ = oTestVm;
+ return True;
+
+if __name__ == '__main__':
+ sys.exit(tdAddBasic1().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py b/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py
new file mode 100755
index 00000000..0b4ef4bf
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py
@@ -0,0 +1,5503 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# pylint: disable=too-many-lines
+
+"""
+VirtualBox Validation Kit - Guest Control Tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155473 $"
+
+# Standard Python imports.
+import errno
+import os
+import random
+import string
+import struct
+import sys
+import threading
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import testfileset;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxtestfileset;
+from testdriver import vboxwrappers;
+from common import utils;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int # pylint: disable=redefined-builtin,invalid-name
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+def limitString(sString, cLimit = 128):
+ """
+ Returns a string with ellipsis ("...") when exceeding the specified limit.
+ Useful for toning down logging. By default strings will be shortened at 128 characters.
+ """
+ if not isinstance(sString, str):
+ sString = str(sString);
+ cLen = len(sString);
+ if not cLen:
+ return '';
+ return (sString[:cLimit] + '...[%d more]' % (cLen - cLimit)) if cLen > cLimit else sString;
+
+class GuestStream(bytearray):
+ """
+ Class for handling a guest process input/output stream.
+
+ @todo write stdout/stderr tests.
+ """
+ def appendStream(self, stream, convertTo = '<b'):
+ """
+ Appends and converts a byte sequence to this object;
+ handy for displaying a guest stream.
+ """
+ self.extend(struct.pack(convertTo, stream));
+
+
+class tdCtxCreds(object):
+ """
+ Provides credentials to pass to the guest.
+ """
+ def __init__(self, sUser = None, sPassword = None, sDomain = None):
+ self.oTestVm = None;
+ self.sUser = sUser;
+ self.sPassword = sPassword;
+ self.sDomain = sDomain;
+
+ def applyDefaultsIfNotSet(self, oTestVm):
+ """
+ Applies credential defaults, based on the test VM (guest OS), if
+ no credentials were set yet.
+
+ Returns success status.
+ """
+ self.oTestVm = oTestVm;
+ if not self.oTestVm:
+ reporter.log('VM object is invalid -- did VBoxSVC or a client crash?');
+ return False;
+
+ if self.sUser is None:
+ self.sUser = self.oTestVm.getTestUser();
+
+ if self.sPassword is None:
+ self.sPassword = self.oTestVm.getTestUserPassword(self.sUser);
+
+ if self.sDomain is None:
+ self.sDomain = '';
+
+ return True;
+
+class tdTestGuestCtrlBase(object):
+ """
+ Base class for all guest control tests.
+
+ Note: This test ASSUMES that working Guest Additions
+ were installed and running on the guest to be tested.
+ """
+ def __init__(self, oCreds = None):
+ self.oGuest = None; ##< IGuest.
+ self.oTestVm = None;
+ self.oCreds = oCreds ##< type: tdCtxCreds
+ self.timeoutMS = 30 * 1000; ##< 30s timeout
+ self.oGuestSession = None; ##< IGuestSession reference or None.
+
+ def setEnvironment(self, oSession, oTxsSession, oTestVm):
+ """
+ Sets the test environment required for this test.
+
+ Returns success status.
+ """
+ _ = oTxsSession;
+
+ fRc = True;
+ try:
+ self.oGuest = oSession.o.console.guest;
+ self.oTestVm = oTestVm;
+ except:
+ fRc = reporter.errorXcpt();
+
+ if self.oCreds is None:
+ self.oCreds = tdCtxCreds();
+
+ fRc = fRc and self.oCreds.applyDefaultsIfNotSet(self.oTestVm);
+
+ if not fRc:
+ reporter.log('Error setting up Guest Control testing environment!');
+
+ return fRc;
+
+ def uploadLogData(self, oTstDrv, aData, sFileName, sDesc):
+ """
+ Uploads (binary) data to a log file for manual (later) inspection.
+ """
+ reporter.log('Creating + uploading log data file "%s"' % sFileName);
+ sHstFileName = os.path.join(oTstDrv.sScratchPath, sFileName);
+ try:
+ with open(sHstFileName, "wb") as oCurTestFile:
+ oCurTestFile.write(aData);
+ except:
+ return reporter.error('Unable to create temporary file for "%s"' % (sDesc,));
+ return reporter.addLogFile(sHstFileName, 'misc/other', sDesc);
+
+ def createSession(self, sName, fIsError = True):
+ """
+ Creates (opens) a guest session.
+ Returns (True, IGuestSession) on success or (False, None) on failure.
+ """
+ if self.oGuestSession is None:
+ if sName is None:
+ sName = "<untitled>";
+
+ reporter.log('Creating session "%s" ...' % (sName,));
+ try:
+ self.oGuestSession = self.oGuest.createSession(self.oCreds.sUser,
+ self.oCreds.sPassword,
+ self.oCreds.sDomain,
+ sName);
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Creating a guest session "%s" failed; sUser="%s", pw="%s", sDomain="%s":'
+ % (sName, self.oCreds.sUser, self.oCreds.sPassword, self.oCreds.sDomain));
+ return (False, None);
+
+ tsStartMs = base.timestampMilli();
+ while base.timestampMilli() - tsStartMs < self.timeoutMS:
+ reporter.log('Waiting for session "%s" to start within %dms...' % (sName, self.timeoutMS));
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start, ];
+ try:
+ waitResult = self.oGuestSession.waitForArray(aeWaitFor, self.timeoutMS);
+
+ # Log session status changes.
+ if waitResult is vboxcon.GuestSessionWaitResult_Status:
+ reporter.log('Session "%s" indicated status change (status is now %d)' \
+ % (sName, self.oGuestSession.status));
+ if self.oGuestSession.status is vboxcon.GuestSessionStatus_Started:
+ # We indicate an error here, as we intentionally waited for the session start
+ # in the wait call above and got back a status change instead.
+ reporter.error('Session "%s" successfully started (thru status change)' % (sName,));
+ break;
+ continue; # Continue waiting for the session to start.
+
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult not in (vboxcon.GuestSessionWaitResult_Start, \
+ vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErr(fIsError, 'Session did not start successfully, returned wait result: %d' \
+ % (waitResult,));
+ return (False, None);
+ reporter.log('Session "%s" successfully started' % (sName,));
+
+ #
+ # Make sure that the test VM configuration and Guest Control use the same path separator style for the guest.
+ #
+ sGstSep = '\\' if self.oGuestSession.pathStyle is vboxcon.PathStyle_DOS else '/';
+ if self.oTestVm.pathSep() != sGstSep:
+ reporter.error('Configured test VM uses a different path style (%s) than Guest Control (%s)' \
+ % (self.oTestVm.pathSep(), sGstSep));
+ break;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Waiting for guest session "%s" (usr=%s;pw=%s;dom=%s) to start failed:'
+ % (sName, self.oCreds.sUser, self.oCreds.sPassword, self.oCreds.sDomain,));
+ return (False, None);
+ else:
+ reporter.log('Warning: Session already set; this is probably not what you want');
+ return (True, self.oGuestSession);
+
+ def setSession(self, oGuestSession):
+ """
+ Sets the current guest session and closes
+ an old one if necessary.
+ """
+ if self.oGuestSession is not None:
+ self.closeSession();
+ self.oGuestSession = oGuestSession;
+ return self.oGuestSession;
+
+ def closeSession(self, fIsError = True):
+ """
+ Closes the guest session.
+ """
+ if self.oGuestSession is not None:
+ try:
+ sName = self.oGuestSession.name;
+ except:
+ return reporter.errorXcpt();
+
+ reporter.log('Closing session "%s" ...' % (sName,));
+ try:
+ self.oGuestSession.close();
+ self.oGuestSession = None;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Closing guest session "%s" failed:' % (sName,));
+ return False;
+ return True;
+
+class tdTestCopyFrom(tdTestGuestCtrlBase):
+ """
+ Test for copying files from the guest to the host.
+ """
+ def __init__(self, sSrc = "", sDst = "", oCreds = None, afFlags = None, oSrc = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sSrc = sSrc;
+ self.sDst = sDst;
+ self.afFlags = afFlags;
+ self.oSrc = oSrc # type: testfileset.TestFsObj
+ if oSrc and not sSrc:
+ self.sSrc = oSrc.sPath;
+
+class tdTestCopyFromDir(tdTestCopyFrom):
+
+ def __init__(self, sSrc = "", sDst = "", oCreds = None, afFlags = None, oSrc = None, fIntoDst = False):
+ tdTestCopyFrom.__init__(self, sSrc, sDst, oCreds, afFlags, oSrc);
+ self.fIntoDst = fIntoDst; # hint to the verification code that sDst == oSrc, rather than sDst+oSrc.sNAme == oSrc.
+
+class tdTestCopyFromFile(tdTestCopyFrom):
+ pass;
+
+class tdTestRemoveHostDir(object):
+ """
+ Test step that removes a host directory tree.
+ """
+ def __init__(self, sDir):
+ self.sDir = sDir;
+
+ def execute(self, oTstDrv, oVmSession, oTxsSession, oTestVm, sMsgPrefix):
+ _ = oTstDrv; _ = oVmSession; _ = oTxsSession; _ = oTestVm; _ = sMsgPrefix;
+ if os.path.exists(self.sDir):
+ if base.wipeDirectory(self.sDir) != 0:
+ return False;
+ try:
+ os.rmdir(self.sDir);
+ except:
+ return reporter.errorXcpt('%s: sDir=%s' % (sMsgPrefix, self.sDir,));
+ return True;
+
+
+
+class tdTestCopyTo(tdTestGuestCtrlBase):
+ """
+ Test for copying files from the host to the guest.
+ """
+ def __init__(self, sSrc = "", sDst = "", oCreds = None, afFlags = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sSrc = sSrc;
+ self.sDst = sDst;
+ self.afFlags = afFlags;
+
+class tdTestCopyToFile(tdTestCopyTo):
+ pass;
+
+class tdTestCopyToDir(tdTestCopyTo):
+ pass;
+
+class tdTestDirCreate(tdTestGuestCtrlBase):
+ """
+ Test for directoryCreate call.
+ """
+ def __init__(self, sDirectory = "", oCreds = None, fMode = 0, afFlags = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sDirectory = sDirectory;
+ self.fMode = fMode;
+ self.afFlags = afFlags;
+
+class tdTestDirCreateTemp(tdTestGuestCtrlBase):
+ """
+ Test for the directoryCreateTemp call.
+ """
+ def __init__(self, sDirectory = "", sTemplate = "", oCreds = None, fMode = 0, fSecure = False):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sDirectory = sDirectory;
+ self.sTemplate = sTemplate;
+ self.fMode = fMode;
+ self.fSecure = fSecure;
+
+class tdTestDirOpen(tdTestGuestCtrlBase):
+ """
+ Test for the directoryOpen call.
+ """
+ def __init__(self, sDirectory = "", oCreds = None, sFilter = "", afFlags = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sDirectory = sDirectory;
+ self.sFilter = sFilter;
+ self.afFlags = afFlags or [];
+
+class tdTestDirRead(tdTestDirOpen):
+ """
+ Test for the opening, reading and closing a certain directory.
+ """
+ def __init__(self, sDirectory = "", oCreds = None, sFilter = "", afFlags = None):
+ tdTestDirOpen.__init__(self, sDirectory, oCreds, sFilter, afFlags);
+
+class tdTestExec(tdTestGuestCtrlBase):
+ """
+ Specifies exactly one guest control execution test.
+ Has a default timeout of 5 minutes (for safety).
+ """
+ def __init__(self, sCmd = "", asArgs = None, aEnv = None, afFlags = None, # pylint: disable=too-many-arguments
+ timeoutMS = 5 * 60 * 1000, oCreds = None, fWaitForExit = True):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sCmd = sCmd;
+ self.asArgs = asArgs if asArgs is not None else [sCmd,];
+ self.aEnv = aEnv;
+ self.afFlags = afFlags or [];
+ self.timeoutMS = timeoutMS;
+ self.fWaitForExit = fWaitForExit;
+ self.uExitStatus = 0;
+ self.iExitCode = 0;
+ self.cbStdOut = 0;
+ self.cbStdErr = 0;
+ self.sBuf = '';
+
+class tdTestFileExists(tdTestGuestCtrlBase):
+ """
+ Test for the file exists API call (fileExists).
+ """
+ def __init__(self, sFile = "", oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+
+class tdTestFileRemove(tdTestGuestCtrlBase):
+ """
+ Test querying guest file information.
+ """
+ def __init__(self, sFile = "", oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+
+class tdTestRemoveBase(tdTestGuestCtrlBase):
+ """
+ Removal base.
+ """
+ def __init__(self, sPath, fRcExpect = True, oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sPath = sPath;
+ self.fRcExpect = fRcExpect;
+
+ def execute(self, oSubTstDrv):
+ """
+ Executes the test, returns True/False.
+ """
+ _ = oSubTstDrv;
+ return True;
+
+ def checkRemoved(self, sType):
+ """ Check that the object was removed using fObjExists. """
+ try:
+ fExists = self.oGuestSession.fsObjExists(self.sPath, False);
+ except:
+ return reporter.errorXcpt('fsObjExists failed on "%s" after deletion (type: %s)' % (self.sPath, sType));
+ if fExists:
+ return reporter.error('fsObjExists says "%s" still exists after deletion (type: %s)!' % (self.sPath, sType));
+ return True;
+
+class tdTestRemoveFile(tdTestRemoveBase):
+ """
+ Remove a single file.
+ """
+ def __init__(self, sPath, fRcExpect = True, oCreds = None):
+ tdTestRemoveBase.__init__(self, sPath, fRcExpect, oCreds);
+
+ def execute(self, oSubTstDrv):
+ reporter.log2('Deleting file "%s" ...' % (limitString(self.sPath),));
+ try:
+ if oSubTstDrv.oTstDrv.fpApiVer >= 5.0:
+ self.oGuestSession.fsObjRemove(self.sPath);
+ else:
+ self.oGuestSession.fileRemove(self.sPath);
+ except:
+ reporter.maybeErrXcpt(self.fRcExpect, 'Removing "%s" failed' % (self.sPath,));
+ return not self.fRcExpect;
+ if not self.fRcExpect:
+ return reporter.error('Expected removing "%s" to failed, but it succeeded' % (self.sPath,));
+
+ return self.checkRemoved('file');
+
+class tdTestRemoveDir(tdTestRemoveBase):
+ """
+ Remove a single directory if empty.
+ """
+ def __init__(self, sPath, fRcExpect = True, oCreds = None):
+ tdTestRemoveBase.__init__(self, sPath, fRcExpect, oCreds);
+
+ def execute(self, oSubTstDrv):
+ _ = oSubTstDrv;
+ reporter.log2('Deleting directory "%s" ...' % (limitString(self.sPath),));
+ try:
+ self.oGuestSession.directoryRemove(self.sPath);
+ except:
+ reporter.maybeErrXcpt(self.fRcExpect, 'Removing "%s" (as a directory) failed' % (self.sPath,));
+ return not self.fRcExpect;
+ if not self.fRcExpect:
+ return reporter.error('Expected removing "%s" (dir) to failed, but it succeeded' % (self.sPath,));
+
+ return self.checkRemoved('directory');
+
+class tdTestRemoveTree(tdTestRemoveBase):
+ """
+ Recursively remove a directory tree.
+ """
+ def __init__(self, sPath, afFlags = None, fRcExpect = True, fNotExist = False, oCreds = None):
+ tdTestRemoveBase.__init__(self, sPath, fRcExpect, oCreds = None);
+ self.afFlags = afFlags if afFlags is not None else [];
+ self.fNotExist = fNotExist; # Hack for the ContentOnly scenario where the dir does not exist.
+
+ def execute(self, oSubTstDrv):
+ reporter.log2('Deleting tree "%s" ...' % (limitString(self.sPath),));
+ try:
+ oProgress = self.oGuestSession.directoryRemoveRecursive(self.sPath, self.afFlags);
+ except:
+ reporter.maybeErrXcpt(self.fRcExpect, 'Removing directory tree "%s" failed (afFlags=%s)'
+ % (self.sPath, self.afFlags));
+ return not self.fRcExpect;
+
+ oWrappedProgress = vboxwrappers.ProgressWrapper(oProgress, oSubTstDrv.oTstDrv.oVBoxMgr, oSubTstDrv.oTstDrv,
+ "remove-tree: %s" % (self.sPath,));
+ oWrappedProgress.wait();
+ if not oWrappedProgress.isSuccess():
+ oWrappedProgress.logResult(fIgnoreErrors = not self.fRcExpect);
+ return not self.fRcExpect;
+ if not self.fRcExpect:
+ return reporter.error('Expected removing "%s" (tree) to failed, but it succeeded' % (self.sPath,));
+
+ if vboxcon.DirectoryRemoveRecFlag_ContentAndDir not in self.afFlags and not self.fNotExist:
+ # Cannot use directoryExists here as it is buggy.
+ try:
+ if oSubTstDrv.oTstDrv.fpApiVer >= 5.0:
+ oFsObjInfo = self.oGuestSession.fsObjQueryInfo(self.sPath, False);
+ else:
+ oFsObjInfo = self.oGuestSession.fileQueryInfo(self.sPath);
+ eType = oFsObjInfo.type;
+ except:
+ return reporter.errorXcpt('sPath=%s' % (self.sPath,));
+ if eType != vboxcon.FsObjType_Directory:
+ return reporter.error('Found file type %d, expected directory (%d) for %s after rmtree/OnlyContent'
+ % (eType, vboxcon.FsObjType_Directory, self.sPath,));
+ return True;
+
+ return self.checkRemoved('tree');
+
+
+class tdTestFileStat(tdTestGuestCtrlBase):
+ """
+ Test querying guest file information.
+ """
+ def __init__(self, sFile = "", oCreds = None, cbSize = 0, eFileType = 0):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+ self.cbSize = cbSize;
+ self.eFileType = eFileType;
+
+class tdTestFileIO(tdTestGuestCtrlBase):
+ """
+ Test for the IGuestFile object.
+ """
+ def __init__(self, sFile = "", oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+
+class tdTestFileQuerySize(tdTestGuestCtrlBase):
+ """
+ Test for the file size query API call (fileQuerySize).
+ """
+ def __init__(self, sFile = "", oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+
+class tdTestFileOpen(tdTestGuestCtrlBase):
+ """
+ Tests opening a guest files.
+ """
+ def __init__(self, sFile = "", eAccessMode = None, eAction = None, eSharing = None,
+ fCreationMode = 0o660, oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sFile = sFile;
+ self.eAccessMode = eAccessMode if eAccessMode is not None else vboxcon.FileAccessMode_ReadOnly;
+ self.eAction = eAction if eAction is not None else vboxcon.FileOpenAction_OpenExisting;
+ self.eSharing = eSharing if eSharing is not None else vboxcon.FileSharingMode_All;
+ self.fCreationMode = fCreationMode;
+ self.afOpenFlags = [];
+ self.oOpenedFile = None;
+
+ def toString(self):
+ """ Get a summary string. """
+ return 'eAccessMode=%s eAction=%s sFile=%s' % (self.eAccessMode, self.eAction, self.sFile);
+
+ def doOpenStep(self, fExpectSuccess):
+ """
+ Does the open step, putting the resulting file in oOpenedFile.
+ """
+ try:
+ self.oOpenedFile = self.oGuestSession.fileOpenEx(self.sFile, self.eAccessMode, self.eAction,
+ self.eSharing, self.fCreationMode, self.afOpenFlags);
+ except:
+ reporter.maybeErrXcpt(fExpectSuccess, 'fileOpenEx(%s, %s, %s, %s, %s, %s)'
+ % (self.sFile, self.eAccessMode, self.eAction, self.eSharing,
+ self.fCreationMode, self.afOpenFlags,));
+ return False;
+ return True;
+
+ def doStepsOnOpenedFile(self, fExpectSuccess, oSubTst):
+ """ Overridden by children to do more testing. """
+ _ = fExpectSuccess; _ = oSubTst;
+ return True;
+
+ def doCloseStep(self):
+ """ Closes the file. """
+ if self.oOpenedFile:
+ try:
+ self.oOpenedFile.close();
+ except:
+ return reporter.errorXcpt('close([%s, %s, %s, %s, %s, %s])'
+ % (self.sFile, self.eAccessMode, self.eAction, self.eSharing,
+ self.fCreationMode, self.afOpenFlags,));
+ self.oOpenedFile = None;
+ return True;
+
+ def doSteps(self, fExpectSuccess, oSubTst):
+ """ Do the tests. """
+ fRc = self.doOpenStep(fExpectSuccess);
+ if fRc is True:
+ fRc = self.doStepsOnOpenedFile(fExpectSuccess, oSubTst);
+ if self.oOpenedFile:
+ fRc = self.doCloseStep() and fRc;
+ return fRc;
+
+
+class tdTestFileOpenCheckSize(tdTestFileOpen):
+ """
+ Opens a file and checks the size.
+ """
+ def __init__(self, sFile = "", eAccessMode = None, eAction = None, eSharing = None,
+ fCreationMode = 0o660, cbOpenExpected = 0, oCreds = None):
+ tdTestFileOpen.__init__(self, sFile, eAccessMode, eAction, eSharing, fCreationMode, oCreds);
+ self.cbOpenExpected = cbOpenExpected;
+
+ def toString(self):
+ return 'cbOpenExpected=%s %s' % (self.cbOpenExpected, tdTestFileOpen.toString(self),);
+
+ def doStepsOnOpenedFile(self, fExpectSuccess, oSubTst):
+ #
+ # Call parent.
+ #
+ fRc = tdTestFileOpen.doStepsOnOpenedFile(self, fExpectSuccess, oSubTst);
+
+ #
+ # Check the size. Requires 6.0 or later (E_NOTIMPL in 5.2).
+ #
+ if oSubTst.oTstDrv.fpApiVer >= 6.0:
+ try:
+ oFsObjInfo = self.oOpenedFile.queryInfo();
+ except:
+ return reporter.errorXcpt('queryInfo([%s, %s, %s, %s, %s, %s])'
+ % (self.sFile, self.eAccessMode, self.eAction, self.eSharing,
+ self.fCreationMode, self.afOpenFlags,));
+ if oFsObjInfo is None:
+ return reporter.error('IGuestFile::queryInfo returned None');
+ try:
+ cbFile = oFsObjInfo.objectSize;
+ except:
+ return reporter.errorXcpt();
+ if cbFile != self.cbOpenExpected:
+ return reporter.error('Wrong file size after open (%d): %s, expected %s (file %s) (#1)'
+ % (self.eAction, cbFile, self.cbOpenExpected, self.sFile));
+
+ try:
+ cbFile = self.oOpenedFile.querySize();
+ except:
+ return reporter.errorXcpt('querySize([%s, %s, %s, %s, %s, %s])'
+ % (self.sFile, self.eAccessMode, self.eAction, self.eSharing,
+ self.fCreationMode, self.afOpenFlags,));
+ if cbFile != self.cbOpenExpected:
+ return reporter.error('Wrong file size after open (%d): %s, expected %s (file %s) (#2)'
+ % (self.eAction, cbFile, self.cbOpenExpected, self.sFile));
+
+ return fRc;
+
+
+class tdTestFileOpenAndWrite(tdTestFileOpen):
+ """
+ Opens the file and writes one or more chunks to it.
+
+ The chunks are a list of tuples(offset, bytes), where offset can be None
+ if no seeking should be performed.
+ """
+ def __init__(self, sFile = "", eAccessMode = None, eAction = None, eSharing = None, # pylint: disable=too-many-arguments
+ fCreationMode = 0o660, atChunks = None, fUseAtApi = False, abContent = None, oCreds = None):
+ tdTestFileOpen.__init__(self, sFile, eAccessMode if eAccessMode is not None else vboxcon.FileAccessMode_WriteOnly,
+ eAction, eSharing, fCreationMode, oCreds);
+ assert atChunks is not None;
+ self.atChunks = atChunks # type: list(tuple(int,bytearray))
+ self.fUseAtApi = fUseAtApi;
+ self.fAppend = ( eAccessMode in (vboxcon.FileAccessMode_AppendOnly, vboxcon.FileAccessMode_AppendRead)
+ or eAction == vboxcon.FileOpenAction_AppendOrCreate);
+ self.abContent = abContent # type: bytearray
+
+ def toString(self):
+ sChunks = ', '.join('%s LB %s' % (tChunk[0], len(tChunk[1]),) for tChunk in self.atChunks);
+ sApi = 'writeAt' if self.fUseAtApi else 'write';
+ return '%s [%s] %s' % (sApi, sChunks, tdTestFileOpen.toString(self),);
+
+ def doStepsOnOpenedFile(self, fExpectSuccess, oSubTst):
+ #
+ # Call parent.
+ #
+ fRc = tdTestFileOpen.doStepsOnOpenedFile(self, fExpectSuccess, oSubTst);
+
+ #
+ # Do the writing.
+ #
+ for offFile, abBuf in self.atChunks:
+ if self.fUseAtApi:
+ #
+ # writeAt:
+ #
+ assert offFile is not None;
+ reporter.log2('writeAt(%s, %s bytes)' % (offFile, len(abBuf),));
+ if self.fAppend:
+ if self.abContent is not None: # Try avoid seek as it updates the cached offset in GuestFileImpl.
+ offExpectAfter = len(self.abContent);
+ else:
+ try:
+ offSave = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ offExpectAfter = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_End);
+ self.oOpenedFile.seek(offSave, vboxcon.FileSeekOrigin_Begin);
+ except:
+ return reporter.errorXcpt();
+ offExpectAfter += len(abBuf);
+ else:
+ offExpectAfter = offFile + len(abBuf);
+
+ try:
+ cbWritten = self.oOpenedFile.writeAt(offFile, abBuf, 30*1000);
+ except:
+ return reporter.errorXcpt('writeAt(%s, %s bytes)' % (offFile, len(abBuf),));
+
+ else:
+ #
+ # write:
+ #
+ if self.fAppend:
+ if self.abContent is not None: # Try avoid seek as it updates the cached offset in GuestFileImpl.
+ offExpectAfter = len(self.abContent);
+ else:
+ try:
+ offSave = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ offExpectAfter = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_End);
+ self.oOpenedFile.seek(offSave, vboxcon.FileSeekOrigin_Begin);
+ except:
+ return reporter.errorXcpt('seek(0,End)');
+ if offFile is not None:
+ try:
+ self.oOpenedFile.seek(offFile, vboxcon.FileSeekOrigin_Begin);
+ except:
+ return reporter.errorXcpt('seek(%s,Begin)' % (offFile,));
+ else:
+ try:
+ offFile = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ except:
+ return reporter.errorXcpt();
+ if not self.fAppend:
+ offExpectAfter = offFile;
+ offExpectAfter += len(abBuf);
+
+ reporter.log2('write(%s bytes @ %s)' % (len(abBuf), offFile,));
+ try:
+ cbWritten = self.oOpenedFile.write(abBuf, 30*1000);
+ except:
+ return reporter.errorXcpt('write(%s bytes @ %s)' % (len(abBuf), offFile));
+
+ #
+ # Check how much was written, ASSUMING nothing we push thru here is too big:
+ #
+ if cbWritten != len(abBuf):
+ fRc = reporter.errorXcpt('Wrote less than expected: %s out of %s, expected all to be written'
+ % (cbWritten, len(abBuf),));
+ if not self.fAppend:
+ offExpectAfter -= len(abBuf) - cbWritten;
+
+ #
+ # Update the file content tracker if we've got one and can:
+ #
+ if self.abContent is not None:
+ if cbWritten < len(abBuf):
+ abBuf = abBuf[:cbWritten];
+
+ #
+ # In append mode, the current file offset shall be disregarded and the
+ # write always goes to the end of the file, regardless of writeAt or write.
+ # Note that RTFileWriteAt only naturally behaves this way on linux and
+ # (probably) windows, so VBoxService makes that behaviour generic across
+ # all OSes.
+ #
+ if self.fAppend:
+ reporter.log2('len(self.abContent)=%s + %s' % (len(self.abContent), cbWritten, ));
+ self.abContent.extend(abBuf);
+ else:
+ if offFile is None:
+ offFile = offExpectAfter - cbWritten;
+ reporter.log2('len(self.abContent)=%s + %s @ %s' % (len(self.abContent), cbWritten, offFile, ));
+ if offFile > len(self.abContent):
+ self.abContent.extend(bytearray(offFile - len(self.abContent)));
+ self.abContent[offFile:offFile + cbWritten] = abBuf;
+ reporter.log2('len(self.abContent)=%s' % (len(self.abContent),));
+
+ #
+ # Check the resulting file offset with IGuestFile::offset.
+ #
+ try:
+ offApi = self.oOpenedFile.offset; # Must be gotten first!
+ offSeek = self.oOpenedFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ reporter.log2('offApi=%s offSeek=%s offExpectAfter=%s' % (offApi, offSeek, offExpectAfter,));
+ if offSeek != offExpectAfter:
+ fRc = reporter.error('Seek offset is %s, expected %s after %s bytes write @ %s (offApi=%s)'
+ % (offSeek, offExpectAfter, len(abBuf), offFile, offApi,));
+ if offApi != offExpectAfter:
+ fRc = reporter.error('IGuestFile::offset is %s, expected %s after %s bytes write @ %s (offSeek=%s)'
+ % (offApi, offExpectAfter, len(abBuf), offFile, offSeek,));
+ # for each chunk - end
+ return fRc;
+
+
+class tdTestFileOpenAndCheckContent(tdTestFileOpen):
+ """
+ Opens the file and checks the content using the read API.
+ """
+ def __init__(self, sFile = "", eSharing = None, abContent = None, cbContentExpected = None, oCreds = None):
+ tdTestFileOpen.__init__(self, sFile = sFile, eSharing = eSharing, oCreds = oCreds);
+ self.abContent = abContent # type: bytearray
+ self.cbContentExpected = cbContentExpected;
+
+ def toString(self):
+ return 'check content %s (%s) %s' % (len(self.abContent), self.cbContentExpected, tdTestFileOpen.toString(self),);
+
+ def doStepsOnOpenedFile(self, fExpectSuccess, oSubTst):
+ #
+ # Call parent.
+ #
+ fRc = tdTestFileOpen.doStepsOnOpenedFile(self, fExpectSuccess, oSubTst);
+
+ #
+ # Check the expected content size.
+ #
+ if self.cbContentExpected is not None:
+ if len(self.abContent) != self.cbContentExpected:
+ fRc = reporter.error('Incorrect abContent size: %s, expected %s'
+ % (len(self.abContent), self.cbContentExpected,));
+
+ #
+ # Read the file and compare it with the content.
+ #
+ offFile = 0;
+ while True:
+ try:
+ abChunk = self.oOpenedFile.read(512*1024, 30*1000);
+ except:
+ return reporter.errorXcpt('read(512KB) @ %s' % (offFile,));
+ cbChunk = len(abChunk);
+ if cbChunk == 0:
+ if offFile != len(self.abContent):
+ fRc = reporter.error('Unexpected EOF @ %s, len(abContent)=%s' % (offFile, len(self.abContent),));
+ break;
+ if offFile + cbChunk > len(self.abContent):
+ fRc = reporter.error('File is larger than expected: at least %s bytes, expected %s bytes'
+ % (offFile + cbChunk, len(self.abContent),));
+ elif not utils.areBytesEqual(abChunk, self.abContent[offFile:(offFile + cbChunk)]):
+ fRc = reporter.error('Mismatch in range %s LB %s!' % (offFile, cbChunk,));
+ offFile += cbChunk;
+
+ return fRc;
+
+
+class tdTestSession(tdTestGuestCtrlBase):
+ """
+ Test the guest session handling.
+ """
+ def __init__(self, sUser = None, sPassword = None, sDomain = None, sSessionName = ""):
+ tdTestGuestCtrlBase.__init__(self, oCreds = tdCtxCreds(sUser, sPassword, sDomain));
+ self.sSessionName = sSessionName;
+
+ def getSessionCount(self, oVBoxMgr):
+ """
+ Helper for returning the number of currently
+ opened guest sessions of a VM.
+ """
+ if self.oGuest is None:
+ return 0;
+ try:
+ aoSession = oVBoxMgr.getArray(self.oGuest, 'sessions')
+ except:
+ reporter.errorXcpt('sSessionName: %s' % (self.sSessionName,));
+ return 0;
+ return len(aoSession);
+
+
+class tdTestSessionEx(tdTestGuestCtrlBase):
+ """
+ Test the guest session.
+ """
+ def __init__(self, aoSteps = None, enmUser = None):
+ tdTestGuestCtrlBase.__init__(self);
+ assert enmUser is None; # For later.
+ self.enmUser = enmUser;
+ self.aoSteps = aoSteps if aoSteps is not None else [];
+
+ def execute(self, oTstDrv, oVmSession, oTxsSession, oTestVm, sMsgPrefix):
+ """
+ Executes the test.
+ """
+ #
+ # Create a session.
+ #
+ assert self.enmUser is None; # For later.
+ self.oCreds = tdCtxCreds();
+ fRc = self.setEnvironment(oVmSession, oTxsSession, oTestVm);
+ if not fRc:
+ return False;
+ reporter.log2('%s: %s steps' % (sMsgPrefix, len(self.aoSteps),));
+ fRc, oCurSession = self.createSession(sMsgPrefix);
+ if fRc is True:
+ #
+ # Execute the tests.
+ #
+ try:
+ fRc = self.executeSteps(oTstDrv, oCurSession, sMsgPrefix);
+ except:
+ fRc = reporter.errorXcpt('%s: Unexpected exception executing test steps' % (sMsgPrefix,));
+
+ #
+ # Close the session.
+ #
+ fRc2 = self.closeSession();
+ if fRc2 is False:
+ fRc = reporter.error('%s: Session could not be closed' % (sMsgPrefix,));
+ else:
+ fRc = reporter.error('%s: Session creation failed' % (sMsgPrefix,));
+ return fRc;
+
+ def executeSteps(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes just the steps.
+ Returns True on success, False on test failure.
+ """
+ fRc = True;
+ for (i, oStep) in enumerate(self.aoSteps):
+ fRc2 = oStep.execute(oTstDrv, oGstCtrlSession, sMsgPrefix + ', step #%d' % i);
+ if fRc2 is True:
+ pass;
+ elif fRc2 is None:
+ reporter.log('%s: skipping remaining %d steps' % (sMsgPrefix, len(self.aoSteps) - i - 1,));
+ break;
+ else:
+ fRc = False;
+ return fRc;
+
+ @staticmethod
+ def executeListTestSessions(aoTests, oTstDrv, oVmSession, oTxsSession, oTestVm, sMsgPrefix):
+ """
+ Works thru a list of tdTestSessionEx object.
+ """
+ fRc = True;
+ for (i, oCurTest) in enumerate(aoTests):
+ try:
+ fRc2 = oCurTest.execute(oTstDrv, oVmSession, oTxsSession, oTestVm, '%s / %#d' % (sMsgPrefix, i,));
+ if fRc2 is not True:
+ fRc = False;
+ except:
+ fRc = reporter.errorXcpt('%s: Unexpected exception executing test #%d' % (sMsgPrefix, i ,));
+
+ return (fRc, oTxsSession);
+
+
+class tdSessionStepBase(object):
+ """
+ Base class for the guest control session test steps.
+ """
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the test step.
+
+ Returns True on success.
+ Returns False on failure (must be reported as error).
+ Returns None if to skip the remaining steps.
+ """
+ _ = oTstDrv;
+ _ = oGstCtrlSession;
+ return reporter.error('%s: Missing execute implementation: %s' % (sMsgPrefix, self,));
+
+
+class tdStepRequireMinimumApiVer(tdSessionStepBase):
+ """
+ Special test step which will cause executeSteps to skip the remaining step
+ if the VBox API is too old:
+ """
+ def __init__(self, fpMinApiVer):
+ self.fpMinApiVer = fpMinApiVer;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """ Returns None if API version is too old, otherwise True. """
+ if oTstDrv.fpApiVer >= self.fpMinApiVer:
+ return True;
+ _ = oGstCtrlSession;
+ _ = sMsgPrefix;
+ return None; # Special return value. Don't use elsewhere.
+
+
+#
+# Scheduling Environment Changes with the Guest Control Session.
+#
+
+class tdStepSessionSetEnv(tdSessionStepBase):
+ """
+ Guest session environment: schedule putenv
+ """
+ def __init__(self, sVar, sValue, hrcExpected = 0):
+ self.sVar = sVar;
+ self.sValue = sValue;
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionSetEnv: sVar=%s sValue=%s hrcExpected=%#x' % (self.sVar, self.sValue, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oGstCtrlSession.environmentScheduleSet(self.sVar, self.sValue);
+ else:
+ oGstCtrlSession.environmentSet(self.sVar, self.sValue);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ return reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (setenv %s=%s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.sVar, self.sValue,));
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in tdStepSessionSetEnv::execute (%s=%s)'
+ % (sMsgPrefix, self.sVar, self.sValue,));
+
+ # Should we succeed?
+ if self.hrcExpected != 0:
+ return reporter.error('%s: Expected hrcExpected=%#x, got S_OK (putenv %s=%s)'
+ % (sMsgPrefix, self.hrcExpected, self.sVar, self.sValue,));
+ return True;
+
+class tdStepSessionUnsetEnv(tdSessionStepBase):
+ """
+ Guest session environment: schedule unset.
+ """
+ def __init__(self, sVar, hrcExpected = 0):
+ self.sVar = sVar;
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionUnsetEnv: sVar=%s hrcExpected=%#x' % (self.sVar, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oGstCtrlSession.environmentScheduleUnset(self.sVar);
+ else:
+ oGstCtrlSession.environmentUnset(self.sVar);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ return reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (unsetenv %s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.sVar,));
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in tdStepSessionUnsetEnv::execute (%s)'
+ % (sMsgPrefix, self.sVar,));
+
+ # Should we succeed?
+ if self.hrcExpected != 0:
+ return reporter.error('%s: Expected hrcExpected=%#x, got S_OK (unsetenv %s)'
+ % (sMsgPrefix, self.hrcExpected, self.sVar,));
+ return True;
+
+class tdStepSessionBulkEnv(tdSessionStepBase):
+ """
+ Guest session environment: Bulk environment changes.
+ """
+ def __init__(self, asEnv = None, hrcExpected = 0):
+ self.asEnv = asEnv if asEnv is not None else [];
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionBulkEnv: asEnv=%s hrcExpected=%#x' % (self.asEnv, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oTstDrv.oVBoxMgr.setArray(oGstCtrlSession, 'environmentChanges', self.asEnv);
+ else:
+ oTstDrv.oVBoxMgr.setArray(oGstCtrlSession, 'environment', self.asEnv);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ return reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (asEnv=%s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.asEnv,));
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception writing the environmentChanges property (asEnv=%s).'
+ % (sMsgPrefix, self.asEnv));
+ return True;
+
+class tdStepSessionClearEnv(tdStepSessionBulkEnv):
+ """
+ Guest session environment: clears the scheduled environment changes.
+ """
+ def __init__(self):
+ tdStepSessionBulkEnv.__init__(self);
+
+
+class tdStepSessionCheckEnv(tdSessionStepBase):
+ """
+ Check the currently scheduled environment changes of a guest control session.
+ """
+ def __init__(self, asEnv = None):
+ self.asEnv = asEnv if asEnv is not None else [];
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionCheckEnv: asEnv=%s' % (self.asEnv,));
+
+ #
+ # Get the environment change list.
+ #
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ asCurEnv = oTstDrv.oVBoxMgr.getArray(oGstCtrlSession, 'environmentChanges');
+ else:
+ asCurEnv = oTstDrv.oVBoxMgr.getArray(oGstCtrlSession, 'environment');
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception reading the environmentChanges property.' % (sMsgPrefix,));
+
+ #
+ # Compare it with the expected one by trying to remove each expected value
+ # and the list anything unexpected.
+ #
+ fRc = True;
+ asCopy = list(asCurEnv); # just in case asCurEnv is immutable
+ for sExpected in self.asEnv:
+ try:
+ asCopy.remove(sExpected);
+ except:
+ fRc = reporter.error('%s: Expected "%s" to be in the resulting environment' % (sMsgPrefix, sExpected,));
+ for sUnexpected in asCopy:
+ fRc = reporter.error('%s: Unexpected "%s" in the resulting environment' % (sMsgPrefix, sUnexpected,));
+
+ if fRc is not True:
+ reporter.log2('%s: Current environment: %s' % (sMsgPrefix, asCurEnv));
+ return fRc;
+
+
+#
+# File system object statistics (i.e. stat()).
+#
+
+class tdStepStat(tdSessionStepBase):
+ """
+ Stats a file system object.
+ """
+ def __init__(self, sPath, hrcExpected = 0, fFound = True, fFollowLinks = True, enmType = None, oTestFsObj = None):
+ self.sPath = sPath;
+ self.hrcExpected = hrcExpected;
+ self.fFound = fFound;
+ self.fFollowLinks = fFollowLinks;
+ self.enmType = enmType if enmType is not None else vboxcon.FsObjType_File;
+ self.cbExactSize = None;
+ self.cbMinSize = None;
+ self.oTestFsObj = oTestFsObj # type: testfileset.TestFsObj
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Execute the test step.
+ """
+ reporter.log2('tdStepStat: sPath=%s enmType=%s hrcExpected=%s fFound=%s fFollowLinks=%s'
+ % (limitString(self.sPath), self.enmType, self.hrcExpected, self.fFound, self.fFollowLinks,));
+
+ # Don't execute non-file tests on older VBox version.
+ if oTstDrv.fpApiVer >= 5.0 or self.enmType == vboxcon.FsObjType_File or not self.fFound:
+ #
+ # Call the API.
+ #
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oFsInfo = oGstCtrlSession.fsObjQueryInfo(self.sPath, self.fFollowLinks);
+ else:
+ oFsInfo = oGstCtrlSession.fileQueryInfo(self.sPath);
+ except vbox.ComException as oXcpt:
+ ## @todo: The error reporting in the API just plain sucks! Most of the errors are
+ ## VBOX_E_IPRT_ERROR and there seems to be no way to distinguish between
+ ## non-existing files/path and a lot of other errors. Fix API and test!
+ if not self.fFound:
+ return True;
+ if vbox.ComError.equal(oXcpt, self.hrcExpected): # Is this an expected failure?
+ return True;
+ return reporter.errorXcpt('%s: Unexpected exception for exiting path "%s" (enmType=%s, hrcExpected=%s):'
+ % (sMsgPrefix, self.sPath, self.enmType, self.hrcExpected,));
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in tdStepStat::execute (%s)'
+ % (sMsgPrefix, self.sPath,));
+ if oFsInfo is None:
+ return reporter.error('%s: "%s" got None instead of IFsObjInfo instance!' % (sMsgPrefix, self.sPath,));
+
+ #
+ # Check type expectations.
+ #
+ try:
+ enmType = oFsInfo.type;
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in reading "IFsObjInfo::type"' % (sMsgPrefix,));
+ if enmType != self.enmType:
+ return reporter.error('%s: "%s" has type %s, expected %s'
+ % (sMsgPrefix, self.sPath, enmType, self.enmType));
+
+ #
+ # Check size expectations.
+ # Note! This is unicode string here on windows, for some reason.
+ # long long mapping perhaps?
+ #
+ try:
+ cbObject = long(oFsInfo.objectSize);
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in reading "IFsObjInfo::objectSize"'
+ % (sMsgPrefix,));
+ if self.cbExactSize is not None \
+ and cbObject != self.cbExactSize:
+ return reporter.error('%s: "%s" has size %s bytes, expected %s bytes'
+ % (sMsgPrefix, self.sPath, cbObject, self.cbExactSize));
+ if self.cbMinSize is not None \
+ and cbObject < self.cbMinSize:
+ return reporter.error('%s: "%s" has size %s bytes, expected as least %s bytes'
+ % (sMsgPrefix, self.sPath, cbObject, self.cbMinSize));
+ return True;
+
+class tdStepStatDir(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sDirPath, oTestDir = None):
+ tdStepStat.__init__(self, sPath = sDirPath, enmType = vboxcon.FsObjType_Directory, oTestFsObj = oTestDir);
+
+class tdStepStatDirEx(tdStepStatDir):
+ """ Checks for an existing directory given a TestDir object. """
+ def __init__(self, oTestDir): # type: (testfileset.TestDir)
+ tdStepStatDir.__init__(self, oTestDir.sPath, oTestDir);
+
+class tdStepStatFile(tdStepStat):
+ """ Checks for an existing file """
+ def __init__(self, sFilePath = None, oTestFile = None):
+ tdStepStat.__init__(self, sPath = sFilePath, enmType = vboxcon.FsObjType_File, oTestFsObj = oTestFile);
+
+class tdStepStatFileEx(tdStepStatFile):
+ """ Checks for an existing file given a TestFile object. """
+ def __init__(self, oTestFile): # type: (testfileset.TestFile)
+ tdStepStatFile.__init__(self, oTestFile.sPath, oTestFile);
+
+class tdStepStatFileSize(tdStepStat):
+ """ Checks for an existing file of a given expected size.. """
+ def __init__(self, sFilePath, cbExactSize = 0):
+ tdStepStat.__init__(self, sPath = sFilePath, enmType = vboxcon.FsObjType_File);
+ self.cbExactSize = cbExactSize;
+
+class tdStepStatFileNotFound(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sPath):
+ tdStepStat.__init__(self, sPath = sPath, fFound = False);
+
+class tdStepStatPathNotFound(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sPath):
+ tdStepStat.__init__(self, sPath = sPath, fFound = False);
+
+
+#
+#
+#
+
+class tdTestSessionFileRefs(tdTestGuestCtrlBase):
+ """
+ Tests session file (IGuestFile) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestSessionDirRefs(tdTestGuestCtrlBase):
+ """
+ Tests session directory (IGuestDirectory) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestSessionProcRefs(tdTestGuestCtrlBase):
+ """
+ Tests session process (IGuestProcess) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestUpdateAdditions(tdTestGuestCtrlBase):
+ """
+ Test updating the Guest Additions inside the guest.
+ """
+ def __init__(self, sSrc = "", asArgs = None, afFlags = None, oCreds = None):
+ tdTestGuestCtrlBase.__init__(self, oCreds = oCreds);
+ self.sSrc = sSrc;
+ self.asArgs = asArgs;
+ self.afFlags = afFlags;
+
+class tdTestResult(object):
+ """
+ Base class for test results.
+ """
+ def __init__(self, fRc = False):
+ ## The overall test result.
+ self.fRc = fRc;
+
+class tdTestResultFailure(tdTestResult):
+ """
+ Base class for test results.
+ """
+ def __init__(self):
+ tdTestResult.__init__(self, fRc = False);
+
+class tdTestResultSuccess(tdTestResult):
+ """
+ Base class for test results.
+ """
+ def __init__(self):
+ tdTestResult.__init__(self, fRc = True);
+
+class tdTestResultDirRead(tdTestResult):
+ """
+ Test result for reading guest directories.
+ """
+ def __init__(self, fRc = False, cFiles = 0, cDirs = 0, cOthers = None):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cFiles = cFiles;
+ self.cDirs = cDirs;
+ self.cOthers = cOthers;
+
+class tdTestResultExec(tdTestResult):
+ """
+ Holds a guest process execution test result,
+ including the exit code, status + afFlags.
+ """
+ def __init__(self, fRc = False, uExitStatus = 500, iExitCode = 0, sBuf = None, cbBuf = 0, cbStdOut = None, cbStdErr = None):
+ tdTestResult.__init__(self);
+ ## The overall test result.
+ self.fRc = fRc;
+ ## Process exit stuff.
+ self.uExitStatus = uExitStatus;
+ self.iExitCode = iExitCode;
+ ## Desired buffer length returned back from stdout/stderr.
+ self.cbBuf = cbBuf;
+ ## Desired buffer result from stdout/stderr. Use with caution!
+ self.sBuf = sBuf;
+ self.cbStdOut = cbStdOut;
+ self.cbStdErr = cbStdErr;
+
+class tdTestResultFileStat(tdTestResult):
+ """
+ Test result for stat'ing guest files.
+ """
+ def __init__(self, fRc = False,
+ cbSize = 0, eFileType = 0):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cbSize = cbSize;
+ self.eFileType = eFileType;
+ ## @todo Add more information.
+
+class tdTestResultFileReadWrite(tdTestResult):
+ """
+ Test result for reading + writing guest directories.
+ """
+ def __init__(self, fRc = False,
+ cbProcessed = 0, offFile = 0, abBuf = None):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cbProcessed = cbProcessed;
+ self.offFile = offFile;
+ self.abBuf = abBuf;
+
+class tdTestResultSession(tdTestResult):
+ """
+ Test result for guest session counts.
+ """
+ def __init__(self, fRc = False, cNumSessions = 0):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cNumSessions = cNumSessions;
+
+class tdDebugSettings(object):
+ """
+ Contains local test debug settings.
+ """
+ def __init__(self, sVBoxServiceExeHst = None):
+ self.sVBoxServiceExeHst = sVBoxServiceExeHst;
+ self.sGstVBoxServiceLogPath = '';
+
+class SubTstDrvAddGuestCtrl(base.SubTestDriverBase):
+ """
+ Sub-test driver for executing guest control (VBoxService, IGuest) tests.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'add-guest-ctrl', 'Guest Control');
+
+ ## @todo base.TestBase.
+ self.asTestsDef = [
+ 'debug',
+ 'session_basic', 'session_env', 'session_file_ref', 'session_dir_ref', 'session_proc_ref', 'session_reboot',
+ 'exec_basic', 'exec_timeout',
+ 'dir_create', 'dir_create_temp', 'dir_read',
+ 'file_open', 'file_remove', 'file_stat', 'file_read', 'file_write',
+ 'copy_to', 'copy_from',
+ 'update_additions'
+ ];
+ self.asTests = self.asTestsDef;
+ self.fSkipKnownBugs = False;
+ self.oTestFiles = None # type: vboxtestfileset.TestFileSet
+ self.oDebug = tdDebugSettings();
+ self.sPathVBoxServiceExeGst = '';
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--add-guest-ctrl-tests':
+ iArg += 1;
+ iNext = self.oTstDrv.requireMoreArgs(1, asArgs, iArg);
+ if asArgs[iArg] == 'all': # Nice for debugging scripts.
+ self.asTests = self.asTestsDef;
+ else:
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--add-guest-ctrl-tests" value "%s" is not valid; valid values are: %s'
+ % (s, ' '.join(self.asTestsDef)));
+ return iNext;
+ if asArgs[iArg] == '--add-guest-ctrl-skip-known-bugs':
+ self.fSkipKnownBugs = True;
+ return iArg + 1;
+ if asArgs[iArg] == '--no-add-guest-ctrl-skip-known-bugs':
+ self.fSkipKnownBugs = False;
+ return iArg + 1;
+ if asArgs[iArg] == '--add-guest-ctrl-debug-img':
+ iArg += 1;
+ iNext = self.oTstDrv.requireMoreArgs(1, asArgs, iArg);
+ self.oDebug.sVBoxServiceExeHst = asArgs[iArg];
+ return iNext;
+ return iArg;
+
+ def showUsage(self):
+ base.SubTestDriverBase.showUsage(self);
+ reporter.log(' --add-guest-ctrl-tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --add-guest-ctrl-skip-known-bugs');
+ reporter.log(' Skips known bugs. Default: --no-add-guest-ctrl-skip-known-bugs');
+ reporter.log('Debugging:');
+ reporter.log(' --add-guest-ctrl-debug-img');
+ reporter.log(' Sets VBoxService image to deploy for debugging');
+ return True;
+
+ def testIt(self, oTestVm, oSession, oTxsSession):
+ """
+ Executes the test.
+
+ Returns fRc, oTxsSession. The latter may have changed.
+ """
+
+ self.sPathVBoxServiceExeGst = oTestVm.pathJoin(self.oTstDrv.getGuestSystemAdminDir(oTestVm), 'VBoxService') \
+ + base.exeSuff();
+
+ reporter.log("Active tests: %s" % (self.asTests,));
+
+ # The tests. Must-succeed tests should be first.
+ atTests = [
+ ( True, self.prepareGuestForDebugging, None, 'Manual Debugging',),
+ ( True, self.prepareGuestForTesting, None, 'Preparations',),
+ ( True, self.testGuestCtrlSession, 'session_basic', 'Session Basics',),
+ ( True, self.testGuestCtrlExec, 'exec_basic', 'Execution',),
+ ( False, self.testGuestCtrlExecTimeout, 'exec_timeout', 'Execution Timeouts',),
+ ( False, self.testGuestCtrlSessionEnvironment, 'session_env', 'Session Environment',),
+ ( False, self.testGuestCtrlSessionFileRefs, 'session_file_ref', 'Session File References',),
+ #( False, self.testGuestCtrlSessionDirRefs, 'session_dir_ref', 'Session Directory References',),
+ ( False, self.testGuestCtrlSessionProcRefs, 'session_proc_ref', 'Session Process References',),
+ ( False, self.testGuestCtrlDirCreate, 'dir_create', 'Creating directories',),
+ ( False, self.testGuestCtrlDirCreateTemp, 'dir_create_temp', 'Creating temporary directories',),
+ ( False, self.testGuestCtrlDirRead, 'dir_read', 'Reading directories',),
+ ( False, self.testGuestCtrlCopyTo, 'copy_to', 'Copy to guest',),
+ ( False, self.testGuestCtrlCopyFrom, 'copy_from', 'Copy from guest',),
+ ( False, self.testGuestCtrlFileStat, 'file_stat', 'Querying file information (stat)',),
+ ( False, self.testGuestCtrlFileOpen, 'file_open', 'File open',),
+ ( False, self.testGuestCtrlFileRead, 'file_read', 'File read',),
+ ( False, self.testGuestCtrlFileWrite, 'file_write', 'File write',),
+ ( False, self.testGuestCtrlFileRemove, 'file_remove', 'Removing files',), # Destroys prepped files.
+ ( False, self.testGuestCtrlUpdateAdditions, 'update_additions', 'Updating Guest Additions',),
+ ];
+
+ if not self.fSkipKnownBugs:
+ atTests.extend([
+ ## @todo Seems to (mainly?) fail on Linux guests, primarily running with systemd as service supervisor.
+ # Needs to be investigated and fixed.
+ ( False, self.testGuestCtrlSessionReboot, 'session_reboot', 'Session w/ Guest Reboot',), # May zap /tmp.
+ ]);
+
+ fRc = True;
+ for fMustSucceed, fnHandler, sShortNm, sTestNm in atTests:
+
+ # If for whatever reason the VM object became invalid, bail out.
+ if not oTestVm:
+ reporter.error('Test VM object invalid (VBoxSVC or client process crashed?), aborting tests');
+ fRc = False;
+ break;
+
+ reporter.testStart(sTestNm);
+ if sShortNm is None or sShortNm in self.asTests:
+ # Returns (fRc, oTxsSession, oSession) - but only the first one is mandatory.
+ aoResult = fnHandler(oSession, oTxsSession, oTestVm);
+ if aoResult is None or isinstance(aoResult, bool):
+ fRcTest = aoResult;
+ else:
+ fRcTest = aoResult[0];
+ if len(aoResult) > 1:
+ oTxsSession = aoResult[1];
+ if len(aoResult) > 2:
+ oSession = aoResult[2];
+ assert len(aoResult) == 3;
+ else:
+ fRcTest = None;
+
+ if fRcTest is False and reporter.testErrorCount() == 0:
+ fRcTest = reporter.error('Buggy test! Returned False w/o logging the error!');
+ if reporter.testDone(fRcTest is None)[1] != 0:
+ fRcTest = False;
+ fRc = False;
+
+ # Stop execution if this is a must-succeed test and it failed.
+ if fRcTest is False and fMustSucceed is True:
+ reporter.log('Skipping any remaining tests since the previous one failed.');
+ break;
+
+ # Upload VBoxService logs on failure.
+ if reporter.testErrorCount() > 0 \
+ and self.oDebug.sGstVBoxServiceLogPath:
+ sVBoxServiceLogsTarGz = 'ga-vboxservice-logs-%s.tar.gz' % oTestVm.sVmName;
+ sGstVBoxServiceLogsTarGz = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), sVBoxServiceLogsTarGz);
+ if self.oTstDrv.txsPackFile(oSession, oTxsSession, \
+ sGstVBoxServiceLogsTarGz, self.oDebug.sGstVBoxServiceLogPath, fIgnoreErrors = True):
+ self.oTstDrv.txsDownloadFiles(oSession, oTxsSession, [ (sGstVBoxServiceLogsTarGz, sVBoxServiceLogsTarGz) ], \
+ fIgnoreErrors = True);
+
+ return (fRc, oTxsSession);
+
+ def prepareGuestForDebugging(self, oSession, oTxsSession, oTestVm): # pylint: disable=unused-argument
+ """
+ Prepares a guest for (manual) debugging.
+
+ This involves copying over and invoking a the locally built VBoxService binary.
+ """
+
+ if self.oDebug.sVBoxServiceExeHst is None: # If no debugging enabled, bail out.
+ reporter.log('Skipping debugging');
+ return True;
+
+ reporter.log('Preparing for debugging ...');
+
+ try:
+ self.vboxServiceControl(oTxsSession, oTestVm, fStart = False);
+
+ self.oTstDrv.sleep(5); # Fudge factor -- wait until the service stopped.
+
+ reporter.log('Uploading "%s" to "%s" ...' % (self.oDebug.sVBoxServiceExeHst, self.sPathVBoxServiceExeGst));
+ oTxsSession.syncUploadFile(self.oDebug.sVBoxServiceExeHst, self.sPathVBoxServiceExeGst);
+
+ if oTestVm.isLinux():
+ oTxsSession.syncChMod(self.sPathVBoxServiceExeGst, 0o755);
+
+ self.vboxServiceControl(oTxsSession, oTestVm, fStart = True);
+
+ self.oTstDrv.sleep(5); # Fudge factor -- wait until the service is ready.
+
+ except:
+ return reporter.errorXcpt('Unable to prepare for debugging');
+
+ return True;
+
+ #
+ # VBoxService handling.
+ #
+ def vboxServiceControl(self, oTxsSession, oTestVm, fStart):
+ """
+ Controls VBoxService on the guest by starting or stopping the service.
+ Returns success indicator.
+ """
+
+ fRc = True;
+
+ if oTestVm.isWindows():
+ sPathSC = os.path.join(self.oTstDrv.getGuestSystemDir(oTestVm), 'sc.exe');
+ if fStart is True:
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Starting VBoxService', 30 * 1000, \
+ sPathSC, (sPathSC, 'start', 'VBoxService'));
+ else:
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Stopping VBoxService', 30 * 1000, \
+ sPathSC, (sPathSC, 'stop', 'VBoxService'));
+ elif oTestVm.isLinux():
+ sPathService = "/sbin/rcvboxadd-service";
+ if fStart is True:
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Starting VBoxService', 30 * 1000, \
+ sPathService, (sPathService, 'start'));
+ else:
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Stopping VBoxService', 30 * 1000, \
+ sPathService, (sPathService, 'stop'));
+ else:
+ reporter.log('Controlling VBoxService not supported for this guest yet');
+
+ return fRc;
+
+ def waitForGuestFacility(self, oSession, eFacilityType, sDesc,
+ eFacilityStatus, cMsTimeout = 30 * 1000):
+ """
+ Waits for a guest facility to enter a certain status.
+ By default the "Active" status is being used.
+
+ Returns success status.
+ """
+
+ reporter.log('Waiting for Guest Additions facility "%s" to change to status %s (%dms timeout)...'
+ % (sDesc, str(eFacilityStatus), cMsTimeout));
+
+ fRc = False;
+
+ eStatusOld = None;
+ tsStart = base.timestampMilli();
+ while base.timestampMilli() - tsStart < cMsTimeout:
+ try:
+ eStatus, _ = oSession.o.console.guest.getFacilityStatus(eFacilityType);
+ reporter.log('Current status is %s' % (str(eStatus)));
+ if eStatusOld is None:
+ eStatusOld = eStatus;
+ except:
+ reporter.errorXcpt('Getting facility status failed');
+ break;
+ if eStatus != eStatusOld:
+ reporter.log('Status changed to %s' % (str(eStatus)));
+ eStatusOld = eStatus;
+ if eStatus == eFacilityStatus:
+ fRc = True;
+ break;
+ self.oTstDrv.sleep(5); # Do some busy waiting.
+
+ if not fRc:
+ reporter.error('Waiting for Guest Additions facility "%s" timed out' % (sDesc));
+ else:
+ reporter.log('Guest Additions facility "%s" reached requested status %s after %dms'
+ % (sDesc, str(eFacilityStatus), base.timestampMilli() - tsStart));
+
+ return fRc;
+
+ #
+ # Guest test files.
+ #
+
+ def prepareGuestForTesting(self, oSession, oTxsSession, oTestVm):
+ """
+ Prepares the VM for testing, uploading a bunch of files and stuff via TXS.
+ Returns success indicator.
+ """
+ _ = oSession;
+
+ #
+ # Make sure the temporary directory exists.
+ #
+ for sDir in [self.oTstDrv.getGuestTempDir(oTestVm), ]:
+ if oTxsSession.syncMkDirPath(sDir, 0o777) is not True:
+ return reporter.error('Failed to create directory "%s"!' % (sDir,));
+
+ # Query the TestExecService (TXS) version first to find out on what we run.
+ fGotTxsVer = self.oTstDrv.txsVer(oSession, oTxsSession, 30 * 100, fIgnoreErrors = True);
+
+ # Whether to enable verbose logging for VBoxService.
+ fEnableVerboseLogging = False;
+
+ # On Windows guests we always can enable verbose logging.
+ if oTestVm.isWindows():
+ fEnableVerboseLogging = True;
+
+ # Old TxS versions had a bug which caused an infinite loop when executing stuff containing "$xxx",
+ # so check if we got the version here first and skip enabling verbose logging nonetheless if needed.
+ if not fGotTxsVer:
+ reporter.log('Too old TxS service running')
+ fEnableVerboseLogging = False;
+
+ #
+ # Enable VBoxService verbose logging.
+ #
+ reporter.log('Enabling verbose VBoxService logging: %s' % (fEnableVerboseLogging));
+ if fEnableVerboseLogging:
+ self.oDebug.sGstVBoxServiceLogPath = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), "VBoxService");
+ if oTxsSession.syncMkDirPath(self.oDebug.sGstVBoxServiceLogPath, 0o777) is not True:
+ return reporter.error('Failed to create directory "%s"!' % (self.oDebug.sGstVBoxServiceLogPath,));
+ sPathLogFile = oTestVm.pathJoin(self.oDebug.sGstVBoxServiceLogPath, 'VBoxService.log');
+
+ reporter.log('VBoxService logs will be stored in "%s"' % (self.oDebug.sGstVBoxServiceLogPath,));
+
+ fRestartVBoxService = False;
+ if oTestVm.isWindows():
+ sPathRegExe = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'reg.exe');
+ sImagePath = '%s -vvvv --logfile %s' % (self.sPathVBoxServiceExeGst, sPathLogFile);
+ fRestartVBoxService = self.oTstDrv.txsRunTest(oTxsSession, 'Enabling VBoxService verbose logging (via registry)',
+ 30 * 1000,
+ sPathRegExe,
+ (sPathRegExe, 'add',
+ 'HKLM\\SYSTEM\\CurrentControlSet\\Services\\VBoxService',
+ '/v', 'ImagePath', '/t', 'REG_SZ', '/d', sImagePath, '/f'));
+ elif oTestVm.isLinux():
+ sPathSed = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'sed');
+ fRestartVBoxService = self.oTstDrv.txsRunTest(oTxsSession, 'Enabling VBoxService verbose logging', 30 * 1000,
+ sPathSed,
+ (sPathSed, '-i', '-e', 's/'
+ '\\$2 \\$3'
+ '/'
+ '\\$2 \\$3 -vvvv --logfile \\/var\\/tmp\\/VBoxService\\/VBoxService.log'
+ '/g',
+ '/sbin/rcvboxadd-service'));
+ else:
+ reporter.log('Verbose logging for VBoxService not supported for this guest yet');
+
+ if fRestartVBoxService:
+ self.vboxServiceControl(oTxsSession, oTestVm, fStart = False);
+ self.oTstDrv.sleep(5);
+ self.vboxServiceControl(oTxsSession, oTestVm, fStart = True);
+ else:
+ reporter.testStart('Waiting for VBoxService to get started');
+ fRc = self.waitForGuestFacility(oSession, vboxcon.AdditionsFacilityType_VBoxService, "VBoxService",
+ vboxcon.AdditionsFacilityStatus_Active);
+ reporter.testDone();
+ if not fRc:
+ return False;
+
+ #
+ # Generate and upload some random files and dirs to the guest.
+ # Note! Make sure we don't run into too-long-path issues when using
+ # the test files on the host if.
+ #
+ cchGst = len(self.oTstDrv.getGuestTempDir(oTestVm)) + 1 + len('addgst-1') + 1;
+ cchHst = len(self.oTstDrv.sScratchPath) + 1 + len('copyto/addgst-1') + 1;
+ cchMaxPath = 230;
+ if cchHst > cchGst:
+ cchMaxPath -= cchHst - cchGst;
+ reporter.log('cchMaxPath=%s (cchHst=%s, cchGst=%s)' % (cchMaxPath, cchHst, cchGst,));
+ self.oTestFiles = vboxtestfileset.TestFileSet(oTestVm,
+ self.oTstDrv.getGuestTempDir(oTestVm), 'addgst-1',
+ # Make sure that we use a lowest common denominator across all supported
+ # platforms, to make testing the randomly generated file paths work
+ # reliably.
+ cchMaxPath = cchMaxPath, asCompatibleWith = [ ('cross') ]);
+ return self.oTestFiles.upload(oTxsSession, self.oTstDrv);
+
+
+ #
+ # gctrlXxxx stuff.
+ #
+
+ def gctrlCopyFileFrom(self, oGuestSession, oTest, fExpected):
+ """
+ Helper function to copy a single file from the guest to the host.
+ """
+
+ # As we pass-in randomly generated file names, the source sometimes can be empty, which
+ # in turn will result in a (correct) error by the API. Simply skip this function then.
+ if not oTest.sSrc:
+ reporter.log2('Skipping guest file "%s"' % (limitString(oTest.sSrc)));
+ return fExpected;
+
+ #
+ # Do the copying.
+ #
+ reporter.log2('Copying guest file "%s" to host "%s"' % (limitString(oTest.sSrc), limitString(oTest.sDst)));
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyFromGuest(oTest.sSrc, oTest.sDst, oTest.afFlags);
+ else:
+ oCurProgress = oGuestSession.copyFrom(oTest.sSrc, oTest.sDst, oTest.afFlags);
+ except:
+ reporter.maybeErrXcpt(fExpected, 'Copy from exception for sSrc="%s", sDst="%s":' % (oTest.sSrc, oTest.sDst,));
+ return False;
+ if oCurProgress is None:
+ return reporter.error('No progress object returned');
+ oProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlFileCopyFrom");
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = not fExpected);
+ return False;
+
+ #
+ # Check the result if we can.
+ #
+ if oTest.oSrc:
+ assert isinstance(oTest.oSrc, testfileset.TestFile);
+ sDst = oTest.sDst;
+ if os.path.isdir(sDst):
+ sDst = os.path.join(sDst, oTest.oSrc.sName);
+ try:
+ oFile = open(sDst, 'rb'); # pylint: disable=consider-using-with
+ except:
+ # Don't report expected non-existing paths / files as an error.
+ return reporter.maybeErrXcpt(fExpected, 'open(%s) failed during verfication (file / path not found)' % (sDst,));
+ fEqual = oTest.oSrc.equalFile(oFile);
+ oFile.close();
+ if not fEqual:
+ return reporter.error('Content differs for "%s"' % (sDst,));
+
+ return True;
+
+ def __compareTestDir(self, oDir, sHostPath): # type: (testfileset.TestDir, str) -> bool
+ """
+ Recursively compare the content of oDir and sHostPath.
+
+ Returns True on success, False + error logging on failure.
+
+ Note! This ASSUMES that nothing else was copied to sHostPath!
+ """
+ #
+ # First check out all the entries and files in the directory.
+ #
+ dLeftUpper = dict(oDir.dChildrenUpper);
+ try:
+ asEntries = os.listdir(sHostPath);
+ except:
+ return reporter.errorXcpt('os.listdir(%s) failed' % (sHostPath,));
+
+ fRc = True;
+ for sEntry in asEntries:
+ sEntryUpper = sEntry.upper();
+ if sEntryUpper not in dLeftUpper:
+ fRc = reporter.error('Unexpected entry "%s" in "%s"' % (sEntry, sHostPath,));
+ else:
+ oFsObj = dLeftUpper[sEntryUpper];
+ del dLeftUpper[sEntryUpper];
+
+ if isinstance(oFsObj, testfileset.TestFile):
+ sFilePath = os.path.join(sHostPath, oFsObj.sName);
+ try:
+ oFile = open(sFilePath, 'rb'); # pylint: disable=consider-using-with
+ except:
+ fRc = reporter.errorXcpt('open(%s) failed during verfication' % (sFilePath,));
+ else:
+ fEqual = oFsObj.equalFile(oFile);
+ oFile.close();
+ if not fEqual:
+ fRc = reporter.error('Content differs for "%s"' % (sFilePath,));
+
+ # List missing entries:
+ for sKey in dLeftUpper:
+ oEntry = dLeftUpper[sKey];
+ fRc = reporter.error('%s: Missing %s "%s" (src path: %s)'
+ % (sHostPath, oEntry.sName,
+ 'file' if isinstance(oEntry, testfileset.TestFile) else 'directory', oEntry.sPath));
+
+ #
+ # Recurse into subdirectories.
+ #
+ for oFsObj in oDir.aoChildren:
+ if isinstance(oFsObj, testfileset.TestDir):
+ fRc = self.__compareTestDir(oFsObj, os.path.join(sHostPath, oFsObj.sName)) and fRc;
+ return fRc;
+
+ def gctrlCopyDirFrom(self, oGuestSession, oTest, fExpected):
+ """
+ Helper function to copy a directory from the guest to the host.
+ """
+
+ # As we pass-in randomly generated directories, the source sometimes can be empty, which
+ # in turn will result in a (correct) error by the API. Simply skip this function then.
+ if not oTest.sSrc:
+ reporter.log2('Skipping guest dir "%s"' % (limitString(oTest.sSrc)));
+ return fExpected;
+
+ #
+ # Do the copying.
+ #
+ reporter.log2('Copying guest dir "%s" to host "%s"' % (limitString(oTest.sSrc), limitString(oTest.sDst)));
+ try:
+ if self.oTstDrv.fpApiVer >= 7.0:
+ ## @todo Make the following new flags implicit for 7.0 for now. Develop dedicated tests for this later and remove.
+ if not oTest.afFlags:
+ oTest.afFlags = [ vboxcon.DirectoryCopyFlag_Recursive, ];
+ elif vboxcon.DirectoryCopyFlag_Recursive not in oTest.afFlags:
+ oTest.afFlags.append(vboxcon.DirectoryCopyFlag_Recursive);
+ ## @todo Ditto.
+ if not oTest.afFlags:
+ oTest.afFlags = [ vboxcon.DirectoryCopyFlag_FollowLinks, ];
+ elif vboxcon.DirectoryCopyFlag_FollowLinks not in oTest.afFlags:
+ oTest.afFlags.append(vboxcon.DirectoryCopyFlag_FollowLinks);
+ oCurProgress = oGuestSession.directoryCopyFromGuest(oTest.sSrc, oTest.sDst, oTest.afFlags);
+ except:
+ reporter.maybeErrXcpt(fExpected, 'Copy dir from exception for sSrc="%s", sDst="%s":' % (oTest.sSrc, oTest.sDst,));
+ return False;
+ if oCurProgress is None:
+ return reporter.error('No progress object returned');
+
+ oProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlDirCopyFrom");
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = not fExpected);
+ return False;
+
+ #
+ # Check the result if we can.
+ #
+ if oTest.oSrc:
+ assert isinstance(oTest.oSrc, testfileset.TestDir);
+ sDst = oTest.sDst;
+ if oTest.fIntoDst:
+ return self.__compareTestDir(oTest.oSrc, os.path.join(sDst, oTest.oSrc.sName));
+ oDummy = testfileset.TestDir(None, 'dummy');
+ oDummy.aoChildren = [oTest.oSrc,]
+ oDummy.dChildrenUpper = { oTest.oSrc.sName.upper(): oTest.oSrc, };
+ return self.__compareTestDir(oDummy, sDst);
+ return True;
+
+ def gctrlCopyFileTo(self, oGuestSession, sSrc, sDst, afFlags, fIsError):
+ """
+ Helper function to copy a single file from the host to the guest.
+
+ afFlags is either None or an array of vboxcon.DirectoryCopyFlag_Xxxx values.
+ """
+ reporter.log2('Copying host file "%s" to guest "%s" (flags %s)' % (limitString(sSrc), limitString(sDst), afFlags));
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyToGuest(sSrc, sDst, afFlags);
+ else:
+ oCurProgress = oGuestSession.copyTo(sSrc, sDst, afFlags);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'sSrc=%s sDst=%s' % (sSrc, sDst,));
+ return False;
+
+ if oCurProgress is None:
+ return reporter.error('No progress object returned');
+ oProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlCopyFileTo");
+
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = not fIsError);
+ return False;
+ except:
+ reporter.maybeErrXcpt(fIsError, 'Wait exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ return False;
+ return True;
+
+ def gctrlCopyDirTo(self, oGuestSession, sSrc, sDst, afFlags, fIsError):
+ """
+ Helper function to copy a directory (tree) from the host to the guest.
+
+ afFlags is either None or an array of vboxcon.DirectoryCopyFlag_Xxxx values.
+ """
+ reporter.log2('Copying host directory "%s" to guest "%s" (flags %s)' % (limitString(sSrc), limitString(sDst), afFlags));
+ try:
+ if self.oTstDrv.fpApiVer >= 7.0:
+ ## @todo Make the following new flags implicit for 7.0 for now. Develop dedicated tests for this later and remove.
+ if not afFlags:
+ afFlags = [ vboxcon.DirectoryCopyFlag_Recursive, ];
+ elif vboxcon.DirectoryCopyFlag_Recursive not in afFlags:
+ afFlags.append(vboxcon.DirectoryCopyFlag_Recursive);
+ ## @todo Ditto.
+ if not afFlags:
+ afFlags = [vboxcon.DirectoryCopyFlag_FollowLinks,];
+ elif vboxcon.DirectoryCopyFlag_FollowLinks not in afFlags:
+ afFlags.append(vboxcon.DirectoryCopyFlag_FollowLinks);
+ oCurProgress = oGuestSession.directoryCopyToGuest(sSrc, sDst, afFlags);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'sSrc=%s sDst=%s' % (sSrc, sDst,));
+ return False;
+
+ if oCurProgress is None:
+ return reporter.error('No progress object returned');
+ oProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlCopyFileTo");
+
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = not fIsError);
+ return False;
+ except:
+ reporter.maybeErrXcpt(fIsError, 'Wait exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ return False;
+ return True;
+
+ def gctrlCreateDir(self, oTest, oRes, oGuestSession):
+ """
+ Helper function to create a guest directory specified in the current test.
+ """
+ reporter.log2('Creating directory "%s"' % (limitString(oTest.sDirectory),));
+ try:
+ oGuestSession.directoryCreate(oTest.sDirectory, oTest.fMode, oTest.afFlags);
+ except:
+ reporter.maybeErrXcpt(oRes.fRc, 'Failed to create "%s" fMode=%o afFlags=%s'
+ % (oTest.sDirectory, oTest.fMode, oTest.afFlags,));
+ return not oRes.fRc;
+ if oRes.fRc is not True:
+ return reporter.error('Did not expect to create directory "%s"!' % (oTest.sDirectory,));
+
+ # Check if the directory now exists.
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ fDirExists = oGuestSession.directoryExists(oTest.sDirectory, False);
+ else:
+ fDirExists = oGuestSession.directoryExists(oTest.sDirectory);
+ except:
+ return reporter.errorXcpt('directoryExists failed on "%s"!' % (oTest.sDirectory,));
+ if not fDirExists:
+ return reporter.errorXcpt('directoryExists returned False on "%s" after directoryCreate succeeded!'
+ % (oTest.sDirectory,));
+ return True;
+
+ def gctrlReadDirTree(self, oTest, oGuestSession, fIsError, sSubDir = None):
+ """
+ Helper function to recursively read a guest directory tree specified in the current test.
+ """
+ sDir = oTest.sDirectory;
+ sFilter = oTest.sFilter;
+ afFlags = oTest.afFlags;
+ oTestVm = oTest.oCreds.oTestVm;
+ sCurDir = oTestVm.pathJoin(sDir, sSubDir) if sSubDir else sDir;
+
+ fRc = True; # Be optimistic.
+ cDirs = 0; # Number of directories read.
+ cFiles = 0; # Number of files read.
+ cOthers = 0; # Other files.
+
+ # Open the directory:
+ reporter.log2('Directory="%s", filter="%s", afFlags="%s"' % (limitString(sCurDir), sFilter, afFlags));
+ try:
+ oCurDir = oGuestSession.directoryOpen(sCurDir, sFilter, afFlags);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'sCurDir=%s sFilter=%s afFlags=%s' % (sCurDir, sFilter, afFlags,))
+ return (False, 0, 0, 0);
+
+ # Read the directory.
+ while fRc is True:
+ try:
+ oFsObjInfo = oCurDir.read();
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.VBOX_E_OBJECT_NOT_FOUND):
+ if self.oTstDrv.fpApiVer > 5.2:
+ reporter.errorXcpt('Error reading directory "%s":' % (sCurDir,));
+ else:
+ # Unlike fileOpen, directoryOpen will not fail if the directory does not exist.
+ reporter.maybeErrXcpt(fIsError, 'Error reading directory "%s":' % (sCurDir,));
+ fRc = False;
+ else:
+ reporter.log2('\tNo more directory entries for "%s"' % (limitString(sCurDir),));
+ break;
+
+ try:
+ sName = oFsObjInfo.name;
+ eType = oFsObjInfo.type;
+ except:
+ fRc = reporter.errorXcpt();
+ break;
+
+ if sName in ('.', '..', ):
+ if eType != vboxcon.FsObjType_Directory:
+ fRc = reporter.error('Wrong type for "%s": %d, expected %d (Directory)'
+ % (sName, eType, vboxcon.FsObjType_Directory));
+ elif eType == vboxcon.FsObjType_Directory:
+ reporter.log2(' Directory "%s"' % limitString(oFsObjInfo.name));
+ aSubResult = self.gctrlReadDirTree(oTest, oGuestSession, fIsError,
+ oTestVm.pathJoin(sSubDir, sName) if sSubDir else sName);
+ fRc = aSubResult[0];
+ cDirs += aSubResult[1] + 1;
+ cFiles += aSubResult[2];
+ cOthers += aSubResult[3];
+ elif eType is vboxcon.FsObjType_File:
+ reporter.log4(' File "%s"' % oFsObjInfo.name);
+ cFiles += 1;
+ elif eType is vboxcon.FsObjType_Symlink:
+ reporter.log4(' Symlink "%s" -- not tested yet' % oFsObjInfo.name);
+ cOthers += 1;
+ elif oTestVm.isWindows() \
+ or oTestVm.isOS2() \
+ or eType not in (vboxcon.FsObjType_Fifo, vboxcon.FsObjType_DevChar, vboxcon.FsObjType_DevBlock,
+ vboxcon.FsObjType_Socket, vboxcon.FsObjType_WhiteOut):
+ fRc = reporter.error('Directory "%s" contains invalid directory entry "%s" (type %d)' %
+ (sCurDir, oFsObjInfo.name, oFsObjInfo.type,));
+ else:
+ cOthers += 1;
+
+ # Close the directory
+ try:
+ oCurDir.close();
+ except:
+ fRc = reporter.errorXcpt('sCurDir=%s' % (sCurDir));
+
+ return (fRc, cDirs, cFiles, cOthers);
+
+ def gctrlReadDirTree2(self, oGuestSession, oDir): # type: (testfileset.TestDir) -> bool
+ """
+ Helper function to recursively read a guest directory tree specified in the current test.
+ """
+
+ #
+ # Process the directory.
+ #
+
+ # Open the directory:
+ try:
+ oCurDir = oGuestSession.directoryOpen(oDir.sPath, '', None);
+ except:
+ return reporter.errorXcpt('sPath=%s' % (oDir.sPath,));
+
+ # Read the directory.
+ dLeftUpper = dict(oDir.dChildrenUpper);
+ cDot = 0;
+ cDotDot = 0;
+ fRc = True;
+ while True:
+ try:
+ oFsObjInfo = oCurDir.read();
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.VBOX_E_OBJECT_NOT_FOUND):
+ fRc = reporter.errorXcpt('Error reading directory "%s":' % (oDir.sPath,));
+ break;
+
+ try:
+ sName = oFsObjInfo.name;
+ eType = oFsObjInfo.type;
+ cbFile = oFsObjInfo.objectSize;
+ ## @todo check further attributes.
+ except:
+ fRc = reporter.errorXcpt();
+ break;
+
+ # '.' and '..' entries are not present in oDir.aoChildren, so special treatment:
+ if sName in ('.', '..', ):
+ if eType != vboxcon.FsObjType_Directory:
+ fRc = reporter.error('Wrong type for "%s": %d, expected %d (Directory)'
+ % (sName, eType, vboxcon.FsObjType_Directory));
+ if sName == '.': cDot += 1;
+ else: cDotDot += 1;
+ else:
+ # Find the child and remove it from the dictionary.
+ sNameUpper = sName.upper();
+ oFsObj = dLeftUpper.get(sNameUpper);
+ if oFsObj is None:
+ fRc = reporter.error('Unknown object "%s" found in "%s" (type %s, size %s)!'
+ % (sName, oDir.sPath, eType, cbFile,));
+ else:
+ del dLeftUpper[sNameUpper];
+
+ # Check type
+ if isinstance(oFsObj, testfileset.TestDir):
+ if eType != vboxcon.FsObjType_Directory:
+ fRc = reporter.error('%s: expected directory (%d), got eType=%d!'
+ % (oFsObj.sPath, vboxcon.FsObjType_Directory, eType,));
+ elif isinstance(oFsObj, testfileset.TestFile):
+ if eType != vboxcon.FsObjType_File:
+ fRc = reporter.error('%s: expected file (%d), got eType=%d!'
+ % (oFsObj.sPath, vboxcon.FsObjType_File, eType,));
+ else:
+ fRc = reporter.error('%s: WTF? type=%s' % (oFsObj.sPath, type(oFsObj),));
+
+ # Check the name.
+ if oFsObj.sName != sName:
+ fRc = reporter.error('%s: expected name "%s", got "%s" instead!' % (oFsObj.sPath, oFsObj.sName, sName,));
+
+ # Check the size if a file.
+ if isinstance(oFsObj, testfileset.TestFile) and cbFile != oFsObj.cbContent:
+ fRc = reporter.error('%s: expected size %s, got %s instead!' % (oFsObj.sPath, oFsObj.cbContent, cbFile,));
+
+ ## @todo check timestamps and attributes.
+
+ # Close the directory
+ try:
+ oCurDir.close();
+ except:
+ fRc = reporter.errorXcpt('oDir.sPath=%s' % (oDir.sPath,));
+
+ # Any files left over?
+ for sKey in dLeftUpper:
+ oFsObj = dLeftUpper[sKey];
+ fRc = reporter.error('%s: Was not returned! (%s)' % (oFsObj.sPath, type(oFsObj),));
+
+ # Check the dot and dot-dot counts.
+ if cDot != 1:
+ fRc = reporter.error('%s: Found %s "." entries, expected exactly 1!' % (oDir.sPath, cDot,));
+ if cDotDot != 1:
+ fRc = reporter.error('%s: Found %s ".." entries, expected exactly 1!' % (oDir.sPath, cDotDot,));
+
+ #
+ # Recurse into subdirectories using info from oDir.
+ #
+ for oFsObj in oDir.aoChildren:
+ if isinstance(oFsObj, testfileset.TestDir):
+ fRc = self.gctrlReadDirTree2(oGuestSession, oFsObj) and fRc;
+
+ return fRc;
+
+ def gctrlExecDoTest(self, i, oTest, oRes, oGuestSession):
+ """
+ Wrapper function around gctrlExecute to provide more sanity checking
+ when needed in actual execution tests.
+ """
+ reporter.log('Testing #%d, cmd="%s" ...' % (i, oTest.sCmd));
+ fRcExec = self.gctrlExecute(oTest, oGuestSession, oRes.fRc);
+ if fRcExec == oRes.fRc:
+ fRc = True;
+ if fRcExec is True:
+ # Compare exit status / code on successful process execution.
+ if oTest.uExitStatus != oRes.uExitStatus \
+ or oTest.iExitCode != oRes.iExitCode:
+ fRc = reporter.error('Test #%d (%s) failed: Got exit status + code %d,%d, expected %d,%d'
+ % (i, oTest.asArgs, oTest.uExitStatus, oTest.iExitCode,
+ oRes.uExitStatus, oRes.iExitCode));
+
+ # Compare test / result buffers on successful process execution.
+ if oTest.sBuf is not None and oRes.sBuf is not None:
+ if not utils.areBytesEqual(oTest.sBuf, oRes.sBuf):
+ fRc = reporter.error('Test #%d (%s) failed: Got buffer\n%s (%d bytes), expected\n%s (%d bytes)'
+ % (i, oTest.asArgs,
+ map(hex, map(ord, oTest.sBuf)), len(oTest.sBuf),
+ map(hex, map(ord, oRes.sBuf)), len(oRes.sBuf)));
+ reporter.log2('Test #%d passed: Buffers match (%d bytes)' % (i, len(oRes.sBuf)));
+ elif oRes.sBuf and not oTest.sBuf:
+ fRc = reporter.error('Test #%d (%s) failed: Got no buffer data, expected\n%s (%dbytes)' %
+ (i, oTest.asArgs, map(hex, map(ord, oRes.sBuf)), len(oRes.sBuf),));
+
+ if oRes.cbStdOut is not None and oRes.cbStdOut != oTest.cbStdOut:
+ fRc = reporter.error('Test #%d (%s) failed: Got %d bytes of stdout data, expected %d'
+ % (i, oTest.asArgs, oTest.cbStdOut, oRes.cbStdOut));
+ if oRes.cbStdErr is not None and oRes.cbStdErr != oTest.cbStdErr:
+ fRc = reporter.error('Test #%d (%s) failed: Got %d bytes of stderr data, expected %d'
+ % (i, oTest.asArgs, oTest.cbStdErr, oRes.cbStdErr));
+ else:
+ fRc = reporter.error('Test #%d (%s) failed: Got %s, expected %s' % (i, oTest.asArgs, fRcExec, oRes.fRc));
+ return fRc;
+
+ def gctrlExecute(self, oTest, oGuestSession, fIsError): # pylint: disable=too-many-statements
+ """
+ Helper function to execute a program on a guest, specified in the current test.
+
+ Note! This weirdo returns results (process exitcode and status) in oTest.
+ """
+ fRc = True; # Be optimistic.
+
+ # Reset the weird result stuff:
+ oTest.cbStdOut = 0;
+ oTest.cbStdErr = 0;
+ oTest.sBuf = '';
+ oTest.uExitStatus = 0;
+ oTest.iExitCode = 0;
+
+ ## @todo Compare execution timeouts!
+ #tsStart = base.timestampMilli();
+
+ try:
+ reporter.log2('Using session user=%s, sDomain=%s, name=%s, timeout=%d'
+ % (oGuestSession.user, oGuestSession.domain, oGuestSession.name, oGuestSession.timeout,));
+ except:
+ return reporter.errorXcpt();
+
+ #
+ # Start the process:
+ #
+ reporter.log2('Executing sCmd=%s, afFlags=%s, timeoutMS=%d, asArgs=%s, asEnv=%s'
+ % (oTest.sCmd, oTest.afFlags, oTest.timeoutMS, limitString(oTest.asArgs), limitString(oTest.aEnv),));
+ try:
+ oProcess = oGuestSession.processCreate(oTest.sCmd,
+ oTest.asArgs if self.oTstDrv.fpApiVer >= 5.0 else oTest.asArgs[1:],
+ oTest.aEnv, oTest.afFlags, oTest.timeoutMS);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'type=%s, asArgs=%s' % (type(oTest.asArgs), oTest.asArgs,));
+ return False;
+ if oProcess is None:
+ return reporter.error('oProcess is None! (%s)' % (oTest.asArgs,));
+
+ #time.sleep(5); # try this if you want to see races here.
+
+ # Wait for the process to start properly:
+ reporter.log2('Process start requested, waiting for start (%dms) ...' % (oTest.timeoutMS,));
+ iPid = -1;
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Start, ];
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, oTest.timeoutMS);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'waitforArray failed for asArgs=%s' % (oTest.asArgs,));
+ fRc = False;
+ else:
+ try:
+ eStatus = oProcess.status;
+ iPid = oProcess.PID;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (oTest.asArgs,));
+ else:
+ reporter.log2('Wait result returned: %d, current process status is: %d' % (eWaitResult, eStatus,));
+
+ #
+ # Wait for the process to run to completion if necessary.
+ #
+ # Note! The above eWaitResult return value can be ignored as it will
+ # (mostly) reflect the process status anyway.
+ #
+ if eStatus == vboxcon.ProcessStatus_Started:
+
+ # What to wait for:
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate, ];
+ if vboxcon.ProcessCreateFlag_WaitForStdOut in oTest.afFlags:
+ aeWaitFor.append(vboxcon.ProcessWaitForFlag_StdOut);
+ if vboxcon.ProcessCreateFlag_WaitForStdErr in oTest.afFlags:
+ aeWaitFor.append(vboxcon.ProcessWaitForFlag_StdErr);
+ ## @todo Add vboxcon.ProcessWaitForFlag_StdIn.
+
+ reporter.log2('Process (PID %d) started, waiting for termination (%dms), aeWaitFor=%s ...'
+ % (iPid, oTest.timeoutMS, aeWaitFor));
+ acbFdOut = [0,0,0];
+ while True:
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, oTest.timeoutMS);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ break;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (oTest.asArgs,));
+ break;
+ #reporter.log2('Wait returned: %d' % (eWaitResult,));
+
+ # Process output:
+ for eFdResult, iFd, sFdNm in [ (vboxcon.ProcessWaitResult_StdOut, 1, 'stdout'),
+ (vboxcon.ProcessWaitResult_StdErr, 2, 'stderr'), ]:
+ if eWaitResult in (eFdResult, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ try:
+ abBuf = oProcess.read(iFd, 64 * 1024, oTest.timeoutMS);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ except:
+ reporter.maybeErrXcpt(fIsError, 'asArgs=%s' % (oTest.asArgs,));
+ else:
+ if abBuf:
+ reporter.log2('Process (PID %d) got %d bytes of %s data (type: %s)'
+ % (iPid, len(abBuf), sFdNm, type(abBuf)));
+ if reporter.getVerbosity() >= 4:
+ sBuf = '';
+ if sys.version_info >= (2, 7):
+ if isinstance(abBuf, memoryview): ## @todo Why is this happening?
+ abBuf = abBuf.tobytes();
+ sBuf = abBuf.decode("utf-8");
+ if sys.version_info <= (2, 7):
+ if isinstance(abBuf, buffer): # (for 3.0+) pylint: disable=undefined-variable
+ sBuf = str(abBuf);
+ for sLine in sBuf.splitlines():
+ reporter.log4('%s: %s' % (sFdNm, sLine));
+ acbFdOut[iFd] += len(abBuf);
+ oTest.sBuf = abBuf; ## @todo Figure out how to uniform + append!
+
+ ## Process input (todo):
+ #if eWaitResult in (vboxcon.ProcessWaitResult_StdIn, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ # reporter.log2('Process (PID %d) needs stdin data' % (iPid,));
+
+ # Termination or error?
+ if eWaitResult in (vboxcon.ProcessWaitResult_Terminate,
+ vboxcon.ProcessWaitResult_Error,
+ vboxcon.ProcessWaitResult_Timeout,):
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (oTest.asArgs,));
+ reporter.log2('Process (PID %d) reported terminate/error/timeout: %d, status: %d'
+ % (iPid, eWaitResult, eStatus,));
+ break;
+
+ # End of the wait loop.
+ _, oTest.cbStdOut, oTest.cbStdErr = acbFdOut;
+
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (oTest.asArgs,));
+ reporter.log2('Final process status (PID %d) is: %d' % (iPid, eStatus));
+ reporter.log2('Process (PID %d) %d stdout, %d stderr' % (iPid, oTest.cbStdOut, oTest.cbStdErr));
+
+ #
+ # Get the final status and exit code of the process.
+ #
+ try:
+ oTest.uExitStatus = oProcess.status;
+ oTest.iExitCode = oProcess.exitCode;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (oTest.asArgs,));
+ reporter.log2('Process (PID %d) has exit code: %d; status: %d ' % (iPid, oTest.iExitCode, oTest.uExitStatus));
+ return fRc;
+
+ def testGuestCtrlSessionEnvironment(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests the guest session environment changes.
+ """
+ aoTests = [
+ # Check basic operations.
+ tdTestSessionEx([ # Initial environment is empty.
+ tdStepSessionCheckEnv(),
+ # Check clearing empty env.
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Check set.
+ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',]),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 can't cope with the remainder.
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Check unset.
+ tdStepSessionUnsetEnv('BAR'),
+ tdStepSessionCheckEnv(['BAR']),
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Set + unset.
+ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',]),
+ tdStepSessionUnsetEnv('FOO'),
+ tdStepSessionCheckEnv(['FOO']),
+ # Bulk environment changes (via attrib) (shall replace existing 'FOO').
+ tdStepSessionBulkEnv( ['PATH=/bin:/usr/bin', 'TMPDIR=/var/tmp', 'USER=root']),
+ tdStepSessionCheckEnv(['PATH=/bin:/usr/bin', 'TMPDIR=/var/tmp', 'USER=root']),
+ ]),
+ tdTestSessionEx([ # Check that setting the same value several times works.
+ tdStepSessionSetEnv('FOO','BAR'),
+ tdStepSessionCheckEnv([ 'FOO=BAR',]),
+ tdStepSessionSetEnv('FOO','BAR2'),
+ tdStepSessionCheckEnv([ 'FOO=BAR2',]),
+ tdStepSessionSetEnv('FOO','BAR3'),
+ tdStepSessionCheckEnv([ 'FOO=BAR3',]),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 can't cope with the remainder.
+ # Add a little unsetting to the mix.
+ tdStepSessionSetEnv('BAR', 'BEAR'),
+ tdStepSessionCheckEnv([ 'FOO=BAR3', 'BAR=BEAR',]),
+ tdStepSessionUnsetEnv('FOO'),
+ tdStepSessionCheckEnv([ 'FOO', 'BAR=BEAR',]),
+ tdStepSessionSetEnv('FOO','BAR4'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR',]),
+ # The environment is case sensitive.
+ tdStepSessionSetEnv('foo','BAR5'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR', 'foo=BAR5']),
+ tdStepSessionUnsetEnv('foo'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR', 'foo']),
+ ]),
+ tdTestSessionEx([ # Bulk settings merges stuff, last entry standing.
+ tdStepSessionBulkEnv(['FOO=bar', 'foo=bar', 'FOO=doofus', 'TMPDIR=/tmp', 'foo=bar2']),
+ tdStepSessionCheckEnv(['FOO=doofus', 'TMPDIR=/tmp', 'foo=bar2']),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 is buggy!
+ tdStepSessionBulkEnv(['2=1+1', 'FOO=doofus2', ]),
+ tdStepSessionCheckEnv(['2=1+1', 'FOO=doofus2' ]),
+ ]),
+ # Invalid variable names.
+ tdTestSessionEx([
+ tdStepSessionSetEnv('', 'FOO', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 is too relaxed checking input!
+ tdStepSessionBulkEnv(['', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionSetEnv('FOO=', 'BAR', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ ]),
+ # A bit more weird keys/values.
+ tdTestSessionEx([ tdStepSessionSetEnv('$$$', ''),
+ tdStepSessionCheckEnv([ '$$$=',]), ]),
+ tdTestSessionEx([ tdStepSessionSetEnv('$$$', '%%%'),
+ tdStepSessionCheckEnv([ '$$$=%%%',]),
+ ]),
+ tdTestSessionEx([ tdStepRequireMinimumApiVer(5.0), # 4.3 is buggy!
+ tdStepSessionSetEnv(u'ß$%ß&', ''),
+ tdStepSessionCheckEnv([ u'ß$%ß&=',]),
+ ]),
+ # Misc stuff.
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', ''),
+ tdStepSessionCheckEnv(['FOO=',]),
+ ]),
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',])
+ ],),
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionSetEnv('BAR', 'BAZ'),
+ tdStepSessionCheckEnv([ 'FOO=BAR', 'BAR=BAZ',]),
+ ]),
+ ];
+ # Leading '=' in the name is okay for windows guests in 6.1 and later (for driver letter CWDs).
+ if (self.oTstDrv.fpApiVer < 6.1 and self.oTstDrv.fpApiVer >= 5.0) or not oTestVm.isWindows():
+ aoTests.append(tdTestSessionEx([tdStepSessionSetEnv('=', '===', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionSetEnv('=FOO', 'BAR', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionBulkEnv(['=', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionBulkEnv(['=FOO', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionBulkEnv(['=D:=D:/tmp', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionSetEnv('=D:', 'D:/temp', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ ]));
+ elif self.oTstDrv.fpApiVer >= 6.1 and oTestVm.isWindows():
+ aoTests.append(tdTestSessionEx([tdStepSessionSetEnv('=D:', 'D:/tmp'),
+ tdStepSessionCheckEnv(['=D:=D:/tmp',]),
+ tdStepSessionBulkEnv(['=D:=D:/temp', '=FOO', 'foo=bar']),
+ tdStepSessionCheckEnv(['=D:=D:/temp', '=FOO', 'foo=bar']),
+ tdStepSessionUnsetEnv('=D:'),
+ tdStepSessionCheckEnv(['=D:', '=FOO', 'foo=bar']),
+ ]));
+
+ return tdTestSessionEx.executeListTestSessions(aoTests, self.oTstDrv, oSession, oTxsSession, oTestVm, 'SessionEnv');
+
+ def testGuestCtrlSession(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests the guest session handling.
+ """
+
+ #
+ # Tests:
+ #
+ atTests = [
+ # Invalid parameters.
+ [ tdTestSession(sUser = ''), tdTestResultSession() ],
+ # User account without a passwort - forbidden.
+ [ tdTestSession(sPassword = "" ), tdTestResultSession() ],
+ # Various wrong credentials.
+ # Note! Only windows cares about sDomain, the other guests ignores it.
+ # Note! On Guest Additions < 4.3 this always succeeds because these don't
+ # support creating dedicated sessions. Instead, guest process creation
+ # then will fail. See note below.
+ [ tdTestSession(sPassword = 'bar'), tdTestResultSession() ],
+ [ tdTestSession(sUser = 'foo', sPassword = 'bar'), tdTestResultSession() ],
+ [ tdTestSession(sPassword = 'bar', sDomain = 'boo'), tdTestResultSession() ],
+ [ tdTestSession(sUser = 'foo', sPassword = 'bar', sDomain = 'boo'), tdTestResultSession() ],
+ ];
+ if oTestVm.isWindows(): # domain is ignored elsewhere.
+ atTests.append([ tdTestSession(sDomain = 'boo'), tdTestResultSession() ]);
+
+ # Finally, correct credentials.
+ atTests.append([ tdTestSession(), tdTestResultSession(fRc = True, cNumSessions = 1) ]);
+
+ #
+ # Run the tests.
+ #
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestSession
+ oCurRes = tTest[1] # type: tdTestResult
+
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ reporter.log('Testing #%d, user="%s", sPassword="%s", sDomain="%s" ...'
+ % (i, oCurTest.oCreds.sUser, oCurTest.oCreds.sPassword, oCurTest.oCreds.sDomain));
+ sCurGuestSessionName = 'testGuestCtrlSession: Test #%d' % (i,);
+ fRc2, oCurGuestSession = oCurTest.createSession(sCurGuestSessionName, fIsError = oCurRes.fRc);
+
+ # See note about < 4.3 Guest Additions above.
+ uProtocolVersion = 2;
+ if oCurGuestSession is not None:
+ try:
+ uProtocolVersion = oCurGuestSession.protocolVersion;
+ except:
+ fRc = reporter.errorXcpt('Test #%d' % (i,));
+
+ if uProtocolVersion >= 2 and fRc2 is not oCurRes.fRc:
+ fRc = reporter.error('Test #%d failed: Session creation failed: Got %s, expected %s' % (i, fRc2, oCurRes.fRc,));
+
+ if fRc2 and oCurGuestSession is None:
+ fRc = reporter.error('Test #%d failed: no session object' % (i,));
+ fRc2 = False;
+
+ if fRc2:
+ if uProtocolVersion >= 2: # For Guest Additions < 4.3 getSessionCount() always will return 1.
+ cCurSessions = oCurTest.getSessionCount(self.oTstDrv.oVBoxMgr);
+ if cCurSessions != oCurRes.cNumSessions:
+ fRc = reporter.error('Test #%d failed: Session count does not match: Got %d, expected %d'
+ % (i, cCurSessions, oCurRes.cNumSessions));
+ try:
+ sObjName = oCurGuestSession.name;
+ except:
+ fRc = reporter.errorXcpt('Test #%d' % (i,));
+ else:
+ if sObjName != sCurGuestSessionName:
+ fRc = reporter.error('Test #%d failed: Session name does not match: Got "%s", expected "%s"'
+ % (i, sObjName, sCurGuestSessionName));
+ fRc2 = oCurTest.closeSession();
+ if fRc2 is False:
+ fRc = reporter.error('Test #%d failed: Session could not be closed' % (i,));
+
+ if fRc is False:
+ return (False, oTxsSession);
+
+ #
+ # Multiple sessions.
+ #
+ cMaxGuestSessions = 31; # Maximum number of concurrent guest session allowed.
+ # Actually, this is 32, but we don't test session 0.
+ aoMultiSessions = {};
+ reporter.log2('Opening multiple guest tsessions at once ...');
+ for i in xrange(cMaxGuestSessions + 1):
+ aoMultiSessions[i] = tdTestSession(sSessionName = 'MultiSession #%d' % (i,));
+ fRc = aoMultiSessions[i].setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+
+ cCurSessions = aoMultiSessions[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ reporter.log2('MultiSession test #%d count is %d' % (i, cCurSessions));
+ if cCurSessions != i:
+ return (reporter.error('MultiSession count is %d, expected %d' % (cCurSessions, i)), oTxsSession);
+ fRc2, _ = aoMultiSessions[i].createSession('MultiSession #%d' % (i,), i < cMaxGuestSessions);
+ if fRc2 is not True:
+ if i < cMaxGuestSessions:
+ return (reporter.error('MultiSession #%d test failed' % (i,)), oTxsSession);
+ reporter.log('MultiSession #%d exceeded concurrent guest session count, good' % (i,));
+ break;
+
+ cCurSessions = aoMultiSessions[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ if cCurSessions is not cMaxGuestSessions:
+ return (reporter.error('Final session count %d, expected %d ' % (cCurSessions, cMaxGuestSessions,)), oTxsSession);
+
+ reporter.log2('Closing MultiSessions ...');
+ for i in xrange(cMaxGuestSessions):
+ # Close this session:
+ oClosedGuestSession = aoMultiSessions[i].oGuestSession;
+ fRc2 = aoMultiSessions[i].closeSession();
+ cCurSessions = aoMultiSessions[i].getSessionCount(self.oTstDrv.oVBoxMgr)
+ reporter.log2('MultiSession #%d count is %d' % (i, cCurSessions,));
+ if fRc2 is False:
+ fRc = reporter.error('Closing MultiSession #%d failed' % (i,));
+ elif cCurSessions != cMaxGuestSessions - (i + 1):
+ fRc = reporter.error('Expected %d session after closing #%d, got %d instead'
+ % (cMaxGuestSessions - (i + 1), cCurSessions, i,));
+ assert aoMultiSessions[i].oGuestSession is None or not fRc2;
+ ## @todo any way to check that the session is closed other than the 'sessions' attribute?
+
+ # Try check that none of the remaining sessions got closed.
+ try:
+ aoGuestSessions = self.oTstDrv.oVBoxMgr.getArray(atTests[0][0].oGuest, 'sessions');
+ except:
+ return (reporter.errorXcpt('i=%d/%d' % (i, cMaxGuestSessions,)), oTxsSession);
+ if oClosedGuestSession in aoGuestSessions:
+ fRc = reporter.error('i=%d/%d: %s should not be in %s'
+ % (i, cMaxGuestSessions, oClosedGuestSession, aoGuestSessions));
+ if i + 1 < cMaxGuestSessions: # Not sure what xrange(2,2) does...
+ for j in xrange(i + 1, cMaxGuestSessions):
+ if aoMultiSessions[j].oGuestSession not in aoGuestSessions:
+ fRc = reporter.error('i=%d/j=%d/%d: %s should be in %s'
+ % (i, j, cMaxGuestSessions, aoMultiSessions[j].oGuestSession, aoGuestSessions));
+ ## @todo any way to check that they work?
+
+ ## @todo Test session timeouts.
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlSessionFileRefs(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests the guest session file reference handling.
+ """
+
+ # Find a file to play around with:
+ sFile = self.oTstDrv.getGuestSystemFileForReading(oTestVm);
+
+ # Use credential defaults.
+ oCreds = tdCtxCreds();
+ oCreds.applyDefaultsIfNotSet(oTestVm);
+
+ # Number of stale guest files to create.
+ cStaleFiles = 10;
+
+ #
+ # Start a session.
+ #
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(oCreds.sUser, oCreds.sPassword, oCreds.sDomain, "testGuestCtrlSessionFileRefs");
+ eWaitResult = oGuestSession.waitForArray(aeWaitFor, 30 * 1000);
+ except:
+ return (reporter.errorXcpt(), oTxsSession);
+
+ # Be nice to Guest Additions < 4.3: They don't support session handling and therefore return WaitFlagNotSupported.
+ if eWaitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ return (reporter.error('Session did not start successfully - wait error: %d' % (eWaitResult,)), oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Open guest files and "forget" them (stale entries).
+ # For them we don't have any references anymore intentionally.
+ #
+ reporter.log2('Opening stale files');
+ fRc = True;
+ for i in xrange(0, cStaleFiles):
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oGuestSession.fileOpen(sFile, vboxcon.FileAccessMode_ReadOnly, vboxcon.FileOpenAction_OpenExisting, 0);
+ else:
+ oGuestSession.fileOpen(sFile, "r", "oe", 0);
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ except:
+ fRc = reporter.errorXcpt('Opening stale file #%d failed:' % (i,));
+ break;
+
+ try: cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ if cFiles != cStaleFiles:
+ fRc = reporter.error('Got %d stale files, expected %d' % (cFiles, cStaleFiles));
+
+ if fRc is True:
+ #
+ # Open non-stale files and close them again.
+ #
+ reporter.log2('Opening non-stale files');
+ aoFiles = [];
+ for i in xrange(0, cStaleFiles):
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurFile = oGuestSession.fileOpen(sFile, vboxcon.FileAccessMode_ReadOnly,
+ vboxcon.FileOpenAction_OpenExisting, 0);
+ else:
+ oCurFile = oGuestSession.fileOpen(sFile, "r", "oe", 0);
+ aoFiles.append(oCurFile);
+ except:
+ fRc = reporter.errorXcpt('Opening non-stale file #%d failed:' % (i,));
+ break;
+
+ # Check the count.
+ try: cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ if cFiles != cStaleFiles * 2:
+ fRc = reporter.error('Got %d total files, expected %d' % (cFiles, cStaleFiles * 2));
+
+ # Close them.
+ reporter.log2('Closing all non-stale files again ...');
+ for i, oFile in enumerate(aoFiles):
+ try:
+ oFile.close();
+ except:
+ fRc = reporter.errorXcpt('Closing non-stale file #%d failed:' % (i,));
+
+ # Check the count again.
+ try: cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ except: fRc = reporter.errorXcpt();
+ # Here we count the stale files (that is, files we don't have a reference
+ # anymore for) and the opened and then closed non-stale files (that we still keep
+ # a reference in aoFiles[] for).
+ if cFiles != cStaleFiles:
+ fRc = reporter.error('Got %d total files, expected %d' % (cFiles, cStaleFiles));
+
+ #
+ # Check that all (referenced) non-stale files are now in the "closed" state.
+ #
+ reporter.log2('Checking statuses of all non-stale files ...');
+ for i, oFile in enumerate(aoFiles):
+ try:
+ eFileStatus = aoFiles[i].status;
+ except:
+ fRc = reporter.errorXcpt('Checking status of file #%d failed:' % (i,));
+ else:
+ if eFileStatus != vboxcon.FileStatus_Closed:
+ fRc = reporter.error('Non-stale file #%d has status %d, expected %d'
+ % (i, eFileStatus, vboxcon.FileStatus_Closed));
+
+ if fRc is True:
+ reporter.log2('All non-stale files closed');
+
+ try: cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ except: fRc = reporter.errorXcpt();
+ else: reporter.log2('Final guest session file count: %d' % (cFiles,));
+
+ #
+ # Now try to close the session and see what happens.
+ # Note! Session closing is why we've been doing all the 'if fRc is True' stuff above rather than returning.
+ #
+ reporter.log2('Closing guest session ...');
+ try:
+ oGuestSession.close();
+ except:
+ fRc = reporter.errorXcpt('Testing for stale processes failed:');
+
+ return (fRc, oTxsSession);
+
+ #def testGuestCtrlSessionDirRefs(self, oSession, oTxsSession, oTestVm):
+ # """
+ # Tests the guest session directory reference handling.
+ # """
+
+ # fRc = True;
+ # return (fRc, oTxsSession);
+
+ def testGuestCtrlSessionProcRefs(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Tests the guest session process reference handling.
+ """
+
+ sShell = self.oTstDrv.getGuestSystemShell(oTestVm);
+ asArgs = [sShell,];
+
+ # Use credential defaults.
+ oCreds = tdCtxCreds();
+ oCreds.applyDefaultsIfNotSet(oTestVm);
+
+ # Number of guest processes per group to create.
+ cProcsPerGroup = 10;
+
+ #
+ # Start a session.
+ #
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(oCreds.sUser, oCreds.sPassword, oCreds.sDomain, "testGuestCtrlSessionProcRefs");
+ eWaitResult = oGuestSession.waitForArray(aeWaitFor, 30 * 1000);
+ except:
+ return (reporter.errorXcpt(), oTxsSession);
+
+ # Be nice to Guest Additions < 4.3: They don't support session handling and therefore return WaitFlagNotSupported.
+ if eWaitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ return (reporter.error('Session did not start successfully - wait error: %d' % (eWaitResult,)), oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Fire off forever-running processes and "forget" them (stale entries).
+ # For them we don't have any references anymore intentionally.
+ #
+ reporter.log('Starting stale processes...');
+ fRc = True;
+ for i in xrange(0, cProcsPerGroup):
+ try:
+ reporter.log2('Starting stale process #%d...' % (i));
+ oGuestSession.processCreate(sShell,
+ asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:], [],
+ [ vboxcon.ProcessCreateFlag_WaitForStdOut ], 30 * 1000);
+ # Note: Not keeping a process reference from the created process above is intentional and part of the test!
+
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ except:
+ fRc = reporter.errorXcpt('Creating stale process #%d failed:' % (i,));
+ break;
+
+ if fRc:
+ reporter.log2('Starting stale processes successful');
+ try: cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ reporter.log2('Proccess count is: %d' % (cProcs));
+ if cProcs != cProcsPerGroup:
+ fRc = reporter.error('Got %d stale processes, expected %d (stale)' % (cProcs, cProcsPerGroup));
+
+ if fRc:
+ #
+ # Fire off non-stale processes and wait for termination.
+ #
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ asArgs = [ sShell, '/C', 'dir', '/S', self.oTstDrv.getGuestSystemDir(oTestVm), ];
+ else:
+ asArgs = [ sShell, '-c', 'ls -la ' + self.oTstDrv.getGuestSystemDir(oTestVm), ];
+ reporter.log('Starting non-stale processes...');
+ aoProcs = [];
+ for i in xrange(0, cProcsPerGroup):
+ try:
+ reporter.log2('Starting non-stale process #%d...' % (i));
+ oCurProc = oGuestSession.processCreate(sShell, asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:],
+ [], [], 0); # Infinite timeout.
+ aoProcs.append(oCurProc);
+ except:
+ fRc = reporter.errorXcpt('Creating non-stale process #%d failed:' % (i,));
+ break;
+
+ try: cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ reporter.log2('Proccess count is: %d' % (cProcs));
+
+ reporter.log('Waiting for non-stale processes to terminate...');
+ for i, oProcess in enumerate(aoProcs):
+ try:
+ reporter.log('Waiting for non-stale process #%d...' % (i));
+ eWaitResult = oProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate, ], 30 * 1000);
+ eProcessStatus = oProcess.status;
+ except:
+ fRc = reporter.errorXcpt('Waiting for non-stale process #%d failed:' % (i,));
+ else:
+ if eProcessStatus != vboxcon.ProcessStatus_TerminatedNormally:
+ fRc = reporter.error('Waiting for non-stale processes #%d resulted in status %d, expected %d (wr=%d)'
+ % (i, eProcessStatus, vboxcon.ProcessStatus_TerminatedNormally, eWaitResult));
+ if fRc:
+ reporter.log('All non-stale processes ended successfully');
+
+ try: cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ reporter.log2('Proccess count is: %d' % (cProcs));
+
+ # Here we count the stale processes (that is, processes we don't have a reference
+ # anymore for) and the started + ended non-stale processes (that we still keep
+ # a reference in aoProcesses[] for).
+ cProcsExpected = cProcsPerGroup * 2;
+ if cProcs != cProcsExpected:
+ fRc = reporter.error('Got %d total processes, expected %d (stale vs. non-stale)' \
+ % (cProcs, cProcsExpected));
+ #
+ # Fire off non-stale blocking processes which are terminated via terminate().
+ #
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ sCmd = sShell;
+ asArgs = [ sCmd, '/C', 'pause'];
+ else:
+ sCmd = '/usr/bin/yes';
+ asArgs = [ sCmd ];
+ reporter.log('Starting blocking processes...');
+ aoProcs = [];
+ for i in xrange(0, cProcsPerGroup):
+ try:
+ reporter.log2('Starting blocking process #%d...' % (i));
+ oCurProc = oGuestSession.processCreate(sCmd, asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:],
+ [], [], 30 * 1000);
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ try:
+ reporter.log('Waiting for blocking process #%d getting started...' % (i));
+ eWaitResult = oCurProc.waitForArray([ vboxcon.ProcessWaitForFlag_Start, ], 30 * 1000);
+ eProcessStatus = oCurProc.status;
+ except:
+ fRc = reporter.errorXcpt('Waiting for blocking process #%d failed:' % (i,));
+ else:
+ if eProcessStatus != vboxcon.ProcessStatus_Started:
+ fRc = reporter.error('Waiting for blocking processes #%d resulted in status %d, expected %d (wr=%d)'
+ % (i, eProcessStatus, vboxcon.ProcessStatus_Started, eWaitResult));
+ aoProcs.append(oCurProc);
+ except:
+ fRc = reporter.errorXcpt('Creating blocking process #%d failed:' % (i,));
+ break;
+
+ if fRc:
+ reporter.log2('Starting blocking processes successful');
+
+ reporter.log2('Terminating blocking processes...');
+ for i, oProcess in enumerate(aoProcs):
+ try:
+ reporter.log('Terminating blocking process #%d...' % (i));
+ oProcess.terminate();
+ except: # Termination might not be supported, just skip and log it.
+ reporter.logXcpt('Termination of blocking process #%d failed, skipped:' % (i,));
+ if self.oTstDrv.fpApiVer >= 6.1: # Termination is supported since 5.2 or so.
+ fRc = False;
+ if fRc:
+ reporter.log('All blocking processes were terminated successfully');
+
+ try: cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ # There still should be 20 processes because we just terminated the 10 blocking ones above.
+ cProcsExpected = cProcsPerGroup * 2;
+ if cProcs != cProcsExpected:
+ fRc = reporter.error('Got %d total processes, expected %d (final)' % (cProcs, cProcsExpected));
+ reporter.log2('Final guest session processes count: %d' % (cProcs,));
+
+ if not fRc:
+ aoProcs = self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes');
+ for i, oProc in enumerate(aoProcs):
+ try:
+ aoArgs = self.oTstDrv.oVBoxMgr.getArray(oProc, 'arguments');
+ reporter.log('Process %d (\'%s\') still around, status is %d' \
+ % (i, ' '.join([str(x) for x in aoArgs]), oProc.status));
+ except:
+ reporter.errorXcpt('Process lookup failed:');
+ #
+ # Now try to close the session and see what happens.
+ #
+ reporter.log('Closing guest session ...');
+ try:
+ oGuestSession.close();
+ except:
+ fRc = reporter.errorXcpt('Closing session for testing process references failed:');
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlExec(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Tests the basic execution feature.
+ """
+
+ # Paths:
+ sVBoxControl = None; ## @todo Get path of installed Guest Additions. Later.
+ sShell = self.oTstDrv.getGuestSystemShell(oTestVm);
+ sShellOpt = '/C' if oTestVm.isWindows() or oTestVm.isOS2() else '-c';
+ sSystemDir = self.oTstDrv.getGuestSystemDir(oTestVm);
+ sFileForReading = self.oTstDrv.getGuestSystemFileForReading(oTestVm);
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ sImageOut = self.oTstDrv.getGuestSystemShell(oTestVm);
+ if oTestVm.isWindows():
+ sVBoxControl = "C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\VBoxControl.exe";
+ else:
+ sImageOut = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'ls');
+ if oTestVm.isLinux(): ## @todo check solaris and darwin.
+ sVBoxControl = "/usr/bin/VBoxControl"; # Symlink
+
+ # Use credential defaults.
+ oCreds = tdCtxCreds();
+ oCreds.applyDefaultsIfNotSet(oTestVm);
+
+ atInvalid = [
+ # Invalid parameters.
+ [ tdTestExec(), tdTestResultExec() ],
+ # Non-existent / invalid image.
+ [ tdTestExec(sCmd = "non-existent"), tdTestResultExec() ],
+ [ tdTestExec(sCmd = "non-existent2"), tdTestResultExec() ],
+ # Use an invalid format string.
+ [ tdTestExec(sCmd = "%$%%%&"), tdTestResultExec() ],
+ # More stuff.
+ [ tdTestExec(sCmd = u"ƒ‰‹ˆ÷‹¸"), tdTestResultExec() ],
+ [ tdTestExec(sCmd = "???://!!!"), tdTestResultExec() ],
+ [ tdTestExec(sCmd = "<>!\\"), tdTestResultExec() ],
+ # Enable as soon as ERROR_BAD_DEVICE is implemented.
+ #[ tdTestExec(sCmd = "CON", tdTestResultExec() ],
+ ];
+
+ atExec = [];
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ atExec += [
+ # Basic execution.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sFileForReading ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sSystemDir + '\\nonexist.dll' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', '/wrongparam' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ [ tdTestExec(sCmd = sShell, asArgs = [ sShell, sShellOpt, 'wrongcommand' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # StdOut.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', 'stdout-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # StdErr.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', 'stderr-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # StdOut + StdErr.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'dir', '/S', 'stdouterr-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ ];
+ # atExec.extend([
+ # FIXME: Failing tests.
+ # Environment variables.
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_NONEXIST' ],
+ # tdTestResultExec(fRc = True, iExitCode = 1) ]
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'windir' ],
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'windir=C:\\WINDOWS\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # aEnv = [ 'TEST_FOO=BAR' ],
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # aEnv = [ 'TEST_FOO=BAR', 'TEST_BAZ=BAR' ],
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ]
+
+ ## @todo Create some files (or get files) we know the output size of to validate output length!
+ ## @todo Add task which gets killed at some random time while letting the guest output something.
+ #];
+ else:
+ atExec += [
+ # Basic execution.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '-R', sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, sFileForReading ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '--wrong-parameter' ]),
+ tdTestResultExec(fRc = True, iExitCode = 2) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/non/existent' ]),
+ tdTestResultExec(fRc = True, iExitCode = 2) ],
+ [ tdTestExec(sCmd = sShell, asArgs = [ sShell, sShellOpt, 'wrongcommand' ]),
+ tdTestResultExec(fRc = True, iExitCode = 127) ],
+ # StdOut.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, 'stdout-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 2) ],
+ # StdErr.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, 'stderr-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 2) ],
+ # StdOut + StdErr.
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, sSystemDir ]),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, 'stdouterr-non-existing' ]),
+ tdTestResultExec(fRc = True, iExitCode = 2) ],
+ ];
+ # atExec.extend([
+ # FIXME: Failing tests.
+ # Environment variables.
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_NONEXIST' ],
+ # tdTestResultExec(fRc = True, iExitCode = 1) ]
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'windir' ],
+ #
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'windir=C:\\WINDOWS\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # aEnv = [ 'TEST_FOO=BAR' ],
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, asArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # aEnv = [ 'TEST_FOO=BAR', 'TEST_BAZ=BAR' ],
+ # afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ]
+
+ ## @todo Create some files (or get files) we know the output size of to validate output length!
+ ## @todo Add task which gets killed at some random time while letting the guest output something.
+ #];
+
+ #
+ #for iExitCode in xrange(0, 127):
+ # atExec.append([ tdTestExec(sCmd = sShell, asArgs = [ sShell, sShellOpt, 'exit %s' % iExitCode ]),
+ # tdTestResultExec(fRc = True, iExitCode = iExitCode) ]);
+
+ if sVBoxControl \
+ and self.oTstDrv.fpApiVer >= 6.0: # Investigate with this doesn't work on (<) 5.2.
+ # Paths with spaces on windows.
+ atExec.append([ tdTestExec(sCmd = sVBoxControl, asArgs = [ sVBoxControl, 'version' ],
+ afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut,
+ vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ tdTestResultExec(fRc = True) ]);
+
+ # Test very long arguments. Be careful when tweaking this to not break the tests.
+ # Regarding paths:
+ # - We have RTPATH_BIG_MAX (64K)
+ # - MSDN says 32K for CreateFileW()
+ # - On Windows, each path component must not be longer than MAX_PATH (260), see
+ # https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits
+ #
+ # Old(er) Windows OSes tend to crash in cmd.exe, so skip this on these OSes.
+ if self.oTstDrv.fpApiVer >= 6.1 \
+ and oTestVm.sKind not in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003'):
+ sEndMarker = '--end-marker';
+ if oTestVm.isWindows() \
+ or oTestVm.isOS2():
+ sCmd = sShell;
+ else:
+ sCmd = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'echo');
+
+ for _ in xrange(0, 16):
+ if oTestVm.isWindows() \
+ or oTestVm.isOS2():
+ asArgs = [ sShell, sShellOpt, "echo" ];
+ else:
+ asArgs = [ sCmd ];
+
+ # Append a random number of arguments with random length.
+ for _ in xrange(0, self.oTestFiles.oRandom.randrange(1, 64)):
+ asArgs.append(''.join(random.choice(string.ascii_lowercase)
+ for _ in range(self.oTestFiles.oRandom.randrange(1, 196))));
+
+ asArgs.append(sEndMarker);
+
+ reporter.log2('asArgs=%s (%d args), type=%s' % (limitString(asArgs), len(asArgs), type(asArgs)));
+
+ ## @todo Check limits; on Ubuntu with 256KB IPRT returns VERR_NOT_IMPLEMENTED.
+ # Use a higher timeout (15 min) than usual for these long checks.
+ atExec.append([ tdTestExec(sCmd, asArgs,
+ afFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut,
+ vboxcon.ProcessCreateFlag_WaitForStdErr ],
+ timeoutMS = 15 * 60 * 1000),
+ tdTestResultExec(fRc = True) ]);
+
+ # Build up the final test array for the first batch.
+ atTests = atInvalid + atExec;
+
+ #
+ # First batch: One session per guest process.
+ #
+ reporter.log('One session per guest process ...');
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestExec
+ oCurRes = tTest[1] # type: tdTestResultExec
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc2, oCurGuestSession = oCurTest.createSession('testGuestCtrlExec: Test #%d' % (i,));
+ if fRc2 is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ fRc = self.gctrlExecDoTest(i, oCurTest, oCurRes, oCurGuestSession) and fRc;
+ fRc = oCurTest.closeSession() and fRc;
+
+ reporter.log('Execution of all tests done, checking for stale sessions');
+
+ # No sessions left?
+ try:
+ aSessions = self.oTstDrv.oVBoxMgr.getArray(oSession.o.console.guest, 'sessions');
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ cSessions = len(aSessions);
+ if cSessions != 0:
+ fRc = reporter.error('Found %d stale session(s), expected 0:' % (cSessions,));
+ for (i, aSession) in enumerate(aSessions):
+ try: reporter.log(' Stale session #%d ("%s")' % (aSession.id, aSession.name));
+ except: reporter.errorXcpt();
+
+ if fRc is not True:
+ return (fRc, oTxsSession);
+
+ reporter.log('Now using one guest session for all tests ...');
+
+ #
+ # Second batch: One session for *all* guest processes.
+ #
+
+ # Create session.
+ reporter.log('Creating session for all tests ...');
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start, ];
+ try:
+ oGuest = oSession.o.console.guest;
+ oCurGuestSession = oGuest.createSession(oCreds.sUser, oCreds.sPassword, oCreds.sDomain,
+ 'testGuestCtrlExec: One session for all tests');
+ except:
+ return (reporter.errorXcpt(), oTxsSession);
+
+ try:
+ eWaitResult = oCurGuestSession.waitForArray(aeWaitFor, 30 * 1000);
+ except:
+ fRc = reporter.errorXcpt('Waiting for guest session to start failed:');
+ else:
+ if eWaitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ fRc = reporter.error('Session did not start successfully, returned wait result: %d' % (eWaitResult,));
+ else:
+ reporter.log('Session successfully started');
+
+ # Do the tests within this session.
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestExec
+ oCurRes = tTest[1] # type: tdTestResultExec
+
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc = self.gctrlExecDoTest(i, oCurTest, oCurRes, oCurGuestSession);
+ if fRc is False:
+ break;
+
+ # Close the session.
+ reporter.log2('Closing guest session ...');
+ try:
+ oCurGuestSession.close();
+ oCurGuestSession = None;
+ except:
+ fRc = reporter.errorXcpt('Closing guest session failed:');
+
+ # No sessions left?
+ reporter.log('Execution of all tests done, checking for stale sessions again');
+ try: cSessions = len(self.oTstDrv.oVBoxMgr.getArray(oSession.o.console.guest, 'sessions'));
+ except: fRc = reporter.errorXcpt();
+ else:
+ if cSessions != 0:
+ fRc = reporter.error('Found %d stale session(s), expected 0' % (cSessions,));
+ return (fRc, oTxsSession);
+
+ def threadForTestGuestCtrlSessionReboot(self, oGuestProcess):
+ """
+ Thread routine which waits for the stale guest process getting terminated (or some error)
+ while the main test routine reboots the guest. It then compares the expected guest process result
+ and logs an error if appropriate.
+ """
+ reporter.log('Waiting for process to get terminated at reboot ...');
+ try:
+ eWaitResult = oGuestProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate ], 5 * 60 * 1000);
+ except:
+ return reporter.errorXcpt('waitForArray failed');
+ try:
+ eStatus = oGuestProcess.status
+ except:
+ return reporter.errorXcpt('failed to get status (wait result %d)' % (eWaitResult,));
+
+ if eWaitResult == vboxcon.ProcessWaitResult_Terminate and eStatus == vboxcon.ProcessStatus_Down:
+ reporter.log('Stale process was correctly terminated (status: down)');
+ return True;
+
+ return reporter.error('Process wait across reboot failed: eWaitResult=%d, expected %d; eStatus=%d, expected %d'
+ % (eWaitResult, vboxcon.ProcessWaitResult_Terminate, eStatus, vboxcon.ProcessStatus_Down,));
+
+ def testGuestCtrlSessionReboot(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests guest object notifications when a guest gets rebooted / shutdown.
+
+ These notifications gets sent from the guest sessions in order to make API clients
+ aware of guest session changes.
+
+ To test that we create a stale guest process and trigger a reboot of the guest.
+ """
+
+ ## @todo backport fixes to 6.0 and maybe 5.2
+ if self.oTstDrv.fpApiVer <= 6.0:
+ reporter.log('Skipping: Required fixes not yet backported!');
+ return None;
+
+ # Use credential defaults.
+ oCreds = tdCtxCreds();
+ oCreds.applyDefaultsIfNotSet(oTestVm);
+
+ fRebooted = False;
+ fRc = True;
+
+ #
+ # Start a session.
+ #
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(oCreds.sUser, oCreds.sPassword, oCreds.sDomain, "testGuestCtrlSessionReboot");
+ eWaitResult = oGuestSession.waitForArray(aeWaitFor, 30 * 1000);
+ except:
+ return (reporter.errorXcpt(), oTxsSession);
+
+ # Be nice to Guest Additions < 4.3: They don't support session handling and therefore return WaitFlagNotSupported.
+ if eWaitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ return (reporter.error('Session did not start successfully - wait error: %d' % (eWaitResult,)), oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Create a process.
+ #
+ # That process will also be used later to see if the session cleanup worked upon reboot.
+ #
+ sImage = self.oTstDrv.getGuestSystemShell(oTestVm);
+ asArgs = [ sImage, ];
+ aEnv = [];
+ afFlags = [];
+ try:
+ oGuestProcess = oGuestSession.processCreate(sImage,
+ asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:], aEnv, afFlags,
+ 30 * 1000);
+ except:
+ fRc = reporter.error('Failed to start shell process (%s)' % (sImage,));
+ else:
+ try:
+ eWaitResult = oGuestProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Start ], 30 * 1000);
+ except:
+ fRc = reporter.errorXcpt('Waiting for shell process (%s) to start failed' % (sImage,));
+ else:
+ # Check the result and state:
+ try: eStatus = oGuestProcess.status;
+ except: fRc = reporter.errorXcpt('Waiting for shell process (%s) to start failed' % (sImage,));
+ else:
+ reporter.log2('Starting process wait result returned: %d; Process status is: %d' % (eWaitResult, eStatus,));
+ if eWaitResult != vboxcon.ProcessWaitResult_Start:
+ fRc = reporter.error('wait for ProcessWaitForFlag_Start failed: %d, expected %d (Start)'
+ % (eWaitResult, vboxcon.ProcessWaitResult_Start,));
+ elif eStatus != vboxcon.ProcessStatus_Started:
+ fRc = reporter.error('Unexpected process status after startup: %d, wanted %d (Started)'
+ % (eStatus, vboxcon.ProcessStatus_Started,));
+ else:
+ # Create a thread that waits on the process to terminate
+ reporter.log('Creating reboot thread ...');
+ oThreadReboot = threading.Thread(target = self.threadForTestGuestCtrlSessionReboot,
+ args = (oGuestProcess,),
+ name = ('threadForTestGuestCtrlSessionReboot'));
+ oThreadReboot.setDaemon(True); # pylint: disable=deprecated-method
+ oThreadReboot.start();
+
+ # Do the reboot.
+ reporter.log('Rebooting guest and reconnecting TXS ...');
+ (oSession, oTxsSession) = self.oTstDrv.txsRebootAndReconnectViaTcp(oSession, oTxsSession,
+ cMsTimeout = 3 * 60000);
+ if oSession \
+ and oTxsSession:
+ # Set reboot flag (needed later for session closing).
+ fRebooted = True;
+ else:
+ reporter.error('Rebooting via TXS failed');
+ try: oGuestProcess.terminate();
+ except: reporter.logXcpt();
+ fRc = False;
+
+ reporter.log('Waiting for thread to finish ...');
+ oThreadReboot.join();
+
+ # Check that the guest session now still has the formerly guest process object created above,
+ # but with the "down" status now (because of guest reboot).
+ try:
+ aoGuestProcs = self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes');
+ if len(aoGuestProcs) == 1:
+ enmProcSts = aoGuestProcs[0].status;
+ if enmProcSts != vboxcon.ProcessStatus_Down:
+ fRc = reporter.error('Old guest process (before reboot) has status %d, expected %s' \
+ % (enmProcSts, vboxcon.ProcessStatus_Down));
+ else:
+ fRc = reporter.error('Old guest session (before reboot) has %d processes registered, expected 1' \
+ % (len(aoGuestProcs)));
+ except:
+ fRc = reporter.errorXcpt();
+ #
+ # Try make sure we don't leave with a stale process on failure.
+ #
+ try: oGuestProcess.terminate();
+ except: reporter.logXcpt();
+
+ #
+ # Close the session.
+ #
+ reporter.log2('Closing guest session ...');
+ try:
+ oGuestSession.close();
+ except:
+ # Closing the guest session will fail when the guest reboot has been triggered,
+ # as the session object will be cleared on a guest reboot.
+ if fRebooted:
+ reporter.logXcpt('Closing guest session failed, good (guest rebooted)');
+ else: # ... otherwise this (still) should succeed. Report so if it doesn't.
+ reporter.errorXcpt('Closing guest session failed');
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlExecTimeout(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests handling of timeouts of started guest processes.
+ """
+
+ sShell = self.oTstDrv.getGuestSystemShell(oTestVm);
+
+ # Use credential defaults.
+ oCreds = tdCtxCreds();
+ oCreds.applyDefaultsIfNotSet(oTestVm);
+
+ #
+ # Create a session.
+ #
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(oCreds.sUser, oCreds.sPassword, oCreds.sDomain, "testGuestCtrlExecTimeout");
+ eWaitResult = oGuestSession.waitForArray([ vboxcon.GuestSessionWaitForFlag_Start, ], 30 * 1000);
+ except:
+ return (reporter.errorXcpt(), oTxsSession);
+
+ # Be nice to Guest Additions < 4.3: They don't support session handling and therefore return WaitFlagNotSupported.
+ if eWaitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ return (reporter.error('Session did not start successfully - wait error: %d' % (eWaitResult,)), oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Create a process which never terminates and should timeout when
+ # waiting for termination.
+ #
+ fRc = True;
+ try:
+ oCurProcess = oGuestSession.processCreate(sShell, [sShell,] if self.oTstDrv.fpApiVer >= 5.0 else [],
+ [], [], 30 * 1000);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ reporter.log('Waiting for process 1 being started ...');
+ try:
+ eWaitResult = oCurProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Start ], 30 * 1000);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ if eWaitResult != vboxcon.ProcessWaitResult_Start:
+ fRc = reporter.error('Waiting for process 1 to start failed, got status %d' % (eWaitResult,));
+ else:
+ for msWait in (1, 32, 2000,):
+ reporter.log('Waiting for process 1 to time out within %sms ...' % (msWait,));
+ try:
+ eWaitResult = oCurProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate, ], msWait);
+ except:
+ fRc = reporter.errorXcpt();
+ break;
+ if eWaitResult != vboxcon.ProcessWaitResult_Timeout:
+ fRc = reporter.error('Waiting for process 1 did not time out in %sms as expected: %d'
+ % (msWait, eWaitResult,));
+ break;
+ reporter.log('Waiting for process 1 timed out in %u ms, good' % (msWait,));
+
+ try:
+ oCurProcess.terminate();
+ except:
+ reporter.errorXcpt();
+ oCurProcess = None;
+
+ #
+ # Create another process that doesn't terminate, but which will be killed by VBoxService
+ # because it ran out of execution time (3 seconds).
+ #
+ try:
+ oCurProcess = oGuestSession.processCreate(sShell, [sShell,] if self.oTstDrv.fpApiVer >= 5.0 else [],
+ [], [], 3 * 1000);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ reporter.log('Waiting for process 2 being started ...');
+ try:
+ eWaitResult = oCurProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Start ], 30 * 1000);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ if eWaitResult != vboxcon.ProcessWaitResult_Start:
+ fRc = reporter.error('Waiting for process 2 to start failed, got status %d' % (eWaitResult,));
+ else:
+ reporter.log('Waiting for process 2 to get killed for running out of execution time ...');
+ try:
+ eWaitResult = oCurProcess.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate, ], 15 * 1000);
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ if eWaitResult != vboxcon.ProcessWaitResult_Timeout:
+ fRc = reporter.error('Waiting for process 2 did not time out when it should, got wait result %d'
+ % (eWaitResult,));
+ else:
+ reporter.log('Waiting for process 2 did not time out, good: %s' % (eWaitResult,));
+ try:
+ eStatus = oCurProcess.status;
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ if eStatus != vboxcon.ProcessStatus_TimedOutKilled:
+ fRc = reporter.error('Status of process 2 wrong; excepted %d, got %d'
+ % (vboxcon.ProcessStatus_TimedOutKilled, eStatus));
+ else:
+ reporter.log('Status of process 2 is TimedOutKilled (%d) is it should be.'
+ % (vboxcon.ProcessStatus_TimedOutKilled,));
+ try:
+ oCurProcess.terminate();
+ except:
+ reporter.logXcpt();
+ oCurProcess = None;
+
+ #
+ # Clean up the session.
+ #
+ try:
+ oGuestSession.close();
+ except:
+ fRc = reporter.errorXcpt();
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirCreate(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests creation of guest directories.
+ """
+
+ sScratch = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'testGuestCtrlDirCreate');
+
+ atTests = [
+ # Invalid stuff.
+ [ tdTestDirCreate(sDirectory = '' ), tdTestResultFailure() ],
+ # More unusual stuff.
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin('..', '.') ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin('..', '..') ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '..' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '../' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '../../' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '/' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '/..' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '/../' ), tdTestResultFailure() ],
+ ];
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ atTests.extend([
+ [ tdTestDirCreate(sDirectory = 'C:\\' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:\\..' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:\\..\\' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:/' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:/.' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:/./' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:/..' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = 'C:/../' ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = '\\\\uncrulez\\foo' ), tdTestResultFailure() ],
+ ]);
+ atTests.extend([
+ # Existing directories and files.
+ [ tdTestDirCreate(sDirectory = self.oTstDrv.getGuestSystemDir(oTestVm) ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = self.oTstDrv.getGuestSystemShell(oTestVm) ), tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = self.oTstDrv.getGuestSystemFileForReading(oTestVm) ), tdTestResultFailure() ],
+ # Creating directories.
+ [ tdTestDirCreate(sDirectory = sScratch ), tdTestResultSuccess() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, 'foo', 'bar', 'baz'),
+ afFlags = (vboxcon.DirectoryCreateFlag_Parents,) ), tdTestResultSuccess() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, 'foo', 'bar', 'baz'),
+ afFlags = (vboxcon.DirectoryCreateFlag_Parents,) ), tdTestResultSuccess() ],
+ # Try format strings as directories.
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, 'foo%sbar%sbaz%d' )), tdTestResultSuccess() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, '%f%%boo%%bar%RI32' )), tdTestResultSuccess() ],
+ # Long random names.
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, self.oTestFiles.generateFilenameEx(36, 28))),
+ tdTestResultSuccess() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, self.oTestFiles.generateFilenameEx(140, 116))),
+ tdTestResultSuccess() ],
+ # Too long names. ASSUMES a guests has a 255 filename length limitation.
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, self.oTestFiles.generateFilenameEx(2048, 256))),
+ tdTestResultFailure() ],
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, self.oTestFiles.generateFilenameEx(2048, 256))),
+ tdTestResultFailure() ],
+ # Missing directory in path.
+ [ tdTestDirCreate(sDirectory = oTestVm.pathJoin(sScratch, 'foo1', 'bar') ), tdTestResultFailure() ],
+ ]);
+
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestDirCreate
+ oCurRes = tTest[1] # type: tdTestResult
+ reporter.log('Testing #%d, sDirectory="%s" ...' % (i, limitString(oCurTest.sDirectory)));
+
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, oCurGuestSession = oCurTest.createSession('testGuestCtrlDirCreate: Test #%d' % (i,));
+ if fRc is False:
+ return reporter.error('Test #%d failed: Could not create session' % (i,));
+
+ fRc = self.gctrlCreateDir(oCurTest, oCurRes, oCurGuestSession);
+
+ fRc = oCurTest.closeSession() and fRc;
+ if fRc is False:
+ fRc = reporter.error('Test #%d failed' % (i,));
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirCreateTemp(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests creation of temporary directories.
+ """
+
+ sSystemDir = self.oTstDrv.getGuestSystemDir(oTestVm);
+ atTests = [
+ # Invalid stuff (template must have one or more trailin 'X'es (upper case only), or a cluster of three or more).
+ [ tdTestDirCreateTemp(sDirectory = ''), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sDirectory = sSystemDir, fMode = 1234), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'xXx', sDirectory = sSystemDir, fMode = 0o700), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'xxx', sDirectory = sSystemDir, fMode = 0o700), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXx', sDirectory = sSystemDir, fMode = 0o700), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'bar', sDirectory = 'whatever', fMode = 0o700), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'foo', sDirectory = 'it is not used', fMode = 0o700), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'X,so', sDirectory = 'pointless test', fMode = 0o700), tdTestResultFailure() ],
+ # Non-existing stuff.
+ [ tdTestDirCreateTemp(sTemplate = 'XXXXXXX',
+ sDirectory = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'non', 'existing')),
+ tdTestResultFailure() ],
+ # Working stuff:
+ [ tdTestDirCreateTemp(sTemplate = 'X', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)), tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXXXXXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm)),
+ tdTestResultFailure() ],
+ ];
+
+ if self.oTstDrv.fpApiVer >= 7.0:
+ # Weird mode set.
+ atTests.extend([
+ [ tdTestDirCreateTemp(sTemplate = 'XXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o42333),
+ tdTestResultFailure() ]
+ ]);
+ # Same as working stuff above, but with a different mode set.
+ atTests.extend([
+ [ tdTestDirCreateTemp(sTemplate = 'X', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXXXXXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fMode = 0o777),
+ tdTestResultFailure() ]
+ ]);
+ # Same as working stuff above, but with secure mode set.
+ atTests.extend([
+ [ tdTestDirCreateTemp(sTemplate = 'X', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'XXXXXXX', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm), fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm),
+ fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm),
+ fSecure = True),
+ tdTestResultFailure() ],
+ [ tdTestDirCreateTemp(sTemplate = 'tmpXXXtst', sDirectory = self.oTstDrv.getGuestTempDir(oTestVm),
+ fSecure = True),
+ tdTestResultFailure() ]
+ ]);
+
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestDirCreateTemp
+ oCurRes = tTest[1] # type: tdTestResult
+ reporter.log('Testing #%d, sTemplate="%s", fMode=%#o, path="%s", secure="%s" ...' %
+ (i, oCurTest.sTemplate, oCurTest.fMode, oCurTest.sDirectory, oCurTest.fSecure));
+
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, oCurGuestSession = oCurTest.createSession('testGuestCtrlDirCreateTemp: Test #%d' % (i,));
+ if fRc is False:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ sDirTemp = '';
+ try:
+ sDirTemp = oCurGuestSession.directoryCreateTemp(oCurTest.sTemplate, oCurTest.fMode,
+ oCurTest.sDirectory, oCurTest.fSecure);
+ except:
+ if oCurRes.fRc is True:
+ fRc = reporter.errorXcpt('Creating temp directory "%s" failed:' % (oCurTest.sDirectory,));
+ else:
+ reporter.logXcpt('Creating temp directory "%s" failed expectedly, skipping:' % (oCurTest.sDirectory,));
+ else:
+ reporter.log2('Temporary directory is: "%s"' % (limitString(sDirTemp),));
+ if not sDirTemp:
+ fRc = reporter.error('Resulting directory is empty!');
+ else:
+ ## @todo This does not work for some unknown reason.
+ #try:
+ # if self.oTstDrv.fpApiVer >= 5.0:
+ # fExists = oCurGuestSession.directoryExists(sDirTemp, False);
+ # else:
+ # fExists = oCurGuestSession.directoryExists(sDirTemp);
+ #except:
+ # fRc = reporter.errorXcpt('sDirTemp=%s' % (sDirTemp,));
+ #else:
+ # if fExists is not True:
+ # fRc = reporter.error('Test #%d failed: Temporary directory "%s" does not exists (%s)'
+ # % (i, sDirTemp, fExists));
+ try:
+ oFsObjInfo = oCurGuestSession.fsObjQueryInfo(sDirTemp, False);
+ eType = oFsObjInfo.type;
+ except:
+ fRc = reporter.errorXcpt('sDirTemp="%s"' % (sDirTemp,));
+ else:
+ reporter.log2('%s: eType=%s (dir=%d)' % (limitString(sDirTemp), eType, vboxcon.FsObjType_Directory,));
+ if eType != vboxcon.FsObjType_Directory:
+ fRc = reporter.error('Temporary directory "%s" not created as a directory: eType=%d'
+ % (sDirTemp, eType));
+ fRc = oCurTest.closeSession() and fRc;
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirRead(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests opening and reading (enumerating) guest directories.
+ """
+
+ sSystemDir = self.oTstDrv.getGuestSystemDir(oTestVm);
+ atTests = [
+ # Invalid stuff.
+ [ tdTestDirRead(sDirectory = ''), tdTestResultDirRead() ],
+ [ tdTestDirRead(sDirectory = sSystemDir, afFlags = [ 1234 ]), tdTestResultDirRead() ],
+ [ tdTestDirRead(sDirectory = sSystemDir, sFilter = '*.foo'), tdTestResultDirRead() ],
+ # Non-existing stuff.
+ [ tdTestDirRead(sDirectory = oTestVm.pathJoin(sSystemDir, 'really-no-such-subdir')), tdTestResultDirRead() ],
+ [ tdTestDirRead(sDirectory = oTestVm.pathJoin(sSystemDir, 'non', 'existing')), tdTestResultDirRead() ],
+ ];
+
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ atTests.extend([
+ # More unusual stuff.
+ [ tdTestDirRead(sDirectory = 'z:\\'), tdTestResultDirRead() ],
+ [ tdTestDirRead(sDirectory = '\\\\uncrulez\\foo'), tdTestResultDirRead() ],
+ ]);
+
+ # Read the system directory (ASSUMES at least 5 files in it):
+ # Windows 7+ has inaccessible system32/com/dmp directory that screws up this test, so skip it on windows:
+ if not oTestVm.isWindows():
+ atTests.append([ tdTestDirRead(sDirectory = sSystemDir),
+ tdTestResultDirRead(fRc = True, cFiles = -5, cDirs = None) ]);
+ ## @todo trailing slash
+
+ # Read from the test file set.
+ atTests.extend([
+ [ tdTestDirRead(sDirectory = self.oTestFiles.oEmptyDir.sPath),
+ tdTestResultDirRead(fRc = True, cFiles = 0, cDirs = 0, cOthers = 0) ],
+ [ tdTestDirRead(sDirectory = self.oTestFiles.oManyDir.sPath),
+ tdTestResultDirRead(fRc = True, cFiles = len(self.oTestFiles.oManyDir.aoChildren), cDirs = 0, cOthers = 0) ],
+ [ tdTestDirRead(sDirectory = self.oTestFiles.oTreeDir.sPath),
+ tdTestResultDirRead(fRc = True, cFiles = self.oTestFiles.cTreeFiles, cDirs = self.oTestFiles.cTreeDirs,
+ cOthers = self.oTestFiles.cTreeOthers) ],
+ ]);
+
+
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestExec
+ oCurRes = tTest[1] # type: tdTestResultDirRead
+
+ reporter.log('Testing #%d, dir="%s" ...' % (i, oCurTest.sDirectory));
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, oCurGuestSession = oCurTest.createSession('testGuestCtrlDirRead: Test #%d' % (i,));
+ if fRc is not True:
+ break;
+ (fRc2, cDirs, cFiles, cOthers) = self.gctrlReadDirTree(oCurTest, oCurGuestSession, oCurRes.fRc);
+ fRc = oCurTest.closeSession() and fRc;
+
+ reporter.log2('Test #%d: Returned %d directories, %d files total' % (i, cDirs, cFiles));
+ if fRc2 is oCurRes.fRc:
+ if fRc2 is True:
+ if oCurRes.cFiles is None:
+ pass; # ignore
+ elif oCurRes.cFiles >= 0 and cFiles != oCurRes.cFiles:
+ fRc = reporter.error('Test #%d failed: Got %d files, expected %d' % (i, cFiles, oCurRes.cFiles));
+ elif oCurRes.cFiles < 0 and cFiles < -oCurRes.cFiles:
+ fRc = reporter.error('Test #%d failed: Got %d files, expected at least %d'
+ % (i, cFiles, -oCurRes.cFiles));
+ if oCurRes.cDirs is None:
+ pass; # ignore
+ elif oCurRes.cDirs >= 0 and cDirs != oCurRes.cDirs:
+ fRc = reporter.error('Test #%d failed: Got %d directories, expected %d' % (i, cDirs, oCurRes.cDirs));
+ elif oCurRes.cDirs < 0 and cDirs < -oCurRes.cDirs:
+ fRc = reporter.error('Test #%d failed: Got %d directories, expected at least %d'
+ % (i, cDirs, -oCurRes.cDirs));
+ if oCurRes.cOthers is None:
+ pass; # ignore
+ elif oCurRes.cOthers >= 0 and cOthers != oCurRes.cOthers:
+ fRc = reporter.error('Test #%d failed: Got %d other types, expected %d' % (i, cOthers, oCurRes.cOthers));
+ elif oCurRes.cOthers < 0 and cOthers < -oCurRes.cOthers:
+ fRc = reporter.error('Test #%d failed: Got %d other types, expected at least %d'
+ % (i, cOthers, -oCurRes.cOthers));
+
+ else:
+ fRc = reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, oCurRes.fRc));
+
+
+ #
+ # Go over a few directories in the test file set and compare names,
+ # types and sizes rather than just the counts like we did above.
+ #
+ if fRc is True:
+ oCurTest = tdTestDirRead();
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if fRc:
+ fRc, oCurGuestSession = oCurTest.createSession('testGuestCtrlDirRead: gctrlReadDirTree2');
+ if fRc is True:
+ for oDir in (self.oTestFiles.oEmptyDir, self.oTestFiles.oManyDir, self.oTestFiles.oTreeDir):
+ reporter.log('Checking "%s" ...' % (oDir.sPath,));
+ fRc = self.gctrlReadDirTree2(oCurGuestSession, oDir) and fRc;
+ fRc = oCurTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+
+ def testGuestCtrlFileRemove(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests removing guest files.
+ """
+
+ #
+ # Create a directory with a few files in it using TXS that we'll use for the initial tests.
+ #
+ asTestDirs = [
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-1'), # [0]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-1', 'subdir-1'), # [1]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-1', 'subdir-1', 'subsubdir-1'), # [2]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-2'), # [3]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-2', 'subdir-2'), # [4]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-2', 'subdir-2', 'subsbudir-2'), # [5]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-3'), # [6]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-4'), # [7]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-5'), # [8]
+ oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'rmtestdir-5', 'subdir-5'), # [9]
+ ]
+ asTestFiles = [
+ oTestVm.pathJoin(asTestDirs[0], 'file-0'), # [0]
+ oTestVm.pathJoin(asTestDirs[0], 'file-1'), # [1]
+ oTestVm.pathJoin(asTestDirs[0], 'file-2'), # [2]
+ oTestVm.pathJoin(asTestDirs[1], 'file-3'), # [3] - subdir-1
+ oTestVm.pathJoin(asTestDirs[1], 'file-4'), # [4] - subdir-1
+ oTestVm.pathJoin(asTestDirs[2], 'file-5'), # [5] - subsubdir-1
+ oTestVm.pathJoin(asTestDirs[3], 'file-6'), # [6] - rmtestdir-2
+ oTestVm.pathJoin(asTestDirs[4], 'file-7'), # [7] - subdir-2
+ oTestVm.pathJoin(asTestDirs[5], 'file-8'), # [8] - subsubdir-2
+ ];
+ for sDir in asTestDirs:
+ if oTxsSession.syncMkDir(sDir, 0o777) is not True:
+ return reporter.error('Failed to create test dir "%s"!' % (sDir,));
+ for sFile in asTestFiles:
+ if oTxsSession.syncUploadString(sFile, sFile, 0o666) is not True:
+ return reporter.error('Failed to create test file "%s"!' % (sFile,));
+
+ #
+ # Tear down the directories and files.
+ #
+ aoTests = [
+ # Negative tests first:
+ tdTestRemoveFile(asTestDirs[0], fRcExpect = False),
+ tdTestRemoveDir(asTestDirs[0], fRcExpect = False),
+ tdTestRemoveDir(asTestFiles[0], fRcExpect = False),
+ tdTestRemoveFile(oTestVm.pathJoin(self.oTestFiles.oEmptyDir.sPath, 'no-such-file'), fRcExpect = False),
+ tdTestRemoveDir(oTestVm.pathJoin(self.oTestFiles.oEmptyDir.sPath, 'no-such-dir'), fRcExpect = False),
+ tdTestRemoveFile(oTestVm.pathJoin(self.oTestFiles.oEmptyDir.sPath, 'no-such-dir', 'no-file'), fRcExpect = False),
+ tdTestRemoveDir(oTestVm.pathJoin(self.oTestFiles.oEmptyDir.sPath, 'no-such-dir', 'no-subdir'), fRcExpect = False),
+ tdTestRemoveTree(asTestDirs[0], afFlags = [], fRcExpect = False), # Only removes empty dirs, this isn't empty.
+ tdTestRemoveTree(asTestDirs[0], afFlags = [vboxcon.DirectoryRemoveRecFlag_None,], fRcExpect = False), # ditto
+ # Empty paths:
+ tdTestRemoveFile('', fRcExpect = False),
+ tdTestRemoveDir('', fRcExpect = False),
+ tdTestRemoveTree('', fRcExpect = False),
+ # Now actually remove stuff:
+ tdTestRemoveDir(asTestDirs[7], fRcExpect = True),
+ tdTestRemoveFile(asTestDirs[6], fRcExpect = False),
+ tdTestRemoveDir(asTestDirs[6], fRcExpect = True),
+ tdTestRemoveFile(asTestFiles[0], fRcExpect = True),
+ tdTestRemoveFile(asTestFiles[0], fRcExpect = False),
+ # 17:
+ tdTestRemoveTree(asTestDirs[8], fRcExpect = True), # Removes empty subdirs and leaves the dir itself.
+ tdTestRemoveDir(asTestDirs[8], fRcExpect = True),
+ tdTestRemoveTree(asTestDirs[3], fRcExpect = False), # Have subdirs & files,
+ tdTestRemoveTree(asTestDirs[3], afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentOnly,], fRcExpect = True),
+ tdTestRemoveDir(asTestDirs[3], fRcExpect = True),
+ tdTestRemoveTree(asTestDirs[0], afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentAndDir,], fRcExpect = True),
+ # No error if already delete (RTDirRemoveRecursive artifact).
+ tdTestRemoveTree(asTestDirs[0], afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentAndDir,], fRcExpect = True),
+ tdTestRemoveTree(asTestDirs[0], afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentOnly,],
+ fNotExist = True, fRcExpect = True),
+ tdTestRemoveTree(asTestDirs[0], afFlags = [vboxcon.DirectoryRemoveRecFlag_None,], fNotExist = True, fRcExpect = True),
+ ];
+
+ #
+ # Execution loop
+ #
+ fRc = True;
+ for (i, oTest) in enumerate(aoTests): # int, tdTestRemoveBase
+ reporter.log('Testing #%d, path="%s" %s ...' % (i, oTest.sPath, oTest.__class__.__name__));
+ fRc = oTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, _ = oTest.createSession('testGuestCtrlFileRemove: Test #%d' % (i,));
+ if fRc is False:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ fRc = oTest.execute(self) and fRc;
+ fRc = oTest.closeSession() and fRc;
+
+ if fRc is True:
+ oCurTest = tdTestDirRead();
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if fRc:
+ fRc, oCurGuestSession = oCurTest.createSession('remove final');
+ if fRc is True:
+
+ #
+ # Delete all the files in the many subdir of the test set.
+ #
+ reporter.log('Deleting the file in "%s" ...' % (self.oTestFiles.oManyDir.sPath,));
+ for oFile in self.oTestFiles.oManyDir.aoChildren:
+ reporter.log2('"%s"' % (limitString(oFile.sPath),));
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurGuestSession.fsObjRemove(oFile.sPath);
+ else:
+ oCurGuestSession.fileRemove(oFile.sPath);
+ except:
+ fRc = reporter.errorXcpt('Removing "%s" failed' % (oFile.sPath,));
+
+ # Remove the directory itself to verify that we've removed all the files in it:
+ reporter.log('Removing the directory "%s" ...' % (self.oTestFiles.oManyDir.sPath,));
+ try:
+ oCurGuestSession.directoryRemove(self.oTestFiles.oManyDir.sPath);
+ except:
+ fRc = reporter.errorXcpt('Removing directory "%s" failed' % (self.oTestFiles.oManyDir.sPath,));
+
+ #
+ # Recursively delete the entire test file tree from the root up.
+ #
+ # Note! On unix we cannot delete the root dir itself since it is residing
+ # in /var/tmp where only the owner may delete it. Root is the owner.
+ #
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentAndDir,];
+ else:
+ afFlags = [vboxcon.DirectoryRemoveRecFlag_ContentOnly,];
+ try:
+ oProgress = oCurGuestSession.directoryRemoveRecursive(self.oTestFiles.oRoot.sPath, afFlags);
+ except:
+ fRc = reporter.errorXcpt('Removing tree "%s" failed' % (self.oTestFiles.oRoot.sPath,));
+ else:
+ oWrappedProgress = vboxwrappers.ProgressWrapper(oProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ "remove-tree-root: %s" % (self.oTestFiles.oRoot.sPath,));
+ reporter.log2('waiting ...')
+ oWrappedProgress.wait();
+ reporter.log2('isSuccess=%s' % (oWrappedProgress.isSuccess(),));
+ if not oWrappedProgress.isSuccess():
+ fRc = oWrappedProgress.logResult();
+
+ fRc = oCurTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+
+ def testGuestCtrlFileStat(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests querying file information through stat.
+ """
+
+ # Basic stuff, existing stuff.
+ aoTests = [
+ tdTestSessionEx([
+ tdStepStatDir('.'),
+ tdStepStatDir('..'),
+ tdStepStatDir(self.oTstDrv.getGuestTempDir(oTestVm)),
+ tdStepStatDir(self.oTstDrv.getGuestSystemDir(oTestVm)),
+ tdStepStatDirEx(self.oTestFiles.oRoot),
+ tdStepStatDirEx(self.oTestFiles.oEmptyDir),
+ tdStepStatDirEx(self.oTestFiles.oTreeDir),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatDirEx(self.oTestFiles.chooseRandomDirFromTree()),
+ tdStepStatFile(self.oTstDrv.getGuestSystemFileForReading(oTestVm)),
+ tdStepStatFile(self.oTstDrv.getGuestSystemShell(oTestVm)),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ tdStepStatFileEx(self.oTestFiles.chooseRandomFile()),
+ ]),
+ ];
+
+ # None existing stuff.
+ sSysDir = self.oTstDrv.getGuestSystemDir(oTestVm);
+ sSep = oTestVm.pathSep();
+ aoTests += [
+ tdTestSessionEx([
+ tdStepStatFileNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory')),
+ tdStepStatPathNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory') + sSep),
+ tdStepStatPathNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory', '.')),
+ tdStepStatPathNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory', 'NoSuchFileOrSubDirectory')),
+ tdStepStatPathNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory', 'NoSuchFileOrSubDirectory') + sSep),
+ tdStepStatPathNotFound(oTestVm.pathJoin(sSysDir, 'NoSuchFileOrDirectory', 'NoSuchFileOrSubDirectory', '.')),
+ #tdStepStatPathNotFound('N:\\'), # ASSUMES nothing mounted on N:!
+ #tdStepStatPathNotFound('\\\\NoSuchUncServerName\\NoSuchShare'),
+ ]),
+ ];
+ # Invalid parameter check.
+ aoTests += [ tdTestSessionEx([ tdStepStat('', vbox.ComError.E_INVALIDARG), ]), ];
+
+ #
+ # Execute the tests.
+ #
+ fRc, oTxsSession = tdTestSessionEx.executeListTestSessions(aoTests, self.oTstDrv, oSession, oTxsSession,
+ oTestVm, 'FsStat');
+ #
+ # Test the full test file set.
+ #
+ if self.oTstDrv.fpApiVer < 5.0:
+ return (fRc, oTxsSession);
+
+ oTest = tdTestGuestCtrlBase();
+ fRc = oTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ return (False, oTxsSession);
+ fRc2, oGuestSession = oTest.createSession('FsStat on TestFileSet');
+ if fRc2 is not True:
+ return (False, oTxsSession);
+
+ for oFsObj in self.oTestFiles.dPaths.values():
+ reporter.log2('testGuestCtrlFileStat: %s sPath=%s'
+ % ('file' if isinstance(oFsObj, testfileset.TestFile) else 'dir ', limitString(oFsObj.sPath),));
+
+ # Query the information:
+ try:
+ oFsInfo = oGuestSession.fsObjQueryInfo(oFsObj.sPath, False);
+ except:
+ fRc = reporter.errorXcpt('sPath=%s type=%s: fsObjQueryInfo trouble!' % (oFsObj.sPath, type(oFsObj),));
+ continue;
+ if oFsInfo is None:
+ fRc = reporter.error('sPath=%s type=%s: No info object returned!' % (oFsObj.sPath, type(oFsObj),));
+ continue;
+
+ # Check attributes:
+ try:
+ eType = oFsInfo.type;
+ cbObject = oFsInfo.objectSize;
+ except:
+ fRc = reporter.errorXcpt('sPath=%s type=%s: attribute access trouble!' % (oFsObj.sPath, type(oFsObj),));
+ continue;
+
+ if isinstance(oFsObj, testfileset.TestFile):
+ if eType != vboxcon.FsObjType_File:
+ fRc = reporter.error('sPath=%s type=file: eType=%s, expected %s!'
+ % (oFsObj.sPath, eType, vboxcon.FsObjType_File));
+ if cbObject != oFsObj.cbContent:
+ fRc = reporter.error('sPath=%s type=file: cbObject=%s, expected %s!'
+ % (oFsObj.sPath, cbObject, oFsObj.cbContent));
+ fFileExists = True;
+ fDirExists = False;
+ elif isinstance(oFsObj, testfileset.TestDir):
+ if eType != vboxcon.FsObjType_Directory:
+ fRc = reporter.error('sPath=%s type=dir: eType=%s, expected %s!'
+ % (oFsObj.sPath, eType, vboxcon.FsObjType_Directory));
+ fFileExists = False;
+ fDirExists = True;
+ else:
+ fRc = reporter.error('sPath=%s type=%s: Unexpected oFsObj type!' % (oFsObj.sPath, type(oFsObj),));
+ continue;
+
+ # Check the directoryExists and fileExists results too.
+ try:
+ fExistsResult = oGuestSession.fileExists(oFsObj.sPath, False);
+ except:
+ fRc = reporter.errorXcpt('sPath=%s type=%s: fileExists trouble!' % (oFsObj.sPath, type(oFsObj),));
+ else:
+ if fExistsResult != fFileExists:
+ fRc = reporter.error('sPath=%s type=%s: fileExists returned %s, expected %s!'
+ % (oFsObj.sPath, type(oFsObj), fExistsResult, fFileExists));
+ try:
+ fExistsResult = oGuestSession.directoryExists(oFsObj.sPath, False);
+ except:
+ fRc = reporter.errorXcpt('sPath=%s type=%s: directoryExists trouble!' % (oFsObj.sPath, type(oFsObj),));
+ else:
+ if fExistsResult != fDirExists:
+ fRc = reporter.error('sPath=%s type=%s: directoryExists returned %s, expected %s!'
+ % (oFsObj.sPath, type(oFsObj), fExistsResult, fDirExists));
+
+ fRc = oTest.closeSession() and fRc;
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlFileOpen(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests opening guest files.
+ """
+ if self.oTstDrv.fpApiVer < 5.0:
+ reporter.log('Skipping because of pre 5.0 API');
+ return None;
+
+ #
+ # Paths.
+ #
+ sTempDir = self.oTstDrv.getGuestTempDir(oTestVm);
+ sFileForReading = self.oTstDrv.getGuestSystemFileForReading(oTestVm);
+ asFiles = [
+ oTestVm.pathJoin(sTempDir, 'file-open-0'),
+ oTestVm.pathJoin(sTempDir, 'file-open-1'),
+ oTestVm.pathJoin(sTempDir, 'file-open-2'),
+ oTestVm.pathJoin(sTempDir, 'file-open-3'),
+ oTestVm.pathJoin(sTempDir, 'file-open-4'),
+ ];
+ asNonEmptyFiles = [
+ oTestVm.pathJoin(sTempDir, 'file-open-10'),
+ oTestVm.pathJoin(sTempDir, 'file-open-11'),
+ oTestVm.pathJoin(sTempDir, 'file-open-12'),
+ oTestVm.pathJoin(sTempDir, 'file-open-13'),
+ ];
+ sContent = 'abcdefghijklmnopqrstuvwxyz0123456789';
+ for sFile in asNonEmptyFiles:
+ if oTxsSession.syncUploadString(sContent, sFile, 0o666) is not True:
+ return reporter.error('Failed to create "%s" via TXS' % (sFile,));
+
+ #
+ # The tests.
+ #
+ atTests = [
+ # Invalid stuff.
+ [ tdTestFileOpen(sFile = ''), tdTestResultFailure() ],
+ # Wrong open mode.
+ [ tdTestFileOpen(sFile = sFileForReading, eAccessMode = -1), tdTestResultFailure() ],
+ # Wrong disposition.
+ [ tdTestFileOpen(sFile = sFileForReading, eAction = -1), tdTestResultFailure() ],
+ # Non-existing file or path.
+ [ tdTestFileOpen(sFile = oTestVm.pathJoin(sTempDir, 'no-such-file-or-dir')), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = oTestVm.pathJoin(sTempDir, 'no-such-file-or-dir'),
+ eAction = vboxcon.FileOpenAction_OpenExistingTruncated), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = oTestVm.pathJoin(sTempDir, 'no-such-file-or-dir'),
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly,
+ eAction = vboxcon.FileOpenAction_OpenExistingTruncated), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = oTestVm.pathJoin(sTempDir, 'no-such-file-or-dir'),
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite,
+ eAction = vboxcon.FileOpenAction_OpenExistingTruncated), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = oTestVm.pathJoin(sTempDir, 'no-such-dir', 'no-such-file')), tdTestResultFailure() ],
+ ];
+ if self.oTstDrv.fpApiVer > 5.2: # Fixed since 6.0.
+ atTests.extend([
+ # Wrong type:
+ [ tdTestFileOpen(sFile = self.oTstDrv.getGuestTempDir(oTestVm)), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = self.oTstDrv.getGuestSystemDir(oTestVm)), tdTestResultFailure() ],
+ ]);
+ atTests.extend([
+ # O_EXCL and such:
+ [ tdTestFileOpen(sFile = sFileForReading, eAction = vboxcon.FileOpenAction_CreateNew,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultFailure() ],
+ [ tdTestFileOpen(sFile = sFileForReading, eAction = vboxcon.FileOpenAction_CreateNew), tdTestResultFailure() ],
+ # Open a file.
+ [ tdTestFileOpen(sFile = sFileForReading), tdTestResultSuccess() ],
+ [ tdTestFileOpen(sFile = sFileForReading,
+ eAction = vboxcon.FileOpenAction_OpenOrCreate), tdTestResultSuccess() ],
+ # Create a new file.
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_CreateNew,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_CreateNew,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultFailure() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_OpenExisting,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_CreateOrReplace,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_OpenOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_OpenExistingTruncated,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[0], eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ # Open or create a new file.
+ [ tdTestFileOpenCheckSize(sFile = asFiles[1], eAction = vboxcon.FileOpenAction_OpenOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ # Create or replace a new file.
+ [ tdTestFileOpenCheckSize(sFile = asFiles[2], eAction = vboxcon.FileOpenAction_CreateOrReplace,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ # Create and append to file (weird stuff).
+ [ tdTestFileOpenCheckSize(sFile = asFiles[3], eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asFiles[4], eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+ # Open the non-empty files in non-destructive modes.
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[0], cbOpenExpected = len(sContent)), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[1], cbOpenExpected = len(sContent),
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[2], cbOpenExpected = len(sContent),
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[0], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_OpenOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[1], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_OpenOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[2], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_OpenOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[0], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_ReadWrite), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[1], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[2], cbOpenExpected = len(sContent),
+ eAction = vboxcon.FileOpenAction_AppendOrCreate,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+
+ # Now the destructive stuff:
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[0], eAccessMode = vboxcon.FileAccessMode_WriteOnly,
+ eAction = vboxcon.FileOpenAction_OpenExistingTruncated), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[1], eAccessMode = vboxcon.FileAccessMode_WriteOnly,
+ eAction = vboxcon.FileOpenAction_CreateOrReplace), tdTestResultSuccess() ],
+ [ tdTestFileOpenCheckSize(sFile = asNonEmptyFiles[2], eAction = vboxcon.FileOpenAction_CreateOrReplace,
+ eAccessMode = vboxcon.FileAccessMode_WriteOnly), tdTestResultSuccess() ],
+ ]);
+
+ #
+ # Do the testing.
+ #
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestFileOpen
+ oCurRes = tTest[1] # type: tdTestResult
+
+ reporter.log('Testing #%d: %s - sFile="%s", eAccessMode=%d, eAction=%d, (%s, %s, %s) ...'
+ % (i, oCurTest.__class__.__name__, oCurTest.sFile, oCurTest.eAccessMode, oCurTest.eAction,
+ oCurTest.eSharing, oCurTest.fCreationMode, oCurTest.afOpenFlags,));
+
+ oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, _ = oCurTest.createSession('testGuestCtrlFileOpen: Test #%d' % (i,));
+ if fRc is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ fRc2 = oCurTest.doSteps(oCurRes.fRc, self);
+ if fRc2 != oCurRes.fRc:
+ fRc = reporter.error('Test #%d result mismatch: Got %s, expected %s' % (i, fRc2, oCurRes.fRc,));
+
+ fRc = oCurTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+
+ def testGuestCtrlFileRead(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-branches,too-many-statements
+ """
+ Tests reading from guest files.
+ """
+ if self.oTstDrv.fpApiVer < 5.0:
+ reporter.log('Skipping because of pre 5.0 API');
+ return None;
+
+ #
+ # Do everything in one session.
+ #
+ oTest = tdTestGuestCtrlBase();
+ fRc = oTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ return (False, oTxsSession);
+ fRc2, oGuestSession = oTest.createSession('FsStat on TestFileSet');
+ if fRc2 is not True:
+ return (False, oTxsSession);
+
+ #
+ # Create a really big zero filled, up to 1 GiB, adding it to the list of
+ # files from the set.
+ #
+ # Note! This code sucks a bit because we don't have a working setSize nor
+ # any way to figure out how much free space there is in the guest.
+ #
+ aoExtraFiles = [];
+ sBigName = self.oTestFiles.generateFilenameEx();
+ sBigPath = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, sBigName);
+ fRc = True;
+ try:
+ oFile = oGuestSession.fileOpenEx(sBigPath, vboxcon.FileAccessMode_ReadWrite, vboxcon.FileOpenAction_CreateOrReplace,
+ vboxcon.FileSharingMode_All, 0, []);
+ except:
+ fRc = reporter.errorXcpt('sBigName=%s' % (sBigPath,));
+ else:
+ # Does setSize work now?
+ fUseFallback = True;
+ try:
+ oFile.setSize(0);
+ oFile.setSize(64);
+ fUseFallback = False;
+ except:
+ reporter.logXcpt();
+
+ # Grow the file till we hit trouble, typical VERR_DISK_FULL, then
+ # reduce the file size if we have a working setSize.
+ cbBigFile = 0;
+ while cbBigFile < (1024 + 32)*1024*1024:
+ if not fUseFallback:
+ cbBigFile += 16*1024*1024;
+ try:
+ oFile.setSize(cbBigFile);
+ except Exception:
+ reporter.logXcpt('cbBigFile=%s' % (sBigPath,));
+ try:
+ cbBigFile -= 16*1024*1024;
+ oFile.setSize(cbBigFile);
+ except:
+ reporter.logXcpt('cbBigFile=%s' % (sBigPath,));
+ break;
+ else:
+ cbBigFile += 32*1024*1024;
+ try:
+ oFile.seek(cbBigFile, vboxcon.FileSeekOrigin_Begin);
+ oFile.write(bytearray(1), 60*1000);
+ except:
+ reporter.logXcpt('cbBigFile=%s' % (sBigPath,));
+ break;
+ try:
+ cbBigFile = oFile.seek(0, vboxcon.FileSeekOrigin_End);
+ except:
+ fRc = reporter.errorXcpt('sBigName=%s' % (sBigPath,));
+ try:
+ oFile.close();
+ except:
+ fRc = reporter.errorXcpt('sBigName=%s' % (sBigPath,));
+ if fRc is True:
+ reporter.log('Big file: %s bytes: %s' % (cbBigFile, sBigPath,));
+ aoExtraFiles.append(testfileset.TestFileZeroFilled(None, sBigPath, cbBigFile));
+ else:
+ try:
+ oGuestSession.fsObjRemove(sBigPath);
+ except:
+ reporter.errorXcpt('fsObjRemove(sBigName=%s)' % (sBigPath,));
+
+ #
+ # Open and read all the files in the test file set.
+ #
+ for oTestFile in aoExtraFiles + self.oTestFiles.aoFiles: # type: testfileset.TestFile
+ reporter.log2('Test file: %s bytes, "%s" ...' % (oTestFile.cbContent, limitString(oTestFile.sPath),));
+
+ #
+ # Open it:
+ #
+ try:
+ oFile = oGuestSession.fileOpenEx(oTestFile.sPath, vboxcon.FileAccessMode_ReadOnly,
+ vboxcon.FileOpenAction_OpenExisting, vboxcon.FileSharingMode_All, 0, []);
+ except:
+ fRc = reporter.errorXcpt('sPath=%s' % (oTestFile.sPath, ));
+ continue;
+
+ #
+ # Read the file in different sized chunks:
+ #
+ if oTestFile.cbContent < 128:
+ acbChunks = xrange(1,128);
+ elif oTestFile.cbContent < 1024:
+ acbChunks = (2048, 127, 63, 32, 29, 17, 16, 15, 9);
+ elif oTestFile.cbContent < 8*1024*1024:
+ acbChunks = (128*1024, 32*1024, 8191, 255);
+ else:
+ acbChunks = (768*1024, 128*1024);
+
+ reporter.log2('Chunked reads');
+
+ for cbChunk in acbChunks:
+ # Read the whole file straight thru:
+ #if oTestFile.cbContent >= 1024*1024: reporter.log2('... cbChunk=%s' % (cbChunk,));
+ offFile = 0;
+ cReads = 0;
+ while offFile <= oTestFile.cbContent:
+ try:
+ abRead = oFile.read(cbChunk, 30*1000);
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbChunk=%s cbContent=%s'
+ % (oTestFile.sPath, offFile, cbChunk, oTestFile.cbContent));
+ break;
+ cbRead = len(abRead);
+ if cbRead == 0 and offFile == oTestFile.cbContent:
+ break;
+ if cbRead <= 0:
+ fRc = reporter.error('%s @%s: cbRead=%s, cbContent=%s'
+ % (oTestFile.sPath, offFile, cbRead, oTestFile.cbContent));
+ break;
+ if not oTestFile.equalMemory(abRead, offFile):
+ fRc = reporter.error('%s: read mismatch @ %s LB %s' % (oTestFile.sPath, offFile, cbRead));
+ break;
+ offFile += cbRead;
+ cReads += 1;
+ if cReads > 8192:
+ break;
+
+ # Seek to start of file.
+ try:
+ offFile = oFile.seek(0, vboxcon.FileSeekOrigin_Begin);
+ except:
+ fRc = reporter.errorXcpt('%s: error seeking to start of file' % (oTestFile.sPath,));
+ break;
+ if offFile != 0:
+ fRc = reporter.error('%s: seek to start of file returned %u, expected 0' % (oTestFile.sPath, offFile));
+ break;
+
+ #
+ # Random reads.
+ #
+ reporter.log2('Random reads (seek)');
+ for _ in xrange(8):
+ offFile = self.oTestFiles.oRandom.randrange(0, oTestFile.cbContent + 1024);
+ cbToRead = self.oTestFiles.oRandom.randrange(1, min(oTestFile.cbContent + 256, 768*1024));
+ #if oTestFile.cbContent >= 1024*1024: reporter.log2('... %s LB %s' % (offFile, cbToRead,));
+
+ try:
+ offActual = oFile.seek(offFile, vboxcon.FileSeekOrigin_Begin);
+ except:
+ fRc = reporter.errorXcpt('%s: error seeking to %s' % (oTestFile.sPath, offFile));
+ break;
+ if offActual != offFile:
+ fRc = reporter.error('%s: seek(%s,Begin) -> %s, expected %s'
+ % (oTestFile.sPath, offFile, offActual, offFile));
+ break;
+
+ try:
+ abRead = oFile.read(cbToRead, 30*1000);
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ cbRead = 0;
+ else:
+ cbRead = len(abRead);
+ if not oTestFile.equalMemory(abRead, offFile):
+ fRc = reporter.error('%s: random read mismatch @ %s LB %s' % (oTestFile.sPath, offFile, cbRead,));
+
+ try:
+ offActual = oFile.offset;
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s (#1)'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ else:
+ if offActual != offFile + cbRead:
+ fRc = reporter.error('%s: IFile.offset is %s, expected %s (offFile=%s cbToRead=%s cbRead=%s) (#1)'
+ % (oTestFile.sPath, offActual, offFile + cbRead, offFile, cbToRead, cbRead));
+ try:
+ offActual = oFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s (#1)'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ else:
+ if offActual != offFile + cbRead:
+ fRc = reporter.error('%s: seek(0,cur) -> %s, expected %s (offFile=%s cbToRead=%s cbRead=%s) (#1)'
+ % (oTestFile.sPath, offActual, offFile + cbRead, offFile, cbToRead, cbRead));
+
+ #
+ # Random reads using readAt.
+ #
+ reporter.log2('Random reads (readAt)');
+ for _ in xrange(12):
+ offFile = self.oTestFiles.oRandom.randrange(0, oTestFile.cbContent + 1024);
+ cbToRead = self.oTestFiles.oRandom.randrange(1, min(oTestFile.cbContent + 256, 768*1024));
+ #if oTestFile.cbContent >= 1024*1024: reporter.log2('... %s LB %s (readAt)' % (offFile, cbToRead,));
+
+ try:
+ abRead = oFile.readAt(offFile, cbToRead, 30*1000);
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ cbRead = 0;
+ else:
+ cbRead = len(abRead);
+ if not oTestFile.equalMemory(abRead, offFile):
+ fRc = reporter.error('%s: random readAt mismatch @ %s LB %s' % (oTestFile.sPath, offFile, cbRead,));
+
+ try:
+ offActual = oFile.offset;
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s (#2)'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ else:
+ if offActual != offFile + cbRead:
+ fRc = reporter.error('%s: IFile.offset is %s, expected %s (offFile=%s cbToRead=%s cbRead=%s) (#2)'
+ % (oTestFile.sPath, offActual, offFile + cbRead, offFile, cbToRead, cbRead));
+
+ try:
+ offActual = oFile.seek(0, vboxcon.FileSeekOrigin_Current);
+ except:
+ fRc = reporter.errorXcpt('%s: offFile=%s cbToRead=%s cbContent=%s (#2)'
+ % (oTestFile.sPath, offFile, cbToRead, oTestFile.cbContent));
+ else:
+ if offActual != offFile + cbRead:
+ fRc = reporter.error('%s: seek(0,cur) -> %s, expected %s (offFile=%s cbToRead=%s cbRead=%s) (#2)'
+ % (oTestFile.sPath, offActual, offFile + cbRead, offFile, cbToRead, cbRead));
+
+ #
+ # A few negative things.
+ #
+
+ # Zero byte reads -> E_INVALIDARG.
+ reporter.log2('Zero byte reads');
+ try:
+ abRead = oFile.read(0, 30*1000);
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_INVALIDARG):
+ fRc = reporter.errorXcpt('read(0,30s) did not raise E_INVALIDARG as expected!');
+ else:
+ fRc = reporter.error('read(0,30s) did not fail!');
+
+ try:
+ abRead = oFile.readAt(0, 0, 30*1000);
+ except Exception as oXcpt:
+ if vbox.ComError.notEqual(oXcpt, vbox.ComError.E_INVALIDARG):
+ fRc = reporter.errorXcpt('readAt(0,0,30s) did not raise E_INVALIDARG as expected!');
+ else:
+ fRc = reporter.error('readAt(0,0,30s) did not fail!');
+
+ # See what happens when we read 1GiB. We should get a max of 1MiB back.
+ ## @todo Document this behaviour in VirtualBox.xidl.
+ reporter.log2('1GB reads');
+ try:
+ oFile.seek(0, vboxcon.FileSeekOrigin_Begin);
+ except:
+ fRc = reporter.error('seek(0)');
+ try:
+ abRead = oFile.read(1024*1024*1024, 30*1000);
+ except:
+ fRc = reporter.errorXcpt('read(1GiB,30s)');
+ else:
+ if len(abRead) != min(oTestFile.cbContent, 1024*1024):
+ fRc = reporter.error('Expected read(1GiB,30s) to return %s bytes, got %s bytes instead'
+ % (min(oTestFile.cbContent, 1024*1024), len(abRead),));
+
+ try:
+ abRead = oFile.readAt(0, 1024*1024*1024, 30*1000);
+ except:
+ fRc = reporter.errorXcpt('readAt(0,1GiB,30s)');
+ else:
+ if len(abRead) != min(oTestFile.cbContent, 1024*1024):
+ reporter.error('Expected readAt(0, 1GiB,30s) to return %s bytes, got %s bytes instead'
+ % (min(oTestFile.cbContent, 1024*1024), len(abRead),));
+
+ #
+ # Check stat info on the file as well as querySize.
+ #
+ if self.oTstDrv.fpApiVer > 5.2:
+ try:
+ oFsObjInfo = oFile.queryInfo();
+ except:
+ fRc = reporter.errorXcpt('%s: queryInfo()' % (oTestFile.sPath,));
+ else:
+ if oFsObjInfo is None:
+ fRc = reporter.error('IGuestFile::queryInfo returned None');
+ else:
+ try:
+ cbFile = oFsObjInfo.objectSize;
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ if cbFile != oTestFile.cbContent:
+ fRc = reporter.error('%s: queryInfo returned incorrect file size: %s, expected %s'
+ % (oTestFile.sPath, cbFile, oTestFile.cbContent));
+
+ try:
+ cbFile = oFile.querySize();
+ except:
+ fRc = reporter.errorXcpt('%s: querySize()' % (oTestFile.sPath,));
+ else:
+ if cbFile != oTestFile.cbContent:
+ fRc = reporter.error('%s: querySize returned incorrect file size: %s, expected %s'
+ % (oTestFile.sPath, cbFile, oTestFile.cbContent));
+
+ #
+ # Use seek to test the file size and do a few other end-relative seeks.
+ #
+ try:
+ cbFile = oFile.seek(0, vboxcon.FileSeekOrigin_End);
+ except:
+ fRc = reporter.errorXcpt('%s: seek(0,End)' % (oTestFile.sPath,));
+ else:
+ if cbFile != oTestFile.cbContent:
+ fRc = reporter.error('%s: seek(0,End) returned incorrect file size: %s, expected %s'
+ % (oTestFile.sPath, cbFile, oTestFile.cbContent));
+ if oTestFile.cbContent > 0:
+ for _ in xrange(5):
+ offSeek = self.oTestFiles.oRandom.randrange(oTestFile.cbContent + 1);
+ try:
+ offFile = oFile.seek(-offSeek, vboxcon.FileSeekOrigin_End);
+ except:
+ fRc = reporter.errorXcpt('%s: seek(%s,End)' % (oTestFile.sPath, -offSeek,));
+ else:
+ if offFile != oTestFile.cbContent - offSeek:
+ fRc = reporter.error('%s: seek(%s,End) returned incorrect offset: %s, expected %s (cbContent=%s)'
+ % (oTestFile.sPath, -offSeek, offSeek, oTestFile.cbContent - offSeek,
+ oTestFile.cbContent,));
+
+ #
+ # Close it and we're done with this file.
+ #
+ try:
+ oFile.close();
+ except:
+ fRc = reporter.errorXcpt('%s: error closing the file' % (oTestFile.sPath,));
+
+ #
+ # Clean up.
+ #
+ for oTestFile in aoExtraFiles:
+ try:
+ oGuestSession.fsObjRemove(sBigPath);
+ except:
+ fRc = reporter.errorXcpt('fsObjRemove(%s)' % (sBigPath,));
+
+ fRc = oTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+
+ def testGuestCtrlFileWrite(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests writing to guest files.
+ """
+ if self.oTstDrv.fpApiVer < 5.0:
+ reporter.log('Skipping because of pre 5.0 API');
+ return None;
+
+ #
+ # The test file and its content.
+ #
+ sFile = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'gctrl-write-1');
+ abContent = bytearray(0);
+
+ #
+ # The tests.
+ #
+ def randBytes(cbHowMany):
+ """ Returns an bytearray of random bytes. """
+ return bytearray(self.oTestFiles.oRandom.getrandbits(8) for _ in xrange(cbHowMany));
+
+ aoTests = [
+ # Write at end:
+ tdTestFileOpenAndWrite(sFile = sFile, eAction = vboxcon.FileOpenAction_CreateNew, abContent = abContent,
+ atChunks = [(None, randBytes(1)), (None, randBytes(77)), (None, randBytes(98)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 1+77+98), # 176
+ # Appending:
+ tdTestFileOpenAndWrite(sFile = sFile, eAction = vboxcon.FileOpenAction_AppendOrCreate, abContent = abContent,
+ atChunks = [(None, randBytes(255)), (None, randBytes(33)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 176 + 255+33), # 464
+ tdTestFileOpenAndWrite(sFile = sFile, eAction = vboxcon.FileOpenAction_AppendOrCreate, abContent = abContent,
+ atChunks = [(10, randBytes(44)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 464 + 44), # 508
+ # Write within existing:
+ tdTestFileOpenAndWrite(sFile = sFile, eAction = vboxcon.FileOpenAction_OpenExisting, abContent = abContent,
+ atChunks = [(0, randBytes(1)), (50, randBytes(77)), (255, randBytes(199)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 508),
+ # Writing around and over the end:
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent,
+ atChunks = [(500, randBytes(9)), (508, randBytes(15)), (512, randBytes(12)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 512+12),
+
+ # writeAt appending:
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent, fUseAtApi = True,
+ atChunks = [(0, randBytes(23)), (6, randBytes(1018)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 6+1018), # 1024
+ # writeAt within existing:
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent, fUseAtApi = True,
+ atChunks = [(1000, randBytes(23)), (1, randBytes(990)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 1024),
+ # writeAt around and over the end:
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent, fUseAtApi = True,
+ atChunks = [(1024, randBytes(63)), (1080, randBytes(968)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 1080+968), # 2048
+
+ # writeAt beyond the end (gap is filled with zeros):
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent, fUseAtApi = True, atChunks = [(3070, randBytes(2)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 3072),
+ # write beyond the end (gap is filled with zeros):
+ tdTestFileOpenAndWrite(sFile = sFile, abContent = abContent, atChunks = [(4090, randBytes(6)),]),
+ tdTestFileOpenAndCheckContent(sFile = sFile, abContent = abContent, cbContentExpected = 4096),
+ ];
+
+ for (i, oCurTest) in enumerate(aoTests):
+ reporter.log('Testing #%d: %s ...' % (i, oCurTest.toString(),));
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, _ = oCurTest.createSession('testGuestCtrlFileWrite: Test #%d' % (i,));
+ if fRc is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ fRc2 = oCurTest.doSteps(True, self);
+ if fRc2 is not True:
+ fRc = reporter.error('Test #%d failed!' % (i,));
+
+ fRc = oCurTest.closeSession() and fRc;
+
+ #
+ # Cleanup
+ #
+ if oTxsSession.syncRmFile(sFile) is not True:
+ fRc = reporter.error('Failed to remove write-test file: %s' % (sFile, ));
+
+ return (fRc, oTxsSession);
+
+ @staticmethod
+ def __generateFile(sName, cbFile):
+ """ Helper for generating a file with a given size. """
+ with open(sName, 'wb') as oFile:
+ while cbFile > 0:
+ cb = cbFile if cbFile < 256*1024 else 256*1024;
+ oFile.write(bytearray(random.getrandbits(8) for _ in xrange(cb)));
+ cbFile -= cb;
+ return True;
+
+ def testGuestCtrlCopyTo(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests copying files from host to the guest.
+ """
+
+ #
+ # Paths and test files.
+ #
+ sScratchHst = os.path.join(self.oTstDrv.sScratchPath, 'copyto');
+ sScratchTestFilesHst = os.path.join(sScratchHst, self.oTestFiles.sSubDir);
+ sScratchEmptyDirHst = os.path.join(sScratchTestFilesHst, self.oTestFiles.oEmptyDir.sName);
+ sScratchNonEmptyDirHst = self.oTestFiles.chooseRandomDirFromTree().buildPath(sScratchHst, os.path.sep);
+ sScratchTreeDirHst = os.path.join(sScratchTestFilesHst, self.oTestFiles.oTreeDir.sName);
+
+ sScratchGst = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'copyto');
+ sScratchDstDir1Gst = oTestVm.pathJoin(sScratchGst, 'dstdir1');
+ sScratchDstDir2Gst = oTestVm.pathJoin(sScratchGst, 'dstdir2');
+ sScratchDstDir3Gst = oTestVm.pathJoin(sScratchGst, 'dstdir3');
+ sScratchDstDir4Gst = oTestVm.pathJoin(sScratchGst, 'dstdir4');
+ sScratchDotDotDirGst = oTestVm.pathJoin(sScratchGst, '..');
+ #sScratchGstNotExist = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'no-such-file-or-directory');
+ sScratchHstNotExist = os.path.join(self.oTstDrv.sScratchPath, 'no-such-file-or-directory');
+ sScratchGstPathNotFound = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'no-such-directory', 'or-file');
+ #sScratchHstPathNotFound = os.path.join(self.oTstDrv.sScratchPath, 'no-such-directory', 'or-file');
+
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ sScratchGstInvalid = "?*|<invalid-name>";
+ else:
+ sScratchGstInvalid = None;
+ if utils.getHostOs() in ('win', 'os2'):
+ sScratchHstInvalid = "?*|<invalid-name>";
+ else:
+ sScratchHstInvalid = None;
+
+ for sDir in (sScratchGst, sScratchDstDir1Gst, sScratchDstDir2Gst, sScratchDstDir3Gst, sScratchDstDir4Gst):
+ if oTxsSession.syncMkDir(sDir, 0o777) is not True:
+ return reporter.error('TXS failed to create directory "%s"!' % (sDir,));
+
+ # Put the test file set under sScratchHst.
+ if os.path.exists(sScratchHst):
+ if base.wipeDirectory(sScratchHst) != 0:
+ return reporter.error('Failed to wipe "%s"' % (sScratchHst,));
+ else:
+ try:
+ os.mkdir(sScratchHst);
+ except:
+ return reporter.errorXcpt('os.mkdir(%s)' % (sScratchHst, ));
+ if self.oTestFiles.writeToDisk(sScratchHst) is not True:
+ return reporter.error('Filed to write test files to "%s" on the host!' % (sScratchHst,));
+
+ # If for whatever reason the directory tree does not exist on the host, let us know.
+ # Copying an non-existing tree *will* fail the tests which otherwise should succeed!
+ assert os.path.exists(sScratchTreeDirHst);
+
+ # Generate a test file in 32MB to 64 MB range.
+ sBigFileHst = os.path.join(self.oTstDrv.sScratchPath, 'gctrl-random.data');
+ cbBigFileHst = random.randrange(32*1024*1024, 64*1024*1024);
+ reporter.log('cbBigFileHst=%s' % (cbBigFileHst,));
+ cbLeft = cbBigFileHst;
+ try:
+ self.__generateFile(sBigFileHst, cbBigFileHst);
+ except:
+ return reporter.errorXcpt('sBigFileHst=%s cbBigFileHst=%s cbLeft=%s' % (sBigFileHst, cbBigFileHst, cbLeft,));
+ reporter.log('cbBigFileHst=%s' % (cbBigFileHst,));
+
+ # Generate an empty file on the host that we can use to save space in the guest.
+ sEmptyFileHst = os.path.join(self.oTstDrv.sScratchPath, 'gctrl-empty.data');
+ try:
+ open(sEmptyFileHst, "wb").close(); # pylint: disable=consider-using-with
+ except:
+ return reporter.errorXcpt('sEmptyFileHst=%s' % (sEmptyFileHst,));
+
+ # os.path.join() is too clever for "..", so we just build up the path here ourselves.
+ sScratchDotDotFileHst = sScratchHst + os.path.sep + '..' + os.path.sep + 'gctrl-empty.data';
+
+ #
+ # Tests.
+ #
+ atTests = [
+ # Nothing given:
+ [ tdTestCopyToFile(), tdTestResultFailure() ],
+ [ tdTestCopyToDir(), tdTestResultFailure() ],
+ # Only source given:
+ [ tdTestCopyToFile(sSrc = sBigFileHst), tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchEmptyDirHst), tdTestResultFailure() ],
+ # Only destination given:
+ [ tdTestCopyToFile(sDst = oTestVm.pathJoin(sScratchGst, 'dstfile')), tdTestResultFailure() ],
+ [ tdTestCopyToDir( sDst = sScratchGst), tdTestResultFailure() ],
+ # Both given, but invalid flags.
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = sScratchGst, afFlags = [ 0x40000000, ] ), tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchEmptyDirHst, sDst = sScratchGst, afFlags = [ 0x40000000, ] ),
+ tdTestResultFailure() ],
+ ];
+ atTests.extend([
+ # Non-existing source, but no destination:
+ [ tdTestCopyToFile(sSrc = sScratchHstNotExist), tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchHstNotExist), tdTestResultFailure() ],
+ # Valid sources, but destination path not found:
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = sScratchGstPathNotFound), tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchEmptyDirHst, sDst = sScratchGstPathNotFound), tdTestResultFailure() ],
+ # Valid destination, but source file/dir not found:
+ [ tdTestCopyToFile(sSrc = sScratchHstNotExist, sDst = oTestVm.pathJoin(sScratchGst, 'dstfile')),
+ tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchHstNotExist, sDst = sScratchGst), tdTestResultFailure() ],
+ # Wrong type:
+ [ tdTestCopyToFile(sSrc = sScratchEmptyDirHst, sDst = oTestVm.pathJoin(sScratchGst, 'dstfile')),
+ tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sBigFileHst, sDst = sScratchGst), tdTestResultFailure() ],
+ ]);
+ # Invalid characters in destination or source path:
+ if sScratchGstInvalid is not None:
+ atTests.extend([
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = oTestVm.pathJoin(sScratchGst, sScratchGstInvalid)),
+ tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = sScratchEmptyDirHst, sDst = oTestVm.pathJoin(sScratchGst, sScratchGstInvalid)),
+ tdTestResultFailure() ],
+ ]);
+ if sScratchHstInvalid is not None:
+ atTests.extend([
+ [ tdTestCopyToFile(sSrc = os.path.join(self.oTstDrv.sScratchPath, sScratchHstInvalid), sDst = sScratchGst),
+ tdTestResultFailure() ],
+ [ tdTestCopyToDir( sSrc = os.path.join(self.oTstDrv.sScratchPath, sScratchHstInvalid), sDst = sScratchGst),
+ tdTestResultFailure() ],
+ ]);
+
+ #
+ # Single file handling.
+ #
+ atTests.extend([
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = oTestVm.pathJoin(sScratchGst, 'HostGABig.dat')),
+ tdTestResultSuccess() ],
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = oTestVm.pathJoin(sScratchGst, 'HostGABig.dat')), # Overwrite
+ tdTestResultSuccess() ],
+ [ tdTestCopyToFile(sSrc = sEmptyFileHst, sDst = oTestVm.pathJoin(sScratchGst, 'HostGABig.dat')), # Overwrite
+ tdTestResultSuccess() ],
+ ]);
+ if self.oTstDrv.fpApiVer > 5.2: # Copying files into directories via Main is supported only 6.0 and later.
+ atTests.extend([
+ # Should succeed, as the file isn't there yet on the destination.
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = sScratchGst + oTestVm.pathSep()), tdTestResultSuccess() ],
+ # Overwrite the existing file.
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = sScratchGst + oTestVm.pathSep()), tdTestResultSuccess() ],
+ # Same file, but with a different name on the destination.
+ [ tdTestCopyToFile(sSrc = sEmptyFileHst, sDst = oTestVm.pathJoin(sScratchGst, os.path.split(sBigFileHst)[1])),
+ tdTestResultSuccess() ], # Overwrite
+ ]);
+
+ if oTestVm.isWindows():
+ # Copy to a Windows alternative data stream (ADS).
+ atTests.extend([
+ [ tdTestCopyToFile(sSrc = sBigFileHst, sDst = oTestVm.pathJoin(sScratchGst, 'HostGABig.dat:ADS-Test')),
+ tdTestResultSuccess() ],
+ [ tdTestCopyToFile(sSrc = sEmptyFileHst, sDst = oTestVm.pathJoin(sScratchGst, 'HostGABig.dat:ADS-Test')),
+ tdTestResultSuccess() ],
+ ]);
+
+ #
+ # Directory handling.
+ #
+ if self.oTstDrv.fpApiVer > 5.2: # Copying directories via Main is supported only in versions > 5.2.
+ atTests.extend([
+ # Without a trailing slash added to the destination this should fail,
+ # as the destination directory already exists.
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir1Gst), tdTestResultFailure() ],
+ # Same existing host directory, but this time with DirectoryCopyFlag_CopyIntoExisting set.
+ # This should copy the contents of oEmptyDirGst to sScratchDstDir1Gst (empty, but anyway).
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir1Gst,
+ afFlags = [vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # Try again.
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir1Gst,
+ afFlags = [vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # With a trailing slash added to the destination, copy the empty guest directory
+ # (should end up as sScratchDstDir2Gst/empty):
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir2Gst + oTestVm.pathSep()),
+ tdTestResultSuccess() ],
+ # Repeat -- this time it should fail, as the destination directory already exists (and
+ # DirectoryCopyFlag_CopyIntoExisting is not specified):
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir2Gst + oTestVm.pathSep()),
+ tdTestResultFailure() ],
+ # Add the DirectoryCopyFlag_CopyIntoExisting flag being set and it should work (again).
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = sScratchDstDir2Gst + oTestVm.pathSep(),
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # Copy with a different destination name just for the heck of it:
+ [ tdTestCopyToDir(sSrc = sScratchEmptyDirHst, sDst = oTestVm.pathJoin(sScratchDstDir2Gst, 'empty2')),
+ tdTestResultSuccess() ],
+ ]);
+ atTests.extend([
+ # Now the same using a directory with files in it:
+ [ tdTestCopyToDir(sSrc = sScratchNonEmptyDirHst, sDst = sScratchDstDir3Gst,
+ afFlags = [vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # Again.
+ [ tdTestCopyToDir(sSrc = sScratchNonEmptyDirHst, sDst = sScratchDstDir3Gst,
+ afFlags = [vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ ]);
+ atTests.extend([
+ # Copy the entire test tree:
+ [ tdTestCopyToDir(sSrc = sScratchTreeDirHst, sDst = sScratchDstDir4Gst + oTestVm.pathSep()),
+ tdTestResultSuccess() ],
+ # Again, should fail this time.
+ [ tdTestCopyToDir(sSrc = sScratchTreeDirHst, sDst = sScratchDstDir4Gst + oTestVm.pathSep()),
+ tdTestResultFailure() ],
+ # Works again, as DirectoryCopyFlag_CopyIntoExisting is specified.
+ [ tdTestCopyToDir(sSrc = sScratchTreeDirHst, sDst = sScratchDstDir4Gst + oTestVm.pathSep(),
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ ]);
+ #
+ # Dotdot path handling.
+ #
+ if self.oTstDrv.fpApiVer >= 6.1:
+ atTests.extend([
+ # Test if copying stuff from a host dotdot ".." directory works.
+ [ tdTestCopyToFile(sSrc = sScratchDotDotFileHst, sDst = sScratchDstDir1Gst + oTestVm.pathSep()),
+ tdTestResultSuccess() ],
+ # Test if copying stuff from the host to a guest's dotdot ".." directory works.
+ # That should fail on destinations.
+ [ tdTestCopyToFile(sSrc = sEmptyFileHst, sDst = sScratchDotDotDirGst), tdTestResultFailure() ],
+ ]);
+
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0]; # tdTestCopyTo
+ oCurRes = tTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sSrc=%s, sDst=%s, afFlags=%s ...'
+ % (i, limitString(oCurTest.sSrc), limitString(oCurTest.sDst), oCurTest.afFlags));
+
+ oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, oCurGuestSession = oCurTest.createSession('testGuestCtrlCopyTo: Test #%d' % (i,));
+ if fRc is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ fRc2 = False;
+ if isinstance(oCurTest, tdTestCopyToFile):
+ fRc2 = self.gctrlCopyFileTo(oCurGuestSession, oCurTest.sSrc, oCurTest.sDst, oCurTest.afFlags, oCurRes.fRc);
+ else:
+ fRc2 = self.gctrlCopyDirTo(oCurGuestSession, oCurTest.sSrc, oCurTest.sDst, oCurTest.afFlags, oCurRes.fRc);
+ if fRc2 is not oCurRes.fRc:
+ fRc = reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, oCurRes.fRc));
+
+ fRc = oCurTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlCopyFrom(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests copying files from guest to the host.
+ """
+
+ reporter.log2('Entered');
+
+ #
+ # Paths.
+ #
+ sScratchHst = os.path.join(self.oTstDrv.sScratchPath, "testGctrlCopyFrom");
+ sScratchDstDir1Hst = os.path.join(sScratchHst, "dstdir1");
+ sScratchDstDir2Hst = os.path.join(sScratchHst, "dstdir2");
+ sScratchDstDir3Hst = os.path.join(sScratchHst, "dstdir3");
+ sScratchDstDir4Hst = os.path.join(sScratchHst, "dstdir4");
+ # os.path.join() is too clever for "..", so we just build up the path here ourselves.
+ sScratchDotDotDirHst = sScratchHst + os.path.sep + '..' + os.path.sep;
+ oExistingFileGst = self.oTestFiles.chooseRandomFile();
+ oNonEmptyDirGst = self.oTestFiles.chooseRandomDirFromTree(fNonEmpty = True);
+ oTreeDirGst = self.oTestFiles.oTreeDir;
+ oEmptyDirGst = self.oTestFiles.oEmptyDir;
+
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ sScratchGstInvalid = "?*|<invalid-name>";
+ else:
+ sScratchGstInvalid = None;
+ if utils.getHostOs() in ('win', 'os2'):
+ sScratchHstInvalid = "?*|<invalid-name>";
+ else:
+ sScratchHstInvalid = None;
+
+ sScratchDotDotDirGst = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), '..');
+
+ if os.path.exists(sScratchHst):
+ if base.wipeDirectory(sScratchHst) != 0:
+ return reporter.error('Failed to wipe "%s"' % (sScratchHst,));
+ else:
+ try:
+ os.mkdir(sScratchHst);
+ except:
+ return reporter.errorXcpt('os.mkdir(%s)' % (sScratchHst, ));
+
+ reporter.log2('Creating host sub dirs ...');
+
+ for sSubDir in (sScratchDstDir1Hst, sScratchDstDir2Hst, sScratchDstDir3Hst, sScratchDstDir4Hst):
+ try:
+ os.mkdir(sSubDir);
+ except:
+ return reporter.errorXcpt('os.mkdir(%s)' % (sSubDir, ));
+
+ reporter.log2('Defining tests ...');
+
+ #
+ # Bad parameter tests.
+ #
+ atTests = [
+ # Missing both source and destination:
+ [ tdTestCopyFromFile(), tdTestResultFailure() ],
+ [ tdTestCopyFromDir(), tdTestResultFailure() ],
+ # Missing source.
+ [ tdTestCopyFromFile(sDst = os.path.join(sScratchHst, 'somefile')), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sDst = sScratchHst), tdTestResultFailure() ],
+ # Missing destination.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sSrc = self.oTestFiles.oManyDir.sPath), tdTestResultFailure() ],
+ # Invalid flags:
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, 'somefile'), afFlags = [0x40000000]),
+ tdTestResultFailure() ],
+ [ tdTestCopyFromDir( oSrc = oEmptyDirGst, sDst = os.path.join(sScratchHst, 'somedir'), afFlags = [ 0x40000000] ),
+ tdTestResultFailure() ],
+ # Non-existing sources:
+ [ tdTestCopyFromFile(sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'no-such-file-or-directory'),
+ sDst = os.path.join(sScratchHst, 'somefile')), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'no-such-file-or-directory'),
+ sDst = os.path.join(sScratchHst, 'somedir')), tdTestResultFailure() ],
+ [ tdTestCopyFromFile(sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'no-such-directory', 'no-such-file'),
+ sDst = os.path.join(sScratchHst, 'somefile')), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, 'no-such-directory', 'no-such-subdir'),
+ sDst = os.path.join(sScratchHst, 'somedir')), tdTestResultFailure() ],
+ # Non-existing destinations:
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst,
+ sDst = os.path.join(sScratchHst, 'no-such-directory', 'somefile') ), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( oSrc = oEmptyDirGst, sDst = os.path.join(sScratchHst, 'no-such-directory', 'somedir') ),
+ tdTestResultFailure() ],
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst,
+ sDst = os.path.join(sScratchHst, 'no-such-directory-slash' + os.path.sep)),
+ tdTestResultFailure() ],
+ # Wrong source type:
+ [ tdTestCopyFromFile(oSrc = oNonEmptyDirGst, sDst = os.path.join(sScratchHst, 'somefile') ), tdTestResultFailure() ],
+ [ tdTestCopyFromDir(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, 'somedir') ), tdTestResultFailure() ],
+ ];
+ # Bogus names:
+ if sScratchHstInvalid:
+ atTests.extend([
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, sScratchHstInvalid)),
+ tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sSrc = self.oTestFiles.oManyDir.sPath, sDst = os.path.join(sScratchHst, sScratchHstInvalid)),
+ tdTestResultFailure() ],
+ ]);
+ if sScratchGstInvalid:
+ atTests.extend([
+ [ tdTestCopyFromFile(sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, sScratchGstInvalid),
+ sDst = os.path.join(sScratchHst, 'somefile')), tdTestResultFailure() ],
+ [ tdTestCopyFromDir( sSrc = oTestVm.pathJoin(self.oTestFiles.oRoot.sPath, sScratchGstInvalid),
+ sDst = os.path.join(sScratchHst, 'somedir')), tdTestResultFailure() ],
+ ]);
+
+ #
+ # Single file copying.
+ #
+ atTests.extend([
+ # Should succeed, as the file isn't there yet on the destination.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, 'copyfile1')), tdTestResultSuccess() ],
+ # Overwrite the existing file.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, 'copyfile1')), tdTestResultSuccess() ],
+ # Same file, but with a different name on the destination.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = os.path.join(sScratchHst, 'copyfile2')), tdTestResultSuccess() ],
+ ]);
+
+ if self.oTstDrv.fpApiVer > 5.2: # Copying files into directories via Main is supported only 6.0 and later.
+ # Copy into a directory.
+ atTests.extend([
+ # This should fail, as sScratchHst exists and is a directory.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = sScratchHst), tdTestResultFailure() ],
+ # Same existing host directory, but this time with a trailing slash.
+ # This should succeed, as the file isn't there yet on the destination.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = sScratchHst + os.path.sep), tdTestResultSuccess() ],
+ # Overwrite the existing file.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = sScratchHst + os.path.sep), tdTestResultSuccess() ],
+ ]);
+
+ #
+ # Directory handling.
+ #
+ if self.oTstDrv.fpApiVer > 5.2: # Copying directories via Main is supported only in versions > 5.2.
+ atTests.extend([
+ # Without a trailing slash added to the destination this should fail,
+ # as the destination directory already exist.
+ [ tdTestCopyFromDir(sSrc = oEmptyDirGst.sPath, sDst = sScratchDstDir1Hst), tdTestResultFailure() ],
+ # Same existing host directory, but this time with DirectoryCopyFlag_CopyIntoExisting set.
+ # This should copy the contents of oEmptyDirGst to sScratchDstDir1Hst (empty, but anyway).
+ [ tdTestCopyFromDir(sSrc = oEmptyDirGst.sPath, sDst = sScratchDstDir1Hst,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # Try again.
+ [ tdTestCopyFromDir(sSrc = oEmptyDirGst.sPath, sDst = sScratchDstDir1Hst,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # With a trailing slash added to the destination, copy the empty guest directory
+ # (should end up as sScratchHst/empty):
+ [ tdTestCopyFromDir(oSrc = oEmptyDirGst, sDst = sScratchDstDir2Hst + os.path.sep), tdTestResultSuccess() ],
+ # Repeat -- this time it should fail, as the destination directory already exists (and
+ # DirectoryCopyFlag_CopyIntoExisting is not specified):
+ [ tdTestCopyFromDir(oSrc = oEmptyDirGst, sDst = sScratchDstDir2Hst + os.path.sep), tdTestResultFailure() ],
+ # Add the DirectoryCopyFlag_CopyIntoExisting flag being set and it should work (again).
+ [ tdTestCopyFromDir(oSrc = oEmptyDirGst, sDst = sScratchDstDir2Hst + os.path.sep,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ # Copy with a different destination name just for the heck of it:
+ [ tdTestCopyFromDir(sSrc = oEmptyDirGst.sPath, sDst = os.path.join(sScratchDstDir2Hst, 'empty2'),
+ fIntoDst = True),
+ tdTestResultSuccess() ],
+ ]);
+ atTests.extend([
+ # Now the same using a directory with files in it:
+ [ tdTestCopyFromDir(oSrc = oNonEmptyDirGst, sDst = sScratchDstDir3Hst + os.path.sep), tdTestResultSuccess() ],
+ # Again.
+ [ tdTestCopyFromDir(oSrc = oNonEmptyDirGst, sDst = sScratchDstDir3Hst, fIntoDst = True,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ ]);
+ atTests.extend([
+ # Copy the entire test tree:
+ [ tdTestCopyFromDir(oSrc = oTreeDirGst, sDst = sScratchDstDir4Hst + os.path.sep), tdTestResultSuccess() ],
+ # Again, should fail this time.
+ [ tdTestCopyFromDir(oSrc = oTreeDirGst, sDst = sScratchDstDir4Hst + os.path.sep), tdTestResultFailure() ],
+ # Works again, as DirectoryCopyFlag_CopyIntoExisting is specified.
+ [ tdTestCopyFromDir(oSrc = oTreeDirGst, sDst = sScratchDstDir4Hst + os.path.sep,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]), tdTestResultSuccess() ],
+ ]);
+ #
+ # Dotdot path handling.
+ #
+ if self.oTstDrv.fpApiVer >= 6.1:
+ atTests.extend([
+ # Test if copying stuff from a guest dotdot ".." directory works.
+ [ tdTestCopyFromDir(sSrc = sScratchDotDotDirGst, sDst = sScratchDstDir1Hst + os.path.sep,
+ afFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting, ]),
+ tdTestResultFailure() ],
+ # Test if copying stuff from the guest to a host's dotdot ".." directory works.
+ # That should fail on destinations.
+ [ tdTestCopyFromFile(oSrc = oExistingFileGst, sDst = sScratchDotDotDirHst), tdTestResultFailure() ],
+ ]);
+
+ reporter.log2('Executing tests ...');
+
+ #
+ # Execute the tests.
+ #
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0]
+ oCurRes = tTest[1] # type: tdTestResult
+ if isinstance(oCurTest, tdTestCopyFrom):
+ reporter.log('Testing #%d, %s: sSrc="%s", sDst="%s", afFlags="%s" ...'
+ % (i, "directory" if isinstance(oCurTest, tdTestCopyFromDir) else "file",
+ limitString(oCurTest.sSrc), limitString(oCurTest.sDst), oCurTest.afFlags,));
+ else:
+ reporter.log('Testing #%d, tdTestRemoveHostDir "%s" ...' % (i, oCurTest.sDir,));
+ if isinstance(oCurTest, tdTestCopyFromDir) and self.oTstDrv.fpApiVer < 6.0:
+ reporter.log('Skipping directoryCopyFromGuest test, not implemented in %s' % (self.oTstDrv.fpApiVer,));
+ continue;
+
+ if isinstance(oCurTest, tdTestRemoveHostDir):
+ fRc = oCurTest.execute(self.oTstDrv, oSession, oTxsSession, oTestVm, 'testing #%d' % (i,));
+ else:
+ fRc = oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc2, oCurGuestSession = oCurTest.createSession('testGuestCtrlCopyFrom: Test #%d' % (i,));
+ if fRc2 is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ if isinstance(oCurTest, tdTestCopyFromFile):
+ fRc2 = self.gctrlCopyFileFrom(oCurGuestSession, oCurTest, oCurRes.fRc);
+ else:
+ fRc2 = self.gctrlCopyDirFrom(oCurGuestSession, oCurTest, oCurRes.fRc);
+
+ if fRc2 != oCurRes.fRc:
+ fRc = reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, oCurRes.fRc));
+
+ fRc = oCurTest.closeSession() and fRc;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlUpdateAdditions(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals
+ """
+ Tests updating the Guest Additions inside the guest.
+
+ """
+
+ ## @todo currently disabled everywhere.
+ if self.oTstDrv.fpApiVer < 100.0:
+ reporter.log("Skipping updating GAs everywhere for now...");
+ return None;
+
+ # Skip test for updating Guest Additions if we run on a too old (Windows) guest.
+ ##
+ ## @todo make it work everywhere!
+ ##
+ if oTestVm.sKind in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003'):
+ reporter.log("Skipping updating GAs on old windows vm (sKind=%s)" % (oTestVm.sKind,));
+ return (None, oTxsSession);
+ if oTestVm.isOS2():
+ reporter.log("Skipping updating GAs on OS/2 guest");
+ return (None, oTxsSession);
+
+ sVBoxValidationKitIso = self.oTstDrv.sVBoxValidationKitIso;
+ if not os.path.isfile(sVBoxValidationKitIso):
+ return reporter.log('Validation Kit .ISO not found at "%s"' % (sVBoxValidationKitIso,));
+
+ sScratch = os.path.join(self.oTstDrv.sScratchPath, "testGctrlUpdateAdditions");
+ try:
+ os.makedirs(sScratch);
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ return reporter.error('Failed: Unable to create scratch directory \"%s\"' % (sScratch,));
+ reporter.log('Scratch path is: %s' % (sScratch,));
+
+ atTests = [];
+ if oTestVm.isWindows():
+ atTests.extend([
+ # Source is missing.
+ [ tdTestUpdateAdditions(sSrc = ''), tdTestResultFailure() ],
+
+ # Wrong flags.
+ [ tdTestUpdateAdditions(sSrc = self.oTstDrv.getGuestAdditionsIso(),
+ afFlags = [ 1234 ]), tdTestResultFailure() ],
+
+ # Non-existing .ISO.
+ [ tdTestUpdateAdditions(sSrc = "non-existing.iso"), tdTestResultFailure() ],
+
+ # Wrong .ISO.
+ [ tdTestUpdateAdditions(sSrc = sVBoxValidationKitIso), tdTestResultFailure() ],
+
+ # The real thing.
+ [ tdTestUpdateAdditions(sSrc = self.oTstDrv.getGuestAdditionsIso()),
+ tdTestResultSuccess() ],
+ # Test the (optional) installer arguments. This will extract the
+ # installer into our guest's scratch directory.
+ [ tdTestUpdateAdditions(sSrc = self.oTstDrv.getGuestAdditionsIso(),
+ asArgs = [ '/extract', '/D=' + sScratch ]),
+ tdTestResultSuccess() ]
+ # Some debg ISO. Only enable locally.
+ #[ tdTestUpdateAdditions(
+ # sSrc = "V:\\Downloads\\VBoxGuestAdditions-r80354.iso"),
+ # tdTestResultSuccess() ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, tTest) in enumerate(atTests):
+ oCurTest = tTest[0] # type: tdTestUpdateAdditions
+ oCurRes = tTest[1] # type: tdTestResult
+ reporter.log('Testing #%d, sSrc="%s", afFlags="%s" ...' % (i, oCurTest.sSrc, oCurTest.afFlags,));
+
+ oCurTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ if not fRc:
+ break;
+ fRc, _ = oCurTest.createSession('Test #%d' % (i,));
+ if fRc is not True:
+ fRc = reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ try:
+ oCurProgress = oCurTest.oGuest.updateGuestAdditions(oCurTest.sSrc, oCurTest.asArgs, oCurTest.afFlags);
+ except:
+ reporter.maybeErrXcpt(oCurRes.fRc, 'Updating Guest Additions exception for sSrc="%s", afFlags="%s":'
+ % (oCurTest.sSrc, oCurTest.afFlags,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr,
+ self.oTstDrv, "gctrlUpGA");
+ oWrapperProgress.wait();
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors = not oCurRes.fRc);
+ fRc = False;
+ else:
+ fRc = reporter.error('No progress object returned');
+
+ oCurTest.closeSession();
+ if fRc is oCurRes.fRc:
+ if fRc:
+ ## @todo Verify if Guest Additions were really updated (build, revision, ...).
+ ## @todo r=bird: Not possible since you're installing the same GAs as before...
+ ## Maybe check creation dates on certain .sys/.dll/.exe files?
+ pass;
+ else:
+ fRc = reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc, oCurRes.fRc));
+ break;
+
+ return (fRc, oTxsSession);
+
+
+
+class tdAddGuestCtrl(vbox.TestDriver): # pylint: disable=too-many-instance-attributes,too-many-public-methods
+ """
+ Guest control using VBoxService on the guest.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+ self.asRsrcs = None;
+ self.fQuick = False; # Don't skip lengthly tests by default.
+ self.addSubTestDriver(SubTstDrvAddGuestCtrl(self));
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ """
+ Shows the testdriver usage.
+ """
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAddGuestCtrl Options:');
+ reporter.log(' --quick');
+ reporter.log(' Same as --virt-modes hwvirt --cpu-counts 1.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ """
+ Parses the testdriver arguments from the command line.
+ """
+ if asArgs[iArg] == '--quick':
+ self.parseOption(['--virt-modes', 'hwvirt'], 0);
+ self.parseOption(['--cpu-counts', '1'], 0);
+ self.fQuick = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ sGaIso = self.getGuestAdditionsIso();
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = sGaIso);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+ #
+ # Test execution helpers.
+ #
+ def testOneCfg(self, oVM, oTestVm): # pylint: disable=too-many-statements
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ self.logVmInfo(oVM);
+
+ fRc = True;
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False);
+ reporter.log("TxsSession: %s" % (oTxsSession,));
+ if oSession is not None:
+ fRc, oTxsSession = self.aoSubTstDrvs[0].testIt(oTestVm, oSession, oTxsSession);
+ self.terminateVmBySession(oSession);
+ else:
+ fRc = False;
+ return fRc;
+
+ def onExit(self, iRc):
+ return vbox.TestDriver.onExit(self, iRc);
+
+ def gctrlReportError(self, progress):
+ """
+ Helper function to report an error of a
+ given progress object.
+ """
+ if progress is None:
+ reporter.log('No progress object to print error for');
+ else:
+ errInfo = progress.errorInfo;
+ if errInfo:
+ reporter.log('%s' % (errInfo.text,));
+ return False;
+
+ def gctrlGetRemainingTime(self, msTimeout, msStart):
+ """
+ Helper function to return the remaining time (in ms)
+ based from a timeout value and the start time (both in ms).
+ """
+ if msTimeout == 0:
+ return 0xFFFFFFFE; # Wait forever.
+ msElapsed = base.timestampMilli() - msStart;
+ if msElapsed > msTimeout:
+ return 0; # No time left.
+ return msTimeout - msElapsed;
+
+ def testGuestCtrlManual(self, oSession, oTxsSession, oTestVm): # pylint: disable=too-many-locals,too-many-statements,unused-argument,unused-variable
+ """
+ For manually testing certain bits.
+ """
+
+ reporter.log('Manual testing ...');
+ fRc = True;
+
+ sUser = 'Administrator';
+ sPassword = 'password';
+
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(sUser,
+ sPassword,
+ "", "Manual Test");
+
+ aWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ _ = oGuestSession.waitForArray(aWaitFor, 30 * 1000);
+
+ sCmd = self.getGuestSystemShell(oTestVm);
+ asArgs = [ sCmd, '/C', 'dir', '/S', 'c:\\windows' ];
+ aEnv = [];
+ afFlags = [];
+
+ for _ in xrange(100):
+ oProc = oGuestSession.processCreate(sCmd, asArgs if self.fpApiVer >= 5.0 else asArgs[1:],
+ aEnv, afFlags, 30 * 1000);
+
+ aWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate ];
+ _ = oProc.waitForArray(aWaitFor, 30 * 1000);
+
+ oGuestSession.close();
+ oGuestSession = None;
+
+ time.sleep(5);
+
+ oSession.o.console.PowerDown();
+
+ return (fRc, oTxsSession);
+
+if __name__ == '__main__':
+ sys.exit(tdAddGuestCtrl().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/additions/tdAddSharedFolders1.py b/src/VBox/ValidationKit/tests/additions/tdAddSharedFolders1.py
new file mode 100755
index 00000000..c50ee4d3
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/tdAddSharedFolders1.py
@@ -0,0 +1,361 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+VirtualBox Validation Kit - Shared Folders #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import os
+import shutil
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from common import utils;
+
+
+class SubTstDrvAddSharedFolders1(base.SubTestDriverBase):
+ """
+ Sub-test driver for executing shared folders tests.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'add-shared-folders', 'Shared Folders');
+
+ self.asTestsDef = [ 'fsperf', ];
+ self.asTests = self.asTestsDef;
+ self.asExtraArgs = [];
+ self.asGstFsPerfPaths = [
+ '${CDROM}/vboxvalidationkit/${OS/ARCH}/FsPerf${EXESUFF}',
+ '${CDROM}/${OS/ARCH}/FsPerf${EXESUFF}',
+ '${TXSDIR}/${OS/ARCH}/FsPerf${EXESUFF}',
+ '${TXSDIR}/FsPerf${EXESUFF}',
+ 'E:/vboxvalidationkit/${OS/ARCH}/FsPerf${EXESUFF}',
+ ];
+ self.sGuestSlash = '';
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--add-shared-folders-tests': # 'add' as in 'additions', not the verb.
+ iArg += 1;
+ iNext = self.oTstDrv.requireMoreArgs(1, asArgs, iArg);
+ if asArgs[iArg] == 'all':
+ self.asTests = self.asTestsDef;
+ else:
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--add-shared-folders-tests" value "%s" is not valid; valid values are: %s'
+ % (s, ' '.join(self.asTestsDef)));
+ return iNext;
+ if asArgs[iArg] == '--add-shared-folders-extra-arg':
+ iArg += 1;
+ iNext = self.oTstDrv.requireMoreArgs(1, asArgs, iArg);
+ self.asExtraArgs.append(asArgs[iArg]);
+ return iNext;
+ return iArg;
+
+ def showUsage(self):
+ base.SubTestDriverBase.showUsage(self);
+ reporter.log(' --add-shared-folders-tests <t1[:t2[:]]>');
+ reporter.log(' Default: all (%s)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --add-shared-folders-extra-arg <fsperf-arg>');
+ reporter.log(' Adds an extra FsPerf argument. Can be repeated.');
+
+ return True;
+
+ def mountShareEx(self, oSession, oTxsSession, sShareName, sHostPath, sGuestMountPoint, fMustSucceed):
+ """
+ Automount a shared folder in the guest, extended version.
+
+ Returns success status, based on fMustSucceed.
+ """
+ reporter.testStart('Automounting "%s"' % (sShareName,));
+
+ reporter.log2('Creating shared folder "%s" at "%s" ...' % (sShareName, sGuestMountPoint));
+ try:
+ oConsole = oSession.o.console;
+ oConsole.createSharedFolder(sShareName, sHostPath, True, True, sGuestMountPoint);
+ except:
+ if fMustSucceed:
+ reporter.errorXcpt('createSharedFolder(%s,%s,True,True,%s)' % (sShareName, sHostPath, sGuestMountPoint));
+ else:
+ reporter.log('createSharedFolder(%s,%s,True,True,%s) failed, good' % (sShareName, sHostPath, sGuestMountPoint));
+ reporter.testDone();
+ return False is fMustSucceed;
+
+ # Check whether we can see the shared folder now. Retry for 30 seconds.
+ msStart = base.timestampMilli();
+ while True:
+ fRc = oTxsSession.syncIsDir(sGuestMountPoint + self.sGuestSlash + 'candle.dir');
+ reporter.log2('candle.dir check -> %s' % (fRc,));
+ if fRc is fMustSucceed:
+ break;
+ if base.timestampMilli() - msStart > 30000:
+ reporter.error('Shared folder mounting timed out!');
+ break;
+ self.oTstDrv.sleep(1);
+
+ reporter.testDone();
+
+ return fRc == fMustSucceed;
+
+ def mountShare(self, oSession, oTxsSession, sShareName, sHostPath, sGuestMountPoint):
+ """
+ Automount a shared folder in the guest.
+
+ Returns success status.
+ """
+ return self.mountShareEx(oSession, oTxsSession, sShareName, sHostPath, sGuestMountPoint, fMustSucceed = True);
+
+ def unmountShareEx(self, oSession, oTxsSession, sShareName, sGuestMountPoint, fMustSucceed):
+ """
+ Unmounts a shared folder in the guest.
+
+ Returns success status, based on fMustSucceed.
+ """
+ reporter.log2('Autounmount');
+ try:
+ oConsole = oSession.o.console;
+ oConsole.removeSharedFolder(sShareName);
+ except:
+ if fMustSucceed:
+ reporter.errorXcpt('removeSharedFolder(%s)' % (sShareName,));
+ else:
+ reporter.log('removeSharedFolder(%s)' % (sShareName,));
+ reporter.testDone();
+ return False is fMustSucceed;
+
+ # Check whether the shared folder is gone on the guest now. Retry for 30 seconds.
+ msStart = base.timestampMilli();
+ while True:
+ fRc = oTxsSession.syncIsDir(sGuestMountPoint + self.sGuestSlash + 'candle.dir');
+ reporter.log2('candle.dir check -> %s' % (fRc,));
+ if fRc is not fMustSucceed:
+ break;
+ if base.timestampMilli() - msStart > 30000:
+ reporter.error('Shared folder unmounting timed out!');
+ fRc = False;
+ break;
+ self.oTstDrv.sleep(1);
+
+ reporter.testDone();
+
+ return fRc is not fMustSucceed;
+
+ def unmountShare(self, oSession, oTxsSession, sShareName, sGuestMountPoint):
+ """
+ Unmounts a shared folder in the guest, extended version.
+
+ Returns success status, based on fMustSucceed.
+ """
+ return self.unmountShareEx(oSession, oTxsSession, sShareName, sGuestMountPoint, fMustSucceed = True);
+
+ def testIt(self, oTestVm, oSession, oTxsSession):
+ """
+ Executes the test.
+
+ Returns fRc, oTxsSession. The latter may have changed.
+ """
+ reporter.log("Active tests: %s" % (self.asTests,));
+
+ #
+ # Skip the test if before 6.0
+ #
+ if self.oTstDrv.fpApiVer < 6.0:
+ reporter.log('Requires 6.0 or later (for now)');
+ return (None, oTxsSession);
+
+ # Guess a free mount point inside the guest.
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ self.sGuestSlash = '\\';
+ else:
+ self.sGuestSlash = '/';
+
+ #
+ # Create the host directory to share. Empty except for a 'candle.dir' subdir
+ # that we use to check that it mounted correctly.
+ #
+ sShareName1 = 'shfl1';
+ sShareHostPath1 = os.path.join(self.oTstDrv.sScratchPath, sShareName1);
+ reporter.log2('Creating shared host folder "%s"...' % (sShareHostPath1,));
+ if os.path.exists(sShareHostPath1):
+ try: shutil.rmtree(sShareHostPath1);
+ except: return (reporter.errorXcpt('shutil.rmtree(%s)' % (sShareHostPath1,)), oTxsSession);
+ try: os.mkdir(sShareHostPath1);
+ except: return (reporter.errorXcpt('os.mkdir(%s)' % (sShareHostPath1,)), oTxsSession);
+ try: os.mkdir(os.path.join(sShareHostPath1, 'candle.dir'));
+ except: return (reporter.errorXcpt('os.mkdir(%s)' % (sShareHostPath1,)), oTxsSession);
+
+ # Guess a free mount point inside the guest.
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ sMountPoint1 = 'V:';
+ else:
+ sMountPoint1 = '/mnt/' + sShareName1;
+
+ fRc = self.mountShare(oSession, oTxsSession, sShareName1, sShareHostPath1, sMountPoint1);
+ if fRc is not True:
+ return (False, oTxsSession); # skip the remainder if we cannot auto mount the folder.
+
+ #
+ # Run FsPerf inside the guest.
+ #
+ fSkip = 'fsperf' not in self.asTests;
+ if fSkip is False:
+ cMbFree = utils.getDiskUsage(sShareHostPath1);
+ if cMbFree >= 16:
+ reporter.log2('Free space: %u MBs' % (cMbFree,));
+ else:
+ reporter.log('Skipping FsPerf because only %u MB free on %s' % (cMbFree, sShareHostPath1,));
+ fSkip = True;
+ if fSkip is False:
+ # Common arguments:
+ asArgs = ['FsPerf', '-d', sMountPoint1 + self.sGuestSlash + 'fstestdir-1', '-s8'];
+
+ # Skip part of mmap on older windows systems without CcCoherencyFlushAndPurgeCache (>= w7).
+ reporter.log2('oTestVm.sGuestOsType=%s' % (oTestVm.sGuestOsType,));
+ if oTestVm.getNonCanonicalGuestOsType() \
+ in [ 'WindowsNT3x', 'WindowsNT4', 'Windows2000', 'WindowsXP', 'WindowsXP_64', 'Windows2003',
+ 'Windows2003_64', 'WindowsVista', 'WindowsVista_64', 'Windows2008', 'Windows2008_64']:
+ asArgs.append('--no-mmap-coherency');
+
+ # Configure I/O block sizes according to guest memory size:
+ cbMbRam = 128;
+ try: cbMbRam = oSession.o.machine.memorySize;
+ except: reporter.errorXcpt();
+ reporter.log2('cbMbRam=%s' % (cbMbRam,));
+ asArgs.append('--set-block-size=1');
+ asArgs.append('--add-block-size=512');
+ asArgs.append('--add-block-size=4096');
+ asArgs.append('--add-block-size=16384');
+ asArgs.append('--add-block-size=65536');
+ asArgs.append('--add-block-size=1048576'); # 1 MiB
+ if cbMbRam >= 512:
+ asArgs.append('--add-block-size=33554432'); # 32 MiB
+ if cbMbRam >= 768:
+ asArgs.append('--add-block-size=134217728'); # 128 MiB
+
+ # Putting lots (10000) of files in a single directory causes issues on OS X
+ # (HFS+ presumably, though could be slow disks) and some linuxes (slow disks,
+ # maybe ext2/3?). So, generally reduce the file count to 4096 everywhere
+ # since we're not here to test the host file systems, and 3072 on macs.
+ if utils.getHostOs() in [ 'darwin', ]:
+ asArgs.append('--many-files=3072');
+ elif utils.getHostOs() in [ 'linux', ]:
+ asArgs.append('--many-files=4096');
+
+ # Add the extra arguments from the command line and kick it off:
+ asArgs.extend(self.asExtraArgs);
+
+ # Run FsPerf:
+ reporter.log2('Starting guest FsPerf (%s)...' % (asArgs,));
+ sFsPerfPath = self._locateGstFsPerf(oTxsSession);
+
+ ## @todo For some odd reason the combined GA/VaKit .ISO (by IPRT/fs/isomakercmd)
+ # sometimes (?) contains FsPerf as non-executable (-r--r--r-- 1 root root) on Linux.
+ #
+ # So work around this for now by copying the desired FsPerf binary to the temp directory,
+ # make it executable and execute it from there.
+ fISOMakerCmdIsBuggy = oTestVm.isLinux();
+ if fISOMakerCmdIsBuggy:
+ sFsPerfPathTemp = oTestVm.pathJoin(self.oTstDrv.getGuestTempDir(oTestVm), 'FsPerf${EXESUFF}');
+ if oTestVm.isWindows() \
+ or oTestVm.isOS2():
+ sCopy = self.oTstDrv.getGuestSystemShell();
+ sCopyArgs = ( sCopy, "/C", "copy", "/Y", sFsPerfPath, sFsPerfPathTemp );
+ else:
+ sCopy = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'cp');
+ sCopyArgs = ( sCopy, "-a", "-v", sFsPerfPath, sFsPerfPathTemp );
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Copying FsPerf', 60 * 1000,
+ sCopy, sCopyArgs, fCheckSessionStatus = True);
+ fRc = fRc and oTxsSession.syncChMod(sFsPerfPathTemp, 0o755);
+ if fRc:
+ sFsPerfPath = sFsPerfPathTemp;
+
+ fRc = self.oTstDrv.txsRunTest(oTxsSession, 'Running FsPerf', 90 * 60 * 1000, sFsPerfPath, asArgs,
+ fCheckSessionStatus = True);
+ reporter.log2('FsPerf -> %s' % (fRc,));
+ if fRc:
+ # Do a bit of diagnosis to find out why this failed.
+ if not oTestVm.isWindows() \
+ and not oTestVm.isOS2():
+ sCmdLs = oTestVm.pathJoin(self.oTstDrv.getGuestSystemDir(oTestVm), 'ls');
+ oTxsSession.syncExec(sCmdLs, (sCmdLs, "-al", sFsPerfPath), fIgnoreErrors = True);
+ oTxsSession.syncExec(sCmdLs, (sCmdLs, "-al", "-R", "/opt"), fIgnoreErrors = True);
+ oTxsSession.syncExec(sCmdLs, (sCmdLs, "-al", "-R", "/media/cdrom"), fIgnoreErrors = True);
+
+ sTestDir = os.path.join(sShareHostPath1, 'fstestdir-1');
+ if os.path.exists(sTestDir):
+ fRc = reporter.errorXcpt('test directory lingers: %s' % (sTestDir,));
+ try: shutil.rmtree(sTestDir);
+ except: fRc = reporter.errorXcpt('shutil.rmtree(%s)' % (sTestDir,));
+ else:
+ reporter.testStart('FsPerf');
+ reporter.testDone(fSkip or fRc is None);
+
+ #
+ # Check if auto-unmounting works.
+ #
+ if fRc is True:
+ fRc = self.unmountShare(oSession, oTxsSession, sShareName1, sMountPoint1);
+
+ ## @todo Add tests for multiple automount shares, random unmounting, reboot test.
+
+ return (fRc, oTxsSession);
+
+ def _locateGstFsPerf(self, oTxsSession):
+ """
+ Returns guest side path to FsPerf.
+ """
+ for sFsPerfPath in self.asGstFsPerfPaths:
+ if oTxsSession.syncIsFile(sFsPerfPath):
+ reporter.log('Using FsPerf at "%s"' % (sFsPerfPath,));
+ return sFsPerfPath;
+ reporter.log('Unable to find guest FsPerf in any of these places: %s' % ('\n '.join(self.asGstFsPerfPaths),));
+ return self.asGstFsPerfPaths[0];
+
+
+
+if __name__ == '__main__':
+ reporter.error('Cannot run standalone, use tdAddBasic1.py');
+ sys.exit(1);
diff --git a/src/VBox/ValidationKit/tests/api/Makefile.kmk b/src/VBox/ValidationKit/tests/api/Makefile.kmk
new file mode 100644
index 00000000..2598e51a
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/Makefile.kmk
@@ -0,0 +1,72 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - API Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsApi
+ValidationKitTestsApi_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsApi_INST = $(INST_VALIDATIONKIT)tests/api/
+ValidationKitTestsApi_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdApi1.py \
+ $(PATH_SUB_CURRENT)/tdAppliance1.py \
+ $(PATH_SUB_CURRENT)/tdCloneMedium1.py \
+ $(PATH_SUB_CURRENT)/tdCreateVMWithDefaults1.py \
+ $(PATH_SUB_CURRENT)/tdMoveMedium1.py \
+ $(PATH_SUB_CURRENT)/tdMoveVm1.py \
+ $(PATH_SUB_CURRENT)/tdPython1.py \
+ $(PATH_SUB_CURRENT)/tdTreeDepth1.py
+
+ifndef VBOX_OSE
+ ValidationKitTestsApi_EXEC_SOURCES += \
+ $(PATH_SUB_CURRENT)/tdCloud1.py \
+ $(PATH_SUB_CURRENT)/tdOciConnection1.py \
+ $(PATH_SUB_CURRENT)/tdOciExport1.py \
+ $(PATH_SUB_CURRENT)/tdOciImage1.py \
+ $(PATH_SUB_CURRENT)/tdOciImport1.py \
+ $(PATH_SUB_CURRENT)/tdOciInstance1.py \
+ $(PATH_SUB_CURRENT)/tdOciProfile1.py
+endif
+ValidationKitTestsApi_SOURCES := \
+ $(wildcard \
+ $(PATH_SUB_CURRENT)/*.ova \
+ )
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsApi_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/api/__init__.py b/src/VBox/ValidationKit/tests/api/__init__.py
new file mode 100644
index 00000000..db7ec260
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/__init__.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Just to make python 2.x happy.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
diff --git a/src/VBox/ValidationKit/tests/api/tdApi1.py b/src/VBox/ValidationKit/tests/api/tdApi1.py
new file mode 100755
index 00000000..2aae19c8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdApi1.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdApi1.py $
+
+"""
+VirtualBox Validation Kit - API Test wrapper #1 combining all API sub-tests
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+
+
+class tdApi1(vbox.TestDriver):
+ """
+ API Test wrapper #1.
+ """
+
+ def __init__(self, aoSubTestDriverClasses = None):
+ vbox.TestDriver.__init__(self)
+ for oSubTestDriverClass in aoSubTestDriverClasses:
+ self.addSubTestDriver(oSubTestDriverClass(self));
+
+ #
+ # Overridden methods.
+ #
+
+ def actionConfig(self):
+ """
+ Import the API.
+ """
+ if not self.importVBoxApi():
+ return False
+ return True
+
+ def actionExecute(self):
+ """
+ Execute the testcase, i.e. all sub-tests.
+ """
+ fRc = True;
+ for oSubTstDrv in self.aoSubTstDrvs:
+ if oSubTstDrv.fEnabled:
+ fRc = oSubTstDrv.testIt() and fRc;
+ return fRc;
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdPython1 import SubTstDrvPython1; # pylint: disable=relative-import
+ from tdAppliance1 import SubTstDrvAppliance1; # pylint: disable=relative-import
+ from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import
+ from tdTreeDepth1 import SubTstDrvTreeDepth1; # pylint: disable=relative-import
+ from tdMoveVm1 import SubTstDrvMoveVm1; # pylint: disable=relative-import
+ from tdCloneMedium1 import SubTstDrvCloneMedium1;# pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvPython1, SubTstDrvAppliance1, SubTstDrvMoveMedium1,
+ SubTstDrvTreeDepth1, SubTstDrvMoveVm1, SubTstDrvCloneMedium1]).main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova
new file mode 100644
index 00000000..aba10dbb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova
new file mode 100644
index 00000000..19c2c4b9
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova
new file mode 100644
index 00000000..66a173aa
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova
new file mode 100644
index 00000000..8027ce64
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova
new file mode 100644
index 00000000..7fbf44ee
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova
new file mode 100644
index 00000000..2434f37e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova
new file mode 100644
index 00000000..a6549b15
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem
new file mode 100644
index 00000000..c155d659
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem
@@ -0,0 +1,74 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALRXOpwrjjFzFZtb
+aTtB+3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mD
+XlqE4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++
+/fKSYRIniCbWp9ahTLH8hjDcw48tAgMBAAECgYAiNl4vHHA4X13dAEWBcW4UtAyt
+k3Ocl5Tx7Cv/aYFU9WI2xSMg+ttdyrxFu+1bASgVk9zs27dYeOGo1OxEfesZzQkT
+mbzvYCdYk9wAWKXQwpp78HZyEsKVipIxO+riH9ph7SFQBzB5NoADPoqwahOmeQQW
+sE3oTJRa9O+JR3muXQJBAOOt4dj+1Rwmdy83j9uOLLfO75l2pJd/hq5gN7+eNFks
+R68cbhFGkrOU13dVLquyqxAoaKc2PgZS+RfGRrohjm8CQQDKxeuqYXyrYBU4jNAS
+TgcR4lbb8HiC1AyQrdiJVtH4qLpqk92M7muHXZQGOXRizHQMrRDY+PcgLoFAnRzJ
+j0ojAkEA3rqL5ivlbtRyY86G/NHpDSdzXT2jZlFq/8tAvkOWEmYu+i9lvaC8gtFo
+t2Stc2olzni5aFq38pfY9lkRd6S8IQJBAJe3LJPnqwrish4Epa38eae07P5U1yY0
+GE6r9EcWEbZ2MDyL9Al9XjEDIDzkAiPmC7JsTx24cda/VPAOXbqlnncCQQCCevzg
+rrnTz0/3iLWkxowiKCE1EvbVALDxPwHi0zvjXbGZs8BodLxeChpJzFImbxRVAIrp
+WvZOuLqhAKjL7OOT
+-----END PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14180722474914962380 (0xc4cc0bf54fb45bcc)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Validity
+ Not Before: Feb 1 01:45:27 2016 GMT
+ Not After : Jan 24 01:45:27 2046 GMT
+ Subject: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b4:57:3a:9c:2b:8e:31:73:15:9b:5b:69:3b:41:
+ fb:79:0d:ae:0a:ea:62:4c:9e:e8:2a:af:ac:e3:83:
+ 67:05:ec:96:3a:ff:a5:db:98:8f:a6:d8:87:93:9d:
+ ce:70:05:aa:07:9e:0a:99:2a:11:ff:f6:a7:99:83:
+ 5e:5a:84:e1:c0:e8:2f:6a:29:2e:4e:cb:ad:d9:95:
+ 0e:a5:fd:ad:49:06:70:67:fa:87:04:e9:d4:40:25:
+ 94:44:81:21:4b:7a:2d:d4:9b:2f:b6:39:82:86:2a:
+ d4:4f:be:fd:f2:92:61:12:27:88:26:d6:a7:d6:a1:
+ 4c:b1:fc:86:30:dc:c3:8f:2d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A
+ X509v3 Authority Key Identifier:
+ keyid:02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 13:f1:33:5f:c7:d9:6c:20:a4:eb:f2:e5:e2:b5:e0:e8:b6:9c:
+ c7:62:4a:39:53:83:11:98:cf:11:3d:58:09:d8:38:78:71:16:
+ d4:24:cc:c8:2e:5a:2b:d3:94:6a:dc:ae:62:e7:81:6a:5f:04:
+ 84:ba:55:8c:dc:6b:ff:aa:78:4f:37:8e:fd:ba:b5:d1:27:83:
+ 47:29:30:92:63:85:53:f0:b1:b9:f4:c7:a8:b1:48:44:4e:30:
+ 6f:50:d3:35:14:87:59:d0:f8:ed:da:07:60:6c:de:6d:53:53:
+ 3d:d7:03:97:1f:6b:13:ce:92:49:20:57:4f:b0:87:30:76:66:
+ d3:43
+-----BEGIN CERTIFICATE-----
+MIICqDCCAhGgAwIBAgIJAMTMC/VPtFvMMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV
+BAYTAkRFMRAwDgYDVQQIDAdFeGFtcGxlMRUwEwYDVQQHDAxGb3IgSW5zdGFuY2Ux
+FjAUBgNVBAoMDUJlaXNwaWVsIEdtYkgxHTAbBgNVBAMMFGJlaXNwaWVsLmV4YW1w
+bGUub3JnMB4XDTE2MDIwMTAxNDUyN1oXDTQ2MDEyNDAxNDUyN1owbTELMAkGA1UE
+BhMCREUxEDAOBgNVBAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEW
+MBQGA1UECgwNQmVpc3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBs
+ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALRXOpwrjjFzFZtbaTtB
++3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mDXlqE
+4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++/fKS
+YRIniCbWp9ahTLH8hjDcw48tAgMBAAGjUDBOMB0GA1UdDgQWBBQCCrC8IWPBUBYe
+jbf0sBxI2OEKKjAfBgNVHSMEGDAWgBQCCrC8IWPBUBYejbf0sBxI2OEKKjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBABPxM1/H2WwgpOvy5eK14Oi2nMdi
+SjlTgxGYzxE9WAnYOHhxFtQkzMguWivTlGrcrmLngWpfBIS6VYzca/+qeE83jv26
+tdEng0cpMJJjhVPwsbn0x6ixSEROMG9Q0zUUh1nQ+O3aB2Bs3m1TUz3XA5cfaxPO
+kkkgV0+whzB2ZtND
+-----END CERTIFICATE-----
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova
new file mode 100644
index 00000000..6a21a19b
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova
new file mode 100644
index 00000000..8e6e2e2e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova
new file mode 100644
index 00000000..d5a92eb2
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova
new file mode 100644
index 00000000..6480e6d5
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem
new file mode 100644
index 00000000..1b682b0d
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem
@@ -0,0 +1,134 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnh
+vNowZYnu7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd7
+7RZJhjoAcfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZ
+gthyQ1PvddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOS
+ZM6BUxKv+PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10U
+ddrtxiI+F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hx
+dnNdwncOR1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRe
+w9OhHZGFnuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQ
+m6N7GxlP+vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef
+6ws+0zbpz8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZ
+uuCdex0CEayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEA
+AQKCAgAnjPGQBqBf0Fv2z/P92WmGu/ZvXFyqU3aycmpRJZKsVTzR66lvkMDNWSx7
+0mJFDvXYqHAROOM/pulJkho+P8Y4/XeU4P5c0hCmJRFTp4oLl/C53h3PsoE1bgXV
+5yMQ5YmKedRpkZirgcDCdG0PWpfE/MWeHA7rgf5aNSTyGh7+YqvV02CpWAMsx4MF
+ttA8/ivOziQhhIRZySsilGazw8jBMHulyXASV63jzok6txzvbY7jjR+HfGFQXgE0
+s0c2wTMVLdA0Pzx5MH51BJtxVJHato7h8n/LfclcwHyDXddJYlb8k8GzKAynGsG+
+ENmfD7btA5xw+teHtULtI+KcysepCeVXW7Fr7PFJe1XOx1c24WrFpjeGdnG3PdqA
+Y9YbjMCD/GpCUICla3N6iRDG3imY+DUWcExVjUo6TUbQ4ofQT4WF7+2Yzg6MRqYu
+41jgF+voCXS4BH8e+ngEjd8VLHma7FPuWujodCERsvxZ7aLXBcZCH1Lje9Y8a79x
+RGX83pVHTWFRyo4wKBpRUz+hwH7MjYdfE2Q+Zl153pXw3au9jX8PUMhx9swWPGRw
+CvLPwtTwrezzdFeXg4wfJwWnvqhKIViery4z3rdjh5Zke/9+7gXmFbZ8w+QUkXXq
+cnBEJySTvpI4B9abpcEI4OO84L/A6ENDO10W4ZGfm5aD2m0vIQKCAQEA6nZAmsSr
+T1CcgrWNcY6civ8o5cMF8fM9ib5QvibRlJN9dpcZPr6+Pp2UPCkjVowk/FybP1Kw
+sqA0aXOL3VR03YpLrqdUDpe9YiFCa3ttDX0pTOdU9QW2RbOrNQVurKhzNETd37cJ
+0RYBOHJY/RxA0F8l16wSr1jTO/SRbWkFJYZS/OaH1B6gCxz6H1IgfKJddrvFZsd8
+6PnLtqsPnsXuxugqHu3ENddRqiiSNyGnqpGu0jQpPBrQEQQN5o8/X9rfNfAobChH
+Nii0va7MHudwaTTo2ytUzEV+v0kueMNU9YXgJgxzSzf1/VQC+HoNI4Vbs9XCbOWV
+z2FVBwXM+0cIMQKCAQEA4Gr1pQAymJq/9p2fiWSR8TVdrYCwvAnBu8QSqSzweNZC
+SRt7Nkhhljm7M2DuUOioMaFAT9CnyD2nJyKSAqqOngb0vo7C53UzgFu+QFMtUx/Z
+t/C96WcLYrErXfnB/sTq0Gp4tpoo1S3FZecabhlZyv1O08WXiNCcEX1VMQz2+iF9
++n2PjFuEomeWHxBWCwfB9/S0pKVHP/7833sQ8Dfsm2EwtPy3v58ln3UguaUDIe5V
+r0TmKr8kDrYgbw1ZpBOd9Wbbvj3vIN34OPoSIrcar67DCp7qYq77LYAhngfdQGed
+MUMsbU1dODWf0kwP7mncTr3mPByQUHa3ImjvJPv1YwKCAQBYa04Dz8U2/RR46pSz
+zW9Vr9Ixi7GTRALiDkaO3z7MRC7daTAZDH/cRzre0TjFa8aK8TWO1NVUF7yMRAnr
+5uzHm17dN7coZasC9b4BoKNIofnQSbEtUgEiGhanwSuyqzf+7zWpJ3LpSd4d9ml+
+0ofSzP8NbZQCUoIeqyWo2CEbvKNRQnLY2M/MQRpGc4dS2TxcCYXxM6v0hDeB5NLY
+MpbQpj80OMB0+YWPoQs7BVMgrR37obYnN4ld0WSYnU7uDDF/OtlTqIDqeMFogyHx
+SaCH3G8wMBAjlNWut59x5WAF033re2iDZlA7P9J6+DQ6QBGMKUHQJWiws2kIY/Sg
+knIRAoIBAQCzlkR/Rxo2LthhZR/PFfEIQrl1Z9+GipRDSxPX2AOT33nqARjnhqK5
+UfexlOcBTj2SgcTyWjp6LoQ9+Bc6FPzODyj5+UqVaJ/PHxuvZCCIPZu/6+I+Dlz5
+HGhk6sJIu5JhOGLjVZhJiDhIZNkstBK8M1tKcvvh23aZNF/hQcu+vOCQfLxMCMyq
+HhTvROZmK04Yu/V3MGBFISuBN32FjmtEqFEO9JGiwZuc8GFAzoEkPRLKkGtUV+Nl
+9m8cD2XlvGESib5djjh3Z8oE5nFu4HJ1lne0XxmX4QlWDwxX51kx+fi7/FJoIZnw
+qlD8PCwfkQ1g4eyFvCHskiPZYHnHce2bAoIBACW40wFIPiDRhjNywEi4miN6P+Xs
+rlM9csmxw2GG6Z4c8H/Z4SNBRQmnj/F6PHsar6SGy9WlR9mNeR2XBn4Pyf/cNjTQ
+bKzV5wPRm6P81SQjhIz4Mxdx1S30AeF1LdagWFiq0on7oRTH2SeKQspdpNeiTDbC
+sBw4SVDKmGZYGZcuT3BdvvZFEW4qncSYuUM7l9bTmsbzid/v8zn/XDQrpdPYnptD
+ljJETKQzlyrJLtTbyFlo3Osf1N4408u3rqhpw2SgKdyMiHndhxkF869Vycll+VMz
+SzPU0wI62BIPWHDBJLnxGBTa+4kUSxP+oDvCfVYCmDcfDRew3MWCK9emJnU=
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Validity
+ Not Before: Feb 15 14:27:56 2016 GMT
+ Not After : Feb 2 14:27:56 2066 GMT
+ Subject: C=DE, ST=Instance-Example, L=Examplecity, O=For Instance GmbH, OU=The Example Unit, CN=subcert.example.org/emailAddress=subcert@example.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:cd:89:6e:78:76:65:6d:2a:a5:2e:b5:40:9c:2c:
+ 90:c7:dc:60:9c:ee:e3:4a:32:ba:c1:7a:a5:6f:19:
+ 25:d1:06:4b:c8:39:e1:bc:da:30:65:89:ee:ed:86:
+ 48:dd:5f:91:b9:5c:d8:6d:cc:e5:12:bd:6a:66:95:
+ 1e:3b:c3:45:d3:89:8a:d6:41:4f:25:18:a7:46:2b:
+ a2:3a:d8:a1:f9:6c:2f:0d:07:7b:ed:16:49:86:3a:
+ 00:71:f1:1e:a2:0b:75:3b:33:99:83:c4:0a:17:50:
+ 0c:1a:55:20:93:9c:d6:99:ae:15:21:2e:f9:64:ce:
+ 86:d5:be:fb:a9:2d:eb:81:ee:8b:f4:dc:19:82:d8:
+ 72:43:53:ef:75:d6:2e:7b:83:0d:12:92:be:d9:e9:
+ 19:66:af:c6:19:9b:dd:fb:23:4f:d1:6a:bb:93:37:
+ 26:c9:d0:a0:08:5f:44:74:b0:99:c2:de:01:1e:63:
+ 92:64:ce:81:53:12:af:f8:f2:0d:66:03:bb:5a:9d:
+ ad:04:11:a2:82:0f:4c:7f:f3:57:a1:f8:cc:1e:9e:
+ d1:23:5e:b1:6b:65:bd:43:f0:ad:d8:0e:ab:6f:94:
+ 80:07:5d:14:75:da:ed:c6:22:3e:17:e2:6f:ee:53:
+ 64:1c:e0:8b:6e:3b:bb:70:4b:3e:63:a5:c8:6e:dd:
+ 65:ed:a5:6a:04:5a:60:a3:a7:30:15:9e:c3:74:b3:
+ c7:66:48:1b:e7:48:71:76:73:5d:c2:77:0e:47:5b:
+ 32:b2:94:c5:e7:8e:21:6e:69:1c:6f:ff:3e:c1:ea:
+ be:aa:0b:03:ff:9e:db:fd:91:ab:c1:bf:2b:b8:1c:
+ 83:cf:42:d9:89:7c:f1:c4:14:5e:c3:d3:a1:1d:91:
+ 85:9e:e0:b4:0f:57:22:22:fa:62:7c:a7:b8:47:f9:
+ 0b:70:48:15:5b:bc:98:a7:85:b5:04:06:60:88:12:
+ 4f:81:70:2b:25:39:54:06:bc:5a:9b:3b:10:9b:a3:
+ 7b:1b:19:4f:fa:f4:c2:9f:2e:c8:26:c4:35:29:6b:
+ b9:06:a9:0f:bf:44:f5:fb:30:f9:bd:44:fd:fe:50:
+ 1a:f9:70:5d:0c:78:58:56:6d:1a:76:be:1d:5c:87:
+ 9f:eb:0b:3e:d3:36:e9:cf:c0:f2:68:3f:c1:83:d2:
+ af:44:a7:67:ce:25:5c:fa:92:b0:91:63:95:ac:5b:
+ d9:e8:db:c1:7b:1d:22:f0:18:21:e0:5a:e9:91:d8:
+ e6:ad:93:19:ba:e0:9d:7b:1d:02:11:ac:ae:61:ec:
+ 60:52:ac:8c:83:d9:b4:1b:01:aa:54:d1:ba:99:50:
+ 78:70:23:a0:d2:b6:b2:6a:a1:0a:ad:bb:54:4b:13:
+ 5a:0f:f3
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha512WithRSAEncryption
+ 2d:1c:49:41:1a:4f:dc:d8:5c:b1:fa:ca:53:38:86:26:6e:56:
+ a5:6d:2e:1d:0d:74:64:0e:89:c3:3a:c7:1d:01:6e:d1:93:b2:
+ c9:37:01:6a:ae:31:42:96:05:d7:df:fd:01:f8:bc:f3:f3:4c:
+ cd:75:ae:16:00:61:78:f2:67:c5:b1:76:76:16:39:ba:d2:6b:
+ 09:ad:99:2b:22:ce:56:89:4d:08:ca:8c:76:4c:50:6b:83:c9:
+ 46:9b:f5:9f:2d:e2:7f:e5:72:aa:76:56:c4:67:83:45:26:b7:
+ e2:ae:f7:1e:61:c9:aa:2e:8d:b8:59:42:84:37:25:c8:16:92:
+ d6:d5
+-----BEGIN CERTIFICATE-----
+MIIEGjCCA4MCAQEwDQYJKoZIhvcNAQENBQAwbTELMAkGA1UEBhMCREUxEDAOBgNV
+BAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEWMBQGA1UECgwNQmVp
+c3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBsZS5vcmcwIBcNMTYw
+MjE1MTQyNzU2WhgPMjA2NjAyMDIxNDI3NTZaMIG3MQswCQYDVQQGEwJERTEZMBcG
+A1UECAwQSW5zdGFuY2UtRXhhbXBsZTEUMBIGA1UEBwwLRXhhbXBsZWNpdHkxGjAY
+BgNVBAoMEUZvciBJbnN0YW5jZSBHbWJIMRkwFwYDVQQLDBBUaGUgRXhhbXBsZSBV
+bml0MRwwGgYDVQQDDBNzdWJjZXJ0LmV4YW1wbGUub3JnMSIwIAYJKoZIhvcNAQkB
+FhNzdWJjZXJ0QGV4YW1wbGUub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnhvNowZYnu
+7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd77RZJhjoA
+cfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZgthyQ1Pv
+ddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOSZM6BUxKv
++PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10UddrtxiI+
+F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hxdnNdwncO
+R1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRew9OhHZGF
+nuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQm6N7GxlP
++vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef6ws+0zbp
+z8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZuuCdex0C
+EayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEAATANBgkq
+hkiG9w0BAQ0FAAOBgQAtHElBGk/c2Fyx+spTOIYmblalbS4dDXRkDonDOscdAW7R
+k7LJNwFqrjFClgXX3/0B+Lzz80zNda4WAGF48mfFsXZ2Fjm60msJrZkrIs5WiU0I
+yox2TFBrg8lGm/WfLeJ/5XKqdlbEZ4NFJrfirvceYcmqLo24WUKENyXIFpLW1Q==
+-----END CERTIFICATE-----
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova
new file mode 100644
index 00000000..48e49059
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1.py b/src/VBox/ValidationKit/tests/api/tdAppliance1.py
new file mode 100755
index 00000000..a92d8b42
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdAppliance1.py $
+
+"""
+VirtualBox Validation Kit - IAppliance Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import tarfile
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxwrappers;
+
+
+class SubTstDrvAppliance1(base.SubTestDriverBase):
+ """
+ Sub-test driver for IAppliance Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'appliance', 'Applicance');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ fRc = True;
+
+ # Import a set of simple OVAs.
+ # Note! Manifests generated by ovftool 4.0.0 does not include the ovf, while the ones b 4.1.0 does.
+ for sOva in (
+ # t1 is a plain VM without any disks, ovftool 4.0 export from fusion
+ 'tdAppliance1-t1.ova',
+ # t2 is a plain VM with one disk. Both 4.0 and 4.1.0 exports.
+ 'tdAppliance1-t2.ova',
+ 'tdAppliance1-t2-ovftool-4.1.0.ova',
+ # t3 is a VM with one gzipped disk and selecting SHA256 on the ovftool cmdline (--compress=9 --shaAlgorithm=sha256).
+ 'tdAppliance1-t3.ova',
+ 'tdAppliance1-t3-ovftool-4.1.0.ova',
+ # t4 is a VM with with two gzipped disk, SHA256 and a (self) signed manifest (--privateKey=./tdAppliance1-t4.pem).
+ 'tdAppliance1-t4.ova',
+ 'tdAppliance1-t4-ovftool-4.1.0.ova',
+ # t5 is a VM with with one gzipped disk, SHA1 and a manifest signed by a valid (2016) DigiCert code signing cert.
+ 'tdAppliance1-t5.ova',
+ 'tdAppliance1-t5-ovftool-4.1.0.ova',
+ # t6 is a VM with with one gzipped disk, SHA1 and a manifest signed by a certificate issued by the t4 certificate,
+ # thus it should be impossible to establish a trusted path to a root CA.
+ 'tdAppliance1-t6.ova',
+ 'tdAppliance1-t6-ovftool-4.1.0.ova',
+ # t7 is based on tdAppliance1-t2-ovftool-4.1.0.ova and has modified to have an invalid InstanceID as well as an
+ # extra readme file. It was tarred up using bsdtar 2.4.12 on windows, so it uses a slightly different tar format and
+ # have different file attributes.
+ 'tdAppliance1-t7-bad-instance.ova',
+ ):
+ reporter.testStart(sOva);
+ try:
+ fRc = self.testImportOva(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc;
+ fRc = self.testImportOvaAsOvf(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc;
+ except:
+ reporter.errorXcpt();
+ fRc = False;
+ fRc = reporter.testDone() and fRc;
+
+ ## @todo more stuff
+ return fRc;
+
+ #
+ # Test execution helpers.
+ #
+
+ def testImportOva(self, sOva):
+ """ xxx """
+ oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox();
+
+ #
+ # Import it as OVA.
+ #
+ try:
+ oAppliance = oVirtualBox.createAppliance();
+ except:
+ return reporter.errorXcpt('IVirtualBox::createAppliance failed');
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance.read(sOva), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'read "%s"' % (sOva,));
+ except:
+ return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOva,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ try:
+ oAppliance.interpret();
+ except:
+ return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOva,));
+
+ #
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance.importMachines([]),
+ self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOva,));
+ except:
+ return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOva,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ #
+ # Export the
+ #
+ ## @todo do more with this OVA. Like untaring it and loading it as an OVF. Export it and import it again.
+
+ return True;
+
+ def testImportOvaAsOvf(self, sOva):
+ """
+ Unpacks the OVA into a subdirectory in the scratch area and imports it as an OVF.
+ """
+ oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox();
+
+ sTmpDir = os.path.join(self.oTstDrv.sScratchPath, os.path.split(sOva)[1] + '-ovf');
+ sOvf = os.path.join(sTmpDir, os.path.splitext(os.path.split(sOva)[1])[0] + '.ovf');
+
+ #
+ # Unpack
+ #
+ try:
+ os.mkdir(sTmpDir, 0o755);
+ oTarFile = tarfile.open(sOva, 'r:*'); # No 'with' support in 2.6. pylint: disable=consider-using-with
+ oTarFile.extractall(sTmpDir);
+ oTarFile.close();
+ except:
+ return reporter.errorXcpt('Unpacking "%s" to "%s" for OVF style importing failed' % (sOvf, sTmpDir,));
+
+ #
+ # Import.
+ #
+ try:
+ oAppliance2 = oVirtualBox.createAppliance();
+ except:
+ return reporter.errorXcpt('IVirtualBox::createAppliance failed (#2)');
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance2.read(sOvf), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'read "%s"' % (sOvf,));
+ except:
+ return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOvf,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ try:
+ oAppliance2.interpret();
+ except:
+ return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOvf,));
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance2.importMachines([]),
+ self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOvf,));
+ except:
+ return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOvf,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ return True;
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tests.api.tdApi1 import tdApi1;
+ sys.exit(tdApi1([SubTstDrvAppliance1]).main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py b/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py
new file mode 100755
index 00000000..056fb2eb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdCloneMedium1.py $
+
+"""
+VirtualBox Validation Kit - Clone Medium Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+from testdriver import vboxwrappers
+
+
+class SubTstDrvCloneMedium1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Clone Medium Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'clone-medium', 'Move Medium');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+
+ return self.testAll()
+
+ #
+ # Test execution helpers.
+ #
+
+ def createTestMedium(self, oVM, sPathSuffix, sFmt = 'VDI', cbSize = 1024*1024, data = None):
+ assert oVM is not None
+
+ oSession = self.oTstDrv.openSession(oVM)
+
+ if oSession is None:
+ return False
+
+ #
+ # Create Medium Object
+ #
+
+ sBaseHdd1Path = os.path.join(self.oTstDrv.sScratchPath, sPathSuffix)
+ sBaseHdd1Fmt = sFmt
+ cbBaseHdd1Size = cbSize
+
+ try:
+ oBaseHdd1 = oSession.createBaseHd(sBaseHdd1Path, sBaseHdd1Fmt, cbBaseHdd1Size)
+ except:
+ return reporter.errorXcpt('createBaseHd failed')
+
+ oMediumIOBaseHdd1 = oBaseHdd1.openForIO(True, "")
+
+ if data:
+ cbWritten = oMediumIOBaseHdd1.write(0, data)
+
+ if cbWritten != 1:
+ return reporter.error("Failed writing to test hdd.")
+
+ oMediumIOBaseHdd1.close()
+
+ return oBaseHdd1
+
+ def cloneMedium(self, oSrcHd, oTgtHd):
+ """
+ Clones medium into target medium.
+ """
+ try:
+ oProgressCom = oSrcHd.cloneTo(oTgtHd, (vboxcon.MediumVariant_Standard, ), None);
+ except:
+ reporter.errorXcpt('failed to clone medium %s to %s' % (oSrcHd.name, oTgtHd.name));
+ return False;
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'clone base disk %s to %s' % (oSrcHd.name, oTgtHd.name));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+ return True;
+
+ def resizeAndCloneMedium(self, oSrcHd, oTgtHd, cbTgtSize):
+ """
+ Clones medium into target medium.
+ """
+
+ try:
+ oProgressCom = oSrcHd.resizeAndCloneTo(oTgtHd, cbTgtSize, (vboxcon.MediumVariant_Standard, ), None);
+ except:
+ reporter.errorXcpt('failed to resize and clone medium %s to %s and to size %d' \
+ % (oSrcHd.name, oTgtHd.name, cbTgtSize));
+ return False;
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'resize and clone base disk %s to %s and to size %d' \
+ % (oSrcHd.name, oTgtHd.name, cbTgtSize));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+ return True;
+
+ def deleteVM(self, oVM):
+ try:
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone);
+ except:
+ reporter.logXcpt();
+
+ try:
+ oProgressCom = oVM.deleteConfig([]);
+ except:
+ reporter.logXcpt();
+ else:
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'Delete VM %s' % (oVM.name));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+
+ return None;
+
+ #
+ # Tests
+ #
+
+ def testCloneOnly(self):
+ """
+ Tests cloning mediums only. No resize.
+ """
+
+ reporter.testStart("testCloneOnly")
+
+ oVM = self.oTstDrv.createTestVM('test-medium-clone-only', 1, None, 4)
+
+ hd1 = self.createTestMedium(oVM, "hd1-cloneonly", data=[0xdeadbeef])
+ hd2 = self.createTestMedium(oVM, "hd2-cloneonly")
+
+ if not self.cloneMedium(hd1, hd2):
+ return False
+
+ oMediumIOhd1 = hd1.openForIO(True, "")
+ dataHd1 = oMediumIOhd1.read(0, 4)
+ oMediumIOhd1.close()
+
+ oMediumIOhd2 = hd2.openForIO(True, "")
+ dataHd2 = oMediumIOhd2.read(0, 4)
+ oMediumIOhd2.close()
+
+ if dataHd1 != dataHd2:
+ reporter.testFailure("Data read is unexpected.")
+
+ self.deleteVM(oVM)
+
+ reporter.testDone()
+ return True
+
+ def testResizeAndClone(self):
+ """
+ Tests resizing and cloning mediums only.
+ """
+
+ reporter.testStart("testResizeAndClone")
+
+ oVM = self.oTstDrv.createTestVM('test-medium-clone-only', 1, None, 4)
+
+ hd1 = self.createTestMedium(oVM, "hd1-resizeandclone", data=[0xdeadbeef])
+ hd2 = self.createTestMedium(oVM, "hd2-resizeandclone")
+
+ if not (hasattr(hd1, "resizeAndCloneTo") and callable(getattr(hd1, "resizeAndCloneTo"))):
+ self.deleteVM(oVM)
+ reporter.testDone()
+ return True
+
+ if not self.resizeAndCloneMedium(hd1, hd2, 1024*1024*2):
+ return False
+
+ oMediumIOhd1 = hd1.openForIO(True, "")
+ dataHd1 = oMediumIOhd1.read(0, 4)
+ oMediumIOhd1.close()
+
+ oMediumIOhd2 = hd2.openForIO(True, "")
+ dataHd2 = oMediumIOhd2.read(0, 4)
+ oMediumIOhd2.close()
+
+ if dataHd1 != dataHd2:
+ reporter.testFailure("Data read is unexpected.")
+
+ if hd2.logicalSize not in (hd1.logicalSize, 1024*1024*2):
+ reporter.testFailure("Target medium did not resize.")
+
+ self.deleteVM(oVM)
+
+ reporter.testDone()
+ return True
+
+ def testAll(self):
+ return self.testCloneOnly() & self.testResizeAndClone()
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvCloneMedium1]).main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py b/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py
new file mode 100755
index 00000000..21892abb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdCreateVMWithDefaults1.py $
+
+"""
+VirtualBox Validation Kit - Create VM with IMachine::applyDefaults() Test
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter;
+from testdriver import vboxcon;
+
+
+class SubTstDrvCreateVMWithDefaults1(base.SubTestDriverBase):
+ """
+ Sub-test driver for VM Move Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'create-vm-with-defaults', 'Create VMs with defaults');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,))
+ reporter.testStart(self.sTestName);
+ fRc = self.testCreateVMWithDefaults();
+ reporter.testDone();
+ return fRc;
+
+
+ def createVMWithDefaults(self, sGuestType):
+ sName = 'testvm_%s' % (sGuestType)
+ # create VM manually, because the self.createTestVM() makes registration inside
+ # the method, but IMachine::applyDefault() must be called before machine is registered
+ try:
+ if self.oTstDrv.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now).
+ oVM = self.oTstDrv.oVBox.createMachine("", sName, [],
+ self.oTstDrv.tryFindGuestOsId(sGuestType),
+ "")
+ elif self.oTstDrv.fpApiVer >= 4.0:
+ oVM = self.oTstDrv.oVBox.createMachine("", sName,
+ self.oTstDrv.tryFindGuestOsId(sGuestType),
+ "", False)
+ elif self.oTstDrv.fpApiVer >= 3.2:
+ oVM = self.oTstDrv.oVBox.createMachine(sName,
+ self.oTstDrv.tryFindGuestOsId(sGuestType),
+ "", "", False)
+ else:
+ oVM = self.oTstDrv.oVBox.createMachine(sName,
+ self.oTstDrv.tryFindGuestOsId(sGuestType),
+ "", "")
+ try:
+ oVM.saveSettings()
+ except:
+ reporter.logXcpt()
+ if self.oTstDrv.fpApiVer >= 4.0:
+ try:
+ if self.oTstDrv.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([])
+ else:
+ oProgress = oVM.delete(None);
+ self.oTstDrv.waitOnProgress(oProgress)
+ except:
+ reporter.logXcpt()
+ else:
+ try: oVM.deleteSettings()
+ except: reporter.logXcpt()
+ raise
+ except:
+ reporter.errorXcpt('failed to create vm "%s"' % (sName))
+ return None
+
+ if oVM is None:
+ return False
+
+ # apply settings
+ fRc = True
+ try:
+ if self.oTstDrv.fpApiVer >= 6.1:
+ oVM.applyDefaults('')
+ oVM.saveSettings();
+ self.oTstDrv.oVBox.registerMachine(oVM)
+ except:
+ reporter.logXcpt()
+ fRc = False
+
+ # Some errors from applyDefaults can be observed only after further settings saving.
+ # Change and save the size of the VM RAM as simple setting change.
+ oSession = self.oTstDrv.openSession(oVM)
+ if oSession is None:
+ fRc = False
+
+ if fRc:
+ try:
+ oSession.memorySize = 4096
+ oSession.saveSettings(True)
+ except:
+ reporter.logXcpt()
+ fRc = False
+
+ # delete VM
+ try:
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone)
+ except:
+ reporter.logXcpt()
+
+ if self.oTstDrv.fpApiVer >= 4.0:
+ try:
+ if self.oTstDrv.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([])
+ else:
+ oProgress = oVM.delete(None)
+ self.oTstDrv.waitOnProgress(oProgress)
+
+ except:
+ reporter.logXcpt()
+
+ else:
+ try: oVM.deleteSettings()
+ except: reporter.logXcpt()
+
+ return fRc
+
+ def testCreateVMWithDefaults(self):
+ """
+ Test create VM with defaults.
+ """
+ if not self.oTstDrv.importVBoxApi():
+ return reporter.error('importVBoxApi');
+
+ # Get the guest OS types.
+ try:
+ aoGuestTypes = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox, 'guestOSTypes')
+ if aoGuestTypes is None or not aoGuestTypes:
+ return reporter.error('No guest OS types');
+ except:
+ return reporter.errorXcpt();
+
+ # Create VMs with defaults for each of the guest types.
+ fRc = True
+ for oGuestType in aoGuestTypes:
+ try:
+ sGuestType = oGuestType.id;
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ reporter.testStart(sGuestType);
+ fRc = self.createVMWithDefaults(sGuestType) and fRc;
+ reporter.testDone();
+ return fRc
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvCreateVMWithDefaults1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py
new file mode 100755
index 00000000..ca3f3891
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdMoveMedium1.py $
+
+"""
+VirtualBox Validation Kit - Medium Move Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+from testdriver import vboxwrappers
+
+
+class SubTstDrvMoveMedium1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Medium Move Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'move-medium', 'Move Medium');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testMediumMove()
+
+ #
+ # Test execution helpers.
+ #
+
+ def moveTo(self, sLocation, aoMediumAttachments):
+ for oAttachment in aoMediumAttachments:
+ try:
+ oMedium = oAttachment.medium
+ reporter.log('Move medium "%s" to "%s"' % (oMedium.name, sLocation,))
+ except:
+ reporter.errorXcpt('failed to get the medium from the IMediumAttachment "%s"' % (oAttachment))
+
+ if self.oTstDrv.fpApiVer >= 5.3 and self.oTstDrv.uRevision > 124748:
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oMedium.moveTo(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'move "%s"' % (oMedium.name,));
+ except:
+ return reporter.errorXcpt('Medium::moveTo("%s") for medium "%s" failed' % (sLocation, oMedium.name,));
+ else:
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oMedium.setLocation(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'move "%s"' % (oMedium.name,));
+ except:
+ return reporter.errorXcpt('Medium::setLocation("%s") for medium "%s" failed' % (sLocation, oMedium.name,));
+
+
+ oProgress.wait()
+ if oProgress.logResult() is False:
+ return False
+ return True
+
+ def checkLocation(self, sLocation, aoMediumAttachments, asFiles):
+ fRc = True
+ for oAttachment in aoMediumAttachments:
+ sFilePath = os.path.join(sLocation, asFiles[oAttachment.port])
+ sActualFilePath = oAttachment.medium.location
+ if os.path.abspath(sFilePath) != os.path.abspath(sActualFilePath):
+ reporter.log('medium location expected to be "%s" but is "%s"' % (sFilePath, sActualFilePath))
+ fRc = False;
+ if not os.path.exists(sFilePath):
+ reporter.log('medium file does not exist at "%s"' % (sFilePath,))
+ fRc = False;
+ return fRc
+
+ def testMediumMove(self):
+ """
+ Test medium moving.
+ """
+ reporter.testStart('medium moving')
+
+ try: ## @todo r=bird: Bad 'ing style.
+ oVM = self.oTstDrv.createTestVM('test-medium-move', 1, None, 4)
+ assert oVM is not None
+
+ # create hard disk images, one for each file-based backend, using the first applicable extension
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats')
+ asFiles = []
+ for oDskFmt in aoDskFmts:
+ aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities')
+ if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \
+ or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps:
+ continue
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): #pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+ if sExt is None:
+ fRc = False
+ break
+ sFile = 'Test' + str(len(asFiles)) + sExt
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile)
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024)
+ if oHd is None:
+ fRc = False
+ break
+
+ # attach HDD, IDE controller exists by default, but we use SATA just in case
+ sController='SATA Controller'
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(asFiles),
+ fImmutable=False, fForceResource=False)
+ if fRc:
+ asFiles.append(sFile)
+
+ fRc = fRc and oSession.saveSettings()
+
+ #create temporary subdirectory in the current working directory
+ sOrigLoc = self.oTstDrv.sScratchPath
+ sNewLoc = os.path.join(sOrigLoc, 'newLocation')
+ os.mkdir(sNewLoc, 0o775)
+
+ aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController)
+ #case 1. Only path without file name, with trailing separator
+ fRc = self.moveTo(sNewLoc + os.sep, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asFiles) and fRc
+
+ #case 2. Only path without file name, without trailing separator
+ fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asFiles) and fRc
+
+ #case 3. Path with file name
+ #The case supposes that user has passed a destination path with a file name but hasn't added an extension/suffix
+ #to this destination file. User supposes that the extension would be added automatically and to be the same as
+ #for the original file. Difficult case, apparently this case should follow mv(1) logic
+ #and the file name is processed as folder name (aka mv(1) logic).
+ #Be discussed.
+ fRc = self.moveTo(os.path.join(sNewLoc, 'newName'), aoMediumAttachments) and fRc
+ asNewFiles = ['newName' + os.path.splitext(s)[1] for s in asFiles]
+ fRc = self.checkLocation(os.path.join(sNewLoc, 'newName'), aoMediumAttachments, asFiles) and fRc
+
+ #after the case the destination path must be corrected
+ sNewLoc = os.path.join(sNewLoc, 'newName')
+
+ #case 4. Only file name
+ fRc = self.moveTo('onlyMediumName', aoMediumAttachments) and fRc
+ asNewFiles = ['onlyMediumName' + os.path.splitext(s)[1] for s in asFiles]
+ if self.oTstDrv.fpApiVer >= 5.3:
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asNewFiles) and fRc
+ else:
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments,
+ [s.replace('.hdd', '.parallels') for s in asNewFiles]) and fRc
+
+ #case 5. Move all files from a snapshot
+ fRc = fRc and oSession.takeSnapshot('Snapshot1')
+ if fRc:
+ aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController)
+ asSnapFiles = [os.path.basename(o.medium.name) for o in aoMediumAttachments]
+ fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asSnapFiles) and fRc
+
+ fRc = oSession.close() and fRc
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvMoveMedium1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdMoveVm1.py b/src/VBox/ValidationKit/tests/api/tdMoveVm1.py
new file mode 100755
index 00000000..3bf058d5
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdMoveVm1.py
@@ -0,0 +1,768 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# "$Id: tdMoveVm1.py $"
+
+"""
+VirtualBox Validation Kit - VM Move Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import os
+import sys
+import shutil
+from collections import defaultdict
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+from testdriver import vboxwrappers
+from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import
+
+
+# @todo r=aeichner: The whole path handling/checking needs fixing to also work on Windows
+# The current quick workaround is to spill os.path.normcase() all over the place when
+# constructing paths. I'll leave the real fix to the original author...
+class SubTstDrvMoveVm1(base.SubTestDriverBase):
+ """
+ Sub-test driver for VM Move Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'move-vm', 'Move VM');
+
+ # Note! Hardcoded indexing in test code further down.
+ self.asRsrcs = [
+ os.path.join('5.3','isos','tdMoveVM1.iso'),
+ os.path.join('5.3','floppy','tdMoveVM1.img')
+ ];
+
+ self.asImagesNames = []
+ self.dsKeys = {
+ 'StandardImage': 'SATA Controller',
+ 'ISOImage': 'IDE Controller',
+ 'FloppyImage': 'Floppy Controller',
+ 'SettingsFile': 'Settings File',
+ 'LogFile': 'Log File',
+ 'SavedStateFile': 'Saved State File',
+ 'SnapshotFile': 'Snapshot File'
+ };
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ reporter.testStart(self.sTestName);
+ reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,))
+ fRc = self.testVMMove();
+ return reporter.testDone(fRc is None)[1] == 0;
+
+ #
+ # Test execution helpers.
+ #
+
+ def createTestMachine(self):
+ """
+ Document me here, not with hashes above.
+ """
+ oVM = self.oTstDrv.createTestVM('test-vm-move', 1, None, 4)
+ if oVM is None:
+ return None
+
+ # create hard disk images, one for each file-based backend, using the first applicable extension
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats')
+
+ for oDskFmt in aoDskFmts:
+ aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities')
+ if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \
+ or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps:
+ continue
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): # pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+ if sExt is None:
+ fRc = False
+ break
+ sFile = 'test-vm-move' + str(len(self.asImagesNames)) + sExt
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile)
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024)
+ if oHd is None:
+ fRc = False
+ break
+
+ # attach HDD, IDE controller exists by default, but we use SATA just in case
+ sController = self.dsKeys['StandardImage']
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(self.asImagesNames),
+ fImmutable=False, fForceResource=False)
+ if fRc:
+ self.asImagesNames.append(sFile)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc
+
+ if fRc is False:
+ oVM = None
+
+ return oVM
+
+ def moveVMToLocation(self, sLocation, oVM):
+ """
+ Document me here, not with hashes above.
+ """
+ fRc = True
+ try:
+
+ ## @todo r=bird: Too much unncessary crap inside try clause. Only oVM.moveTo needs to be here.
+ ## Though, you could make an argument for oVM.name too, perhaps.
+
+ # move machine
+ reporter.log('Moving machine "%s" to the "%s"' % (oVM.name, sLocation))
+ sType = 'basic'
+ oProgress = vboxwrappers.ProgressWrapper(oVM.moveTo(sLocation, sType), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'moving machine "%s"' % (oVM.name,))
+
+ except:
+ return reporter.errorXcpt('Machine::moveTo("%s") for machine "%s" failed' % (sLocation, oVM.name,))
+
+ oProgress.wait()
+ if oProgress.logResult() is False:
+ fRc = False
+ reporter.log('Progress object returned False')
+ else:
+ fRc = True
+
+ return fRc
+
+ def checkLocation(self, oMachine, dsReferenceFiles):
+ """
+ Document me.
+
+ Prerequisites:
+ 1. All standard images are attached to SATA controller
+ 2. All ISO images are attached to IDE controller
+ 3. All floppy images are attached to Floppy controller
+ 4. The type defaultdict from collection is used here (some sort of multimap data structure)
+ 5. The dsReferenceFiles parameter here is the structure defaultdict(set):
+ [
+ ('StandardImage': ['somedisk.vdi', 'somedisk.vmdk',...]),
+ ('ISOImage': ['somedisk_1.iso','somedisk_2.iso',...]),
+ ('FloppyImage': ['somedisk_1.img','somedisk_2.img',...]),
+ ('SnapshotFile': ['snapshot file 1','snapshot file 2', ...]),
+ ('SettingsFile', ['setting file',...]),
+ ('SavedStateFile': ['state file 1','state file 2',...]),
+ ('LogFile': ['log file 1','log file 2',...]),
+ ]
+ """
+
+ fRc = True
+
+ for sKey, sValue in self.dsKeys.items():
+ aActuals = set()
+ aReferences = set()
+
+ # Check standard images locations, ISO files locations, floppy images locations, snapshots files locations
+ if sKey in ('StandardImage', 'ISOImage', 'FloppyImage',):
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sValue) ##@todo r=bird: API call, try-except!
+ for oAttachment in aoMediumAttachments:
+ aActuals.add(os.path.normcase(oAttachment.medium.location))
+
+ elif sKey == 'SnapshotFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getSnapshotsFiles(oMachine)
+
+ # Check setting file location
+ elif sKey == 'SettingsFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals.add(os.path.normcase(oMachine.settingsFilePath))
+
+ # Check saved state files location
+ elif sKey == 'SavedStateFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getStatesFiles(oMachine)
+
+ # Check log files location
+ elif sKey == 'LogFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getLogFiles(oMachine)
+
+ if aActuals:
+ reporter.log('Check %s' % (sKey))
+ intersection = aReferences.intersection(aActuals)
+ for eachItem in intersection:
+ reporter.log('Item location "%s" is correct' % (eachItem))
+
+ difference = aReferences.difference(aActuals)
+ for eachItem in difference:
+ reporter.log('Item location "%s" isn\'t correct' % (eachItem))
+
+ reporter.log('####### Reference locations: #######')
+ for eachItem in aActuals:
+ reporter.log(' "%s"' % (eachItem))
+
+ if len(intersection) != len(aActuals):
+ reporter.log('Not all items in the right location. Check it.')
+ fRc = False
+
+ return fRc
+
+ def checkAPIVersion(self):
+ return self.oTstDrv.fpApiVer >= 5.3;
+
+ @staticmethod
+ def __safeListDir(sDir):
+ """ Wrapper around os.listdir that returns empty array instead of exceptions. """
+ try:
+ return os.listdir(sDir);
+ except:
+ return [];
+
+ def __getStatesFiles(self, oMachine, fPrint = False):
+ asStateFilesList = set()
+ sFolder = oMachine.snapshotFolder;
+ for sFile in self.__safeListDir(sFolder):
+ if sFile.endswith(".sav"):
+ sFullPath = os.path.normcase(os.path.join(sFolder, sFile));
+ asStateFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("State file is %s" % (sFullPath))
+ return asStateFilesList
+
+ def __getSnapshotsFiles(self, oMachine, fPrint = False):
+ asSnapshotsFilesList = set()
+ sFolder = oMachine.snapshotFolder
+ for sFile in self.__safeListDir(sFolder):
+ if sFile.endswith(".sav") is False:
+ sFullPath = os.path.normcase(os.path.join(sFolder, sFile));
+ asSnapshotsFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("Snapshot file is %s" % (sFullPath))
+ return asSnapshotsFilesList
+
+ def __getLogFiles(self, oMachine, fPrint = False):
+ asLogFilesList = set()
+ sFolder = oMachine.logFolder
+ for sFile in self.__safeListDir(sFolder):
+ if sFile.endswith(".log"):
+ sFullPath = os.path.normcase(os.path.join(sFolder, sFile));
+ asLogFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("Log file is %s" % (sFullPath))
+ return asLogFilesList
+
+
+ def __testScenario_2(self, oSession, oMachine, sNewLoc, sOldLoc):
+ """
+ All disks attached to VM are located inside the VM's folder.
+ There are no any snapshots and logs.
+ """
+
+ sController = self.dsKeys['StandardImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ oSubTstDrvMoveMedium1Instance = SubTstDrvMoveMedium1(self.oTstDrv)
+ oSubTstDrvMoveMedium1Instance.moveTo(sOldLoc, aoMediumAttachments)
+
+ del oSubTstDrvMoveMedium1Instance
+
+ dsReferenceFiles = defaultdict(set)
+
+ for s in self.asImagesNames:
+ reporter.log('"%s"' % (s,))
+ dsReferenceFiles['StandardImage'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep + s))
+
+ sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))
+ dsReferenceFiles['SettingsFile'].add(os.path.normcase(sSettingFile))
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('2nd scenario: Couldn\'t save machine settings')
+
+ return fRc
+
+ def __testScenario_3(self, oSession, oMachine, sNewLoc):
+ """
+ There are snapshots
+ """
+
+ # At moment, it's used only one snapshot due to the difficulty to get
+ # all attachments of the machine (i.e. not only attached at moment)
+ cSnap = 1
+
+ for counter in range(1,cSnap+1):
+ strSnapshot = 'Snapshot' + str(counter)
+ fRc = oSession.takeSnapshot(strSnapshot)
+ if fRc is False:
+ reporter.testFailure('3rd scenario: Can\'t take snapshot "%s"' % (strSnapshot,))
+
+ dsReferenceFiles = defaultdict(set)
+
+ sController = self.dsKeys['StandardImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ if fRc is True:
+ for oAttachment in aoMediumAttachments:
+ sRes = oAttachment.medium.location.rpartition(os.sep)
+ dsReferenceFiles['SnapshotFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep +
+ 'Snapshots' + os.sep + sRes[2]))
+
+ sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))
+ dsReferenceFiles['SettingsFile'].add(os.path.normcase(sSettingFile))
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('3rd scenario: Couldn\'t save machine settings')
+
+ return fRc
+
+ def __testScenario_4(self, oMachine, sNewLoc):
+ """
+ There are one or more save state files in the snapshots folder
+ and some files in the logs folder.
+ Here we run VM, next stop it in the "save" state.
+ And next move VM
+ """
+
+ # Run VM and get new Session object.
+ oSession = self.oTstDrv.startVm(oMachine);
+ if not oSession:
+ return False;
+
+ # Some time interval should be here for not closing VM just after start.
+ self.oTstDrv.waitForTasks(1000);
+
+ if oMachine.state != self.oTstDrv.oVBoxMgr.constants.MachineState_Running:
+ reporter.log("Machine '%s' is not Running" % (oMachine.name))
+ fRc = False
+
+ # Call Session::saveState(), already closes session unless it failed.
+ fRc = oSession.saveState()
+ if fRc is True:
+ reporter.log("Machine is in saved state")
+
+ fRc = self.oTstDrv.terminateVmBySession(oSession)
+
+ if fRc is True:
+ # Create a new Session object for moving VM.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ asLogs = self.__getLogFiles(oMachine)
+ for sFile in asLogs:
+ sRes = sFile.rpartition(os.sep)
+ dsReferenceFiles['LogFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep +
+ 'Logs' + os.sep + sRes[2]))
+
+ asStates = self.__getStatesFiles(oMachine)
+ for sFile in asStates:
+ sRes = sFile.rpartition(os.sep)
+ dsReferenceFiles['SavedStateFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep +
+ 'Snapshots' + os.sep + sRes[2]))
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ # cleaning up: get rid of saved state
+ fRes = oSession.discardSavedState(True)
+ if fRes is False:
+ reporter.log('4th scenario: Failed to discard the saved state of machine')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('4th scenario: Couldn\'t close machine session')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Terminate machine by session failed... !!!!!!!!!!!!!!!!!!')
+
+ return fRc
+
+ def __testScenario_5(self, oMachine, sNewLoc, sOldLoc):
+ """
+ There is an ISO image (.iso) attached to the VM.
+ Prerequisites - there is IDE Controller and there are no any images attached to it.
+ """
+
+ fRc = True
+ sISOImageName = 'tdMoveVM1.iso'
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ # Create a new Session object.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ sISOLoc = self.asRsrcs[0] # '5.3/isos/tdMoveVM1.iso'
+ reporter.log("sHost is '%s', sResourcePath is '%s'" % (self.oTstDrv.sHost, self.oTstDrv.sResourcePath))
+ sISOLoc = self.oTstDrv.getFullResourceName(sISOLoc)
+ reporter.log("sISOLoc is '%s'" % (sISOLoc,))
+
+ if not os.path.exists(sISOLoc):
+ reporter.log('ISO file does not exist at "%s"' % (sISOLoc,))
+ fRc = False
+
+ # Copy ISO image from the common resource folder into machine folder.
+ shutil.copy(sISOLoc, sOldLoc)
+
+ # Attach ISO image to the IDE controller.
+ if fRc is True:
+ # Set actual ISO location.
+ sISOLoc = sOldLoc + os.sep + sISOImageName
+ reporter.log("sISOLoc is '%s'" % (sISOLoc,))
+ if not os.path.exists(sISOLoc):
+ reporter.log('ISO file does not exist at "%s"' % (sISOLoc,))
+ fRc = False
+
+ sController=self.dsKeys['ISOImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ iPort = len(aoMediumAttachments)
+ fRc = oSession.attachDvd(sISOLoc, sController, iPort, iDevice = 0)
+ dsReferenceFiles['ISOImage'].add(os.path.normcase(os.path.join(os.path.join(sNewLoc, oMachine.name), sISOImageName)))
+
+ if fRc is True:
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Attach ISO image failed... !!!!!!!!!!!!!!!!!!')
+
+ # Detach ISO image.
+ fRes = oSession.detachHd(sController, iPort, 0)
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t detach image from the controller %s '
+ 'port %s device %s' % (sController, iPort, 0))
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t save machine settings')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t close machine session')
+
+ return fRc
+
+ def __testScenario_6(self, oMachine, sNewLoc, sOldLoc):
+ """
+ There is a floppy image (.img) attached to the VM.
+ Prerequisites - there is Floppy Controller and there are no any images attached to it.
+ """
+
+ fRc = True
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ # Create a new Session object.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ sFloppyLoc = self.asRsrcs[1] # '5.3/floppy/tdMoveVM1.img'
+ sFloppyLoc = self.oTstDrv.getFullResourceName(sFloppyLoc)
+
+ if not os.path.exists(sFloppyLoc):
+ reporter.log('Floppy disk does not exist at "%s"' % (sFloppyLoc,))
+ fRc = False
+
+ # Copy floppy image from the common resource folder into machine folder.
+ shutil.copy(sFloppyLoc, sOldLoc)
+
+ # Attach floppy image.
+ if fRc is True:
+ # Set actual floppy location.
+ sFloppyImageName = 'tdMoveVM1.img'
+ sFloppyLoc = sOldLoc + os.sep + sFloppyImageName
+ sController=self.dsKeys['FloppyImage']
+ fRc = fRc and oSession.attachFloppy(sFloppyLoc, sController, 0, 0)
+ dsReferenceFiles['FloppyImage'].add(os.path.normcase(os.path.join(os.path.join(sNewLoc, oMachine.name),
+ sFloppyImageName)))
+
+ if fRc is True:
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Attach floppy image failed... !!!!!!!!!!!!!!!!!!')
+
+ # Detach floppy image.
+ fRes = oSession.detachHd(sController, 0, 0)
+ if fRes is False:
+ reporter.log('6th scenario: Couldn\'t detach image from the controller %s port %s device %s' % (sController, 0, 0))
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.testFailure('6th scenario: Couldn\'t save machine settings')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('6th scenario: Couldn\'t close machine session')
+ return fRc
+
+
+ def testVMMove(self):
+ """
+ Test machine moving.
+ """
+ if not self.oTstDrv.importVBoxApi():
+ return False
+
+ fSupported = self.checkAPIVersion()
+ reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,))
+
+ if fSupported is False:
+ reporter.log('API version %s is too old. Just skip this test.' % (self.oTstDrv.fpApiVer))
+ return None;
+ reporter.log('API version is "%s".' % (self.oTstDrv.fpApiVer))
+
+ # Scenarios
+ # 1. All disks attached to VM are located outside the VM's folder.
+ # There are no any snapshots and logs.
+ # In this case only VM setting file should be moved (.vbox file)
+ #
+ # 2. All disks attached to VM are located inside the VM's folder.
+ # There are no any snapshots and logs.
+ #
+ # 3. There are snapshots.
+ #
+ # 4. There are one or more save state files in the snapshots folder
+ # and some files in the logs folder.
+ #
+ # 5. There is an ISO image (.iso) attached to the VM.
+ #
+ # 6. There is a floppy image (.img) attached to the VM.
+ #
+ # 7. There are shareable disk and immutable disk attached to the VM.
+
+ try: ## @todo r=bird: Would be nice to use sub-tests here for each scenario, however
+ ## this try/catch as well as lots of return points makes that very hard.
+ ## Big try/catch stuff like this should be avoided.
+ # Create test machine.
+ oMachine = self.createTestMachine()
+ if oMachine is None:
+ reporter.error('Failed to create test machine')
+
+ # Create temporary subdirectory in the current working directory.
+ sOrigLoc = self.oTstDrv.sScratchPath
+ sBaseLoc = os.path.join(sOrigLoc, 'moveFolder')
+ os.mkdir(sBaseLoc, 0o775)
+
+ # lock machine
+ # get session machine
+ oSession = self.oTstDrv.openSession(oMachine)
+ fRc = True
+
+ sNewLoc = sBaseLoc + os.sep
+
+ dsReferenceFiles = defaultdict(set)
+
+ #
+ # 1. case:
+ #
+ # All disks attached to VM are located outside the VM's folder.
+ # There are no any snapshots and logs.
+ # In this case only VM setting file should be moved (.vbox file)
+ #
+ reporter.log("Scenario #1:");
+ for s in self.asImagesNames:
+ reporter.log('"%s"' % (s,))
+ dsReferenceFiles['StandardImage'].add(os.path.normcase(os.path.join(sOrigLoc, s)))
+
+ sSettingFile = os.path.normcase(os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox')))
+ dsReferenceFiles['SettingsFile'].add(sSettingFile)
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ return False;
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ return False;
+
+ fRc = oSession.saveSettings()
+ if fRc is False:
+ reporter.testFailure('1st scenario: Couldn\'t save machine settings')
+
+ #
+ # 2. case:
+ #
+ # All disks attached to VM are located inside the VM's folder.
+ # There are no any snapshots and logs.
+ #
+ reporter.log("Scenario #2:");
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_2nd_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ fRc = self.__testScenario_2(oSession, oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return False;
+
+ #
+ # 3. case:
+ #
+ # There are snapshots.
+ #
+ reporter.log("Scenario #3:");
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_3rd_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ fRc = self.__testScenario_3(oSession, oMachine, sNewLoc)
+ if fRc is False:
+ return False;
+
+ #
+ # 4. case:
+ #
+ # There are one or more save state files in the snapshots folder
+ # and some files in the logs folder.
+ # Here we run VM, next stop it in the "save" state.
+ # And next move VM
+ #
+ reporter.log("Scenario #4:");
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_4th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ # Close Session object because after starting VM we get new instance of session
+ fRc = oSession.close() and fRc
+ if fRc is False:
+ reporter.log('Couldn\'t close machine session')
+
+ del oSession
+
+ fRc = self.__testScenario_4(oMachine, sNewLoc)
+ if fRc is False:
+ return False;
+
+ #
+ # 5. case:
+ #
+ # There is an ISO image (.iso) attached to the VM.
+ # Prerequisites - there is IDE Controller and there are no any images attached to it.
+ #
+ reporter.log("Scenario #5:");
+ sOldLoc = sNewLoc + os.sep + oMachine.name
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_5th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+ fRc = self.__testScenario_5(oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return False;
+
+ #
+ # 6. case:
+ #
+ # There is a floppy image (.img) attached to the VM.
+ # Prerequisites - there is Floppy Controller and there are no any images attached to it.
+ #
+ reporter.log("Scenario #6:");
+ sOldLoc = sNewLoc + os.sep + oMachine.name
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_6th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+ fRc = self.__testScenario_6(oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return False;
+
+# #
+# # 7. case:
+# #
+# # There are shareable disk and immutable disk attached to the VM.
+# #
+# reporter.log("Scenario #7:");
+# fRc = fRc and oSession.saveSettings()
+# if fRc is False:
+# reporter.log('Couldn\'t save machine settings')
+#
+
+ assert fRc is True
+ except:
+ reporter.errorXcpt()
+
+ return fRc;
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvMoveVm1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdPython1.py b/src/VBox/ValidationKit/tests/api/tdPython1.py
new file mode 100755
index 00000000..dd9dffc7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdPython1.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdPython1.py $
+
+"""
+VirtualBox Validation Kit - Python Bindings Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import time
+import threading
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+
+
+class SubTstDrvPython1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Python Bindings Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'python-binding', 'Python bindings');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testEventQueueWaiting() \
+ and self.testEventQueueInterrupt();
+
+ #
+ # Test execution helpers.
+ #
+
+ def testEventQueueWaitingThreadProc(self):
+ """ Thread procedure for checking that waitForEvents fails when not called by the main thread. """
+ try:
+ rc2 = self.oTstDrv.oVBoxMgr.waitForEvents(0);
+ except:
+ return True;
+ reporter.error('waitForEvents() returned "%s" when called on a worker thread, expected exception.' % (rc2,));
+ return False;
+
+ def testEventQueueWaiting(self):
+ """
+ Test event queue waiting.
+ """
+ reporter.testStart('waitForEvents');
+
+ # Check return values and such.
+ for cMsTimeout in (0, 1, 2, 3, 256, 1000, 0):
+ iLoop = 0;
+ while True:
+ try:
+ rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout);
+ except:
+ reporter.errorXcpt();
+ break;
+ if not isinstance(rc, int):
+ reporter.error('waitForEvents returns non-integer type');
+ break;
+ if rc == 1:
+ break;
+ if rc != 0:
+ reporter.error('waitForEvents returns "%s", expected 0 or 1' % (rc,));
+ break;
+ iLoop += 1;
+ if iLoop > 10240:
+ reporter.error('waitForEvents returns 0 (success) %u times. '
+ 'Expected 1 (timeout/interrupt) after a call or two.'
+ % (iLoop,));
+ break;
+ if reporter.testErrorCount() != 0:
+ break;
+
+ # Check that we get an exception when trying to call the method from
+ # a different thread.
+ reporter.log('If running a debug build, you will see an ignored assertion now. Please ignore it.')
+ sVBoxAssertSaved = os.environ.get('VBOX_ASSERT', 'breakpoint');
+ os.environ['VBOX_ASSERT'] = 'ignore';
+ oThread = threading.Thread(target=self.testEventQueueWaitingThreadProc);
+ oThread.start();
+ oThread.join();
+ os.environ['VBOX_ASSERT'] = sVBoxAssertSaved;
+
+ return reporter.testDone()[1] == 0;
+
+ def interruptWaitEventsThreadProc(self):
+ """ Thread procedure that's used for waking up the main thread. """
+ time.sleep(2);
+ try:
+ rc2 = self.oTstDrv.oVBoxMgr.interruptWaitEvents();
+ except:
+ reporter.errorXcpt();
+ else:
+ if rc2 is True:
+ return True;
+ reporter.error('interruptWaitEvents returned "%s" when called from other thread, expected True' % (rc2,));
+ return False;
+
+ def testEventQueueInterrupt(self):
+ """
+ Test interrupting an event queue wait.
+ """
+ reporter.testStart('interruptWait');
+
+ # interrupt ourselves first and check the return value.
+ for i in range(0, 10):
+ try:
+ rc = self.oTstDrv.oVBoxMgr.interruptWaitEvents();
+ except:
+ reporter.errorXcpt();
+ break;
+ if rc is not True:
+ reporter.error('interruptWaitEvents returned "%s" expected True' % (rc,));
+ break
+
+ if reporter.testErrorCount() == 0:
+ #
+ # Interrupt a waitForEvents call.
+ #
+ # This test ASSUMES that no other events are posted to the thread's
+ # event queue once we've drained it. Also ASSUMES the box is
+ # relatively fast and not too busy because we're timing sensitive.
+ #
+ for i in range(0, 4):
+ # Try quiesce the event queue.
+ for _ in range(1, 100):
+ self.oTstDrv.oVBoxMgr.waitForEvents(0);
+
+ # Create a thread that will interrupt us in 2 seconds.
+ try:
+ oThread = threading.Thread(target=self.interruptWaitEventsThreadProc);
+ oThread.setDaemon(False); # pylint: disable=deprecated-method
+ except:
+ reporter.errorXcpt();
+ break;
+
+ cMsTimeout = 20000;
+ if i == 2:
+ cMsTimeout = -1;
+ elif i == 3:
+ cMsTimeout = -999999;
+
+ # Do the wait.
+ oThread.start();
+ msNow = base.timestampMilli();
+ try:
+ rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout);
+ except:
+ reporter.errorXcpt();
+ else:
+ msElapsed = base.timestampMilli() - msNow;
+
+ # Check the return code and elapsed time.
+ if not isinstance(rc, int):
+ reporter.error('waitForEvents returns non-integer type after %u ms, expected 1' % (msElapsed,));
+ elif rc != 1:
+ reporter.error('waitForEvents returned "%s" after %u ms, expected 1' % (rc, msElapsed));
+ if msElapsed > 15000:
+ reporter.error('waitForEvents after %u ms, expected just above 2-3 seconds' % (msElapsed,));
+ elif msElapsed < 100:
+ reporter.error('waitForEvents after %u ms, expected more than 100 ms.' % (msElapsed,));
+
+ oThread.join();
+ oThread = None;
+ if reporter.testErrorCount() != 0:
+ break;
+ reporter.log('Iteration %u was successful...' % (i + 1,));
+ return reporter.testDone()[1] == 0;
+
+
+if __name__ == '__main__':
+ from tests.api.tdApi1 import tdApi1;
+ sys.exit(tdApi1([SubTstDrvPython1]).main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py
new file mode 100755
index 00000000..e69a7d17
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdTreeDepth1.py $
+
+"""
+VirtualBox Validation Kit - Medium and Snapshot Tree Depth Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 156148 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import random
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class SubTstDrvTreeDepth1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Medium and Snapshot Tree Depth Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, oTstDrv, 'tree-depth', 'Media and Snapshot tree depths');
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testMediumTreeDepth() \
+ and self.testSnapshotTreeDepth()
+
+ #
+ # Test execution helpers.
+ #
+
+ def testMediumTreeDepth(self):
+ """
+ Test medium tree depth.
+ """
+ reporter.testStart('mediumTreeDepth')
+
+ try:
+ oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
+ oVM = self.oTstDrv.createTestVM('test-medium', 1, None, 4)
+ assert oVM is not None
+
+ # create chain with up to 64 disk images (medium tree depth limit)
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ cImages = random.randrange(1, 64);
+ reporter.log('Creating chain with %d disk images' % (cImages))
+ for i in range(1, cImages + 1):
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi')
+ if i == 1:
+ oHd = oSession.createBaseHd(sHddPath, cb=1024*1024)
+ else:
+ oHd = oSession.createDiffHd(oHd, sHddPath)
+ if oHd is None:
+ fRc = False
+ break
+
+ # modify the VM config, attach HDD
+ fRc = fRc and oSession.attachHd(sHddPath, sController='SATA Controller', fImmutable=False, fForceResource=False)
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc
+ ## @todo r=klaus: count known hard disk images, should be cImages
+
+ # unregister, making sure the images are closed
+ sSettingsFile = oVM.settingsFilePath
+ fDetachAll = random.choice([False, True])
+ if fDetachAll:
+ reporter.log('unregistering VM, DetachAll style')
+ else:
+ reporter.log('unregistering VM, UnregisterOnly style')
+ self.oTstDrv.forgetTestMachine(oVM)
+ if fDetachAll:
+ aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly)
+ for oHD in aoHDs:
+ oHD.close()
+ aoHDs = None
+ else:
+ oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
+ oVM = None
+
+ # If there is no base image (expected) then there are no leftover
+ # child images either. Can be changed later once the todos above
+ # and below are resolved.
+ cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
+ reporter.log('API reports %i base images' % (cBaseImages))
+ fRc = fRc and cBaseImages == 0
+ if cBaseImages != 0:
+ reporter.error('Got %d initial base images, expected %d' % (cBaseImages, 0));
+
+ # re-register to test loading of settings
+ reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
+ if self.oTstDrv.fpApiVer >= 7.0:
+ # Needs a password parameter since 7.0.
+ oVM = oVBox.openMachine(sSettingsFile, "")
+ else:
+ oVM = oVBox.openMachine(sSettingsFile)
+ oVBox.registerMachine(oVM);
+ ## @todo r=klaus: count known hard disk images, should be cImages
+
+ reporter.log('unregistering VM')
+ oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
+ oVM = None
+
+ cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
+ reporter.log('API reports %i base images' % (cBaseImages))
+ fRc = fRc and cBaseImages == 0
+ if cBaseImages != 0:
+ reporter.error('Got %d base images after unregistering, expected %d' % (cBaseImages, 0));
+
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+ def testSnapshotTreeDepth(self):
+ """
+ Test snapshot tree depth.
+ """
+ reporter.testStart('snapshotTreeDepth')
+
+ try:
+ oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
+ oVM = self.oTstDrv.createTestVM('test-snap', 1, None, 4)
+ assert oVM is not None
+
+ # modify the VM config, create and attach empty HDD
+ oSession = self.oTstDrv.openSession(oVM)
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'TestSnapEmpty.vdi')
+ fRc = True
+ fRc = fRc and oSession.createAndAttachHd(sHddPath, cb=1024*1024, sController='SATA Controller', fImmutable=False)
+ fRc = fRc and oSession.saveSettings()
+
+ # take up to 200 snapshots (250 is the snapshot tree depth limit (settings.h:SETTINGS_SNAPSHOT_DEPTH_MAX))
+ cSnapshots = random.randrange(1, 200);
+ reporter.log('Taking %d snapshots' % (cSnapshots))
+ for i in range(1, cSnapshots + 1):
+ fRc = fRc and oSession.takeSnapshot('Snapshot ' + str(i))
+ fRc = oSession.close() and fRc
+ oSession = None
+ reporter.log('API reports %i snapshots' % (oVM.snapshotCount))
+ fRc = fRc and oVM.snapshotCount == cSnapshots
+ if oVM.snapshotCount != cSnapshots:
+ reporter.error('Got %d initial snapshots, expected %d' % (oVM.snapshotCount, cSnapshots));
+
+ # unregister, making sure the images are closed
+ sSettingsFile = oVM.settingsFilePath
+ fDetachAll = random.choice([False, True])
+ if fDetachAll:
+ reporter.log('unregistering VM, DetachAll style')
+ else:
+ reporter.log('unregistering VM, UnregisterOnly style')
+ self.oTstDrv.forgetTestMachine(oVM)
+ if fDetachAll:
+ aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly)
+ for oHD in aoHDs:
+ oHD.close()
+ aoHDs = None
+ else:
+ oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
+ oVM = None
+
+ # If there is no base image (expected) then there are no leftover
+ # child images either. Can be changed later once the todos above
+ # and below are resolved.
+ cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
+ reporter.log('API reports %i base images' % (cBaseImages))
+ fRc = fRc and cBaseImages == 0
+ if cBaseImages != 0:
+ reporter.error('Got %d initial base images, expected %d' % (cBaseImages, 0));
+
+ # re-register to test loading of settings
+ reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
+ if self.oTstDrv.fpApiVer >= 7.0:
+ # Needs a password parameter since 7.0.
+ oVM = oVBox.openMachine(sSettingsFile, "")
+ else:
+ oVM = oVBox.openMachine(sSettingsFile)
+ oVBox.registerMachine(oVM);
+ reporter.log('API reports %i snapshots' % (oVM.snapshotCount))
+ fRc = fRc and oVM.snapshotCount == cSnapshots
+ if oVM.snapshotCount != cSnapshots:
+ reporter.error('Got %d snapshots after re-registering, expected %d' % (oVM.snapshotCount, cSnapshots));
+
+ reporter.log('unregistering VM')
+ oVM.unregister(vboxcon.CleanupMode_UnregisterOnly)
+ oVM = None
+
+ cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
+ reporter.log('API reports %i base images' % (cBaseImages))
+ fRc = fRc and cBaseImages == 0
+ if cBaseImages != 0:
+ reporter.error('Got %d base images after unregistering, expected %d' % (cBaseImages, 0));
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvTreeDepth1]).main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/audio/Makefile.kmk b/src/VBox/ValidationKit/tests/audio/Makefile.kmk
new file mode 100644
index 00000000..977cf454
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/audio/Makefile.kmk
@@ -0,0 +1,50 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Audio tests.
+#
+
+#
+# Copyright (C) 2021-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsAudio
+ValidationKitTestsAudio_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsAudio_INST = $(INST_VALIDATIONKIT)tests/audio/
+ValidationKitTestsAudio_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdAudioTest.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsAudio_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/audio/tdAudioTest.py b/src/VBox/ValidationKit/tests/audio/tdAudioTest.py
new file mode 100755
index 00000000..67a1bcb5
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/audio/tdAudioTest.py
@@ -0,0 +1,823 @@
+# -*- coding: utf-8 -*-
+# $Id: tdAudioTest.py $
+
+"""
+AudioTest test driver which invokes the VKAT (Validation Kit Audio Test)
+binary to perform the actual audio tests.
+
+The generated test set archive on the guest will be downloaded by TXS
+to the host for later audio comparison / verification.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2021-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+from datetime import datetime
+import os
+import sys
+import subprocess
+import time
+import threading
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter
+from testdriver import base
+from testdriver import vbox
+from testdriver import vboxcon;
+from testdriver import vboxtestvms
+from common import utils;
+
+# pylint: disable=unnecessary-semicolon
+
+class tdAudioTest(vbox.TestDriver):
+ """
+ Runs various audio tests.
+ """
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+ self.asGstVkatPaths = [
+ # Debugging stuff (SCP'd over to the guest).
+ '/tmp/vkat',
+ '/tmp/VBoxAudioTest',
+ 'C:\\Temp\\vkat',
+ 'C:\\Temp\\VBoxAudioTest',
+ # Validation Kit .ISO.
+ '${CDROM}/vboxvalidationkit/${OS/ARCH}/vkat${EXESUFF}',
+ '${CDROM}/${OS/ARCH}/vkat${EXESUFF}',
+ # Test VMs.
+ '/opt/apps/vkat',
+ '/opt/apps/VBoxAudioTest',
+ '/apps/vkat',
+ '/apps/VBoxAudioTest',
+ 'C:\\Apps\\vkat${EXESUFF}',
+ 'C:\\Apps\\VBoxAudioTest${EXESUFF}',
+ ## @todo VBoxAudioTest on Guest Additions?
+ ];
+ self.asTestsDef = [
+ 'guest_tone_playback', 'guest_tone_recording'
+ ];
+ self.asTests = self.asTestsDef;
+
+ # Optional arguments passing to VKAT when doing the actual audio tests.
+ self.asVkatTestArgs = [];
+ # Optional arguments passing to VKAT when verifying audio test sets.
+ self.asVkatVerifyArgs = [];
+
+ # Exit code of last host process execution, shared between exeuction thread and main thread.
+ # This ASSUMES that we only have one thread running at a time. Rather hacky, but does the job for now.
+ self.iThreadHstProcRc = 0;
+
+ # Enable audio debug mode.
+ #
+ # This is needed in order to load and use the Validation Kit audio driver,
+ # which in turn is being used in conjunction with the guest side to record
+ # output (guest is playing back) and injecting input (guest is recording).
+ self.asOptExtraData = [
+ 'VBoxInternal2/Audio/Debug/Enabled:true',
+ ];
+
+ # Name of the running VM to use for running the test driver. Optional, and None if not being used.
+ self.sRunningVmName = None;
+
+ # Audio controller type to use.
+ # If set to None, the OS' recommended controller type will be used (defined by Main).
+ self.sAudioControllerType = None;
+
+ def showUsage(self):
+ """
+ Shows the audio test driver-specific command line options.
+ """
+ fRc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAudioTest Options:');
+ reporter.log(' --runningvmname <vmname>');
+ reporter.log(' --audio-tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --audio-controller-type <HDA|AC97|SB16>');
+ reporter.log(' Default: recommended controller');
+ reporter.log(' --audio-test-count <number>');
+ reporter.log(' Default: 0 (means random)');
+ reporter.log(' --audio-test-tone-duration <ms>');
+ reporter.log(' Default: 0 (means random)');
+ reporter.log(' --audio-verify-max-diff-count <number>');
+ reporter.log(' Default: 0 (strict)');
+ reporter.log(' --audio-verify-max-diff-percent <0-100>');
+ reporter.log(' Default: 0 (strict)');
+ reporter.log(' --audio-verify-max-size-percent <0-100>');
+ reporter.log(' Default: 0 (strict)');
+ return fRc;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parses the audio test driver-specific command line options.
+ """
+ if asArgs[iArg] == '--runningvmname':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--runningvmname" needs VM name');
+
+ self.sRunningVmName = asArgs[iArg];
+ elif asArgs[iArg] == '--audio-tests':
+ iArg += 1;
+ if asArgs[iArg] == 'all': # Nice for debugging scripts.
+ self.asTests = self.asTestsDef;
+ else:
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--audio-tests" value "%s" is not valid; valid values are: %s'
+ % (s, ' '.join(self.asTestsDef)));
+ elif asArgs[iArg] == '--audio-controller-type':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ if asArgs[iArg] == 'HDA' \
+ or asArgs[iArg] == 'AC97' \
+ or asArgs[iArg] == 'SB16':
+ self.sAudioControllerType = asArgs[iArg];
+ else:
+ raise base.InvalidOption('The "--audio-controller-type" value "%s" is not valid' % (asArgs[iArg]));
+ elif asArgs[iArg] == '--audio-test-count' \
+ or asArgs[iArg] == '--audio-test-tone-duration':
+ # Strip the "--audio-test-" prefix and keep the options as defined in VKAT,
+ # e.g. "--audio-test-count" -> "--count". That way we don't
+ # need to do any special argument translation and whatnot.
+ self.asVkatTestArgs.extend(['--' + asArgs[iArg][len('--audio-test-'):]]);
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ self.asVkatTestArgs.extend([asArgs[iArg]]);
+ elif asArgs[iArg] == '--audio-verify-max-diff-count' \
+ or asArgs[iArg] == '--audio-verify-max-diff-percent' \
+ or asArgs[iArg] == '--audio-verify-max-size-percent':
+ # Strip the "--audio-verify-" prefix and keep the options as defined in VKAT,
+ # e.g. "--audio-verify-max-diff-count" -> "--max-diff-count". That way we don't
+ # need to do any special argument translation and whatnot.
+ self.asVkatVerifyArgs.extend(['--' + asArgs[iArg][len('--audio-verify-'):]]);
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ self.asVkatVerifyArgs.extend([asArgs[iArg]]);
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionVerify(self):
+ """
+ Verifies the test driver before running.
+ """
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ """
+ Configures the test driver before running.
+ """
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+
+ # Make sure that the Validation Kit .ISO is mounted
+ # to find the VKAT (Validation Kit Audio Test) binary on it.
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Executes the test driver.
+ """
+
+ # Disable maximum logging line restrictions per group.
+ # This comes in handy when running this test driver in a (very) verbose mode, e.g. for debugging.
+ os.environ['VBOX_LOG_MAX_PER_GROUP'] = '0';
+ os.environ['VBOX_RELEASE_LOG_MAX_PER_GROUP'] = '0';
+ os.environ['VKAT_RELEASE_LOG_MAX_PER_GROUP'] = '0';
+
+ if self.sRunningVmName is None:
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig);
+ return self.actionExecuteOnRunnigVM();
+
+ def actionExecuteOnRunnigVM(self):
+ """
+ Executes the tests in an already configured + running VM.
+ """
+ if not self.importVBoxApi():
+ return False;
+
+ fRc = True;
+
+ oVM = None;
+ oVirtualBox = None;
+
+ oVirtualBox = self.oVBoxMgr.getVirtualBox();
+ try:
+ oVM = oVirtualBox.findMachine(self.sRunningVmName);
+ if oVM.state != self.oVBoxMgr.constants.MachineState_Running:
+ reporter.error("Machine '%s' is not in Running state (state is %d)" % (self.sRunningVmName, oVM.state));
+ fRc = False;
+ except:
+ reporter.errorXcpt("Machine '%s' not found" % (self.sRunningVmName));
+ fRc = False;
+
+ if fRc:
+ oSession = self.openSession(oVM);
+ if oSession:
+ # Tweak this to your likings.
+ oTestVm = vboxtestvms.TestVm('runningvm', sKind = 'WindowsXP'); #sKind = 'WindowsXP' # sKind = 'Ubuntu_64'
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 30 * 1000);
+ if fRc:
+ self.doTest(oTestVm, oSession, oTxsSession);
+ else:
+ reporter.error("Unable to open session for machine '%s'" % (self.sRunningVmName));
+ fRc = False;
+
+ if oVM:
+ del oVM;
+ if oVirtualBox:
+ del oVirtualBox;
+ return fRc;
+
+ def getGstVkatLogFilePath(self, oTestVm):
+ """
+ Returns the log file path of VKAT running on the guest (daemonized).
+ """
+ return oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'vkat-guest.log');
+
+ def locateGstBinary(self, oSession, oTxsSession, asPaths):
+ """
+ Locates a guest binary on the guest by checking the paths in \a asPaths.
+ """
+ for sCurPath in asPaths:
+ reporter.log2('Checking for \"%s\" ...' % (sCurPath));
+ if self.txsIsFile(oSession, oTxsSession, sCurPath, fIgnoreErrors = True):
+ return (True, sCurPath);
+ reporter.error('Unable to find guest binary in any of these places:\n%s' % ('\n'.join(asPaths),));
+ return (False, "");
+
+ def executeHstLoop(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
+ """
+ Inner loop which handles the execution of a host binary.
+
+ Might be called synchronously in main thread or via the thread exeuction helper (asynchronous).
+ """
+ fRc = False;
+
+ asEnvTmp = os.environ.copy();
+ if asEnv:
+ for sEnv in asEnv:
+ sKey, sValue = sEnv.split('=');
+ reporter.log2('Setting env var \"%s\" -> \"%s\"' % (sKey, sValue));
+ os.environ[sKey] = sValue; # Also apply it to the current environment.
+ asEnvTmp[sKey] = sValue;
+
+ try:
+ # Spawn process.
+ if fAsAdmin \
+ and utils.getHostOs() != 'win':
+ oProcess = utils.sudoProcessStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
+ else:
+ oProcess = utils.processStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
+
+ if not oProcess:
+ reporter.error('Starting process for "%s" failed!' % (sWhat));
+ return False;
+
+ iPid = oProcess.pid;
+ self.pidFileAdd(iPid, sWhat);
+
+ iRc = 0;
+
+ # For Python 3.x we provide "real-time" output.
+ if sys.version_info[0] >= 3:
+ while oProcess.stdout.readable(): # pylint: disable=no-member
+ sStdOut = oProcess.stdout.readline();
+ if sStdOut:
+ sStdOut = sStdOut.strip();
+ reporter.log('%s: %s' % (sWhat, sStdOut));
+ iRc = oProcess.poll();
+ if iRc is not None:
+ break;
+ else:
+ # For Python 2.x it's too much hassle to set the file descriptor options (O_NONBLOCK) and stuff,
+ # so just use communicate() here and dump everythiong all at once when finished.
+ sStdOut = oProcess.communicate();
+ if sStdOut:
+ reporter.log('%s: %s' % (sWhat, sStdOut));
+ iRc = oProcess.poll();
+
+ if iRc == 0:
+ reporter.log('*** %s: exit code %d' % (sWhat, iRc));
+ fRc = True;
+ else:
+ reporter.log('!*! %s: exit code %d' % (sWhat, iRc));
+
+ self.pidFileRemove(iPid);
+
+ # Save thread result code.
+ self.iThreadHstProcRc = iRc;
+
+ except:
+ reporter.logXcpt('Executing "%s" failed!' % (sWhat));
+
+ return fRc;
+
+ def executeHstThread(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
+ """
+ Thread execution helper to run a process on the host.
+ """
+ fRc = self.executeHstLoop(sWhat, asArgs, asEnv, fAsAdmin);
+ if fRc:
+ reporter.log('Executing \"%s\" on host done' % (sWhat,));
+ else:
+ reporter.log('Executing \"%s\" on host failed' % (sWhat,));
+
+ def executeHst(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
+ """
+ Runs a binary (image) with optional admin (root) rights on the host and
+ waits until it terminates.
+
+ Windows currently is not supported yet running stuff as Administrator.
+
+ Returns success status (exit code is 0).
+ """
+ reporter.log('Executing \"%s\" on host (as admin = %s)' % (sWhat, fAsAdmin));
+
+ try: sys.stdout.flush();
+ except: pass;
+ try: sys.stderr.flush();
+ except: pass;
+
+ # Initialize thread rc.
+ self.iThreadHstProcRc = -42;
+
+ try:
+ oThread = threading.Thread(target = self.executeHstThread, args = [ sWhat, asArgs, asEnv, fAsAdmin ]);
+ oThread.start();
+ while oThread.join(0.1):
+ if not oThread.is_alive():
+ break;
+ self.processEvents(0);
+ reporter.log2('Thread returned exit code for "%s": %d' % (sWhat, self.iThreadHstProcRc));
+ except:
+ reporter.logXcpt('Starting thread for "%s" failed' % (sWhat,));
+
+ return self.iThreadHstProcRc == 0;
+
+ def getWinFirewallArgsDisable(self, sOsType):
+ """
+ Returns the command line arguments for Windows OSes
+ to disable the built-in firewall (if any).
+
+ If not supported, returns an empty array.
+ """
+ if sOsType == 'vista': # pylint: disable=no-else-return
+ # Vista and up.
+ return (['netsh.exe', 'advfirewall', 'set', 'allprofiles', 'state', 'off']);
+ elif sOsType == 'xp': # Older stuff (XP / 2003).
+ return(['netsh.exe', 'firewall', 'set', 'opmode', 'mode=DISABLE']);
+ # Not supported / available.
+ return [];
+
+ def disableGstFirewall(self, oTestVm, oTxsSession):
+ """
+ Disables the firewall on a guest (if any).
+
+ Needs elevated / admin / root privileges.
+
+ Returns success status, not logged.
+ """
+ fRc = False;
+
+ asArgs = [];
+ sOsType = '';
+ if oTestVm.isWindows():
+ if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x']:
+ sOsType = 'nt3x'; # Not supported, but define it anyway.
+ elif oTestVm.sKind in ('Windows2000', 'WindowsXP', 'Windows2003'):
+ sOsType = 'xp';
+ else:
+ sOsType = 'vista';
+ asArgs = self.getWinFirewallArgsDisable(sOsType);
+ else:
+ sOsType = 'unsupported';
+
+ reporter.log('Disabling firewall on guest (type: %s) ...' % (sOsType,));
+
+ if asArgs:
+ fRc = self.txsRunTest(oTxsSession, 'Disabling guest firewall', 3 * 60 * 1000, \
+ oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), asArgs[0]), asArgs);
+ if not fRc:
+ reporter.error('Disabling firewall on guest returned exit code error %d' % (self.getLastRcFromTxs(oTxsSession)));
+ else:
+ reporter.log('Firewall not available on guest, skipping');
+ fRc = True; # Not available, just skip.
+
+ return fRc;
+
+ def disableHstFirewall(self):
+ """
+ Disables the firewall on the host (if any).
+
+ Needs elevated / admin / root privileges.
+
+ Returns success status, not logged.
+ """
+ fRc = False;
+
+ asArgs = [];
+ sOsType = sys.platform;
+
+ if sOsType == 'win32':
+ reporter.log('Disabling firewall on host (type: %s) ...' % (sOsType));
+
+ ## @todo For now we ASSUME that we don't run (and don't support even) on old(er)
+ # Windows hosts than Vista.
+ asArgs = self.getWinFirewallArgsDisable('vista');
+ if asArgs:
+ fRc = self.executeHst('Disabling host firewall', asArgs, fAsAdmin = True);
+ else:
+ reporter.log('Firewall not available on host, skipping');
+ fRc = True; # Not available, just skip.
+
+ return fRc;
+
+ def getLastRcFromTxs(self, oTxsSession):
+ """
+ Extracts the last exit code reported by TXS from a run before.
+ Assumes that nothing else has been run on the same TXS session in the meantime.
+ """
+ iRc = 0;
+ (_, sOpcode, abPayload) = oTxsSession.getLastReply();
+ if sOpcode.startswith('PROC NOK '): # Extract process rc
+ iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
+ return iRc;
+
+ def startVkatOnGuest(self, oTestVm, oSession, oTxsSession, sTag):
+ """
+ Starts VKAT on the guest (running in background).
+ """
+ sPathTemp = self.getGuestTempDir(oTestVm);
+ sPathAudioOut = oTestVm.pathJoin(sPathTemp, 'vkat-guest-out');
+ sPathAudioTemp = oTestVm.pathJoin(sPathTemp, 'vkat-guest-temp');
+
+ reporter.log('Guest audio test temp path is \"%s\"' % (sPathAudioOut));
+ reporter.log('Guest audio test output path is \"%s\"' % (sPathAudioTemp));
+ reporter.log('Guest audio test tag is \"%s\"' % (sTag));
+
+ fRc, sVkatExe = self.locateGstBinary(oSession, oTxsSession, self.asGstVkatPaths);
+ if fRc:
+ reporter.log('Using VKAT on guest at \"%s\"' % (sVkatExe));
+
+ sCmd = '';
+ asArgs = [];
+
+ asArgsVkat = [ sVkatExe, 'test', '--mode', 'guest', '--probe-backends', \
+ '--tempdir', sPathAudioTemp, '--outdir', sPathAudioOut, \
+ '--tag', sTag ];
+
+ asArgs.extend(asArgsVkat);
+
+ for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
+ asArgs.extend([ '-v' ]);
+
+ # Needed for NATed VMs.
+ asArgs.extend(['--tcp-connect-addr', '10.0.2.2' ]);
+
+ if oTestVm.sKind in 'Oracle_64':
+ #
+ # Some Linux distros have a bug / are configured (?) so that processes started by init system
+ # cannot access the PulseAudio server ("Connection refused"), for example OL 8.1.
+ #
+ # To work around this, we use the (hopefully) configured user "vbox" and run it under its behalf,
+ # as the Test Execution Service (TxS) currently does not implement impersonation yet.
+ #
+ asSU = [ '/bin/su',
+ '/usr/bin/su',
+ '/usr/local/bin/su' ];
+ fRc, sCmd = self.locateGstBinary(oSession, oTxsSession, asSU);
+ if fRc:
+ sCmdArgs = '';
+ for sArg in asArgs:
+ sCmdArgs += sArg + " ";
+ asArgs = [ sCmd, oTestVm.getTestUser(), '-c', sCmdArgs ];
+ else:
+ reporter.log('Unable to find SU on guest, falling back to regular starting ...')
+
+ if not sCmd: # Just start it with the same privileges as TxS.
+ sCmd = sVkatExe;
+
+ reporter.log2('startVkatOnGuest: sCmd=%s' % (sCmd,));
+ reporter.log2('startVkatOnGuest: asArgs=%s' % (asArgs,));
+
+ #
+ # Add own environment stuff.
+ #
+ asEnv = [];
+
+ # Write the log file to some deterministic place so TxS can retrieve it later.
+ sVkatLogFile = 'VKAT_RELEASE_LOG_DEST=file=' + self.getGstVkatLogFilePath(oTestVm);
+ asEnv.extend([ sVkatLogFile ]);
+
+ #
+ # Execute asynchronously on the guest.
+ #
+ fRc = oTxsSession.asyncExec(sCmd, asArgs, asEnv, cMsTimeout = 15 * 60 * 1000, sPrefix = '[VKAT Guest] ');
+ if fRc:
+ self.addTask(oTxsSession);
+
+ if not fRc:
+ reporter.error('VKAT on guest returned exit code error %d' % (self.getLastRcFromTxs(oTxsSession)));
+ else:
+ reporter.error('VKAT on guest not found');
+
+ return fRc;
+
+ def runTests(self, oTestVm, oSession, oTxsSession, sDesc, sTag, asTests):
+ """
+ Runs one or more tests using VKAT on the host, which in turn will
+ communicate with VKAT running on the guest and the Validation Kit
+ audio driver ATS (Audio Testing Service).
+ """
+ _ = oTestVm, oSession, oTxsSession;
+
+ sPathTemp = self.sScratchPath;
+ sPathAudioOut = os.path.join(sPathTemp, 'vkat-host-out-%s' % (sTag));
+ sPathAudioTemp = os.path.join(sPathTemp, 'vkat-host-temp-%s' % (sTag));
+
+ reporter.log('Host audio test temp path is \"%s\"' % (sPathAudioOut));
+ reporter.log('Host audio test output path is \"%s\"' % (sPathAudioTemp));
+ reporter.log('Host audio test tag is \"%s\"' % (sTag));
+
+ reporter.testStart(sDesc);
+
+ sVkatExe = self.getBinTool('vkat');
+
+ reporter.log('Using VKAT on host at: \"%s\"' % (sVkatExe));
+
+ # Build the base command line, exclude all tests by default.
+ asArgs = [ sVkatExe, 'test', '--mode', 'host', '--probe-backends',
+ '--tempdir', sPathAudioTemp, '--outdir', sPathAudioOut, '-a',
+ '--tag', sTag,
+ '--no-audio-ok', # Enables running on hosts which do not have any audio hardware.
+ '--no-verify' ]; # We do the verification separately in the step below.
+
+ for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
+ asArgs.extend([ '-v' ]);
+
+ if self.asVkatTestArgs:
+ asArgs += self.asVkatTestArgs;
+
+ # ... and extend it with wanted tests.
+ asArgs.extend(asTests);
+
+ #
+ # Let VKAT on the host run synchronously.
+ #
+ fRc = self.executeHst("VKAT Host", asArgs);
+
+ reporter.testDone();
+
+ if fRc:
+ #
+ # When running the test(s) above were successful, do the verification step next.
+ # This gives us a bit more fine-grained test results in the test manager.
+ #
+ reporter.testStart('Verifying audio data');
+
+ sNameSetHst = '%s-host.tar.gz' % (sTag);
+ sPathSetHst = os.path.join(sPathAudioOut, sNameSetHst);
+ sNameSetGst = '%s-guest.tar.gz' % (sTag);
+ sPathSetGst = os.path.join(sPathAudioOut, sNameSetGst);
+
+ asArgs = [ sVkatExe, 'verify', sPathSetHst, sPathSetGst ];
+
+ for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
+ asArgs.extend([ '-v' ]);
+
+ if self.asVkatVerifyArgs:
+ asArgs += self.asVkatVerifyArgs;
+
+ fRc = self.executeHst("VKAT Host Verify", asArgs);
+ if fRc:
+ reporter.log("Verification audio data successful");
+ else:
+ #
+ # Add the test sets to the test manager for later (manual) diagnosis.
+ #
+ reporter.addLogFile(sPathSetGst, 'misc/other', 'Guest audio test set');
+ reporter.addLogFile(sPathSetHst, 'misc/other', 'Host audio test set');
+
+ reporter.error("Verification of audio data failed");
+
+ reporter.testDone();
+
+ return fRc;
+
+ def doTest(self, oTestVm, oSession, oTxsSession):
+ """
+ Executes the specified audio tests.
+ """
+
+ # Disable any OS-specific firewalls preventing VKAT / ATS to run.
+ fRc = self.disableHstFirewall();
+ fRc = self.disableGstFirewall(oTestVm, oTxsSession) and fRc;
+
+ if not fRc:
+ return False;
+
+ reporter.log("Active tests: %s" % (self.asTests,));
+
+ # Define a tag for the whole run.
+ sTag = oTestVm.sVmName + "_" + datetime.now().strftime("%Y%m%d_%H%M%S");
+
+ fRc = self.startVkatOnGuest(oTestVm, oSession, oTxsSession, sTag);
+ if fRc:
+ #
+ # Execute the tests using VKAT on the guest side (in guest mode).
+ #
+ if "guest_tone_playback" in self.asTests:
+ fRc = self.runTests(oTestVm, oSession, oTxsSession, \
+ 'Guest audio playback', sTag + "_test_playback", \
+ asTests = [ '-i0' ]);
+ if "guest_tone_recording" in self.asTests:
+ fRc = fRc and self.runTests(oTestVm, oSession, oTxsSession, \
+ 'Guest audio recording', sTag + "_test_recording", \
+ asTests = [ '-i1' ]);
+
+ # Cancel guest VKAT execution task summoned by startVkatOnGuest().
+ oTxsSession.cancelTask();
+
+ #
+ # Retrieve log files for diagnosis.
+ #
+ self.txsDownloadFiles(oSession, oTxsSession,
+ [ ( self.getGstVkatLogFilePath(oTestVm),
+ 'vkat-guest-%s.log' % (oTestVm.sVmName,),),
+ ],
+ fIgnoreErrors = True);
+
+ # A bit of diagnosis on error.
+ ## @todo Remove this later when stuff runs stable.
+ if not fRc:
+ reporter.log('Kernel messages:');
+ sCmdDmesg = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'dmesg');
+ oTxsSession.syncExec(sCmdDmesg, (sCmdDmesg), fIgnoreErrors = True);
+ reporter.log('Loaded kernel modules:');
+ sCmdLsMod = oTestVm.pathJoin(self.getGuestSystemAdminDir(oTestVm), 'lsmod');
+ oTxsSession.syncExec(sCmdLsMod, (sCmdLsMod), fIgnoreErrors = True);
+
+ return fRc;
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs tests using one specific VM config.
+ """
+
+ self.logVmInfo(oVM);
+
+ reporter.testStart("Audio Testing");
+
+ fSkip = False;
+
+ if oTestVm.isWindows() \
+ and oTestVm.sKind in ('WindowsNT4', 'Windows2000'): # Too old for DirectSound and WASAPI backends.
+ reporter.log('Audio testing skipped, not implemented/available for that OS yet.');
+ fSkip = True;
+
+ if not fSkip \
+ and self.fpApiVer < 7.0:
+ reporter.log('Audio testing for non-trunk builds skipped.');
+ fSkip = True;
+
+ if not fSkip:
+ sVkatExe = self.getBinTool('vkat');
+ asArgs = [ sVkatExe, 'enum', '--probe-backends' ];
+ for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
+ asArgs.extend([ '-v' ]);
+ fRc = self.executeHst("VKAT Host Audio Probing", asArgs);
+ if not fRc:
+ # Not fatal, as VBox then should fall back to the NULL audio backend (also worth having as a test case).
+ reporter.log('Warning: Backend probing on host failed, no audio available (pure server installation?)');
+
+ if fSkip:
+ reporter.testDone(fSkipped = True);
+ return True;
+
+ # Reconfigure the VM.
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+
+ cVerbosity = reporter.getVerbosity();
+ if cVerbosity >= 2: # Explicitly set verbosity via extra-data when >= level 2.
+ self.asOptExtraData.extend([ 'VBoxInternal2/Audio/Debug/Level:' + str(cVerbosity) ]);
+
+ # Set extra data.
+ for sExtraData in self.asOptExtraData:
+ sKey, sValue = sExtraData.split(':');
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue));
+ fRc = oSession.setExtraData(sKey, sValue) and fRc;
+
+ # Make sure that the VM's audio adapter is configured the way we need it to.
+ if self.fpApiVer >= 4.0:
+ enmAudioControllerType = None;
+ reporter.log('Configuring audio controller type ...');
+ if self.sAudioControllerType is None:
+ oOsType = oSession.getOsType();
+ enmAudioControllerType = oOsType.recommendedAudioController;
+ else:
+ if self.sAudioControllerType == 'HDA':
+ enmAudioControllerType = vboxcon.AudioControllerType_HDA;
+ elif self.sAudioControllerType == 'AC97':
+ enmAudioControllerType = vboxcon.AudioControllerType_AC97;
+ elif self.sAudioControllerType == 'SB16':
+ enmAudioControllerType = vboxcon.AudioControllerType_SB16;
+ assert enmAudioControllerType is not None;
+
+ # For now we're encforcing to test the HDA emulation only, regardless of
+ # what the recommended audio controller type from above was.
+ ## @todo Make other emulations work as well.
+ fEncforceHDA = True;
+
+ if fEncforceHDA:
+ enmAudioControllerType = vboxcon.AudioControllerType_HDA;
+ reporter.log('Enforcing audio controller type to HDA');
+
+ reporter.log('Setting user-defined audio controller type to %d' % (enmAudioControllerType));
+ oSession.setupAudio(enmAudioControllerType,
+ fEnable = True, fEnableIn = True, fEnableOut = True);
+
+ # Save the settings.
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc;
+
+ reporter.testStart('Waiting for TXS');
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
+ fCdWait = True,
+ cMsTimeout = 3 * 60 * 1000,
+ sFileCdWait = '${OS/ARCH}/vkat${EXESUFF}');
+ reporter.testDone();
+
+ reporter.log('Waiting for any OS startup sounds getting played (to skip those) ...');
+ time.sleep(5);
+
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ fRc = self.doTest(oTestVm, oSession, oTxsSession);
+
+ # Cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+
+ reporter.testDone();
+ return fRc;
+
+ def onExit(self, iRc):
+ """
+ Exit handler for this test driver.
+ """
+ return vbox.TestDriver.onExit(self, iRc);
+
+if __name__ == '__main__':
+ sys.exit(tdAudioTest().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py b/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py
new file mode 100755
index 00000000..55502b16
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+# $Id: tdGuestHostTimings.py $
+
+"""
+????????
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+import os
+import sys
+import time
+import subprocess
+import re
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter
+from testdriver import base
+from testdriver import vbox
+from testdriver import vboxcon
+from testdriver import vboxtestvms
+
+class tdGuestHostTimings(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.sSessionTypeDef = 'gui';
+
+ self.oTestVmSet = self.oTestVmManager.getStandardVmSet('nat') ## ???
+
+ # Use the command line "--test-vms mw7x64 execute" to run the only "mw7x64" VM
+ oTestVm = vboxtestvms.TestVm('mw7x64', oSet = self.oTestVmSet, sHd = 'mw7x64.vdi',
+ sKind = 'Windows7', acCpusSup = range(1, 2), fIoApic = True, sFirmwareType = 'bios',
+ asParavirtModesSup = ['hyperv'], asVirtModesSup = ['hwvirt-np'],
+ sHddControllerType = 'SATA Controller');
+
+ self.oTestVmSet.aoTestVms.append(oTestVm);
+
+ self.sVMname = None
+
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdGuestHostTimings Options:');
+ reporter.log(' --runningvmname <vmname>');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--runningvmname':
+ iArg += 1
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "----runningvmname" needs VM name')
+
+ self.sVMname = asArgs[iArg]
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+ return iArg + 1
+
+ def actionConfig(self):
+ return True
+
+ def actionExecute(self):
+ #self.sTempPathHost = os.environ.get("IPRT_TMPDIR")
+ self.sTempPathHost = os.path.normpath(os.environ.get("TEMP") + "/VBoxAudioValKit")
+
+ if self.sVMname is None:
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+ else:
+ return self.actionExecuteOnRunnigVM()
+
+ def doTest(self, oSession):
+ oConsole = oSession.console
+ oGuest = oConsole.guest
+
+ sOSTypeId = oGuest.OSTypeId.lower()
+ if sOSTypeId.find("win") == -1 :
+ reporter.log("Only Windows guests are currently supported")
+ reporter.testDone()
+ return True
+
+ oGuestSession = oGuest.createSession("Administrator", "password", "", "Audio Validation Kit")
+ guestSessionWaitResult = oGuestSession.waitFor(self.oVBoxMgr.constants.GuestSessionWaitResult_Start, 2000)
+ reporter.log("guestSessionWaitResult = %d" % guestSessionWaitResult)
+
+ for duration in range(3, 6):
+ reporter.testStart("Checking for duration of " + str(duration) + " seconds")
+ sPathToPlayer = "D:\\win\\" + ("amd64" if (sOSTypeId.find('_64') >= 0) else "x86") + "\\ntPlayToneWaveX.exe"
+ oProcess = oGuestSession.processCreate(sPathToPlayer, ["xxx0", "--total-duration-in-secs", str(duration)], [], [], 0)
+ processWaitResult = oProcess.waitFor(self.oVBoxMgr.constants.ProcessWaitForFlag_Start, 1000)
+ reporter.log("Started: pid %d, waitResult %d" % (oProcess.PID, processWaitResult))
+
+ processWaitResult = oProcess.waitFor(self.oVBoxMgr.constants.ProcessWaitForFlag_Terminate, 2 * duration * 1000)
+ reporter.log("Terminated: pid %d, waitResult %d" % (oProcess.PID, processWaitResult))
+ time.sleep(1) # Give audio backend sometime to save a stream to .wav file
+
+ absFileName = self.seekLatestAudioFileName(oGuestSession, duration)
+
+ if absFileName is None:
+ reporter.testFailure("Unable to find audio file")
+ continue
+
+ reporter.log("Checking audio file '" + absFileName + "'")
+
+ diff = self.checkGuestHostTimings(absFileName + ".timing")
+ if diff is not None:
+ if diff > 0.0: # Guest sends data quicker than a host can play
+ if diff > 0.01: # 1% is probably good threshold here
+ reporter.testFailure("Guest sends audio buffers too quickly")
+ else:
+ diff = -diff; # Much worse case: guest sends data very slow, host feels starvation
+ if diff > 0.005: # 0.5% is probably good threshold here
+ reporter.testFailure("Guest sends audio buffers too slowly")
+
+ reporter.testDone()
+ else:
+ reporter.testFailure("Unable to parse a file with timings")
+
+ oGuestSession.close()
+
+ del oGuest
+ del oConsole
+
+ return True
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ #self.logVmInfo(oVM)
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
+ fCdWait = True,
+ cMsTimeout = 60 * 1000)
+ if oSession is not None and oTxsSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest started. Connection to TXS service established.')
+ self.doTest(oSessionWrapper.o)
+
+ return True
+
+ def actionExecuteOnRunnigVM(self):
+ if not self.importVBoxApi():
+ return False;
+
+ oVirtualBox = self.oVBoxMgr.getVirtualBox()
+ oMachine = oVirtualBox.findMachine(self.sVMname)
+
+ if oMachine == None:
+ reporter.log("Machine '%s' is unknown" % (oMachine.name))
+ return False
+
+ if oMachine.state != self.oVBoxMgr.constants.MachineState_Running:
+ reporter.log("Machine '%s' is not Running" % (oMachine.name))
+ return False
+
+ oSession = self.oVBoxMgr.mgr.getSessionObject(oVirtualBox)
+ oMachine.lockMachine(oSession, self.oVBoxMgr.constants.LockType_Shared)
+
+ self.doTest(oSession);
+
+ oSession.unlockMachine()
+
+ del oSession
+ del oMachine
+ del oVirtualBox
+ return True
+
+ def seekLatestAudioFileName(self, guestSession, duration):
+
+ listOfFiles = os.listdir(self.sTempPathHost)
+ # Assuming that .wav files are named like 2016-11-15T12_08_27.669573100Z.wav by VBOX audio backend
+ # So that sorting by name = sorting by creation date
+ listOfFiles.sort(reverse = True)
+
+ for fileName in listOfFiles:
+ if not fileName.endswith(".wav"):
+ continue
+
+ absFileName = os.path.join(self.sTempPathHost, fileName)
+
+ # Ignore too small wav files (usually uncompleted audio streams)
+ statInfo = os.stat(absFileName)
+ if statInfo.st_size > 100:
+ return absFileName
+
+ return
+
+ def checkGuestHostTimings(self, absFileName):
+ with open(absFileName) as f:
+ for line_terminated in f:
+ line = line_terminated.rstrip('\n')
+
+ reporter.log("Last line is: " + line)
+ matchObj = re.match( r'(\d+) (\d+)', line, re.I)
+ if matchObj:
+ hostTime = int(matchObj.group(1))
+ guestTime = int(matchObj.group(2))
+
+ diff = float(guestTime - hostTime) / hostTime
+ return diff
+
+ return
+
+if __name__ == '__main__':
+ sys.exit(tdGuestHostTimings().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/autostart/Makefile.kmk b/src/VBox/ValidationKit/tests/autostart/Makefile.kmk
new file mode 100644
index 00000000..72793ef4
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/autostart/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Autostart.
+#
+
+#
+# Copyright (C) 2013-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsAutostart
+ValidationKitTestsAutostart_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsAutostart_INST = $(INST_VALIDATIONKIT)tests/autostart/
+ValidationKitTestsAutostart_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdAutostart1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsAutostart_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py b/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py
new file mode 100755
index 00000000..813c7eb7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py
@@ -0,0 +1,1443 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Autostart testcase using <please-tell-what-I-am-doing>.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2013-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Id: tdAutostart1.py $"
+
+# Standard Python imports.
+import os;
+import sys;
+import re;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+from testdriver import vboxwrappers;
+
+class VBoxManageStdOutWrapper(object):
+ """ Parser for VBoxManage list runningvms """
+
+ def __init__(self):
+ self.sVmRunning = '';
+
+ def __del__(self):
+ self.close();
+
+ def close(self):
+ """file.close"""
+ return;
+
+ def read(self, cb):
+ """file.read"""
+ _ = cb;
+ return "";
+
+ def write(self, sText):
+ """VBoxManage stdout write"""
+ if sText is None:
+ return None;
+ try: sText = str(sText); # pylint: disable=redefined-variable-type
+ except: pass;
+ asLines = sText.splitlines();
+ for sLine in asLines:
+ sLine = sLine.strip();
+ reporter.log('Logging: ' + sLine);
+ # Extract the value
+ idxVmNameStart = sLine.find('"');
+ if idxVmNameStart == -1:
+ raise Exception('VBoxManageStdOutWrapper: Invalid output');
+ idxVmNameStart += 1;
+ idxVmNameEnd = idxVmNameStart;
+ while sLine[idxVmNameEnd] != '"':
+ idxVmNameEnd += 1;
+ self.sVmRunning = sLine[idxVmNameStart:idxVmNameEnd];
+ reporter.log('Logging: ' + self.sVmRunning);
+ return None;
+
+class tdAutostartOs(vboxtestvms.BaseTestVm):
+ """
+ Base autostart helper class to provide common methods.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None):
+ vboxtestvms.BaseTestVm.__init__(self, sVmName, oSet = oSet, sKind = sKind);
+ self.oTstDrv = oTstDrv;
+ self.sHdd = sHdd;
+ self.eNic0Type = eNic0Type;
+ self.cMbRam = cMbRam;
+ self.cCpus = cCpus;
+ self.fPae = fPae;
+ self.sGuestAdditionsIso = sGuestAdditionsIso;
+ self.asTestBuildDirs = oTstDrv.asTestBuildDirs;
+ self.sVBoxInstaller = "";
+ self.asVirtModesSup = ['hwvirt-np',];
+ self.asParavirtModesSup = ['default',];
+
+ def _findFile(self, sRegExp, asTestBuildDirs):
+ """
+ Returns a filepath based on the given regex and paths to look into
+ or None if no matching file is found.
+ """
+ oRegExp = re.compile(sRegExp);
+ for sTestBuildDir in asTestBuildDirs:
+ try:
+ #return most recent file if there are several ones matching the pattern
+ asFiles = [s for s in os.listdir(sTestBuildDir)
+ if os.path.isfile(os.path.join(sTestBuildDir, s))];
+ asFiles = (s for s in asFiles
+ if oRegExp.match(os.path.basename(s))
+ and os.path.exists(sTestBuildDir + '/' + s));
+ asFiles = sorted(asFiles, reverse = True,
+ key = lambda s, sTstBuildDir = sTestBuildDir: os.path.getmtime(os.path.join(sTstBuildDir, s)));
+ if asFiles:
+ return sTestBuildDir + '/' + asFiles[0];
+ except:
+ pass;
+ reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, ','.join(asTestBuildDirs)));
+ return None;
+
+ def _createAutostartCfg(self, sDefaultPolicy = 'allow', asUserAllow = (), asUserDeny = ()):
+ """
+ Creates a autostart config for VirtualBox
+ """
+ sVBoxCfg = 'default_policy=' + sDefaultPolicy + '\n';
+ for sUserAllow in asUserAllow:
+ sVBoxCfg = sVBoxCfg + sUserAllow + ' = {\n allow = true\n }\n';
+ for sUserDeny in asUserDeny:
+ sVBoxCfg = sVBoxCfg + sUserDeny + ' = {\n allow = false\n }\n';
+ return sVBoxCfg;
+
+ def _waitAdditionsIsRunning(self, oGuest, fWaitTrayControl):
+ """
+ Check is the additions running
+ """
+ cAttempt = 0;
+ fRc = False;
+ while cAttempt < 30:
+ fRc = oGuest.additionsRunLevel in [vboxcon.AdditionsRunLevelType_Userland,
+ vboxcon.AdditionsRunLevelType_Desktop];
+ if fRc:
+ eServiceStatus, _ = oGuest.getFacilityStatus(vboxcon.AdditionsFacilityType_VBoxService);
+ fRc = eServiceStatus == vboxcon.AdditionsFacilityStatus_Active;
+ if fRc and not fWaitTrayControl:
+ break;
+ if fRc:
+ eServiceStatus, _ = oGuest.getFacilityStatus(vboxcon.AdditionsFacilityType_VBoxTrayClient);
+ fRc = eServiceStatus == vboxcon.AdditionsFacilityStatus_Active;
+ if fRc:
+ break;
+ self.oTstDrv.sleep(10);
+ cAttempt += 1;
+ return fRc;
+
+ def createSession(self, oSession, sName, sUser, sPassword, cMsTimeout = 10 * 1000, fIsError = True):
+ """
+ Creates (opens) a guest session.
+ Returns (True, IGuestSession) on success or (False, None) on failure.
+ """
+ oGuest = oSession.o.console.guest;
+ if sName is None:
+ sName = "<untitled>";
+ reporter.log('Creating session "%s" ...' % (sName,));
+ try:
+ oGuestSession = oGuest.createSession(sUser, sPassword, '', sName);
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Creating a guest session "%s" failed; sUser="%s", pw="%s"'
+ % (sName, sUser, sPassword));
+ return (False, None);
+ reporter.log('Waiting for session "%s" to start within %dms...' % (sName, cMsTimeout));
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start, ];
+ try:
+ waitResult = oGuestSession.waitForArray(aeWaitFor, cMsTimeout);
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErr(fIsError, 'Session did not start successfully, returned wait result: %d' % (waitResult,));
+ return (False, None);
+ reporter.log('Session "%s" successfully started' % (sName,));
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Waiting for guest session "%s" (usr=%s;pw=%s) to start failed:'
+ % (sName, sUser, sPassword,));
+ return (False, None);
+ return (True, oGuestSession);
+
+ def closeSession(self, oGuestSession, fIsError = True):
+ """
+ Closes the guest session.
+ """
+ if oGuestSession is not None:
+ try:
+ sName = oGuestSession.name;
+ except:
+ return reporter.errorXcpt();
+ reporter.log('Closing session "%s" ...' % (sName,));
+ try:
+ oGuestSession.close();
+ oGuestSession = None;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Closing guest session "%s" failed:' % (sName,));
+ return False;
+ return True;
+
+ def guestProcessExecute(self, oGuestSession, sTestName, cMsTimeout, sExecName, asArgs = (),
+ fGetStdOut = True, fIsError = True):
+ """
+ Helper function to execute a program on a guest, specified in the current test.
+ Returns (True, ProcessStatus, ProcessExitCode, ProcessStdOutBuffer) on success or (False, 0, 0, None) on failure.
+ """
+ _ = sTestName;
+ fRc = True; # Be optimistic.
+ reporter.log2('Using session user=%s, name=%s, timeout=%d'
+ % (oGuestSession.user, oGuestSession.name, oGuestSession.timeout,));
+ #
+ # Start the process:
+ #
+ reporter.log2('Executing sCmd=%s, timeoutMS=%d, asArgs=%s'
+ % (sExecName, cMsTimeout, asArgs, ));
+ fTaskFlags = [];
+ if fGetStdOut:
+ fTaskFlags = [vboxcon.ProcessCreateFlag_WaitForStdOut,
+ vboxcon.ProcessCreateFlag_WaitForStdErr];
+ try:
+ oProcess = oGuestSession.processCreate(sExecName,
+ asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:],
+ [], fTaskFlags, cMsTimeout);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'asArgs=%s' % (asArgs,));
+ return (False, 0, 0, None);
+ if oProcess is None:
+ return (reporter.error('oProcess is None! (%s)' % (asArgs,)), 0, 0, None);
+ #time.sleep(5); # try this if you want to see races here.
+ # Wait for the process to start properly:
+ reporter.log2('Process start requested, waiting for start (%dms) ...' % (cMsTimeout,));
+ iPid = -1;
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Start, ];
+ aBuf = None;
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, cMsTimeout);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'waitforArray failed for asArgs=%s' % (asArgs,));
+ fRc = False;
+ else:
+ try:
+ eStatus = oProcess.status;
+ iPid = oProcess.PID;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ else:
+ reporter.log2('Wait result returned: %d, current process status is: %d' % (eWaitResult, eStatus,));
+ #
+ # Wait for the process to run to completion if necessary.
+ #
+ # Note! The above eWaitResult return value can be ignored as it will
+ # (mostly) reflect the process status anyway.
+ #
+ if eStatus == vboxcon.ProcessStatus_Started:
+ # What to wait for:
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate,
+ vboxcon.ProcessWaitForFlag_StdOut,
+ vboxcon.ProcessWaitForFlag_StdErr];
+ reporter.log2('Process (PID %d) started, waiting for termination (%dms), aeWaitFor=%s ...'
+ % (iPid, cMsTimeout, aeWaitFor));
+ acbFdOut = [0,0,0];
+ while True:
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, cMsTimeout);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ break;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ break;
+ reporter.log2('Wait returned: %d' % (eWaitResult,));
+ # Process output:
+ for eFdResult, iFd, sFdNm in [ (vboxcon.ProcessWaitResult_StdOut, 1, 'stdout'),
+ (vboxcon.ProcessWaitResult_StdErr, 2, 'stderr'), ]:
+ if eWaitResult in (eFdResult, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ reporter.log2('Reading %s ...' % (sFdNm,));
+ try:
+ abBuf = oProcess.read(iFd, 64 * 1024, cMsTimeout);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ except:
+ pass; ## @todo test for timeouts and fail on anything else!
+ else:
+ if abBuf:
+ reporter.log2('Process (PID %d) got %d bytes of %s data' % (iPid, len(abBuf), sFdNm,));
+ acbFdOut[iFd] += len(abBuf);
+ ## @todo Figure out how to uniform + append!
+ sBuf = '';
+ if sys.version_info >= (2, 7) and isinstance(abBuf, memoryview):
+ abBuf = abBuf.tobytes();
+ sBuf = abBuf.decode("utf-8");
+ else:
+ sBuf = str(abBuf);
+ if aBuf:
+ aBuf += sBuf;
+ else:
+ aBuf = sBuf;
+ ## Process input (todo):
+ #if eWaitResult in (vboxcon.ProcessWaitResult_StdIn, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ # reporter.log2('Process (PID %d) needs stdin data' % (iPid,));
+ # Termination or error?
+ if eWaitResult in (vboxcon.ProcessWaitResult_Terminate,
+ vboxcon.ProcessWaitResult_Error,
+ vboxcon.ProcessWaitResult_Timeout,):
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Process (PID %d) reported terminate/error/timeout: %d, status: %d'
+ % (iPid, eWaitResult, eStatus,));
+ break;
+ # End of the wait loop.
+ _, cbStdOut, cbStdErr = acbFdOut;
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Final process status (PID %d) is: %d' % (iPid, eStatus));
+ reporter.log2('Process (PID %d) %d stdout, %d stderr' % (iPid, cbStdOut, cbStdErr));
+ #
+ # Get the final status and exit code of the process.
+ #
+ try:
+ uExitStatus = oProcess.status;
+ iExitCode = oProcess.exitCode;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Process (PID %d) has exit code: %d; status: %d ' % (iPid, iExitCode, uExitStatus));
+ return (fRc, uExitStatus, iExitCode, aBuf);
+
+ def uploadString(self, oGuestSession, sSrcString, sDst):
+ """
+ Upload the string into guest.
+ """
+ fRc = True;
+ try:
+ oFile = oGuestSession.fileOpenEx(sDst, vboxcon.FileAccessMode_ReadWrite, vboxcon.FileOpenAction_CreateOrReplace,
+ vboxcon.FileSharingMode_All, 0, []);
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not create and open the file %s' % sDst);
+ else:
+ try:
+ oFile.write(bytearray(sSrcString), 60*1000);
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not write the string into the file %s' % sDst);
+ try:
+ oFile.close();
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not close the file %s' % sDst);
+ return fRc;
+
+ def uploadFile(self, oGuestSession, sSrc, sDst):
+ """
+ Upload the string into guest.
+ """
+ fRc = True;
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyToGuest(sSrc, sDst, [0]);
+ else:
+ oCurProgress = oGuestSession.copyTo(sSrc, sDst, [0]);
+ except:
+ reporter.maybeErrXcpt(True, 'Upload file exception for sSrc="%s":'
+ % (self.sGuestAdditionsIso,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "uploadFile");
+ oWrapperProgress.wait();
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors = False);
+ fRc = False;
+ else:
+ fRc = reporter.error('No progress object returned');
+ return fRc;
+
+ def downloadFile(self, oGuestSession, sSrc, sDst, fIgnoreErrors = False):
+ """
+ Get a file (sSrc) from the guest storing it on the host (sDst).
+ """
+ fRc = True;
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyFromGuest(sSrc, sDst, [0]);
+ else:
+ oCurProgress = oGuestSession.copyFrom(sSrc, sDst, [0]);
+ except:
+ if not fIgnoreErrors:
+ reporter.errorXcpt('Download file exception for sSrc="%s":' % (sSrc,));
+ else:
+ reporter.log('warning: Download file exception for sSrc="%s":' % (sSrc,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr,
+ self.oTstDrv, "downloadFile");
+ oWrapperProgress.wait();
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors);
+ fRc = False;
+ else:
+ if not fIgnoreErrors:
+ reporter.error('No progress object returned');
+ else:
+ reporter.log('warning: No progress object returned');
+ fRc = False;
+ return fRc;
+
+ def downloadFiles(self, oGuestSession, asFiles, fIgnoreErrors = False):
+ """
+ Convenience function to get files from the guest and stores it
+ into the scratch directory for later (manual) review.
+ Returns True on success.
+ Returns False on failure, logged.
+ """
+ fRc = True;
+ for sGstFile in asFiles:
+ ## @todo r=bird: You need to use the guest specific path functions here.
+ ## Best would be to add basenameEx to common/pathutils.py. See how joinEx
+ ## is used by BaseTestVm::pathJoin and such.
+ sTmpFile = os.path.join(self.oTstDrv.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
+ reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
+ # First try to remove (unlink) an existing temporary file, as we don't truncate the file.
+ try: os.unlink(sTmpFile);
+ except: pass;
+ ## @todo Check for already existing files on the host and create a new
+ # name for the current file to download.
+ fRc = self.downloadFile(oGuestSession, sGstFile, sTmpFile, fIgnoreErrors);
+ if fRc:
+ reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
+ else:
+ if fIgnoreErrors is not True:
+ reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
+ return fRc;
+ reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
+ return True;
+
+ def _checkVmIsReady(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Start a guest process',
+ 30 * 1000, '/sbin/ifconfig',
+ ['ifconfig',],
+ False, False);
+ return fRc;
+
+ def waitVmIsReady(self, oSession, fWaitTrayControl):
+ """
+ Waits the VM is ready after start or reboot.
+ Returns result (true or false) and guest session obtained
+ """
+ _ = fWaitTrayControl;
+ # Give the VM a time to reboot
+ self.oTstDrv.sleep(30);
+ # Waiting the VM is ready.
+ # To do it, one will try to open the guest session and start the guest process in loop
+ if not self._waitAdditionsIsRunning(oSession.o.console.guest, False):
+ return (False, None);
+ cAttempt = 0;
+ oGuestSession = None;
+ fRc = False;
+ while cAttempt < 30:
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: vbox',
+ 'vbox', 'password', 10 * 1000, False);
+ if fRc:
+ fRc = self._checkVmIsReady(oGuestSession);
+ if fRc:
+ break;
+ self.closeSession(oGuestSession, False);
+ self.oTstDrv.sleep(10);
+ cAttempt += 1;
+ return (fRc, oGuestSession);
+
+ def _rebootVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Reboot the VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['sudo', 'reboot'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the reboot utility failed');
+ return fRc;
+
+ def rebootVMAndCheckReady(self, oSession, oGuestSession):
+ """
+ Reboot the VM and wait the VM is ready.
+ Returns result and guest session obtained after reboot
+ """
+ reporter.testStart('Reboot VM and wait for readiness');
+ fRc = self._rebootVM(oGuestSession);
+ fRc = self.closeSession(oGuestSession, True) and fRc and True; # pychecker hack.
+ if fRc:
+ (fRc, oGuestSession) = self.waitVmIsReady(oSession, False);
+ if not fRc:
+ reporter.error('VM is not ready after reboot');
+ reporter.testDone();
+ return (fRc, oGuestSession);
+
+ def _powerDownVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Power down the VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['sudo', 'poweroff'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the poweroff utility failed');
+ return fRc;
+
+ def powerDownVM(self, oGuestSession):
+ """
+ Power down the VM by calling guest process without wating
+ the VM is really powered off. Also, closes the guest session.
+ It helps the terminateBySession to stop the VM without aborting.
+ """
+ if oGuestSession is None:
+ return False;
+ reporter.testStart('Power down the VM');
+ fRc = self._powerDownVM(oGuestSession);
+ fRc = self.closeSession(oGuestSession, True) and fRc and True; # pychecker hack.
+ if not fRc:
+ reporter.error('Power down the VM failed');
+ reporter.testDone();
+ return fRc;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Installs the Windows guest additions using the test execution service.
+ """
+ _ = oSession;
+ _ = oGuestSession;
+ _ = oVM;
+ reporter.error('Not implemented');
+ return False;
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ _ = oGuestSession;
+ reporter.error('Not implemented');
+ return False;
+
+ def configureAutostart(self, oGuestSession, sDefaultPolicy = 'allow', asUserAllow = (), asUserDeny = ()):
+ """
+ Configures the autostart feature in the guest.
+ """
+ _ = oGuestSession;
+ _ = sDefaultPolicy;
+ _ = asUserAllow; # pylint: disable=redefined-variable-type
+ _ = asUserDeny;
+ reporter.error('Not implemented');
+ return False;
+
+ def createUser(self, oGuestSession, sUser):
+ """
+ Create a new user with the given name
+ """
+ _ = oGuestSession;
+ _ = sUser;
+ reporter.error('Not implemented');
+ return False;
+
+ def checkForRunningVM(self, oSession, oGuestSession, sUser, sVmName):
+ """
+ Check for VM running in the guest after autostart.
+ Due to the sUser is created whithout password,
+ all calls will be perfomed using 'sudo -u sUser'
+ """
+ _ = oSession;
+ _ = oGuestSession;
+ _ = sUser;
+ _ = sVmName;
+ reporter.error('Not implemented');
+ return False;
+
+ def getResourceSet(self):
+ asRet = [];
+ if not os.path.isabs(self.sHdd):
+ asRet.append(self.sHdd);
+ return asRet;
+
+ def _createVmDoIt(self, oTestDrv, eNic0AttachType, sDvdImage):
+ """
+ Creates the VM.
+ Returns Wrapped VM object on success, None on failure.
+ """
+ _ = eNic0AttachType;
+ _ = sDvdImage;
+ return oTestDrv.createTestVM(self.sVmName, self.iGroup, self.sHdd, sKind = self.sKind, \
+ fIoApic = True, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = self.eNic0Type, cMbRam = self.cMbRam, \
+ sHddControllerType = "SATA Controller", fPae = self.fPae, \
+ cCpus = self.cCpus, sDvdImage = self.sGuestAdditionsIso);
+
+ def _createVmPost(self, oTestDrv, oVM, eNic0AttachType, sDvdImage):
+ _ = eNic0AttachType;
+ _ = sDvdImage;
+ fRc = True;
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(True);
+ fRc = fRc and oSession.enableNestedPaging(True);
+ fRc = fRc and oSession.enableNestedHwVirt(True);
+ # disable 3D until the error is fixed.
+ fRc = fRc and oSession.setAccelerate3DEnabled(False);
+ fRc = fRc and oSession.setVRamSize(256);
+ fRc = fRc and oSession.setVideoControllerType(vboxcon.GraphicsControllerType_VBoxSVGA);
+ fRc = fRc and oSession.enableUsbOhci(True);
+ fRc = fRc and oSession.enableUsbHid(True);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+ return oVM if fRc else None;
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ #
+ # Current test uses precofigured VMs. This override disables any changes in the machine.
+ #
+ _ = cCpus;
+ _ = sVirtMode;
+ _ = sParavirtMode;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is None:
+ return (False, None);
+ return (True, oVM);
+
+class tdAutostartOsLinux(tdAutostartOs):
+ """
+ Autostart support methods for Linux guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None):
+ tdAutostartOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso);
+ try: self.sVBoxInstaller = '^VirtualBox-.*\\.run$';
+ except: pass;
+ return;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Install guest additions in the guest.
+ """
+ reporter.testStart('Install Guest Additions');
+ fRc = False;
+ # Install Kernel headers, which are required for actually installing the Linux Additions.
+ if oVM.OSTypeId.startswith('Debian') \
+ or oVM.OSTypeId.startswith('Ubuntu'):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Kernel headers',
+ 5 * 60 *1000, '/usr/bin/apt-get',
+ ['/usr/bin/apt-get', 'install', '-y',
+ 'linux-headers-generic'],
+ False, True);
+ if not fRc:
+ reporter.error('Error installing Kernel headers');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Guest Additions depdendencies',
+ 5 * 60 *1000, '/usr/bin/apt-get',
+ ['/usr/bin/apt-get', 'install', '-y', 'build-essential',
+ 'perl'], False, True);
+ if not fRc:
+ reporter.error('Error installing additional installer dependencies');
+ elif oVM.OSTypeId.startswith('OL') \
+ or oVM.OSTypeId.startswith('Oracle') \
+ or oVM.OSTypeId.startswith('RHEL') \
+ or oVM.OSTypeId.startswith('Redhat') \
+ or oVM.OSTypeId.startswith('Cent'):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Kernel headers',
+ 5 * 60 *1000, '/usr/bin/yum',
+ ['/usr/bin/yum', '-y', 'install', 'kernel-headers'],
+ False, True);
+ if not fRc:
+ reporter.error('Error installing Kernel headers');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Guest Additions depdendencies',
+ 5 * 60 *1000, '/usr/bin/yum',
+ ['/usr/bin/yum', '-y', 'install', 'make', 'automake', 'gcc',
+ 'kernel-devel', 'dkms', 'bzip2', 'perl'], False, True);
+ if not fRc:
+ reporter.error('Error installing additional installer dependencies');
+ else:
+ reporter.error('Installing Linux Additions for the "%s" is not supported yet' % oVM.OSTypeId);
+ fRc = False;
+ if fRc:
+ #
+ # The actual install.
+ # Also tell the installer to produce the appropriate log files.
+ #
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing guest additions',
+ 10 * 60 *1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/sh',
+ '/media/cdrom/VBoxLinuxAdditions.run'],
+ False, True);
+ if fRc:
+ # Due to the GA updates as separate process the above function returns before
+ # the actual installation finished. So just wait until the GA installed
+ fRc = self.closeSession(oGuestSession);
+ if fRc:
+ (fRc, oGuestSession) = self.waitVmIsReady(oSession, False);
+ # Download log files.
+ # Ignore errors as all files above might not be present for whatever reason.
+ #
+ if fRc:
+ asLogFile = [];
+ asLogFile.append('/var/log/vboxadd-install.log');
+ self.downloadFiles(oGuestSession, asLogFile, fIgnoreErrors = True);
+ else:
+ reporter.error('Installing guest additions failed: Error occured during vbox installer execution')
+ if fRc:
+ (fRc, oGuestSession) = self.rebootVMAndCheckReady(oSession, oGuestSession);
+ if not fRc:
+ reporter.error('Reboot after installing GuestAdditions failed');
+ reporter.testDone();
+ return (fRc, oGuestSession);
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ reporter.testStart('Install Virtualbox into the guest VM');
+ sTestBuild = self._findFile(self.sVBoxInstaller, self.asTestBuildDirs);
+ reporter.log("Virtualbox install file: %s" % os.path.basename(sTestBuild));
+ fRc = sTestBuild is not None;
+ if fRc:
+ fRc = self.uploadFile(oGuestSession, sTestBuild,
+ '/tmp/' + os.path.basename(sTestBuild));
+ else:
+ reporter.error("VirtualBox install package is not defined");
+
+ if not fRc:
+ reporter.error('Upload the vbox installer into guest VM failed');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession,
+ 'Allowing execution for the vbox installer',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/chmod', '755',
+ '/tmp/' + os.path.basename(sTestBuild)],
+ False, True);
+ if not fRc:
+ reporter.error('Allowing execution for the vbox installer failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 240 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo',
+ '/tmp/' + os.path.basename(sTestBuild),],
+ False, True);
+ if not fRc:
+ reporter.error('Installing VBox failed');
+ reporter.testDone();
+ return fRc;
+
+ def configureAutostart(self, oGuestSession, sDefaultPolicy = 'allow', asUserAllow = (), asUserDeny = ()):
+ """
+ Configures the autostart feature in the guest.
+ """
+ reporter.testStart('Configure autostart');
+ # Create autostart database directory writeable for everyone
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Creating autostart database',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/mkdir', '-m', '1777', '/etc/vbox/autostart.d'],
+ False, True);
+ if not fRc:
+ reporter.error('Creating autostart database failed');
+ # Create /etc/default/virtualbox
+ if fRc:
+ sVBoxCfg = 'VBOXAUTOSTART_CONFIG=/etc/vbox/autostart.cfg\n' \
+ + 'VBOXAUTOSTART_DB=/etc/vbox/autostart.d\n';
+ fRc = self.uploadString(oGuestSession, sVBoxCfg, '/tmp/virtualbox');
+ if not fRc:
+ reporter.error('Upload to /tmp/virtualbox failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Moving to destination',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/mv', '/tmp/virtualbox',
+ '/etc/default/virtualbox'],
+ False, True);
+ if not fRc:
+ reporter.error('Moving the /tmp/virtualbox to destination failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Setting permissions',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/chmod', '644',
+ '/etc/default/virtualbox'],
+ False, True);
+ if not fRc:
+ reporter.error('Setting permissions for the virtualbox failed');
+ if fRc:
+ sVBoxCfg = self._createAutostartCfg(sDefaultPolicy, asUserAllow, asUserDeny);
+ fRc = self.uploadString(oGuestSession, sVBoxCfg, '/tmp/autostart.cfg');
+ if not fRc:
+ reporter.error('Upload to /tmp/autostart.cfg failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Moving to destination',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/mv', '/tmp/autostart.cfg',
+ '/etc/vbox/autostart.cfg'],
+ False, True);
+ if not fRc:
+ reporter.error('Moving the /tmp/autostart.cfg to destination failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Setting permissions',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/chmod', '644',
+ '/etc/vbox/autostart.cfg'],
+ False, True);
+ if not fRc:
+ reporter.error('Setting permissions for the autostart.cfg failed');
+ reporter.testDone();
+ return fRc;
+
+ def createUser(self, oGuestSession, sUser):
+ """
+ Create a new user with the given name
+ """
+ reporter.testStart('Create user %s' % sUser);
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Creating new user',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/usr/sbin/useradd', '-m', '-U',
+ sUser], False, True);
+ if not fRc:
+ reporter.error('Create user %s failed' % sUser);
+ reporter.testDone();
+ return fRc;
+
+ # pylint: enable=too-many-arguments
+ def createTestVM(self, oSession, oGuestSession, sUser, sVmName):
+ """
+ Create a test VM in the guest and enable autostart.
+ Due to the sUser is created whithout password,
+ all calls will be perfomed using 'sudo -u sUser'
+ """
+ _ = oSession;
+ reporter.testStart('Create test VM for user %s' % sUser);
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Configuring autostart database',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '-u', sUser, '-H', '/opt/VirtualBox/VBoxManage',
+ 'setproperty', 'autostartdbpath', '/etc/vbox/autostart.d'],
+ False, True);
+ if not fRc:
+ reporter.error('Configuring autostart database failed');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Create VM ' + sVmName,
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '-u', sUser, '-H',
+ '/opt/VirtualBox/VBoxManage', 'createvm',
+ '--name', sVmName, '--register'], False, True);
+ if not fRc:
+ reporter.error('Create VM %s failed' % sVmName);
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Enabling autostart for test VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '-u', sUser, '-H',
+ '/opt/VirtualBox/VBoxManage', 'modifyvm',
+ sVmName, '--autostart-enabled', 'on'], False, True);
+ if not fRc:
+ reporter.error('Enabling autostart for %s failed' % sVmName);
+ reporter.testDone();
+ return fRc;
+
+ def checkForRunningVM(self, oSession, oGuestSession, sUser, sVmName):
+ """
+ Check for VM running in the guest after autostart.
+ Due to the sUser is created whithout password,
+ all calls will be perfomed using 'sudo -u sUser'
+ """
+ self.oTstDrv.sleep(30);
+ _ = oSession;
+ reporter.testStart('Check the VM %s is running for user %s' % (sVmName, sUser));
+ (fRc, _, _, aBuf) = self.guestProcessExecute(oGuestSession, 'Check for running VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '-u', sUser, '-H',
+ '/opt/VirtualBox/VBoxManage',
+ 'list', 'runningvms'], True, True);
+ if not fRc:
+ reporter.error('Checking the VM %s is running for user %s failed' % (sVmName, sUser));
+ else:
+ bufWrapper = VBoxManageStdOutWrapper();
+ bufWrapper.write(aBuf);
+ fRc = bufWrapper.sVmRunning == sVmName;
+ reporter.testDone();
+ return fRc;
+
+class tdAutostartOsDarwin(tdAutostartOs):
+ """
+ Autostart support methods for Darwin guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None):
+ tdAutostartOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso);
+ raise base.GenError('Testing the autostart functionality for Darwin is not implemented');
+
+class tdAutostartOsSolaris(tdAutostartOs):
+ """
+ Autostart support methods for Solaris guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None):
+ tdAutostartOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso);
+ raise base.GenError('Testing the autostart functionality for Solaris is not implemented');
+
+class tdAutostartOsWin(tdAutostartOs):
+ """
+ Autostart support methods for Windows guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None):
+ tdAutostartOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso);
+ try: self.sVBoxInstaller = '^VirtualBox-.*\\.(exe|msi)$';
+ except: pass;
+ return;
+
+ def _checkVmIsReady(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Start a guest process',
+ 30 * 1000, 'C:\\Windows\\System32\\ipconfig.exe',
+ ['C:\\Windows\\System32\\ipconfig.exe',],
+ False, False);
+ return fRc;
+
+ def _rebootVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Reboot the VM',
+ 30 * 1000, 'C:\\Windows\\System32\\shutdown.exe',
+ ['C:\\Windows\\System32\\shutdown.exe', '/f',
+ '/r', '/t', '0'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the shutdown utility failed');
+ return fRc;
+
+ def _powerDownVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Power down the VM',
+ 30 * 1000, 'C:\\Windows\\System32\\shutdown.exe',
+ ['C:\\Windows\\System32\\shutdown.exe', '/f',
+ '/s', '/t', '0'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the shutdown utility failed');
+ return fRc;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Installs the Windows guest additions using the test execution service.
+ """
+ _ = oVM;
+ reporter.testStart('Install Guest Additions');
+ asLogFiles = [];
+ fRc = self.closeSession(oGuestSession, True); # pychecker hack.
+ try:
+ oCurProgress = oSession.o.console.guest.updateGuestAdditions(self.sGuestAdditionsIso, ['/l',], None);
+ except:
+ reporter.maybeErrXcpt(True, 'Updating Guest Additions exception for sSrc="%s":'
+ % (self.sGuestAdditionsIso,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr,
+ self.oTstDrv, "installAdditions");
+ oWrapperProgress.wait(cMsTimeout = 10 * 60 * 1000);
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors = False);
+ fRc = False;
+ else:
+ fRc = reporter.error('No progress object returned');
+ #---------------------------------------
+ #
+ ##
+ ## Install the public signing key.
+ ##
+ #
+ #self.oTstDrv.sleep(60 * 2);
+ #
+ #if oVM.OSTypeId not in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003'):
+ # (fRc, _, _, _) = \
+ # self.guestProcessExecute(oGuestSession, 'Installing SHA1 certificate',
+ # 60 * 1000, 'D:\\cert\\VBoxCertUtil.exe',
+ # ['D:\\cert\\VBoxCertUtil.exe', 'add-trusted-publisher',
+ # 'D:\\cert\\vbox-sha1.cer'],
+ # False, True);
+ # if not fRc:
+ # reporter.error('Error installing SHA1 certificate');
+ # else:
+ # (fRc, _, _, _) = \
+ # self.guestProcessExecute(oGuestSession, 'Installing SHA1 certificate',
+ # 60 * 1000, 'D:\\cert\\VBoxCertUtil.exe',
+ # ['D:\\cert\\VBoxCertUtil.exe', 'add-trusted-publisher',
+ # 'D:\\cert\\vbox-sha256.cer'],
+ # False, True);
+ # if not fRc:
+ # reporter.error('Error installing SHA256 certificate');
+ #
+ #(fRc, _, _, _) = \
+ # self.guestProcessExecute(oGuestSession, 'Installing GA',
+ # 60 * 1000, 'D:\\VBoxWindowsAdditions.exe',
+ # ['D:\\VBoxWindowsAdditions.exe', '/S', '/l',
+ # '/no_vboxservice_exit'],
+ # False, True);
+ #
+ #if fRc:
+ # # Due to the GA updates as separate process the above function returns before
+ # # the actual installation finished. So just wait until the GA installed
+ # fRc = self.closeSession(oGuestSession, True);
+ # if fRc:
+ # (fRc, oGuestSession) = self.waitVmIsReady(oSession, False, False);
+ #---------------------------------------
+ # Store the result and try download logs anyway.
+ fGaRc = fRc;
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: vbox',
+ 'vbox', 'password', 10 * 1000, True);
+ if fRc is True:
+ (fRc, oGuestSession) = self.rebootVMAndCheckReady(oSession, oGuestSession);
+ if fRc is True:
+ # Add the Windows Guest Additions installer files to the files we want to download
+ # from the guest.
+ sGuestAddsDir = 'C:/Program Files/Oracle/VirtualBox Guest Additions/';
+ asLogFiles.append(sGuestAddsDir + 'install.log');
+ # Note: There won't be a install_ui.log because of the silent installation.
+ asLogFiles.append(sGuestAddsDir + 'install_drivers.log');
+ # Download log files.
+ # Ignore errors as all files above might not be present (or in different locations)
+ # on different Windows guests.
+ #
+ self.downloadFiles(oGuestSession, asLogFiles, fIgnoreErrors = True);
+ else:
+ reporter.error('Reboot after installing GuestAdditions failed');
+ else:
+ reporter.error('Create session for user vbox after GA updating failed');
+ reporter.testDone();
+ return (fRc and fGaRc, oGuestSession);
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ reporter.testStart('Install Virtualbox into the guest VM');
+ # Used windows image already contains the C:\Temp
+ sTestBuild = self._findFile(self.sVBoxInstaller, self.asTestBuildDirs);
+ reporter.log("Virtualbox install file: %s" % os.path.basename(sTestBuild));
+ fRc = sTestBuild is not None;
+ if fRc:
+ fRc = self.uploadFile(oGuestSession, sTestBuild,
+ 'C:\\Temp\\' + os.path.basename(sTestBuild));
+ else:
+ reporter.error("VirtualBox install package is not defined");
+
+ if not fRc:
+ reporter.error('Upload the installing into guest VM failed');
+ else:
+ if sTestBuild.endswith('.msi'):
+ sLogFile = 'C:/Temp/VBoxInstallLog.txt';
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 600 * 1000, 'C:\\Windows\\System32\\msiexec.exe',
+ ['msiexec', '/quiet', '/norestart', '/i',
+ 'C:\\Temp\\' + os.path.basename(sTestBuild),
+ '/lv', sLogFile],
+ False, True);
+ if not fRc:
+ reporter.error('Installing the VBox from msi installer failed');
+ else:
+ sLogFile = 'C:/Temp/Virtualbox/VBoxInstallLog.txt';
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 600 * 1000, 'C:\\Temp\\' + os.path.basename(sTestBuild),
+ ['C:\\Temp\\' + os.path.basename(sTestBuild), '-vvvv',
+ '--silent', '--logging',
+ '--msiparams', 'REBOOT=ReallySuppress'],
+ False, True);
+ if not fRc:
+ reporter.error('Installing the VBox failed');
+ else:
+ (_, _, _, aBuf) = self.guestProcessExecute(oGuestSession, 'Check installation',
+ 240 * 1000, 'C:\\Windows\\System32\\cmd.exe',
+ ['c:\\Windows\\System32\\cmd.exe', '/c',
+ 'dir', 'C:\\Program Files\\Oracle\\VirtualBox\\*.*'],
+ True, True);
+ reporter.log('Content of VirtualBxox folder:');
+ reporter.log(str(aBuf));
+ asLogFiles = [sLogFile,];
+ self.downloadFiles(oGuestSession, asLogFiles, fIgnoreErrors = True);
+ reporter.testDone();
+ return fRc;
+
+ def configureAutostart(self, oGuestSession, sDefaultPolicy = 'allow', asUserAllow = (), asUserDeny = ()):
+ """
+ Configures the autostart feature in the guest.
+ """
+ reporter.testStart('Configure autostart');
+ # Create autostart database directory writeable for everyone
+ (fRc, _, _, _) = \
+ self.guestProcessExecute(oGuestSession, 'Setting the autostart environment variable',
+ 30 * 1000, 'C:\\Windows\\System32\\reg.exe',
+ ['reg', 'add',
+ 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
+ '/v', 'VBOXAUTOSTART_CONFIG', '/d',
+ 'C:\\ProgramData\\autostart.cfg', '/f'],
+ False, True);
+ if fRc:
+ sVBoxCfg = self._createAutostartCfg(sDefaultPolicy, asUserAllow, asUserDeny);
+ fRc = self.uploadString(oGuestSession, sVBoxCfg, 'C:\\ProgramData\\autostart.cfg');
+ if not fRc:
+ reporter.error('Upload the autostart.cfg failed');
+ else:
+ reporter.error('Setting the autostart environment variable failed');
+ reporter.testDone();
+ return fRc;
+
+ def createTestVM(self, oSession, oGuestSession, sUser, sVmName):
+ """
+ Create a test VM in the guest and enable autostart.
+ """
+ _ = oGuestSession;
+ reporter.testStart('Create test VM for user %s' % sUser);
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: %s' % (sUser,),
+ sUser, 'password', 10 * 1000, True);
+ if not fRc:
+ reporter.error('Create session for user %s failed' % sUser);
+ else:
+ (fRc, _, _, _) = \
+ self.guestProcessExecute(oGuestSession, 'Create VM ' + sVmName,
+ 30 * 1000, 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe',
+ ['C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe', 'createvm',
+ '--name', sVmName, '--register'], False, True);
+ if not fRc:
+ reporter.error('Create VM %s for user %s failed' % (sVmName, sUser));
+ else:
+ (fRc, _, _, _) = \
+ self.guestProcessExecute(oGuestSession, 'Enabling autostart for test VM',
+ 30 * 1000, 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe',
+ ['C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe',
+ 'modifyvm', sVmName, '--autostart-enabled', 'on'], False, True);
+ if not fRc:
+ reporter.error('Enabling autostart for VM %s for user %s failed' % (sVmName, sUser));
+ if fRc:
+ fRc = self.uploadString(oGuestSession, 'password', 'C:\\ProgramData\\password.cfg');
+ if not fRc:
+ reporter.error('Upload the password.cfg failed');
+ if fRc:
+ (fRc, _, _, _) = \
+ self.guestProcessExecute(oGuestSession, 'Install autostart service for the user',
+ 30 * 1000, 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxAutostartSvc.exe',
+ ['C:\\Program Files\\Oracle\\VirtualBox\\VBoxAutostartSvc.exe',
+ 'install', '--user=' + sUser,
+ '--password-file=C:\\ProgramData\\password.cfg'],
+ False, True);
+ if not fRc:
+ reporter.error('Install autostart service for user %s failed' % sUser);
+ fRc1 = self.closeSession(oGuestSession, True);
+ if not fRc1:
+ reporter.error('Closing session for user %s failed' % sUser);
+ fRc = fRc1 and fRc and True; # pychecker hack.
+ reporter.testDone();
+ return fRc;
+
+ def checkForRunningVM(self, oSession, oGuestSession, sUser, sVmName):
+ """
+ Check for VM running in the guest after autostart.
+ """
+ self.oTstDrv.sleep(30);
+ _ = oGuestSession;
+ reporter.testStart('Check the VM %s is running for user %s' % (sVmName, sUser));
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: %s' % (sUser,),
+ sUser, 'password', 10 * 1000, True);
+ if not fRc:
+ reporter.error('Create session for user %s failed' % sUser);
+ else:
+ (fRc, _, _, aBuf) = self.guestProcessExecute(oGuestSession, 'Check for running VM',
+ 60 * 1000, 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe',
+ [ 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe',
+ 'list', 'runningvms' ], True, True);
+ if not fRc:
+ reporter.error('Checking the VM %s is running for user %s failed' % (sVmName, sUser));
+ else:
+ bufWrapper = VBoxManageStdOutWrapper();
+ bufWrapper.write(aBuf);
+ fRc = bufWrapper.sVmRunning == sVmName;
+ fRc1 = self.closeSession(oGuestSession, True);
+ if not fRc1:
+ reporter.error('Closing session for user %s failed' % sUser);
+ fRc = fRc1 and fRc and True; # pychecker hack.
+ reporter.testDone();
+ return fRc;
+
+ def createUser(self, oGuestSession, sUser):
+ """
+ Create a new user with the given name
+ """
+ reporter.testStart('Create user %s' % sUser);
+ # Create user
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Creating user %s to run a VM' % sUser,
+ 30 * 1000, 'C:\\Windows\\System32\\net.exe',
+ ['net', 'user', sUser, 'password', '/add' ], False, True);
+ if not fRc:
+ reporter.error('Creating user %s to run a VM failed' % sUser);
+ # Add the user to Administrators group
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Adding the user %s to Administrators group' % sUser,
+ 30 * 1000, 'C:\\Windows\\System32\\net.exe',
+ ['net', 'localgroup', 'Administrators', sUser, '/add' ], False, True);
+ if not fRc:
+ reporter.error('Adding the user %s to Administrators group failed' % sUser);
+ #Allow the user to logon as service
+ if fRc:
+ sSecPolicyEditor = """
+$sUser = '%s'
+$oUser = New-Object System.Security.Principal.NTAccount("$sUser")
+$oSID = $oUser.Translate([System.Security.Principal.SecurityIdentifier])
+$sExportFile = 'C:\\Temp\\cfg.inf'
+$sSecDb = 'C:\\Temp\\secedt.sdb'
+$sImportFile = 'C:\\Temp\\newcfg.inf'
+secedit /export /cfg $sExportFile
+$sCurrServiceLogonRight = Get-Content -Path $sExportFile |
+ Where-Object {$_ -Match 'SeServiceLogonRight'}
+$asFileContent = @'
+[Unicode]
+Unicode=yes
+[System Access]
+[Event Audit]
+[Registry Values]
+[Version]
+signature="$CHICAGO$"
+Revision=1
+[Profile Description]
+Description=GrantLogOnAsAService security template
+[Privilege Rights]
+{0}*{1}
+'@ -f $(
+ if($sCurrServiceLogonRight){"$sCurrServiceLogonRight,"}
+ else{'SeServiceLogonRight = '}
+ ), $oSid.Value
+Set-Content -Path $sImportFile -Value $asFileContent
+secedit /import /db $sSecDb /cfg $sImportFile
+secedit /configure /db $sSecDb
+Remove-Item -Path $sExportFile
+Remove-Item -Path $sSecDb
+Remove-Item -Path $sImportFile
+ """ % (sUser,);
+ fRc = self.uploadString(oGuestSession, sSecPolicyEditor, 'C:\\Temp\\adjustsec.ps1');
+ if not fRc:
+ reporter.error('Upload the adjustsec.ps1 failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession,
+ 'Setting the "Logon as service" policy to the user %s' % sUser,
+ 300 * 1000, 'C:\\Windows\\System32\\cmd.exe',
+ ['cmd.exe', '/c', "type C:\\Temp\\adjustsec.ps1 | powershell -"],
+ False, True);
+ if not fRc:
+ reporter.error('Setting the "Logon as service" policy to the user %s failed' % sUser);
+ try:
+ oGuestSession.fsObjRemove('C:\\Temp\\adjustsec.ps1');
+ except:
+ fRc = reporter.errorXcpt('Removing policy script failed');
+ reporter.testDone();
+ return fRc;
+
+class tdAutostart(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Autostart testcase.
+ """
+ ksOsLinux = 'tst-linux'
+ ksOsWindows = 'tst-win'
+ ksOsDarwin = 'tst-darwin'
+ ksOsSolaris = 'tst-solaris'
+ ksOsFreeBSD = 'tst-freebsd'
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = [self.ksOsWindows, self.ksOsLinux]; #[self.ksOsLinux, self.ksOsWindows];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ ## @todo r=bird: The --test-build-dirs option as primary way to get the installation files to test
+ ## is not an acceptable test practice as we don't know wtf you're testing. See defect for more.
+ self.asTestBuildDirs = [os.path.join(self.sScratchPath, 'bin'),];
+ self.sGuestAdditionsIso = None; #'D:/AlexD/TestBox/TestAdditionalFiles/VBoxGuestAdditions_6.1.2.iso';
+ oSet = vboxtestvms.TestVmSet(self.oTestVmManager, acCpus = [2], asVirtModes = ['hwvirt-np',], fIgnoreSkippedVm = True);
+ # pylint: disable=line-too-long
+ self.asTestVmClasses = {
+ 'win' : tdAutostartOsWin(oSet, self, self.ksOsWindows, 'Windows7_64', \
+ '6.0/windows7piglit/windows7piglit.vdi', eNic0Type = None, cMbRam = 2048, \
+ cCpus = 2, fPae = True, sGuestAdditionsIso = self.getGuestAdditionsIso()),
+ 'linux' : tdAutostartOsLinux(oSet, self, self.ksOsLinux, 'Ubuntu_64', \
+ '6.0/ub1804piglit/ub1804piglit.vdi', eNic0Type = None, \
+ cMbRam = 2048, cCpus = 2, fPae = None, sGuestAdditionsIso = self.getGuestAdditionsIso()),
+ 'solaris' : None, #'tdAutostartOsSolaris',
+ 'darwin' : None #'tdAutostartOsDarwin'
+ };
+ oSet.aoTestVms.extend([oTestVm for oTestVm in self.asTestVmClasses.values() if oTestVm is not None]);
+ sOs = self.getBuildOs();
+ if sOs in self.asTestVmClasses:
+ for oTestVM in oSet.aoTestVms:
+ if oTestVM is not None:
+ oTestVM.fSkip = oTestVM != self.asTestVmClasses[sOs];
+
+ # pylint: enable=line-too-long
+ self.oTestVmSet = oSet;
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAutostart Options:');
+ reporter.log(' --test-build-dirs <path1[,path2[,...]]>');
+ reporter.log(' The list of directories with VirtualBox distros. Overrides default path.');
+ reporter.log(' Default path is $TESTBOX_SCRATCH_PATH/bin.');
+ reporter.log(' --vbox-<os>-build <path>');
+ reporter.log(' The path to vbox build for the specified OS.');
+ reporter.log(' The OS can be one of "win", "linux", "solaris" and "darwin".');
+ reporter.log(' This option alse enables corresponding VM for testing.');
+ reporter.log(' (Default behaviour is testing only VM having host-like OS.)');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--test-build-dirs':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-build-dirs" takes a path argument');
+ self.asTestBuildDirs = asArgs[iArg].split(',');
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.asTestBuildDirs = self.asTestBuildDirs;
+ elif asArgs[iArg] in [ '--vbox-%s-build' % sKey for sKey in self.asTestVmClasses]:
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "%s" take a path argument' % (asArgs[iArg - 1],));
+ oMatch = re.match("--vbox-([^-]+)-build", asArgs[iArg - 1]);
+ if oMatch is not None:
+ sOs = oMatch.group(1);
+ oTestVm = self.asTestVmClasses.get(sOs);
+ if oTestVm is not None:
+ oTestVm.sTestBuild = asArgs[iArg];
+ oTestVm.fSkip = False;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testAutostartOneCfg)
+
+ #
+ # Test execution helpers.
+ #
+ def testAutostartOneCfg(self, oVM, oTestVm):
+ # Reconfigure the VM
+ fRc = True;
+ self.logVmInfo(oVM);
+ oSession = self.startVmByName(oTestVm.sVmName);
+ if oSession is not None:
+ sTestUserAllow = 'test1';
+ sTestUserDeny = 'test2';
+ sTestVmName = 'TestVM';
+ #wait the VM is ready after starting
+ (fRc, oGuestSession) = oTestVm.waitVmIsReady(oSession, True);
+ #install fresh guest additions
+ if fRc:
+ (fRc, oGuestSession) = oTestVm.installAdditions(oSession, oGuestSession, oVM);
+ # Create two new users
+ fRc = fRc and oTestVm.createUser(oGuestSession, sTestUserAllow);
+ fRc = fRc and oTestVm.createUser(oGuestSession, sTestUserDeny);
+ if fRc is True:
+ # Install VBox first
+ fRc = oTestVm.installVirtualBox(oGuestSession);
+ if fRc is True:
+ fRc = oTestVm.configureAutostart(oGuestSession, 'allow', (sTestUserAllow,), (sTestUserDeny,));
+ if fRc is True:
+ # Create a VM with autostart enabled in the guest for both users
+ fRc = oTestVm.createTestVM(oSession, oGuestSession, sTestUserAllow, sTestVmName);
+ fRc = fRc and oTestVm.createTestVM(oSession, oGuestSession, sTestUserDeny, sTestVmName);
+ if fRc is True:
+ # Reboot the guest
+ (fRc, oGuestSession) = oTestVm.rebootVMAndCheckReady(oSession, oGuestSession);
+ if fRc is True:
+ # Fudge factor - Allow the guest VMs to finish starting up.
+ self.sleep(60);
+ fRc = oTestVm.checkForRunningVM(oSession, oGuestSession, sTestUserAllow, sTestVmName);
+ if fRc is True:
+ fRc = oTestVm.checkForRunningVM(oSession, oGuestSession, sTestUserDeny, sTestVmName);
+ if fRc is True:
+ reporter.error('Test VM is running inside the guest for denied user');
+ fRc = not fRc;
+ else:
+ reporter.error('Test VM is not running inside the guest for allowed user');
+ else:
+ reporter.error('Rebooting the guest failed');
+ else:
+ reporter.error('Creating test VM failed');
+ else:
+ reporter.error('Configuring autostart in the guest failed');
+ else:
+ reporter.error('Installing VirtualBox in the guest failed');
+ else:
+ reporter.error('Creating test users failed');
+ if oGuestSession is not None:
+ try: oTestVm.powerDownVM(oGuestSession);
+ except: pass;
+ try: self.terminateVmBySession(oSession);
+ except: pass;
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdAutostart().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk b/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk
new file mode 100644
index 00000000..fb89639d
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Benchmarks.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsBenchmarks
+ValidationKitTestsBenchmarks_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsBenchmarks_INST = $(INST_VALIDATIONKIT)tests/benchmarks/
+ValidationKitTestsBenchmarks_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdBenchmark1.py \
+ $(PATH_SUB_CURRENT)/tdBenchmark2.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsBenchmarks_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py
new file mode 100755
index 00000000..33f34cc6
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Test that runs various benchmarks.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxtestvms;
+
+
+class tdBenchmark1(vbox.TestDriver):
+ """
+ Benchmark #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ oTestVm = vboxtestvms.BootSectorTestVm(self.oTestVmSet, 'tst-bs-test1',
+ os.path.join(self.sVBoxBootSectors, 'bootsector2-test1.img'));
+ self.oTestVmSet.aoTestVms.append(oTestVm);
+
+
+ #
+ # Overridden methods.
+ #
+
+
+ def actionConfig(self):
+ self._detectValidationKit();
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneCfg(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ fRc = False;
+
+ sXmlFile = self.prepareResultFile();
+ asEnv = [ 'IPRT_TEST_FILE=' + sXmlFile];
+
+ self.logVmInfo(oVM);
+ oSession = self.startVm(oVM, sName = oTestVm.sVmName, asEnv = asEnv);
+ if oSession is not None:
+ cMsTimeout = 15*60*1000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = self.adjustTimeoutMs(180 * 60000);
+
+ oRc = self.waitForTasks(cMsTimeout);
+ if oRc == oSession:
+ fRc = oSession.assertPoweredOff();
+ else:
+ reporter.error('oRc=%s, expected %s' % (oRc, oSession));
+
+ reporter.addSubXmlFile(sXmlFile);
+ self.terminateVmBySession(oSession);
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdBenchmark1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark2.py b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark2.py
new file mode 100755
index 00000000..4eda0ddf
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark2.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdBenchmark2.py $
+
+"""
+VirtualBox Validation Kit - Test that runs various benchmarks.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+
+
+class tdBenchmark2(vbox.TestDriver):
+ """
+ Benchmark #2 - Memory.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ oTestVm = vboxtestvms.BootSectorTestVm(self.oTestVmSet, 'tst-bs-memalloc-1',
+ os.path.join(self.sVBoxBootSectors, 'bs3-memalloc-1.img'));
+ self.oTestVmSet.aoTestVms.append(oTestVm);
+
+
+ #
+ # Overridden methods.
+ #
+
+
+ def actionConfig(self):
+ self._detectValidationKit();
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneCfg(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ fRc = False;
+
+ #
+ # Determin the RAM configurations we want to test.
+ #
+ cMbMaxGuestRam = self.oVBox.systemProperties.maxGuestRAM;
+ cMbHostAvail = self.oVBox.host.memoryAvailable;
+ cMbHostTotal = self.oVBox.host.memorySize;
+ reporter.log('cMbMaxGuestRam=%s cMbHostAvail=%s cMbHostTotal=%s' % (cMbMaxGuestRam, cMbHostAvail, cMbHostTotal,));
+
+ cMbHostAvail -= cMbHostAvail // 7; # Rough 14% safety/overhead margin.
+ if cMbMaxGuestRam < cMbHostAvail:
+ # Currently: 2048 GiB, 1536 GiB, 1024 GiB, 512 GiB, 256 GiB, 128 GiB, 64 GiB, 32 GiB
+ acMbRam = [ cMbMaxGuestRam, cMbMaxGuestRam // 4 * 3, cMbMaxGuestRam // 2, cMbMaxGuestRam // 4,
+ cMbMaxGuestRam // 8, cMbMaxGuestRam // 16 ];
+ if acMbRam[-1] > 64*1024:
+ acMbRam.append(64*1024);
+ if acMbRam[-1] > 32*1024:
+ acMbRam.append(32*1024);
+ elif cMbHostAvail > 8*1024:
+ # First entry is available memory rounded down to the nearest 8 GiB
+ cMbHostAvail = cMbHostAvail & ~(8 * 1024 - 1);
+ acMbRam = [ cMbHostAvail, ];
+
+ # The remaining entries are powers of two below that, up to 6 of these stopping at 16 GiB.
+ cMb = 8*1024;
+ while cMb < cMbHostAvail:
+ cMb *= 2;
+ while len(acMbRam) < 7 and cMb > 16 * 1024:
+ cMb //= 2;
+ acMbRam.append(cMb);
+ elif cMbHostAvail >= 16000 and cMbHostAvail > 7168:
+ # Desperate attempt at getting some darwin testruns too. We've got two
+ # with 16 GiB and they usually end up with just short of 8GiB of free RAM.
+ acMbRam = [7168,];
+ else:
+ reporter.log("Less than 8GB of host RAM available for VMs, skipping test");
+ return None;
+ reporter.log("RAM configurations: %s" % (acMbRam));
+
+ # Large pages only work with nested paging.
+ afLargePages = [False, ];
+ try:
+ if oVM.getHWVirtExProperty(vboxcon.HWVirtExPropertyType_NestedPaging):
+ afLargePages = [True, False];
+ except:
+ return reporter.errorXcpt("Failed to get HWVirtExPropertyType_NestedPaging");
+
+ #
+ # Test the RAM configurations.
+ #
+ for fLargePages in afLargePages:
+ sLargePages = 'large pages' if fLargePages is True else 'no large pages';
+ for cMbRam in acMbRam:
+ reporter.testStart('%s MiB, %s' % (cMbRam, sLargePages));
+
+ # Reconfigure the VM:
+ fRc = False
+ oSession = self.openSession(oVM);
+ if oSession:
+ fRc = oSession.setRamSize(cMbRam);
+ fRc = oSession.setLargePages(fLargePages) and fRc;
+ if fRc:
+ fRc = oSession.saveSettings();
+ if not fRc:
+ oSession.discardSettings(True);
+ oSession.close();
+ if fRc:
+ # Set up the result file
+ sXmlFile = self.prepareResultFile();
+ asEnv = [ 'IPRT_TEST_FILE=' + sXmlFile, ];
+
+ # Do the test:
+ self.logVmInfo(oVM);
+ oSession = self.startVm(oVM, sName = oTestVm.sVmName, asEnv = asEnv);
+ if oSession is not None:
+ cMsTimeout = 15 * 60000 + cMbRam // 168;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = self.adjustTimeoutMs(180 * 60000 + cMbRam // 168);
+
+ oRc = self.waitForTasks(cMsTimeout);
+ if oRc == oSession:
+ fRc = oSession.assertPoweredOff();
+ else:
+ reporter.error('oRc=%s, expected %s' % (oRc, oSession));
+
+ reporter.addSubXmlFile(sXmlFile);
+ self.terminateVmBySession(oSession);
+ else:
+ reporter.errorXcpt("Failed to set memory size to %s MiB or setting largePages to %s" % (cMbRam, fLargePages));
+ reporter.testDone();
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdBenchmark2().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/cpu/Makefile.kmk b/src/VBox/ValidationKit/tests/cpu/Makefile.kmk
new file mode 100644
index 00000000..b72fa098
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/cpu/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - CPU Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsCpu
+ValidationKitTestsCpu_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsCpu_INST = $(INST_VALIDATIONKIT)tests/cpu/
+ValidationKitTestsCpu_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdCpuPae1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsCpu_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py b/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py
new file mode 100755
index 00000000..b0d6c342
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py
@@ -0,0 +1,264 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdCpuPae1.py $
+
+"""
+VirtualBox Validation Kit - Catch PAE not enabled.
+
+Test that switching into PAE mode when it isn't enable, check that it produces
+the right runtime error.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+
+
+class tdCpuPae1ConsoleCallbacks(vbox.ConsoleEventHandlerBase):
+ """
+ For catching the PAE runtime error.
+ """
+ def __init__(self, dArgs):
+ oTstDrv = dArgs['oTstDrv'];
+ oVBoxMgr = dArgs['oVBoxMgr']; _ = oVBoxMgr;
+
+ vbox.ConsoleEventHandlerBase.__init__(self, dArgs, 'tdCpuPae1');
+ self.oTstDrv = oTstDrv;
+
+ def onRuntimeError(self, fFatal, sErrId, sMessage):
+ """ Verify the error. """
+ reporter.log('onRuntimeError: fFatal=%s sErrId="%s" sMessage="%s"' % (fFatal, sErrId, sMessage));
+ if sErrId != 'PAEmode':
+ reporter.testFailure('sErrId=%s, expected PAEmode' % (sErrId,));
+ elif fFatal is not True:
+ reporter.testFailure('fFatal=%s, expected True' % (fFatal,));
+ else:
+ self.oTstDrv.fCallbackSuccess = True;
+ self.oTstDrv.fCallbackFired = True;
+ self.oVBoxMgr.interruptWaitEvents();
+ return None;
+
+
+class tdCpuPae1(vbox.TestDriver):
+ """
+ PAE Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asSkipTests = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.fCallbackFired = False;
+ self.fCallbackSuccess = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdCpuPae1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes raw --cpu-counts 1 32');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--quick':
+ self.asVirtModes = ['raw',];
+ self.acCpus = [1,];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ return [];
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure a VM with the PAE bootsector as floppy image.
+ #
+
+ oVM = self.createTestVM('tst-bs-pae', 2, sKind = 'Other', fVirtEx = False, fPae = False, \
+ sFloppy = os.path.join(self.sVBoxBootSectors, 'bootsector-pae.img') );
+ if oVM is None:
+ return False;
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.test1();
+
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1OneCfg(self, oVM, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupBootLogo(True, 2500); # Race avoidance fudge.
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Zap the state (used by the callback).
+ self.fCallbackFired = False;
+ self.fCallbackSuccess = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession = self.startVm(oVM)
+ if oSession is not None:
+ # Set up a callback for catching the runtime error. !Races the guest bootup!
+ oConsoleCallbacks = oSession.registerDerivedEventHandler(tdCpuPae1ConsoleCallbacks, {'oTstDrv':self,})
+
+ fRc = False;
+ if oConsoleCallbacks is not None:
+ # Wait for 30 seconds for something to finish.
+ tsStart = base.timestampMilli();
+ while base.timestampMilli() - tsStart < 30000:
+ oTask = self.waitForTasks(1000);
+ if oTask is not None:
+ break;
+ if self.fCallbackFired:
+ break;
+ if not self.fCallbackFired:
+ reporter.testFailure('the callback did not fire');
+ fRc = self.fCallbackSuccess;
+
+ # cleanup.
+ oConsoleCallbacks.unregister();
+ self.terminateVmBySession(oSession) #, fRc);
+ else:
+ fRc = False;
+ return fRc;
+
+
+ def test1(self):
+ """
+ Executes test #1 - Negative API testing.
+
+ ASSUMES that the VMs are
+ """
+ reporter.testStart('Test 1');
+ oVM = self.getVmByName('tst-bs-pae');
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ self.test1OneCfg(oVM, cCpus, fHwVirt, fNestedPaging);
+
+ reporter.testDone();
+ reporter.testDone();
+
+ return reporter.testDone()[1] == 0;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdCpuPae1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/installation/Makefile.kmk b/src/VBox/ValidationKit/tests/installation/Makefile.kmk
new file mode 100644
index 00000000..b901296f
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Automatic guest OS installation tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitInstallationTests
+ValidationKitInstallationTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitInstallationTests_INST = $(INST_VALIDATIONKIT)tests/installation/
+ValidationKitInstallationTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdGuestOsInstTest1.py \
+ $(PATH_SUB_CURRENT)/tdGuestOsInstOs2.py \
+ $(PATH_SUB_CURRENT)/tdGuestOsUnattendedInst1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitInstallationTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py
new file mode 100755
index 00000000..caba4e76
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdGuestOsInstOs2.py $
+
+"""
+VirtualBox Validation Kit - OS/2 install tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class tdGuestOsInstOs2(vbox.TestDriver):
+ """
+ OS/2 unattended installation.
+
+ Scenario:
+ - Create new VM that corresponds specified installation ISO image
+ - Create HDD that corresponds to OS type that will be installed
+ - Set VM boot order: HDD, Floppy, ISO
+ - Start VM: sinse there is no OS installed on HDD, VM will booted from floppy
+ - After first reboot VM will continue installation from HDD automatically
+ - Wait for incomming TCP connection (guest should initiate such a
+ connection in case installation has been completed successfully)
+ """
+
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+
+ # VM parameters required to run ISO image.
+ # Format: (cBytesHdd, sKind)
+ kaoVmParams = {
+ 'acp2-txs.iso': ( 2*1024*1024*1024, 'OS2', ksIdeController ),
+ 'mcp2-txs.iso': ( 2*1024*1024*1024, 'OS2', ksIdeController ),
+ }
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+
+ self.sVmName = 'TestVM'
+ self.sHddName = 'TestHdd.vdi'
+ self.sIso = None
+ self.sFloppy = None
+ self.sIsoPathBase = os.path.join(self.sResourcePath, '4.2', 'isos')
+ self.fEnableIOAPIC = True
+ self.cCpus = 1
+ self.fEnableNestedPaging = True
+ self.fEnablePAE = False
+ self.asExtraData = []
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log(' --install-iso <ISO file name>')
+ reporter.log(' --cpus <# CPUs>')
+ reporter.log(' --no-ioapic')
+ reporter.log(' --no-nested-paging')
+ reporter.log(' --pae')
+ reporter.log(' --set-extradata <key>:value')
+ reporter.log(' Set VM extra data. This command line option might be used multiple times.')
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+ if asArgs[iArg] == '--install-iso':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--install-iso" option requires an argument')
+ self.sIso = asArgs[iArg]
+ elif asArgs[iArg] == '--cpus':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpus" option requires an argument')
+ self.cCpus = int(asArgs[iArg])
+ elif asArgs[iArg] == '--no-ioapic':
+ self.fEnableIOAPIC = False
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.fEnableNestedPaging = False
+ elif asArgs[iArg] == '--pae':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--extra-mem':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--set-extradata':
+ iArg = self.requireMoreArgs(1, asArgs, iArg)
+ self.asExtraData.append(asArgs[iArg])
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def actionConfig(self):
+ """
+ Configure pre-conditions.
+ """
+
+ if not self.importVBoxApi():
+ return False
+
+ assert self.sIso is not None
+ if self.sIso not in self.kaoVmParams:
+ reporter.log('Error: unknown ISO image specified: %s' % self.sIso)
+ return False
+
+ # Get VM params specific to ISO image
+ cBytesHdd, sKind, sController = self.kaoVmParams[self.sIso]
+
+ # Create VM itself
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT
+ self.sIso = os.path.join(self.sIsoPathBase, self.sIso)
+ assert os.path.isfile(self.sIso)
+
+ self.sFloppy = os.path.join(self.sIsoPathBase, os.path.splitext(self.sIso)[0] + '.img')
+
+ oVM = self.createTestVM(self.sVmName, 1, sKind = sKind, sDvdImage = self.sIso, cCpus = self.cCpus,
+ sFloppy = self.sFloppy, eNic0AttachType = eNic0AttachType)
+ assert oVM is not None
+
+ oSession = self.openSession(oVM)
+
+ # Create HDD
+ sHddPath = os.path.join(self.sScratchPath, self.sHddName)
+ fRc = True
+ if sController == self.ksSataController:
+ fRc = oSession.setStorageControllerType(vboxcon.StorageControllerType_IntelAhci, sController)
+
+ fRc = fRc and oSession.createAndAttachHd(sHddPath, cb = cBytesHdd,
+ sController = sController, iPort = 0, fImmutable=False)
+ if sController == self.ksSataController:
+ fRc = fRc and oSession.setStorageControllerPortCount(sController, 1)
+
+ # Set proper boot order
+ fRc = fRc and oSession.setBootOrder(1, vboxcon.DeviceType_HardDisk)
+ fRc = fRc and oSession.setBootOrder(2, vboxcon.DeviceType_Floppy)
+
+ # Enable HW virt
+ fRc = fRc and oSession.enableVirtEx(True)
+
+ # Enable I/O APIC
+ fRc = fRc and oSession.enableIoApic(self.fEnableIOAPIC)
+
+ # Enable Nested Paging
+ fRc = fRc and oSession.enableNestedPaging(self.fEnableNestedPaging)
+
+ # Enable PAE
+ fRc = fRc and oSession.enablePae(self.fEnablePAE)
+
+ # Remote desktop
+ oSession.setupVrdp(True)
+
+ # Set extra data
+ if self.asExtraData:
+ for sExtraData in self.asExtraData:
+ try:
+ sKey, sValue = sExtraData.split(':')
+ except ValueError:
+ raise base.InvalidOption('Invalid extradata specified: %s' % sExtraData)
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue))
+ fRc = fRc and oSession.setExtraData(sKey, sValue)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close()
+ assert fRc is True
+
+ return vbox.TestDriver.actionConfig(self)
+
+ def actionExecute(self):
+ """
+ Execute the testcase itself.
+ """
+ if not self.importVBoxApi():
+ return False
+ return self.testDoInstallGuestOs()
+
+ #
+ # Test execution helpers.
+ #
+
+ def testDoInstallGuestOs(self):
+ """
+ Install guest OS and wait for result
+ """
+ reporter.testStart('Installing %s' % (os.path.basename(self.sIso),))
+
+ cMsTimeout = 40*60000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = 180 * 60000; # will be adjusted down.
+
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(self.sVmName, fCdWait = False, cMsTimeout = cMsTimeout)
+ if oSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest reported success')
+ reporter.testDone()
+ fRc = self.terminateVmBySession(oSession)
+ return fRc is True
+ reporter.error('Installation of %s has failed' % (self.sIso,))
+ reporter.testDone()
+ return False
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsInstOs2().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py
new file mode 100755
index 00000000..9c5c4ceb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py
@@ -0,0 +1,409 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdGuestOsInstTest1.py $
+
+"""
+VirtualBox Validation Kit - Guest OS installation tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+
+
+class InstallTestVm(vboxtestvms.TestVm):
+ """ Installation test VM. """
+
+ ## @name The primary controller, to which the disk will be attached.
+ ## @{
+ ksScsiController = 'SCSI Controller'
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+ ## @}
+
+ ## @name VM option flags (OR together).
+ ## @{
+ kf32Bit = 0x01;
+ kf64Bit = 0x02;
+ # most likely for ancient Linux kernels assuming that AMD processors have always an I/O-APIC
+ kfReqIoApic = 0x10;
+ kfReqIoApicSmp = 0x20;
+ kfReqPae = 0x40;
+ kfIdeIrqDelay = 0x80;
+ kfUbuntuNewAmdBug = 0x100;
+ kfNoWin81Paravirt = 0x200;
+ ## @}
+
+ ## IRQ delay extra data config for win2k VMs.
+ kasIdeIrqDelay = [ 'VBoxInternal/Devices/piix3ide/0/Config/IRQDelay:1', ];
+
+ ## Install ISO path relative to the testrsrc root.
+ ksIsoPathBase = os.path.join('4.2', 'isos');
+
+
+ def __init__(self, oSet, sVmName, sKind, sInstallIso, sHdCtrlNm, cGbHdd, fFlags):
+ vboxtestvms.TestVm.__init__(self, sVmName, oSet = oSet, sKind = sKind, sHddControllerType = sHdCtrlNm,
+ fRandomPvPMode = (fFlags & self.kfNoWin81Paravirt) == 0);
+ self.sDvdImage = os.path.join(self.ksIsoPathBase, sInstallIso);
+ self.cGbHdd = cGbHdd;
+ self.fInstVmFlags = fFlags;
+ if fFlags & self.kfReqPae:
+ self.fPae = True;
+ if fFlags & (self.kfReqIoApic | self.kfReqIoApicSmp):
+ self.fIoApic = True;
+
+ # Tweaks
+ self.iOptRamAdjust = 0;
+ self.asExtraData = [];
+ if fFlags & self.kfIdeIrqDelay:
+ self.asExtraData = self.kasIdeIrqDelay;
+
+ def detatchAndDeleteHd(self, oTestDrv):
+ """
+ Detaches and deletes the HD.
+ Returns success indicator, error info logged.
+ """
+ fRc = False;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is not None:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ (fRc, oHd) = oSession.detachHd(self.sHddControllerType, iPort = 0, iDevice = 0);
+ if fRc is True and oHd is not None:
+ fRc = oSession.saveSettings();
+ fRc = fRc and oTestDrv.oVBox.deleteHdByMedium(oHd);
+ fRc = fRc and oSession.saveSettings(); # Necessary for media reg?
+ fRc = oSession.close() and fRc;
+ return fRc;
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ #
+ # Do the standard reconfig in the base class first, it'll figure out
+ # if we can run the VM as requested.
+ #
+ (fRc, oVM) = vboxtestvms.TestVm.getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode);
+
+ #
+ # Make sure there is no HD from the previous run attached nor taking
+ # up storage on the host.
+ #
+ if fRc is True:
+ fRc = self.detatchAndDeleteHd(oTestDrv);
+
+ #
+ # Check for ubuntu installer vs. AMD host CPU.
+ #
+ if fRc is True and (self.fInstVmFlags & self.kfUbuntuNewAmdBug):
+ if self.isHostCpuAffectedByUbuntuNewAmdBug(oTestDrv):
+ return (None, None); # (skip)
+
+ #
+ # Make adjustments to the default config, and adding a fresh HD.
+ #
+ if fRc is True:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ if self.sHddControllerType == self.ksSataController:
+ fRc = fRc and oSession.setStorageControllerType(vboxcon.StorageControllerType_IntelAhci,
+ self.sHddControllerType);
+ fRc = fRc and oSession.setStorageControllerPortCount(self.sHddControllerType, 1);
+ elif self.sHddControllerType == self.ksScsiController:
+ fRc = fRc and oSession.setStorageControllerType(vboxcon.StorageControllerType_LsiLogic,
+ self.sHddControllerType);
+ try:
+ sHddPath = os.path.join(os.path.dirname(oVM.settingsFilePath),
+ '%s-%s-%s.vdi' % (self.sVmName, sVirtMode, cCpus,));
+ except:
+ reporter.errorXcpt();
+ sHddPath = None;
+ fRc = False;
+
+ fRc = fRc and oSession.createAndAttachHd(sHddPath,
+ cb = self.cGbHdd * 1024*1024*1024,
+ sController = self.sHddControllerType,
+ iPort = 0,
+ fImmutable = False);
+
+ # Set proper boot order
+ fRc = fRc and oSession.setBootOrder(1, vboxcon.DeviceType_HardDisk)
+ fRc = fRc and oSession.setBootOrder(2, vboxcon.DeviceType_DVD)
+
+ # Adjust memory if requested.
+ if self.iOptRamAdjust != 0:
+ fRc = fRc and oSession.setRamSize(oSession.o.machine.memorySize + self.iOptRamAdjust);
+
+ # Set extra data
+ for sExtraData in self.asExtraData:
+ try:
+ sKey, sValue = sExtraData.split(':')
+ except ValueError:
+ raise base.InvalidOption('Invalid extradata specified: %s' % sExtraData)
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue))
+ fRc = fRc and oSession.setExtraData(sKey, sValue)
+
+ # Other variations?
+
+ # Save the settings.
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc;
+ else:
+ fRc = False;
+ if fRc is not True:
+ oVM = None;
+
+ # Done.
+ return (fRc, oVM)
+
+
+
+
+
+class tdGuestOsInstTest1(vbox.TestDriver):
+ """
+ Guest OS installation tests.
+
+ Scenario:
+ - Create new VM that corresponds specified installation ISO image.
+ - Create HDD that corresponds to OS type that will be installed.
+ - Boot VM from ISO image (i.e. install guest OS).
+ - Wait for incomming TCP connection (guest should initiate such a
+ connection in case installation has been completed successfully).
+ """
+
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+ self.fLegacyOptions = False;
+ assert self.fEnableVrdp; # in parent driver.
+
+ #
+ # Our install test VM set.
+ #
+ oSet = vboxtestvms.TestVmSet(self.oTestVmManager, fIgnoreSkippedVm = True);
+ oSet.aoTestVms.extend([
+ # pylint: disable=line-too-long
+ InstallTestVm(oSet, 'tst-fedora4', 'Fedora', 'fedora4-txs.iso', InstallTestVm.ksIdeController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-fedora5', 'Fedora', 'fedora5-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApicSmp),
+ InstallTestVm(oSet, 'tst-fedora6', 'Fedora', 'fedora6-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-fedora7', 'Fedora', 'fedora7-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-fedora9', 'Fedora', 'fedora9-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-fedora18-64', 'Fedora_64', 'fedora18-x64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-fedora18', 'Fedora', 'fedora18-txs.iso', InstallTestVm.ksScsiController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-ols6', 'Oracle', 'ols6-i386-txs.iso', InstallTestVm.ksSataController, 12, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ols6-64', 'Oracle_64', 'ols6-x86_64-txs.iso', InstallTestVm.ksSataController, 12, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-rhel5', 'RedHat', 'rhel5-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-suse102', 'OpenSUSE', 'opensuse102-txs.iso', InstallTestVm.ksIdeController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqIoApic),
+ ## @todo InstallTestVm(oSet, 'tst-ubuntu606', 'Ubuntu', 'ubuntu606-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ ## @todo InstallTestVm(oSet, 'tst-ubuntu710', 'Ubuntu', 'ubuntu710-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-ubuntu804', 'Ubuntu', 'ubuntu804-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-ubuntu804-64', 'Ubuntu_64', 'ubuntu804-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-ubuntu904', 'Ubuntu', 'ubuntu904-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ubuntu904-64', 'Ubuntu_64', 'ubuntu904-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ #InstallTestVm(oSet, 'tst-ubuntu1404', 'Ubuntu', 'ubuntu1404-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae), bird: Is 14.04 one of the 'older ones'?
+ InstallTestVm(oSet, 'tst-ubuntu1404', 'Ubuntu', 'ubuntu1404-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ubuntu1404-64','Ubuntu_64', 'ubuntu1404-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-debian7', 'Debian', 'debian-7.0.0-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-debian7-64', 'Debian_64', 'debian-7.0.0-x64-txs.iso', InstallTestVm.ksScsiController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-vista-64', 'WindowsVista_64', 'vista-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-vista-32', 'WindowsVista', 'vista-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w7-64', 'Windows7_64', 'win7-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-w7-32', 'Windows7', 'win7-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w2k3', 'Windows2003', 'win2k3ent-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w2k', 'Windows2000', 'win2ksp0-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfIdeIrqDelay),
+ InstallTestVm(oSet, 'tst-w2ksp4', 'Windows2000', 'win2ksp4-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfIdeIrqDelay),
+ InstallTestVm(oSet, 'tst-wxp', 'WindowsXP', 'winxppro-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-wxpsp2', 'WindowsXP', 'winxpsp2-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-wxp64', 'WindowsXP_64', 'winxp64-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf64Bit),
+ ## @todo disable paravirt for Windows 8.1 guests as long as it's not fixed in the code
+ InstallTestVm(oSet, 'tst-w81-32', 'Windows81', 'win81-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w81-64', 'Windows81_64', 'win81-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-w10-32', 'Windows10', 'win10-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-w10-64', 'Windows10_64', 'win10-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ # pylint: enable=line-too-long
+ ]);
+ self.oTestVmSet = oSet;
+
+
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log('');
+ reporter.log('tdGuestOsInstTest1 options:');
+ reporter.log(' --ioapic, --no-ioapic');
+ reporter.log(' Enable or disable the I/O apic.');
+ reporter.log(' Default: --ioapic');
+ reporter.log(' --pae, --no-pae');
+ reporter.log(' Enable or disable PAE support for 32-bit guests.');
+ reporter.log(' Default: Guest dependent.');
+ reporter.log(' --ram-adjust <MBs>')
+ reporter.log(' Adjust the VM ram size by the given delta. Both negative and positive');
+ reporter.log(' values are accepted.');
+ reporter.log(' --set-extradata <key>:value')
+ reporter.log(' Set VM extra data. This command line option might be used multiple times.')
+ reporter.log('obsolete:');
+ reporter.log(' --nested-paging, --no-nested-paging');
+ reporter.log(' --raw-mode');
+ reporter.log(' --cpus <# CPUs>');
+ reporter.log(' --install-iso <ISO file name>');
+
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+
+ if False is True:
+ pass;
+ elif asArgs[iArg] == '--ioapic':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fIoApic = True;
+ elif asArgs[iArg] == '--no-ioapic':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fIoApic = False;
+ elif asArgs[iArg] == '--pae':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fPae = True;
+ elif asArgs[iArg] == '--no-pae':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fPae = False;
+ elif asArgs[iArg] == '--ram-adjust':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.iOptRamAdjust = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--set-extradata':
+ iArg = self.requireMoreArgs(1, asArgs, iArg)
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.asExtraData.append(asArgs[iArg]);
+
+ # legacy, to be removed once TM is reconfigured.
+ elif asArgs[iArg] == '--install-iso':
+ self.legacyOptions();
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fSkip = os.path.basename(oTestVm.sDvdImage) != asArgs[iArg];
+ elif asArgs[iArg] == '--cpus':
+ self.legacyOptions();
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self.oTestVmSet.acCpus = [ int(asArgs[iArg]), ];
+ elif asArgs[iArg] == '--raw-mode':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'raw', ];
+ elif asArgs[iArg] == '--nested-paging':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'hwvirt-np', ];
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'hwvirt', ];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def legacyOptions(self):
+ """ Enables legacy option mode. """
+ if not self.fLegacyOptions:
+ self.fLegacyOptions = True;
+ self.oTestVmSet.asVirtModes = [ 'hwvirt', ];
+ self.oTestVmSet.acCpus = [ 1, ];
+ return True;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Install guest OS and wait for result
+ """
+
+ self.logVmInfo(oVM)
+ reporter.testStart('Installing %s' % (oTestVm.sVmName,))
+
+ cMsTimeout = 40*60000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = 180 * 60000; # will be adjusted down.
+
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False, cMsTimeout = cMsTimeout);
+ if oSession is not None:
+ # The guest has connected to TXS, so we're done (for now anyways).
+ reporter.log('Guest reported success')
+ ## @todo Do save + restore.
+
+ reporter.testDone()
+ fRc = self.terminateVmBySession(oSession)
+ return fRc is True
+
+ reporter.error('Installation of %s has failed' % (oTestVm.sVmName,))
+ oTestVm.detatchAndDeleteHd(self); # Save space.
+ reporter.testDone()
+ return False
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsInstTest1().main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/installation/tdGuestOsUnattendedInst1.py b/src/VBox/ValidationKit/tests/installation/tdGuestOsUnattendedInst1.py
new file mode 100755
index 00000000..cdc2ef59
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/tdGuestOsUnattendedInst1.py
@@ -0,0 +1,769 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdGuestOsUnattendedInst1.py $
+
+"""
+VirtualBox Validation Kit - Guest OS unattended installation tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import copy;
+import os;
+import sys;
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+from common import utils;
+
+# Sub-test driver imports.
+sys.path.append(os.path.join(g_ksValidationKitDir, 'tests', 'additions'));
+from tdAddGuestCtrl import SubTstDrvAddGuestCtrl;
+from tdAddSharedFolders1 import SubTstDrvAddSharedFolders1;
+
+
+class UnattendedVm(vboxtestvms.BaseTestVm):
+ """ Unattended Installation test VM. """
+
+ ## @name VM option flags (OR together).
+ ## @{
+ kfUbuntuAvx2Crash = 0x0001; ##< Disables AVX2 as ubuntu 16.04 think it means AVX512 is available and compiz crashes.
+ kfNoGAs = 0x0002; ##< No guest additions installation possible.
+ kfKeyFile = 0x0004; ##< ISO requires a .key file containing the product key.
+ kfNeedCom1 = 0x0008; ##< Need serial port, typically for satifying the windows kernel debugger.
+
+ kfIdeIrqDelay = 0x1000;
+ kfUbuntuNewAmdBug = 0x2000;
+ kfNoWin81Paravirt = 0x4000;
+ kfAvoidNetwork = 0x8000;
+ ## @}
+
+ ## kfUbuntuAvx2Crash: Extra data that disables AVX2.
+ kasUbuntuAvx2Crash = [ '/CPUM/IsaExts/AVX2:0', ];
+
+ ## IRQ delay extra data config for win2k VMs.
+ kasIdeIrqDelay = [ 'VBoxInternal/Devices/piix3ide/0/Config/IRQDelay:1', ];
+
+ def __init__(self, oSet, sVmName, sKind, sInstallIso, fFlags = 0):
+ vboxtestvms.BaseTestVm.__init__(self, sVmName, oSet = oSet, sKind = sKind,
+ fRandomPvPModeCrap = (fFlags & self.kfNoWin81Paravirt) == 0);
+ self.sInstallIso = sInstallIso;
+ self.fInstVmFlags = fFlags;
+
+ # Adjustments over the defaults.
+ self.iOptRamAdjust = 0;
+ self.fOptIoApic = None;
+ self.fOptPae = None;
+ self.fOptInstallAdditions = False;
+ self.asOptExtraData = [];
+ if fFlags & self.kfUbuntuAvx2Crash:
+ self.asOptExtraData += self.kasUbuntuAvx2Crash;
+ if fFlags & self.kfIdeIrqDelay:
+ self.asOptExtraData += self.kasIdeIrqDelay;
+ if fFlags & self.kfNeedCom1:
+ self.fCom1RawFile = True;
+
+ def _unattendedConfigure(self, oIUnattended, oTestDrv): # type: (Any, vbox.TestDriver) -> bool
+ """
+ Configures the unattended install object.
+
+ The ISO attribute has been set and detectIsoOS has been done, the rest of the
+ setup is done here.
+
+ Returns True on success, False w/ errors logged on failure.
+ """
+
+ #
+ # Make it install the TXS.
+ #
+ try: oIUnattended.installTestExecService = True;
+ except: return reporter.errorXcpt();
+ try: oIUnattended.validationKitIsoPath = oTestDrv.sVBoxValidationKitIso;
+ except: return reporter.errorXcpt();
+ oTestDrv.processPendingEvents();
+
+ #
+ # Avoid using network during unattended install (stalls Debian installs).
+ #
+ if self.fInstVmFlags & UnattendedVm.kfAvoidNetwork:
+ try: oIUnattended.avoidUpdatesOverNetwork = True;
+ except: return reporter.errorXcpt();
+
+ #
+ # Install GAs?
+ #
+ if self.fOptInstallAdditions:
+ if (self.fInstVmFlags & self.kfNoGAs) == 0:
+ try: oIUnattended.installGuestAdditions = True;
+ except: return reporter.errorXcpt();
+ try: oIUnattended.additionsIsoPath = oTestDrv.getGuestAdditionsIso();
+ except: return reporter.errorXcpt();
+ oTestDrv.processPendingEvents();
+ else:
+ reporter.log("Warning! Ignoring request to install Guest Additions as kfNoGAs is set!");
+
+ #
+ # Product key?
+ #
+ if self.fInstVmFlags & UnattendedVm.kfKeyFile:
+ sKeyFile = '';
+ sKey = '';
+ try:
+ sKeyFile = oIUnattended.isoPath + '.key';
+ oFile = utils.openNoInherit(sKeyFile);
+ for sLine in oFile:
+ sLine = sLine.strip();
+ if sLine and not sLine.startswith(';') and not sLine.startswith('#') and not sLine.startswith('//'):
+ sKey = sLine;
+ break;
+ oFile.close();
+ except:
+ return reporter.errorXcpt('sKeyFile=%s' % (sKeyFile,));
+ if not sKey:
+ return reporter.error('No key in keyfile (%s)!' % (sKeyFile,));
+ try: oIUnattended.productKey = sKey;
+ except: return reporter.errorXcpt();
+
+ return True;
+
+ def _unattendedDoIt(self, oIUnattended, oVM, oTestDrv): # type: (Any, Any, vbox.TestDriver) -> bool
+ """
+ Does the unattended installation preparing, media construction and VM reconfiguration.
+
+ Returns True on success, False w/ errors logged on failure.
+ """
+
+ # Associate oVM with the installer:
+ try:
+ oIUnattended.machine = oVM;
+ except:
+ return reporter.errorXcpt();
+ oTestDrv.processPendingEvents();
+
+ # Prepare and log it:
+ try:
+ oIUnattended.prepare();
+ except:
+ return reporter.errorXcpt("IUnattended.prepare failed");
+ oTestDrv.processPendingEvents();
+
+ reporter.log('IUnattended attributes after prepare():');
+ self._unattendedLogIt(oIUnattended, oTestDrv);
+
+ # Create media:
+ try:
+ oIUnattended.constructMedia();
+ except:
+ return reporter.errorXcpt("IUnattended.constructMedia failed");
+ oTestDrv.processPendingEvents();
+
+ # Reconfigure the VM:
+ try:
+ oIUnattended.reconfigureVM();
+ except:
+ return reporter.errorXcpt("IUnattended.reconfigureVM failed");
+ oTestDrv.processPendingEvents();
+
+ return True;
+
+ def _unattendedLogIt(self, oIUnattended, oTestDrv):
+ """
+ Logs the attributes of the unattended installation object.
+ """
+ fRc = True;
+ asAttribs = ( 'isoPath', 'user', 'password', 'fullUserName', 'productKey', 'additionsIsoPath', 'installGuestAdditions',
+ 'validationKitIsoPath', 'installTestExecService', 'timeZone', 'locale', 'language', 'country', 'proxy',
+ 'packageSelectionAdjustments', 'hostname', 'auxiliaryBasePath', 'imageIndex', 'machine',
+ 'scriptTemplatePath', 'postInstallScriptTemplatePath', 'postInstallCommand',
+ 'extraInstallKernelParameters', 'detectedOSTypeId', 'detectedOSVersion', 'detectedOSLanguages',
+ 'detectedOSFlavor', 'detectedOSHints', );
+ for sAttrib in asAttribs:
+ try:
+ oValue = getattr(oIUnattended, sAttrib);
+ except:
+ fRc = reporter.errorXcpt('sAttrib=%s' % sAttrib);
+ else:
+ reporter.log('%s: %s' % (sAttrib.rjust(32), oValue,));
+ oTestDrv.processPendingEvents();
+ return fRc;
+
+
+ #
+ # Overriden methods.
+ #
+
+ def getResourceSet(self):
+ asRet = [];
+ if not os.path.isabs(self.sInstallIso):
+ asRet.append(self.sInstallIso);
+ if self.fInstVmFlags & UnattendedVm.kfKeyFile:
+ asRet.append(self.sInstallIso + '.key');
+ return asRet;
+
+ def _createVmDoIt(self, oTestDrv, eNic0AttachType, sDvdImage):
+ #
+ # Use HostOnly networking for ubuntu and debian VMs to prevent them from
+ # downloading updates and doing database updates during installation.
+ # We want predicable results.
+ #
+ if eNic0AttachType is None:
+ if self.isLinux() \
+ and ( 'ubuntu' in self.sKind.lower()
+ or 'debian' in self.sKind.lower()):
+ eNic0AttachType = vboxcon.NetworkAttachmentType_HostOnly;
+
+ # Also use it for windows xp to prevent it from ever going online.
+ if self.sKind in ('WindowsXP','WindowsXP_64',):
+ eNic0AttachType = vboxcon.NetworkAttachmentType_HostOnly;
+
+ #
+ # Use host-only networks instead of host-only adapters for trunk builds on Mac OS.
+ #
+ if eNic0AttachType == vboxcon.NetworkAttachmentType_HostOnly \
+ and utils.getHostOs() == 'darwin' \
+ and oTestDrv.fpApiVer >= 7.0:
+ eNic0AttachType = vboxcon.NetworkAttachmentType_HostOnlyNetwork;
+
+ return vboxtestvms.BaseTestVm._createVmDoIt(self, oTestDrv, eNic0AttachType, sDvdImage); # pylint: disable=protected-access
+
+
+ def _createVmPost(self, oTestDrv, oVM, eNic0AttachType, sDvdImage):
+ #
+ # Adjust the ram, I/O APIC and stuff.
+ #
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is None:
+ return None;
+
+ fRc = True;
+
+ ## Set proper boot order - IUnattended::reconfigureVM does this, doesn't it?
+ #fRc = fRc and oSession.setBootOrder(1, vboxcon.DeviceType_HardDisk)
+ #fRc = fRc and oSession.setBootOrder(2, vboxcon.DeviceType_DVD)
+
+ # Adjust memory if requested.
+ if self.iOptRamAdjust != 0:
+ try: cMbRam = oSession.o.machine.memorySize;
+ except: fRc = reporter.errorXcpt();
+ else:
+ fRc = oSession.setRamSize(cMbRam + self.iOptRamAdjust) and fRc;
+
+ # I/O APIC:
+ if self.fOptIoApic is not None:
+ fRc = oSession.enableIoApic(self.fOptIoApic) and fRc;
+
+ # I/O APIC:
+ if self.fOptPae is not None:
+ fRc = oSession.enablePae(self.fOptPae) and fRc;
+
+ # Set extra data
+ for sExtraData in self.asOptExtraData:
+ sKey, sValue = sExtraData.split(':');
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue))
+ fRc = oSession.setExtraData(sKey, sValue) and fRc;
+
+ # Save the settings.
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc;
+
+ return oVM if fRc else None;
+
+ def _skipVmTest(self, oTestDrv, oVM):
+ _ = oVM;
+ #
+ # Check for ubuntu installer vs. AMD host CPU.
+ #
+ if self.fInstVmFlags & self.kfUbuntuNewAmdBug:
+ if self.isHostCpuAffectedByUbuntuNewAmdBug(oTestDrv):
+ return True;
+
+ return vboxtestvms.BaseTestVm._skipVmTest(self, oTestDrv, oVM); # pylint: disable=protected-access
+
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ #
+ # Do the standard reconfig in the base class first, it'll figure out
+ # if we can run the VM as requested.
+ #
+ (fRc, oVM) = vboxtestvms.BaseTestVm.getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode);
+ if fRc is True:
+ #
+ # Make sure there is no HD from the previous run attached nor taking
+ # up storage on the host.
+ #
+ fRc = self.recreateRecommendedHdd(oVM, oTestDrv);
+ if fRc is True:
+ #
+ # Set up unattended installation.
+ #
+ try:
+ oIUnattended = oTestDrv.oVBox.createUnattendedInstaller();
+ except:
+ fRc = reporter.errorXcpt();
+ if fRc is True:
+ fRc = self.unattendedDetectOs(oIUnattended, oTestDrv);
+ if fRc is True:
+ fRc = self._unattendedConfigure(oIUnattended, oTestDrv);
+ if fRc is True:
+ fRc = self._unattendedDoIt(oIUnattended, oVM, oTestDrv);
+
+ # Done.
+ return (fRc, oVM)
+
+ def isLoggedOntoDesktop(self):
+ #
+ # Normally all unattended installations should end up on the desktop.
+ # An exception is a minimal install, but we currently don't support that.
+ #
+ return True;
+
+ def getTestUser(self):
+ # Default unattended installation user (parent knowns its password).
+ return 'vboxuser';
+
+
+ #
+ # Our methods.
+ #
+
+ def unattendedDetectOs(self, oIUnattended, oTestDrv): # type: (Any, vbox.TestDriver) -> bool
+ """
+ Does the detectIsoOS operation and checks that the detect OSTypeId matches.
+
+ Returns True on success, False w/ errors logged on failure.
+ """
+
+ #
+ # Point the installer at the ISO and do the detection.
+ #
+ sInstallIso = self.sInstallIso
+ if not os.path.isabs(sInstallIso):
+ sInstallIso = os.path.join(oTestDrv.sResourcePath, sInstallIso);
+
+ try:
+ oIUnattended.isoPath = sInstallIso;
+ except:
+ return reporter.errorXcpt('sInstallIso=%s' % (sInstallIso,));
+
+ try:
+ oIUnattended.detectIsoOS();
+ except:
+ if oTestDrv.oVBoxMgr.xcptIsNotEqual(None, oTestDrv.oVBoxMgr.statuses.E_NOTIMPL):
+ return reporter.errorXcpt('sInstallIso=%s' % (sInstallIso,));
+
+ #
+ # Get and log the result.
+ #
+ # Note! Current (6.0.97) fails with E_NOTIMPL even if it does some work.
+ #
+ try:
+ sDetectedOSTypeId = oIUnattended.detectedOSTypeId;
+ sDetectedOSVersion = oIUnattended.detectedOSVersion;
+ sDetectedOSFlavor = oIUnattended.detectedOSFlavor;
+ sDetectedOSLanguages = oIUnattended.detectedOSLanguages;
+ sDetectedOSHints = oIUnattended.detectedOSHints;
+ except:
+ return reporter.errorXcpt('sInstallIso=%s' % (sInstallIso,));
+
+ reporter.log('detectIsoOS result for "%s" (vm %s):' % (sInstallIso, self.sVmName));
+ reporter.log(' DetectedOSTypeId: %s' % (sDetectedOSTypeId,));
+ reporter.log(' DetectedOSVersion: %s' % (sDetectedOSVersion,));
+ reporter.log(' DetectedOSFlavor: %s' % (sDetectedOSFlavor,));
+ reporter.log(' DetectedOSLanguages: %s' % (sDetectedOSLanguages,));
+ reporter.log(' DetectedOSHints: %s' % (sDetectedOSHints,));
+
+ #
+ # Check if the OS type matches.
+ #
+ if self.sKind != sDetectedOSTypeId:
+ return reporter.error('sInstallIso=%s: DetectedOSTypeId is %s, expected %s'
+ % (sInstallIso, sDetectedOSTypeId, self.sKind));
+
+ return True;
+
+
+class tdGuestOsInstTest1(vbox.TestDriver):
+ """
+ Unattended Guest OS installation tests using IUnattended.
+
+ Scenario:
+ - Create a new VM with default settings using IMachine::applyDefaults.
+ - Setup unattended installation using IUnattended.
+ - Start the VM and do the installation.
+ - Wait for TXS to report for service.
+ - If installing GAs:
+ - Wait for GAs to report operational runlevel.
+ - Save & restore state.
+ - If installing GAs:
+ - Test guest properties (todo).
+ - Test guest controls.
+ - Test shared folders.
+ """
+
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+ self.fLegacyOptions = False;
+ assert self.fEnableVrdp; # in parent driver.
+
+ #
+ # Our install test VM set.
+ #
+ oSet = vboxtestvms.TestVmSet(self.oTestVmManager, fIgnoreSkippedVm = True);
+ # pylint: disable=line-too-long
+ oSet.aoTestVms.extend([
+ #
+ # Windows. The older windows ISOs requires a keyfile (for xp sp3
+ # pick a key from the PID.INF file on the ISO).
+ #
+ UnattendedVm(oSet, 'tst-xp-32', 'WindowsXP', '6.0/uaisos/en_winxp_pro_x86_build2600_iso.img', UnattendedVm.kfKeyFile), # >=2GiB
+ UnattendedVm(oSet, 'tst-xpsp2-32', 'WindowsXP', '6.0/uaisos/en_winxp_pro_with_sp2.iso', UnattendedVm.kfKeyFile), # >=2GiB
+ UnattendedVm(oSet, 'tst-xpsp3-32', 'WindowsXP', '6.0/uaisos/en_windows_xp_professional_with_service_pack_3_x86_cd_x14-80428.iso', UnattendedVm.kfKeyFile), # >=2GiB
+ UnattendedVm(oSet, 'tst-xp-64', 'WindowsXP_64', '6.0/uaisos/en_win_xp_pro_x64_vl.iso', UnattendedVm.kfKeyFile), # >=3GiB
+ UnattendedVm(oSet, 'tst-xpsp2-64', 'WindowsXP_64', '6.0/uaisos/en_win_xp_pro_x64_with_sp2_vl_x13-41611.iso', UnattendedVm.kfKeyFile), # >=3GiB
+ #fixme: UnattendedVm(oSet, 'tst-xpchk-64', 'WindowsXP_64', '6.0/uaisos/en_windows_xp_professional_x64_chk.iso', UnattendedVm.kfKeyFile | UnattendedVm.kfNeedCom1), # >=3GiB
+ # No key files needed:
+ UnattendedVm(oSet, 'tst-vista-32', 'WindowsVista', '6.0/uaisos/en_windows_vista_ee_x86_dvd_vl_x13-17271.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-vista-64', 'WindowsVista_64', '6.0/uaisos/en_windows_vista_enterprise_x64_dvd_vl_x13-17316.iso'), # >=8GiB
+ UnattendedVm(oSet, 'tst-vistasp1-32', 'WindowsVista', '6.0/uaisos/en_windows_vista_enterprise_with_service_pack_1_x86_dvd_x14-55954.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-vistasp1-64', 'WindowsVista_64', '6.0/uaisos/en_windows_vista_enterprise_with_service_pack_1_x64_dvd_x14-55934.iso'), # >=9GiB
+ UnattendedVm(oSet, 'tst-vistasp2-32', 'WindowsVista', '6.0/uaisos/en_windows_vista_enterprise_sp2_x86_dvd_342329.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-vistasp2-64', 'WindowsVista_64', '6.0/uaisos/en_windows_vista_enterprise_sp2_x64_dvd_342332.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w7-32', 'Windows7', '6.0/uaisos/en_windows_7_enterprise_x86_dvd_x15-70745.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-w7-64', 'Windows7_64', '6.0/uaisos/en_windows_7_enterprise_x64_dvd_x15-70749.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w7sp1-32', 'Windows7', '6.0/uaisos/en_windows_7_enterprise_with_sp1_x86_dvd_u_677710.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-w7sp1-64', 'Windows7_64', '6.0/uaisos/en_windows_7_enterprise_with_sp1_x64_dvd_u_677651.iso'), # >=8GiB
+ UnattendedVm(oSet, 'tst-w8-32', 'Windows8', '6.0/uaisos/en_windows_8_enterprise_x86_dvd_917587.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-w8-64', 'Windows8_64', '6.0/uaisos/en_windows_8_enterprise_x64_dvd_917522.iso'), # >=9GiB
+ UnattendedVm(oSet, 'tst-w81-32', 'Windows81', '6.0/uaisos/en_windows_8_1_enterprise_x86_dvd_2791510.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-w81-64', 'Windows81_64', '6.0/uaisos/en_windows_8_1_enterprise_x64_dvd_2791088.iso'), # >=8GiB
+ UnattendedVm(oSet, 'tst-w10-1507-32', 'Windows10', '6.0/uaisos/en_windows_10_pro_10240_x86_dvd.iso'), # >=6GiB
+ UnattendedVm(oSet, 'tst-w10-1507-64', 'Windows10_64', '6.0/uaisos/en_windows_10_pro_10240_x64_dvd.iso'), # >=9GiB
+ UnattendedVm(oSet, 'tst-w10-1511-32', 'Windows10', '6.0/uaisos/en_windows_10_enterprise_version_1511_updated_feb_2016_x86_dvd_8378870.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1511-64', 'Windows10_64', '6.0/uaisos/en_windows_10_enterprise_version_1511_x64_dvd_7224901.iso'), # >=9GiB
+ UnattendedVm(oSet, 'tst-w10-1607-32', 'Windows10', '6.0/uaisos/en_windows_10_enterprise_version_1607_updated_jul_2016_x86_dvd_9060097.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1607-64', 'Windows10_64', '6.0/uaisos/en_windows_10_enterprise_version_1607_updated_jul_2016_x64_dvd_9054264.iso'), # >=9GiB
+ UnattendedVm(oSet, 'tst-w10-1703-32', 'Windows10', '6.0/uaisos/en_windows_10_enterprise_version_1703_updated_march_2017_x86_dvd_10188981.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1703-64', 'Windows10_64', '6.0/uaisos/en_windows_10_enterprise_version_1703_updated_march_2017_x64_dvd_10189290.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w10-1709-32', 'Windows10', '6.0/uaisos/en_windows_10_multi-edition_vl_version_1709_updated_sept_2017_x86_dvd_100090759.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1709-64', 'Windows10_64', '6.0/uaisos/en_windows_10_multi-edition_vl_version_1709_updated_sept_2017_x64_dvd_100090741.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w10-1803-32', 'Windows10', '6.0/uaisos/en_windows_10_business_editions_version_1803_updated_march_2018_x86_dvd_12063341.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1803-64', 'Windows10_64', '6.0/uaisos/en_windows_10_business_editions_version_1803_updated_march_2018_x64_dvd_12063333.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w10-1809-32', 'Windows10', '6.0/uaisos/en_windows_10_business_edition_version_1809_updated_sept_2018_x86_dvd_2f92403b.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1809-64', 'Windows10_64', '6.0/uaisos/en_windows_10_business_edition_version_1809_updated_sept_2018_x64_dvd_f0b7dc68.iso'), # >=10GiB
+ UnattendedVm(oSet, 'tst-w10-1903-32', 'Windows10', '6.0/uaisos/en_windows_10_business_editions_version_1903_x86_dvd_ca4f0f49.iso'), # >=7GiB
+ UnattendedVm(oSet, 'tst-w10-1903-64', 'Windows10_64', '6.0/uaisos/en_windows_10_business_editions_version_1903_x64_dvd_37200948.iso'), # >=10GiB
+ #
+ # Ubuntu
+ #
+ ## @todo 15.10 fails with grub install error.
+ #UnattendedVm(oSet, 'tst-ubuntu-15.10-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-15.10-desktop-amd64.iso'),
+ UnattendedVm(oSet, 'tst-ubuntu-16.04-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04-desktop-amd64.iso', # ~5GiB
+ UnattendedVm.kfUbuntuAvx2Crash),
+ UnattendedVm(oSet, 'tst-ubuntu-16.04-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.1-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.1-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.1-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.1-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.2-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.2-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.2-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.2-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.3-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.3-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.3-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.3-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.4-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.4-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.4-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.4-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.5-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.5-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.5-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.5-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.6-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.04.6-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.04.6-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.04.6-desktop-i386.iso'), # >=4.5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-16.10-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-16.10-desktop-amd64.iso'), # >=5.5GiB
+ ## @todo 16.10-32 doesn't ask for an IP, so it always fails.
+ #UnattendedVm(oSet, 'tst-ubuntu-16.10-32', 'Ubuntu', '6.0/uaisos/ubuntu-16.10-desktop-i386.iso'), # >=5.5GiB?
+ UnattendedVm(oSet, 'tst-ubuntu-17.04-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-17.04-desktop-amd64.iso'), # >=5GiB
+ UnattendedVm(oSet, 'tst-ubuntu-17.04-32', 'Ubuntu', '6.0/uaisos/ubuntu-17.04-desktop-i386.iso'), # >=4.5GiB
+ ## @todo ubuntu 17.10, 18.04 & 18.10 do not work. They misses all the the build tools (make, gcc, perl, ++)
+ ## and has signed kmods:
+ UnattendedVm(oSet, 'tst-ubuntu-17.10-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-17.10-desktop-amd64.iso', # >=4Gib
+ UnattendedVm.kfNoGAs),
+ UnattendedVm(oSet, 'tst-ubuntu-18.04-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-18.04-desktop-amd64.iso', # >=6GiB
+ UnattendedVm.kfNoGAs),
+ # 18.10 hangs reading install DVD during "starting partitioner..."
+ #UnattendedVm(oSet, 'tst-ubuntu-18.10-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-18.10-desktop-amd64.iso',
+ # UnattendedVm.kfNoGAs),
+ UnattendedVm(oSet, 'tst-ubuntu-19.04-64', 'Ubuntu_64', '6.0/uaisos/ubuntu-19.04-desktop-amd64.iso', # >=6GiB
+ UnattendedVm.kfNoGAs),
+ UnattendedVm(oSet, 'tst-debian-9.3-64', 'Debian_64', '6.0/uaisos/debian-9.3.0-amd64-DVD-1.iso', # >=6GiB?
+ UnattendedVm.kfAvoidNetwork | UnattendedVm.kfNoGAs),
+ UnattendedVm(oSet, 'tst-debian-9.4-64', 'Debian_64', '6.0/uaisos/debian-9.4.0-amd64-DVD-1.iso', # >=6GiB?
+ UnattendedVm.kfAvoidNetwork | UnattendedVm.kfNoGAs),
+ UnattendedVm(oSet, 'tst-debian-10.0-64', 'Debian_64', '6.0/uaisos/debian-10.0.0-amd64-DVD-1.iso', # >=6GiB?
+ UnattendedVm.kfAvoidNetwork),
+ #
+ # OS/2.
+ #
+ UnattendedVm(oSet, 'tst-acp2', 'OS2Warp45', '7.0/uaisos/acp2_us_cd2.iso'), # ~400MiB
+ ## @todo mcp2 too?
+ ]);
+ # pylint: enable=line-too-long
+ self.oTestVmSet = oSet;
+
+ # For option parsing:
+ self.aoSelectedVms = oSet.aoTestVms # type: list(UnattendedVm)
+
+ # Number of VMs to test in parallel:
+ self.cInParallel = 1;
+
+ # Whether to do the save-and-restore test.
+ self.fTestSaveAndRestore = True;
+
+ #
+ # Sub-test drivers.
+ #
+ self.addSubTestDriver(SubTstDrvAddSharedFolders1(self));
+ self.addSubTestDriver(SubTstDrvAddGuestCtrl(self));
+
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log('');
+ reporter.log('tdGuestOsUnattendedInst1 options:');
+ reporter.log(' --parallel <num>');
+ reporter.log(' Number of VMs to test in parallel.');
+ reporter.log(' Default: 1');
+ reporter.log('');
+ reporter.log(' Options for working on selected test VMs:');
+ reporter.log(' --select <vm1[:vm2[:..]]>');
+ reporter.log(' Selects a test VM for the following configuration alterations.');
+ reporter.log(' Default: All possible test VMs');
+ reporter.log(' --copy <old-vm>=<new-vm>');
+ reporter.log(' Creates and selects <new-vm> as a copy of <old-vm>.');
+ reporter.log(' --guest-type <guest-os-type>');
+ reporter.log(' Sets the guest-os type of the currently selected test VM.');
+ reporter.log(' --install-iso <ISO file name>');
+ reporter.log(' Sets ISO image to use for the selected test VM.');
+ reporter.log(' --ram-adjust <MBs>');
+ reporter.log(' Adjust the VM ram size by the given delta. Both negative and positive');
+ reporter.log(' values are accepted.');
+ reporter.log(' --max-cpus <# CPUs>');
+ reporter.log(' Sets the maximum number of guest CPUs for the selected VM.');
+ reporter.log(' --set-extradata <key>:value');
+ reporter.log(' Set VM extra data for the selected VM. Can be repeated.');
+ reporter.log(' --ioapic, --no-ioapic');
+ reporter.log(' Enable or disable the I/O apic for the selected VM.');
+ reporter.log(' --pae, --no-pae');
+ reporter.log(' Enable or disable PAE support (32-bit guests only) for the selected VM.');
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+
+ if asArgs[iArg] == '--parallel':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self.cInParallel = int(asArgs[iArg]);
+ if self.cInParallel <= 0:
+ self.cInParallel = 1;
+ elif asArgs[iArg] == '--select':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self.aoSelectedVms = [];
+ for sTestVm in asArgs[iArg].split(':'):
+ oTestVm = self.oTestVmSet.findTestVmByName(sTestVm);
+ if not oTestVm:
+ raise base.InvalidOption('Unknown test VM: %s' % (sTestVm,));
+ self.aoSelectedVms.append(oTestVm);
+ elif asArgs[iArg] == '--copy':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ asNames = asArgs[iArg].split('=');
+ if len(asNames) != 2 or not asNames[0] or not asNames[1]:
+ raise base.InvalidOption('The --copy option expects value on the form "old=new": %s' % (asArgs[iArg],));
+ oOldTestVm = self.oTestVmSet.findTestVmByName(asNames[0]);
+ if not oOldTestVm:
+ raise base.InvalidOption('Unknown test VM: %s' % (asNames[0],));
+ oNewTestVm = copy.deepcopy(oOldTestVm);
+ oNewTestVm.sVmName = asNames[1];
+ self.oTestVmSet.aoTestVms.append(oNewTestVm);
+ self.aoSelectedVms = [oNewTestVm];
+ elif asArgs[iArg] == '--guest-type':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.sKind = asArgs[iArg];
+ elif asArgs[iArg] == '--install-iso':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.sInstallIso = asArgs[iArg];
+ elif asArgs[iArg] == '--ram-adjust':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.iOptRamAdjust = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--max-cpus':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.iOptMaxCpus = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--set-extradata':
+ iArg = self.requireMoreArgs(1, asArgs, iArg)
+ sExtraData = asArgs[iArg];
+ try: _, _ = sExtraData.split(':');
+ except: raise base.InvalidOption('Invalid extradata specified: %s' % (sExtraData, ));
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.asOptExtraData.append(sExtraData);
+ elif asArgs[iArg] == '--ioapic':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptIoApic = True;
+ elif asArgs[iArg] == '--no-ioapic':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptIoApic = False;
+ elif asArgs[iArg] == '--pae':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptPae = True;
+ elif asArgs[iArg] == '--no-pae':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptPae = False;
+ elif asArgs[iArg] == '--install-additions':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptInstallAdditions = True;
+ elif asArgs[iArg] == '--no-install-additions':
+ for oTestVm in self.aoSelectedVms:
+ oTestVm.fOptInstallAdditions = False;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+ def testOneVmConfig(self, oVM, oTestVm): # type: (Any, UnattendedVm) -> bool
+ """
+ Install guest OS and wait for result
+ """
+
+ self.logVmInfo(oVM)
+ reporter.testStart('Installing %s%s' % (oTestVm.sVmName, ' with GAs' if oTestVm.fOptInstallAdditions else ''))
+
+ cMsTimeout = 40*60000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = 180 * 60000; # will be adjusted down.
+
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False, cMsTimeout = cMsTimeout);
+ #oSession = self.startVmByName(oTestVm.sVmName); # (for quickly testing waitForGAs)
+ if oSession is not None:
+ # The guest has connected to TXS.
+ reporter.log('Guest reported success via TXS.');
+ reporter.testDone();
+
+ fRc = True;
+ # Kudge: GAs doesn't come up correctly, so we have to reboot the guest first:
+ # Looks like VBoxService isn't there.
+ if oTestVm.fOptInstallAdditions:
+ reporter.testStart('Rebooting');
+ fRc, oTxsSession = self.txsRebootAndReconnectViaTcp(oSession, oTxsSession);
+ reporter.testDone();
+
+ # If we're installing GAs, wait for them to come online:
+ if oTestVm.fOptInstallAdditions and fRc is True:
+ reporter.testStart('Guest additions');
+ aenmRunLevels = [vboxcon.AdditionsRunLevelType_Userland,];
+ if oTestVm.isLoggedOntoDesktop():
+ aenmRunLevels.append(vboxcon.AdditionsRunLevelType_Desktop);
+ fRc = self.waitForGAs(oSession, cMsTimeout = cMsTimeout / 2, aenmWaitForRunLevels = aenmRunLevels,
+ aenmWaitForActive = (vboxcon.AdditionsFacilityType_VBoxGuestDriver,
+ vboxcon.AdditionsFacilityType_VBoxService,));
+ reporter.testDone();
+
+ # Now do a save & restore test:
+ if fRc is True and self.fTestSaveAndRestore:
+ fRc, oSession, oTxsSession = self.testSaveAndRestore(oSession, oTxsSession, oTestVm);
+
+ # Test GAs if requested:
+ if oTestVm.fOptInstallAdditions and fRc is True:
+ for oSubTstDrv in self.aoSubTstDrvs:
+ if oSubTstDrv.fEnabled:
+ reporter.testStart(oSubTstDrv.sTestName);
+ fRc2, oTxsSession = oSubTstDrv.testIt(oTestVm, oSession, oTxsSession);
+ reporter.testDone(fRc2 is None);
+ if fRc2 is False:
+ fRc = False;
+
+ if oSession is not None:
+ fRc = self.terminateVmBySession(oSession) and fRc;
+ return fRc is True
+
+ reporter.error('Installation of %s has failed' % (oTestVm.sVmName,))
+ #oTestVm.detatchAndDeleteHd(self); # Save space.
+ reporter.testDone()
+ return False
+
+ def testSaveAndRestore(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests saving and restoring the VM.
+ """
+ _ = oTestVm;
+ reporter.testStart('Save');
+ ## @todo
+ reporter.testDone();
+ reporter.testStart('Restore');
+ ## @todo
+ reporter.testDone();
+ return (True, oSession, oTxsSession);
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsInstTest1().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/network/Makefile.kmk b/src/VBox/ValidationKit/tests/network/Makefile.kmk
new file mode 100644
index 00000000..c77a0d87
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/network/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Network Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsNetwork
+ValidationKitTestsNetwork_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsNetwork_INST = $(INST_VALIDATIONKIT)tests/network/
+ValidationKitTestsNetwork_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdNetBenchmark1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsNetwork_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py b/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py
new file mode 100755
index 00000000..c1dc9602
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py
@@ -0,0 +1,633 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdNetBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Networking benchmark #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import socket
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdNetBenchmark1(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Networking benchmark #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.sLocalName = socket.getfqdn();
+ self.sLocalIP = None
+ self.sRemoteName = None;
+ self.sRemoteIP = None;
+ self.sGuestName = None;
+ self.sGuestIP = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asTestVMsDef = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.asNicTypesDef = ['E1000', 'PCNet', 'Virtio',];
+ self.asNicTypes = self.asNicTypesDef;
+ self.sNicAttachmentDef = 'bridged';
+ self.sNicAttachment = self.sNicAttachmentDef;
+ self.asSetupsDef = ['g2h', 'g2r', 'g2g',];
+ self.asSetups = self.asSetupsDef;
+ self.cSecsRunDef = 30;
+ self.cSecsRun = self.cSecsRunDef;
+ self.asTestsDef = ['tcp-latency', 'tcp-throughput', 'udp-latency', 'udp-throughput', 'tbench'];
+ self.asTests = self.asTestsDef
+ self.acbLatencyPktsDef = [32, 1024, 4096, 8192, 65536,];
+ self.acbLatencyPkts = self.acbLatencyPktsDef
+ self.acbThroughputPktsDef = [8192, 65536];
+ self.acbThroughputPkts = self.acbThroughputPktsDef
+
+ try: self.sLocalName = socket.gethostbyname(self.sLocalName);
+ except: pass;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdNetBenchmark1 Options:');
+ reporter.log(' --remote-host <hostname|address>');
+ reporter.log(' --local-host <hostname|address>');
+ reporter.log(' --guest-host <hostname|address>');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --nic-types <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asNicTypes)));
+ reporter.log(' --nic-attachment <bridged|nat>');
+ reporter.log(' Default: %s' % (self.sNicAttachmentDef));
+ reporter.log(' --setups <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asSetupsDef)));
+ reporter.log(' --secs-per-run <seconds>');
+ reporter.log(' Default: %s' % (self.cSecsRunDef));
+ reporter.log(' --tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --latency-sizes <size1[:size2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbLatencyPktsDef))); # pychecker bug?
+ reporter.log(' --throughput-sizes <size1[:size2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbThroughputPktsDef))); # pychecker bug?
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes hwvirt --cpu-counts 1 --secs-per-run 5 --latency-sizes 32');
+ reporter.log(' --throughput-sizes 8192 --test-vms tst-rhel5:tst-win2k3ent:tst-sol10');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--remote-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--remote-host" takes an IP address or a hostname');
+ self.sRemoteName = asArgs[iArg];
+ elif asArgs[iArg] == '--local-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--local-host" takes an IP address or a hostname');
+ self.sLocalName = asArgs[iArg];
+ elif asArgs[iArg] == '--guest-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--guest-host" takes an IP address or a hostname');
+ self.sGuestName = asArgs[iArg];
+ elif asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--nic-types':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-types" takes a colon separated list of NIC types');
+ self.asNicTypes = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--nic-attachment':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-attachment" takes an argument');
+ self.sNicAttachment = asArgs[iArg];
+ if self.sNicAttachment not in ('bridged', 'nat'):
+ raise base.InvalidOption('The "--nic-attachment" value "%s" is not supported. Valid values are: bridged, nat' \
+ % (self.sNicAttachment));
+ elif asArgs[iArg] == '--setups':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--setups" takes a colon separated list of setups');
+ self.asSetups = asArgs[iArg].split(':');
+ for s in self.asSetups:
+ if s not in self.asSetupsDef:
+ raise base.InvalidOption('The "--setups" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asSetupsDef)));
+ elif asArgs[iArg] == '--secs-per-run':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--secs-per-run" takes second count');
+ try: self.cSecsRun = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--secs-per-run" value "%s" is not an integer' % (self.cSecsRun,));
+ if self.cSecsRun <= 0:
+ raise base.InvalidOption('The "--secs-per-run" value "%s" is zero or negative.' % (self.cSecsRun,));
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--tests" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestsDef)));
+ elif asArgs[iArg] == '--latency-sizes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--latency-sizes" takes a colon separated list of sizes');
+ self.acbLatencyPkts = [];
+ for s in asArgs[iArg].split(':'):
+ try: cb = int(s);
+ except: raise base.InvalidOption('The "--latency-sizes" value "%s" is not an integer' % (s,));
+ if cb <= 0: raise base.InvalidOption('The "--latency-sizes" value "%s" is zero or negative' % (s,));
+ self.acbLatencyPkts.append(cb);
+ elif asArgs[iArg] == '--throughput-sizes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--throughput-sizes" takes a colon separated list of sizes');
+ self.acbThroughputPkts = [];
+ for s in asArgs[iArg].split(':'):
+ try: cb = int(s);
+ except: raise base.InvalidOption('The "--throughput-sizes" value "%s" is not an integer' % (s,));
+ if cb <= 0: raise base.InvalidOption('The "--throughput-sizes" value "%s" is zero or negative' % (s,));
+ self.acbThroughputPkts.append(cb);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--quick':
+ self.cSecsRun = 5;
+ self.asVirtModes = ['hwvirt',];
+ self.acCpus = [1,];
+ self.acbLatencyPkts = [32,];
+ self.acbThroughputPkts = [8192,];
+ self.asTestVMs = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10',];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ # Resolve any names we've been given.
+ self.sLocalIP = base.tryGetHostByName(self.sLocalName);
+ self.sRemoteIP = base.tryGetHostByName(self.sRemoteName);
+ self.sGuestIP = base.tryGetHostByName(self.sGuestName);
+
+ reporter.log('Local IP : %s' % (self.sLocalIP));
+ reporter.log('Remote IP: %s' % (self.sRemoteIP));
+ if self.sGuestIP is None:
+ reporter.log('Guest IP : use tst-guest2guest');
+ else:
+ reporter.log('Guest IP : %s' % (self.sGuestIP));
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-rhel5' in self.asTestVMs or 'g2g' in self.asSetups:
+ self.asRsrcs.append('3.0/tcp/rhel5.vdi');
+ if 'tst-rhel5-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5-64.vdi');
+ if 'tst-sles11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11.vdi');
+ if 'tst-sles11-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11-64.vdi');
+ if 'tst-oel' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel.vdi');
+ if 'tst-oel-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel-64.vdi');
+ if 'tst-win2k3ent' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-acpi.vdi');
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-64.vdi');
+ if 'tst-win2k8' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k8.vdi');
+ if 'tst-sol10' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris10.vdi');
+ if 'tst-sol11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris11.vdi');
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ # Guest to Guest VM.
+ if self.sGuestName is None and 'g2g' in self.asSetups:
+ oVM = self.createTestVM('tst-guest2guest', 0, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ eNic0Type = vboxcon.NetworkAdapterType_I82545EM, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged, \
+ fVirtEx = True, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+ self.oGuestToGuestVM = oVM;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+ eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ if self.sNicAttachment == 'nat':
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+
+ # Linux VMs
+ if 'tst-rhel5' in self.asTestVMs:
+ oVM = self.createTestVM('tst-rhel5', 1, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-rhel5-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-rhel5-64', 1, '3.0/tcp/rhel5-64.vdi', sKind = 'RedHat_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sles11', 1, '3.0/tcp/sles11.vdi', sKind = 'OpenSUSE', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sles11-64', 1, '3.0/tcp/sles11-64.vdi', sKind = 'OpenSUSE_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel' in self.asTestVMs:
+ oVM = self.createTestVM('tst-oel', 1, '3.0/tcp/oel.vdi', sKind = 'Oracle', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-oel-64', 1, '3.0/tcp/oel-64.vdi', sKind = 'Oracle_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Windows VMs
+ if 'tst-win2k3ent' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k3ent', 1, '3.0/tcp/win2k3ent-acpi.vdi', sKind = 'Windows2003', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k3ent-64', 1, '3.0/tcp/win2k3ent-64.vdi', sKind = 'Windows2003_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k8' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k8', 1, '3.0/tcp/win2k8.vdi', sKind = 'Windows2008_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Solaris VMs
+ if 'tst-sol10' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sol10', 1, '3.0/tcp/solaris10.vdi', sKind = 'Solaris_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sol11' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sol11', 1, '3.0/tcp/os2009-11.vdi', sKind = 'Solaris_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1RunTestProgs(self, oTxsSession, fRc, sTestName, sAddr):
+ """
+ Runs all the test programs against one 'server' machine.
+ """
+ reporter.testStart(sTestName);
+
+ reporter.testStart('TCP latency');
+ if fRc and 'tcp-latency' in self.asTests and sAddr is not None:
+ for cbPkt in self.acbLatencyPkts:
+ fRc = self.txsRunTest(oTxsSession, '%u bytes' % (cbPkt), self.cSecsRun * 1000 * 4,
+ '${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--client', sAddr, '--interval', self.cSecsRun, '--len', cbPkt,
+ '--mode', 'latency'));
+ if not fRc:
+ break;
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('TCP throughput');
+ if fRc and 'tcp-throughput' in self.asTests and sAddr is not None:
+ for cbPkt in self.acbThroughputPkts:
+ fRc = self.txsRunTest(oTxsSession, '%u bytes' % (cbPkt), self.cSecsRun * 2 * 1000 * 4,
+ '${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--client', sAddr, '--interval', self.cSecsRun, '--len', cbPkt,
+ '--mode', 'throughput'));
+ if not fRc:
+ break;
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('UDP latency');
+ if fRc and 'udp-latency' in self.asTests and sAddr is not None:
+ ## @todo Netperf w/UDP.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('UDP throughput');
+ if fRc and 'udp-throughput' in self.asTests and sAddr is not None:
+ ## @todo Netperf w/UDP.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('tbench');
+ if fRc and 'tbench' in self.asTests and sAddr is not None:
+ ## @todo tbench.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testDone(not fRc);
+ return fRc;
+
+ def test1OneCfg(self, sVmName, eNicType, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.setNicType(eNicType);
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ # Benchmark #1 - guest <-> host.
+ if 'g2h' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> host', self.sLocalIP);
+
+ # Benchmark #2 - guest <-> host.
+ if 'g2r' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> remote', self.sRemoteIP);
+
+ # Benchmark #3 - guest <-> guest.
+ if 'g2g' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> guest', self.sGuestIP);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1OneVM(self, sVmName, asSkipNicTypes = (), asSupVirtModes = None, rSupCpus = range(1, 256)):
+ """
+ Runs one VM thru the various configurations.
+ """
+ if asSupVirtModes is None:
+ asSupVirtModes = self.asVirtModes;
+
+ reporter.testStart(sVmName);
+ fRc = True;
+ for sNicType in self.asNicTypes:
+ if sNicType in asSkipNicTypes:
+ continue;
+ reporter.testStart(sNicType);
+
+ if sNicType == 'E1000':
+ eNicType = vboxcon.NetworkAdapterType_I82545EM;
+ elif sNicType == 'PCNet':
+ eNicType = vboxcon.NetworkAdapterType_Am79C973;
+ elif sNicType == 'Virtio':
+ eNicType = vboxcon.NetworkAdapterType_Virtio;
+ else:
+ eNicType = None;
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ if cCpus not in rSupCpus:
+ continue;
+ if sVirtMode not in asSupVirtModes:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ fRc = self.test1OneCfg(sVmName, eNicType, cCpus, fHwVirt, fNestedPaging) and fRc and True; # pychecker hack.
+
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ # Start the VM for the guest to guest testing, if required.
+ fRc = True;
+ if 'g2g' in self.asSetups and self.sGuestName is None:
+ self.oGuestToGuestSess, self.oGuestToGuestTxs = self.startVmAndConnectToTxsViaTcp('tst-guest2guest', fCdWait = True);
+ if self.oGuestToGuestSess is None:
+ return False;
+ self.sGuestIP = self.oGuestToGuestSess.getPrimaryIp();
+ reporter.log('tst-guest2guest IP: %s' % (self.sGuestIP));
+
+ # Start the test servers on it.
+ fRc = self.oGuestToGuestTxs.syncExec('${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--server', '--daemonize'), fWithTestPipe=False);
+
+ # Loop thru the test VMs.
+ if fRc:
+ for sVM in self.asTestVMs:
+ # figure args.
+ asSkipNicTypes = [];
+ if sVM not in ('tst-sles11', 'tst-sles11-64'):
+ asSkipNicTypes.append('Virtio');
+ if sVM in ('tst-sol11', 'tst-sol10'):
+ asSkipNicTypes.append('PCNet');
+ asSupVirtModes = None;
+ if sVM in ('tst-sol11', 'tst-sol10'): # 64-bit only
+ asSupVirtModes = ('hwvirt', 'hwvirt-np',);
+
+ # run test on the VM.
+ if not self.test1OneVM(sVM, asSkipNicTypes, asSupVirtModes):
+ fRc = False;
+
+ # Kill the guest to guest VM and clean up the state.
+ if self.oGuestToGuestSess is not None:
+ self.terminateVmBySession(self.oGuestToGuestSess);
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.sGuestIP = None;
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdNetBenchmark1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/Makefile.kmk b/src/VBox/ValidationKit/tests/selftests/Makefile.kmk
new file mode 100644
index 00000000..946f10b8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/Makefile.kmk
@@ -0,0 +1,54 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Testsuite & TestManager Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSelfTests
+ValidationKitTestsSelfTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSelfTests_INST = $(INST_VALIDATIONKIT)tests/selftests/
+ValidationKitTestsSelfTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSelfTest1.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest2.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest3.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest4.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSelfTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py
new file mode 100755
index 00000000..3509f3e6
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest1.py $
+
+"""
+Test Manager Self Test - Dummy Test Driver.
+"""
+
+from __future__ import print_function;
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+import sys;
+import os;
+
+print('dummydriver.py: hello world!');
+print('dummydriver.py: args: %s' % (sys.argv,));
+
+print('dummydriver.py: environment:');
+for sVar in sorted(os.environ.keys()): # pylint: disable=consider-iterating-dictionary
+ print('%s=%s' % (sVar, os.environ[sVar]));
+
+if sys.argv[-1] in [ 'all', 'execute' ]:
+
+ import time;
+
+ for i in range(10, 1, -1):
+ print('dummydriver.py: %u...', i);
+ sys.stdout.flush();
+ time.sleep(1);
+ print('dummydriver.py: ...0! done');
+
+sys.exit(0);
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py
new file mode 100755
index 00000000..6c0e12b8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest2.py $
+
+"""
+Test Manager / Suite Self Test #2 - Everything should succeed.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+class tdSelfTest2(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #2 - Everything should succeed.
+ """
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+
+
+ def actionExecute(self):
+ reporter.testStart('reporter.testXXXX API');
+ reporter.testValue('value-name1', 123456789, 'ms');
+
+ reporter.testStart('subtest');
+ reporter.testValue('value-name2', 11223344, 'times');
+ reporter.testDone();
+
+ reporter.testStart('subtest2');
+ reporter.testValue('value-name3', 39, 'sec');
+ reporter.testValue('value-name4', 42, 'ns');
+ reporter.testDone();
+
+ reporter.testStart('subtest3');
+ reporter.testDone(fSkipped = True);
+
+ # No spaces in XML.
+ reporter.testStart('subtest4');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<?xml version="1.0" encoding="UTF-8" ?>');
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Passed timestamp="%s"/>' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.write('<End timestamp="%s" errors="0"/>' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.close();
+ oSubXmlFile = None;
+ reporter.testDone();
+
+ # Spaces + funny line endings.
+ reporter.testStart('subtest5');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<?xml version="1.0" encoding="UTF-8" ?>\r\n');
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar2">\n\n\t\n\r\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub2">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write(' <Passed timestamp="%s"/>\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write(' </Test>\n');
+ oSubXmlFile.write(' <End timestamp="%s" errors="0"/>\r' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.close();
+ reporter.testDone();
+
+ # A few long log times for WUI log testing.
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+
+ # Upload a file.
+ reporter.addLogFile(__file__, sKind = 'log/release/vm');
+
+ reporter.testDone();
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest2().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py
new file mode 100755
index 00000000..1044dff0
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest3.py $
+
+"""
+Test Manager / Suite Self Test #3 - Bad XML input and other Failures.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+class tdSelfTest3(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #3 - Bad XML input and other Failures.
+ """
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+
+
+ def actionExecute(self):
+
+ # Testing PushHint/PopHint.
+ reporter.testStart('Negative XML #1');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar3">\n\n\t\n\r\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub3">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="subsub1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.close();
+ reporter.testDone();
+
+ # Missing end, like we had with IRPT at one time.
+ reporter.testStart('Negative XML #2 (IPRT)');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write("""
+<?xml version="1.0" encoding="UTF-8" ?>
+<Test timestamp="2013-05-29T08:59:05.930602000Z" name="tstRTGetOpt">
+ <Test timestamp="2013-05-29T08:59:05.930656000Z" name="Basics">
+ <Passed timestamp="2013-05-29T08:59:05.930756000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.930995000Z" name="RTGetOpt - IPv4">
+ <Passed timestamp="2013-05-29T08:59:05.931036000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931161000Z" name="RTGetOpt - MAC Address">
+ <Passed timestamp="2013-05-29T08:59:05.931194000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931313000Z" name="RTGetOpt - Option w/ Index">
+ <Passed timestamp="2013-05-29T08:59:05.931357000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931475000Z" name="RTGetOptFetchValue">
+ <Passed timestamp="2013-05-29T08:59:05.931516000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931640000Z" name="RTGetOpt - bool on/off">
+ <Passed timestamp="2013-05-29T08:59:05.931687000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931807000Z" name="Standard options">
+ <Passed timestamp="2013-05-29T08:59:05.931843000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931963000Z" name="Options first">
+ <Passed timestamp="2013-05-29T08:59:05.932035000Z"/>
+ </Test>
+""");
+ oSubXmlFile.close();
+ oSubXmlFile = None;
+ reporter.testDone();
+
+ # The use of testFailure.
+ reporter.testStart('Using testFailure()');
+ reporter.testValue('value-name3', 12345678, 'times');
+ reporter.testFailure('failure detail message');
+ reporter.testDone();
+
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest3().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py
new file mode 100755
index 00000000..c1eb60b6
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest4.py $
+
+"""
+Test Manager / Suite Self Test #4 - Testing result overflow handling.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver.base import TestDriverBase, InvalidOption;
+
+
+class tdSelfTest4(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #4 - Testing result overflow handling.
+ """
+
+ ## Valid tests.
+ kasValidTests = [ 'immediate-sub-tests', 'total-sub-tests', 'immediate-values', 'total-values', 'immediate-messages'];
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+ self.sOptWhich = 'immediate-sub-tests';
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--test':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ if asArgs[iArg] not in self.kasValidTests:
+ raise InvalidOption('Invalid test name "%s". Must be one of: %s'
+ % (asArgs[iArg], ', '.join(self.kasValidTests),));
+ self.sOptWhich = asArgs[iArg];
+ else:
+ return TestDriverBase.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionExecute(self):
+ # Too many immediate sub-tests.
+ if self.sOptWhich == 'immediate-sub-tests':
+ reporter.testStart('Too many immediate sub-tests (negative)');
+ for i in range(1024):
+ reporter.testStart('subsub%d' % i);
+ reporter.testDone();
+ # Too many sub-tests in total.
+ elif self.sOptWhich == 'total-sub-tests':
+ reporter.testStart('Too many sub-tests (negative)');
+ # 32 * 256 = 2^(5+8) = 2^13 = 8192.
+ for i in range(32):
+ reporter.testStart('subsub%d' % i);
+ for j in range(256):
+ reporter.testStart('subsubsub%d' % j);
+ reporter.testDone();
+ reporter.testDone();
+ # Too many immediate values.
+ elif self.sOptWhich == 'immediate-values':
+ reporter.testStart('Too many immediate values (negative)');
+ for i in range(512):
+ reporter.testValue('value%d' % i, i, 'times');
+ # Too many values in total.
+ elif self.sOptWhich == 'total-values':
+ reporter.testStart('Too many sub-tests (negative)');
+ for i in range(256):
+ reporter.testStart('subsub%d' % i);
+ for j in range(64):
+ reporter.testValue('value%d' % j, i * 10000 + j, 'times');
+ reporter.testDone();
+ # Too many failure reasons (only immediate since the limit is extremely low).
+ elif self.sOptWhich == 'immediate-messages':
+ reporter.testStart('Too many immediate messages (negative)');
+ for i in range(16):
+ reporter.testFailure('Detail %d' % i);
+ else:
+ reporter.testStart('Unknown test %s' % (self.sOptWhich,));
+ reporter.error('Invalid test selected: %s' % (self.sOptWhich,));
+ reporter.testDone();
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest4().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/serial/Makefile.kmk b/src/VBox/ValidationKit/tests/serial/Makefile.kmk
new file mode 100644
index 00000000..363e10f8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Serial port.
+#
+
+#
+# Copyright (C) 2018-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSerial
+ValidationKitTestsSerial_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSerial_INST = $(INST_VALIDATIONKIT)tests/serial/
+ValidationKitTestsSerial_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSerial1.py \
+ $(PATH_SUB_CURRENT)/loopback.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSerial_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/serial/loopback.py b/src/VBox/ValidationKit/tests/serial/loopback.py
new file mode 100755
index 00000000..b5ad831a
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/loopback.py
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+# $Id: loopback.py $
+
+"""
+VirtualBox Validation Kit - Serial loopback module.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2018-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+#import os;
+import socket;
+import threading;
+
+
+g_ksLoopbackTcpServ = 'TcpServ';
+g_ksLoopbackTcpClient = 'TcpClient';
+g_ksLoopbackNamedPipeServ = 'NamedPipeServ';
+g_ksLoopbackNamedPipeClient = 'NamedPipeClient';
+
+class SerialLoopbackTcpServ(object):
+ """
+ Handler for a server TCP style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ sHost, sPort = sLocation.split(':');
+ self.oConn = None;
+ self.oSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
+ self.oSock.settimeout(iTimeout);
+ self.oSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
+ self.oSock.bind((sHost, int(sPort)));
+ self.oSock.listen(1);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ if self.oSock is not None:
+ self.oSock.close();
+ self.oSock = None;
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+ self.oSock.close();
+ self.oSock = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ if self.oConn is None:
+ oConn, _ = self.oSock.accept();
+ self.oConn = oConn;
+ else:
+ abData = self.oConn.recv(1024); # pylint: disable=no-member
+ if abData is not None:
+ self.oConn.send(abData); # pylint: disable=no-member
+ except:
+ pass;
+
+class SerialLoopbackTcpClient(object):
+ """
+ Handler for a client TCP style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ sHost, sPort = sLocation.split(':');
+ self.oConn = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
+ self.oConn.connect((sHost, int(sPort)));
+ self.oConn.settimeout(iTimeout);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ abData = self.oConn.recv(1024);
+ if abData is not None:
+ self.oConn.send(abData);
+ except:
+ pass;
+
+class SerialLoopbackNamedPipeServ(object):
+ """
+ Handler for a named pipe server style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ self.oConn = None;
+ self.oSock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); # pylint: disable=no-member
+ self.oSock.settimeout(iTimeout);
+ self.oSock.bind(sLocation);
+ self.oSock.listen(1);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ if self.oSock is not None:
+ self.oSock.close();
+ self.oSock = None;
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+ self.oSock.close();
+ self.oSock = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ if self.oConn is None:
+ oConn, _ = self.oSock.accept();
+ self.oConn = oConn;
+ else:
+ abData = self.oConn.recv(1024); # pylint: disable=no-member
+ if abData is not None:
+ self.oConn.send(abData); # pylint: disable=no-member
+ except:
+ pass;
+
+class SerialLoopbackNamedPipeClient(object):
+ """
+ Handler for a named pipe client style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ self.oConn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); # pylint: disable=no-member
+ self.oConn.connect(sLocation);
+ self.oConn.settimeout(iTimeout);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ abData = self.oConn.recv(1024);
+ if abData is not None:
+ self.oConn.send(abData);
+ except:
+ pass;
+
+class SerialLoopback(object):
+ """
+ Serial port loopback module working with TCP and named pipes.
+ """
+
+ def __init__(self, sType, sLocation):
+ self.fShutdown = False;
+ self.sType = sType;
+ self.sLocation = sLocation;
+ self.oLock = threading.Lock();
+ self.oThread = threading.Thread(target=self.threadWorker, args=(), name=('SerLoopback'));
+
+ if sType == g_ksLoopbackTcpServ:
+ self.oIoPumper = SerialLoopbackTcpServ(sLocation, 0.5);
+ self.oThread.start();
+ elif sType == g_ksLoopbackNamedPipeServ:
+ self.oIoPumper = SerialLoopbackNamedPipeServ(sLocation, 0.5); # pylint: disable=redefined-variable-type
+ self.oThread.start();
+
+ def connect(self):
+ """
+ Connects to the server for a client type version.
+ """
+ fRc = True;
+ try:
+ if self.sType == g_ksLoopbackTcpClient:
+ self.oIoPumper = SerialLoopbackTcpClient(self.sLocation, 0.5);
+ elif self.sType == g_ksLoopbackNamedPipeClient:
+ self.oIoPumper = SerialLoopbackNamedPipeClient(self.sLocation, 0.5); # pylint: disable=redefined-variable-type
+ except:
+ fRc = False;
+ else:
+ self.oThread.start();
+ return fRc;
+
+ def shutdown(self):
+ """
+ Shutdown any connection and wait for it to become idle.
+ """
+ with self.oLock:
+ self.fShutdown = True;
+ self.oIoPumper.shutdown();
+
+ def isShutdown(self):
+ """
+ Returns whether the I/O pumping thread should shut down.
+ """
+ with self.oLock:
+ fShutdown = self.fShutdown;
+
+ return fShutdown;
+
+ def threadWorker(self):
+ """
+ The threaded worker.
+ """
+ while not self.isShutdown():
+ self.oIoPumper.pumpIo();
+
diff --git a/src/VBox/ValidationKit/tests/serial/tdSerial1.py b/src/VBox/ValidationKit/tests/serial/tdSerial1.py
new file mode 100755
index 00000000..8f5f3ab7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/tdSerial1.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSerial1.py $
+
+"""
+VirtualBox Validation Kit - Serial port testing #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2018-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import random;
+import string;
+import struct;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+import loopback;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class tdSerial1(vbox.TestDriver):
+ """
+ VBox serial port testing #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.selectSet(self.oTestVmManager.kfGrpStdSmoke);
+ self.asSerialModesDef = ['RawFile', 'Tcp', 'TcpServ', 'NamedPipe', 'NamedPipeServ', 'HostDev'];
+ self.asSerialModes = self.asSerialModesDef;
+ self.asSerialTestsDef = ['Write', 'ReadWrite'];
+ self.asSerialTests = self.asSerialTestsDef;
+ self.asUartsDef = ['16450', '16550A', '16750'];
+ self.asUarts = self.asUartsDef;
+ self.oLoopback = None;
+ self.sLocation = None;
+ self.fVerboseTest = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdSerial1 Options:');
+ reporter.log(' --serial-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asSerialModesDef)));
+ reporter.log(' --serial-tests <t1[:t2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asSerialTestsDef)));
+ reporter.log(' --uarts <u1[:u2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asUartsDef)));
+ reporter.log(' --verbose-test');
+ reporter.log(' Whether to enable verbose output when running the');
+ reporter.log(' test utility inside the VM');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--serial-modes':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--serial-modes" takes a colon separated list of serial port modes to test');
+ self.asSerialModes = asArgs[iArg].split(':');
+ for s in self.asSerialModes:
+ if s not in self.asSerialModesDef:
+ reporter.log('warning: The "--serial-modes" value "%s" is not a valid serial port mode.' % (s));
+ elif asArgs[iArg] == '--serial-tests':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--serial-tests" takes a colon separated list of serial port tests');
+ self.asSerialTests = asArgs[iArg].split(':');
+ for s in self.asSerialTests:
+ if s not in self.asSerialTestsDef:
+ reporter.log('warning: The "--serial-tests" value "%s" is not a valid serial port test.' % (s));
+ elif asArgs[iArg] == '--aurts':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--uarts" takes a colon separated list of uarts to test');
+ self.asUarts = asArgs[iArg].split(':');
+ for s in self.asUarts:
+ if s not in self.asUartsDef:
+ reporter.log('warning: The "--uarts" value "%s" is not a valid uart.' % (s));
+ elif asArgs[iArg] == '--verbose-test':
+ iArg += 1;
+ self.fVerboseTest = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+
+ return iArg + 1;
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def _generateRawPortFilename(self, oTestDrv, oTestVm, sInfix, sSuffix):
+ """ Generates a raw port filename. """
+ random.seed();
+ sRandom = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10));
+ return os.path.join(oTestDrv.sScratchPath, oTestVm.sVmName + sInfix + sRandom + sSuffix);
+
+ def setupSerialMode(self, oSession, oTestVm, sMode):
+ """
+ Sets up the serial mode.
+ """
+ fRc = True;
+ fServer = False;
+ sLocation = None;
+ ePortMode = vboxcon.PortMode_Disconnected;
+ if sMode == 'RawFile':
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_RawFile;
+ elif sMode == 'Tcp':
+ sLocation = '127.0.0.1:1234';
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackTcpServ, sLocation);
+ ePortMode = vboxcon.PortMode_TCP;
+ elif sMode == 'TcpServ':
+ fServer = True;
+ sLocation = '1234';
+ ePortMode = vboxcon.PortMode_TCP;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackTcpClient, '127.0.0.1:1234');
+ elif sMode == 'NamedPipe':
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_HostPipe;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackNamedPipeServ, sLocation);
+ elif sMode == 'NamedPipeServ':
+ fServer = True;
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_HostPipe;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackNamedPipeClient, sLocation);
+ elif sMode == 'HostDev':
+ sLocation = '/dev/ttyUSB0';
+ ePortMode = vboxcon.PortMode_HostDevice;
+ else:
+ reporter.log('warning, invalid mode %s given' % (sMode, ));
+ fRc = False;
+
+ if fRc:
+ fRc = oSession.changeSerialPortAttachment(0, ePortMode, sLocation, fServer);
+ if fRc and sMode in ('TcpServ', 'NamedPipeServ',):
+ self.sleep(2); # Fudge to allow the TCP server to get started.
+ fRc = self.oLoopback.connect();
+ if not fRc:
+ reporter.log('Failed to connect to %s' % (sLocation, ));
+ self.sLocation = sLocation;
+
+ return fRc;
+
+ def testWrite(self, oSession, oTxsSession, oTestVm, sMode):
+ """
+ Does a simple write test verifying the output.
+ """
+ _ = oSession;
+
+ reporter.testStart('Write');
+ tupCmdLine = ('SerialTest', '--tests', 'write', '--txbytes', '1048576');
+ if self.fVerboseTest:
+ tupCmdLine += ('--verbose',);
+ if oTestVm.isWindows():
+ tupCmdLine += ('--device', r'\\.\COM1',);
+ elif oTestVm.isLinux():
+ tupCmdLine += ('--device', r'/dev/ttyS0',);
+
+ fRc = self.txsRunTest(oTxsSession, 'SerialTest', 3600 * 1000, \
+ '${CDROM}/${OS/ARCH}/SerialTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running serial test utility failed');
+ elif sMode == 'RawFile':
+ # Open serial port and verify
+ cLast = 0;
+ try:
+ with open(self.sLocation, 'rb') as oFile:
+ sFmt = '=I';
+ cBytes = 4;
+ for i in xrange(1048576 // 4):
+ _ = i;
+ sData = oFile.read(cBytes);
+ tupUnpacked = struct.unpack(sFmt, sData);
+ cLast = cLast + 1;
+ if tupUnpacked[0] != cLast:
+ reporter.testFailure('Corruption detected, expected counter value %s, got %s'
+ % (cLast + 1, tupUnpacked[0],));
+ break;
+ except:
+ reporter.logXcpt();
+ reporter.testFailure('Verifying the written data failed');
+ reporter.testDone();
+ return fRc;
+
+ def testReadWrite(self, oSession, oTxsSession, oTestVm):
+ """
+ Does a simple write test verifying the output.
+ """
+ _ = oSession;
+
+ reporter.testStart('ReadWrite');
+ tupCmdLine = ('SerialTest', '--tests', 'readwrite', '--txbytes', '1048576');
+ if self.fVerboseTest:
+ tupCmdLine += ('--verbose',);
+ if oTestVm.isWindows():
+ tupCmdLine += ('--device', r'\\.\COM1',);
+ elif oTestVm.isLinux():
+ tupCmdLine += ('--device', r'/dev/ttyS0',);
+
+ fRc = self.txsRunTest(oTxsSession, 'SerialTest', 600 * 1000, \
+ '${CDROM}/${OS/ARCH}/SerialTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running serial test utility failed');
+
+ reporter.testDone();
+ return fRc;
+
+ def isModeCompatibleWithTest(self, sMode, sTest):
+ """
+ Returns whether the given port mode and test combination is
+ supported for testing.
+ """
+ if sMode == 'RawFile' and sTest == 'ReadWrite':
+ return False;
+ if sMode != 'RawFile' and sTest == 'Write':
+ return False;
+ return True;
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ for sUart in self.asUarts:
+ reporter.testStart(sUart);
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.enableSerialPort(0);
+
+ fRc = fRc and oSession.setExtraData("VBoxInternal/Devices/serial/0/Config/UartType", "string:" + sUart);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc;
+ oSession = None;
+ else:
+ fRc = False;
+
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ for sMode in self.asSerialModes:
+ reporter.testStart(sMode);
+ fRc = self.setupSerialMode(oSession, oTestVm, sMode);
+ if fRc:
+ for sTest in self.asSerialTests:
+ # Skip tests which don't work with the current mode.
+ if self.isModeCompatibleWithTest(sMode, sTest):
+ if sTest == 'Write':
+ fRc = self.testWrite(oSession, oTxsSession, oTestVm, sMode);
+ if sTest == 'ReadWrite':
+ fRc = self.testReadWrite(oSession, oTxsSession, oTestVm);
+ if self.oLoopback is not None:
+ self.oLoopback.shutdown();
+ self.oLoopback = None;
+
+ reporter.testDone();
+
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+ reporter.testDone();
+
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdSerial1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py b/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py
new file mode 100755
index 00000000..2407e92e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+VMM Guest OS boot tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import time
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class tdGuestOsBootTest1(vbox.TestDriver):
+ """
+ VMM Unit Tests Set.
+
+ Scenario:
+ - Create VM that corresponds to Guest OS pre-installed on selected HDD
+ - Start VM and wait for TXS server connection (which is started after Guest successfully booted)
+ """
+
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+
+ # VM parameters required to run HDD image.
+ # Format: { HDD image filename: (sKind, HDD controller type) }
+ kaoVmParams = {
+ 't-win80.vdi': ( 'Windows 8 (64 bit)', ksSataController ),
+ }
+
+ # List of platforms which are able to suspend and resume host automatically.
+ # In order to add new platform, self._SuspendResume() should be adapted.
+ kasSuspendAllowedPlatforms = ( 'darwin' )
+
+ kcMsVmStartLimit = 5 * 60000
+ kcMsVmShutdownLimit = 1 * 60000
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+
+ self.sVmName = 'TestVM'
+ self.sHddName = None
+ self.sHddPathBase = os.path.join(self.sResourcePath, '4.2', 'nat', 'win80')
+ self.oVM = None
+
+ # TODO: that should be moved to some common place
+ self.fEnableIOAPIC = True
+ self.cCpus = 1
+ self.fEnableNestedPaging = True
+ self.fEnablePAE = False
+ self.fSuspendHost = False
+ self.cSecSuspendTime = 60
+ self.cShutdownIters = 1
+ self.fExtraVm = False
+ self.sExtraVmName = "TestVM-Extra"
+ self.oExtraVM = None
+ self.fLocalCatch = False
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log(' --boot-hdd <HDD image file name>')
+
+ reporter.log(' --cpus <# CPUs>')
+ reporter.log(' --no-ioapic')
+ reporter.log(' --no-nested-paging')
+ reporter.log(' --pae')
+ reporter.log(' --suspend-host')
+ reporter.log(' --suspend-time <sec>')
+ reporter.log(' --shutdown-iters <# iters>')
+ reporter.log(' --extra-vm')
+ reporter.log(' --local-catch')
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+ if asArgs[iArg] == '--boot-hdd':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--boot-hdd" option requires an argument')
+ self.sHddName = asArgs[iArg]
+
+ elif asArgs[iArg] == '--cpus':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpus" option requires an argument')
+ self.cCpus = int(asArgs[iArg])
+ elif asArgs[iArg] == '--no-ioapic':
+ self.fEnableIOAPIC = False
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.fEnableNestedPaging = False
+ elif asArgs[iArg] == '--pae':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--suspend-host':
+ self.fSuspendHost = True
+ elif asArgs[iArg] == '--suspend-time':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--suspend-time" option requires an argument')
+ self.cSecSuspendTime = int(asArgs[iArg])
+ elif asArgs[iArg] == '--shutdown-iters':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--shutdown-iters" option requires an argument')
+ self.cShutdownIters = int(asArgs[iArg])
+ elif asArgs[iArg] == '--extra-vm':
+ self.fExtraVm = True
+ elif asArgs[iArg] == '--local-catch':
+ self.fLocalCatch = True
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def getResourceSet(self):
+ """
+ Returns a set of file and/or directory names relative to
+ TESTBOX_PATH_RESOURCES.
+ """
+ return [os.path.join(self.sHddPathBase, sRsrc) for sRsrc in self.kaoVmParams];
+
+ def _addVM(self, sVmName, sNicTraceFile=None):
+ """
+ Create VM
+ """
+ # Get VM params specific to HDD image
+ sKind, sController = self.kaoVmParams[self.sHddName]
+
+ # Create VM itself
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT
+ sHddPath = os.path.join(self.sHddPathBase, self.sHddName)
+ assert os.path.isfile(sHddPath)
+
+ oVM = \
+ self.createTestVM(sVmName, 1, sKind=sKind, cCpus=self.cCpus,
+ eNic0AttachType=eNic0AttachType, sDvdImage = self.sVBoxValidationKitIso)
+ assert oVM is not None
+
+ oSession = self.openSession(oVM)
+
+ # Attach an HDD
+ fRc = oSession.attachHd(sHddPath, sController, fImmutable=True)
+
+ # Enable HW virt
+ fRc = fRc and oSession.enableVirtEx(True)
+
+ # Enable I/O APIC
+ fRc = fRc and oSession.enableIoApic(self.fEnableIOAPIC)
+
+ # Enable Nested Paging
+ fRc = fRc and oSession.enableNestedPaging(self.fEnableNestedPaging)
+
+ # Enable PAE
+ fRc = fRc and oSession.enablePae(self.fEnablePAE)
+
+ if (sNicTraceFile is not None):
+ fRc = fRc and oSession.setNicTraceEnabled(True, sNicTraceFile)
+
+ # Remote desktop
+ oSession.setupVrdp(True)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = fRc and oSession.close()
+ assert fRc is True
+
+ return oVM
+
+ def actionConfig(self):
+ """
+ Configure pre-conditions.
+ """
+
+ if not self.importVBoxApi():
+ return False
+
+ # Save time: do not start VM if there is no way to suspend host
+ if (self.fSuspendHost is True and sys.platform not in self.kasSuspendAllowedPlatforms):
+ reporter.log('Platform [%s] is not in the list of supported platforms' % sys.platform)
+ return False
+
+ assert self.sHddName is not None
+ if self.sHddName not in self.kaoVmParams:
+ reporter.log('Error: unknown HDD image specified: %s' % self.sHddName)
+ return False
+
+ if (self.fExtraVm is True):
+ self.oExtraVM = self._addVM(self.sExtraVmName)
+
+ self.oVM = self._addVM(self.sVmName)
+
+ return vbox.TestDriver.actionConfig(self)
+
+ def _SuspendResume(self, cSecTimeout):
+ """
+ Put host into sleep and automatically resume it after specified timeout.
+ """
+ fRc = False
+
+ if (sys.platform == 'darwin'):
+ tsStart = time.time()
+ fRc = os.system("/usr/bin/pmset relative wake %d" % self.cSecSuspendTime)
+ fRc |= os.system("/usr/bin/pmset sleepnow")
+ # Wait for host to wake up
+ while ((time.time() - tsStart) < self.cSecSuspendTime):
+ self.sleep(0.1)
+
+ return fRc == 0
+
+ def _waitKeyboardInterrupt(self):
+ """
+ Idle loop until user press CTRL+C
+ """
+ reporter.log('[LOCAL CATCH]: waiting for keyboard interrupt')
+ while (True):
+ try:
+ self.sleep(1)
+ except KeyboardInterrupt:
+ reporter.log('[LOCAL CATCH]: keyboard interrupt occurred')
+ break
+
+ def actionExecute(self):
+ """
+ Execute the testcase itself.
+ """
+ #self.logVmInfo(self.oVM)
+
+ reporter.testStart('SHUTDOWN GUEST')
+
+ cIter = 0
+ fRc = True
+
+ if (self.fExtraVm is True):
+ oExtraSession, oExtraTxsSession = self.startVmAndConnectToTxsViaTcp(self.sExtraVmName,
+ fCdWait=False,
+ cMsTimeout=self.kcMsVmStartLimit)
+ if oExtraSession is None or oExtraTxsSession is None:
+ reporter.error('Unable to start extra VM.')
+ if (self.fLocalCatch is True):
+ self._waitKeyboardInterrupt()
+ reporter.testDone()
+ return False
+
+ while (cIter < self.cShutdownIters):
+
+ cIter += 1
+
+ reporter.log("Starting iteration #%d." % cIter)
+
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(self.sVmName,
+ fCdWait=False,
+ cMsTimeout=self.kcMsVmStartLimit)
+ if oSession is not None and oTxsSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest started. Connection to TXS service established.')
+
+ if (self.fSuspendHost is True):
+ reporter.log("Disconnect form TXS.")
+ fRc = fRc and self.txsDisconnect(oSession, oTxsSession)
+ if (fRc is not True):
+ reporter.log("Disconnect form TXS failed.")
+ else:
+ reporter.log('Put host to sleep and resume it automatically after %d seconds.' % self.cSecSuspendTime)
+ fRc = fRc and self._SuspendResume(self.cSecSuspendTime)
+ if (fRc is True):
+ reporter.log("Sleep/resume success.")
+ else:
+ reporter.log("Sleep/resume failed.")
+ reporter.log("Re-connect to TXS in 10 seconds.")
+ self.sleep(10)
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 2 * 60 * 10000)
+ if (fRc is not True):
+ reporter.log("Re-connect to TXS failed.")
+
+ if (fRc is True):
+ reporter.log('Attempt to shutdown guest.')
+ fRc = fRc and oTxsSession.syncShutdown(cMsTimeout=(4 * 60 * 1000))
+ if (fRc is True):
+ reporter.log('Shutdown request issued successfully.')
+ self.waitOnDirectSessionClose(self.oVM, self.kcMsVmShutdownLimit)
+ reporter.log('Shutdown %s.' % ('success' if fRc is True else 'failed'))
+ else:
+ reporter.error('Shutdown request failed.')
+
+ # Do not terminate failing VM in order to catch it.
+ if (fRc is not True and self.fLocalCatch is True):
+ self._waitKeyboardInterrupt()
+ break
+
+ fRc = fRc and self.terminateVmBySession(oSession)
+ reporter.log('VM terminated.')
+
+ else:
+ reporter.error('Guest did not start (iteration %d of %d)' % (cIter, self.cShutdownIters))
+ fRc = False
+
+ # Stop if fail
+ if (fRc is not True):
+ break
+
+ # Local catch at the end.
+ if (self.fLocalCatch is True):
+ reporter.log("Test completed. Waiting for user to press CTRL+C.")
+ self._waitKeyboardInterrupt()
+
+ if (self.fExtraVm is True):
+ fRc = fRc and self.terminateVmBySession(oExtraSession)
+
+ reporter.testDone()
+ return fRc is True
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsBootTest1().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk b/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk
new file mode 100644
index 00000000..74e46f1d
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Smoke Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSmokeTests
+ValidationKitTestsSmokeTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSmokeTests_INST = $(INST_VALIDATIONKIT)tests/smoketests/
+ValidationKitTestsSmokeTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSmokeTest1.py \
+ $(PATH_SUB_CURRENT)/tdExoticOrAncient1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSmokeTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py b/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py
new file mode 100755
index 00000000..392aa849
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdExoticOrAncient1.py $
+
+"""
+VirtualBox Validation Kit - Exotic and/or ancient OSes #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import vbox;
+
+
+class tdExoticOrAncient1(vbox.TestDriver):
+ """
+ VBox exotic and/or ancient OSes #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.selectSet( self.oTestVmManager.kfGrpAncient
+ | self.oTestVmManager.kfGrpExotic);
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Simple test.
+ self.logVmInfo(oVM);
+ if oTestVm.fGrouping & self.oTestVmManager.kfGrpNoTxs:
+ sResult = self.runVmAndMonitorComRawFile(oTestVm.sVmName, oTestVm.sCom1RawFile);
+ ## @todo sResult = self.runVmAndMonitorComRawFile(oTestVm.sVmName, oTestVm.getCom1RawFile());
+ return sResult == 'PASSED';
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True);
+ if oSession is not None:
+ return self.terminateVmBySession(oSession);
+ return False;
+
+if __name__ == '__main__':
+ sys.exit(tdExoticOrAncient1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py b/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py
new file mode 100755
index 00000000..f2477430
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSmokeTest1.py $
+
+"""
+VirtualBox Validation Kit - Smoke Test #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdSmokeTest1(vbox.TestDriver):
+ """
+ VBox Smoke Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet();
+ self.sNicAttachmentDef = 'mixed';
+ self.sNicAttachment = self.sNicAttachmentDef;
+ self.fQuick = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('Smoke Test #1 options:');
+ reporter.log(' --nic-attachment <bridged|nat|mixed>');
+ reporter.log(' Default: %s' % (self.sNicAttachmentDef));
+ reporter.log(' --quick');
+ reporter.log(' Very selective testing.')
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--nic-attachment':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-attachment" takes an argument');
+ self.sNicAttachment = asArgs[iArg];
+ if self.sNicAttachment not in ('bridged', 'nat', 'mixed'):
+ raise base.InvalidOption('The "--nic-attachment" value "%s" is not supported. Valid values are: bridged, nat' \
+ % (self.sNicAttachment));
+ elif asArgs[iArg] == '--quick':
+ # Disable all but a few VMs and configurations.
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ if oTestVm.sVmName == 'tst-win2k3ent': # 32-bit paging
+ oTestVm.asVirtModesSup = [ 'hwvirt' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-rhel5': # 32-bit paging
+ oTestVm.asVirtModesSup = [ 'raw' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-win2k8': # 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-sol10-64': # SMP, 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt' ];
+ oTestVm.acCpusSup = range(2, 3);
+ elif oTestVm.sVmName == 'tst-sol10': # SMP, 32-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(2, 3);
+ elif oTestVm.sVmName == 'tst-nsthwvirt-ubuntu-64': # Nested hw.virt, 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(1, 2);
+ else:
+ oTestVm.fSkip = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ # Do the configuring.
+ if self.sNicAttachment == 'nat': eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ elif self.sNicAttachment == 'bridged': eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ else: eNic0AttachType = None;
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Simple test.
+ self.logVmInfo(oVM);
+ # Try waiting for a bit longer (15 minutes) until the CD is available to avoid running into timeouts.
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True, cMsCdWait = 15 * 60 * 1000);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ ## @todo do some quick tests: save, restore, execute some test program, shut down the guest.
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ return True;
+ return None;
+
+if __name__ == '__main__':
+ sys.exit(tdSmokeTest1().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/storage/Makefile.kmk b/src/VBox/ValidationKit/tests/storage/Makefile.kmk
new file mode 100644
index 00000000..bcb88ec8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/Makefile.kmk
@@ -0,0 +1,56 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Storage Tests.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsStorage
+ValidationKitTestsStorage_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsStorage_INST = $(INST_VALIDATIONKIT)tests/storage/
+ValidationKitTestsStorage_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdStorageBenchmark1.py \
+ $(PATH_SUB_CURRENT)/tdStorageSnapshotMerging1.py \
+ $(PATH_SUB_CURRENT)/tdStorageStress1.py \
+ $(PATH_SUB_CURRENT)/tdStorageRawDrive1.py \
+ $(PATH_SUB_CURRENT)/remoteexecutor.py \
+ $(PATH_SUB_CURRENT)/storagecfg.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsStorage_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/storage/remoteexecutor.py b/src/VBox/ValidationKit/tests/storage/remoteexecutor.py
new file mode 100755
index 00000000..7f9b1532
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/remoteexecutor.py
@@ -0,0 +1,314 @@
+# -*- coding: utf-8 -*-
+# $Id: remoteexecutor.py $
+
+"""
+VirtualBox Validation Kit - Storage benchmark, test execution helpers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import array;
+import os;
+import shutil;
+import sys;
+if sys.version_info[0] >= 3:
+ from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+else:
+ from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+import subprocess;
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+
+
+
+class StdInOutBuffer(object):
+ """ Standard input output buffer """
+
+ def __init__(self, sInput = None):
+ self.sInput = StringIO();
+ if sInput is not None:
+ self.sInput.write(self._toString(sInput));
+ self.sInput.seek(0);
+ self.sOutput = '';
+
+ def _toString(self, sText):
+ """
+ Converts any possible array to
+ a string.
+ """
+ if isinstance(sText, array.array):
+ try:
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ return str(sText.tostring()); # pylint: disable=no-member
+ return str(sText.tobytes());
+ except:
+ pass;
+ elif isinstance(sText, bytes):
+ return sText.decode('utf-8');
+
+ return sText;
+
+ def read(self, cb):
+ """file.read"""
+ return self.sInput.read(cb);
+
+ def write(self, sText):
+ """file.write"""
+ self.sOutput += self._toString(sText);
+ return None;
+
+ def getOutput(self):
+ """
+ Returns the output of the buffer.
+ """
+ return self.sOutput;
+
+ def close(self):
+ """ file.close """
+ return;
+
+class RemoteExecutor(object):
+ """
+ Helper for executing tests remotely through TXS or locally
+ """
+
+ def __init__(self, oTxsSession = None, asBinaryPaths = None, sScratchPath = None):
+ self.oTxsSession = oTxsSession;
+ self.asPaths = asBinaryPaths;
+ self.sScratchPath = sScratchPath;
+ if self.asPaths is None:
+ self.asPaths = [ ];
+
+ def _getBinaryPath(self, sBinary):
+ """
+ Returns the complete path of the given binary if found
+ from the configured search path or None if not found.
+ """
+ for sPath in self.asPaths:
+ sFile = sPath + '/' + sBinary;
+ if self.isFile(sFile):
+ return sFile;
+ return sBinary;
+
+ def _sudoExecuteSync(self, asArgs, sInput):
+ """
+ Executes a sudo child process synchronously.
+ Returns a tuple [True, 0] if the process executed successfully
+ and returned 0, otherwise [False, rc] is returned.
+ """
+ reporter.log('Executing [sudo]: %s' % (asArgs, ));
+ reporter.flushall();
+ fRc = True;
+ sOutput = '';
+ sError = '';
+ try:
+ oProcess = utils.sudoProcessPopen(asArgs, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell = False, close_fds = False);
+
+ sOutput, sError = oProcess.communicate(sInput);
+ iExitCode = oProcess.poll();
+
+ if iExitCode != 0:
+ fRc = False;
+ except:
+ reporter.errorXcpt();
+ fRc = False;
+ reporter.log('Exit code [sudo]: %s (%s)' % (fRc, asArgs));
+ return (fRc, str(sOutput), str(sError));
+
+ def _execLocallyOrThroughTxs(self, sExec, asArgs, sInput, cMsTimeout):
+ """
+ Executes the given program locally or through TXS based on the
+ current config.
+ """
+ fRc = False;
+ sOutput = None;
+ if self.oTxsSession is not None:
+ reporter.log('Executing [remote]: %s %s %s' % (sExec, asArgs, sInput));
+ reporter.flushall();
+ oStdOut = StdInOutBuffer();
+ oStdErr = StdInOutBuffer();
+ oTestPipe = reporter.FileWrapperTestPipe();
+ oStdIn = None;
+ if sInput is not None:
+ oStdIn = StdInOutBuffer(sInput);
+ else:
+ oStdIn = '/dev/null'; # pylint: disable=redefined-variable-type
+ fRc = self.oTxsSession.syncExecEx(sExec, (sExec,) + asArgs,
+ oStdIn = oStdIn, oStdOut = oStdOut,
+ oStdErr = oStdErr, oTestPipe = oTestPipe,
+ cMsTimeout = cMsTimeout);
+ sOutput = oStdOut.getOutput();
+ sError = oStdErr.getOutput();
+ if fRc is False:
+ reporter.log('Exit code [remote]: %s (stdout: %s stderr: %s)' % (fRc, sOutput, sError));
+ else:
+ reporter.log('Exit code [remote]: %s' % (fRc,));
+ else:
+ fRc, sOutput, sError = self._sudoExecuteSync([sExec, ] + list(asArgs), sInput);
+ return (fRc, sOutput, sError);
+
+ def execBinary(self, sExec, asArgs, sInput = None, cMsTimeout = 3600000):
+ """
+ Executes the given binary with the given arguments
+ providing some optional input through stdin and
+ returning whether the process exited successfully and the output
+ in a string.
+ """
+
+ fRc = True;
+ sOutput = None;
+ sError = None;
+ sBinary = self._getBinaryPath(sExec);
+ if sBinary is not None:
+ fRc, sOutput, sError = self._execLocallyOrThroughTxs(sBinary, asArgs, sInput, cMsTimeout);
+ else:
+ fRc = False;
+ return (fRc, sOutput, sError);
+
+ def execBinaryNoStdOut(self, sExec, asArgs, sInput = None):
+ """
+ Executes the given binary with the given arguments
+ providing some optional input through stdin and
+ returning whether the process exited successfully.
+ """
+ fRc, _, _ = self.execBinary(sExec, asArgs, sInput);
+ return fRc;
+
+ def copyFile(self, sLocalFile, sFilename, cMsTimeout = 30000):
+ """
+ Copies the local file to the remote destination
+ if configured
+
+ Returns a file ID which can be used as an input parameter
+ to execBinary() resolving to the real filepath on the remote side
+ or locally.
+ """
+ sFileId = None;
+ if self.oTxsSession is not None:
+ sFileId = '${SCRATCH}/' + sFilename;
+ fRc = self.oTxsSession.syncUploadFile(sLocalFile, sFileId, cMsTimeout);
+ if not fRc:
+ sFileId = None;
+ else:
+ sFileId = self.sScratchPath + '/' + sFilename;
+ try:
+ shutil.copy(sLocalFile, sFileId);
+ except:
+ sFileId = None;
+
+ return sFileId;
+
+ def copyString(self, sContent, sFilename, cMsTimeout = 30000):
+ """
+ Creates a file remotely or locally with the given content.
+
+ Returns a file ID which can be used as an input parameter
+ to execBinary() resolving to the real filepath on the remote side
+ or locally.
+ """
+ sFileId = None;
+ if self.oTxsSession is not None:
+ sFileId = '${SCRATCH}/' + sFilename;
+ fRc = self.oTxsSession.syncUploadString(sContent, sFileId, cMsTimeout);
+ if not fRc:
+ sFileId = None;
+ else:
+ sFileId = self.sScratchPath + '/' + sFilename;
+ try:
+ with open(sFileId, 'wb') as oFile:
+ oFile.write(sContent);
+ except:
+ sFileId = None;
+
+ return sFileId;
+
+ def mkDir(self, sDir, fMode = 0o700, cMsTimeout = 30000):
+ """
+ Creates a new directory at the given location.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncMkDir(sDir, fMode, cMsTimeout);
+ elif not os.path.isdir(sDir):
+ fRc = os.mkdir(sDir, fMode);
+
+ return fRc;
+
+ def rmDir(self, sDir, cMsTimeout = 30000):
+ """
+ Removes the given directory.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncRmDir(sDir, cMsTimeout);
+ else:
+ fRc = self.execBinaryNoStdOut('rmdir', (sDir,));
+
+ return fRc;
+
+ def rmTree(self, sDir, cMsTimeout = 30000):
+ """
+ Recursively removes all files and sub directories including the given directory.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncRmTree(sDir, cMsTimeout);
+ else:
+ try:
+ shutil.rmtree(sDir, ignore_errors=True);
+ except:
+ fRc = False;
+
+ return fRc;
+
+ def isFile(self, sPath, cMsTimeout = 30000):
+ """
+ Checks that the given file exists.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncIsFile(sPath, cMsTimeout);
+ else:
+ try:
+ fRc = os.path.isfile(sPath);
+ except:
+ fRc = False;
+
+ return fRc;
diff --git a/src/VBox/ValidationKit/tests/storage/storagecfg.py b/src/VBox/ValidationKit/tests/storage/storagecfg.py
new file mode 100755
index 00000000..c6bb2266
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/storagecfg.py
@@ -0,0 +1,681 @@
+# -*- coding: utf-8 -*-
+# $Id: storagecfg.py $
+
+"""
+VirtualBox Validation Kit - Storage test configuration API.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import os;
+import re;
+
+
+class StorageDisk(object):
+ """
+ Class representing a disk for testing.
+ """
+
+ def __init__(self, sPath, fRamDisk = False):
+ self.sPath = sPath;
+ self.fUsed = False;
+ self.fRamDisk = fRamDisk;
+
+ def getPath(self):
+ """
+ Return the disk path.
+ """
+ return self.sPath;
+
+ def isUsed(self):
+ """
+ Returns whether the disk is currently in use.
+ """
+ return self.fUsed;
+
+ def isRamDisk(self):
+ """
+ Returns whether the disk objecthas a RAM backing.
+ """
+ return self.fRamDisk;
+
+ def setUsed(self, fUsed):
+ """
+ Sets the used flag for the disk.
+ """
+ if fUsed:
+ if self.fUsed:
+ return False;
+
+ self.fUsed = True;
+ else:
+ self.fUsed = fUsed;
+
+ return True;
+
+class StorageConfigOs(object):
+ """
+ Base class for a single hosts OS storage configuration.
+ """
+
+ def _getDisksMatchingRegExpWithPath(self, sPath, sRegExp):
+ """
+ Adds new disks to the config matching the given regular expression.
+ """
+
+ lstDisks = [];
+ oRegExp = re.compile(sRegExp);
+ asFiles = os.listdir(sPath);
+ for sFile in asFiles:
+ if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sPath + '/' + sFile):
+ lstDisks.append(StorageDisk(sPath + '/' + sFile));
+
+ return lstDisks;
+
+class StorageConfigOsSolaris(StorageConfigOs):
+ """
+ Class implementing the Solaris specifics for a storage configuration.
+ """
+
+ def __init__(self):
+ StorageConfigOs.__init__(self);
+ self.idxRamDisk = 0;
+
+ def _getActivePoolsStartingWith(self, oExec, sPoolIdStart):
+ """
+ Returns a list of pools starting with the given ID or None on failure.
+ """
+ lstPools = None;
+ fRc, sOutput, _ = oExec.execBinary('zpool', ('list', '-H'));
+ if fRc:
+ lstPools = [];
+ asPools = sOutput.splitlines();
+ for sPool in asPools:
+ if sPool.startswith(sPoolIdStart):
+ # Extract the whole name and add it to the list.
+ asItems = sPool.split('\t');
+ lstPools.append(asItems[0]);
+ return lstPools;
+
+ def _getActiveVolumesInPoolStartingWith(self, oExec, sPool, sVolumeIdStart):
+ """
+ Returns a list of active volumes for the given pool starting with the given
+ identifier or None on failure.
+ """
+ lstVolumes = None;
+ fRc, sOutput, _ = oExec.execBinary('zfs', ('list', '-H'));
+ if fRc:
+ lstVolumes = [];
+ asVolumes = sOutput.splitlines();
+ for sVolume in asVolumes:
+ if sVolume.startswith(sPool + '/' + sVolumeIdStart):
+ # Extract the whole name and add it to the list.
+ asItems = sVolume.split('\t');
+ lstVolumes.append(asItems[0]);
+ return lstVolumes;
+
+ def getDisksMatchingRegExp(self, sRegExp):
+ """
+ Returns a list of disks matching the regular expression.
+ """
+ return self._getDisksMatchingRegExpWithPath('/dev/dsk', sRegExp);
+
+ def getMntBase(self):
+ """
+ Returns the mountpoint base for the host.
+ """
+ return '/pools';
+
+ def createStoragePool(self, oExec, sPool, asDisks, sRaidLvl):
+ """
+ Creates a new storage pool with the given disks and the given RAID level.
+ """
+ sZPoolRaid = None;
+ if len(asDisks) > 1 and (sRaidLvl == 'raid5' or sRaidLvl is None):
+ sZPoolRaid = 'raidz';
+
+ fRc = True;
+ if sZPoolRaid is not None:
+ fRc = oExec.execBinaryNoStdOut('zpool', ('create', '-f', sPool, sZPoolRaid,) + tuple(asDisks));
+ else:
+ fRc = oExec.execBinaryNoStdOut('zpool', ('create', '-f', sPool,) + tuple(asDisks));
+
+ return fRc;
+
+ def createVolume(self, oExec, sPool, sVol, sMountPoint, cbVol = None):
+ """
+ Creates and mounts a filesystem at the given mountpoint using the
+ given pool and volume IDs.
+ """
+ fRc = True;
+ if cbVol is not None:
+ fRc = oExec.execBinaryNoStdOut('zfs', ('create', '-o', 'mountpoint='+sMountPoint, '-V', cbVol, sPool + '/' + sVol));
+ else:
+ fRc = oExec.execBinaryNoStdOut('zfs', ('create', '-o', 'mountpoint='+sMountPoint, sPool + '/' + sVol));
+
+ # @todo Add proper parameters to set proper owner:group ownership, the testcase broke in r133060 for Solaris
+ # because ceating directories is now done using the python mkdir API instead of calling 'sudo mkdir...'.
+ # No one noticed though because testboxstor1 went out of action before...
+ # Will get fixed as soon as I'm back home.
+ if fRc:
+ fRc = oExec.execBinaryNoStdOut('chmod', ('777', sMountPoint));
+
+ return fRc;
+
+ def destroyVolume(self, oExec, sPool, sVol):
+ """
+ Destroys the given volume.
+ """
+ fRc = oExec.execBinaryNoStdOut('zfs', ('destroy', sPool + '/' + sVol));
+ return fRc;
+
+ def destroyPool(self, oExec, sPool):
+ """
+ Destroys the given storage pool.
+ """
+ fRc = oExec.execBinaryNoStdOut('zpool', ('destroy', sPool));
+ return fRc;
+
+ def cleanupPoolsAndVolumes(self, oExec, sPoolIdStart, sVolIdStart):
+ """
+ Cleans up any pools and volumes starting with the name in the given
+ parameters.
+ """
+ fRc = True;
+ lstPools = self._getActivePoolsStartingWith(oExec, sPoolIdStart);
+ if lstPools is not None:
+ for sPool in lstPools:
+ lstVolumes = self._getActiveVolumesInPoolStartingWith(oExec, sPool, sVolIdStart);
+ if lstVolumes is not None:
+ # Destroy all the volumes first
+ for sVolume in lstVolumes:
+ fRc2 = oExec.execBinaryNoStdOut('zfs', ('destroy', sVolume));
+ if not fRc2:
+ fRc = fRc2;
+
+ # Destroy the pool
+ fRc2 = self.destroyPool(oExec, sPool);
+ if not fRc2:
+ fRc = fRc2;
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def createRamDisk(self, oExec, cbRamDisk):
+ """
+ Creates a RAM backed disk with the given size.
+ """
+ oDisk = None;
+ sRamDiskName = 'ramdisk%u' % (self.idxRamDisk,);
+ fRc, _ , _ = oExec.execBinary('ramdiskadm', ('-a', sRamDiskName, str(cbRamDisk)));
+ if fRc:
+ self.idxRamDisk += 1;
+ oDisk = StorageDisk('/dev/ramdisk/%s' % (sRamDiskName, ), True);
+
+ return oDisk;
+
+ def destroyRamDisk(self, oExec, oDisk):
+ """
+ Destroys the given ramdisk object.
+ """
+ sRamDiskName = os.path.basename(oDisk.getPath());
+ return oExec.execBinaryNoStdOut('ramdiskadm', ('-d', sRamDiskName));
+
+class StorageConfigOsLinux(StorageConfigOs):
+ """
+ Class implementing the Linux specifics for a storage configuration.
+ """
+
+ def __init__(self):
+ StorageConfigOs.__init__(self);
+ self.dSimplePools = { }; # Simple storage pools which don't use lvm (just one partition)
+ self.dMounts = { }; # Pool/Volume to mountpoint mapping.
+
+ def _getDmRaidLevelFromLvl(self, sRaidLvl):
+ """
+ Converts our raid level indicators to something mdadm can understand.
+ """
+ if sRaidLvl is None or sRaidLvl == 'raid0':
+ return 'stripe';
+ if sRaidLvl == 'raid5':
+ return '5';
+ if sRaidLvl == 'raid1':
+ return 'mirror';
+ return 'stripe';
+
+ def getDisksMatchingRegExp(self, sRegExp):
+ """
+ Returns a list of disks matching the regular expression.
+ """
+ return self._getDisksMatchingRegExpWithPath('/dev/', sRegExp);
+
+ def getMntBase(self):
+ """
+ Returns the mountpoint base for the host.
+ """
+ return '/mnt';
+
+ def createStoragePool(self, oExec, sPool, asDisks, sRaidLvl):
+ """
+ Creates a new storage pool with the given disks and the given RAID level.
+ """
+ fRc = True;
+ if len(asDisks) == 1 and sRaidLvl is None:
+ # Doesn't require LVM, put into the simple pools dictionary so we can
+ # use it when creating a volume later.
+ self.dSimplePools[sPool] = asDisks[0];
+ else:
+ # If a RAID is required use dm-raid first to create one.
+ asLvmPvDisks = asDisks;
+ fRc = oExec.execBinaryNoStdOut('mdadm', ('--create', '/dev/md0', '--assume-clean',
+ '--level=' + self._getDmRaidLevelFromLvl(sRaidLvl),
+ '--raid-devices=' + str(len(asDisks))) + tuple(asDisks));
+ if fRc:
+ # /dev/md0 is the only block device to use for our volume group.
+ asLvmPvDisks = [ '/dev/md0' ];
+
+ # Create a physical volume on every disk first.
+ for sLvmPvDisk in asLvmPvDisks:
+ fRc = oExec.execBinaryNoStdOut('pvcreate', (sLvmPvDisk, ));
+ if not fRc:
+ break;
+
+ if fRc:
+ # Create volume group with all physical volumes included
+ fRc = oExec.execBinaryNoStdOut('vgcreate', (sPool, ) + tuple(asLvmPvDisks));
+ return fRc;
+
+ def createVolume(self, oExec, sPool, sVol, sMountPoint, cbVol = None):
+ """
+ Creates and mounts a filesystem at the given mountpoint using the
+ given pool and volume IDs.
+ """
+ fRc = True;
+ sBlkDev = None;
+ if sPool in self.dSimplePools:
+ sDiskPath = self.dSimplePools.get(sPool);
+ if sDiskPath.find('zram') != -1:
+ sBlkDev = sDiskPath;
+ else:
+ # Create a partition with the requested size
+ sFdiskScript = ';\n'; # Single partition filling everything
+ if cbVol is not None:
+ sFdiskScript = ',' + str(cbVol // 512) + '\n'; # Get number of sectors
+ fRc = oExec.execBinaryNoStdOut('sfdisk', ('--no-reread', '--wipe', 'always', '-q', '-f', sDiskPath), \
+ sFdiskScript);
+ if fRc:
+ if sDiskPath.find('nvme') != -1:
+ sBlkDev = sDiskPath + 'p1';
+ else:
+ sBlkDev = sDiskPath + '1';
+ else:
+ if cbVol is None:
+ fRc = oExec.execBinaryNoStdOut('lvcreate', ('-l', '100%FREE', '-n', sVol, sPool));
+ else:
+ fRc = oExec.execBinaryNoStdOut('lvcreate', ('-L', str(cbVol), '-n', sVol, sPool));
+ if fRc:
+ sBlkDev = '/dev/mapper' + sPool + '-' + sVol;
+
+ if fRc is True and sBlkDev is not None:
+ # Create a filesystem and mount it
+ fRc = oExec.execBinaryNoStdOut('mkfs.ext4', ('-F', '-F', sBlkDev,));
+ fRc = fRc and oExec.mkDir(sMountPoint);
+ fRc = fRc and oExec.execBinaryNoStdOut('mount', (sBlkDev, sMountPoint));
+ if fRc:
+ self.dMounts[sPool + '/' + sVol] = sMountPoint;
+ return fRc;
+
+ def destroyVolume(self, oExec, sPool, sVol):
+ """
+ Destroys the given volume.
+ """
+ # Unmount first
+ sMountPoint = self.dMounts[sPool + '/' + sVol];
+ fRc = oExec.execBinaryNoStdOut('umount', (sMountPoint,));
+ self.dMounts.pop(sPool + '/' + sVol);
+ oExec.rmDir(sMountPoint);
+ if sPool in self.dSimplePools:
+ # Wipe partition table
+ sDiskPath = self.dSimplePools.get(sPool);
+ if sDiskPath.find('zram') == -1:
+ fRc = oExec.execBinaryNoStdOut('sfdisk', ('--no-reread', '--wipe', 'always', '-q', '-f', '--delete', \
+ sDiskPath));
+ else:
+ fRc = oExec.execBinaryNoStdOut('lvremove', (sPool + '/' + sVol,));
+ return fRc;
+
+ def destroyPool(self, oExec, sPool):
+ """
+ Destroys the given storage pool.
+ """
+ fRc = True;
+ if sPool in self.dSimplePools:
+ self.dSimplePools.pop(sPool);
+ else:
+ fRc = oExec.execBinaryNoStdOut('vgremove', (sPool,));
+ return fRc;
+
+ def cleanupPoolsAndVolumes(self, oExec, sPoolIdStart, sVolIdStart):
+ """
+ Cleans up any pools and volumes starting with the name in the given
+ parameters.
+ """
+ # @todo: Needs implementation, for LVM based configs a similar approach can be used
+ # as for Solaris.
+ _ = oExec;
+ _ = sPoolIdStart;
+ _ = sVolIdStart;
+ return True;
+
+ def createRamDisk(self, oExec, cbRamDisk):
+ """
+ Creates a RAM backed disk with the given size.
+ """
+ # Make sure the ZRAM module is loaded.
+ oDisk = None;
+ fRc = oExec.execBinaryNoStdOut('modprobe', ('zram',));
+ if fRc:
+ fRc, sOut, _ = oExec.execBinary('zramctl', ('--raw', '-f', '-s', str(cbRamDisk)));
+ if fRc:
+ oDisk = StorageDisk(sOut.rstrip(), True);
+
+ return oDisk;
+
+ def destroyRamDisk(self, oExec, oDisk):
+ """
+ Destroys the given ramdisk object.
+ """
+ return oExec.execBinaryNoStdOut('zramctl', ('-r', oDisk.getPath()));
+
+## @name Host disk config types.
+## @{
+g_ksDiskCfgStatic = 'StaticDir';
+g_ksDiskCfgRegExp = 'RegExp';
+g_ksDiskCfgList = 'DiskList';
+## @}
+
+class DiskCfg(object):
+ """
+ Host disk configuration.
+ """
+
+ def __init__(self, sTargetOs, sCfgType, oDisks):
+ self.sTargetOs = sTargetOs;
+ self.sCfgType = sCfgType;
+ self.oDisks = oDisks;
+
+ def getTargetOs(self):
+ return self.sTargetOs;
+
+ def getCfgType(self):
+ return self.sCfgType;
+
+ def isCfgStaticDir(self):
+ return self.sCfgType == g_ksDiskCfgStatic;
+
+ def isCfgRegExp(self):
+ return self.sCfgType == g_ksDiskCfgRegExp;
+
+ def isCfgList(self):
+ return self.sCfgType == g_ksDiskCfgList;
+
+ def getDisks(self):
+ return self.oDisks;
+
+class StorageCfg(object):
+ """
+ Storage configuration helper class taking care of the different host OS.
+ """
+
+ def __init__(self, oExec, oDiskCfg):
+ self.oExec = oExec;
+ self.lstDisks = [ ]; # List of disks present in the system.
+ self.dPools = { }; # Dictionary of storage pools.
+ self.dVols = { }; # Dictionary of volumes.
+ self.iPoolId = 0;
+ self.iVolId = 0;
+ self.oDiskCfg = oDiskCfg;
+
+ fRc = True;
+ oStorOs = None;
+ if oDiskCfg.getTargetOs() == 'solaris':
+ oStorOs = StorageConfigOsSolaris();
+ elif oDiskCfg.getTargetOs() == 'linux':
+ oStorOs = StorageConfigOsLinux(); # pylint: disable=redefined-variable-type
+ elif not oDiskCfg.isCfgStaticDir():
+ # For unknown hosts only allow a static testing directory we don't care about setting up
+ fRc = False;
+
+ if fRc:
+ self.oStorOs = oStorOs;
+ if oDiskCfg.isCfgRegExp():
+ self.lstDisks = oStorOs.getDisksMatchingRegExp(oDiskCfg.getDisks());
+ elif oDiskCfg.isCfgList():
+ # Assume a list of of disks and add.
+ for sDisk in oDiskCfg.getDisks():
+ self.lstDisks.append(StorageDisk(sDisk));
+ elif oDiskCfg.isCfgStaticDir():
+ if not os.path.exists(oDiskCfg.getDisks()):
+ self.oExec.mkDir(oDiskCfg.getDisks(), 0o700);
+
+ def __del__(self):
+ self.cleanup();
+ self.oDiskCfg = None;
+
+ def cleanup(self):
+ """
+ Cleans up any created storage configs.
+ """
+
+ if not self.oDiskCfg.isCfgStaticDir():
+ # Destroy all volumes first.
+ for sMountPoint in list(self.dVols.keys()): # pylint: disable=consider-iterating-dictionary
+ self.destroyVolume(sMountPoint);
+
+ # Destroy all pools.
+ for sPool in list(self.dPools.keys()): # pylint: disable=consider-iterating-dictionary
+ self.destroyStoragePool(sPool);
+
+ self.dVols.clear();
+ self.dPools.clear();
+ self.iPoolId = 0;
+ self.iVolId = 0;
+
+ def getRawDisk(self):
+ """
+ Returns a raw disk device from the list of free devices for use.
+ """
+
+ for oDisk in self.lstDisks:
+ if oDisk.isUsed() is False:
+ oDisk.setUsed(True);
+ return oDisk.getPath();
+
+ return None;
+
+ def getUnusedDiskCount(self):
+ """
+ Returns the number of unused disks.
+ """
+
+ cDisksUnused = 0;
+ for oDisk in self.lstDisks:
+ if not oDisk.isUsed():
+ cDisksUnused += 1;
+
+ return cDisksUnused;
+
+ def createStoragePool(self, cDisks = 0, sRaidLvl = None,
+ cbPool = None, fRamDisk = False):
+ """
+ Create a new storage pool
+ """
+ lstDisks = [ ];
+ fRc = True;
+ sPool = None;
+
+ if not self.oDiskCfg.isCfgStaticDir():
+ if fRamDisk:
+ oDisk = self.oStorOs.createRamDisk(self.oExec, cbPool);
+ if oDisk is not None:
+ lstDisks.append(oDisk);
+ cDisks = 1;
+ else:
+ if cDisks == 0:
+ cDisks = self.getUnusedDiskCount();
+
+ for oDisk in self.lstDisks:
+ if not oDisk.isUsed():
+ oDisk.setUsed(True);
+ lstDisks.append(oDisk);
+ if len(lstDisks) == cDisks:
+ break;
+
+ # Enough drives to satisfy the request?
+ if len(lstDisks) == cDisks:
+ # Create a list of all device paths
+ lstDiskPaths = [ ];
+ for oDisk in lstDisks:
+ lstDiskPaths.append(oDisk.getPath());
+
+ # Find a name for the pool
+ sPool = 'pool' + str(self.iPoolId);
+ self.iPoolId += 1;
+
+ fRc = self.oStorOs.createStoragePool(self.oExec, sPool, lstDiskPaths, sRaidLvl);
+ if fRc:
+ self.dPools[sPool] = lstDisks;
+ else:
+ self.iPoolId -= 1;
+ else:
+ fRc = False;
+
+ # Cleanup in case of error.
+ if not fRc:
+ for oDisk in lstDisks:
+ oDisk.setUsed(False);
+ if oDisk.isRamDisk():
+ self.oStorOs.destroyRamDisk(self.oExec, oDisk);
+ else:
+ sPool = 'StaticDummy';
+
+ return fRc, sPool;
+
+ def destroyStoragePool(self, sPool):
+ """
+ Destroys the storage pool with the given ID.
+ """
+
+ fRc = True;
+
+ if not self.oDiskCfg.isCfgStaticDir():
+ lstDisks = self.dPools.get(sPool);
+ if lstDisks is not None:
+ fRc = self.oStorOs.destroyPool(self.oExec, sPool);
+ if fRc:
+ # Mark disks as unused
+ self.dPools.pop(sPool);
+ for oDisk in lstDisks:
+ oDisk.setUsed(False);
+ if oDisk.isRamDisk():
+ self.oStorOs.destroyRamDisk(self.oExec, oDisk);
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def createVolume(self, sPool, cbVol = None):
+ """
+ Creates a new volume from the given pool returning the mountpoint.
+ """
+
+ fRc = True;
+ sMountPoint = None;
+ if not self.oDiskCfg.isCfgStaticDir():
+ if sPool in self.dPools:
+ sVol = 'vol' + str(self.iVolId);
+ sMountPoint = self.oStorOs.getMntBase() + '/' + sVol;
+ self.iVolId += 1;
+ fRc = self.oStorOs.createVolume(self.oExec, sPool, sVol, sMountPoint, cbVol);
+ if fRc:
+ self.dVols[sMountPoint] = (sVol, sPool);
+ else:
+ self.iVolId -= 1;
+ else:
+ fRc = False;
+ else:
+ sMountPoint = self.oDiskCfg.getDisks();
+
+ return fRc, sMountPoint;
+
+ def destroyVolume(self, sMountPoint):
+ """
+ Destroy the volume at the given mount point.
+ """
+
+ fRc = True;
+ if not self.oDiskCfg.isCfgStaticDir():
+ sVol, sPool = self.dVols.get(sMountPoint);
+ if sVol is not None:
+ fRc = self.oStorOs.destroyVolume(self.oExec, sPool, sVol);
+ if fRc:
+ self.dVols.pop(sMountPoint);
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def mkDirOnVolume(self, sMountPoint, sDir, fMode = 0o700):
+ """
+ Creates a new directory on the volume pointed to by the given mount point.
+ """
+ return self.oExec.mkDir(sMountPoint + '/' + sDir, fMode);
+
+ def cleanupLeftovers(self):
+ """
+ Tries to cleanup any leftover pools and volumes from a failed previous run.
+ """
+ if not self.oDiskCfg.isCfgStaticDir():
+ return self.oStorOs.cleanupPoolsAndVolumes(self.oExec, 'pool', 'vol');
+
+ fRc = True;
+ if os.path.exists(self.oDiskCfg.getDisks()):
+ for sEntry in os.listdir(self.oDiskCfg.getDisks()):
+ fRc = fRc and self.oExec.rmTree(os.path.join(self.oDiskCfg.getDisks(), sEntry));
+
+ return fRc;
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py b/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py
new file mode 100755
index 00000000..17dc09b2
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py
@@ -0,0 +1,1469 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdStorageBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Storage benchmark.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import socket;
+import sys;
+if sys.version_info[0] >= 3:
+ from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+else:
+ from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import constants;
+from common import utils;
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxwrappers;
+
+import remoteexecutor;
+import storagecfg;
+
+
+class FioTest(object):
+ """
+ Flexible I/O tester testcase.
+ """
+
+ kdHostIoEngine = {
+ 'solaris': ('solarisaio', False),
+ 'linux': ('libaio', True)
+ };
+
+ def __init__(self, oExecutor, dCfg = None):
+ self.oExecutor = oExecutor;
+ self.sCfgFileId = None;
+ self.dCfg = dCfg;
+ self.sError = None;
+ self.sResult = None;
+
+ def prepare(self, cMsTimeout = 30000):
+ """ Prepares the testcase """
+ reporter.testStart('Fio');
+
+ sTargetOs = self.dCfg.get('TargetOs', 'linux');
+ sIoEngine, fDirectIo = self.kdHostIoEngine.get(sTargetOs);
+ if sIoEngine is None:
+ return False;
+
+ cfgBuf = StringIO();
+ cfgBuf.write('[global]\n');
+ cfgBuf.write('bs=' + str(self.dCfg.get('RecordSize', 4096)) + '\n');
+ cfgBuf.write('ioengine=' + sIoEngine + '\n');
+ cfgBuf.write('iodepth=' + str(self.dCfg.get('QueueDepth', 32)) + '\n');
+ cfgBuf.write('size=' + str(self.dCfg.get('TestsetSize', 2147483648)) + '\n');
+ if fDirectIo:
+ cfgBuf.write('direct=1\n');
+ else:
+ cfgBuf.write('direct=0\n');
+ cfgBuf.write('directory=' + self.dCfg.get('FilePath', '/mnt') + '\n');
+ cfgBuf.write('filename=fio.test.file');
+
+ cfgBuf.write('[seq-write]\n');
+ cfgBuf.write('rw=write\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[rand-write]\n');
+ cfgBuf.write('rw=randwrite\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[seq-read]\n');
+ cfgBuf.write('rw=read\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[rand-read]\n');
+ cfgBuf.write('rw=randread\n');
+ cfgBuf.write('stonewall\n');
+
+ self.sCfgFileId = self.oExecutor.copyString(cfgBuf.getvalue(), 'aio-test', cMsTimeout);
+ return self.sCfgFileId is not None;
+
+ def run(self, cMsTimeout = 30000):
+ """ Runs the testcase """
+ fRc, sOutput, sError = self.oExecutor.execBinary('fio', (self.sCfgFileId,), cMsTimeout = cMsTimeout);
+ if fRc:
+ self.sResult = sOutput;
+ else:
+ self.sError = ('Binary: fio\n' +
+ '\nOutput:\n\n' +
+ sOutput +
+ '\nError:\n\n' +
+ sError);
+ return fRc;
+
+ def cleanup(self):
+ """ Cleans up any leftovers from the testcase. """
+ reporter.testDone();
+ return True;
+
+ def reportResult(self):
+ """
+ Reports the test results to the test manager.
+ """
+ return True;
+
+ def getErrorReport(self):
+ """
+ Returns the error report in case the testcase failed.
+ """
+ return self.sError;
+
+class IozoneTest(object):
+ """
+ I/O zone testcase.
+ """
+ def __init__(self, oExecutor, dCfg = None):
+ self.oExecutor = oExecutor;
+ self.sResult = None;
+ self.sError = None;
+ self.lstTests = [ ('initial writers', 'FirstWrite'),
+ ('rewriters', 'Rewrite'),
+ ('re-readers', 'ReRead'),
+ ('stride readers', 'StrideRead'),
+ ('reverse readers', 'ReverseRead'),
+ ('random readers', 'RandomRead'),
+ ('mixed workload', 'MixedWorkload'),
+ ('random writers', 'RandomWrite'),
+ ('pwrite writers', 'PWrite'),
+ ('pread readers', 'PRead'),
+ ('fwriters', 'FWrite'),
+ ('freaders', 'FRead'),
+ ('readers', 'FirstRead')];
+ self.sRecordSize = str(int(dCfg.get('RecordSize', 4096) / 1024));
+ self.sTestsetSize = str(int(dCfg.get('TestsetSize', 2147483648) / 1024));
+ self.sQueueDepth = str(int(dCfg.get('QueueDepth', 32)));
+ self.sFilePath = dCfg.get('FilePath', '/mnt/iozone');
+ self.fDirectIo = True;
+
+ sTargetOs = dCfg.get('TargetOs');
+ if sTargetOs == 'solaris':
+ self.fDirectIo = False;
+
+ def prepare(self, cMsTimeout = 30000):
+ """ Prepares the testcase """
+ reporter.testStart('IoZone');
+ _ = cMsTimeout;
+ return True; # Nothing to do.
+
+ def run(self, cMsTimeout = 30000):
+ """ Runs the testcase """
+ tupArgs = ('-r', self.sRecordSize, '-s', self.sTestsetSize, \
+ '-t', '1', '-T', '-F', self.sFilePath + '/iozone.tmp');
+ if self.fDirectIo:
+ tupArgs += ('-I',);
+ fRc, sOutput, sError = self.oExecutor.execBinary('iozone', tupArgs, cMsTimeout = cMsTimeout);
+ if fRc:
+ self.sResult = sOutput;
+ else:
+ self.sError = ('Binary: iozone\n' +
+ '\nOutput:\n\n' +
+ sOutput +
+ '\nError:\n\n' +
+ sError);
+ return fRc;
+
+ def cleanup(self):
+ """ Cleans up any leftovers from the testcase. """
+ reporter.testDone();
+ return True;
+
+ def reportResult(self):
+ """
+ Reports the test results to the test manager.
+ """
+
+ fRc = True;
+ if self.sResult is not None:
+ try:
+ asLines = self.sResult.splitlines();
+ for sLine in asLines:
+ sLine = sLine.strip();
+ if sLine.startswith('Children') is True:
+ # Extract the value
+ idxValue = sLine.rfind('=');
+ if idxValue == -1:
+ raise Exception('IozoneTest: Invalid state');
+
+ idxValue += 1;
+ while sLine[idxValue] == ' ':
+ idxValue += 1;
+
+ # Get the reported value, cut off after the decimal point
+ # it is not supported by the testmanager yet and is not really
+ # relevant anyway.
+ idxValueEnd = idxValue;
+ while sLine[idxValueEnd].isdigit():
+ idxValueEnd += 1;
+
+ for sNeedle, sTestVal in self.lstTests:
+ if sLine.rfind(sNeedle) != -1:
+ reporter.testValue(sTestVal, sLine[idxValue:idxValueEnd],
+ constants.valueunit.g_asNames[constants.valueunit.KILOBYTES_PER_SEC]);
+ break;
+ except:
+ fRc = False;
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def getErrorReport(self):
+ """
+ Returns the error report in case the testcase failed.
+ """
+ return self.sError;
+
+class IoPerfTest(object):
+ """
+ IoPerf testcase.
+ """
+ def __init__(self, oExecutor, dCfg = None):
+ self.oExecutor = oExecutor;
+ self.sResult = None;
+ self.sError = None;
+ self.sRecordSize = str(dCfg.get('RecordSize', 4094));
+ self.sTestsetSize = str(dCfg.get('TestsetSize', 2147483648));
+ self.sQueueDepth = str(dCfg.get('QueueDepth', 32));
+ self.sFilePath = dCfg.get('FilePath', '/mnt');
+ self.fDirectIo = True;
+ self.asGstIoPerfPaths = [
+ '${CDROM}/vboxvalidationkit/${OS/ARCH}/IoPerf${EXESUFF}',
+ '${CDROM}/${OS/ARCH}/IoPerf${EXESUFF}',
+ ];
+
+ sTargetOs = dCfg.get('TargetOs');
+ if sTargetOs == 'solaris':
+ self.fDirectIo = False;
+
+ def _locateGstIoPerf(self):
+ """
+ Returns guest side path to FsPerf.
+ """
+ for sIoPerfPath in self.asGstIoPerfPaths:
+ if self.oExecutor.isFile(sIoPerfPath):
+ return sIoPerfPath;
+ reporter.log('Unable to find guest FsPerf in any of these places: %s' % ('\n '.join(self.asGstIoPerfPaths),));
+ return self.asGstIoPerfPaths[0];
+
+ def prepare(self, cMsTimeout = 30000):
+ """ Prepares the testcase """
+ _ = cMsTimeout;
+ return True; # Nothing to do.
+
+ def run(self, cMsTimeout = 30000):
+ """ Runs the testcase """
+ tupArgs = ('--block-size', self.sRecordSize, '--test-set-size', self.sTestsetSize, \
+ '--maximum-requests', self.sQueueDepth, '--dir', self.sFilePath + '/ioperfdir-1');
+ if self.fDirectIo:
+ tupArgs += ('--use-cache', 'off');
+ fRc, sOutput, sError = self.oExecutor.execBinary(self._locateGstIoPerf(), tupArgs, cMsTimeout = cMsTimeout);
+ if fRc:
+ self.sResult = sOutput;
+ else:
+ if sError is None:
+ sError = '';
+ if sOutput is None:
+ sOutput = '';
+ self.sError = ('Binary: IoPerf\n' +
+ '\nOutput:\n\n' +
+ sOutput +
+ '\nError:\n\n' +
+ sError);
+ return fRc;
+
+ def cleanup(self):
+ """ Cleans up any leftovers from the testcase. """
+ return True;
+
+ def reportResult(self):
+ """
+ Reports the test results to the test manager.
+ """
+ # Should be done using the test pipe already.
+ return True;
+
+ def getErrorReport(self):
+ """
+ Returns the error report in case the testcase failed.
+ """
+ return self.sError;
+
+class StorTestCfgMgr(object):
+ """
+ Manages the different testcases.
+ """
+
+ def __init__(self, aasTestLvls, aasTestsBlacklist, fnIsCfgSupported = None):
+ self.aasTestsBlacklist = aasTestsBlacklist;
+ self.at4TestLvls = [];
+ self.iTestLvl = 0;
+ self.fnIsCfgSupported = fnIsCfgSupported;
+ for asTestLvl in aasTestLvls:
+ if isinstance(asTestLvl, tuple):
+ asTestLvl, fSubTestStartAuto, fnTestFmt = asTestLvl;
+ self.at4TestLvls.append((0, fSubTestStartAuto, fnTestFmt, asTestLvl));
+ else:
+ self.at4TestLvls.append((0, True, None, asTestLvl));
+
+ self.at4TestLvls.reverse();
+
+ # Get the first non blacklisted test.
+ asTestCfg = self.getCurrentTestCfg();
+ while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
+ asTestCfg = self.advanceTestCfg();
+
+ iLvl = 0;
+ for sCfg in asTestCfg:
+ sSubTest = self.getTestIdString(sCfg, iLvl);
+ if sSubTest is not None:
+ reporter.testStart('%s' % (sSubTest,));
+ iLvl += 1;
+
+ def __del__(self):
+ # Make sure the tests are marked as done.
+ while self.iTestLvl < len(self.at4TestLvls):
+ reporter.testDone();
+ self.iTestLvl += 1;
+
+ def getTestIdString(self, oCfg, iLvl):
+ """
+ Returns a potentially formatted string for the test name.
+ """
+
+ # The order of the test levels is reversed so get the level starting
+ # from the end.
+ _, fSubTestStartAuto, fnTestFmt, _ = self.at4TestLvls[len(self.at4TestLvls) - 1 - iLvl];
+ if not fSubTestStartAuto:
+ return None;
+ if fnTestFmt is not None:
+ return fnTestFmt(oCfg);
+ return oCfg;
+
+ def isTestCfgBlacklisted(self, asTestCfg):
+ """
+ Returns whether the given test config is black listed.
+ """
+ fBlacklisted = False;
+
+ for asTestBlacklist in self.aasTestsBlacklist:
+ iLvl = 0;
+ fBlacklisted = True;
+ while iLvl < len(asTestBlacklist) and iLvl < len(asTestCfg):
+ if asTestBlacklist[iLvl] != asTestCfg[iLvl] and asTestBlacklist[iLvl] != '*':
+ fBlacklisted = False;
+ break;
+
+ iLvl += 1;
+
+ if not fBlacklisted and self.fnIsCfgSupported is not None:
+ fBlacklisted = not self.fnIsCfgSupported(asTestCfg);
+
+ return fBlacklisted;
+
+ def advanceTestCfg(self):
+ """
+ Advances to the next test config and returns it as an
+ array of strings or an empty config if there is no test left anymore.
+ """
+ iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg = self.at4TestLvls[self.iTestLvl];
+ iTestCfg += 1;
+ self.at4TestLvls[self.iTestLvl] = (iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg);
+ while iTestCfg == len(asTestCfg) and self.iTestLvl < len(self.at4TestLvls):
+ self.at4TestLvls[self.iTestLvl] = (0, fSubTestStartAuto, fnTestFmt, asTestCfg);
+ self.iTestLvl += 1;
+ if self.iTestLvl < len(self.at4TestLvls):
+ iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg = self.at4TestLvls[self.iTestLvl];
+ iTestCfg += 1;
+ self.at4TestLvls[self.iTestLvl] = (iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg);
+ if iTestCfg < len(asTestCfg):
+ self.iTestLvl = 0;
+ break;
+ else:
+ break; # We reached the end of our tests.
+
+ return self.getCurrentTestCfg();
+
+ def getCurrentTestCfg(self):
+ """
+ Returns the current not black listed test config as an array of strings.
+ """
+ asTestCfg = [];
+
+ if self.iTestLvl < len(self.at4TestLvls):
+ for t4TestLvl in self.at4TestLvls:
+ iTestCfg, _, _, asTestLvl = t4TestLvl;
+ asTestCfg.append(asTestLvl[iTestCfg]);
+
+ asTestCfg.reverse()
+
+ return asTestCfg;
+
+ def getNextTestCfg(self):
+ """
+ Returns the next not blacklisted test config or an empty list if
+ there is no test left.
+ """
+ asTestCfgCur = self.getCurrentTestCfg();
+
+ asTestCfg = self.advanceTestCfg();
+ while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
+ asTestCfg = self.advanceTestCfg();
+
+ # Compare the current and next config and close the approriate test
+ # categories.
+ #reporter.testDone(fSkippedLast);
+ if asTestCfg:
+ idxSame = 0;
+ while asTestCfgCur[idxSame] == asTestCfg[idxSame]:
+ idxSame += 1;
+
+ for i in range(idxSame, len(asTestCfg) - 1):
+ reporter.testDone();
+
+ for i in range(idxSame, len(asTestCfg)):
+ sSubTest = self.getTestIdString(asTestCfg[i], i);
+ if sSubTest is not None:
+ reporter.testStart('%s' % (sSubTest,));
+
+ else:
+ # No more tests, mark all tests as done
+ for i in range(0, len(asTestCfgCur) - 1):
+ reporter.testDone();
+
+ return asTestCfg;
+
+class tdStorageBenchmark(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Storage benchmark.
+ """
+
+ # Global storage configs for the testbox
+ kdStorageCfgs = {
+ # Testbox configs (Flag whether to test raw mode on the testbox, disk configuration)
+ 'testboxstor1.de.oracle.com': (True, storagecfg.DiskCfg('solaris', storagecfg.g_ksDiskCfgRegExp, r'c[3-9]t\dd0\Z')),
+ # Windows testbox doesn't return testboxstor2.de.oracle.com from socket.getfqdn()
+ 'testboxstor2': (False, storagecfg.DiskCfg('win', storagecfg.g_ksDiskCfgStatic, 'D:\\StorageTest')),
+
+ # Local test configs for the testcase developer
+ 'adaris': (True, storagecfg.DiskCfg('linux', storagecfg.g_ksDiskCfgStatic, \
+ '/home/alexander/StorageScratch')),
+ 'daedalus': (True, storagecfg.DiskCfg('darwin', storagecfg.g_ksDiskCfgStatic, \
+ '/Volumes/VirtualBox/Testsuite/StorageScratch')),
+ 'windows10': (True, storagecfg.DiskCfg('win', storagecfg.g_ksDiskCfgStatic, \
+ 'L:\\Testsuite\\StorageTest')),
+ };
+
+ # Available test sets.
+ kdTestSets = {
+ # Mostly for developing and debugging the testcase.
+ 'Fast': {
+ 'RecordSize': 65536,
+ 'TestsetSize': 104857600, # 100 MiB
+ 'QueueDepth': 32,
+ 'DiskSizeGb': 2
+ },
+ # For quick functionality tests where benchmark results are not required.
+ 'Functionality': {
+ 'RecordSize': 65536,
+ 'TestsetSize': 2147483648, # 2 GiB
+ 'QueueDepth': 32,
+ 'DiskSizeGb': 10
+ },
+ # For benchmarking the I/O stack.
+ 'Benchmark': {
+ 'RecordSize': 65536,
+ 'TestsetSize': 21474836480, # 20 Gib
+ 'QueueDepth': 32,
+ 'DiskSizeGb': 30
+ },
+ # For stress testing which takes a lot of time.
+ 'Stress': {
+ 'RecordSize': 65536,
+ 'TestsetSize': 2199023255552, # 2 TiB
+ 'QueueDepth': 32,
+ 'DiskSizeGb': 10000
+ },
+ };
+
+ # Dictionary mapping the virtualization mode mnemonics to a little less cryptic
+ # strings used in test descriptions.
+ kdVirtModeDescs = {
+ 'raw' : 'Raw-mode',
+ 'hwvirt' : 'HwVirt',
+ 'hwvirt-np' : 'NestedPaging'
+ };
+
+ kdHostIoCacheDescs = {
+ 'default' : 'HostCacheDef',
+ 'hostiocache' : 'HostCacheOn',
+ 'no-hostiocache' : 'HostCacheOff'
+ };
+
+ # Password ID for encryption.
+ ksPwId = 'EncPwId';
+
+ # Array indexes for the test configs.
+ kiVmName = 0;
+ kiStorageCtrl = 1;
+ kiHostIoCache = 2;
+ kiDiskFmt = 3;
+ kiDiskVar = 4;
+ kiCpuCount = 5;
+ kiVirtMode = 6;
+ kiTestSet = 7;
+ kiIoTest = 8;
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = ['tst-storage', 'tst-storage32'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef;
+ self.acCpusDef = [1, 2];
+ self.acCpus = self.acCpusDef;
+ self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic', 'NVMe', 'VirtIoScsi'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ self.asHostIoCacheDef = ['default', 'hostiocache', 'no-hostiocache'];
+ self.asHostIoCache = self.asHostIoCacheDef;
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.asDiskVariantsDef = ['Dynamic', 'Fixed', 'DynamicSplit2G', 'FixedSplit2G', 'Network'];
+ self.asDiskVariants = self.asDiskVariantsDef;
+ self.asTestsDef = ['ioperf'];
+ self.asTests = self.asTestsDef;
+ self.asTestSetsDef = ['Fast', 'Functionality', 'Benchmark', 'Stress'];
+ self.asTestSets = self.asTestSetsDef;
+ self.asIscsiTargetsDef = [ ]; # @todo: Configure one target for basic iSCSI testing
+ self.asIscsiTargets = self.asIscsiTargetsDef;
+ self.cDiffLvlsDef = 0;
+ self.cDiffLvls = self.cDiffLvlsDef;
+ self.fTestHost = False;
+ self.fUseScratch = False;
+ self.fRecreateStorCfg = True;
+ self.fReportBenchmarkResults = True;
+ self.fTestRawMode = False;
+ self.oStorCfg = None;
+ self.sIoLogPathDef = self.sScratchPath;
+ self.sIoLogPath = self.sIoLogPathDef;
+ self.fIoLog = False;
+ self.fUseRamDiskDef = False;
+ self.fUseRamDisk = self.fUseRamDiskDef;
+ self.fEncryptDiskDef = False;
+ self.fEncryptDisk = self.fEncryptDiskDef;
+ self.sEncryptPwDef = 'TestTestTest';
+ self.sEncryptPw = self.sEncryptPwDef;
+ self.sEncryptAlgoDef = 'AES-XTS256-PLAIN64';
+ self.sEncryptAlgo = self.sEncryptAlgoDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageBenchmark1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrlsDef)));
+ reporter.log(' --host-io-cache <setting1[:setting2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asHostIoCacheDef)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormatsDef)));
+ reporter.log(' --disk-variants <variant1[:variant2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskVariantsDef)));
+ reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asIscsiTargetsDef)));
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTestsDef)));
+ reporter.log(' --test-sets <set1[:set2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTestSetsDef)));
+ reporter.log(' --diff-levels <number of diffs>');
+ reporter.log(' Default: %s' % (self.cDiffLvlsDef));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --test-host');
+ reporter.log(' Do all configured tests on the host first and report the results');
+ reporter.log(' to get a baseline');
+ reporter.log(' --use-scratch');
+ reporter.log(' Use the scratch directory for testing instead of setting up');
+ reporter.log(' fresh volumes on dedicated disks (for development)');
+ reporter.log(' --always-wipe-storage-cfg');
+ reporter.log(' Recreate the host storage config before each test');
+ reporter.log(' --dont-wipe-storage-cfg');
+ reporter.log(' Don\'t recreate the host storage config before each test');
+ reporter.log(' --report-benchmark-results');
+ reporter.log(' Report all benchmark results');
+ reporter.log(' --dont-report-benchmark-results');
+ reporter.log(' Don\'t report any benchmark results');
+ reporter.log(' --io-log-path <path>');
+ reporter.log(' Default: %s' % (self.sIoLogPathDef));
+ reporter.log(' --enable-io-log');
+ reporter.log(' Whether to enable I/O logging for each test');
+ reporter.log(' --use-ramdisk');
+ reporter.log(' Default: %s' % (self.fUseRamDiskDef));
+ reporter.log(' --encrypt-disk');
+ reporter.log(' Default: %s' % (self.fEncryptDiskDef));
+ reporter.log(' --encrypt-password');
+ reporter.log(' Default: %s' % (self.sEncryptPwDef));
+ reporter.log(' --encrypt-algorithm');
+ reporter.log(' Default: %s' % (self.sEncryptAlgoDef));
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--host-io-cache':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--host-io-cache" takes a colon separated list of I/O cache settings');
+ self.asHostIoCache = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-variants':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--disk-variants" takes a colon separated list of disk variants');
+ self.asDiskVariants = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--iscsi-targets':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
+ self.asIscsiTargets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests to run');
+ self.asTests = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--test-sets':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-sets" takes a colon separated list of test sets');
+ self.asTestSets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--diff-levels':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--diff-levels" takes an integer');
+ try: self.cDiffLvls = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--diff-levels" value "%s" is not an integer' % (asArgs[iArg],));
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--test-host':
+ self.fTestHost = True;
+ elif asArgs[iArg] == '--use-scratch':
+ self.fUseScratch = True;
+ elif asArgs[iArg] == '--always-wipe-storage-cfg':
+ self.fRecreateStorCfg = True;
+ elif asArgs[iArg] == '--dont-wipe-storage-cfg':
+ self.fRecreateStorCfg = False;
+ elif asArgs[iArg] == '--report-benchmark-results':
+ self.fReportBenchmarkResults = True;
+ elif asArgs[iArg] == '--dont-report-benchmark-results':
+ self.fReportBenchmarkResults = False;
+ elif asArgs[iArg] == '--io-log-path':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--io-log-path" takes a path argument');
+ self.sIoLogPath = asArgs[iArg];
+ elif asArgs[iArg] == '--enable-io-log':
+ self.fIoLog = True;
+ elif asArgs[iArg] == '--use-ramdisk':
+ self.fUseRamDisk = True;
+ elif asArgs[iArg] == '--encrypt-disk':
+ self.fEncryptDisk = True;
+ elif asArgs[iArg] == '--encrypt-password':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-password" takes a string');
+ self.sEncryptPw = asArgs[iArg];
+ elif asArgs[iArg] == '--encrypt-algorithm':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-algorithm" takes a string');
+ self.sEncryptAlgo = asArgs[iArg];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-storage' in self.asTestVMs:
+ self.asRsrcs.append('5.0/storage/tst-storage.vdi');
+ if 'tst-storage32' in self.asTestVMs:
+ self.asRsrcs.append('5.0/storage/tst-storage32.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-storage' in self.asTestVMs:
+ oVM = self.createTestVM('tst-storage', 1, '5.0/storage/tst-storage.vdi', sKind = 'ArchLinux_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
+ sDvdImage = self.sVBoxValidationKitIso);
+ if oVM is None:
+ return False;
+
+ if 'tst-storage32' in self.asTestVMs:
+ oVM = self.createTestVM('tst-storage32', 1, '5.0/storage/tst-storage32.vdi', sKind = 'ArchLinux', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
+ sDvdImage = self.sVBoxValidationKitIso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def prepareStorage(self, oStorCfg, fRamDisk = False, cbPool = None):
+ """
+ Prepares the host storage for disk images or direct testing on the host.
+ """
+ # Create a basic pool with the default configuration.
+ sMountPoint = None;
+ fRc, sPoolId = oStorCfg.createStoragePool(cbPool = cbPool, fRamDisk = fRamDisk);
+ if fRc:
+ fRc, sMountPoint = oStorCfg.createVolume(sPoolId);
+ if not fRc:
+ sMountPoint = None;
+ oStorCfg.cleanup();
+
+ return sMountPoint;
+
+ def cleanupStorage(self, oStorCfg):
+ """
+ Cleans up any created storage space for a test.
+ """
+ return oStorCfg.cleanup();
+
+ def getGuestDisk(self, oSession, oTxsSession, eStorageController):
+ """
+ Gets the path of the disk in the guest to use for testing.
+ """
+ lstDisks = None;
+
+ # The naming scheme for NVMe is different and we don't have
+ # to query the guest for unformatted disks here because the disk with the OS
+ # is not attached to a NVMe controller.
+ if eStorageController == vboxcon.StorageControllerType_NVMe:
+ lstDisks = [ '/dev/nvme0n1' ];
+ else:
+ # Find a unformatted disk (no partition).
+ # @todo: This is a hack because LIST and STAT are not yet implemented
+ # in TXS (get to this eventually)
+ lstBlkDev = [ '/dev/sda', '/dev/sdb' ];
+ for sBlkDev in lstBlkDev:
+ fRc = oTxsSession.syncExec('/usr/bin/ls', ('ls', sBlkDev + '1'));
+ if not fRc:
+ lstDisks = [ sBlkDev ];
+ break;
+
+ _ = oSession;
+ return lstDisks;
+
+ def mountValidationKitIso(self, oVmExec):
+ """
+ Hack to get the vlaidation kit ISO mounted in the guest as it was left out
+ originally and I don't feel like respinning the disk image.
+ """
+ fRc = oVmExec.mkDir('/media');
+ if fRc:
+ fRc = oVmExec.mkDir('/media/cdrom');
+ if fRc:
+ fRc = oVmExec.execBinaryNoStdOut('mount', ('/dev/sr0', '/media/cdrom'));
+
+ return fRc;
+
+ def getDiskFormatVariantsForTesting(self, sDiskFmt, asVariants):
+ """
+ Returns a list of disk variants for testing supported by the given
+ disk format and selected for testing.
+ """
+ lstDskFmts = self.oVBoxMgr.getArray(self.oVBox.systemProperties, 'mediumFormats');
+ for oDskFmt in lstDskFmts:
+ if oDskFmt.id == sDiskFmt:
+ lstDskVariants = [];
+ lstCaps = self.oVBoxMgr.getArray(oDskFmt, 'capabilities');
+
+ if vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
+ and 'Dynamic' in asVariants:
+ lstDskVariants.append('Dynamic');
+
+ if vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
+ and 'Fixed' in asVariants:
+ lstDskVariants.append('Fixed');
+
+ if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
+ and vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
+ and 'DynamicSplit2G' in asVariants:
+ lstDskVariants.append('DynamicSplit2G');
+
+ if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
+ and vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
+ and 'FixedSplit2G' in asVariants:
+ lstDskVariants.append('FixedSplit2G');
+
+ if vboxcon.MediumFormatCapabilities_TcpNetworking in lstCaps \
+ and 'Network' in asVariants:
+ lstDskVariants.append('Network'); # Solely for iSCSI to get a non empty list
+
+ return lstDskVariants;
+
+ return [];
+
+ def convDiskToMediumVariant(self, sDiskVariant):
+ """
+ Returns a tuple of medium variant flags matching the given disk variant.
+ """
+ tMediumVariant = None;
+ if sDiskVariant == 'Dynamic':
+ tMediumVariant = (vboxcon.MediumVariant_Standard, );
+ elif sDiskVariant == 'Fixed':
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, );
+ elif sDiskVariant == 'DynamicSplit2G':
+ tMediumVariant = (vboxcon.MediumVariant_Standard, vboxcon.MediumVariant_VmdkSplit2G);
+ elif sDiskVariant == 'FixedSplit2G':
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, vboxcon.MediumVariant_VmdkSplit2G);
+
+ return tMediumVariant;
+
+ def getStorageCtrlFromName(self, sStorageCtrl):
+ """
+ Resolves the storage controller string to the matching constant.
+ """
+ eStorageCtrl = None;
+
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ elif sStorageCtrl == 'NVMe':
+ eStorageCtrl = vboxcon.StorageControllerType_NVMe;
+ elif sStorageCtrl == 'VirtIoScsi':
+ eStorageCtrl = vboxcon.StorageControllerType_VirtioSCSI;
+
+ return eStorageCtrl;
+
+ def getStorageDriverFromEnum(self, eStorageCtrl, fHardDisk):
+ """
+ Returns the appropriate driver name for the given storage controller
+ and a flag whether the driver has the generic SCSI driver attached.
+ """
+ if eStorageCtrl == vboxcon.StorageControllerType_IntelAhci:
+ if fHardDisk:
+ return ('ahci', False);
+ return ('ahci', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_PIIX4:
+ return ('piix3ide', False);
+ if eStorageCtrl == vboxcon.StorageControllerType_LsiLogicSas:
+ return ('lsilogicsas', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_LsiLogic:
+ return ('lsilogicscsi', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_BusLogic:
+ return ('buslogic', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_NVMe:
+ return ('nvme', False);
+ if eStorageCtrl == vboxcon.StorageControllerType_VirtioSCSI:
+ return ('virtio-scsi', True);
+
+ return ('<invalid>', False);
+
+ def isTestCfgSupported(self, asTestCfg):
+ """
+ Returns whether a specific test config is supported.
+ """
+
+ # Check whether the disk variant is supported by the selected format.
+ asVariants = self.getDiskFormatVariantsForTesting(asTestCfg[self.kiDiskFmt], [ asTestCfg[self.kiDiskVar] ]);
+ if not asVariants:
+ return False;
+
+ # For iSCSI check whether we have targets configured.
+ if asTestCfg[self.kiDiskFmt] == 'iSCSI' and not self.asIscsiTargets:
+ return False;
+
+ # Check for virt mode, CPU count and selected VM.
+ if asTestCfg[self.kiVirtMode] == 'raw' \
+ and ( asTestCfg[self.kiCpuCount] > 1 \
+ or asTestCfg[self.kiVmName] == 'tst-storage' \
+ or not self.fTestRawMode):
+ return False;
+
+ # IDE does not support the no host I/O cache setting
+ if asTestCfg[self.kiHostIoCache] == 'no-hostiocache' \
+ and asTestCfg[self.kiStorageCtrl] == 'IDE':
+ return False;
+
+ return True;
+
+ def fnFormatCpuString(self, cCpus):
+ """
+ Formats the CPU count to be readable.
+ """
+ if cCpus == 1:
+ return '1 cpu';
+ return '%u cpus' % (cCpus);
+
+ def fnFormatVirtMode(self, sVirtMode):
+ """
+ Formats the virtualization mode to be a little less cryptic for use in test
+ descriptions.
+ """
+ return self.kdVirtModeDescs[sVirtMode];
+
+ def fnFormatHostIoCache(self, sHostIoCache):
+ """
+ Formats the host I/O cache mode to be a little less cryptic for use in test
+ descriptions.
+ """
+ return self.kdHostIoCacheDescs[sHostIoCache];
+
+ def testBenchmark(self, sTargetOs, sBenchmark, sMountpoint, oExecutor, dTestSet, \
+ cMsTimeout = 3600000):
+ """
+ Runs the given benchmark on the test host.
+ """
+
+ dTestSet['FilePath'] = sMountpoint;
+ dTestSet['TargetOs'] = sTargetOs;
+
+ oTst = None;
+ if sBenchmark == 'iozone':
+ oTst = IozoneTest(oExecutor, dTestSet);
+ elif sBenchmark == 'fio':
+ oTst = FioTest(oExecutor, dTestSet); # pylint: disable=redefined-variable-type
+ elif sBenchmark == 'ioperf':
+ oTst = IoPerfTest(oExecutor, dTestSet); # pylint: disable=redefined-variable-type
+
+ if oTst is not None:
+ fRc = oTst.prepare();
+ if fRc:
+ fRc = oTst.run(cMsTimeout);
+ if fRc:
+ if self.fReportBenchmarkResults:
+ fRc = oTst.reportResult();
+ else:
+ reporter.testFailure('Running the testcase failed');
+ reporter.addLogString(oTst.getErrorReport(), sBenchmark + '.log',
+ 'log/release/client', 'Benchmark raw output');
+ else:
+ reporter.testFailure('Preparing the testcase failed');
+
+ oTst.cleanup();
+
+ return fRc;
+
+ def createHd(self, oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, \
+ sDiskPath, cbDisk):
+ """
+ Creates a new disk with the given parameters returning the medium object
+ on success.
+ """
+
+ oHd = None;
+ if sDiskFormat == "iSCSI" and iDiffLvl == 0:
+ listNames = [];
+ listValues = [];
+ listValues = self.asIscsiTargets[0].split('|');
+ listNames.append('TargetAddress');
+ listNames.append('TargetName');
+ listNames.append('LUN');
+
+ if self.fpApiVer >= 5.0:
+ oHd = oSession.oVBox.createMedium(sDiskFormat, sDiskPath, vboxcon.AccessMode_ReadWrite, \
+ vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = oSession.oVBox.createHardDisk(sDiskFormat, sDiskPath);
+ oHd.type = vboxcon.MediumType_Normal;
+ oHd.setProperties(listNames, listValues);
+ else:
+ if iDiffLvl == 0:
+ tMediumVariant = self.convDiskToMediumVariant(sDiskVariant);
+ oHd = oSession.createBaseHd(sDiskPath + '/base.img', sDiskFormat, cbDisk, \
+ cMsTimeout = 3600 * 1000, tMediumVariant = tMediumVariant);
+ else:
+ sDiskPath = sDiskPath + '/diff_%u.img' % (iDiffLvl);
+ oHd = oSession.createDiffHd(oHdParent, sDiskPath, None);
+
+ if oHd is not None and iDiffLvl == 0 and self.fEncryptDisk:
+ try:
+ oIProgress = oHd.changeEncryption('', self.sEncryptAlgo, self.sEncryptPw, self.ksPwId);
+ oProgress = vboxwrappers.ProgressWrapper(oIProgress, self.oVBoxMgr, self, 'Encrypting "%s"' % (sDiskPath,));
+ oProgress.wait(60*60000); # Wait for up to one hour, fixed disks take longer to encrypt.
+ if oProgress.logResult() is False:
+ raise base.GenError('Encrypting disk "%s" failed' % (sDiskPath, ));
+ except:
+ reporter.errorXcpt('changeEncryption("%s","%s","%s") failed on "%s"' \
+ % ('', self.sEncryptAlgo, self.sEncryptPw, oSession.sName) );
+ self.oVBox.deleteHdByMedium(oHd);
+ oHd = None;
+ else:
+ reporter.log('Encrypted "%s"' % (sDiskPath,));
+
+ return oHd;
+
+ def startVmAndConnect(self, sVmName):
+ """
+ Our own implementation of startVmAndConnectToTxsViaTcp to make it possible
+ to add passwords to a running VM when encryption is used.
+ """
+ oSession = self.startVmByName(sVmName);
+ if oSession is not None:
+ # Add password to the session in case encryption is used.
+ fRc = True;
+ if self.fEncryptDisk:
+ try:
+ if self.fpApiVer >= 7.0:
+ oSession.o.console.addEncryptionPassword(self.ksPwId, self.sEncryptPw, False);
+ else:
+ oSession.o.console.addDiskEncryptionPassword(self.ksPwId, self.sEncryptPw, False);
+ except:
+ reporter.logXcpt();
+ fRc = False;
+
+ # Connect to TXS.
+ if fRc:
+ reporter.log2('startVmAndConnect: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 15*60000, fNatForwardingForTxs = True);
+ if fRc is True:
+ if fRc is True:
+ # Success!
+ return (oSession, oTxsSession);
+ else:
+ reporter.error('startVmAndConnect: txsDoConnectViaTcp failed');
+ # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
+
+ self.terminateVmBySession(oSession);
+
+ return (None, None);
+
+ def testOneCfg(self, sVmName, eStorageController, sHostIoCache, sDiskFormat, # pylint: disable=too-many-arguments,too-many-locals,too-many-statements
+ sDiskVariant, sDiskPath, cCpus, sIoTest, sVirtMode, sTestSet):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ dTestSet = self.kdTestSets.get(sTestSet);
+ cbDisk = dTestSet.get('DiskSizeGb') * 1024*1024*1024;
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+
+ fRc = True;
+ if sDiskFormat == 'iSCSI':
+ sDiskPath = self.asIscsiTargets[0];
+ elif self.fUseScratch:
+ sDiskPath = self.sScratchPath;
+ else:
+ # If requested recreate the storage space to start with a clean config
+ # for benchmarks
+ if self.fRecreateStorCfg:
+ sMountPoint = self.prepareStorage(self.oStorCfg, self.fUseRamDisk, 2 * cbDisk);
+ if sMountPoint is not None:
+ # Create a directory where every normal user can write to.
+ self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', 0o777);
+ sDiskPath = sMountPoint + '/test';
+ else:
+ fRc = False;
+ reporter.testFailure('Failed to prepare storage for VM');
+
+ if not fRc:
+ return fRc;
+
+ lstDisks = []; # List of disks we have to delete afterwards.
+
+ for iDiffLvl in range(self.cDiffLvls + 1):
+ sIoLogFile = None;
+
+ if iDiffLvl == 0:
+ reporter.testStart('Base');
+ else:
+ reporter.testStart('Diff %u' % (iDiffLvl));
+
+ # Reconfigure the VM
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ #
+ # Disable audio controller which shares the interrupt line with the BusLogic controller and is suspected to cause
+ # rare test failures because the device initialization fails.
+ #
+ fRc = oSession.setupAudio(vboxcon.AudioControllerType_AC97, False);
+ # Attach HD
+ fRc = fRc and oSession.ensureControllerAttached(self.controllerTypeToName(eStorageController));
+ fRc = fRc and oSession.setStorageControllerType(eStorageController,
+ self.controllerTypeToName(eStorageController));
+
+ if sHostIoCache == 'hostiocache':
+ fRc = fRc and oSession.setStorageControllerHostIoCache(self.controllerTypeToName(eStorageController), True);
+ elif sHostIoCache == 'no-hostiocache':
+ fRc = fRc and oSession.setStorageControllerHostIoCache(self.controllerTypeToName(eStorageController), False);
+
+ iDevice = 0;
+ if eStorageController in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
+ iDevice = 1; # Master is for the OS.
+
+ oHdParent = None;
+ if iDiffLvl > 0:
+ oHdParent = lstDisks[0];
+ oHd = self.createHd(oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, sDiskPath, cbDisk);
+ if oHd is not None:
+ lstDisks.insert(0, oHd);
+ try:
+ if oSession.fpApiVer >= 4.0:
+ oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
+ 0, iDevice, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
+ 0, iDevice, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (self.controllerTypeToName(eStorageController), 1, 0, oHd.id, oSession.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sDiskPath, oSession.sName));
+ else:
+ fRc = False;
+
+ # Set up the I/O logging config if enabled
+ if fRc and self.fIoLog:
+ try:
+ oSession.o.machine.setExtraData('VBoxInternal2/EnableDiskIntegrityDriver', '1');
+
+ iLun = 0;
+ if eStorageController in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
+ iLun = 1
+ sDrv, fDrvScsi = self.getStorageDriverFromEnum(eStorageController, True);
+ if fDrvScsi:
+ sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/AttachedDriver/Config' % (sDrv, iLun);
+ else:
+ sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/Config' % (sDrv, iLun);
+
+ sIoLogFile = '%s/%s.iolog' % (self.sIoLogPath, sDrv);
+ print(sCfgmPath);
+ print(sIoLogFile);
+ oSession.o.machine.setExtraData('%s/IoLog' % (sCfgmPath,), sIoLogFile);
+ except:
+ reporter.logXcpt();
+
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnect(sVmName);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ # Prepare the storage on the guest
+ lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin' ];
+ oExecVm = remoteexecutor.RemoteExecutor(oTxsSession, lstBinaryPaths, '${SCRATCH}');
+ fRc = self.mountValidationKitIso(oExecVm);
+ if fRc:
+ oGstDiskCfg = storagecfg.DiskCfg('linux', storagecfg.g_ksDiskCfgList,
+ self.getGuestDisk(oSession, oTxsSession, eStorageController));
+ oStorCfgVm = storagecfg.StorageCfg(oExecVm, oGstDiskCfg);
+
+ iTry = 0;
+ while iTry < 3:
+ sMountPoint = self.prepareStorage(oStorCfgVm);
+ if sMountPoint is not None:
+ reporter.log('Prepared storage on %s try' % (iTry + 1,));
+ break;
+ iTry = iTry + 1;
+ self.sleep(5);
+
+ if sMountPoint is not None:
+ # 3 hours max (Benchmark and QED takes a lot of time)
+ self.testBenchmark('linux', sIoTest, sMountPoint, oExecVm, dTestSet, cMsTimeout = 3 * 3600 * 1000);
+ self.cleanupStorage(oStorCfgVm);
+ else:
+ reporter.testFailure('Failed to prepare storage for the guest benchmark');
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+
+ # Add the I/O log if it exists and the test failed
+ if reporter.testErrorCount() > 0 \
+ and sIoLogFile is not None \
+ and os.path.exists(sIoLogFile):
+ reporter.addLogFile(sIoLogFile, 'misc/other', 'I/O log');
+ os.remove(sIoLogFile);
+ else:
+ reporter.testFailure('Failed to mount validation kit ISO');
+
+ else:
+ fRc = False;
+
+ # Remove disk
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ try:
+ oSession.o.machine.detachDevice(self.controllerTypeToName(eStorageController), 0, iDevice);
+
+ # Remove storage controller if it is not an IDE controller.
+ if eStorageController not in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
+ oSession.o.machine.removeStorageController(self.controllerTypeToName(eStorageController));
+
+ oSession.saveSettings();
+ oSession.saveSettings();
+ oSession.close();
+ oSession = None;
+ except:
+ reporter.errorXcpt('failed to detach/delete disk %s from storage controller' % (sDiskPath));
+ else:
+ fRc = False;
+
+ reporter.testDone();
+
+ # Delete all disks
+ for oHd in lstDisks:
+ self.oVBox.deleteHdByMedium(oHd);
+
+ # Cleanup storage area
+ if sDiskFormat != 'iSCSI' and not self.fUseScratch and self.fRecreateStorCfg:
+ self.cleanupStorage(self.oStorCfg);
+
+ return fRc;
+
+ def testStorage(self, sDiskPath = None):
+ """
+ Runs the storage testcase through the selected configurations
+ """
+
+ aasTestCfgs = [];
+ aasTestCfgs.insert(self.kiVmName, self.asTestVMs);
+ aasTestCfgs.insert(self.kiStorageCtrl, self.asStorageCtrls);
+ aasTestCfgs.insert(self.kiHostIoCache, (self.asHostIoCache, True, self.fnFormatHostIoCache));
+ aasTestCfgs.insert(self.kiDiskFmt, self.asDiskFormats);
+ aasTestCfgs.insert(self.kiDiskVar, self.asDiskVariants);
+ aasTestCfgs.insert(self.kiCpuCount, (self.acCpus, True, self.fnFormatCpuString));
+ aasTestCfgs.insert(self.kiVirtMode, (self.asVirtModes, True, self.fnFormatVirtMode));
+ aasTestCfgs.insert(self.kiTestSet, self.asTestSets);
+ aasTestCfgs.insert(self.kiIoTest, (self.asTests, False, None));
+
+ aasTestsBlacklist = [];
+ aasTestsBlacklist.append(['tst-storage', 'BusLogic']); # 64bit Linux is broken with BusLogic
+
+ oTstCfgMgr = StorTestCfgMgr(aasTestCfgs, aasTestsBlacklist, self.isTestCfgSupported);
+
+ fRc = True;
+ asTestCfg = oTstCfgMgr.getCurrentTestCfg();
+ while asTestCfg:
+ fRc = self.testOneCfg(asTestCfg[self.kiVmName], self.getStorageCtrlFromName(asTestCfg[self.kiStorageCtrl]), \
+ asTestCfg[self.kiHostIoCache], asTestCfg[self.kiDiskFmt], asTestCfg[self.kiDiskVar],
+ sDiskPath, asTestCfg[self.kiCpuCount], asTestCfg[self.kiIoTest], \
+ asTestCfg[self.kiVirtMode], asTestCfg[self.kiTestSet]) and fRc and True; # pychecker hack.
+
+ asTestCfg = oTstCfgMgr.getNextTestCfg();
+
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ fRc = True;
+ tupTstCfg = self.kdStorageCfgs.get(socket.getfqdn().lower());
+ if tupTstCfg is None:
+ tupTstCfg = self.kdStorageCfgs.get(socket.gethostname().lower());
+
+ # Test the host first if requested
+ if tupTstCfg is not None or self.fUseScratch:
+ self.fTestRawMode = tupTstCfg[0];
+ oDiskCfg = tupTstCfg[1];
+ lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', \
+ '/opt/csw/bin', '/usr/ccs/bin', '/usr/sfw/bin'];
+ oExecutor = remoteexecutor.RemoteExecutor(None, lstBinaryPaths, self.sScratchPath);
+ if not self.fUseScratch:
+ self.oStorCfg = storagecfg.StorageCfg(oExecutor, oDiskCfg);
+
+ # Try to cleanup any leftovers from a previous run first.
+ fRc = self.oStorCfg.cleanupLeftovers();
+ if not fRc:
+ reporter.error('Failed to cleanup any leftovers from a previous run');
+
+ if self.fTestHost:
+ reporter.testStart('Host');
+ if self.fUseScratch:
+ sMountPoint = self.sScratchPath;
+ else:
+ sMountPoint = self.prepareStorage(self.oStorCfg);
+ if sMountPoint is not None:
+ for sIoTest in self.asTests:
+ for sTestSet in self.asTestSets:
+ reporter.testStart(sTestSet);
+ dTestSet = self.kdTestSets.get(sTestSet);
+ self.testBenchmark(utils.getHostOs(), sIoTest, sMountPoint, oExecutor, dTestSet);
+ reporter.testDone();
+ self.cleanupStorage(self.oStorCfg);
+ else:
+ reporter.testFailure('Failed to prepare host storage');
+ fRc = False;
+ reporter.testDone();
+ else:
+ # Create the storage space first if it is not done before every test.
+ sMountPoint = None;
+ if self.fUseScratch:
+ sMountPoint = self.sScratchPath;
+ elif not self.fRecreateStorCfg:
+ reporter.testStart('Create host storage');
+ sMountPoint = self.prepareStorage(self.oStorCfg);
+ if sMountPoint is None:
+ reporter.testFailure('Failed to prepare host storage');
+ fRc = False;
+ self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', 0o777);
+ sMountPoint = sMountPoint + '/test';
+ reporter.testDone();
+
+ if fRc:
+ # Run the storage tests.
+ if not self.testStorage(sMountPoint):
+ fRc = False;
+
+ if not self.fRecreateStorCfg and not self.fUseScratch:
+ self.cleanupStorage(self.oStorCfg);
+ else:
+ reporter.testFailure('Could not get disk configuration for host: %s' % (socket.getfqdn().lower()));
+ fRc = False;
+
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdStorageBenchmark().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageRawDrive1.py b/src/VBox/ValidationKit/tests/storage/tdStorageRawDrive1.py
new file mode 100755
index 00000000..482453de
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageRawDrive1.py
@@ -0,0 +1,1692 @@
+
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+VirtualBox Validation Kit - VMDK raw disk tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2013-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Id: tdStorageRawDrive1.py $"
+
+# Standard Python imports.
+import os;
+import re;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+from testdriver import vboxwrappers;
+
+
+class tdStorageRawDriveOs(vboxtestvms.BaseTestVm):
+ """
+ Base autostart helper class to provide common methods.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None, sBootSector = None):
+ vboxtestvms.BaseTestVm.__init__(self, sVmName, oSet = oSet, sKind = sKind);
+ self.oTstDrv = oTstDrv;
+ self.sHdd = sHdd;
+ self.eNic0Type = eNic0Type;
+ self.cMbRam = cMbRam;
+ self.cCpus = cCpus;
+ self.fPae = fPae;
+ self.sGuestAdditionsIso = sGuestAdditionsIso;
+ self.asTestBuildDirs = oTstDrv.asTestBuildDirs;
+ self.sVBoxInstaller = "";
+ self.sVMDKPath='/home/vbox/vmdk';
+ self.asVirtModesSup = ['hwvirt-np',];
+ self.asParavirtModesSup = ['default',];
+ self.sBootSector = sBootSector;
+ self.sPathDelimiter = '/';
+
+ # Had to move it here from oTestDrv because the output is platform-dependent
+ self.asHdds = \
+ { '6.1/storage/t-mbr.vdi' :
+ {
+ 'Header' :
+ {
+ #Drive: /dev/sdb
+ 'Model' : '"ATA VBOX HARDDISK"',
+ 'UUID' : '62d4f394-0000-0000-0000-000000000000',
+ 'Size' : '2.0GiB',
+ 'Sector Size' : '512 bytes',
+ 'Scheme' : 'MBR',
+ },
+ 'Partitions' :
+ {
+ 'Partitions' :
+ [
+ '$(1) 07 10.0MiB 1.0MiB 0/ 32/33 1/102/37 no IFS',
+ '$(2) 83 10.0MiB 11.0MiB 5/ 93/33 11/ 29/14 no Linux',
+ '$(3) 07 10.0MiB 21.0MiB 2/172/43 3/242/47 no IFS',
+ '$(4) 07 10.0MiB 32.0MiB 4/ 20/17 5/ 90/21 no IFS',
+ '$(5) 83 10.0MiB 43.0MiB 5/122/54 6/192/58 no Linux',
+ '$(6) 07 10.0MiB 54.0MiB 6/225/28 8/ 40/32 no IFS',
+ '$(7) 83 10.0MiB 65.0MiB 8/ 73/ 2 9/143/ 6 no Linux',
+ '$(8) 07 1.9GiB 76.0MiB 9/175/39 260/243/47 no IFS',
+ ],
+ 'PartitionNumbers' : [1, 2, 3, 5, 6, 7, 8, 9],
+ },
+ } ,
+ '6.1/storage/t-gpt.vdi' :
+ {
+ 'Header' :
+ {
+ #Drive: /dev/sdc
+ 'Model' : '"ATA VBOX HARDDISK"',
+ 'UUID' : '7b642ab1-9d44-b844-a860-ce71e0686274',
+ 'Size' : '2.0GiB',
+ 'Sector Size' : '512 bytes',
+ 'Scheme' : 'GPT',
+ },
+ 'Partitions' :
+ {
+ 'Partitions' :
+ [
+ '$(1) WindowsBasicData 560b261d-081f-fb4a-8df8-c64fffcb2bd1 10.0MiB 1.0MiB off',
+ '$(2) LinuxData 629f66be-0254-7c4f-a328-cc033e4de124 10.0MiB 11.0MiB off',
+ '$(3) WindowsBasicData d3f56c96-3b28-7f44-a53d-85b8bc93bd91 10.0MiB 21.0MiB off',
+ '$(4) LinuxData 27c0f5ad-74c8-d54f-835f-06e51b3f10ef 10.0MiB 31.0MiB off',
+ '$(5) WindowsBasicData 6cf1fdf0-b2ae-3849-9cfa-c056f9d8b722 10.0MiB 41.0MiB off',
+ '$(6) LinuxData 017bcbed-8b96-be4d-925a-2f872194fbe6 10.0MiB 51.0MiB off',
+ '$(7) WindowsBasicData af6c4f89-8fc3-5049-9d98-3e2e98061073 10.0MiB 61.0MiB off',
+ '$(8) LinuxData 9704d7cd-810f-4d44-ac78-432ebc16143f 10.0MiB 71.0MiB off',
+ '$(9) WindowsBasicData a05f8e09-f9e7-5b4e-bb4e-e9f8fde3110e 1.9GiB 81.0MiB off',
+ ],
+ 'PartitionNumbers' : [1, 2, 3, 4, 5, 6, 7, 8, 9],
+ },
+
+ }
+ };
+ self.asActions = \
+ [
+ {
+ 'action' : 'whole drive',
+ 'options' : [],
+ 'data-crc' : {},
+ 'createType' : 'fullDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' : ['RW 0 FLAT "$(disk)" 0',],
+ '6.1/storage/t-gpt.vdi' : ['RW 0 FLAT "$(disk)" 0',],
+ },
+ },
+ {
+ 'action' : '1 partition',
+ 'options' : ['--property', 'Partitions=1'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 2681429243,
+ '6.1/storage/t-gpt.vdi' : 1391394051,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ {
+ 'action' : '2 partitions',
+ 'options' : ['--property', 'Partitions=1,$(4)'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 2681429243,
+ '6.1/storage/t-gpt.vdi' : 1391394051,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 FLAT "$(disk)" 65536',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 FLAT "$(disk)" 63488',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ {
+ 'action' : '1 partition with boot sector',
+ 'options' : ['--property', 'Partitions=1',
+ '--property-file', 'BootSector=$(bootsector)'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 3980784439,
+ '6.1/storage/t-gpt.vdi' : 1152317131,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ {
+ 'action' : '2 partitions with boot sector',
+ 'options' : ['--property', 'Partitions=1,$(4)',
+ '--property-file', 'BootSector=$(bootsector)'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 3980784439,
+ '6.1/storage/t-gpt.vdi' : 1152317131,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 FLAT "$(disk)" 65536',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(disk)" 2048',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 FLAT "$(disk)" 63488',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ {
+ 'action' : '1 partition with relative names',
+ 'options' : ['--property', 'Partitions=1', '--property', 'Relative=1'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 2681429243,
+ '6.1/storage/t-gpt.vdi' : 1391394051,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(part)1" 0',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(part)1" 0',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ {
+ 'action' : '2 partitions with relative names',
+ 'options' : ['--property', 'Partitions=1,$(4)', '--property', 'Relative=1'],
+ 'data-crc' : {'6.1/storage/t-mbr.vdi' : 2681429243,
+ '6.1/storage/t-gpt.vdi' : 1391394051,
+ },
+ 'createType' : 'partitionedDevice',
+ 'extents' : { '6.1/storage/t-mbr.vdi' :
+ ['RW 2048 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 20480 FLAT "$(part)1" 0',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 2048',
+ 'RW 20480 FLAT "$(part)$(4)" 0',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 4096',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 6144',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 8192',
+ 'RW 20480 ZERO',
+ 'RW 2048 FLAT "vmdktest-pt.vmdk" 10240',
+ 'RW 4036608 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ '6.1/storage/t-gpt.vdi' :
+ ['RW 1 FLAT "vmdktest-pt.vmdk" 0',
+ 'RW 2047 FLAT "vmdktest-pt.vmdk" 1',
+ 'RW 20480 FLAT "$(part)1" 0',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 FLAT "$(part)$(4)" 0',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 20480 ZERO',
+ 'RW 4026368 ZERO',
+ 'RW 36028797014771712 ZERO',
+ ],
+ },
+ },
+ ];
+
+
+ def _findFile(self, sRegExp, asTestBuildDirs):
+ """
+ Returns a filepath based on the given regex and paths to look into
+ or None if no matching file is found.
+ """
+ oRegExp = re.compile(sRegExp);
+ for sTestBuildDir in asTestBuildDirs:
+ try:
+ #return most recent file if there are several ones matching the pattern
+ asFiles = [s for s in os.listdir(sTestBuildDir)
+ if os.path.isfile(os.path.join(sTestBuildDir, s))];
+ asFiles = (s for s in asFiles
+ if oRegExp.match(os.path.basename(s))
+ and os.path.exists(sTestBuildDir + '/' + s));
+ asFiles = sorted(asFiles, reverse = True,
+ key = lambda s, sTstBuildDir = sTestBuildDir: os.path.getmtime(os.path.join(sTstBuildDir, s)));
+ if asFiles:
+ return sTestBuildDir + '/' + asFiles[0];
+ except:
+ pass;
+ reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, ','.join(asTestBuildDirs)));
+ return None;
+
+ def _waitAdditionsIsRunning(self, oGuest, fWaitTrayControl):
+ """
+ Check is the additions running
+ """
+ cAttempt = 0;
+ fRc = False;
+ while cAttempt < 30:
+ fRc = oGuest.additionsRunLevel in [vboxcon.AdditionsRunLevelType_Userland,
+ vboxcon.AdditionsRunLevelType_Desktop];
+ if fRc:
+ eServiceStatus, _ = oGuest.getFacilityStatus(vboxcon.AdditionsFacilityType_VBoxService);
+ fRc = eServiceStatus == vboxcon.AdditionsFacilityStatus_Active;
+ if fRc and not fWaitTrayControl:
+ break;
+ if fRc:
+ eServiceStatus, _ = oGuest.getFacilityStatus(vboxcon.AdditionsFacilityType_VBoxTrayClient);
+ fRc = eServiceStatus == vboxcon.AdditionsFacilityStatus_Active;
+ if fRc:
+ break;
+ self.oTstDrv.sleep(10);
+ cAttempt += 1;
+ return fRc;
+
+ def createSession(self, oSession, sName, sUser, sPassword, cMsTimeout = 10 * 1000, fIsError = True):
+ """
+ Creates (opens) a guest session.
+ Returns (True, IGuestSession) on success or (False, None) on failure.
+ """
+ oGuest = oSession.o.console.guest;
+ if sName is None:
+ sName = "<untitled>";
+ reporter.log('Creating session "%s" ...' % (sName,));
+ try:
+ oGuestSession = oGuest.createSession(sUser, sPassword, '', sName);
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Creating a guest session "%s" failed; sUser="%s", pw="%s"'
+ % (sName, sUser, sPassword));
+ return (False, None);
+ reporter.log('Waiting for session "%s" to start within %dms...' % (sName, cMsTimeout));
+ aeWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start, ];
+ try:
+ waitResult = oGuestSession.waitForArray(aeWaitFor, cMsTimeout);
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult not in (vboxcon.GuestSessionWaitResult_Start, vboxcon.GuestSessionWaitResult_WaitFlagNotSupported):
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErr(fIsError, 'Session did not start successfully, returned wait result: %d' % (waitResult,));
+ return (False, None);
+ reporter.log('Session "%s" successfully started' % (sName,));
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Waiting for guest session "%s" (usr=%s;pw=%s) to start failed:'
+ % (sName, sUser, sPassword,));
+ return (False, None);
+ return (True, oGuestSession);
+
+ def closeSession(self, oGuestSession, fIsError = True):
+ """
+ Closes the guest session.
+ """
+ if oGuestSession is not None:
+ try:
+ sName = oGuestSession.name;
+ except:
+ return reporter.errorXcpt();
+ reporter.log('Closing session "%s" ...' % (sName,));
+ try:
+ oGuestSession.close();
+ oGuestSession = None;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.maybeErrXcpt(fIsError, 'Closing guest session "%s" failed:' % (sName,));
+ return False;
+ return True;
+
+ def guestProcessExecute(self, oGuestSession, sTestName, cMsTimeout, sExecName, asArgs = (),
+ fGetStdOut = True, fIsError = True):
+ """
+ Helper function to execute a program on a guest, specified in the current test.
+ Returns (True, ProcessStatus, ProcessExitCode, ProcessStdOutBuffer) on success or (False, 0, 0, None) on failure.
+ """
+ _ = sTestName;
+ fRc = True; # Be optimistic.
+ reporter.log2('Using session user=%s, name=%s, timeout=%d'
+ % (oGuestSession.user, oGuestSession.name, oGuestSession.timeout,));
+ #
+ # Start the process:
+ #
+ reporter.log2('Executing sCmd=%s, timeoutMS=%d, asArgs=%s'
+ % (sExecName, cMsTimeout, asArgs, ));
+ fTaskFlags = [];
+ if fGetStdOut:
+ fTaskFlags = [vboxcon.ProcessCreateFlag_WaitForStdOut,
+ vboxcon.ProcessCreateFlag_WaitForStdErr];
+ try:
+ oProcess = oGuestSession.processCreate(sExecName,
+ asArgs if self.oTstDrv.fpApiVer >= 5.0 else asArgs[1:],
+ [], fTaskFlags, cMsTimeout);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'asArgs=%s' % (asArgs,));
+ return (False, 0, 0, None);
+ if oProcess is None:
+ return (reporter.error('oProcess is None! (%s)' % (asArgs,)), 0, 0, None);
+ #time.sleep(5); # try this if you want to see races here.
+ # Wait for the process to start properly:
+ reporter.log2('Process start requested, waiting for start (%dms) ...' % (cMsTimeout,));
+ iPid = -1;
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Start, ];
+ aBuf = None;
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, cMsTimeout);
+ except:
+ reporter.maybeErrXcpt(fIsError, 'waitforArray failed for asArgs=%s' % (asArgs,));
+ fRc = False;
+ else:
+ try:
+ eStatus = oProcess.status;
+ iPid = oProcess.PID;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ else:
+ reporter.log2('Wait result returned: %d, current process status is: %d' % (eWaitResult, eStatus,));
+ #
+ # Wait for the process to run to completion if necessary.
+ #
+ # Note! The above eWaitResult return value can be ignored as it will
+ # (mostly) reflect the process status anyway.
+ #
+ if eStatus == vboxcon.ProcessStatus_Started:
+ # What to wait for:
+ aeWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate,
+ vboxcon.ProcessWaitForFlag_StdOut,
+ vboxcon.ProcessWaitForFlag_StdErr];
+ reporter.log2('Process (PID %d) started, waiting for termination (%dms), aeWaitFor=%s ...'
+ % (iPid, cMsTimeout, aeWaitFor));
+ acbFdOut = [0,0,0];
+ while True:
+ try:
+ eWaitResult = oProcess.waitForArray(aeWaitFor, cMsTimeout);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ break;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ break;
+ reporter.log2('Wait returned: %d' % (eWaitResult,));
+ # Process output:
+ for eFdResult, iFd, sFdNm in [ (vboxcon.ProcessWaitResult_StdOut, 1, 'stdout'),
+ (vboxcon.ProcessWaitResult_StdErr, 2, 'stderr'), ]:
+ if eWaitResult in (eFdResult, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ reporter.log2('Reading %s ...' % (sFdNm,));
+ try:
+ abBuf = oProcess.read(iFd, 64 * 1024, cMsTimeout);
+ except KeyboardInterrupt: # Not sure how helpful this is, but whatever.
+ reporter.error('Process (PID %d) execution interrupted' % (iPid,));
+ try: oProcess.close();
+ except: pass;
+ except:
+ pass; ## @todo test for timeouts and fail on anything else!
+ else:
+ if abBuf:
+ reporter.log2('Process (PID %d) got %d bytes of %s data' % (iPid, len(abBuf), sFdNm,));
+ acbFdOut[iFd] += len(abBuf);
+ ## @todo Figure out how to uniform + append!
+ sBuf = '';
+ if sys.version_info >= (2, 7) and isinstance(abBuf, memoryview):
+ abBuf = abBuf.tobytes();
+ sBuf = abBuf.decode("utf-8");
+ else:
+ sBuf = str(abBuf);
+ if aBuf:
+ aBuf += sBuf;
+ else:
+ aBuf = sBuf;
+ ## Process input (todo):
+ #if eWaitResult in (vboxcon.ProcessWaitResult_StdIn, vboxcon.ProcessWaitResult_WaitFlagNotSupported):
+ # reporter.log2('Process (PID %d) needs stdin data' % (iPid,));
+ # Termination or error?
+ if eWaitResult in (vboxcon.ProcessWaitResult_Terminate,
+ vboxcon.ProcessWaitResult_Error,
+ vboxcon.ProcessWaitResult_Timeout,):
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Process (PID %d) reported terminate/error/timeout: %d, status: %d'
+ % (iPid, eWaitResult, eStatus,));
+ break;
+ # End of the wait loop.
+ _, cbStdOut, cbStdErr = acbFdOut;
+ try: eStatus = oProcess.status;
+ except: fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Final process status (PID %d) is: %d' % (iPid, eStatus));
+ reporter.log2('Process (PID %d) %d stdout, %d stderr' % (iPid, cbStdOut, cbStdErr));
+ #
+ # Get the final status and exit code of the process.
+ #
+ try:
+ uExitStatus = oProcess.status;
+ iExitCode = oProcess.exitCode;
+ except:
+ fRc = reporter.errorXcpt('asArgs=%s' % (asArgs,));
+ reporter.log2('Process (PID %d) has exit code: %d; status: %d ' % (iPid, iExitCode, uExitStatus));
+ return (fRc, uExitStatus, iExitCode, aBuf);
+
+ def uploadString(self, oGuestSession, sSrcString, sDst):
+ """
+ Upload the string into guest.
+ """
+ fRc = True;
+ try:
+ oFile = oGuestSession.fileOpenEx(sDst, vboxcon.FileAccessMode_ReadWrite, vboxcon.FileOpenAction_CreateOrReplace,
+ vboxcon.FileSharingMode_All, 0, []);
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not create and open the file %s' % sDst);
+ else:
+ try:
+ oFile.write(bytearray(sSrcString), 60*1000);
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not write the string into the file %s' % sDst);
+ try:
+ oFile.close();
+ except:
+ fRc = reporter.errorXcpt('Upload string failed. Could not close the file %s' % sDst);
+ return fRc;
+
+ def uploadFile(self, oGuestSession, sSrc, sDst):
+ """
+ Upload the string into guest.
+ """
+ fRc = True;
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyToGuest(sSrc, sDst, [0]);
+ else:
+ oCurProgress = oGuestSession.copyTo(sSrc, sDst, [0]);
+ except:
+ reporter.maybeErrXcpt(True, 'Upload file exception for sSrc="%s":'
+ % (self.sGuestAdditionsIso,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "uploadFile");
+ oWrapperProgress.wait();
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors = False);
+ fRc = False;
+ else:
+ fRc = reporter.error('No progress object returned');
+ return fRc;
+
+ def downloadFile(self, oGuestSession, sSrc, sDst, fIgnoreErrors = False):
+ """
+ Get a file (sSrc) from the guest storing it on the host (sDst).
+ """
+ fRc = True;
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurProgress = oGuestSession.fileCopyFromGuest(sSrc, sDst, [0]);
+ else:
+ oCurProgress = oGuestSession.copyFrom(sSrc, sDst, [0]);
+ except:
+ if not fIgnoreErrors:
+ reporter.errorXcpt('Download file exception for sSrc="%s":' % (sSrc,));
+ else:
+ reporter.log('warning: Download file exception for sSrc="%s":' % (sSrc,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr,
+ self.oTstDrv, "downloadFile");
+ oWrapperProgress.wait();
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors);
+ fRc = False;
+ else:
+ if not fIgnoreErrors:
+ reporter.error('No progress object returned');
+ else:
+ reporter.log('warning: No progress object returned');
+ fRc = False;
+ return fRc;
+
+ def downloadFiles(self, oGuestSession, asFiles, fIgnoreErrors = False):
+ """
+ Convenience function to get files from the guest and stores it
+ into the scratch directory for later (manual) review.
+ Returns True on success.
+ Returns False on failure, logged.
+ """
+ fRc = True;
+ for sGstFile in asFiles:
+ ## @todo r=bird: You need to use the guest specific path functions here.
+ ## Best would be to add basenameEx to common/pathutils.py. See how joinEx
+ ## is used by BaseTestVm::pathJoin and such.
+ sTmpFile = os.path.join(self.oTstDrv.sScratchPath, 'tmp-' + os.path.basename(sGstFile));
+ reporter.log2('Downloading file "%s" to "%s" ...' % (sGstFile, sTmpFile));
+ # First try to remove (unlink) an existing temporary file, as we don't truncate the file.
+ try: os.unlink(sTmpFile);
+ except: pass;
+ ## @todo Check for already existing files on the host and create a new
+ # name for the current file to download.
+ fRc = self.downloadFile(oGuestSession, sGstFile, sTmpFile, fIgnoreErrors);
+ if fRc:
+ reporter.addLogFile(sTmpFile, 'misc/other', 'guest - ' + sGstFile);
+ else:
+ if fIgnoreErrors is not True:
+ reporter.error('error downloading file "%s" to "%s"' % (sGstFile, sTmpFile));
+ return fRc;
+ reporter.log('warning: file "%s" was not downloaded, ignoring.' % (sGstFile,));
+ return True;
+
+ def _checkVmIsReady(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Start a guest process',
+ 30 * 1000, '/sbin/ifconfig',
+ ['ifconfig',],
+ False, False);
+ return fRc;
+
+ def waitVmIsReady(self, oSession, fWaitTrayControl):
+ """
+ Waits the VM is ready after start or reboot.
+ Returns result (true or false) and guest session obtained
+ """
+ _ = fWaitTrayControl;
+ # Give the VM a time to reboot
+ self.oTstDrv.sleep(30);
+ # Waiting the VM is ready.
+ # To do it, one will try to open the guest session and start the guest process in loop
+ if not self._waitAdditionsIsRunning(oSession.o.console.guest, False):
+ return (False, None);
+ cAttempt = 0;
+ oGuestSession = None;
+ fRc = False;
+ while cAttempt < 30:
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: vbox',
+ 'vbox', 'password', 10 * 1000, False);
+ if fRc:
+ fRc = self._checkVmIsReady(oGuestSession);
+ if fRc:
+ break;
+ self.closeSession(oGuestSession, False);
+ self.oTstDrv.sleep(10);
+ cAttempt += 1;
+ return (fRc, oGuestSession);
+
+ def _rebootVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Reboot the VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['sudo', 'reboot'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the reboot utility failed');
+ return fRc;
+
+ def rebootVMAndCheckReady(self, oSession, oGuestSession):
+ """
+ Reboot the VM and wait the VM is ready.
+ Returns result and guest session obtained after reboot
+ """
+ reporter.testStart('Reboot VM and wait for readiness');
+ fRc = self._rebootVM(oGuestSession);
+ fRc = self.closeSession(oGuestSession, True) and fRc and True; # pychecker hack.
+ if fRc:
+ (fRc, oGuestSession) = self.waitVmIsReady(oSession, False);
+ if not fRc:
+ reporter.error('VM is not ready after reboot');
+ reporter.testDone();
+ return (fRc, oGuestSession);
+
+ def _powerDownVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Power down the VM',
+ 30 * 1000, '/usr/bin/sudo',
+ ['sudo', 'poweroff'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the poweroff utility failed');
+ return fRc;
+
+ def powerDownVM(self, oGuestSession):
+ """
+ Power down the VM by calling guest process without wating
+ the VM is really powered off. Also, closes the guest session.
+ It helps the terminateBySession to stop the VM without aborting.
+ """
+ if oGuestSession is None:
+ return False;
+ reporter.testStart('Power down the VM');
+ fRc = self._powerDownVM(oGuestSession);
+ fRc = self.closeSession(oGuestSession, True) and fRc and True; # pychecker hack.
+ if not fRc:
+ reporter.error('Power down the VM failed');
+ reporter.testDone();
+ return fRc;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Installs the Windows guest additions using the test execution service.
+ """
+ _ = oSession;
+ _ = oGuestSession;
+ _ = oVM;
+ reporter.error('Not implemented');
+ return False;
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ _ = oGuestSession;
+ reporter.error('Not implemented');
+ return False;
+
+ def getResourceSet(self):
+ asRet = [];
+ if not os.path.isabs(self.sHdd):
+ asRet.append(self.sHdd);
+ return asRet;
+
+ def _createVmDoIt(self, oTestDrv, eNic0AttachType, sDvdImage):
+ """
+ Creates the VM.
+ Returns Wrapped VM object on success, None on failure.
+ """
+ _ = eNic0AttachType;
+ _ = sDvdImage;
+ return oTestDrv.createTestVM(self.sVmName, self.iGroup, self.sHdd, sKind = self.sKind, \
+ fIoApic = True, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = self.eNic0Type, cMbRam = self.cMbRam, \
+ sHddControllerType = "SATA Controller", fPae = self.fPae, \
+ cCpus = self.cCpus, sDvdImage = self.sGuestAdditionsIso);
+
+ def _createVmPost(self, oTestDrv, oVM, eNic0AttachType, sDvdImage):
+ _ = eNic0AttachType;
+ _ = sDvdImage;
+ fRc = True;
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(True);
+ # nested paging doesn't need for the test
+ #fRc = fRc and oSession.enableNestedPaging(True);
+ #fRc = fRc and oSession.enableNestedHwVirt(True);
+ # disable 3D until the error is fixed.
+ fRc = fRc and oSession.setAccelerate3DEnabled(False);
+ fRc = fRc and oSession.setVRamSize(256);
+ fRc = fRc and oSession.setVideoControllerType(vboxcon.GraphicsControllerType_VBoxSVGA);
+ fRc = fRc and oSession.enableUsbOhci(True);
+ fRc = fRc and oSession.enableUsbHid(True);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+ return oVM if fRc else None;
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ #
+ # Current test uses precofigured VMs. This override disables any changes in the machine.
+ #
+ _ = cCpus;
+ _ = sVirtMode;
+ _ = sParavirtMode;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is None:
+ return (False, None);
+ return (True, oVM);
+
+ def reattachHdd(self, oVM, sHdd, asHdds):
+ """
+ Attach required hdd and remove all others from asHdds list.
+ """
+ reporter.testStart("Reattach hdd");
+ oSession = self.oTstDrv.openSession(oVM);
+ fRc = False;
+ if oSession is not None:
+ # for simplicity and because we are using VMs having "SATA controller"
+ # we will add the hdds to only "SATA controller"
+ iPortNew = 0;
+ fFound = False;
+ try:
+ aoAttachments = self.oTstDrv.oVBox.oVBoxMgr.getArray(oVM, 'mediumAttachments');
+ except:
+ fRc = reporter.errorXcpt();
+ else:
+ for oAtt in aoAttachments:
+ try:
+ sCtrl = oAtt.controller
+ iPort = oAtt.port;
+ iDev = oAtt.device;
+ eType = oAtt.type;
+ except:
+ fRc = reporter.errorXcpt();
+ break;
+
+ fDetached = False;
+ if eType == vboxcon.DeviceType_HardDisk:
+ oMedium = oVM.getMedium(sCtrl, iPort, iDev);
+ if oMedium.location.endswith(sHdd):
+ fRc = True;
+ fFound = True;
+ break;
+ for sHddVar in asHdds:
+ if oMedium.location.endswith(sHddVar) \
+ or oMedium.parent is not None and oMedium.parent.location.endswith(sHddVar) :
+ (fRc, oOldHd) = oSession.detachHd(sCtrl, iPort, iDev);
+ if fRc and oOldHd is not None:
+ fRc = oSession.saveSettings();
+ if oMedium.parent is not None:
+ fRc = fRc and self.oTstDrv.oVBox.deleteHdByMedium(oOldHd);
+ else:
+ fRc = fRc and oOldHd.close();
+ fRc = fRc and oSession.saveSettings();
+ fDetached = True;
+ if not fDetached and sCtrl == 'SATA Controller' and iPort + 1 > iPortNew:
+ iPortNew = iPort + 1;
+ if not fFound:
+ fRc = oSession.attachHd(sHdd, 'SATA Controller', iPortNew, 0);
+ if fRc:
+ fRc = oSession.saveSettings();
+ else:
+ oSession.discadSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack
+ else:
+ reporter.error("Open session for '%s' failed" % self.sVmName);
+ fRc = False;
+ reporter.testDone();
+ return fRc;
+
+ def _callVBoxManage(self, oGuestSession, sTestName, cMsTimeout, asArgs = (),
+ fGetStdOut = True, fIsError = True):
+ return self.guestProcessExecute(oGuestSession, sTestName,
+ cMsTimeout, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/opt/VirtualBox/VBoxManage'] + asArgs, fGetStdOut, fIsError);
+
+ def listHostDrives(self, oGuestSession, sHdd):
+ """
+ Define path of the specified drive using 'VBoxManage list hostdrives'.
+ """
+ reporter.testStart("List host drives");
+ sDrive = None;
+ (fRc, _, _, aBuf) = self._callVBoxManage(oGuestSession, 'List host drives', 60 * 1000,
+ ['list', 'hostdrives'], True, True);
+ if not fRc:
+ reporter.error('List host drives in the VM %s failed' % (self.sVmName, ));
+ else:
+ if aBuf is None:
+ fRc = reporter.error('"List host drives" output is empty for the VM %s' % (self.sVmName, ));
+ else:
+ asHddData = self.asHdds[sHdd];
+
+ try: aBuf = str(aBuf); # pylint: disable=redefined-variable-type
+ except: pass;
+ asLines = aBuf.splitlines();
+ oRegExp = re.compile(r'^\s*([^:]+)\s*:\s*(.+)\s*$');
+
+ # pylint: disable=no-init
+ class ParseState(object):
+ kiNothing = 0;
+ kiDrive = 1;
+ kiPartition = 2;
+
+ iParseState = ParseState.kiNothing;
+ asKeysNotFound = asHddData['Header'].keys();
+ idxPartition = 0;
+ for sLine in asLines:
+ if not sLine or sLine.startswith('#') or sLine.startswith("\n"):
+ continue;
+ oMatch = oRegExp.match(sLine);
+ if oMatch is not None:
+ sKey = oMatch.group(1);
+ sValue = oMatch.group(2);
+ if sKey is not None and sKey == 'Drive':
+ # we found required disk if we found all required disk info and partitions
+ if sDrive and not asKeysNotFound and idxPartition >= len(asHddData['Partitions']['Partitions']):
+ break;
+ sDrive = sValue;
+ iParseState = ParseState.kiDrive;
+ asKeysNotFound = asKeysNotFound = asHddData['Header'].keys();
+ idxPartition = 0;
+ continue;
+ if iParseState == ParseState.kiDrive:
+ if sLine.strip().startswith('Partitions:'):
+ iParseState = ParseState.kiPartition;
+ continue;
+ if oMatch is None or sKey is None:
+ continue;
+ if sKey in asHddData['Header'].keys() and asHddData['Header'][sKey] == sValue:
+ asKeysNotFound.remove(sKey);
+ continue;
+ if iParseState == ParseState.kiPartition:
+ if idxPartition < len(asHddData['Partitions']['Partitions']):
+ sPart = asHddData['Partitions']['Partitions'][idxPartition];
+ sPart = sPart.replace('$(' + str(idxPartition + 1) + ')',
+ str(asHddData['Partitions']['PartitionNumbers'][idxPartition]));
+ if sLine.strip() == sPart:
+ idxPartition += 1;
+ continue;
+ fRc = sDrive and not asKeysNotFound and idxPartition >= len(asHddData['Partitions']['Partitions']);
+ if fRc:
+ reporter.log("Path to the drive '%s' in the VM '%s': %s " % (sHdd, self.sVmName, sDrive));
+ else:
+ reporter.error("Path to drive '%s' not found in the VM '%s'" % (sHdd, self.sVmName));
+ reporter.testDone();
+ return (fRc, sDrive);
+
+ def convertDiskToPartitionPrefix(self, sDisk):
+ return sDisk;
+
+ def checkVMDKDescriptor(self, asDescriptor, sHdd, sRawDrive, asAction):
+ """
+ Check VMDK descriptor of the disk created
+ """
+ if asDescriptor is None \
+ or asDescriptor[0] != '# Disk DescriptorFile' \
+ and asDescriptor[0] != '# Disk Descriptor File' \
+ and asDescriptor[0] != '#Disk Descriptor File' \
+ and asDescriptor[0] != '#Disk DescriptorFile':
+ return reporter.error("VMDK descriptor has invalid format");
+
+ # pylint: disable=no-init
+ class DescriptorParseState(object):
+ kiHeader = 1;
+ kiExtent = 2;
+ kiDatabase = 3;
+
+ asHddData = self.asHdds[sHdd];
+ iParseState = DescriptorParseState.kiHeader;
+
+ asHeader = { 'version' : '1',
+ 'CID' : '*',
+ 'parentCID' : 'ffffffff',
+ 'createType' : '$'
+ };
+
+ asDatabase = { 'ddb.virtualHWVersion' : '4',
+ 'ddb.adapterType' : 'ide',
+ 'ddb.uuid.image' : '*',
+ 'ddb.uuid.parent' : '00000000-0000-0000-0000-000000000000',
+ 'ddb.uuid.modification' : '00000000-0000-0000-0000-000000000000',
+ 'ddb.uuid.parentmodification' : '00000000-0000-0000-0000-000000000000'
+ };
+
+ oRegExp = re.compile(r'^\s*([^=]+)\s*=\s*\"*([^\"]+)\"*\s*$');
+ iExtentIdx = 0;
+
+ for sLine in asDescriptor:
+ if not sLine or sLine.startswith('#') or sLine.startswith("\n"):
+ continue;
+
+ if iParseState == DescriptorParseState.kiHeader:
+ if sLine.startswith('ddb.'):
+ return reporter.error("VMDK descriptor has invalid order of sections");
+ if sLine.startswith("RW") \
+ or sLine.startswith("RDONLY") \
+ or sLine.startswith("NOACCESS"):
+ iParseState = DescriptorParseState.kiExtent;
+ else:
+ oMatch = oRegExp.match(sLine);
+ if oMatch is None:
+ return reporter.error("VMDK descriptor contains lines in invalid form");
+ sKey = oMatch.group(1).strip();
+ sValue = oMatch.group(2).strip();
+ if sKey not in asHeader:
+ return reporter.error("VMDK descriptor has invalid format");
+ sDictValue = asHeader[sKey];
+ if sDictValue == '$':
+ sDictValue = asAction[sKey];
+ if sDictValue not in ('*', sValue):
+ return reporter.error("VMDK descriptor has value which was not expected");
+ continue;
+
+ if iParseState == DescriptorParseState.kiExtent:
+ if sLine.startswith('ddb.'):
+ iParseState = DescriptorParseState.kiDatabase;
+ else:
+ if not sLine.startswith("RW") \
+ and not sLine.startswith("RDONLY") \
+ and not sLine.startswith("NOACCESS"):
+ return reporter.error("VMDK descriptor has invalid order of sections");
+ sExtent = asAction['extents'][sHdd][iExtentIdx];
+ sExtent = sExtent.replace('$(disk)', sRawDrive);
+ sExtent = sExtent.replace('$(part)', self.convertDiskToPartitionPrefix(sRawDrive));
+ sExtent = re.sub(r'\$\((\d+)\)',
+ lambda oMatch: str(asHddData['Partitions']['PartitionNumbers'][int(oMatch.group(1)) - 1]),
+ sExtent);
+ if sExtent != sLine.strip():
+ return reporter.error("VMDK descriptor has invalid order of sections");
+ iExtentIdx += 1;
+ continue;
+
+ if iParseState == DescriptorParseState.kiDatabase:
+ if not sLine.startswith('ddb.'):
+ return reporter.error("VMDK descriptor has invalid order of sections");
+ oMatch = oRegExp.match(sLine);
+ if oMatch is None:
+ return reporter.error("VMDK descriptor contains lines in invalid form");
+ sKey = oMatch.group(1).strip();
+ sValue = oMatch.group(2).strip();
+ if sKey not in asDatabase:
+ return reporter.error("VMDK descriptor has invalid format");
+ sDictValue = asDatabase[sKey];
+ if sDictValue not in ('*', sValue):
+ return reporter.error("VMDK descriptor has value which was not expected");
+ continue;
+ return iParseState == DescriptorParseState.kiDatabase;
+
+ def _setPermissionsToVmdkFiles(self, oGuestSession):
+ """
+ Sets 0644 permissions to all files in the self.sVMDKPath allowing reading them by 'vbox' user.
+ """
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession,
+ 'Allowing reading of the VMDK content by vbox user',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/chmod', '644',
+ self.sVMDKPath + '/vmdktest.vmdk', self.sVMDKPath + '/vmdktest-pt.vmdk'],
+ False, True);
+ return fRc;
+
+ def createDrives(self, oGuestSession, sHdd, sRawDrive):
+ """
+ Creates VMDK Raw file and check correctness
+ """
+ reporter.testStart("Create VMDK disks");
+ asHddData = self.asHdds[sHdd];
+ fRc = True;
+ try: oGuestSession.directoryCreate(self.sVMDKPath, 0o777, (vboxcon.DirectoryCreateFlag_Parents,));
+ except: fRc = reporter.errorXcpt('Create directory for VMDK files failed in the VM %s' % (self.sVmName));
+ if fRc:
+ sBootSectorGuestPath = self.sVMDKPath + self.sPathDelimiter + 't-bootsector.bin';
+ try: fExists = oGuestSession.fileExists(sBootSectorGuestPath, False);
+ except: fExists = False;
+ if not fExists:
+ sBootSectorPath = self.oTstDrv.getFullResourceName(self.sBootSector);
+ fRc = self.uploadFile(oGuestSession, sBootSectorPath, sBootSectorGuestPath);
+
+ for action in self.asActions:
+ reporter.testStart("Create VMDK disk: %s" % action["action"]);
+ asOptions = action['options'];
+ asOptions = [option.replace('$(bootsector)', sBootSectorGuestPath) for option in asOptions];
+ asOptions = [re.sub(r'\$\((\d+)\)',
+ lambda oMatch: str(asHddData['Partitions']['PartitionNumbers'][int(oMatch.group(1)) - 1]),
+ option)
+ for option in asOptions];
+ (fRc, _, _, _) = self._callVBoxManage(oGuestSession, 'Create VMDK disk', 60 * 1000,
+ ['createmedium', '--filename',
+ self.sVMDKPath + self.sPathDelimiter + 'vmdktest.vmdk',
+ '--format', 'VMDK', '--variant', 'RawDisk',
+ '--property', 'RawDrive=%s' % (sRawDrive,) ] + asOptions,
+ False, True);
+ if not fRc:
+ reporter.error('Create VMDK raw drive variant "%s" failed in the VM %s' % (action["action"], self.sVmName));
+ else:
+ fRc = self._setPermissionsToVmdkFiles(oGuestSession);
+ if not fRc:
+ reporter.error('Setting permissions to VMDK files failed');
+ else:
+ sSrcFile = self.sVMDKPath + self.sPathDelimiter + 'vmdktest.vmdk';
+ sDstFile = os.path.join(self.oTstDrv.sScratchPath, 'guest-vmdktest.vmdk');
+ reporter.log2('Downloading file "%s" to "%s" ...' % (sSrcFile, sDstFile));
+ # First try to remove (unlink) an existing temporary file, as we don't truncate the file.
+ try: os.unlink(sDstFile);
+ except: pass;
+ fRc = self.downloadFile(oGuestSession, sSrcFile, sDstFile, False);
+ if not fRc:
+ reporter.error('Download vmdktest.vmdk from guest to host failed');
+ else:
+ with open(sDstFile) as oFile: # pylint: disable=unspecified-encoding
+ asDescriptor = [row.strip() for row in oFile];
+ if not asDescriptor:
+ fRc = reporter.error('Reading vmdktest.vmdk from guest filed');
+ else:
+ fRc = self.checkVMDKDescriptor(asDescriptor, sHdd, sRawDrive, action);
+ if not fRc:
+ reporter.error('Cheking vmdktest.vmdk from guest filed');
+ elif action['data-crc']:
+ sSrcFile = self.sVMDKPath + self.sPathDelimiter + 'vmdktest-pt.vmdk';
+ sDstFile = os.path.join(self.oTstDrv.sScratchPath, 'guest-vmdktest-pt.vmdk');
+ reporter.log2('Downloading file "%s" to "%s" ...' % (sSrcFile, sDstFile));
+ # First try to remove (unlink) an existing temporary file, as we don't truncate the file.
+ try: os.unlink(sDstFile);
+ except: pass;
+ fRc = self.downloadFile(oGuestSession, sSrcFile, sDstFile, False);
+ if not fRc:
+ reporter.error('Download vmdktest-pt.vmdk from guest to host failed');
+ else:
+ uResCrc32 = utils.calcCrc32OfFile(sDstFile);
+ if uResCrc32 != action['data-crc'][sHdd]:
+ fRc = reporter.error('vmdktest-pt.vmdk does not match what was expected');
+ (fRc1, _, _, _) = self._callVBoxManage(oGuestSession, 'Delete VMDK disk', 60 * 1000,
+ ['closemedium',
+ self.sVMDKPath + self.sPathDelimiter + 'vmdktest.vmdk',
+ '--delete'],
+ False, True);
+ if not fRc1:
+ reporter.error('Delete VMDK raw drive variant "%s" failed in the VM %s' %
+ (action["action"], self.sVmName));
+ fRc = fRc and fRc1;
+ reporter.testDone();
+ if not fRc:
+ break;
+ else:
+ reporter.error('Create %s dir failed in the VM %s' % (self.sVMDKPath, self.sVmName));
+
+ reporter.testDone();
+ return fRc;
+
+
+class tdStorageRawDriveOsLinux(tdStorageRawDriveOs):
+ """
+ Autostart support methods for Linux guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None, sBootSector = None):
+ tdStorageRawDriveOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso, sBootSector);
+ self.sVBoxInstaller = '^VirtualBox-.*\\.run$';
+ return;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Install guest additions in the guest.
+ """
+ reporter.testStart('Install Guest Additions');
+ fRc = False;
+ # Install Kernel headers, which are required for actually installing the Linux Additions.
+ if oVM.OSTypeId.startswith('Debian') \
+ or oVM.OSTypeId.startswith('Ubuntu'):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Kernel headers',
+ 5 * 60 *1000, '/usr/bin/apt-get',
+ ['/usr/bin/apt-get', 'install', '-y',
+ 'linux-headers-generic'],
+ False, True);
+ if not fRc:
+ reporter.error('Error installing Kernel headers');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Guest Additions depdendencies',
+ 5 * 60 *1000, '/usr/bin/apt-get',
+ ['/usr/bin/apt-get', 'install', '-y', 'build-essential',
+ 'perl'], False, True);
+ if not fRc:
+ reporter.error('Error installing additional installer dependencies');
+ elif oVM.OSTypeId.startswith('OL') \
+ or oVM.OSTypeId.startswith('Oracle') \
+ or oVM.OSTypeId.startswith('RHEL') \
+ or oVM.OSTypeId.startswith('Redhat') \
+ or oVM.OSTypeId.startswith('Cent'):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Kernel headers',
+ 5 * 60 *1000, '/usr/bin/yum',
+ ['/usr/bin/yum', '-y', 'install', 'kernel-headers'],
+ False, True);
+ if not fRc:
+ reporter.error('Error installing Kernel headers');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing Guest Additions depdendencies',
+ 5 * 60 *1000, '/usr/bin/yum',
+ ['/usr/bin/yum', '-y', 'install', 'make', 'automake', 'gcc',
+ 'kernel-devel', 'dkms', 'bzip2', 'perl'], False, True);
+ if not fRc:
+ reporter.error('Error installing additional installer dependencies');
+ else:
+ reporter.error('Installing Linux Additions for the "%s" is not supported yet' % oVM.OSTypeId);
+ fRc = False;
+ if fRc:
+ #
+ # The actual install.
+ # Also tell the installer to produce the appropriate log files.
+ #
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing guest additions',
+ 10 * 60 *1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/sh',
+ '/media/cdrom/VBoxLinuxAdditions.run'],
+ False, True);
+ if fRc:
+ # Due to the GA updates as separate process the above function returns before
+ # the actual installation finished. So just wait until the GA installed
+ fRc = self.closeSession(oGuestSession);
+ if fRc:
+ (fRc, oGuestSession) = self.waitVmIsReady(oSession, False);
+ # Download log files.
+ # Ignore errors as all files above might not be present for whatever reason.
+ #
+ if fRc:
+ asLogFile = [];
+ asLogFile.append('/var/log/vboxadd-install.log');
+ self.downloadFiles(oGuestSession, asLogFile, fIgnoreErrors = True);
+ else:
+ reporter.error('Installing guest additions failed: Error occured during vbox installer execution')
+ if fRc:
+ (fRc, oGuestSession) = self.rebootVMAndCheckReady(oSession, oGuestSession);
+ if not fRc:
+ reporter.error('Reboot after installing GuestAdditions failed');
+ reporter.testDone();
+ return (fRc, oGuestSession);
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ reporter.testStart('Install Virtualbox into the guest VM');
+ sTestBuild = self._findFile(self.sVBoxInstaller, self.asTestBuildDirs);
+ reporter.log("Virtualbox install file: %s" % os.path.basename(sTestBuild));
+ fRc = sTestBuild is not None;
+ if fRc:
+ fRc = self.uploadFile(oGuestSession, sTestBuild,
+ '/tmp/' + os.path.basename(sTestBuild));
+ else:
+ reporter.error("VirtualBox install package is not defined");
+
+ if not fRc:
+ reporter.error('Upload the vbox installer into guest VM failed');
+ else:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession,
+ 'Allowing execution for the vbox installer',
+ 30 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo', '/bin/chmod', '755',
+ '/tmp/' + os.path.basename(sTestBuild)],
+ False, True);
+ if not fRc:
+ reporter.error('Allowing execution for the vbox installer failed');
+ if fRc:
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 240 * 1000, '/usr/bin/sudo',
+ ['/usr/bin/sudo',
+ '/tmp/' + os.path.basename(sTestBuild),],
+ False, True);
+ if not fRc:
+ reporter.error('Installing VBox failed');
+ reporter.testDone();
+ return fRc;
+
+class tdStorageRawDriveOsDarwin(tdStorageRawDriveOs):
+ """
+ Autostart support methods for Darwin guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None, sBootSector = None):
+ tdStorageRawDriveOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso, sBootSector);
+ raise base.GenError('Testing the autostart functionality for Darwin is not implemented');
+
+class tdStorageRawDriveOsSolaris(tdStorageRawDriveOs):
+ """
+ Autostart support methods for Solaris guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None, sBootSector = None):
+ tdStorageRawDriveOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso, sBootSector);
+ raise base.GenError('Testing the autostart functionality for Solaris is not implemented');
+
+class tdStorageRawDriveOsWin(tdStorageRawDriveOs):
+ """
+ Autostart support methods for Windows guests.
+ """
+ # pylint: disable=too-many-arguments
+ def __init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type = None, cMbRam = None, \
+ cCpus = 1, fPae = None, sGuestAdditionsIso = None, sBootSector = None):
+ tdStorageRawDriveOs.__init__(self, oSet, oTstDrv, sVmName, sKind, sHdd, eNic0Type, cMbRam, \
+ cCpus, fPae, sGuestAdditionsIso, sBootSector);
+ self.sVBoxInstaller = r'^VirtualBox-.*\.(exe|msi)$';
+ self.sVMDKPath=r'C:\Temp\vmdk';
+ self.sPathDelimiter = '\\';
+ self.asHdds['6.1/storage/t-mbr.vdi']['Header']['Model'] = '"VBOX HARDDISK"';
+ self.asHdds['6.1/storage/t-gpt.vdi']['Header']['Model'] = '"VBOX HARDDISK"';
+ self.asHdds['6.1/storage/t-mbr.vdi']['Partitions']['PartitionNumbers'] = [1, 2, 3, 4, 5, 6, 7, 8];
+ return;
+
+ def _checkVmIsReady(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Start a guest process',
+ 30 * 1000, 'C:\\Windows\\System32\\ipconfig.exe',
+ ['C:\\Windows\\System32\\ipconfig.exe',],
+ False, False);
+ return fRc;
+
+ def _rebootVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Reboot the VM',
+ 30 * 1000, 'C:\\Windows\\System32\\shutdown.exe',
+ ['C:\\Windows\\System32\\shutdown.exe', '/f',
+ '/r', '/t', '0'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the shutdown utility failed');
+ return fRc;
+
+ def _powerDownVM(self, oGuestSession):
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Power down the VM',
+ 30 * 1000, 'C:\\Windows\\System32\\shutdown.exe',
+ ['C:\\Windows\\System32\\shutdown.exe', '/f',
+ '/s', '/t', '0'],
+ False, True);
+ if not fRc:
+ reporter.error('Calling the shutdown utility failed');
+ return fRc;
+
+ def _callVBoxManage(self, oGuestSession, sTestName, cMsTimeout, asArgs = (),
+ fGetStdOut = True, fIsError = True):
+ return self.guestProcessExecute(oGuestSession, sTestName,
+ cMsTimeout, r'C:\Program Files\Oracle\VirtualBox\VBoxManage.exe',
+ [r'C:\Program Files\Oracle\VirtualBox\VBoxManage.exe',] + asArgs, fGetStdOut, fIsError);
+
+ def _setPermissionsToVmdkFiles(self, oGuestSession):
+ """
+ Sets 0644 permissions to all files in the self.sVMDKPath allowing reading them by 'vbox' user.
+ """
+ _ = oGuestSession;
+ # It is not required in case of Windows
+ return True;
+
+ def installAdditions(self, oSession, oGuestSession, oVM):
+ """
+ Installs the Windows guest additions using the test execution service.
+ """
+ _ = oVM;
+ reporter.testStart('Install Guest Additions');
+ asLogFiles = [];
+ fRc = self.closeSession(oGuestSession, True); # pychecker hack.
+ try:
+ oCurProgress = oSession.o.console.guest.updateGuestAdditions(self.sGuestAdditionsIso, ['/l',], None);
+ except:
+ reporter.maybeErrXcpt(True, 'Updating Guest Additions exception for sSrc="%s":'
+ % (self.sGuestAdditionsIso,));
+ fRc = False;
+ else:
+ if oCurProgress is not None:
+ oWrapperProgress = vboxwrappers.ProgressWrapper(oCurProgress, self.oTstDrv.oVBoxMgr,
+ self.oTstDrv, "installAdditions");
+ oWrapperProgress.wait(cMsTimeout = 10 * 60 * 1000);
+ if not oWrapperProgress.isSuccess():
+ oWrapperProgress.logResult(fIgnoreErrors = False);
+ fRc = False;
+ else:
+ fRc = reporter.error('No progress object returned');
+
+ # Store the result and try download logs anyway.
+ fGaRc = fRc;
+ fRc, oGuestSession = self.createSession(oSession, 'Session for user: vbox',
+ 'vbox', 'password', 10 * 1000, True);
+ if fRc is True:
+ (fRc, oGuestSession) = self.rebootVMAndCheckReady(oSession, oGuestSession);
+ if fRc is True:
+ # Add the Windows Guest Additions installer files to the files we want to download
+ # from the guest.
+ sGuestAddsDir = 'C:/Program Files/Oracle/VirtualBox Guest Additions/';
+ asLogFiles.append(sGuestAddsDir + 'install.log');
+ # Note: There won't be a install_ui.log because of the silent installation.
+ asLogFiles.append(sGuestAddsDir + 'install_drivers.log');
+ # Download log files.
+ # Ignore errors as all files above might not be present (or in different locations)
+ # on different Windows guests.
+ #
+ self.downloadFiles(oGuestSession, asLogFiles, fIgnoreErrors = True);
+ else:
+ reporter.error('Reboot after installing GuestAdditions failed');
+ else:
+ reporter.error('Create session for user vbox after GA updating failed');
+ reporter.testDone();
+ return (fRc and fGaRc, oGuestSession);
+
+ def installVirtualBox(self, oGuestSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ reporter.testStart('Install Virtualbox into the guest VM');
+ # Used windows image already contains the C:\Temp
+ sTestBuild = self._findFile(self.sVBoxInstaller, self.asTestBuildDirs);
+ reporter.log("Virtualbox install file: %s" % os.path.basename(sTestBuild));
+ fRc = sTestBuild is not None;
+ if fRc:
+ fRc = self.uploadFile(oGuestSession, sTestBuild,
+ 'C:\\Temp\\' + os.path.basename(sTestBuild));
+ else:
+ reporter.error("VirtualBox install package is not defined");
+
+ if not fRc:
+ reporter.error('Upload the installing into guest VM failed');
+ else:
+ if sTestBuild.endswith('.msi'):
+ sLogFile = 'C:/Temp/VBoxInstallLog.txt';
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 600 * 1000, 'C:\\Windows\\System32\\msiexec.exe',
+ ['msiexec', '/quiet', '/norestart', '/i',
+ 'C:\\Temp\\' + os.path.basename(sTestBuild),
+ '/lv', sLogFile],
+ False, True);
+ if not fRc:
+ reporter.error('Installing the VBox from msi installer failed');
+ else:
+ sLogFile = 'C:/Temp/Virtualbox/VBoxInstallLog.txt';
+ (fRc, _, _, _) = self.guestProcessExecute(oGuestSession, 'Installing VBox',
+ 600 * 1000, 'C:\\Temp\\' + os.path.basename(sTestBuild),
+ ['C:\\Temp\\' + os.path.basename(sTestBuild), '-vvvv',
+ '--silent', '--logging',
+ '--msiparams', 'REBOOT=ReallySuppress'],
+ False, True);
+ if not fRc:
+ reporter.error('Installing the VBox failed');
+ else:
+ (_, _, _, aBuf) = self.guestProcessExecute(oGuestSession, 'Check installation',
+ 240 * 1000, 'C:\\Windows\\System32\\cmd.exe',
+ ['c:\\Windows\\System32\\cmd.exe', '/c',
+ 'dir', 'C:\\Program Files\\Oracle\\VirtualBox\\*.*'],
+ True, True);
+ reporter.log('Content of VirtualBxox folder:');
+ reporter.log(str(aBuf));
+ asLogFiles = [sLogFile,];
+ self.downloadFiles(oGuestSession, asLogFiles, fIgnoreErrors = True);
+ reporter.testDone();
+ return fRc;
+
+ def convertDiskToPartitionPrefix(self, sDisk):
+ # Convert \\.\PhysicalDriveX into \\.\HarddiskXPartition
+ oMatch = re.match(r'^\\\\.\\PhysicalDrive(\d+)$', sDisk);
+ if oMatch is None:
+ return None;
+ return r'\\.\Harddisk' + oMatch.group(1) + 'Partition';
+
+class tdStorageRawDrive(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Autostart testcase.
+ """
+ ksOsLinux = 'tst-linux';
+ ksOsWindows = 'tst-win';
+ ksOsDarwin = 'tst-darwin';
+ ksOsSolaris = 'tst-solaris';
+ ksOsFreeBSD = 'tst-freebsd';
+ ksBootSectorPath = '6.1/storage/t-bootsector.bin';
+ kasHdds = ['6.1/storage/t-gpt.vdi', '6.1/storage/t-mbr.vdi'];
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asSkipVMs = [];
+ ## @todo r=bird: The --test-build-dirs option as primary way to get the installation files to test
+ ## is not an acceptable test practice as we don't know wtf you're testing. See defect for more.
+ self.asTestBuildDirs = [os.path.join(self.sScratchPath, 'bin'),];
+ self.sGuestAdditionsIso = None; #'D:/AlexD/TestBox/TestAdditionalFiles/VBoxGuestAdditions_6.1.2.iso';
+ oSet = vboxtestvms.TestVmSet(self.oTestVmManager, acCpus = [2], asVirtModes = ['hwvirt-np',], fIgnoreSkippedVm = True);
+ # pylint: disable=line-too-long
+ self.asTestVmClasses = {
+ 'win' : None, #tdStorageRawDriveOsWin(oSet, self, self.ksOsWindows, 'Windows7_64', \
+ #'6.0/windows7piglit/windows7piglit.vdi', eNic0Type = None, cMbRam = 2048, \
+ #cCpus = 2, fPae = True, sGuestAdditionsIso = self.getGuestAdditionsIso(),
+ #sBootSector = self.ksBootSectorPath),
+ 'linux' : tdStorageRawDriveOsLinux(oSet, self, self.ksOsLinux, 'Ubuntu_64', \
+ '6.0/ub1804piglit/ub1804piglit.vdi', eNic0Type = None, \
+ cMbRam = 2048, cCpus = 2, fPae = None, sGuestAdditionsIso = self.getGuestAdditionsIso(),
+ sBootSector = self.ksBootSectorPath),
+ 'solaris' : None, #'tdAutostartOsSolaris',
+ 'darwin' : None #'tdAutostartOsDarwin'
+ };
+ oSet.aoTestVms.extend([oTestVm for oTestVm in self.asTestVmClasses.values() if oTestVm is not None]);
+ sOs = self.getBuildOs();
+ if sOs in self.asTestVmClasses:
+ for oTestVM in oSet.aoTestVms:
+ if oTestVM is not None:
+ oTestVM.fSkip = oTestVM != self.asTestVmClasses[sOs];
+ # pylint: enable=line-too-long
+ self.oTestVmSet = oSet;
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAutostart Options:');
+ reporter.log(' --test-build-dirs <path1[,path2[,...]]>');
+ reporter.log(' The list of directories with VirtualBox distros. Overrides default path.');
+ reporter.log(' Default path is $TESTBOX_SCRATCH_PATH/bin.');
+ reporter.log(' --vbox-<os>-build <path>');
+ reporter.log(' The path to vbox build for the specified OS.');
+ reporter.log(' The OS can be one of "win", "linux", "solaris" and "darwin".');
+ reporter.log(' This option alse enables corresponding VM for testing.');
+ reporter.log(' (Default behaviour is testing only VM having host-like OS.)');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--test-build-dirs':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-build-dirs" takes a path argument');
+ self.asTestBuildDirs = asArgs[iArg].split(',');
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.asTestBuildDirs = self.asTestBuildDirs;
+ elif asArgs[iArg] in [ '--vbox-%s-build' % sKey for sKey in self.asTestVmClasses]:
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "%s" take a path argument' % (asArgs[iArg - 1],));
+ oMatch = re.match("--vbox-([^-]+)-build", asArgs[iArg - 1]);
+ if oMatch is not None:
+ sOs = oMatch.group(1);
+ oTestVm = self.asTestVmClasses.get(sOs);
+ if oTestVm is not None:
+ oTestVm.sTestBuild = asArgs[iArg];
+ oTestVm.fSkip = False;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ asRsrcs = self.kasHdds[:];
+ asRsrcs.extend([self.ksBootSectorPath,]);
+ asRsrcs.extend(vbox.TestDriver.getResourceSet(self));
+ return asRsrcs;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testAutostartOneVfg)
+
+ #
+ # Test execution helpers.
+ #
+ def testAutostartOneVfg(self, oVM, oTestVm):
+ fRc = True;
+ self.logVmInfo(oVM);
+
+ for sHdd in self.kasHdds:
+ reporter.testStart('%s with %s disk' % ( oTestVm.sVmName, sHdd))
+ fRc = oTestVm.reattachHdd(oVM, sHdd, self.kasHdds);
+ if fRc:
+ oSession = self.startVmByName(oTestVm.sVmName);
+ if oSession is not None:
+ (fRc, oGuestSession) = oTestVm.waitVmIsReady(oSession, True);
+ if fRc:
+ if fRc:
+ (fRc, oGuestSession) = oTestVm.installAdditions(oSession, oGuestSession, oVM);
+ if fRc:
+ fRc = oTestVm.installVirtualBox(oGuestSession);
+ if fRc:
+ (fRc, sRawDrive) = oTestVm.listHostDrives(oGuestSession, sHdd);
+ if fRc:
+ fRc = oTestVm.createDrives(oGuestSession, sHdd, sRawDrive);
+ if not fRc:
+ reporter.error('Create VMDK raw drives failed');
+ else:
+ reporter.error('List host drives failed');
+ else:
+ reporter.error('Installing VirtualBox in the guest failed');
+ else:
+ reporter.error('Creating Guest Additions failed');
+ else:
+ reporter.error('Waiting for start VM failed');
+ if oGuestSession is not None:
+ try: oTestVm.powerDownVM(oGuestSession);
+ except: pass;
+ try: self.terminateVmBySession(oSession);
+ except: pass;
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+ else:
+ reporter.error('Attaching %s to %s failed' % (sHdd, oTestVm.sVmName));
+ reporter.testDone();
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdStorageRawDrive().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py b/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py
new file mode 100755
index 00000000..d6904114
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py
@@ -0,0 +1,414 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdStorageSnapshotMerging1.py $
+
+"""
+VirtualBox Validation Kit - Storage snapshotting and merging testcase.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2013-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxwrappers;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+class tdStorageSnapshot(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Storage benchmark.
+ """
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asStorageCtrlsDef = ['AHCI'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ #self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.sRndData = os.urandom(100*1024*1024);
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageSnapshot1 Options:');
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormats)));
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = ['5.3/storage/mergeMedium/t-orig.vdi',
+ '5.3/storage/mergeMedium/t-fixed.vdi',
+ '5.3/storage/mergeMedium/t-resized.vdi'];
+ return self.asRsrcs;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+ def resizeMedium(self, oMedium, cbNewSize):
+ if oMedium.deviceType is not vboxcon.DeviceType_HardDisk:
+ return False;
+
+ if oMedium.type is not vboxcon.MediumType_Normal:
+ return False;
+
+ #currently only VDI can be resizable. Medium variant is not checked, because testcase creates disks itself
+ oMediumFormat = oMedium.mediumFormat;
+ if oMediumFormat.id != 'VDI':
+ return False;
+
+ cbCurrSize = oMedium.logicalSize;
+ # currently reduce is not supported
+ if cbNewSize < cbCurrSize:
+ return False;
+
+ try:
+ oProgressCom = oMedium.resize(cbNewSize);
+ except:
+ reporter.logXcpt('IMedium::resize failed on %s' % (oMedium.name));
+ return False;
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oVBox.oTstDrv,
+ 'Resize medium %s' % (oMedium.name));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+ return True;
+
+ def getMedium(self, oVM, sController):
+ oMediumAttachments = oVM.getMediumAttachmentsOfController(sController);
+
+ for oAttachment in oMediumAttachments:
+ oMedium = oAttachment.medium;
+ if oMedium.deviceType is not vboxcon.DeviceType_HardDisk:
+ continue;
+ if oMedium.type is not vboxcon.MediumType_Normal:
+ continue;
+ return oMedium;
+
+ return None;
+
+ def getSnapshotMedium(self, oSnapshot, sController):
+ oVM = oSnapshot.machine;
+ oMedium = self.getMedium(oVM, sController);
+
+ aoMediumChildren = self.oVBoxMgr.getArray(oMedium, 'children')
+ if aoMediumChildren is None or not aoMediumChildren:
+ return None;
+
+ for oChildMedium in aoMediumChildren:
+ for uSnapshotId in oChildMedium.getSnapshotIds(oVM.id):
+ if uSnapshotId == oVM.id:
+ return oChildMedium;
+
+ return None;
+
+ def openMedium(self, sHd, fImmutable = False):
+ """
+ Opens medium in readonly mode.
+ Returns Medium object on success and None on failure. Error information is logged.
+ """
+ sFullName = self.oVBox.oTstDrv.getFullResourceName(sHd);
+ try:
+ oHd = self.oVBox.findHardDisk(sFullName);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly, False);
+ elif self.fpApiVer >= 4.0:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly);
+ else:
+ oHd = self.oVBox.openHardDisk(sFullName, vboxcon.AccessMode_ReadOnly, False, "", False, "");
+
+ except:
+ reporter.errorXcpt('failed to open hd "%s"' % (sFullName));
+ return None;
+
+ try:
+ if fImmutable:
+ oHd.type = vboxcon.MediumType_Immutable;
+ else:
+ oHd.type = vboxcon.MediumType_Normal;
+
+ except:
+ if fImmutable:
+ reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd));
+ else:
+ reporter.errorXcpt('failed to set hd "%s" normal' % (sHd));
+
+ return None;
+
+ return oHd;
+
+ def cloneMedium(self, oSrcHd, oTgtHd):
+ """
+ Clones medium into target medium.
+ """
+ try:
+ oProgressCom = oSrcHd.cloneTo(oTgtHd, (vboxcon.MediumVariant_Standard, ), None);
+ except:
+ reporter.errorXcpt('failed to clone medium %s to %s' % (oSrcHd.name, oTgtHd.name));
+ return False;
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oVBox.oTstDrv,
+ 'clone base disk %s to %s' % (oSrcHd.name, oTgtHd.name));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+ return True;
+
+ def deleteVM(self, oVM):
+ try:
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone);
+ except:
+ reporter.logXcpt();
+
+ if self.fpApiVer >= 4.0:
+ try:
+ if self.fpApiVer >= 4.3:
+ oProgressCom = oVM.deleteConfig([]);
+ else:
+ oProgressCom = oVM.delete(None);
+ except:
+ reporter.logXcpt();
+ else:
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oVBox.oTstDrv,
+ 'Delete VM %s' % (oVM.name));
+ oProgress.wait(cMsTimeout = 15*60*1000); # 15 min
+ oProgress.logResult();
+ else:
+ try: oVM.deleteSettings();
+ except: reporter.logXcpt();
+
+ return None;
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1OneCfg(self, eStorageController, oDskFmt):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): #pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+
+ if sExt is None:
+ return False;
+
+ oOrigBaseHd = self.openMedium('5.3/storage/mergeMedium/t-orig.vdi');
+ if oOrigBaseHd is None:
+ return False;
+
+ #currently only VDI can be resizable. Medium variant is not checked, because testcase creates disks itself
+ fFmtDynamic = oDskFmt.id == 'VDI';
+ sOrigWithDiffHd = '5.3/storage/mergeMedium/t-fixed.vdi'
+ uOrigCrc = long(0x7a417cbb);
+
+ if fFmtDynamic:
+ sOrigWithDiffHd = '5.3/storage/mergeMedium/t-resized.vdi';
+ uOrigCrc = long(0xa8f5daa3);
+
+ oOrigWithDiffHd = self.openMedium(sOrigWithDiffHd);
+ if oOrigWithDiffHd is None:
+ return False;
+
+ oVM = self.createTestVM('testvm', 1, None);
+ if oVM is None:
+ return False;
+
+ sController = self.controllerTypeToName(eStorageController);
+
+ # Reconfigure the VM
+ oSession = self.openSession(oVM);
+ if oSession is None:
+ return False;
+ # Attach HD
+
+ fRc = True;
+ sFile = 't-base' + sExt;
+ sHddPath = os.path.join(self.oVBox.oTstDrv.sScratchPath, sFile);
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=oOrigBaseHd.logicalSize,
+ cMsTimeout = 15 * 60 * 1000); # 15 min
+ #if oSession.createBaseHd can't create disk because it exists, oHd will point to some stub object anyway
+ fRc = fRc and oHd is not None and (oHd.logicalSize == oOrigBaseHd.logicalSize);
+ fRc = fRc and self.cloneMedium(oOrigBaseHd, oHd);
+
+ fRc = fRc and oSession.ensureControllerAttached(sController);
+ fRc = fRc and oSession.setStorageControllerType(eStorageController, sController);
+ fRc = fRc and oSession.saveSettings();
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = 0, fImmutable=False, fForceResource=False)
+
+ if fRc:
+ oSession.takeSnapshot('Base snapshot');
+ oSnapshot = oSession.findSnapshot('Base snapshot');
+
+ if oSnapshot is not None:
+ oSnapshotMedium = self.getSnapshotMedium(oSnapshot, sController);
+ fRc = oSnapshotMedium is not None;
+
+ if fFmtDynamic:
+ fRc = fRc and self.resizeMedium(oSnapshotMedium, oOrigWithDiffHd.logicalSize);
+ fRc = fRc and self.cloneMedium(oOrigWithDiffHd, oSnapshotMedium);
+ fRc = fRc and oSession.deleteSnapshot(oSnapshot.id, cMsTimeout = 120 * 1000);
+
+ if fRc:
+ # disk for result test by checksum
+ sResFilePath = os.path.join(self.oVBox.oTstDrv.sScratchPath, 't_res.vmdk');
+ sResFilePathRaw = os.path.join(self.oVBox.oTstDrv.sScratchPath, 't_res-flat.vmdk');
+ oResHd = oSession.createBaseHd(sResFilePath, sFmt='VMDK', cb=oOrigWithDiffHd.logicalSize,
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, ),
+ cMsTimeout = 15 * 60 * 1000); # 15 min
+ fRc = oResHd is not None;
+ fRc = fRc and self.cloneMedium(oHd, oResHd);
+
+ uResCrc32 = long(0);
+ if fRc:
+ uResCrc32 = long(utils.calcCrc32OfFile(sResFilePathRaw));
+ if uResCrc32 == uOrigCrc:
+ reporter.log('Snapshot merged successfully. Crc32 is correct');
+ fRc = True;
+ else:
+ reporter.error('Snapshot merging failed. Crc32 is invalid');
+ fRc = False;
+
+ self.oVBox.deleteHdByMedium(oResHd);
+
+ if oSession is not None:
+ if oHd is not None:
+ oSession.detachHd(sController, iPort = 0, iDevice = 0);
+
+ oSession.saveSettings(fClose = True);
+ if oHd is not None:
+ self.oVBox.deleteHdByMedium(oHd);
+
+ self.deleteVM(oVM);
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1 thru the various configurations.
+ """
+ if not self.importVBoxApi():
+ return False;
+
+ sVmName = 'testvm';
+ reporter.testStart(sVmName);
+
+ aoDskFmts = self.oVBoxMgr.getArray(self.oVBox.systemProperties, 'mediumFormats')
+ if aoDskFmts is None or not aoDskFmts:
+ return False;
+
+ fRc = True;
+ for sStorageCtrl in self.asStorageCtrls:
+ reporter.testStart(sStorageCtrl);
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ else:
+ eStorageCtrl = None;
+
+ for oDskFmt in aoDskFmts:
+ if oDskFmt.id in self.asDiskFormats:
+ reporter.testStart('%s' % (oDskFmt.id));
+ fRc = self.test1OneCfg(eStorageCtrl, oDskFmt);
+ reporter.testDone();
+ if not fRc:
+ break;
+
+ reporter.testDone();
+ if not fRc:
+ break;
+
+ reporter.testDone();
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdStorageSnapshot().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py b/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py
new file mode 100755
index 00000000..55beb6fd
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py
@@ -0,0 +1,513 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Storage testcase using xfstests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Id: tdStorageStress1.py $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdStorageStress(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ Storage testcase.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asTestVMsDef = ['tst-debian'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.asTestsDef = ['xfstests'];
+ self.asTests = self.asTestsDef;
+ self.asGuestFs = ['xfs', 'ext4', 'btrfs'];
+ self.asGuestFsDef = self.asGuestFs;
+ self.asIscsiTargetsDef = ['aurora|iqn.2011-03.home.aurora:aurora.storagebench|1'];
+ self.asIscsiTargets = self.asIscsiTargetsDef;
+ self.asDirsDef = ['/run/media/alexander/OWCSSD/alexander', \
+ '/run/media/alexander/CrucialSSD/alexander', \
+ '/run/media/alexander/HardDisk/alexander', \
+ '/home/alexander'];
+ self.asDirs = self.asDirsDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageBenchmark1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormats)));
+ reporter.log(' --disk-dirs <path1[:path2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDirs)));
+ reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asIscsiTargets)));
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTests)));
+ reporter.log(' --guest-fs <fs1[:fs2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asGuestFs)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-dirs':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-dirs" takes a colon separated list of directories');
+ self.asDirs = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--iscsi-targets':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
+ self.asIscsiTargets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of disk formats');
+ self.asTests = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--guest-fs':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--guest-fs" takes a colon separated list of filesystem identifiers');
+ self.asGuestFs = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-debian' in self.asTestVMs:
+ self.asRsrcs.append('4.2/storage/debian.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../../VBoxValidationKitStorIo.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../../VBoxValidationKitStorIo.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKitStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuiteStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKitStorIo.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuiteStorIo.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/VirtualBox/VBoxValidationKitStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/VirtualBox/VBoxTestSuiteStorIo.iso';
+
+
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-debian' in self.asTestVMs:
+ oVM = self.createTestVM('tst-debian', 1, '4.2/storage/debian.vdi', sKind = 'Debian_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1RunTestProgs(self, oSession, oTxsSession, fRc, sTestName, sGuestFs):
+ """
+ Runs all the test programs on the test machine.
+ """
+ _ = oSession;
+
+ reporter.testStart(sTestName);
+
+ sMkfsCmd = 'mkfs.' + sGuestFs;
+
+ # Prepare test disks, just create filesystem without partition
+ reporter.testStart('Preparation');
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create FS 1', 60000, \
+ '/sbin/' + sMkfsCmd,
+ (sMkfsCmd, '/dev/sdb'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create FS 2', 60000, \
+ '/sbin/' + sMkfsCmd,
+ (sMkfsCmd, '/dev/sdc'));
+
+ # Create test and scratch directory
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create /mnt/test', 10000, \
+ '/bin/mkdir',
+ ('mkdir', '/mnt/test'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create /mnt/scratch', 10000, \
+ '/bin/mkdir',
+ ('mkdir', '/mnt/scratch'));
+
+ # Mount test and scratch directory.
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Mount /mnt/test', 10000, \
+ '/bin/mount',
+ ('mount', '/dev/sdb','/mnt/test'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Mount /mnt/scratch', 10000, \
+ '/bin/mount',
+ ('mount', '/dev/sdc','/mnt/scratch'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Copying xfstests', 10000, \
+ '/bin/cp',
+ ('cp', '-r','${CDROM}/${OS.ARCH}/xfstests', '/tmp'));
+
+ reporter.testDone();
+
+ # Run xfstests (this sh + cd crap is required because the cwd for the script must be in the root
+ # of the xfstests directory...)
+ reporter.testStart('xfstests');
+ if fRc and 'xfstests' in self.asTests:
+ fRc = self.txsRunTest(oTxsSession, 'xfstests', 3600000,
+ '/bin/sh',
+ ('sh', '-c', '(cd /tmp/xfstests && ./check -g auto)'),
+ ('TEST_DIR=/mnt/test', 'TEST_DEV=/dev/sdb', 'SCRATCH_MNT=/mnt/scratch', 'SCRATCH_DEV=/dev/sdc',
+ 'FSTYP=' + sGuestFs));
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testDone(not fRc);
+ return fRc;
+
+ # pylint: disable=too-many-arguments
+
+ def test1OneCfg(self, sVmName, eStorageController, sDiskFormat, sDiskPath1, sDiskPath2, \
+ sGuestFs, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ # Attach HD
+ fRc = oSession.ensureControllerAttached(self.controllerTypeToName(eStorageController));
+ fRc = fRc and oSession.setStorageControllerType(eStorageController, self.controllerTypeToName(eStorageController));
+
+ if sDiskFormat == "iSCSI":
+ listNames = [];
+ listValues = [];
+ listValues = sDiskPath1.split('|');
+ listNames.append('TargetAddress');
+ listNames.append('TargetName');
+ listNames.append('LUN');
+
+ if self.fpApiVer >= 5.0:
+ oHd = oSession.oVBox.createMedium(sDiskFormat, sDiskPath1, vboxcon.AccessMode_ReadWrite, \
+ vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = oSession.oVBox.createHardDisk(sDiskFormat, sDiskPath1);
+ oHd.type = vboxcon.MediumType_Normal;
+ oHd.setProperties(listNames, listValues);
+
+ # Attach it.
+ if fRc is True:
+ try:
+ if oSession.fpApiVer >= 4.0:
+ oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
+ 1, 0, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
+ 1, 0, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (self.controllerTypeToName(eStorageController), 1, 0, oHd.id, oSession.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sDiskPath1, oSession.sName));
+ else:
+ fRc = fRc and oSession.createAndAttachHd(sDiskPath1, sDiskFormat, self.controllerTypeToName(eStorageController),
+ cb = 10*1024*1024*1024, iPort = 1, fImmutable = False);
+ fRc = fRc and oSession.createAndAttachHd(sDiskPath2, sDiskFormat, self.controllerTypeToName(eStorageController),
+ cb = 10*1024*1024*1024, iPort = 2, fImmutable = False);
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = False, fNatForwardingForTxs = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ fRc = self.test1RunTestProgs(oSession, oTxsSession, fRc, 'stress testing', sGuestFs);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+
+ # Remove disk
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ try:
+ oSession.o.machine.detachDevice(self.controllerTypeToName(eStorageController), 1, 0);
+ oSession.o.machine.detachDevice(self.controllerTypeToName(eStorageController), 2, 0);
+
+ # Remove storage controller if it is not an IDE controller.
+ if eStorageController not in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
+ oSession.o.machine.removeStorageController(self.controllerTypeToName(eStorageController));
+
+ oSession.saveSettings();
+ oSession.oVBox.deleteHdByLocation(sDiskPath1);
+ oSession.oVBox.deleteHdByLocation(sDiskPath2);
+ oSession.saveSettings();
+ oSession.close();
+ oSession = None;
+ except:
+ reporter.errorXcpt('failed to detach/delete disks %s and %s from storage controller' % \
+ (sDiskPath1, sDiskPath2));
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1OneVM(self, sVmName):
+ """
+ Runs one VM thru the various configurations.
+ """
+ reporter.testStart(sVmName);
+ fRc = True;
+ for sStorageCtrl in self.asStorageCtrls:
+ reporter.testStart(sStorageCtrl);
+
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ else:
+ eStorageCtrl = None;
+
+ for sDiskFormat in self.asDiskFormats:
+ reporter.testStart('%s' % (sDiskFormat,));
+
+ asPaths = self.asDirs;
+
+ for sDir in asPaths:
+ reporter.testStart('%s' % (sDir,));
+
+ sPathDisk1 = sDir + "/disk1.disk";
+ sPathDisk2 = sDir + "/disk2.disk";
+
+ for sGuestFs in self.asGuestFs:
+ reporter.testStart('%s' % (sGuestFs,));
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus,));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ fRc = self.test1OneCfg(sVmName, eStorageCtrl, sDiskFormat, sPathDisk1, sPathDisk2, \
+ sGuestFs, cCpus, fHwVirt, fNestedPaging) and fRc and True;
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ # Loop thru the test VMs.
+ for sVM in self.asTestVMs:
+ # run test on the VM.
+ if not self.test1OneVM(sVM):
+ fRc = False;
+ else:
+ fRc = True;
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdStorageStress().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk b/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk
new file mode 100644
index 00000000..f01e9d25
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Teleportation.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsTeleportation
+ValidationKitTestsTeleportation_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsTeleportation_INST = $(INST_VALIDATIONKIT)testcases/cpu/
+ValidationKitTestsTeleportation_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdTeleportLocal1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsTeleportation_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py b/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py
new file mode 100755
index 00000000..ca739865
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py
@@ -0,0 +1,963 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdTeleportLocal1.py $
+
+"""
+VirtualBox Validation Kit - Local teleportation testdriver.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+
+
+class tdTeleportLocal1(vbox.TestDriver):
+ """
+ Local Teleportation Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+
+ self.asTestsDef = ['test1', 'test2'];
+ self.asTests = ['test1', 'test2'];
+ self.asSkipTests = [];
+ self.asTestVMsDef = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdTeleportLocal1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Run the specified tests.');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --skip-tests <test1[:test2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes hwvirt --cpu-counts 1');
+ reporter.log(' --test-vms tst-rhel5:tst-win2k3ent:tst-sol10');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ if asArgs[iArg]:
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ else:
+ self.asTestVMs = [];
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--skip-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes colon separated list');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ reporter.log('warning: The "--tests" value "%s" does not specify any of our tests.' % (s));
+ elif asArgs[iArg] == '--skip-tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-tests" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipTests:
+ if s not in self.asTestsDef:
+ reporter.log('warning: The "--skip-tests" value "%s" does not specify any of our tests.' % (s));
+ elif asArgs[iArg] == '--quick':
+ self.asVirtModes = ['hwvirt',];
+ self.acCpus = [1,];
+ #self.asTestVMs = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10',];
+ self.asTestVMs = ['tst-rhel5', ];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test VM list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ # Remove skipped tests from the test list.
+ for sTest in self.asSkipTests:
+ try: self.asTests.remove(sTest);
+ except: pass;
+
+ # If no test2, then no test VMs.
+ if 'test2' not in self.asTests:
+ self.asTestVMs = [];
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-rhel5' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5.vdi');
+ if 'tst-rhel5-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5-64.vdi');
+ if 'tst-sles11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11.vdi');
+ if 'tst-sles11-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11-64.vdi');
+ if 'tst-oel' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel.vdi');
+ if 'tst-oel-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel-64.vdi');
+ if 'tst-win2k3ent' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-acpi.vdi');
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-64.vdi');
+ if 'tst-win2k8' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k8.vdi');
+ if 'tst-sol10' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris10.vdi');
+ if 'tst-sol11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris11.vdi');
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ ## @todo actionConfig() and getResourceSet() are working on the same
+ # set of VMs as tdNetBenchmark1, creating common base class would be
+ # a good idea.
+
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is not None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the empty VMs we're going to use for the first tests.
+ #
+
+ if 'test1' in self.asTests:
+ #oVM = self.createTestVMs('tst-empty-hwvirt', 0, sKind = 'Other', fVirtEx = True);
+ #if oVM is None:
+ # return False;
+
+ oVM = self.createTestVMs('tst-empty-raw', 2, sKind = 'Other', fVirtEx = False);
+ if oVM is None:
+ return False;
+
+ #
+ # Configure the VMs we're going to use for the last test.
+ #
+
+ # Linux VMs
+ if 'tst-rhel5' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-rhel5', 1, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-rhel5-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-rhel5-64', 1, '3.0/tcp/rhel5-64.vdi', sKind = 'RedHat_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sles11', 1, '3.0/tcp/sles11.vdi', sKind = 'OpenSUSE', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sles11-64', 1, '3.0/tcp/sles11-64.vdi', sKind = 'OpenSUSE_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-oel', 1, '3.0/tcp/oel.vdi', sKind = 'Oracle', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-oel-64', 1, '3.0/tcp/oel-64.vdi', sKind = 'Oracle_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Windows VMs
+ if 'tst-win2k3ent' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k3ent', 1, '3.0/tcp/win2k3ent-acpi.vdi', sKind = 'Windows2003', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k3ent-64', 1, '3.0/tcp/win2k3ent-64.vdi', sKind = 'Windows2003_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k8' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k8', 1, '3.0/tcp/win2k8.vdi', sKind = 'Windows2008_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Solaris VMs
+ if 'tst-sol10' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sol10', 1, '3.0/tcp/solaris10.vdi', sKind = 'Solaris_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sol11' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sol11', 1, '3.0/tcp/os2009-11.vdi', sKind = 'Solaris_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = 'test1' not in self.asTests or self.test1();
+ if fRc: fRc = 'test2' not in self.asTests or self.test2();
+ return fRc;
+
+
+ #
+ # Test config helpers.
+ #
+
+ def createTestVMs(self, sName, iGroup, *tArgs, **dKeywordArgs):
+ """
+ Wrapper around vbox.createTestVM for creating two VMs, the source
+ (sName-1) and target (sName-2).
+
+ Returns the 2nd VM object on success, None on failure.
+ """
+ sName1 = sName + '-1';
+ oVM = self.createTestVM(sName1, iGroup * 2, *tArgs, **dKeywordArgs);
+ if oVM is not None:
+ sName2 = sName + '-2';
+ oVM = self.createTestVM(sName2, iGroup * 2 + 1, *tArgs, **dKeywordArgs);
+ return oVM;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test2Teleport(self, oVmSrc, oSessionSrc, oVmDst):
+ """
+ Attempts a teleportation.
+
+ Returns the input parameters for the next test2Teleport call (source
+ and destiation are switched around). The input session is closed and
+ removed from the task list, while the return session is in the list.
+ """
+
+ # Enable the teleporter of the VM.
+ oSession = self.openSession(oVmDst);
+ fRc = oSession is not None
+ if fRc:
+ fRc = oSession.enableTeleporter();
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ if fRc:
+ # Start the destination VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+
+ # Do the teleportation.
+ try:
+ uDstPort = oVmDst.teleporterPort;
+ except:
+ reporter.logXcpt();
+ uDstPort = 6502;
+ oProgressSrc = oSessionSrc.teleport('localhost', uDstPort, 'password', 1024);
+ if oProgressSrc is not None:
+ oProgressSrc.wait();
+ if oProgressSrc.isSuccess():
+ oProgressDst.wait();
+ if oProgressSrc.isSuccess() and oProgressDst.isSuccess():
+
+ # Terminate the source VM.
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+
+ # Return with the source and destination swapped.
+ return oVmDst, oSessionDst, oVmSrc;
+
+ # Failure / bail out.
+ oProgressSrc.logResult();
+ oProgressDst.logResult();
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ return oVmSrc, oSessionSrc, oVmDst;
+
+ def test2OneCfg(self, sVmBaseName, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Reconfigure the source VM.
+ oVmSrc = self.getVmByName(sVmBaseName + '-1');
+ fRc = True;
+ oSession = self.openSession(oVmSrc);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupTeleporter(False, uPort=6501, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Reconfigure the destination VM.
+ oVmDst = self.getVmByName(sVmBaseName + '-2');
+ oSession = self.openSession(oVmDst);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupTeleporter(True, uPort=6502, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Simple test.
+ if fRc is True:
+ self.logVmInfo(oVmSrc);
+ self.logVmInfo(oVmDst);
+
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ # Simple back and forth to test the ice...
+ cTeleportations = 0;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+ if reporter.testErrorCount() == 0:
+ cTeleportations += 1;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+
+ # Teleport back and forth for a while.
+ msStart = base.timestampMilli();
+ while reporter.testErrorCount() == 0:
+ cTeleportations += 1;
+ if oSessionSrc.txsTryConnectViaTcp(2500, 'localhost') is True:
+ break;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > 5*60000:
+ reporter.testFailure('TXS did not show up after %u min of teleporting (%u)...' \
+ % (cMsElapsed / 60000.0, cTeleportations));
+ break;
+
+ # Clean up the source VM.
+ self.terminateVmBySession(oSessionSrc)
+ return None;
+
+ def test2OneVM(self, sVmBaseName, asSupVirtModes = None, rSupCpus = range(1, 256)):
+ """
+ Runs one VM (a pair really) thru the various configurations.
+ """
+ if asSupVirtModes is None:
+ asSupVirtModes = self.asVirtModes;
+
+ reporter.testStart(sVmBaseName);
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ if cCpus not in rSupCpus:
+ continue;
+ if sVirtMode not in asSupVirtModes:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ self.test2OneCfg(sVmBaseName, cCpus, fHwVirt, fNestedPaging);
+
+ reporter.testDone();
+ reporter.testDone();
+ return reporter.testDone()[1] == 0;
+
+ def test2(self):
+ """
+ Executes test #2.
+ """
+
+ # Loop thru the test VMs.
+ fRc = True;
+ for sVM in self.asTestVMs:
+ # figure args.
+ asSupVirtModes = None;
+ if sVM in ('tst-sol11', 'tst-sol10'): # 64-bit only
+ asSupVirtModes = ['hwvirt', 'hwvirt-np',];
+
+ # run test on the VM.
+ if not self.test2OneVM(sVM, asSupVirtModes):
+ fRc = False;
+
+ return fRc;
+ #
+ # Test #1
+ #
+
+ def test1ResetVmConfig(self, oVM, fTeleporterEnabled = False):
+ """
+ Resets the teleportation config for the specified VM.
+ Returns True on success, False on failure.
+ """
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.setupTeleporter(fTeleporterEnabled, uPort=6502, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ if not oSession.close(): fRc = False;
+ oSession = None;
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1Sub7(self, oVmSrc, oVmDst):
+ """
+ Test the password check.
+ """
+ reporter.testStart('Bad password');
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ tsPasswords = ('password-bad', 'passwor', 'pass', 'p', '', 'Password', );
+ for sPassword in tsPasswords:
+ reporter.testStart(sPassword);
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, sPassword);
+ if oProgressSrc:
+ oProgressSrc.wait();
+ reporter.log('src: %s' % oProgressSrc.stringifyResult());
+ if oProgressSrc.isSuccess():
+ reporter.testFailure('IConsole::teleport succeeded with bad password "%s"' % sPassword);
+ elif oProgressSrc.getErrInfoResultCode() != vbox.ComError.E_FAIL:
+ reporter.testFailure('IConsole::teleport returns %s instead of E_FAIL' \
+ % (vbox.ComError.toString(oProgressSrc.getErrInfoResultCode()),));
+ elif oProgressSrc.getErrInfoText() != 'Invalid password':
+ reporter.testFailure('IConsole::teleport returns "%s" instead of "Invalid password"' \
+ % (oProgressSrc.getErrInfoText(),));
+ elif oProgressDst.isCompleted():
+ reporter.testFailure('Destination completed unexpectedly after bad password "%s"' \
+ % sPassword);
+ else:
+ reporter.testFailure('IConsole::teleport failed with password "%s"' % sPassword);
+ if reporter.testDone()[1] != 0:
+ break;
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub6(self, oVmSrc, oVmDst):
+ """
+ Misconfigure the target VM and check that teleportation fails with the
+ same status and message on both ends.
+ xTracker: #4813
+ """
+ reporter.testStart('Misconfiguration & error message');
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Give the source a bit more RAM.
+ oSession = self.openSession(oVmSrc);
+ if oSession is not None:
+ try: cbMB = oVmSrc.memorySize + 4;
+ except: cbMB = 1; fRc = False;
+ fRc = oSession.setRamSize(cbMB);
+ if not oSession.saveSettings(): fRc = False;
+ if not oSession.close(): fRc = False;
+ oSession = None;
+ else:
+ fRc = False;
+ if fRc:
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ # Try teleport.
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, 'password');
+ if oProgressSrc:
+ oProgressSrc.wait();
+ oProgressDst.wait();
+
+ reporter.log('src: %s' % oProgressSrc.stringifyResult());
+ reporter.log('dst: %s' % oProgressDst.stringifyResult());
+
+ # Make sure it failed.
+ if oProgressSrc.isSuccess() and oProgressDst.isSuccess():
+ reporter.testFailure('The teleporation did not fail as expected');
+
+ # Compare the results.
+ if oProgressSrc.getResult() != oProgressDst.getResult():
+ reporter.testFailure('Result differs - src=%s dst=%s' \
+ % (vbox.ComError.toString(oProgressSrc.getResult()),\
+ vbox.ComError.toString(oProgressDst.getResult())));
+ elif oProgressSrc.getErrInfoResultCode() != oProgressDst.getErrInfoResultCode():
+ reporter.testFailure('ErrorInfo::resultCode differs - src=%s dst=%s' \
+ % (vbox.ComError.toString(oProgressSrc.getErrInfoResultCode()),\
+ vbox.ComError.toString(oProgressDst.getErrInfoResultCode())));
+ elif oProgressSrc.getErrInfoText() != oProgressDst.getErrInfoText():
+ reporter.testFailure('ErrorInfo::text differs - src="%s" dst="%s"' \
+ % (oProgressSrc.getErrInfoText(), oProgressDst.getErrInfoText()));
+
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False)
+ self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True);
+ else:
+ reporter.testFailure('reconfig #2 failed');
+ else:
+ reporter.testFailure('reconfig #1 failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub5(self, oVmSrc, oVmDst):
+ """
+ Test that basic teleporting works.
+ xTracker: #4749
+ """
+ reporter.testStart('Simple teleportation');
+ for cSecsX2 in range(0, 10):
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ self.sleep(cSecsX2 / 2);
+ # Try teleport.
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, 'password');
+ if oProgressSrc:
+ oProgressSrc.wait();
+ oProgressDst.wait();
+
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub4(self, oVM):
+ """
+ Test that we can start and cancel a teleportation target.
+ (No source VM trying to connect here.)
+ xTracker: #4965
+ """
+ reporter.testStart('openRemoteSession cancel');
+ for cSecsX2 in range(0, 10):
+ if self.test1ResetVmConfig(oVM, fTeleporterEnabled = True):
+ oSession, oProgress = self.startVmEx(oVM, fWait = False);
+ if oSession is not None:
+ self.sleep(cSecsX2 / 2);
+ oProgress.cancel();
+ oProgress.wait();
+ self.terminateVmBySession(oSession, oProgress);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub3(self, oVM):
+ """
+ Test that starting a teleportation target VM will fail if we give it
+ a bad address to bind to.
+ """
+ reporter.testStart('bad IMachine::teleporterAddress');
+
+ # re-configure it with a bad bind-to address.
+ fRc = False;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.setupTeleporter(True, uPort=6502, sAddress='no.such.hostname.should.ever.exist.duh');
+ if not oSession.saveSettings(fClose=True): fRc = False;
+ oSession = None;
+ if fRc:
+ # Try start it.
+ oSession, oProgress = self.startVmEx(oVM, fWait = False);
+ if oSession is not None:
+ oProgress.wait();
+ ## TODO: exact error code and look for the IPRT right string.
+ if not oProgress.isCompleted() or oProgress.getResult() >= 0:
+ reporter.testFailure('%s' % (oProgress.stringifyResult(),));
+ self.terminateVmBySession(oSession, oProgress);
+
+ # put back the old teleporter setup.
+ self.test1ResetVmConfig(oVM, fTeleporterEnabled = True);
+ else:
+ reporter.testFailure('reconfig #1 failed');
+ return reporter.testDone()[1] == 0;
+
+ # test1Sub2 - start
+
+ def test1Sub2SetEnabled(self, oSession, fEnabled):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterEnabled = fEnabled;
+ except:
+ reporter.testFailureXcpt('machine.teleporterEnabled=%s' % (fEnabled,));
+ return False;
+ try:
+ fNew = oSession.o.machine.teleporterEnabled;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if fNew != fEnabled:
+ reporter.testFailure('machine.teleporterEnabled=%s but afterwards it is actually %s' % (fEnabled, fNew));
+ return False;
+ return True;
+
+ def test1Sub2SetPassword(self, oSession, sPassword):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterPassword = sPassword;
+ except:
+ reporter.testFailureXcpt('machine.teleporterPassword=%s' % (sPassword,));
+ return False;
+ try:
+ sNew = oSession.o.machine.teleporterPassword;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if sNew != sPassword:
+ reporter.testFailure('machine.teleporterPassword="%s" but afterwards it is actually "%s"' % (sPassword, sNew));
+ return False;
+ return True;
+
+ def test1Sub2SetPort(self, oSession, uPort, fInvalid = False):
+ """ This can fail, thus fInvalid."""
+ if not fInvalid:
+ uOld = uPort;
+ else:
+ try: uOld = oSession.o.machine.teleporterPort;
+ except: return reporter.testFailureXcpt();
+
+ try:
+ oSession.o.machine.teleporterPort = uPort;
+ except Exception as oXcpt:
+ if not fInvalid or vbox.ComError.notEqual(oXcpt, vbox.ComError.E_INVALIDARG):
+ return reporter.testFailureXcpt('machine.teleporterPort=%u' % (uPort,));
+ else:
+ if fInvalid:
+ return reporter.testFailureXcpt('machine.teleporterPort=%u succeeded unexpectedly' % (uPort,));
+
+ try: uNew = oSession.o.machine.teleporterPort;
+ except: return reporter.testFailureXcpt();
+ if uNew != uOld:
+ if not fInvalid:
+ reporter.testFailure('machine.teleporterPort=%u but afterwards it is actually %u' % (uPort, uNew));
+ else:
+ reporter.testFailure('machine.teleporterPort is %u after failure, expected %u' % (uNew, uOld));
+ return False;
+ return True;
+
+ def test1Sub2SetAddress(self, oSession, sAddress):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterAddress = sAddress;
+ except:
+ reporter.testFailureXcpt('machine.teleporterAddress=%s' % (sAddress,));
+ return False;
+ try:
+ sNew = oSession.o.machine.teleporterAddress;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if sNew != sAddress:
+ reporter.testFailure('machine.teleporterAddress="%s" but afterwards it is actually "%s"' % (sAddress, sNew));
+ return False;
+ return True;
+
+ def test1Sub2(self, oVM):
+ """
+ Test the attributes, making sure that we get exceptions on bad values.
+ """
+ reporter.testStart('IMachine::teleport*');
+
+ # Save the original teleporter attributes for the discard test.
+ try:
+ sOrgAddress = oVM.teleporterAddress;
+ uOrgPort = oVM.teleporterPort;
+ sOrgPassword = oVM.teleporterPassword;
+ fOrgEnabled = oVM.teleporterEnabled;
+ except:
+ reporter.testFailureXcpt();
+ else:
+ # Open a session and start messing with the properties.
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ # Anything goes for the address.
+ reporter.testStart('teleporterAddress');
+ self.test1Sub2SetAddress(oSession, '');
+ self.test1Sub2SetAddress(oSession, '1');
+ self.test1Sub2SetAddress(oSession, 'Anything goes! ^&$@!$%^');
+ reporter.testDone();
+
+ # The port is restricted to {0..65535}.
+ reporter.testStart('teleporterPort');
+ for uPort in range(0, 1000) + range(16000, 17000) + range(32000, 33000) + range(65000, 65536):
+ if not self.test1Sub2SetPort(oSession, uPort):
+ break;
+ self.processPendingEvents();
+ reporter.testDone();
+
+ reporter.testStart('teleporterPort negative');
+ self.test1Sub2SetPort(oSession, 65536, True);
+ self.test1Sub2SetPort(oSession, 999999, True);
+ reporter.testDone();
+
+ # Anything goes for the password.
+ reporter.testStart('teleporterPassword');
+ self.test1Sub2SetPassword(oSession, 'password');
+ self.test1Sub2SetPassword(oSession, '');
+ self.test1Sub2SetPassword(oSession, '1');
+ self.test1Sub2SetPassword(oSession, 'Anything goes! ^&$@!$%^');
+ reporter.testDone();
+
+ # Just test that it works.
+ reporter.testStart('teleporterEnabled');
+ self.test1Sub2SetEnabled(oSession, True);
+ self.test1Sub2SetEnabled(oSession, True);
+ self.test1Sub2SetEnabled(oSession, False);
+ self.test1Sub2SetEnabled(oSession, False);
+ reporter.testDone();
+
+ # Finally, discard the changes, close the session and check
+ # that we're back to the originals.
+ if not oSession.discardSettings(True):
+ reporter.testFailure('Failed to discard settings & close the session')
+ else:
+ reporter.testFailure('Failed to open VM session')
+ try:
+ if oVM.teleporterAddress != sOrgAddress: reporter.testFailure('Rollback failed for teleporterAddress');
+ if oVM.teleporterPort != uOrgPort: reporter.testFailure('Rollback failed for teleporterPort');
+ if oVM.teleporterPassword != sOrgPassword: reporter.testFailure('Rollback failed for teleporterPassword');
+ if oVM.teleporterEnabled != fOrgEnabled: reporter.testFailure('Rollback failed for teleporterEnabled');
+ except:
+ reporter.testFailureXcpt();
+ return reporter.testDone()[1] != 0;
+
+ # test1Sub1 - start
+
+ def test1Sub1DoTeleport(self, oSession, sHostname, uPort, sPassword, cMsMaxDowntime, hrcExpected, sTestName):
+ """ Do a bad IConsole::teleport call and check the result."""
+ reporter.testStart(sTestName);
+ fRc = False;
+ try:
+ oProgress = oSession.o.console.teleport(sHostname, uPort, sPassword, cMsMaxDowntime);
+ except vbox.ComException as oXcpt:
+ if vbox.ComError.equal(oXcpt, hrcExpected):
+ fRc = True;
+ else:
+ reporter.testFailure('hresult %s, expected %s' \
+ % (vbox.ComError.toString(oXcpt.hresult),
+ vbox.ComError.toString(hrcExpected)));
+ except Exception as oXcpt:
+ reporter.testFailure('Unexpected exception %s' % (oXcpt));
+ else:
+ reporter.testFailure('Unpexected success');
+ oProgress.cancel();
+ oProgress.wait();
+ reporter.testDone();
+ return fRc;
+
+ def test1Sub1(self, oVM):
+ """ Test simple IConsole::teleport() failure paths. """
+ reporter.testStart('IConsole::teleport');
+ oSession = self.startVm(oVM);
+ if oSession:
+ self.test1Sub1DoTeleport(oSession, 'localhost', 65536, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'Bad port value 65536');
+ self.test1Sub1DoTeleport(oSession, 'localhost', 0, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'Bad port value 0');
+ self.test1Sub1DoTeleport(oSession, 'localhost', 5000, 'password', 0,
+ vbox.ComError.E_INVALIDARG, 'Bad max downtime');
+ self.test1Sub1DoTeleport(oSession, '', 5000, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'No hostname');
+ self.test1Sub1DoTeleport(oSession, 'no.such.hostname.should.ever.exist.duh', 5000, 'password', 0,
+ vbox.ComError.E_INVALIDARG, 'Non-existing host');
+
+ self.terminateVmBySession(oSession)
+ else:
+ reporter.testFailure('startVm');
+ return reporter.testDone()[1] == 0;
+
+
+ def test1(self):
+ """
+ Executes test #1 - Negative API testing.
+
+ ASSUMES that the VMs are
+ """
+ reporter.testStart('Test 1');
+
+ # Get the VMs.
+ #oVmHwVirt1 = self.getVmByName('tst-empty-hwvirt-1');
+ #oVmHwVirt2 = self.getVmByName('tst-empty-hwvirt-2');
+ oVmRaw1 = self.getVmByName('tst-empty-raw-1');
+ oVmRaw2 = self.getVmByName('tst-empty-raw-2');
+
+ # Reset their teleportation related configuration.
+ fRc = True;
+ #for oVM in (oVmHwVirt1, oVmHwVirt2, oVmRaw1, oVmRaw2):
+ for oVM in (oVmRaw1, oVmRaw2):
+ if not self.test1ResetVmConfig(oVM): fRc = False;
+
+ # Do the testing (don't both with fRc on the subtests).
+ if fRc:
+ self.test1Sub1(oVmRaw1);
+ self.test1Sub2(oVmRaw2);
+ self.test1Sub3(oVmRaw2);
+ self.test1Sub4(oVmRaw2);
+ self.processPendingEvents();
+ self.test1Sub5(oVmRaw1, oVmRaw2);
+ self.test1Sub6(oVmRaw1, oVmRaw2);
+ self.test1Sub7(oVmRaw1, oVmRaw2);
+ else:
+ reporter.testFailure('Failed to reset the VM configs')
+ return reporter.testDone()[1] == 0;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdTeleportLocal1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/unittests/Makefile.kmk b/src/VBox/ValidationKit/tests/unittests/Makefile.kmk
new file mode 100644
index 00000000..1f3c23a9
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/unittests/Makefile.kmk
@@ -0,0 +1,51 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Unit Tests.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsUnitTests
+ValidationKitTestsUnitTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsUnitTests_INST = $(INST_VALIDATIONKIT)tests/unittests/
+ValidationKitTestsUnitTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdUnitTest1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsUnitTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py b/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py
new file mode 100755
index 00000000..c308d372
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py
@@ -0,0 +1,1282 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdUnitTest1.py $
+
+"""
+VirtualBox Validation Kit - Unit Tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 157454 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import re
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdUnitTest1(vbox.TestDriver):
+ """
+ Unit Tests.
+ """
+
+ ## The temporary exclude list.
+ ## @note This shall be empty before we release 4.3!
+ kdTestCasesBuggyPerOs = {
+ 'darwin': {
+ 'testcase/tstX86-1': '', # 'FSTP M32R, ST0' fails; no idea why.
+ },
+ 'linux': {
+ 'testcase/tstRTFileAio': '', # See xTracker #8035.
+ },
+ 'linux.amd64': {
+ 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
+ # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
+ },
+ 'solaris': {
+ 'testcase/tstIntNet-1': '', # Fails opening rge0, probably a generic issue figuring which nic to use.
+ 'testcase/tstIprtList': '', # Crashes in the multithreaded test, I think.
+ 'testcase/tstRTCritSect': '', # Fairness/whatever issue here.
+ 'testcase/tstRTR0MemUserKernelDriver': '', # Failes when kernel to kernel buffers.
+ 'testcase/tstRTSemRW': '', # line 338: RTSemRWReleaseRead(hSemRW): got VERR_ACCESS_DENIED
+ 'testcase/tstRTStrAlloc': '', # VERR_NO_STR_MEMORY!
+ 'testcase/tstRTFileQuerySize-1': '', # VERR_DEV_IO_ERROR on /dev/null!
+ 'testcase/tstLow' : '', # VERR_NOT_SUPPORTED - allocating kernel memory with physical backing
+ # below 4GB (RTR0MemObjAllocLow) for running code (fExecutable=true)
+ # isn't implemented.
+ 'testcase/tstContiguous' : '', # VERR_NOT_SUPPORTED - allocating kernel memory with contiguous physical
+ # backing below 4GB (RTR0MemObjAllocCont) for running code
+ # (fExecutable=true) isn't implemented.
+ 'tstPDMQueue' : '' # VERR_NOT_SUPPORTED - running without the support driver (vboxdrv) isn't
+ # supported on Solaris (VMCREATE_F_DRIVERLESS/SUPR3INIT_F_DRIVERLESS).
+ },
+ 'solaris.amd64': {
+ 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
+ # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
+ },
+ 'win': {
+ 'testcase/tstFile': '', # ??
+ 'testcase/tstIntNet-1': '', # possibly same issue as solaris.
+ 'testcase/tstMouseImpl': '', # STATUS_ACCESS_VIOLATION
+ 'testcase/tstRTR0ThreadPreemptionDriver': '', # ??
+ 'testcase/tstRTPath': '<4.3.51r89894',
+ 'testcase/tstRTPipe': '', # ??
+ 'testcase/tstRTR0MemUserKernelDriver': '', # ??
+ 'testcase/tstRTR0SemMutexDriver': '', # ??
+ 'testcase/tstRTStrAlloc': '', # ??
+ 'testcase/tstRTStrFormat': '', # ??
+ 'testcase/tstRTSystemQueryOsInfo': '', # ??
+ 'testcase/tstRTTemp': '', # ??
+ 'testcase/tstRTTime': '', # ??
+ 'testcase/tstTime-2': '', # Total time differs too much! ... delta=-10859859
+ 'testcase/tstTime-4': '', # Needs to be converted to DLL; ditto for tstTime-2.
+ 'testcase/tstUtf8': '', # ??
+ 'testcase/tstVMMR0CallHost-2': '', # STATUS_STACK_OVERFLOW
+ 'testcase/tstX86-1': '', # Fails on win.x86.
+ 'tscpasswd': '', # ??
+ 'tstVMREQ': '', # ?? Same as darwin.x86?
+ },
+ 'win.x86': {
+ 'testcase/tstRTR0TimerDriver': '', # See xTracker #8041.
+ }
+ };
+
+ kdTestCasesBuggy = {
+ 'testcase/tstGuestPropSvc': '', # GET_NOTIFICATION fails on testboxlin5.de.oracle.com and others.
+ 'testcase/tstRTProcCreateEx': '', # Seen failing on wei01-b6ka-9.de.oracle.com.
+ 'testcase/tstTimer': '', # Sometimes fails on linux, not important atm.
+ 'testcase/tstGIP-2': '', # 2015-09-10: Fails regularly. E.g. TestSetID 2744205 (testboxsh2),
+ # 2743961 (wei01-b6kc-6). The responsible engineer should reenable
+ # it once it has been fixed.
+ };
+
+ ## The permanent exclude list.
+ # @note Stripped of extensions!
+ kdTestCasesBlackList = {
+ 'testcase/tstClipboardX11Smoke': '', # (Old naming, deprecated) Needs X, not available on all test boxes.
+ 'testcase/tstClipboardGH-X11Smoke': '', # (New name) Ditto.
+ 'testcase/tstClipboardMockHGCM': '', # Ditto.
+ 'tstClipboardQt': '', # Is interactive and needs Qt, needed for Qt clipboard bugfixing.
+ 'testcase/tstClipboardQt': '', # In case it moves here.
+ 'tstDragAndDropQt': '', # Is interactive and needs Qt, needed for Qt drag'n drop bugfixing.
+ 'testcase/tstDragAndDropQt': '', # In case it moves here.
+ 'testcase/tstFileLock': '',
+ 'testcase/tstDisasm-2': '', # without parameters it will disassembler 1GB starting from 0
+ 'testcase/tstFileAppendWin-1': '',
+ 'testcase/tstDir': '', # useless without parameters
+ 'testcase/tstDir-2': '', # useless without parameters
+ 'testcase/tstGlobalConfig': '',
+ 'testcase/tstHostHardwareLinux': '', # must be killed with CTRL-C
+ 'testcase/tstHttp': '', # Talks to outside servers.
+ 'testcase/tstRTHttp': '', # parameters required
+ 'testcase/tstLdr-2': '', # parameters required
+ 'testcase/tstLdr-3': '', # parameters required
+ 'testcase/tstLdr': '', # parameters required
+ 'testcase/tstLdrLoad': '', # parameters required
+ 'testcase/tstMove': '', # parameters required
+ 'testcase/tstRTR0Timer': '', # loads 'tstRTR0Timer.r0'
+ 'testcase/tstRTR0ThreadDriver': '', # loads 'tstRTR0Thread.r0'
+ 'testcase/tstRunTestcases': '', # that's a script like this one
+ 'testcase/tstRTReqPool': '', # fails sometimes, testcase buggy
+ 'testcase/tstRTS3': '', # parameters required
+ 'testcase/tstSDL': '', # graphics test
+ 'testcase/tstSupLoadModule': '', # Needs parameters and vboxdrv access. Covered elsewhere.
+ 'testcase/tstSeamlessX11': '', # graphics test
+ 'testcase/tstTime-3': '', # parameters required
+ 'testcase/tstVBoxControl': '', # works only inside a guest
+ 'testcase/tstVDCopy': '', # parameters required
+ 'testcase/tstVDFill': '', # parameters required
+ 'tstAnimate': '', # parameters required
+ 'testcase/tstAPI': '', # user interaction required
+ 'tstCollector': '', # takes forever
+ 'testcase/tstHeadless': '', # parameters required
+ 'tstHeadless': '', # parameters required
+ 'tstMicroRC': '', # required by tstMicro
+ 'tstVBoxDbg': '', # interactive test
+ 'testcase/tstTestServMgr': '', # some strange xpcom18a4 test, does not work
+ 'tstTestServMgr': '', # some strange xpcom18a4 test, does not work
+ 'tstPDMAsyncCompletion': '', # parameters required
+ 'testcase/tstXptDump': '', # parameters required
+ 'tstXptDump': '', # parameters required
+ 'testcase/tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
+ 'tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
+ 'testcase/tstSimpleTypeLib': '', # parameters required
+ 'tstSimpleTypeLib': '', # parameters required
+ 'testcase/tstTestAtoms': '', # additional test file (words.txt) required
+ 'tstTestAtoms': '', # additional test file (words.txt) required
+ 'testcase/tstXptLink': '', # parameters required
+ 'tstXptLink': '', # parameters required
+ 'tstXPCOMCGlue': '', # user interaction required
+ 'testcase/tstXPCOMCGlue': '', # user interaction required
+ 'testcase/tstCAPIGlue': '', # user interaction required
+ 'testcase/tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
+ 'tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
+ 'testcase/tstRTFilesystem': '', # parameters required
+ 'testcase/tstRTDvm': '', # parameters required
+ 'tstSSLCertDownloads': '', # Obsolete.
+ # later
+ 'testcase/tstIntNetR0': '', # RTSPINLOCK_FLAGS_INTERRUPT_SAFE == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
+ # slow stuff
+ 'testcase/tstAvl': '', # SLOW!
+ 'testcase/tstRTAvl': '', # SLOW! (new name)
+ 'testcase/tstVD': '', # 8GB fixed-sized vmdk
+ # failed or hang
+ 'testcase/tstCryptoPkcs7Verify': '', # hang
+ 'tstOVF': '', # hang (only ancient version, now in new place)
+ 'testcase/tstRTLockValidator': '', # Lock validation is not enabled for critical sections
+ 'testcase/tstGuestControlSvc': '', # failed: line 288: testHost(&svcTable):
+ # expected VINF_SUCCESS, got VERR_NOT_FOUND
+ 'testcase/tstRTMemEf': '', # failed w/o error message
+ 'testcase/tstSupSem': '', # failed: SRE Timeout Accuracy (ms) : FAILED (1 errors)
+ 'testcase/tstCryptoPkcs7Sign': '', # failed: 29330:
+ # error:02001002:lib(2):func(1):reason(2):NA:0:fopen('server.pem': '','r')
+ 'testcase/tstCompressionBenchmark': '', # failed: error: RTZipBlockCompress failed
+ # for 'RTZipBlock/LZJB' (#4): VERR_NOT_SUPPORTED
+ 'tstPDMAsyncCompletionStress': '', # VERR_INVALID_PARAMETER (cbSize = 0)
+ 'tstMicro': '', # doesn't work on solaris, fix later if we care.
+ 'tstVMM-HwAccm': '', # failed: Only checked AMD-V on linux
+ 'tstVMM-HM': '', # failed: Only checked AMD-V on linux
+ 'tstVMMFork': '', # failed: xtracker 6171
+ 'tstTestFactory': '', # some strange xpcom18a4 test, does not work
+ 'testcase/tstRTSemXRoads': '', # sporadically failed: Traffic - 8 threads per direction, 10 sec :
+ # FAILED (8 errors)
+ 'tstVBoxAPILinux': '', # creates VirtualBox directories for root user because of sudo
+ # (should be in vbox)
+ 'testcase/tstVMStructDTrace': '', # This is a D-script generator.
+ 'tstVMStructRC': '', # This is a C-code generator.
+ 'tstDeviceStructSizeRC': '', # This is a C-code generator.
+ 'testcase/tstTSC': '', # Doesn't test anything and might fail with HT or/and too many cores.
+ 'testcase/tstOpenUSBDev': '', # Not a useful testcase.
+ 'testcase/tstX86-1': '', # Really more guest side.
+ 'testcase/tstX86-FpuSaveRestore': '', # Experiments, could be useful for the guest not the host.
+ 'tstAsmStructsRC': '', # Testcase run during build time (fails to find libstdc++.so.6 on some
+ # Solaris testboxes).
+ };
+
+ # Suffix exclude list.
+ kasSuffixBlackList = [
+ '.r0',
+ '.gc',
+ '.debug',
+ '.rel',
+ '.sys',
+ '.ko',
+ '.o',
+ '.obj',
+ '.lib',
+ '.a',
+ '.so',
+ '.dll',
+ '.dylib',
+ '.tmp',
+ '.log',
+ '.py',
+ '.pyc',
+ '.pyo',
+ '.pdb',
+ '.dSYM',
+ '.sym',
+ '.template',
+ '.expected',
+ '.expect',
+ ];
+
+ # White list, which contains tests considered to be safe to execute,
+ # even on remote targets (guests).
+ #
+ # When --only-whitelist is specified, this is the only list being checked for.
+ kdTestCasesWhiteList = {
+ 'testcase/tstFile': '',
+ 'testcase/tstFileLock': '',
+ 'testcase/tstClipboardMockHGCM': '', # Requires X on Linux OSes. Execute on remote targets only (guests).
+ 'testcase/tstRTLocalIpc': '',
+ 'testcase/tstRTPathQueryInfo': '',
+ 'testcase/tstRTPipe': '',
+ 'testcase/tstRTProcCreateEx': '',
+ 'testcase/tstRTProcCreatePrf': '',
+ 'testcase/tstRTProcIsRunningByName': '',
+ 'testcase/tstRTProcQueryUsername': '',
+ 'testcase/tstRTProcWait': '',
+ 'testcase/tstTime-2': '',
+ 'testcase/tstTime-3': '',
+ 'testcase/tstTime-4': '',
+ 'testcase/tstTimer': '',
+ 'testcase/tstThread-1': '',
+ 'testcase/tstUtf8': ''
+ };
+
+ # Test dependency list -- libraries.
+ # Needed in order to execute testcases on remote targets which don't have a VBox installation present.
+ kdTestCaseDepsLibs = [
+ "VBoxRT"
+ ];
+
+ ## The exclude list.
+ # @note Stripped extensions!
+ kasHardened = [
+ "testcase/tstIntNet-1",
+ "testcase/tstR0ThreadPreemptionDriver", # VBox 4.3
+ "testcase/tstRTR0ThreadPreemptionDriver",
+ "testcase/tstRTR0MemUserKernelDriver",
+ "testcase/tstRTR0SemMutexDriver",
+ "testcase/tstRTR0TimerDriver",
+ "testcase/tstRTR0ThreadDriver",
+ 'testcase/tstRTR0DbgKrnlInfoDriver',
+ "tstInt",
+ "tstPDMQueue", # Comment in testcase says its driverless, but it needs driver access.
+ "tstVMM",
+ "tstVMMFork",
+ "tstVMREQ",
+ 'testcase/tstCFGM',
+ 'testcase/tstContiguous',
+ 'testcase/tstGetPagingMode',
+ 'testcase/tstGIP-2',
+ 'testcase/tstInit',
+ 'testcase/tstLow',
+ 'testcase/tstMMHyperHeap',
+ 'testcase/tstPage',
+ 'testcase/tstPin',
+ 'testcase/tstRTTime', 'testcase/tstTime', # GIP test case.
+ 'testcase/tstRTTime-2', 'testcase/tstTime-2', # GIP test case.
+ 'testcase/tstRTTime-4', 'testcase/tstTime-4', # GIP test case.
+ 'testcase/tstSSM',
+ 'testcase/tstSupSem-Zombie',
+ ]
+
+ ## Argument lists
+ kdArguments = {
+ 'testcase/tstbntest': [ '-out', os.devnull, ], # Very noisy.
+ };
+
+
+ ## Status code translations.
+ ## @{
+ kdExitCodeNames = {
+ 0: 'RTEXITCODE_SUCCESS',
+ 1: 'RTEXITCODE_FAILURE',
+ 2: 'RTEXITCODE_SYNTAX',
+ 3: 'RTEXITCODE_INIT',
+ 4: 'RTEXITCODE_SKIPPED',
+ };
+ kdExitCodeNamesWin = {
+ -1073741515: 'STATUS_DLL_NOT_FOUND',
+ -1073741512: 'STATUS_ORDINAL_NOT_FOUND',
+ -1073741511: 'STATUS_ENTRYPOINT_NOT_FOUND',
+ -1073741502: 'STATUS_DLL_INIT_FAILED',
+ -1073741500: 'STATUS_UNHANDLED_EXCEPTION',
+ -1073741499: 'STATUS_APP_INIT_FAILURE',
+ -1073741819: 'STATUS_ACCESS_VIOLATION',
+ -1073741571: 'STATUS_STACK_OVERFLOW',
+ };
+ ## @}
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self);
+
+ # We need to set a default test VM set here -- otherwise the test
+ # driver base class won't let us use the "--test-vms" switch.
+ #
+ # See the "--local" switch in self.parseOption().
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+
+ # Selected NIC attachment.
+ self.sNicAttachment = '';
+
+ # Session handling stuff.
+ # Only needed for remote tests executed by TxS.
+ self.oSession = None;
+ self.oTxsSession = None;
+
+ self.sVBoxInstallRoot = None;
+
+ ## Testing mode being used:
+ # "local": Execute unit tests locally (same host, default).
+ # "remote-copy": Copies unit tests from host to the remote, then executing it.
+ # "remote-exec": Executes unit tests right on the remote from a given source.
+ self.sMode = 'local';
+
+ self.cSkipped = 0;
+ self.cPassed = 0;
+ self.cFailed = 0;
+
+ # The source directory where our unit tests live.
+ # This most likely is our out/ or some staging directory and
+ # also acts the source for copying over the testcases to a remote target.
+ self.sUnitTestsPathSrc = None;
+
+ # Array of environment variables with NAME=VAL entries
+ # to be applied for testcases.
+ #
+ # This is also needed for testcases which are being executed remotely.
+ self.asEnv = [];
+
+ # The destination directory our unit tests live when being
+ # copied over to a remote target (via TxS).
+ self.sUnitTestsPathDst = None;
+
+ # The executable suffix to use for the executing the actual testcases.
+ # Will be re-set when executing the testcases on a remote (VM) once we know
+ # what type of suffix to use then (based on guest OS).
+ self.sExeSuff = base.exeSuff();
+
+ self.aiVBoxVer = (4, 3, 0, 0);
+
+ # For testing testcase logic.
+ self.fDryRun = False;
+ self.fOnlyWhiteList = False;
+
+ @staticmethod
+ def _sanitizePath(sPath):
+ """
+ Does a little bit of sanitizing a given path by removing quoting, if any.
+
+ This is needed because handed-in paths via command line arguments can contain variables like "${CDROM}"
+ which might need to get processed by TXS on the guest side first.
+
+ Returns the sanitized path.
+ """
+ if sPath is None: # Keep uninitialized strings as-is.
+ return None;
+ return sPath.strip('\"').strip('\'');
+
+ def _detectPaths(self):
+ """
+ Internal worker for actionVerify and actionExecute that detects paths.
+
+ This sets sVBoxInstallRoot and sUnitTestsPathBase and returns True/False.
+ """
+
+ reporter.log2('Detecting paths ...');
+
+ #
+ # Do some sanity checking first.
+ #
+ if self.sMode == 'remote-exec' and not self.sUnitTestsPathSrc: # There is no way we can figure this out automatically.
+ reporter.error('Unit tests source must be specified explicitly for selected mode!');
+ return False;
+
+ #
+ # We need a VBox install (/ build) to test.
+ #
+ if False is True: ## @todo r=andy ??
+ if not self.importVBoxApi():
+ reporter.error('Unabled to import the VBox Python API.');
+ return False;
+ else:
+ self._detectBuild();
+ if self.oBuild is None:
+ reporter.error('Unabled to detect the VBox build.');
+ return False;
+
+ #
+ # Where are the files installed?
+ # Solaris requires special handling because of it's multi arch subdirs.
+ #
+ if not self.sVBoxInstallRoot:
+ self.sVBoxInstallRoot = self.oBuild.sInstallPath;
+ if not self.oBuild.isDevBuild() and utils.getHostOs() == 'solaris':
+ sArchDir = utils.getHostArch();
+ if sArchDir == 'x86': sArchDir = 'i386';
+ self.sVBoxInstallRoot = os.path.join(self.sVBoxInstallRoot, sArchDir);
+
+ ## @todo r=andy Make sure the install root really exists and is accessible.
+
+ # Add the installation root to the PATH on windows so we can get DLLs from it.
+ if utils.getHostOs() == 'win':
+ sPathName = 'PATH';
+ if not sPathName in os.environ:
+ sPathName = 'Path';
+ sPath = os.environ.get(sPathName, '.');
+ if sPath and sPath[-1] != ';':
+ sPath += ';';
+ os.environ[sPathName] = sPath + self.sVBoxInstallRoot + ';';
+ else:
+ reporter.log2('VBox installation root already set to "%s"' % (self.sVBoxInstallRoot));
+
+ self.sVBoxInstallRoot = self._sanitizePath(self.sVBoxInstallRoot);
+
+ #
+ # The unittests are generally not installed, so look for them.
+ #
+ if not self.sUnitTestsPathSrc:
+ sBinOrDist = 'dist' if utils.getHostOs() in [ 'darwin', ] else 'bin';
+ asCandidates = [
+ self.oBuild.sInstallPath,
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), self.oBuild.sType, sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'release', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'debug', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'strict', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'dbgopt', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'profile', sBinOrDist),
+ os.path.join(self.sScratchPath, sBinOrDist + '.' + utils.getHostArch()),
+ os.path.join(self.sScratchPath, sBinOrDist, utils.getHostArch()),
+ os.path.join(self.sScratchPath, sBinOrDist),
+ ];
+ if utils.getHostOs() == 'darwin':
+ for i in range(1, len(asCandidates)):
+ asCandidates[i] = os.path.join(asCandidates[i], 'VirtualBox.app', 'Contents', 'MacOS');
+
+ for sCandidat in asCandidates:
+ # The path of tstVMStructSize acts as a beacon to know where all other testcases are.
+ sFileBeacon = os.path.join(sCandidat, 'testcase', 'tstVMStructSize' + self.sExeSuff);
+ reporter.log2('Searching for "%s" ...' % sFileBeacon);
+ if os.path.exists(sFileBeacon):
+ self.sUnitTestsPathSrc = sCandidat;
+ break
+
+ if self.sUnitTestsPathSrc:
+ reporter.log('Unit test source dir path: ', self.sUnitTestsPathSrc)
+ else:
+ reporter.error('Unable to find unit test source dir. Candidates: %s' % (asCandidates,));
+ if reporter.getVerbosity() >= 2:
+ reporter.log('Contents of "%s"' % self.sScratchPath);
+ for paths, dirs, files in os.walk(self.sScratchPath):
+ reporter.log('{} {} {}'.format(repr(paths), repr(dirs), repr(files)));
+ return False
+
+ else:
+ reporter.log2('Unit test source dir already set to "%s"' % (self.sUnitTestsPathSrc))
+ reporter.log('Unit test source dir path: ', self.sUnitTestsPathSrc)
+
+ self.sUnitTestsPathSrc = self._sanitizePath(self.sUnitTestsPathSrc);
+
+ return True;
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Shows the testdriver usage.
+ """
+ fRc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('Unit Test #1 options:');
+ reporter.log(' --dryrun');
+ reporter.log(' Performs a dryrun (no tests being executed).');
+ reporter.log(' --mode <local|remote-copy|remote-exec>');
+ reporter.log(' Specifies the test execution mode:');
+ reporter.log(' local: Locally on the same machine.');
+ reporter.log(' remote-copy: On remote (guest) by copying them from the local source.');
+ reporter.log(' remote-exec: On remote (guest) directly (needs unit test source).');
+ reporter.log(' --only-whitelist');
+ reporter.log(' Only processes the white list.');
+ reporter.log(' --quick');
+ reporter.log(' Very selective testing.');
+ reporter.log(' --unittest-source <dir>');
+ reporter.log(' Sets the unit test source to <dir>.');
+ reporter.log(' Also used for remote execution.');
+ reporter.log(' --vbox-install-root <dir>');
+ reporter.log(' Sets the VBox install root to <dir>.');
+ reporter.log(' Also used for remote execution.');
+ return fRc;
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Parses the testdriver arguments from the command line.
+ """
+ if asArgs[iArg] == '--dryrun':
+ self.fDryRun = True;
+ elif asArgs[iArg] == '--mode':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ if asArgs[iArg] in ('local', 'remote-copy', 'remote-exec',):
+ self.sMode = asArgs[iArg];
+ else:
+ raise base.InvalidOption('Argument "%s" invalid' % (asArgs[iArg]));
+ elif asArgs[iArg] == '--unittest-source':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ self.sUnitTestsPathSrc = asArgs[iArg];
+ elif asArgs[iArg] == '--only-whitelist':
+ self.fOnlyWhiteList = True;
+ elif asArgs[iArg] == '--quick':
+ self.fOnlyWhiteList = True;
+ elif asArgs[iArg] == '--vbox-install-root':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
+ self.sVBoxInstallRoot = asArgs[iArg];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionVerify(self):
+ if not self._detectPaths():
+ return False;
+
+ if self.oTestVmSet:
+ return vbox.TestDriver.actionVerify(self);
+
+ return True;
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ # Do the configuring.
+ if self.isRemoteMode():
+ if self.sNicAttachment == 'nat': eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ elif self.sNicAttachment == 'bridged': eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ else: eNic0AttachType = None;
+
+ # Make sure to mount the Validation Kit .ISO so that TxS has the chance
+ # to update itself.
+ #
+ # This is necessary as a lot of our test VMs nowadays have a very old TxS
+ # installed which don't understand commands like uploading files to the guest.
+ # Uploading files is needed for this test driver, however.
+ #
+ ## @todo Get rid of this as soon as we create test VMs in a descriptive (automated) manner.
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType,
+ sDvdImage = self.sVBoxValidationKitIso);
+
+ return True;
+
+ def actionExecute(self):
+ # Make sure vboxapi has been imported so we can execute the driver without going thru
+ # a former configuring step.
+ if not self.importVBoxApi():
+ return False;
+ if not self._detectPaths():
+ return False;
+ reporter.log2('Unit test source path is "%s"\n' % self.sUnitTestsPathSrc);
+
+ if not self.sUnitTestsPathDst:
+ self.sUnitTestsPathDst = self.sScratchPath;
+ reporter.log2('Unit test destination path is "%s"\n' % self.sUnitTestsPathDst);
+
+ if self.isRemoteMode(): # Run on a test VM (guest).
+ if self.fpApiVer < 7.0: ## @todo Needs Validation Kit .ISO tweaking (including the unit tests) first.
+ reporter.log('Remote unit tests for non-trunk builds skipped.');
+ fRc = True;
+ else:
+ assert self.oTestVmSet is not None;
+ fRc = self.oTestVmSet.actionExecute(self, self.testOneVmConfig);
+ else: # Run locally (host).
+ self._figureVersion();
+ self._makeEnvironmentChanges();
+
+ # If this is an ASAN build and we're on linux, make sure we've got
+ # libasan.so.N in the LD_LIBRARY_PATH or stuff w/o a RPATH entry
+ # pointing to /opt/VirtualBox will fail (like tstAsmStructs).
+ if self.getBuildType() == 'asan' and utils.getHostOs() in ('linux',):
+ sLdLibraryPath = '';
+ if 'LD_LIBRARY_PATH' in os.environ:
+ sLdLibraryPath = os.environ['LD_LIBRARY_PATH'] + ':';
+ sLdLibraryPath += self.oBuild.sInstallPath;
+ os.environ['LD_LIBRARY_PATH'] = sLdLibraryPath;
+
+ fRc = self._testRunUnitTests(None);
+
+ return fRc;
+
+ #
+ # Misc.
+ #
+ def isRemoteMode(self):
+ """ Predicate method for checking if in any remote mode. """
+ return self.sMode.startswith('remote');
+
+ #
+ # Test execution helpers.
+ #
+
+ def _testRunUnitTests(self, oTestVm):
+ """
+ Main function to execute all unit tests.
+ """
+
+ # Determine executable suffix based on selected execution mode.
+ if self.isRemoteMode(): # Run on a test VM (guest).
+ if oTestVm.isWindows():
+ self.sExeSuff = '.exe';
+ else:
+ self.sExeSuff = '';
+ else:
+ # For local tests this already is set in __init__
+ pass;
+
+ self._testRunUnitTestsSet(oTestVm, r'^tst*', 'testcase');
+ self._testRunUnitTestsSet(oTestVm, r'^tst*', '.');
+
+ fRc = self.cFailed == 0;
+
+ reporter.log('');
+ if self.fDryRun:
+ reporter.log('*********************************************************');
+ reporter.log('DRY RUN - DRY RUN - DRY RUN - DRY RUN - DRY RUN - DRY RUN');
+ reporter.log('*********************************************************');
+ reporter.log('*********************************************************');
+ reporter.log(' Target: %s' % (oTestVm.sVmName if oTestVm else 'local',));
+ reporter.log(' Mode: %s' % (self.sMode,));
+ reporter.log(' Exe suffix: %s' % (self.sExeSuff,));
+ reporter.log('Unit tests source: %s %s'
+ % (self.sUnitTestsPathSrc, '(on remote)' if self.sMode == 'remote-exec' else '',));
+ reporter.log('VBox install root: %s %s'
+ % (self.sVBoxInstallRoot, '(on remote)' if self.sMode == 'remote-exec' else '',));
+ reporter.log('*********************************************************');
+ reporter.log('*** PASSED: %d' % (self.cPassed,));
+ reporter.log('*** FAILED: %d' % (self.cFailed,));
+ reporter.log('*** SKIPPED: %d' % (self.cSkipped,));
+ reporter.log('*** TOTAL: %d' % (self.cPassed + self.cFailed + self.cSkipped,));
+
+ return fRc;
+
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Simple test.
+ self.logVmInfo(oVM);
+
+ if not self.fDryRun:
+ # Try waiting for a bit longer (5 minutes) until the CD is available to avoid running into timeouts.
+ self.oSession, self.oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
+ fCdWait = not self.fDryRun,
+ cMsCdWait = 5 * 60 * 1000);
+ if self.oSession is None:
+ return False;
+
+ self.addTask(self.oTxsSession);
+
+ # Determine the unit tests destination path.
+ self.sUnitTestsPathDst = oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'testUnitTests');
+
+ # Run the unit tests.
+ self._testRunUnitTests(oTestVm);
+
+ # Cleanup.
+ if self.oSession is not None:
+ self.removeTask(self.oTxsSession);
+ self.terminateVmBySession(self.oSession);
+ return True;
+
+ #
+ # Test execution helpers.
+ #
+
+ def _figureVersion(self):
+ """ Tries to figure which VBox version this is, setting self.aiVBoxVer. """
+ try:
+ sVer = utils.processOutputChecked(['VBoxManage', '--version'])
+
+ sVer = sVer.strip();
+ sVer = re.sub(r'_BETA.*r', '.', sVer);
+ sVer = re.sub(r'_ALPHA.*r', '.', sVer);
+ sVer = re.sub(r'_RC.*r', '.', sVer);
+ sVer = re.sub('_SPB', '', sVer)
+ sVer = sVer.replace('r', '.');
+
+ self.aiVBoxVer = [int(sComp) for sComp in sVer.split('.')];
+
+ reporter.log('VBox version: %s' % (self.aiVBoxVer,));
+ except:
+ reporter.logXcpt();
+ return False;
+ return True;
+
+ def _compareVersion(self, aiVer):
+ """
+ Compares the give version string with the vbox version string,
+ returning a result similar to C strcmp(). aiVer is on the right side.
+ """
+ cComponents = min(len(self.aiVBoxVer), len(aiVer));
+ for i in range(cComponents):
+ if self.aiVBoxVer[i] < aiVer[i]:
+ return -1;
+ if self.aiVBoxVer[i] > aiVer[i]:
+ return 1;
+ return len(self.aiVBoxVer) - len(aiVer);
+
+ def _isExcluded(self, sTest, dExclList):
+ """ Checks if the testcase is excluded or not. """
+ if sTest in dExclList:
+ sFullExpr = dExclList[sTest].replace(' ', '').strip();
+ if sFullExpr == '':
+ return True;
+
+ # Consider each exclusion expression. These are generally ranges,
+ # either open ended or closed: "<4.3.51r12345", ">=4.3.0 && <=4.3.4".
+ asExprs = sFullExpr.split(';');
+ for sExpr in asExprs:
+
+ # Split it on the and operator and process each sub expression.
+ fResult = True;
+ for sSubExpr in sExpr.split('&&'):
+ # Split out the comparison operator and the version value.
+ if sSubExpr.startswith('<=') or sSubExpr.startswith('>='):
+ sOp = sSubExpr[:2];
+ sValue = sSubExpr[2:];
+ elif sSubExpr.startswith('<') or sSubExpr.startswith('>') or sSubExpr.startswith('='):
+ sOp = sSubExpr[:1];
+ sValue = sSubExpr[1:];
+ else:
+ sOp = sValue = '';
+
+ # Convert the version value, making sure we've got a valid one.
+ try: aiValue = [int(sComp) for sComp in sValue.replace('r', '.').split('.')];
+ except: aiValue = ();
+ if not aiValue or len(aiValue) > 4:
+ reporter.error('Invalid exclusion expression for %s: "%s" [%s]' % (sTest, sSubExpr, dExclList[sTest]));
+ return True;
+
+ # Do the compare.
+ iCmp = self._compareVersion(aiValue);
+ if sOp == '>=' and iCmp < 0:
+ fResult = False;
+ elif sOp == '>' and iCmp <= 0:
+ fResult = False;
+ elif sOp == '<' and iCmp >= 0:
+ fResult = False;
+ elif sOp == '>=' and iCmp < 0:
+ fResult = False;
+ reporter.log2('iCmp=%s; %s %s %s -> %s' % (iCmp, self.aiVBoxVer, sOp, aiValue, fResult));
+
+ # Did the expression match?
+ if fResult:
+ return True;
+
+ return False;
+
+ def _sudoExecuteSync(self, asArgs):
+ """
+ Executes a sudo child process synchronously.
+ Returns True if the process executed successfully and returned 0,
+ otherwise False is returned.
+ """
+ reporter.log2('Executing [sudo]: %s' % (asArgs, ));
+ if self.isRemoteMode():
+ iRc = -1; ## @todo Not used remotely yet.
+ else:
+ try:
+ iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return False;
+ reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
+ return iRc == 0;
+
+
+ def _logExpandString(self, sString, cVerbosity = 2):
+ """
+ Expands a given string by asking TxS on the guest side and logs it.
+ Uses log level 2 by default.
+
+ No-op if no TxS involved.
+ """
+ if reporter.getVerbosity() < cVerbosity or self.oTxsSession is None:
+ return;
+ sStringExp = self.oTxsSession.syncExpandString(sString);
+ if not sStringExp:
+ return;
+ reporter.log2('_logExpandString: "%s" -> "%s"' % (sString, sStringExp));
+
+ def _wrapPathExists(self, sPath):
+ """
+ Creates the directory specified sPath (including parents).
+ """
+ reporter.log2('_wrapPathExists: %s' % (sPath,));
+ if self.fDryRun:
+ return True;
+ fRc = False;
+ if self.isRemoteMode():
+ self._logExpandString(sPath);
+ fRc = self.oTxsSession.syncIsDir(sPath, fIgnoreErrors = True);
+ if not fRc:
+ fRc = self.oTxsSession.syncIsFile(sPath, fIgnoreErrors = True);
+ else:
+ fRc = os.path.exists(sPath);
+ return fRc;
+
+ def _wrapMkDir(self, sPath):
+ """
+ Creates the directory specified sPath (including parents).
+ """
+ reporter.log2('_wrapMkDir: %s' % (sPath,));
+ if self.fDryRun:
+ return True;
+ fRc = True;
+ if self.isRemoteMode():
+ fRc = self.oTxsSession.syncMkDirPath(sPath, fMode = 0o755);
+ else:
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.makedirs(sPath, 0o755);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/mkdir', '-p', '-m', '0755', sPath]);
+ if not fRc:
+ reporter.log('Failed to create dir "%s".' % (sPath,));
+ return fRc;
+
+ def _wrapCopyFile(self, sSrc, sDst, iMode):
+ """
+ Copies a file.
+ """
+ reporter.log2('_wrapCopyFile: %s -> %s (mode: %o)' % (sSrc, sDst, iMode,));
+ if self.fDryRun:
+ return True;
+ fRc = True;
+ if self.isRemoteMode():
+ self._logExpandString(sSrc);
+ self._logExpandString(sDst);
+ if self.sMode == 'remote-exec':
+ self.oTxsSession.syncCopyFile(sSrc, sDst, iMode);
+ else:
+ fRc = self.oTxsSession.syncUploadFile(sSrc, sDst);
+ if fRc:
+ fRc = self.oTxsSession.syncChMod(sDst, iMode);
+ else:
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ utils.copyFileSimple(sSrc, sDst);
+ os.chmod(sDst, iMode);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/cp', sSrc, sDst]);
+ if fRc:
+ fRc = self._sudoExecuteSync(['/bin/chmod', '%o' % (iMode,), sDst]);
+ if fRc is not True:
+ raise Exception('Failed to chmod "%s".' % (sDst,));
+ if not fRc:
+ reporter.log('Failed to copy "%s" to "%s".' % (sSrc, sDst,));
+ return fRc;
+
+ def _wrapDeleteFile(self, sPath):
+ """
+ Deletes a file.
+ """
+ reporter.log2('_wrapDeleteFile: %s' % (sPath,));
+ if self.fDryRun:
+ return True;
+ fRc = True;
+ if self.isRemoteMode():
+ if self.oTxsSession.syncIsFile(sPath):
+ fRc = self.oTxsSession.syncRmFile(sPath, fIgnoreErrors = True);
+ else:
+ if os.path.exists(sPath):
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.remove(sPath);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/rm', sPath]);
+ if not fRc:
+ reporter.log('Failed to remove "%s".' % (sPath,));
+ return fRc;
+
+ def _wrapRemoveDir(self, sPath):
+ """
+ Removes a directory.
+ """
+ reporter.log2('_wrapRemoveDir: %s' % (sPath,));
+ if self.fDryRun:
+ return True;
+ fRc = True;
+ if self.isRemoteMode():
+ if self.oTxsSession.syncIsDir(sPath):
+ fRc = self.oTxsSession.syncRmDir(sPath, fIgnoreErrors = True);
+ else:
+ if os.path.exists(sPath):
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.rmdir(sPath);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/rmdir', sPath]);
+ if not fRc:
+ reporter.log('Failed to remove "%s".' % (sPath,));
+ return fRc;
+
+ def _envSet(self, sName, sValue):
+ if self.isRemoteMode():
+ # For remote execution we cache the environment block and pass it
+ # right when the process execution happens.
+ self.asEnv.append([ sName, sValue ]);
+ else:
+ os.environ[sName] = sValue;
+ return True;
+
+ def _executeTestCase(self, oTestVm, sName, sFilePathAbs, sTestCaseSubDir, oDevNull): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Executes a test case.
+
+ sFilePathAbs contains the absolute path (including OS-dependent executable suffix) of the testcase.
+
+ Returns @c true if testcase was skipped, or @c if not.
+ """
+
+ fSkipped = False;
+
+ #
+ # If hardening is enabled, some test cases and their dependencies
+ # needs to be copied to and execute from the source
+ # directory in order to work. They also have to be executed as
+ # root, i.e. via sudo.
+ #
+ fHardened = sName in self.kasHardened and self.sUnitTestsPathSrc != self.sVBoxInstallRoot;
+ fCopyToRemote = self.isRemoteMode();
+ fCopyDeps = self.isRemoteMode();
+ asFilesToRemove = []; # Stuff to clean up.
+ asDirsToRemove = []; # Ditto.
+
+ if fHardened or fCopyToRemote:
+ if fCopyToRemote:
+ sDstDir = os.path.join(self.sUnitTestsPathDst, sTestCaseSubDir);
+ else:
+ sDstDir = os.path.join(self.sVBoxInstallRoot, sTestCaseSubDir);
+ if not self._wrapPathExists(sDstDir):
+ self._wrapMkDir(sDstDir);
+ asDirsToRemove.append(sDstDir);
+
+ sSrc = sFilePathAbs;
+ # If the testcase source does not exist for whatever reason, just mark it as skipped
+ # instead of reporting an error.
+ if not self._wrapPathExists(sSrc):
+ self.cSkipped += 1;
+ fSkipped = True;
+ return fSkipped;
+
+ sDst = os.path.join(sDstDir, os.path.basename(sFilePathAbs));
+ fModeExe = 0;
+ fModeDeps = 0;
+ if not oTestVm or (oTestVm and not oTestVm.isWindows()): ## @todo NT4 does not like the chmod. Investigate this!
+ fModeExe = 0o755;
+ fModeDeps = 0o644;
+ self._wrapCopyFile(sSrc, sDst, fModeExe);
+ asFilesToRemove.append(sDst);
+
+ # Copy required dependencies to destination.
+ if fCopyDeps:
+ for sLib in self.kdTestCaseDepsLibs:
+ for sSuff in [ '.dll', '.so', '.dylib' ]:
+ assert self.sVBoxInstallRoot is not None;
+ sSrc = os.path.join(self.sVBoxInstallRoot, sLib + sSuff);
+ if self._wrapPathExists(sSrc):
+ sDst = os.path.join(sDstDir, os.path.basename(sSrc));
+ self._wrapCopyFile(sSrc, sDst, fModeDeps);
+ asFilesToRemove.append(sDst);
+
+ # Copy any associated .dll/.so/.dylib.
+ for sSuff in [ '.dll', '.so', '.dylib' ]:
+ sSrc = os.path.splitext(sFilePathAbs)[0] + sSuff;
+ if os.path.exists(sSrc):
+ sDst = os.path.join(sDstDir, os.path.basename(sSrc));
+ self._wrapCopyFile(sSrc, sDst, fModeDeps);
+ asFilesToRemove.append(sDst);
+
+ # Copy any associated .r0, .rc and .gc modules.
+ offDriver = sFilePathAbs.rfind('Driver')
+ if offDriver > 0:
+ for sSuff in [ '.r0', 'RC.rc', 'RC.gc' ]:
+ sSrc = sFilePathAbs[:offDriver] + sSuff;
+ if os.path.exists(sSrc):
+ sDst = os.path.join(sDstDir, os.path.basename(sSrc));
+ self._wrapCopyFile(sSrc, sDst, fModeDeps);
+ asFilesToRemove.append(sDst);
+
+ sFilePathAbs = os.path.join(sDstDir, os.path.basename(sFilePathAbs));
+
+ #
+ # Set up arguments and environment.
+ #
+ asArgs = [sFilePathAbs,]
+ if sName in self.kdArguments:
+ asArgs.extend(self.kdArguments[sName]);
+
+ sXmlFile = os.path.join(self.sUnitTestsPathDst, 'result.xml');
+
+ self._envSet('IPRT_TEST_OMIT_TOP_TEST', '1');
+ self._envSet('IPRT_TEST_FILE', sXmlFile);
+
+ if self._wrapPathExists(sXmlFile):
+ try: os.unlink(sXmlFile);
+ except: self._wrapDeleteFile(sXmlFile);
+
+ #
+ # Execute the test case.
+ #
+ # Windows is confusing output. Trying a few things to get rid of this.
+ # First, flush both stderr and stdout before running the child. Second,
+ # assign the child stderr to stdout. If this doesn't help, we'll have
+ # to capture the child output.
+ #
+ reporter.log('*** Executing %s%s...' % (asArgs, ' [hardened]' if fHardened else ''));
+ try: sys.stdout.flush();
+ except: pass;
+ try: sys.stderr.flush();
+ except: pass;
+
+ iRc = 0;
+
+ if not self.fDryRun:
+ if fCopyToRemote:
+ fRc = self.txsRunTest(self.oTxsSession, sName, cMsTimeout = 30 * 60 * 1000, sExecName = asArgs[0],
+ asArgs = asArgs, asAddEnv = self.asEnv, fCheckSessionStatus = True);
+ if fRc:
+ iRc = 0;
+ else:
+ (_, sOpcode, abPayload) = self.oTxsSession.getLastReply();
+ if sOpcode.startswith('PROC NOK '): # Extract process rc.
+ iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
+ if iRc == 0: # Might happen if the testcase misses some dependencies. Set it to -42 then.
+ iRc = -42;
+ else:
+ iRc = -1; ## @todo
+ else:
+ oChild = None;
+ try:
+ if fHardened:
+ oChild = utils.sudoProcessPopen(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
+ else:
+ oChild = utils.processPopenSafe(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
+ except:
+ if sName in [ 'tstAsmStructsRC', # 32-bit, may fail to start on 64-bit linux. Just ignore.
+ ]:
+ reporter.logXcpt();
+ fSkipped = True;
+ else:
+ reporter.errorXcpt();
+ iRc = 1023;
+ oChild = None;
+
+ if oChild is not None:
+ self.pidFileAdd(oChild.pid, sName, fSudo = fHardened);
+ iRc = oChild.wait();
+ self.pidFileRemove(oChild.pid);
+ #
+ # Clean up
+ #
+ for sPath in asFilesToRemove:
+ self._wrapDeleteFile(sPath);
+ for sPath in asDirsToRemove:
+ self._wrapRemoveDir(sPath);
+
+ #
+ # Report.
+ #
+ if os.path.exists(sXmlFile):
+ reporter.addSubXmlFile(sXmlFile);
+ if fHardened:
+ self._wrapDeleteFile(sXmlFile);
+ else:
+ os.unlink(sXmlFile);
+
+ if iRc == 0:
+ reporter.log('*** %s: exit code %d' % (sFilePathAbs, iRc));
+ self.cPassed += 1;
+
+ elif iRc == 4: # RTEXITCODE_SKIPPED
+ reporter.log('*** %s: exit code %d (RTEXITCODE_SKIPPED)' % (sFilePathAbs, iRc));
+ fSkipped = True;
+ self.cSkipped += 1;
+
+ elif fSkipped:
+ reporter.log('*** %s: exit code %d (Skipped)' % (sFilePathAbs, iRc));
+ self.cSkipped += 1;
+
+ else:
+ sName = self.kdExitCodeNames.get(iRc, '');
+ if iRc in self.kdExitCodeNamesWin and utils.getHostOs() == 'win':
+ sName = self.kdExitCodeNamesWin[iRc];
+ if sName != '':
+ sName = ' (%s)' % (sName);
+
+ if iRc != 1:
+ reporter.testFailure('Exit status: %d%s' % (iRc, sName));
+ reporter.log( '!*! %s: exit code %d%s' % (sFilePathAbs, iRc, sName));
+ else:
+ reporter.error('!*! %s: exit code %d%s' % (sFilePathAbs, iRc, sName));
+ self.cFailed += 1;
+
+ return fSkipped;
+
+ def _testRunUnitTestsSet(self, oTestVm, sTestCasePattern, sTestCaseSubDir):
+ """
+ Run subset of the unit tests set.
+ """
+
+ # Open /dev/null for use as stdin further down.
+ try:
+ oDevNull = open(os.path.devnull, 'w+'); # pylint: disable=consider-using-with,unspecified-encoding
+ except:
+ oDevNull = None;
+
+ # Determin the host OS specific exclusion lists.
+ dTestCasesBuggyForHostOs = self.kdTestCasesBuggyPerOs.get(utils.getHostOs(), []);
+ dTestCasesBuggyForHostOs.update(self.kdTestCasesBuggyPerOs.get(utils.getHostOsDotArch(), []));
+
+ ## @todo Add filtering for more specific OSes (like OL server, doesn't have X installed) by adding a separate
+ # black list + using utils.getHostOsVersion().
+
+ #
+ # Process the file list and run everything looking like a testcase.
+ #
+ if not self.fOnlyWhiteList:
+ if self.sMode in ('local', 'remote-copy'):
+ asFiles = sorted(os.listdir(os.path.join(self.sUnitTestsPathSrc, sTestCaseSubDir)));
+ else: # 'remote-exec'
+ ## @todo Implement remote file enumeration / directory listing.
+ reporter.error('Sorry, no remote file enumeration implemented yet!\nUse --only-whitelist instead.');
+ return;
+ else:
+ # Transform our dict into a list, where the keys are the list elements.
+ asFiles = list(self.kdTestCasesWhiteList.keys());
+ # Make sure to only keep the list item's base name so that the iteration down below works
+ # with our white list without any additional modification.
+ asFiles = [os.path.basename(s) for s in asFiles];
+
+ for sFilename in asFiles:
+ # When executing in remote execution mode, make sure to append the executable suffix here, as
+ # the (white / black) lists do not contain any OS-specific executable suffixes.
+ if self.sMode == 'remote-exec':
+ sFilename = sFilename + self.sExeSuff;
+ # Separate base and suffix and morph the base into something we
+ # can use for reporting and array lookups.
+ sBaseName = os.path.basename(sFilename);
+ sName, sSuffix = os.path.splitext(sBaseName);
+ if sTestCaseSubDir != '.':
+ sName = sTestCaseSubDir + '/' + sName;
+
+ reporter.log2('sTestCasePattern=%s, sBaseName=%s, sName=%s, sSuffix=%s, sFileName=%s'
+ % (sTestCasePattern, sBaseName, sName, sSuffix, sFilename,));
+
+ # Process white list first, if set.
+ if self.fOnlyWhiteList \
+ and not self._isExcluded(sName, self.kdTestCasesWhiteList):
+ # (No testStart/Done or accounting here!)
+ reporter.log('%s: SKIPPED (not in white list)' % (sName,));
+ continue;
+
+ # Basic exclusion.
+ if not re.match(sTestCasePattern, sBaseName) \
+ or sSuffix in self.kasSuffixBlackList:
+ reporter.log2('"%s" is not a test case.' % (sName,));
+ continue;
+
+ # When not only processing the white list, do some more checking first.
+ if not self.fOnlyWhiteList:
+ # Check if the testcase is black listed or buggy before executing it.
+ if self._isExcluded(sName, self.kdTestCasesBlackList):
+ # (No testStart/Done or accounting here!)
+ reporter.log('%s: SKIPPED (blacklisted)' % (sName,));
+ continue;
+
+ if self._isExcluded(sName, self.kdTestCasesBuggy):
+ reporter.testStart(sName);
+ reporter.log('%s: Skipping, buggy in general.' % (sName,));
+ reporter.testDone(fSkipped = True);
+ self.cSkipped += 1;
+ continue;
+
+ if self._isExcluded(sName, dTestCasesBuggyForHostOs):
+ reporter.testStart(sName);
+ reporter.log('%s: Skipping, buggy on %s.' % (sName, utils.getHostOs(),));
+ reporter.testDone(fSkipped = True);
+ self.cSkipped += 1;
+ continue;
+ else:
+ # Passed the white list check already above.
+ pass;
+
+ sFilePathAbs = os.path.normpath(os.path.join(self.sUnitTestsPathSrc, os.path.join(sTestCaseSubDir, sFilename)));
+ reporter.log2('sFilePathAbs=%s\n' % (sFilePathAbs,));
+ reporter.testStart(sName);
+ try:
+ fSkipped = self._executeTestCase(oTestVm, sName, sFilePathAbs, sTestCaseSubDir, oDevNull);
+ except:
+ reporter.errorXcpt('!*!');
+ self.cFailed += 1;
+ fSkipped = False;
+ reporter.testDone(fSkipped);
+
+
+if __name__ == '__main__':
+ sys.exit(tdUnitTest1().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/usb/Makefile.kmk b/src/VBox/ValidationKit/tests/usb/Makefile.kmk
new file mode 100644
index 00000000..3a4741cc
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/Makefile.kmk
@@ -0,0 +1,53 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - USB.
+#
+
+#
+# Copyright (C) 2014-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsUsb
+ValidationKitTestsUsb_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsUsb_INST = $(INST_VALIDATIONKIT)tests/usb/
+ValidationKitTestsUsb_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdUsb1.py \
+ $(PATH_SUB_CURRENT)/usbgadget.py \
+ $(PATH_SUB_CURRENT)/tst-utsgadget.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsUsb_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/usb/tdUsb1.py b/src/VBox/ValidationKit/tests/usb/tdUsb1.py
new file mode 100755
index 00000000..9dd20ebf
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/tdUsb1.py
@@ -0,0 +1,590 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdUsb1.py $
+
+"""
+VirtualBox Validation Kit - USB testcase and benchmark.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2014-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+import socket;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+# USB gadget control import
+import usbgadget;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class tdUsbBenchmark(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
+ """
+ USB benchmark.
+ """
+
+ # The available test devices
+ #
+ # The first key is the hostname of the host the test is running on.
+ # It contains a new dictionary with the attached gadgets based on the
+ # USB speed we want to test (Low, Full, High, Super).
+ # The parameters consist of the hostname of the gadget in the network
+ # and the hardware type.
+ kdGadgetParams = {
+ 'adaris': {
+ 'Low': ('usbtest.de.oracle.com', None),
+ 'Full': ('usbtest.de.oracle.com', None),
+ 'High': ('usbtest.de.oracle.com', None),
+ 'Super': ('usbtest.de.oracle.com', None)
+ },
+ };
+
+ # Mappings of USB controllers to supported USB device speeds.
+ kdUsbSpeedMappings = {
+ 'OHCI': ['Low', 'Full'],
+ 'EHCI': ['High'],
+ 'XHCI': ['Low', 'Full', 'High', 'Super']
+ };
+
+ # Tests currently disabled because they fail, need investigation.
+ kdUsbTestsDisabled = {
+ 'Low': [24],
+ 'Full': [24],
+ 'High': [24],
+ 'Super': [24]
+ };
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = ['tst-arch'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw'];
+ self.asVirtModes = self.asVirtModesDef;
+ self.acCpusDef = [1, 2,];
+ self.acCpus = self.acCpusDef;
+ self.asUsbCtrlsDef = ['OHCI', 'EHCI', 'XHCI'];
+ self.asUsbCtrls = self.asUsbCtrlsDef;
+ self.asUsbSpeedDef = ['Low', 'Full', 'High', 'Super'];
+ self.asUsbSpeed = self.asUsbSpeedDef;
+ self.asUsbTestsDef = ['Compliance', 'Reattach'];
+ self.asUsbTests = self.asUsbTestsDef;
+ self.cUsbReattachCyclesDef = 100;
+ self.cUsbReattachCycles = self.cUsbReattachCyclesDef;
+ self.sHostname = socket.gethostname().lower();
+ self.sGadgetHostnameDef = 'usbtest.de.oracle.com';
+ self.uGadgetPortDef = None;
+ self.sUsbCapturePathDef = self.sScratchPath;
+ self.sUsbCapturePath = self.sUsbCapturePathDef;
+ self.fUsbCapture = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdUsb1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --usb-ctrls <u1[:u2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbCtrlsDef)));
+ reporter.log(' --usb-speed <s1[:s2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbSpeedDef)));
+ reporter.log(' --usb-tests <s1[:s2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbTestsDef)));
+ reporter.log(' --usb-reattach-cycles <cycles>');
+ reporter.log(' Default: %s' % (self.cUsbReattachCyclesDef));
+ reporter.log(' --hostname: <hostname>');
+ reporter.log(' Default: %s' % (self.sHostname));
+ reporter.log(' --default-gadget-host <hostname>');
+ reporter.log(' Default: %s' % (self.sGadgetHostnameDef));
+ reporter.log(' --default-gadget-port <port>');
+ reporter.log(' Default: %s' % (6042));
+ reporter.log(' --usb-capture-path <path>');
+ reporter.log(' Default: %s' % (self.sUsbCapturePathDef));
+ reporter.log(' --usb-capture');
+ reporter.log(' Whether to capture the USB traffic for each test');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--usb-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-ctrls" takes a colon separated list of USB controllers');
+ self.asUsbCtrls = asArgs[iArg].split(':');
+ for s in self.asUsbCtrls:
+ if s not in self.asUsbCtrlsDef:
+ reporter.log('warning: The "--usb-ctrls" value "%s" is not a valid USB controller.' % (s));
+ elif asArgs[iArg] == '--usb-speed':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-speed" takes a colon separated list of USB speeds');
+ self.asUsbSpeed = asArgs[iArg].split(':');
+ for s in self.asUsbSpeed:
+ if s not in self.asUsbSpeedDef:
+ reporter.log('warning: The "--usb-speed" value "%s" is not a valid USB speed.' % (s));
+ elif asArgs[iArg] == '--usb-tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-tests" takes a colon separated list of USB tests');
+ self.asUsbTests = asArgs[iArg].split(':');
+ for s in self.asUsbTests:
+ if s not in self.asUsbTestsDef:
+ reporter.log('warning: The "--usb-tests" value "%s" is not a valid USB test.' % (s));
+ elif asArgs[iArg] == '--usb-reattach-cycles':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-reattach-cycles" takes cycle count');
+ try: self.cUsbReattachCycles = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--usb-reattach-cycles" value "%s" is not an integer' \
+ % (asArgs[iArg],));
+ if self.cUsbReattachCycles <= 0:
+ raise base.InvalidOption('The "--usb-reattach-cycles" value "%s" is zero or negative.' \
+ % (self.cUsbReattachCycles,));
+ elif asArgs[iArg] == '--hostname':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--hostname" takes a hostname');
+ self.sHostname = asArgs[iArg];
+ elif asArgs[iArg] == '--default-gadget-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--default-gadget-host" takes a hostname');
+ self.sGadgetHostnameDef = asArgs[iArg];
+ elif asArgs[iArg] == '--default-gadget-port':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--default-gadget-port" takes port number');
+ try: self.uGadgetPortDef = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--default-gadget-port" value "%s" is not an integer' \
+ % (asArgs[iArg],));
+ if self.uGadgetPortDef <= 0:
+ raise base.InvalidOption('The "--default-gadget-port" value "%s" is zero or negative.' \
+ % (self.uGadgetPortDef,));
+ elif asArgs[iArg] == '--usb-capture-path':
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-capture-path" takes a path argument');
+ self.sUsbCapturePath = asArgs[iArg];
+ elif asArgs[iArg] == '--usb-capture':
+ self.fUsbCapture = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+
+ if 'tst-arch' in self.asTestVMs:
+ self.asRsrcs.append('4.2/usb/tst-arch.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-arch' in self.asTestVMs:
+ oVM = self.createTestVM('tst-arch', 1, '4.2/usb/tst-arch.vdi', sKind = 'ArchLinux_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.testUsb();
+ return fRc;
+
+ def getGadgetParams(self, sHostname, sSpeed):
+ """
+ Returns the gadget hostname and port from the
+ given hostname the test is running on and device speed we want to test.
+ """
+ kdGadgetsConfigured = self.kdGadgetParams.get(sHostname);
+ if kdGadgetsConfigured is not None:
+ return kdGadgetsConfigured.get(sSpeed);
+
+ return (self.sGadgetHostnameDef, self.uGadgetPortDef);
+
+ def getCaptureFilePath(self, sUsbCtrl, sSpeed):
+ """
+ Returns capture filename from the given data.
+ """
+
+ return '%s%s%s-%s.pcap' % (self.sUsbCapturePath, os.sep, sUsbCtrl, sSpeed);
+
+ def attachUsbDeviceToVm(self, oSession, sVendorId, sProductId, iBusId,
+ sCaptureFile = None):
+ """
+ Attaches the given USB device to the VM either via a filter
+ or directly if capturing the USB traffic is enabled.
+
+ Returns True on success, False on failure.
+ """
+ fRc = False;
+ if sCaptureFile is None:
+ fRc = oSession.addUsbDeviceFilter('Compliance device', sVendorId = sVendorId, sProductId = sProductId, \
+ sPort = format(iBusId, 'x'));
+ else:
+ # Search for the correct device in the USB device list waiting for some time
+ # to let it appear.
+ iVendorId = int(sVendorId, 16);
+ iProductId = int(sProductId, 16);
+
+ # Try a few times to give VBoxSVC a chance to detect the new device.
+ for _ in xrange(5):
+ fFound = False;
+ aoUsbDevs = self.oVBoxMgr.getArray(self.oVBox.host, 'USBDevices');
+ for oUsbDev in aoUsbDevs:
+ if oUsbDev.vendorId == iVendorId \
+ and oUsbDev.productId == iProductId \
+ and oUsbDev.port == iBusId:
+ fFound = True;
+ fRc = oSession.attachUsbDevice(oUsbDev.id, sCaptureFile);
+ break;
+
+ if fFound:
+ break;
+
+ # Wait a moment until the next try.
+ self.sleep(1);
+
+ if fRc:
+ # Wait a moment to let the USB device appear
+ self.sleep(9);
+
+ return fRc;
+
+ #
+ # Test execution helpers.
+ #
+ def testUsbCompliance(self, oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile = None):
+ """
+ Test VirtualBoxs USB stack in a VM.
+ """
+ # Get configured USB test devices from hostname we are running on
+ sGadgetHost, uGadgetPort = self.getGadgetParams(self.sHostname, sSpeed);
+
+ oUsbGadget = usbgadget.UsbGadget();
+ reporter.log('Connecting to UTS: ' + sGadgetHost);
+ fRc = oUsbGadget.connectTo(30 * 1000, sGadgetHost, uPort = uGadgetPort, fTryConnect = True);
+ if fRc is True:
+ reporter.log('Connect succeeded');
+ self.oVBox.host.addUSBDeviceSource('USBIP', sGadgetHost, sGadgetHost + (':%s' % oUsbGadget.getUsbIpPort()), [], []);
+
+ fSuperSpeed = False;
+ if sSpeed == 'Super':
+ fSuperSpeed = True;
+
+ # Create test device gadget and a filter to attach the device automatically.
+ fRc = oUsbGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, fSuperSpeed);
+ if fRc is True:
+ iBusId, _ = oUsbGadget.getGadgetBusAndDevId();
+ fRc = self.attachUsbDeviceToVm(oSession, '0525', 'a4a0', iBusId, sCaptureFile);
+ if fRc is True:
+ tupCmdLine = ('UsbTest', );
+ # Exclude a few tests which hang and cause a timeout, need investigation.
+ lstTestsExclude = self.kdUsbTestsDisabled.get(sSpeed);
+ for iTestExclude in lstTestsExclude:
+ tupCmdLine = tupCmdLine + ('--exclude', str(iTestExclude));
+
+ fRc = self.txsRunTest(oTxsSession, 'UsbTest', 3600 * 1000, \
+ '${CDROM}/${OS/ARCH}/UsbTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running USB test utility failed');
+ else:
+ reporter.testFailure('Failed to attach USB device to VM');
+ oUsbGadget.disconnectFrom();
+ else:
+ reporter.testFailure('Failed to impersonate test device');
+
+ self.oVBox.host.removeUSBDeviceSource(sGadgetHost);
+ else:
+ reporter.log('warning: Failed to connect to USB gadget');
+ fRc = None
+
+ _ = sUsbCtrl;
+ return fRc;
+
+ def testUsbReattach(self, oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile = None): # pylint: disable=unused-argument
+ """
+ Tests that rapid connect/disconnect cycles work.
+ """
+ # Get configured USB test devices from hostname we are running on
+ sGadgetHost, uGadgetPort = self.getGadgetParams(self.sHostname, sSpeed);
+
+ oUsbGadget = usbgadget.UsbGadget();
+ reporter.log('Connecting to UTS: ' + sGadgetHost);
+ fRc = oUsbGadget.connectTo(30 * 1000, sGadgetHost, uPort = uGadgetPort, fTryConnect = True);
+ if fRc is True:
+ self.oVBox.host.addUSBDeviceSource('USBIP', sGadgetHost, sGadgetHost + (':%s' % oUsbGadget.getUsbIpPort()), [], []);
+
+ fSuperSpeed = False;
+ if sSpeed == 'Super':
+ fSuperSpeed = True;
+
+ # Create test device gadget and a filter to attach the device automatically.
+ fRc = oUsbGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, fSuperSpeed);
+ if fRc is True:
+ iBusId, _ = oUsbGadget.getGadgetBusAndDevId();
+ fRc = self.attachUsbDeviceToVm(oSession, '0525', 'a4a0', iBusId, sCaptureFile);
+ if fRc is True:
+
+ # Wait a moment to let the USB device appear
+ self.sleep(3);
+
+ # Do a rapid disconnect reconnect cycle. Wait a second before disconnecting
+ # again or it will happen so fast that the VM can't attach the new device.
+ # @todo: Get rid of the constant wait and use an event to get notified when
+ # the device was attached.
+ for iCycle in xrange (0, self.cUsbReattachCycles):
+ fRc = oUsbGadget.disconnectUsb();
+ fRc = fRc and oUsbGadget.connectUsb();
+ if not fRc:
+ reporter.testFailure('Reattach cycle %s failed on the gadget device' % (iCycle));
+ break;
+ self.sleep(1);
+
+ else:
+ reporter.testFailure('Failed to create USB device filter');
+
+ oUsbGadget.disconnectFrom();
+ else:
+ reporter.testFailure('Failed to impersonate test device');
+ else:
+ reporter.log('warning: Failed to connect to USB gadget');
+ fRc = None
+
+ return fRc;
+
+ def testUsbOneCfg(self, sVmName, sUsbCtrl, sSpeed, sUsbTest):
+ """
+ Runs the specified VM thru one specified test.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(True);
+ fRc = fRc and oSession.enableNestedPaging(True);
+
+ # Make sure controllers are disabled initially.
+ fRc = fRc and oSession.enableUsbOhci(False);
+ fRc = fRc and oSession.enableUsbEhci(False);
+ fRc = fRc and oSession.enableUsbXhci(False);
+
+ if sUsbCtrl == 'OHCI':
+ fRc = fRc and oSession.enableUsbOhci(True);
+ elif sUsbCtrl == 'EHCI':
+ fRc = fRc and oSession.enableUsbEhci(True);
+ elif sUsbCtrl == 'XHCI':
+ fRc = fRc and oSession.enableUsbXhci(True);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = False, fNatForwardingForTxs = False);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ sCaptureFile = None;
+ if self.fUsbCapture:
+ sCaptureFile = self.getCaptureFilePath(sUsbCtrl, sSpeed);
+
+ if sUsbTest == 'Compliance':
+ fRc = self.testUsbCompliance(oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile);
+ elif sUsbTest == 'Reattach':
+ fRc = self.testUsbReattach(oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+
+ # Add the traffic dump if it exists and the test failed
+ if reporter.testErrorCount() > 0 \
+ and sCaptureFile is not None \
+ and os.path.exists(sCaptureFile):
+ reporter.addLogFile(sCaptureFile, 'misc/other', 'USB traffic dump');
+ else:
+ fRc = False;
+ return fRc;
+
+ def testUsbForOneVM(self, sVmName):
+ """
+ Runs one VM thru the various configurations.
+ """
+ fRc = False;
+ reporter.testStart(sVmName);
+ for sUsbCtrl in self.asUsbCtrls:
+ reporter.testStart(sUsbCtrl)
+ for sUsbSpeed in self.asUsbSpeed:
+ asSupportedSpeeds = self.kdUsbSpeedMappings.get(sUsbCtrl);
+ if sUsbSpeed in asSupportedSpeeds:
+ reporter.testStart(sUsbSpeed)
+ for sUsbTest in self.asUsbTests:
+ reporter.testStart(sUsbTest)
+ fRc = self.testUsbOneCfg(sVmName, sUsbCtrl, sUsbSpeed, sUsbTest);
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def testUsb(self):
+ """
+ Executes USB test.
+ """
+
+ reporter.log("Running on host: " + self.sHostname);
+
+ # Loop thru the test VMs.
+ for sVM in self.asTestVMs:
+ # run test on the VM.
+ fRc = self.testUsbForOneVM(sVM);
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdUsbBenchmark().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py b/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py
new file mode 100755
index 00000000..03245e00
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+# $Id: tst-utsgadget.py $
+
+"""
+Simple testcase for usbgadget2.py.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard python imports.
+import sys
+
+# Validation Kit imports.
+sys.path.insert(0, '.');
+sys.path.insert(0, '..');
+sys.path.insert(0, '../..');
+from common import utils;
+from testdriver import reporter;
+import usbgadget;
+
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+g_cTests = 0;
+g_cFailures = 0
+
+def boolRes(rc, fExpect = True):
+ """Checks a boolean result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if isinstance(rc, bool):
+ if rc == fExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def stringRes(rc, sExpect):
+ """Checks a string result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if utils.isString(rc):
+ if rc == sExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def main(asArgs): # pylint: disable=missing-docstring,too-many-locals,too-many-statements
+ cMsTimeout = long(30*1000);
+ sAddress = 'localhost';
+ uPort = None;
+ fStdTests = True;
+
+ i = 1;
+ while i < len(asArgs):
+ if asArgs[i] == '--hostname':
+ sAddress = asArgs[i + 1];
+ i = i + 2;
+ elif asArgs[i] == '--port':
+ uPort = int(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--timeout':
+ cMsTimeout = long(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--help':
+ print('tst-utsgadget.py [--hostname <addr|name>] [--port <num>] [--timeout <cMS>]');
+ return 0;
+ else:
+ print('Unknown argument: %s' % (asArgs[i],));
+ return 2;
+
+ oGadget = usbgadget.UsbGadget();
+ if uPort is None:
+ rc = oGadget.connectTo(cMsTimeout, sAddress);
+ else:
+ rc = oGadget.connectTo(cMsTimeout, sAddress, uPort = uPort);
+ if rc is False:
+ print('connectTo failed');
+ return 1;
+
+ if fStdTests:
+ rc = oGadget.getUsbIpPort() is not None;
+ print('%s: getUsbIpPort() -> %s' % (boolRes(rc), oGadget.getUsbIpPort(),));
+
+ rc = oGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest);
+ print('%s: impersonate()' % (boolRes(rc),));
+
+ rc = oGadget.disconnectUsb();
+ print('%s: disconnectUsb()' % (boolRes(rc),));
+
+ rc = oGadget.connectUsb();
+ print('%s: connectUsb()' % (boolRes(rc),));
+
+ rc = oGadget.clearImpersonation();
+ print('%s: clearImpersonation()' % (boolRes(rc),));
+
+ # Test super speed (and therefore passing configuration items)
+ rc = oGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, True);
+ print('%s: impersonate(, True)' % (boolRes(rc),));
+
+ rc = oGadget.clearImpersonation();
+ print('%s: clearImpersonation()' % (boolRes(rc),));
+
+ # Done
+ rc = oGadget.disconnectFrom();
+ print('%s: disconnectFrom() -> %s' % (boolRes(rc), rc,));
+
+ if g_cFailures != 0:
+ print('tst-utsgadget.py: %u out of %u test failed' % (g_cFailures, g_cTests,));
+ return 1;
+ print('tst-utsgadget.py: all %u tests passed!' % (g_cTests,));
+ return 0;
+
+
+if __name__ == '__main__':
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ sys.exit(main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/usb/usbgadget.py b/src/VBox/ValidationKit/tests/usb/usbgadget.py
new file mode 100755
index 00000000..0a7c4805
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/usbgadget.py
@@ -0,0 +1,1478 @@
+# -*- coding: utf-8 -*-
+# $Id: usbgadget.py $
+# pylint: disable=too-many-lines
+
+"""
+UTS (USB Test Service) client.
+"""
+__copyright__ = \
+"""
+Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+in the VirtualBox distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+
+SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+"""
+__version__ = "$Revision: 155244 $"
+
+# Standard Python imports.
+import array
+import errno
+import select
+import socket
+import sys;
+import threading
+import time
+import zlib
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver.base import TdTaskBase;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+## @name USB gadget impersonation string constants.
+## @{
+g_ksGadgetImpersonationInvalid = 'Invalid';
+g_ksGadgetImpersonationTest = 'Test';
+g_ksGadgetImpersonationMsd = 'Msd';
+g_ksGadgetImpersonationWebcam = 'Webcam';
+g_ksGadgetImpersonationEther = 'Ether';
+## @}
+
+## @name USB gadget type used in the UTS protocol.
+## @{
+g_kiGadgetTypeTest = 1;
+## @}
+
+## @name USB gadget access methods used in the UTS protocol.
+## @{
+g_kiGadgetAccessUsbIp = 1;
+## @}
+
+## @name USB gadget config types.
+## @{
+g_kiGadgetCfgTypeBool = 1;
+g_kiGadgetCfgTypeString = 2;
+g_kiGadgetCfgTypeUInt8 = 3;
+g_kiGadgetCfgTypeUInt16 = 4;
+g_kiGadgetCfgTypeUInt32 = 5;
+g_kiGadgetCfgTypeUInt64 = 6;
+g_kiGadgetCfgTypeInt8 = 7;
+g_kiGadgetCfgTypeInt16 = 8;
+g_kiGadgetCfgTypeInt32 = 9;
+g_kiGadgetCfgTypeInt64 = 10;
+## @}
+
+#
+# Helpers for decoding data received from the UTS.
+# These are used both the Session and Transport classes.
+#
+
+def getU64(abData, off):
+ """Get a U64 field."""
+ return abData[off] \
+ + abData[off + 1] * 256 \
+ + abData[off + 2] * 65536 \
+ + abData[off + 3] * 16777216 \
+ + abData[off + 4] * 4294967296 \
+ + abData[off + 5] * 1099511627776 \
+ + abData[off + 6] * 281474976710656 \
+ + abData[off + 7] * 72057594037927936;
+
+def getU32(abData, off):
+ """Get a U32 field."""
+ return abData[off] \
+ + abData[off + 1] * 256 \
+ + abData[off + 2] * 65536 \
+ + abData[off + 3] * 16777216;
+
+def getU16(abData, off):
+ """Get a U16 field."""
+ return abData[off] \
+ + abData[off + 1] * 256;
+
+def getU8(abData, off):
+ """Get a U8 field."""
+ return abData[off];
+
+def getSZ(abData, off, sDefault = None):
+ """
+ Get a zero-terminated string field.
+ Returns sDefault if the string is invalid.
+ """
+ cchStr = getSZLen(abData, off);
+ if cchStr >= 0:
+ abStr = abData[off:(off + cchStr)];
+ try:
+ return abStr.tostring().decode('utf_8');
+ except:
+ reporter.errorXcpt('getSZ(,%u)' % (off));
+ return sDefault;
+
+def getSZLen(abData, off):
+ """
+ Get the length of a zero-terminated string field, in bytes.
+ Returns -1 if off is beyond the data packet or not properly terminated.
+ """
+ cbData = len(abData);
+ if off >= cbData:
+ return -1;
+
+ offCur = off;
+ while abData[offCur] != 0:
+ offCur = offCur + 1;
+ if offCur >= cbData:
+ return -1;
+
+ return offCur - off;
+
+def isValidOpcodeEncoding(sOpcode):
+ """
+ Checks if the specified opcode is valid or not.
+ Returns True on success.
+ Returns False if it is invalid, details in the log.
+ """
+ sSet1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ sSet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ ";
+ if len(sOpcode) != 8:
+ reporter.error("invalid opcode length: %s" % (len(sOpcode)));
+ return False;
+ for i in range(0, 1):
+ if sSet1.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ for i in range(2, 7):
+ if sSet2.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ return True;
+
+#
+# Helper for encoding data sent to the UTS.
+#
+
+def u32ToByteArray(u32):
+ """Encodes the u32 value as a little endian byte (B) array."""
+ return array.array('B', \
+ ( u32 % 256, \
+ (u32 // 256) % 256, \
+ (u32 // 65536) % 256, \
+ (u32 // 16777216) % 256) );
+
+def u16ToByteArray(u16):
+ """Encodes the u16 value as a little endian byte (B) array."""
+ return array.array('B', \
+ ( u16 % 256, \
+ (u16 // 256) % 256) );
+
+def u8ToByteArray(uint8):
+ """Encodes the u8 value as a little endian byte (B) array."""
+ return array.array('B', (uint8 % 256));
+
+def zeroByteArray(cb):
+ """Returns an array with the given size containing 0."""
+ abArray = array.array('B', (0, ));
+ cb = cb - 1;
+ for i in range(cb): # pylint: disable=unused-variable
+ abArray.append(0);
+ return abArray;
+
+def strToByteArry(sStr):
+ """Encodes the string as a little endian byte (B) array including the terminator."""
+ abArray = array.array('B');
+ sUtf8 = sStr.encode('utf_8');
+ for ch in sUtf8:
+ abArray.append(ord(ch));
+ abArray.append(0);
+ return abArray;
+
+def cfgListToByteArray(lst):
+ """Encodes the given config list as a little endian byte (B) array."""
+ abArray = array.array('B');
+ if lst is not None:
+ for t3Item in lst:
+ # Encode they key size
+ abArray.extend(u32ToByteArray(len(t3Item[0]) + 1)); # Include terminator
+ abArray.extend(u32ToByteArray(t3Item[1])) # Config type
+ abArray.extend(u32ToByteArray(len(t3Item[2]) + 1)); # Value size including temrinator.
+ abArray.extend(u32ToByteArray(0)); # Reserved field.
+
+ abArray.extend(strToByteArry(t3Item[0]));
+ abArray.extend(strToByteArry(t3Item[2]));
+
+ return abArray;
+
+class TransportBase(object):
+ """
+ Base class for the transport layer.
+ """
+
+ def __init__(self, sCaller):
+ self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
+ self.fDummy = 0;
+ self.abReadAheadHdr = array.array('B');
+
+ def toString(self):
+ """
+ Stringify the instance for logging and debugging.
+ """
+ return '<%s: abReadAheadHdr=%s, sDbgCreated=%s>' % (type(self).__name__, self.abReadAheadHdr, self.sDbgCreated);
+
+ def __str__(self):
+ return self.toString();
+
+ def cancelConnect(self):
+ """
+ Cancels any pending connect() call.
+ Returns None;
+ """
+ return None;
+
+ def connect(self, cMsTimeout):
+ """
+ Quietly attempts to connect to the UTS.
+
+ Returns True on success.
+ Returns False on retryable errors (no logging).
+ Returns None on fatal errors with details in the log.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def disconnect(self, fQuiet = False):
+ """
+ Disconnect from the UTS.
+
+ Returns True.
+
+ Override this method, don't call super.
+ """
+ _ = fQuiet;
+ return True;
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ """
+ Sends the bytes in the buffer abBuf to the UTS.
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: len(abBuf) is always a multiple of 16.
+ """
+ _ = abBuf; _ = cMsTimeout;
+ return False;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ """
+ Receive cb number of bytes from the UTS.
+
+ Returns the bytes (array('B')) on success.
+ Returns None on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: cb is always a multiple of 16.
+ """
+ _ = cb; _ = cMsTimeout; _ = fNoDataOk;
+ return None;
+
+ def isConnectionOk(self):
+ """
+ Checks if the connection is OK.
+
+ Returns True if it is.
+ Returns False if it isn't (caller should call diconnect).
+
+ Override this method, don't call super.
+ """
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ """
+ Checks if there is incoming bytes, optionally waiting cMsTimeout
+ milliseconds for something to arrive.
+
+ Returns True if there is, False if there isn't.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def sendMsgInt(self, sOpcode, cMsTimeout, abPayload = array.array('B')):
+ """
+ Sends a message (opcode + encoded payload).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ """
+ # Fix + check the opcode.
+ if len(sOpcode) < 2:
+ reporter.fatal('sendMsgInt: invalid opcode length: %d (\"%s\")' % (len(sOpcode), sOpcode));
+ return False;
+ sOpcode = sOpcode.ljust(8);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('sendMsgInt: invalid opcode encoding: \"%s\"' % (sOpcode));
+ return False;
+
+ # Start construct the message.
+ cbMsg = 16 + len(abPayload);
+ abMsg = array.array('B');
+ abMsg.extend(u32ToByteArray(cbMsg));
+ abMsg.extend((0, 0, 0, 0)); # uCrc32
+ try:
+ abMsg.extend(array.array('B', \
+ ( ord(sOpcode[0]), \
+ ord(sOpcode[1]), \
+ ord(sOpcode[2]), \
+ ord(sOpcode[3]), \
+ ord(sOpcode[4]), \
+ ord(sOpcode[5]), \
+ ord(sOpcode[6]), \
+ ord(sOpcode[7]) ) ) );
+ if abPayload:
+ abMsg.extend(abPayload);
+ except:
+ reporter.fatalXcpt('sendMsgInt: packing problem...');
+ return False;
+
+ # checksum it, padd it and send it off.
+ uCrc32 = zlib.crc32(abMsg[8:]);
+ abMsg[4:8] = u32ToByteArray(uCrc32);
+
+ while len(abMsg) % 16:
+ abMsg.append(0);
+
+ reporter.log2('sendMsgInt: op=%s len=%d to=%d' % (sOpcode, len(abMsg), cMsTimeout));
+ return self.sendBytes(abMsg, cMsTimeout);
+
+ def recvMsg(self, cMsTimeout, fNoDataOk = False):
+ """
+ Receives a message from the UTS.
+
+ Returns the message three-tuple: length, opcode, payload.
+ Returns (None, None, None) on failure and error details in the log.
+ """
+
+ # Read the header.
+ if self.abReadAheadHdr:
+ assert(len(self.abReadAheadHdr) == 16);
+ abHdr = self.abReadAheadHdr;
+ self.abReadAheadHdr = array.array('B');
+ else:
+ abHdr = self.recvBytes(16, cMsTimeout, fNoDataOk); # (virtual method) # pylint: disable=assignment-from-none
+ if abHdr is None:
+ return (None, None, None);
+ if len(abHdr) != 16:
+ reporter.fatal('recvBytes(16) returns %d bytes!' % (len(abHdr)));
+ return (None, None, None);
+
+ # Unpack and validate the header.
+ cbMsg = getU32(abHdr, 0);
+ uCrc32 = getU32(abHdr, 4);
+ sOpcode = abHdr[8:16].tostring().decode('ascii');
+
+ if cbMsg < 16:
+ reporter.fatal('recvMsg: message length is out of range: %s (min 16 bytes)' % (cbMsg));
+ return (None, None, None);
+ if cbMsg > 1024*1024:
+ reporter.fatal('recvMsg: message length is out of range: %s (max 1MB)' % (cbMsg));
+ return (None, None, None);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('recvMsg: invalid opcode \"%s\"' % (sOpcode));
+ return (None, None, None);
+
+ # Get the payload (if any), dropping the padding.
+ abPayload = array.array('B');
+ if cbMsg > 16:
+ if cbMsg % 16:
+ cbPadding = 16 - (cbMsg % 16);
+ else:
+ cbPadding = 0;
+ abPayload = self.recvBytes(cbMsg - 16 + cbPadding, cMsTimeout, False); # pylint: disable=assignment-from-none
+ if abPayload is None:
+ self.abReadAheadHdr = abHdr;
+ if not fNoDataOk :
+ reporter.log('recvMsg: failed to recv payload bytes!');
+ return (None, None, None);
+
+ while cbPadding > 0:
+ abPayload.pop();
+ cbPadding = cbPadding - 1;
+
+ # Check the CRC-32.
+ if uCrc32 != 0:
+ uActualCrc32 = zlib.crc32(abHdr[8:]);
+ if cbMsg > 16:
+ uActualCrc32 = zlib.crc32(abPayload, uActualCrc32);
+ uActualCrc32 = uActualCrc32 & 0xffffffff;
+ if uCrc32 != uActualCrc32:
+ reporter.fatal('recvMsg: crc error: expected %s, got %s' % (hex(uCrc32), hex(uActualCrc32)));
+ return (None, None, None);
+
+ reporter.log2('recvMsg: op=%s len=%d' % (sOpcode, len(abPayload)));
+ return (cbMsg, sOpcode, abPayload);
+
+ def sendMsg(self, sOpcode, cMsTimeout, aoPayload = ()):
+ """
+ Sends a message (opcode + payload tuple).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ Returns None if you pass the incorrectly typed parameters.
+ """
+ # Encode the payload.
+ abPayload = array.array('B');
+ for o in aoPayload:
+ try:
+ if utils.isString(o):
+ # the primitive approach...
+ sUtf8 = o.encode('utf_8');
+ for ch in sUtf8:
+ abPayload.append(ord(ch))
+ abPayload.append(0);
+ elif isinstance(o, long):
+ if o < 0 or o > 0xffffffff:
+ reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
+ return None;
+ abPayload.extend(u32ToByteArray(o));
+ elif isinstance(o, int):
+ if o < 0 or o > 0xffffffff:
+ reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
+ return None;
+ abPayload.extend(u32ToByteArray(o));
+ elif isinstance(o, array.array):
+ abPayload.extend(o);
+ else:
+ reporter.fatal('sendMsg: unexpected payload type: %s (%s) (aoPayload=%s)' % (type(o), o, aoPayload));
+ return None;
+ except:
+ reporter.fatalXcpt('sendMsg: screwed up the encoding code...');
+ return None;
+ return self.sendMsgInt(sOpcode, cMsTimeout, abPayload);
+
+
+class Session(TdTaskBase):
+ """
+ A USB Test Service (UTS) client session.
+ """
+
+ def __init__(self, oTransport, cMsTimeout, cMsIdleFudge, fTryConnect = False):
+ """
+ Construct a UTS session.
+
+ This starts by connecting to the UTS and will enter the signalled state
+ when connected or the timeout has been reached.
+ """
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.oTransport = oTransport;
+ self.sStatus = "";
+ self.cMsTimeout = 0;
+ self.fErr = True; # Whether to report errors as error.
+ self.msStart = 0;
+ self.oThread = None;
+ self.fnTask = self.taskDummy;
+ self.aTaskArgs = None;
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.fScrewedUpMsgState = False;
+ self.fTryConnect = fTryConnect;
+
+ if not self.startTask(cMsTimeout, False, "connecting", self.taskConnect, (cMsIdleFudge,)):
+ raise base.GenError("startTask failed");
+
+ def __del__(self):
+ """Make sure to cancel the task when deleted."""
+ self.cancelTask();
+
+ def toString(self):
+ return '<%s fnTask=%s, aTaskArgs=%s, sStatus=%s, oTaskRc=%s, cMsTimeout=%s,' \
+ ' msStart=%s, fTryConnect=%s, fErr=%s, fScrewedUpMsgState=%s, t3oReply=%s oTransport=%s, oThread=%s>' \
+ % (TdTaskBase.toString(self), self.fnTask, self.aTaskArgs, self.sStatus, self.oTaskRc, self.cMsTimeout,
+ self.msStart, self.fTryConnect, self.fErr, self.fScrewedUpMsgState, self.t3oReply, self.oTransport, self.oThread);
+
+ def taskDummy(self):
+ """Place holder to catch broken state handling."""
+ raise Exception();
+
+ def startTask(self, cMsTimeout, fIgnoreErrors, sStatus, fnTask, aArgs = ()):
+ """
+ Kicks of a new task.
+
+ cMsTimeout: The task timeout in milliseconds. Values less than
+ 500 ms will be adjusted to 500 ms. This means it is
+ OK to use negative value.
+ sStatus: The task status.
+ fnTask: The method that'll execute the task.
+ aArgs: Arguments to pass to fnTask.
+
+ Returns True on success, False + error in log on failure.
+ """
+ if not self.cancelTask():
+ reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: failed to cancel previous task.');
+ return False;
+
+ # Change status and make sure we're the
+ self.lockTask();
+ if self.sStatus != "":
+ self.unlockTask();
+ reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: race.');
+ return False;
+ self.sStatus = "setup";
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.resetTaskLocked();
+ self.unlockTask();
+
+ self.cMsTimeout = max(cMsTimeout, 500);
+ self.fErr = not fIgnoreErrors;
+ self.fnTask = fnTask;
+ self.aTaskArgs = aArgs;
+ self.oThread = threading.Thread(target=self.taskThread, args=(), name=('UTS-%s' % (sStatus)));
+ self.oThread.setDaemon(True); # pylint: disable=deprecated-method
+ self.msStart = base.timestampMilli();
+
+ self.lockTask();
+ self.sStatus = sStatus;
+ self.unlockTask();
+ self.oThread.start();
+
+ return True;
+
+ def cancelTask(self, fSync = True):
+ """
+ Attempts to cancel any pending tasks.
+ Returns success indicator (True/False).
+ """
+ self.lockTask();
+
+ if self.sStatus == "":
+ self.unlockTask();
+ return True;
+ if self.sStatus == "setup":
+ self.unlockTask();
+ return False;
+ if self.sStatus == "cancelled":
+ self.unlockTask();
+ return False;
+
+ reporter.log('utsclient: cancelling "%s"...' % (self.sStatus));
+ if self.sStatus == 'connecting':
+ self.oTransport.cancelConnect();
+
+ self.sStatus = "cancelled";
+ oThread = self.oThread;
+ self.unlockTask();
+
+ if not fSync:
+ return False;
+
+ oThread.join(61.0);
+
+ if sys.version_info < (3, 9, 0):
+ # Removed since Python 3.9.
+ return oThread.isAlive(); # pylint: disable=no-member
+ return oThread.is_alive();
+
+ def taskThread(self):
+ """
+ The task thread function.
+ This does some housekeeping activities around the real task method call.
+ """
+ if not self.isCancelled():
+ try:
+ fnTask = self.fnTask;
+ oTaskRc = fnTask(*self.aTaskArgs);
+ except:
+ reporter.fatalXcpt('taskThread', 15);
+ oTaskRc = None;
+ else:
+ reporter.log('taskThread: cancelled already');
+
+ self.lockTask();
+
+ reporter.log('taskThread: signalling task with status "%s", oTaskRc=%s' % (self.sStatus, oTaskRc));
+ self.oTaskRc = oTaskRc;
+ self.oThread = None;
+ self.sStatus = '';
+ self.signalTaskLocked();
+
+ self.unlockTask();
+ return None;
+
+ def isCancelled(self):
+ """Internal method for checking if the task has been cancelled."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ self.unlockTask();
+ if sStatus == "cancelled":
+ return True;
+ return False;
+
+ def hasTimedOut(self):
+ """Internal method for checking if the task has timed out or not."""
+ cMsLeft = self.getMsLeft();
+ if cMsLeft <= 0:
+ return True;
+ return False;
+
+ def getMsLeft(self, cMsMin = 0, cMsMax = -1):
+ """Gets the time left until the timeout."""
+ cMsElapsed = base.timestampMilli() - self.msStart;
+ if cMsElapsed < 0:
+ return cMsMin;
+ cMsLeft = self.cMsTimeout - cMsElapsed;
+ if cMsLeft <= cMsMin:
+ return cMsMin;
+ if cMsLeft > cMsMax > 0:
+ return cMsMax
+ return cMsLeft;
+
+ def recvReply(self, cMsTimeout = None, fNoDataOk = False):
+ """
+ Wrapper for TransportBase.recvMsg that stashes the response away
+ so the client can inspect it later on.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(cMsTimeout, fNoDataOk);
+ self.lockTask();
+ self.t3oReply = (cbMsg, sOpcode, abPayload);
+ self.unlockTask();
+ return (cbMsg, sOpcode, abPayload);
+
+ def recvAck(self, fNoDataOk = False):
+ """
+ Receives an ACK or error response from the UTS.
+
+ Returns True on success.
+ Returns False on timeout or transport error.
+ Returns (sOpcode, sDetails) tuple on failure. The opcode is stripped
+ and there are always details of some sort or another.
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply(None, fNoDataOk);
+ if cbMsg is None:
+ return False;
+ sOpcode = sOpcode.strip()
+ if sOpcode == "ACK":
+ return True;
+ return (sOpcode, getSZ(abPayload, 16, sOpcode));
+
+ def recvAckLogged(self, sCommand, fNoDataOk = False):
+ """
+ Wrapper for recvAck and logging.
+ Returns True on success (ACK).
+ Returns False on time, transport error and errors signalled by UTS.
+ """
+ rc = self.recvAck(fNoDataOk);
+ if rc is not True and not fNoDataOk:
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ else:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, rc[0], rc[1]));
+ rc = False;
+ return rc;
+
+ def recvTrueFalse(self, sCommand):
+ """
+ Receives a TRUE/FALSE response from the UTS.
+ Returns True on TRUE, False on FALSE and None on error/other (logged).
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is None:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ return None;
+
+ sOpcode = sOpcode.strip()
+ if sOpcode == "TRUE":
+ return True;
+ if sOpcode == "FALSE":
+ return False;
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % \
+ (sCommand, sOpcode, getSZ(abPayload, 16, sOpcode)));
+ return None;
+
+ def sendMsg(self, sOpcode, aoPayload = (), cMsTimeout = None):
+ """
+ Wrapper for TransportBase.sendMsg that inserts the correct timeout.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ return self.oTransport.sendMsg(sOpcode, cMsTimeout, aoPayload);
+
+ def asyncToSync(self, fnAsync, *aArgs):
+ """
+ Wraps an asynchronous task into a synchronous operation.
+
+ Returns False on failure, task return status on success.
+ """
+ rc = fnAsync(*aArgs);
+ if rc is False:
+ reporter.log2('asyncToSync(%s): returns False (#1)' % (fnAsync));
+ return rc;
+
+ rc = self.waitForTask(self.cMsTimeout + 5000);
+ if rc is False:
+ reporter.maybeErrXcpt(self.fErr, 'asyncToSync: waitForTask failed...');
+ self.cancelTask();
+ #reporter.log2('asyncToSync(%s): returns False (#2)' % (fnAsync, rc));
+ return False;
+
+ rc = self.getResult();
+ #reporter.log2('asyncToSync(%s): returns %s' % (fnAsync, rc));
+ return rc;
+
+ #
+ # Connection tasks.
+ #
+
+ def taskConnect(self, cMsIdleFudge):
+ """Tries to connect to the UTS"""
+ while not self.isCancelled():
+ reporter.log2('taskConnect: connecting ...');
+ rc = self.oTransport.connect(self.getMsLeft(500));
+ if rc is True:
+ reporter.log('taskConnect: succeeded');
+ return self.taskGreet(cMsIdleFudge);
+ if rc is None:
+ reporter.log2('taskConnect: unable to connect');
+ return None;
+ if self.hasTimedOut():
+ reporter.log2('taskConnect: timed out');
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: timed out');
+ return False;
+ time.sleep(self.getMsLeft(1, 1000) / 1000.0);
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: cancelled');
+ return False;
+
+ def taskGreet(self, cMsIdleFudge):
+ """Greets the UTS"""
+ sHostname = socket.gethostname().lower();
+ cbFill = 68 - len(sHostname) - 1;
+ rc = self.sendMsg("HOWDY", ((1 << 16) | 0, 0x1, len(sHostname), sHostname, zeroByteArray(cbFill)));
+ if rc is True:
+ rc = self.recvAckLogged("HOWDY", self.fTryConnect);
+ if rc is True:
+ while cMsIdleFudge > 0:
+ cMsIdleFudge -= 1000;
+ time.sleep(1);
+ else:
+ self.oTransport.disconnect(self.fTryConnect);
+ return rc;
+
+ def taskBye(self):
+ """Says goodbye to the UTS"""
+ rc = self.sendMsg("BYE");
+ if rc is True:
+ rc = self.recvAckLogged("BYE");
+ self.oTransport.disconnect();
+ return rc;
+
+ #
+ # Gadget tasks.
+ #
+
+ def taskGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None):
+ """Creates a new gadget on UTS"""
+ cCfgItems = 0;
+ if lstCfg is not None:
+ cCfgItems = len(lstCfg);
+ fRc = self.sendMsg("GDGTCRT", (iGadgetType, iGadgetAccess, cCfgItems, 0, cfgListToByteArray(lstCfg)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTCRT");
+ return fRc;
+
+ def taskGadgetDestroy(self, iGadgetId):
+ """Destroys the given gadget handle on UTS"""
+ fRc = self.sendMsg("GDGTDTOR", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTDTOR");
+ return fRc;
+
+ def taskGadgetConnect(self, iGadgetId):
+ """Connects the given gadget handle on UTS"""
+ fRc = self.sendMsg("GDGTCNCT", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTCNCT");
+ return fRc;
+
+ def taskGadgetDisconnect(self, iGadgetId):
+ """Disconnects the given gadget handle from UTS"""
+ fRc = self.sendMsg("GDGTDCNT", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTDCNT");
+ return fRc;
+
+ #
+ # Public methods - generic task queries
+ #
+
+ def isSuccess(self):
+ """Returns True if the task completed successfully, otherwise False."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return False;
+ if oTaskRc is False or oTaskRc is None:
+ return False;
+ return True;
+
+ def getResult(self):
+ """
+ Returns the result of a completed task.
+ Returns None if not completed yet or no previous task.
+ """
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return None;
+ return oTaskRc;
+
+ def getLastReply(self):
+ """
+ Returns the last reply three-tuple: cbMsg, sOpcode, abPayload.
+ Returns a None, None, None three-tuple if there was no last reply.
+ """
+ self.lockTask();
+ t3oReply = self.t3oReply;
+ self.unlockTask();
+ return t3oReply;
+
+ #
+ # Public methods - connection.
+ #
+
+ def asyncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a disconnect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "bye", self.taskBye);
+
+ def syncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncDisconnect, cMsTimeout, fIgnoreErrors);
+
+ #
+ # Public methods - gadget API
+ #
+
+ def asyncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget create task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetCreate", self.taskGadgetCreate, \
+ (iGadgetType, iGadgetAccess, lstCfg));
+
+ def syncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetCreate, iGadgetType, iGadgetAccess, lstCfg, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget destroy task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDestroy", self.taskGadgetDestroy, \
+ (iGadgetId, ));
+
+ def syncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetDestroy, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget connect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetConnect", self.taskGadgetConnect, \
+ (iGadgetId, ));
+
+ def syncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetConnect, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget disconnect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDisconnect", self.taskGadgetDisconnect, \
+ (iGadgetId, ));
+
+ def syncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetDisconnect, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+
+class TransportTcp(TransportBase):
+ """
+ TCP transport layer for the UTS client session class.
+ """
+
+ def __init__(self, sHostname, uPort):
+ """
+ Save the parameters. The session will call us back to make the
+ connection later on its worker thread.
+ """
+ TransportBase.__init__(self, utils.getCallerName());
+ self.sHostname = sHostname;
+ self.uPort = uPort if uPort is not None else 6042;
+ self.oSocket = None;
+ self.oWakeupW = None;
+ self.oWakeupR = None;
+ self.fConnectCanceled = False;
+ self.fIsConnecting = False;
+ self.oCv = threading.Condition();
+ self.abReadAhead = array.array('B');
+
+ def toString(self):
+ return '<%s sHostname=%s, uPort=%s, oSocket=%s,'\
+ ' fConnectCanceled=%s, fIsConnecting=%s, oCv=%s, abReadAhead=%s>' \
+ % (TransportBase.toString(self), self.sHostname, self.uPort, self.oSocket,
+ self.fConnectCanceled, self.fIsConnecting, self.oCv, self.abReadAhead);
+
+ def __isInProgressXcpt(self, oXcpt):
+ """ In progress exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.EINPROGRESS:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.EWOULDBLOCK:
+ return True;
+ if utils.getHostOs() == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=no-member
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isWouldBlockXcpt(self, oXcpt):
+ """ Would block exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.EWOULDBLOCK:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.EAGAIN:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isConnectionReset(self, oXcpt):
+ """ Connection reset by Peer or others. """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.ECONNRESET:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.ENETRESET:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def _closeWakeupSockets(self):
+ """ Closes the wakup sockets. Caller should own the CV. """
+ oWakeupR = self.oWakeupR;
+ self.oWakeupR = None;
+ if oWakeupR is not None:
+ oWakeupR.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ oWakeupW.close();
+
+ return None;
+
+ def cancelConnect(self):
+ # This is bad stuff.
+ self.oCv.acquire();
+ reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
+ self.fConnectCanceled = True;
+ if self.fIsConnecting:
+ oSocket = self.oSocket;
+ self.oSocket = None;
+ if oSocket is not None:
+ reporter.log2('TransportTcp::cancelConnect: closing the socket');
+ oSocket.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ reporter.log2('TransportTcp::cancelConnect: wakeup call');
+ try: oWakeupW.send('cancelled!\n');
+ except: reporter.logXcpt();
+ try: oWakeupW.shutdown(socket.SHUT_WR);
+ except: reporter.logXcpt();
+ oWakeupW.close();
+ self.oCv.release();
+
+ def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
+ """ Connects to the UTS server as client. """
+
+ # Connect w/ timeouts.
+ rc = None;
+ try:
+ oSocket.connect((self.sHostname, self.uPort));
+ rc = True;
+ except socket.error as oXcpt:
+ iRc = oXcpt.errno;
+ if self.__isInProgressXcpt(oXcpt):
+ # Do the actual waiting.
+ reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
+ try:
+ ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
+ if len(ttRc[1]) + len(ttRc[2]) == 0:
+ raise socket.error(errno.ETIMEDOUT, 'select timed out');
+ iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
+ rc = iRc == 0;
+ except socket.error as oXcpt2:
+ iRc = oXcpt2.errno;
+ except:
+ iRc = -42;
+ reporter.fatalXcpt('socket.select() on connect failed');
+
+ if rc is True:
+ pass;
+ elif iRc in (errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.EINTR, errno.ENETDOWN, errno.ENETUNREACH, errno.ETIMEDOUT):
+ rc = False; # try again.
+ else:
+ if iRc != errno.EBADF or not self.fConnectCanceled:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
+ reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
+ except:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
+ return rc;
+
+
+ def connect(self, cMsTimeout):
+ # Create a non-blocking socket.
+ reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
+ try:
+ oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
+ except:
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+ try:
+ oSocket.setblocking(0);
+ except:
+ oSocket.close();
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+
+ # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
+ oWakeupR = None;
+ oWakeupW = None;
+ if hasattr(socket, 'socketpair'):
+ try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=no-member
+ except: reporter.logXcpt('socket.socketpair() failed');
+
+ # Update the state.
+ self.oCv.acquire();
+ rc = None;
+ if not self.fConnectCanceled:
+ self.oSocket = oSocket;
+ self.oWakeupW = oWakeupW;
+ self.oWakeupR = oWakeupR;
+ self.fIsConnecting = True;
+ self.oCv.release();
+
+ # Try connect.
+ if oWakeupR is None:
+ oWakeupR = oSocket; # Avoid select failure.
+ rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
+ oSocket = None;
+
+ # Update the state and cleanup on failure/cancel.
+ self.oCv.acquire();
+ if rc is True and self.fConnectCanceled:
+ rc = False;
+ self.fIsConnecting = False;
+
+ if rc is not True:
+ if self.oSocket is not None:
+ self.oSocket.close();
+ self.oSocket = None;
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ reporter.log2('TransportTcp::connect: returning %s' % (rc,));
+ return rc;
+
+ def disconnect(self, fQuiet = False):
+ if self.oSocket is not None:
+ self.abReadAhead = array.array('B');
+
+ # Try a shutting down the socket gracefully (draining it).
+ try:
+ self.oSocket.shutdown(socket.SHUT_WR);
+ except:
+ if not fQuiet:
+ reporter.error('shutdown(SHUT_WR)');
+ try:
+ self.oSocket.setblocking(0); # just in case it's not set.
+ sData = "1";
+ while sData:
+ sData = self.oSocket.recv(16384);
+ except:
+ pass;
+
+ # Close it.
+ self.oCv.acquire();
+ try: self.oSocket.setblocking(1);
+ except: pass;
+ self.oSocket.close();
+ self.oSocket = None;
+ else:
+ self.oCv.acquire();
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.sendBytes: No connection.');
+ return False;
+
+ # Try send it all.
+ try:
+ cbSent = self.oSocket.send(abBuf);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ return False;
+ cbSent = 0;
+
+ # Do a timed send.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
+ break;
+
+ # wait.
+ try:
+ ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: select returned with exception');
+ break;
+ if not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.sendBytes: select failed');
+ break;
+
+ # Try send more.
+ try:
+ cbSent += self.oSocket.send(abBuf[cbSent:]);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ break;
+
+ return False;
+
+ def __returnReadAheadBytes(self, cb):
+ """ Internal worker for recvBytes. """
+ assert(len(self.abReadAhead) >= cb);
+ abRet = self.abReadAhead[:cb];
+ self.abReadAhead = self.abReadAhead[cb:];
+ return abRet;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
+ return None;
+
+ # Try read in some more data without bothering with timeout handling first.
+ if len(self.abReadAhead) < cb:
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if abBuf:
+ self.abReadAhead.extend(array.array('B', abBuf));
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
+ return None;
+
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ # Timeout loop.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
+ break;
+
+ # Wait.
+ try:
+ ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[0]:
+ reporter.error('TranportTcp.recvBytes: select returned with exception');
+ break;
+ if not ttRc[0]:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.recvBytes: select failed');
+ break;
+
+ # Try read more.
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if not abBuf:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ self.disconnect();
+ return None;
+
+ self.abReadAhead.extend(array.array('B', abBuf));
+
+ except Exception as oXcpt:
+ reporter.log('recv => exception %s' % (oXcpt,));
+ if not self.__isWouldBlockXcpt(oXcpt):
+ if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
+ reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+
+ # Done?
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
+ return None;
+
+ def isConnectionOk(self):
+ if self.oSocket is None:
+ return False;
+ try:
+ ttRc = select.select([], [], [self.oSocket], 0.0);
+ if ttRc[2]:
+ return False;
+
+ self.oSocket.send(array.array('B')); # send zero bytes.
+ except:
+ return False;
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ try:
+ ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
+ if not ttRc[0]:
+ return False;
+ except:
+ pass;
+ return True;
+
+
+class UsbGadget(object):
+ """
+ USB Gadget control class using the USBT Test Service to talk to the external
+ board behaving like a USB device.
+ """
+
+ def __init__(self):
+ self.oUtsSession = None;
+ self.sImpersonation = g_ksGadgetImpersonationInvalid;
+ self.idGadget = None;
+ self.iBusId = None;
+ self.iDevId = None;
+ self.iUsbIpPort = None;
+
+ def clearImpersonation(self):
+ """
+ Removes the current impersonation of the gadget.
+ """
+ fRc = True;
+
+ if self.idGadget is not None:
+ fRc = self.oUtsSession.syncGadgetDestroy(self.idGadget);
+ self.idGadget = None;
+ self.iBusId = None;
+ self.iDevId = None;
+
+ return fRc;
+
+ def disconnectUsb(self):
+ """
+ Disconnects the USB gadget from the host. (USB connection not network
+ connection used for control)
+ """
+ return self.oUtsSession.syncGadgetDisconnect(self.idGadget);
+
+ def connectUsb(self):
+ """
+ Connect the USB gadget to the host.
+ """
+ return self.oUtsSession.syncGadgetConnect(self.idGadget);
+
+ def impersonate(self, sImpersonation, fSuperSpeed = False):
+ """
+ Impersonate a given device.
+ """
+
+ # Clear any previous impersonation
+ self.clearImpersonation();
+ self.sImpersonation = sImpersonation;
+
+ fRc = False;
+ if sImpersonation == g_ksGadgetImpersonationTest:
+ lstCfg = [];
+ if fSuperSpeed is True:
+ lstCfg.append( ('Gadget/SuperSpeed', g_kiGadgetCfgTypeBool, 'true') );
+ fDone = self.oUtsSession.syncGadgetCreate(g_kiGadgetTypeTest, g_kiGadgetAccessUsbIp, lstCfg);
+ if fDone is True and self.oUtsSession.isSuccess():
+ # Get the gadget ID.
+ _, _, abPayload = self.oUtsSession.getLastReply();
+
+ fRc = True;
+ self.idGadget = getU32(abPayload, 16);
+ self.iBusId = getU32(abPayload, 20);
+ self.iDevId = getU32(abPayload, 24);
+ else:
+ reporter.log('Invalid or unsupported impersonation');
+
+ return fRc;
+
+ def getUsbIpPort(self):
+ """
+ Returns the port the USB/IP server is listening on if requested,
+ None if USB/IP is not supported.
+ """
+ return self.iUsbIpPort;
+
+ def getGadgetBusAndDevId(self):
+ """
+ Returns the bus ad device ID of the gadget as a tuple.
+ """
+ return (self.iBusId, self.iDevId);
+
+ def connectTo(self, cMsTimeout, sHostname, uPort = None, fUsbIpSupport = True, cMsIdleFudge = 0, fTryConnect = False):
+ """
+ Connects to the specified target device.
+ Returns True on Success.
+ Returns False otherwise.
+ """
+ fRc = True;
+
+ # @todo
+ if fUsbIpSupport is False:
+ return False;
+
+ reporter.log2('openTcpSession(%s, %s, %s, %s)' % \
+ (cMsTimeout, sHostname, uPort, cMsIdleFudge));
+ try:
+ oTransport = TransportTcp(sHostname, uPort);
+ self.oUtsSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect);
+
+ if self.oUtsSession is not None:
+ fDone = self.oUtsSession.waitForTask(30*1000);
+ reporter.log('connect: waitForTask -> %s, result %s' % (fDone, self.oUtsSession.getResult()));
+ if fDone is True and self.oUtsSession.isSuccess():
+ # Parse the reply.
+ _, _, abPayload = self.oUtsSession.getLastReply();
+
+ if getU32(abPayload, 20) is g_kiGadgetAccessUsbIp:
+ fRc = True;
+ self.iUsbIpPort = getU32(abPayload, 24);
+ else:
+ reporter.log('Gadget doesn\'t support access over USB/IP despite being requested');
+ fRc = False;
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+ except:
+ reporter.errorXcpt(None, 15);
+ return False;
+
+ return fRc;
+
+ def disconnectFrom(self):
+ """
+ Disconnects from the target device.
+ """
+ fRc = True;
+
+ self.clearImpersonation();
+ if self.oUtsSession is not None:
+ fRc = self.oUtsSession.syncDisconnect();
+
+ return fRc;
diff --git a/src/VBox/ValidationKit/utils/Makefile.kmk b/src/VBox/ValidationKit/utils/Makefile.kmk
new file mode 100644
index 00000000..b527a640
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/Makefile.kmk
@@ -0,0 +1,78 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Utilities.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Include sub-makefiles.
+#
+include $(PATH_SUB_CURRENT)/TestExecServ/Makefile.kmk
+include $(PATH_SUB_CURRENT)/audio/Makefile.kmk
+ifeq ($(KBUILD_TARGET),win)
+ include $(PATH_SUB_CURRENT)/clipboard/Makefile.kmk
+endif
+include $(PATH_SUB_CURRENT)/cpu/Makefile.kmk
+include $(PATH_SUB_CURRENT)/fs/Makefile.kmk
+include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
+include $(PATH_SUB_CURRENT)/network/Makefile.kmk
+ifeq ($(KBUILD_TARGET),win)
+ include $(PATH_SUB_CURRENT)/nt/Makefile.kmk
+endif
+include $(PATH_SUB_CURRENT)/serial/Makefile.kmk
+include $(PATH_SUB_CURRENT)/storage/Makefile.kmk
+ifeq ($(KBUILD_TARGET),linux)
+ include $(PATH_SUB_CURRENT)/usb/Makefile.kmk
+endif
+
+#
+# On OS/2 the binaries requires the libc DLLs
+# (no official static linking support).
+#
+INSTALLS.os2 += ValidationKitOs2LibC
+ValidationKitOs2LibC_TEMPLATE = VBoxValidationKitR3
+ValidationKitOs2LibC_SOURCES = \
+ $(KBUILD_BIN_PATH)/libc06.dll \
+ $(KBUILD_BIN_PATH)/libc061.dll \
+ $(KBUILD_BIN_PATH)/libc062.dll \
+ $(KBUILD_BIN_PATH)/libc063.dll \
+ $(KBUILD_BIN_PATH)/libc064.dll \
+ $(KBUILD_BIN_PATH)/libc065.dll \
+ $(KBUILD_BIN_PATH)/libc066.dll
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/Makefile.kmk b/src/VBox/ValidationKit/utils/TestExecServ/Makefile.kmk
new file mode 100644
index 00000000..7fbf9c47
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/Makefile.kmk
@@ -0,0 +1,87 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - The Basic Remote Execution Service.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+PROGRAMS += TestExecService
+TestExecService_TEMPLATE = VBoxValidationKitR3
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ TestExecService_DEFS = \
+ KBUILD_TARGET="$(KBUILD_TARGET)" \
+ KBUILD_TARGET_ARCH="$(KBUILD_TARGET_ARCH)"
+else
+ TestExecService_DEFS = \
+ KBUILD_TARGET=\"$(KBUILD_TARGET)\" \
+ KBUILD_TARGET_ARCH=\"$(KBUILD_TARGET_ARCH)\"
+endif
+TestExecService_SOURCES = \
+ TestExecService.cpp \
+ TestExecServiceTcp.cpp
+
+ifn1of ($(KBUILD_TARGET), os2)
+ TestExecService_SOURCES += \
+ TestExecServiceSerial.cpp
+endif
+
+INSTALLS += TestExecServiceFiles
+TestExecServiceFiles_TEMPLATE = VBoxValidationKitR3
+TestExecServiceFiles_INST = $(INST_VALIDATIONKIT)
+TestExecServiceFiles_SOURCES := \
+ vboxtxs-readme.txt
+
+TestExecServiceFiles_EXEC_SOURCES.linux := \
+ $(PATH_SUB_CURRENT)/linux/vboxtxs.sh=>linux/vboxtxs \
+ $(PATH_SUB_CURRENT)/linux/vboxtxs.service=>linux/vboxtxs.service \
+ $(PATH_SUB_CURRENT)/linux/vboxtxs-nat.sh=>linux/vboxtxs-nat
+
+TestExecServiceFiles_SOURCES.solaris := \
+ $(PATH_SUB_CURRENT)/solaris/vboxtxs.xml=>solaris/vboxtxs.xml \
+ $(PATH_SUB_CURRENT)/solaris/vboxtxs-sol10.xml=>solaris/vboxtxs-sol10.xml
+TestExecServiceFiles_EXEC_SOURCES.solaris := \
+ $(PATH_SUB_CURRENT)/solaris/vboxtxs.sh=>solaris/vboxtxs.sh
+
+TestExecServiceFiles_SOURCES.win := \
+ $(PATH_SUB_CURRENT)/win/vboxtxs.reg=>win/vboxtxs.reg \
+ $(PATH_SUB_CURRENT)/win/vboxtxs-nat.reg=>win/vboxtxs-nat.reg
+TestExecServiceFiles_EXEC_SOURCES.win := \
+ $(PATH_SUB_CURRENT)/win/vboxtxs.cmd=>win/vboxtxs.cmd \
+ $(PATH_SUB_CURRENT)/win/vboxtxs-nat.cmd=>win/vboxtxs-nat.cmd
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/TestExecService.cpp b/src/VBox/ValidationKit/utils/TestExecServ/TestExecService.cpp
new file mode 100644
index 00000000..002d746b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/TestExecService.cpp
@@ -0,0 +1,4037 @@
+/* $Id: TestExecService.cpp $ */
+/** @file
+ * TestExecServ - Basic Remote Execution Service.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/cdrom.h>
+#include <iprt/critsect.h>
+#include <iprt/crc.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/handle.h>
+#include <iprt/initterm.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/uuid.h>
+#include <iprt/zip.h>
+
+#include <package-generated.h>
+#include "product-generated.h"
+
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+#include "product-generated.h"
+#include "TestExecServiceInternal.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Handle IDs used by txsDoExec for the poll set.
+ */
+typedef enum TXSEXECHNDID
+{
+ TXSEXECHNDID_STDIN = 0,
+ TXSEXECHNDID_STDOUT,
+ TXSEXECHNDID_STDERR,
+ TXSEXECHNDID_TESTPIPE,
+ TXSEXECHNDID_STDIN_WRITABLE,
+ TXSEXECHNDID_TRANSPORT,
+ TXSEXECHNDID_THREAD
+} TXSEXECHNDID;
+
+
+/**
+ * For buffering process input supplied by the client.
+ */
+typedef struct TXSEXECSTDINBUF
+{
+ /** The mount of buffered data. */
+ size_t cb;
+ /** The current data offset. */
+ size_t off;
+ /** The data buffer. */
+ char *pch;
+ /** The amount of allocated buffer space. */
+ size_t cbAllocated;
+ /** Send further input into the bit bucket (stdin is dead). */
+ bool fBitBucket;
+ /** The CRC-32 for standard input (received part). */
+ uint32_t uCrc32;
+} TXSEXECSTDINBUF;
+/** Pointer to a standard input buffer. */
+typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF;
+
+/**
+ * TXS child process info.
+ */
+typedef struct TXSEXEC
+{
+ PCTXSPKTHDR pPktHdr;
+ RTMSINTERVAL cMsTimeout;
+ int rcReplySend;
+
+ RTPOLLSET hPollSet;
+ RTPIPE hStdInW;
+ RTPIPE hStdOutR;
+ RTPIPE hStdErrR;
+ RTPIPE hTestPipeR;
+ RTPIPE hWakeUpPipeR;
+ RTTHREAD hThreadWaiter;
+
+ /** @name For the setup phase
+ * @{ */
+ struct StdPipe
+ {
+ RTHANDLE hChild;
+ PRTHANDLE phChild;
+ } StdIn,
+ StdOut,
+ StdErr;
+ RTPIPE hTestPipeW;
+ RTENV hEnv;
+ /** @} */
+
+ /** For serializating some access. */
+ RTCRITSECT CritSect;
+ /** @name Members protected by the critical section.
+ * @{ */
+ RTPROCESS hProcess;
+ /** The process status. Only valid when fProcessAlive is cleared. */
+ RTPROCSTATUS ProcessStatus;
+ /** Set when the process is alive, clear when dead. */
+ bool volatile fProcessAlive;
+ /** The end of the pipe that hThreadWaiter writes to. */
+ RTPIPE hWakeUpPipeW;
+ /** @} */
+} TXSEXEC;
+/** Pointer to a the TXS child process info. */
+typedef TXSEXEC *PTXSEXEC;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Transport layers.
+ */
+static const PCTXSTRANSPORT g_apTransports[] =
+{
+ &g_TcpTransport,
+#ifndef RT_OS_OS2
+ &g_SerialTransport,
+#endif
+ //&g_FileSysTransport,
+ //&g_GuestPropTransport,
+ //&g_TestDevTransport,
+};
+
+/** The release logger. */
+static PRTLOGGER g_pRelLogger;
+/** The select transport layer. */
+static PCTXSTRANSPORT g_pTransport;
+/** The scratch path. */
+static char g_szScratchPath[RTPATH_MAX];
+/** The default scratch path. */
+static char g_szDefScratchPath[RTPATH_MAX];
+/** The CD/DVD-ROM path. */
+static char g_szCdRomPath[RTPATH_MAX];
+/** The default CD/DVD-ROM path. */
+static char g_szDefCdRomPath[RTPATH_MAX];
+/** The directory containing the TXS executable. */
+static char g_szTxsDir[RTPATH_MAX];
+/** The current working directory for TXS (doesn't change). */
+static char g_szCwd[RTPATH_MAX];
+/** The operating system short name. */
+static char g_szOsShortName[16];
+/** The CPU architecture short name. */
+static char g_szArchShortName[16];
+/** The combined "OS.arch" name. */
+static char g_szOsDotArchShortName[32];
+/** The combined "OS/arch" name. */
+static char g_szOsSlashArchShortName[32];
+/** The executable suffix. */
+static char g_szExeSuff[8];
+/** The shell script suffix. */
+static char g_szScriptSuff[8];
+/** UUID identifying this TXS instance. This can be used to see if TXS
+ * has been restarted or not. */
+static RTUUID g_InstanceUuid;
+/** Whether to display the output of the child process or not. */
+static bool g_fDisplayOutput = true;
+/** Whether to terminate or not.
+ * @todo implement signals and stuff. */
+static bool volatile g_fTerminate = false;
+/** Verbosity level. */
+uint32_t g_cVerbose = 1;
+
+
+/**
+ * Calculates the checksum value, zero any padding space and send the packet.
+ *
+ * @returns IPRT status code.
+ * @param pPkt The packet to send. Must point to a correctly
+ * aligned buffer.
+ */
+static int txsSendPkt(PTXSPKTHDR pPkt)
+{
+ Assert(pPkt->cb >= sizeof(*pPkt));
+ pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(TXSPKTHDR, achOpcode));
+ if (pPkt->cb != RT_ALIGN_32(pPkt->cb, TXSPKT_ALIGNMENT))
+ memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, TXSPKT_ALIGNMENT) - pPkt->cb);
+
+ Log(("txsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));
+ Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
+ int rc = g_pTransport->pfnSendPkt(pPkt);
+ while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
+ rc = g_pTransport->pfnSendPkt(pPkt);
+ if (RT_FAILURE(rc))
+ Log(("txsSendPkt: rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+/**
+ * Sends a babble reply and disconnects the client (if applicable).
+ *
+ * @param pszOpcode The BABBLE opcode.
+ */
+static void txsReplyBabble(const char *pszOpcode)
+{
+ TXSPKTHDR Reply;
+ Reply.cb = sizeof(Reply);
+ Reply.uCrc32 = 0;
+ memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
+
+ g_pTransport->pfnBabble(&Reply, 20*1000);
+}
+
+/**
+ * Receive and validate a packet.
+ *
+ * Will send bable responses to malformed packets that results in a error status
+ * code.
+ *
+ * @returns IPRT status code.
+ * @param ppPktHdr Where to return the packet on success. Free
+ * with RTMemFree.
+ * @param fAutoRetryOnFailure Whether to retry on error.
+ */
+static int txsRecvPkt(PPTXSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
+{
+ for (;;)
+ {
+ PTXSPKTHDR pPktHdr;
+ int rc = g_pTransport->pfnRecvPkt(&pPktHdr);
+ if (RT_SUCCESS(rc))
+ {
+ /* validate the packet. */
+ if ( pPktHdr->cb >= sizeof(TXSPKTHDR)
+ && pPktHdr->cb < TXSPKT_MAX_SIZE)
+ {
+ Log2(("txsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
+ "%.*Rhxd\n",
+ pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
+ uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
+ ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(TXSPKTHDR, achOpcode))
+ : 0;
+ if (pPktHdr->uCrc32 == uCrc32Calc)
+ {
+ AssertCompileMemberSize(TXSPKTHDR, achOpcode, 8);
+ if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
+ && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
+ && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
+ )
+ {
+ Log(("txsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
+ *ppPktHdr = pPktHdr;
+ return rc;
+ }
+
+ rc = VERR_IO_BAD_COMMAND;
+ }
+ else
+ {
+ Log(("txsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
+ pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
+ rc = VERR_IO_CRC;
+ }
+ }
+ else
+ rc = VERR_IO_BAD_LENGTH;
+
+ /* Send babble reply and disconnect the client if the transport is
+ connection oriented. */
+ if (rc == VERR_IO_BAD_LENGTH)
+ txsReplyBabble("BABBLE L");
+ else if (rc == VERR_IO_CRC)
+ txsReplyBabble("BABBLE C");
+ else if (rc == VERR_IO_BAD_COMMAND)
+ txsReplyBabble("BABBLE O");
+ else
+ txsReplyBabble("BABBLE ");
+ RTMemFree(pPktHdr);
+ }
+
+ /* Try again or return failure? */
+ if ( g_fTerminate
+ || rc != VERR_INTERRUPTED
+ || !fAutoRetryOnFailure
+ )
+ {
+ Log(("txsRecvPkt: rc=%Rrc\n", rc));
+ return rc;
+ }
+ }
+}
+
+/**
+ * Make a simple reply, only status opcode.
+ *
+ * @returns IPRT status code of the send.
+ * @param pReply The reply packet.
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param cbExtra Bytes in addition to the header.
+ */
+static int txsReplyInternal(PTXSPKTHDR pReply, const char *pszOpcode, size_t cbExtra)
+{
+ /* copy the opcode, don't be too strict in case of a padding screw up. */
+ size_t cchOpcode = strlen(pszOpcode);
+ if (RT_LIKELY(cchOpcode == sizeof(pReply->achOpcode)))
+ memcpy(pReply->achOpcode, pszOpcode, sizeof(pReply->achOpcode));
+ else
+ {
+ Assert(cchOpcode == sizeof(pReply->achOpcode));
+ while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
+ cchOpcode--;
+ AssertMsgReturn(cchOpcode < sizeof(pReply->achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
+ memcpy(pReply->achOpcode, pszOpcode, cchOpcode);
+ memset(&pReply->achOpcode[cchOpcode], ' ', sizeof(pReply->achOpcode) - cchOpcode);
+ }
+
+ pReply->cb = (uint32_t)sizeof(TXSPKTHDR) + (uint32_t)cbExtra;
+ pReply->uCrc32 = 0; /* (txsSendPkt sets it) */
+
+ return txsSendPkt(pReply);
+}
+
+/**
+ * Make a simple reply, only status opcode.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The original packet (for future use).
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ */
+static int txsReplySimple(PCTXSPKTHDR pPktHdr, const char *pszOpcode)
+{
+ TXSPKTHDR Pkt;
+ NOREF(pPktHdr);
+ return txsReplyInternal(&Pkt, pszOpcode, 0);
+}
+
+/**
+ * Acknowledges a packet with success.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The original packet (for future use).
+ */
+static int txsReplyAck(PCTXSPKTHDR pPktHdr)
+{
+ return txsReplySimple(pPktHdr, "ACK ");
+}
+
+/**
+ * Replies with a failure.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The original packet (for future use).
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param pszDetailFmt Longer description of the problem (format
+ * string).
+ * @param va Format arguments.
+ */
+static int txsReplyFailureV(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, va_list va)
+{
+ NOREF(pPktHdr);
+ union
+ {
+ TXSPKTHDR Hdr;
+ char ach[256];
+ } uPkt;
+
+ size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(TXSPKTHDR)],
+ sizeof(uPkt) - sizeof(TXSPKTHDR),
+ pszDetailFmt, va);
+ return txsReplyInternal(&uPkt.Hdr, pszOpcode, cchDetail + 1);
+}
+
+/**
+ * Replies with a failure.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The original packet (for future use).
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param pszDetailFmt Longer description of the problem (format
+ * string).
+ * @param ... Format arguments.
+ */
+static int txsReplyFailure(PCTXSPKTHDR pPktHdr, const char *pszOpcode, const char *pszDetailFmt, ...)
+{
+ va_list va;
+ va_start(va, pszDetailFmt);
+ int rc = txsReplyFailureV(pPktHdr, pszOpcode, pszDetailFmt, va);
+ va_end(va);
+ return rc;
+}
+
+/**
+ * Replies according to the return code.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The packet to reply to.
+ * @param rcOperation The status code to report.
+ * @param pszOperationFmt The operation that failed. Typically giving the
+ * function call with important arguments.
+ * @param ... Arguments to the format string.
+ */
+static int txsReplyRC(PCTXSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
+{
+ if (RT_SUCCESS(rcOperation))
+ return txsReplyAck(pPktHdr);
+
+ char szOperation[128];
+ va_list va;
+ va_start(va, pszOperationFmt);
+ RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
+ va_end(va);
+
+ return txsReplyFailure(pPktHdr, "FAILED ", "%s failed with rc=%Rrc (opcode '%.8s')",
+ szOperation, rcOperation, pPktHdr->achOpcode);
+}
+
+/**
+ * Signal a bad packet minum size.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The packet to reply to.
+ * @param cbMin The minimum size.
+ */
+static int txsReplyBadMinSize(PCTXSPKTHDR pPktHdr, size_t cbMin)
+{
+ return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at least %zu bytes, got %u (opcode '%.8s')",
+ cbMin, pPktHdr->cb, pPktHdr->achOpcode);
+}
+
+/**
+ * Signal a bad packet exact size.
+ *
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The packet to reply to.
+ * @param cb The wanted size.
+ */
+static int txsReplyBadSize(PCTXSPKTHDR pPktHdr, size_t cb)
+{
+ return txsReplyFailure(pPktHdr, "BAD SIZE", "Expected at %zu bytes, got %u (opcode '%.8s')",
+ cb, pPktHdr->cb, pPktHdr->achOpcode);
+}
+
+/**
+ * Deals with a command that isn't implemented yet.
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The packet which opcode isn't implemented.
+ */
+static int txsReplyNotImplemented(PCTXSPKTHDR pPktHdr)
+{
+ return txsReplyFailure(pPktHdr, "NOT IMPL", "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);
+}
+
+/**
+ * Deals with a unknown command.
+ * @returns IPRT status code of the send.
+ * @param pPktHdr The packet to reply to.
+ */
+static int txsReplyUnknown(PCTXSPKTHDR pPktHdr)
+{
+ return txsReplyFailure(pPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known", pPktHdr->achOpcode);
+}
+
+/**
+ * Replaces a variable with its value.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param ppszNew In/Out.
+ * @param pcchNew In/Out. (Messed up on failure.)
+ * @param offVar Variable offset.
+ * @param cchVar Variable length.
+ * @param pszValue The value.
+ * @param cchValue Value length.
+ */
+static int txsReplaceStringVariable(char **ppszNew, size_t *pcchNew, size_t offVar, size_t cchVar,
+ const char *pszValue, size_t cchValue)
+{
+ size_t const cchAfter = *pcchNew - offVar - cchVar;
+ if (cchVar < cchValue)
+ {
+ *pcchNew += cchValue - cchVar;
+ int rc = RTStrRealloc(ppszNew, *pcchNew + 1);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ char *pszNew = *ppszNew;
+ memmove(&pszNew[offVar + cchValue], &pszNew[offVar + cchVar], cchAfter + 1);
+ memcpy(&pszNew[offVar], pszValue, cchValue);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Replace the variables found in the source string, returning a new string that
+ * lives on the string heap.
+ *
+ * @returns Boolean success indicator. Will reply to the client with all the
+ * gory detail on failure.
+ * @param pPktHdr The packet the string relates to. For replying
+ * on error.
+ * @param pszSrc The source string.
+ * @param ppszNew Where to return the new string.
+ * @param prcSend Where to return the status code of the send on
+ * failure.
+ */
+static int txsReplaceStringVariables(PCTXSPKTHDR pPktHdr, const char *pszSrc, char **ppszNew, int *prcSend)
+{
+ /* Lazy approach that employs memmove. */
+ size_t cchNew = strlen(pszSrc);
+ char *pszNew = RTStrDup(pszSrc);
+ char *pszDollar = pszNew;
+ while (pszDollar && (pszDollar = strchr(pszDollar, '$')) != NULL)
+ {
+ if (pszDollar[1] == '{')
+ {
+ char *pszEnd = strchr(&pszDollar[2], '}');
+ if (pszEnd)
+ {
+#define IF_VARIABLE_DO(pszDollar, szVarExpr, pszValue) \
+ if ( cchVar == sizeof(szVarExpr) - 1 \
+ && !memcmp(pszDollar, szVarExpr, sizeof(szVarExpr) - 1) ) \
+ { \
+ size_t const cchValue = strlen(pszValue); \
+ rc = txsReplaceStringVariable(&pszNew, &cchNew, offDollar, \
+ sizeof(szVarExpr) - 1, pszValue, cchValue); \
+ offDollar += cchValue; \
+ }
+ int rc;
+ size_t const cchVar = pszEnd - pszDollar + 1; /* includes "${}" */
+ size_t offDollar = pszDollar - pszNew;
+ IF_VARIABLE_DO(pszDollar, "${CDROM}", g_szCdRomPath)
+ else IF_VARIABLE_DO(pszDollar, "${SCRATCH}", g_szScratchPath)
+ else IF_VARIABLE_DO(pszDollar, "${ARCH}", g_szArchShortName)
+ else IF_VARIABLE_DO(pszDollar, "${OS}", g_szOsShortName)
+ else IF_VARIABLE_DO(pszDollar, "${OS.ARCH}", g_szOsDotArchShortName)
+ else IF_VARIABLE_DO(pszDollar, "${OS/ARCH}", g_szOsSlashArchShortName)
+ else IF_VARIABLE_DO(pszDollar, "${EXESUFF}", g_szExeSuff)
+ else IF_VARIABLE_DO(pszDollar, "${SCRIPTSUFF}", g_szScriptSuff)
+ else IF_VARIABLE_DO(pszDollar, "${TXSDIR}", g_szTxsDir)
+ else IF_VARIABLE_DO(pszDollar, "${CWD}", g_szCwd)
+ else if ( cchVar >= sizeof("${env.") + 1
+ && memcmp(pszDollar, RT_STR_TUPLE("${env.")) == 0)
+ {
+ const char *pszEnvVar = pszDollar + 6;
+ size_t cchValue = 0;
+ char szValue[RTPATH_MAX];
+ *pszEnd = '\0';
+ rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, szValue, sizeof(szValue), &cchValue);
+ if (RT_SUCCESS(rc))
+ {
+ *pszEnd = '}';
+ rc = txsReplaceStringVariable(&pszNew, &cchNew, offDollar, cchVar, szValue, cchValue);
+ offDollar += cchValue;
+ }
+ else
+ {
+ if (rc == VERR_ENV_VAR_NOT_FOUND)
+ *prcSend = txsReplyFailure(pPktHdr, "UNKN VAR", "Environment variable '%s' encountered in '%s'",
+ pszEnvVar, pszSrc);
+ else
+ *prcSend = txsReplyFailure(pPktHdr, "FAILDENV",
+ "RTEnvGetEx(,'%s',,,) failed with %Rrc (opcode '%.8s')",
+ pszEnvVar, rc, pPktHdr->achOpcode);
+ RTStrFree(pszNew);
+ *ppszNew = NULL;
+ return false;
+ }
+ }
+ else
+ {
+ RTStrFree(pszNew);
+ *prcSend = txsReplyFailure(pPktHdr, "UNKN VAR", "Unknown variable '%.*s' encountered in '%s'",
+ cchVar, pszDollar, pszSrc);
+ *ppszNew = NULL;
+ return false;
+ }
+ pszDollar = &pszNew[offDollar];
+
+ if (RT_FAILURE(rc))
+ {
+ RTStrFree(pszNew);
+ *prcSend = txsReplyRC(pPktHdr, rc, "RTStrRealloc");
+ *ppszNew = NULL;
+ return false;
+ }
+#undef IF_VARIABLE_DO
+ }
+ }
+ /* Undo dollar escape sequences: $$ -> $ */
+ else if (pszDollar[1] == '$')
+ {
+ size_t cchLeft = cchNew - (&pszDollar[1] - pszNew);
+ memmove(pszDollar, &pszDollar[1], cchLeft);
+ pszDollar[cchLeft] = '\0';
+ cchNew -= 1;
+ }
+ else /* No match, move to next char to avoid endless looping. */
+ pszDollar++;
+ }
+
+ *ppszNew = pszNew;
+ *prcSend = VINF_SUCCESS;
+ return true;
+}
+
+/**
+ * Checks if the string is valid and returns the expanded version.
+ *
+ * @returns true if valid, false if invalid.
+ * @param pPktHdr The packet being unpacked.
+ * @param pszArgName The argument name.
+ * @param psz Pointer to the string within pPktHdr.
+ * @param ppszExp Where to return the expanded string. Must be
+ * freed by calling RTStrFree().
+ * @param ppszNext Where to return the pointer to the next field.
+ * If NULL, then we assume this string is at the
+ * end of the packet and will make sure it has the
+ * advertised length.
+ * @param prcSend Where to return the status code of the send on
+ * failure.
+ */
+static bool txsIsStringValid(PCTXSPKTHDR pPktHdr, const char *pszArgName, const char *psz,
+ char **ppszExp, const char **ppszNext, int *prcSend)
+{
+ *ppszExp = NULL;
+ if (ppszNext)
+ *ppszNext = NULL;
+
+ size_t const off = psz - (const char *)pPktHdr;
+ if (pPktHdr->cb <= off)
+ {
+ *prcSend = txsReplyFailure(pPktHdr, "STR MISS", "Missing string argument '%s' in '%.8s'",
+ pszArgName, pPktHdr->achOpcode);
+ return false;
+ }
+
+ size_t const cchMax = pPktHdr->cb - off;
+ const char *pszEnd = RTStrEnd(psz, cchMax);
+ if (!pszEnd)
+ {
+ *prcSend = txsReplyFailure(pPktHdr, "STR TERM", "The string argument '%s' in '%.8s' is unterminated",
+ pszArgName, pPktHdr->achOpcode);
+ return false;
+ }
+
+ if (!ppszNext && (size_t)(pszEnd - psz) != cchMax - 1)
+ {
+ *prcSend = txsReplyFailure(pPktHdr, "STR SHRT", "The string argument '%s' in '%.8s' is shorter than advertised",
+ pszArgName, pPktHdr->achOpcode);
+ return false;
+ }
+
+ if (!txsReplaceStringVariables(pPktHdr, psz, ppszExp, prcSend))
+ return false;
+ if (ppszNext)
+ *ppszNext = pszEnd + 1;
+ return true;
+}
+
+/**
+ * Validates a packet with a single string after the header.
+ *
+ * @returns true if valid, false if invalid.
+ * @param pPktHdr The packet.
+ * @param pszArgName The argument name.
+ * @param ppszExp Where to return the string pointer. Variables
+ * will be replaced and it must therefore be freed
+ * by calling RTStrFree().
+ * @param prcSend Where to return the status code of the send on
+ * failure.
+ */
+static bool txsIsStringPktValid(PCTXSPKTHDR pPktHdr, const char *pszArgName, char **ppszExp, int *prcSend)
+{
+ if (pPktHdr->cb < sizeof(TXSPKTHDR) + 2)
+ {
+ *ppszExp = NULL;
+ *prcSend = txsReplyBadMinSize(pPktHdr, sizeof(TXSPKTHDR) + 2);
+ return false;
+ }
+
+ return txsIsStringValid(pPktHdr, pszArgName, (const char *)(pPktHdr + 1), ppszExp, NULL, prcSend);
+}
+
+/**
+ * Checks if the two opcodes match.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pPktHdr The packet header.
+ * @param pszOpcode2 The opcode we're comparing with. Does not have
+ * to be the whole 8 chars long.
+ */
+DECLINLINE(bool) txsIsSameOpcode(PCTXSPKTHDR pPktHdr, const char *pszOpcode2)
+{
+ if (pPktHdr->achOpcode[0] != pszOpcode2[0])
+ return false;
+ if (pPktHdr->achOpcode[1] != pszOpcode2[1])
+ return false;
+
+ unsigned i = 2;
+ while ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
+ && pszOpcode2[i] != '\0')
+ {
+ if (pPktHdr->achOpcode[i] != pszOpcode2[i])
+ break;
+ i++;
+ }
+
+ if ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
+ && pszOpcode2[i] == '\0')
+ {
+ while ( i < RT_SIZEOFMEMB(TXSPKTHDR, achOpcode)
+ && pPktHdr->achOpcode[i] == ' ')
+ i++;
+ }
+
+ return i == RT_SIZEOFMEMB(TXSPKTHDR, achOpcode);
+}
+
+/**
+ * Used by txsDoGetFile to wait for a reply ACK from the client.
+ *
+ * @returns VINF_SUCCESS on ACK, VERR_GENERAL_FAILURE on NACK,
+ * VERR_NET_NOT_CONNECTED on unknown response (sending a bable reply),
+ * or whatever txsRecvPkt returns.
+ * @param pPktHdr The original packet (for future use).
+ */
+static int txsWaitForAck(PCTXSPKTHDR pPktHdr)
+{
+ NOREF(pPktHdr);
+ /** @todo timeout? */
+ PTXSPKTHDR pReply;
+ int rc = txsRecvPkt(&pReply, false /*fAutoRetryOnFailure*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (txsIsSameOpcode(pReply, "ACK"))
+ rc = VINF_SUCCESS;
+ else if (txsIsSameOpcode(pReply, "NACK"))
+ rc = VERR_GENERAL_FAILURE;
+ else
+ {
+ txsReplyBabble("BABBLE ");
+ rc = VERR_NET_NOT_CONNECTED;
+ }
+ RTMemFree(pReply);
+ }
+ return rc;
+}
+
+/**
+ * Expands the variables in the string and sends it back to the host.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The expand string packet.
+ */
+static int txsDoExpandString(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszExpanded;
+ if (!txsIsStringPktValid(pPktHdr, "string", &pszExpanded, &rc))
+ return rc;
+
+ struct
+ {
+ TXSPKTHDR Hdr;
+ char szString[_64K];
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+
+ size_t const cbExpanded = strlen(pszExpanded) + 1;
+ if (cbExpanded <= sizeof(Pkt.szString))
+ {
+ memcpy(Pkt.szString, pszExpanded, cbExpanded);
+ rc = txsReplyInternal(&Pkt.Hdr, "STRING ", cbExpanded);
+ }
+ else
+ {
+ memcpy(Pkt.szString, pszExpanded, sizeof(Pkt.szString));
+ Pkt.szString[0] = '\0';
+ rc = txsReplyInternal(&Pkt.Hdr, "SHORTSTR", sizeof(Pkt.szString));
+ }
+
+ RTStrFree(pszExpanded);
+ return rc;
+}
+
+/**
+ * Packs a tar file / directory.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The pack file packet.
+ */
+static int txsDoPackFile(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszFile = NULL;
+ char *pszSource = NULL;
+
+ /* Packet cursor. */
+ const char *pch = (const char *)(pPktHdr + 1);
+
+ if (txsIsStringValid(pPktHdr, "file", pch, &pszFile, &pch, &rc))
+ {
+ if (txsIsStringValid(pPktHdr, "source", pch, &pszSource, &pch, &rc))
+ {
+ char *pszSuff = RTPathSuffix(pszFile);
+
+ const char *apszArgs[7];
+ unsigned cArgs = 0;
+
+ apszArgs[cArgs++] = "RTTar";
+ apszArgs[cArgs++] = "--create";
+
+ apszArgs[cArgs++] = "--file";
+ apszArgs[cArgs++] = pszFile;
+
+ if ( pszSuff
+ && ( !RTStrICmp(pszSuff, ".gz")
+ || !RTStrICmp(pszSuff, ".tgz")))
+ apszArgs[cArgs++] = "--gzip";
+
+ apszArgs[cArgs++] = pszSource;
+
+ RTEXITCODE rcExit = RTZipTarCmd(cArgs, (char **)apszArgs);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ rc = VERR_GENERAL_FAILURE; /** @todo proper return code. */
+ else
+ rc = VINF_SUCCESS;
+
+ rc = txsReplyRC(pPktHdr, rc, "RTZipTarCmd(\"%s\",\"%s\")",
+ pszFile, pszSource);
+
+ RTStrFree(pszSource);
+ }
+ RTStrFree(pszFile);
+ }
+
+ return rc;
+}
+
+/**
+ * Unpacks a tar file.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The unpack file packet.
+ */
+static int txsDoUnpackFile(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszFile = NULL;
+ char *pszDirectory = NULL;
+
+ /* Packet cursor. */
+ const char *pch = (const char *)(pPktHdr + 1);
+
+ if (txsIsStringValid(pPktHdr, "file", pch, &pszFile, &pch, &rc))
+ {
+ if (txsIsStringValid(pPktHdr, "directory", pch, &pszDirectory, &pch, &rc))
+ {
+ char *pszSuff = RTPathSuffix(pszFile);
+
+ const char *apszArgs[7];
+ unsigned cArgs = 0;
+
+ apszArgs[cArgs++] = "RTTar";
+ apszArgs[cArgs++] = "--extract";
+
+ apszArgs[cArgs++] = "--file";
+ apszArgs[cArgs++] = pszFile;
+
+ apszArgs[cArgs++] = "--directory";
+ apszArgs[cArgs++] = pszDirectory;
+
+ if ( pszSuff
+ && ( !RTStrICmp(pszSuff, ".gz")
+ || !RTStrICmp(pszSuff, ".tgz")))
+ apszArgs[cArgs++] = "--gunzip";
+
+ RTEXITCODE rcExit = RTZipTarCmd(cArgs, (char **)apszArgs);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ rc = VERR_GENERAL_FAILURE; /** @todo proper return code. */
+ else
+ rc = VINF_SUCCESS;
+
+ rc = txsReplyRC(pPktHdr, rc, "RTZipTarCmd(\"%s\",\"%s\")",
+ pszFile, pszDirectory);
+
+ RTStrFree(pszDirectory);
+ }
+ RTStrFree(pszFile);
+ }
+
+ return rc;
+}
+
+/**
+ * Downloads a file to the client.
+ *
+ * The transfer sends a stream of DATA packets (0 or more) and ends it all with
+ * a ACK packet. If an error occurs, a FAILURE packet is sent and the transfer
+ * aborted.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The get file packet.
+ */
+static int txsDoGetFile(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
+ return rc;
+
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uMyCrc32 = RTCrc32Start();
+ for (;;)
+ {
+ struct
+ {
+ TXSPKTHDR Hdr;
+ uint32_t uCrc32;
+ char ab[_64K];
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+ size_t cbRead;
+ rc = RTFileRead(hFile, &Pkt.ab[0], _64K, &cbRead);
+ if (RT_FAILURE(rc) || cbRead == 0)
+ {
+ if (rc == VERR_EOF || (RT_SUCCESS(rc) && cbRead == 0))
+ {
+ Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
+ rc = txsReplyInternal(&Pkt.Hdr, "DATA EOF", sizeof(uint32_t));
+ if (RT_SUCCESS(rc))
+ rc = txsWaitForAck(&Pkt.Hdr);
+ }
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTFileRead");
+ break;
+ }
+
+ uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead);
+ Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
+ rc = txsReplyInternal(&Pkt.Hdr, "DATA ", cbRead + sizeof(uint32_t));
+ if (RT_FAILURE(rc))
+ break;
+ rc = txsWaitForAck(&Pkt.Hdr);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ RTFileClose(hFile);
+ }
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath);
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Copies a file from the source to the destination locally.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The copy file packet.
+ */
+static int txsDoCopyFile(PCTXSPKTHDR pPktHdr)
+{
+ /* After the packet header follows a 32-bit file mode,
+ * the remainder of the packet are two zero terminated paths. */
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+
+ /* Packet cursor. */
+ const char *pch = (const char *)(pPktHdr + 1);
+
+ int rc;
+
+ RTFMODE const fMode = *(RTFMODE const *)pch;
+
+ char *pszSrc;
+ if (txsIsStringValid(pPktHdr, "source", (const char *)pch + sizeof(RTFMODE), &pszSrc, &pch, &rc))
+ {
+ char *pszDst;
+ if (txsIsStringValid(pPktHdr, "dest", pch, &pszDst, NULL /* Check for string termination */, &rc))
+ {
+ rc = RTFileCopy(pszSrc, pszDst);
+ if (RT_SUCCESS(rc))
+ {
+ if (fMode) /* Do we need to set the file mode? */
+ {
+ rc = RTPathSetMode(pszDst, fMode);
+ if (RT_FAILURE(rc))
+ rc = txsReplyRC(pPktHdr, rc, "RTPathSetMode(\"%s\", %#x)", pszDst, fMode);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = txsReplyAck(pPktHdr);
+ }
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTFileCopy");
+ RTStrFree(pszDst);
+ }
+
+ RTStrFree(pszSrc);
+ }
+
+ return rc;
+}
+
+/**
+ * Uploads a file from the client.
+ *
+ * The transfer sends a stream of DATA packets (0 or more) and ends it all with
+ * a DATA EOF packet. We ACK each of these, so that if a write error occurs we
+ * can abort the transfer straight away.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The put file packet.
+ * @param fHasMode Set if the packet starts with a mode field.
+ */
+static int txsDoPutFile(PCTXSPKTHDR pPktHdr, bool fHasMode)
+{
+ int rc;
+ RTFMODE fMode = 0;
+ char *pszPath;
+ if (!fHasMode)
+ {
+ if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
+ return rc;
+ }
+ else
+ {
+ /* After the packet header follows a mode mask and the remainder of
+ the packet is the zero terminated file name. */
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+ if (!txsIsStringValid(pPktHdr, "file", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
+ return rc;
+ fMode = *(RTFMODE const *)(pPktHdr + 1);
+ fMode <<= RTFILE_O_CREATE_MODE_SHIFT;
+ fMode &= RTFILE_O_CREATE_MODE_MASK;
+ }
+
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszPath, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE | fMode);
+ if (RT_SUCCESS(rc))
+ {
+ bool fSuccess = false;
+ rc = txsReplyAck(pPktHdr);
+ if (RT_SUCCESS(rc))
+ {
+ if (fMode)
+ RTFileSetMode(hFile, fMode);
+
+ /*
+ * Read client command packets and process them.
+ */
+ uint32_t uMyCrc32 = RTCrc32Start();
+ for (;;)
+ {
+ PTXSPKTHDR pDataPktHdr;
+ rc = txsRecvPkt(&pDataPktHdr, false /*fAutoRetryOnFailure*/);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (txsIsSameOpcode(pDataPktHdr, "DATA"))
+ {
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(uint32_t);
+ if (pDataPktHdr->cb >= cbMin)
+ {
+ size_t cbData = pDataPktHdr->cb - cbMin;
+ const void *pvData = (const char *)pDataPktHdr + cbMin;
+ uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1);
+
+ uMyCrc32 = RTCrc32Process(uMyCrc32, pvData, cbData);
+ if (RTCrc32Finish(uMyCrc32) == uCrc32)
+ {
+ rc = RTFileWrite(hFile, pvData, cbData, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = txsReplyAck(pDataPktHdr);
+ RTMemFree(pDataPktHdr);
+ continue;
+ }
+
+ rc = txsReplyRC(pDataPktHdr, rc, "RTFileWrite");
+ }
+ else
+ rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32);
+ }
+ else
+ rc = txsReplyBadMinSize(pPktHdr, cbMin);
+ }
+ else if (txsIsSameOpcode(pDataPktHdr, "DATA EOF"))
+ {
+ if (pDataPktHdr->cb == sizeof(TXSPKTHDR) + sizeof(uint32_t))
+ {
+ uint32_t uCrc32 = *(uint32_t const *)(pDataPktHdr + 1);
+ if (RTCrc32Finish(uMyCrc32) == uCrc32)
+ {
+ rc = txsReplyAck(pDataPktHdr);
+ fSuccess = RT_SUCCESS(rc);
+ }
+ else
+ rc = txsReplyFailure(pDataPktHdr, "BAD DCRC", "mycrc=%#x your=%#x", uMyCrc32, uCrc32);
+ }
+ else
+ rc = txsReplyAck(pDataPktHdr);
+ }
+ else if (txsIsSameOpcode(pDataPktHdr, "ABORT"))
+ rc = txsReplyAck(pDataPktHdr);
+ else
+ rc = txsReplyFailure(pDataPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during PUT FILE", pDataPktHdr->achOpcode);
+ RTMemFree(pDataPktHdr);
+ break;
+ }
+ }
+
+ RTFileClose(hFile);
+
+ /*
+ * Delete the file on failure.
+ */
+ if (!fSuccess)
+ RTFileDelete(pszPath);
+ }
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTFileOpen(,\"%s\",)", pszPath);
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * List the entries in the specified directory.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The list packet.
+ */
+static int txsDoList(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
+ return rc;
+
+ rc = txsReplyNotImplemented(pPktHdr);
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Worker for STAT and LSTAT for packing down the file info reply.
+ *
+ * @returns IPRT status code from send.
+ * @param pInfo The info to pack down.
+ */
+static int txsReplyObjInfo(PCRTFSOBJINFO pInfo)
+{
+ struct
+ {
+ TXSPKTHDR Hdr;
+ int64_t cbObject;
+ int64_t cbAllocated;
+ int64_t nsAccessTime;
+ int64_t nsModificationTime;
+ int64_t nsChangeTime;
+ int64_t nsBirthTime;
+ uint32_t fMode;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t cHardLinks;
+ uint64_t INodeIdDevice;
+ uint64_t INodeId;
+ uint64_t Device;
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+
+ Pkt.cbObject = pInfo->cbObject;
+ Pkt.cbAllocated = pInfo->cbAllocated;
+ Pkt.nsAccessTime = RTTimeSpecGetNano(&pInfo->AccessTime);
+ Pkt.nsModificationTime = RTTimeSpecGetNano(&pInfo->ModificationTime);
+ Pkt.nsChangeTime = RTTimeSpecGetNano(&pInfo->ChangeTime);
+ Pkt.nsBirthTime = RTTimeSpecGetNano(&pInfo->BirthTime);
+ Pkt.fMode = pInfo->Attr.fMode;
+ Pkt.uid = pInfo->Attr.u.Unix.uid;
+ Pkt.gid = pInfo->Attr.u.Unix.gid;
+ Pkt.cHardLinks = pInfo->Attr.u.Unix.cHardlinks;
+ Pkt.INodeIdDevice = pInfo->Attr.u.Unix.INodeIdDevice;
+ Pkt.INodeId = pInfo->Attr.u.Unix.INodeId;
+ Pkt.Device = pInfo->Attr.u.Unix.Device;
+
+ return txsReplyInternal(&Pkt.Hdr, "FILEINFO", sizeof(Pkt) - TXSPKT_ALIGNMENT - sizeof(TXSPKTHDR));
+}
+
+/**
+ * Get info about a file system object, following all but the symbolic links
+ * except in the final path component.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The lstat packet.
+ */
+static int txsDoLStat(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc))
+ return rc;
+
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ rc = txsReplyObjInfo(&Info);
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,ON_LINK)", pszPath);
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Get info about a file system object, following all symbolic links.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The stat packet.
+ */
+static int txsDoStat(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "path", &pszPath, &rc))
+ return rc;
+
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
+ if (RT_SUCCESS(rc))
+ rc = txsReplyObjInfo(&Info);
+ else
+ rc = txsReplyRC(pPktHdr, rc, "RTPathQueryInfoEx(\"%s\",,UNIX,FOLLOW_LINK)", pszPath);
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Checks if the specified path is a symbolic link.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The issymlnk packet.
+ */
+static int txsDoIsSymlnk(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc))
+ return rc;
+
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(Info.Attr.fMode))
+ rc = txsReplySimple(pPktHdr, "TRUE ");
+ else
+ rc = txsReplySimple(pPktHdr, "FALSE ");
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Checks if the specified path is a file or not.
+ *
+ * If the final path element is a symbolic link to a file, we'll return
+ * FALSE.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The isfile packet.
+ */
+static int txsDoIsFile(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
+ return rc;
+
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc) && RTFS_IS_FILE(Info.Attr.fMode))
+ rc = txsReplySimple(pPktHdr, "TRUE ");
+ else
+ rc = txsReplySimple(pPktHdr, "FALSE ");
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Checks if the specified path is a directory or not.
+ *
+ * If the final path element is a symbolic link to a directory, we'll return
+ * FALSE.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The isdir packet.
+ */
+static int txsDoIsDir(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
+ return rc;
+
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfoEx(pszPath, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(Info.Attr.fMode))
+ rc = txsReplySimple(pPktHdr, "TRUE ");
+ else
+ rc = txsReplySimple(pPktHdr, "FALSE ");
+
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Changes the owner of a file, directory or symbolic link.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The chmod packet.
+ */
+static int txsDoChOwn(PCTXSPKTHDR pPktHdr)
+{
+#ifdef RT_OS_WINDOWS
+ return txsReplyNotImplemented(pPktHdr);
+#else
+ /* After the packet header follows a 32-bit UID and 32-bit GID, while the
+ remainder of the packet is the zero terminated path. */
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+
+ int rc;
+ char *pszPath;
+ if (!txsIsStringValid(pPktHdr, "path", (const char *)(pPktHdr + 1) + sizeof(uint32_t) * 2, &pszPath, NULL, &rc))
+ return rc;
+
+ uint32_t uid = ((uint32_t const *)(pPktHdr + 1))[0];
+ uint32_t gid = ((uint32_t const *)(pPktHdr + 1))[1];
+
+ rc = RTPathSetOwnerEx(pszPath, uid, gid, RTPATH_F_ON_LINK);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTPathSetOwnerEx(\"%s\", %u, %u)", pszPath, uid, gid);
+ RTStrFree(pszPath);
+ return rc;
+#endif
+}
+
+/**
+ * Changes the mode of a file or directory.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The chmod packet.
+ */
+static int txsDoChMod(PCTXSPKTHDR pPktHdr)
+{
+ /* After the packet header follows a mode mask and the remainder of
+ the packet is the zero terminated file name. */
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+
+ int rc;
+ char *pszPath;
+ if (!txsIsStringValid(pPktHdr, "path", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
+ return rc;
+
+ RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1);
+
+ rc = RTPathSetMode(pszPath, fMode);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTPathSetMode(\"%s\", %o)", pszPath, fMode);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Removes a directory tree.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The rmtree packet.
+ */
+static int txsDoRmTree(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
+ return rc;
+
+ rc = RTDirRemoveRecursive(pszPath, 0 /*fFlags*/);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTDirRemoveRecusive(\"%s\",0)", pszPath);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Removes a symbolic link.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The rmsymlink packet.
+ */
+static int txsDoRmSymlnk(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "symlink", &pszPath, &rc))
+ return rc;
+
+ rc = RTSymlinkDelete(pszPath, 0);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTSymlinkDelete(\"%s\")", pszPath);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Removes a file.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The rmfile packet.
+ */
+static int txsDoRmFile(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "file", &pszPath, &rc))
+ return rc;
+
+ rc = RTFileDelete(pszPath);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTFileDelete(\"%s\")", pszPath);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Removes a directory.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The rmdir packet.
+ */
+static int txsDoRmDir(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ char *pszPath;
+ if (!txsIsStringPktValid(pPktHdr, "dir", &pszPath, &rc))
+ return rc;
+
+ rc = RTDirRemove(pszPath);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTDirRemove(\"%s\")", pszPath);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Creates a symbolic link.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The mksymlnk packet.
+ */
+static int txsDoMkSymlnk(PCTXSPKTHDR pPktHdr)
+{
+ return txsReplyNotImplemented(pPktHdr);
+}
+
+/**
+ * Creates a directory and all its parents.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The mkdir -p packet.
+ */
+static int txsDoMkDrPath(PCTXSPKTHDR pPktHdr)
+{
+ /* The same format as the MKDIR command. */
+ if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2)
+ return txsReplyBadMinSize(pPktHdr, sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2);
+
+ int rc;
+ char *pszPath;
+ if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
+ return rc;
+
+ RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1);
+
+ rc = RTDirCreateFullPathEx(pszPath, fMode, RTDIRCREATE_FLAGS_IGNORE_UMASK);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTDirCreateFullPath(\"%s\", %#x)", pszPath, fMode);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Creates a directory.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The mkdir packet.
+ */
+static int txsDoMkDir(PCTXSPKTHDR pPktHdr)
+{
+ /* After the packet header follows a mode mask and the remainder of
+ the packet is the zero terminated directory name. */
+ size_t const cbMin = sizeof(TXSPKTHDR) + sizeof(RTFMODE) + 2;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+
+ int rc;
+ char *pszPath;
+ if (!txsIsStringValid(pPktHdr, "dir", (const char *)(pPktHdr + 1) + sizeof(RTFMODE), &pszPath, NULL, &rc))
+ return rc;
+
+ RTFMODE fMode = *(RTFMODE const *)(pPktHdr + 1);
+ rc = RTDirCreate(pszPath, fMode, RTDIRCREATE_FLAGS_IGNORE_UMASK);
+
+ rc = txsReplyRC(pPktHdr, rc, "RTDirCreate(\"%s\", %#x)", pszPath, fMode);
+ RTStrFree(pszPath);
+ return rc;
+}
+
+/**
+ * Cleans up the scratch area.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The shutdown packet.
+ */
+static int txsDoCleanup(PCTXSPKTHDR pPktHdr)
+{
+ int rc = RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
+ return txsReplyRC(pPktHdr, rc, "RTDirRemoveRecursive(\"%s\", CONTENT_ONLY)", g_szScratchPath);
+}
+
+/**
+ * Ejects the specified DVD/CD drive.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The eject packet.
+ */
+static int txsDoCdEject(PCTXSPKTHDR pPktHdr)
+{
+ /* After the packet header follows a uint32_t ordinal. */
+ size_t const cbExpected = sizeof(TXSPKTHDR) + sizeof(uint32_t);
+ if (pPktHdr->cb != cbExpected)
+ return txsReplyBadSize(pPktHdr, cbExpected);
+ uint32_t iOrdinal = *(uint32_t const *)(pPktHdr + 1);
+
+ RTCDROM hCdrom;
+ int rc = RTCdromOpenByOrdinal(iOrdinal, RTCDROM_O_CONTROL, &hCdrom);
+ if (RT_FAILURE(rc))
+ return txsReplyRC(pPktHdr, rc, "RTCdromOpenByOrdinal(%u, RTCDROM_O_CONTROL, )", iOrdinal);
+ rc = RTCdromEject(hCdrom, true /*fForce*/);
+ RTCdromRelease(hCdrom);
+
+ return txsReplyRC(pPktHdr, rc, "RTCdromEject(ord=%u, fForce=true)", iOrdinal);
+}
+
+/**
+ * Common worker for txsDoShutdown and txsDoReboot.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The reboot packet.
+ * @param fAction Which action to take.
+ */
+static int txsCommonShutdownReboot(PCTXSPKTHDR pPktHdr, uint32_t fAction)
+{
+ /*
+ * We ACK the reboot & shutdown before actually performing them, then we
+ * terminate the transport layer.
+ *
+ * This is to make sure the client isn't stuck with a dead connection. The
+ * transport layer termination also make sure we won't accept new
+ * connections in case the client is too eager to reconnect to a rebooted
+ * test victim. On the down side, we cannot easily report RTSystemShutdown
+ * failures failures this way. But the client can kind of figure it out by
+ * reconnecting and seeing that our UUID was unchanged.
+ */
+ int rc;
+ if (pPktHdr->cb != sizeof(TXSPKTHDR))
+ return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
+ g_pTransport->pfnNotifyReboot();
+ rc = txsReplyAck(pPktHdr);
+ RTThreadSleep(2560); /* fudge factor */
+ g_pTransport->pfnTerm();
+
+ /*
+ * Do the job, if it fails we'll restart the transport layer.
+ */
+#if 0
+ rc = VINF_SUCCESS;
+#else
+ rc = RTSystemShutdown(0 /*cMsDelay*/,
+ fAction | RTSYSTEM_SHUTDOWN_PLANNED | RTSYSTEM_SHUTDOWN_FORCE,
+ "Test Execution Service");
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ RTMsgInfo(fAction == RTSYSTEM_SHUTDOWN_REBOOT ? "Rebooting...\n" : "Shutting down...\n");
+ g_fTerminate = true;
+ }
+ else
+ {
+ RTMsgError("RTSystemShutdown w/ fAction=%#x failed: %Rrc", fAction, rc);
+
+ int rc2 = g_pTransport->pfnInit();
+ if (RT_FAILURE(rc2))
+ {
+ g_fTerminate = true;
+ rc = rc2;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Shuts down the machine, powering it off if possible.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The shutdown packet.
+ */
+static int txsDoShutdown(PCTXSPKTHDR pPktHdr)
+{
+ return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_POWER_OFF_HALT);
+}
+
+/**
+ * Reboots the machine.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The reboot packet.
+ */
+static int txsDoReboot(PCTXSPKTHDR pPktHdr)
+{
+ return txsCommonShutdownReboot(pPktHdr, RTSYSTEM_SHUTDOWN_REBOOT);
+}
+
+/**
+ * Verifies and acknowledges a "UUID" request.
+ *
+ * @returns IPRT status code.
+ * @param pPktHdr The UUID packet.
+ */
+static int txsDoUuid(PCTXSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(TXSPKTHDR))
+ return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
+
+ struct
+ {
+ TXSPKTHDR Hdr;
+ char szUuid[RTUUID_STR_LENGTH];
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+
+ int rc = RTUuidToStr(&g_InstanceUuid, Pkt.szUuid, sizeof(Pkt.szUuid));
+ if (RT_FAILURE(rc))
+ return txsReplyRC(pPktHdr, rc, "RTUuidToStr");
+ return txsReplyInternal(&Pkt.Hdr, "ACK UUID", strlen(Pkt.szUuid) + 1);
+}
+
+/**
+ * Verifies and acknowledges a "BYE" request.
+ *
+ * @returns IPRT status code.
+ * @param pPktHdr The bye packet.
+ */
+static int txsDoBye(PCTXSPKTHDR pPktHdr)
+{
+ int rc;
+ if (pPktHdr->cb == sizeof(TXSPKTHDR))
+ rc = txsReplyAck(pPktHdr);
+ else
+ rc = txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
+ g_pTransport->pfnNotifyBye();
+ return rc;
+}
+
+/**
+ * Verifies and acknowledges a "VER" request.
+ *
+ * @returns IPRT status code.
+ * @param pPktHdr The version packet.
+ */
+static int txsDoVer(PCTXSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(TXSPKTHDR))
+ return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
+
+ struct
+ {
+ TXSPKTHDR Hdr;
+ char szVer[96];
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+
+ if (RTStrPrintf2(Pkt.szVer, sizeof(Pkt.szVer), "%s r%s %s.%s (%s %s)",
+ RTBldCfgVersion(), RTBldCfgRevisionStr(), KBUILD_TARGET, KBUILD_TARGET_ARCH, __DATE__, __TIME__) > 0)
+ {
+ return txsReplyInternal(&Pkt.Hdr, "ACK VER ", strlen(Pkt.szVer) + 1);
+ }
+
+ return txsReplyRC(pPktHdr, VERR_BUFFER_OVERFLOW, "RTStrPrintf2");
+}
+
+/**
+ * Verifies and acknowledges a "HOWDY" request.
+ *
+ * @returns IPRT status code.
+ * @param pPktHdr The howdy packet.
+ */
+static int txsDoHowdy(PCTXSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(TXSPKTHDR))
+ return txsReplyBadSize(pPktHdr, sizeof(TXSPKTHDR));
+ int rc = txsReplyAck(pPktHdr);
+ if (RT_SUCCESS(rc))
+ {
+ g_pTransport->pfnNotifyHowdy();
+ RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
+ }
+ return rc;
+}
+
+/**
+ * Replies according to the return code.
+ *
+ * @returns rcOperation and pTxsExec->rcReplySend.
+ * @param pTxsExec The TXSEXEC instance.
+ * @param rcOperation The status code to report.
+ * @param pszOperationFmt The operation that failed. Typically giving the
+ * function call with important arguments.
+ * @param ... Arguments to the format string.
+ */
+static int txsExecReplyRC(PTXSEXEC pTxsExec, int rcOperation, const char *pszOperationFmt, ...)
+{
+ AssertStmt(RT_FAILURE_NP(rcOperation), rcOperation = VERR_IPE_UNEXPECTED_INFO_STATUS);
+
+ char szOperation[128];
+ va_list va;
+ va_start(va, pszOperationFmt);
+ RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
+ va_end(va);
+
+ pTxsExec->rcReplySend = txsReplyFailure(pTxsExec->pPktHdr, "FAILED ",
+ "%s failed with rc=%Rrc (opcode '%.8s')",
+ szOperation, rcOperation, pTxsExec->pPktHdr->achOpcode);
+ return rcOperation;
+}
+
+
+/**
+ * Sends the process exit status reply to the TXS client.
+ *
+ * @returns IPRT status code of the send.
+ * @param pTxsExec The TXSEXEC instance.
+ * @param fProcessAlive Whether the process is still alive (against our
+ * will).
+ * @param fProcessTimedOut Whether the process timed out.
+ * @param MsProcessKilled When the process was killed, UINT64_MAX if not.
+ */
+static int txsExecSendExitStatus(PTXSEXEC pTxsExec, bool fProcessAlive, bool fProcessTimedOut, uint64_t MsProcessKilled)
+{
+ int rc;
+ if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
+ {
+ rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOK");
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process timed out and was killed\n");
+ }
+ else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
+ {
+ rc = txsReplySimple(pTxsExec->pPktHdr, "PROC TOA");
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process timed out and was not killed successfully\n");
+ }
+ else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX))
+ rc = txsReplySimple(pTxsExec->pPktHdr, "PROC DWN");
+ else if (fProcessAlive)
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process is alive when it should not");
+ AssertFailed();
+ }
+ else if (MsProcessKilled != UINT64_MAX)
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "Doofus! process has been killed when it should not");
+ AssertFailed();
+ }
+ else if ( pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL
+ && pTxsExec->ProcessStatus.iStatus == 0)
+ {
+ rc = txsReplySimple(pTxsExec->pPktHdr, "PROC OK ");
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process exited with status: 0\n");
+ }
+ else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC NOK", "%d", pTxsExec->ProcessStatus.iStatus);
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process exited with status: %d\n", pTxsExec->ProcessStatus.iStatus);
+ }
+ else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC SIG", "%d", pTxsExec->ProcessStatus.iStatus);
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process exited with status: signal %d\n", pTxsExec->ProcessStatus.iStatus);
+ }
+ else if (pTxsExec->ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC ABD", "");
+ if (g_fDisplayOutput)
+ RTPrintf("txs: Process exited with status: abend\n");
+ }
+ else
+ {
+ rc = txsReplyFailure(pTxsExec->pPktHdr, "PROC DOO", "enmReason=%d iStatus=%d",
+ pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus);
+ AssertMsgFailed(("enmReason=%d iStatus=%d", pTxsExec->ProcessStatus.enmReason, pTxsExec->ProcessStatus.iStatus));
+ }
+ return rc;
+}
+
+/**
+ * Handle pending output data or error on standard out, standard error or the
+ * test pipe.
+ *
+ * @returns IPRT status code from client send.
+ * @param hPollSet The polling set.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phPipeR The pipe handle.
+ * @param puCrc32 The current CRC-32 of the stream. (In/Out)
+ * @param enmHndId The handle ID.
+ * @param pszOpcode The opcode for the data upload.
+ *
+ * @todo Put the last 4 parameters into a struct!
+ */
+static int txsDoExecHlpHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
+ uint32_t *puCrc32, TXSEXECHNDID enmHndId, const char *pszOpcode)
+{
+ Log(("txsDoExecHlpHandleOutputEvent: %s fPollEvt=%#x\n", pszOpcode, fPollEvt));
+
+ /*
+ * Try drain the pipe before acting on any errors.
+ */
+ int rc = VINF_SUCCESS;
+ struct
+ {
+ TXSPKTHDR Hdr;
+ uint32_t uCrc32;
+ char abBuf[_64K];
+ char abPadding[TXSPKT_ALIGNMENT];
+ } Pkt;
+ size_t cbRead;
+ int rc2 = RTPipeRead(*phPipeR, Pkt.abBuf, sizeof(Pkt.abBuf), &cbRead);
+ if (RT_SUCCESS(rc2) && cbRead)
+ {
+ Log(("Crc32=%#x ", *puCrc32));
+ *puCrc32 = RTCrc32Process(*puCrc32, Pkt.abBuf, cbRead);
+ Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32));
+ Pkt.uCrc32 = RTCrc32Finish(*puCrc32);
+ if (g_fDisplayOutput)
+ {
+ if (enmHndId == TXSEXECHNDID_STDOUT)
+ RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
+ else if (enmHndId == TXSEXECHNDID_STDERR)
+ RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
+ }
+
+ rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t));
+
+ /* Make sure we go another poll round in case there was too much data
+ for the buffer to hold. */
+ fPollEvt &= RTPOLL_EVT_ERROR;
+ }
+ else if (RT_FAILURE(rc2))
+ {
+ fPollEvt |= RTPOLL_EVT_ERROR;
+ AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
+ }
+
+ /*
+ * If an error was raised signalled,
+ */
+ if (fPollEvt & RTPOLL_EVT_ERROR)
+ {
+ rc2 = RTPollSetRemove(hPollSet, enmHndId);
+ AssertRC(rc2);
+
+ rc2 = RTPipeClose(*phPipeR);
+ AssertRC(rc2);
+ *phPipeR = NIL_RTPIPE;
+ }
+ return rc;
+}
+
+/**
+ * Try write some more data to the standard input of the child.
+ *
+ * @returns IPRT status code.
+ * @param pStdInBuf The standard input buffer.
+ * @param hStdInW The standard input pipe.
+ */
+static int txsDoExecHlpWriteStdIn(PTXSEXECSTDINBUF pStdInBuf, RTPIPE hStdInW)
+{
+ size_t cbToWrite = pStdInBuf->cb - pStdInBuf->off;
+ size_t cbWritten;
+ int rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbWritten == cbToWrite);
+ pStdInBuf->off += cbWritten;
+ }
+ return rc;
+}
+
+/**
+ * Handle an error event on standard input.
+ *
+ * @param hPollSet The polling set.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phStdInW The standard input pipe handle.
+ * @param pStdInBuf The standard input buffer.
+ */
+static void txsDoExecHlpHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
+ PTXSEXECSTDINBUF pStdInBuf)
+{
+ NOREF(fPollEvt);
+ int rc2;
+ if (pStdInBuf->off < pStdInBuf->cb)
+ {
+ rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
+ AssertRC(rc2);
+ }
+
+ rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN);
+ AssertRC(rc2);
+
+ rc2 = RTPipeClose(*phStdInW);
+ AssertRC(rc2);
+ *phStdInW = NIL_RTPIPE;
+
+ RTMemFree(pStdInBuf->pch);
+ pStdInBuf->pch = NULL;
+ pStdInBuf->off = 0;
+ pStdInBuf->cb = 0;
+ pStdInBuf->cbAllocated = 0;
+ pStdInBuf->fBitBucket = true;
+}
+
+/**
+ * Handle an event indicating we can write to the standard input pipe of the
+ * child process.
+ *
+ * @param hPollSet The polling set.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param phStdInW The standard input pipe.
+ * @param pStdInBuf The standard input buffer.
+ */
+static void txsDoExecHlpHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
+ PTXSEXECSTDINBUF pStdInBuf)
+{
+ int rc;
+ if (!(fPollEvt & RTPOLL_EVT_ERROR))
+ {
+ rc = txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
+ if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE)
+ {
+ /** @todo do we need to do something about this error condition? */
+ AssertRC(rc);
+ }
+
+ if (pStdInBuf->off < pStdInBuf->cb)
+ {
+ rc = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
+ AssertRC(rc);
+ }
+ }
+ else
+ txsDoExecHlpHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
+}
+
+/**
+ * Handle a transport event or successful pfnPollIn() call.
+ *
+ * @returns IPRT status code from client send.
+ * @retval VINF_EOF indicates ABORT command.
+ *
+ * @param hPollSet The polling set.
+ * @param fPollEvt The event mask returned by RTPollNoResume.
+ * @param idPollHnd The handle ID.
+ * @param phStdInW The standard input pipe.
+ * @param pStdInBuf The standard input buffer.
+ */
+static int txsDoExecHlpHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
+ PRTPIPE phStdInW, PTXSEXECSTDINBUF pStdInBuf)
+{
+ /* ASSUMES the transport layer will detect or clear any error condition. */
+ NOREF(fPollEvt); NOREF(idPollHnd);
+ Log(("txsDoExecHlpHandleTransportEvent\n"));
+ /** @todo Use a callback for this case? */
+
+ /*
+ * Read client command packet and process it.
+ */
+ /** @todo Sometimes this hangs on windows because there isn't any data pending.
+ * We probably get woken up at the wrong time or in the wrong way, i.e. RTPoll()
+ * is busted for sockets.
+ *
+ * Temporary workaround: Poll for input before trying to read it. */
+ if (!g_pTransport->pfnPollIn())
+ {
+ Log(("Bad transport event\n"));
+ RTThreadYield();
+ return VINF_SUCCESS;
+ }
+ PTXSPKTHDR pPktHdr;
+ int rc = txsRecvPkt(&pPktHdr, false /*fAutoRetryOnFailure*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ Log(("Bad transport event\n"));
+
+ /*
+ * The most common thing here would be a STDIN request with data
+ * for the child process.
+ */
+ if (txsIsSameOpcode(pPktHdr, "STDIN"))
+ {
+ if ( !pStdInBuf->fBitBucket
+ && pPktHdr->cb >= sizeof(TXSPKTHDR) + sizeof(uint32_t))
+ {
+ uint32_t uCrc32 = *(uint32_t *)(pPktHdr + 1);
+ const char *pch = (const char *)(pPktHdr + 1) + sizeof(uint32_t);
+ size_t cb = pPktHdr->cb - sizeof(TXSPKTHDR) - sizeof(uint32_t);
+
+ /* Check the CRC */
+ pStdInBuf->uCrc32 = RTCrc32Process(pStdInBuf->uCrc32, pch, cb);
+ if (RTCrc32Finish(pStdInBuf->uCrc32) == uCrc32)
+ {
+
+ /* Rewind the buffer if it's empty. */
+ size_t cbInBuf = pStdInBuf->cb - pStdInBuf->off;
+ bool const fAddToSet = cbInBuf == 0;
+ if (fAddToSet)
+ pStdInBuf->cb = pStdInBuf->off = 0;
+
+ /* Try and see if we can simply append the data. */
+ if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated)
+ {
+ memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb);
+ pStdInBuf->cb += cb;
+ rc = txsReplyAck(pPktHdr);
+ }
+ else
+ {
+ /* Try write a bit or two before we move+realloc the buffer. */
+ if (cbInBuf > 0)
+ txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
+
+ /* Move any buffered data to the front. */
+ cbInBuf = pStdInBuf->cb - pStdInBuf->off;
+ if (cbInBuf == 0)
+ pStdInBuf->cb = pStdInBuf->off = 0;
+ else
+ {
+ memmove(pStdInBuf->pch, &pStdInBuf->pch[pStdInBuf->off], cbInBuf);
+ pStdInBuf->cb = cbInBuf;
+ pStdInBuf->off = 0;
+ }
+
+ /* Do we need to grow the buffer? */
+ if (cb + pStdInBuf->cb > pStdInBuf->cbAllocated)
+ {
+ size_t cbAlloc = pStdInBuf->cb + cb;
+ cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
+ void *pvNew = RTMemRealloc(pStdInBuf->pch, cbAlloc);
+ if (pvNew)
+ {
+ pStdInBuf->pch = (char *)pvNew;
+ pStdInBuf->cbAllocated = cbAlloc;
+ }
+ }
+
+ /* Finally, copy the data. */
+ if (cb + pStdInBuf->cb <= pStdInBuf->cbAllocated)
+ {
+ memcpy(&pStdInBuf->pch[pStdInBuf->cb], pch, cb);
+ pStdInBuf->cb += cb;
+ rc = txsReplyAck(pPktHdr);
+ }
+ else
+ rc = txsReplySimple(pPktHdr, "STDINMEM");
+ }
+
+ /*
+ * Flush the buffered data and add/remove the standard input
+ * handle from the set.
+ */
+ txsDoExecHlpWriteStdIn(pStdInBuf, *phStdInW);
+ if (fAddToSet && pStdInBuf->off < pStdInBuf->cb)
+ {
+ int rc2 = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, TXSEXECHNDID_STDIN_WRITABLE);
+ AssertRC(rc2);
+ }
+ else if (!fAddToSet && pStdInBuf->off >= pStdInBuf->cb)
+ {
+ int rc2 = RTPollSetRemove(hPollSet, TXSEXECHNDID_STDIN_WRITABLE);
+ AssertRC(rc2);
+ }
+ }
+ else
+ rc = txsReplyFailure(pPktHdr, "STDINCRC", "Invalid CRC checksum expected %#x got %#x",
+ pStdInBuf->uCrc32, uCrc32);
+ }
+ else if (pPktHdr->cb < sizeof(TXSPKTHDR) + sizeof(uint32_t))
+ rc = txsReplySimple(pPktHdr, "STDINBAD");
+ else
+ rc = txsReplySimple(pPktHdr, "STDINIGN");
+ }
+ /*
+ * Marks the end of the stream for stdin.
+ */
+ else if (txsIsSameOpcode(pPktHdr, "STDINEOS"))
+ {
+ if (RT_LIKELY(pPktHdr->cb == sizeof(TXSPKTHDR)))
+ {
+ /* Close the pipe. */
+ txsDoExecHlpHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
+ rc = txsReplyAck(pPktHdr);
+ }
+ else
+ rc = txsReplySimple(pPktHdr, "STDINBAD");
+ }
+ /*
+ * The only other two requests are connection oriented and we return a error
+ * code so that we unwind the whole EXEC shebang and start afresh.
+ */
+ else if (txsIsSameOpcode(pPktHdr, "BYE"))
+ {
+ rc = txsDoBye(pPktHdr);
+ if (RT_SUCCESS(rc))
+ rc = VERR_NET_NOT_CONNECTED;
+ }
+ else if (txsIsSameOpcode(pPktHdr, "HOWDY"))
+ {
+ rc = txsDoHowdy(pPktHdr);
+ if (RT_SUCCESS(rc))
+ rc = VERR_NET_NOT_CONNECTED;
+ }
+ else if (txsIsSameOpcode(pPktHdr, "ABORT"))
+ {
+ rc = txsReplyAck(pPktHdr);
+ if (RT_SUCCESS(rc))
+ rc = VINF_EOF; /* this is but ugly! */
+ }
+ else
+ rc = txsReplyFailure(pPktHdr, "UNKNOWN ", "Opcode '%.8s' is not known or not recognized during EXEC", pPktHdr->achOpcode);
+
+ RTMemFree(pPktHdr);
+ return rc;
+}
+
+/**
+ * Handles the output and input of the process, waits for it finish up.
+ *
+ * @returns IPRT status code from reply send.
+ * @param pTxsExec The TXSEXEC instance.
+ */
+static int txsDoExecHlp2(PTXSEXEC pTxsExec)
+{
+ int rc; /* client send. */
+ int rc2;
+ TXSEXECSTDINBUF StdInBuf = { 0, 0, NULL, 0, pTxsExec->hStdInW == NIL_RTPIPE, RTCrc32Start() };
+ uint32_t uStdOutCrc32 = RTCrc32Start();
+ uint32_t uStdErrCrc32 = uStdOutCrc32;
+ uint32_t uTestPipeCrc32 = uStdOutCrc32;
+ uint64_t const MsStart = RTTimeMilliTS();
+ bool fProcessTimedOut = false;
+ uint64_t MsProcessKilled = UINT64_MAX;
+ RTMSINTERVAL const cMsPollBase = g_pTransport->pfnPollSetAdd || pTxsExec->hStdInW == NIL_RTPIPE
+ ? RT_MS_5SEC : 100;
+ RTMSINTERVAL cMsPollCur = 0;
+
+ /*
+ * Before entering the loop, tell the client that we've started the guest
+ * and that it's now OK to send input to the process. (This is not the
+ * final ACK, so the packet header is NULL ... kind of bogus.)
+ */
+ rc = txsReplyAck(NULL);
+
+ /*
+ * Process input, output, the test pipe and client requests.
+ */
+ while ( RT_SUCCESS(rc)
+ && RT_UNLIKELY(!g_fTerminate))
+ {
+ /*
+ * Wait/Process all pending events.
+ */
+ uint32_t idPollHnd;
+ uint32_t fPollEvt;
+ Log3(("Calling RTPollNoResume(,%u,)...\n", cMsPollCur));
+ rc2 = RTPollNoResume(pTxsExec->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
+ Log3(("RTPollNoResume -> fPollEvt=%#x idPollHnd=%u\n", fPollEvt, idPollHnd));
+ if (g_fTerminate)
+ continue;
+ cMsPollCur = 0; /* no rest until we've checked everything. */
+
+ if (RT_SUCCESS(rc2))
+ {
+ switch (idPollHnd)
+ {
+ case TXSEXECHNDID_STDOUT:
+ rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdOutR, &uStdOutCrc32,
+ TXSEXECHNDID_STDOUT, "STDOUT ");
+ break;
+
+ case TXSEXECHNDID_STDERR:
+ rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdErrR, &uStdErrCrc32,
+ TXSEXECHNDID_STDERR, "STDERR ");
+ break;
+
+ case TXSEXECHNDID_TESTPIPE:
+ rc = txsDoExecHlpHandleOutputEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hTestPipeR, &uTestPipeCrc32,
+ TXSEXECHNDID_TESTPIPE, "TESTPIPE");
+ break;
+
+ case TXSEXECHNDID_STDIN:
+ txsDoExecHlpHandleStdInErrorEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf);
+ break;
+
+ case TXSEXECHNDID_STDIN_WRITABLE:
+ txsDoExecHlpHandleStdInWritableEvent(pTxsExec->hPollSet, fPollEvt, &pTxsExec->hStdInW, &StdInBuf);
+ break;
+
+ case TXSEXECHNDID_THREAD:
+ rc2 = RTPollSetRemove(pTxsExec->hPollSet, TXSEXECHNDID_THREAD); AssertRC(rc2);
+ break;
+
+ default:
+ rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, fPollEvt, idPollHnd, &pTxsExec->hStdInW,
+ &StdInBuf);
+ break;
+ }
+ if (RT_FAILURE(rc) || rc == VINF_EOF)
+ break; /* abort command, or client dead or something */
+ continue;
+ }
+
+ /*
+ * Check for incoming data.
+ */
+ if (g_pTransport->pfnPollIn())
+ {
+ rc = txsDoExecHlpHandleTransportEvent(pTxsExec->hPollSet, 0, UINT32_MAX, &pTxsExec->hStdInW, &StdInBuf);
+ if (RT_FAILURE(rc) || rc == VINF_EOF)
+ break; /* abort command, or client dead or something */
+ continue;
+ }
+
+ /*
+ * If the process has terminated, we're should head out.
+ */
+ if (!ASMAtomicReadBool(&pTxsExec->fProcessAlive))
+ break;
+
+ /*
+ * Check for timed out, killing the process.
+ */
+ uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
+ if (pTxsExec->cMsTimeout != RT_INDEFINITE_WAIT)
+ {
+ uint64_t u64Now = RTTimeMilliTS();
+ uint64_t cMsElapsed = u64Now - MsStart;
+ if (cMsElapsed >= pTxsExec->cMsTimeout)
+ {
+ fProcessTimedOut = true;
+ if ( MsProcessKilled == UINT64_MAX
+ || u64Now - MsProcessKilled > RT_MS_1SEC)
+ {
+ if ( MsProcessKilled != UINT64_MAX
+ && u64Now - MsProcessKilled > 20*RT_MS_1MIN)
+ break; /* give up after 20 mins */
+ RTCritSectEnter(&pTxsExec->CritSect);
+ if (pTxsExec->fProcessAlive)
+ RTProcTerminate(pTxsExec->hProcess);
+ RTCritSectLeave(&pTxsExec->CritSect);
+ MsProcessKilled = u64Now;
+ continue;
+ }
+ cMilliesLeft = RT_MS_10SEC;
+ }
+ else
+ cMilliesLeft = pTxsExec->cMsTimeout - (uint32_t)cMsElapsed;
+ }
+
+ /* Reset the polling interval since we've done all pending work. */
+ cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
+ }
+
+ /*
+ * At this point we should hopefully only have to wait 0 ms on the thread
+ * to release the handle... But if for instance the process refuses to die,
+ * we'll have to try kill it again. Bothersome.
+ */
+ for (size_t i = 0; i < 22; i++)
+ {
+ rc2 = RTThreadWait(pTxsExec->hThreadWaiter, RT_MS_1SEC / 2, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pTxsExec->hThreadWaiter = NIL_RTTHREAD;
+ Assert(!pTxsExec->fProcessAlive);
+ break;
+ }
+ if (i == 0 || i == 10 || i == 15 || i == 18 || i > 20)
+ {
+ RTCritSectEnter(&pTxsExec->CritSect);
+ if (pTxsExec->fProcessAlive)
+ RTProcTerminate(pTxsExec->hProcess);
+ RTCritSectLeave(&pTxsExec->CritSect);
+ }
+ }
+
+ /*
+ * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
+ * clients exec packet now.
+ */
+ if (RT_SUCCESS(rc))
+ rc = txsExecSendExitStatus(pTxsExec, pTxsExec->fProcessAlive, fProcessTimedOut, MsProcessKilled);
+
+ RTMemFree(StdInBuf.pch);
+ return rc;
+}
+
+/**
+ * Creates a poll set for the pipes and let the transport layer add stuff to it
+ * as well.
+ *
+ * @returns IPRT status code, reply to client made on error.
+ * @param pTxsExec The TXSEXEC instance.
+ */
+static int txsExecSetupPollSet(PTXSEXEC pTxsExec)
+{
+ int rc = RTPollSetCreate(&pTxsExec->hPollSet);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetCreate");
+
+ rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdInW, RTPOLL_EVT_ERROR, TXSEXECHNDID_STDIN);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdin");
+
+ rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
+ TXSEXECHNDID_STDOUT);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stdout");
+
+ rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
+ TXSEXECHNDID_STDERR);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/stderr");
+
+ rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hTestPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
+ TXSEXECHNDID_TESTPIPE);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/test");
+
+ rc = RTPollSetAddPipe(pTxsExec->hPollSet, pTxsExec->hWakeUpPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
+ TXSEXECHNDID_THREAD);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPollSetAddPipe/wakeup");
+
+ if (g_pTransport->pfnPollSetAdd)
+ {
+ rc = g_pTransport->pfnPollSetAdd(pTxsExec->hPollSet, TXSEXECHNDID_TRANSPORT);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "%s->pfnPollSetAdd/stdin", g_pTransport->szName);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Thread that calls RTProcWait and signals the main thread when it returns.
+ *
+ * The thread is created before the process is started and is waiting for a user
+ * signal from the main thread before it calls RTProcWait.
+ *
+ * @returns VINF_SUCCESS (ignored).
+ * @param hThreadSelf The thread handle.
+ * @param pvUser The TXEEXEC structure.
+ */
+static DECLCALLBACK(int) txsExecWaitThreadProc(RTTHREAD hThreadSelf, void *pvUser)
+{
+ PTXSEXEC pTxsExec = (PTXSEXEC)pvUser;
+
+ /* Wait for the go ahead... */
+ int rc = RTThreadUserWait(hThreadSelf, RT_INDEFINITE_WAIT); AssertRC(rc);
+
+ RTCritSectEnter(&pTxsExec->CritSect);
+ for (;;)
+ {
+ RTCritSectLeave(&pTxsExec->CritSect);
+ rc = RTProcWaitNoResume(pTxsExec->hProcess, RTPROCWAIT_FLAGS_BLOCK, &pTxsExec->ProcessStatus);
+ RTCritSectEnter(&pTxsExec->CritSect);
+
+ /* If the pipe is NIL, the destructor wants us to get lost ASAP. */
+ if (pTxsExec->hWakeUpPipeW == NIL_RTPIPE)
+ break;
+
+ if (RT_FAILURE(rc))
+ {
+ rc = RTProcWait(pTxsExec->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &pTxsExec->ProcessStatus);
+ if (rc == VERR_PROCESS_RUNNING)
+ continue;
+
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ pTxsExec->ProcessStatus.iStatus = rc;
+ pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
+ }
+ }
+
+ /* The process finished, signal the main thread over the pipe. */
+ ASMAtomicWriteBool(&pTxsExec->fProcessAlive, false);
+ size_t cbIgnored;
+ RTPipeWrite(pTxsExec->hWakeUpPipeW, "done", 4, &cbIgnored);
+ RTPipeClose(pTxsExec->hWakeUpPipeW);
+ pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
+ break;
+ }
+ RTCritSectLeave(&pTxsExec->CritSect);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Sets up the thread that waits for the process to complete.
+ *
+ * @returns IPRT status code, reply to client made on error.
+ * @param pTxsExec The TXSEXEC instance.
+ */
+static int txsExecSetupThread(PTXSEXEC pTxsExec)
+{
+ int rc = RTPipeCreate(&pTxsExec->hWakeUpPipeR, &pTxsExec->hWakeUpPipeW, 0 /*fFlags*/);
+ if (RT_FAILURE(rc))
+ {
+ pTxsExec->hWakeUpPipeR = pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
+ return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/wait");
+ }
+
+ rc = RTThreadCreate(&pTxsExec->hThreadWaiter, txsExecWaitThreadProc,
+ pTxsExec, 0 /*cbStack */, RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE, "TxsProcW");
+ if (RT_FAILURE(rc))
+ {
+ pTxsExec->hThreadWaiter = NIL_RTTHREAD;
+ return txsExecReplyRC(pTxsExec, rc, "RTThreadCreate");
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Sets up the test pipe.
+ *
+ * @returns IPRT status code, reply to client made on error.
+ * @param pTxsExec The TXSEXEC instance.
+ * @param pszTestPipe How to set up the test pipe.
+ */
+static int txsExecSetupTestPipe(PTXSEXEC pTxsExec, const char *pszTestPipe)
+{
+ if (strcmp(pszTestPipe, "|"))
+ return VINF_SUCCESS;
+
+ int rc = RTPipeCreate(&pTxsExec->hTestPipeR, &pTxsExec->hTestPipeW, RTPIPE_C_INHERIT_WRITE);
+ if (RT_FAILURE(rc))
+ {
+ pTxsExec->hTestPipeR = pTxsExec->hTestPipeW = NIL_RTPIPE;
+ return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/test/%s", pszTestPipe);
+ }
+
+ char szVal[64];
+ RTStrPrintf(szVal, sizeof(szVal), "%#llx", (uint64_t)RTPipeToNative(pTxsExec->hTestPipeW));
+ rc = RTEnvSetEx(pTxsExec->hEnv, "IPRT_TEST_PIPE", szVal);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTEnvSetEx/test/%s", pszTestPipe);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Sets up the redirection / pipe / nothing for one of the standard handles.
+ *
+ * @returns IPRT status code, reply to client made on error.
+ * @param pTxsExec The TXSEXEC instance.
+ * @param pszHowTo How to set up this standard handle.
+ * @param pszStdWhat For what to setup redirection (stdin/stdout/stderr).
+ * @param fd Which standard handle it is (0 == stdin, 1 ==
+ * stdout, 2 == stderr).
+ * @param ph The generic handle that @a pph may be set
+ * pointing to. Always set.
+ * @param pph Pointer to the RTProcCreateExec argument.
+ * Always set.
+ * @param phPipe Where to return the end of the pipe that we
+ * should service. Always set.
+ */
+static int txsExecSetupRedir(PTXSEXEC pTxsExec, const char *pszHowTo, const char *pszStdWhat, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
+{
+ ph->enmType = RTHANDLETYPE_PIPE;
+ ph->u.hPipe = NIL_RTPIPE;
+ *pph = NULL;
+ *phPipe = NIL_RTPIPE;
+
+ int rc;
+ if (!strcmp(pszHowTo, "|"))
+ {
+ /*
+ * Setup a pipe for forwarding to/from the client.
+ */
+ if (fd == 0)
+ rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
+ else
+ rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTPipeCreate/%s/%s", pszStdWhat, pszHowTo);
+ ph->enmType = RTHANDLETYPE_PIPE;
+ *pph = ph;
+ }
+ else if (!strcmp(pszHowTo, "/dev/null"))
+ {
+ /*
+ * Redirect to/from /dev/null.
+ */
+ RTFILE hFile;
+ rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTFileOpenBitBucket/%s/%s", pszStdWhat, pszHowTo);
+
+ ph->enmType = RTHANDLETYPE_FILE;
+ ph->u.hFile = hFile;
+ *pph = ph;
+ }
+ else if (*pszHowTo)
+ {
+ /*
+ * Redirect to/from file.
+ */
+ uint32_t fFlags;
+ if (fd == 0)
+ fFlags = RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN;
+ else
+ {
+ if (pszHowTo[0] != '>' || pszHowTo[1] != '>')
+ fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE;
+ else
+ {
+ /* append */
+ pszHowTo += 2;
+ fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
+ }
+ }
+
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszHowTo, fFlags);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTFileOpen/%s/%s", pszStdWhat, pszHowTo);
+
+ ph->enmType = RTHANDLETYPE_FILE;
+ ph->u.hFile = hFile;
+ *pph = ph;
+ }
+ else
+ /* same as parent (us) */
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+/**
+ * Create the environment.
+ *
+ * @returns IPRT status code, reply to client made on error.
+ * @param pTxsExec The TXSEXEC instance.
+ * @param cEnvVars The number of environment variables.
+ * @param papszEnv The environment variables (var=value).
+ */
+static int txsExecSetupEnv(PTXSEXEC pTxsExec, uint32_t cEnvVars, const char * const *papszEnv)
+{
+ /*
+ * Create the environment.
+ */
+ int rc = RTEnvClone(&pTxsExec->hEnv, RTENV_DEFAULT);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTEnvClone");
+
+ for (size_t i = 0; i < cEnvVars; i++)
+ {
+ rc = RTEnvPutEx(pTxsExec->hEnv, papszEnv[i]);
+ if (RT_FAILURE(rc))
+ return txsExecReplyRC(pTxsExec, rc, "RTEnvPutEx(,'%s')", papszEnv[i]);
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Deletes the TXSEXEC structure and frees the memory backing it.
+ *
+ * @param pTxsExec The structure to destroy.
+ */
+static void txsExecDestroy(PTXSEXEC pTxsExec)
+{
+ int rc2;
+
+ rc2 = RTEnvDestroy(pTxsExec->hEnv); AssertRC(rc2);
+ pTxsExec->hEnv = NIL_RTENV;
+ rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2);
+ pTxsExec->hTestPipeW = NIL_RTPIPE;
+ rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2);
+ pTxsExec->StdErr.phChild = NULL;
+ rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2);
+ pTxsExec->StdOut.phChild = NULL;
+ rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2);
+ pTxsExec->StdIn.phChild = NULL;
+
+ rc2 = RTPipeClose(pTxsExec->hTestPipeR); AssertRC(rc2);
+ pTxsExec->hTestPipeR = NIL_RTPIPE;
+ rc2 = RTPipeClose(pTxsExec->hStdErrR); AssertRC(rc2);
+ pTxsExec->hStdErrR = NIL_RTPIPE;
+ rc2 = RTPipeClose(pTxsExec->hStdOutR); AssertRC(rc2);
+ pTxsExec->hStdOutR = NIL_RTPIPE;
+ rc2 = RTPipeClose(pTxsExec->hStdInW); AssertRC(rc2);
+ pTxsExec->hStdInW = NIL_RTPIPE;
+
+ RTPollSetDestroy(pTxsExec->hPollSet);
+ pTxsExec->hPollSet = NIL_RTPOLLSET;
+
+ /*
+ * If the process is still running we're in a bit of a fix... Try kill it,
+ * although that's potentially racing process termination and reusage of
+ * the pid.
+ */
+ RTCritSectEnter(&pTxsExec->CritSect);
+
+ RTPipeClose(pTxsExec->hWakeUpPipeW);
+ pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
+ RTPipeClose(pTxsExec->hWakeUpPipeR);
+ pTxsExec->hWakeUpPipeR = NIL_RTPIPE;
+
+ if ( pTxsExec->hProcess != NIL_RTPROCESS
+ && pTxsExec->fProcessAlive)
+ RTProcTerminate(pTxsExec->hProcess);
+
+ RTCritSectLeave(&pTxsExec->CritSect);
+
+ int rcThread = VINF_SUCCESS;
+ if (pTxsExec->hThreadWaiter != NIL_RTTHREAD)
+ rcThread = RTThreadWait(pTxsExec->hThreadWaiter, 5000, NULL);
+ if (RT_SUCCESS(rcThread))
+ {
+ pTxsExec->hThreadWaiter = NIL_RTTHREAD;
+ RTCritSectDelete(&pTxsExec->CritSect);
+ RTMemFree(pTxsExec);
+ }
+ /* else: leak it or RTThreadWait may cause heap corruption later. */
+}
+
+/**
+ * Initializes the TXSEXEC structure.
+ *
+ * @returns VINF_SUCCESS and non-NULL *ppTxsExec on success, reply send status
+ * and *ppTxsExec set to NULL on failure.
+ * @param pPktHdr The exec packet.
+ * @param cMsTimeout The time parameter.
+ * @param ppTxsExec Where to return the structure.
+ */
+static int txsExecCreate(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsTimeout, PTXSEXEC *ppTxsExec)
+{
+ *ppTxsExec = NULL;
+
+ /*
+ * Allocate the basic resources.
+ */
+ PTXSEXEC pTxsExec = (PTXSEXEC)RTMemAlloc(sizeof(*pTxsExec));
+ if (!pTxsExec)
+ return txsReplyRC(pPktHdr, VERR_NO_MEMORY, "RTMemAlloc(%zu)", sizeof(*pTxsExec));
+ int rc = RTCritSectInit(&pTxsExec->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pTxsExec);
+ return txsReplyRC(pPktHdr, rc, "RTCritSectInit");
+ }
+
+ /*
+ * Initialize the member to NIL values.
+ */
+ pTxsExec->pPktHdr = pPktHdr;
+ pTxsExec->cMsTimeout = cMsTimeout;
+ pTxsExec->rcReplySend = VINF_SUCCESS;
+
+ pTxsExec->hPollSet = NIL_RTPOLLSET;
+ pTxsExec->hStdInW = NIL_RTPIPE;
+ pTxsExec->hStdOutR = NIL_RTPIPE;
+ pTxsExec->hStdErrR = NIL_RTPIPE;
+ pTxsExec->hTestPipeR = NIL_RTPIPE;
+ pTxsExec->hWakeUpPipeR = NIL_RTPIPE;
+ pTxsExec->hThreadWaiter = NIL_RTTHREAD;
+
+ pTxsExec->StdIn.phChild = NULL;
+ pTxsExec->StdOut.phChild = NULL;
+ pTxsExec->StdErr.phChild = NULL;
+ pTxsExec->hTestPipeW = NIL_RTPIPE;
+ pTxsExec->hEnv = NIL_RTENV;
+
+ pTxsExec->hProcess = NIL_RTPROCESS;
+ pTxsExec->ProcessStatus.iStatus = 254;
+ pTxsExec->ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
+ pTxsExec->fProcessAlive = false;
+ pTxsExec->hWakeUpPipeW = NIL_RTPIPE;
+
+ *ppTxsExec = pTxsExec;
+ return VINF_SUCCESS;
+}
+
+/**
+ * txsDoExec helper that takes over when txsDoExec has expanded the packet.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The exec packet.
+ * @param fFlags Flags, reserved for future use.
+ * @param pszExecName The executable name.
+ * @param cArgs The argument count.
+ * @param papszArgs The arguments.
+ * @param cEnvVars The environment variable count.
+ * @param papszEnv The environment variables.
+ * @param pszStdIn How to deal with standard in.
+ * @param pszStdOut How to deal with standard out.
+ * @param pszStdErr How to deal with standard err.
+ * @param pszTestPipe How to deal with the test pipe.
+ * @param pszUsername The user to run the program as.
+ * @param cMillies The process time limit in milliseconds.
+ */
+static int txsDoExecHlp(PCTXSPKTHDR pPktHdr, uint32_t fFlags, const char *pszExecName,
+ uint32_t cArgs, const char * const *papszArgs,
+ uint32_t cEnvVars, const char * const *papszEnv,
+ const char *pszStdIn, const char *pszStdOut, const char *pszStdErr, const char *pszTestPipe,
+ const char *pszUsername, RTMSINTERVAL cMillies)
+{
+ int rc2;
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Input validation, filter out things we don't yet support..
+ */
+ Assert(!fFlags);
+ if (!*pszExecName)
+ return txsReplyFailure(pPktHdr, "STR ZERO", "Executable name is empty");
+ if (!*pszStdIn)
+ return txsReplyFailure(pPktHdr, "STR ZERO", "The stdin howto is empty");
+ if (!*pszStdOut)
+ return txsReplyFailure(pPktHdr, "STR ZERO", "The stdout howto is empty");
+ if (!*pszStdErr)
+ return txsReplyFailure(pPktHdr, "STR ZERO", "The stderr howto is empty");
+ if (!*pszTestPipe)
+ return txsReplyFailure(pPktHdr, "STR ZERO", "The testpipe howto is empty");
+ if (strcmp(pszTestPipe, "|") && strcmp(pszTestPipe, "/dev/null"))
+ return txsReplyFailure(pPktHdr, "BAD TSTP", "Only \"|\" and \"/dev/null\" are allowed as testpipe howtos ('%s')",
+ pszTestPipe);
+ if (*pszUsername)
+ return txsReplyFailure(pPktHdr, "NOT IMPL", "Executing as a specific user is not implemented ('%s')", pszUsername);
+
+ /*
+ * Prepare for process launch.
+ */
+ PTXSEXEC pTxsExec;
+ int rc = txsExecCreate(pPktHdr, cMillies, &pTxsExec);
+ if (pTxsExec == NULL)
+ return rc;
+ rc = txsExecSetupEnv(pTxsExec, cEnvVars, papszEnv);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupRedir(pTxsExec, pszStdIn, "StdIn", 0, &pTxsExec->StdIn.hChild, &pTxsExec->StdIn.phChild, &pTxsExec->hStdInW);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupRedir(pTxsExec, pszStdOut, "StdOut", 1, &pTxsExec->StdOut.hChild, &pTxsExec->StdOut.phChild, &pTxsExec->hStdOutR);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupRedir(pTxsExec, pszStdErr, "StdErr", 2, &pTxsExec->StdErr.hChild, &pTxsExec->StdErr.phChild, &pTxsExec->hStdErrR);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupTestPipe(pTxsExec, pszTestPipe);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupThread(pTxsExec);
+ if (RT_SUCCESS(rc))
+ rc = txsExecSetupPollSet(pTxsExec);
+ if (RT_SUCCESS(rc))
+ {
+ char szPathResolved[RTPATH_MAX + 1];
+ rc = RTPathReal(pszExecName, szPathResolved, sizeof(szPathResolved));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the process.
+ */
+ if (g_fDisplayOutput)
+ {
+ RTPrintf("txs: Executing \"%s\" -> \"%s\": ", pszExecName, szPathResolved);
+ for (uint32_t i = 0; i < cArgs; i++)
+ RTPrintf(" \"%s\"", papszArgs[i]);
+ RTPrintf("\n");
+ }
+
+ rc = RTProcCreateEx(szPathResolved, papszArgs, pTxsExec->hEnv, 0 /*fFlags*/,
+ pTxsExec->StdIn.phChild, pTxsExec->StdOut.phChild, pTxsExec->StdErr.phChild,
+ *pszUsername ? pszUsername : NULL, NULL, NULL,
+ &pTxsExec->hProcess);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteBool(&pTxsExec->fProcessAlive, true);
+ rc2 = RTThreadUserSignal(pTxsExec->hThreadWaiter); AssertRC(rc2);
+
+ /*
+ * Close the child ends of any pipes and redirected files.
+ */
+ rc2 = RTHandleClose(pTxsExec->StdIn.phChild); AssertRC(rc2);
+ pTxsExec->StdIn.phChild = NULL;
+ rc2 = RTHandleClose(pTxsExec->StdOut.phChild); AssertRC(rc2);
+ pTxsExec->StdOut.phChild = NULL;
+ rc2 = RTHandleClose(pTxsExec->StdErr.phChild); AssertRC(rc2);
+ pTxsExec->StdErr.phChild = NULL;
+ rc2 = RTPipeClose(pTxsExec->hTestPipeW); AssertRC(rc2);
+ pTxsExec->hTestPipeW = NIL_RTPIPE;
+
+ /*
+ * Let another worker function funnel output and input to the
+ * client as well as the process exit code.
+ */
+ rc = txsDoExecHlp2(pTxsExec);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ rc = txsReplyFailure(pPktHdr, "FAILED ", "Executing process \"%s\" failed with %Rrc",
+ pszExecName, rc);
+ }
+ else
+ rc = pTxsExec->rcReplySend;
+ txsExecDestroy(pTxsExec);
+ return rc;
+}
+
+/**
+ * Execute a program.
+ *
+ * @returns IPRT status code from send.
+ * @param pPktHdr The exec packet.
+ */
+static int txsDoExec(PCTXSPKTHDR pPktHdr)
+{
+ /*
+ * This packet has a lot of parameters, most of which are zero terminated
+ * strings. The strings used in items 7 thru 10 are either file names,
+ * "/dev/null" or a pipe char (|).
+ *
+ * Packet content:
+ * 1. Flags reserved for future use (32-bit unsigned).
+ * 2. The executable name (string).
+ * 3. The argument count given as a 32-bit unsigned integer.
+ * 4. The arguments strings.
+ * 5. The number of environment strings (32-bit unsigned).
+ * 6. The environment strings (var=val) to apply the environment.
+ * 7. What to do about standard in (string).
+ * 8. What to do about standard out (string).
+ * 9. What to do about standard err (string).
+ * 10. What to do about the test pipe (string).
+ * 11. The name of the user to run the program as (string). Empty string
+ * means running it as the current user.
+ * 12. Process time limit in milliseconds (32-bit unsigned). Max == no limit.
+ */
+ size_t const cbMin = sizeof(TXSPKTHDR)
+ + sizeof(uint32_t) /* flags */ + 2
+ + sizeof(uint32_t) /* argc */ + 2 /* argv */
+ + sizeof(uint32_t) + 0 /* environ */
+ + 4 * 1
+ + sizeof(uint32_t) /* timeout */;
+ if (pPktHdr->cb < cbMin)
+ return txsReplyBadMinSize(pPktHdr, cbMin);
+
+ /* unpack the packet */
+ char const *pchEnd = (char const *)pPktHdr + pPktHdr->cb;
+ char const *pch = (char const *)(pPktHdr + 1); /* cursor */
+
+ /* 1. flags */
+ uint32_t const fFlags = *(uint32_t const *)pch;
+ pch += sizeof(uint32_t);
+ if (fFlags != 0)
+ return txsReplyFailure(pPktHdr, "BAD FLAG", "Invalid EXEC flags %#x, expected 0", fFlags);
+
+ /* 2. exec name */
+ int rc;
+ char *pszExecName = NULL;
+ if (!txsIsStringValid(pPktHdr, "execname", pch, &pszExecName, &pch, &rc))
+ return rc;
+
+ /* 3. argc */
+ uint32_t const cArgs = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xff;
+ pch += sizeof(uint32_t);
+ if (cArgs * 1 >= (size_t)(pchEnd - pch))
+ rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Bad or missing argument count (%#x)", cArgs);
+ else if (cArgs > 128)
+ rc = txsReplyFailure(pPktHdr, "BAD ARGC", "Too many arguments (%#x)", cArgs);
+ else
+ {
+ char **papszArgs = (char **)RTMemTmpAllocZ(sizeof(char *) * (cArgs + 1));
+ if (papszArgs)
+ {
+ /* 4. argv */
+ bool fOk = true;
+ for (size_t i = 0; i < cArgs && fOk; i++)
+ {
+ fOk = txsIsStringValid(pPktHdr, "argvN", pch, &papszArgs[i], &pch, &rc);
+ if (!fOk)
+ break;
+ }
+ if (fOk)
+ {
+ /* 5. cEnvVars */
+ uint32_t const cEnvVars = (size_t)(pchEnd - pch) > sizeof(uint32_t) ? *(uint32_t const *)pch : 0xfff;
+ pch += sizeof(uint32_t);
+ if (cEnvVars * 1 >= (size_t)(pchEnd - pch))
+ rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Bad or missing environment variable count (%#x)", cEnvVars);
+ else if (cEnvVars > 256)
+ rc = txsReplyFailure(pPktHdr, "BAD ENVC", "Too many environment variables (%#x)", cEnvVars);
+ else
+ {
+ char **papszEnv = (char **)RTMemTmpAllocZ(sizeof(char *) * (cEnvVars + 1));
+ if (papszEnv)
+ {
+ /* 6. environ */
+ for (size_t i = 0; i < cEnvVars && fOk; i++)
+ {
+ fOk = txsIsStringValid(pPktHdr, "envN", pch, &papszEnv[i], &pch, &rc);
+ if (!fOk) /* Bail out on error. */
+ break;
+ }
+ if (fOk)
+ {
+ /* 7. stdout */
+ char *pszStdIn;
+ if (txsIsStringValid(pPktHdr, "stdin", pch, &pszStdIn, &pch, &rc))
+ {
+ /* 8. stdout */
+ char *pszStdOut;
+ if (txsIsStringValid(pPktHdr, "stdout", pch, &pszStdOut, &pch, &rc))
+ {
+ /* 9. stderr */
+ char *pszStdErr;
+ if (txsIsStringValid(pPktHdr, "stderr", pch, &pszStdErr, &pch, &rc))
+ {
+ /* 10. testpipe */
+ char *pszTestPipe;
+ if (txsIsStringValid(pPktHdr, "testpipe", pch, &pszTestPipe, &pch, &rc))
+ {
+ /* 11. username */
+ char *pszUsername;
+ if (txsIsStringValid(pPktHdr, "username", pch, &pszUsername, &pch, &rc))
+ {
+ /** @todo No password value? */
+
+ /* 12. time limit */
+ uint32_t const cMillies = (size_t)(pchEnd - pch) >= sizeof(uint32_t)
+ ? *(uint32_t const *)pch
+ : 0;
+ if ((size_t)(pchEnd - pch) > sizeof(uint32_t))
+ rc = txsReplyFailure(pPktHdr, "BAD END ", "Timeout argument not at end of packet (%#x)", (size_t)(pchEnd - pch));
+ else if ((size_t)(pchEnd - pch) < sizeof(uint32_t))
+ rc = txsReplyFailure(pPktHdr, "BAD NOTO", "No timeout argument");
+ else if (cMillies < 1000)
+ rc = txsReplyFailure(pPktHdr, "BAD TO ", "Timeout is less than a second (%#x)", cMillies);
+ else
+ {
+ pch += sizeof(uint32_t);
+
+ /*
+ * Time to employ a helper here before we go way beyond
+ * the right margin...
+ */
+ rc = txsDoExecHlp(pPktHdr, fFlags, pszExecName,
+ cArgs, papszArgs,
+ cEnvVars, papszEnv,
+ pszStdIn, pszStdOut, pszStdErr, pszTestPipe,
+ pszUsername,
+ cMillies == UINT32_MAX ? RT_INDEFINITE_WAIT : cMillies);
+ }
+ RTStrFree(pszUsername);
+ }
+ RTStrFree(pszTestPipe);
+ }
+ RTStrFree(pszStdErr);
+ }
+ RTStrFree(pszStdOut);
+ }
+ RTStrFree(pszStdIn);
+ }
+ }
+ for (size_t i = 0; i < cEnvVars; i++)
+ RTStrFree(papszEnv[i]);
+ RTMemTmpFree(papszEnv);
+ }
+ else
+ rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes environ", sizeof(char *) * (cEnvVars + 1));
+ }
+ }
+ for (size_t i = 0; i < cArgs; i++)
+ RTStrFree(papszArgs[i]);
+ RTMemTmpFree(papszArgs);
+ }
+ else
+ rc = txsReplyFailure(pPktHdr, "NO MEM ", "Failed to allocate %zu bytes for argv", sizeof(char *) * (cArgs + 1));
+ }
+ RTStrFree(pszExecName);
+
+ return rc;
+}
+
+/**
+ * The main loop.
+ *
+ * @returns exit code.
+ */
+static RTEXITCODE txsMainLoop(void)
+{
+ if (g_cVerbose > 0)
+ RTMsgInfo("txsMainLoop: start...\n");
+ RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;
+ while (!g_fTerminate)
+ {
+ /*
+ * Read client command packet and process it.
+ */
+ PTXSPKTHDR pPktHdr;
+ int rc = txsRecvPkt(&pPktHdr, true /*fAutoRetryOnFailure*/);
+ if (RT_FAILURE(rc))
+ continue;
+ if (g_cVerbose > 0)
+ RTMsgInfo("txsMainLoop: CMD: %.8s...", pPktHdr->achOpcode);
+
+ /*
+ * Do a string switch on the opcode bit.
+ */
+ /* Connection: */
+ if ( txsIsSameOpcode(pPktHdr, "HOWDY "))
+ rc = txsDoHowdy(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "BYE "))
+ rc = txsDoBye(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "VER "))
+ rc = txsDoVer(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "UUID "))
+ rc = txsDoUuid(pPktHdr);
+ /* Process: */
+ else if (txsIsSameOpcode(pPktHdr, "EXEC "))
+ rc = txsDoExec(pPktHdr);
+ /* Admin: */
+ else if (txsIsSameOpcode(pPktHdr, "REBOOT "))
+ rc = txsDoReboot(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "SHUTDOWN"))
+ rc = txsDoShutdown(pPktHdr);
+ /* CD/DVD control: */
+ else if (txsIsSameOpcode(pPktHdr, "CD EJECT"))
+ rc = txsDoCdEject(pPktHdr);
+ /* File system: */
+ else if (txsIsSameOpcode(pPktHdr, "CLEANUP "))
+ rc = txsDoCleanup(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "MKDIR "))
+ rc = txsDoMkDir(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "MKDRPATH"))
+ rc = txsDoMkDrPath(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "MKSYMLNK"))
+ rc = txsDoMkSymlnk(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "RMDIR "))
+ rc = txsDoRmDir(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "RMFILE "))
+ rc = txsDoRmFile(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "RMSYMLNK"))
+ rc = txsDoRmSymlnk(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "RMTREE "))
+ rc = txsDoRmTree(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "CHMOD "))
+ rc = txsDoChMod(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "CHOWN "))
+ rc = txsDoChOwn(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "ISDIR "))
+ rc = txsDoIsDir(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "ISFILE "))
+ rc = txsDoIsFile(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "ISSYMLNK"))
+ rc = txsDoIsSymlnk(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "STAT "))
+ rc = txsDoStat(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "LSTAT "))
+ rc = txsDoLStat(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "LIST "))
+ rc = txsDoList(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "CPFILE "))
+ rc = txsDoCopyFile(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "PUT FILE"))
+ rc = txsDoPutFile(pPktHdr, false /*fHasMode*/);
+ else if (txsIsSameOpcode(pPktHdr, "PUT2FILE"))
+ rc = txsDoPutFile(pPktHdr, true /*fHasMode*/);
+ else if (txsIsSameOpcode(pPktHdr, "GET FILE"))
+ rc = txsDoGetFile(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "PKFILE "))
+ rc = txsDoPackFile(pPktHdr);
+ else if (txsIsSameOpcode(pPktHdr, "UNPKFILE"))
+ rc = txsDoUnpackFile(pPktHdr);
+ /* Misc: */
+ else if (txsIsSameOpcode(pPktHdr, "EXP STR "))
+ rc = txsDoExpandString(pPktHdr);
+ else
+ rc = txsReplyUnknown(pPktHdr);
+
+ if (g_cVerbose > 0)
+ RTMsgInfo("txsMainLoop: CMD: %.8s -> %Rrc", pPktHdr->achOpcode, rc);
+ RTMemFree(pPktHdr);
+ }
+
+ if (g_cVerbose > 0)
+ RTMsgInfo("txsMainLoop: end\n");
+ return enmExitCode;
+}
+
+
+/**
+ * Finalizes the scratch directory, making sure it exists.
+ *
+ * @returns exit code.
+ */
+static RTEXITCODE txsFinalizeScratch(void)
+{
+ RTPathStripTrailingSlash(g_szScratchPath);
+ char *pszFilename = RTPathFilename(g_szScratchPath);
+ if (!pszFilename)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "cannot use root for scratch (%s)\n", g_szScratchPath);
+
+ int rc;
+ if (strchr(pszFilename, 'X'))
+ {
+ char ch = *pszFilename;
+ rc = RTDirCreateFullPath(g_szScratchPath, 0700);
+ *pszFilename = ch;
+ if (RT_SUCCESS(rc))
+ rc = RTDirCreateTemp(g_szScratchPath, 0700);
+ }
+ else
+ {
+ if (RTDirExists(g_szScratchPath))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTDirCreateFullPath(g_szScratchPath, 0700);
+ }
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create scratch directory: %Rrc (%s)\n", rc, g_szScratchPath);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Attempts to complete an upgrade by updating the original and relaunching
+ * ourselves from there again.
+ *
+ * On failure, we'll continue running as the temporary copy.
+ *
+ * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
+ * @param argc The number of arguments.
+ * @param argv The argument vector.
+ * @param pfExit For indicating exit when the exit code is zero.
+ * @param pszUpgrading The upgraded image path.
+ */
+static RTEXITCODE txsAutoUpdateStage2(int argc, char **argv, bool *pfExit, const char *pszUpgrading)
+{
+ if (g_cVerbose > 0)
+ RTMsgInfo("Auto update stage 2...");
+
+ /*
+ * Copy the current executable onto the original.
+ * Note that we're racing the original program on some platforms, thus the
+ * 60 sec sleep mess.
+ */
+ char szUpgradePath[RTPATH_MAX];
+ if (!RTProcGetExecutablePath(szUpgradePath, sizeof(szUpgradePath)))
+ {
+ RTMsgError("RTProcGetExecutablePath failed (step 2)\n");
+ return RTEXITCODE_SUCCESS;
+ }
+ void *pvUpgrade;
+ size_t cbUpgrade;
+ int rc = RTFileReadAll(szUpgradePath, &pvUpgrade, &cbUpgrade);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc (step 2)\n", szUpgradePath, rc);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ uint64_t StartMilliTS = RTTimeMilliTS();
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszUpgrading,
+ RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE
+ | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
+ while ( RT_FAILURE(rc)
+ && RTTimeMilliTS() - StartMilliTS < 60000)
+ {
+ RTThreadSleep(1000);
+ rc = RTFileOpen(&hFile, pszUpgrading,
+ RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE
+ | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pvUpgrade, cbUpgrade, NULL);
+ RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Relaunch the service with the original name, foricbly barring
+ * further upgrade cycles in case of bugs (and simplifying the code).
+ */
+ const char **papszArgs = (const char **)RTMemAlloc((argc + 1 + 1) * sizeof(char **));
+ if (papszArgs)
+ {
+ papszArgs[0] = pszUpgrading;
+ for (int i = 1; i < argc; i++)
+ papszArgs[i] = argv[i];
+ papszArgs[argc] = "--no-auto-upgrade";
+ papszArgs[argc + 1] = NULL;
+
+ RTMsgInfo("Launching upgraded image: \"%s\"\n", pszUpgrading);
+ RTPROCESS hProc;
+ rc = RTProcCreate(pszUpgrading, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
+ if (RT_SUCCESS(rc))
+ *pfExit = true;
+ else
+ RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 2)\n", pszUpgrading, rc);
+ RTMemFree(papszArgs);
+ }
+ else
+ RTMsgError("RTMemAlloc failed during upgrade attempt (stage 2)\n");
+ }
+ else
+ RTMsgError("RTFileWrite(%s,,%zu): %Rrc (step 2) - BAD\n", pszUpgrading, cbUpgrade, rc);
+ }
+ else
+ RTMsgError("RTFileOpen(,%s,): %Rrc\n", pszUpgrading, rc);
+ RTFileReadAllFree(pvUpgrade, cbUpgrade);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Checks for an upgrade and respawns if there is.
+ *
+ * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
+ * @param argc The number of arguments.
+ * @param argv The argument vector.
+ * @param cSecsCdWait Number of seconds to wait on the CD.
+ * @param pfExit For indicating exit when the exit code is zero.
+ */
+static RTEXITCODE txsAutoUpdateStage1(int argc, char **argv, uint32_t cSecsCdWait, bool *pfExit)
+{
+ if (g_cVerbose > 1)
+ RTMsgInfo("Auto update stage 1...");
+
+ /*
+ * Figure names of the current service image and the potential upgrade.
+ */
+ char szOrgPath[RTPATH_MAX];
+ if (!RTProcGetExecutablePath(szOrgPath, sizeof(szOrgPath)))
+ {
+ RTMsgError("RTProcGetExecutablePath failed\n");
+ return RTEXITCODE_SUCCESS;
+ }
+
+ char szUpgradePath[RTPATH_MAX];
+ int rc = RTPathJoin(szUpgradePath, sizeof(szUpgradePath), g_szCdRomPath, g_szOsSlashArchShortName);
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szUpgradePath, sizeof(szUpgradePath), RTPathFilename(szOrgPath));
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Failed to construct path to potential service upgrade: %Rrc\n", rc);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ /*
+ * Query information about the two images and read the entire potential source file.
+ * Because the CD may take a little time to be mounted when the system boots, we
+ * need to do some fudging here.
+ */
+ uint64_t nsStart = RTTimeNanoTS();
+ RTFSOBJINFO UpgradeInfo;
+ for (;;)
+ {
+ rc = RTPathQueryInfo(szUpgradePath, &UpgradeInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ break;
+ if ( rc != VERR_FILE_NOT_FOUND
+ && rc != VERR_PATH_NOT_FOUND
+ && rc != VERR_MEDIA_NOT_PRESENT
+ && rc != VERR_MEDIA_NOT_RECOGNIZED)
+ {
+ RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (upgrade)\n", szUpgradePath, rc);
+ return RTEXITCODE_SUCCESS;
+ }
+ uint64_t cNsElapsed = RTTimeNanoTS() - nsStart;
+ if (cNsElapsed >= cSecsCdWait * RT_NS_1SEC_64)
+ {
+ if (g_cVerbose > 0)
+ RTMsgInfo("Auto update: Giving up waiting for media.");
+ return RTEXITCODE_SUCCESS;
+ }
+ RTThreadSleep(500);
+ }
+
+ RTFSOBJINFO OrgInfo;
+ rc = RTPathQueryInfo(szOrgPath, &OrgInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ void *pvUpgrade;
+ size_t cbUpgrade;
+ rc = RTFileReadAllEx(szUpgradePath, 0, UpgradeInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvUpgrade, &cbUpgrade);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTPathQueryInfo(\"%s\"): %Rrc (old)\n", szOrgPath, rc);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ /*
+ * Compare and see if we've got a different service image or not.
+ */
+ if (OrgInfo.cbObject == UpgradeInfo.cbObject)
+ {
+ /* must compare bytes. */
+ void *pvOrg;
+ size_t cbOrg;
+ rc = RTFileReadAllEx(szOrgPath, 0, OrgInfo.cbObject, RTFILE_RDALL_O_DENY_NONE, &pvOrg, &cbOrg);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTFileReadAllEx(\"%s\"): %Rrc\n", szOrgPath, rc);
+ RTFileReadAllFree(pvUpgrade, cbUpgrade);
+ return RTEXITCODE_SUCCESS;
+ }
+ bool fSame = !memcmp(pvUpgrade, pvOrg, OrgInfo.cbObject);
+ RTFileReadAllFree(pvOrg, cbOrg);
+ if (fSame)
+ {
+ RTFileReadAllFree(pvUpgrade, cbUpgrade);
+ if (g_cVerbose > 0)
+ RTMsgInfo("Auto update: Not necessary.");
+ return RTEXITCODE_SUCCESS;
+ }
+ }
+
+ /*
+ * Should upgrade. Start by creating an executable copy of the update
+ * image in the scratch area.
+ */
+ RTEXITCODE rcExit = txsFinalizeScratch();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ char szTmpPath[RTPATH_MAX];
+ rc = RTPathJoin(szTmpPath, sizeof(szTmpPath), g_szScratchPath, RTPathFilename(szOrgPath));
+ if (RT_SUCCESS(rc))
+ {
+ RTFileDelete(szTmpPath); /* shouldn't hurt. */
+
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, szTmpPath,
+ RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE
+ | (0755 << RTFILE_O_CREATE_MODE_SHIFT));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pvUpgrade, UpgradeInfo.cbObject, NULL);
+ RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try execute the new image and quit if it works.
+ */
+ const char **papszArgs = (const char **)RTMemAlloc((argc + 2 + 1) * sizeof(char **));
+ if (papszArgs)
+ {
+ papszArgs[0] = szTmpPath;
+ for (int i = 1; i < argc; i++)
+ papszArgs[i] = argv[i];
+ papszArgs[argc] = "--upgrading";
+ papszArgs[argc + 1] = szOrgPath;
+ papszArgs[argc + 2] = NULL;
+
+ RTMsgInfo("Launching intermediate automatic upgrade stage: \"%s\"\n", szTmpPath);
+ RTPROCESS hProc;
+ rc = RTProcCreate(szTmpPath, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
+ if (RT_SUCCESS(rc))
+ *pfExit = true;
+ else
+ RTMsgError("RTProcCreate(\"%s\"): %Rrc (upgrade stage 1)\n", szTmpPath, rc);
+ RTMemFree(papszArgs);
+ }
+ else
+ RTMsgError("RTMemAlloc failed during upgrade attempt (stage)\n");
+ }
+ else
+ RTMsgError("RTFileWrite(%s,,%zu): %Rrc\n", szTmpPath, UpgradeInfo.cbObject, rc);
+ }
+ else
+ RTMsgError("RTFileOpen(,%s,): %Rrc\n", szTmpPath, rc);
+ }
+ else
+ RTMsgError("Failed to construct path to temporary upgrade image: %Rrc\n", rc);
+ }
+
+ RTFileReadAllFree(pvUpgrade, cbUpgrade);
+ return rcExit;
+}
+
+/**
+ * Determines the default configuration.
+ */
+static void txsSetDefaults(void)
+{
+ /*
+ * OS and ARCH.
+ */
+ AssertCompile(sizeof(KBUILD_TARGET) <= sizeof(g_szOsShortName));
+ strcpy(g_szOsShortName, KBUILD_TARGET);
+
+ AssertCompile(sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szArchShortName));
+ strcpy(g_szArchShortName, KBUILD_TARGET_ARCH);
+
+ AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsDotArchShortName));
+ strcpy(g_szOsDotArchShortName, KBUILD_TARGET);
+ g_szOsDotArchShortName[sizeof(KBUILD_TARGET) - 1] = '.';
+ strcpy(&g_szOsDotArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
+
+ AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsSlashArchShortName));
+ strcpy(g_szOsSlashArchShortName, KBUILD_TARGET);
+ g_szOsSlashArchShortName[sizeof(KBUILD_TARGET) - 1] = '/';
+ strcpy(&g_szOsSlashArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ strcpy(g_szExeSuff, ".exe");
+ strcpy(g_szScriptSuff, ".cmd");
+#else
+ strcpy(g_szExeSuff, "");
+ strcpy(g_szScriptSuff, ".sh");
+#endif
+
+ int rc = RTPathGetCurrent(g_szCwd, sizeof(g_szCwd));
+ if (RT_FAILURE(rc))
+ RTMsgError("RTPathGetCurrent failed: %Rrc\n", rc);
+ g_szCwd[sizeof(g_szCwd) - 1] = '\0';
+
+ if (!RTProcGetExecutablePath(g_szTxsDir, sizeof(g_szTxsDir)))
+ RTMsgError("RTProcGetExecutablePath failed!\n");
+ g_szTxsDir[sizeof(g_szTxsDir) - 1] = '\0';
+ RTPathStripFilename(g_szTxsDir);
+ RTPathStripTrailingSlash(g_szTxsDir);
+
+ /*
+ * The CD/DVD-ROM location.
+ */
+ /** @todo do a better job here :-) */
+#ifdef RT_OS_WINDOWS
+ strcpy(g_szDefCdRomPath, "D:/");
+#elif defined(RT_OS_OS2)
+ strcpy(g_szDefCdRomPath, "D:/");
+#else
+ if (RTDirExists("/media"))
+ strcpy(g_szDefCdRomPath, "/media/cdrom");
+ else
+ strcpy(g_szDefCdRomPath, "/mnt/cdrom");
+#endif
+ strcpy(g_szCdRomPath, g_szDefCdRomPath);
+
+ /*
+ * Temporary directory.
+ */
+ rc = RTPathTemp(g_szDefScratchPath, sizeof(g_szDefScratchPath));
+ if (RT_SUCCESS(rc))
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS)
+ rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "txs-XXXX.tmp");
+#else
+ rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "txs-XXXXXXXXX.tmp");
+#endif
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc);
+ strcpy(g_szDefScratchPath, "/tmp/txs-XXXX.tmp");
+ }
+ strcpy(g_szScratchPath, g_szDefScratchPath);
+
+ /*
+ * The default transporter is the first one.
+ */
+ g_pTransport = g_apTransports[0];
+}
+
+/**
+ * Prints the usage.
+ *
+ * @param pStrm Where to print it.
+ * @param pszArgv0 The program name (argv[0]).
+ */
+static void txsUsage(PRTSTREAM pStrm, const char *pszArgv0)
+{
+ RTStrmPrintf(pStrm,
+ "Usage: %Rbn [options]\n"
+ "\n"
+ "Options:\n"
+ " --cdrom <path>\n"
+ " Where the CD/DVD-ROM will be mounted.\n"
+ " Default: %s\n"
+ " --scratch <path>\n"
+ " Where to put scratch files.\n"
+ " Default: %s \n"
+ ,
+ pszArgv0,
+ g_szDefCdRomPath,
+ g_szDefScratchPath);
+ RTStrmPrintf(pStrm,
+ " --transport <name>\n"
+ " Use the specified transport layer, one of the following:\n");
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ RTStrmPrintf(pStrm, " %s - %s\n", g_apTransports[i]->szName, g_apTransports[i]->pszDesc);
+ RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName);
+ RTStrmPrintf(pStrm,
+ " --auto-upgrade, --no-auto-upgrade\n"
+ " To enable or disable the automatic upgrade mechanism where any different\n"
+ " version found on the CD-ROM on startup will replace the initial copy.\n"
+ " Default: --auto-upgrade\n"
+ " --wait-cdrom <secs>\n"
+ " Number of seconds to wait for the CD-ROM to be mounted before giving up\n"
+ " on automatic upgrading.\n"
+ " Default: --wait-cdrom 1; solaris: --wait-cdrom 8\n"
+ " --upgrading <org-path>\n"
+ " Internal use only.\n");
+ RTStrmPrintf(pStrm,
+ " --display-output, --no-display-output\n"
+ " Display the output and the result of all child processes.\n");
+ RTStrmPrintf(pStrm,
+ " --foreground\n"
+ " Don't daemonize, run in the foreground.\n");
+ RTStrmPrintf(pStrm,
+ " --verbose, -v\n"
+ " Increases the verbosity level. Can be specified multiple times.\n");
+ RTStrmPrintf(pStrm,
+ " --quiet, -q\n"
+ " Mutes any logging output.\n");
+ RTStrmPrintf(pStrm,
+ " --help, -h, -?\n"
+ " Display this message and exit.\n"
+ " --version, -V\n"
+ " Display the version and exit.\n");
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (g_apTransports[i]->cOpts)
+ {
+ RTStrmPrintf(pStrm,
+ "\n"
+ "Options for %s:\n", g_apTransports[i]->szName);
+ g_apTransports[i]->pfnUsage(g_pStdOut);
+ }
+}
+
+/**
+ * Parses the arguments.
+ *
+ * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
+ * @param argc The number of arguments.
+ * @param argv The argument vector.
+ * @param pfExit For indicating exit when the exit code is zero.
+ */
+static RTEXITCODE txsParseArgv(int argc, char **argv, bool *pfExit)
+{
+ *pfExit = false;
+
+ /*
+ * Storage for locally handled options.
+ */
+ bool fAutoUpgrade = true;
+ bool fDaemonize = true;
+ bool fDaemonized = false;
+ const char *pszUpgrading = NULL;
+#ifdef RT_OS_SOLARIS
+ uint32_t cSecsCdWait = 8;
+#else
+ uint32_t cSecsCdWait = 1;
+#endif
+
+ /*
+ * Combine the base and transport layer option arrays.
+ */
+ static const RTGETOPTDEF s_aBaseOptions[] =
+ {
+ { "--transport", 't', RTGETOPT_REQ_STRING },
+ { "--cdrom", 'c', RTGETOPT_REQ_STRING },
+ { "--wait-cdrom", 'w', RTGETOPT_REQ_UINT32 },
+ { "--scratch", 's', RTGETOPT_REQ_STRING },
+ { "--auto-upgrade", 'a', RTGETOPT_REQ_NOTHING },
+ { "--no-auto-upgrade", 'A', RTGETOPT_REQ_NOTHING },
+ { "--upgrading", 'U', RTGETOPT_REQ_STRING },
+ { "--display-output", 'd', RTGETOPT_REQ_NOTHING },
+ { "--no-display-output",'D', RTGETOPT_REQ_NOTHING },
+ { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
+ { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ };
+
+ size_t cOptions = RT_ELEMENTS(s_aBaseOptions);
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ cOptions += g_apTransports[i]->cOpts;
+
+ PRTGETOPTDEF paOptions = (PRTGETOPTDEF)alloca(cOptions * sizeof(RTGETOPTDEF));
+ if (!paOptions)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "alloca failed\n");
+
+ memcpy(paOptions, s_aBaseOptions, sizeof(s_aBaseOptions));
+ cOptions = RT_ELEMENTS(s_aBaseOptions);
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ {
+ memcpy(&paOptions[cOptions], g_apTransports[i]->paOpts, g_apTransports[i]->cOpts * sizeof(RTGETOPTDEF));
+ cOptions += g_apTransports[i]->cOpts;
+ }
+
+ /*
+ * Parse the arguments.
+ */
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv, paOptions, cOptions, 1, 0 /* fFlags */);
+ AssertRC(rc);
+
+ int ch;
+ RTGETOPTUNION Val;
+ while ((ch = RTGetOpt(&GetState, &Val)))
+ {
+ switch (ch)
+ {
+ case 'a':
+ fAutoUpgrade = true;
+ break;
+
+ case 'A':
+ fAutoUpgrade = false;
+ break;
+
+ case 'c':
+ rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "CD/DVD-ROM is path too long (%Rrc)\n", rc);
+ break;
+
+ case 'd':
+ g_fDisplayOutput = true;
+ break;
+
+ case 'D':
+ g_fDisplayOutput = false;
+ break;
+
+ case 'f':
+ fDaemonize = false;
+ break;
+
+ case 'h':
+ txsUsage(g_pStdOut, argv[0]);
+ *pfExit = true;
+ return RTEXITCODE_SUCCESS;
+
+ case 's':
+ rc = RTStrCopy(g_szScratchPath, sizeof(g_szScratchPath), Val.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "scratch path is too long (%Rrc)\n", rc);
+ break;
+
+ case 't':
+ {
+ PCTXSTRANSPORT pTransport = NULL;
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (!strcmp(g_apTransports[i]->szName, Val.psz))
+ {
+ pTransport = g_apTransports[i];
+ break;
+ }
+ if (!pTransport)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown transport layer name '%s'\n", Val.psz);
+ g_pTransport = pTransport;
+ break;
+ }
+
+ case 'U':
+ pszUpgrading = Val.psz;
+ break;
+
+ case 'w':
+ cSecsCdWait = Val.u32;
+ break;
+
+ case 'q':
+ g_cVerbose = 0;
+ break;
+
+ case 'v':
+ g_cVerbose++;
+ break;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ *pfExit = true;
+ return RTEXITCODE_SUCCESS;
+
+ case 'Z':
+ fDaemonized = true;
+ fDaemonize = false;
+ break;
+
+ default:
+ {
+ rc = VERR_TRY_AGAIN;
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (g_apTransports[i]->cOpts)
+ {
+ rc = g_apTransports[i]->pfnOption(ch, &Val);
+ if (RT_SUCCESS(rc))
+ break;
+ if (rc != VERR_TRY_AGAIN)
+ {
+ *pfExit = true;
+ return RTEXITCODE_SYNTAX;
+ }
+ }
+ if (rc == VERR_TRY_AGAIN)
+ {
+ *pfExit = true;
+ return RTGetOptPrintError(ch, &Val);
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Handle automatic upgrading of the service.
+ */
+ if (fAutoUpgrade && !*pfExit)
+ {
+ RTEXITCODE rcExit;
+ if (pszUpgrading)
+ rcExit = txsAutoUpdateStage2(argc, argv, pfExit, pszUpgrading);
+ else
+ rcExit = txsAutoUpdateStage1(argc, argv, cSecsCdWait, pfExit);
+ if ( *pfExit
+ || rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ }
+
+ /*
+ * Daemonize ourselves if asked to.
+ */
+ if (fDaemonize && !*pfExit)
+ {
+ if (g_cVerbose > 0)
+ RTMsgInfo("Daemonizing...");
+ rc = RTProcDaemonize(argv, "--daemonized");
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
+ *pfExit = true;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
+ */
+static DECLCALLBACK(void) logHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
+{
+ /* Some introductory information. */
+ static RTTIMESPEC s_TimeSpec;
+ char szTmp[256];
+ if (enmPhase == RTLOGPHASE_BEGIN)
+ RTTimeNow(&s_TimeSpec);
+ RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
+
+ switch (enmPhase)
+ {
+ case RTLOGPHASE_BEGIN:
+ {
+ pfnLog(pLoggerRelease,
+ "TestExecService (Validation Kit TxS) %s r%s (verbosity: %u) %s %s (%s %s) release log\n"
+ "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n"
+ "Log opened %s\n",
+ RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbose,
+ KBUILD_TARGET, KBUILD_TARGET_ARCH,
+ __DATE__, __TIME__, szTmp);
+
+ int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
+
+ /* the package type is interesting for Linux distributions */
+ char szExecName[RTPATH_MAX];
+ char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+ pfnLog(pLoggerRelease,
+ "Executable: %s\n"
+ "Process ID: %u\n"
+ "Package type: %s"
+#ifdef VBOX_OSE
+ " (OSE)"
+#endif
+ "\n",
+ pszExecName ? pszExecName : "unknown",
+ RTProcSelf(),
+ VBOX_PACKAGE_STRING);
+ break;
+ }
+
+ case RTLOGPHASE_PREROTATE:
+ pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_POSTROTATE:
+ pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_END:
+ pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
+ break;
+
+ default:
+ /* nothing */
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Initialize the runtime.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Determine defaults and parse the arguments.
+ */
+ txsSetDefaults();
+ bool fExit;
+ RTEXITCODE rcExit = txsParseArgv(argc, argv, &fExit);
+ if (rcExit != RTEXITCODE_SUCCESS || fExit)
+ return rcExit;
+
+ /*
+ * Enable (release) TxS logging to stdout + file. This is independent from the actual test cases being run.
+ *
+ * Keep the log file path + naming predictable (the OS' temp dir) so that we later can retrieve it
+ * from the host side without guessing much.
+ *
+ * If enabling logging fails for some reason, just tell but don't bail out to not make tests fail.
+ */
+ char szLogFile[RTPATH_MAX];
+ rc = RTPathTemp(szLogFile, sizeof(szLogFile));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathAppend(szLogFile, sizeof(szLogFile), "vbox-txs-release.log");
+ if (RT_FAILURE(rc))
+ RTMsgError("RTPathAppend failed when constructing log file path: %Rrc\n", rc);
+ }
+ else
+ RTMsgError("RTPathTemp failed when constructing log file path: %Rrc\n", rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
+ rc = RTLogCreateEx(&g_pRelLogger, "VBOX_TXS_RELEASE_LOG", fFlags, "all",
+ RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, UINT32_MAX /* cMaxEntriesPerGroup */,
+ 0 /*cBufDescs*/, NULL /* paBufDescs */, RTLOGDEST_STDOUT | RTLOGDEST_FILE,
+ logHeaderFooter /* pfnPhase */ ,
+ 10 /* cHistory */, 100 * _1M /* cbHistoryFileMax */, RT_SEC_1DAY /* cSecsHistoryTimeSlot */,
+ NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
+ NULL /* pErrInfo */, "%s", szLogFile);
+ if (RT_SUCCESS(rc))
+ {
+ RTLogRelSetDefaultInstance(g_pRelLogger);
+ if (g_cVerbose)
+ {
+ RTMsgInfo("Setting verbosity logging to level %u\n", g_cVerbose);
+ switch (g_cVerbose) /* Not very elegant, but has to do it for now. */
+ {
+ case 1:
+ rc = RTLogGroupSettings(g_pRelLogger, "all.e.l.l2");
+ break;
+
+ case 2:
+ rc = RTLogGroupSettings(g_pRelLogger, "all.e.l.l2.l3");
+ break;
+
+ case 3:
+ rc = RTLogGroupSettings(g_pRelLogger, "all.e.l.l2.l3.l4");
+ break;
+
+ case 4:
+ RT_FALL_THROUGH();
+ default:
+ rc = RTLogGroupSettings(g_pRelLogger, "all.e.l.l2.l3.l4.f");
+ break;
+ }
+ if (RT_FAILURE(rc))
+ RTMsgError("Setting logging groups failed, rc=%Rrc\n", rc);
+ }
+ }
+ else
+ RTMsgError("Failed to create release logger: %Rrc", rc);
+
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("Log file written to '%s'\n", szLogFile);
+ }
+
+ /*
+ * Generate a UUID for this TXS instance.
+ */
+ rc = RTUuidCreate(&g_InstanceUuid);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc);
+ if (g_cVerbose > 0)
+ RTMsgInfo("Instance UUID: %RTuuid", &g_InstanceUuid);
+
+ /*
+ * Finalize the scratch directory and initialize the transport layer.
+ */
+ rcExit = txsFinalizeScratch();
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ rc = g_pTransport->pfnInit();
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+
+ /*
+ * Ok, start working
+ */
+ rcExit = txsMainLoop();
+
+ /*
+ * Cleanup.
+ */
+ g_pTransport->pfnTerm();
+
+ return rcExit;
+}
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceInternal.h b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceInternal.h
new file mode 100644
index 00000000..3aaf4578
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceInternal.h
@@ -0,0 +1,232 @@
+/* $Id: TestExecServiceInternal.h $ */
+/** @file
+ * TestExecServ - Basic Remote Execution Service, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_TestExecServ_TestExecServiceInternal_h
+#define VBOX_INCLUDED_SRC_TestExecServ_TestExecServiceInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Packet header.
+ */
+typedef struct TXSPKTHDR
+{
+ /** The unpadded packet length. This include this header. */
+ uint32_t cb;
+ /** The CRC-32 for the packet starting from the opcode field. 0 if the packet
+ * hasn't been CRCed. */
+ uint32_t uCrc32;
+ /** Packet opcode, an unterminated ASCII string. */
+ uint8_t achOpcode[8];
+} TXSPKTHDR;
+AssertCompileSize(TXSPKTHDR, 16);
+/** Pointer to a packet header. */
+typedef TXSPKTHDR *PTXSPKTHDR;
+/** Pointer to a packet header. */
+typedef TXSPKTHDR const *PCTXSPKTHDR;
+/** Pointer to a packet header pointer. */
+typedef PTXSPKTHDR *PPTXSPKTHDR;
+
+/** Packet alignment. */
+#define TXSPKT_ALIGNMENT 16
+/** Max packet size. */
+#define TXSPKT_MAX_SIZE _256K
+
+
+/**
+ * Transport layer descriptor.
+ */
+typedef struct TXSTRANSPORT
+{
+ /** The name. */
+ char szName[16];
+ /** The description. */
+ const char *pszDesc;
+ /** Pointer to an array of options. */
+ PCRTGETOPTDEF paOpts;
+ /** The number of options in the array. */
+ size_t cOpts;
+
+ /**
+ * Print the usage information for this transport layer.
+ *
+ * @param pStream The stream to print the usage info to.
+ *
+ * @remarks This is only required if TXSTRANSPORT::cOpts is greater than 0.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnUsage,(PRTSTREAM pStream));
+
+ /**
+ * Handle an option.
+ *
+ * When encountering an options that is not part of the base options, we'll call
+ * this method for each transport layer until one handles it.
+ *
+ * @retval VINF_SUCCESS if handled.
+ * @retval VERR_TRY_AGAIN if not handled.
+ * @retval VERR_INVALID_PARAMETER if we should exit with a non-zero status.
+ *
+ * @param ch The short option value.
+ * @param pVal Pointer to the value union.
+ *
+ * @remarks This is only required if TXSTRANSPORT::cOpts is greater than 0.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnOption,(int ch, PCRTGETOPTUNION pVal));
+
+ /**
+ * Initializes the transport layer.
+ *
+ * @returns IPRT status code. On errors, the transport layer shall call
+ * RTMsgError to display the error details to the user.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnInit,(void));
+
+ /**
+ * Terminate the transport layer, closing and freeing resources.
+ *
+ * On errors, the transport layer shall call RTMsgError to display the error
+ * details to the user.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnTerm,(void));
+
+ /**
+ * Polls for incoming packets.
+ *
+ * @returns true if there are pending packets, false if there isn't.
+ */
+ DECLR3CALLBACKMEMBER(bool, pfnPollIn,(void));
+
+ /**
+ * Adds any pollable handles to the poll set.
+ *
+ * This is optional and layers that doesn't have anything that can be polled
+ * shall set this method pointer to NULL to indicate that pfnPollIn must be used
+ * instead.
+ *
+ * @returns IPRT status code.
+ * @param hPollSet The poll set to add them to.
+ * @param idStart The handle ID to start at.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnPollSetAdd,(RTPOLLSET hPollSet, uint32_t idStart));
+
+ /**
+ * Receives an incoming packet.
+ *
+ * This will block until the data becomes available or we're interrupted by a
+ * signal or something.
+ *
+ * @returns IPRT status code. On error conditions other than VERR_INTERRUPTED,
+ * the current operation will be aborted when applicable. When
+ * interrupted, the transport layer will store the data until the next
+ * receive call.
+ *
+ * @param ppPktHdr Where to return the pointer to the packet we've
+ * read. This is allocated from the heap using
+ * RTMemAlloc (w/ TXSPKT_ALIGNMENT) and must be
+ * free by calling RTMemFree.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnRecvPkt,(PPTXSPKTHDR ppPktHdr));
+
+ /**
+ * Sends an outgoing packet.
+ *
+ * This will block until the data has been written.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_INTERRUPTED if interrupted before anything was sent.
+ *
+ * @param pPktHdr The packet to send. The size is given by
+ * aligning the size in the header by
+ * TXSPKT_ALIGNMENT.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnSendPkt,(PCTXSPKTHDR pPktHdr));
+
+ /**
+ * Sends a babble packet and disconnects the client (if applicable).
+ *
+ * @param pPktHdr The packet to send. The size is given by
+ * aligning the size in the header by
+ * TXSPKT_ALIGNMENT.
+ * @param cMsSendTimeout The send timeout measured in milliseconds.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnBabble,(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout));
+
+ /**
+ * Notification about a client HOWDY.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyHowdy,(void));
+
+ /**
+ * Notification about a client BYE.
+ *
+ * For connection oriented transport layers, it would be good to disconnect the
+ * client at this point.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyBye,(void));
+
+ /**
+ * Notification about a REBOOT or SHUTDOWN.
+ *
+ * For connection oriented transport layers, stop listening for and
+ * accepting at this point.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyReboot,(void));
+
+ /** Non-zero end marker. */
+ uint32_t u32EndMarker;
+} TXSTRANSPORT;
+/** Pointer to a const transport layer descriptor. */
+typedef const struct TXSTRANSPORT *PCTXSTRANSPORT;
+
+
+extern TXSTRANSPORT const g_TcpTransport;
+extern TXSTRANSPORT const g_SerialTransport;
+extern TXSTRANSPORT const g_FileSysTransport;
+extern TXSTRANSPORT const g_GuestPropTransport;
+extern TXSTRANSPORT const g_TestDevTransport;
+
+extern uint32_t g_cVerbose;
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_TestExecServ_TestExecServiceInternal_h */
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceSerial.cpp b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceSerial.cpp
new file mode 100644
index 00000000..5bfa1059
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceSerial.cpp
@@ -0,0 +1,417 @@
+/* $Id: TestExecServiceSerial.cpp $ */
+/** @file
+ * TestExecServ - Basic Remote Execution Service, Serial port Transport Layer.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+#include <iprt/serialport.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "TestExecServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The default baud rate port. */
+#define TXS_SERIAL_DEF_BAUDRATE 115200
+/** The default serial device to use. */
+#if defined(RT_OS_LINUX)
+# define TXS_SERIAL_DEF_DEVICE "/dev/ttyS0"
+#elif defined(RT_OS_WINDOWS)
+# define TXS_SERIAL_DEF_DEVICE "COM1"
+#elif defined(RT_OS_SOLARIS)
+# define TXS_SERIAL_DEF_DEVICE "<todo>"
+#elif defined(RT_OS_FREEBSD)
+# define TXS_SERIAL_DEF_DEVICE "<todo>"
+#elif defined(RT_OS_DARWIN)
+# define TXS_SERIAL_DEF_DEVICE "<todo>"
+#else
+# error "Port me"
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name Serial Parameters
+ * @{ */
+/** The addresses to bind to. Empty string means any. */
+static uint32_t g_uSerialBaudRate = TXS_SERIAL_DEF_BAUDRATE;
+/** The serial port device to use. */
+static char g_szSerialDevice[256] = TXS_SERIAL_DEF_DEVICE;
+/** @} */
+
+/** The serial port handle. */
+static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT;
+/** The size of the stashed data. */
+static size_t g_cbSerialStashed = 0;
+/** The size of the stashed data allocation. */
+static size_t g_cbSerialStashedAlloced = 0;
+/** The stashed data. */
+static uint8_t *g_pbSerialStashed = NULL;
+
+
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyReboot}
+ */
+static DECLCALLBACK(void) txsSerialNotifyReboot(void)
+{
+ /* nothing to do here */
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyBye}
+ */
+static DECLCALLBACK(void) txsSerialNotifyBye(void)
+{
+ /* nothing to do here */
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyHowdy}
+ */
+static DECLCALLBACK(void) txsSerialNotifyHowdy(void)
+{
+ /* nothing to do here */
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnBabble}
+ */
+static DECLCALLBACK(void) txsSerialBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
+{
+ Assert(g_hSerialPort != NIL_RTSERIALPORT);
+
+ /*
+ * Try send the babble reply.
+ */
+ NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
+ int rc;
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
+ do rc = RTSerialPortWrite(g_hSerialPort, pPktHdr, cbToSend, NULL);
+ while (rc == VERR_INTERRUPTED);
+
+ /*
+ * Disconnect the client.
+ */
+ Log(("txsSerialBabble: RTSerialPortWrite rc=%Rrc\n", rc));
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnSendPkt}
+ */
+static DECLCALLBACK(int) txsSerialSendPkt(PCTXSPKTHDR pPktHdr)
+{
+ Assert(g_hSerialPort != NIL_RTSERIALPORT);
+ Assert(pPktHdr->cb >= sizeof(TXSPKTHDR));
+
+ /*
+ * Write it.
+ */
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
+ int rc = RTSerialPortWrite(g_hSerialPort, pPktHdr, cbToSend, NULL);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_INTERRUPTED)
+ {
+ /* assume fatal connection error. */
+ Log(("RTSerialPortWrite -> %Rrc\n", rc));
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnRecvPkt}
+ */
+static DECLCALLBACK(int) txsSerialRecvPkt(PPTXSPKTHDR ppPktHdr)
+{
+ Assert(g_hSerialPort != NIL_RTSERIALPORT);
+
+ int rc = VINF_SUCCESS;
+ *ppPktHdr = NULL;
+
+ /*
+ * Read state.
+ */
+ size_t offData = 0;
+ size_t cbData = 0;
+ size_t cbDataAlloced;
+ uint8_t *pbData = NULL;
+
+ /*
+ * Any stashed data?
+ */
+ if (g_cbSerialStashedAlloced)
+ {
+ offData = g_cbSerialStashed;
+ cbDataAlloced = g_cbSerialStashedAlloced;
+ pbData = g_pbSerialStashed;
+
+ g_cbSerialStashed = 0;
+ g_cbSerialStashedAlloced = 0;
+ g_pbSerialStashed = NULL;
+ }
+ else
+ {
+ cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);
+ pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
+ if (!pbData)
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Read and valid the length.
+ */
+ while (offData < sizeof(uint32_t))
+ {
+ size_t cbRead = sizeof(uint32_t) - offData;
+ rc = RTSerialPortRead(g_hSerialPort, pbData + offData, cbRead, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ offData += cbRead;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ ASMCompilerBarrier(); /* paranoia^3 */
+ cbData = *(uint32_t volatile *)pbData;
+ if (cbData >= sizeof(TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)
+ {
+ /*
+ * Align the length and reallocate the return packet it necessary.
+ */
+ cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);
+ if (cbData > cbDataAlloced)
+ {
+ void *pvNew = RTMemRealloc(pbData, cbData);
+ if (pvNew)
+ {
+ pbData = (uint8_t *)pvNew;
+ cbDataAlloced = cbData;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the remainder of the data.
+ */
+ while (offData < cbData)
+ {
+ size_t cbRead = cbData - offData;
+ rc = RTSerialPortRead(g_hSerialPort, pbData + offData, cbRead, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ offData += cbRead;
+ }
+ }
+ }
+ else
+ rc = VERR_NET_PROTOCOL_ERROR;
+ }
+ if (RT_SUCCESS(rc))
+ *ppPktHdr = (PTXSPKTHDR)pbData;
+ else
+ {
+ /*
+ * Deal with errors.
+ */
+ if (rc == VERR_INTERRUPTED)
+ {
+ /* stash it away for the next call. */
+ g_cbSerialStashed = cbData;
+ g_cbSerialStashedAlloced = cbDataAlloced;
+ g_pbSerialStashed = pbData;
+ }
+ else
+ {
+ RTMemFree(pbData);
+
+ /* assume fatal connection error. */
+ Log(("txsSerialRecvPkt: RTSerialPortRead -> %Rrc\n", rc));
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnPollIn}
+ */
+static DECLCALLBACK(bool) txsSerialPollIn(void)
+{
+ Assert(g_hSerialPort != NIL_RTSERIALPORT);
+
+ uint32_t fEvtsRecv = 0;
+ int rc = RTSerialPortEvtPoll(g_hSerialPort, RTSERIALPORT_EVT_F_DATA_RX,
+ &fEvtsRecv, 0/*cMillies*/);
+ return RT_SUCCESS(rc);
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnTerm}
+ */
+static DECLCALLBACK(void) txsSerialTerm(void)
+{
+ if (g_hSerialPort != NIL_RTSERIALPORT)
+ RTSerialPortClose(g_hSerialPort);
+
+ /* Clean up stashing. */
+ if (g_pbSerialStashed)
+ RTMemFree(g_pbSerialStashed);
+ g_pbSerialStashed = NULL;
+ g_cbSerialStashed = 0;
+ g_cbSerialStashedAlloced = 0;
+
+ Log(("txsSerialTerm: done\n"));
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnInit}
+ */
+static DECLCALLBACK(int) txsSerialInit(void)
+{
+ uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ | RTSERIALPORT_OPEN_F_WRITE;
+ int rc = RTSerialPortOpen(&g_hSerialPort, &g_szSerialDevice[0], fOpenFlags);
+ if (RT_SUCCESS(rc))
+ {
+ RTSERIALPORTCFG SerPortCfg;
+
+ SerPortCfg.uBaudRate = g_uSerialBaudRate;
+ SerPortCfg.enmParity = RTSERIALPORTPARITY_NONE;
+ SerPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
+ SerPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
+ rc = RTSerialPortCfgSet(g_hSerialPort, &SerPortCfg, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTSerialPortCfgSet() failed: %Rrc\n", rc);
+ RTSerialPortClose(g_hSerialPort);
+ g_hSerialPort = NIL_RTSERIALPORT;
+ }
+ }
+ else
+ RTMsgError("RTSerialPortOpen(, %s, %#x) failed: %Rrc\n",
+ g_szSerialDevice, fOpenFlags, rc);
+
+ return rc;
+}
+
+/** Options */
+enum TXSSERIALOPT
+{
+ TXSSERIALOPT_BAUDRATE = 1000,
+ TXSSERIALOPT_DEVICE
+};
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnOption}
+ */
+static DECLCALLBACK(int) txsSerialOption(int ch, PCRTGETOPTUNION pVal)
+{
+ int rc;
+
+ switch (ch)
+ {
+ case TXSSERIALOPT_DEVICE:
+ rc = RTStrCopy(g_szSerialDevice, sizeof(g_szSerialDevice), pVal->psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Serial port device path is too long (%Rrc)", rc);
+ if (!g_szSerialDevice[0])
+ strcpy(g_szSerialDevice, TXS_SERIAL_DEF_DEVICE);
+ return VINF_SUCCESS;
+ case TXSSERIALOPT_BAUDRATE:
+ g_uSerialBaudRate = pVal->u32 == 0 ? TXS_SERIAL_DEF_BAUDRATE : pVal->u32;
+ return VINF_SUCCESS;
+ }
+ return VERR_TRY_AGAIN;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnUsage}
+ */
+DECLCALLBACK(void) txsSerialUsage(PRTSTREAM pStream)
+{
+ RTStrmPrintf(pStream,
+ " --serial-device <device>\n"
+ " Selects the serial port to use.\n"
+ " Default: %s\n"
+ " --serial-baudrate <baudrate>\n"
+ " Selects the baudrate to set the serial port to.\n"
+ " Default: %u\n"
+ , TXS_SERIAL_DEF_DEVICE, TXS_SERIAL_DEF_BAUDRATE);
+}
+
+/** Command line options for the serial transport layer. */
+static const RTGETOPTDEF g_SerialOpts[] =
+{
+ { "--serial-device", TXSSERIALOPT_DEVICE, RTGETOPT_REQ_STRING },
+ { "--serial-baudrate", TXSSERIALOPT_BAUDRATE, RTGETOPT_REQ_UINT32 }
+};
+
+/** Serial port transport layer. */
+const TXSTRANSPORT g_SerialTransport =
+{
+ /* .szName = */ "serial",
+ /* .pszDesc = */ "Serial",
+ /* .cOpts = */ &g_SerialOpts[0],
+ /* .paOpts = */ RT_ELEMENTS(g_SerialOpts),
+ /* .pfnUsage = */ txsSerialUsage,
+ /* .pfnOption = */ txsSerialOption,
+ /* .pfnInit = */ txsSerialInit,
+ /* .pfnTerm = */ txsSerialTerm,
+ /* .pfnPollIn = */ txsSerialPollIn,
+ /* .pfnPollSetAdd = */ NULL,
+ /* .pfnRecvPkt = */ txsSerialRecvPkt,
+ /* .pfnSendPkt = */ txsSerialSendPkt,
+ /* .pfnBabble = */ txsSerialBabble,
+ /* .pfnNotifyHowdy = */ txsSerialNotifyHowdy,
+ /* .pfnNotifyBye = */ txsSerialNotifyBye,
+ /* .pfnNotifyReboot = */ txsSerialNotifyReboot,
+ /* .u32EndMarker = */ UINT32_C(0x12345678)
+};
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceTcp.cpp b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceTcp.cpp
new file mode 100644
index 00000000..2c78477e
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceTcp.cpp
@@ -0,0 +1,842 @@
+/* $Id: TestExecServiceTcp.cpp $ */
+/** @file
+ * TestExecServ - Basic Remote Execution Service, TCP/IP Transport Layer.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/tcp.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "TestExecServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The default server port. */
+#define TXS_TCP_DEF_BIND_PORT 5042
+/** The default client port. */
+#define TXS_TCP_DEF_CONNECT_PORT 5048
+
+/** The default server bind address. */
+#define TXS_TCP_DEF_BIND_ADDRESS ""
+/** The default client connect address (i.e. of the host server). */
+#define TXS_TCP_DEF_CONNECT_ADDRESS "10.0.2.2"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name TCP Parameters
+ * @{ */
+static enum { TXSTCPMODE_BOTH, TXSTCPMODE_CLIENT, TXSTCPMODE_SERVER }
+ g_enmTcpMode = TXSTCPMODE_BOTH;
+
+/** The addresses to bind to. Empty string means any. */
+static char g_szTcpBindAddr[256] = TXS_TCP_DEF_BIND_ADDRESS;
+/** The TCP port to listen to. */
+static uint32_t g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;
+/** The addresses to connect to if fRevesedSetupMode is @c true. */
+static char g_szTcpConnectAddr[256] = TXS_TCP_DEF_CONNECT_ADDRESS;
+/** The TCP port to listen to. */
+static uint32_t g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;
+/** @} */
+
+/** Critical section for serializing access to the next few variables. */
+static RTCRITSECT g_TcpCritSect;
+/** Pointer to the TCP server instance. */
+static PRTTCPSERVER g_pTcpServer = NULL;
+/** Thread calling RTTcpServerListen2. */
+static RTTHREAD g_hThreadTcpServer = NIL_RTTHREAD;
+/** Thread calling RTTcpClientConnect. */
+static RTTHREAD g_hThreadTcpConnect = NIL_RTTHREAD;
+/** The main thread handle (for signalling). */
+static RTTHREAD g_hThreadMain = NIL_RTTHREAD;
+/** Stop connecting attempts when set. */
+static bool g_fTcpStopConnecting = false;
+/** Connect cancel cookie. */
+static PRTTCPCLIENTCONNECTCANCEL volatile g_pTcpConnectCancelCookie = NULL;
+
+/** Socket of the current client. */
+static RTSOCKET g_hTcpClient = NIL_RTSOCKET;
+/** Indicates whether g_hTcpClient comes from the server or from a client
+ * connect (relevant when closing it). */
+static bool g_fTcpClientFromServer = false;
+/** The size of the stashed data. */
+static size_t g_cbTcpStashed = 0;
+/** The size of the stashed data allocation. */
+static size_t g_cbTcpStashedAlloced = 0;
+/** The stashed data. */
+static uint8_t *g_pbTcpStashed = NULL;
+
+
+
+/**
+ * Disconnects the current client.
+ */
+static void txsTcpDisconnectClient(void)
+{
+ int rc;
+ if (g_fTcpClientFromServer)
+ rc = RTTcpServerDisconnectClient2(g_hTcpClient);
+ else
+ rc = RTTcpClientClose(g_hTcpClient);
+ AssertRCSuccess(rc);
+ g_hTcpClient = NIL_RTSOCKET;
+}
+
+/**
+ * Sets the current client socket in a safe manner.
+ *
+ * @returns NIL_RTSOCKET if consumed, other wise hTcpClient.
+ * @param hTcpClient The client socket.
+ */
+static RTSOCKET txsTcpSetClient(RTSOCKET hTcpClient)
+{
+ RTCritSectEnter(&g_TcpCritSect);
+ if ( g_hTcpClient == NIL_RTSOCKET
+ && !g_fTcpStopConnecting
+ && g_hThreadMain != NIL_RTTHREAD
+ )
+ {
+ g_fTcpClientFromServer = true;
+ g_hTcpClient = hTcpClient;
+ int rc = RTThreadUserSignal(g_hThreadMain); AssertRC(rc);
+ hTcpClient = NIL_RTSOCKET;
+ }
+ RTCritSectLeave(&g_TcpCritSect);
+ return hTcpClient;
+}
+
+/**
+ * Server mode connection thread.
+ *
+ * @returns iprt status code.
+ * @param hSelf Thread handle. Ignored.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) txsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
+{
+ RTSOCKET hTcpClient;
+ int rc = RTTcpServerListen2(g_pTcpServer, &hTcpClient);
+ Log(("txsTcpConnectServerThread: RTTcpServerListen2 -> %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ {
+ hTcpClient = txsTcpSetClient(hTcpClient);
+ RTTcpServerDisconnectClient2(hTcpClient);
+ }
+
+ RT_NOREF2(hSelf, pvUser);
+ return rc;
+}
+
+/**
+ * Checks if it's a fatal RTTcpClientConnect return code.
+ *
+ * @returns true / false.
+ * @param rc The IPRT status code.
+ */
+static bool txsTcpIsFatalClientConnectStatus(int rc)
+{
+ return rc != VERR_NET_UNREACHABLE
+ && rc != VERR_NET_HOST_DOWN
+ && rc != VERR_NET_HOST_UNREACHABLE
+ && rc != VERR_NET_CONNECTION_REFUSED
+ && rc != VERR_TIMEOUT
+ && rc != VERR_NET_CONNECTION_TIMED_OUT;
+}
+
+/**
+ * Client mode connection thread.
+ *
+ * @returns iprt status code.
+ * @param hSelf Thread handle. Use to sleep on. The main thread will
+ * signal it to speed up thread shutdown.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) txsTcpClientConnectThread(RTTHREAD hSelf, void *pvUser)
+{
+ RT_NOREF1(pvUser);
+
+ for (;;)
+ {
+ /* Stop? */
+ RTCritSectEnter(&g_TcpCritSect);
+ bool fStop = g_fTcpStopConnecting;
+ RTCritSectLeave(&g_TcpCritSect);
+ if (fStop)
+ return VINF_SUCCESS;
+
+ /* Try connect. */ /** @todo make cancelable! */
+ RTSOCKET hTcpClient;
+ Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort));
+ int rc = RTTcpClientConnectEx(g_szTcpConnectAddr, g_uTcpConnectPort, &hTcpClient,
+ RT_SOCKETCONNECT_DEFAULT_WAIT, &g_pTcpConnectCancelCookie);
+ Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ {
+ hTcpClient = txsTcpSetClient(hTcpClient);
+ RTTcpClientCloseEx(hTcpClient, true /* fGracefulShutdown*/);
+ break;
+ }
+
+ if (txsTcpIsFatalClientConnectStatus(rc))
+ return rc;
+
+ /* Delay a wee bit before retrying. */
+ RTThreadUserWait(hSelf, 1536);
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wait on the threads to complete.
+ *
+ * @returns Thread status (if collected), otherwise VINF_SUCCESS.
+ * @param cMillies The period to wait on each thread.
+ */
+static int txsTcpConnectWaitOnThreads(RTMSINTERVAL cMillies)
+{
+ int rcRet = VINF_SUCCESS;
+
+ if (g_hThreadTcpConnect != NIL_RTTHREAD)
+ {
+ int rcThread;
+ int rc2 = RTThreadWait(g_hThreadTcpConnect, cMillies, &rcThread);
+ if (RT_SUCCESS(rc2))
+ {
+ g_hThreadTcpConnect = NIL_RTTHREAD;
+ rcRet = rcThread;
+ }
+ }
+
+ if (g_hThreadTcpServer != NIL_RTTHREAD)
+ {
+ int rcThread;
+ int rc2 = RTThreadWait(g_hThreadTcpServer, cMillies, &rcThread);
+ if (RT_SUCCESS(rc2))
+ {
+ g_hThreadTcpServer = NIL_RTTHREAD;
+ if (RT_SUCCESS(rc2))
+ rcRet = rcThread;
+ }
+ }
+ return rcRet;
+}
+
+/**
+ * Connects to the peer.
+ *
+ * @returns VBox status code. Updates g_hTcpClient and g_fTcpClientFromServer on
+ * success
+ */
+static int txsTcpConnect(void)
+{
+ int rc;
+ if (g_enmTcpMode == TXSTCPMODE_SERVER)
+ {
+ g_fTcpClientFromServer = true;
+ rc = RTTcpServerListen2(g_pTcpServer, &g_hTcpClient);
+ Log(("txsTcpRecvPkt: RTTcpServerListen2 -> %Rrc\n", rc));
+ }
+ else if (g_enmTcpMode == TXSTCPMODE_CLIENT)
+ {
+ g_fTcpClientFromServer = false;
+ for (;;)
+ {
+ Log2(("Calling RTTcpClientConnect(%s, %u,)...\n", g_szTcpConnectAddr, g_uTcpConnectPort));
+ rc = RTTcpClientConnect(g_szTcpConnectAddr, g_uTcpConnectPort, &g_hTcpClient);
+ Log(("txsTcpRecvPkt: RTTcpClientConnect -> %Rrc\n", rc));
+ if (RT_SUCCESS(rc) || txsTcpIsFatalClientConnectStatus(rc))
+ break;
+
+ /* Delay a wee bit before retrying. */
+ RTThreadSleep(1536);
+ }
+ }
+ else
+ {
+ Assert(g_enmTcpMode == TXSTCPMODE_BOTH);
+ RTTHREAD hSelf = RTThreadSelf();
+
+ /*
+ * Create client threads.
+ */
+ RTCritSectEnter(&g_TcpCritSect);
+ RTThreadUserReset(hSelf);
+ g_hThreadMain = hSelf;
+ g_fTcpStopConnecting = false;
+ RTCritSectLeave(&g_TcpCritSect);
+
+ txsTcpConnectWaitOnThreads(32);
+
+ rc = VINF_SUCCESS;
+ if (g_hThreadTcpConnect == NIL_RTTHREAD)
+ {
+ g_pTcpConnectCancelCookie = NULL;
+ rc = RTThreadCreate(&g_hThreadTcpConnect, txsTcpClientConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE, "tcpconn");
+ }
+ if (g_hThreadTcpServer == NIL_RTTHREAD && RT_SUCCESS(rc))
+ rc = RTThreadCreate(&g_hThreadTcpServer, txsTcpServerConnectThread, NULL, 0, RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE, "tcpserv");
+
+ RTCritSectEnter(&g_TcpCritSect);
+
+ /*
+ * Wait for connection to be established.
+ */
+ while ( RT_SUCCESS(rc)
+ && g_hTcpClient == NIL_RTSOCKET)
+ {
+ RTCritSectLeave(&g_TcpCritSect);
+ RTThreadUserWait(hSelf, 1536);
+ rc = txsTcpConnectWaitOnThreads(0);
+ RTCritSectEnter(&g_TcpCritSect);
+ }
+
+ /*
+ * Cancel the threads.
+ */
+ g_hThreadMain = NIL_RTTHREAD;
+ g_fTcpStopConnecting = true;
+
+ RTCritSectLeave(&g_TcpCritSect);
+ RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
+ }
+
+ AssertMsg(RT_SUCCESS(rc) ? g_hTcpClient != NIL_RTSOCKET : g_hTcpClient == NIL_RTSOCKET, ("%Rrc %p\n", rc, g_hTcpClient));
+ g_cbTcpStashed = 0;
+ return rc;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyReboot}
+ */
+static DECLCALLBACK(void) txsTcpNotifyReboot(void)
+{
+ Log(("txsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
+ if (g_pTcpServer)
+ {
+ int rc = RTTcpServerDestroy(g_pTcpServer);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpServerDestroy failed in txsTcpNotifyReboot: %Rrc", rc);
+ g_pTcpServer = NULL;
+ }
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyBye}
+ */
+static DECLCALLBACK(void) txsTcpNotifyBye(void)
+{
+ Log(("txsTcpNotifyBye: txsTcpDisconnectClient %RTsock\n", g_hTcpClient));
+ txsTcpDisconnectClient();
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnNotifyHowdy}
+ */
+static DECLCALLBACK(void) txsTcpNotifyHowdy(void)
+{
+ /* nothing to do here */
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnBabble}
+ */
+static DECLCALLBACK(void) txsTcpBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
+{
+ /*
+ * Quietly ignore already disconnected client.
+ */
+ RTSOCKET hTcpClient = g_hTcpClient;
+ if (hTcpClient == NIL_RTSOCKET)
+ return;
+
+ /*
+ * Try send the babble reply.
+ */
+ NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
+ int rc;
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
+ do rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
+ while (rc == VERR_INTERRUPTED);
+
+ /*
+ * Disconnect the client.
+ */
+ Log(("txsTcpBabble: txsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", g_hTcpClient, rc));
+ txsTcpDisconnectClient();
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnSendPkt}
+ */
+static DECLCALLBACK(int) txsTcpSendPkt(PCTXSPKTHDR pPktHdr)
+{
+ Assert(pPktHdr->cb >= sizeof(TXSPKTHDR));
+
+ /*
+ * Fail if no client connection.
+ */
+ RTSOCKET hTcpClient = g_hTcpClient;
+ if (hTcpClient == NIL_RTSOCKET)
+ return VERR_NET_NOT_CONNECTED;
+
+ /*
+ * Write it.
+ */
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
+ int rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_INTERRUPTED)
+ {
+ /* assume fatal connection error. */
+ Log(("RTTcpWrite -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
+ txsTcpDisconnectClient();
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnRecvPkt}
+ */
+static DECLCALLBACK(int) txsTcpRecvPkt(PPTXSPKTHDR ppPktHdr)
+{
+ int rc = VINF_SUCCESS;
+ *ppPktHdr = NULL;
+
+ /*
+ * Do we have to wait for a client to connect?
+ */
+ RTSOCKET hTcpClient = g_hTcpClient;
+ if (hTcpClient == NIL_RTSOCKET)
+ {
+ rc = txsTcpConnect();
+ if (RT_FAILURE(rc))
+ return rc;
+ hTcpClient = g_hTcpClient; Assert(hTcpClient != NIL_RTSOCKET);
+ }
+
+ /*
+ * Read state.
+ */
+ size_t offData = 0;
+ size_t cbData = 0;
+ size_t cbDataAlloced;
+ uint8_t *pbData = NULL;
+
+ /*
+ * Any stashed data?
+ */
+ if (g_cbTcpStashedAlloced)
+ {
+ offData = g_cbTcpStashed;
+ cbDataAlloced = g_cbTcpStashedAlloced;
+ pbData = g_pbTcpStashed;
+
+ g_cbTcpStashed = 0;
+ g_cbTcpStashedAlloced = 0;
+ g_pbTcpStashed = NULL;
+ }
+ else
+ {
+ cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);
+ pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
+ if (!pbData)
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Read and valid the length.
+ */
+ while (offData < sizeof(uint32_t))
+ {
+ size_t cbRead;
+ rc = RTTcpRead(hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbRead == 0)
+ {
+ Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
+ rc = VERR_NET_NOT_CONNECTED;
+ break;
+ }
+ offData += cbRead;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ ASMCompilerBarrier(); /* paranoia^3 */
+ cbData = *(uint32_t volatile *)pbData;
+ if (cbData >= sizeof(TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)
+ {
+ /*
+ * Align the length and reallocate the return packet it necessary.
+ */
+ cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);
+ if (cbData > cbDataAlloced)
+ {
+ void *pvNew = RTMemRealloc(pbData, cbData);
+ if (pvNew)
+ {
+ pbData = (uint8_t *)pvNew;
+ cbDataAlloced = cbData;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the remainder of the data.
+ */
+ while (offData < cbData)
+ {
+ size_t cbRead;
+ rc = RTTcpRead(hTcpClient, pbData + offData, cbData - offData, &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbRead == 0)
+ {
+ Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
+ rc = VERR_NET_NOT_CONNECTED;
+ break;
+ }
+ offData += cbRead;
+ }
+ }
+ }
+ else
+ rc = VERR_NET_PROTOCOL_ERROR;
+ }
+ if (RT_SUCCESS(rc))
+ *ppPktHdr = (PTXSPKTHDR)pbData;
+ else
+ {
+ /*
+ * Deal with errors.
+ */
+ if (rc == VERR_INTERRUPTED)
+ {
+ /* stash it away for the next call. */
+ g_cbTcpStashed = cbData;
+ g_cbTcpStashedAlloced = cbDataAlloced;
+ g_pbTcpStashed = pbData;
+ }
+ else
+ {
+ RTMemFree(pbData);
+
+ /* assume fatal connection error. */
+ Log(("txsTcpRecvPkt: RTTcpRead -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient));
+ txsTcpDisconnectClient();
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd}
+ */
+static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart)
+{
+ return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnPollIn}
+ */
+static DECLCALLBACK(bool) txsTcpPollIn(void)
+{
+ RTSOCKET hTcpClient = g_hTcpClient;
+ if (hTcpClient == NIL_RTSOCKET)
+ return false;
+ int rc = RTTcpSelectOne(hTcpClient, 0/*cMillies*/);
+ return RT_SUCCESS(rc);
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnTerm}
+ */
+static DECLCALLBACK(void) txsTcpTerm(void)
+{
+ /* Signal thread */
+ if (RTCritSectIsInitialized(&g_TcpCritSect))
+ {
+ RTCritSectEnter(&g_TcpCritSect);
+ g_fTcpStopConnecting = true;
+ RTCritSectLeave(&g_TcpCritSect);
+ }
+
+ if (g_hThreadTcpConnect != NIL_RTTHREAD)
+ {
+ RTThreadUserSignal(g_hThreadTcpConnect);
+ RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
+ }
+
+ /* Shut down the server (will wake up thread). */
+ if (g_pTcpServer)
+ {
+ Log(("txsTcpTerm: Destroying server...\n"));
+ int rc = RTTcpServerDestroy(g_pTcpServer);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc);
+ g_pTcpServer = NULL;
+ }
+
+ /* Shut down client */
+ if (g_hTcpClient != NIL_RTSOCKET)
+ {
+ if (g_fTcpClientFromServer)
+ {
+ Log(("txsTcpTerm: Disconnecting client...\n"));
+ int rc = RTTcpServerDisconnectClient2(g_hTcpClient);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
+ }
+ else
+ {
+ int rc = RTTcpClientClose(g_hTcpClient);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
+ }
+ g_hTcpClient = NIL_RTSOCKET;
+ }
+
+ /* Clean up stashing. */
+ RTMemFree(g_pbTcpStashed);
+ g_pbTcpStashed = NULL;
+ g_cbTcpStashed = 0;
+ g_cbTcpStashedAlloced = 0;
+
+ /* Wait for the thread (they should've had some time to quit by now). */
+ txsTcpConnectWaitOnThreads(15000);
+
+ /* Finally, clean up the critical section. */
+ if (RTCritSectIsInitialized(&g_TcpCritSect))
+ RTCritSectDelete(&g_TcpCritSect);
+
+ Log(("txsTcpTerm: done\n"));
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnInit}
+ */
+static DECLCALLBACK(int) txsTcpInit(void)
+{
+ int rc = RTCritSectInit(&g_TcpCritSect);
+ if (RT_SUCCESS(rc) && g_enmTcpMode != TXSTCPMODE_CLIENT)
+ {
+ rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NET_DOWN)
+ {
+ RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
+ g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
+ uint64_t StartMs = RTTimeMilliTS();
+ do
+ {
+ RTThreadSleep(1000);
+ rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
+ } while ( rc == VERR_NET_DOWN
+ && RTTimeMilliTS() - StartMs < 20000);
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
+ }
+ if (RT_FAILURE(rc))
+ {
+ g_pTcpServer = NULL;
+ RTCritSectDelete(&g_TcpCritSect);
+ RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
+ g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/** Options */
+enum TXSTCPOPT
+{
+ TXSTCPOPT_MODE = 1000,
+ TXSTCPOPT_BIND_ADDRESS,
+ TXSTCPOPT_BIND_PORT,
+ TXSTCPOPT_CONNECT_ADDRESS,
+ TXSTCPOPT_CONNECT_PORT,
+
+ /* legacy: */
+ TXSTCPOPT_LEGACY_PORT,
+ TXSTCPOPT_LEGACY_CONNECT
+};
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnOption}
+ */
+static DECLCALLBACK(int) txsTcpOption(int ch, PCRTGETOPTUNION pVal)
+{
+ int rc;
+
+ switch (ch)
+ {
+ case TXSTCPOPT_MODE:
+ if (!strcmp(pVal->psz, "both"))
+ g_enmTcpMode = TXSTCPMODE_BOTH;
+ else if (!strcmp(pVal->psz, "client"))
+ g_enmTcpMode = TXSTCPMODE_CLIENT;
+ else if (!strcmp(pVal->psz, "server"))
+ g_enmTcpMode = TXSTCPMODE_SERVER;
+ else
+ return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid TCP mode: '%s'\n", pVal->psz);
+ return VINF_SUCCESS;
+
+ case TXSTCPOPT_BIND_ADDRESS:
+ rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
+ return VINF_SUCCESS;
+
+ case TXSTCPOPT_BIND_PORT:
+ g_uTcpBindPort = pVal->u16 == 0 ? TXS_TCP_DEF_BIND_PORT : pVal->u16;
+ return VINF_SUCCESS;
+
+ case TXSTCPOPT_LEGACY_CONNECT:
+ g_enmTcpMode = TXSTCPMODE_CLIENT;
+ RT_FALL_THRU();
+ case TXSTCPOPT_CONNECT_ADDRESS:
+ rc = RTStrCopy(g_szTcpConnectAddr, sizeof(g_szTcpConnectAddr), pVal->psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP connect address is too long (%Rrc)", rc);
+ if (!g_szTcpConnectAddr[0])
+ strcpy(g_szTcpConnectAddr, TXS_TCP_DEF_CONNECT_ADDRESS);
+ return VINF_SUCCESS;
+
+ case TXSTCPOPT_CONNECT_PORT:
+ g_uTcpConnectPort = pVal->u16 == 0 ? TXS_TCP_DEF_CONNECT_PORT : pVal->u16;
+ return VINF_SUCCESS;
+
+ case TXSTCPOPT_LEGACY_PORT:
+ if (pVal->u16 == 0)
+ {
+ g_uTcpBindPort = TXS_TCP_DEF_BIND_PORT;
+ g_uTcpConnectPort = TXS_TCP_DEF_CONNECT_PORT;
+ }
+ else
+ {
+ g_uTcpBindPort = pVal->u16;
+ g_uTcpConnectPort = pVal->u16;
+ }
+ return VINF_SUCCESS;
+ }
+ return VERR_TRY_AGAIN;
+}
+
+/**
+ * @interface_method_impl{TXSTRANSPORT,pfnUsage}
+ */
+DECLCALLBACK(void) txsTcpUsage(PRTSTREAM pStream)
+{
+ RTStrmPrintf(pStream,
+ " --tcp-mode <both|client|server>\n"
+ " Selects the mode of operation.\n"
+ " Default: both\n"
+ " --tcp-bind-address <address>\n"
+ " The address(es) to listen to TCP connection on. Empty string\n"
+ " means any address, this is the default.\n"
+ " --tcp-bind-port <port>\n"
+ " The port to listen to TCP connections on.\n"
+ " Default: %u\n"
+ " --tcp-connect-address <address>\n"
+ " The address of the server to try connect to in client mode.\n"
+ " Default: " TXS_TCP_DEF_CONNECT_ADDRESS "\n"
+ " --tcp-connect-port <port>\n"
+ " The port on the server to connect to in client mode.\n"
+ " Default: %u\n"
+ , TXS_TCP_DEF_BIND_PORT, TXS_TCP_DEF_CONNECT_PORT);
+}
+
+/** Command line options for the TCP/IP transport layer. */
+static const RTGETOPTDEF g_TcpOpts[] =
+{
+ { "--tcp-mode", TXSTCPOPT_MODE, RTGETOPT_REQ_STRING },
+ { "--tcp-bind-address", TXSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--tcp-bind-port", TXSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 },
+ { "--tcp-connect-address", TXSTCPOPT_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--tcp-connect-port", TXSTCPOPT_CONNECT_PORT, RTGETOPT_REQ_UINT16 },
+
+ /* legacy */
+ { "--tcp-port", TXSTCPOPT_LEGACY_PORT, RTGETOPT_REQ_UINT16 },
+ { "--tcp-connect", TXSTCPOPT_LEGACY_CONNECT, RTGETOPT_REQ_STRING },
+};
+
+/** TCP/IP transport layer. */
+const TXSTRANSPORT g_TcpTransport =
+{
+ /* .szName = */ "tcp",
+ /* .pszDesc = */ "TCP/IP",
+ /* .cOpts = */ &g_TcpOpts[0],
+ /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
+ /* .pfnUsage = */ txsTcpUsage,
+ /* .pfnOption = */ txsTcpOption,
+ /* .pfnInit = */ txsTcpInit,
+ /* .pfnTerm = */ txsTcpTerm,
+ /* .pfnPollIn = */ txsTcpPollIn,
+ /* .pfnPollSetAdd = */ txsTcpPollSetAdd,
+ /* .pfnRecvPkt = */ txsTcpRecvPkt,
+ /* .pfnSendPkt = */ txsTcpSendPkt,
+ /* .pfnBabble = */ txsTcpBabble,
+ /* .pfnNotifyHowdy = */ txsTcpNotifyHowdy,
+ /* .pfnNotifyBye = */ txsTcpNotifyBye,
+ /* .pfnNotifyReboot = */ txsTcpNotifyReboot,
+ /* .u32EndMarker = */ UINT32_C(0x12345678)
+};
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-nat.sh b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-nat.sh
new file mode 100755
index 00000000..c666e480
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-nat.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+## @file
+# VirtualBox Test Execution Service Init Script for NATted setups.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# chkconfig: 35 35 65
+# description: VirtualBox Test Execution Service
+#
+### BEGIN INIT INFO
+# Provides: vboxtxs
+# Required-Start: $network
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Description: VirtualBox Test Execution Service
+### END INIT INFO
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+SCRIPTNAME=vboxtxs-nat.sh
+
+CDROM_PATH=/media/cdrom
+SCRATCH_PATH=/tmp/vboxtxs-scratch
+
+PIDFILE="/var/run/vboxtxs"
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+begin_msg()
+{
+ test -n "${2}" && echo "${SCRIPTNAME}: ${1}."
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+succ_msg()
+{
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+fail_msg()
+{
+ echo "${SCRIPTNAME}: failed: ${1}." >&2
+ logger -t "${SCRIPTNAME}" "failed: ${1}."
+}
+
+killproc() {
+ kp_binary="${1##*/}"
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill -9 "${kp_binary}"
+ return 0
+}
+
+case "`uname -m`" in
+ AMD64|amd64|X86_64|x86_64)
+ binary=/opt/validationkit/linux/amd64/TestExecService
+ ;;
+
+ i386|x86|i486|i586|i686|i787)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+
+ *)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+esac
+
+fixAndTestBinary() {
+ chmod a+x "$binary" 2> /dev/null > /dev/null
+ test -x "$binary" || {
+ echo "Cannot run $binary"
+ exit 1
+ }
+}
+
+start() {
+ if ! test -f $PIDFILE; then
+ begin_msg "Starting VirtualBox Test Execution Service" console
+ fixAndTestBinary
+ mount /dev/cdrom "${CDROM_PATH}" 2> /dev/null > /dev/null
+ $binary --auto-upgrade --scratch="${SCRATCH_PATH}" --cdrom="${CDROM_PATH}" \
+ --no-display-output --tcp-connect 10.0.2.2 > /dev/null
+ RETVAL=$?
+ test $RETVAL -eq 0 && sleep 2 && echo `pidof TestExecService` > $PIDFILE
+ if ! test -s "${PIDFILE}"; then
+ RETVAL=5
+ fi
+ if test $RETVAL -eq 0; then
+ succ_msg "VirtualBox Test Execution service started"
+ else
+ fail_msg "VirtualBox Test Execution service failed to start"
+ fi
+ fi
+ return $RETVAL
+}
+
+stop() {
+ if test -f $PIDFILE; then
+ begin_msg "Stopping VirtualBox Test Execution Service" console
+ killproc $binary
+ fi
+}
+
+restart() {
+ stop && start
+}
+
+status() {
+ echo -n "Checking for vboxtxs"
+ if [ -f $PIDFILE ]; then
+ echo " ...running"
+ else
+ echo " ...not running"
+ fi
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+status)
+ status
+ ;;
+setup)
+ ;;
+cleanup)
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.desktop b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.desktop
new file mode 100644
index 00000000..95e7ddcb
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+Encoding=UTF-8
+Name=vboxtxs
+Exec=sudo sh -c "/opt/validationkit/linux/vboxtxs-runvm start"
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.sh b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.sh
new file mode 100755
index 00000000..e44b1546
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs-runvm.sh
@@ -0,0 +1,208 @@
+#!/bin/sh
+## @file
+# VirtualBox Test Execution Service Init Script.
+#
+
+#
+# Copyright (C) 2018-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# chkconfig: 35 35 65
+# description: VirtualBox Test Execution Service
+#
+### BEGIN INIT INFO
+# Provides: vboxtxs-runvm
+# Required-Start: $ALL
+# Required-Stop:
+# Default-Start: 5
+# Default-Stop: 0 1 6
+# Description: VirtualBox Test Execution Service
+### END INIT INFO
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+SCRIPTNAME=vboxtxs-runvm.sh
+
+CDROM_PATH=/media/cdrom
+SCRATCH_PATH=/tmp/vboxtxs-scratch
+SMOKEOUTPUT_PATH=/tmp/vboxtxs-smoketestoutput
+DEVKMSG_PATH=/dev/kmsg
+
+PIDFILE="/var/run/vboxtxs"
+
+export TESTBOX_PATH_RESOURCES="/home/vbox/testrsrc"
+SMOKETEST_SCRIPT="/opt/validationkit/tests/smoketests/tdSmokeTest1.py"
+PYTHON_BINARY="python"
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+kernlog_msg() {
+ test -n "$2" && echo "${SCRIPTNAME}: ${1}"
+ echo "${SCRIPTNAME}: ${1}" > $DEVKMSG_PATH
+}
+
+dumpfile_to_kernlog() {
+ if test -f "$1"; then
+ kernlog_msg "---------------------- DUMP BEGIN ----------------------"
+ cat "$1" | while read LINE
+ do
+ kernlog_msg "${LINE}"
+ done
+ kernlog_msg "---------------------- DUMP END ------------------------"
+ rm -f "$1"
+ else
+ kernlog_msg "${1}: file not found" console
+ fi
+}
+
+killproc()
+{
+ kp_binary="${1##*/}"
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill -9 "${kp_binary}"
+ return 0
+}
+
+case "`uname -m`" in
+ AMD64|amd64|X86_64|x86_64)
+ binary=/opt/validationkit/linux/amd64/TestExecService
+ ;;
+
+ i386|x86|i486|i586|i686|i787)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+
+ *)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+esac
+
+fixAndTestBinary() {
+ chmod a+x "$binary" 2> /dev/null > /dev/null
+ test -x "$binary" || {
+ echo "Cannot run $binary"
+ exit 1
+ }
+}
+
+testRsrcPath() {
+ test -d "$TESTBOX_PATH_RESOURCES" || {
+ echo "TESTBOX_PATH_RESOURCES directory not found"
+ exit 1
+ }
+}
+
+start() {
+ if ! test -f $PIDFILE; then
+ kernlog_msg "Starting Nested Smoke Test" console
+ fixAndTestBinary
+ testRsrcPath
+ $PYTHON_BINARY $SMOKETEST_SCRIPT -v -v -d --vbox-session-type gui --nic-attachment nat --quick all 1> "${SMOKEOUTPUT_PATH}" 2>&1
+ RETVAL=$?
+ dumpfile_to_kernlog "${SMOKEOUTPUT_PATH}"
+ sync
+ sleep 15
+ if test $RETVAL -eq 0; then
+ kernlog_msg "Nested Smoke Test done; Starting Test Execution service" console
+ mkdir -p "${CDROM_PATH}"
+ mount -o ro /dev/cdrom "${CDROM_PATH}" 2> /dev/null > /dev/null
+ $binary --auto-upgrade --scratch="${SCRATCH_PATH}" --cdrom="${CDROM_PATH}" --no-display-output > /dev/null
+ RETVAL=$?
+ test $RETVAL -eq 0 && sleep 3 && echo `pidof TestExecService` > $PIDFILE
+ if ! test -s "${PIDFILE}"; then
+ RETVAL=5
+ fi
+ if test $RETVAL -eq 0; then
+ kernlog_msg "Test Execution service started" console
+ else
+ kernlog_msg "Test Execution service failed to start" console
+ RETVAL=6
+ fi
+ else
+ kernlog_msg "Smoke Test failed! error code ${RETVAL}" console
+ RETVAL=7
+ fi
+ else
+ kernlog_msg "Starting Nested Smoke Test failed! Pidfile ${PIDFILE} exists" console
+ RETVAL=9
+ fi
+ return $RETVAL
+}
+
+stop() {
+ if test -f $PIDFILE; then
+ kernlog_msg "Stopping Test Execution Service"
+ killproc $binary
+ rm -f $PIDFILE
+ fi
+}
+
+restart() {
+ stop && start
+}
+
+status() {
+ echo -n "Checking for vboxtxs"
+ if [ -f $PIDFILE ]; then
+ echo " ...running"
+ else
+ echo " ...not running"
+ fi
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+status)
+ status
+ ;;
+setup)
+ ;;
+cleanup)
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.service b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.service
new file mode 100644
index 00000000..b61426e9
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=VirtualBox Test Execution Service
+SourcePath=/opt/validationkit/linux/vboxtxs
+
+[Service]
+Type=forking
+Restart=no
+TimeoutSec=5min
+IgnoreSIGPIPE=no
+KillMode=process
+GuessMainPID=no
+RemainAfterExit=yes
+ExecStart=/opt/validationkit/linux/vboxtxs start
+ExecStop=/opt/validationkit/linux/vboxtxs stop
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.sh b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.sh
new file mode 100755
index 00000000..887ac2d2
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/linux/vboxtxs.sh
@@ -0,0 +1,173 @@
+#!/bin/sh
+## @file
+# VirtualBox Test Execution Service Init Script.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# chkconfig: 35 35 65
+# description: VirtualBox Test Execution Service
+#
+### BEGIN INIT INFO
+# Provides: vboxtxs
+# Required-Start: $network
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Description: VirtualBox Test Execution Service
+### END INIT INFO
+
+PATH=$PATH:/bin:/sbin:/usr/sbin
+SCRIPTNAME=vboxtxs.sh
+
+CDROM_PATH=/media/cdrom
+SCRATCH_PATH=/tmp/vboxtxs-scratch
+
+PIDFILE="/var/run/vboxtxs"
+
+# Preamble for Gentoo
+if [ "`which $0`" = "/sbin/rc" ]; then
+ shift
+fi
+
+begin_msg()
+{
+ test -n "${2}" && echo "${SCRIPTNAME}: ${1}."
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+succ_msg()
+{
+ logger -t "${SCRIPTNAME}" "${1}."
+}
+
+fail_msg()
+{
+ echo "${SCRIPTNAME}: failed: ${1}." >&2
+ logger -t "${SCRIPTNAME}" "failed: ${1}."
+}
+
+killproc() {
+ kp_binary="${1##*/}"
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill "${kp_binary}" || return 0
+ sleep 1
+ pkill -9 "${kp_binary}"
+ return 0
+}
+
+case "`uname -m`" in
+ AMD64|amd64|X86_64|x86_64)
+ binary=/opt/validationkit/linux/amd64/TestExecService
+ ;;
+
+ i386|x86|i486|i586|i686|i787)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+
+ *)
+ binary=/opt/validationkit/linux/x86/TestExecService
+ ;;
+esac
+
+fixAndTestBinary() {
+ chmod a+x "$binary" 2> /dev/null > /dev/null
+ test -x "$binary" || {
+ echo "Cannot run $binary"
+ exit 1
+ }
+}
+
+start() {
+ if ! test -f $PIDFILE; then
+ begin_msg "Starting VirtualBox Test Execution Service" console
+ fixAndTestBinary
+ mount /dev/cdrom "${CDROM_PATH}" 2> /dev/null > /dev/null
+ $binary --auto-upgrade --scratch="${SCRATCH_PATH}" --cdrom="${CDROM_PATH}" --no-display-output > /dev/null
+ RETVAL=$?
+ test $RETVAL -eq 0 && sleep 2 && echo `pidof TestExecService` > $PIDFILE
+ if ! test -s "${PIDFILE}"; then
+ RETVAL=5
+ fi
+ if test $RETVAL -eq 0; then
+ succ_msg "VirtualBox Test Execution service started"
+ else
+ fail_msg "VirtualBox Test Execution service failed to start"
+ fi
+ fi
+ return $RETVAL
+}
+
+stop() {
+ if test -f $PIDFILE; then
+ begin_msg "Stopping VirtualBox Test Execution Service" console
+ killproc $binary
+ fi
+}
+
+restart() {
+ stop && start
+}
+
+status() {
+ echo -n "Checking for vboxtxs"
+ if [ -f $PIDFILE ]; then
+ echo " ...running"
+ else
+ echo " ...not running"
+ fi
+}
+
+case "$1" in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+restart)
+ restart
+ ;;
+status)
+ status
+ ;;
+setup)
+ ;;
+cleanup)
+ ;;
+*)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs-sol10.xml b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs-sol10.xml
new file mode 100644
index 00000000..a93901f6
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs-sol10.xml
@@ -0,0 +1,84 @@
+<?xml version='1.0'?>
+<!--
+ Solaris SMF service manifest for the VirtualBox Test eXecution Service.
+ $Id: vboxtxs-sol10.xml $
+-->
+<!--
+ Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox distribution, in which case the provisions of the
+ CDDL are applicable instead of those of the GPL.
+
+ You may elect to license modified versions of this file under the
+ terms and conditions of either the GPL or the CDDL or both.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='export'>
+<service name='system/virtualbox/vboxtxs' type='service' version='1'>
+
+ <create_default_instance enabled='false' />
+ <single_instance/>
+
+ <!-- Wait for the network to start up -->
+ <dependency name='milestone-network' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/network:default' />
+ </dependency>
+
+ <!-- Wait for devices to be initialized as we depend on vboxguest (PCI) -->
+ <dependency name='milestone-devices' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+
+ <!-- We wish to be started as late as possible... so go crazy with deps. -->
+ <dependency name='multi-user' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user:default' />
+ </dependency>
+ <dependency name='multi-user-server' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user-server:default' />
+ </dependency>
+ <dependency name='filesystem-local' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+ <dependency name='filesystem-autofs' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/autofs:default' />
+ </dependency>
+ <dependency name='filesystem-volfs' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/volfs:default' />
+ </dependency>
+
+ <!-- start + stop methods -->
+ <exec_method type='method' name='start' exec='/opt/VBoxTest/testsuite/solaris/vboxtxs.sh' timeout_seconds='30' />
+ <exec_method type='method' name='stop' exec=':kill' timeout_seconds='60' />
+
+ <!-- Description -->
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox Test eXecution Service</loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.sh b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.sh
new file mode 100755
index 00000000..f9f5b9ad
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+## @file
+# VirtualBox Test Execution Service Architecture Wrapper for Solaris.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# 1. Change directory to the script directory (usually /opt/VBoxTest/).
+set -x
+MY_DIR=`dirname "$0"`
+cd "${MY_DIR}"
+
+# 2. Determine the architecture.
+MY_ARCH=`isainfo -k`
+case "${MY_ARCH}" in
+ amd64)
+ MY_ARCH=amd64
+ ;;
+ i386)
+ MY_ARCH=x86
+ ;;
+ *)
+ echo "vboxtxs.sh: Unsupported architecture '${MY_ARCH}' returned by isainfo -k." >&2
+ exit 2;
+ ;;
+esac
+
+# 3. Exec the service.
+exec "./${MY_ARCH}/TestExecService" \
+ --cdrom="/cdrom/cdrom0/" \
+ --scratch="/var/tmp/VBoxTest/" \
+ --no-display-output \
+ $*
+exit 3;
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.xml b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.xml
new file mode 100644
index 00000000..6bbc614e
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/solaris/vboxtxs.xml
@@ -0,0 +1,84 @@
+<?xml version='1.0'?>
+<!--
+ Solaris SMF service manifest for the VirtualBox Test eXecution Service.
+ $Id: vboxtxs.xml $
+-->
+<!--
+ Copyright (C) 2010-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox distribution, in which case the provisions of the
+ CDDL are applicable instead of those of the GPL.
+
+ You may elect to license modified versions of this file under the
+ terms and conditions of either the GPL or the CDDL or both.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='export'>
+<service name='system/virtualbox/vboxtxs' type='service' version='1'>
+
+ <create_default_instance enabled='false' />
+ <single_instance/>
+
+ <!-- Wait for the network to start up -->
+ <dependency name='milestone-network' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/network:default' />
+ </dependency>
+
+ <!-- Wait for devices to be initialized as we depend on vboxguest (PCI) -->
+ <dependency name='milestone-devices' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+
+ <!-- We wish to be started as late as possible... so go crazy with deps. -->
+ <dependency name='multi-user' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user:default' />
+ </dependency>
+ <dependency name='multi-user-server' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/milestone/multi-user-server:default' />
+ </dependency>
+ <dependency name='filesystem-local' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+ <dependency name='filesystem-autofs' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/autofs:default' />
+ </dependency>
+ <dependency name='filesystem-rmvolmgr' grouping='require_all' restart_on='none' type='service'>
+ <service_fmri value='svc:/system/filesystem/rmvolmgr:default' />
+ </dependency>
+
+ <!-- start + stop methods -->
+ <exec_method type='method' name='start' exec='/opt/VBoxTest/testsuite/solaris/vboxtxs.sh' timeout_seconds='30' />
+ <exec_method type='method' name='stop' exec=':kill' timeout_seconds='60' />
+
+ <!-- Description -->
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox Test eXecution Service</loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-readme.txt b/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-readme.txt
new file mode 100644
index 00000000..70de141f
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-readme.txt
@@ -0,0 +1,145 @@
+$Id: vboxtxs-readme.txt $
+
+
+VirtualBox Test eXecution Service
+=================================
+
+This readme briefly describes how to install the Test eXecution Service (TXS)
+on the various systems.
+
+There are currently two transport options for the TXS:
+
+ - The default is to use it in TCP server mode, i.e. the test script needs
+ to know the guest's IP and therefore requires guest additions to be
+ installed as well. (Please use the latest stable additions compatible with
+ the VBox host versions you intend to test.)
+
+ - The alternative is for NATted setups where TXS will act like a TCP client
+ and try connect to the test script on the host. Since this require that
+ TXS knows which IP to connect to, it's only really possible in a NATted
+ setup where we know the host IP is 10.0.2.2.
+
+Since r85596 TXS operates in both modes by default so the nat version of
+the init scripts is not required anymore. Instead the other type can be installed
+for both cases.
+
+Linux Installation
+------------------
+
+1. mkdir -p /opt/validationkit
+2. scp/download VBoxValidationKit*.zip there.
+3. unzip VBoxValidationKit*.zip
+4. chmod -R u+w,a+x /opt/validationkit/ && chown -R root.root /opt/
+5. cd /etc/init.d/
+
+6 a) For init.rc distros:
+ Link up the right init script (see connection type above):
+ nat) ln -s ../../opt/validationkit/linux/vboxtxs-nat ./vboxtxs
+ other) ln -s ../../opt/validationkit/linux/vboxtxs ./vboxtxs
+6 b) Add vboxtxs to runlevels 2, 3, 5 and any other that makes sense
+ on the distro. There is usually some command for doing this, e.g.
+ ```update-rc.d vboxtxs defaults && update-rc.d vboxtxs enable``` (Debian-based)
+ or
+ ```chkconfig --add vboxtxs``` (OL/RHEL)
+
+ ... or ...
+
+7 a) For systemd distros: Link/copy up the vboxtxs.system to [/usr]/lib/systemd/, e.g.
+ cp /opt/validationkit/linux/vboxtxs.service /etc/systemd/system
+ b) Enable the vboxtxs service via:
+ systemctl enable vboxtxs
+
+ For all distros again:
+
+8a. Check the CD-ROM location (--cdrom <path>) in vboxtxs and fix it so it's correct, make sure
+ to update in svn as well.
+8b. Optional: If no suitable CD-ROM location is available on the guest yet, do a:
+ mkdir -p /media/cdrom; vi /etc/fstab
+ and enter this in /etc/fstab:
+ /dev/sr0<tab>/media/cdrom<tab>udf,iso9660<tab>user,noauto,exec,utf8<tab>0<tab>0
+8c. Optional: If SELinux denies execution of TXS, make sure to allow this, based on
+ how the distribution handles SELinux exceptions. Often there even is a GUI for that
+ (e.g. Oracle Linux 8+).
+9. Make sure that the package sources are still valid and up to date (apt / yum / ++)
+10. reboot / done.
+11. Do test.
+
+
+OS/2 Installation
+--------------------
+
+1. Start an "OS/2 Window" ("OS/2 System" -> "Command Prompts")
+2. md C:\Apps
+3. cd C:\Apps
+4. Mount the validationkit iso.
+5. copy D:\os2\x86\* C:\Apps
+5. copy D:\os2\x86\libc*.dll C:\OS2\DLL\
+6. Open C:\startup.cmd in an editor (tedit.exe for instance or e.exe).
+7. Add the line "start /C C:\Apps\TestExecService.exe --foreground" at the top of the file.
+8. reboot / done
+9. Do test.
+
+
+Solaris Installation
+--------------------
+
+1. Start the guest and open a root console.
+2. mkdir -p /opt/VBoxTest
+3. cd /opt/VBoxTest
+4. scp/download VBoxValidationKit*.zip there.
+5. unzip VBoxValidationKit*.zip
+6. chmod -R u+w,a+x /opt/VBoxTest/
+7. Import the right service setup depending on the Solaris version:
+ <= 10u9) /usr/sbin/svccfg import /opt/VBoxTest/validationkit/solaris/vboxtxs-sol10.xml
+ >= 11.0) /usr/sbin/svccfg import /opt/VBoxTest/validationkit/solaris/vboxtxs.xml
+8. /usr/sbin/svcadm enable svc:/system/virtualbox/vboxtxs
+9. reboot / done.
+
+To remove the service before repeating steps 7 & 8:
+1. /usr/sbin/svcadm disable -s svc:/system/virtualbox/vboxtxs:default
+2. /usr/sbin/svccfg delete svc:/system/virtualbox/vboxtxs:default
+
+Note. To configure dhcp for more a new interface the files
+/etc/hostname.<if#X> and /etc/dhcp.<ifnm#> have to exist. If you want the VM
+to work with any network card you throw at it, create /etc/*.pcn[01] and
+/etc/*.e1000g[012] as Solaris will remember it has seen the other variants
+before and use a different instance number (or something to that effect).
+
+
+Windows Installation
+--------------------
+
+1. Log on as Administrator.
+2. Make sure you have set a secure password, which you'll need in step 9.
+3. Start CMD.EXE or equivalent.
+4. md C:\Apps
+5. cd C:\Apps
+6. Mount the validationkit iso.
+7. copy D:\win\* C:\Apps
+8. copy D:\win\<x86 or amd64>\* C:\Apps
+9. Put the password from step 2 into the right service setup (see connection
+ type above) and import it into the registry:
+ nat) start C:\Apps\vboxtxs-nat.reg
+ other) start C:\Apps\vboxtxs.reg
+10. Make sure that the CD-ROM location is assigned to D: (via "Disk Management").
+11. reboot / done
+12. Do test.
+
+NT 3.1 and 3.x tricks:
+- Make sure the file system is NTFS. Observed issues converting 2GB partitions,
+ more success with smaller.
+- For NT3.1 PCNET drivers can be found on the net. No DHCP, so NAT only with
+ IP 10.0.2.15, 10.0.2.2 as gateway, and 10.0.2.3 as DNS with --natdnsproxy1 on.
+- On NT3.1 you need to add SystemDrive=C: to the environment.
+- Need to perform registry edits manually.
+- Use startup folder instead of non-exising Windows/Run key.
+
+
+Testing the setup
+-----------------
+
+1. Make sure the validationkit.iso is inserted.
+2. Boot / reboot the guest.
+3. Depending on the TXS transport options:
+ nat) python testdriver/tst-txsclient.py --reversed-setup
+ other) python testdriver/tst-txsclient.py --hostname <guest-ip>
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-runvm-readme.txt b/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-runvm-readme.txt
new file mode 100644
index 00000000..f8745391
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/vboxtxs-runvm-readme.txt
@@ -0,0 +1,54 @@
+$Id: vboxtxs-runvm-readme.txt $
+
+
+VirtualBox Test eXecution Service
+=================================
+
+This readme briefly describes how to install the Test eXecution Service (TXS)
+for nested hardware-virtualization smoke testing on the various systems.
+
+The basic idea is to execute one smoke test within the VM and then launch
+the regular TXS service in the VM to report success or failure to the host.
+
+Linux Installation
+------------------
+
+1. scp/download latest release build of VirtualBox and install it in the VM.
+2. scp/download the required smoke test VDI from remote test-resource to
+ /home/vbox/testrsrc/3.0/tcp/win2k3ent-acpi.vdi
+3. cd /root
+3. scp/download VBoxValidationKit*.zip there.
+5. unzip VBoxValidationKit*.zip
+6. chmod -R u+w,a+x /opt/validationkit/
+7a. Gnome: Copy /opt/validationkit/linux/vboxtxs-runvm.desktop to /etc/xdg/autostart
+7b. KDE/Others: TODO: Document other desktop managers
+8. Add the vbox user to sudo group using:
+ sudo usermod -a -G sudo vbox
+9. Ensure no password is required for vbox when using sudo:
+ sudo echo "vbox ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/username
+10. Check the cdrom location and /dev/kmsg equivalent of your linux distro
+ in /opt/validationkit/linux/vboxtxs-runvm and fix it so it's correct
+11. Reboot / done.
+
+TODO: Document other OSes as we add them.
+
+Note: vboxtxs-runvm uses a GUI session to launch the nested-VM for better
+visibility when troubleshooting the nested smoke test.
+
+If this causes problems try troubleshooting the XAUTHORITY and DISPLAY
+environment variables in vboxtxs-runvm.service. It might differ depending
+on the display manager of the particular linux distro.
+
+
+
+Testing the setup
+-----------------
+
+1. Make sure the validationkit.iso is inserted.
+2. Boot / reboot the guest.
+3. To test the connection - Depending on the TXS transport options:
+ nat) python testdriver/tst-txsclient.py --reversed-setup
+ other) python testdriver/tst-txsclient.py --hostname <guest-ip>
+4. To test the smoke test:
+ python tests/smoketests/tdSmokeTest1.py -v -v -d --vbox-session-type gui --test-vms <guest-name>
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.cmd b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.cmd
new file mode 100644
index 00000000..5d0ffa0c
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.cmd
@@ -0,0 +1,41 @@
+@REM REM @file
+@REM VirtualBox Test Execution Service Init Script for NATted VMs.
+@REM
+
+@REM
+REM
+REM Copyright (C) 2006-2023 Oracle and/or its affiliates.
+REM
+REM This file is part of VirtualBox base platform packages, as
+REM available from https://www.virtualbox.org.
+REM
+REM This program is free software; you can redistribute it and/or
+REM modify it under the terms of the GNU General Public License
+REM as published by the Free Software Foundation, in version 3 of the
+REM License.
+REM
+REM This program is distributed in the hope that it will be useful, but
+REM WITHOUT ANY WARRANTY; without even the implied warranty of
+REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+REM General Public License for more details.
+REM
+REM You should have received a copy of the GNU General Public License
+REM along with this program; if not, see <https://www.gnu.org/licenses>.
+REM
+REM The contents of this file may alternatively be used under the terms
+REM of the Common Development and Distribution License Version 1.0
+REM (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+REM in the VirtualBox distribution, in which case the provisions of the
+REM CDDL are applicable instead of those of the GPL.
+REM
+REM You may elect to license modified versions of this file under the
+REM terms and conditions of either the GPL or the CDDL or both.
+REM
+REM SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+REM
+
+%SystemDrive%\Apps\TestExecService.exe --foreground --display-output ^
+--cdrom D:\ --scratch C:\Temp\vboxtest --auto-upgrade ^
+--tcp-connect 10.0.2.2
+pause
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.reg b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.reg
new file mode 100644
index 00000000..20ab995b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs-nat.reg
@@ -0,0 +1,13 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
+"PowerdownAfterShutdown"="1"
+"AutoAdminLogon"="1"
+"ForceAutoLogon"="1"
+"DefaultUserName"="Administrator"
+; Placeholder password for test VM, see TestExecServ/vboxtxs-readme.txt
+"DefaultPassword"="Please_put_your_secure_password_here_see_TestExecServ/vboxtxs-readme.txt"
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
+"NTConfiguration"="c:\\Apps\\vboxtxs-nat.cmd"
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.cmd b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.cmd
new file mode 100644
index 00000000..6b41153e
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.cmd
@@ -0,0 +1,40 @@
+@REM REM @file
+@REM VirtualBox Test Execution Service Init Script.
+@REM
+
+@REM
+REM
+REM Copyright (C) 2006-2023 Oracle and/or its affiliates.
+REM
+REM This file is part of VirtualBox base platform packages, as
+REM available from https://www.virtualbox.org.
+REM
+REM This program is free software; you can redistribute it and/or
+REM modify it under the terms of the GNU General Public License
+REM as published by the Free Software Foundation, in version 3 of the
+REM License.
+REM
+REM This program is distributed in the hope that it will be useful, but
+REM WITHOUT ANY WARRANTY; without even the implied warranty of
+REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+REM General Public License for more details.
+REM
+REM You should have received a copy of the GNU General Public License
+REM along with this program; if not, see <https://www.gnu.org/licenses>.
+REM
+REM The contents of this file may alternatively be used under the terms
+REM of the Common Development and Distribution License Version 1.0
+REM (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+REM in the VirtualBox distribution, in which case the provisions of the
+REM CDDL are applicable instead of those of the GPL.
+REM
+REM You may elect to license modified versions of this file under the
+REM terms and conditions of either the GPL or the CDDL or both.
+REM
+REM SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+REM
+
+%SystemDrive%\Apps\TestExecService.exe --foreground --display-output ^
+--cdrom D:\ --scratch C:\Temp\vboxtest --auto-upgrade
+pause
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.reg b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.reg
new file mode 100644
index 00000000..037ac425
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.reg
@@ -0,0 +1,13 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
+"PowerdownAfterShutdown"="1"
+"AutoAdminLogon"="1"
+"ForceAutoLogon"="1"
+"DefaultUserName"="Administrator"
+; Sample password for test VM, see TestExecServ/vboxtxs-readme.txt
+"DefaultPassword"="Please_put_your_secure_password_here_see_TestExecServ/vboxtxs-readme.txt"
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
+"NTConfiguration"="c:\\Apps\\vboxtxs.cmd"
+
diff --git a/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.xml b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.xml
new file mode 100644
index 00000000..4dde1f83
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/TestExecServ/win/vboxtxs.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-16"?>
+<!--
+ Copyright (C) 2019-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox distribution, in which case the provisions of the
+ CDDL are applicable instead of those of the GPL.
+
+ You may elect to license modified versions of this file under the
+ terms and conditions of either the GPL or the CDDL or both.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
+ <RegistrationInfo>
+ <Date>2019-05-27T18:41:34.3048528</Date>
+ <Author>Administrator</Author>
+ </RegistrationInfo>
+ <Triggers>
+ <LogonTrigger>
+ <Enabled>true</Enabled>
+ <UserId>Administrator</UserId>
+ </LogonTrigger>
+ </Triggers>
+ <Principals>
+ <Principal id="Author">
+ <UserId>Administrator</UserId>
+ <LogonType>InteractiveToken</LogonType>
+ <RunLevel>HighestAvailable</RunLevel>
+ </Principal>
+ </Principals>
+ <Settings>
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
+ <AllowHardTerminate>true</AllowHardTerminate>
+ <StartWhenAvailable>false</StartWhenAvailable>
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
+ <IdleSettings>
+ <StopOnIdleEnd>false</StopOnIdleEnd>
+ <RestartOnIdle>false</RestartOnIdle>
+ </IdleSettings>
+ <AllowStartOnDemand>true</AllowStartOnDemand>
+ <Enabled>true</Enabled>
+ <Hidden>false</Hidden>
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
+ <WakeToRun>false</WakeToRun>
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
+ <Priority>7</Priority>
+ </Settings>
+ <Actions Context="Author">
+ <Exec>
+ <Command>C:\Apps\vboxtxs.cmd</Command>
+ </Exec>
+ </Actions>
+</Task>
+
diff --git a/src/VBox/ValidationKit/utils/audio/Makefile.kmk b/src/VBox/ValidationKit/utils/audio/Makefile.kmk
new file mode 100644
index 00000000..f249189f
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/Makefile.kmk
@@ -0,0 +1,220 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Audio Utilities.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Make sure the ValKit config file is included when the additions build
+# is including just this makefile.
+#
+ifndef VBOX_VALIDATIONKIT_CONFIG_KMK_INCLUDED
+ include $(PATH_ROOT)/src/VBox/ValidationKit/Config.kmk
+endif
+
+
+#
+# Globals.
+#
+VBOX_PATH_SRC_DEVICES = $(PATH_ROOT)/src/VBox/Devices
+VKAT_PATH_AUDIO = $(VBOX_PATH_SRC_DEVICES)/Audio
+
+
+#
+# Append what we build here to PROGRAMS (at the top because it's a bit messy).
+#
+ifn1of ($(KBUILD_TARGET), os2 freebsd netbsd openbsd)
+ if defined(VBOX_ONLY_VALIDATIONKIT) || !defined(VBOX_ONLY_BUILD)
+ PROGRAMS += vkat
+ if defined(VBOX_WITH_HOST_SHIPPING_AUDIO_TEST) && !defined(VBOX_ONLY_BUILD)
+ PROGRAMS += vkathost
+ endif
+ endif
+ if defined(VBOX_WITH_ADDITIONS_SHIPPING_AUDIO_TEST) \
+ && defined(VBOX_WITH_ADDITIONS) \
+ && !defined(VBOX_WITH_ADDITIONS_FROM_BUILD_SERVER) \
+ && (defined(VBOX_ONLY_ADDITIONS) || !defined(VBOX_ONLY_BUILD))
+ PROGRAMS += vkatadd
+ endif
+endif
+
+
+#
+# Utility to play sine wave to Default Audio Device.
+#
+if 0 # Disabled for now; does not work without WinMM.dll import validator files.
+ PROGRAMS.win += ntPlayToneWaveX
+ ntPlayToneWaveX_TEMPLATE = VBoxValidationKitR3
+ ntPlayToneWaveX_SOURCES = ntPlayToneWaveX.cpp
+ ntPlayToneWaveX_LIBS += \
+ WinMM.Lib
+endif
+
+
+#
+# The Validation Kit Audio Test (VKAT) utility.
+#
+vkat_TEMPLATE = VBoxValidationKitR3
+vkat_VBOX_IMPORT_CHECKER.win.x86 = nt4
+vkat_DEFS = VBOX_AUDIO_VKAT IN_VMM_R3 IN_VMM_STATIC
+vkat_INCS = \
+ $(PATH_ROOT)/src/VBox/Devices/build \
+ $(PATH_ROOT)/src/VBox/Devices \
+ $(PATH_ROOT)/src/VBox/Devices/Audio
+vkat_SOURCES = \
+ vkat.cpp \
+ vkatCommon.cpp \
+ vkatCmdGeneric.cpp \
+ vkatDriverStack.cpp \
+ $(VKAT_PATH_AUDIO)/AudioTest.cpp \
+ $(VKAT_PATH_AUDIO)/DrvAudio.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioNull.cpp \
+ $(VKAT_PATH_AUDIO)/AudioMixer.cpp \
+ $(VKAT_PATH_AUDIO)/AudioMixBuffer.cpp \
+ $(VKAT_PATH_AUDIO)/AudioHlp.cpp
+
+# Debug stuff.
+ifdef VBOX_WITH_AUDIO_DEBUG
+ vkat_DEFS += VBOX_WITH_AUDIO_DEBUG
+ vkat_SOURCES += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioDebug.cpp
+endif
+
+# Self-test stuff.
+vkat_DEFS += VBOX_WITH_AUDIO_VALIDATIONKIT
+vkat_SOURCES += \
+ vkatCmdSelfTest.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioValidationKit.cpp \
+ $(VKAT_PATH_AUDIO)/AudioTestService.cpp \
+ $(VKAT_PATH_AUDIO)/AudioTestServiceClient.cpp \
+ $(VKAT_PATH_AUDIO)/AudioTestServiceProtocol.cpp \
+ $(VKAT_PATH_AUDIO)/AudioTestServiceTcp.cpp
+
+ifdef VBOX_WITH_AUDIO_PULSE
+ vkat_DEFS += VBOX_WITH_AUDIO_PULSE
+ vkat_SOURCES += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioPulseAudioStubs.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioPulseAudio.cpp
+endif
+
+ifdef VBOX_WITH_AUDIO_ALSA
+ vkat_DEFS += VBOX_WITH_AUDIO_ALSA
+ vkat_SOURCES += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioAlsa.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioAlsaStubs.cpp
+endif
+
+ifdef VBOX_WITH_AUDIO_OSS
+ vkat_DEFS += VBOX_WITH_AUDIO_OSS
+ vkat_SOURCES += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioOss.cpp
+endif
+
+vkat_SOURCES.win += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioDSound.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioWasApi.cpp
+ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
+ vkat_DEFS.win += VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
+ vkat_SOURCES.win += \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioDSoundMMNotifClient.cpp
+endif
+
+vkat_SOURCES.darwin = \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioCoreAudio.cpp \
+ $(VKAT_PATH_AUDIO)/DrvHostAudioCoreAudioAuth.mm
+vkat_LDFLAGS.darwin = \
+ -framework CoreAudio \
+ -framework AudioUnit \
+ -framework AudioToolbox \
+ -framework Foundation
+ifn1of ($(VBOX_DEF_MACOSX_VERSION_MIN), 10.4 10.5 10.6)
+ vkat_LDFLAGS.darwin += \
+ -framework AVFoundation
+endif
+
+
+#
+# The additions variant of the audio test utility.
+#
+# We name it VBoxAudioTest though, to not clutter up Guest Additions
+# installations with cryptic binaries not sporting 'VBox' as prefix.
+#
+vkatadd_TEMPLATE = VBoxGuestR3Exe
+vkatadd_EXTENDS = vkat
+vkatadd_EXTENDS_BY = appending
+vkatadd_NAME = VBoxAudioTest
+vkatadd_SDKS = VBoxZlibStatic
+vkatadd_LDFLAGS.darwin = -framework IOKit
+vkatadd_LIBS.solaris = m
+
+
+#
+# Build the valkit vkat to bin as VBoxAudioTest, so that it can be shipped with
+# the host installer too.
+#
+# Note: We also need to have this as a signed binary, so don't just copy the
+# vkat binary to bin/ directory but built this as an own binary.
+#
+vkathost_TEMPLATE := VBoxR3Exe
+vkathost_EXTENDS := vkat
+vkathost_INST := $(INST_BIN)
+vkathost_NAME := VBoxAudioTest
+vkathost_SOURCES = \
+ $(vkat_SOURCES) \
+ $(VBOX_PATH_SRC_DEVICES)/build/VBoxDD.d
+vkathost_LIBS = \
+ $(LIB_RUNTIME)
+
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) \
+ && 0 ## @todo r=bird: Disabled because nobody really wants or needs to run this during build other than Andy.
+ ## And more importantly, it breaks the build (os2, bsd*).
+
+ PROGRAMS += tstVkatHostSelftest
+ tstVkatHostSelftest_EXTENDS = vkat
+ tstVkatHostSelftest_EXTENDS_BY = appending
+ tstVkatHostSelftest_INST = $(INST_TESTCASE)
+ tstVkatHostSelftest_DEFS.debug = VBOX_WITH_EF_WRAPS
+
+ TESTING += $(tstVkatHostSelftest_0_OUTDIR)/tstVkatHostSelftest.run
+ $$(tstVkatHostSelftest_0_OUTDIR)/tstVkatHostSelftest.run: $$(tstVkatHostSelftest_1_TARGET)
+ export VKAT_RELEASE_LOG=-all; $(tstVkatHostSelftest_1_TARGET) selftest
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/audio/ntPlayToneWaveX.cpp b/src/VBox/ValidationKit/utils/audio/ntPlayToneWaveX.cpp
new file mode 100644
index 00000000..e07165dc
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/ntPlayToneWaveX.cpp
@@ -0,0 +1,226 @@
+/* $Id: ntPlayToneWaveX.cpp $ */
+/** @file
+ * ????
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/errcore.h>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+uint32_t g_cSamplesPerSec = 44100;
+uint32_t g_cSamplesPerPeriod = 100; // 441.0Hz for 44.1kHz
+uint32_t g_cSamplesInBuffer = 4096;
+double g_rdSecDuration = 5.0;
+
+uint32_t g_cbSample; // assuming 16-bit stereo (for now)
+
+HWAVEOUT g_hWaveOut;
+HANDLE g_hWavEvent;
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--samples-per-sec", 's', RTGETOPT_REQ_UINT32 },
+ { "--period-in-samples", 'p', RTGETOPT_REQ_UINT32 },
+ { "--bufsize-in-samples", 'b', RTGETOPT_REQ_UINT32 },
+ { "--total-duration-in-secs", 'd', RTGETOPT_REQ_UINT32 }
+ };
+
+ RTGETOPTSTATE State;
+ RTGetOptInit(&State, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ RTGETOPTUNION ValueUnion;
+ int chOpt;
+ while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
+ {
+ switch (chOpt)
+ {
+ case 's': g_cSamplesPerSec = ValueUnion.u32; break;
+ case 'p': g_cSamplesPerPeriod = ValueUnion.u32; break;
+ case 'b': g_cSamplesInBuffer = ValueUnion.u32; break;
+ case 'd': g_rdSecDuration = ValueUnion.u32; break;
+ case 'h':
+ RTPrintf("usage: ntPlayToneWaveX.exe\n"
+ "[-s|--samples-per-sec]\n"
+ "[-p|--period-in-samples]\n"
+ "[-b|--bufsize-in-samples]\n"
+ "[-d|--total-duration-in-secs]\n"
+ "\n"
+ "Plays sine tone using ancient waveX API\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(chOpt, &ValueUnion);
+ }
+ }
+
+
+ WAVEFORMATEX waveFormatEx = { 0 };
+ MMRESULT mmresult;
+
+ waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
+ waveFormatEx.nChannels = 2;
+ waveFormatEx.nSamplesPerSec = g_cSamplesPerSec;
+ waveFormatEx.wBitsPerSample = 16;
+ waveFormatEx.nBlockAlign = g_cbSample = waveFormatEx.nChannels * waveFormatEx.wBitsPerSample / 8;
+ waveFormatEx.nAvgBytesPerSec = waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec;
+ waveFormatEx.cbSize = 0;
+
+ g_hWavEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ mmresult = waveOutOpen(&g_hWaveOut, WAVE_MAPPER, &waveFormatEx, (DWORD_PTR)g_hWavEvent, NULL, CALLBACK_EVENT);
+
+ if (mmresult != MMSYSERR_NOERROR)
+ {
+ RTMsgError("waveOutOpen failed with 0x%X\n", mmresult);
+ return -1;
+ }
+
+
+ uint32_t ui32SamplesToPlayTotal = (uint32_t)(g_rdSecDuration * g_cSamplesPerSec);
+ uint32_t ui32SamplesToPlay = ui32SamplesToPlayTotal;
+ uint32_t ui32SamplesPlayed = 0;
+ uint32_t ui32SamplesForWavBuf;
+
+ WAVEHDR waveHdr1 = {0}, waveHdr2 = {0}, *pWaveHdr, *pWaveHdrPlaying, *pWaveHdrWaiting;
+ uint32_t i, k;
+ DWORD res;
+
+ int16_t *i16Samples1 = (int16_t *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, g_cSamplesInBuffer * g_cbSample);
+ int16_t *i16Samples2 = (int16_t *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, g_cSamplesInBuffer * g_cbSample);
+
+ k = 0; // This is discrete time really!!!
+
+ for (i = 0; i < g_cSamplesInBuffer; i++, k++)
+ {
+ i16Samples1[2 * i] = (uint16_t)(10000.0 * sin(2.0 * M_PI * k / g_cSamplesPerPeriod));
+ i16Samples1[2 * i + 1] = i16Samples1[2 * i];
+ }
+
+ ui32SamplesForWavBuf = min(ui32SamplesToPlay, g_cSamplesInBuffer);
+
+ waveHdr1.lpData = (LPSTR)i16Samples1;
+ waveHdr1.dwBufferLength = ui32SamplesForWavBuf * g_cbSample;
+ waveHdr1.dwFlags = 0;
+ waveHdr1.dwLoops = 0;
+
+ ui32SamplesToPlay -= ui32SamplesForWavBuf;
+ ui32SamplesPlayed += ui32SamplesForWavBuf;
+
+ pWaveHdrPlaying = &waveHdr1;
+
+ mmresult = waveOutPrepareHeader(g_hWaveOut, pWaveHdrPlaying, sizeof(WAVEHDR));
+ mmresult = waveOutWrite(g_hWaveOut, pWaveHdrPlaying, sizeof(WAVEHDR));
+ //RTMsgInfo("waveOutWrite completes with %d\n", mmresult);
+
+ res = WaitForSingleObject(g_hWavEvent, INFINITE);
+ //RTMsgInfo("WaitForSingleObject completes with %d\n\n", res);
+
+ waveHdr2.lpData = (LPSTR)i16Samples2;
+ waveHdr2.dwBufferLength = 0;
+ waveHdr2.dwFlags = 0;
+ waveHdr2.dwLoops = 0;
+
+ pWaveHdrWaiting = &waveHdr2;
+
+ while (ui32SamplesToPlay > 0)
+ {
+ int16_t *i16Samples = (int16_t *)pWaveHdrWaiting->lpData;
+
+ for (i = 0; i < g_cSamplesInBuffer; i++, k++)
+ {
+ i16Samples[2 * i] = (uint16_t)(10000.0 * sin(2.0 * M_PI * k / g_cSamplesPerPeriod));
+ i16Samples[2 * i + 1] = i16Samples[2 * i];
+ }
+
+ ui32SamplesForWavBuf = min(ui32SamplesToPlay, g_cSamplesInBuffer);
+
+ pWaveHdrWaiting->dwBufferLength = ui32SamplesForWavBuf * g_cbSample;
+ pWaveHdrWaiting->dwFlags = 0;
+ pWaveHdrWaiting->dwLoops = 0;
+
+
+ ui32SamplesToPlay -= ui32SamplesForWavBuf;
+ ui32SamplesPlayed += ui32SamplesForWavBuf;
+
+ mmresult = waveOutPrepareHeader(g_hWaveOut, pWaveHdrWaiting, sizeof(WAVEHDR));
+ mmresult = waveOutWrite(g_hWaveOut, pWaveHdrWaiting, sizeof(WAVEHDR));
+ //RTMsgInfo("waveOutWrite completes with %d\n", mmresult);
+
+ res = WaitForSingleObject(g_hWavEvent, INFINITE);
+ //RTMsgInfo("WaitForSingleObject completes with %d\n\n", res);
+
+ mmresult = waveOutUnprepareHeader(g_hWaveOut, pWaveHdrPlaying, sizeof(WAVEHDR));
+ //RTMsgInfo("waveOutUnprepareHeader completes with %d\n", mmresult);
+
+ pWaveHdr = pWaveHdrWaiting;
+ pWaveHdrWaiting = pWaveHdrPlaying;
+ pWaveHdrPlaying = pWaveHdr;
+ }
+
+ while (mmresult = waveOutUnprepareHeader(g_hWaveOut, pWaveHdrPlaying, sizeof(WAVEHDR)))
+ {
+ //Expecting WAVERR_STILLPLAYING
+ //RTMsgInfo("waveOutUnprepareHeader failed with 0x%X\n", mmresult);
+ Sleep(100);
+ }
+
+ if (mmresult == MMSYSERR_NOERROR)
+ {
+ waveOutClose(g_hWaveOut);
+ }
+
+ HeapFree(GetProcessHeap(), 0, i16Samples1);
+ HeapFree(GetProcessHeap(), 0, i16Samples2);
+}
+
diff --git a/src/VBox/ValidationKit/utils/audio/readme.txt b/src/VBox/ValidationKit/utils/audio/readme.txt
new file mode 100644
index 00000000..ba894b5a
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/readme.txt
@@ -0,0 +1,2 @@
+
+See docs/VBoxAudioValidationKitReadMe.txt or docs/VBoxAudioValidationKitReadMe.html.
diff --git a/src/VBox/ValidationKit/utils/audio/vkat.cpp b/src/VBox/ValidationKit/utils/audio/vkat.cpp
new file mode 100644
index 00000000..0ff17e4c
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkat.cpp
@@ -0,0 +1,1649 @@
+/* $Id: vkat.cpp $ */
+/** @file
+ * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_AUDIO_TEST
+
+#include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+#include <package-generated.h>
+#include "product-generated.h"
+
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h> /* for CoInitializeEx and SetConsoleCtrlHandler */
+#else
+# include <signal.h>
+#endif
+
+#include "vkatInternal.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB, PAUDIOTESTVERIFYOPTS pOpts);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Backends description table.
+ *
+ * @note The first backend in the array is the default one for the platform.
+ */
+AUDIOTESTBACKENDDESC const g_aBackends[] =
+{
+#ifdef VBOX_WITH_AUDIO_PULSE
+ { &g_DrvHostPulseAudio, "pulseaudio" },
+ { &g_DrvHostPulseAudio, "pulse" },
+ { &g_DrvHostPulseAudio, "pa" },
+#endif
+/*
+ * Note: ALSA has to come second so that PulseAudio above always is the default on Linux-y OSes
+ * -- most distros are using an ALSA plugin for PulseAudio nowadays.
+ * However, some of these configurations do not seem to work by default (can't create audio streams).
+ *
+ * If PulseAudio is not available, the (optional) probing ("--probe-backends") will choose the "pure" ALSA stack instead then.
+ */
+#if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
+ { &g_DrvHostALSAAudio, "alsa" },
+#endif
+#ifdef VBOX_WITH_AUDIO_OSS
+ { &g_DrvHostOSSAudio, "oss" },
+#endif
+#if defined(RT_OS_DARWIN)
+ { &g_DrvHostCoreAudio, "coreaudio" },
+ { &g_DrvHostCoreAudio, "core" },
+ { &g_DrvHostCoreAudio, "ca" },
+#endif
+#if defined(RT_OS_WINDOWS)
+ { &g_DrvHostAudioWas, "wasapi" },
+ { &g_DrvHostAudioWas, "was" },
+ { &g_DrvHostDSound, "directsound" },
+ { &g_DrvHostDSound, "dsound" },
+ { &g_DrvHostDSound, "ds" },
+#endif
+#ifdef VBOX_WITH_AUDIO_DEBUG
+ { &g_DrvHostDebugAudio, "debug" },
+#endif
+ { &g_DrvHostValidationKitAudio, "valkit" }
+};
+AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
+/** Number of backends defined. */
+unsigned g_cBackends = RT_ELEMENTS(g_aBackends);
+
+/**
+ * Long option values for the 'test' command.
+ */
+enum
+{
+ VKAT_TEST_OPT_COUNT = 900,
+ VKAT_TEST_OPT_DEV,
+ VKAT_TEST_OPT_GUEST_ATS_ADDR,
+ VKAT_TEST_OPT_GUEST_ATS_PORT,
+ VKAT_TEST_OPT_HOST_ATS_ADDR,
+ VKAT_TEST_OPT_HOST_ATS_PORT,
+ VKAT_TEST_OPT_MODE,
+ VKAT_TEST_OPT_NO_AUDIO_OK,
+ VKAT_TEST_OPT_NO_VERIFY,
+ VKAT_TEST_OPT_OUTDIR,
+ VKAT_TEST_OPT_PAUSE,
+ VKAT_TEST_OPT_PCM_HZ,
+ VKAT_TEST_OPT_PCM_BIT,
+ VKAT_TEST_OPT_PCM_CHAN,
+ VKAT_TEST_OPT_PCM_SIGNED,
+ VKAT_TEST_OPT_PROBE_BACKENDS,
+ VKAT_TEST_OPT_TAG,
+ VKAT_TEST_OPT_TEMPDIR,
+ VKAT_TEST_OPT_VOL,
+ VKAT_TEST_OPT_TCP_BIND_ADDRESS,
+ VKAT_TEST_OPT_TCP_BIND_PORT,
+ VKAT_TEST_OPT_TCP_CONNECT_ADDRESS,
+ VKAT_TEST_OPT_TCP_CONNECT_PORT,
+ VKAT_TEST_OPT_TONE_DURATION_MS,
+ VKAT_TEST_OPT_TONE_VOL_PERCENT
+};
+
+/**
+ * Long option values for the 'verify' command.
+ */
+enum
+{
+ VKAT_VERIFY_OPT_MAX_DIFF_COUNT = 900,
+ VKAT_VERIFY_OPT_MAX_DIFF_PERCENT,
+ VKAT_VERIFY_OPT_MAX_SIZE_PERCENT,
+ VKAT_VERIFY_OPT_NORMALIZE
+};
+
+/**
+ * Common command line parameters.
+ */
+static const RTGETOPTDEF g_aCmdCommonOptions[] =
+{
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--daemonize", AUDIO_TEST_OPT_CMN_DAEMONIZE, RTGETOPT_REQ_NOTHING },
+ { "--daemonized", AUDIO_TEST_OPT_CMN_DAEMONIZED, RTGETOPT_REQ_NOTHING },
+ { "--debug-audio", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE, RTGETOPT_REQ_NOTHING },
+ { "--debug-audio-path", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH, RTGETOPT_REQ_STRING },
+};
+
+/**
+ * Command line parameters for test mode.
+ */
+static const RTGETOPTDEF g_aCmdTestOptions[] =
+{
+ { "--backend", 'b', RTGETOPT_REQ_STRING },
+ { "--drvaudio", 'd', RTGETOPT_REQ_NOTHING },
+ { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
+ { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
+ { "--guest-ats-addr", VKAT_TEST_OPT_GUEST_ATS_ADDR, RTGETOPT_REQ_STRING },
+ { "--guest-ats-port", VKAT_TEST_OPT_GUEST_ATS_PORT, RTGETOPT_REQ_UINT32 },
+ { "--host-ats-address", VKAT_TEST_OPT_HOST_ATS_ADDR, RTGETOPT_REQ_STRING },
+ { "--host-ats-port", VKAT_TEST_OPT_HOST_ATS_PORT, RTGETOPT_REQ_UINT32 },
+ { "--include", 'i', RTGETOPT_REQ_UINT32 },
+ { "--outdir", VKAT_TEST_OPT_OUTDIR, RTGETOPT_REQ_STRING },
+ { "--count", VKAT_TEST_OPT_COUNT, RTGETOPT_REQ_UINT32 },
+ { "--device", VKAT_TEST_OPT_DEV, RTGETOPT_REQ_STRING },
+ { "--pause", VKAT_TEST_OPT_PAUSE, RTGETOPT_REQ_UINT32 },
+ { "--pcm-bit", VKAT_TEST_OPT_PCM_BIT, RTGETOPT_REQ_UINT8 },
+ { "--pcm-chan", VKAT_TEST_OPT_PCM_CHAN, RTGETOPT_REQ_UINT8 },
+ { "--pcm-hz", VKAT_TEST_OPT_PCM_HZ, RTGETOPT_REQ_UINT16 },
+ { "--pcm-signed", VKAT_TEST_OPT_PCM_SIGNED, RTGETOPT_REQ_BOOL },
+ { "--probe-backends", VKAT_TEST_OPT_PROBE_BACKENDS, RTGETOPT_REQ_NOTHING },
+ { "--mode", VKAT_TEST_OPT_MODE, RTGETOPT_REQ_STRING },
+ { "--no-audio-ok", VKAT_TEST_OPT_NO_AUDIO_OK, RTGETOPT_REQ_NOTHING },
+ { "--no-verify", VKAT_TEST_OPT_NO_VERIFY, RTGETOPT_REQ_NOTHING },
+ { "--tag", VKAT_TEST_OPT_TAG, RTGETOPT_REQ_STRING },
+ { "--tempdir", VKAT_TEST_OPT_TEMPDIR, RTGETOPT_REQ_STRING },
+ { "--vol", VKAT_TEST_OPT_VOL, RTGETOPT_REQ_UINT8 },
+ { "--tcp-bind-addr", VKAT_TEST_OPT_TCP_BIND_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--tcp-bind-port", VKAT_TEST_OPT_TCP_BIND_PORT, RTGETOPT_REQ_UINT16 },
+ { "--tcp-connect-addr", VKAT_TEST_OPT_TCP_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--tcp-connect-port", VKAT_TEST_OPT_TCP_CONNECT_PORT, RTGETOPT_REQ_UINT16 },
+ { "--tone-duration", VKAT_TEST_OPT_TONE_DURATION_MS, RTGETOPT_REQ_UINT32 },
+ { "--tone-vol", VKAT_TEST_OPT_TONE_VOL_PERCENT, RTGETOPT_REQ_UINT8 }
+};
+
+/**
+ * Command line parameters for verification mode.
+ */
+static const RTGETOPTDEF g_aCmdVerifyOptions[] =
+{
+ { "--max-diff-count", VKAT_VERIFY_OPT_MAX_DIFF_COUNT, RTGETOPT_REQ_UINT32 },
+ { "--max-diff-percent", VKAT_VERIFY_OPT_MAX_DIFF_PERCENT, RTGETOPT_REQ_UINT8 },
+ { "--max-size-percent", VKAT_VERIFY_OPT_MAX_SIZE_PERCENT, RTGETOPT_REQ_UINT8 },
+ { "--normalize", VKAT_VERIFY_OPT_NORMALIZE, RTGETOPT_REQ_BOOL }
+};
+
+/** Terminate ASAP if set. Set on Ctrl-C. */
+bool volatile g_fTerminate = false;
+/** The release logger. */
+PRTLOGGER g_pRelLogger = NULL;
+/** The test handle. */
+RTTEST g_hTest;
+/** The current verbosity level. */
+unsigned g_uVerbosity = 0;
+/** DrvAudio: Enable debug (or not). */
+bool g_fDrvAudioDebug = false;
+/** DrvAudio: The debug output path. */
+const char *g_pszDrvAudioDebug = NULL;
+
+
+/**
+ * Get default backend.
+ */
+PCPDMDRVREG AudioTestGetDefaultBackend(void)
+{
+ return g_aBackends[0].pDrvReg;
+}
+
+
+/**
+ * Helper for handling --backend options.
+ *
+ * @returns Pointer to the specified backend, NULL if not found (error
+ * displayed).
+ * @param pszBackend The backend option value.
+ */
+PCPDMDRVREG AudioTestFindBackendOpt(const char *pszBackend)
+{
+ for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
+ if ( strcmp(pszBackend, g_aBackends[i].pszName) == 0
+ || strcmp(pszBackend, g_aBackends[i].pDrvReg->szName) == 0)
+ return g_aBackends[i].pDrvReg;
+ RTMsgError("Unknown backend: '%s'\n\n", pszBackend);
+ RTPrintf("Supported backend values are: ");
+ for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
+ {
+ if (i > 0)
+ RTPrintf(", ");
+ RTPrintf(g_aBackends[i].pszName);
+ }
+ RTPrintf("\n");
+ return NULL;
+}
+
+
+/*********************************************************************************************************************************
+* Test callbacks *
+*********************************************************************************************************************************/
+
+/**
+ * @copydoc FNAUDIOTESTSETUP
+ */
+static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
+{
+ RT_NOREF(pTstDesc, ppvCtx);
+
+ int rc = VINF_SUCCESS;
+
+ if (strlen(pTstEnv->szDev))
+ {
+ rc = audioTestDriverStackSetDevice(pTstEnv->pDrvStack, PDMAUDIODIR_OUT, pTstEnv->szDev);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
+ pTstParmsAcq->enmDir = PDMAUDIODIR_OUT;
+
+ pTstParmsAcq->TestTone = pTstEnv->ToneParms;
+
+ pTstParmsAcq->TestTone.Hdr.idxTest = pTstEnv->idxTest; /* Assign unique test ID. */
+
+ return rc;
+}
+
+/**
+ * @copydoc FNAUDIOTESTEXEC
+ */
+static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
+{
+ RT_NOREF(pvCtx);
+
+ int rc = VINF_SUCCESS;
+
+ PAUDIOTESTTONEPARMS const pToneParms = &pTstParms->TestTone;
+
+ uint32_t const idxTest = pToneParms->Hdr.idxTest;
+
+ RTTIMESPEC NowTimeSpec;
+ RTTimeExplode(&pToneParms->Hdr.tsCreated, RTTimeNow(&NowTimeSpec));
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing test tone (%RU16Hz, %RU32ms)\n",
+ idxTest, (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
+
+ /*
+ * 1. Arm the (host) ValKit ATS with the recording parameters.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Telling ValKit audio driver on host to record new tone ...\n", idxTest);
+
+ rc = AudioTestSvcClientToneRecord(&pTstEnv->u.Host.AtsClValKit, pToneParms);
+ if (RT_SUCCESS(rc))
+ {
+ /* Give the Validaiton Kit audio driver on the host a bit of time to register / arming the new test. */
+ RTThreadSleep(5000); /* Fudge factor. */
+
+ /*
+ * 2. Tell VKAT on guest to start playback.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Telling VKAT on guest to play tone ...\n", idxTest);
+
+ rc = AudioTestSvcClientTonePlay(&pTstEnv->u.Host.AtsClGuest, pToneParms);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: AudioTestSvcClientTonePlay() failed with %Rrc\n", idxTest, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "Test #%RU32: AudioTestSvcClientToneRecord() failed with %Rrc\n", idxTest, rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing tone done\n", idxTest);
+
+ /* Give the audio stack a random amount of time for draining data before the next iteration. */
+ if (pTstEnv->cIterations > 1)
+ RTThreadSleep(RTRandU32Ex(2000, 5000)); /** @todo Implement some dedicated ATS command for this? */
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: Playing test tone failed with %Rrc\n", idxTest, rc);
+
+ return rc;
+}
+
+/**
+ * @copydoc FNAUDIOTESTDESTROY
+ */
+static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
+{
+ RT_NOREF(pTstEnv, pvCtx);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc FNAUDIOTESTSETUP
+ */
+static DECLCALLBACK(int) audioTestRecordToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
+{
+ RT_NOREF(pTstDesc, ppvCtx);
+
+ int rc = VINF_SUCCESS;
+
+ if (strlen(pTstEnv->szDev))
+ {
+ rc = audioTestDriverStackSetDevice(pTstEnv->pDrvStack, PDMAUDIODIR_IN, pTstEnv->szDev);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
+ pTstParmsAcq->enmDir = PDMAUDIODIR_IN;
+
+ pTstParmsAcq->TestTone = pTstEnv->ToneParms;
+
+ pTstParmsAcq->TestTone.Hdr.idxTest = pTstEnv->idxTest; /* Assign unique test ID. */
+
+ return rc;
+}
+
+/**
+ * @copydoc FNAUDIOTESTEXEC
+ */
+static DECLCALLBACK(int) audioTestRecordToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
+{
+ RT_NOREF(pvCtx);
+
+ int rc = VINF_SUCCESS;
+
+ PAUDIOTESTTONEPARMS const pToneParms = &pTstParms->TestTone;
+
+ uint32_t const idxTest = pToneParms->Hdr.idxTest;
+
+ RTTIMESPEC NowTimeSpec;
+ RTTimeExplode(&pToneParms->Hdr.tsCreated, RTTimeNow(&NowTimeSpec));
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording test tone (%RU16Hz, %RU32ms)\n",
+ idxTest, (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
+
+ /*
+ * 1. Arm the (host) ValKit ATS with the playback parameters.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Telling ValKit audio driver on host to inject recording data ...\n", idxTest);
+
+ rc = AudioTestSvcClientTonePlay(&pTstEnv->u.Host.AtsClValKit, &pTstParms->TestTone);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * 2. Tell the guest ATS to start recording.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Telling VKAT on guest to record audio ...\n", idxTest);
+
+ rc = AudioTestSvcClientToneRecord(&pTstEnv->u.Host.AtsClGuest, &pTstParms->TestTone);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: AudioTestSvcClientToneRecord() failed with %Rrc\n", idxTest, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "Test #%RU32: AudioTestSvcClientTonePlay() failed with %Rrc\n", idxTest, rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait a bit to let the left over audio bits being processed. */
+ if (pTstEnv->cIterations > 1)
+ RTThreadSleep(RTRandU32Ex(2000, 5000)); /** @todo Implement some dedicated ATS command for this? */
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: Recording test tone failed with %Rrc\n", idxTest, rc);
+
+ return rc;
+}
+
+/**
+ * @copydoc FNAUDIOTESTDESTROY
+ */
+static DECLCALLBACK(int) audioTestRecordToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
+{
+ RT_NOREF(pTstEnv, pvCtx);
+
+ return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+* Test execution *
+*********************************************************************************************************************************/
+
+/** Test definition table. */
+AUDIOTESTDESC g_aTests[] =
+{
+ /* pszTest fExcluded pfnSetup */
+ { "PlayTone", false, audioTestPlayToneSetup, audioTestPlayToneExec, audioTestPlayToneDestroy },
+ { "RecordTone", false, audioTestRecordToneSetup, audioTestRecordToneExec, audioTestRecordToneDestroy }
+};
+/** Number of tests defined. */
+unsigned g_cTests = RT_ELEMENTS(g_aTests);
+
+/**
+ * Runs one specific audio test.
+ *
+ * @returns VBox status code.
+ * @param pTstEnv Test environment to use for running the test.
+ * @param pTstDesc Test to run.
+ */
+static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc)
+{
+ int rc = VINF_SUCCESS;
+
+ AUDIOTESTPARMS TstParms;
+ audioTestParmsInit(&TstParms);
+
+ RTTestSub(g_hTest, pTstDesc->pszName);
+
+ if (pTstDesc->fExcluded)
+ {
+ RTTestSkipped(g_hTest, "Test #%RU32 is excluded from list, skipping", pTstEnv->idxTest);
+ return VINF_SUCCESS;
+ }
+
+ pTstEnv->cIterations = pTstEnv->cIterations == 0 ? RTRandU32Ex(1, 10) : pTstEnv->cIterations;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32 (%RU32 iterations total)\n", pTstEnv->idxTest, pTstEnv->cIterations);
+
+ void *pvCtx = NULL; /* Test-specific opaque context. Optional and can be NULL. */
+
+ AssertPtr(pTstDesc->pfnExec);
+ for (uint32_t i = 0; i < pTstEnv->cIterations; i++)
+ {
+ int rc2;
+
+ if (pTstDesc->pfnSetup)
+ {
+ rc2 = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Test #%RU32 setup failed with %Rrc\n", pTstEnv->idxTest, rc2);
+ }
+ else
+ rc2 = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc2))
+ {
+ AssertPtrBreakStmt(pTstDesc->pfnExec, VERR_INVALID_POINTER);
+ rc2 = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Test #%RU32 execution failed with %Rrc\n", pTstEnv->idxTest, rc2);
+ }
+
+ if (pTstDesc->pfnDestroy)
+ {
+ rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Test #%RU32 destruction failed with %Rrc\n", pTstEnv->idxTest, rc2);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ /* Keep going. */
+ pTstEnv->idxTest++;
+ }
+
+ RTTestSubDone(g_hTest);
+
+ audioTestParmsDestroy(&TstParms);
+
+ return rc;
+}
+
+/**
+ * Runs all specified tests in a row.
+ *
+ * @returns VBox status code.
+ * @param pTstEnv Test environment to use for running all tests.
+ */
+int audioTestWorker(PAUDIOTESTENV pTstEnv)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest ATS running\n");
+
+ while (!g_fTerminate)
+ RTThreadSleep(100);
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down guest ATS ...\n");
+
+ int rc2 = AudioTestSvcStop(pTstEnv->pSrv);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest ATS shutdown complete\n");
+ }
+ else if (pTstEnv->enmMode == AUDIOTESTMODE_HOST)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Telling ValKit audio driver on host to begin a new test set ...\n");
+ rc = AudioTestSvcClientTestSetBegin(&pTstEnv->u.Host.AtsClValKit, pTstEnv->szTag);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Telling VKAT on guest to begin a new test set ...\n");
+ rc = AudioTestSvcClientTestSetBegin(&pTstEnv->u.Host.AtsClGuest, pTstEnv->szTag);
+ if (RT_FAILURE(rc))
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Beginning test set on guest failed with %Rrc\n", rc);
+ }
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Beginning test set on host (Validation Kit audio driver) failed with %Rrc\n", rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+ {
+ int rc2 = audioTestOne(pTstEnv, &g_aTests[i]);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ if (g_fTerminate)
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Fudge! */
+ RTMSINTERVAL const msWait = RTRandU32Ex(RT_MS_1SEC, RT_MS_5SEC);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Waiting %RU32ms to let guest and the audio stack process remaining data ...\n", msWait);
+ RTThreadSleep(msWait);
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Ending test set on guest ...\n");
+ int rc2 = AudioTestSvcClientTestSetEnd(&pTstEnv->u.Host.AtsClGuest, pTstEnv->szTag);
+ if (RT_FAILURE(rc2))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Ending test set on guest failed with %Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Ending test set on host (Validation Kit audio driver) ...\n");
+ rc2 = AudioTestSvcClientTestSetEnd(&pTstEnv->u.Host.AtsClValKit, pTstEnv->szTag);
+ if (RT_FAILURE(rc2))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Ending test set on host (Validation Kit audio driver) failed with %Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ if ( !g_fTerminate
+ && RT_SUCCESS(rc))
+ {
+ /*
+ * Download guest + Validation Kit audio driver test sets to our output directory.
+ */
+ char szFileName[RTPATH_MAX];
+ if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s-guest.tar.gz", pTstEnv->szTag))
+ {
+ rc = RTPathJoin(pTstEnv->u.Host.szPathTestSetGuest, sizeof(pTstEnv->u.Host.szPathTestSetGuest),
+ pTstEnv->szPathOut, szFileName);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s-host.tar.gz", pTstEnv->szTag))
+ {
+ rc = RTPathJoin(pTstEnv->u.Host.szPathTestSetValKit, sizeof(pTstEnv->u.Host.szPathTestSetValKit),
+ pTstEnv->szPathOut, szFileName);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Downloading guest test set to '%s'\n",
+ pTstEnv->u.Host.szPathTestSetGuest);
+ rc = AudioTestSvcClientTestSetDownload(&pTstEnv->u.Host.AtsClGuest,
+ pTstEnv->szTag, pTstEnv->u.Host.szPathTestSetGuest);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Downloading host test set to '%s'\n",
+ pTstEnv->u.Host.szPathTestSetValKit);
+ rc = AudioTestSvcClientTestSetDownload(&pTstEnv->u.Host.AtsClValKit,
+ pTstEnv->szTag, pTstEnv->u.Host.szPathTestSetValKit);
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if ( RT_SUCCESS(rc)
+ && !pTstEnv->fSkipVerify)
+ {
+ rc = audioVerifyOne(pTstEnv->u.Host.szPathTestSetGuest, pTstEnv->u.Host.szPathTestSetValKit, NULL /* pOpts */);
+ }
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification skipped\n");
+
+ if (!pTstEnv->fSkipVerify)
+ {
+ RTFileDelete(pTstEnv->u.Host.szPathTestSetGuest);
+ RTFileDelete(pTstEnv->u.Host.szPathTestSetValKit);
+ }
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Leaving test set files behind\n");
+ }
+ }
+ }
+ else
+ rc = VERR_NOT_IMPLEMENTED;
+
+ /* Clean up. */
+ RTDirRemove(pTstEnv->szPathTemp);
+ RTDirRemove(pTstEnv->szPathOut);
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test worker failed with %Rrc", rc);
+
+ return rc;
+}
+
+/** Option help for the 'test' command. */
+static DECLCALLBACK(const char *) audioTestCmdTestHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";
+ case 'b': return "The audio backend to use";
+ case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
+ case 'e': return "Exclude the given test id from the list";
+ case 'i': return "Include the given test id in the list";
+ case VKAT_TEST_OPT_COUNT: return "Number of test iterations to perform for selected tests\n"
+ " Default: random number";
+ case VKAT_TEST_OPT_DEV: return "Name of the input/output device to use\n"
+ " Default: default device";
+ case VKAT_TEST_OPT_TONE_DURATION_MS: return "Test tone duration to play / record (ms)\n"
+ " Default: random duration";
+ case VKAT_TEST_OPT_TONE_VOL_PERCENT: return "Test tone volume (percent)\n"
+ " Default: 100";
+ case VKAT_TEST_OPT_GUEST_ATS_ADDR: return "Address of guest ATS to connect to\n"
+ " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR;
+ case VKAT_TEST_OPT_GUEST_ATS_PORT: return "Port of guest ATS to connect to (needs NAT port forwarding)\n"
+ " Default: 6042"; /* ATS_TCP_DEF_CONNECT_PORT_GUEST */
+ case VKAT_TEST_OPT_HOST_ATS_ADDR: return "Address of host ATS to connect to\n"
+ " Default: " ATS_TCP_DEF_CONNECT_HOST_ADDR_STR;
+ case VKAT_TEST_OPT_HOST_ATS_PORT: return "Port of host ATS to connect to\n"
+ " Default: 6052"; /* ATS_TCP_DEF_BIND_PORT_VALKIT */
+ case VKAT_TEST_OPT_MODE: return "Test mode to use when running the tests\n"
+ " Available modes:\n"
+ " guest: Run as a guest-side ATS\n"
+ " host: Run as a host-side ATS";
+ case VKAT_TEST_OPT_NO_AUDIO_OK: return "Enables running without any found audio hardware (e.g. servers)";
+ case VKAT_TEST_OPT_NO_VERIFY: return "Skips the verification step";
+ case VKAT_TEST_OPT_OUTDIR: return "Output directory to use";
+ case VKAT_TEST_OPT_PAUSE: return "Not yet implemented";
+ case VKAT_TEST_OPT_PCM_HZ: return "PCM Hertz (Hz) rate to use\n"
+ " Default: 44100";
+ case VKAT_TEST_OPT_PCM_BIT: return "PCM sample bits (i.e. 16) to use\n"
+ " Default: 16";
+ case VKAT_TEST_OPT_PCM_CHAN: return "PCM channels to use\n"
+ " Default: 2";
+ case VKAT_TEST_OPT_PCM_SIGNED: return "PCM samples to use (signed = true, unsigned = false)\n"
+ " Default: true";
+ case VKAT_TEST_OPT_PROBE_BACKENDS: return "Probes all (available) backends until a working one is found";
+ case VKAT_TEST_OPT_TAG: return "Test set tag to use";
+ case VKAT_TEST_OPT_TEMPDIR: return "Temporary directory to use";
+ case VKAT_TEST_OPT_VOL: return "Audio volume (percent) to use";
+ case VKAT_TEST_OPT_TCP_BIND_ADDRESS: return "TCP address listening to (server mode)";
+ case VKAT_TEST_OPT_TCP_BIND_PORT: return "TCP port listening to (server mode)";
+ case VKAT_TEST_OPT_TCP_CONNECT_ADDRESS: return "TCP address to connect to (client mode)";
+ case VKAT_TEST_OPT_TCP_CONNECT_PORT: return "TCP port to connect to (client mode)";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * Main (entry) function for the testing functionality of VKAT.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioTestMain(PRTGETOPTSTATE pGetState)
+{
+ AUDIOTESTENV TstEnv;
+ audioTestEnvInit(&TstEnv);
+
+ int rc;
+
+ PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
+ uint8_t cPcmSampleBit = 0;
+ uint8_t cPcmChannels = 0;
+ uint32_t uPcmHz = 0;
+ bool fPcmSigned = true;
+ bool fProbeBackends = false;
+ bool fNoAudioOk = false;
+
+ const char *pszGuestTcpAddr = NULL;
+ uint16_t uGuestTcpPort = ATS_TCP_DEF_BIND_PORT_GUEST;
+ const char *pszValKitTcpAddr = NULL;
+ uint16_t uValKitTcpPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'a':
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+ g_aTests[i].fExcluded = true;
+ break;
+
+ case 'b':
+ pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
+ if (pDrvReg == NULL)
+ return RTEXITCODE_SYNTAX;
+ break;
+
+ case 'd':
+ TstEnv.IoOpts.fWithDrvAudio = true;
+ break;
+
+ case 'e':
+ if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
+ g_aTests[ValueUnion.u32].fExcluded = true;
+ break;
+
+ case VKAT_TEST_OPT_GUEST_ATS_ADDR:
+ pszGuestTcpAddr = ValueUnion.psz;
+ break;
+
+ case VKAT_TEST_OPT_GUEST_ATS_PORT:
+ uGuestTcpPort = ValueUnion.u32;
+ break;
+
+ case VKAT_TEST_OPT_HOST_ATS_ADDR:
+ pszValKitTcpAddr = ValueUnion.psz;
+ break;
+
+ case VKAT_TEST_OPT_HOST_ATS_PORT:
+ uValKitTcpPort = ValueUnion.u32;
+ break;
+
+ case VKAT_TEST_OPT_MODE:
+ if (TstEnv.enmMode != AUDIOTESTMODE_UNKNOWN)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Test mode (guest / host) already specified");
+ TstEnv.enmMode = RTStrICmp(ValueUnion.psz, "guest") == 0 ? AUDIOTESTMODE_GUEST : AUDIOTESTMODE_HOST;
+ break;
+
+ case VKAT_TEST_OPT_NO_AUDIO_OK:
+ fNoAudioOk = true;
+ break;
+
+ case VKAT_TEST_OPT_NO_VERIFY:
+ TstEnv.fSkipVerify = true;
+ break;
+
+ case 'i':
+ if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
+ g_aTests[ValueUnion.u32].fExcluded = false;
+ break;
+
+ case VKAT_TEST_OPT_COUNT:
+ TstEnv.cIterations = ValueUnion.u32;
+ break;
+
+ case VKAT_TEST_OPT_DEV:
+ rc = RTStrCopy(TstEnv.szDev, sizeof(TstEnv.szDev), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Failed to copy out device: %Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_TONE_DURATION_MS:
+ TstEnv.ToneParms.msDuration = ValueUnion.u32;
+ break;
+
+ case VKAT_TEST_OPT_TONE_VOL_PERCENT:
+ TstEnv.ToneParms.uVolumePercent = ValueUnion.u8;
+ break;
+
+ case VKAT_TEST_OPT_PAUSE:
+ return RTMsgErrorExitFailure("Not yet implemented!");
+
+ case VKAT_TEST_OPT_OUTDIR:
+ rc = RTStrCopy(TstEnv.szPathOut, sizeof(TstEnv.szPathOut), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Failed to copy out directory: %Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_PCM_BIT:
+ cPcmSampleBit = ValueUnion.u8;
+ break;
+
+ case VKAT_TEST_OPT_PCM_CHAN:
+ cPcmChannels = ValueUnion.u8;
+ break;
+
+ case VKAT_TEST_OPT_PCM_HZ:
+ uPcmHz = ValueUnion.u32;
+ break;
+
+ case VKAT_TEST_OPT_PCM_SIGNED:
+ fPcmSigned = ValueUnion.f;
+ break;
+
+ case VKAT_TEST_OPT_PROBE_BACKENDS:
+ fProbeBackends = true;
+ break;
+
+ case VKAT_TEST_OPT_TAG:
+ rc = RTStrCopy(TstEnv.szTag, sizeof(TstEnv.szTag), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Tag invalid, rc=%Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_TEMPDIR:
+ rc = RTStrCopy(TstEnv.szPathTemp, sizeof(TstEnv.szPathTemp), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Temp dir invalid, rc=%Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_VOL:
+ TstEnv.IoOpts.uVolumePercent = ValueUnion.u8;
+ break;
+
+ case VKAT_TEST_OPT_TCP_BIND_ADDRESS:
+ rc = RTStrCopy(TstEnv.TcpOpts.szBindAddr, sizeof(TstEnv.TcpOpts.szBindAddr), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Bind address invalid, rc=%Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_TCP_BIND_PORT:
+ TstEnv.TcpOpts.uBindPort = ValueUnion.u16;
+ break;
+
+ case VKAT_TEST_OPT_TCP_CONNECT_ADDRESS:
+ rc = RTStrCopy(TstEnv.TcpOpts.szConnectAddr, sizeof(TstEnv.TcpOpts.szConnectAddr), ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Connect address invalid, rc=%Rrc", rc);
+ break;
+
+ case VKAT_TEST_OPT_TCP_CONNECT_PORT:
+ TstEnv.TcpOpts.uConnectPort = ValueUnion.u16;
+ break;
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdTest);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Start testing.
+ */
+ RTTestBanner(g_hTest);
+
+ if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No test mode (--mode) specified!\n");
+
+ /* Validate TCP options. */
+ if ( TstEnv.TcpOpts.szBindAddr[0]
+ && TstEnv.TcpOpts.szConnectAddr[0])
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one TCP connection mode (connect as client *or* bind as server) can be specified) at a time!\n");
+
+ /* Set new (override standard) I/O PCM properties if set by the user. */
+ if ( cPcmSampleBit
+ || cPcmChannels
+ || uPcmHz)
+ {
+ PDMAudioPropsInit(&TstEnv.IoOpts.Props,
+ cPcmSampleBit ? cPcmSampleBit / 2 : 2 /* 16-bit */, fPcmSigned /* fSigned */,
+ cPcmChannels ? cPcmChannels : 2 /* Stereo */, uPcmHz ? uPcmHz : 44100);
+ }
+
+ /* Do this first before everything else below. */
+ rc = AudioTestDriverStackPerformSelftest();
+ if (RT_FAILURE(rc))
+ {
+ if (!fNoAudioOk)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Testing driver stack failed: %Rrc\n", rc);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Warning: Testing driver stack not possible (%Rrc), but --no-audio-ok was specified. Running on a server without audio hardware?\n", rc);
+ }
+
+ AUDIOTESTDRVSTACK DrvStack;
+ if (fProbeBackends)
+ rc = audioTestDriverStackProbe(&DrvStack, pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, TstEnv.IoOpts.fWithDrvAudio); /** @todo Make in/out configurable, too. */
+ else
+ rc = audioTestDriverStackInitEx(&DrvStack, pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, TstEnv.IoOpts.fWithDrvAudio); /** @todo Make in/out configurable, too. */
+ if (RT_FAILURE(rc))
+ {
+ if (!fNoAudioOk)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unable to init driver stack: %Rrc\n", rc);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Warning: Initializing driver stack not possible (%Rrc), but --no-audio-ok was specified. Running on a server without audio hardware?\n", rc);
+ }
+
+ PPDMAUDIOHOSTDEV pDev;
+ rc = audioTestDevicesEnumerateAndCheck(&DrvStack, TstEnv.szDev, &pDev);
+ if (RT_FAILURE(rc))
+ {
+ if (!fNoAudioOk)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Enumerating device(s) failed: %Rrc\n", rc);
+ }
+
+ /* For now all tests have the same test environment and driver stack. */
+ rc = audioTestEnvCreate(&TstEnv, &DrvStack);
+ if (RT_SUCCESS(rc))
+ rc = audioTestWorker(&TstEnv);
+
+ audioTestEnvDestroy(&TstEnv);
+ audioTestDriverStackDelete(&DrvStack);
+
+ if (RT_FAILURE(rc)) /* Let us know that something went wrong in case we forgot to mention it. */
+ RTTestFailed(g_hTest, "Testing failed with %Rrc\n", rc);
+
+ /*
+ * Print summary and exit.
+ */
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
+
+const VKATCMD g_CmdTest =
+{
+ "test",
+ audioTestMain,
+ "Runs audio tests and creates an audio test set.",
+ g_aCmdTestOptions,
+ RT_ELEMENTS(g_aCmdTestOptions),
+ audioTestCmdTestHelp,
+ true /* fNeedsTransport */
+};
+
+
+/*********************************************************************************************************************************
+* Command: verify *
+*********************************************************************************************************************************/
+
+static int audioVerifyOpenTestSet(const char *pszPathSet, PAUDIOTESTSET pSet)
+{
+ int rc;
+
+ char szPathExtracted[RTPATH_MAX];
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Opening test set '%s'\n", pszPathSet);
+
+ const bool fPacked = AudioTestSetIsPacked(pszPathSet);
+
+ if (fPacked)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set is an archive and needs to be unpacked\n");
+
+ if (!RTFileExists(pszPathSet))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set '%s' does not exist\n", pszPathSet);
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ char szPathTemp[RTPATH_MAX];
+ rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using temporary directory '%s'\n", szPathTemp);
+
+ rc = RTPathJoin(szPathExtracted, sizeof(szPathExtracted), szPathTemp, "vkat-testset-XXXX");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirCreateTemp(szPathExtracted, 0755);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unpacking archive to '%s'\n", szPathExtracted);
+ rc = AudioTestSetUnpack(pszPathSet, szPathExtracted);
+ if (RT_SUCCESS(rc))
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Archive successfully unpacked\n");
+ }
+ }
+ }
+ }
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ rc = AudioTestSetOpen(pSet, fPacked ? szPathExtracted : pszPathSet);
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Unable to open / unpack test set archive: %Rrc", rc);
+
+ return rc;
+}
+
+/**
+ * Verifies one test set pair.
+ *
+ * @returns VBox status code.
+ * @param pszPathSetA Absolute path to test set A.
+ * @param pszPathSetB Absolute path to test set B.
+ * @param pOpts Verification options to use. Optional.
+ * When NULL, the (very strict) defaults will be used.
+ */
+static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB, PAUDIOTESTVERIFYOPTS pOpts)
+{
+ RTTestSubF(g_hTest, "Verifying");
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verifying test set '%s' with test set '%s'\n", pszPathSetA, pszPathSetB);
+
+ AUDIOTESTSET SetA, SetB;
+ int rc = audioVerifyOpenTestSet(pszPathSetA, &SetA);
+ if (RT_SUCCESS(rc))
+ {
+ rc = audioVerifyOpenTestSet(pszPathSetB, &SetB);
+ if (RT_SUCCESS(rc))
+ {
+ AUDIOTESTERRORDESC errDesc;
+ if (pOpts)
+ rc = AudioTestSetVerifyEx(&SetA, &SetB, pOpts, &errDesc);
+ else
+ rc = AudioTestSetVerify(&SetA, &SetB, &errDesc);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t const cErr = AudioTestErrorDescCount(&errDesc);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%RU32 errors occurred while verifying\n", cErr);
+
+ /** @todo Use some AudioTestErrorXXX API for enumeration here later. */
+ PAUDIOTESTERRORENTRY pErrEntry;
+ RTListForEach(&errDesc.List, pErrEntry, AUDIOTESTERRORENTRY, Node)
+ {
+ if (RT_FAILURE(pErrEntry->rc))
+ RTTestFailed(g_hTest, "%s\n", pErrEntry->szDesc);
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%s\n", pErrEntry->szDesc);
+ }
+
+ if (cErr == 0)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification successful\n");
+
+ AudioTestErrorDescDestroy(&errDesc);
+ }
+ else
+ RTTestFailed(g_hTest, "Verification failed with %Rrc", rc);
+
+#ifdef DEBUG
+ if (g_fDrvAudioDebug)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "\n"
+ "Use the following command line to re-run verification in the debugger:\n"
+ "gdb --args ./VBoxAudioTest -vvvv --debug-audio verify \"%s\" \"%s\"\n",
+ SetA.szPathAbs, SetB.szPathAbs);
+#endif
+ if (!g_fDrvAudioDebug) /* Don't wipe stuff when debugging. Can be useful for introspecting data. */
+ AudioTestSetWipe(&SetB);
+ AudioTestSetClose(&SetB);
+ }
+
+ if (!g_fDrvAudioDebug) /* Ditto. */
+ AudioTestSetWipe(&SetA);
+ AudioTestSetClose(&SetA);
+ }
+
+ RTTestSubDone(g_hTest);
+
+ return rc;
+}
+
+/** Option help for the 'verify' command. */
+static DECLCALLBACK(const char *) audioTestCmdVerifyHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case VKAT_VERIFY_OPT_MAX_DIFF_COUNT: return "Specifies the maximum number of differences\n"
+ " Default: 0 (strict)";
+ case VKAT_VERIFY_OPT_MAX_DIFF_PERCENT: return "Specifies the maximum difference (percent)\n"
+ " Default: 0 (strict)";
+ case VKAT_VERIFY_OPT_MAX_SIZE_PERCENT: return "Specifies the maximum size difference (percent)\n"
+ " Default: 1 (strict)";
+ case VKAT_VERIFY_OPT_NORMALIZE: return "Enables / disables audio data normalization\n"
+ " Default: false";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * Main (entry) function for the verification functionality of VKAT.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioVerifyMain(PRTGETOPTSTATE pGetState)
+{
+ /*
+ * Parse options and process arguments.
+ */
+ const char *apszSets[2] = { NULL, NULL };
+ unsigned iTestSet = 0;
+
+ AUDIOTESTVERIFYOPTS Opts;
+ AudioTestSetVerifyOptsInit(&Opts);
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case VKAT_VERIFY_OPT_MAX_DIFF_COUNT:
+ Opts.cMaxDiff = ValueUnion.u32;
+ break;
+
+ case VKAT_VERIFY_OPT_MAX_DIFF_PERCENT:
+ Opts.uMaxDiffPercent = ValueUnion.u8;
+ break;
+
+ case VKAT_VERIFY_OPT_MAX_SIZE_PERCENT:
+ Opts.uMaxSizePercent = ValueUnion.u8;
+ break;
+
+ case VKAT_VERIFY_OPT_NORMALIZE:
+ Opts.fNormalize = ValueUnion.f;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (iTestSet == 0)
+ RTTestBanner(g_hTest);
+ if (iTestSet >= RT_ELEMENTS(apszSets))
+ return RTMsgErrorExitFailure("Only two test sets can be verified at one time");
+ apszSets[iTestSet++] = ValueUnion.psz;
+ break;
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdVerify);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (!iTestSet)
+ return RTMsgErrorExitFailure("At least one test set must be specified");
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * If only test set A is given, default to the current directory
+ * for test set B.
+ */
+ char szDirCur[RTPATH_MAX];
+ if (iTestSet == 1)
+ {
+ rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
+ if (RT_SUCCESS(rc))
+ apszSets[1] = szDirCur;
+ else
+ RTTestFailed(g_hTest, "Failed to retrieve current directory: %Rrc", rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ audioVerifyOne(apszSets[0], apszSets[1], &Opts);
+
+ /*
+ * Print summary and exit.
+ */
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
+
+const VKATCMD g_CmdVerify =
+{
+ "verify",
+ audioVerifyMain,
+ "Verifies a formerly created audio test set.",
+ g_aCmdVerifyOptions,
+ RT_ELEMENTS(g_aCmdVerifyOptions),
+ audioTestCmdVerifyHelp,
+ false /* fNeedsTransport */
+};
+
+
+/*********************************************************************************************************************************
+* Main *
+*********************************************************************************************************************************/
+
+/**
+ * Ctrl-C signal handler.
+ *
+ * This just sets g_fTerminate and hope it will be noticed soon.
+ *
+ * On non-Windows it restores the SIGINT action to default, so that a second
+ * Ctrl-C will have the normal effect (just in case the code doesn't respond to
+ * g_fTerminate).
+ */
+#ifdef RT_OS_WINDOWS
+static BOOL CALLBACK audioTestConsoleCtrlHandler(DWORD dwCtrlType) RT_NOEXCEPT
+{
+ if (dwCtrlType != CTRL_C_EVENT && dwCtrlType != CTRL_BREAK_EVENT)
+ return false;
+ RTPrintf(dwCtrlType == CTRL_C_EVENT ? "Ctrl-C!\n" : "Ctrl-Break!\n");
+
+ ASMAtomicWriteBool(&g_fTerminate, true);
+
+ return true;
+}
+#else
+static void audioTestSignalHandler(int iSig) RT_NOEXCEPT
+{
+ Assert(iSig == SIGINT); RT_NOREF(iSig);
+ RTPrintf("Ctrl-C!\n");
+
+ ASMAtomicWriteBool(&g_fTerminate, true);
+
+ signal(SIGINT, SIG_DFL);
+}
+#endif
+
+/**
+ * Commands.
+ */
+static const VKATCMD * const g_apCommands[] =
+{
+ &g_CmdTest,
+ &g_CmdVerify,
+ &g_CmdBackends,
+ &g_CmdEnum,
+ &g_CmdPlay,
+ &g_CmdRec,
+ &g_CmdSelfTest
+};
+
+/**
+ * Shows tool usage text.
+ */
+RTEXITCODE audioTestUsage(PRTSTREAM pStrm, PCVKATCMD pOnlyCmd)
+{
+ RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n", RTProcShortName());
+ RTStrmPrintf(pStrm,
+ "\n"
+ "Global Options:\n"
+ " --debug-audio\n"
+ " Enables (DrvAudio) debugging\n"
+ " --debug-audio-path=<path>\n"
+ " Tells DrvAudio where to put its debug output (wav-files)\n"
+ " -q, --quiet\n"
+ " Sets verbosity to zero\n"
+ " -v, --verbose\n"
+ " Increase verbosity\n"
+ " -V, --version\n"
+ " Displays version\n"
+ " -h, -?, --help\n"
+ " Displays help\n"
+ );
+
+ for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
+ {
+ PCVKATCMD const pCmd = g_apCommands[iCmd];
+ if (!pOnlyCmd || pCmd == pOnlyCmd)
+ {
+ RTStrmPrintf(pStrm,
+ "\n"
+ "Command '%s':\n"
+ " %s\n"
+ "Options for '%s':\n",
+ pCmd->pszCommand, pCmd->pszDesc, pCmd->pszCommand);
+ PCRTGETOPTDEF const paOptions = pCmd->paOptions;
+ for (unsigned i = 0; i < pCmd->cOptions; i++)
+ {
+ if (RT_C_IS_PRINT(paOptions[i].iShort))
+ RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
+ else
+ RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);
+
+ const char *pszHelp = NULL;
+ if (pCmd->pfnOptionHelp)
+ pszHelp = pCmd->pfnOptionHelp(&paOptions[i]);
+ if (pszHelp)
+ RTStrmPrintf(pStrm, " %s\n", pszHelp);
+ }
+
+ if (pCmd->fNeedsTransport)
+ for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
+ g_apTransports[iTx]->pfnUsage(pStrm);
+ }
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Lists the commands and their descriptions.
+ */
+static RTEXITCODE audioTestListCommands(PRTSTREAM pStrm)
+{
+ RTStrmPrintf(pStrm, "Commands:\n");
+ for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
+ RTStrmPrintf(pStrm, "%8s - %s\n", g_apCommands[iCmd]->pszCommand, g_apCommands[iCmd]->pszDesc);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Shows tool version.
+ */
+RTEXITCODE audioTestVersion(void)
+{
+ RTPrintf("%s\n", RTBldCfgRevisionStr());
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Shows the logo.
+ *
+ * @param pStream Output stream to show logo on.
+ */
+void audioTestShowLogo(PRTSTREAM pStream)
+{
+ RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) Version " VBOX_VERSION_STRING " - r%s\n"
+ "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n", RTBldCfgRevisionStr());
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init IPRT.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Handle special command line options which need parsing before
+ * everything else.
+ */
+ /** @todo r=bird: this isn't at all syntactically sane, because you don't know
+ * how to parse past the command (can almost be done safely thought, since
+ * you've got the option definitions for every command at hand). So, if someone
+ * wants to play a file named "-v.wav", you'll incorrectly take that as two 'v'
+ * options. The parsing has to stop when you get to the command, i.e. first
+ * VINF_GETOPT_NOT_OPTION or anything that isn't a common option. Daemonizing
+ * when for instance encountering an invalid command, is not correct.
+ *
+ * Btw. you MUST however process the 'q' option in parallel to 'v' here, they
+ * are oposites. For instance '-vqvvv' is supposed to give you level 3 logging,
+ * not quiet! So, either you process both 'v' and 'q' here, or you pospone them
+ * (better option).
+ */
+ /** @todo r=bird: Is the daemonizing needed? The testcase doesn't seem to use
+ * it... If you don't need it, drop it as it make the parsing complex
+ * and illogical. The --daemonized / --damonize options should be
+ * required to before the command, then okay. */
+ bool fDaemonize = false;
+ bool fDaemonized = false;
+
+ RTGETOPTSTATE GetState;
+ rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,
+ RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case AUDIO_TEST_OPT_CMN_DAEMONIZE:
+ fDaemonize = true;
+ break;
+
+ case AUDIO_TEST_OPT_CMN_DAEMONIZED:
+ fDaemonized = true;
+ break;
+
+ /* Has to be defined here and not in AUDIO_TEST_COMMON_OPTION_CASES, to get the logger
+ * configured before the specific command handlers down below come into play. */
+ case 'v':
+ g_uVerbosity++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /** @todo add something to suppress this stuff. */
+ audioTestShowLogo(g_pStdOut);
+
+ if (fDaemonize)
+ {
+ if (!fDaemonized)
+ {
+ rc = RTProcDaemonize(argv, "--daemonized");
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize() failed with %Rrc\n", rc);
+
+ RTMsgInfo("Starting in background (daemonizing) ...");
+ return RTEXITCODE_SUCCESS;
+ }
+ /* else continue running in background. */
+ }
+
+ /*
+ * Init test and globals.
+ * Note: Needs to be done *after* daemonizing, otherwise the child will fail!
+ */
+ rc = RTTestCreate("AudioTest", &g_hTest);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTTestCreate() failed with %Rrc\n", rc);
+
+#ifdef RT_OS_WINDOWS
+ HRESULT hrc = CoInitializeEx(NULL /*pReserved*/, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE);
+ if (FAILED(hrc))
+ RTMsgWarning("CoInitializeEx failed: %#x", hrc);
+#endif
+
+ /*
+ * Configure release logging to go to stdout.
+ */
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
+ rc = RTLogCreate(&g_pRelLogger, fFlags, "all.e.l", "VKAT_RELEASE_LOG",
+ RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTLogRelSetDefaultInstance(g_pRelLogger);
+ if (g_uVerbosity)
+ {
+ RTMsgInfo("Setting verbosity logging to level %u\n", g_uVerbosity);
+ switch (g_uVerbosity) /* Not very elegant, but has to do it for now. */
+ {
+ case 1:
+ rc = RTLogGroupSettings(g_pRelLogger,
+ "drv_audio.e.l+drv_host_audio.e.l+"
+ "audio_mixer.e.l+audio_test.e.l");
+ break;
+
+ case 2:
+ rc = RTLogGroupSettings(g_pRelLogger,
+ "drv_audio.e.l.l2+drv_host_audio.e.l.l2+"
+ "audio_mixer.e.l.l2+audio_test.e.l.l2");
+ break;
+
+ case 3:
+ rc = RTLogGroupSettings(g_pRelLogger,
+ "drv_audio.e.l.l2.l3+drv_host_audio.e.l.l2.l3+"
+ "audio_mixer.e.l.l2.l3+audio_test.e.l.l2.l3");
+ break;
+
+ case 4:
+ RT_FALL_THROUGH();
+ default:
+ rc = RTLogGroupSettings(g_pRelLogger,
+ "drv_audio.e.l.l2.l3.l4.f+drv_host_audio.e.l.l2.l3.l4.f+"
+ "audio_mixer.e.l.l2.l3.l4.f+audio_test.e.l.l2.l3.l4.f");
+ break;
+ }
+ if (RT_FAILURE(rc))
+ RTMsgError("Setting debug logging failed, rc=%Rrc\n", rc);
+ }
+ }
+ else
+ RTMsgWarning("Failed to create release logger: %Rrc", rc);
+
+ /*
+ * Install a Ctrl-C signal handler.
+ */
+#ifdef RT_OS_WINDOWS
+ SetConsoleCtrlHandler(audioTestConsoleCtrlHandler, TRUE);
+#else
+ struct sigaction sa;
+ RT_ZERO(sa);
+ sa.sa_handler = audioTestSignalHandler;
+ sigaction(SIGINT, &sa, NULL);
+#endif
+
+ /*
+ * Process common options.
+ */
+ RT_ZERO(GetState);
+ rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,
+ RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, NULL);
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
+ {
+ PCVKATCMD const pCmd = g_apCommands[iCmd];
+ if (strcmp(ValueUnion.psz, pCmd->pszCommand) == 0)
+ {
+ /* Count the combined option definitions: */
+ size_t cCombinedOptions = pCmd->cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
+ if (pCmd->fNeedsTransport)
+ for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
+ cCombinedOptions += g_apTransports[iTx]->cOpts;
+
+ /* Combine the option definitions: */
+ PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
+ if (paCombinedOptions)
+ {
+ uint32_t idxOpts = 0;
+ memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
+ idxOpts += RT_ELEMENTS(g_aCmdCommonOptions);
+
+ memcpy(&paCombinedOptions[idxOpts], pCmd->paOptions, pCmd->cOptions * sizeof(RTGETOPTDEF));
+ idxOpts += (uint32_t)pCmd->cOptions;
+
+ if (pCmd->fNeedsTransport)
+ for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
+ {
+ memcpy(&paCombinedOptions[idxOpts],
+ g_apTransports[iTx]->paOpts, g_apTransports[iTx]->cOpts * sizeof(RTGETOPTDEF));
+ idxOpts += (uint32_t)g_apTransports[iTx]->cOpts;
+ }
+
+ /* Re-initialize the option getter state and pass it to the command handler. */
+ rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
+ GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ if (RT_SUCCESS(rc))
+ {
+ RTEXITCODE rcExit = pCmd->pfnHandler(&GetState);
+ RTMemFree(paCombinedOptions);
+ return rcExit;
+ }
+ RTMemFree(paCombinedOptions);
+ return RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
+ }
+ return RTMsgErrorExitFailure("Out of memory!");
+ }
+ }
+ RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
+ audioTestListCommands(g_pStdErr);
+ return RTEXITCODE_SYNTAX;
+ }
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ RTMsgError("No command specified!\n");
+ audioTestListCommands(g_pStdErr);
+ return RTEXITCODE_SYNTAX;
+}
diff --git a/src/VBox/ValidationKit/utils/audio/vkatCmdGeneric.cpp b/src/VBox/ValidationKit/utils/audio/vkatCmdGeneric.cpp
new file mode 100644
index 00000000..18eb9487
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkatCmdGeneric.cpp
@@ -0,0 +1,1169 @@
+/* $Id: vkatCmdGeneric.cpp $ */
+/** @file
+ * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/errcore.h>
+#include <iprt/message.h>
+#include <iprt/rand.h>
+#include <iprt/test.h>
+
+#include "vkatInternal.h"
+
+
+/*********************************************************************************************************************************
+* Command: backends *
+*********************************************************************************************************************************/
+
+/**
+ * Options for 'backends'.
+ */
+static const RTGETOPTDEF g_aCmdBackendsOptions[] =
+{
+ { "--dummy", 'd', RTGETOPT_REQ_NOTHING }, /* just a placeholder */
+};
+
+
+/** The 'backends' command option help. */
+static DECLCALLBACK(const char *) audioTestCmdBackendsHelp(PCRTGETOPTDEF pOpt)
+{
+ RT_NOREF(pOpt);
+ return NULL;
+}
+
+/**
+ * The 'backends' command handler.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioTestCmdBackendsHandler(PRTGETOPTSTATE pGetState)
+{
+ /*
+ * Parse options.
+ */
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdBackends);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * List the backends.
+ */
+ RTPrintf("Backends (%u):\n", g_cBackends);
+ for (size_t i = 0; i < g_cBackends; i++)
+ RTPrintf(" %12s - %s\n", g_aBackends[i].pszName, g_aBackends[i].pDrvReg->pszDescription);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Command table entry for 'backends'.
+ */
+const VKATCMD g_CmdBackends =
+{
+ /* .pszCommand = */ "backends",
+ /* .pfnHandler = */ audioTestCmdBackendsHandler,
+ /* .pszDesc = */ "Lists the compiled in audio backends.",
+ /* .paOptions = */ g_aCmdBackendsOptions,
+ /* .cOptions = */ 0 /*RT_ELEMENTS(g_aCmdBackendsOptions)*/,
+ /* .pfnOptionHelp = */ audioTestCmdBackendsHelp,
+ /* .fNeedsTransport = */ false
+};
+
+
+/*********************************************************************************************************************************
+* Command: enum *
+*********************************************************************************************************************************/
+
+
+
+/**
+ * Long option values for the 'enum' command.
+ */
+enum
+{
+ VKAT_ENUM_OPT_PROBE_BACKENDS = 900
+};
+
+/**
+ * Options for 'enum'.
+ */
+static const RTGETOPTDEF g_aCmdEnumOptions[] =
+{
+ { "--backend", 'b', RTGETOPT_REQ_STRING },
+ { "--probe-backends", VKAT_ENUM_OPT_PROBE_BACKENDS, RTGETOPT_REQ_NOTHING }
+};
+
+
+/** The 'enum' command option help. */
+static DECLCALLBACK(const char *) audioTestCmdEnumHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case 'b': return "The audio backend to use";
+ case VKAT_ENUM_OPT_PROBE_BACKENDS: return "Probes all (available) backends until a working one is found";
+ default: return NULL;
+ }
+}
+
+/**
+ * The 'enum' command handler.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioTestCmdEnumHandler(PRTGETOPTSTATE pGetState)
+{
+ /*
+ * Parse options.
+ */
+ /* Option values: */
+ PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
+ bool fProbeBackends = false;
+
+ /* Argument processing loop: */
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'b':
+ pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
+ if (pDrvReg == NULL)
+ return RTEXITCODE_SYNTAX;
+ break;
+
+ case VKAT_ENUM_OPT_PROBE_BACKENDS:
+ fProbeBackends = true;
+ break;
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdEnum);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ int rc;
+
+ AUDIOTESTDRVSTACK DrvStack;
+ if (fProbeBackends)
+ rc = audioTestDriverStackProbe(&DrvStack, pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, false /* fWithDrvAudio */);
+ else
+ rc = audioTestDriverStackInitEx(&DrvStack, pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, false /* fWithDrvAudio */);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to init driver stack: %Rrc\n", rc);
+
+ /*
+ * Do the enumeration.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+
+ if (DrvStack.pIHostAudio->pfnGetDevices)
+ {
+ PDMAUDIOHOSTENUM Enum;
+ rc = DrvStack.pIHostAudio->pfnGetDevices(DrvStack.pIHostAudio, &Enum);
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("Found %u device%s\n", Enum.cDevices, Enum.cDevices != 1 ? "s" : "");
+
+ PPDMAUDIOHOSTDEV pHostDev;
+ RTListForEach(&Enum.LstDevices, pHostDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ RTPrintf("\nDevice \"%s\":\n", pHostDev->pszName);
+
+ char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
+ if (pHostDev->cMaxInputChannels && !pHostDev->cMaxOutputChannels && pHostDev->enmUsage == PDMAUDIODIR_IN)
+ RTPrintf(" Input: max %u channels (%s)\n",
+ pHostDev->cMaxInputChannels, PDMAudioHostDevFlagsToString(szFlags, pHostDev->fFlags));
+ else if (!pHostDev->cMaxInputChannels && pHostDev->cMaxOutputChannels && pHostDev->enmUsage == PDMAUDIODIR_OUT)
+ RTPrintf(" Output: max %u channels (%s)\n",
+ pHostDev->cMaxOutputChannels, PDMAudioHostDevFlagsToString(szFlags, pHostDev->fFlags));
+ else
+ RTPrintf(" %s: max %u output channels, max %u input channels (%s)\n",
+ PDMAudioDirGetName(pHostDev->enmUsage), pHostDev->cMaxOutputChannels,
+ pHostDev->cMaxInputChannels, PDMAudioHostDevFlagsToString(szFlags, pHostDev->fFlags));
+
+ if (pHostDev->pszId && *pHostDev->pszId)
+ RTPrintf(" ID: \"%s\"\n", pHostDev->pszId);
+ }
+
+ PDMAudioHostEnumDelete(&Enum);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Enumeration failed: %Rrc\n", rc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Enumeration not supported by backend '%s'\n", pDrvReg->szName);
+ audioTestDriverStackDelete(&DrvStack);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Command table entry for 'enum'.
+ */
+const VKATCMD g_CmdEnum =
+{
+ "enum",
+ audioTestCmdEnumHandler,
+ "Enumerates audio devices.",
+ g_aCmdEnumOptions,
+ RT_ELEMENTS(g_aCmdEnumOptions),
+ audioTestCmdEnumHelp,
+ false /* fNeedsTransport */
+};
+
+
+
+
+/*********************************************************************************************************************************
+* Command: play *
+*********************************************************************************************************************************/
+
+/**
+ * Worker for audioTestPlayOne implementing the play loop.
+ */
+static RTEXITCODE audioTestPlayOneInner(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTWAVEFILE pWaveFile,
+ PCPDMAUDIOSTREAMCFG pCfgAcq, const char *pszFile)
+{
+ uint32_t const cbPreBuffer = PDMAudioPropsFramesToBytes(pMix->pProps, pCfgAcq->Backend.cFramesPreBuffering);
+ uint64_t const nsStarted = RTTimeNanoTS();
+ uint64_t nsDonePreBuffering = 0;
+
+ /*
+ * Transfer data as quickly as we're allowed.
+ */
+ uint8_t abSamples[16384];
+ uint32_t const cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
+ uint64_t offStream = 0;
+ while (!g_fTerminate)
+ {
+ /* Read a chunk from the wave file. */
+ size_t cbSamples = 0;
+ int rc = AudioTestWaveFileRead(pWaveFile, abSamples, cbSamplesAligned, &cbSamples);
+ if (RT_SUCCESS(rc) && cbSamples > 0)
+ {
+ /* Pace ourselves a little. */
+ if (offStream >= cbPreBuffer)
+ {
+ if (!nsDonePreBuffering)
+ nsDonePreBuffering = RTTimeNanoTS();
+ uint64_t const cNsWritten = PDMAudioPropsBytesToNano64(pMix->pProps, offStream - cbPreBuffer);
+ uint64_t const cNsElapsed = RTTimeNanoTS() - nsStarted;
+ if (cNsWritten > cNsElapsed + RT_NS_10MS)
+ RTThreadSleep((cNsWritten - cNsElapsed - RT_NS_10MS / 2) / RT_NS_1MS);
+ }
+
+ /* Transfer the data to the audio stream. */
+ for (uint32_t offSamples = 0; offSamples < cbSamples;)
+ {
+ uint32_t const cbCanWrite = AudioTestMixStreamGetWritable(pMix);
+ if (cbCanWrite > 0)
+ {
+ uint32_t const cbToPlay = RT_MIN(cbCanWrite, (uint32_t)cbSamples - offSamples);
+ uint32_t cbPlayed = 0;
+ rc = AudioTestMixStreamPlay(pMix, &abSamples[offSamples], cbToPlay, &cbPlayed);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbPlayed)
+ {
+ offSamples += cbPlayed;
+ offStream += cbPlayed;
+ }
+ else
+ return RTMsgErrorExitFailure("Played zero bytes - %#x bytes reported playable!\n", cbCanWrite);
+ }
+ else
+ return RTMsgErrorExitFailure("Failed to play %#x bytes: %Rrc\n", cbToPlay, rc);
+ }
+ else if (AudioTestMixStreamIsOkay(pMix))
+ RTThreadSleep(RT_MIN(RT_MAX(1, pCfgAcq->Device.cMsSchedulingHint), 256));
+ else
+ return RTMsgErrorExitFailure("Stream is not okay!\n");
+ }
+ }
+ else if (RT_SUCCESS(rc) && cbSamples == 0)
+ break;
+ else
+ return RTMsgErrorExitFailure("Error reading wav file '%s': %Rrc", pszFile, rc);
+ }
+
+ /*
+ * Drain the stream.
+ */
+ if (g_uVerbosity > 0)
+ RTMsgInfo("%'RU64 ns: Draining...\n", RTTimeNanoTS() - nsStarted);
+ int rc = AudioTestMixStreamDrain(pMix, true /*fSync*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("%'RU64 ns: Done\n", RTTimeNanoTS() - nsStarted);
+ }
+ else
+ return RTMsgErrorExitFailure("Draining failed: %Rrc", rc);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Worker for audioTestCmdPlayHandler that plays one file.
+ */
+static RTEXITCODE audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, const char *pszDevId,
+ PAUDIOTESTIOOPTS pIoOpts)
+{
+ char szTmp[128];
+
+ /*
+ * First we must open the file and determin the format.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ AUDIOTESTWAVEFILE WaveFile;
+ int rc = AudioTestWaveFileOpen(pszFile, &WaveFile, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
+
+ if (g_uVerbosity > 0)
+ {
+ RTMsgInfo("Opened '%s' for playing\n", pszFile);
+ RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));
+ RTMsgInfo("Size: %'RU32 bytes / %#RX32 / %'RU32 frames / %'RU64 ns\n",
+ WaveFile.cbSamples, WaveFile.cbSamples,
+ PDMAudioPropsBytesToFrames(&WaveFile.Props, WaveFile.cbSamples),
+ PDMAudioPropsBytesToNano(&WaveFile.Props, WaveFile.cbSamples));
+ }
+
+ /*
+ * Construct the driver stack.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+ AUDIOTESTDRVSTACK DrvStack;
+ rc = audioTestDriverStackInit(&DrvStack, pDrvReg, pIoOpts->fWithDrvAudio);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the output device if one is specified.
+ */
+ rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_OUT, pszDevId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open a stream for the output.
+ */
+ uint8_t const cChannels = PDMAudioPropsChannels(&pIoOpts->Props);
+
+ PDMAUDIOPCMPROPS ReqProps = WaveFile.Props;
+ if (cChannels != 0 && PDMAudioPropsChannels(&ReqProps) != cChannels)
+ PDMAudioPropsSetChannels(&ReqProps, cChannels);
+
+ uint8_t const cbSample = PDMAudioPropsSampleSize(&pIoOpts->Props);
+ if (cbSample != 0)
+ PDMAudioPropsSetSampleSize(&ReqProps, cbSample);
+
+ uint32_t const uHz = PDMAudioPropsHz(&pIoOpts->Props);
+ if (uHz != 0)
+ ReqProps.uHz = uHz;
+
+ PDMAUDIOSTREAMCFG CfgAcq;
+ PPDMAUDIOSTREAM pStream = NULL;
+ rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &ReqProps, pIoOpts->cMsBufferSize,
+ pIoOpts->cMsPreBuffer, pIoOpts->cMsSchedulingHint, &pStream, &CfgAcq);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Automatically enable the mixer if the wave file and the
+ * output parameters doesn't match.
+ */
+ if ( !pIoOpts->fWithMixer
+ && ( !PDMAudioPropsAreEqual(&WaveFile.Props, &pStream->Cfg.Props)
+ || pIoOpts->uVolumePercent != 100)
+ )
+ {
+ RTMsgInfo("Enabling the mixer buffer.\n");
+ pIoOpts->fWithMixer = true;
+ }
+
+ /*
+ * Create a mixer wrapper. This is just a thin wrapper if fWithMixer
+ * is false, otherwise it's doing mixing, resampling and recoding.
+ */
+ AUDIOTESTDRVMIXSTREAM Mix;
+ rc = AudioTestMixStreamInit(&Mix, &DrvStack, pStream, pIoOpts->fWithMixer ? &WaveFile.Props : NULL, 100 /*ms*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Stream: %s cbBackend=%#RX32%s\n",
+ PDMAudioPropsToString(&pStream->Cfg.Props, szTmp, sizeof(szTmp)),
+ pStream->cbBackend, pIoOpts->fWithMixer ? " mixed" : "");
+
+ if (pIoOpts->fWithMixer)
+ AudioTestMixStreamSetVolume(&Mix, pIoOpts->uVolumePercent);
+
+ /*
+ * Enable the stream and start playing.
+ */
+ rc = AudioTestMixStreamEnable(&Mix);
+ if (RT_SUCCESS(rc))
+ rcExit = audioTestPlayOneInner(&Mix, &WaveFile, &CfgAcq, pszFile);
+ else
+ rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc);
+
+ /*
+ * Clean up.
+ */
+ AudioTestMixStreamTerm(&Mix);
+ }
+ audioTestDriverStackStreamDestroy(&DrvStack, pStream);
+ pStream = NULL;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
+ audioTestDriverStackDelete(&DrvStack);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
+ AudioTestWaveFileClose(&WaveFile);
+ return rcExit;
+}
+
+/**
+ * Worker for audioTestCmdPlayHandler that plays one test tone.
+ */
+static RTEXITCODE audioTestPlayTestToneOne(PAUDIOTESTTONEPARMS pToneParms,
+ PCPDMDRVREG pDrvReg, const char *pszDevId,
+ PAUDIOTESTIOOPTS pIoOpts)
+{
+ char szTmp[128];
+
+ AUDIOTESTSTREAM TstStream;
+ RT_ZERO(TstStream);
+
+ /*
+ * Construct the driver stack.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+ AUDIOTESTDRVSTACK DrvStack;
+ int rc = audioTestDriverStackInit(&DrvStack, pDrvReg, pIoOpts->fWithDrvAudio);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the output device if one is specified.
+ */
+ rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_OUT, pszDevId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open a stream for the output.
+ */
+ uint8_t const cChannels = PDMAudioPropsChannels(&pIoOpts->Props);
+
+ PDMAUDIOPCMPROPS ReqProps = pToneParms->Props;
+ if (cChannels != 0 && PDMAudioPropsChannels(&ReqProps) != cChannels)
+ PDMAudioPropsSetChannels(&ReqProps, cChannels);
+
+ uint8_t const cbSample = PDMAudioPropsSampleSize(&pIoOpts->Props);
+ if (cbSample != 0)
+ PDMAudioPropsSetSampleSize(&ReqProps, cbSample);
+
+ uint32_t const uHz = PDMAudioPropsHz(&pIoOpts->Props);
+ if (uHz != 0)
+ ReqProps.uHz = uHz;
+
+ rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &ReqProps, pIoOpts->cMsBufferSize,
+ pIoOpts->cMsPreBuffer, pIoOpts->cMsSchedulingHint, &TstStream.pStream, &TstStream.Cfg);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Automatically enable the mixer if the wave file and the
+ * output parameters doesn't match.
+ */
+ if ( !pIoOpts->fWithMixer
+ && ( !PDMAudioPropsAreEqual(&pToneParms->Props, &TstStream.pStream->Cfg.Props)
+ || pToneParms->uVolumePercent != 100)
+ )
+ {
+ RTMsgInfo("Enabling the mixer buffer.\n");
+ pIoOpts->fWithMixer = true;
+ }
+
+ /*
+ * Create a mixer wrapper. This is just a thin wrapper if fWithMixer
+ * is false, otherwise it's doing mixing, resampling and recoding.
+ */
+ rc = AudioTestMixStreamInit(&TstStream.Mix, &DrvStack, TstStream.pStream,
+ pIoOpts->fWithMixer ? &pToneParms->Props : NULL, 100 /*ms*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Stream: %s cbBackend=%#RX32%s\n",
+ PDMAudioPropsToString(&TstStream.pStream->Cfg.Props, szTmp, sizeof(szTmp)),
+ TstStream.pStream->cbBackend, pIoOpts->fWithMixer ? " mixed" : "");
+
+ /*
+ * Enable the stream and start playing.
+ */
+ rc = AudioTestMixStreamEnable(&TstStream.Mix);
+ if (RT_SUCCESS(rc))
+ {
+ if (pIoOpts->fWithMixer)
+ AudioTestMixStreamSetVolume(&TstStream.Mix, pToneParms->uVolumePercent);
+
+ rc = audioTestPlayTone(pIoOpts, NULL /* pTstEnv */, &TstStream, pToneParms);
+ if (RT_SUCCESS(rc))
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc);
+
+ /*
+ * Clean up.
+ */
+ AudioTestMixStreamTerm(&TstStream.Mix);
+ }
+ audioTestDriverStackStreamDestroy(&DrvStack, TstStream.pStream);
+ TstStream.pStream = NULL;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
+ audioTestDriverStackDelete(&DrvStack);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
+ return rcExit;
+}
+
+
+/**
+ * Long option values for the 'play' command.
+ */
+enum
+{
+ VKAT_PLAY_OPT_TONE_DUR = 900,
+ VKAT_PLAY_OPT_TONE_FREQ,
+ VKAT_PLAY_OPT_TONE_VOL,
+ VKAT_PLAY_OPT_VOL
+};
+
+
+/**
+ * Options for 'play'.
+ */
+static const RTGETOPTDEF g_aCmdPlayOptions[] =
+{
+ { "--backend", 'b', RTGETOPT_REQ_STRING },
+ { "--channels", 'c', RTGETOPT_REQ_UINT8 },
+ { "--hz", 'f', RTGETOPT_REQ_UINT32 },
+ { "--frequency", 'f', RTGETOPT_REQ_UINT32 },
+ { "--sample-size", 'z', RTGETOPT_REQ_UINT8 },
+ { "--test-tone", 't', RTGETOPT_REQ_NOTHING },
+ { "--tone-dur", VKAT_PLAY_OPT_TONE_DUR, RTGETOPT_REQ_UINT32 },
+ { "--tone-freq", VKAT_PLAY_OPT_TONE_FREQ, RTGETOPT_REQ_UINT32 },
+ { "--tone-vol", VKAT_PLAY_OPT_TONE_VOL, RTGETOPT_REQ_UINT32 },
+ { "--output-device", 'o', RTGETOPT_REQ_STRING },
+ { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },
+ { "--with-mixer", 'm', RTGETOPT_REQ_NOTHING },
+ { "--vol", VKAT_PLAY_OPT_VOL, RTGETOPT_REQ_UINT8 }
+};
+
+
+/** The 'play' command option help. */
+static DECLCALLBACK(const char *) audioTestCmdPlayHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case 'b': return "The audio backend to use";
+ case 'c': return "Number of backend output channels";
+ case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
+ case 'f': return "Output frequency (Hz)";
+ case 'z': return "Output sample size (bits)";
+ case 't': return "Plays a test tone. Can be specified multiple times";
+ case 'm': return "Go via the mixer";
+ case 'o': return "The ID of the output device to use";
+ case VKAT_PLAY_OPT_TONE_DUR: return "Test tone duration (ms)";
+ case VKAT_PLAY_OPT_TONE_FREQ: return "Test tone frequency (Hz)";
+ case VKAT_PLAY_OPT_TONE_VOL: return "Test tone volume (percent)";
+ case VKAT_PLAY_OPT_VOL: return "Playback volume (percent)";
+ default: return NULL;
+ }
+}
+
+
+/**
+ * The 'play' command handler.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioTestCmdPlayHandler(PRTGETOPTSTATE pGetState)
+{
+ /* Option values: */
+ PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
+ const char *pszDevId = NULL;
+ uint32_t cTestTones = 0;
+ uint8_t cbSample = 0;
+ uint8_t cChannels = 0;
+ uint32_t uHz = 0;
+
+ AUDIOTESTIOOPTS IoOpts;
+ audioTestIoOptsInitDefaults(&IoOpts);
+
+ AUDIOTESTTONEPARMS ToneParms;
+ audioTestToneParmsInit(&ToneParms);
+
+ /* Argument processing loop: */
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'b':
+ pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
+ if (pDrvReg == NULL)
+ return RTEXITCODE_SYNTAX;
+ break;
+
+ case 'c':
+ cChannels = ValueUnion.u8;
+ break;
+
+ case 'd':
+ IoOpts.fWithDrvAudio = true;
+ break;
+
+ case 'f':
+ uHz = ValueUnion.u32;
+ break;
+
+ case 'm':
+ IoOpts.fWithMixer = true;
+ break;
+
+ case 'o':
+ pszDevId = ValueUnion.psz;
+ break;
+
+ case 't':
+ cTestTones++;
+ break;
+
+ case 'z':
+ cbSample = ValueUnion.u8 / 8;
+ break;
+
+ case VKAT_PLAY_OPT_TONE_DUR:
+ ToneParms.msDuration = ValueUnion.u32;
+ break;
+
+ case VKAT_PLAY_OPT_TONE_FREQ:
+ ToneParms.dbFreqHz = ValueUnion.u32;
+ break;
+
+ case VKAT_PLAY_OPT_TONE_VOL:
+ ToneParms.uVolumePercent = ValueUnion.u8;
+ if (ToneParms.uVolumePercent > 100)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid tonevolume (0-100)");
+ break;
+
+ case VKAT_PLAY_OPT_VOL:
+ IoOpts.uVolumePercent = ValueUnion.u8;
+ if (IoOpts.uVolumePercent > 100)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid playback volume (0-100)");
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ if (cTestTones)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Playing test tones (-t) cannot be combined with playing files");
+
+ /* Set new (override standard) I/O PCM properties if set by the user. */
+ PDMAudioPropsInit(&IoOpts.Props,
+ cbSample ? cbSample : 2 /* 16-bit */, true /* fSigned */,
+ cChannels ? cChannels : 2 /* Stereo */, uHz ? uHz : 44100);
+
+ RTEXITCODE rcExit = audioTestPlayOne(ValueUnion.psz, pDrvReg, pszDevId, &IoOpts);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ break;
+ }
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdPlay);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ while (cTestTones--)
+ {
+ /* Use some sane defaults if no PCM props are set by the user. */
+ PDMAudioPropsInit(&ToneParms.Props,
+ cbSample ? cbSample : 2 /* 16-bit */, true /* fSigned */,
+ cChannels ? cChannels : 2 /* Stereo */, uHz ? uHz : 44100);
+
+ RTEXITCODE rcExit = audioTestPlayTestToneOne(&ToneParms, pDrvReg, pszDevId, &IoOpts);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Command table entry for 'play'.
+ */
+const VKATCMD g_CmdPlay =
+{
+ "play",
+ audioTestCmdPlayHandler,
+ "Plays one or more wave files.",
+ g_aCmdPlayOptions,
+ RT_ELEMENTS(g_aCmdPlayOptions),
+ audioTestCmdPlayHelp,
+ false /* fNeedsTransport */
+};
+
+
+/*********************************************************************************************************************************
+* Command: rec *
+*********************************************************************************************************************************/
+
+/**
+ * Worker for audioTestRecOne implementing the recording loop.
+ */
+static RTEXITCODE audioTestRecOneInner(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTWAVEFILE pWaveFile,
+ PCPDMAUDIOSTREAMCFG pCfgAcq, uint64_t cMaxFrames, const char *pszFile)
+{
+ int rc;
+ uint64_t const nsStarted = RTTimeNanoTS();
+
+ /*
+ * Transfer data as quickly as we're allowed.
+ */
+ uint8_t abSamples[16384];
+ uint32_t const cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
+ uint64_t cFramesCapturedTotal = 0;
+ while (!g_fTerminate && cFramesCapturedTotal < cMaxFrames)
+ {
+ /*
+ * Anything we can read?
+ */
+ uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
+ if (cbCanRead)
+ {
+ uint32_t const cbToRead = RT_MIN(cbCanRead, cbSamplesAligned);
+ uint32_t cbCaptured = 0;
+ rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbCaptured);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbCaptured)
+ {
+ uint32_t cFramesCaptured = PDMAudioPropsBytesToFrames(pMix->pProps, cbCaptured);
+ if (cFramesCaptured + cFramesCaptured < cMaxFrames)
+ { /* likely */ }
+ else
+ {
+ cFramesCaptured = cMaxFrames - cFramesCaptured;
+ cbCaptured = PDMAudioPropsFramesToBytes(pMix->pProps, cFramesCaptured);
+ }
+
+ rc = AudioTestWaveFileWrite(pWaveFile, abSamples, cbCaptured);
+ if (RT_SUCCESS(rc))
+ cFramesCapturedTotal += cFramesCaptured;
+ else
+ return RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszFile, rc);
+ }
+ else
+ return RTMsgErrorExitFailure("Captured zero bytes - %#x bytes reported readable!\n", cbCanRead);
+ }
+ else
+ return RTMsgErrorExitFailure("Failed to capture %#x bytes: %Rrc (%#x available)\n", cbToRead, rc, cbCanRead);
+ }
+ else if (AudioTestMixStreamIsOkay(pMix))
+ RTThreadSleep(RT_MIN(RT_MAX(1, pCfgAcq->Device.cMsSchedulingHint), 256));
+ else
+ return RTMsgErrorExitFailure("Stream is not okay!\n");
+ }
+
+ /*
+ * Disable the stream.
+ */
+ rc = AudioTestMixStreamDisable(pMix);
+ if (RT_SUCCESS(rc) && g_uVerbosity > 0)
+ RTMsgInfo("%'RU64 ns: Stopped after recording %RU64 frames%s\n", RTTimeNanoTS() - nsStarted, cFramesCapturedTotal,
+ g_fTerminate ? " - Ctrl-C" : ".");
+ else if (RT_FAILURE(rc))
+ return RTMsgErrorExitFailure("Disabling stream failed: %Rrc", rc);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Worker for audioTestCmdRecHandler that recs one file.
+ */
+static RTEXITCODE audioTestRecOne(const char *pszFile, uint8_t cWaveChannels, uint8_t cbWaveSample, uint32_t uWaveHz,
+ PCPDMDRVREG pDrvReg, const char *pszDevId, PAUDIOTESTIOOPTS pIoOpts,
+ uint64_t cMaxFrames, uint64_t cNsMaxDuration)
+{
+ /*
+ * Construct the driver stack.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+ AUDIOTESTDRVSTACK DrvStack;
+ int rc = audioTestDriverStackInit(&DrvStack, pDrvReg, pIoOpts->fWithDrvAudio);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the input device if one is specified.
+ */
+ rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_IN, pszDevId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create an input stream.
+ */
+ PDMAUDIOPCMPROPS ReqProps;
+ PDMAudioPropsInit(&ReqProps,
+ pIoOpts->Props.cbSampleX ? pIoOpts->Props.cbSampleX : cbWaveSample ? cbWaveSample : 2,
+ pIoOpts->Props.fSigned,
+ pIoOpts->Props.cChannelsX ? pIoOpts->Props.cChannelsX : cWaveChannels ? cWaveChannels : 2,
+ pIoOpts->Props.uHz ? pIoOpts->Props.uHz : uWaveHz ? uWaveHz : 44100);
+
+ PDMAUDIOSTREAMCFG CfgAcq;
+ PPDMAUDIOSTREAM pStream = NULL;
+ rc = audioTestDriverStackStreamCreateInput(&DrvStack, &ReqProps, pIoOpts->cMsBufferSize,
+ pIoOpts->cMsPreBuffer, pIoOpts->cMsSchedulingHint, &pStream, &CfgAcq);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Determine the wave file properties. If it differs from the stream
+ * properties, make sure the mixer is enabled.
+ */
+ PDMAUDIOPCMPROPS WaveProps;
+ PDMAudioPropsInit(&WaveProps,
+ cbWaveSample ? cbWaveSample : PDMAudioPropsSampleSize(&CfgAcq.Props),
+ true /*fSigned*/,
+ cWaveChannels ? cWaveChannels : PDMAudioPropsChannels(&CfgAcq.Props),
+ uWaveHz ? uWaveHz : PDMAudioPropsHz(&CfgAcq.Props));
+ if (!pIoOpts->fWithMixer && !PDMAudioPropsAreEqual(&WaveProps, &CfgAcq.Props))
+ {
+ RTMsgInfo("Enabling the mixer buffer.\n");
+ pIoOpts->fWithMixer = true;
+ }
+
+ /* Console the max duration into frames now that we've got the wave file format. */
+ if (cMaxFrames != UINT64_MAX && cNsMaxDuration != UINT64_MAX)
+ {
+ uint64_t cMaxFrames2 = PDMAudioPropsNanoToBytes64(&WaveProps, cNsMaxDuration);
+ cMaxFrames = RT_MAX(cMaxFrames, cMaxFrames2);
+ }
+ else if (cNsMaxDuration != UINT64_MAX)
+ cMaxFrames = PDMAudioPropsNanoToBytes64(&WaveProps, cNsMaxDuration);
+
+ /*
+ * Create a mixer wrapper. This is just a thin wrapper if fWithMixer
+ * is false, otherwise it's doing mixing, resampling and recoding.
+ */
+ AUDIOTESTDRVMIXSTREAM Mix;
+ rc = AudioTestMixStreamInit(&Mix, &DrvStack, pStream, pIoOpts->fWithMixer ? &WaveProps : NULL, 100 /*ms*/);
+ if (RT_SUCCESS(rc))
+ {
+ char szTmp[128];
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Stream: %s cbBackend=%#RX32%s\n",
+ PDMAudioPropsToString(&pStream->Cfg.Props, szTmp, sizeof(szTmp)),
+ pStream->cbBackend, pIoOpts->fWithMixer ? " mixed" : "");
+
+ /*
+ * Open the wave output file.
+ */
+ AUDIOTESTWAVEFILE WaveFile;
+ RTERRINFOSTATIC ErrInfo;
+ rc = AudioTestWaveFileCreate(pszFile, &WaveProps, &WaveFile, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ {
+ RTMsgInfo("Opened '%s' for playing\n", pszFile);
+ RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));
+ }
+
+ /*
+ * Enable the stream and start recording.
+ */
+ rc = AudioTestMixStreamEnable(&Mix);
+ if (RT_SUCCESS(rc))
+ rcExit = audioTestRecOneInner(&Mix, &WaveFile, &CfgAcq, cMaxFrames, pszFile);
+ else
+ rcExit = RTMsgErrorExitFailure("Enabling the input stream failed: %Rrc", rc);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ AudioTestMixStreamDisable(&Mix);
+
+ /*
+ * Clean up.
+ */
+ rc = AudioTestWaveFileClose(&WaveFile);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszFile, rc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core.pszMsg);
+
+ AudioTestMixStreamTerm(&Mix);
+ }
+ audioTestDriverStackStreamDestroy(&DrvStack, pStream);
+ pStream = NULL;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
+ audioTestDriverStackDelete(&DrvStack);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
+ return rcExit;
+}
+
+
+/**
+ * Options for 'rec'.
+ */
+static const RTGETOPTDEF g_aCmdRecOptions[] =
+{
+ { "--backend", 'b', RTGETOPT_REQ_STRING },
+ { "--channels", 'c', RTGETOPT_REQ_UINT8 },
+ { "--hz", 'f', RTGETOPT_REQ_UINT32 },
+ { "--frequency", 'f', RTGETOPT_REQ_UINT32 },
+ { "--sample-size", 'z', RTGETOPT_REQ_UINT8 },
+ { "--input-device", 'i', RTGETOPT_REQ_STRING },
+ { "--wav-channels", 'C', RTGETOPT_REQ_UINT8 },
+ { "--wav-hz", 'F', RTGETOPT_REQ_UINT32 },
+ { "--wav-frequency", 'F', RTGETOPT_REQ_UINT32 },
+ { "--wav-sample-size", 'Z', RTGETOPT_REQ_UINT8 },
+ { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },
+ { "--with-mixer", 'm', RTGETOPT_REQ_NOTHING },
+ { "--max-frames", 'r', RTGETOPT_REQ_UINT64 },
+ { "--max-sec", 's', RTGETOPT_REQ_UINT64 },
+ { "--max-seconds", 's', RTGETOPT_REQ_UINT64 },
+ { "--max-ms", 't', RTGETOPT_REQ_UINT64 },
+ { "--max-milliseconds", 't', RTGETOPT_REQ_UINT64 },
+ { "--max-ns", 'T', RTGETOPT_REQ_UINT64 },
+ { "--max-nanoseconds", 'T', RTGETOPT_REQ_UINT64 },
+};
+
+
+/** The 'rec' command option help. */
+static DECLCALLBACK(const char *) audioTestCmdRecHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case 'b': return "The audio backend to use.";
+ case 'c': return "Number of backend input channels";
+ case 'C': return "Number of wave-file channels";
+ case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";
+ case 'f': return "Input frequency (Hz)";
+ case 'F': return "Wave-file frequency (Hz)";
+ case 'z': return "Input sample size (bits)";
+ case 'Z': return "Wave-file sample size (bits)";
+ case 'm': return "Go via the mixer.";
+ case 'i': return "The ID of the input device to use.";
+ case 'r': return "Max recording duration in frames.";
+ case 's': return "Max recording duration in seconds.";
+ case 't': return "Max recording duration in milliseconds.";
+ case 'T': return "Max recording duration in nanoseconds.";
+ default: return NULL;
+ }
+}
+
+
+/**
+ * The 'rec' command handler.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+static DECLCALLBACK(RTEXITCODE) audioTestCmdRecHandler(PRTGETOPTSTATE pGetState)
+{
+ /* Option values: */
+ PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
+ const char *pszDevId = NULL;
+ uint8_t cbSample = 0;
+ uint8_t cChannels = 0;
+ uint32_t uHz = 0;
+ uint8_t cbWaveSample = 0;
+ uint8_t cWaveChannels = 0;
+ uint32_t uWaveHz = 0;
+ uint64_t cMaxFrames = UINT64_MAX;
+ uint64_t cNsMaxDuration = UINT64_MAX;
+
+ AUDIOTESTIOOPTS IoOpts;
+ audioTestIoOptsInitDefaults(&IoOpts);
+
+ /* Argument processing loop: */
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ while ((ch = RTGetOpt(pGetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'b':
+ pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
+ if (pDrvReg == NULL)
+ return RTEXITCODE_SYNTAX;
+ break;
+
+ case 'c':
+ cChannels = ValueUnion.u8;
+ break;
+
+ case 'C':
+ cWaveChannels = ValueUnion.u8;
+ break;
+
+ case 'd':
+ IoOpts.fWithDrvAudio = true;
+ break;
+
+ case 'f':
+ uHz = ValueUnion.u32;
+ break;
+
+ case 'F':
+ uWaveHz = ValueUnion.u32;
+ break;
+
+ case 'i':
+ pszDevId = ValueUnion.psz;
+ break;
+
+ case 'm':
+ IoOpts.fWithMixer = true;
+ break;
+
+ case 'r':
+ cMaxFrames = ValueUnion.u64;
+ break;
+
+ case 's':
+ cNsMaxDuration = ValueUnion.u64 >= UINT64_MAX / RT_NS_1SEC ? UINT64_MAX : ValueUnion.u64 * RT_NS_1SEC;
+ break;
+
+ case 't':
+ cNsMaxDuration = ValueUnion.u64 >= UINT64_MAX / RT_NS_1MS ? UINT64_MAX : ValueUnion.u64 * RT_NS_1MS;
+ break;
+
+ case 'T':
+ cNsMaxDuration = ValueUnion.u64;
+ break;
+
+ case 'z':
+ cbSample = ValueUnion.u8 / 8;
+ break;
+
+ case 'Z':
+ cbWaveSample = ValueUnion.u8 / 8;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ if ( cbSample
+ || cChannels
+ || uHz)
+ {
+ /* Set new (override standard) I/O PCM properties if set by the user. */
+ PDMAudioPropsInit(&IoOpts.Props,
+ cbSample ? cbSample : 2 /* 16-bit */, true /* fSigned */,
+ cChannels ? cChannels : 2 /* Stereo */, uHz ? uHz : 44100);
+ }
+
+ RTEXITCODE rcExit = audioTestRecOne(ValueUnion.psz, cWaveChannels, cbWaveSample, uWaveHz,
+ pDrvReg, pszDevId, &IoOpts,
+ cMaxFrames, cNsMaxDuration);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ break;
+ }
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdRec);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Command table entry for 'rec'.
+ */
+const VKATCMD g_CmdRec =
+{
+ "rec",
+ audioTestCmdRecHandler,
+ "Records audio to a wave file.",
+ g_aCmdRecOptions,
+ RT_ELEMENTS(g_aCmdRecOptions),
+ audioTestCmdRecHelp,
+ false /* fNeedsTransport */
+};
+
diff --git a/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp b/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp
new file mode 100644
index 00000000..3259398d
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp
@@ -0,0 +1,481 @@
+/* $Id: vkatCmdSelfTest.cpp $ */
+/** @file
+ * Validation Kit Audio Test (VKAT) - Self test.
+ *
+ * Self-test which does a complete audio testing framework run without the need
+ * of a VM or other infrastructure, i.e. all required parts are running locally
+ * on the same machine.
+ *
+ * This self-test does the following:
+ * - 1. Creates a separate thread for the guest side VKAT and connects to the ATS instance on
+ * the host side at port 6052 (ATS_TCP_DEF_BIND_PORT_HOST).
+ * - 2. Uses the Validation Kit audio backend, which in turn creates an ATS instance
+ * listening at port 6062 (ATS_TCP_DEF_BIND_PORT_VALKIT).
+ * - 3. Uses the host test environment which creates an ATS instance
+ * listening at port 6052 (ATS_TCP_DEF_BIND_PORT_HOST).
+ * - 4. Executes a complete test run locally (e.g. without any guest (VM) involved).
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/rand.h>
+#include <iprt/test.h>
+
+#include "Audio/AudioHlp.h"
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "vkatInternal.h"
+
+
+/*********************************************************************************************************************************
+* Internal structures *
+*********************************************************************************************************************************/
+
+/**
+ * Structure for keeping a VKAT self test context.
+ */
+typedef struct SELFTESTCTX
+{
+ /** Common tag for guest and host side. */
+ char szTag[AUDIOTEST_TAG_MAX];
+ /** The driver stack in use. */
+ AUDIOTESTDRVSTACK DrvStack;
+ /** Audio driver to use.
+ * Defaults to the platform's default driver. */
+ PCPDMDRVREG pDrvReg;
+ struct
+ {
+ AUDIOTESTENV TstEnv;
+ /** Where to bind the address of the guest ATS instance to.
+ * Defaults to localhost (127.0.0.1) if empty. */
+ char szAtsAddr[64];
+ /** Port of the guest ATS instance.
+ * Defaults to ATS_ALT_PORT if not set. */
+ uint32_t uAtsPort;
+ } Guest;
+ struct
+ {
+ AUDIOTESTENV TstEnv;
+ /** Address of the guest ATS instance.
+ * Defaults to localhost (127.0.0.1) if not set. */
+ char szGuestAtsAddr[64];
+ /** Port of the guest ATS instance.
+ * Defaults to ATS_DEFAULT_PORT if not set. */
+ uint32_t uGuestAtsPort;
+ /** Address of the Validation Kit audio driver ATS instance.
+ * Defaults to localhost (127.0.0.1) if not set. */
+ char szValKitAtsAddr[64];
+ /** Port of the Validation Kit audio driver ATS instance.
+ * Defaults to ATS_ALT_PORT if not set. */
+ uint32_t uValKitAtsPort;
+ } Host;
+} SELFTESTCTX;
+/** Pointer to a VKAT self test context. */
+typedef SELFTESTCTX *PSELFTESTCTX;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** The global self-text context. */
+static SELFTESTCTX g_Ctx;
+
+
+/*********************************************************************************************************************************
+* Driver stack self-test implementation *
+*********************************************************************************************************************************/
+
+/**
+ * Performs a (quick) audio driver stack self test.
+ *
+ * Local only, no guest/host communication involved.
+ *
+ * @returns VBox status code.
+ */
+int AudioTestDriverStackPerformSelftest(void)
+{
+ PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Testing driver stack started\n");
+
+ AUDIOTESTDRVSTACK DrvStack;
+ int rc = audioTestDriverStackProbe(&DrvStack, pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, false /* fWithDrvAudio */);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ AUDIOTESTIOOPTS IoOpts;
+ audioTestIoOptsInitDefaults(&IoOpts);
+
+ PPDMAUDIOSTREAM pStream;
+ PDMAUDIOSTREAMCFG CfgAcq;
+ rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &IoOpts.Props,
+ IoOpts.cMsBufferSize, IoOpts.cMsPreBuffer, IoOpts.cMsSchedulingHint,
+ &pStream, &CfgAcq);
+ AssertRCReturn(rc, rc);
+
+ rc = audioTestDriverStackStreamEnable(&DrvStack, pStream);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ RTTEST_CHECK_RET(g_hTest, audioTestDriverStackStreamIsOkay(&DrvStack, pStream), VERR_AUDIO_STREAM_NOT_READY);
+
+ uint8_t abBuf[_4K];
+ memset(abBuf, 0x42, sizeof(abBuf));
+
+ uint32_t cbWritten;
+ rc = audioTestDriverStackStreamPlay(&DrvStack, pStream, abBuf, sizeof(abBuf), &cbWritten);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+ RTTEST_CHECK_RET(g_hTest, cbWritten == sizeof(abBuf), VERR_AUDIO_STREAM_NOT_READY);
+
+ audioTestDriverStackStreamDrain(&DrvStack, pStream, true /* fSync */);
+ audioTestDriverStackStreamDestroy(&DrvStack, pStream);
+
+ audioTestDriverStackDelete(&DrvStack);
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Testing driver stack ended with %Rrc\n", rc);
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Self-test implementation *
+*********************************************************************************************************************************/
+
+/**
+ * Thread callback for mocking the guest (VM) side of things.
+ *
+ * @returns VBox status code.
+ * @param hThread Thread handle.
+ * @param pvUser Pointer to user-supplied data.
+ */
+static DECLCALLBACK(int) audioTestSelftestGuestAtsThread(RTTHREAD hThread, void *pvUser)
+{
+ RT_NOREF(hThread);
+ PSELFTESTCTX pCtx = (PSELFTESTCTX)pvUser;
+
+ PAUDIOTESTENV pTstEnvGst = &pCtx->Guest.TstEnv;
+
+ audioTestEnvInit(pTstEnvGst);
+
+ /* Flag the environment for self test mode. */
+ pTstEnvGst->fSelftest = true;
+
+ /* Tweak the address the guest ATS is trying to connect to the host if anything else is specified.
+ * Note: The host also runs on the same host (this self-test is completely self-contained and does not need a VM). */
+ if (!pTstEnvGst->TcpOpts.szConnectAddr[0])
+ RTStrCopy(pTstEnvGst->TcpOpts.szConnectAddr, sizeof(pTstEnvGst->TcpOpts.szConnectAddr), "127.0.0.1");
+
+ /* Generate tag for guest side. */
+ int rc = RTStrCopy(pTstEnvGst->szTag, sizeof(pTstEnvGst->szTag), pCtx->szTag);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ rc = AudioTestPathCreateTemp(pTstEnvGst->szPathTemp, sizeof(pTstEnvGst->szPathTemp), "selftest-guest");
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ rc = AudioTestPathCreateTemp(pTstEnvGst->szPathOut, sizeof(pTstEnvGst->szPathOut), "selftest-out");
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ pTstEnvGst->enmMode = AUDIOTESTMODE_GUEST;
+
+ rc = audioTestEnvCreate(pTstEnvGst, &pCtx->DrvStack);
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadUserSignal(hThread);
+
+ rc = audioTestWorker(pTstEnvGst);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
+
+ audioTestEnvDestroy(pTstEnvGst);
+ }
+
+ return rc;
+}
+
+/**
+ * Main function for performing the self test.
+ *
+ * @returns RTEXITCODE
+ * @param pCtx Self test context to use.
+ */
+RTEXITCODE audioTestDoSelftest(PSELFTESTCTX pCtx)
+{
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Running self test ...\n");
+
+ /* Generate a common tag for guest and host side. */
+ int rc = AudioTestGenTag(pCtx->szTag, sizeof(pCtx->szTag));
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
+
+ PAUDIOTESTENV pTstEnvHst = &pCtx->Host.TstEnv;
+
+ audioTestEnvInit(pTstEnvHst);
+
+ /* Flag the environment for self test mode. */
+ pTstEnvHst->fSelftest = true;
+
+ /* One test iteration with a 5s maximum test tone is enough for a (quick) self test. */
+ pTstEnvHst->cIterations = 1;
+ pTstEnvHst->ToneParms.msDuration = RTRandU32Ex(500, RT_MS_5SEC);
+
+ /* Generate tag for host side. */
+ rc = RTStrCopy(pTstEnvHst->szTag, sizeof(pTstEnvHst->szTag), pCtx->szTag);
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
+
+ rc = AudioTestPathCreateTemp(pTstEnvHst->szPathTemp, sizeof(pTstEnvHst->szPathTemp), "selftest-tmp");
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
+
+ rc = AudioTestPathCreateTemp(pTstEnvHst->szPathOut, sizeof(pTstEnvHst->szPathOut), "selftest-out");
+ RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
+
+ /*
+ * Step 1.
+ */
+ RTTHREAD hThreadGstAts = NIL_RTTHREAD;
+
+ bool const fStartGuestAts = RTStrNLen(pCtx->Host.szGuestAtsAddr, sizeof(pCtx->Host.szGuestAtsAddr)) == 0;
+ if (fStartGuestAts)
+ {
+ /* Step 1b. */
+ rc = RTThreadCreate(&hThreadGstAts, audioTestSelftestGuestAtsThread, pCtx, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
+ "VKATGstAts");
+ if (RT_SUCCESS(rc))
+ rc = RTThreadUserWait(hThreadGstAts, RT_MS_30SEC);
+ }
+
+ RTThreadSleep(2000); /* Fudge: Wait until guest ATS is up. 2 seconds should be enough (tm). */
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Steps 2 + 3.
+ */
+ pTstEnvHst->enmMode = AUDIOTESTMODE_HOST;
+
+ rc = audioTestEnvCreate(pTstEnvHst, &pCtx->DrvStack);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Step 4.
+ */
+ rc = audioTestWorker(pTstEnvHst);
+ RTTEST_CHECK_RC_OK(g_hTest, rc);
+
+ audioTestEnvDestroy(pTstEnvHst);
+ }
+ }
+
+ /*
+ * Shutting down.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down self test\n");
+
+ /* If we started the guest ATS ourselves, wait for it to terminate properly. */
+ if (fStartGuestAts)
+ {
+ int rcThread;
+ int rc2 = RTThreadWait(hThreadGstAts, RT_MS_30SEC, &rcThread);
+ if (RT_SUCCESS(rc2))
+ rc2 = rcThread;
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Shutting down guest ATS failed with %Rrc\n", rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Self test failed with %Rrc\n", rc);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/*********************************************************************************************************************************
+* Command: selftest *
+*********************************************************************************************************************************/
+
+/**
+ * Command line parameters for self-test mode.
+ */
+static const RTGETOPTDEF s_aCmdSelftestOptions[] =
+{
+ { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
+ { "--backend", 'b', RTGETOPT_REQ_STRING },
+ { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },
+ { "--with-mixer", 'm', RTGETOPT_REQ_NOTHING },
+ { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
+ { "--include", 'i', RTGETOPT_REQ_UINT32 }
+};
+
+/** the 'selftest' command option help. */
+static DECLCALLBACK(const char *) audioTestCmdSelftestHelp(PCRTGETOPTDEF pOpt)
+{
+ switch (pOpt->iShort)
+ {
+ case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";
+ case 'b': return "The audio backend to use";
+ case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
+ case 'e': return "Exclude the given test id from the list";
+ case 'i': return "Include the given test id in the list";
+ case 'm': return "Use the internal mixing engine explicitly";
+ default: return NULL;
+ }
+}
+
+/**
+ * The 'selftest' command handler.
+ *
+ * @returns Program exit code.
+ * @param pGetState RTGetOpt state.
+ */
+DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
+{
+ RT_ZERO(g_Ctx);
+
+ audioTestEnvInit(&g_Ctx.Guest.TstEnv);
+ audioTestEnvInit(&g_Ctx.Host.TstEnv);
+
+ AUDIOTESTIOOPTS IoOpts;
+ audioTestIoOptsInitDefaults(&IoOpts);
+
+ /* Argument processing loop: */
+ int rc;
+ RTGETOPTUNION ValueUnion;
+ while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'a':
+ for (unsigned i = 0; i < g_cTests; i++)
+ g_aTests[i].fExcluded = true;
+ break;
+
+ case 'b':
+ g_Ctx.pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
+ if (g_Ctx.pDrvReg == NULL)
+ return RTEXITCODE_SYNTAX;
+ break;
+
+ case 'd':
+ IoOpts.fWithDrvAudio = true;
+ break;
+
+ case 'e':
+ if (ValueUnion.u32 >= g_cTests)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
+ g_aTests[ValueUnion.u32].fExcluded = true;
+ break;
+
+ case 'i':
+ if (ValueUnion.u32 >= g_cTests)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
+ g_aTests[ValueUnion.u32].fExcluded = false;
+ break;
+
+ case 'm':
+ IoOpts.fWithMixer = true;
+ break;
+
+ AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdSelfTest);
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /* For simplicity both test environments, guest and host, will have the same I/O options.
+ ** @todo Make this indepedent by a prefix, "--[guest|host]-<option>" -> e.g. "--guest-with-drv-audio". */
+ memcpy(&g_Ctx.Guest.TstEnv.IoOpts, &IoOpts, sizeof(AUDIOTESTIOOPTS));
+ memcpy(&g_Ctx.Host.TstEnv.IoOpts, &IoOpts, sizeof(AUDIOTESTIOOPTS));
+
+ rc = AudioTestDriverStackPerformSelftest();
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Testing driver stack failed: %Rrc\n", rc);
+
+ /* Go with the Validation Kit audio backend if nothing else is specified. */
+ if (g_Ctx.pDrvReg == NULL)
+ g_Ctx.pDrvReg = AudioTestFindBackendOpt("valkit");
+
+ /*
+ * In self-test mode the guest and the host side have to share the same driver stack,
+ * as we don't have any device emulation between the two sides.
+ *
+ * This is necessary to actually get the played/recorded audio to from/to the guest
+ * and host respectively.
+ *
+ * Choosing any other backend than the Validation Kit above *will* break this self-test!
+ */
+ rc = audioTestDriverStackInitEx(&g_Ctx.DrvStack, g_Ctx.pDrvReg,
+ true /* fEnabledIn */, true /* fEnabledOut */, g_Ctx.Host.TstEnv.IoOpts.fWithDrvAudio);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to init driver stack: %Rrc\n", rc);
+
+ /*
+ * Start testing.
+ */
+ RTTestBanner(g_hTest);
+
+ int rc2 = audioTestDoSelftest(&g_Ctx);
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Self test failed with rc=%Rrc", rc2);
+
+ audioTestDriverStackDelete(&g_Ctx.DrvStack);
+
+ /*
+ * Print summary and exit.
+ */
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
+/**
+ * Command table entry for 'selftest'.
+ */
+const VKATCMD g_CmdSelfTest =
+{
+ "selftest",
+ audioTestCmdSelftestHandler,
+ "Performs self-tests.",
+ s_aCmdSelftestOptions,
+ RT_ELEMENTS(s_aCmdSelftestOptions),
+ audioTestCmdSelftestHelp,
+ true /* fNeedsTransport */
+};
+
diff --git a/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp b/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp
new file mode 100644
index 00000000..12ea83be
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp
@@ -0,0 +1,1760 @@
+/* $Id: vkatCommon.cpp $ */
+/** @file
+ * Validation Kit Audio Test (VKAT) - Self test code.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_AUDIO_TEST
+#include <iprt/log.h>
+
+#ifdef VBOX_WITH_AUDIO_ALSA
+# include "DrvHostAudioAlsaStubsMangling.h"
+# include <alsa/asoundlib.h>
+# include <alsa/control.h> /* For device enumeration. */
+# include <alsa/version.h>
+# include "DrvHostAudioAlsaStubs.h"
+#endif
+#ifdef VBOX_WITH_AUDIO_OSS
+# include <errno.h>
+# include <fcntl.h>
+# include <sys/ioctl.h>
+# include <sys/mman.h>
+# include <sys/soundcard.h>
+# include <unistd.h>
+#endif
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <iprt/win/audioclient.h>
+# include <endpointvolume.h> /* For IAudioEndpointVolume. */
+# include <audiopolicy.h> /* For IAudioSessionManager. */
+# include <AudioSessionTypes.h>
+# include <Mmdeviceapi.h>
+#endif
+
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/rand.h>
+#include <iprt/test.h>
+
+#include "Audio/AudioHlp.h"
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "vkatInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int audioTestStreamInit(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream, PDMAUDIODIR enmDir, PAUDIOTESTIOOPTS pPlayOpt);
+static int audioTestStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream);
+
+
+/*********************************************************************************************************************************
+* Volume handling. *
+*********************************************************************************************************************************/
+
+#ifdef VBOX_WITH_AUDIO_ALSA
+/**
+ * Sets the system's master volume via ALSA, if available.
+ *
+ * @returns VBox status code.
+ * @param uVolPercent Volume (in percent) to set.
+ */
+static int audioTestSetMasterVolumeALSA(unsigned uVolPercent)
+{
+ int rc = audioLoadAlsaLib();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ int err;
+ snd_mixer_t *handle;
+
+# define ALSA_CHECK_RET(a_Exp, a_Text) \
+ if (!(a_Exp)) \
+ { \
+ AssertLogRelMsg(a_Exp, a_Text); \
+ if (handle) \
+ snd_mixer_close(handle); \
+ return VERR_GENERAL_FAILURE; \
+ }
+
+# define ALSA_CHECK_ERR_RET(a_Text) \
+ ALSA_CHECK_RET(err >= 0, a_Text)
+
+ err = snd_mixer_open(&handle, 0 /* Index */);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to open mixer: %s\n", snd_strerror(err)));
+ err = snd_mixer_attach(handle, "default");
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to attach to default sink: %s\n", snd_strerror(err)));
+ err = snd_mixer_selem_register(handle, NULL, NULL);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to attach to default sink: %s\n", snd_strerror(err)));
+ err = snd_mixer_load(handle);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to load mixer: %s\n", snd_strerror(err)));
+
+ snd_mixer_selem_id_t *sid = NULL;
+ snd_mixer_selem_id_alloca(&sid);
+
+ snd_mixer_selem_id_set_index(sid, 0 /* Index */);
+ snd_mixer_selem_id_set_name(sid, "Master");
+
+ snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
+ ALSA_CHECK_RET(elem != NULL, ("ALSA: Failed to find mixer element: %s\n", snd_strerror(err)));
+
+ long uVolMin, uVolMax;
+ snd_mixer_selem_get_playback_volume_range(elem, &uVolMin, &uVolMax);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to get playback volume range: %s\n", snd_strerror(err)));
+
+ long const uVol = RT_MIN(uVolPercent, 100) * uVolMax / 100;
+
+ err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, uVol);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to set playback volume left: %s\n", snd_strerror(err)));
+ err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, uVol);
+ ALSA_CHECK_ERR_RET(("ALSA: Failed to set playback volume right: %s\n", snd_strerror(err)));
+
+ snd_mixer_close(handle);
+
+ return VINF_SUCCESS;
+
+# undef ALSA_CHECK_RET
+# undef ALSA_CHECK_ERR_RET
+}
+#endif /* VBOX_WITH_AUDIO_ALSA */
+
+#ifdef VBOX_WITH_AUDIO_OSS
+/**
+ * Sets the system's master volume via OSS, if available.
+ *
+ * @returns VBox status code.
+ * @param uVolPercent Volume (in percent) to set.
+ */
+static int audioTestSetMasterVolumeOSS(unsigned uVolPercent)
+{
+ int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0);
+ if (hFile == -1)
+ {
+ /* Try opening the mixing device instead. */
+ hFile = open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0);
+ }
+
+ if (hFile != -1)
+ {
+ /* OSS maps 0 (muted) - 100 (max), so just use uVolPercent unmodified here. */
+ uint16_t uVol = RT_MAKE_U16(uVolPercent, uVolPercent);
+ AssertLogRelMsgReturnStmt(ioctl(hFile, SOUND_MIXER_PCM /* SNDCTL_DSP_SETPLAYVOL */, &uVol) >= 0,
+ ("OSS: Failed to set DSP playback volume: %s (%d)\n",
+ strerror(errno), errno), close(hFile), RTErrConvertFromErrno(errno));
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_SUPPORTED;
+}
+#endif /* VBOX_WITH_AUDIO_OSS */
+
+#ifdef RT_OS_WINDOWS
+static int audioTestSetMasterVolumeWASAPI(unsigned uVolPercent)
+{
+ HRESULT hr;
+
+# define WASAPI_CHECK_HR_RET(a_Text) \
+ if (FAILED(hr)) \
+ { \
+ AssertLogRelMsgFailed(a_Text); \
+ return VERR_GENERAL_FAILURE; \
+ }
+
+ hr = CoInitialize(NULL);
+ WASAPI_CHECK_HR_RET(("CoInitialize() failed, hr=%Rhrc", hr));
+ IMMDeviceEnumerator* pIEnumerator = NULL;
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&pIEnumerator);
+ WASAPI_CHECK_HR_RET(("WASAPI: Unable to create IMMDeviceEnumerator, hr=%Rhrc", hr));
+
+ IMMDevice *pIMMDevice = NULL;
+ hr = pIEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eConsole, &pIMMDevice);
+ WASAPI_CHECK_HR_RET(("WASAPI: Unable to get audio endpoint, hr=%Rhrc", hr));
+ pIEnumerator->Release();
+
+ IAudioEndpointVolume *pIAudioEndpointVolume = NULL;
+ hr = pIMMDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void **)&pIAudioEndpointVolume);
+ WASAPI_CHECK_HR_RET(("WASAPI: Unable to activate audio endpoint volume, hr=%Rhrc", hr));
+ pIMMDevice->Release();
+
+ float dbMin, dbMax, dbInc;
+ hr = pIAudioEndpointVolume->GetVolumeRange(&dbMin, &dbMax, &dbInc);
+ WASAPI_CHECK_HR_RET(("WASAPI: Unable to get volume range, hr=%Rhrc", hr));
+
+ float const dbSteps = (dbMax - dbMin) / dbInc;
+ float const dbStepsPerPercent = (dbSteps * dbInc) / 100;
+ float const dbVol = dbMin + (dbStepsPerPercent * (float(RT_MIN(uVolPercent, 100.0))));
+
+ hr = pIAudioEndpointVolume->SetMasterVolumeLevel(dbVol, NULL);
+ WASAPI_CHECK_HR_RET(("WASAPI: Unable to set master volume level, hr=%Rhrc", hr));
+ pIAudioEndpointVolume->Release();
+
+ return VINF_SUCCESS;
+
+# undef WASAPI_CHECK_HR_RET
+}
+#endif /* RT_OS_WINDOWS */
+
+/**
+ * Sets the system's master volume, if available.
+ *
+ * @returns VBox status code. VERR_NOT_SUPPORTED if not supported.
+ * @param uVolPercent Volume (in percent) to set.
+ */
+int audioTestSetMasterVolume(unsigned uVolPercent)
+{
+ int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_AUDIO_ALSA
+ rc = audioTestSetMasterVolumeALSA(uVolPercent);
+ if (RT_SUCCESS(rc))
+ return rc;
+ /* else try OSS (if available) below. */
+#endif /* VBOX_WITH_AUDIO_ALSA */
+
+#ifdef VBOX_WITH_AUDIO_OSS
+ rc = audioTestSetMasterVolumeOSS(uVolPercent);
+ if (RT_SUCCESS(rc))
+ return rc;
+#endif /* VBOX_WITH_AUDIO_OSS */
+
+#ifdef RT_OS_WINDOWS
+ rc = audioTestSetMasterVolumeWASAPI(uVolPercent);
+ if (RT_SUCCESS(rc))
+ return rc;
+#endif
+
+ RT_NOREF(rc, uVolPercent);
+ /** @todo Port other platforms. */
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/*********************************************************************************************************************************
+* Device enumeration + handling. *
+*********************************************************************************************************************************/
+
+/**
+ * Enumerates audio devices and optionally searches for a specific device.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack Driver stack to use for enumeration.
+ * @param pszDev Device name to search for. Can be NULL if the default device shall be used.
+ * @param ppDev Where to return the pointer of the device enumeration of \a pTstEnv when a
+ * specific device was found.
+ */
+int audioTestDevicesEnumerateAndCheck(PAUDIOTESTDRVSTACK pDrvStack, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
+{
+ RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev && *pszDev ? pszDev : "[Default]");
+
+ if (!pDrvStack->pIHostAudio->pfnGetDevices)
+ {
+ RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
+ return VINF_NOT_SUPPORTED;
+ }
+
+ Assert(pszDev == NULL || ppDev);
+
+ if (ppDev)
+ *ppDev = NULL;
+
+ int rc = pDrvStack->pIHostAudio->pfnGetDevices(pDrvStack->pIHostAudio, &pDrvStack->DevEnum);
+ if (RT_SUCCESS(rc))
+ {
+ PPDMAUDIOHOSTDEV pDev;
+ RTListForEach(&pDrvStack->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+ {
+ char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
+ if (pDev->pszId)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);
+
+ if ( (pszDev && *pszDev)
+ && !RTStrCmp(pDev->pszName, pszDev))
+ {
+ *ppDev = pDev;
+ }
+ }
+ }
+ else
+ RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ if ( (pszDev && *pszDev)
+ && *ppDev == NULL)
+ {
+ RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
+ rc = VERR_NOT_FOUND;
+ }
+ }
+
+ RTTestSubDone(g_hTest);
+ return rc;
+}
+
+static int audioTestStreamInit(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream,
+ PDMAUDIODIR enmDir, PAUDIOTESTIOOPTS pIoOpts)
+{
+ int rc;
+
+ if (enmDir == PDMAUDIODIR_IN)
+ rc = audioTestDriverStackStreamCreateInput(pDrvStack, &pIoOpts->Props, pIoOpts->cMsBufferSize,
+ pIoOpts->cMsPreBuffer, pIoOpts->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
+ else if (enmDir == PDMAUDIODIR_OUT)
+ rc = audioTestDriverStackStreamCreateOutput(pDrvStack, &pIoOpts->Props, pIoOpts->cMsBufferSize,
+ pIoOpts->cMsPreBuffer, pIoOpts->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!pDrvStack->pIAudioConnector)
+ {
+ pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
+ }
+ else
+ pStream->pBackend = NULL;
+
+ /*
+ * Automatically enable the mixer if the PCM properties don't match.
+ */
+ if ( !pIoOpts->fWithMixer
+ && !PDMAudioPropsAreEqual(&pIoOpts->Props, &pStream->Cfg.Props))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enabling stream mixer\n");
+ pIoOpts->fWithMixer = true;
+ }
+
+ rc = AudioTestMixStreamInit(&pStream->Mix, pDrvStack, pStream->pStream,
+ pIoOpts->fWithMixer ? &pIoOpts->Props : NULL, 100 /* ms */); /** @todo Configure mixer buffer? */
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Initializing %s stream failed with %Rrc", enmDir == PDMAUDIODIR_IN ? "input" : "output", rc);
+
+ return rc;
+}
+
+/**
+ * Destroys an audio test stream.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack Driver stack the stream belongs to.
+ * @param pStream Audio stream to destroy.
+ */
+static int audioTestStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+
+ if (pStream->pStream)
+ {
+ /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
+
+ audioTestDriverStackStreamDestroy(pDrvStack, pStream->pStream);
+ pStream->pStream = NULL;
+ pStream->pBackend = NULL;
+ }
+
+ AudioTestMixStreamTerm(&pStream->Mix);
+
+ return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+* Test Primitives *
+*********************************************************************************************************************************/
+
+/**
+ * Initializes test tone parameters (partly with random values).
+
+ * @param pToneParms Test tone parameters to initialize.
+ */
+void audioTestToneParmsInit(PAUDIOTESTTONEPARMS pToneParms)
+{
+ RT_BZERO(pToneParms, sizeof(AUDIOTESTTONEPARMS));
+
+ /**
+ * Set default (randomized) test tone parameters if not set explicitly.
+ */
+ pToneParms->dbFreqHz = AudioTestToneGetRandomFreq();
+ pToneParms->msDuration = RTRandU32Ex(200, RT_MS_30SEC);
+ pToneParms->uVolumePercent = 100; /* We always go with maximum volume for now. */
+
+ PDMAudioPropsInit(&pToneParms->Props,
+ 2 /* 16-bit */, true /* fPcmSigned */, 2 /* cPcmChannels */, 44100 /* uPcmHz */);
+}
+
+/**
+ * Initializes I/O options with some sane default values.
+ *
+ * @param pIoOpts I/O options to initialize.
+ */
+void audioTestIoOptsInitDefaults(PAUDIOTESTIOOPTS pIoOpts)
+{
+ RT_BZERO(pIoOpts, sizeof(AUDIOTESTIOOPTS));
+
+ /* Initialize the PCM properties to some sane values. */
+ PDMAudioPropsInit(&pIoOpts->Props,
+ 2 /* 16-bit */, true /* fPcmSigned */, 2 /* cPcmChannels */, 44100 /* uPcmHz */);
+
+ pIoOpts->cMsBufferSize = UINT32_MAX;
+ pIoOpts->cMsPreBuffer = UINT32_MAX;
+ pIoOpts->cMsSchedulingHint = UINT32_MAX;
+ pIoOpts->uVolumePercent = 100; /* Use maximum volume by default. */
+}
+
+#if 0 /* Unused */
+/**
+ * Returns a random scheduling hint (in ms).
+ */
+DECLINLINE(uint32_t) audioTestEnvGetRandomSchedulingHint(void)
+{
+ static const unsigned s_aSchedulingHintsMs[] =
+ {
+ 10,
+ 25,
+ 50,
+ 100,
+ 200,
+ 250
+ };
+
+ return s_aSchedulingHintsMs[RTRandU32Ex(0, RT_ELEMENTS(s_aSchedulingHintsMs) - 1)];
+}
+#endif
+
+/**
+ * Plays a test tone on a specific audio test stream.
+ *
+ * @returns VBox status code.
+ * @param pIoOpts I/O options to use.
+ * @param pTstEnv Test environment to use for running the test.
+ * Optional and can be NULL (for simple playback only).
+ * @param pStream Stream to use for playing the tone.
+ * @param pParms Tone parameters to use.
+ *
+ * @note Blocking function.
+ */
+int audioTestPlayTone(PAUDIOTESTIOOPTS pIoOpts, PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
+{
+ uint32_t const idxTest = (uint8_t)pParms->Hdr.idxTest;
+
+ AUDIOTESTTONE TstTone;
+ AudioTestToneInit(&TstTone, &pStream->Cfg.Props, pParms->dbFreqHz);
+
+ char const *pcszPathOut = NULL;
+ if (pTstEnv)
+ pcszPathOut = pTstEnv->Set.szPathAbs;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing test tone (tone frequency is %RU16Hz, %RU32ms, %RU8%% volume)\n",
+ idxTest, (uint16_t)pParms->dbFreqHz, pParms->msDuration, pParms->uVolumePercent);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Using %RU32ms stream scheduling hint\n",
+ idxTest, pStream->Cfg.Device.cMsSchedulingHint);
+ if (pcszPathOut)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Writing to '%s'\n", idxTest, pcszPathOut);
+
+ int rc;
+
+ /** @todo Use .WAV here? */
+ AUDIOTESTOBJ Obj;
+ RT_ZERO(Obj); /* Shut up MSVC. */
+ if (pTstEnv)
+ {
+ rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-play.pcm", &Obj);
+ AssertRCReturn(rc, rc);
+ }
+
+ uint8_t const uVolPercent = pIoOpts->uVolumePercent;
+ int rc2 = audioTestSetMasterVolume(uVolPercent);
+ if (RT_FAILURE(rc2))
+ {
+ if (rc2 == VERR_NOT_SUPPORTED)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Setting system's master volume is not supported on this platform, skipping\n");
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Setting system's master volume failed with %Rrc\n", rc2);
+ }
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Set system's master volume to %RU8%%\n", uVolPercent);
+
+ rc = AudioTestMixStreamEnable(&pStream->Mix);
+ if ( RT_SUCCESS(rc)
+ && AudioTestMixStreamIsOkay(&pStream->Mix))
+ {
+ uint32_t cbToWriteTotal = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
+ AssertStmt(cbToWriteTotal, rc = VERR_INVALID_PARAMETER);
+ uint32_t cbWrittenTotal = 0;
+
+ /* We play a pre + post beacon before + after the actual test tone.
+ * We always start with the pre beacon. */
+ AUDIOTESTTONEBEACON Beacon;
+ AudioTestBeaconInit(&Beacon, (uint8_t)pParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, &pStream->Cfg.Props);
+
+ uint32_t const cbBeacon = AudioTestBeaconGetSize(&Beacon);
+ if (cbBeacon)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing 2 x %RU32 bytes pre/post beacons\n",
+ idxTest, cbBeacon);
+
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing %s beacon ...\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing %RU32 bytes total\n", idxTest, cbToWriteTotal);
+
+ AudioTestObjAddMetadataStr(Obj, "test_id=%04RU32\n", pParms->Hdr.idxTest);
+ AudioTestObjAddMetadataStr(Obj, "beacon_type=%RU32\n", (uint32_t)AudioTestBeaconGetType(&Beacon));
+ AudioTestObjAddMetadataStr(Obj, "beacon_pre_bytes=%RU32\n", cbBeacon);
+ AudioTestObjAddMetadataStr(Obj, "beacon_post_bytes=%RU32\n", cbBeacon);
+ AudioTestObjAddMetadataStr(Obj, "stream_to_write_total_bytes=%RU32\n", cbToWriteTotal);
+ AudioTestObjAddMetadataStr(Obj, "stream_period_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesPeriod);
+ AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesBufferSize);
+ AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesPreBuffering);
+ /* Note: This mostly is provided by backend (e.g. PulseAudio / ALSA / ++) and
+ * has nothing to do with the device emulation scheduling hint. */
+ AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pStream->Cfg.Device.cMsSchedulingHint);
+
+ PAUDIOTESTDRVMIXSTREAM pMix = &pStream->Mix;
+
+ uint32_t const cbPreBuffer = PDMAudioPropsFramesToBytes(pMix->pProps, pStream->Cfg.Backend.cFramesPreBuffering);
+ uint64_t const nsStarted = RTTimeNanoTS();
+ uint64_t nsDonePreBuffering = 0;
+
+ uint64_t offStream = 0;
+ uint64_t nsTimeout = RT_MS_5MIN_64 * RT_NS_1MS;
+ uint64_t nsLastMsgCantWrite = 0; /* Timestamp (in ns) when the last message of an unwritable stream was shown. */
+ uint64_t nsLastWrite = 0;
+
+ AUDIOTESTSTATE enmState = AUDIOTESTSTATE_PRE;
+ uint8_t abBuf[_16K];
+
+ for (;;)
+ {
+ uint64_t const nsNow = RTTimeNanoTS();
+ if (!nsLastWrite)
+ nsLastWrite = nsNow;
+
+ /* Pace ourselves a little. */
+ if (offStream >= cbPreBuffer)
+ {
+ if (!nsDonePreBuffering)
+ nsDonePreBuffering = nsNow;
+ uint64_t const cNsWritten = PDMAudioPropsBytesToNano64(pMix->pProps, offStream - cbPreBuffer);
+ uint64_t const cNsElapsed = nsNow - nsStarted;
+ if (cNsWritten > cNsElapsed + RT_NS_10MS)
+ RTThreadSleep((cNsWritten - cNsElapsed - RT_NS_10MS / 2) / RT_NS_1MS);
+ }
+
+ uint32_t cbWritten = 0;
+ uint32_t const cbCanWrite = AudioTestMixStreamGetWritable(&pStream->Mix);
+ if (cbCanWrite)
+ {
+ if (g_uVerbosity >= 4)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Stream is writable with %RU64ms (%RU32 bytes)\n",
+ idxTest, PDMAudioPropsBytesToMilli(pMix->pProps, cbCanWrite), cbCanWrite);
+
+ switch (enmState)
+ {
+ case AUDIOTESTSTATE_PRE:
+ RT_FALL_THROUGH();
+ case AUDIOTESTSTATE_POST:
+ {
+ if (g_uVerbosity >= 4)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: %RU32 bytes (%RU64ms) beacon data remaining\n",
+ idxTest, AudioTestBeaconGetRemaining(&Beacon),
+ PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, AudioTestBeaconGetRemaining(&Beacon)));
+
+ bool fGoToNextStage = false;
+
+ if ( AudioTestBeaconGetSize(&Beacon)
+ && !AudioTestBeaconIsComplete(&Beacon))
+ {
+ bool const fStarted = AudioTestBeaconGetRemaining(&Beacon) == AudioTestBeaconGetSize(&Beacon);
+
+ uint32_t const cbBeaconRemaining = AudioTestBeaconGetRemaining(&Beacon);
+ AssertBreakStmt(cbBeaconRemaining, VERR_WRONG_ORDER);
+
+ /* Limit to exactly one beacon (pre or post). */
+ uint32_t const cbToWrite = RT_MIN(sizeof(abBuf), RT_MIN(cbCanWrite, cbBeaconRemaining));
+
+ rc = AudioTestBeaconWrite(&Beacon, abBuf, cbToWrite);
+ if (RT_SUCCESS(rc))
+ {
+ rc = AudioTestMixStreamPlay(&pStream->Mix, abBuf, cbToWrite, &cbWritten);
+ if ( RT_SUCCESS(rc)
+ && pTstEnv)
+ {
+ /* Also write the beacon data to the test object.
+ * Note: We use cbPlayed here instead of cbToPlay to know if the data actually was
+ * reported as being played by the audio stack. */
+ rc = AudioTestObjWrite(Obj, abBuf, cbWritten);
+ }
+ }
+
+ if ( fStarted
+ && g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Writing %s beacon begin\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ if (AudioTestBeaconIsComplete(&Beacon))
+ {
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Writing %s beacon end\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ fGoToNextStage = true;
+ }
+ }
+ else
+ fGoToNextStage = true;
+
+ if (fGoToNextStage)
+ {
+ if (enmState == AUDIOTESTSTATE_PRE)
+ enmState = AUDIOTESTSTATE_RUN;
+ else if (enmState == AUDIOTESTSTATE_POST)
+ enmState = AUDIOTESTSTATE_DONE;
+ }
+ break;
+ }
+
+ case AUDIOTESTSTATE_RUN:
+ {
+ uint32_t cbToWrite = RT_MIN(sizeof(abBuf), cbCanWrite);
+ cbToWrite = RT_MIN(cbToWrite, cbToWriteTotal - cbWrittenTotal);
+
+ if (g_uVerbosity >= 4)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Playing back %RU32 bytes\n", idxTest, cbToWrite);
+
+ if (cbToWrite)
+ {
+ rc = AudioTestToneGenerate(&TstTone, abBuf, cbToWrite, &cbToWrite);
+ if (RT_SUCCESS(rc))
+ {
+ if (pTstEnv)
+ {
+ /* Write stuff to disk before trying to play it. Helps analysis later. */
+ rc = AudioTestObjWrite(Obj, abBuf, cbToWrite);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = AudioTestMixStreamPlay(&pStream->Mix, abBuf, cbToWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ AssertBreakStmt(cbWritten <= cbToWrite, rc = VERR_TOO_MUCH_DATA);
+
+ offStream += cbWritten;
+
+ if (cbWritten != cbToWrite)
+ RTTestFailed(g_hTest, "Test #%RU32: Only played %RU32/%RU32 bytes",
+ idxTest, cbWritten, cbToWrite);
+
+ if (cbWritten)
+ nsLastWrite = nsNow;
+
+ if (g_uVerbosity >= 4)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Played back %RU32 bytes\n", idxTest, cbWritten);
+
+ cbWrittenTotal += cbWritten;
+ }
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ const bool fComplete = cbWrittenTotal >= cbToWriteTotal;
+ if (fComplete)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing back audio data ended\n", idxTest);
+
+ enmState = AUDIOTESTSTATE_POST;
+
+ /* Re-use the beacon object, but this time it's the post beacon. */
+ AudioTestBeaconInit(&Beacon, (uint8_t)idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
+ &pStream->Cfg.Props);
+ }
+ }
+ else
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Playing back failed with %Rrc\n", idxTest, rc);
+ break;
+ }
+
+ case AUDIOTESTSTATE_DONE:
+ {
+ /* Handled below. */
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+
+ if (enmState == AUDIOTESTSTATE_DONE)
+ break;
+
+ nsLastMsgCantWrite = 0;
+ }
+ else if (AudioTestMixStreamIsOkay(&pStream->Mix))
+ {
+ RTMSINTERVAL const msSleep = RT_MIN(RT_MAX(1, pStream->Cfg.Device.cMsSchedulingHint), 256);
+
+ if ( g_uVerbosity >= 3
+ && ( !nsLastMsgCantWrite
+ || (nsNow - nsLastMsgCantWrite) > RT_NS_10SEC)) /* Don't spam the output too much. */
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Waiting %RU32ms for stream to be writable again (last write %RU64ns ago) ...\n",
+ idxTest, msSleep, nsNow - nsLastWrite);
+ nsLastMsgCantWrite = nsNow;
+ }
+
+ RTThreadSleep(msSleep);
+ }
+ else
+ AssertFailedBreakStmt(rc = VERR_AUDIO_STREAM_NOT_READY);
+
+ /* Fail-safe in case something screwed up while playing back. */
+ uint64_t const cNsElapsed = nsNow - nsStarted;
+ if (cNsElapsed > nsTimeout)
+ {
+ RTTestFailed(g_hTest, "Test #%RU32: Playback took too long (running %RU64 vs. timeout %RU64), aborting\n",
+ idxTest, cNsElapsed, nsTimeout);
+ rc = VERR_TIMEOUT;
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ } /* for */
+
+ if (cbWrittenTotal != cbToWriteTotal)
+ RTTestFailed(g_hTest, "Test #%RU32: Playback ended unexpectedly (%RU32/%RU32 played)\n",
+ idxTest, cbWrittenTotal, cbToWriteTotal);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Draining stream ...\n", idxTest);
+ rc = AudioTestMixStreamDrain(&pStream->Mix, true /*fSync*/);
+ }
+ }
+ else
+ rc = VERR_AUDIO_STREAM_NOT_READY;
+
+ if (pTstEnv)
+ {
+ rc2 = AudioTestObjClose(Obj);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: Playing tone failed with %Rrc\n", idxTest, rc);
+
+ return rc;
+}
+
+/**
+ * Records a test tone from a specific audio test stream.
+ *
+ * @returns VBox status code.
+ * @param pIoOpts I/O options to use.
+ * @param pTstEnv Test environment to use for running the test.
+ * @param pStream Stream to use for recording the tone.
+ * @param pParms Tone parameters to use.
+ *
+ * @note Blocking function.
+ */
+static int audioTestRecordTone(PAUDIOTESTIOOPTS pIoOpts, PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
+{
+ uint32_t const idxTest = (uint8_t)pParms->Hdr.idxTest;
+
+ const char *pcszPathOut = pTstEnv->Set.szPathAbs;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording test tone (tone frequency is %RU16Hz, %RU32ms)\n",
+ idxTest, (uint16_t)pParms->dbFreqHz, pParms->msDuration);
+ RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Test #%RU32: Writing to '%s'\n", idxTest, pcszPathOut);
+
+ /** @todo Use .WAV here? */
+ AUDIOTESTOBJ Obj;
+ int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-rec.pcm", &Obj);
+ AssertRCReturn(rc, rc);
+
+ PAUDIOTESTDRVMIXSTREAM pMix = &pStream->Mix;
+
+ rc = AudioTestMixStreamEnable(pMix);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbRecTotal = 0; /* Counts everything, including silence / whatever. */
+ uint32_t cbTestToRec = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
+ uint32_t cbTestRec = 0;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording %RU32 bytes total\n", idxTest, cbTestToRec);
+
+ /* We expect a pre + post beacon before + after the actual test tone.
+ * We always start with the pre beacon. */
+ AUDIOTESTTONEBEACON Beacon;
+ AudioTestBeaconInit(&Beacon, (uint8_t)pParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, &pStream->Cfg.Props);
+
+ uint32_t const cbBeacon = AudioTestBeaconGetSize(&Beacon);
+ if (cbBeacon)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Expecting 2 x %RU32 bytes pre/post beacons\n",
+ idxTest, cbBeacon);
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Waiting for %s beacon ...\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ }
+
+ AudioTestObjAddMetadataStr(Obj, "test_id=%04RU32\n", pParms->Hdr.idxTest);
+ AudioTestObjAddMetadataStr(Obj, "beacon_type=%RU32\n", (uint32_t)AudioTestBeaconGetType(&Beacon));
+ AudioTestObjAddMetadataStr(Obj, "beacon_pre_bytes=%RU32\n", cbBeacon);
+ AudioTestObjAddMetadataStr(Obj, "beacon_post_bytes=%RU32\n", cbBeacon);
+ AudioTestObjAddMetadataStr(Obj, "stream_to_record_bytes=%RU32\n", cbTestToRec);
+ AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_ms=%RU32\n", pIoOpts->cMsBufferSize);
+ AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_ms=%RU32\n", pIoOpts->cMsPreBuffer);
+ /* Note: This mostly is provided by backend (e.g. PulseAudio / ALSA / ++) and
+ * has nothing to do with the device emulation scheduling hint. */
+ AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pIoOpts->cMsSchedulingHint);
+
+ uint8_t abSamples[16384];
+ uint32_t const cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
+
+ uint64_t const nsStarted = RTTimeNanoTS();
+
+ uint64_t nsTimeout = RT_MS_5MIN_64 * RT_NS_1MS;
+ uint64_t nsLastMsgCantRead = 0; /* Timestamp (in ns) when the last message of an unreadable stream was shown. */
+
+ AUDIOTESTSTATE enmState = AUDIOTESTSTATE_PRE;
+
+ while (!g_fTerminate)
+ {
+ uint64_t const nsNow = RTTimeNanoTS();
+
+ /*
+ * Anything we can read?
+ */
+ uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
+ if (cbCanRead)
+ {
+ if (g_uVerbosity >= 3)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Stream is readable with %RU64ms (%RU32 bytes)\n",
+ idxTest, PDMAudioPropsBytesToMilli(pMix->pProps, cbCanRead), cbCanRead);
+
+ uint32_t const cbToRead = RT_MIN(cbCanRead, cbSamplesAligned);
+ uint32_t cbRecorded = 0;
+ rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbRecorded);
+ if (RT_SUCCESS(rc))
+ {
+ /* Flag indicating whether the whole block we're going to play is silence or not. */
+ bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, abSamples, cbRecorded);
+
+ cbRecTotal += cbRecorded; /* Do a bit of accounting. */
+
+ switch (enmState)
+ {
+ case AUDIOTESTSTATE_PRE:
+ RT_FALL_THROUGH();
+ case AUDIOTESTSTATE_POST:
+ {
+ bool fGoToNextStage = false;
+
+ if ( AudioTestBeaconGetSize(&Beacon)
+ && !AudioTestBeaconIsComplete(&Beacon))
+ {
+ bool const fStarted = AudioTestBeaconGetRemaining(&Beacon) == AudioTestBeaconGetSize(&Beacon);
+
+ size_t uOff;
+ rc = AudioTestBeaconAddConsecutive(&Beacon, abSamples, cbRecorded, &uOff);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * When being in the AUDIOTESTSTATE_PRE state, we might get more audio data
+ * than we need for the pre-beacon to complete. In other words, that "more data"
+ * needs to be counted to the actual recorded test tone data then.
+ */
+ if (enmState == AUDIOTESTSTATE_PRE)
+ cbTestRec += cbRecorded - (uint32_t)uOff;
+ }
+
+ if ( fStarted
+ && g_uVerbosity >= 3)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Detection of %s beacon started (%RU32ms recorded so far)\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType),
+ PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRecTotal));
+
+ if (AudioTestBeaconIsComplete(&Beacon))
+ {
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Detected %s beacon\n",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ fGoToNextStage = true;
+ }
+ }
+ else
+ fGoToNextStage = true;
+
+ if (fGoToNextStage)
+ {
+ if (enmState == AUDIOTESTSTATE_PRE)
+ enmState = AUDIOTESTSTATE_RUN;
+ else if (enmState == AUDIOTESTSTATE_POST)
+ enmState = AUDIOTESTSTATE_DONE;
+ }
+ break;
+ }
+
+ case AUDIOTESTSTATE_RUN:
+ {
+ /* Whether we count all silence as recorded data or not.
+ * Currently we don't, as otherwise consequtively played tones will be cut off in the end. */
+ if (!fIsAllSilence)
+ {
+ uint32_t const cbToAddMax = cbTestToRec - cbTestRec;
+
+ /* Don't read more than we're told to.
+ * After the actual test tone data there might come a post beacon which also
+ * needs to be handled in the AUDIOTESTSTATE_POST state then. */
+ if (cbRecorded > cbToAddMax)
+ cbRecorded = cbToAddMax;
+
+ cbTestRec += cbRecorded;
+ }
+
+ if (cbTestToRec - cbTestRec == 0) /* Done recording the test tone? */
+ {
+ enmState = AUDIOTESTSTATE_POST;
+
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recording tone data done", idxTest);
+
+ if (AudioTestBeaconGetSize(&Beacon))
+ {
+ /* Re-use the beacon object, but this time it's the post beacon. */
+ AudioTestBeaconInit(&Beacon, (uint8_t)pParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
+ &pStream->Cfg.Props);
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Test #%RU32: Waiting for %s beacon ...",
+ idxTest, AudioTestBeaconTypeGetName(Beacon.enmType));
+ }
+ }
+ break;
+ }
+
+ case AUDIOTESTSTATE_DONE:
+ {
+ /* Nothing to do here. */
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+
+ if (cbRecorded)
+ {
+ /* Always write (record) everything, no matter if the current audio contains complete silence or not.
+ * Might be also become handy later if we want to have a look at start/stop timings and so on. */
+ rc = AudioTestObjWrite(Obj, abSamples, cbRecorded);
+ AssertRCBreak(rc);
+ }
+
+ if (enmState == AUDIOTESTSTATE_DONE) /* Bail out when in state "done". */
+ break;
+ }
+ else if (AudioTestMixStreamIsOkay(pMix))
+ {
+ RTMSINTERVAL const msSleep = RT_MIN(RT_MAX(1, pStream->Cfg.Device.cMsSchedulingHint), 256);
+
+ if ( g_uVerbosity >= 3
+ && ( !nsLastMsgCantRead
+ || (nsNow - nsLastMsgCantRead) > RT_NS_10SEC)) /* Don't spam the output too much. */
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Waiting %RU32ms for stream to be readable again ...\n",
+ idxTest, msSleep);
+ nsLastMsgCantRead = nsNow;
+ }
+
+ RTThreadSleep(msSleep);
+ }
+
+ /* Fail-safe in case something screwed up while playing back. */
+ uint64_t const cNsElapsed = nsNow - nsStarted;
+ if (cNsElapsed > nsTimeout)
+ {
+ RTTestFailed(g_hTest, "Test #%RU32: Recording took too long (running %RU64 vs. timeout %RU64), aborting\n",
+ idxTest, cNsElapsed, nsTimeout);
+ rc = VERR_TIMEOUT;
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (g_uVerbosity >= 2)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32: Recorded %RU32 bytes total\n", idxTest, cbRecTotal);
+ if (cbTestRec != cbTestToRec)
+ {
+ RTTestFailed(g_hTest, "Test #%RU32: Recording ended unexpectedly (%RU32/%RU32 recorded)\n",
+ idxTest, cbTestRec, cbTestToRec);
+ rc = VERR_WRONG_ORDER; /** @todo Find a better rc. */
+ }
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: Recording failed (state is '%s')\n", idxTest, AudioTestStateToStr(enmState));
+
+ int rc2 = AudioTestMixStreamDisable(pMix);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ int rc2 = AudioTestObjClose(Obj);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test #%RU32: Recording tone done failed with %Rrc\n", idxTest, rc);
+
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* ATS Callback Implementations *
+*********************************************************************************************************************************/
+
+/** @copydoc ATSCALLBACKS::pfnHowdy
+ *
+ * @note Runs as part of the guest ATS.
+ */
+static DECLCALLBACK(int) audioTestGstAtsHowdyCallback(void const *pvUser)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+
+ AssertReturn(pCtx->cClients <= UINT8_MAX - 1, VERR_BUFFER_OVERFLOW);
+
+ pCtx->cClients++;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "New client connected, now %RU8 total\n", pCtx->cClients);
+
+ return VINF_SUCCESS;
+}
+
+/** @copydoc ATSCALLBACKS::pfnBye
+ *
+ * @note Runs as part of the guest ATS.
+ */
+static DECLCALLBACK(int) audioTestGstAtsByeCallback(void const *pvUser)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+
+ AssertReturn(pCtx->cClients, VERR_WRONG_ORDER);
+ pCtx->cClients--;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Client wants to disconnect, %RU8 remaining\n", pCtx->cClients);
+
+ if (0 == pCtx->cClients) /* All clients disconnected? Tear things down. */
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Last client disconnected, terminating server ...\n");
+ ASMAtomicWriteBool(&g_fTerminate, true);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetBegin
+ *
+ * @note Runs as part of the guest ATS.
+ */
+static DECLCALLBACK(int) audioTestGstAtsTestSetBeginCallback(void const *pvUser, const char *pszTag)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+ PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for beginning test set '%s' in '%s'\n", pszTag, pTstEnv->szPathTemp);
+
+ return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetEnd
+ *
+ * @note Runs as part of the guest ATS.
+ */
+static DECLCALLBACK(int) audioTestGstAtsTestSetEndCallback(void const *pvUser, const char *pszTag)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+ PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for ending test set '%s'\n", pszTag);
+
+ /* Pack up everything to be ready for transmission. */
+ return audioTestEnvPrologue(pTstEnv, true /* fPack */, pCtx->szTestSetArchive, sizeof(pCtx->szTestSetArchive));
+}
+
+/** @copydoc ATSCALLBACKS::pfnTonePlay
+ *
+ * @note Runs as part of the guest ATS.
+ */
+static DECLCALLBACK(int) audioTestGstAtsTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+ PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
+ PAUDIOTESTIOOPTS pIoOpts = &pTstEnv->IoOpts;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for playing test tone #%RU32 (%RU16Hz, %RU32ms) ...\n",
+ pToneParms->Hdr.idxTest, (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
+
+ char szTimeCreated[RTTIME_STR_LEN];
+ RTTimeToString(&pToneParms->Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test created (caller UTC): %s\n", szTimeCreated);
+
+ const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
+
+ int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_OUT, pIoOpts);
+ if (RT_SUCCESS(rc))
+ {
+ AUDIOTESTPARMS TstParms;
+ RT_ZERO(TstParms);
+ TstParms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
+ TstParms.enmDir = PDMAUDIODIR_OUT;
+ TstParms.TestTone = *pToneParms;
+
+ PAUDIOTESTENTRY pTst;
+ rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
+ if (RT_SUCCESS(rc))
+ {
+ rc = audioTestPlayTone(&pTstEnv->IoOpts, pTstEnv, pTstStream, pToneParms);
+ if (RT_SUCCESS(rc))
+ {
+ AudioTestSetTestDone(pTst);
+ }
+ else
+ AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
+ }
+
+ int rc2 = audioTestStreamDestroy(pTstEnv->pDrvStack, pTstStream);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
+
+ return rc;
+}
+
+/** @copydoc ATSCALLBACKS::pfnToneRecord */
+static DECLCALLBACK(int) audioTestGstAtsToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
+{
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+ PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
+ PAUDIOTESTIOOPTS pIoOpts = &pTstEnv->IoOpts;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for recording test tone #%RU32 (%RU32ms) ...\n",
+ pToneParms->Hdr.idxTest, pToneParms->msDuration);
+
+ char szTimeCreated[RTTIME_STR_LEN];
+ RTTimeToString(&pToneParms->Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test created (caller UTC): %s\n", szTimeCreated);
+
+ const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
+
+ int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_IN, pIoOpts);
+ if (RT_SUCCESS(rc))
+ {
+ AUDIOTESTPARMS TstParms;
+ RT_ZERO(TstParms);
+ TstParms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
+ TstParms.enmDir = PDMAUDIODIR_IN;
+ TstParms.TestTone = *pToneParms;
+
+ PAUDIOTESTENTRY pTst;
+ rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone from host", &TstParms, &pTst);
+ if (RT_SUCCESS(rc))
+ {
+ rc = audioTestRecordTone(pIoOpts, pTstEnv, pTstStream, pToneParms);
+ if (RT_SUCCESS(rc))
+ {
+ AudioTestSetTestDone(pTst);
+ }
+ else
+ AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
+ }
+
+ int rc2 = audioTestStreamDestroy(pTstEnv->pDrvStack, pTstStream);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
+
+ return rc;
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
+static DECLCALLBACK(int) audioTestGstAtsTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+
+ if (!RTFileExists(pCtx->szTestSetArchive)) /* Has the archive successfully been created yet? */
+ return VERR_WRONG_ORDER;
+
+ int rc = RTFileOpen(&pCtx->hTestSetArchive, pCtx->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t uSize;
+ rc = RTFileQuerySize(pCtx->hTestSetArchive, &uSize);
+ if (RT_SUCCESS(rc))
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Sending test set '%s' (%zu bytes)\n", pCtx->szTestSetArchive, uSize);
+ }
+
+ return rc;
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
+static DECLCALLBACK(int) audioTestGstAtsTestSetSendReadCallback(void const *pvUser,
+ const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
+{
+ RT_NOREF(pszTag);
+
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+
+ return RTFileRead(pCtx->hTestSetArchive, pvBuf, cbBuf, pcbRead);
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
+static DECLCALLBACK(int) audioTestGstAtsTestSetSendEndCallback(void const *pvUser, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+
+ PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
+
+ int rc = RTFileClose(pCtx->hTestSetArchive);
+ if (RT_SUCCESS(rc))
+ {
+ pCtx->hTestSetArchive = NIL_RTFILE;
+ }
+
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Implementation of audio test environment handling *
+*********************************************************************************************************************************/
+
+/**
+ * Connects an ATS client via TCP/IP to a peer.
+ *
+ * @returns VBox status code.
+ * @param pTstEnv Test environment to use.
+ * @param pClient Client to connect.
+ * @param pszWhat Hint of what to connect to where.
+ * @param pTcpOpts Pointer to TCP options to use.
+ */
+int audioTestEnvConnectViaTcp(PAUDIOTESTENV pTstEnv, PATSCLIENT pClient, const char *pszWhat, PAUDIOTESTENVTCPOPTS pTcpOpts)
+{
+ RT_NOREF(pTstEnv);
+
+ RTGETOPTUNION Val;
+ RT_ZERO(Val);
+
+ Val.u32 = pTcpOpts->enmConnMode;
+ int rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONN_MODE, &Val);
+ AssertRCReturn(rc, rc);
+
+ if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
+ || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
+ {
+ Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
+ Val.u16 = pTcpOpts->uBindPort;
+ rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_PORT, &Val);
+ AssertRCReturn(rc, rc);
+
+ if (pTcpOpts->szBindAddr[0])
+ {
+ Val.psz = pTcpOpts->szBindAddr;
+ rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_ADDRESS, &Val);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "No bind address specified!\n");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by listening as server at %s:%RU32 ...\n",
+ pszWhat, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
+ }
+
+
+ if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
+ || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
+ {
+ Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
+ Val.u16 = pTcpOpts->uConnectPort;
+ rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_PORT, &Val);
+ AssertRCReturn(rc, rc);
+
+ if (pTcpOpts->szConnectAddr[0])
+ {
+ Val.psz = pTcpOpts->szConnectAddr;
+ rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_ADDRESS, &Val);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "No connect address specified!\n");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by connecting as client to %s:%RU32 ...\n",
+ pszWhat, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
+ }
+
+ rc = AudioTestSvcClientConnect(pClient);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "Connecting %s failed with %Rrc\n", pszWhat, rc);
+ return rc;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Successfully connected %s\n", pszWhat);
+ return rc;
+}
+
+/**
+ * Configures and starts an ATS TCP/IP server.
+ *
+ * @returns VBox status code.
+ * @param pSrv ATS server instance to configure and start.
+ * @param pCallbacks ATS callback table to use.
+ * @param pszDesc Hint of server type which is being started.
+ * @param pTcpOpts TCP options to use.
+ */
+int audioTestEnvConfigureAndStartTcpServer(PATSSERVER pSrv, PCATSCALLBACKS pCallbacks, const char *pszDesc,
+ PAUDIOTESTENVTCPOPTS pTcpOpts)
+{
+ RTGETOPTUNION Val;
+ RT_ZERO(Val);
+
+ int rc = AudioTestSvcInit(pSrv, pCallbacks);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ Val.u32 = pTcpOpts->enmConnMode;
+ rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONN_MODE, &Val);
+ AssertRCReturn(rc, rc);
+
+ if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
+ || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
+ {
+ Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
+ Val.u16 = pTcpOpts->uBindPort;
+ rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_PORT, &Val);
+ AssertRCReturn(rc, rc);
+
+ if (pTcpOpts->szBindAddr[0])
+ {
+ Val.psz = pTcpOpts->szBindAddr;
+ rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_ADDRESS, &Val);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "No bind address specified!\n");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s at %s:%RU32 ...\n",
+ pszDesc, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
+ }
+
+
+ if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
+ || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
+ {
+ Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
+ Val.u16 = pTcpOpts->uConnectPort;
+ rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_PORT, &Val);
+ AssertRCReturn(rc, rc);
+
+ if (pTcpOpts->szConnectAddr[0])
+ {
+ Val.psz = pTcpOpts->szConnectAddr;
+ rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_ADDRESS, &Val);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "No connect address specified!\n");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s by connecting as client to %s:%RU32 ...\n",
+ pszDesc, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = AudioTestSvcStart(pSrv);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Starting server for %s failed with %Rrc\n", pszDesc, rc);
+ }
+
+ return rc;
+}
+
+/**
+ * Initializes an audio test environment.
+ *
+ * @param pTstEnv Audio test environment to initialize.
+ */
+void audioTestEnvInit(PAUDIOTESTENV pTstEnv)
+{
+ RT_BZERO(pTstEnv, sizeof(AUDIOTESTENV));
+
+ audioTestIoOptsInitDefaults(&pTstEnv->IoOpts);
+ audioTestToneParmsInit(&pTstEnv->ToneParms);
+}
+
+/**
+ * Creates an audio test environment.
+ *
+ * @returns VBox status code.
+ * @param pTstEnv Audio test environment to create.
+ * @param pDrvStack Driver stack to use.
+ */
+int audioTestEnvCreate(PAUDIOTESTENV pTstEnv, PAUDIOTESTDRVSTACK pDrvStack)
+{
+ AssertReturn(PDMAudioPropsAreValid(&pTstEnv->IoOpts.Props), VERR_WRONG_ORDER);
+
+ int rc = VINF_SUCCESS;
+
+ pTstEnv->pDrvStack = pDrvStack;
+
+ /*
+ * Set sane defaults if not already set.
+ */
+ if (!RTStrNLen(pTstEnv->szTag, sizeof(pTstEnv->szTag)))
+ {
+ rc = AudioTestGenTag(pTstEnv->szTag, sizeof(pTstEnv->szTag));
+ AssertRCReturn(rc, rc);
+ }
+
+ if (!RTStrNLen(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp)))
+ {
+ rc = AudioTestPathGetTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp));
+ AssertRCReturn(rc, rc);
+ }
+
+ if (!RTStrNLen(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut)))
+ {
+ rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), pTstEnv->szPathTemp, "vkat-temp");
+ AssertRCReturn(rc, rc);
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Initializing environment for mode '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
+
+ char szPathTemp[RTPATH_MAX];
+ if ( !strlen(pTstEnv->szPathTemp)
+ || !strlen(pTstEnv->szPathOut))
+ rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
+
+ if ( RT_SUCCESS(rc)
+ && !strlen(pTstEnv->szPathTemp))
+ rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirCreate(pTstEnv->szPathTemp, RTFS_UNIX_IRWXU, 0 /* fFlags */);
+ if (rc == VERR_ALREADY_EXISTS)
+ rc = VINF_SUCCESS;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && !strlen(pTstEnv->szPathOut))
+ rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirCreate(pTstEnv->szPathOut, RTFS_UNIX_IRWXU, 0 /* fFlags */);
+ if (rc == VERR_ALREADY_EXISTS)
+ rc = VINF_SUCCESS;
+ }
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /**
+ * For NAT'ed VMs we use (default):
+ * - client mode (uConnectAddr / uConnectPort) on the guest.
+ * - server mode (uBindAddr / uBindPort) on the host.
+ */
+ if ( !pTstEnv->TcpOpts.szConnectAddr[0]
+ && !pTstEnv->TcpOpts.szBindAddr[0])
+ RTStrCopy(pTstEnv->TcpOpts.szBindAddr, sizeof(pTstEnv->TcpOpts.szBindAddr), "0.0.0.0");
+
+ /*
+ * Determine connection mode based on set variables.
+ */
+ if ( pTstEnv->TcpOpts.szBindAddr[0]
+ && pTstEnv->TcpOpts.szConnectAddr[0])
+ {
+ pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_BOTH;
+ }
+ else if (pTstEnv->TcpOpts.szBindAddr[0])
+ pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_SERVER;
+ else /* "Reversed mode", i.e. used for NATed VMs. */
+ pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
+
+ /* Set a back reference to the test environment for the callback context. */
+ pTstEnv->CallbackCtx.pTstEnv = pTstEnv;
+
+ ATSCALLBACKS Callbacks;
+ RT_ZERO(Callbacks);
+ Callbacks.pvUser = &pTstEnv->CallbackCtx;
+
+ if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
+ {
+ Callbacks.pfnHowdy = audioTestGstAtsHowdyCallback;
+ Callbacks.pfnBye = audioTestGstAtsByeCallback;
+ Callbacks.pfnTestSetBegin = audioTestGstAtsTestSetBeginCallback;
+ Callbacks.pfnTestSetEnd = audioTestGstAtsTestSetEndCallback;
+ Callbacks.pfnTonePlay = audioTestGstAtsTonePlayCallback;
+ Callbacks.pfnToneRecord = audioTestGstAtsToneRecordCallback;
+ Callbacks.pfnTestSetSendBegin = audioTestGstAtsTestSetSendBeginCallback;
+ Callbacks.pfnTestSetSendRead = audioTestGstAtsTestSetSendReadCallback;
+ Callbacks.pfnTestSetSendEnd = audioTestGstAtsTestSetSendEndCallback;
+
+ if (!pTstEnv->TcpOpts.uBindPort)
+ pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_GUEST;
+
+ if (!pTstEnv->TcpOpts.uConnectPort)
+ pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_GUEST;
+
+ pTstEnv->pSrv = (PATSSERVER)RTMemAlloc(sizeof(ATSSERVER));
+ AssertPtrReturn(pTstEnv->pSrv, VERR_NO_MEMORY);
+
+ /*
+ * Start the ATS (Audio Test Service) on the guest side.
+ * That service then will perform playback and recording operations on the guest, triggered from the host.
+ *
+ * When running this in self-test mode, that service also can be run on the host if nothing else is specified.
+ * Note that we have to bind to "0.0.0.0" by default so that the host can connect to it.
+ */
+ rc = audioTestEnvConfigureAndStartTcpServer(pTstEnv->pSrv, &Callbacks, "guest", &pTstEnv->TcpOpts);
+ }
+ else /* Host mode */
+ {
+ if (!pTstEnv->TcpOpts.uBindPort)
+ pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_HOST;
+
+ if (!pTstEnv->TcpOpts.uConnectPort)
+ pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_HOST_PORT_FWD;
+
+ /**
+ * Note: Don't set pTstEnv->TcpOpts.szTcpConnectAddr by default here, as this specifies what connection mode
+ * (client / server / both) we use on the host.
+ */
+
+ /* We need to start a server on the host so that VMs configured with NAT networking
+ * can connect to it as well. */
+ rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClGuest);
+ if (RT_SUCCESS(rc))
+ rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClGuest,
+ "host -> guest", &pTstEnv->TcpOpts);
+ if (RT_SUCCESS(rc))
+ {
+ AUDIOTESTENVTCPOPTS ValKitTcpOpts;
+ RT_ZERO(ValKitTcpOpts);
+
+ /* We only connect as client to the Validation Kit audio driver ATS. */
+ ValKitTcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
+
+ /* For now we ASSUME that the Validation Kit audio driver ATS runs on the same host as VKAT (this binary) runs on. */
+ ValKitTcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_VALKIT; /** @todo Make this dynamic. */
+ RTStrCopy(ValKitTcpOpts.szConnectAddr, sizeof(ValKitTcpOpts.szConnectAddr), ATS_TCP_DEF_CONNECT_HOST_ADDR_STR); /** @todo Ditto. */
+
+ rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClValKit);
+ if (RT_SUCCESS(rc))
+ {
+ rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClValKit,
+ "host -> valkit", &ValKitTcpOpts);
+ if (RT_FAILURE(rc))
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unable to connect to the Validation Kit audio driver!\n"
+ "There could be multiple reasons:\n\n"
+ " - Wrong host being used\n"
+ " - VirtualBox host version is too old\n"
+ " - Audio debug mode is not enabled\n"
+ " - Support for Validation Kit audio driver is not included\n"
+ " - Firewall / network configuration problem\n");
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Destroys an audio test environment.
+ *
+ * @param pTstEnv Audio test environment to destroy.
+ */
+void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
+{
+ if (!pTstEnv)
+ return;
+
+ /* When in host mode, we need to destroy our ATS clients in order to also let
+ * the ATS server(s) know we're going to quit. */
+ if (pTstEnv->enmMode == AUDIOTESTMODE_HOST)
+ {
+ AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClValKit);
+ AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClGuest);
+ }
+
+ if (pTstEnv->pSrv)
+ {
+ int rc2 = AudioTestSvcDestroy(pTstEnv->pSrv);
+ AssertRC(rc2);
+
+ RTMemFree(pTstEnv->pSrv);
+ pTstEnv->pSrv = NULL;
+ }
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
+ {
+ int rc2 = audioTestStreamDestroy(pTstEnv->pDrvStack, &pTstEnv->aStreams[i]);
+ if (RT_FAILURE(rc2))
+ RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
+ }
+
+ /* Try cleaning up a bit. */
+ RTDirRemove(pTstEnv->szPathTemp);
+ RTDirRemove(pTstEnv->szPathOut);
+
+ pTstEnv->pDrvStack = NULL;
+}
+
+/**
+ * Closes, packs up and destroys a test environment.
+ *
+ * @returns VBox status code.
+ * @param pTstEnv Test environment to handle.
+ * @param fPack Whether to pack the test set up before destroying / wiping it.
+ * @param pszPackFile Where to store the packed test set file on success. Can be NULL if \a fPack is \c false.
+ * @param cbPackFile Size (in bytes) of \a pszPackFile. Can be 0 if \a fPack is \c false.
+ */
+int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv, bool fPack, char *pszPackFile, size_t cbPackFile)
+{
+ /* Close the test set first. */
+ AudioTestSetClose(&pTstEnv->Set);
+
+ int rc = VINF_SUCCESS;
+
+ if (fPack)
+ {
+ /* Before destroying the test environment, pack up the test set so
+ * that it's ready for transmission. */
+ rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, pszPackFile, cbPackFile);
+ if (RT_SUCCESS(rc))
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", pszPackFile);
+ }
+
+ if (!g_fDrvAudioDebug) /* Don't wipe stuff when debugging. Can be useful for introspecting data. */
+ /* ignore rc */ AudioTestSetWipe(&pTstEnv->Set);
+
+ AudioTestSetDestroy(&pTstEnv->Set);
+
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * Initializes an audio test parameters set.
+ *
+ * @param pTstParms Test parameters set to initialize.
+ */
+void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
+{
+ RT_ZERO(*pTstParms);
+}
+
+/**
+ * Destroys an audio test parameters set.
+ *
+ * @param pTstParms Test parameters set to destroy.
+ */
+void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
+{
+ if (!pTstParms)
+ return;
+
+ return;
+}
+
diff --git a/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp b/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp
new file mode 100644
index 00000000..89dbb4ae
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp
@@ -0,0 +1,1621 @@
+/* $Id: vkatDriverStack.cpp $ */
+/** @file
+ * Validation Kit Audio Test (VKAT) - Driver stack code.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_AUDIO_TEST
+#include <iprt/log.h>
+
+#include <iprt/errcore.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/test.h>
+
+
+/**
+ * Internal driver instance data
+ * @note This must be put here as it's needed before pdmdrv.h is included.
+ */
+typedef struct PDMDRVINSINT
+{
+ /** The stack the drive belongs to. */
+ struct AUDIOTESTDRVSTACK *pStack;
+} PDMDRVINSINT;
+#define PDMDRVINSINT_DECLARED
+
+#include "vkatInternal.h"
+#include "VBoxDD.h"
+
+
+
+/*********************************************************************************************************************************
+* Fake PDM Driver Handling. *
+*********************************************************************************************************************************/
+
+/** @name Driver Fakes/Stubs
+ *
+ * @{ */
+
+VMMR3DECL(PCFGMNODE) audioTestDrvHlp_CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath)
+{
+ RT_NOREF(pNode, pszPath);
+ return NULL;
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString)
+{
+ if (pNode != NULL)
+ {
+ PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
+ if (g_uVerbosity > 2)
+ RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString);
+
+ if ( ( strcmp(pDrvReg->szName, "PulseAudio") == 0
+ || strcmp(pDrvReg->szName, "HostAudioWas") == 0)
+ && strcmp(pszName, "VmName") == 0)
+ return RTStrCopy(pszString, cchString, "vkat");
+
+ if ( strcmp(pDrvReg->szName, "HostAudioWas") == 0
+ && strcmp(pszName, "VmUuid") == 0)
+ return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e");
+ }
+ else if (g_uVerbosity > 2)
+ RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString);
+
+ return VERR_CFGM_VALUE_NOT_FOUND;
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString)
+{
+ char szStr[128];
+ int rc = audioTestDrvHlp_CFGMR3QueryString(pNode, pszName, szStr, sizeof(szStr));
+ if (RT_SUCCESS(rc))
+ *ppszString = RTStrDup(szStr);
+
+ return rc;
+}
+
+
+VMMR3DECL(void) audioTestDrvHlp_MMR3HeapFree(PPDMDRVINS pDrvIns, void *pv)
+{
+ RT_NOREF(pDrvIns);
+
+ /* counterpart to CFGMR3QueryStringAlloc */
+ RTStrFree((char *)pv);
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
+{
+ PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
+ if (RT_VALID_PTR(pDrvReg))
+ {
+ const char *pszRet = pszDef;
+ if ( g_pszDrvAudioDebug
+ && strcmp(pDrvReg->szName, "AUDIO") == 0
+ && strcmp(pszName, "DebugPathOut") == 0)
+ pszRet = g_pszDrvAudioDebug;
+
+ int rc = RTStrCopy(pszString, cchString, pszRet);
+
+ if (g_uVerbosity > 2)
+ RTPrintf("debug: CFGMR3QueryStringDef([%s], %s, %p, %#x, %s) -> '%s' + %Rrc\n",
+ pDrvReg->szName, pszName, pszString, cchString, pszDef, pszRet, rc);
+ return rc;
+ }
+
+ if (g_uVerbosity > 2)
+ RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef);
+ return RTStrCopy(pszString, cchString, pszDef);
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef)
+{
+ PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
+ if (RT_VALID_PTR(pDrvReg))
+ {
+ *pf = fDef;
+ if ( strcmp(pDrvReg->szName, "AUDIO") == 0
+ && strcmp(pszName, "DebugEnabled") == 0)
+ *pf = g_fDrvAudioDebug;
+
+ if (g_uVerbosity > 2)
+ RTPrintf("debug: CFGMR3QueryBoolDef([%s], %s, %p, %RTbool) -> %RTbool\n", pDrvReg->szName, pszName, pf, fDef, *pf);
+ return VINF_SUCCESS;
+ }
+ *pf = fDef;
+ return VINF_SUCCESS;
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8)
+{
+ RT_NOREF(pNode, pszName, pu8);
+ return VERR_CFGM_VALUE_NOT_FOUND;
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32)
+{
+ RT_NOREF(pNode, pszName, pu32);
+ return VERR_CFGM_VALUE_NOT_FOUND;
+}
+
+
+VMMR3DECL(int) audioTestDrvHlp_CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode,
+ const char *pszValidValues, const char *pszValidNodes,
+ const char *pszWho, uint32_t uInstance)
+{
+ RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance);
+ return VINF_SUCCESS;
+}
+
+/** @} */
+
+/** @name Driver Helper Fakes
+ * @{ */
+
+static DECLCALLBACK(int) audioTestDrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface)
+{
+ /* DrvAudio must be allowed to attach the backend driver (paranoid
+ backend drivers may call us to check that nothing is attached). */
+ if (strcmp(pDrvIns->pReg->szName, "AUDIO") == 0)
+ {
+ PAUDIOTESTDRVSTACK pDrvStack = pDrvIns->Internal.s.pStack;
+ AssertReturn(pDrvStack->pDrvBackendIns == NULL, VERR_PDM_DRIVER_ALREADY_ATTACHED);
+
+ if (g_uVerbosity > 1)
+ RTMsgInfo("Attaching backend '%s' to DrvAudio...\n", pDrvStack->pDrvReg->szName);
+ int rc = audioTestDrvConstruct(pDrvStack, pDrvStack->pDrvReg, pDrvIns, &pDrvStack->pDrvBackendIns);
+ if (RT_SUCCESS(rc))
+ {
+ if (ppBaseInterface)
+ *ppBaseInterface = &pDrvStack->pDrvBackendIns->IBase;
+ }
+ else
+ RTMsgError("Failed to attach backend: %Rrc", rc);
+ return rc;
+ }
+ RT_NOREF(fFlags);
+ return VERR_PDM_NO_ATTACHED_DRIVER;
+}
+
+
+static DECLCALLBACK(void) audioTestDrvHlp_STAMRegister(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, const char *pszName,
+ STAMUNIT enmUnit, const char *pszDesc)
+{
+ RT_NOREF(pDrvIns, pvSample, enmType, pszName, enmUnit, pszDesc);
+}
+
+
+static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
+ STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
+ const char *pszName, ...)
+{
+ RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName);
+}
+
+
+static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
+ STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
+ const char *pszName, va_list args)
+{
+ RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
+}
+
+
+static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample)
+{
+ RT_NOREF(pDrvIns, pvSample);
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix)
+{
+ RT_NOREF(pDrvIns, pszPrefix);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Get the driver helpers.
+ */
+static const PDMDRVHLPR3 *audioTestFakeGetDrvHlp(void)
+{
+ /*
+ * Note! No initializer for s_DrvHlp (also why it's not a file global).
+ * We do not want to have to update this code every time PDMDRVHLPR3
+ * grows new entries or are otherwise modified. Only when the
+ * entries used by the audio driver changes do we want to change
+ * our code.
+ */
+ static PDMDRVHLPR3 s_DrvHlp;
+ if (s_DrvHlp.u32Version != PDM_DRVHLPR3_VERSION)
+ {
+ s_DrvHlp.u32Version = PDM_DRVHLPR3_VERSION;
+ s_DrvHlp.u32TheEnd = PDM_DRVHLPR3_VERSION;
+ s_DrvHlp.pfnAttach = audioTestDrvHlp_Attach;
+ s_DrvHlp.pfnSTAMRegister = audioTestDrvHlp_STAMRegister;
+ s_DrvHlp.pfnSTAMRegisterF = audioTestDrvHlp_STAMRegisterF;
+ s_DrvHlp.pfnSTAMRegisterV = audioTestDrvHlp_STAMRegisterV;
+ s_DrvHlp.pfnSTAMDeregister = audioTestDrvHlp_STAMDeregister;
+ s_DrvHlp.pfnSTAMDeregisterByPrefix = audioTestDrvHlp_STAMDeregisterByPrefix;
+ s_DrvHlp.pfnCFGMGetChild = audioTestDrvHlp_CFGMR3GetChild;
+ s_DrvHlp.pfnCFGMQueryString = audioTestDrvHlp_CFGMR3QueryString;
+ s_DrvHlp.pfnCFGMQueryStringAlloc = audioTestDrvHlp_CFGMR3QueryStringAlloc;
+ s_DrvHlp.pfnMMHeapFree = audioTestDrvHlp_MMR3HeapFree;
+ s_DrvHlp.pfnCFGMQueryStringDef = audioTestDrvHlp_CFGMR3QueryStringDef;
+ s_DrvHlp.pfnCFGMQueryBoolDef = audioTestDrvHlp_CFGMR3QueryBoolDef;
+ s_DrvHlp.pfnCFGMQueryU8 = audioTestDrvHlp_CFGMR3QueryU8;
+ s_DrvHlp.pfnCFGMQueryU32 = audioTestDrvHlp_CFGMR3QueryU32;
+ s_DrvHlp.pfnCFGMValidateConfig = audioTestDrvHlp_CFGMR3ValidateConfig;
+ }
+ return &s_DrvHlp;
+}
+
+/** @} */
+
+
+/**
+ * Implementation of PDMIBASE::pfnQueryInterface for a fake device above
+ * DrvAudio.
+ */
+static DECLCALLBACK(void *) audioTestFakeDeviceIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
+ RTMsgWarning("audioTestFakeDeviceIBaseQueryInterface: Unknown interface: %s\n", pszIID);
+ return NULL;
+}
+
+/** IBase interface for a fake device above DrvAudio. */
+static PDMIBASE g_AudioTestFakeDeviceIBase = { audioTestFakeDeviceIBaseQueryInterface };
+
+
+static DECLCALLBACK(int) audioTestIHostAudioPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream,
+ uintptr_t uUser, void *pvUser)
+{
+ RT_NOREF(pInterface, pStream, uUser, pvUser);
+ RTMsgWarning("audioTestIHostAudioPort_DoOnWorkerThread was called\n");
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser)
+{
+ RT_NOREF(pInterface, enmDir, pvUser);
+ RTMsgWarning("audioTestIHostAudioPort_NotifyDeviceChanged was called\n");
+}
+
+
+static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface,
+ PPDMAUDIOBACKENDSTREAM pStream)
+{
+ RT_NOREF(pInterface, pStream);
+ RTMsgWarning("audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch was called\n");
+}
+
+
+static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface,
+ PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
+{
+ RT_NOREF(pInterface, pStream, fReInit);
+ RTMsgWarning("audioTestIHostAudioPort_StreamNotifyDeviceChanged was called\n");
+}
+
+
+static DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface)
+{
+ RT_NOREF(pInterface);
+ RTMsgWarning("audioTestIHostAudioPort_NotifyDevicesChanged was called\n");
+}
+
+
+static PDMIHOSTAUDIOPORT g_AudioTestIHostAudioPort =
+{
+ audioTestIHostAudioPort_DoOnWorkerThread,
+ audioTestIHostAudioPort_NotifyDeviceChanged,
+ audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch,
+ audioTestIHostAudioPort_StreamNotifyDeviceChanged,
+ audioTestIHostAudioPort_NotifyDevicesChanged,
+};
+
+
+/**
+ * Implementation of PDMIBASE::pfnQueryInterface for a fake DrvAudio above a
+ * backend.
+ */
+static DECLCALLBACK(void *) audioTestFakeDrvAudioIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &g_AudioTestIHostAudioPort);
+ RTMsgWarning("audioTestFakeDrvAudioIBaseQueryInterface: Unknown interface: %s\n", pszIID);
+ return NULL;
+}
+
+
+/** IBase interface for a fake DrvAudio above a lonesome backend. */
+static PDMIBASE g_AudioTestFakeDrvAudioIBase = { audioTestFakeDrvAudioIBaseQueryInterface };
+
+
+
+/**
+ * Constructs a PDM audio driver instance.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The stack this is associated with.
+ * @param pDrvReg PDM driver registration record to use for construction.
+ * @param pParentDrvIns The parent driver (if any).
+ * @param ppDrvIns Where to return the driver instance structure.
+ */
+int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
+ PPPDMDRVINS ppDrvIns)
+{
+ /* The destruct function must have valid data to work with. */
+ *ppDrvIns = NULL;
+
+ /*
+ * Check registration structure validation (doesn't need to be too
+ * thorough, PDM check it in detail on every VM startup).
+ */
+ AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER);
+ RTMsgInfo("Initializing backend '%s' ...\n", pDrvReg->szName);
+ AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the instance data structure.
+ */
+ PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance]));
+ RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY);
+
+ pDrvIns->u32Version = PDM_DRVINS_VERSION;
+ pDrvIns->iInstance = 0;
+ pDrvIns->pHlpR3 = audioTestFakeGetDrvHlp();
+ pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0];
+ pDrvIns->pReg = pDrvReg;
+ pDrvIns->pCfg = (PCFGMNODE)pDrvReg;
+ pDrvIns->Internal.s.pStack = pDrvStack;
+ pDrvIns->pUpBase = NULL;
+ pDrvIns->pDownBase = NULL;
+ if (pParentDrvIns)
+ {
+ Assert(pParentDrvIns->pDownBase == NULL);
+ pParentDrvIns->pDownBase = &pDrvIns->IBase;
+ pDrvIns->pUpBase = &pParentDrvIns->IBase;
+ }
+ else if (strcmp(pDrvReg->szName, "AUDIO") == 0)
+ pDrvIns->pUpBase = &g_AudioTestFakeDeviceIBase;
+ else
+ pDrvIns->pUpBase = &g_AudioTestFakeDrvAudioIBase;
+
+ /*
+ * Invoke the constructor.
+ */
+ int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ *ppDrvIns = pDrvIns;
+ return VINF_SUCCESS;
+ }
+
+ if (pDrvReg->pfnDestruct)
+ pDrvReg->pfnDestruct(pDrvIns);
+ RTMemFree(pDrvIns);
+ return rc;
+}
+
+
+/**
+ * Destructs a PDM audio driver instance.
+ *
+ * @param pDrvIns Driver instance to destruct.
+ */
+static void audioTestDrvDestruct(PPDMDRVINS pDrvIns)
+{
+ if (pDrvIns)
+ {
+ Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
+
+ if (pDrvIns->pReg->pfnDestruct)
+ pDrvIns->pReg->pfnDestruct(pDrvIns);
+
+ pDrvIns->u32Version = 0;
+ pDrvIns->pReg = NULL;
+ RTMemFree(pDrvIns);
+ }
+}
+
+
+/**
+ * Sends the PDM driver a power off notification.
+ *
+ * @param pDrvIns Driver instance to notify.
+ */
+static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns)
+{
+ if (pDrvIns)
+ {
+ Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
+ if (pDrvIns->pReg->pfnPowerOff)
+ pDrvIns->pReg->pfnPowerOff(pDrvIns);
+ }
+}
+
+
+/**
+ * Deletes a driver stack.
+ *
+ * This will power off and destroy the drivers.
+ */
+void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
+{
+ /*
+ * Do power off notifications (top to bottom).
+ */
+ audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns);
+ audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns);
+
+ /*
+ * Drivers are destroyed from bottom to top (closest to the device).
+ */
+ audioTestDrvDestruct(pDrvStack->pDrvBackendIns);
+ pDrvStack->pDrvBackendIns = NULL;
+ pDrvStack->pIHostAudio = NULL;
+
+ audioTestDrvDestruct(pDrvStack->pDrvAudioIns);
+ pDrvStack->pDrvAudioIns = NULL;
+ pDrvStack->pIAudioConnector = NULL;
+
+ PDMAudioHostEnumDelete(&pDrvStack->DevEnum);
+}
+
+
+/**
+ * Initializes a driver stack, extended version.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The driver stack to initialize.
+ * @param pDrvReg The backend driver to use.
+ * @param fEnabledIn Whether input is enabled or not on creation time.
+ * @param fEnabledOut Whether output is enabled or not on creation time.
+ * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
+ */
+int audioTestDriverStackInitEx(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio)
+{
+ int rc;
+
+ RT_ZERO(*pDrvStack);
+ pDrvStack->pDrvReg = pDrvReg;
+
+ PDMAudioHostEnumInit(&pDrvStack->DevEnum);
+
+ if (!fWithDrvAudio)
+ rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns);
+ else
+ {
+ rc = audioTestDrvConstruct(pDrvStack, &g_DrvAUDIO, NULL /*pParentDrvIns*/, &pDrvStack->pDrvAudioIns);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pDrvStack->pDrvAudioIns);
+ PPDMIBASE const pIBase = &pDrvStack->pDrvAudioIns->IBase;
+ pDrvStack->pIAudioConnector = (PPDMIAUDIOCONNECTOR)pIBase->pfnQueryInterface(pIBase, PDMIAUDIOCONNECTOR_IID);
+ if (pDrvStack->pIAudioConnector)
+ {
+ /* Both input and output is disabled by default. */
+ if (fEnabledIn)
+ rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_IN, true);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (fEnabledOut)
+ rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_OUT, true);
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "Failed to enabled input and output: %Rrc", rc);
+ audioTestDriverStackDelete(pDrvStack);
+ }
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "Failed to query PDMIAUDIOCONNECTOR");
+ audioTestDriverStackDelete(pDrvStack);
+ rc = VERR_PDM_MISSING_INTERFACE;
+ }
+ }
+ }
+
+ /*
+ * Get the IHostAudio interface and check that the host driver is working.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ PPDMIBASE const pIBase = &pDrvStack->pDrvBackendIns->IBase;
+ pDrvStack->pIHostAudio = (PPDMIHOSTAUDIO)pIBase->pfnQueryInterface(pIBase, PDMIHOSTAUDIO_IID);
+ if (pDrvStack->pIHostAudio)
+ {
+ PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT);
+ if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING)
+ return VINF_SUCCESS;
+
+ RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus);
+ }
+ else
+ RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName);
+ audioTestDriverStackDelete(pDrvStack);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initializes a driver stack.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The driver stack to initialize.
+ * @param pDrvReg The backend driver to use.
+ * @param fEnabledIn Whether input is enabled or not on creation time.
+ * @param fEnabledOut Whether output is enabled or not on creation time.
+ * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
+ */
+int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
+{
+ return audioTestDriverStackInitEx(pDrvStack, pDrvReg, true /* fEnabledIn */, true /* fEnabledOut */, fWithDrvAudio);
+}
+
+/**
+ * Initializes a driver stack by probing all backends in the order of appearance
+ * in the backends description table.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The driver stack to initialize.
+ * @param pDrvReg The backend driver to use.
+ * @param fEnabledIn Whether input is enabled or not on creation time.
+ * @param fEnabledOut Whether output is enabled or not on creation time.
+ * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
+ */
+int audioTestDriverStackProbe(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio)
+{
+ int rc = VERR_IPE_UNINITIALIZED_STATUS; /* Shut up MSVC. */
+
+ for (size_t i = 0; i < g_cBackends; i++)
+ {
+ pDrvReg = g_aBackends[i].pDrvReg;
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing for backend '%s' ...\n", g_aBackends[i].pszName);
+
+ rc = audioTestDriverStackInitEx(pDrvStack, pDrvReg, fEnabledIn, fEnabledOut, fWithDrvAudio); /** @todo Make in/out configurable, too. */
+ if (RT_SUCCESS(rc))
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' successful\n", g_aBackends[i].pszName);
+ return rc;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' failed with %Rrc, trying next one\n",
+ g_aBackends[i].pszName, rc);
+ continue;
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing all backends failed\n");
+ return rc;
+}
+
+/**
+ * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
+ */
+int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
+{
+ int rc;
+ if ( pDrvStack->pIHostAudio
+ && pDrvStack->pIHostAudio->pfnSetDevice)
+ rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId);
+ else if (!pszDevId || *pszDevId)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_INVALID_FUNCTION;
+ return rc;
+}
+
+
+/**
+ * Common stream creation code.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The audio driver stack to create it via.
+ * @param pCfgReq The requested config.
+ * @param ppStream Where to return the stream pointer on success.
+ * @param pCfgAcq Where to return the actual (well, not necessarily when
+ * using DrvAudio, but probably the same) stream config on
+ * success (not used as input).
+ */
+static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOSTREAMCFG pCfgReq,
+ PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+ char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16];
+ int rc;
+ *ppStream = NULL;
+
+ if (pDrvStack->pIAudioConnector)
+ {
+ /*
+ * DrvAudio does most of the work here.
+ */
+ rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, 0 /*fFlags*/, pCfgReq, ppStream);
+ if (RT_SUCCESS(rc))
+ {
+ *pCfgAcq = (*ppStream)->Cfg;
+ RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp)));
+ return rc;
+ }
+ /* else: Don't set RTTestFailed(...) here, as test boxes (servers) don't have any audio hardware.
+ * Caller has check the rc then. */
+ }
+ else
+ {
+ /*
+ * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM
+ * structure actually is for this backend.
+ */
+ PDMAUDIOBACKENDCFG BackendCfg;
+ rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg);
+ if (RT_SUCCESS(rc))
+ {
+ if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM))
+ {
+ /*
+ * Allocate and initialize the stream.
+ */
+ uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream;
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream);
+ if (pStreamAt)
+ {
+ pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
+ pStreamAt->Core.Cfg = *pCfgReq;
+ pStreamAt->Core.cbBackend = cbStream;
+
+ pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC;
+ pStreamAt->Backend.pStream = &pStreamAt->Core;
+
+ /*
+ * Call the backend to create the stream.
+ */
+ rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend,
+ pCfgReq, &pStreamAt->Core.Cfg);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 1)
+ RTMsgInfo("Created backend stream: %s\n",
+ PDMAudioStrmCfgToString(&pStreamAt->Core.Cfg, szTmp, sizeof(szTmp)));
+
+ /* Return if stream is ready: */
+ if (rc == VINF_SUCCESS)
+ {
+ *ppStream = &pStreamAt->Core;
+ *pCfgAcq = pStreamAt->Core.Cfg;
+ return VINF_SUCCESS;
+ }
+ if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
+ {
+ /*
+ * Do async init right here and now.
+ */
+ rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend,
+ false /*fDestroyed*/);
+ if (RT_SUCCESS(rc))
+ {
+ *ppStream = &pStreamAt->Core;
+ *pCfgAcq = pStreamAt->Core.Cfg;
+ return VINF_SUCCESS;
+ }
+
+ RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc);
+ rc = VERR_IPE_UNEXPECTED_INFO_STATUS;
+ }
+ pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
+ }
+ /* else: Don't set RTTestFailed(...) here, as test boxes (servers) don't have any audio hardware.
+ * Caller has check the rc then. */
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "Out of memory!\n");
+ rc = VERR_NO_MEMORY;
+ }
+ RTMemFree(pStreamAt);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM));
+ rc = VERR_OUT_OF_RANGE;
+ }
+ }
+ else
+ RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates an output stream.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The audio driver stack to create it via.
+ * @param pProps The audio properties to use.
+ * @param cMsBufferSize The buffer size in milliseconds.
+ * @param cMsPreBuffer The pre-buffering amount in milliseconds.
+ * @param cMsSchedulingHint The scheduling hint in milliseconds.
+ * @param ppStream Where to return the stream pointer on success.
+ * @param pCfgAcq Where to return the actual (well, not
+ * necessarily when using DrvAudio, but probably
+ * the same) stream config on success (not used as
+ * input).
+ */
+int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
+ uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+ PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+ /*
+ * Calculate the stream config.
+ */
+ PDMAUDIOSTREAMCFG CfgReq;
+ int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
+ AssertRC(rc);
+ CfgReq.enmDir = PDMAUDIODIR_OUT;
+ CfgReq.enmPath = PDMAUDIOPATH_OUT_FRONT;
+ CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
+ ? 10 : cMsSchedulingHint;
+ if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
+ CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
+ else
+ CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
+ cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
+ ? 300 : cMsBufferSize);
+ if (cMsPreBuffer == UINT32_MAX)
+ CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */
+ : CfgReq.Backend.cFramesBufferSize * 2 / 3;
+ else
+ CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
+ if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16
+ && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
+ {
+ RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
+ CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
+ CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
+ ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
+ }
+
+ static uint32_t s_idxStream = 0;
+ uint32_t const idxStream = s_idxStream++;
+ RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream);
+
+ /*
+ * Call common code to do the actual work.
+ */
+ return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
+}
+
+
+/**
+ * Creates an input stream.
+ *
+ * @returns VBox status code.
+ * @param pDrvStack The audio driver stack to create it via.
+ * @param pProps The audio properties to use.
+ * @param cMsBufferSize The buffer size in milliseconds.
+ * @param cMsPreBuffer The pre-buffering amount in milliseconds.
+ * @param cMsSchedulingHint The scheduling hint in milliseconds.
+ * @param ppStream Where to return the stream pointer on success.
+ * @param pCfgAcq Where to return the actual (well, not
+ * necessarily when using DrvAudio, but probably
+ * the same) stream config on success (not used as
+ * input).
+ */
+int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
+ uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+ PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+ /*
+ * Calculate the stream config.
+ */
+ PDMAUDIOSTREAMCFG CfgReq;
+ int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
+ AssertRC(rc);
+ CfgReq.enmDir = PDMAUDIODIR_IN;
+ CfgReq.enmPath = PDMAUDIOPATH_IN_LINE;
+ CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
+ ? 10 : cMsSchedulingHint;
+ if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
+ CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
+ else
+ CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
+ cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
+ ? 300 : cMsBufferSize);
+ if (cMsPreBuffer == UINT32_MAX)
+ CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */
+ : CfgReq.Backend.cFramesBufferSize / 2;
+ else
+ CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
+ if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */
+ && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
+ {
+ RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
+ CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
+ CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
+ ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
+ }
+
+ static uint32_t s_idxStream = 0;
+ uint32_t const idxStream = s_idxStream++;
+ RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream);
+
+ /*
+ * Call common code to do the actual work.
+ */
+ return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
+}
+
+
+/**
+ * Destroys a stream.
+ *
+ * @param pDrvStack Driver stack the stream to destroy is assigned to.
+ * @param pStream Stream to destroy. Pointer will be NULL (invalid) after successful return.
+ */
+void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ if (!pStream)
+ return;
+
+ if (pDrvStack->pIAudioConnector)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IAudioConnector) ...\n", pStream->Cfg.szName);
+ int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc);
+ }
+ else
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IHostAudio) ...\n", pStream->Cfg.szName);
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
+ if (RT_SUCCESS(rc))
+ {
+ pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC;
+ pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC;
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' done\n", pStream->Cfg.szName);
+
+ RTMemFree(pStreamAt);
+
+ pStreamAt = NULL;
+ pStream = NULL;
+ }
+ else
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc);
+ }
+}
+
+
+/**
+ * Enables a stream.
+ */
+int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ int rc;
+ if (pDrvStack->pIAudioConnector)
+ {
+ rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
+ }
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ rc = pDrvStack->pIHostAudio->pfnStreamEnable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamEnable failed: %Rrc", rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Disables a stream.
+ */
+int AudioTestDriverStackStreamDisable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ int rc;
+ if (pDrvStack->pIAudioConnector)
+ {
+ rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DISABLE);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamControl/DISABLE failed: %Rrc", rc);
+ }
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ rc = pDrvStack->pIHostAudio->pfnStreamDisable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDisable failed: %Rrc", rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Drains an output stream.
+ */
+int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
+{
+ int rc;
+ if (pDrvStack->pIAudioConnector)
+ {
+ /*
+ * Issue the drain request.
+ */
+ rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN);
+ if (RT_SUCCESS(rc) && fSync)
+ {
+ /*
+ * This is a synchronous drain, so wait for the driver to change state to inactive.
+ */
+ PDMAUDIOSTREAMSTATE enmState;
+ while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream))
+ >= PDMAUDIOSTREAMSTATE_ENABLED)
+ {
+ RTThreadSleep(2);
+ rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc);
+ break;
+ }
+ }
+ if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE)
+ {
+ RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState));
+ rc = VERR_AUDIO_STREAM_NOT_READY;
+ }
+ }
+ else if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
+ }
+ else
+ {
+ /*
+ * Issue the drain request.
+ */
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ rc = pDrvStack->pIHostAudio->pfnStreamDrain(pDrvStack->pIHostAudio, &pStreamAt->Backend);
+ if (RT_SUCCESS(rc) && fSync)
+ {
+ RTMSINTERVAL const msTimeout = RT_MS_5MIN; /* 5 minutes should be really enough for draining our stuff. */
+ uint64_t const tsStart = RTTimeMilliTS();
+
+ /*
+ * This is a synchronous drain, so wait for the driver to change state to inactive.
+ */
+ PDMHOSTAUDIOSTREAMSTATE enmHostState;
+ while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend))
+ == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
+ {
+ RTThreadSleep(2);
+ uint32_t cbWritten = UINT32_MAX;
+ rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend,
+ NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc);
+ break;
+ }
+ if (cbWritten != 0)
+ {
+ RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten);
+ rc = VERR_MISSING;
+ break;
+ }
+
+ /* Fail-safe for audio stacks and/or implementations which mess up draining.
+ *
+ * Note: On some testboxes draining never seems to finish and thus is getting aborted, no clue why.
+ * The test result in the end still could be correct, although the actual draining problem
+ * needs to be investigated further.
+ *
+ * So don't make this (and the stream state check below) an error for now and just warn about it.
+ *
+ ** @todo Investigate draining issues on testboxes.
+ */
+ if (RTTimeMilliTS() - tsStart > msTimeout)
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Warning: Draining stream took too long (timeout is %RU32ms), giving up", msTimeout);
+ break;
+ }
+ }
+ if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY)
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Warning: Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState));
+ }
+ else if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Checks if the stream is okay.
+ * @returns true if okay, false if not.
+ */
+bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ /*
+ * Get the stream status and check if it means is okay or not.
+ */
+ bool fRc = false;
+ if (pDrvStack->pIAudioConnector)
+ {
+ PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream);
+ switch (enmState)
+ {
+ case PDMAUDIOSTREAMSTATE_NOT_WORKING:
+ case PDMAUDIOSTREAMSTATE_NEED_REINIT:
+ break;
+ case PDMAUDIOSTREAMSTATE_INACTIVE:
+ case PDMAUDIOSTREAMSTATE_ENABLED:
+ case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
+ case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
+ fRc = true;
+ break;
+ /* no default */
+ case PDMAUDIOSTREAMSTATE_INVALID:
+ case PDMAUDIOSTREAMSTATE_END:
+ case PDMAUDIOSTREAMSTATE_32BIT_HACK:
+ break;
+ }
+ }
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio,
+ &pStreamAt->Backend);
+ switch (enmHostState)
+ {
+ case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
+ case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
+ break;
+ case PDMHOSTAUDIOSTREAMSTATE_OKAY:
+ case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
+ case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
+ fRc = true;
+ break;
+ /* no default */
+ case PDMHOSTAUDIOSTREAMSTATE_INVALID:
+ case PDMHOSTAUDIOSTREAMSTATE_END:
+ case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
+ break;
+ }
+ }
+ return fRc;
+}
+
+
+/**
+ * Gets the number of bytes it's currently possible to write to the stream.
+ */
+uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ uint32_t cbWritable;
+ if (pDrvStack->pIAudioConnector)
+ cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream);
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
+ }
+ return cbWritable;
+}
+
+
+/**
+ * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
+ */
+int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
+ void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
+{
+ int rc;
+ if (pDrvStack->pIAudioConnector)
+ {
+ rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
+ }
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the number of bytes it's currently possible to write to the stream.
+ */
+uint32_t audioTestDriverStackStreamGetReadable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
+{
+ uint32_t cbReadable;
+ if (pDrvStack->pIAudioConnector)
+ cbReadable = pDrvStack->pIAudioConnector->pfnStreamGetReadable(pDrvStack->pIAudioConnector, pStream);
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ cbReadable = pDrvStack->pIHostAudio->pfnStreamGetReadable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
+ }
+ return cbReadable;
+}
+
+
+/**
+ * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
+ */
+int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
+ void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
+{
+ int rc;
+ if (pDrvStack->pIAudioConnector)
+ {
+ rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
+ }
+ else
+ {
+ PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
+ rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured);
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
+ }
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Mixed streams *
+*********************************************************************************************************************************/
+
+/**
+ * Initializing mixing for a stream.
+ *
+ * This can be used as a do-nothing wrapper for the stack.
+ *
+ * @returns VBox status code.
+ * @param pMix The mixing state.
+ * @param pStream The stream to mix to/from.
+ * @param pProps The mixer properties. Pass NULL for no mixing, just
+ * wrap the driver stack functionality.
+ * @param cMsBuffer The buffer size.
+ */
+int AudioTestMixStreamInit(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
+ PCPDMAUDIOPCMPROPS pProps, uint32_t cMsBuffer)
+{
+ RT_ZERO(*pMix);
+
+ AssertReturn(pDrvStack, VERR_INVALID_PARAMETER);
+ AssertReturn(pStream, VERR_INVALID_PARAMETER);
+
+ pMix->pDrvStack = pDrvStack;
+ pMix->pStream = pStream;
+ if (!pProps)
+ {
+ pMix->pProps = &pStream->Cfg.Props;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Okay, we're doing mixing so we need to set up the mixer buffer
+ * and associated states.
+ */
+ pMix->fDoMixing = true;
+ int rc = AudioMixBufInit(&pMix->MixBuf, "mixer", pProps, PDMAudioPropsMilliToFrames(pProps, cMsBuffer));
+ if (RT_SUCCESS(rc))
+ {
+ pMix->pProps = &pMix->MixBuf.Props;
+
+ if (pStream->Cfg.enmDir == PDMAUDIODIR_IN)
+ {
+ rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pMix->MixBuf.Props);
+ if (RT_SUCCESS(rc))
+ {
+ rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pStream->Cfg.Props);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ }
+ else if (pStream->Cfg.enmDir == PDMAUDIODIR_OUT)
+ {
+ rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pMix->MixBuf.Props);
+ if (RT_SUCCESS(rc))
+ {
+ rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pStream->Cfg.Props);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "Bogus stream direction!");
+ rc = VERR_INVALID_STATE;
+ }
+ }
+ else
+ RTTestFailed(g_hTest, "AudioMixBufInit failed: %Rrc", rc);
+ RT_ZERO(*pMix);
+ return rc;
+}
+
+
+/**
+ * Terminate mixing (leaves the stream untouched).
+ *
+ * @param pMix The mixing state.
+ */
+void AudioTestMixStreamTerm(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ if (pMix->fDoMixing)
+ {
+ AudioMixBufTerm(&pMix->MixBuf);
+ pMix->pStream = NULL;
+ }
+ RT_ZERO(*pMix);
+}
+
+
+/**
+ * Worker that transports data between the mixer buffer and the drivers.
+ *
+ * @returns VBox status code.
+ * @param pMix The mixer stream setup to do transfers for.
+ */
+static int audioTestMixStreamTransfer(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ uint8_t abBuf[16384];
+ if (pMix->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
+ {
+ /*
+ * Try fill up the mixer buffer as much as possible.
+ *
+ * Slight fun part is that we have to calculate conversion
+ * ratio and be rather pessimistic about it.
+ */
+ uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->pStream->Cfg.Props, sizeof(abBuf));
+ for (;;)
+ {
+ /*
+ * Figure out how much we can move in this iteration.
+ */
+ uint32_t cDstFrames = AudioMixBufFree(&pMix->MixBuf);
+ if (!cDstFrames)
+ break;
+
+ uint32_t cbReadable = audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
+ if (!cbReadable)
+ break;
+
+ uint32_t cbToRead;
+ if (PDMAudioPropsHz(&pMix->pStream->Cfg.Props) == PDMAudioPropsHz(&pMix->MixBuf.Props))
+ cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props, cDstFrames);
+ else
+ cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props,
+ (uint64_t)cDstFrames * PDMAudioPropsHz(&pMix->pStream->Cfg.Props)
+ / PDMAudioPropsHz(&pMix->MixBuf.Props));
+ cbToRead = RT_MIN(cbToRead, RT_MIN(cbReadable, cbBuf));
+ if (!cbToRead)
+ break;
+
+ /*
+ * Get the data.
+ */
+ uint32_t cbCaptured = 0;
+ int rc = audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, abBuf, cbToRead, &cbCaptured);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(cbCaptured == cbToRead);
+ AssertBreak(cbCaptured > 0);
+
+ /*
+ * Feed it to the mixer.
+ */
+ uint32_t cDstFramesWritten = 0;
+ if ((abBuf[0] >> 4) & 1) /* some cheap random */
+ AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
+ 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
+ else
+ {
+ AudioMixBufSilence(&pMix->MixBuf, &pMix->WriteState, 0 /*offFrame*/, cDstFrames);
+ AudioMixBufBlend(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
+ 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
+ }
+ AudioMixBufCommit(&pMix->MixBuf, cDstFramesWritten);
+ }
+ }
+ else
+ {
+ /*
+ * The goal here is to empty the mixer buffer by transfering all
+ * the data to the drivers.
+ */
+ uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, sizeof(abBuf));
+ for (;;)
+ {
+ uint32_t cFrames = AudioMixBufUsed(&pMix->MixBuf);
+ if (!cFrames)
+ break;
+
+ uint32_t cbWritable = audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
+ if (!cbWritable)
+ break;
+
+ uint32_t cSrcFramesPeeked;
+ uint32_t cbDstPeeked;
+ AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cSrcFramesPeeked,
+ &pMix->PeekState, abBuf, RT_MIN(cbBuf, cbWritable), &cbDstPeeked);
+ AudioMixBufAdvance(&pMix->MixBuf, cSrcFramesPeeked);
+
+ if (!cbDstPeeked)
+ break;
+
+ uint32_t offBuf = 0;
+ while (offBuf < cbDstPeeked)
+ {
+ uint32_t cbPlayed = 0;
+ int rc = audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream,
+ &abBuf[offBuf], cbDstPeeked - offBuf, &cbPlayed);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!cbPlayed)
+ RTThreadSleep(1);
+ offBuf += cbPlayed;
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Same as audioTestDriverStackStreamEnable.
+ */
+int AudioTestMixStreamEnable(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ return audioTestDriverStackStreamEnable(pMix->pDrvStack, pMix->pStream);
+}
+
+
+/**
+ * Same as audioTestDriverStackStreamDrain.
+ */
+int AudioTestMixStreamDrain(PAUDIOTESTDRVMIXSTREAM pMix, bool fSync)
+{
+ /*
+ * If we're mixing, we must first make sure the buffer is empty.
+ */
+ if (pMix->fDoMixing)
+ {
+ audioTestMixStreamTransfer(pMix);
+ while (AudioMixBufUsed(&pMix->MixBuf) > 0)
+ {
+ RTThreadSleep(1);
+ audioTestMixStreamTransfer(pMix);
+ }
+ }
+
+ /*
+ * Then we do the regular work.
+ */
+ return audioTestDriverStackStreamDrain(pMix->pDrvStack, pMix->pStream, fSync);
+}
+
+/**
+ * Same as audioTestDriverStackStreamDisable.
+ */
+int AudioTestMixStreamDisable(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ return AudioTestDriverStackStreamDisable(pMix->pDrvStack, pMix->pStream);
+}
+
+
+/**
+ * Same as audioTestDriverStackStreamIsOkay.
+ */
+bool AudioTestMixStreamIsOkay(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ return audioTestDriverStackStreamIsOkay(pMix->pDrvStack, pMix->pStream);
+}
+
+
+/**
+ * Same as audioTestDriverStackStreamGetWritable
+ */
+uint32_t AudioTestMixStreamGetWritable(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ if (!pMix->fDoMixing)
+ return audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
+ uint32_t cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
+ if (!cbRet)
+ {
+ audioTestMixStreamTransfer(pMix);
+ cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
+ }
+ return cbRet;
+}
+
+
+
+
+/**
+ * Same as audioTestDriverStackStreamPlay.
+ */
+int AudioTestMixStreamPlay(PAUDIOTESTDRVMIXSTREAM pMix, void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
+{
+ if (!pMix->fDoMixing)
+ return audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbPlayed);
+
+ *pcbPlayed = 0;
+
+ int rc = audioTestMixStreamTransfer(pMix);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
+ while (cbBuf >= cbFrame)
+ {
+ uint32_t const cFrames = AudioMixBufFree(&pMix->MixBuf);
+ if (!cFrames)
+ break;
+ uint32_t cbToWrite = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
+ cbToWrite = RT_MIN(cbToWrite, cbBuf);
+ cbToWrite = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToWrite);
+
+ uint32_t cFramesWritten = 0;
+ AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, pvBuf, cbToWrite, 0 /*offDstFrame*/, cFrames, &cFramesWritten);
+ Assert(cFramesWritten == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbToWrite));
+ AudioMixBufCommit(&pMix->MixBuf, cFramesWritten);
+
+ *pcbPlayed += cbToWrite;
+ cbBuf -= cbToWrite;
+ pvBuf = (uint8_t const *)pvBuf + cbToWrite;
+
+ rc = audioTestMixStreamTransfer(pMix);
+ if (RT_FAILURE(rc))
+ return *pcbPlayed ? VINF_SUCCESS : rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Same as audioTestDriverStackStreamGetReadable
+ */
+uint32_t AudioTestMixStreamGetReadable(PAUDIOTESTDRVMIXSTREAM pMix)
+{
+ if (!pMix->fDoMixing)
+ return audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
+
+ audioTestMixStreamTransfer(pMix);
+ uint32_t cbRet = AudioMixBufUsedBytes(&pMix->MixBuf);
+ return cbRet;
+}
+
+
+
+
+/**
+ * Same as audioTestDriverStackStreamCapture.
+ */
+int AudioTestMixStreamCapture(PAUDIOTESTDRVMIXSTREAM pMix, void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
+{
+ if (!pMix->fDoMixing)
+ return audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbCaptured);
+
+ *pcbCaptured = 0;
+
+ int rc = audioTestMixStreamTransfer(pMix);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
+ while (cbBuf >= cbFrame)
+ {
+ uint32_t const cFrames = AudioMixBufUsed(&pMix->MixBuf);
+ if (!cFrames)
+ break;
+ uint32_t cbToRead = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
+ cbToRead = RT_MIN(cbToRead, cbBuf);
+ cbToRead = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToRead);
+
+ uint32_t cFramesPeeked = 0;
+ uint32_t cbPeeked = 0;
+ AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cFramesPeeked, &pMix->PeekState, pvBuf, cbToRead, &cbPeeked);
+ Assert(cFramesPeeked == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbPeeked));
+ AudioMixBufAdvance(&pMix->MixBuf, cFramesPeeked);
+
+ *pcbCaptured += cbToRead;
+ cbBuf -= cbToRead;
+ pvBuf = (uint8_t *)pvBuf + cbToRead;
+
+ rc = audioTestMixStreamTransfer(pMix);
+ if (RT_FAILURE(rc))
+ return *pcbCaptured ? VINF_SUCCESS : rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Sets the volume of a mixing stream.
+ *
+ * @param pMix Mixing stream to set volume for.
+ * @param uVolumePercent Volume to set (in percent, 0-100).
+ */
+void AudioTestMixStreamSetVolume(PAUDIOTESTDRVMIXSTREAM pMix, uint8_t uVolumePercent)
+{
+ AssertReturnVoid(pMix->fDoMixing);
+
+ uint8_t const uVol = (PDMAUDIO_VOLUME_MAX / 100) * uVolumePercent;
+
+ PDMAUDIOVOLUME Vol;
+ RT_ZERO(Vol);
+ for (size_t i = 0; i < RT_ELEMENTS(Vol.auChannels); i++)
+ Vol.auChannels[i] = uVol;
+ AudioMixBufSetVolume(&pMix->MixBuf, &Vol);
+}
+
diff --git a/src/VBox/ValidationKit/utils/audio/vkatInternal.h b/src/VBox/ValidationKit/utils/audio/vkatInternal.h
new file mode 100644
index 00000000..aed2ff6c
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/audio/vkatInternal.h
@@ -0,0 +1,547 @@
+/* $Id: vkatInternal.h $ */
+/** @file
+ * VKAT - Internal header file for common definitions + structs.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_audio_vkatInternal_h
+#define VBOX_INCLUDED_SRC_audio_vkatInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/getopt.h>
+
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmaudioinline.h>
+#include <VBox/vmm/pdmaudiohostenuminline.h>
+
+#include "Audio/AudioMixBuffer.h"
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Audio driver stack.
+ *
+ * This can be just be backend driver alone or DrvAudio with a backend.
+ * @todo add automatic resampling via mixer so we can test more of the audio
+ * stack used by the device emulations.
+ */
+typedef struct AUDIOTESTDRVSTACK
+{
+ /** The device registration record for the backend. */
+ PCPDMDRVREG pDrvReg;
+ /** The backend driver instance. */
+ PPDMDRVINS pDrvBackendIns;
+ /** The backend's audio interface. */
+ PPDMIHOSTAUDIO pIHostAudio;
+
+ /** The DrvAudio instance. */
+ PPDMDRVINS pDrvAudioIns;
+ /** This is NULL if we don't use DrvAudio. */
+ PPDMIAUDIOCONNECTOR pIAudioConnector;
+
+ /** The current (last) audio device enumeration to use. */
+ PDMAUDIOHOSTENUM DevEnum;
+} AUDIOTESTDRVSTACK;
+/** Pointer to an audio driver stack. */
+typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK;
+
+/**
+ * Backend-only stream structure.
+ */
+typedef struct AUDIOTESTDRVSTACKSTREAM
+{
+ /** The public stream data. */
+ PDMAUDIOSTREAM Core;
+ /** The backend data (variable size). */
+ PDMAUDIOBACKENDSTREAM Backend;
+} AUDIOTESTDRVSTACKSTREAM;
+/** Pointer to a backend-only stream structure. */
+typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM;
+
+/**
+ * Mixer setup for a stream.
+ */
+typedef struct AUDIOTESTDRVMIXSTREAM
+{
+ /** Pointer to the driver stack. */
+ PAUDIOTESTDRVSTACK pDrvStack;
+ /** Pointer to the stream. */
+ PPDMAUDIOSTREAM pStream;
+ /** Properties to use. */
+ PCPDMAUDIOPCMPROPS pProps;
+ /** Set if we're mixing or just passing thru to the driver stack. */
+ bool fDoMixing;
+ /** Mixer buffer. */
+ AUDIOMIXBUF MixBuf;
+ /** Write state. */
+ AUDIOMIXBUFWRITESTATE WriteState;
+ /** Peek state. */
+ AUDIOMIXBUFPEEKSTATE PeekState;
+} AUDIOTESTDRVMIXSTREAM;
+/** Pointer to mixer setup for a stream. */
+typedef AUDIOTESTDRVMIXSTREAM *PAUDIOTESTDRVMIXSTREAM;
+
+/**
+ * Enumeration specifying the current audio test mode.
+ */
+typedef enum AUDIOTESTMODE
+{
+ /** Unknown mode. */
+ AUDIOTESTMODE_UNKNOWN = 0,
+ /** VKAT is running on the guest side. */
+ AUDIOTESTMODE_GUEST,
+ /** VKAT is running on the host side. */
+ AUDIOTESTMODE_HOST
+} AUDIOTESTMODE;
+
+struct AUDIOTESTENV;
+/** Pointer a audio test environment. */
+typedef AUDIOTESTENV *PAUDIOTESTENV;
+
+struct AUDIOTESTDESC;
+/** Pointer a audio test descriptor. */
+typedef AUDIOTESTDESC *PAUDIOTESTDESC;
+
+/**
+ * Callback to set up the test parameters for a specific test.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code
+ * otherwise indicating the kind of error.
+ * @param pszTest Test name.
+ * @param pTstParmsAcq The audio test parameters to set up.
+ */
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
+/** Pointer to an audio test setup callback. */
+typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
+
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
+/** Pointer to an audio test exec callback. */
+typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
+
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx));
+/** Pointer to an audio test destroy callback. */
+typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
+
+/**
+ * Structure for keeping an audio test audio stream.
+ */
+typedef struct AUDIOTESTSTREAM
+{
+ /** The PDM stream. */
+ PPDMAUDIOSTREAM pStream;
+ /** The backend stream. */
+ PPDMAUDIOBACKENDSTREAM pBackend;
+ /** The stream config. */
+ PDMAUDIOSTREAMCFG Cfg;
+ /** Associated mixing stream. Optional. */
+ AUDIOTESTDRVMIXSTREAM Mix;
+} AUDIOTESTSTREAM;
+/** Pointer to audio test stream. */
+typedef AUDIOTESTSTREAM *PAUDIOTESTSTREAM;
+
+/** Maximum audio streams a test environment can handle. */
+#define AUDIOTESTENV_MAX_STREAMS 8
+
+/**
+ * Structure for keeping TCP/IP-specific options.
+ */
+typedef struct AUDIOTESTENVTCPOPTS
+{
+ /** Connection mode(s) to use. */
+ ATSCONNMODE enmConnMode;
+ /** Bind address (server mode). When empty, "0.0.0.0" (any host) will be used. */
+ char szBindAddr[128];
+ /** Bind port (server mode). */
+ uint16_t uBindPort;
+ /** Connection address (client mode). */
+ char szConnectAddr[128];
+ /** Connection port (client mode). */
+ uint16_t uConnectPort;
+} AUDIOTESTENVTCPOPTS;
+/** Pointer to audio test TCP options. */
+typedef AUDIOTESTENVTCPOPTS *PAUDIOTESTENVTCPOPTS;
+
+/**
+ * Structure holding additional I/O options.
+ */
+typedef struct AUDIOTESTIOOPTS
+{
+ /** Whether to use the audio connector or not. */
+ bool fWithDrvAudio;
+ /** Whether to use a mixing buffer or not. */
+ bool fWithMixer;
+ /** Buffer size (in ms). */
+ uint32_t cMsBufferSize;
+ /** Pre-buffering size (in ms). */
+ uint32_t cMsPreBuffer;
+ /** Scheduling (in ms). */
+ uint32_t cMsSchedulingHint;
+ /** Audio vlume to use (in percent). */
+ uint8_t uVolumePercent;
+ /** PCM audio properties to use. */
+ PDMAUDIOPCMPROPS Props;
+} AUDIOTESTIOOPTS;
+/** Pointer to additional playback options. */
+typedef AUDIOTESTIOOPTS *PAUDIOTESTIOOPTS;
+
+/**
+ * Structure for keeping a user context for the test service callbacks.
+ */
+typedef struct ATSCALLBACKCTX
+{
+ /** The test environment bound to this context. */
+ PAUDIOTESTENV pTstEnv;
+ /** Absolute path to the packed up test set archive.
+ * Keep it simple for now and only support one (open) archive at a time. */
+ char szTestSetArchive[RTPATH_MAX];
+ /** File handle to the (opened) test set archive for reading. */
+ RTFILE hTestSetArchive;
+ /** Number of currently connected clients. */
+ uint8_t cClients;
+} ATSCALLBACKCTX;
+typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
+
+/**
+ * Audio test environment parameters.
+ *
+ * This is global to all tests defined.
+ */
+typedef struct AUDIOTESTENV
+{
+ /** Audio testing mode. */
+ AUDIOTESTMODE enmMode;
+ /** Whether self test mode is active or not. */
+ bool fSelftest;
+ /** Whether skip the actual verification or not. */
+ bool fSkipVerify;
+ /** Name of the audio device to use.
+ * If empty the default audio device will be used. */
+ char szDev[128];
+ /** Zero-based index of current test (will be increased for every run test). */
+ uint32_t idxTest;
+ /** Number of iterations for *all* tests specified.
+ * When set to 0 (default), a random value (see specific test) will be chosen. */
+ uint32_t cIterations;
+ /** I/O options to use. */
+ AUDIOTESTIOOPTS IoOpts;
+ /** Test tone parameters to use. */
+ AUDIOTESTTONEPARMS ToneParms;
+ /** Output path for storing the test environment's final test files. */
+ char szTag[AUDIOTEST_TAG_MAX];
+ /** Output path for storing the test environment's final test files. */
+ char szPathOut[RTPATH_MAX];
+ /** Temporary path for this test environment. */
+ char szPathTemp[RTPATH_MAX];
+ /** Pointer to audio test driver stack to use. */
+ PAUDIOTESTDRVSTACK pDrvStack;
+ /** Audio stream. */
+ AUDIOTESTSTREAM aStreams[AUDIOTESTENV_MAX_STREAMS];
+ /** The audio test set to use. */
+ AUDIOTESTSET Set;
+ /** TCP options to use for ATS. */
+ AUDIOTESTENVTCPOPTS TcpOpts;
+ /** ATS server instance to use.
+ * NULL if not in use. */
+ PATSSERVER pSrv;
+ /** ATS callback context to use. */
+ ATSCALLBACKCTX CallbackCtx;
+ union
+ {
+ struct
+ {
+ /** Client connected to the ATS on the guest side. */
+ ATSCLIENT AtsClGuest;
+ /** Path to the guest's test set downloaded to the host. */
+ char szPathTestSetGuest[RTPATH_MAX];
+ /** Client connected to the Validation Kit audio driver ATS. */
+ ATSCLIENT AtsClValKit;
+ /** Path to the Validation Kit audio driver's test set downloaded to the host. */
+ char szPathTestSetValKit[RTPATH_MAX];
+ } Host;
+ } u;
+} AUDIOTESTENV;
+
+/**
+ * Audio test descriptor.
+ */
+typedef struct AUDIOTESTDESC
+{
+ /** (Sort of) Descriptive test name. */
+ const char *pszName;
+ /** Flag whether the test is excluded. */
+ bool fExcluded;
+ /** The setup callback. */
+ PFNAUDIOTESTSETUP pfnSetup;
+ /** The exec callback. */
+ PFNAUDIOTESTEXEC pfnExec;
+ /** The destruction callback. */
+ PFNAUDIOTESTDESTROY pfnDestroy;
+} AUDIOTESTDESC;
+
+/**
+ * Backend description.
+ */
+typedef struct AUDIOTESTBACKENDDESC
+{
+ /** The driver registration structure. */
+ PCPDMDRVREG pDrvReg;
+ /** The backend name.
+ * Aliases are implemented by having multiple entries for the same backend. */
+ const char *pszName;
+} AUDIOTESTBACKENDDESC;
+
+/**
+ * VKAT command table entry.
+ */
+typedef struct VKATCMD
+{
+ /** The command name. */
+ const char *pszCommand;
+ /** The command handler. */
+ DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(PRTGETOPTSTATE pGetState));
+
+ /** Command description. */
+ const char *pszDesc;
+ /** Options array. */
+ PCRTGETOPTDEF paOptions;
+ /** Number of options in the option array. */
+ size_t cOptions;
+ /** Gets help for an option. */
+ DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt));
+ /** Flag indicating if the command needs the ATS transport layer.
+ * Needed for command line parsing. */
+ bool fNeedsTransport;
+} VKATCMD;
+/** Pointer to a const VKAT command entry. */
+typedef VKATCMD const *PCVKATCMD;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Terminate ASAP if set. Set on Ctrl-C. */
+extern bool volatile g_fTerminate;
+/** The release logger. */
+extern PRTLOGGER g_pRelLogger;
+
+/** The test handle. */
+extern RTTEST g_hTest;
+/** The current verbosity level. */
+extern unsigned g_uVerbosity;
+/** DrvAudio: Enable debug (or not). */
+extern bool g_fDrvAudioDebug;
+/** DrvAudio: The debug output path. */
+extern const char *g_pszDrvAudioDebug;
+
+extern const VKATCMD g_CmdTest;
+extern const VKATCMD g_CmdVerify;
+extern const VKATCMD g_CmdBackends;
+extern const VKATCMD g_CmdEnum;
+extern const VKATCMD g_CmdPlay;
+extern const VKATCMD g_CmdRec;
+extern const VKATCMD g_CmdSelfTest;
+
+
+extern AUDIOTESTDESC g_aTests[];
+extern unsigned g_cTests;
+
+extern AUDIOTESTBACKENDDESC const g_aBackends[];
+extern unsigned g_cBackends;
+
+
+/*********************************************************************************************************************************
+* Prototypes *
+*********************************************************************************************************************************/
+
+/** @name Command line handlers
+ * @{ */
+RTEXITCODE audioTestUsage(PRTSTREAM pStrm, PCVKATCMD pOnlyCmd);
+RTEXITCODE audioTestVersion(void);
+void audioTestShowLogo(PRTSTREAM pStream);
+/** @} */
+
+/** @name Driver stack
+ * @{ */
+int AudioTestDriverStackPerformSelftest(void);
+
+void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack);
+int audioTestDriverStackInitEx(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio);
+int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio);
+int audioTestDriverStackProbe(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio);
+int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId);
+/** @} */
+
+/** @name Driver
+ * @{ */
+int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns, PPPDMDRVINS ppDrvIns);
+/** @} */
+
+/** @name Driver stack stream
+ * @{ */
+int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
+ uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+ PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq);
+int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
+ uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+ PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq);
+void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync);
+int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+int AudioTestDriverStackStreamDisable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, void const *pvBuf,
+ uint32_t cbBuf, uint32_t *pcbPlayed);
+uint32_t audioTestDriverStackStreamGetReadable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream);
+int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
+ void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured);
+/** @} */
+
+/** @name Backend handling
+ * @{ */
+PCPDMDRVREG AudioTestGetDefaultBackend(void);
+PCPDMDRVREG AudioTestFindBackendOpt(const char *pszBackend);
+/** @} */
+
+/** @name Mixing stream
+ * @{ */
+int AudioTestMixStreamInit(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
+ PCPDMAUDIOPCMPROPS pProps, uint32_t cMsBuffer);
+void AudioTestMixStreamTerm(PAUDIOTESTDRVMIXSTREAM pMix);
+int AudioTestMixStreamEnable(PAUDIOTESTDRVMIXSTREAM pMix);
+int AudioTestMixStreamDrain(PAUDIOTESTDRVMIXSTREAM pMix, bool fSync);
+int AudioTestMixStreamDisable(PAUDIOTESTDRVMIXSTREAM pMix);
+bool AudioTestMixStreamIsOkay(PAUDIOTESTDRVMIXSTREAM pMix);
+uint32_t AudioTestMixStreamGetWritable(PAUDIOTESTDRVMIXSTREAM pMix);
+int AudioTestMixStreamPlay(PAUDIOTESTDRVMIXSTREAM pMix, void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed);
+uint32_t AudioTestMixStreamGetReadable(PAUDIOTESTDRVMIXSTREAM pMix);
+int AudioTestMixStreamCapture(PAUDIOTESTDRVMIXSTREAM pMix, void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured);
+void AudioTestMixStreamSetVolume(PAUDIOTESTDRVMIXSTREAM pMix, uint8_t uVolumePercent);
+/** @} */
+
+/** @name Device handling
+ * @{ */
+int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev);
+int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev);
+
+int audioTestDevicesEnumerateAndCheck(PAUDIOTESTDRVSTACK pDrvStack, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev);
+/** @} */
+
+/** @name ATS routines
+ * @{ */
+int audioTestEnvConnectToValKitAts(PAUDIOTESTENV pTstEnv,
+ const char *pszHostTcpAddr, uint32_t uHostTcpPort);
+/** @} */
+
+/** @name Test environment handling
+ * @{ */
+void audioTestEnvInit(PAUDIOTESTENV pTstEnv);
+int audioTestEnvCreate(PAUDIOTESTENV pTstEnv, PAUDIOTESTDRVSTACK pDrvStack);
+void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv);
+int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv, bool fPack, char *pszPackFile, size_t cbPackFile);
+
+void audioTestParmsInit(PAUDIOTESTPARMS pTstParms);
+void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms);
+/** @} */
+
+int audioTestWorker(PAUDIOTESTENV pTstEnv);
+
+/** @todo Test tone handling */
+int audioTestPlayTone(PAUDIOTESTIOOPTS pIoOpts, PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms);
+void audioTestToneParmsInit(PAUDIOTESTTONEPARMS pToneParms);
+/** @} */
+
+void audioTestIoOptsInitDefaults(PAUDIOTESTIOOPTS pIoOpts);
+
+
+/*********************************************************************************************************************************
+* Common command line stuff *
+*********************************************************************************************************************************/
+
+/**
+ * Common long options values.
+ */
+enum
+{
+ AUDIO_TEST_OPT_CMN_DAEMONIZE = 256,
+ AUDIO_TEST_OPT_CMN_DAEMONIZED,
+ AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE,
+ AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH
+};
+
+/** For use in the option switch to handle common options. */
+#define AUDIO_TEST_COMMON_OPTION_CASES(a_ValueUnion, a_pCmd) \
+ case 'q': \
+ g_uVerbosity = 0; \
+ if (g_pRelLogger) \
+ RTLogGroupSettings(g_pRelLogger, "all=0 all.e"); \
+ break; \
+ \
+ case 'v': \
+ /* No-op here, has been handled by main() already. */ /** @todo r-bird: -q works, so -v must too! */ \
+ break; \
+ \
+ case 'V': \
+ return audioTestVersion(); \
+ \
+ case 'h': \
+ return audioTestUsage(g_pStdOut, a_pCmd); \
+ \
+ case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE: \
+ g_fDrvAudioDebug = true; \
+ break; \
+ \
+ case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH: \
+ g_pszDrvAudioDebug = (a_ValueUnion).psz; \
+ break; \
+ case AUDIO_TEST_OPT_CMN_DAEMONIZE: \
+ break; \
+ case AUDIO_TEST_OPT_CMN_DAEMONIZED: \
+ break;
+
+#endif /* !VBOX_INCLUDED_SRC_audio_vkatInternal_h */
+
diff --git a/src/VBox/ValidationKit/utils/clipboard/ClipUtil.cpp b/src/VBox/ValidationKit/utils/clipboard/ClipUtil.cpp
new file mode 100644
index 00000000..4c1e10c5
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/clipboard/ClipUtil.cpp
@@ -0,0 +1,1793 @@
+/* $Id: ClipUtil.cpp $ */
+/** @file
+ * ClipUtil - Clipboard Utility
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_OS2
+# define INCL_BASE
+# define INCL_PM
+# define INCL_ERRORS
+# include <os2.h>
+# undef RT_MAX
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/utf16.h>
+#include <iprt/zero.h>
+
+#ifdef RT_OS_DARWIN
+/** @todo */
+#elif defined(RT_OS_WINDOWS)
+# include <iprt/nt/nt-and-windows.h>
+#elif !defined(RT_OS_OS2)
+# include <X11/Xlib.h>
+# include <X11/Xatom.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) || defined(RT_OS_DARWIN)
+# undef MULTI_TARGET_CLIPBOARD
+# undef CU_X11
+#else
+# define MULTI_TARGET_CLIPBOARD
+# define CU_X11
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Clipboard format descriptor.
+ */
+typedef struct CLIPUTILFORMAT
+{
+ /** Format name. */
+ const char *pszName;
+
+#if defined(RT_OS_WINDOWS)
+ /** Windows integer format (CF_XXXX). */
+ UINT fFormat;
+ /** Windows string format name. */
+ const WCHAR *pwszFormat;
+
+#elif defined(RT_OS_OS2)
+ /** OS/2 integer format. */
+ ULONG fFormat;
+ /** OS/2 string format name. */
+ const char *pszFormat;
+
+#elif defined(RT_OS_DARWIN)
+ /** Native format (flavor). */
+ CFStringRef *hStrFormat;
+#else
+ /** The X11 atom for the format. */
+ Atom uAtom;
+ /** The X11 atom name if uAtom must be termined dynamically. */
+ const char *pszAtomName;
+ /** @todo X11 */
+#endif
+
+ /** Description. */
+ const char *pszDesc;
+ /** CLIPUTILFORMAT_F_XXX. */
+ uint32_t fFlags;
+} CLIPUTILFORMAT;
+/** Pointer to a clipobard format descriptor. */
+typedef CLIPUTILFORMAT const *PCCLIPUTILFORMAT;
+
+/** Convert to/from UTF-8. */
+#define CLIPUTILFORMAT_F_CONVERT_UTF8 RT_BIT_32(0)
+/** Ad hoc entry. */
+#define CLIPUTILFORMAT_F_AD_HOC RT_BIT_32(1)
+
+
+#ifdef MULTI_TARGET_CLIPBOARD
+/**
+ * Clipboard target descriptor.
+ */
+typedef struct CLIPUTILTARGET
+{
+ /** Target name. */
+ const char *pszName;
+ /** The X11 atom for the target. */
+ Atom uAtom;
+ /** The X11 atom name if uAtom must be termined dynamically. */
+ const char *pszAtomName;
+ /** Description. */
+ const char *pszDesc;
+} CLIPUTILTARGET;
+/** Pointer to clipboard target descriptor. */
+typedef CLIPUTILTARGET const *PCCLIPUTILTARGET;
+#endif /* MULTI_TARGET_CLIPBOARD */
+
+
+#ifdef RT_OS_OS2
+/** Header for Odin32 specific clipboard entries.
+ * (Used to get the correct size of the data.)
+ */
+typedef struct _Odin32ClipboardHeader
+{
+ /** Magic (CLIPHEADER_MAGIC) */
+ char achMagic[8];
+ /** Size of the following data.
+ * (The interpretation depends on the type.) */
+ unsigned cbData;
+ /** Odin32 format number. */
+ unsigned uFormat;
+} CLIPHEADER, *PCLIPHEADER;
+
+#define CLIPHEADER_MAGIC "Odin\1\0\1"
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ { "--list", 'l', RTGETOPT_REQ_NOTHING },
+ { "--get", 'g', RTGETOPT_REQ_STRING },
+ { "--get-file", 'G', RTGETOPT_REQ_STRING },
+ { "--put", 'p', RTGETOPT_REQ_STRING },
+ { "--put-file", 'P', RTGETOPT_REQ_STRING },
+ { "--check", 'c', RTGETOPT_REQ_STRING },
+ { "--check-file", 'C', RTGETOPT_REQ_STRING },
+ { "--check-not", 'n', RTGETOPT_REQ_STRING },
+ { "--zap", 'z', RTGETOPT_REQ_NOTHING },
+#ifdef MULTI_TARGET_CLIPBOARD
+ { "--target", 't', RTGETOPT_REQ_STRING },
+#endif
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ { "--close", 'k', RTGETOPT_REQ_NOTHING },
+#endif
+ { "--wait", 'w', RTGETOPT_REQ_UINT32 },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING }, /* for Usage() */
+};
+
+/** Format descriptors. */
+static CLIPUTILFORMAT g_aFormats[] =
+{
+#if defined(RT_OS_WINDOWS)
+ { "text/ansi", CF_TEXT, NULL, "ANSI text", 0 },
+ { "text/utf-16", CF_UNICODETEXT, NULL, "UTF-16 text", 0 },
+ { "text/utf-8", CF_UNICODETEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
+ /* https://docs.microsoft.com/en-us/windows/desktop/dataxchg/html-clipboard-format */
+ { "text/html", 0, L"HTML Format", "HTML text", 0 },
+ { "bitmap", CF_DIB, NULL, "Bitmap (DIB)", 0 },
+ { "bitmap/v5", CF_DIBV5, NULL, "Bitmap version 5 (DIBv5)", 0 },
+#elif defined(RT_OS_OS2)
+ { "text/ascii", CF_TEXT, NULL, "ASCII text", 0 },
+ { "text/utf-8", CF_TEXT, NULL, "UTF-8 text", CLIPUTILFORMAT_F_CONVERT_UTF8 },
+ { "text/utf-16", 0, "Odin32 UnicodeText", "UTF-16 text", 0},
+#elif defined(RT_OS_DARWIN)
+ { "text/utf-8", kUTTypeUTF8PlainText, "UTF-8 text", 0 },
+ { "text/utf-16", kUTTypeUTF16PlainText, "UTF-16 text", 0 },
+#else
+ /** @todo X11 */
+ { "text/utf-8", None, "UTF8_STRING", "UTF-8 text", 0 },
+#endif
+};
+
+#ifdef MULTI_TARGET_CLIPBOARD
+/** Target descriptors. */
+static CLIPUTILTARGET g_aTargets[] =
+{
+ { "clipboard", 0, "CLIPBOARD", "XA_CLIPBOARD: The clipboard (default)" },
+ { "primary", XA_PRIMARY, NULL, "XA_PRIMARY: Primary selected text (middle mouse button)" },
+ { "secondary", XA_SECONDARY, NULL, "XA_SECONDARY: Secondary selected text (with ctrl)" },
+};
+
+/** The current clipboard target. */
+static CLIPUTILTARGET *g_pTarget = &g_aTargets[0];
+#endif /* MULTI_TARGET_CLIPBOARD */
+
+/** The -v/-q state. */
+static unsigned g_uVerbosity = 1;
+
+#ifdef RT_OS_DARWIN
+
+#elif defined(RT_OS_OS2)
+/** Anchorblock handle. */
+static HAB g_hOs2Ab = NULLHANDLE;
+/** The message queue handle. */
+static HMQ g_hOs2MsgQueue = NULLHANDLE;
+/** Windows that becomes clipboard owner when setting data. */
+static HWND g_hOs2Wnd = NULLHANDLE;
+/** Set if we've opened the clipboard. */
+static bool g_fOs2OpenedClipboard = false;
+/** Set if we're the clipboard owner. */
+static bool g_fOs2ClipboardOwner = false;
+/** Set when we receive a WM_TIMER message during DoWait(). */
+static bool volatile g_fOs2TimerTicked = false;
+
+#elif defined(RT_OS_WINDOWS)
+/** Set if we've opened the clipboard. */
+static bool g_fWinOpenedClipboard = false;
+/** Set when we receive a WM_TIMER message during DoWait(). */
+static bool volatile g_fWinTimerTicked = false;
+/** Window that becomes clipboard owner when setting data. */
+static HWND g_hWinWnd = NULL;
+
+#else
+/** Number of errors (incremented by error handle callback). */
+static uint32_t volatile g_cX11Errors;
+/** The X11 display. */
+static Display *g_pX11Display = NULL;
+/** The X11 dummy window. */
+static Window g_hX11Window = 0;
+/** TARGETS */
+static Atom g_uX11AtomTargets;
+/** MULTIPLE */
+static Atom g_uX11AtomMultiple;
+
+#endif
+
+
+/**
+ * Gets a format descriptor, complaining if invalid format.
+ *
+ * @returns Pointer to the descriptor if found, NULL + msg if not.
+ * @param pszFormat The format to get.
+ */
+static PCCLIPUTILFORMAT GetFormatDesc(const char *pszFormat)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
+ if (strcmp(pszFormat, g_aFormats[i].pszName) == 0)
+ {
+#if defined(RT_OS_DARWIN)
+ /** @todo */
+
+#elif defined(RT_OS_OS2)
+ if (g_aFormats[i].pszFormat && g_aFormats[i].fFormat == 0)
+ {
+ g_aFormats[i].fFormat = WinAddAtom(WinQuerySystemAtomTable(), g_aFormats[i].pszFormat);
+ if (g_aFormats[i].fFormat == 0)
+ RTMsgError("WinAddAtom(,%s) failed: %#x", g_aFormats[i].pszFormat, WinGetLastError(g_hOs2Ab));
+ }
+
+#elif defined(RT_OS_WINDOWS)
+ if (g_aFormats[i].pwszFormat && g_aFormats[i].fFormat == 0)
+ {
+ g_aFormats[i].fFormat = RegisterClipboardFormatW(g_aFormats[i].pwszFormat);
+ if (g_aFormats[i].fFormat == 0)
+ RTMsgError("RegisterClipboardFormatW(%ls) failed: %u (%#x)",
+ g_aFormats[i].pwszFormat, GetLastError(), GetLastError());
+ }
+#elif defined(CU_X11)
+ if (g_aFormats[i].pszAtomName && g_aFormats[i].uAtom == 0)
+ g_aFormats[i].uAtom = XInternAtom(g_pX11Display, g_aFormats[i].pszAtomName, False);
+#endif
+ return &g_aFormats[i];
+ }
+
+ /*
+ * Try register the format.
+ */
+ static CLIPUTILFORMAT AdHoc;
+ AdHoc.pszName = pszFormat;
+ AdHoc.pszDesc = pszFormat;
+ AdHoc.fFlags = CLIPUTILFORMAT_F_AD_HOC;
+#ifdef RT_OS_DARWIN
+/** @todo */
+
+#elif defined(RT_OS_OS2)
+ AdHoc.pszFormat = pszFormat;
+ AdHoc.fFormat = WinAddAtom(WinQuerySystemAtomTable(), pszFormat);
+ if (AdHoc.fFormat == 0)
+ {
+ RTMsgError("Invalid format '%s' (%#x)", pszFormat, WinGetLastError(g_hOs2Ab));
+ return NULL;
+ }
+
+#elif defined(RT_OS_WINDOWS)
+ AdHoc.pwszFormat = NULL;
+ AdHoc.fFormat = RegisterClipboardFormatA(pszFormat);
+ if (AdHoc.fFormat == 0)
+ {
+ RTMsgError("RegisterClipboardFormatA(%s) failed: %u (%#x)", pszFormat, GetLastError(), GetLastError());
+ return NULL;
+ }
+
+#else
+ AdHoc.pszAtomName = pszFormat;
+ AdHoc.uAtom = XInternAtom(g_pX11Display, pszFormat, False);
+ if (AdHoc.uAtom == None)
+ {
+ RTMsgError("Invalid format '%s' or out of memory for X11 atoms", pszFormat);
+ return NULL;
+ }
+
+#endif
+ return &AdHoc;
+}
+
+
+#ifdef RT_OS_DARWIN
+
+/** @todo */
+
+
+#elif defined(RT_OS_OS2)
+
+/**
+ * The window procedure for the object window.
+ *
+ * @returns Message result.
+ *
+ * @param hwnd The window handle.
+ * @param msg The message.
+ * @param mp1 Message parameter 1.
+ * @param mp2 Message parameter 2.
+ */
+static MRESULT EXPENTRY CuOs2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+ if (g_uVerbosity > 2)
+ RTMsgInfo("CuOs2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
+
+ switch (msg)
+ {
+ case WM_CREATE:
+ return NULL; /* FALSE(/NULL) == Continue*/
+ case WM_DESTROY:
+ break;
+
+ /*
+ * Clipboard viewer message - the content has been changed.
+ * This is sent *after* releasing the clipboard sem
+ * and during the WinSetClipbrdViewer call.
+ */
+ case WM_DRAWCLIPBOARD:
+ break;
+
+ /*
+ * Clipboard owner message - the content was replaced.
+ * This is sent by someone with an open clipboard, so don't try open it now.
+ */
+ case WM_DESTROYCLIPBOARD:
+ break;
+
+ /*
+ * Clipboard owner message - somebody is requesting us to render a format.
+ * This is called by someone which owns the clipboard, but that's fine.
+ */
+ case WM_RENDERFMT:
+ break;
+
+ /*
+ * Clipboard owner message - we're about to quit and should render all formats.
+ */
+ case WM_RENDERALLFMTS:
+ break;
+
+ /*
+ * Clipboard owner messages dealing with owner drawn content.
+ * We shouldn't be seeing any of these.
+ */
+ case WM_PAINTCLIPBOARD:
+ case WM_SIZECLIPBOARD:
+ case WM_HSCROLLCLIPBOARD:
+ case WM_VSCROLLCLIPBOARD:
+ AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+ break;
+
+ /*
+ * We shouldn't be seeing any other messages according to the docs.
+ * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message
+ * during WinCreateWindow. So, ignore that and assert on anything else.
+ */
+ default:
+ AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+ case WM_ADJUSTWINDOWPOS:
+ break;
+
+ /*
+ * We use this window fielding WM_TIMER during DoWait.
+ */
+ case WM_TIMER:
+ if (SHORT1FROMMP(mp1) == 1)
+ g_fOs2TimerTicked = true;
+ break;
+ }
+ return NULL;
+}
+
+
+/**
+ * Initialize the OS/2 bits.
+ */
+static RTEXITCODE CuOs2Init(void)
+{
+ g_hOs2Ab = WinInitialize(0);
+ if (g_hOs2Ab == NULLHANDLE)
+ return RTMsgErrorExitFailure("WinInitialize failed!");
+
+ g_hOs2MsgQueue = WinCreateMsgQueue(g_hOs2Ab, 10);
+ if (g_hOs2MsgQueue == NULLHANDLE)
+ return RTMsgErrorExitFailure("WinCreateMsgQueue failed: %#x", WinGetLastError(g_hOs2Ab));
+
+ static char s_szClass[] = "VBox-ClipUtilClipboardClass";
+ if (!WinRegisterClass(g_hOs2Wnd, (PCSZ)s_szClass, CuOs2WinProc, 0, 0))
+ return RTMsgErrorExitFailure("WinRegisterClass failed: %#x", WinGetLastError(g_hOs2Ab));
+
+ g_hOs2Wnd = WinCreateWindow(HWND_OBJECT, /* hwndParent */
+ (PCSZ)s_szClass, /* pszClass */
+ (PCSZ)"VirtualBox Clipboard Utility", /* pszName */
+ 0, /* flStyle */
+ 0, 0, 0, 0, /* x, y, cx, cy */
+ NULLHANDLE, /* hwndOwner */
+ HWND_BOTTOM, /* hwndInsertBehind */
+ 42, /* id */
+ NULL, /* pCtlData */
+ NULL); /* pPresParams */
+ if (g_hOs2Wnd == NULLHANDLE)
+ return RTMsgErrorExitFailure("WinCreateWindow failed: %#x", WinGetLastError(g_hOs2Ab));
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Terminates the OS/2 bits.
+ */
+static RTEXITCODE CuOs2Term(void)
+{
+ if (g_fOs2OpenedClipboard)
+ {
+ if (!WinCloseClipbrd(g_hOs2Ab))
+ return RTMsgErrorExitFailure("WinCloseClipbrd failed: %#x", WinGetLastError(g_hOs2Ab));
+ g_fOs2OpenedClipboard = false;
+ }
+
+ WinDestroyWindow(g_hOs2Wnd);
+ g_hOs2Wnd = NULLHANDLE;
+
+ WinDestroyMsgQueue(g_hOs2MsgQueue);
+ g_hOs2MsgQueue = NULLHANDLE;
+
+ WinTerminate(g_hOs2Ab);
+ g_hOs2Ab = NULLHANDLE;
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Opens the OS/2 clipboard.
+ */
+static RTEXITCODE CuOs2OpenClipboardIfNecessary(void)
+{
+ if (g_fOs2OpenedClipboard)
+ return RTEXITCODE_SUCCESS;
+ if (WinOpenClipbrd(g_hOs2Ab))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Opened the clipboard\n");
+ g_fOs2OpenedClipboard = true;
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExitFailure("WinOpenClipbrd failed: %#x", WinGetLastError(g_hOs2Ab));
+}
+
+
+#elif defined(RT_OS_WINDOWS)
+
+/**
+ * Window procedure for the clipboard owner window on Windows.
+ */
+static LRESULT CALLBACK CuWinWndProc(HWND hWnd, UINT idMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
+{
+ if (g_uVerbosity > 2)
+ RTMsgInfo("CuWinWndProc: hWnd=%p idMsg=%#05x wParam=%#zx lParam=%#zx\n", hWnd, idMsg, wParam, lParam);
+
+ switch (idMsg)
+ {
+ case WM_TIMER:
+ if (wParam == 1)
+ g_fWinTimerTicked = true;
+ break;
+ }
+ return DefWindowProc(hWnd, idMsg, wParam, lParam);
+}
+
+
+/**
+ * Initialize the Windows bits.
+ */
+static RTEXITCODE CuWinInit(void)
+{
+ /* Register the window class: */
+ static wchar_t s_wszClass[] = L"VBox-ClipUtilClipboardClass";
+ WNDCLASSW WndCls = {0};
+ WndCls.style = CS_NOCLOSE;
+ WndCls.lpfnWndProc = CuWinWndProc;
+ WndCls.cbClsExtra = 0;
+ WndCls.cbWndExtra = 0;
+ WndCls.hInstance = (HINSTANCE)GetModuleHandle(NULL);
+ WndCls.hIcon = NULL;
+ WndCls.hCursor = NULL;
+ WndCls.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ WndCls.lpszMenuName = NULL;
+ WndCls.lpszClassName = s_wszClass;
+
+ ATOM uAtomWndClass = RegisterClassW(&WndCls);
+ if (!uAtomWndClass)
+ return RTMsgErrorExitFailure("RegisterClassW failed: %u (%#x)", GetLastError(), GetLastError());
+
+ /* Create the clipboard owner window: */
+ g_hWinWnd = CreateWindowExW(WS_EX_TRANSPARENT, /* fExStyle */
+ s_wszClass, /* pwszClass */
+ L"VirtualBox Clipboard Utility", /* pwszName */
+ 0, /* fStyle */
+ 0, 0, 0, 0, /* x, y, cx, cy */
+ HWND_MESSAGE, /* hWndParent */
+ NULL, /* hMenu */
+ (HINSTANCE)GetModuleHandle(NULL), /* hinstance */
+ NULL); /* pParam */
+ if (g_hWinWnd == NULL)
+ return RTMsgErrorExitFailure("CreateWindowExW failed: %u (%#x)", GetLastError(), GetLastError());
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Terminates the Windows bits.
+ */
+static RTEXITCODE CuWinTerm(void)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ if (g_fWinOpenedClipboard)
+ {
+ if (CloseClipboard())
+ g_fWinOpenedClipboard = false;
+ else
+ rcExit = RTMsgErrorExitFailure("CloseClipboard failed: %u (%#x)", GetLastError(), GetLastError());
+ }
+
+ if (g_hWinWnd != NULL)
+ {
+ if (!DestroyWindow(g_hWinWnd))
+ rcExit = RTMsgErrorExitFailure("DestroyWindow failed: %u (%#x)", GetLastError(), GetLastError());
+ g_hWinWnd = NULL;
+ }
+
+ return rcExit;
+}
+
+
+/**
+ * Opens the window clipboard.
+ */
+static RTEXITCODE WinOpenClipboardIfNecessary(void)
+{
+ if (g_fWinOpenedClipboard)
+ return RTEXITCODE_SUCCESS;
+ if (OpenClipboard(g_hWinWnd))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Opened the clipboard\n");
+ g_fWinOpenedClipboard = true;
+ return RTEXITCODE_SUCCESS;
+ }
+ return RTMsgErrorExitFailure("OpenClipboard failed: %u (%#x)", GetLastError(), GetLastError());
+}
+
+
+#else /* X11: */
+
+/**
+ * Error handler callback.
+ */
+static int CuX11ErrorCallback(Display *pX11Display, XErrorEvent *pErrEvt)
+{
+ g_cX11Errors++;
+ char szErr[2048];
+ XGetErrorText(pX11Display, pErrEvt->error_code, szErr, sizeof(szErr));
+ RTMsgError("An X Window protocol error occurred: %s\n"
+ " Request code: %u\n"
+ " Minor code: %u\n"
+ " Serial number of the failed request: %u\n",
+ szErr, pErrEvt->request_code, pErrEvt->minor_code, pErrEvt->serial);
+ return 0;
+}
+
+
+/**
+ * Initialize the X11 bits.
+ */
+static RTEXITCODE CuX11Init(void)
+{
+ /*
+ * Open the X11 display and create a little dummy window.
+ */
+ XSetErrorHandler(CuX11ErrorCallback);
+ g_pX11Display = XOpenDisplay(NULL);
+ if (!g_pX11Display)
+ return RTMsgErrorExitFailure("XOpenDisplay failed");
+
+ int const iDefaultScreen = DefaultScreen(g_pX11Display);
+ g_hX11Window = XCreateSimpleWindow(g_pX11Display,
+ RootWindow(g_pX11Display, iDefaultScreen),
+ 0 /*x*/, 0 /*y*/,
+ 1 /*cx*/, 1 /*cy*/,
+ 0 /*cPxlBorder*/,
+ BlackPixel(g_pX11Display, iDefaultScreen) /*Border*/,
+ WhitePixel(g_pX11Display, iDefaultScreen) /*Background*/);
+
+ /*
+ * Resolve any unknown atom values we might need later.
+ */
+ for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
+ if (g_aTargets[i].pszAtomName)
+ {
+ g_aTargets[i].uAtom = XInternAtom(g_pX11Display, g_aTargets[i].pszAtomName, False);
+ if (g_uVerbosity > 2)
+ RTPrintf("target %s atom=%#x\n", g_aTargets[i].pszName, g_aTargets[i].uAtom);
+ }
+
+ g_uX11AtomTargets = XInternAtom(g_pX11Display, "TARGETS", False);
+ g_uX11AtomMultiple = XInternAtom(g_pX11Display, "MULTIPLE", False);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+#endif /* X11 */
+
+
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+/**
+ * Closes the clipboard if open.
+ */
+static RTEXITCODE CuCloseClipboard(void)
+{
+# if defined(RT_OS_OS2)
+ if (g_fOs2OpenedClipboard)
+ {
+ if (!WinCloseClipbrd(g_hOs2Ab))
+ return RTMsgErrorExitFailure("WinCloseClipbrd failed: %#x", WinGetLastError(g_hOs2Ab));
+ g_fOs2OpenedClipboard = false;
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Closed the clipboard.\n");
+ }
+# else
+ if (g_fWinOpenedClipboard)
+ {
+ if (!CloseClipboard())
+ return RTMsgErrorExitFailure("CloseClipboard failed: %u (%#x)", GetLastError(), GetLastError());
+ g_fWinOpenedClipboard = false;
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Closed the clipboard.\n");
+ }
+# endif
+ else if (g_uVerbosity > 0)
+ RTMsgInfo("No need to close clipboard, not opened.\n");
+
+ return RTEXITCODE_SUCCESS;
+}
+#endif /* RT_OS_OS2 || RT_OS_WINDOWS */
+
+
+/**
+ * Lists the clipboard format.
+ */
+static RTEXITCODE ListClipboardContent(void)
+{
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ HATOMTBL const hAtomTbl = WinQuerySystemAtomTable();
+ uint32_t idx = 0;
+ ULONG fFormat = 0;
+ while ((fFormat = WinEnumClipbrdFmts(g_hOs2Ab)) != 0)
+ {
+ char szName[256] = {0};
+ ULONG cchRet = WinQueryAtomName(hAtomTbl, fFormat, szName, sizeof(szName));
+ if (cchRet != 0)
+ RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, szName);
+ else
+ {
+ const char *pszName = NULL;
+ switch (fFormat)
+ {
+ case CF_TEXT: pszName = "CF_TEXT"; break;
+ case CF_BITMAP: pszName = "CF_BITMAP"; break;
+ case CF_DSPTEXT: pszName = "CF_DSPTEXT"; break;
+ case CF_DSPBITMAP: pszName = "CF_DSPBITMAP"; break;
+ case CF_METAFILE: pszName = "CF_METAFILE"; break;
+ case CF_DSPMETAFILE: pszName = "CF_DSPMETAFILE"; break;
+ case CF_PALETTE: pszName = "CF_PALETTE"; break;
+ default:
+ break;
+ }
+ if (pszName)
+ RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, pszName);
+ else
+ RTPrintf("#%02u: %#06x\n", idx, fFormat);
+ }
+
+ idx++;
+ }
+ }
+
+ return rcExit;
+
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ SetLastError(0);
+ uint32_t idx = 0;
+ UINT fFormat = 0;
+ while ((fFormat = EnumClipboardFormats(fFormat)) != 0)
+ {
+ WCHAR wszName[256];
+ int cchName = GetClipboardFormatNameW(fFormat, wszName, RT_ELEMENTS(wszName));
+ if (cchName > 0)
+ RTPrintf("#%02u: %#06x - %ls\n", idx, fFormat, wszName);
+ else
+ {
+ const char *pszName = NULL;
+ switch (fFormat)
+ {
+ case CF_TEXT: pszName = "CF_TEXT"; break;
+ case CF_BITMAP: pszName = "CF_BITMAP"; break;
+ case CF_METAFILEPICT: pszName = "CF_METAFILEPICT"; break;
+ case CF_SYLK: pszName = "CF_SYLK"; break;
+ case CF_DIF: pszName = "CF_DIF"; break;
+ case CF_TIFF: pszName = "CF_TIFF"; break;
+ case CF_OEMTEXT: pszName = "CF_OEMTEXT"; break;
+ case CF_DIB: pszName = "CF_DIB"; break;
+ case CF_PALETTE: pszName = "CF_PALETTE"; break;
+ case CF_PENDATA: pszName = "CF_PENDATA"; break;
+ case CF_RIFF: pszName = "CF_RIFF"; break;
+ case CF_WAVE: pszName = "CF_WAVE"; break;
+ case CF_UNICODETEXT: pszName = "CF_UNICODETEXT"; break;
+ case CF_ENHMETAFILE: pszName = "CF_ENHMETAFILE"; break;
+ case CF_HDROP: pszName = "CF_HDROP"; break;
+ case CF_LOCALE: pszName = "CF_LOCALE"; break;
+ case CF_DIBV5: pszName = "CF_DIBV5"; break;
+ default:
+ break;
+ }
+ if (pszName)
+ RTPrintf("#%02u: %#06x - %s\n", idx, fFormat, pszName);
+ else
+ RTPrintf("#%02u: %#06x\n", idx, fFormat);
+ }
+
+ idx++;
+ }
+ if (idx == 0)
+ RTPrintf("Empty\n");
+ }
+ return rcExit;
+
+#elif defined(CU_X11)
+ /* Request the TARGETS property: */
+ Atom uAtomDst = g_uX11AtomTargets;
+ int rc = XConvertSelection(g_pX11Display, g_pTarget->uAtom, g_uX11AtomTargets, uAtomDst, g_hX11Window, CurrentTime);
+ if (g_uVerbosity > 1)
+ RTPrintf("XConvertSelection -> %d\n", rc);
+
+ /* Wait for the reply: */
+ for (;;)
+ {
+ XEvent Evt = {0};
+ rc = XNextEvent(g_pX11Display, &Evt);
+ if (Evt.type == SelectionNotify)
+ {
+ if (g_uVerbosity > 1)
+ RTPrintf("XNextEvent -> %d; type=SelectionNotify\n", rc);
+ if (Evt.xselection.selection == g_pTarget->uAtom)
+ {
+ if (Evt.xselection.property == None)
+ return RTMsgErrorExitFailure("XConvertSelection(,%s,TARGETS,) failed", g_pTarget->pszName);
+
+ /* Get the TARGETS property data: */
+ Atom uAtomRetType = 0;
+ int iActualFmt = 0;
+ unsigned long cbLeftToRead = 0;
+ unsigned long cItems = 0;
+ unsigned char *pbData = NULL;
+ rc = XGetWindowProperty(g_pX11Display, g_hX11Window, uAtomDst,
+ 0 /*offset*/, sizeof(Atom) * 4096 /* should be enough */, True /*fDelete*/, XA_ATOM,
+ &uAtomRetType, &iActualFmt, &cItems, &cbLeftToRead, &pbData);
+ if (g_uVerbosity > 1)
+ RTPrintf("XConvertSelection -> %d; uAtomRetType=%u iActualFmt=%d cItems=%lu cbLeftToRead=%lu pbData=%p\n",
+ rc, uAtomRetType, iActualFmt, cItems, cbLeftToRead, pbData);
+ if (pbData && cItems > 0)
+ {
+ /* Display the TARGETS: */
+ Atom const *paTargets = (Atom const *)pbData;
+ for (unsigned long i = 0; i < cItems; i++)
+ {
+ const char *pszName = XGetAtomName(g_pX11Display, paTargets[i]);
+ if (pszName)
+ RTPrintf("#%02u: %#06x - %s\n", i, paTargets[i], pszName);
+ else
+ RTPrintf("#%02u: %#06x\n", i, paTargets[i]);
+ }
+ }
+ else
+ RTMsgInfo("Empty");
+ if (pbData)
+ XFree(pbData);
+ return RTEXITCODE_SUCCESS;
+ }
+ }
+ else if (g_uVerbosity > 1)
+ RTPrintf("XNextEvent -> %d; type=%d\n", rc, Evt.type);
+ }
+
+#else
+ return RTMsgErrorExitFailure("ListClipboardContent is not implemented");
+#endif
+}
+
+
+/**
+ * Reads the given clipboard format and stores it in on the heap.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to get.
+ * @param ppvData Where to return the pointer to the data. Free using
+ * RTMemFree when done.
+ * @param pcbData Where to return the amount of data returned.
+ */
+static RTEXITCODE ReadClipboardData(PCCLIPUTILFORMAT pFmtDesc, void **ppvData, size_t *pcbData)
+{
+ *ppvData = NULL;
+ *pcbData = 0;
+
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ ULONG fFmtInfo = 0;
+ if (WinQueryClipbrdFmtInfo(g_hOs2Ab, pFmtDesc->fFormat, &fFmtInfo))
+ {
+ ULONG uData = WinQueryClipbrdData(g_hOs2Ab, pFmtDesc->fFormat);
+ if (fFmtInfo & CFI_POINTER)
+ {
+ PCLIPHEADER pOdinHdr = (PCLIPHEADER)uData;
+ if (pFmtDesc->fFormat == CF_TEXT)
+ {
+ if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
+ {
+ char *pszUtf8 = NULL;
+ int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)uData);
+ if (RT_SUCCESS(rc))
+ {
+ *pcbData = strlen(pszUtf8) + 1;
+ *ppvData = RTMemDup(pszUtf8, *pcbData);
+ RTStrFree(pszUtf8);
+ }
+ else
+ return RTMsgErrorExitFailure("RTStrCurrentCPToUtf8 failed: %Rrc", rc);
+ }
+ else
+ {
+ *pcbData = strlen((const char *)uData) + 1;
+ *ppvData = RTMemDup((const char *)uData, *pcbData);
+ }
+ }
+ else if ( strcmp(pFmtDesc->pszFormat, "Odin32 UnicodeText") == 0
+ && memcmp(pOdinHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pOdinHdr->achMagic)) == 0)
+ {
+ *pcbData = pOdinHdr->cbData;
+ *ppvData = RTMemDup(pOdinHdr + 1, pOdinHdr->cbData);
+ }
+ else
+ {
+ /* We could use DosQueryMem here to figure out the size of the allocation... */
+ *pcbData = PAGE_SIZE - (uData & PAGE_OFFSET_MASK);
+ *ppvData = RTMemDup((void const *)uData, *pcbData);
+ }
+ }
+ else
+ {
+ *pcbData = sizeof(uData);
+ *ppvData = RTMemDup(&uData, sizeof(uData));
+ }
+ if (!*ppvData)
+ rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", *pcbData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("WinQueryClipbrdFmtInfo(,%s,) failed: %#x\n",
+ pFmtDesc->pszName, WinGetLastError(g_hOs2Ab));
+ }
+ return rcExit;
+
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ HANDLE hData = GetClipboardData(pFmtDesc->fFormat);
+ if (hData != NULL)
+ {
+ SIZE_T const cbData = GlobalSize(hData);
+ PVOID const pvData = GlobalLock(hData);
+ if (pvData != NULL)
+ {
+ *pcbData = cbData;
+ if (cbData != 0)
+ {
+ if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
+ {
+ char *pszUtf8 = NULL;
+ size_t cchUtf8 = 0;
+ int rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvData, cbData / sizeof(RTUTF16), &pszUtf8, 0, &cchUtf8);
+ if (RT_SUCCESS(rc))
+ {
+ *pcbData = cchUtf8 + 1;
+ *ppvData = RTMemDup(pszUtf8, cchUtf8 + 1);
+ RTStrFree(pszUtf8);
+ if (!*ppvData)
+ rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("RTUtf16ToUtf8Ex failed: %Rrc", rc);
+ }
+ else
+ {
+ *ppvData = RTMemDup(pvData, cbData);
+ if (!*ppvData)
+ rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", cbData);
+ }
+ }
+ GlobalUnlock(hData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
+ pFmtDesc->pszName, GetLastError(), GetLastError());
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("GetClipboardData(%s) failed: %u (%#x)\n",
+ pFmtDesc->pszName, GetLastError(), GetLastError());
+ }
+ return rcExit;
+
+#elif defined(CU_X11)
+
+ /* Request the data: */
+ Atom const uAtomDst = pFmtDesc->uAtom;
+ int rc = XConvertSelection(g_pX11Display, g_pTarget->uAtom, pFmtDesc->uAtom, uAtomDst, g_hX11Window, CurrentTime);
+ if (g_uVerbosity > 1)
+ RTPrintf("XConvertSelection -> %d\n", rc);
+
+ /* Wait for the reply: */
+ for (;;)
+ {
+ XEvent Evt = {0};
+ rc = XNextEvent(g_pX11Display, &Evt);
+ if (Evt.type == SelectionNotify)
+ {
+ if (g_uVerbosity > 1)
+ RTPrintf("XNextEvent -> %d; type=SelectionNotify\n", rc);
+ if (Evt.xselection.selection == g_pTarget->uAtom)
+ {
+ if (Evt.xselection.property == None)
+ return RTMsgErrorExitFailure("XConvertSelection(,%s,%s,) failed", g_pTarget->pszName, pFmtDesc->pszName);
+
+ /*
+ * Retrieve the data.
+ */
+ Atom uAtomRetType = 0;
+ int cBitsActualFmt = 0;
+ unsigned long cbLeftToRead = 0;
+ unsigned long cItems = 0;
+ unsigned char *pbData = NULL;
+ rc = XGetWindowProperty(g_pX11Display, g_hX11Window, uAtomDst,
+ 0 /*offset*/, _64M, False/*fDelete*/, AnyPropertyType,
+ &uAtomRetType, &cBitsActualFmt, &cItems, &cbLeftToRead, &pbData);
+ if (g_uVerbosity > 1)
+ RTPrintf("XConvertSelection -> %d; uAtomRetType=%u cBitsActualFmt=%d cItems=%lu cbLeftToRead=%lu pbData=%p\n",
+ rc, uAtomRetType, cBitsActualFmt, cItems, cbLeftToRead, pbData);
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ if (pbData && cItems > 0)
+ {
+ *pcbData = cItems * (cBitsActualFmt / 8);
+ *ppvData = RTMemDup(pbData, *pcbData);
+ if (!*ppvData)
+ rcExit = RTMsgErrorExitFailure("Out of memory allocating %#zx bytes.", *pcbData);
+ }
+ if (pbData)
+ XFree(pbData);
+ XDeleteProperty(g_pX11Display, g_hX11Window, uAtomDst);
+ return rcExit;
+ }
+ }
+ else if (g_uVerbosity > 1)
+ RTPrintf("XNextEvent -> %d; type=%d\n", rc, Evt.type);
+ }
+
+#else
+ RT_NOREF(pFmtDesc);
+ return RTMsgErrorExitFailure("ReadClipboardData is not implemented\n");
+#endif
+}
+
+
+/**
+ * Puts the given data and format on the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format.
+ * @param pvData The data.
+ * @param cbData The amount of data in bytes.
+ */
+static RTEXITCODE WriteClipboardData(PCCLIPUTILFORMAT pFmtDesc, void const *pvData, size_t cbData)
+{
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /** @todo do we need to become owner? */
+
+ /* Convert to local code page if needed: */
+ char *pszLocale = NULL;
+ if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
+ {
+ int rc = RTStrUtf8ToCurrentCPEx(&pszLocale, (char *)pvData, cbData);
+ if (RT_SUCCESS(rc))
+ {
+ pvData = pszLocale;
+ cbData = strlen(pszLocale) + 1;
+ }
+ else
+ return RTMsgErrorExitFailure("RTStrUtf8ToCurrentCPEx failed: %Rrc\n", rc);
+ }
+
+ /* Allocate a bunch of shared memory for the object. */
+ PVOID pvShared = NULL;
+ APIRET orc = DosAllocSharedMem(&pvShared, NULL, cbData,
+ OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+ if (orc == NO_ERROR)
+ {
+ memcpy(pvShared, pvData, cbData);
+
+ if (WinSetClipbrdData(g_hOs2Ab, (uintptr_t)pvShared, pFmtDesc->fFormat, CFI_POINTER))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Put '%s' on the clipboard: %p LB %zu\n", pFmtDesc->pszName, pvShared, cbData);
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ {
+ rcExit = RTMsgErrorExitFailure("WinSetClipbrdData(,%p LB %#x,%s,) failed: %#x\n",
+ pvShared, cbData, pFmtDesc->pszName, WinGetLastError(g_hOs2Ab));
+ DosFreeMem(pvShared);
+ }
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("DosAllocSharedMem(,, %#x,) -> %u", cbData, orc);
+ RTStrFree(pszLocale);
+ }
+ return rcExit;
+
+
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Do input data conversion.
+ */
+ PRTUTF16 pwszFree = NULL;
+ if (pFmtDesc->fFlags & CLIPUTILFORMAT_F_CONVERT_UTF8)
+ {
+ size_t cwcConv = 0;
+ int rc = RTStrToUtf16Ex((char const *)pvData, cbData, &pwszFree, 0, &cwcConv);
+ if (RT_SUCCESS(rc))
+ {
+ pvData = pwszFree;
+ cbData = cwcConv * sizeof(RTUTF16);
+ }
+ else
+ return RTMsgErrorExitFailure("RTStrToTUtf16Ex failed: %Rrc\n", rc);
+ }
+
+ /*
+ * Text formats generally include the zero terminator.
+ */
+ uint32_t cbZeroPadding = 0;
+ if (pFmtDesc->fFormat == CF_UNICODETEXT)
+ cbZeroPadding = sizeof(WCHAR);
+ else if (pFmtDesc->fFormat == CF_TEXT)
+ cbZeroPadding = sizeof(char);
+
+ HANDLE hDstData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, cbData + cbZeroPadding);
+ if (hDstData)
+ {
+ if (cbData)
+ {
+ PVOID pvDstData = GlobalLock(hDstData);
+ if (pvDstData)
+ memcpy(pvDstData, pvData, cbData);
+ else
+ rcExit = RTMsgErrorExitFailure("GlobalLock failed: %u (%#x)\n", GetLastError(), GetLastError());
+ }
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ if (SetClipboardData(pFmtDesc->fFormat, hDstData))
+ {
+ if (g_uVerbosity > 0)
+ RTMsgInfo("Put '%s' on the clipboard: %p LB %zu\n", pFmtDesc->pszName, hDstData, cbData + cbZeroPadding);
+ }
+ else
+ {
+ rcExit = RTMsgErrorExitFailure("SetClipboardData(%s) failed: %u (%#x)\n",
+ pFmtDesc->pszName, GetLastError(), GetLastError());
+ GlobalFree(hDstData);
+ }
+ }
+ else
+ GlobalFree(hDstData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("GlobalAlloc(,%#zx) failed: %u (%#x)\n",
+ cbData + cbZeroPadding, GetLastError(), GetLastError());
+ }
+ return rcExit;
+
+#else
+ RT_NOREF(pFmtDesc, pvData, cbData);
+ return RTMsgErrorExitFailure("WriteClipboardData is not implemented\n");
+#endif
+}
+
+
+/**
+ * Check if the given data + format matches what's actually on the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to compare.
+ * @param pvExpect The expected clipboard data.
+ * @param cbExpect The size of the expected clipboard data.
+ */
+static RTEXITCODE CompareDataWithClipboard(PCCLIPUTILFORMAT pFmtDesc, void const *pvExpect, size_t cbExpect)
+{
+ void *pvData = NULL;
+ size_t cbData = 0;
+ RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ if ( cbData == cbExpect
+ && memcmp(pvData, pvExpect, cbData) == 0)
+ rcExit = RTEXITCODE_SUCCESS;
+ else
+ rcExit = RTMsgErrorExitFailure("Mismatch for '%s' (cbData=%#zx cbExpect=%#zx)\n",
+ pFmtDesc->pszName, cbData, cbExpect);
+ RTMemFree(pvData);
+ }
+ return rcExit;
+}
+
+
+/**
+ * Gets the given clipboard format.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to get.
+ * @param pStrmOut Where to output the data.
+ * @param fIsStdOut Set if @a pStrmOut is standard output, clear if not.
+ */
+static RTEXITCODE ClipboardContentToStdOut(PCCLIPUTILFORMAT pFmtDesc)
+{
+ void *pvData = NULL;
+ size_t cbData = 0;
+ RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ int rc = RTStrmWrite(g_pStdOut, pvData, cbData);
+ RTMemFree(pvData);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExitFailure("Error writing %#zx bytes to standard output: %Rrc", cbData, rc);
+ }
+ return rcExit;
+}
+
+
+/**
+ * Gets the given clipboard format.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to get.
+ * @param pszFilename The output filename.
+ */
+static RTEXITCODE ClipboardContentToFile(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
+{
+ void *pvData = NULL;
+ size_t cbData = 0;
+ RTEXITCODE rcExit = ReadClipboardData(pFmtDesc, &pvData, &cbData);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ RTFILE hFile = NIL_RTFILE;
+ int rc = RTFileOpen(&hFile, pszFilename,
+ RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE
+ | (0770 << RTFILE_O_CREATE_MODE_SHIFT));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pvData, cbData, NULL);
+ int const rc2 = RTFileClose(hFile);
+ if (RT_FAILURE(rc) || RT_FAILURE(rc2))
+ {
+ if (RT_FAILURE_NP(rc))
+ RTMsgError("Writing %#z bytes to '%s' failed: %Rrc", cbData, pszFilename, rc);
+ else
+ RTMsgError("Closing '%s' failed: %Rrc", pszFilename, rc2);
+ RTMsgInfo("Deleting '%s'.", pszFilename);
+ RTFileDelete(pszFilename);
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to open '%s' for writing: %Rrc", pszFilename, rc);
+ RTMemFree(pvData);
+ }
+ return rcExit;
+}
+
+
+/**
+ * Puts the given format + data onto the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to put.
+ * @param pszData The string data.
+ */
+static RTEXITCODE PutStringOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
+{
+ return WriteClipboardData(pFmtDesc, pszData, strlen(pszData));
+}
+
+
+/**
+ * Puts a format + file content onto the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to put.
+ * @param pszFilename The filename.
+ */
+static RTEXITCODE PutFileOnClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ void *pvData = NULL;
+ size_t cbData = 0;
+ int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
+ if (RT_SUCCESS(rc))
+ {
+ rcExit = WriteClipboardData(pFmtDesc, pvData, cbData);
+ RTFileReadAllFree(pvData, cbData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
+ return rcExit;
+}
+
+
+/**
+ * Checks if the given format + data matches what's on the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to check.
+ * @param pszData The string data.
+ */
+static RTEXITCODE CheckStringAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszData)
+{
+ return CompareDataWithClipboard(pFmtDesc, pszData, strlen(pszData));
+}
+
+
+/**
+ * Check if the given format + file content matches what's on the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to check.
+ * @param pszFilename The filename.
+ */
+static RTEXITCODE CheckFileAgainstClipboard(PCCLIPUTILFORMAT pFmtDesc, const char *pszFilename)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ void *pvData = NULL;
+ size_t cbData = 0;
+ int rc = RTFileReadAll(pszFilename, &pvData, &cbData);
+ if (RT_SUCCESS(rc))
+ {
+ rcExit = CompareDataWithClipboard(pFmtDesc, pvData, cbData);
+ RTFileReadAllFree(pvData, cbData);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Failed to open and read '%s' into memory: %Rrc", pszFilename, rc);
+ return rcExit;
+}
+
+
+/**
+ * Check that the given format is not on the clipboard.
+ *
+ * @returns Success indicator.
+ * @param pFmtDesc The format to check.
+ */
+static RTEXITCODE CheckFormatNotOnClipboard(PCCLIPUTILFORMAT pFmtDesc)
+{
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ ULONG fFmtInfo = 0;
+ if (WinQueryClipbrdFmtInfo(g_hOs2Ab, pFmtDesc->fFormat, &fFmtInfo))
+ rcExit = RTMsgErrorExitFailure("Format '%s' is present");
+ }
+ return rcExit;
+
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ if (IsClipboardFormatAvailable(pFmtDesc->fFormat))
+ rcExit = RTMsgErrorExitFailure("Format '%s' is present");
+ }
+ return rcExit;
+
+#else
+ RT_NOREF(pFmtDesc);
+ return RTMsgErrorExitFailure("CheckFormatNotOnClipboard is not implemented");
+#endif
+}
+
+
+/**
+ * Empties the clipboard.
+ *
+ * @returns Success indicator.
+ */
+static RTEXITCODE ZapAllClipboardData(void)
+{
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2OpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ ULONG fFmtInfo = 0;
+ if (WinEmptyClipbrd(g_hOs2Ab))
+ {
+ WinSetClipbrdOwner(g_hOs2Ab, g_hOs2Wnd); /* Probably unnecessary? */
+ WinSetClipbrdOwner(g_hOs2Ab, NULLHANDLE);
+ g_fOs2ClipboardOwner = false;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("WinEmptyClipbrd() failed: %#x\n", WinGetLastError(g_hOs2Ab));
+ }
+ return rcExit;
+
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = WinOpenClipboardIfNecessary();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ if (!EmptyClipboard())
+ rcExit = RTMsgErrorExitFailure("EmptyClipboard() failed: %u (%#x)\n", GetLastError(), GetLastError());
+ }
+ return rcExit;
+
+#else
+ return RTMsgErrorExitFailure("ZapAllClipboardData is not implemented");
+#endif
+}
+
+
+/**
+ * Waits/delays at least @a cMsWait milliseconds.
+ *
+ * @returns Success indicator.
+ * @param cMsWait Minimum wait/delay time in milliseconds.
+ */
+static RTEXITCODE DoWait(uint32_t cMsWait)
+{
+ uint64_t const msStart = RTTimeMilliTS();
+ if (g_uVerbosity > 1)
+ RTMsgInfo("Waiting %u ms...\n", cMsWait);
+
+#if defined(RT_OS_OS2)
+ /*
+ * Arm a timer which will timeout after the desired period and
+ * quit when we've dispatched it.
+ */
+ g_fOs2TimerTicked = false;
+ if (WinStartTimer(g_hOs2Ab, g_hOs2Wnd, 1 /*idEvent*/, cMsWait + 1) != 0)
+ {
+ QMSG Msg;
+ while (WinGetMsg(g_hOs2Ab, &Msg, NULL, 0, 0))
+ {
+ WinDispatchMsg(g_hOs2Ab, &Msg);
+ if (g_fOs2TimerTicked || RTTimeMilliTS() - msStart >= cMsWait)
+ break;
+ }
+
+ if (!WinStopTimer(g_hOs2Ab, g_hOs2Wnd, 1 /*idEvent*/))
+ RTMsgWarning("WinStopTimer failed: %#x", WinGetLastError(g_hOs2Ab));
+ }
+ else
+ return RTMsgErrorExitFailure("WinStartTimer(,,,%u ms) failed: %#x", cMsWait + 1, WinGetLastError(g_hOs2Ab));
+
+#elif defined(RT_OS_WINDOWS)
+ /*
+ * Arm a timer which will timeout after the desired period and
+ * quit when we've dispatched it.
+ */
+ g_fWinTimerTicked = false;
+ if (SetTimer(g_hWinWnd, 1 /*idEvent*/, cMsWait + 1, NULL /*pfnTimerProc*/) != 0)
+ {
+ MSG Msg;
+ while (GetMessageW(&Msg, NULL, 0, 0))
+ {
+ TranslateMessage(&Msg);
+ DispatchMessageW(&Msg);
+ if (g_fWinTimerTicked || RTTimeMilliTS() - msStart >= cMsWait)
+ break;
+ }
+
+ if (!KillTimer(g_hWinWnd, 1 /*idEvent*/))
+ RTMsgWarning("KillTimer failed: %u (%#x)", GetLastError(), GetLastError());
+ }
+ else
+ return RTMsgErrorExitFailure("SetTimer(,,%u ms,) failed: %u (%#x)", cMsWait + 1, GetLastError(), GetLastError());
+
+#else
+/** @todo X11 needs to run it's message queue too, because if we're offering
+ * things on the "clipboard" we must reply to requests for them. */
+ /*
+ * Just a plain simple RTThreadSleep option (will probably not be used in the end):
+ */
+ for (;;)
+ {
+ uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
+ if (cMsElapsed >= cMsWait)
+ break;
+ RTThreadSleep(cMsWait - cMsElapsed);
+ }
+#endif
+
+ if (g_uVerbosity > 2)
+ RTMsgInfo("Done waiting after %u ms.\n", RTTimeMilliTS() - msStart);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Display the usage to @a pStrm.
+ */
+static void Usage(PRTSTREAM pStrm)
+{
+ RTStrmPrintf(pStrm,
+ "usage: %s [--get <fmt> [--get ...]] [--get-file <fmt> <file> [--get-file ...]]\n"
+ " %s [--zap] [--put <fmt> <content> [--put ...]] [--put-file <fmt> <file> [--put-file ...]] [--wait <ms>]\n"
+ " %s [--check <fmt> <expected> [--check ...]] [--check-file <fmt> <file> [--check-file ...]]\n"
+ " [--check-no <fmt> [--check-no ...]]\n"
+ , RTProcShortName(), RTProcShortName(), RTProcShortName());
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "Actions/Options:\n");
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'l': pszHelp = "List the clipboard content."; break;
+ case 'g': pszHelp = "Get given clipboard format and writes it to standard output."; break;
+ case 'G': pszHelp = "Get given clipboard format and writes it to the specified file."; break;
+ case 'p': pszHelp = "Puts given format and content on the clipboard."; break;
+ case 'P': pszHelp = "Puts given format and file content on the clipboard."; break;
+ case 'c': pszHelp = "Checks that the given format and content matches the clipboard."; break;
+ case 'C': pszHelp = "Checks that the given format and file content matches the clipboard."; break;
+ case 'n': pszHelp = "Checks that the given format is not on the clipboard."; break;
+ case 'z': pszHelp = "Zaps the clipboard content."; break;
+#ifdef MULTI_TARGET_CLIPBOARD
+ case 't': pszHelp = "Selects the target clipboard."; break;
+#endif
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ case 'k': pszHelp = "Closes the clipboard if open (win,os2)."; break;
+#endif
+ case 'w': pszHelp = "Waits a given number of milliseconds before continuing."; break;
+ case 'v': pszHelp = "More verbose execution."; break;
+ case 'q': pszHelp = "Quiet execution."; break;
+ case 'h': pszHelp = "Displays this help and exit"; break;
+ case 'V': pszHelp = "Displays the program revision"; break;
+
+ default:
+ pszHelp = "Option undocumented";
+ break;
+ }
+ if ((unsigned)g_aCmdOptions[i].iShort < 127U)
+ {
+ char szOpt[64];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
+ }
+ else
+ RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
+ }
+ RTStrmPrintf(pStrm,
+ "\n"
+ "Note! Options are processed in the order they are given.\n");
+
+ RTStrmPrintf(pStrm, "\nFormats:\n");
+ for (size_t i = 0; i < RT_ELEMENTS(g_aFormats); i++)
+ RTStrmPrintf(pStrm, " %-12s: %s\n", g_aFormats[i].pszName, g_aFormats[i].pszDesc);
+
+#ifdef MULTI_TARGET_CLIPBOARD
+ RTStrmPrintf(pStrm, "\nTarget:\n");
+ for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
+ RTStrmPrintf(pStrm, " %-12s: %s\n", g_aTargets[i].pszName, g_aTargets[i].pszDesc);
+#endif
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Host specific init.
+ */
+#ifdef RT_OS_DARWIN
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+#elif defined(RT_OS_OS2)
+ RTEXITCODE rcExit = CuOs2Init();
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit = CuWinInit();
+#else
+ RTEXITCODE rcExit = CuX11Init();
+#endif
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Process options (in order).
+ */
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
+ switch (rc)
+ {
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ case 'k':
+ rcExit2 = CuCloseClipboard();
+ break;
+#endif
+
+ case 'l':
+ rcExit2 = ListClipboardContent();
+ break;
+
+ case 'g':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ rcExit2 = ClipboardContentToStdOut(pFmtDesc);
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'G':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ {
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_SUCCESS(rc))
+ rcExit2 = ClipboardContentToFile(pFmtDesc, ValueUnion.psz);
+ else
+ return RTMsgErrorExitFailure("No filename given with --get-file");
+ }
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'p':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ {
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_SUCCESS(rc))
+ rcExit2 = PutStringOnClipboard(pFmtDesc, ValueUnion.psz);
+ else
+ return RTMsgErrorExitFailure("No data string given with --put");
+ }
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'P':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ {
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_SUCCESS(rc))
+ rcExit2 = PutFileOnClipboard(pFmtDesc, ValueUnion.psz);
+ else
+ return RTMsgErrorExitFailure("No filename given with --put-file");
+ }
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'c':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ {
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_SUCCESS(rc))
+ rcExit2 = CheckStringAgainstClipboard(pFmtDesc, ValueUnion.psz);
+ else
+ return RTMsgErrorExitFailure("No data string given with --check");
+ }
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'C':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ {
+ rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_SUCCESS(rc))
+ rcExit2 = CheckFileAgainstClipboard(pFmtDesc, ValueUnion.psz);
+ else
+ return RTMsgErrorExitFailure("No filename given with --check-file");
+ }
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+ case 'n':
+ {
+ PCCLIPUTILFORMAT pFmtDesc = GetFormatDesc(ValueUnion.psz);
+ if (pFmtDesc)
+ rcExit2 = CheckFormatNotOnClipboard(pFmtDesc);
+ else
+ rcExit2 = RTEXITCODE_FAILURE;
+ break;
+ }
+
+
+ case 'z':
+ rcExit2 = ZapAllClipboardData();
+ break;
+
+#ifdef MULTI_TARGET_CLIPBOARD
+ case 't':
+ {
+ CLIPUTILTARGET *pNewTarget = NULL;
+ for (size_t i = 0; i < RT_ELEMENTS(g_aTargets); i++)
+ if (strcmp(ValueUnion.psz, g_aTargets[i].pszName) == 0)
+ {
+ pNewTarget = &g_aTargets[i];
+ break;
+ }
+ if (!pNewTarget)
+ return RTMsgErrorExitFailure("Unknown target '%s'", ValueUnion.psz);
+ if (pNewTarget != g_pTarget && g_uVerbosity > 0)
+ RTMsgInfo("Switching from '%s' to '%s'\n", g_pTarget->pszName, pNewTarget->pszName);
+ g_pTarget = pNewTarget;
+ break;
+ }
+#endif
+
+ case 'w':
+ rcExit2 = DoWait(ValueUnion.u32);
+ break;
+
+ case 'q':
+ g_uVerbosity = 0;
+ break;
+
+ case 'v':
+ g_uVerbosity++;
+ break;
+
+ case 'h':
+ Usage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ {
+ char szRev[] = "$Revision: 155244 $";
+ szRev[RT_ELEMENTS(szRev) - 2] = '\0';
+ RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
+ return RTEXITCODE_SUCCESS;
+ }
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+
+ if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ }
+
+ /*
+ * Host specific cleanup.
+ */
+#if defined(RT_OS_OS2)
+ RTEXITCODE rcExit2 = CuOs2Term();
+#elif defined(RT_OS_WINDOWS)
+ RTEXITCODE rcExit2 = CuWinTerm();
+#else
+ RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
+#endif
+ if (rcExit2 != RTEXITCODE_SUCCESS && rcExit != RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/clipboard/Makefile.kmk b/src/VBox/ValidationKit/utils/clipboard/Makefile.kmk
new file mode 100644
index 00000000..13e54dba
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/clipboard/Makefile.kmk
@@ -0,0 +1,53 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Clipboard tests.
+#
+
+#
+# Copyright (C) 2021-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Clipboard Utility.
+#
+PROGRAMS += ClipUtil
+ClipUtil_TEMPLATE = VBoxValidationKitR3
+ClipUtil_SOURCES = ClipUtil.cpp
+ifn1of ($(KBUILD_TARGET), darwin os2 win)
+ ClipUtil_LIBPATH = $(VBOX_LIBPATH_X11)
+ ClipUtil_LIBS = X11 Xmu
+endif
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/cpu/Makefile.kmk b/src/VBox/ValidationKit/utils/cpu/Makefile.kmk
new file mode 100644
index 00000000..219f928b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/Makefile.kmk
@@ -0,0 +1,84 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - CPU Test Utilities.
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if1of ($(KBUILD_TARGET_ARCH), x86 amd64)
+ PROGRAMS += xmmsaving
+ xmmsaving_TEMPLATE = VBoxValidationKitR3
+ xmmsaving_SOURCES = xmmsaving.cpp xmmsaving-asm.asm
+endif
+
+if1of ($(KBUILD_TARGET_ARCH), x86 amd64)
+ PROGRAMS += exceptionsR3
+ exceptionsR3_TEMPLATE = VBoxValidationKitR3
+ exceptionsR3_SOURCES = exceptionsR3.cpp exceptionsR3-asm.asm
+endif
+
+PROGRAMS += cpu-numa
+cpu-numa_TEMPLATE = VBoxValidationKitR3
+cpu-numa_SOURCES = cpu-numa.cpp
+
+PROGRAMS += cpu-alloc-all-mem
+cpu-alloc-all-mem_TEMPLATE = VBoxValidationKitR3
+cpu-alloc-all-mem_SOURCES = cpu-alloc-all-mem.cpp
+
+if1of ($(KBUILD_TARGET_ARCH), x86 amd64)
+ ifneq ($(KBUILD_HOST),os2)
+ PROGRAMS += cidet-app
+ endif
+ cidet-app_TEMPLATE = VBoxValidationKitR3
+ cidet-app_SOURCES = \
+ cidet-app.cpp \
+ cidet-appA.asm \
+ cidet-core.cpp \
+ cidet-instr-1.cpp
+ cidet-app_DEFS = IN_DIS
+ cidet-app_DEFS.linux = CIDET_REDUCED_CTX
+ cidet-app_LIBS = $(PATH_STAGE_LIB)/DisasmR3Static$(VBOX_SUFF_LIB)
+ cidet-app_VBOX_IMPORT_CHECKER.win.x86 = $(NO_SUCH_VARIABLE) # doesn't work on NT4 yet.
+endif
+
+if1of ($(KBUILD_TARGET_ARCH), x86 amd64)
+ PROGRAMS += rdtsc
+ rdtsc_TEMPLATE = VBoxValidationKitR3
+ rdtsc_SOURCES = rdtsc.cpp rdtsc-asm.asm
+endif
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp
new file mode 100644
index 00000000..40ab8e7b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet-app.cpp
@@ -0,0 +1,1376 @@
+/* $Id: cidet-app.cpp $ */
+/** @file
+ * CPU Instruction Decoding & Execution Tests - Ring-3 Driver Application.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "cidet.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#else
+# define USE_SIGNALS
+# include <signal.h>
+# include <unistd.h>
+# include <sys/ucontext.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def CIDET_LEAVE_GS_ALONE
+ * Leave GS alone on 64-bit darwin (gs is 0, no ldt or gdt entry to load that'll
+ * restore the lower 32-bits of the base when saving and restoring the register).
+ */
+#if (defined(RT_OS_DARWIN) && defined(RT_ARCH_AMD64)) || defined(DOXYGEN_RUNNING)
+# define CIDET_LEAVE_GS_ALONE
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * CIDET driver app buffer.
+ */
+typedef struct CIDETAPPBUF
+{
+ /** The buffer size. */
+ size_t cb;
+ /** The normal allocation.
+ * There is a fence page before this as well as at pbNormal+cb. */
+ uint8_t *pbNormal;
+ /** The low memory allocation (32-bit addressable if 64-bit host, 16-bit
+ * addressable if 32-bit host). */
+ uint8_t *pbLow;
+ /** Set if we're using the normal buffer, clear if it's the low one. */
+ bool fUsingNormal : 1;
+ /** Set if the buffer is armed, clear if mostly accessible. */
+ bool fArmed : 1;
+ /** Set if this is a code buffer. */
+ bool fIsCode : 1;
+ /** The memory protection for the pages (RTMEM_PROT_XXX). */
+ uint8_t fDefaultProt : 3;
+ /** The memory protection for the last page (RTMEM_PROT_XXX). */
+ uint8_t fLastPageProt : 3;
+ /** The buffer index. */
+ uint16_t idxCfg;
+} CIDETAPPBUF;
+/** Pointer to a CIDET driver app buffer. */
+typedef CIDETAPPBUF *PCIDETAPPBUF;
+
+/** Number of code buffers. */
+#define CIDETAPP_CODE_BUF_COUNT 1
+/** Number of data buffers. */
+#define CIDETAPP_DATA_BUF_COUNT 1
+
+
+/**
+ * CIDET driver app instance.
+ */
+typedef struct CIDETAPP
+{
+ /** The core structure. */
+ CIDETCORE Core;
+ /** The execute return context. */
+ CIDETCPUCTX ExecuteCtx;
+ /** Code buffers (runs parallel to g_aCodeBufCfgs). */
+ CIDETAPPBUF aCodeBuffers[CIDETAPP_CODE_BUF_COUNT];
+ /** Data buffers (runs parallel to g_aDataBufCfgs). */
+ CIDETAPPBUF aDataBuffers[CIDETAPP_DATA_BUF_COUNT];
+
+ /** The lowest stack address. */
+ uint8_t *pbStackLow;
+ /** The end of the stack allocation (highest address). */
+ uint8_t *pbStackEnd;
+ /** Stack size (= pbStackEnd - pbStackLow). */
+ uint32_t cbStack;
+ /** Whether we're currently using the 'lock int3' to deal with tricky stack. */
+ bool fUsingLockedInt3;
+} CIDETAPP;
+/** Pointer to a CIDET driver app instance. */
+typedef CIDETAPP *PCIDETAPP;
+/** Pointer to a pointer to a CIDET driver app instance. */
+typedef PCIDETAPP *PPCIDETAPP;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The test instance handle. */
+static RTTEST g_hTest;
+/** Points to the instance data while executing, NULL if not executing or if
+ * we've already handled the first exception while executing. */
+static PCIDETAPP volatile g_pExecutingThis;
+#ifdef USE_SIGNALS
+/** The default signal mask. */
+static sigset_t g_ProcSigMask;
+/** The alternative signal stack. */
+static stack_t g_AltStack;
+#endif
+
+
+/** Code buffer configurations (parallel to CIDETAPP::aCodeBuffers). */
+static CIDETBUFCFG g_aCodeBufCfgs[CIDETAPP_CODE_BUF_COUNT] =
+{
+ {
+ "Normal",
+ CIDETBUF_PROT_RWX | CIDETBUF_DPL_3 | CIDETBUF_DPL_SAME | CIDETBUF_SEG_ER | CIDETBUF_KIND_CODE,
+ },
+};
+
+/** Data buffer configurations (parallel to CIDETAPP::aDataBuffers). */
+static CIDETBUFCFG g_aDataBufCfgs[CIDETAPP_DATA_BUF_COUNT] =
+{
+ {
+ "Normal",
+ CIDETBUF_PROT_RWX | CIDETBUF_DPL_3 | CIDETBUF_DPL_SAME | CIDETBUF_SEG_RW | CIDETBUF_KIND_DATA,
+ },
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLASM(void) CidetAppSaveAndRestoreCtx(void);
+DECLASM(void) CidetAppRestoreCtx(PCCIDETCPUCTX pRestoreCtx);
+DECLASM(void) CidetAppExecute(PCIDETCPUCTX pSaveCtx, PCCIDETCPUCTX pRestoreCtx);
+
+
+/*
+ *
+ *
+ * Exception and signal handling.
+ * Exception and signal handling.
+ * Exception and signal handling.
+ *
+ *
+ */
+
+#ifdef RT_OS_WINDOWS
+static int CidetAppXcptFilter(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF
+{
+ /*
+ * Grab the this point. We expect at most one signal.
+ */
+ PCIDETAPP pThis = g_pExecutingThis;
+ g_pExecutingThis = NULL;
+ if (pThis == NULL)
+ {
+ /* we're up the infamous creek... */
+ for (;;) ExitProcess(2);
+ }
+
+ /*
+ * Gather CPU state information from the context structure.
+ */
+ CONTEXT *pSrcCtx = pXcptPtrs->ContextRecord;
+# ifdef RT_ARCH_AMD64
+ if ( (pSrcCtx->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ != (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ __debugbreak();
+ pThis->Core.ActualCtx.rip = pSrcCtx->Rip;
+ pThis->Core.ActualCtx.rfl = pSrcCtx->EFlags;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pSrcCtx->Rax;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pSrcCtx->Rcx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pSrcCtx->Rdx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pSrcCtx->Rbx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pSrcCtx->Rsp;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pSrcCtx->Rbp;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pSrcCtx->Rsi;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pSrcCtx->Rdi;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x8] = pSrcCtx->R8;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x9] = pSrcCtx->R9;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = pSrcCtx->R10;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = pSrcCtx->R11;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = pSrcCtx->R12;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = pSrcCtx->R13;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = pSrcCtx->R14;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = pSrcCtx->R15;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pSrcCtx->SegEs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pSrcCtx->SegCs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pSrcCtx->SegSs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pSrcCtx->SegDs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pSrcCtx->SegFs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pSrcCtx->SegGs;
+ if (pSrcCtx->ContextFlags & CONTEXT_FLOATING_POINT)
+ {
+ /* ... */
+ }
+ if (pSrcCtx->ContextFlags & CONTEXT_DEBUG_REGISTERS)
+ {
+ /* ... */
+ }
+
+# elif defined(RT_ARCH_X86)
+ if ( (pSrcCtx->ContextFlags & (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ != (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS))
+ __debugbreak();
+ pThis->Core.ActualCtx.rip = pSrcCtx->Eip;
+ pThis->Core.ActualCtx.rfl = pSrcCtx->EFlags;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pSrcCtx->Eax;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pSrcCtx->Ecx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pSrcCtx->Edx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pSrcCtx->Ebx;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pSrcCtx->Esp;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pSrcCtx->Ebp;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pSrcCtx->Esi;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pSrcCtx->Edi;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x8] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x9] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = 0;
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = 0;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pSrcCtx->SegEs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pSrcCtx->SegCs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pSrcCtx->SegSs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pSrcCtx->SegDs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pSrcCtx->SegFs;
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pSrcCtx->SegGs;
+ if (pSrcCtx->ContextFlags & CONTEXT_FLOATING_POINT)
+ {
+ /* ... */
+ }
+ if (pSrcCtx->ContextFlags & CONTEXT_EXTENDED_REGISTERS)
+ {
+ /* ... */
+ }
+ if (pSrcCtx->ContextFlags & CONTEXT_DEBUG_REGISTERS)
+ {
+ /* ... */
+ }
+# else
+# error "Not supported"
+# endif
+
+ /*
+ * Add/Adjust CPU state information according to the exception code.
+ */
+ pThis->Core.ActualCtx.uErr = UINT64_MAX;
+ switch (pXcptPtrs->ExceptionRecord->ExceptionCode)
+ {
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_DE;
+ break;
+ case EXCEPTION_SINGLE_STEP:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_DB;
+ break;
+ case EXCEPTION_BREAKPOINT:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_BP;
+ break;
+ case EXCEPTION_INT_OVERFLOW:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_OF;
+ break;
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_BR;
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_UD;
+ break;
+
+ case EXCEPTION_PRIV_INSTRUCTION:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_GP;
+ pThis->Core.ActualCtx.uErr = 0;
+ break;
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ {
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_PF;
+ pThis->Core.ActualCtx.cr2 = pXcptPtrs->ExceptionRecord->ExceptionInformation[1];
+ pThis->Core.ActualCtx.uErr = 0;
+ if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT)
+ pThis->Core.ActualCtx.uErr = X86_TRAP_PF_RW;
+ else if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
+ pThis->Core.ActualCtx.uErr = X86_TRAP_PF_ID;
+ else if (pXcptPtrs->ExceptionRecord->ExceptionInformation[0] != EXCEPTION_READ_FAULT)
+ AssertFatalFailed();
+
+ MEMORY_BASIC_INFORMATION MemInfo = {0};
+ if (VirtualQuery((PVOID)pXcptPtrs->ExceptionRecord->ExceptionInformation[1], &MemInfo, sizeof(MemInfo)) > 0)
+ switch (MemInfo.Protect & 0xff)
+ {
+ case PAGE_NOACCESS:
+ break;
+ case PAGE_READONLY:
+ case PAGE_READWRITE:
+ case PAGE_WRITECOPY:
+ case PAGE_EXECUTE:
+ case PAGE_EXECUTE_READ:
+ case PAGE_EXECUTE_READWRITE:
+ case PAGE_EXECUTE_WRITECOPY:
+ pThis->Core.ActualCtx.uErr |= X86_TRAP_PF_P;
+ break;
+ default:
+ AssertFatalFailed();
+ }
+ break;
+ }
+
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_MF;
+ break;
+
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ pThis->Core.ActualCtx.uXcpt = X86_XCPT_AC;
+ break;
+
+ default:
+ pThis->Core.ActualCtx.uXcpt = pXcptPtrs->ExceptionRecord->ExceptionCode;
+ break;
+ }
+
+ /*
+ * Our own personal long jump implementation.
+ */
+ CidetAppRestoreCtx(&pThis->ExecuteCtx);
+
+ /* Won't return...*/
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+
+/**
+ * Vectored exception handler.
+ *
+ * @returns Long jumps or terminates the process.
+ * @param pXcptPtrs The exception record.
+ */
+static LONG CALLBACK CidetAppVectoredXcptHandler(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF
+{
+ RTStrmPrintf(g_pStdErr, "CidetAppVectoredXcptHandler!\n");
+ CidetAppXcptFilter(pXcptPtrs);
+
+ /* won't get here. */
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+/**
+ * Unhandled exception filter.
+ *
+ * @returns Long jumps or terminates the process.
+ * @param pXcptPtrs The exception record.
+ */
+static LONG CALLBACK CidetAppUnhandledXcptFilter(EXCEPTION_POINTERS *pXcptPtrs) RT_NOTHROW_DEF
+{
+ RTStrmPrintf(g_pStdErr, "CidetAppUnhandledXcptFilter!\n");
+ CidetAppXcptFilter(pXcptPtrs);
+
+ /* won't get here. */
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+#elif defined(USE_SIGNALS)
+/**
+ * Signal handler.
+ */
+static void CidetAppSigHandler(int iSignal, siginfo_t *pSigInfo, void *pvCtx)
+{
+# if 1
+ if ( !g_pExecutingThis
+ || !g_pExecutingThis->fUsingLockedInt3
+ || iSignal != SIGILL)
+ {
+ RTStrmPrintf(g_pStdErr, "signal %d pSigInfo=%p pvCtx=%p", iSignal, pSigInfo, pvCtx);
+ if (pSigInfo)
+ RTStrmPrintf(g_pStdErr, " si_addr=%p si_code=%#x sival_ptr=%p sival_int=%d",
+ pSigInfo->si_addr, pSigInfo->si_code, pSigInfo->si_value.sival_ptr, pSigInfo->si_value.sival_int);
+ RTStrmPrintf(g_pStdErr, "\n");
+ }
+# endif
+
+ /*
+ * Grab the this point. We expect at most one signal.
+ */
+ PCIDETAPP pThis = g_pExecutingThis;
+ g_pExecutingThis = NULL;
+ if (pThis == NULL)
+ {
+ /* we're up the infamous creek... */
+ RTStrmPrintf(g_pStdErr, "Creek time!\n");
+ for (;;) _exit(2);
+ }
+
+ /*
+ * Gather all the CPU state information available.
+ */
+# ifdef RT_OS_LINUX
+ ucontext_t const *pCtx = (ucontext_t const *)pvCtx;
+# ifdef RT_ARCH_AMD64
+
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pCtx->uc_mcontext.gregs[REG_RAX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pCtx->uc_mcontext.gregs[REG_RCX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pCtx->uc_mcontext.gregs[REG_RDX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pCtx->uc_mcontext.gregs[REG_RBX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pCtx->uc_mcontext.gregs[REG_RSP];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pCtx->uc_mcontext.gregs[REG_RBP];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pCtx->uc_mcontext.gregs[REG_RSI];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pCtx->uc_mcontext.gregs[REG_RDI];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x8 ] = pCtx->uc_mcontext.gregs[REG_R8];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x9 ] = pCtx->uc_mcontext.gregs[REG_R9];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x10] = pCtx->uc_mcontext.gregs[REG_R10];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x11] = pCtx->uc_mcontext.gregs[REG_R11];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x12] = pCtx->uc_mcontext.gregs[REG_R12];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x13] = pCtx->uc_mcontext.gregs[REG_R13];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x14] = pCtx->uc_mcontext.gregs[REG_R14];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_x15] = pCtx->uc_mcontext.gregs[REG_R15];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = RT_LO_U16((uint32_t)pCtx->uc_mcontext.gregs[REG_CSGSFS]);
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = RT_HI_U16((uint32_t)pCtx->uc_mcontext.gregs[REG_CSGSFS]);
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = (uint16_t)RT_HI_U32(pCtx->uc_mcontext.gregs[REG_CSGSFS]);
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = ASMGetDS();
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = ASMGetES();
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = ASMGetSS();
+ pThis->Core.ActualCtx.rip = pCtx->uc_mcontext.gregs[REG_RIP];
+ pThis->Core.ActualCtx.rfl = pCtx->uc_mcontext.gregs[REG_EFL];
+ pThis->Core.ActualCtx.cr2 = pCtx->uc_mcontext.gregs[REG_CR2];
+ pThis->Core.ActualCtx.uXcpt = pCtx->uc_mcontext.gregs[REG_TRAPNO];
+ pThis->Core.ActualCtx.uErr = pCtx->uc_mcontext.gregs[REG_ERR];
+
+ /* Fudge the FS and GS registers as setup_sigcontext returns 0. */
+ if (pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] == 0)
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pThis->Core.ExpectedCtx.aSRegs[X86_SREG_FS];
+ if (pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] == 0)
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pThis->Core.ExpectedCtx.aSRegs[X86_SREG_GS];
+
+# elif defined(RT_ARCH_X86)
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xAX] = pCtx->uc_mcontext.gregs[REG_EAX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xCX] = pCtx->uc_mcontext.gregs[REG_ECX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDX] = pCtx->uc_mcontext.gregs[REG_EDX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBX] = pCtx->uc_mcontext.gregs[REG_EBX];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSP] = pCtx->uc_mcontext.gregs[REG_ESP];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xBP] = pCtx->uc_mcontext.gregs[REG_EBP];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xSI] = pCtx->uc_mcontext.gregs[REG_ESI];
+ pThis->Core.ActualCtx.aGRegs[X86_GREG_xDI] = pCtx->uc_mcontext.gregs[REG_EDI];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_CS] = pCtx->uc_mcontext.gregs[REG_CS];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_DS] = pCtx->uc_mcontext.gregs[REG_DS];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_ES] = pCtx->uc_mcontext.gregs[REG_ES];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_FS] = pCtx->uc_mcontext.gregs[REG_FS];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_GS] = pCtx->uc_mcontext.gregs[REG_GS];
+ pThis->Core.ActualCtx.aSRegs[X86_SREG_SS] = pCtx->uc_mcontext.gregs[REG_SS];
+ pThis->Core.ActualCtx.rip = pCtx->uc_mcontext.gregs[REG_EIP];
+ pThis->Core.ActualCtx.rfl = pCtx->uc_mcontext.gregs[REG_EFL];
+ pThis->Core.ActualCtx.cr2 = pCtx->uc_mcontext.cr2;
+ pThis->Core.ActualCtx.uXcpt = pCtx->uc_mcontext.gregs[REG_TRAPNO];
+ pThis->Core.ActualCtx.uErr = pCtx->uc_mcontext.gregs[REG_ERR];
+
+# else
+# error "Unsupported arch."
+# endif
+
+ /* Adjust uErr. */
+ switch (pThis->Core.ActualCtx.uXcpt)
+ {
+ case X86_XCPT_TS:
+ case X86_XCPT_NP:
+ case X86_XCPT_SS:
+ case X86_XCPT_GP:
+ case X86_XCPT_PF:
+ case X86_XCPT_AC:
+ case X86_XCPT_DF:
+ break;
+ default:
+ pThis->Core.ActualCtx.uErr = UINT64_MAX;
+ break;
+ }
+
+# if 0
+ /* Fudge the resume flag (it's probably always set here). */
+ if ( (pThis->Core.ActualCtx.rfl & X86_EFL_RF)
+ && !(pThis->Core.ExpectedCtx.rfl & X86_EFL_RF))
+ pThis->Core.ActualCtx.rfl &= ~X86_EFL_RF;
+# endif
+
+# else
+ /** @todo */
+# endif
+
+
+ /*
+ * Check for the 'lock int3' instruction used for tricky stacks.
+ */
+ if ( pThis->fUsingLockedInt3
+ && pThis->Core.ActualCtx.uXcpt == X86_XCPT_UD
+ && pThis->Core.ActualCtx.rip == pThis->Core.CodeBuf.uEffBufAddr - pThis->Core.CodeBuf.offSegBase
+ + pThis->Core.CodeBuf.offActive + pThis->Core.CodeBuf.cbActive )
+ {
+ pThis->Core.ActualCtx.uXcpt = UINT32_MAX;
+ Assert(pThis->Core.ActualCtx.uErr == UINT64_MAX);
+ pThis->Core.ActualCtx.rfl &= ~X86_EFL_RF;
+ }
+
+ /*
+ * Jump back to CidetAppCbExecute.
+ */
+ CidetAppRestoreCtx(&pThis->ExecuteCtx);
+}
+#endif
+
+
+
+/*
+ *
+ * Buffer handling
+ * Buffer handling
+ * Buffer handling
+ *
+ *
+ */
+
+static int cidetAppAllocateAndConfigureOneBuffer(PCIDETAPP pThis, PCIDETAPPBUF pBuf, uint16_t idxBuf, bool fIsCode,
+ uint32_t fFlags)
+{
+ RT_NOREF_PV(pThis);
+ static uint8_t const s_afBufProtToDefaultMemProt[] =
+ {
+ /* [0] = */ RTMEM_PROT_NONE,
+ /* [1] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC,
+ /* [2] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE,
+ /* [3] = */ RTMEM_PROT_READ | RTMEM_PROT_EXEC,
+ /* [4] = */ RTMEM_PROT_READ,
+ /* [5] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC,
+ /* [6] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC,
+ /* [7] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC,
+ /* [8] = */ RTMEM_PROT_NONE,
+ /* [9] = */ RTMEM_PROT_NONE,
+ /* [10] = */ RTMEM_PROT_NONE,
+ /* [11] = */ RTMEM_PROT_NONE,
+ /* [12] = */ RTMEM_PROT_NONE,
+ /* [13] = */ RTMEM_PROT_NONE,
+ /* [14] = */ RTMEM_PROT_NONE,
+ /* [15] = */ RTMEM_PROT_NONE,
+ };
+ static uint8_t const s_afBufProtToLastPageMemProt[] =
+ {
+ /* [0] = */ RTMEM_PROT_NONE,
+ /* [1] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC,
+ /* [2] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE,
+ /* [3] = */ RTMEM_PROT_READ | RTMEM_PROT_EXEC,
+ /* [4] = */ RTMEM_PROT_READ,
+ /* [5] = */ RTMEM_PROT_NONE,
+ /* [6] = */ RTMEM_PROT_READ | RTMEM_PROT_WRITE,
+ /* [7] = */ RTMEM_PROT_READ,
+ /* [8] = */ RTMEM_PROT_NONE,
+ /* [9] = */ RTMEM_PROT_NONE,
+ /* [10] = */ RTMEM_PROT_NONE,
+ /* [11] = */ RTMEM_PROT_NONE,
+ /* [12] = */ RTMEM_PROT_NONE,
+ /* [13] = */ RTMEM_PROT_NONE,
+ /* [14] = */ RTMEM_PROT_NONE,
+ /* [15] = */ RTMEM_PROT_NONE,
+ };
+
+ int rc;
+ Assert(CIDETBUF_IS_CODE(fFlags) == fIsCode);
+ pBuf->fIsCode = fIsCode;
+ pBuf->idxCfg = idxBuf;
+ pBuf->fUsingNormal = true;
+ pBuf->fDefaultProt = s_afBufProtToDefaultMemProt[fFlags & CIDETBUF_PROT_MASK];
+ pBuf->fLastPageProt = s_afBufProtToLastPageMemProt[fFlags & CIDETBUF_PROT_MASK];
+ if (pBuf->fDefaultProt != RTMEM_PROT_NONE)
+ {
+ /*
+ * Allocate a 3 page buffer plus two fence pages.
+ */
+ pBuf->cb = fIsCode ? CIDET_CODE_BUF_SIZE : CIDET_DATA_BUF_SIZE;
+ pBuf->pbNormal = (uint8_t *)RTMemPageAlloc(PAGE_SIZE + pBuf->cb + PAGE_SIZE);
+ if (pBuf->pbNormal)
+ {
+ memset(pBuf->pbNormal, 0x55, PAGE_SIZE);
+ memset(pBuf->pbNormal + PAGE_SIZE, 0xcc, pBuf->cb);
+ memset(pBuf->pbNormal + PAGE_SIZE + pBuf->cb, 0x77, PAGE_SIZE);
+
+ /* Set up fence pages. */
+ rc = RTMemProtect(pBuf->pbNormal, PAGE_SIZE, RTMEM_PROT_NONE); /* fence */
+ if (RT_SUCCESS(rc))
+ rc = RTMemProtect(pBuf->pbNormal + PAGE_SIZE + pBuf->cb, PAGE_SIZE, RTMEM_PROT_NONE); /* fence */
+ pBuf->pbNormal += PAGE_SIZE;
+
+ /* Default protection + read + write. */
+ if (RT_SUCCESS(rc))
+ rc = RTMemProtect(pBuf->pbNormal, pBuf->cb, pBuf->fDefaultProt | RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+
+ /*
+ * Allocate a low memory buffer or LDT if necessary.
+ */
+ if ( RT_SUCCESS(rc)
+ && (uintptr_t)pBuf->pbNormal + pBuf->cb > RT_BIT_64(sizeof(uintptr_t) / 2 * 8))
+ {
+ /** @todo Buffers for the other addressing mode. */
+ pBuf->pbLow = NULL;
+ }
+ else
+ pBuf->pbLow = pBuf->pbNormal;
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ }
+ else
+ rc = RTTestIFailedRc(VERR_NO_PAGE_MEMORY, "Error allocating three pages.");
+ }
+ else
+ rc = RTTestIFailedRc(VERR_NO_PAGE_MEMORY, "Unsupported buffer config: fFlags=%#x, idxBuf=%u", fFlags, idxBuf);
+ return rc;
+}
+
+
+static void CidetAppDeleteBuffer(PCIDETAPPBUF pBuf)
+{
+ RTMemProtect(pBuf->pbNormal - PAGE_SIZE, PAGE_SIZE + pBuf->cb + PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemPageFree(pBuf->pbNormal - PAGE_SIZE, PAGE_SIZE + pBuf->cb + PAGE_SIZE);
+ if (pBuf->pbLow != pBuf->pbNormal && pBuf->pbLow)
+ {
+ RTMemProtect(pBuf->pbLow, pBuf->cb, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemFreeEx(pBuf->pbLow, pBuf->cb);
+ }
+}
+
+
+static bool CidetAppArmBuf(PCIDETAPP pThis, PCIDETAPPBUF pAppBuf)
+{
+ RT_NOREF_PV(pThis);
+ uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow);
+ if (pAppBuf->fLastPageProt == pAppBuf->fDefaultProt)
+ {
+ if ((pAppBuf->fDefaultProt & (RTMEM_PROT_READ | RTMEM_PROT_WRITE)) != (RTMEM_PROT_READ | RTMEM_PROT_WRITE))
+ RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf, pAppBuf->cb, pAppBuf->fDefaultProt), VINF_SUCCESS, false);
+ }
+ else
+ {
+ if ((pAppBuf->fDefaultProt & (RTMEM_PROT_READ | RTMEM_PROT_WRITE)) != (RTMEM_PROT_READ | RTMEM_PROT_WRITE))
+ RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf, pAppBuf->cb - PAGE_SIZE, pAppBuf->fDefaultProt), VINF_SUCCESS, false);
+ RTTESTI_CHECK_RC_RET(RTMemProtect(pbUsingBuf + pAppBuf->cb - PAGE_SIZE, PAGE_SIZE, pAppBuf->fLastPageProt),
+ VINF_SUCCESS, false);
+ }
+ pAppBuf->fArmed = true;
+ return true;
+}
+
+
+static bool CidetAppDearmBuf(PCIDETAPP pThis, PCIDETAPPBUF pAppBuf)
+{
+ RT_NOREF_PV(pThis);
+ uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow);
+ int rc = RTMemProtect(pbUsingBuf, pAppBuf->cb, pAppBuf->fDefaultProt | RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTMemProtect failed on %s buf #%u: %Rrc", pAppBuf->fIsCode ? "code" : "data", pAppBuf->idxCfg, rc);
+ return false;
+ }
+ pAppBuf->fArmed = false;
+ return true;
+}
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnReInitDataBuf}
+ */
+static DECLCALLBACK(bool) CidetAppCbReInitDataBuf(PCIDETCORE pThis, PCIDETBUF pBuf)
+{
+ PCIDETAPP pThisApp = (PCIDETAPP)pThis;
+ PCIDETAPPBUF pAppBuf = &pThisApp->aDataBuffers[pBuf->idxCfg];
+ Assert(CIDETBUF_IS_DATA(pBuf->pCfg->fFlags));
+
+ /*
+ * De-arm the buffer.
+ */
+ if (pAppBuf->fArmed)
+ if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf)))
+ return false;
+
+ /*
+ * Check the allocation requirements.
+ */
+ if (RT_UNLIKELY((size_t)pBuf->off + pBuf->cb > pAppBuf->cb))
+ {
+ RTTestIFailed("Buffer too small; off=%#x cb=%#x pAppBuf->cb=%#x (%s)",
+ pBuf->off, pBuf->cb, pAppBuf->cb, pBuf->pCfg->pszName);
+ return false;
+ }
+
+ /*
+ * Do we need to use the low buffer? Check that we have one, if we need it.
+ */
+ bool fUseNormal = pThis->cbAddrMode == ARCH_BITS / 8;
+ if (!fUseNormal && !pAppBuf->pbLow)
+ return false;
+
+ /*
+ * Update the state.
+ */
+ pAppBuf->fUsingNormal = fUseNormal;
+
+ pBuf->offActive = pBuf->off;
+ pBuf->cbActive = pBuf->cb;
+ pBuf->cbPrologue = 0;
+ pBuf->cbEpilogue = 0;
+ pBuf->uSeg = UINT32_MAX;
+ pBuf->cbActiveSegLimit = UINT64_MAX;
+ pBuf->uSegBase = 0;
+ if (fUseNormal)
+ pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbNormal;
+ else
+ pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbLow;
+
+ return true;
+}
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnSetupDataBuf}
+ */
+static DECLCALLBACK(bool) CidetAppCbSetupDataBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvSrc)
+{
+ PCIDETAPP pThisApp = (PCIDETAPP)pThis;
+ PCIDETAPPBUF pAppBuf = &pThisApp->aDataBuffers[pBuf->idxCfg];
+ Assert(CIDETBUF_IS_DATA(pBuf->pCfg->fFlags));
+ Assert(!pAppBuf->fArmed);
+
+
+ /*
+ * Copy over the data.
+ */
+ uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow);
+ memcpy(pbUsingBuf + pBuf->offActive, pvSrc, pBuf->cbActive);
+
+ /*
+ * Arm the buffer.
+ */
+ return CidetAppArmBuf(pThisApp, pAppBuf);
+}
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnIsBufEqual}
+ */
+static DECLCALLBACK(bool) CidetAppCbIsBufEqual(PCIDETCORE pThis, struct CIDETBUF *pBuf, void const *pvExpected)
+{
+ PCIDETAPP pThisApp = (PCIDETAPP)pThis;
+ PCIDETAPPBUF pAppBuf = CIDETBUF_IS_CODE(pBuf->pCfg->fFlags)
+ ? &pThisApp->aCodeBuffers[pBuf->idxCfg]
+ : &pThisApp->aDataBuffers[pBuf->idxCfg];
+
+ /*
+ * Disarm the buffer if we can't read it all.
+ */
+ if ( pAppBuf->fArmed
+ && ( !(pAppBuf->fLastPageProt & RTMEM_PROT_READ)
+ || !(pAppBuf->fDefaultProt & RTMEM_PROT_READ)) )
+ if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf)))
+ return false;
+
+ /*
+ * Do the comparing.
+ */
+ uint8_t *pbUsingBuf = (pAppBuf->fUsingNormal ? pAppBuf->pbNormal : pAppBuf->pbLow);
+ if (memcmp(pbUsingBuf + pBuf->offActive, pvExpected, pBuf->cbActive) != 0)
+ {
+ /** @todo RTMEM_PROT_NONE may kill content on some hosts... */
+ return false;
+ }
+
+ /** @todo check padding. */
+ return true;
+}
+
+
+/*
+ *
+ * Code buffer, prologue, epilogue, and execution.
+ * Code buffer, prologue, epilogue, and execution.
+ * Code buffer, prologue, epilogue, and execution.
+ *
+ *
+ */
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnReInitCodeBuf}
+ */
+static DECLCALLBACK(bool) CidetAppCbReInitCodeBuf(PCIDETCORE pThis, PCIDETBUF pBuf)
+{
+ PCIDETAPP pThisApp = (PCIDETAPP)pThis;
+ PCIDETAPPBUF pAppBuf = &pThisApp->aCodeBuffers[pBuf->idxCfg];
+ Assert(CIDETBUF_IS_CODE(pBuf->pCfg->fFlags));
+ Assert(pAppBuf->fUsingNormal);
+
+ /*
+ * De-arm the buffer.
+ */
+ if (pAppBuf->fArmed)
+ if (RT_UNLIKELY(!CidetAppDearmBuf(pThisApp, pAppBuf)))
+ return false;
+
+ /*
+ * Determin the prologue and epilogue sizes.
+ */
+ uint16_t cbPrologue = 0;
+ uint16_t cbEpilogue = ARCH_BITS == 64 ? 0x56 : 0x4e;
+ if (pThis->InCtx.fTrickyStack)
+ cbEpilogue = 16;
+
+ /*
+ * Check the allocation requirements.
+ */
+ if (RT_UNLIKELY( cbPrologue > pBuf->off
+ || (size_t)pBuf->off + pBuf->cb + cbEpilogue > pAppBuf->cb))
+ {
+ RTTestIFailed("Buffer too small; off=%#x cb=%#x cbPro=%#x cbEpi=%#x pAppBuf->cb=%#x (%s)",
+ pBuf->off, pBuf->cb, cbPrologue, cbEpilogue, pAppBuf->cb, pBuf->pCfg->pszName);
+ return false;
+ }
+
+ /*
+ * Update the state.
+ */
+ pAppBuf->fUsingNormal = true;
+
+ pBuf->cbActive = pBuf->cb;
+ pBuf->offActive = pBuf->off;
+ pBuf->cbPrologue = cbPrologue;
+ pBuf->cbEpilogue = cbEpilogue;
+ pBuf->uSeg = UINT32_MAX;
+ pBuf->cbActiveSegLimit = UINT64_MAX;
+ pBuf->uSegBase = 0;
+ pBuf->uEffBufAddr = (uintptr_t)pAppBuf->pbNormal;
+
+ return true;
+}
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnSetupCodeBuf}
+ */
+static DECLCALLBACK(bool) CidetAppCbSetupCodeBuf(PCIDETCORE pThis, PCIDETBUF pBuf, void const *pvInstr)
+{
+ PCIDETAPP pThisApp = (PCIDETAPP)pThis;
+ PCIDETAPPBUF pAppBuf =&pThisApp->aCodeBuffers[pBuf->idxCfg];
+ Assert(CIDETBUF_IS_CODE(pBuf->pCfg->fFlags));
+ Assert(pAppBuf->fUsingNormal);
+ Assert(!pAppBuf->fArmed);
+
+ /*
+ * Emit prologue code.
+ */
+ uint8_t *pbDst = pAppBuf->pbNormal + pBuf->offActive - pBuf->cbPrologue;
+
+ /*
+ * Copy over the code.
+ */
+ Assert(pbDst == &pAppBuf->pbNormal[pBuf->offActive]);
+ memcpy(pbDst, pvInstr, pBuf->cbActive);
+ pbDst += pBuf->cbActive;
+
+ /*
+ * Emit epilogue code.
+ */
+ if (!pThis->InCtx.fTrickyStack)
+ {
+ /*
+ * The stack is reasonably good, do minimal work.
+ *
+ * Note! Ideally, we would just fill in 16 int3s here and check that
+ * we hit the first right one. However, if we wish to run this
+ * code with IEM, we better skip unnecessary trips to ring-0.
+ */
+ uint8_t * const pbStartEpilogue = pbDst;
+
+ /* jmp $+6 */
+ *pbDst++ = 0xeb;
+ *pbDst++ = 0x06; /* This is a push es, so if the decoder is one off, we'll hit the int 3 below. */
+
+ /* Six int3s for trapping incorrectly decoded instructions. */
+ *pbDst++ = 0xcc;
+ *pbDst++ = 0xcc;
+ *pbDst++ = 0xcc;
+ *pbDst++ = 0xcc;
+ *pbDst++ = 0xcc;
+ *pbDst++ = 0xcc;
+
+ /* push rip / call $+0 */
+ *pbDst++ = 0xe8;
+ *pbDst++ = 0x00;
+ *pbDst++ = 0x00;
+ *pbDst++ = 0x00;
+ *pbDst++ = 0x00;
+ uint8_t offRipAdjust = (uint8_t)(uintptr_t)(pbStartEpilogue - pbDst);
+
+ /* push xCX */
+ *pbDst++ = 0x51;
+
+ /* mov xCX, [xSP + xCB] */
+ *pbDst++ = 0x48;
+ *pbDst++ = 0x8b;
+ *pbDst++ = 0x4c;
+ *pbDst++ = 0x24;
+ *pbDst++ = sizeof(uintptr_t);
+
+ /* lea xCX, [xCX - 24] */
+ *pbDst++ = 0x48;
+ *pbDst++ = 0x8d;
+ *pbDst++ = 0x49;
+ *pbDst++ = offRipAdjust;
+
+ /* mov xCX, [xSP + xCB] */
+ *pbDst++ = 0x48;
+ *pbDst++ = 0x89;
+ *pbDst++ = 0x4c;
+ *pbDst++ = 0x24;
+ *pbDst++ = sizeof(uintptr_t);
+
+ /* mov xCX, &pThis->ActualCtx */
+#ifdef RT_ARCH_AMD64
+ *pbDst++ = 0x48;
+#endif
+ *pbDst++ = 0xb9;
+ *(uintptr_t *)pbDst = (uintptr_t)&pThisApp->Core.ActualCtx;
+ pbDst += sizeof(uintptr_t);
+
+ /* pop [ss:rcx + ActualCtx.aGRegs[X86_GREG_xCX]] */
+ *pbDst++ = 0x36;
+ *pbDst++ = 0x8f;
+ *pbDst++ = 0x41;
+ *pbDst++ = RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xCX]);
+ Assert(RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xCX]) < 0x7f);
+
+ /* mov [ss:rcx + ActualCtx.aGRegs[X86_GREG_xDX]], rdx */
+ *pbDst++ = 0x36;
+#ifdef RT_ARCH_AMD64
+ *pbDst++ = 0x48;
+#endif
+ *pbDst++ = 0x89;
+ *pbDst++ = 0x51;
+ *pbDst++ = RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xDX]);
+ Assert(RT_UOFFSETOF(CIDETCPUCTX, aGRegs[X86_GREG_xDX]) < 0x7f);
+
+ /* mov [ss:rcx + ActualCtx.aSRegs[X86_GREG_DS]], ds */
+ *pbDst++ = 0x36;
+ *pbDst++ = 0x8c;
+ *pbDst++ = 0x99;
+ *(uint32_t *)pbDst = RT_UOFFSETOF(CIDETCPUCTX, aSRegs[X86_SREG_DS]);
+ pbDst += sizeof(uint32_t);
+
+ /* mov edx, 0XXYYh */
+ *pbDst++ = 0xba;
+ *(uint32_t *)pbDst = pThisApp->Core.InTemplateCtx.aSRegs[X86_SREG_DS];
+ pbDst += sizeof(uint32_t);
+
+ /* mov ds, dx */
+ *pbDst++ = 0x8e;
+ *pbDst++ = 0xda;
+
+ /* mov xDX, &pThisApp->ExecuteCtx */
+#ifdef RT_ARCH_AMD64
+ *pbDst++ = 0x48;
+#endif
+ *pbDst++ = 0xba;
+ *(uintptr_t *)pbDst = (uintptr_t)&pThisApp->ExecuteCtx;
+ pbDst += sizeof(uintptr_t);
+
+#ifdef RT_ARCH_AMD64
+ /* jmp [cs:$ wrt rip] */
+ *pbDst++ = 0xff;
+ *pbDst++ = 0x25;
+ *(uint32_t *)pbDst = 0;
+ pbDst += sizeof(uint32_t);
+#else
+ /* jmp NAME(CidetAppSaveAndRestoreCtx) */
+ *pbDst++ = 0xb9;
+#endif
+ *(uintptr_t *)pbDst = (uintptr_t)CidetAppSaveAndRestoreCtx;
+ pbDst += sizeof(uintptr_t);
+
+ /* int3 */
+ *pbDst++ = 0xcc;
+
+ pThisApp->fUsingLockedInt3 = false;
+
+ }
+ else
+ {
+ /*
+ * Tricky stack, so just make it raise #UD after a successful run.
+ */
+ *pbDst++ = 0xf0; /* lock prefix */
+ memset(pbDst, 0xcc, 15); /* int3 */
+ pbDst += 15;
+
+ pThisApp->fUsingLockedInt3 = true;
+ }
+
+ AssertMsg(pbDst == &pAppBuf->pbNormal[pBuf->offActive + pBuf->cb + pBuf->cbEpilogue],
+ ("cbEpilogue=%#x, actual %#x\n", pBuf->cbEpilogue, pbDst - &pAppBuf->pbNormal[pBuf->offActive + pBuf->cb]));
+
+ /*
+ * Arm the buffer.
+ */
+ return CidetAppArmBuf(pThisApp, pAppBuf);
+}
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnExecute}
+ */
+static DECLCALLBACK(bool) CidetAppCbExecute(PCIDETCORE pThis)
+{
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
+ /* Skip tricky stack because windows cannot dispatch exception if RSP/ESP is bad. */
+ if (pThis->InCtx.fTrickyStack)
+ return false;
+#endif
+
+ g_pExecutingThis = (PCIDETAPP)pThis;
+#ifdef RT_OS_WINDOWS
+ __try
+ {
+ CidetAppExecute(&((PCIDETAPP)pThis)->ExecuteCtx, &pThis->InCtx);
+ }
+ __except (CidetAppXcptFilter(GetExceptionInformation()))
+ {
+ /* Won't end up here... */
+ }
+ g_pExecutingThis = NULL;
+#else
+ CidetAppExecute(&((PCIDETAPP)pThis)->ExecuteCtx, &pThis->InCtx);
+ if (g_pExecutingThis)
+ g_pExecutingThis = NULL;
+ else
+ {
+ RTTESTI_CHECK_RC(sigprocmask(SIG_SETMASK, &g_ProcSigMask, NULL), 0);
+ RTTESTI_CHECK_RC(sigaltstack(&g_AltStack, NULL), 0);
+ }
+#endif
+
+ return true;
+}
+
+
+
+
+/*
+ *
+ *
+ * CIDET Application.
+ * CIDET Application.
+ * CIDET Application.
+ *
+ *
+ */
+
+
+/**
+ * @interface_method_impl{CIDETCORE,pfnFailure}
+ */
+static DECLCALLBACK(void) CidetAppCbFailureV(PCIDETCORE pThis, const char *pszFormat, va_list va)
+{
+ RT_NOREF_PV(pThis);
+ RTTestIFailedV(pszFormat, va);
+}
+
+
+static int cidetAppAllocateAndConfigureBuffers(PCIDETAPP pThis)
+{
+ /*
+ * Code buffers.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCodeBuffers); i++)
+ {
+ int rc = cidetAppAllocateAndConfigureOneBuffer(pThis, &pThis->aCodeBuffers[i], i, true /*fCode*/,
+ g_aCodeBufCfgs[i].fFlags);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Data buffers.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDataBuffers); i++)
+ {
+ int rc = cidetAppAllocateAndConfigureOneBuffer(pThis, &pThis->aDataBuffers[i], i, false /*fCode*/,
+ g_aDataBufCfgs[i].fFlags);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Stack.
+ */
+ pThis->cbStack = _32K;
+ pThis->pbStackLow = (uint8_t *)RTMemPageAlloc(pThis->cbStack);
+ if (!pThis->pbStackLow)
+ {
+ RTTestIFailed("Failed to allocate %u bytes for stack\n", pThis->cbStack);
+ return false;
+ }
+ pThis->pbStackEnd = pThis->pbStackLow + pThis->cbStack;
+
+ return true;
+}
+
+
+static int CidetAppCreate(PPCIDETAPP ppThis)
+{
+ *ppThis = NULL;
+
+ PCIDETAPP pThis = (PCIDETAPP)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Error allocating %zu bytes.", sizeof(*pThis));
+
+ /* Create a random source. */
+ RTRAND hRand;
+ int rc = RTRandAdvCreateParkMiller(&hRand);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t uSeed = ASMReadTSC();
+ rc = RTRandAdvSeed(hRand, uSeed);
+ if (RT_SUCCESS(rc))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Random seed %#llx\n", uSeed);
+
+ /* Initialize the CIDET structure. */
+ rc = CidetCoreInit(&pThis->Core, hRand);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Core.pfnReInitDataBuf = CidetAppCbReInitDataBuf;
+ pThis->Core.pfnSetupDataBuf = CidetAppCbSetupDataBuf;
+ pThis->Core.pfnIsBufEqual = CidetAppCbIsBufEqual;
+ pThis->Core.pfnReInitCodeBuf = CidetAppCbReInitCodeBuf;
+ pThis->Core.pfnSetupCodeBuf = CidetAppCbSetupCodeBuf;
+ pThis->Core.pfnExecute = CidetAppCbExecute;
+ pThis->Core.pfnFailure = CidetAppCbFailureV;
+
+ pThis->Core.paCodeBufConfigs = g_aCodeBufCfgs;
+ pThis->Core.cCodeBufConfigs = CIDETAPP_CODE_BUF_COUNT;
+ pThis->Core.paDataBufConfigs = g_aDataBufCfgs;
+ pThis->Core.cDataBufConfigs = CIDETAPP_DATA_BUF_COUNT;
+
+ rc = cidetAppAllocateAndConfigureBuffers(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CidetCoreSetTargetMode(&pThis->Core, ARCH_BITS == 32 ? CIDETMODE_PP_32 : CIDETMODE_LM_64);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_CS] = ASMGetCS();
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_DS] = ASMGetDS();
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_ES] = ASMGetES();
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_FS] = ASMGetFS();
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_GS] = ASMGetGS();
+ pThis->Core.InTemplateCtx.aSRegs[X86_SREG_SS] = ASMGetSS();
+ pThis->Core.InTemplateCtx.aGRegs[X86_GREG_xSP] = (uintptr_t)pThis->pbStackEnd - 64;
+
+ pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_CS;
+ pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_DS;
+ pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_ES;
+#if !defined(RT_OS_WINDOWS)
+ pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_FS;
+#endif
+#if !defined(CIDET_LEAVE_GS_ALONE)
+ pThis->Core.fTestCfg |= CIDET_TESTCFG_SEG_PRF_GS;
+#endif
+
+ *ppThis = pThis;
+ return VINF_SUCCESS;
+ }
+ rc = RTTestIFailedRc(rc, "Error setting target mode: %Rrc", rc);
+ }
+ CidetCoreDelete(&pThis->Core);
+ }
+ else
+ {
+ rc = RTTestIFailedRc(rc, "CidetCoreInit failed: %Rrc", rc);
+ RTRandAdvDestroy(hRand);
+ }
+ }
+ else
+ rc = RTTestIFailedRc(rc, "RTRandAdvCreate failed: %Rrc", rc);
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+static void CidetAppDestroy(PCIDETAPP pThis)
+{
+ CidetCoreDelete(&pThis->Core);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCodeBuffers); i++)
+ CidetAppDeleteBuffer(&pThis->aCodeBuffers[i]);
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDataBuffers); i++)
+ CidetAppDeleteBuffer(&pThis->aDataBuffers[i]);
+ RTMemPageFree(pThis->pbStackLow, pThis->cbStack);
+
+ RTMemFree(pThis);
+}
+
+
+static void CidetAppTestBunch(PCIDETAPP pThis, PCCIDETINSTR paInstructions, uint32_t cInstructions, const char *pszBunchName)
+{
+ for (uint32_t iInstr = 0; iInstr < cInstructions; iInstr++)
+ {
+ RTTestSubF(g_hTest, "%s - %s", pszBunchName, paInstructions[iInstr].pszMnemonic);
+ CidetCoreTestInstruction(&pThis->Core, &paInstructions[iInstr]);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Initialize the runtime.
+ */
+ RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, 0, "cidet-app", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--noop", 'n', RTGETOPT_REQ_NOTHING },
+ };
+
+ int chOpt;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((chOpt = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (chOpt)
+ {
+ case 'n':
+ break;
+
+ case 'h':
+ RTPrintf("usage: %s\n", argv[0]);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTGetOptPrintError(chOpt, &ValueUnion);
+ }
+ }
+
+#ifdef USE_SIGNALS
+ /*
+ * Set up signal handlers with alternate stack.
+ */
+ /* Get the default signal mask. */
+ RTTESTI_CHECK_RC_RET(sigprocmask(SIG_BLOCK, NULL, &g_ProcSigMask), 0, RTEXITCODE_FAILURE);
+
+ /* Alternative stack so we can play with esp/rsp. */
+ RT_ZERO(g_AltStack);
+ g_AltStack.ss_flags = 0;
+# ifdef SIGSTKSZ
+ g_AltStack.ss_size = RT_MAX(SIGSTKSZ, _128K);
+# else
+ g_AltStack.ss_size = _128K;
+# endif
+#ifdef RT_OS_FREEBSD
+ g_AltStack.ss_sp = (char *)RTMemPageAlloc(g_AltStack.ss_size);
+#else
+ g_AltStack.ss_sp = RTMemPageAlloc(g_AltStack.ss_size);
+#endif
+ RTTESTI_CHECK_RET(g_AltStack.ss_sp != NULL, RTEXITCODE_FAILURE);
+ RTTESTI_CHECK_RC_RET(sigaltstack(&g_AltStack, NULL), 0, RTEXITCODE_FAILURE);
+
+ /* Default signal action config. */
+ struct sigaction Act;
+ RT_ZERO(Act);
+ Act.sa_sigaction = CidetAppSigHandler;
+ Act.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ sigfillset(&Act.sa_mask);
+
+ /* Hook the signals we might need. */
+ sigaction(SIGILL, &Act, NULL);
+ sigaction(SIGTRAP, &Act, NULL);
+# ifdef SIGEMT
+ sigaction(SIGEMT, &Act, NULL);
+# endif
+ sigaction(SIGFPE, &Act, NULL);
+ sigaction(SIGBUS, &Act, NULL);
+ sigaction(SIGSEGV, &Act, NULL);
+
+#elif defined(RT_OS_WINDOWS)
+ /*
+ * Register vectored exception handler and override the default unhandled
+ * exception filter, just to be on the safe side...
+ */
+ RTTESTI_CHECK(AddVectoredExceptionHandler(1 /* first */, CidetAppVectoredXcptHandler) != NULL);
+ SetUnhandledExceptionFilter(CidetAppUnhandledXcptFilter);
+#endif
+
+ /*
+ * Do the work.
+ */
+ RTTestBanner(g_hTest);
+
+ PCIDETAPP pThis;
+ int rc = CidetAppCreate(&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ CidetAppTestBunch(pThis, g_aCidetInstructions1, g_cCidetInstructions1, "First Bunch");
+
+ CidetAppDestroy(pThis);
+ }
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm b/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm
new file mode 100644
index 00000000..55feb013
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet-appA.asm
@@ -0,0 +1,319 @@
+; $Id: cidet-appA.asm $
+;; @file
+; CPU Instruction Decoding & Execution Tests - Ring-3 Driver Application, Assembly Code.
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+%include "cidet.mac"
+
+
+;*******************************************************************************
+;* Global Variables *
+;*******************************************************************************
+%ifdef RT_ARCH_X86
+;; Used by CidetAppSaveAndRestoreCtx when we have a tricky target stack.
+g_uTargetEip dd 0
+g_uTargetCs dw 0
+%endif
+
+
+;;
+; Leave GS alone on 64-bit darwin (gs is 0, no ldt or gdt entry to load that'll
+; restore the lower 32-bits of the base when saving and restoring the register).
+%ifdef RT_OS_DARWIN
+ %ifdef RT_ARCH_AMD64
+ %define CIDET_LEAVE_GS_ALONE
+ %endif
+%endif
+
+
+
+BEGINCODE
+
+;;
+; ASSUMES that it's called and the EIP/RIP is found on the stack.
+;
+; @param pSaveCtx ds:xCX The context to save; DS, xDX and xCX have
+; already been saved by the caller.
+; @param pRestoreCtx ds:xDX The context to restore.
+;
+BEGINPROC CidetAppSaveAndRestoreCtx
+ ;
+ ; Save the stack pointer and program counter first so we can later
+ ; bypass this step if we need to.
+ ;
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8], xAX ; need scratch register.
+ lea xAX, [xSP + xCB]
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8], xAX
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2], ss
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2], cs
+ mov xAX, [xSP]
+ mov [xCX + CIDETCPUCTX.rip], xAX
+ jmp CidetAppSaveAndRestoreCtx_1
+
+GLOBALNAME CidetAppSaveAndRestoreCtx_NoSsSpCsIp
+ mov [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8], xAX
+CidetAppSaveAndRestoreCtx_1:
+
+ ; Flags.
+%ifdef RT_ARCH_AMD64
+ pushfq
+%else
+ pushfd
+%endif
+ pop xAX
+ mov [xCX + CIDETCPUCTX.rfl], xAX
+
+ ; Segment registers.
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_ES * 2], es
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_FS * 2], fs
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_GS * 2], gs
+
+ ; Remaining GPRs.
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8], xBX
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8], xBP
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8], xSI
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8], xDI
+%ifdef RT_ARCH_AMD64
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8], r8
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8], r9
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8], r10
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8], r11
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8], r12
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8], r13
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8], r14
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8], r15
+ xor eax, eax
+ mov [xCX + CIDETCPUCTX.cr2], rax
+ %ifndef CIDET_REDUCED_CTX
+ mov [xCX + CIDETCPUCTX.cr0], rax
+ mov [xCX + CIDETCPUCTX.cr3], rax
+ mov [xCX + CIDETCPUCTX.cr4], rax
+ mov [xCX + CIDETCPUCTX.cr8], rax
+ mov [xCX + CIDETCPUCTX.dr0], rax
+ mov [xCX + CIDETCPUCTX.dr1], rax
+ mov [xCX + CIDETCPUCTX.dr2], rax
+ mov [xCX + CIDETCPUCTX.dr3], rax
+ mov [xCX + CIDETCPUCTX.dr6], rax
+ mov [xCX + CIDETCPUCTX.dr7], rax
+ mov [xCX + CIDETCPUCTX.tr], ax
+ mov [xCX + CIDETCPUCTX.ldtr], ax
+ %endif
+%else
+ xor eax, eax
+ mov [xCX + CIDETCPUCTX.rfl + 4], eax
+ mov [xCX + CIDETCPUCTX.rip + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8 ], eax
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8 + 4], eax
+ mov [xCX + CIDETCPUCTX.cr2 ], eax
+ mov [xCX + CIDETCPUCTX.cr2 + 4], eax
+ %ifndef CIDET_REDUCED_CTX
+ mov [xCX + CIDETCPUCTX.cr0 ], eax
+ mov [xCX + CIDETCPUCTX.cr0 + 4], eax
+ mov [xCX + CIDETCPUCTX.cr3 ], eax
+ mov [xCX + CIDETCPUCTX.cr3 + 4], eax
+ mov [xCX + CIDETCPUCTX.cr4 ], eax
+ mov [xCX + CIDETCPUCTX.cr4 + 4], eax
+ mov [xCX + CIDETCPUCTX.cr8 ], eax
+ mov [xCX + CIDETCPUCTX.cr8 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr0 ], eax
+ mov [xCX + CIDETCPUCTX.dr0 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr1 ], eax
+ mov [xCX + CIDETCPUCTX.dr1 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr2 ], eax
+ mov [xCX + CIDETCPUCTX.dr2 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr3 ], eax
+ mov [xCX + CIDETCPUCTX.dr3 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr6 ], eax
+ mov [xCX + CIDETCPUCTX.dr6 + 4], eax
+ mov [xCX + CIDETCPUCTX.dr7 ], eax
+ mov [xCX + CIDETCPUCTX.dr7 + 4], eax
+ mov [xCX + CIDETCPUCTX.tr], ax
+ mov [xCX + CIDETCPUCTX.ldtr], ax
+ %endif
+%endif
+ dec xAX
+ mov [xCX + CIDETCPUCTX.uErr], xAX
+%ifdef RT_ARCH_X86
+ mov [xCX + CIDETCPUCTX.uErr + 4], eax
+%endif
+ mov [xCX + CIDETCPUCTX.uXcpt], eax
+
+ ;
+ ; Restore the other state (pointer in xDX).
+ ;
+NAME(CidetAppSaveAndRestoreCtx_Restore):
+
+ ; Restore ES, FS, and GS.
+ mov es, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_ES * 2]
+ mov fs, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_FS * 2]
+%ifndef CIDET_LEAVE_GS_ALONE
+ mov gs, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_GS * 2]
+%endif
+
+ ; Restore most GPRs (except xCX, xAX and xSP).
+ mov xCX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8]
+ mov xBX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xBX * 8]
+ mov xBP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xBP * 8]
+ mov xSI, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSI * 8]
+ mov xDI, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDI * 8]
+%ifdef RT_ARCH_AMD64
+ mov r8, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x8 * 8]
+ mov r9, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x9 * 8]
+ mov r10, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x10 * 8]
+ mov r11, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x11 * 8]
+ mov r12, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x12 * 8]
+ mov r13, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x13 * 8]
+ mov r14, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x14 * 8]
+ mov r15, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_x15 * 8]
+%endif
+
+%ifdef RT_ARCH_AMD64
+ ; Create an iret frame which restores SS:RSP, RFLAGS, and CS:RIP.
+ movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2]
+ push xAX
+ push qword [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8]
+ push qword [xDX + CIDETCPUCTX.rfl]
+ movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2]
+ push xAX
+ push qword [xDX + CIDETCPUCTX.rip]
+
+ ; Restore DS, xAX and xDX then do the iret.
+ mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2]
+ mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8]
+ mov xDX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8]
+ iretq
+%else
+ ; In 32-bit mode iret doesn't restore CS:ESP for us, so we have to
+ ; make a choice whether the SS:ESP is more important than EFLAGS.
+ cmp byte [xDX + CIDETCPUCTX.fTrickyStack], 0
+ jne .tricky_stack
+
+ mov ss, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2]
+ mov xSP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8]
+
+ push dword [xDX + CIDETCPUCTX.rfl] ; iret frame
+ movzx eax, word [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2] ; iret frame
+ push xAX ; iret frame
+ push dword [xDX + CIDETCPUCTX.rip] ; iret frame
+
+ mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8]
+ mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2]
+ mov xDX, [cs:xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8]
+ iretd
+
+.tricky_stack:
+ mov xAX, [xDX + CIDETCPUCTX.rip]
+ mov [g_uTargetEip], xAX
+ mov ax, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_CS * 2]
+ mov [g_uTargetCs], ax
+ push dword [xDX + CIDETCPUCTX.rfl]
+ popfd
+ mov ss, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_SS * 2]
+ mov xSP, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xSP * 8]
+ mov xAX, [xDX + CIDETCPUCTX.aGRegs + X86_GREG_xAX * 8]
+ mov ds, [xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2]
+ mov xDX, [cs:xDX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8]
+ jmp far [cs:g_uTargetEip]
+%endif
+ENDPROC CidetAppSaveAndRestoreCtx
+
+
+;;
+; C callable version of CidetAppSaveAndRestoreCtx more or less.
+;
+; @param pSaveCtx x86:esp+4 gcc:rdi msc:rcx
+; @param pRestoreCtx x86:esp+8 gcc:rsi msc:rdx
+BEGINPROC CidetAppExecute
+%ifdef RT_ARCH_X86
+ mov ecx, [esp + 4]
+ mov edx, [esp + 8]
+%elifdef ASM_CALL64_GCC
+ mov rcx, rdi
+ mov rdx, rsi
+%elifndef ASM_CALL64_MSC
+ %error "unsupport arch."
+%endif
+ mov word [xCX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2], ds
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xDX * 8], xDX
+ mov [xCX + CIDETCPUCTX.aGRegs + X86_GREG_xCX * 8], xCX
+ jmp NAME(CidetAppSaveAndRestoreCtx)
+ENDPROC CidetAppExecute
+
+
+;;
+; C callable restore function.
+;
+; @param pRestoreCtx x86:esp+4 gcc:rdi msc:rcx
+BEGINPROC CidetAppRestoreCtx
+%ifdef RT_ARCH_X86
+ mov edx, [esp + 4]
+%elifdef ASM_CALL64_GCC
+ mov rdx, rdi
+%elifdef ASM_CALL64_MSC
+ mov rdx, rcx
+%else
+ %error "unsupport arch."
+%endif
+ mov ds, [cs:xDX + CIDETCPUCTX.aSRegs + X86_SREG_DS * 2]
+ jmp NAME(CidetAppSaveAndRestoreCtx_Restore)
+ENDPROC CidetAppRestoreCtx
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp
new file mode 100644
index 00000000..ff350861
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp
@@ -0,0 +1,2368 @@
+/* $Id: cidet-core.cpp $ */
+/** @file
+ * CPU Instruction Decoding & Execution Tests - Simple Instructions.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define CIDET_INSTR_TEST_OP_FLAG(a_pInstr, a_fFlag) \
+ ( ((a_pInstr)->afOperands[0] & (a_fFlag)) \
+ || ((a_pInstr)->afOperands[1] & (a_fFlag)) \
+ || ( (a_pInstr)->cOperands > 2 \
+ && ( ((a_pInstr)->afOperands[2] & (a_fFlag)) \
+ || ((a_pInstr)->afOperands[3] & (a_fFlag)) ) ) )
+
+#define CIDET_INSTR_TEST_OP_MASK_VALUE(a_pInstr, a_fMask, a_fValue) \
+ ( ((a_pInstr)->afOperands[0] & (a_fMask)) == (a_fValue) \
+ || ((a_pInstr)->afOperands[1] & (a_fMask)) == (a_fValue) \
+ || ( (a_pInstr)->cOperands > 2 \
+ && ( ((a_pInstr)->afOperands[2] & (a_fMask)) == (a_fValue) \
+ || ((a_pInstr)->afOperands[3] & (a_fMask)) == (a_fValue) ) ) )
+
+/** @def CIDET_DPRINTF
+ * Debug printf. */
+#if 1 //def DEBUG_bird
+# define CIDET_DPRINTF(a) do { RTPrintf a; } while (0)
+# define CIDET_DPRINTF_ENABLED
+#else
+# define CIDET_DPRINTF(a) do { } while (0)
+#endif
+
+/** @def CIDET_DEBUG_DISAS
+ * Enables instruction disassembly. */
+#if defined(DOXYGEN_RUNNING)
+# define CIDET_DEBUG_DISAS 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "cidet.h"
+
+#include <iprt/assert.h>
+#include <iprt/rand.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+#if defined(CIDET_DPRINTF_ENABLED) || defined(CIDET_DEBUG_DISAS)
+# include <VBox/dis.h>
+# include <iprt/stream.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** For translating CIDET_OF_Z_XXX values (after shifting). */
+uint16_t const g_acbCidetOfSizes[] =
+{
+ /* [CIDET_OF_Z_NONE] = */ 0,
+ /* [CIDET_OF_Z_BYTE] = */ 1,
+ /* [CIDET_OF_Z_WORD] = */ 2,
+ /* [CIDET_OF_Z_DWORD] = */ 4,
+ /* [CIDET_OF_Z_QWORD] = */ 8,
+ /* [CIDET_OF_Z_TBYTE] = */ 10,
+ /* [CIDET_OF_Z_OWORD] = */ 16,
+ /* [CIDET_OF_Z_YWORD] = */ 32,
+ /* [CIDET_OF_Z_ZWORD] = */ 64,
+ /* [CIDET_OF_Z_VAR_WDQ] = */ UINT16_MAX,
+ /* [0xa] = */ 0,
+ /* [0xb] = */ 0,
+ /* [0xc] = */ 0,
+ /* [0xd] = */ 0,
+ /* [0xe] = */ 0,
+ /* [CIDET_OF_Z_SPECIAL] = */ UINT16_MAX - 1,
+};
+
+
+/** Converts operand sizes in bytes to 64-bit masks. */
+static const uint64_t g_au64ByteSizeToMask[] =
+{
+ UINT64_C(0x0000000000000000),
+ UINT64_C(0x00000000000000ff),
+ UINT64_C(0x000000000000ffff),
+ UINT64_C(0x0000000000ffffff),
+ UINT64_C(0x00000000ffffffff),
+ UINT64_C(0x000000ffffffffff),
+ UINT64_C(0x0000ffffffffffff),
+ UINT64_C(0x00ffffffffffffff),
+ UINT64_C(0xffffffffffffffff),
+};
+
+/** Converts operand sizes in bytes to 64-bit signed max values. */
+static const int64_t g_ai64ByteSizeToMax[] =
+{
+ INT64_C(0x0000000000000000),
+ INT64_C(0x000000000000007f),
+ INT64_C(0x0000000000007fff),
+ INT64_C(0x00000000007fffff),
+ INT64_C(0x000000007fffffff),
+ INT64_C(0x0000007fffffffff),
+ INT64_C(0x00007fffffffffff),
+ INT64_C(0x007fffffffffffff),
+ INT64_C(0x7fffffffffffffff),
+};
+
+
+bool CidetInstrHasMrmMemOperand(PCCIDETINSTR pInstr)
+{
+ return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_M);
+}
+
+
+bool CidetInstrHasMrmRegOperand(PCCIDETINSTR pInstr)
+{
+ return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_R);
+}
+
+
+bool CidetInstrRespondsToOperandSizePrefixes(PCCIDETINSTR pInstr)
+{
+ return CIDET_INSTR_TEST_OP_MASK_VALUE(pInstr, CIDET_OF_Z_MASK, CIDET_OF_Z_VAR_WDQ);
+}
+
+
+
+
+int CidetCoreInit(PCIDETCORE pThis, RTRAND hRand)
+{
+ AssertPtr(pThis);
+ AssertPtr(hRand);
+
+ RT_ZERO(*pThis);
+ pThis->u32Magic = CIDETCORE_MAGIC;
+ pThis->hRand = hRand;
+ return VINF_SUCCESS;
+}
+
+
+void CidetCoreDelete(PCIDETCORE pThis)
+{
+ AssertPtr(pThis); Assert(pThis->u32Magic == CIDETCORE_MAGIC);
+
+ RTRandAdvDestroy(pThis->hRand);
+ RT_ZERO(*pThis);
+}
+
+
+/**
+ * Report a test failure via CIDET::pfnFailure
+ *
+ * @returns false
+ * @param pThis Pointer to the core structure.
+ * @param pszFormat Format string containing failure details.
+ * @param va Arguments referenced in @a pszFormat.
+ */
+int CidetCoreSetErrorV(PCIDETCORE pThis, const char *pszFormat, va_list va)
+{
+ pThis->pfnFailure(pThis, pszFormat, va);
+ return false;
+}
+
+
+/**
+ * Report a test failure via CIDET::pfnFailure
+ *
+ * @returns false
+ * @param pThis Pointer to the core structure.
+ * @param pszFormat Format string containing failure details.
+ * @param ... Arguments referenced in @a pszFormat.
+ */
+bool CidetCoreSetError(PCIDETCORE pThis, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ CidetCoreSetErrorV(pThis, pszFormat, va);
+ va_end(va);
+ return false;
+}
+
+
+/**
+ * Get a signed random number, with a given number of significant bytes.
+ *
+ * @returns Random number.
+ * @param pThis Pointer to the core structure.
+ * @param cbSignificant The number of significant bytes.
+ */
+int64_t CidetCoreGetRandS64(PCIDETCORE pThis, uint8_t cbSignificant)
+{
+ int64_t iVal = RTRandAdvS64(pThis->hRand);
+ switch (cbSignificant)
+ {
+ case 1: return (int8_t)iVal;
+ case 2: return (int16_t)iVal;
+ case 4: return (int32_t)iVal;
+ case 8: return iVal;
+ default:
+ AssertReleaseFailed();
+ return iVal;
+ }
+}
+
+
+/**
+ * Get an unsigned random number, with a given number of significant bytes.
+ *
+ * @returns Random number.
+ * @param pThis Pointer to the core structure.
+ * @param cbSignificant The number of significant bytes.
+ */
+uint64_t CidetCoreGetRandU64(PCIDETCORE pThis, uint8_t cbSignificant)
+{
+ Assert(cbSignificant == 1 || cbSignificant == 2 || cbSignificant == 4 || cbSignificant == 8);
+
+ uint64_t uVal = RTRandAdvU64(pThis->hRand);
+ uVal &= g_au64ByteSizeToMask[cbSignificant];
+
+ return uVal;
+}
+
+
+
+void CidetCoreInitializeCtxTemplate(PCIDETCORE pThis)
+{
+ pThis->InTemplateCtx.rip = UINT64_MAX;
+ pThis->InTemplateCtx.rfl = X86_EFL_1 | X86_EFL_ID | X86_EFL_IF;
+
+ unsigned i = RT_ELEMENTS(pThis->InTemplateCtx.aGRegs);
+ if (CIDETMODE_IS_LM(pThis->bMode))
+ while (i-- > 0)
+ pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0x3fefcc00daba005d)
+ | ((uint64_t)i << 32)
+ | ((uint32_t)i << 8);
+ else
+ while (i-- > 0)
+ pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0xfada009b)
+ | ((uint32_t)i << 12)
+ | ((uint32_t)i << 8);
+ i = RT_ELEMENTS(pThis->InTemplateCtx.aSRegs);
+ while (i-- > 0)
+ pThis->InTemplateCtx.aSRegs[i] = 0; /* Front end sets these afterwards. */
+ pThis->InTemplateCtx.cr2 = 0;
+#ifndef CIDET_REDUCED_CTX
+ pThis->InTemplateCtx.tr = 0;
+ pThis->InTemplateCtx.ldtr = 0;
+ pThis->InTemplateCtx.cr0 = 0;
+ pThis->InTemplateCtx.cr3 = 0;
+ pThis->InTemplateCtx.cr4 = 0;
+ pThis->InTemplateCtx.cr8 = 0;
+#endif
+ pThis->InTemplateCtx.fIgnoredRFlags = 0;
+ pThis->InTemplateCtx.uXcpt = UINT32_MAX;
+ pThis->InTemplateCtx.uErr = UINT64_MAX;
+ pThis->InTemplateCtx.fTrickyStack = false;
+}
+
+
+/**
+ * Sets the target mode.
+ *
+ * Caller must set up default selector values after calling this function.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the core structure.
+ * @param bMode The new mode.
+ */
+int CidetCoreSetTargetMode(PCIDETCORE pThis, uint8_t bMode)
+{
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertReturn(pThis->u32Magic == CIDETCORE_MAGIC, VERR_INVALID_HANDLE);
+ switch (bMode)
+ {
+ //case CIDETMODE_RM:
+ //case CIDETMODE_PE_16:
+ //case CIDETMODE_PE_32:
+ //case CIDETMODE_PE_V86:
+ //case CIDETMODE_PP_16:
+ case CIDETMODE_PP_32:
+ //case CIDETMODE_PP_V86:
+ //case CIDETMODE_PAE_16:
+ case CIDETMODE_PAE_32:
+ //case CIDETMODE_PAE_V86:
+ //case CIDETMODE_LM_S16:
+ //case CIDETMODE_LM_32:
+ case CIDETMODE_LM_64:
+ break;
+ default:
+ return VERR_NOT_IMPLEMENTED;
+ }
+ pThis->bMode = bMode;
+ CidetCoreInitializeCtxTemplate(pThis);
+ return VINF_SUCCESS;
+}
+
+
+bool CidetCoreIsEncodingCompatibleWithInstruction(PCIDETCORE pThis)
+{
+ RT_NOREF_PV(pThis);
+ return true;
+}
+
+
+/**
+ * Selects the next address size mode.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ */
+static bool cidetCoreSetupNextBaseEncoding_AddressSize(PCIDETCORE pThis)
+{
+ if (pThis->fAddrSizePrf)
+ {
+ /*
+ * Reset to default.
+ */
+ pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode);
+ pThis->fAddrSizePrf = false;
+ }
+ else
+ {
+ /*
+ * The other addressing size.
+ */
+ if (CIDETMODE_IS_64BIT(pThis->bMode))
+ pThis->cbAddrMode = 4;
+ else if (CIDETMODE_IS_32BIT(pThis->bMode))
+ pThis->cbAddrMode = 2;
+ else
+ {
+ AssertRelease(CIDETMODE_IS_16BIT(pThis->bMode));
+ pThis->cbAddrMode = 2;
+ }
+ pThis->fAddrSizePrf = true;
+ }
+ return pThis->fAddrSizePrf;
+}
+
+
+/**
+ * Selects the first REG encoding.
+ *
+ * @param pThis The core state structure.
+ */
+static void cidetCoreSetupFirstBaseEncoding_MrmReg(PCIDETCORE pThis)
+{
+ pThis->aOperands[pThis->idxMrmRegOp].iReg = 0;
+ pThis->aOperands[pThis->idxMrmRegOp].fIsMem = false;
+ pThis->aOperands[pThis->idxMrmRegOp].fIsRipRelative = false;
+ pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false;
+ pThis->aOperands[pThis->idxMrmRegOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRegOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRegOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRegOp].uMemScale = 1;
+ pThis->aOperands[pThis->idxMrmRegOp].iEffSeg = UINT8_MAX;
+ pThis->bModRm &= ~X86_MODRM_REG_MASK;
+ pThis->fRexR = false;
+}
+
+
+/**
+ * Selects the next REG (ModR/M) encoding.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_MrmReg(PCIDETCORE pThis, uint8_t iReg)
+{
+ Assert(pThis->idxMrmRegOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRegOp].fIsMem);
+ Assert(iReg < 16);
+
+ /*
+ * Clear the collision flags here because of the byte register kludge.
+ */
+ pThis->fHasRegCollisionDirect = false;
+ pThis->fHasRegCollisionMemBase = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasRegCollisionMem = false;
+
+ /*
+ * Clear the REX prefix and high byte register tracking too. ASSUMES MrmReg is after MrmRmMod.
+ */
+ Assert(!pThis->fNoRexPrefixMrmRm);
+ Assert(!pThis->fHasHighByteRegInMrmRm);
+ pThis->fNoRexPrefixMrmReg = false;
+ pThis->fNoRexPrefix = false;
+ pThis->fHasHighByteRegInMrmReg = false;
+ pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false;
+
+ /*
+ * Special kludge for ah, ch, dh, bh, spl, bpl, sil, and dil.
+ * Needs extra care in 64-bit mode and special collision detection code.
+ */
+ CIDET_DPRINTF(("aOperands[%u].cb=%u fGpr=%u iReg=%d fRex=%d fRexW=%u fRexX=%u fRexB=%u fRexR=%d\n",
+ pThis->idxMrmRegOp, pThis->aOperands[pThis->idxMrmRegOp].cb, CIDET_OF_K_IS_GPR(pThis->fMrmRegOp), iReg,
+ pThis->fRex, pThis->fRexW, pThis->fRexX, pThis->fRexB, pThis->fRexR));
+ if ( pThis->aOperands[pThis->idxMrmRegOp].cb == 1
+ && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
+ && iReg >= 3
+ && ( iReg <= 6
+ || (CIDETMODE_IS_64BIT(pThis->bMode) && iReg == 7 && !pThis->fRex)) )
+
+ {
+ if (!pThis->fRex && iReg >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix)
+ {
+ /* The AMD64 low variants: spl, bpl, sil and dil. */
+ pThis->fRex = true;
+ pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP;
+
+ /* Check for collisions. */
+ if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
+ {
+ Assert(!pThis->fHasHighByteRegInMrmRm);
+ if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
+ pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp)
+ && iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg;
+ else
+ {
+ Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
+
+ pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
+ pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ }
+ }
+ else
+ {
+ /* Next register: ah, ch, dh and bh. */
+ iReg++;
+ pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg;
+ pThis->bModRm &= ~X86_MODRM_REG_MASK;
+ pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT;
+ pThis->fRex = false;
+ pThis->fRexR = false;
+ pThis->fNoRexPrefixMrmReg = true;
+ pThis->fNoRexPrefix = true;
+ pThis->fHasHighByteRegInMrmReg = true;
+ pThis->fHasStackRegInMrmReg = false;
+ pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = true;
+ Assert(!pThis->fRexW); Assert(!pThis->fRexX); Assert(!pThis->fRexB);
+
+ /* Check for collisions. */
+ if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
+ {
+ if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
+ pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp)
+ && ( ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1
+ && iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg
+ && pThis->fHasHighByteRegInMrmRm)
+ || ( pThis->aOperands[pThis->idxMrmRmOp].cb > 1
+ && iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iReg));
+ else
+ {
+ Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
+
+ pThis->fHasRegCollisionMemBase = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
+ pThis->fHasRegCollisionMemIndex = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ }
+ }
+ return true;
+ }
+
+ Assert(!pThis->fRex || (iReg == 7 && CIDETMODE_IS_64BIT(pThis->bMode)));
+ pThis->fRex = false;
+
+ /*
+ * Next register.
+ */
+ iReg = (iReg + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) ? 15 : 7);
+
+ pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg;
+ pThis->bModRm &= ~X86_MODRM_REG_MASK;
+ pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT;
+ pThis->fRexR = iReg >= 8;
+ pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+
+ /*
+ * Register collision detection.
+ */
+ if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
+ {
+ if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
+ pThis->fHasRegCollisionDirect = iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg
+ && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
+ else if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
+ {
+ Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
+ pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
+ pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ }
+ Assert(!pThis->fSib);
+
+ return iReg != 0;
+}
+
+
+/**
+ * Selects the next MOD & R/M encoding, 16-bit addressing variant.
+ *
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg)
+{
+ if (CidetInstrHasMrmRegOperand(pThis->pCurInstr))
+ {
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT;
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ pThis->fHasMemoryOperand = false;
+ pThis->fHasRegCollisionDirect = iReg == 0
+ && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMem = false;
+ pThis->fHasRegCollisionMemBase = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasStackRegInMrmRmBase = false;
+ }
+ else
+ {
+ Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr));
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ pThis->fHasMemoryOperand = true;
+ pThis->fHasRegCollisionDirect = false;
+ iReg -= pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ pThis->fHasStackRegInMrmRmBase = false;
+ }
+}
+
+
+/**
+ * Selects the next MOD & R/M encoding, 16-bit addressing variant.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg)
+{
+ AssertRelease(!pThis->fRexB);
+ AssertRelease(!pThis->fRexX);
+ uint8_t iRm = pThis->bModRm & X86_MODRM_RM_MASK;
+ uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK;
+ if (iMod == 3)
+ {
+ /*
+ * Register access mode.
+ */
+ Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
+ Assert(!pThis->fHasMemoryOperand);
+ Assert(!pThis->fHasRegCollisionMem);
+ Assert(!pThis->fHasRegCollisionMemBase);
+ Assert(!pThis->fHasRegCollisionMemIndex);
+ if (iRm < 7)
+ {
+ iRm++;
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
+ pThis->bModRm &= ~X86_MODRM_RM_MASK;
+ pThis->bModRm |= iRm;
+ pThis->fHasRegCollisionDirect = iRm == iReg
+ && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
+ pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRmOp);
+ return true;
+ }
+
+ /* If no memory modes, we're done. */
+ if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr))
+ {
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg);
+ return false;
+ }
+
+ /* Next mode: 16-bit memory addressing without displacement. */
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ iMod = 0;
+ }
+ else
+ {
+ /*
+ * Memory access mode.
+ */
+ Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
+ Assert(pThis->fHasMemoryOperand);
+ if (iRm < 7)
+ {
+ iRm++;
+ switch (iRm)
+ {
+ case 1:
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
+ break;
+ case 2:
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
+ break;
+ case 3:
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
+ break;
+ case 4:
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
+ break;
+ case 5:
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
+ break;
+ case 6:
+ if (iMod == 0)
+ {
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 2;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ }
+ else
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ break;
+ case 7:
+ if (iMod == 0)
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ break;
+ default: AssertReleaseFailed();
+ }
+ pThis->bModRm &= ~X86_MODRM_RM_MASK;
+ pThis->bModRm |= iRm;
+ if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
+ {
+ iReg -= pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
+ pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ return true;
+ }
+
+ /* Last mode? */
+ if (iMod >= 2)
+ {
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg);
+ return false;
+ }
+
+ /* Next memory addressing mode (if any). */
+ iMod++;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp++;
+ }
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT;
+ pThis->fHasMemoryOperand = true;
+ pThis->fHasRegCollisionDirect = false;
+ pThis->fHasStackRegInMrmRmBase = false;
+ if (CIDET_OF_K_IS_GPR(pThis->fMrmRmOp))
+ {
+ iReg -= pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX;
+ pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ return true;
+}
+
+
+/**
+ * Selects the first MOD & R/M encoding, 32-bit and 64-bit addressing variant.
+ *
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ * @param f64Bit Set if 64-bit, clear if 32-bit.
+ */
+static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit)
+{
+ RT_NOREF_PV(f64Bit);
+ if (CidetInstrHasMrmRegOperand(pThis->pCurInstr))
+ {
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT;
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ pThis->fHasMemoryOperand = false;
+ pThis->fHasRegCollisionDirect = iReg == 0
+ && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMem = false;
+ pThis->fHasRegCollisionMemBase = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasStackRegInMrmRmBase = false;
+ }
+ else
+ {
+ Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr));
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ pThis->fHasMemoryOperand = true;
+ pThis->fHasRegCollisionDirect = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4 && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase;
+ pThis->fHasStackRegInMrmRmBase = false;
+ }
+}
+
+
+/**
+ * Selects the next MOD & R/M encoding, 32-bit and 64-bit addressing variant.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ * @param f64Bit Set if 64-bit, clear if 32-bit.
+ */
+static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit)
+{
+ AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode));
+ AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode));
+ uint8_t iRm = (pThis->bModRm & X86_MODRM_RM_MASK) + pThis->fRexB * 8;
+ uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK;
+ if (iMod == 3)
+ {
+ /*
+ * Register access mode.
+ */
+ Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
+ Assert(!pThis->fHasMemoryOperand);
+ Assert(!pThis->fHasRegCollisionMem);
+ Assert(!pThis->fHasRegCollisionMemBase);
+ Assert(!pThis->fHasRegCollisionMemIndex);
+
+ if (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX && !pThis->fNoRexPrefix) /* should be ignored. */
+ {
+ pThis->fRexX = true;
+ return true;
+ }
+
+ /* Reset the byte register kludges variables. */
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
+ pThis->fHasHighByteRegInMrmRm = false;
+ pThis->fNoRexPrefixMrmRm = false;
+ pThis->fNoRexPrefix = pThis->fNoRexPrefixMrmReg;
+
+ if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7))
+ {
+ /*
+ * Byte register kludge.
+ */
+ if ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1
+ && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
+ && iRm >= 3
+ && ( iRm <= 6
+ || (iRm == 7 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX) ) )
+ {
+ if (!pThis->fRexX && iRm >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix)
+ {
+ /* The AMD64 low variants: spl, bpl, sil and dil. (Using fRexX here as REG covers fRex.) */
+ pThis->fRexX = true;
+ pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
+ && iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ }
+ else
+ {
+ /* Next register: ah, ch, dh and bh. */
+ iRm++;
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
+ pThis->bModRm &= ~X86_MODRM_RM_MASK;
+ pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ if (!pThis->fRexR && !pThis->fRexW && !pThis->fRex)
+ {
+ pThis->fNoRexPrefixMrmRm = true;
+ pThis->fNoRexPrefix = true;
+ pThis->fHasHighByteRegInMrmRm = true;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = true;
+ pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
+ && iRm - 4 == iReg - pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasStackRegInMrmRmBase = false;
+
+ }
+ else
+ {
+ /* Can't do the high stuff, so do the spl, bpl, sil and dil variation instead.
+ Note! We don't set the RexX yet since the base register or operand width holds it down. */
+ pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
+ && iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ }
+ }
+ }
+ /*
+ * Normal register.
+ */
+ else
+ {
+ iRm++;
+ pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
+ pThis->bModRm &= ~X86_MODRM_RM_MASK;
+ pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
+ pThis->fRexB = iRm >= 8;
+ pThis->fRexX = false;
+ pThis->fHasRegCollisionDirect = iRm == iReg && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
+ pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ }
+ return true;
+ }
+
+ /* If no memory modes, we're done. */
+ if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr))
+ {
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit);
+ return false;
+ }
+
+ /* Next mode: 32-bit/64-bit memory addressing without displacement. */
+ pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ iMod = 0;
+ }
+ else
+ {
+ /*
+ * Memory access mode.
+ */
+ Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
+ Assert(pThis->fHasMemoryOperand);
+ Assert(!pThis->fHasStackRegInMrmRmBase);
+ if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7))
+ {
+ iRm++;
+ if (iRm == 12)
+ iRm++; /* Leave REX.B=1 to the next-sib-base function. */
+ if (iRm == 4)
+ {
+ /* SIB */
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = 0;
+ pThis->fSib = true;
+ pThis->bSib = 0;
+ }
+ else if ((iRm & 7) == 5 && iMod == 0)
+ {
+ /* Absolute or wrt rip addressing. */
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = CIDETMODE_IS_64BIT(pThis->bMode);
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4;
+ }
+ else
+ {
+ if ((iRm & 7) == 6 && iMod == 0)
+ {
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
+ }
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iRm;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ }
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->bModRm &= ~X86_MODRM_RM_MASK;
+ pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
+ pThis->fRexB = iRm >= 8;
+ pThis->fRexX = false;
+ if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
+ {
+ iReg -= pThis->fHasHighByteRegInMrmReg * 4;
+ pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
+ pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ }
+ return true;
+ }
+
+ /* Last mode? */
+ if (iMod >= 2)
+ {
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit);
+ return false;
+ }
+
+ /* Next memory addressing mode (if any). */
+ iMod++;
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = iMod == 1 ? 1 : 4;
+ }
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
+ pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT;
+ pThis->fRexB = false;
+ pThis->fRexX = false;
+ pThis->fHasMemoryOperand = true;
+ pThis->fHasRegCollisionDirect = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4
+ && CIDET_OF_K_IS_GPR(pThis->fMrmRmOp);
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase;
+ pThis->fHasStackRegInMrmRmBase = false;
+ return true;
+}
+
+
+/**
+ * Selects the next MOD & R/M encoding.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_MrmRmMod(PCIDETCORE pThis, uint8_t iReg)
+{
+ if (pThis->cbAddrMode == 2)
+ return cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(pThis, iReg);
+ if (pThis->cbAddrMode == 4)
+ return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, false);
+ if (pThis->cbAddrMode == 8)
+ return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, true);
+ AssertReleaseFailedReturn(false);
+}
+
+
+
+/**
+ * Selects the next SIB base register (/ encoding).
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_SibBase(PCIDETCORE pThis, uint8_t iReg)
+{
+ AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode));
+
+ uint8_t iBase = (pThis->bSib & X86_SIB_BASE_MASK) + pThis->fRexB * 8;
+ iBase = (iBase + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7);
+
+ if ((iBase & 7) == 5 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0)
+ {
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
+ }
+ else
+ {
+ if ((iBase & 7) == 6 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0)
+ pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
+ pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iBase;
+ }
+ pThis->bSib &= ~X86_SIB_BASE_MASK;
+ pThis->bSib |= iBase & X86_SIB_BASE_MASK;
+ pThis->fRexB = iBase >= 8;
+ pThis->fHasRegCollisionMemBase = pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg
+ == iReg - pThis->fHasHighByteRegInMrmReg * 4
+ && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+ pThis->fHasStackRegInMrmRmBase = iBase == X86_GREG_xSP;
+
+ return iBase != 0;
+}
+
+
+/**
+ * Selects the next SIB index register (/ encoding).
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_SibIndex(PCIDETCORE pThis, uint8_t iReg)
+{
+ AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode));
+ Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
+
+ uint8_t iIndex = ((pThis->bSib >> X86_SIB_INDEX_SHIFT) & X86_SIB_INDEX_SMASK) + pThis->fRexX * 8;
+ iIndex = (iIndex + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7);
+
+ if (iIndex == 4 && !pThis->fUsesVexIndexRegs)
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
+ else
+ pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = iIndex;
+ pThis->bSib &= ~X86_SIB_INDEX_MASK;
+ pThis->bSib |= (iIndex & X86_SIB_INDEX_SMASK) << X86_SIB_INDEX_SHIFT;
+ pThis->fRexX = iIndex >= 8;
+ pThis->fHasRegCollisionMemIndex = pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg
+ == iReg - pThis->fHasHighByteRegInMrmReg * 4
+ && ( !pThis->fUsesVexIndexRegs
+ ? CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) : CIDET_OF_K_IS_VRX(pThis->fMrmRegOp) );
+ pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
+
+ return iIndex != 0;
+}
+
+
+/**
+ * Selects the next SIB scale.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ * @param iReg The value of MODRM.REG /w REX.R applied.
+ */
+static bool cidetCoreSetupNextBaseEncoding_SibScale(PCIDETCORE pThis, uint8_t iReg)
+{
+ RT_NOREF_PV(iReg);
+ switch ((pThis->bSib >> X86_SIB_SCALE_SHIFT) & X86_SIB_SCALE_SMASK)
+ {
+ case 0:
+ pThis->bSib |= 1 << X86_SIB_SCALE_SHIFT;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 2;
+ return true;
+ case 1:
+ pThis->bSib &= ~X86_SIB_SCALE_MASK;
+ pThis->bSib |= 2 << X86_SIB_SCALE_SHIFT;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 4;
+ return true;
+ case 2:
+ pThis->bSib |= 3 << X86_SIB_SCALE_SHIFT;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 8;
+ return true;
+ case 3:
+ pThis->bSib &= ~X86_SIB_SCALE_MASK;
+ pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
+ return false;
+
+ default: AssertReleaseFailedReturn(false);
+ }
+}
+
+
+/**
+ * Selects the next segment prefix.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ */
+static bool cidetCoreSetupNextBaseEncoding_SegmentPrefix(PCIDETCORE pThis)
+{
+ if ( pThis->fHasMemoryOperand
+ && (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_MASK))
+ {
+ switch (pThis->uSegPrf)
+ {
+ case X86_SREG_COUNT:
+ pThis->uSegPrf = X86_SREG_ES;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_ES)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_ES:
+ pThis->uSegPrf = X86_SREG_CS;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_CS)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_CS:
+ pThis->uSegPrf = X86_SREG_SS;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_SS)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_SS:
+ pThis->uSegPrf = X86_SREG_DS;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_DS)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_DS:
+ pThis->uSegPrf = X86_SREG_FS;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_FS)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_FS:
+ pThis->uSegPrf = X86_SREG_GS;
+ if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_GS)
+ return true;
+ RT_FALL_THRU();
+ case X86_SREG_GS:
+ break;
+ default: AssertReleaseFailedBreak();
+ }
+ pThis->uSegPrf = X86_SREG_COUNT;
+ }
+ return false;
+}
+
+
+/**
+ * Updates the variable sized operands.
+ *
+ * @param pThis The core state structure.
+ */
+static void cidetCoreUpdateOperandSizes(PCIDETCORE pThis)
+{
+ uint8_t iOp = pThis->cOperands;
+ while (iOp-- > 0)
+ pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp);
+}
+
+
+/**
+ * Selects the next operand size.
+ *
+ * @returns @c true if done, @c false if the next wheel needs to be moved.
+ * @param pThis The core state structure.
+ */
+static bool cidetCoreSetupNextBaseEncoding_OperandSize(PCIDETCORE pThis)
+{
+ if (CidetInstrRespondsToOperandSizePrefixes(pThis->pCurInstr))
+ {
+ if (CIDETMODE_IS_64BIT(pThis->bMode))
+ {
+ switch (pThis->fOpSizePrf + pThis->fRexW * 2)
+ {
+ case 0:
+ pThis->fOpSizePrf = true;
+ cidetCoreUpdateOperandSizes(pThis);
+ return true;
+ case 1:
+ pThis->fOpSizePrf = false;
+ if (pThis->fNoRexPrefix)
+ break;
+ pThis->fRexW = true;
+ cidetCoreUpdateOperandSizes(pThis);
+ return true;
+ case 2:
+ pThis->fOpSizePrf = true; /* check that it's ignored. */
+ cidetCoreUpdateOperandSizes(pThis);
+ return true;
+ default: AssertReleaseFailed();
+ case 3:
+ break;
+ }
+ }
+ else
+ {
+ if (!pThis->fOpSizePrf)
+ {
+ pThis->fOpSizePrf = true;
+ cidetCoreUpdateOperandSizes(pThis);
+ return true;
+ }
+ }
+ pThis->fRexW = false;
+ pThis->fOpSizePrf = false;
+ cidetCoreUpdateOperandSizes(pThis);
+ }
+ return false;
+}
+
+
+bool CidetCoreSetupNextBaseEncoding(PCIDETCORE pThis)
+{
+ if (pThis->fUsesModRm)
+ {
+ /*
+ * The wheels are lined up as follows:
+ * 1. Address size prefix.
+ * 2. MODRM.MOD
+ * 3. MODRM.REG + REX.R
+ * 4. MODRM.R/M + REX.B
+ * 5. SIB - MODRM.R/M == 4 && MODRM.MOD != 3:
+ * 5a) SIB.BASE + REX.B
+ * 5b) SIB.INDEX + REX.X
+ * 5c) SIB.SCALE
+ * 6. Segment prefix overrides if applicable and supported (memory).
+ * 7. Operand size prefix and REX.W if applicable.
+ */
+ if (cidetCoreSetupNextBaseEncoding_OperandSize(pThis))
+ return true;
+ if (cidetCoreSetupNextBaseEncoding_SegmentPrefix(pThis))
+ return true;
+
+ /* The ModR/M register value for collision detection. */
+ uint8_t iReg = ((pThis->bModRm >> X86_MODRM_REG_SHIFT) & X86_MODRM_REG_SMASK) + pThis->fRexR * 8;
+
+ if (pThis->fSib)
+ {
+ AssertRelease(pThis->fHasMemoryOperand);
+ if (cidetCoreSetupNextBaseEncoding_SibScale(pThis, iReg))
+ return true;
+ if (cidetCoreSetupNextBaseEncoding_SibIndex(pThis, iReg))
+ return true;
+ if (cidetCoreSetupNextBaseEncoding_SibBase(pThis, iReg))
+ return true;
+ Assert(pThis->bSib == 0);
+ pThis->fSib = false;
+ }
+
+ if (cidetCoreSetupNextBaseEncoding_MrmRmMod(pThis, iReg))
+ return true;
+ if (cidetCoreSetupNextBaseEncoding_MrmReg(pThis, iReg))
+ return true;
+ if (cidetCoreSetupNextBaseEncoding_AddressSize(pThis))
+ return true;
+ }
+ else
+ AssertFailedReturn(false);
+ return false;
+}
+
+
+bool CidetCoreSetupFirstBaseEncoding(PCIDETCORE pThis)
+{
+ /*
+ * Reset all the knobs and wheels.
+ */
+ pThis->fSib = false;
+ pThis->uSegPrf = X86_SREG_COUNT;
+ pThis->fAddrSizePrf = false;
+ pThis->fOpSizePrf = false;
+ pThis->fRexW = false;
+ pThis->fRexR = false;
+ pThis->fRexX = false;
+ pThis->fRexB = false;
+ pThis->fRex = false;
+ pThis->bModRm = 0;
+ pThis->bSib = 0;
+
+ /* Indicators. */
+ pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode);
+ pThis->fHasMemoryOperand = false;
+ pThis->fHasRegCollisionMem = false;
+ pThis->fHasRegCollisionMemBase = false;
+ pThis->fHasRegCollisionMemIndex = false;
+ pThis->fHasStackRegInMrmRmBase = false;
+
+ /*
+ * Now, drill down on the instruction encoding.
+ */
+ if (pThis->pCurInstr->fFlags & CIDET_IF_MODRM)
+ {
+ Assert(pThis->fUsesModRm == true);
+ cidetCoreSetupFirstBaseEncoding_MrmReg(pThis);
+ if (pThis->cbAddrMode == 2)
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, 0);
+ else if (pThis->cbAddrMode == 4)
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, false);
+ else if (pThis->cbAddrMode == 8)
+ cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, true);
+ else
+ AssertReleaseFailedReturn(false);
+ }
+ else
+ AssertFailedReturn(false);
+ return true;
+}
+
+
+/**
+ * The next memory operand configuration.
+ *
+ * @returns true if new one to test, false if we've reached end already.
+ * @param pThis The core state structure.
+ */
+bool CidetCoreSetupNextMemoryOperandConfig(PCIDETCORE pThis)
+{
+ RT_NOREF_PV(pThis);
+ return false;
+}
+
+
+/**
+ * Sets up the first memory operand configuration and counts memory operands.
+ *
+ * @returns true on success, false if no data buffers configured or failure.
+ * @param pThis The core state structure.
+ */
+bool CidetCoreSetupFirstMemoryOperandConfig(PCIDETCORE pThis)
+{
+ pThis->cMemoryOperands = 0;
+ PCIDETBUF pDataBuf = &pThis->DataBuf;
+ uint8_t idxOp = pThis->cOperands;
+ while (idxOp-- > 0)
+ if (!pThis->aOperands[idxOp].fIsMem)
+ pThis->aOperands[idxOp].pDataBuf = NULL;
+ else
+ {
+ if (RT_UNLIKELY(!pThis->cDataBufConfigs))
+ return false;
+
+ pDataBuf->idxCfg = 0;
+ pDataBuf->pCfg = &pThis->paDataBufConfigs[0];
+ pDataBuf->off = 0;
+ pDataBuf->cb = pThis->aOperands[idxOp].cb;
+ pDataBuf->cbSegLimit = UINT16_MAX;
+ pDataBuf->offSegBase = 0;
+ pDataBuf->fActive = false;
+ pDataBuf->idxOp = idxOp;
+ pDataBuf->fXcptAfterInstruction = false;
+ pDataBuf->enmExpectXcpt = kCidetExpectXcpt_None;
+ pThis->aOperands[idxOp].pDataBuf = pDataBuf;
+ pThis->cMemoryOperands++;
+ pDataBuf++;
+ }
+
+ /** @todo implement more than one memory operand. */
+ AssertReleaseReturn(pThis->cMemoryOperands <= 1, false);
+ return true;
+}
+
+
+/**
+ * The next code buffer configuration.
+ *
+ * @returns true if new one to test, false if we've reached end already.
+ * @param pThis The core state structure.
+ */
+bool CidetCoreSetupNextCodeBufferConfig(PCIDETCORE pThis)
+{
+ RT_NOREF_PV(pThis);
+ return false;
+}
+
+
+/**
+ * Sets up the first code buffer configuration.
+ *
+ * @returns true on success, false if no data buffers configured or failure.
+ * @param pThis The core state structure.
+ */
+bool CidetCoreSetupFirstCodeBufferConfig(PCIDETCORE pThis)
+{
+ Assert(pThis->cCodeBufConfigs > 0);
+ Assert(CIDETBUF_IS_CODE(pThis->paCodeBufConfigs[0].fFlags));
+ pThis->CodeBuf.idxCfg = 0;
+ pThis->CodeBuf.pCfg = &pThis->paCodeBufConfigs[0];
+ pThis->CodeBuf.off = 0;
+ pThis->CodeBuf.cb = 0x1000;
+ pThis->CodeBuf.cbSegLimit = UINT16_MAX;
+ pThis->CodeBuf.offSegBase = 0;
+ pThis->CodeBuf.fActive = true;
+ pThis->CodeBuf.idxOp = 7;
+ pThis->CodeBuf.fXcptAfterInstruction = false;
+ pThis->CodeBuf.enmExpectXcpt = kCidetExpectXcpt_None;
+ return true;
+}
+
+
+/**
+ * Gets the (encoded) size of the given operand in the current context.
+ *
+ * @returns Size in bytes.
+ * @param pThis The core state structure (for context).
+ * @param iOp The operand index.
+ */
+uint32_t CidetCoreGetOperandSize(PCIDETCORE pThis, uint8_t iOp)
+{
+ Assert(iOp < RT_ELEMENTS(pThis->aOperands));
+ uint32_t cbOp = g_acbCidetOfSizes[(pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) >> CIDET_OF_Z_SHIFT];
+ if (cbOp == UINT16_MAX)
+ {
+ Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_VAR_WDQ);
+ if (CIDETMODE_IS_64BIT(pThis->bMode))
+ {
+ if (pThis->fRexW)
+ cbOp = 8;
+ else if (!pThis->fOpSizePrf)
+ cbOp = 4;
+ else
+ cbOp = 2;
+ }
+ else if (CIDETMODE_IS_32BIT(pThis->bMode))
+ cbOp = !pThis->fOpSizePrf ? 4 : 2;
+ else
+ {
+ Assert(CIDETMODE_IS_16BIT(pThis->bMode));
+ cbOp = !pThis->fOpSizePrf ? 2 : 4;
+ }
+ return cbOp;
+ }
+
+ if (cbOp == UINT16_MAX - 1)
+ {
+ Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_SPECIAL);
+ AssertReleaseFailedReturn(0);
+ }
+
+ if (cbOp)
+ {
+#ifdef VBOX_STRICT
+ switch (cbOp)
+ {
+ case 1: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_BYTE); break;
+ case 2: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_WORD); break;
+ case 4: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_DWORD); break;
+ case 8: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_QWORD); break;
+ case 10: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_TBYTE); break;
+ case 16: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_OWORD); break;
+ case 32: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_YWORD); break;
+ case 64: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_ZWORD); break;
+ default: AssertFailed();
+ }
+#endif
+ return cbOp;
+ }
+ AssertReleaseFailedReturn(0);
+}
+
+
+bool CideCoreSetInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr)
+{
+ AssertReleaseMsgReturn(RT_VALID_PTR(pInstr), ("%p\n", pInstr), false);
+
+ pThis->pCurInstr = pInstr;
+
+ /*
+ * Extract info from the instruction descriptor.
+ */
+ pThis->fUsesModRm = false;
+ pThis->fUsesVexIndexRegs = false;
+ pThis->idxMrmRegOp = 7;
+ pThis->idxMrmRmOp = 7;
+ pThis->fMrmRegOp = 0;
+ pThis->fMrmRmOp = 0;
+ pThis->fInstrFlags = pInstr->fFlags;
+ pThis->cOperands = pInstr->cOperands;
+ if (pInstr->fFlags & CIDET_IF_MODRM)
+ {
+ pThis->fUsesModRm = true;
+ for (uint8_t iOp = 0; iOp < pInstr->cOperands; iOp++)
+ if (pInstr->afOperands[iOp] & CIDET_OF_M_REG)
+ {
+ pThis->idxMrmRegOp = iOp;
+ pThis->fMrmRegOp = pInstr->afOperands[iOp];
+ }
+ else if (pInstr->afOperands[iOp] & CIDET_OF_M_RM)
+ {
+ pThis->idxMrmRmOp = iOp;
+ pThis->fMrmRmOp = pInstr->afOperands[iOp];
+ }
+ }
+ else
+ AssertFailedReturn(false);
+
+ uint8_t iOp;
+ for (iOp = 0; iOp < pInstr->cOperands; iOp++)
+ {
+ pThis->aOperands[iOp].fFlags = pInstr->afOperands[iOp];
+ pThis->aOperands[iOp].iReg = UINT8_MAX;
+ pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp);
+ pThis->aOperands[iOp].fIsImmediate = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_IMM;
+ pThis->aOperands[iOp].fIsMem = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_MEM;
+ pThis->aOperands[iOp].fIsRipRelative = false;
+ pThis->aOperands[iOp].cbMemDisp = 0;
+ pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[iOp].uMemScale = 1;
+ pThis->aOperands[iOp].iEffSeg = UINT8_MAX;
+ pThis->aOperands[iOp].offSeg = UINT64_MAX;
+ pThis->aOperands[iOp].uEffAddr = UINT64_MAX;
+ pThis->aOperands[iOp].uImmDispValue = UINT64_MAX;
+ pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX;
+ pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX;
+ pThis->aOperands[iOp].In.pv = NULL;
+ pThis->aOperands[iOp].Expected.pv = NULL;
+ pThis->aOperands[iOp].pDataBuf = NULL;
+ }
+
+ for (; iOp < RT_ELEMENTS(pThis->aOperands); iOp++)
+ {
+ pThis->aOperands[iOp].fFlags = 0;
+ pThis->aOperands[iOp].iReg = UINT8_MAX;
+ pThis->aOperands[iOp].cb = 0;
+ pThis->aOperands[iOp].fIsImmediate = false;
+ pThis->aOperands[iOp].fIsMem = false;
+ pThis->aOperands[iOp].fIsRipRelative = false;
+ pThis->aOperands[iOp].cbMemDisp = 0;
+ pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX;
+ pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX;
+ pThis->aOperands[iOp].uMemScale = 1;
+ pThis->aOperands[iOp].iEffSeg = UINT8_MAX;
+ pThis->aOperands[iOp].offSeg = UINT64_MAX;
+ pThis->aOperands[iOp].uEffAddr = UINT64_MAX;
+ pThis->aOperands[iOp].uImmDispValue = UINT64_MAX;
+ pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX;
+ pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX;
+ pThis->aOperands[iOp].In.pv = NULL;
+ pThis->aOperands[iOp].Expected.pv = NULL;
+ pThis->aOperands[iOp].pDataBuf = NULL;
+ }
+
+ /*
+ * Reset various things.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aiInOut); i++)
+ pThis->aiInOut[i] = 0;
+
+ return true;
+}
+
+
+bool CidetCoreSetupInOut(PCIDETCORE pThis)
+{
+ /*
+ * Enumerate the operands.
+ */
+ uint8_t *pbBuf = &pThis->abBuf[0];
+ pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
+
+ uint8_t idxOp = pThis->cOperands;
+ while (idxOp-- > 0)
+ {
+ if (pThis->aOperands[idxOp].fIsMem)
+ {
+ /*
+ * Memory operand.
+ */
+ Assert(pThis->aOperands[idxOp].fIsMem);
+
+ /* Set the In & Expected members to point to temporary buffer space. */
+ pThis->aOperands[idxOp].Expected.pu8 = pbBuf;
+ pbBuf += pThis->aOperands[idxOp].cb;
+ pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
+
+ pThis->aOperands[idxOp].In.pu8 = pbBuf;
+ pbBuf += pThis->aOperands[idxOp].cb;
+ pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
+
+ /* Initialize the buffer we're gonna use. */
+ pThis->aOperands[idxOp].iEffSeg = pThis->uSegPrf != X86_SREG_COUNT
+ ? pThis->uSegPrf
+ : !(pThis->aOperands[idxOp].fFlags & CIDET_OF_ALWAYS_SEG_ES) ? X86_SREG_DS
+ : X86_SREG_ES;
+
+ PCIDETBUF pDataBuf = pThis->aOperands[idxOp].pDataBuf;
+ AssertReleaseReturn(pDataBuf, false);
+ Assert(pDataBuf->cb == pThis->aOperands[idxOp].cb);
+ Assert(pDataBuf->idxOp == idxOp);
+ if (!pThis->pfnReInitDataBuf(pThis, pDataBuf))
+ {
+ pThis->cSkippedReInitDataBuf++;
+ return false;
+ }
+ pDataBuf->fActive = true;
+
+ /* Calc buffer related operand members. */
+ pThis->aOperands[idxOp].uEffAddr = pDataBuf->uEffBufAddr + pDataBuf->off;
+ uint64_t offSeg = pThis->aOperands[idxOp].uEffAddr - pDataBuf->uSegBase;
+ pThis->aOperands[idxOp].offSeg = offSeg;
+ AssertRelease(offSeg <= g_au64ByteSizeToMask[pThis->cbAddrMode]);
+
+ /*
+ * Select register and displacement values for the buffer addressing (works on offSeg).
+ */
+ uint8_t const iMemIndexReg = pThis->aOperands[idxOp].iMemIndexReg;
+ uint8_t const iMemBaseReg = pThis->aOperands[idxOp].iMemBaseReg;
+ if (pThis->aOperands[idxOp].fIsRipRelative)
+ {
+ /* rip relative. */
+ pThis->aOperands[idxOp].uImmDispValue = offSeg - (pThis->InCtx.rip + pThis->cbInstr);
+ Assert(pThis->aOperands[idxOp].cbMemDisp == 4);
+ if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue > INT32_MAX
+ || (int64_t)pThis->aOperands[idxOp].uImmDispValue < INT32_MIN)
+ {
+ pThis->cSkippedDataBufWrtRip++;
+ return false;
+ }
+ }
+ else if (iMemBaseReg != UINT8_MAX)
+ {
+ if ( iMemBaseReg != iMemIndexReg
+ || pThis->fUsesVexIndexRegs)
+ {
+ /* [base] or [base + disp] or [base + index * scale] or [base + index * scale + disp] */
+ if (pThis->aOperands[idxOp].cbMemDisp > 0)
+ {
+ pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
+ offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
+ }
+
+ if (iMemIndexReg != UINT8_MAX)
+ {
+ pThis->aOperands[idxOp].uMemIndexRegValue = CidetCoreGetRandU64(pThis, pThis->cbAddrMode);
+ offSeg -= pThis->aOperands[idxOp].uMemIndexRegValue * pThis->aOperands[idxOp].uMemScale;
+ }
+
+ pThis->aOperands[idxOp].uMemBaseRegValue = offSeg & g_au64ByteSizeToMask[pThis->cbAddrMode];
+ }
+ else
+ {
+ /* base == index; [base + index * scale] or [base * (scale + 1)]. */
+ uint8_t const uEffScale = pThis->aOperands[idxOp].uMemScale + 1;
+ if (pThis->aOperands[idxOp].cbMemDisp > 0)
+ {
+ pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
+ offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
+ offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode];
+ uint8_t uRemainder = offSeg % uEffScale;
+ if (uRemainder != 0)
+ {
+ Assert(pThis->aOperands[idxOp].cbMemDisp < 8);
+ Assert( (int64_t)pThis->aOperands[idxOp].uImmDispValue
+ <= g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp]);
+ pThis->aOperands[idxOp].uImmDispValue = (int64_t)pThis->aOperands[idxOp].uImmDispValue
+ + uRemainder;
+ offSeg -= uRemainder;
+ if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue
+ > g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp])
+ {
+ pThis->aOperands[idxOp].uImmDispValue -= uEffScale;
+ offSeg += uEffScale;
+ }
+ Assert(offSeg % uEffScale == 0);
+ }
+ }
+ else
+ {
+ offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode];
+ if (offSeg % uEffScale != 0)
+ {
+ pThis->cSkippedSameBaseIndexRemainder++;
+ return false;
+ }
+ }
+ offSeg /= uEffScale;
+ pThis->aOperands[idxOp].uMemBaseRegValue = pThis->aOperands[idxOp].uMemIndexRegValue = offSeg;
+ }
+ }
+ else if (iMemIndexReg != UINT8_MAX)
+ {
+ /* [index * scale] or [index * scale + disp] */
+ if (pThis->aOperands[idxOp].cbMemDisp > 0)
+ {
+ pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
+ offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
+ pThis->aOperands[idxOp].uImmDispValue += offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1);
+ offSeg &= ~(RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1);
+ }
+ else if (offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1))
+ {
+ pThis->cSkippedOnlyIndexRemainder++;
+ return false;
+ }
+
+ pThis->aOperands[idxOp].uMemIndexRegValue = offSeg / pThis->aOperands[idxOp].uMemScale;
+ Assert((offSeg % pThis->aOperands[idxOp].uMemScale) == 0);
+ AssertRelease(!pThis->fUsesVexIndexRegs); /** @todo implement VEX indexing */
+ }
+ else
+ {
+ /* [disp] */
+ Assert( pThis->aOperands[idxOp].cbMemDisp == 8
+ || pThis->aOperands[idxOp].cbMemDisp == 4
+ || pThis->aOperands[idxOp].cbMemDisp == 2
+ || pThis->aOperands[idxOp].cbMemDisp == 1);
+ if ( pThis->aOperands[idxOp].cbMemDisp == 4
+ ? (int64_t)offSeg != (int32_t)offSeg
+ : pThis->aOperands[idxOp].cbMemDisp == 2
+ ? (int64_t)offSeg != (int16_t)offSeg
+ : pThis->aOperands[idxOp].cbMemDisp == 1
+ ? (int64_t)offSeg != (int8_t)offSeg
+ : false /* 8 */)
+ {
+ pThis->cSkippedDirectAddressingOverflow++;
+ return false;
+ }
+ pThis->aOperands[idxOp].uImmDispValue = offSeg;
+ }
+
+ /*
+ * Modify the input and expected output contexts with the base and
+ * index register values. To simplify verification and the work
+ * here, we update the uMemBaseRegValue and uMemIndexRegValue
+ * members to reflect the whole register.
+ */
+ if (iMemBaseReg != UINT8_MAX)
+ {
+ if (pThis->cbAddrMode == 4)
+ {
+ pThis->aOperands[idxOp].uMemBaseRegValue &= UINT32_MAX;
+ pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffff00000000);
+ }
+ else if (pThis->cbAddrMode == 2)
+ {
+ pThis->aOperands[idxOp].uMemBaseRegValue &= UINT16_MAX;
+ pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffffffff0000);
+ }
+ pThis->InCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue;
+ pThis->ExpectedCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue;
+ }
+
+ if (iMemIndexReg != UINT8_MAX)
+ {
+ if (pThis->cbAddrMode == 4)
+ {
+ pThis->aOperands[idxOp].uMemIndexRegValue &= UINT32_MAX;
+ pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffff00000000);
+ }
+ else if (pThis->cbAddrMode == 2)
+ {
+ pThis->aOperands[idxOp].uMemIndexRegValue &= UINT16_MAX;
+ pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffffffff0000);
+ }
+ pThis->InCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue;
+ pThis->ExpectedCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue;
+ }
+ }
+ else
+ {
+ /*
+ * Non-memory, so clear the memory related members.
+ */
+ Assert(!pThis->aOperands[idxOp].fIsMem);
+ pThis->aOperands[idxOp].iEffSeg = UINT8_MAX;
+ pThis->aOperands[idxOp].offSeg = UINT64_MAX;
+ pThis->aOperands[idxOp].uEffAddr = UINT64_MAX;
+ pThis->aOperands[idxOp].pDataBuf = NULL;
+
+ switch (pThis->aOperands[idxOp].fFlags & CIDET_OF_K_MASK)
+ {
+ case CIDET_OF_K_GPR:
+ if (!pThis->aOperands[idxOp].fIsHighByteRegister)
+ {
+ pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg];
+ pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg];
+ }
+ else
+ {
+ pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4];
+ pThis->aOperands[idxOp].In.pu8++;
+ pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4];
+ pThis->aOperands[idxOp].Expected.pu8++;
+ }
+ break;
+
+ case CIDET_OF_K_IMM:
+ pThis->aOperands[idxOp].In.pv = NULL;
+ pThis->aOperands[idxOp].Expected.pv = NULL;
+ break;
+
+ case CIDET_OF_K_SREG:
+ if (pThis->aOperands[idxOp].iReg < RT_ELEMENTS(pThis->InCtx.aSRegs))
+ {
+ pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aSRegs[pThis->aOperands[idxOp].iReg];
+ pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aSRegs[pThis->aOperands[idxOp].iReg];
+ }
+ else
+ {
+ pThis->aOperands[idxOp].In.pv = NULL;
+ pThis->aOperands[idxOp].Expected.pv = NULL;
+ }
+ break;
+
+ case CIDET_OF_K_CR:
+ case CIDET_OF_K_SSE:
+ case CIDET_OF_K_AVX:
+ case CIDET_OF_K_AVX512:
+ case CIDET_OF_K_FPU:
+ case CIDET_OF_K_MMX:
+ case CIDET_OF_K_AVXFUTURE:
+ case CIDET_OF_K_SPECIAL:
+ case CIDET_OF_K_TEST:
+ /** @todo Implement testing these registers. */
+ case CIDET_OF_K_NONE:
+ default:
+ AssertReleaseFailedReturn(false);
+ }
+ }
+ }
+ AssertRelease((uintptr_t)pbBuf - (uintptr_t)&pThis->abBuf[0] <= sizeof(pThis->abBuf));
+
+ /*
+ * Call instruction specific setup function (for operand values and flags).
+ */
+ int rc = pThis->pCurInstr->pfnSetupInOut(pThis, false /*fInvalid*/);
+ if (RT_FAILURE(rc))
+ {
+ pThis->cSkippedSetupInOut++;
+ return false;
+ }
+
+ /*
+ * Do the 2nd set of the memory operand preparations.
+ */
+ if (pThis->fHasMemoryOperand)
+ {
+ idxOp = pThis->cOperands;
+ while (idxOp-- > 0)
+ if (pThis->aOperands[idxOp].fIsMem)
+ {
+ Assert(pThis->aOperands[idxOp].pDataBuf);
+ if (!pThis->pfnSetupDataBuf(pThis, pThis->aOperands[idxOp].pDataBuf, pThis->aOperands[idxOp].In.pv))
+ {
+ pThis->cSkippedSetupDataBuf++;
+ return false;
+ }
+
+ Assert( pThis->aOperands[idxOp].iMemBaseReg == UINT8_MAX
+ || pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemBaseReg] == pThis->aOperands[idxOp].uMemBaseRegValue);
+ Assert( pThis->aOperands[idxOp].iMemIndexReg == UINT8_MAX
+ || ( !pThis->fUsesVexIndexRegs
+ ? pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemIndexReg]
+ == pThis->aOperands[idxOp].uMemIndexRegValue
+ : false /** @todo VEX indexing */));
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Figures the instruction length.
+ *
+ * This is a duplicate of CidetCoreAssemble() with the buffer updates removed.
+ *
+ * @returns true and pThis->cbInstr on success, false on failure.
+ * @param pThis The core state structure (for context).
+ */
+bool CidetCoreAssembleLength(PCIDETCORE pThis)
+{
+ uint8_t off = 0;
+
+ /*
+ * Prefixes.
+ */
+ if (1)
+ {
+ if (pThis->fAddrSizePrf)
+ off++;
+ if (pThis->fOpSizePrf)
+ off++;
+ }
+ else
+ {
+ /** @todo prefix list. */
+ }
+
+ /*
+ * Prefixes that must come right before the opcode.
+ */
+ /** @todo VEX and EVEX. */
+ if (pThis->fVex)
+ {
+ /** @todo VEX and EVEX. */
+ }
+ else if (pThis->fEvex)
+ {
+ /** @todo VEX and EVEX. */
+ }
+ else
+ {
+ if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex)
+ off++;
+ }
+
+ /*
+ * The opcode.
+ */
+ //uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode;
+ switch (pThis->pCurInstr->cbOpcode)
+ {
+ case 3: off++; RT_FALL_THRU();
+ case 2: off++; RT_FALL_THRU();
+ case 1: off++;
+ break;
+ default:
+ AssertReleaseFailedReturn(false);
+ }
+
+ /*
+ * Mod R/M
+ */
+ if (pThis->fUsesModRm)
+ {
+ off++;
+ if (pThis->fSib)
+ off++;
+ if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
+ {
+ //uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue;
+ switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp)
+ {
+ case 0: break;
+ case 8:
+ case 7:
+ case 6:
+ case 5:
+ case 4:
+ case 3:
+ case 2:
+ case 1:
+ break;
+ default: AssertReleaseFailedReturn(false);
+ }
+ off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp;
+ }
+ }
+
+ /*
+ * Immediates.
+ */
+ uint8_t iOp = pThis->cOperands;
+ while (iOp-- > 0)
+ if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM)
+ {
+ //uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue;
+ switch (pThis->aOperands[iOp].cb)
+ {
+ case 8:
+ case 7:
+ case 6:
+ case 5:
+ case 4:
+ case 3:
+ case 2:
+ case 1:
+ break;
+ default: AssertReleaseFailedReturn(false);
+ }
+ off += pThis->aOperands[iOp].cb;
+ }
+
+ pThis->cbInstr = off;
+ return true;
+}
+
+
+/**
+ * Assembles the instruction.
+ *
+ * This is a duplicate of CidetCoreAssembleLength() with buffer writes.
+ *
+ * @returns true and pThis->cbInstr and pThis->abInstr on success, false on
+ * failure.
+ * @param pThis The core state structure (for context).
+ */
+bool CidetCoreAssemble(PCIDETCORE pThis)
+{
+ uint8_t off = 0;
+
+ /*
+ * Prefixes.
+ */
+ if (1)
+ {
+ if (pThis->fAddrSizePrf)
+ pThis->abInstr[off++] = 0x67;
+ if (pThis->fOpSizePrf)
+ pThis->abInstr[off++] = 0x66;
+ }
+ else
+ {
+ /** @todo prefix list. */
+ }
+
+ /*
+ * Prefixes that must come right before the opcode.
+ */
+ /** @todo VEX and EVEX. */
+ if (pThis->fVex)
+ {
+ /** @todo VEX and EVEX. */
+ }
+ else if (pThis->fEvex)
+ {
+ /** @todo VEX and EVEX. */
+ }
+ else
+ {
+ if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex)
+ pThis->abInstr[off++] = 0x40 | (pThis->fRexB * 1) | (pThis->fRexX * 2) | (pThis->fRexR * 4) | (pThis->fRexW * 8);
+ }
+
+ /*
+ * The opcode.
+ */
+ uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode;
+ switch (pThis->pCurInstr->cbOpcode)
+ {
+ case 3: pThis->abInstr[off++] = *pbOpcode++; RT_FALL_THRU();
+ case 2: pThis->abInstr[off++] = *pbOpcode++; RT_FALL_THRU();
+ case 1: pThis->abInstr[off++] = *pbOpcode++;
+ break;
+ default:
+ AssertReleaseFailedReturn(false);
+ }
+
+ /*
+ * Mod R/M
+ */
+ if (pThis->fUsesModRm)
+ {
+ pThis->abInstr[off++] = pThis->bModRm;
+ if (pThis->fSib)
+ pThis->abInstr[off++] = pThis->bSib;
+ if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
+ {
+ uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue;
+ switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp)
+ {
+ case 0: break;
+ case 8: pThis->abInstr[off + 3] = (uDispValue >> 56) & UINT8_C(0xff); RT_FALL_THRU();
+ case 7: pThis->abInstr[off + 3] = (uDispValue >> 48) & UINT8_C(0xff); RT_FALL_THRU();
+ case 6: pThis->abInstr[off + 3] = (uDispValue >> 40) & UINT8_C(0xff); RT_FALL_THRU();
+ case 5: pThis->abInstr[off + 3] = (uDispValue >> 32) & UINT8_C(0xff); RT_FALL_THRU();
+ case 4: pThis->abInstr[off + 3] = (uDispValue >> 24) & UINT8_C(0xff); RT_FALL_THRU();
+ case 3: pThis->abInstr[off + 2] = (uDispValue >> 16) & UINT8_C(0xff); RT_FALL_THRU();
+ case 2: pThis->abInstr[off + 1] = (uDispValue >> 8) & UINT8_C(0xff); RT_FALL_THRU();
+ case 1: pThis->abInstr[off] = uDispValue & UINT8_C(0xff);
+ break;
+ default: AssertReleaseFailedReturn(false);
+ }
+ off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp;
+ }
+ }
+
+ /*
+ * Immediates.
+ */
+ uint8_t iOp = pThis->cOperands;
+ while (iOp-- > 0)
+ if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM)
+ {
+ uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue;
+ switch (pThis->aOperands[iOp].cb)
+ {
+ case 8: pThis->abInstr[off + 3] = (uImmValue >> 56) & UINT8_C(0xff); RT_FALL_THRU();
+ case 7: pThis->abInstr[off + 3] = (uImmValue >> 48) & UINT8_C(0xff); RT_FALL_THRU();
+ case 6: pThis->abInstr[off + 3] = (uImmValue >> 40) & UINT8_C(0xff); RT_FALL_THRU();
+ case 5: pThis->abInstr[off + 3] = (uImmValue >> 32) & UINT8_C(0xff); RT_FALL_THRU();
+ case 4: pThis->abInstr[off + 3] = (uImmValue >> 24) & UINT8_C(0xff); RT_FALL_THRU();
+ case 3: pThis->abInstr[off + 2] = (uImmValue >> 16) & UINT8_C(0xff); RT_FALL_THRU();
+ case 2: pThis->abInstr[off + 1] = (uImmValue >> 8) & UINT8_C(0xff); RT_FALL_THRU();
+ case 1: pThis->abInstr[off] = uImmValue & UINT8_C(0xff);
+ break;
+ default: AssertReleaseFailedReturn(false);
+ }
+ off += pThis->aOperands[iOp].cb;
+ }
+
+ pThis->cbInstr = off;
+ return true;
+}
+
+
+bool CidetCoreReInitCodeBuf(PCIDETCORE pThis)
+{
+ /*
+ * Re-initialize the buffer. Requires instruction length and positioning.
+ */
+ if (CidetCoreAssembleLength(pThis))
+ {
+ pThis->CodeBuf.cb = pThis->cbInstr;
+ pThis->CodeBuf.off = CIDET_CODE_BUF_SIZE - PAGE_SIZE - pThis->cbInstr;
+ if (pThis->pfnReInitCodeBuf(pThis, &pThis->CodeBuf))
+ {
+ pThis->CodeBuf.fActive = true;
+
+ /*
+ * Update the RIP and CS values in the input and expected contexts.
+ */
+ pThis->InCtx.rip = pThis->CodeBuf.uEffBufAddr + pThis->CodeBuf.offActive - pThis->CodeBuf.uSegBase;
+ pThis->ExpectedCtx.rip = pThis->InCtx.rip + pThis->cbInstr; /** @todo account for expected traps. */
+ if (pThis->CodeBuf.uSeg != UINT32_MAX)
+ {
+ pThis->InCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg;
+ pThis->ExpectedCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg;
+ }
+ return true;
+ }
+ else
+ pThis->cSkippedReInitCodeBuf++;
+ }
+ else
+ pThis->cSkippedAssemble++;
+ return false;
+}
+
+
+#ifdef CIDET_DEBUG_DISAS
+/**
+ * @callback_method_impl{FNDISREADBYTES}
+ */
+static DECLCALLBACK(int) cidetCoreDisReadBytes(PDISSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead)
+{
+ PCIDETCORE pThis = (PCIDETCORE)pDis->pvUser;
+ memcpy(&pDis->abInstr[offInstr], &pThis->abInstr[offInstr], cbMaxRead);
+ pDis->cbCachedInstr = offInstr + cbMaxRead;
+ return VINF_SUCCESS;
+}
+#endif
+
+
+bool CidetCoreSetupCodeBuf(PCIDETCORE pThis, unsigned iSubTest)
+{
+ if (CidetCoreAssemble(pThis))
+ {
+ //CIDET_DPRINTF(("%04u: %.*Rhxs\n", i, pThis->cbInstr, pThis->abInstr));
+#ifdef CIDET_DEBUG_DISAS
+ DISCPUSTATE Dis;
+ char szInstr[80] = {0};
+ uint32_t cbInstr;
+ int rcDis = DISInstrToStrEx(pThis->InCtx.rip,
+ CIDETMODE_IS_64BIT(pThis->bMode) ? DISCPUMODE_64BIT
+ : CIDETMODE_IS_32BIT(pThis->bMode) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
+ cidetCoreDisReadBytes,
+ pThis,
+ DISOPTYPE_ALL,
+ &Dis,
+ &cbInstr,
+ szInstr, sizeof(szInstr));
+ CIDET_DPRINTF(("%04u: %s", iSubTest, szInstr));
+ Assert(cbInstr == pThis->cbInstr);
+#else
+ RT_NOREF_PV(iSubTest);
+#endif
+ if (pThis->pfnSetupCodeBuf(pThis, &pThis->CodeBuf, pThis->abInstr))
+ {
+ return true;
+ }
+ pThis->cSkippedSetupCodeBuf++;
+ }
+ else
+ pThis->cSkippedAssemble++;
+ return false;
+}
+
+
+/**
+ * Compares the output with the output expectations.
+ *
+ * @returns true if ok, false if not (calls pfnFailure too).
+ * @param pThis The core state structure.
+ */
+bool CidetCoreCheckResults(PCIDETCORE pThis)
+{
+ if (memcmp(&pThis->ActualCtx, &pThis->ExpectedCtx, CIDETCPUCTX_COMPARE_SIZE) == 0)
+ return true;
+
+ unsigned cDiffs = 0;
+#define IF_FIELD_DIFFERS_SET_ERROR(a_Field, a_Fmt) \
+ if (pThis->ActualCtx.a_Field != pThis->ExpectedCtx.a_Field) \
+ { \
+ CidetCoreSetError(pThis, #a_Field " differs: got %#llx expected %#llx", \
+ pThis->ActualCtx.a_Field, pThis->ExpectedCtx.a_Field); \
+ cDiffs++; \
+ } else do { } while (0)
+
+ IF_FIELD_DIFFERS_SET_ERROR(rip, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(rfl, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xAX], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBX], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xCX], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDX], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSP], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBP], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSI], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDI], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x8], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x10], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x11], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x12], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x13], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x14], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x15], "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_CS], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_SS], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_DS], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_ES], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_FS], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_GS], "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(uXcpt, "%#04x");
+ IF_FIELD_DIFFERS_SET_ERROR(uErr, "%#04llx");
+ IF_FIELD_DIFFERS_SET_ERROR(cr2, "%#010llx");
+#ifndef CIDET_REDUCED_CTX
+ IF_FIELD_DIFFERS_SET_ERROR(tr, "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(ldtr, "%#06x");
+ IF_FIELD_DIFFERS_SET_ERROR(cr0, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(cr3, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(cr4, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(cr8, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr0, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr1, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr2, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr3, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr6, "%#010llx");
+ IF_FIELD_DIFFERS_SET_ERROR(dr7, "%#010llx");
+#endif
+
+AssertMsgFailed(("cDiffs=%d\n", cDiffs));
+ Assert(cDiffs > 0);
+ return cDiffs == 0;
+}
+
+
+bool CidetCoreTest_Basic(PCIDETCORE pThis)
+{
+ /*
+ * Iterate all encodings.
+ */
+ if (!CidetCoreSetupFirstBaseEncoding(pThis))
+ return CidetCoreSetError(pThis, "CidetCoreSetupFirstBaseEncoding failed");
+ unsigned cExecuted = 0;
+ unsigned cSkipped = 0;
+ do
+ {
+ /*
+ * Iterate data buffer configurations (one iteration if none).
+ */
+ if (CidetCoreSetupFirstMemoryOperandConfig(pThis))
+ {
+ do
+ {
+ /*
+ * Iterate code buffer configurations.
+ */
+ if (!CidetCoreSetupFirstCodeBufferConfig(pThis))
+ return CidetCoreSetError(pThis, "CidetCoreSetupFirstMemoryOperandConfig failed");
+ do
+ {
+ /*
+ * Set up inputs and expected outputs, then emit the test code.
+ */
+ pThis->InCtx = pThis->InTemplateCtx;
+ pThis->InCtx.fTrickyStack = pThis->fHasStackRegInMrmRmBase || pThis->fHasStackRegInMrmReg;
+ pThis->ExpectedCtx = pThis->InCtx;
+ if ( CidetCoreReInitCodeBuf(pThis)
+ && CidetCoreSetupInOut(pThis)
+ && CidetCoreSetupCodeBuf(pThis, cSkipped + cExecuted)
+ )
+ {
+ if (pThis->pfnExecute(pThis))
+ {
+ cExecuted++;
+
+ /*
+ * Check the result against our expectations.
+ */
+ CidetCoreCheckResults(pThis);
+ /** @todo check result. */
+
+ }
+ else
+ cSkipped++;
+ }
+ else
+ cSkipped++;
+ } while (CidetCoreSetupNextCodeBufferConfig(pThis));
+ } while (CidetCoreSetupNextMemoryOperandConfig(pThis));
+ }
+ else
+ cSkipped++;
+ } while (CidetCoreSetupNextBaseEncoding(pThis));
+
+ CIDET_DPRINTF(("CidetCoreTest_Basic: cExecuted=%u cSkipped=%u\n"
+ " cSkippedSetupInOut =%u\n"
+ " cSkippedReInitDataBuf =%u\n"
+ " cSkippedSetupDataBuf =%u\n"
+ " cSkippedDataBufWrtRip =%u\n"
+ " cSkippedAssemble =%u\n"
+ " cSkippedReInitCodeBuf =%u\n"
+ " cSkippedSetupCodeBuf =%u\n"
+ " cSkippedSameBaseIndexRemainder =%u\n"
+ " cSkippedOnlyIndexRemainder =%u\n"
+ " cSkippedDirectAddressingOverflow =%u\n"
+ ,
+ cExecuted, cSkipped,
+ pThis->cSkippedSetupInOut,
+ pThis->cSkippedReInitDataBuf,
+ pThis->cSkippedSetupDataBuf,
+ pThis->cSkippedDataBufWrtRip,
+ pThis->cSkippedAssemble,
+ pThis->cSkippedReInitCodeBuf,
+ pThis->cSkippedSetupCodeBuf,
+ pThis->cSkippedSameBaseIndexRemainder,
+ pThis->cSkippedOnlyIndexRemainder,
+ pThis->cSkippedDirectAddressingOverflow
+ ));
+
+ return true;
+}
+
+
+bool CidetCoreTestInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr)
+{
+ AssertReleaseMsgReturn(RT_VALID_PTR(pThis), ("%p\n", pThis), false);
+ AssertReleaseReturn(pThis->u32Magic == CIDETCORE_MAGIC, false);
+ AssertReleaseReturn(pThis->cCodeBufConfigs > 0, false);
+
+ if (!CideCoreSetInstruction(pThis, pInstr))
+ return CidetCoreSetError(pThis, "CideCoreSetInstruction failed");
+
+ bool fResult = CidetCoreTest_Basic(pThis);
+
+ return fResult;
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp
new file mode 100644
index 00000000..4cbbd5db
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet-instr-1.cpp
@@ -0,0 +1,297 @@
+/* $Id: cidet-instr-1.cpp $ */
+/** @file
+ * CPU Instruction Decoding & Execution Tests - First bunch of instructions.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "cidet.h"
+#include <VBox/err.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*
+ * Shorter defines for the EFLAGS to save table space.
+ */
+#undef CF
+#undef PF
+#undef AF
+#undef ZF
+#undef SF
+#undef OF
+
+#define CF X86_EFL_CF
+#define PF X86_EFL_PF
+#define AF X86_EFL_AF
+#define ZF X86_EFL_ZF
+#define SF X86_EFL_SF
+#define OF X86_EFL_OF
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct CIDET2IN1OUTWITHFLAGSU8ENTRY
+{
+ uint8_t uIn1;
+ uint8_t uIn2;
+ uint16_t fEFlagsIn;
+ uint8_t uOut;
+ uint16_t fEFlagsOut;
+} CIDET2IN1OUTWITHFLAGSU8ENTRY;
+typedef CIDET2IN1OUTWITHFLAGSU8ENTRY const *PCCIDET2IN1OUTWITHFLAGSU8ENTRY;
+
+typedef struct CIDET2IN1OUTWITHFLAGSU16ENTRY
+{
+ uint16_t uIn1;
+ uint16_t uIn2;
+ uint16_t fEFlagsIn;
+ uint16_t uOut;
+ uint16_t fEFlagsOut;
+} CIDET2IN1OUTWITHFLAGSU16ENTRY;
+typedef CIDET2IN1OUTWITHFLAGSU16ENTRY const *PCCIDET2IN1OUTWITHFLAGSU16ENTRY;
+
+typedef struct CIDET2IN1OUTWITHFLAGSU32ENTRY
+{
+ uint32_t uIn1;
+ uint32_t uIn2;
+ uint16_t fEFlagsIn;
+ uint32_t uOut;
+ uint16_t fEFlagsOut;
+} CIDET2IN1OUTWITHFLAGSU32ENTRY;
+typedef CIDET2IN1OUTWITHFLAGSU32ENTRY const *PCCIDET2IN1OUTWITHFLAGSU32ENTRY;
+
+typedef struct CIDET2IN1OUTWITHFLAGSU64ENTRY
+{
+ uint64_t uIn1;
+ uint64_t uIn2;
+ uint16_t fEFlagsIn;
+ uint64_t uOut;
+ uint16_t fEFlagsOut;
+} CIDET2IN1OUTWITHFLAGSU64ENTRY;
+typedef CIDET2IN1OUTWITHFLAGSU64ENTRY const *PCCIDET2IN1OUTWITHFLAGSU64ENTRY;
+
+typedef struct CIDET2IN1OUTWITHFLAGS
+{
+ PCCIDET2IN1OUTWITHFLAGSU8ENTRY pa8Entries;
+ PCCIDET2IN1OUTWITHFLAGSU16ENTRY pa16Entries;
+ PCCIDET2IN1OUTWITHFLAGSU32ENTRY pa32Entries;
+ PCCIDET2IN1OUTWITHFLAGSU64ENTRY pa64Entries;
+ uint16_t c8Entries;
+ uint16_t c16Entries;
+ uint16_t c32Entries;
+ uint16_t c64Entries;
+ uint32_t fRelevantEFlags;
+} CIDET2IN1OUTWITHFLAGS;
+
+#define CIDET2IN1OUTWITHFLAGS_INITIALIZER(a_fRelevantEFlags) \
+ { \
+ &s_a8Results[0], &s_a16Results[0], &s_a32Results[0], &s_a64Results[0], \
+ RT_ELEMENTS(s_a8Results), RT_ELEMENTS(s_a16Results), RT_ELEMENTS(s_a32Results), RT_ELEMENTS(s_a64Results), \
+ (a_fRelevantEFlags) \
+ }
+
+
+/**
+ * Generic worker for a FNCIDETSETUPINOUT function with two GPR/MEM registers,
+ * storing result in the first and flags.
+ *
+ * @returns See FNCIDETSETUPINOUT.
+ * @param pThis The core CIDET state structure. The InCtx
+ * and ExpectedCtx members will be modified.
+ * @param fInvalid When set, get the next invalid operands that will
+ * cause exceptions/faults.
+ * @param pResults The result collection.
+ */
+static int CidetGenericIn2Out1WithFlags(PCIDETCORE pThis, bool fInvalid, CIDET2IN1OUTWITHFLAGS const *pResults)
+{
+ int rc;
+
+ Assert(pThis->idxMrmRegOp < 2);
+ Assert(pThis->idxMrmRmOp < 2);
+ Assert(pThis->idxMrmRmOp != pThis->idxMrmRegOp);
+ AssertCompile(RT_ELEMENTS(pThis->aiInOut) >= 4);
+
+ if (!fInvalid)
+ {
+ if ( !pThis->fHasRegCollisionDirect
+ && !pThis->fHasRegCollisionMem)
+ {
+ pThis->InCtx.rfl &= ~(uint64_t)pResults->fRelevantEFlags;
+ pThis->ExpectedCtx.rfl &= ~(uint64_t)pResults->fRelevantEFlags;
+ switch (pThis->aOperands[0].cb)
+ {
+ case 1:
+ {
+ uint16_t idx = ++pThis->aiInOut[0] % pResults->c8Entries;
+ PCCIDET2IN1OUTWITHFLAGSU8ENTRY pEntry = &pResults->pa8Entries[idx];
+ rc = idx ? VINF_SUCCESS : VINF_EOF;
+
+ *pThis->aOperands[0].In.pu8 = pEntry->uIn1;
+ *pThis->aOperands[1].In.pu8 = pEntry->uIn2;
+ pThis->InCtx.rfl |= pEntry->fEFlagsIn;
+
+ *pThis->aOperands[0].Expected.pu8 = pEntry->uOut;
+ *pThis->aOperands[1].Expected.pu8 = pEntry->uIn2;
+ pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut;
+ break;
+ }
+
+ case 2:
+ {
+ uint16_t idx = ++pThis->aiInOut[1] % pResults->c16Entries;
+ PCCIDET2IN1OUTWITHFLAGSU16ENTRY pEntry = &pResults->pa16Entries[idx];
+ rc = idx ? VINF_SUCCESS : VINF_EOF;
+
+ *pThis->aOperands[0].In.pu16 = pEntry->uIn1;
+ *pThis->aOperands[1].In.pu16 = pEntry->uIn2;
+ pThis->InCtx.rfl |= pEntry->fEFlagsIn;
+
+ *pThis->aOperands[0].Expected.pu16 = pEntry->uOut;
+ *pThis->aOperands[1].Expected.pu16 = pEntry->uIn2;
+ pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut;
+ break;
+ }
+
+ case 4:
+ {
+ uint16_t idx = ++pThis->aiInOut[2] % pResults->c32Entries;
+ PCCIDET2IN1OUTWITHFLAGSU32ENTRY pEntry = &pResults->pa32Entries[idx];
+ rc = idx ? VINF_SUCCESS : VINF_EOF;
+
+ *pThis->aOperands[0].In.pu32 = pEntry->uIn1;
+ *pThis->aOperands[1].In.pu32 = pEntry->uIn2;
+ pThis->InCtx.rfl |= pEntry->fEFlagsIn;
+
+ *pThis->aOperands[0].Expected.pu32 = pEntry->uOut;
+ if (!pThis->aOperands[0].fIsMem)
+ pThis->aOperands[0].Expected.pu32[1] = 0;
+ *pThis->aOperands[1].Expected.pu32 = pEntry->uIn2;
+ pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut;
+ break;
+ }
+
+ case 8:
+ {
+ uint16_t idx = ++pThis->aiInOut[3] % pResults->c64Entries;
+ PCCIDET2IN1OUTWITHFLAGSU64ENTRY pEntry = &pResults->pa64Entries[idx];
+ rc = idx ? VINF_SUCCESS : VINF_EOF;
+
+ *pThis->aOperands[0].In.pu64 = pEntry->uIn1;
+ *pThis->aOperands[1].In.pu64 = pEntry->uIn2;
+ pThis->InCtx.rfl |= pEntry->fEFlagsIn;
+
+ *pThis->aOperands[0].Expected.pu64 = pEntry->uOut;
+ *pThis->aOperands[1].Expected.pu64 = pEntry->uIn2;
+ pThis->ExpectedCtx.rfl |= pEntry->fEFlagsOut;
+ break;
+ }
+
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ rc = VERR_NO_DATA;
+ return rc;
+}
+
+
+static DECLCALLBACK(int) cidetInOutAdd(PCIDETCORE pThis, bool fInvalid)
+{
+ static const CIDET2IN1OUTWITHFLAGSU8ENTRY s_a8Results[] =
+ {
+ { UINT8_C(0x00), UINT8_C(0x00), 0, UINT8_C(0x00), ZF | PF },
+ { UINT8_C(0xff), UINT8_C(0x01), 0, UINT8_C(0x00), CF | ZF | AF | PF },
+ { UINT8_C(0x7f), UINT8_C(0x80), 0, UINT8_C(0xff), SF | PF },
+ { UINT8_C(0x01), UINT8_C(0x01), 0, UINT8_C(0x02), 0 },
+ };
+ static const CIDET2IN1OUTWITHFLAGSU16ENTRY s_a16Results[] =
+ {
+ { UINT16_C(0x0000), UINT16_C(0x0000), 0, UINT16_C(0x0000), ZF | PF },
+ { UINT16_C(0xfefd), UINT16_C(0x0103), 0, UINT16_C(0x0000), CF | ZF | AF | PF },
+ { UINT16_C(0x8e7d), UINT16_C(0x7182), 0, UINT16_C(0xffff), SF | PF },
+ { UINT16_C(0x0001), UINT16_C(0x0001), 0, UINT16_C(0x0002), 0 },
+ };
+ static const CIDET2IN1OUTWITHFLAGSU32ENTRY s_a32Results[] =
+ {
+ { UINT32_C(0x00000000), UINT32_C(0x00000000), 0, UINT32_C(0x00000000), ZF | PF },
+ { UINT32_C(0xfefdfcfb), UINT32_C(0x01020305), 0, UINT32_C(0x00000000), CF | ZF | AF | PF },
+ { UINT32_C(0x8efdfcfb), UINT32_C(0x71020304), 0, UINT32_C(0xffffffff), SF | PF },
+ { UINT32_C(0x00000001), UINT32_C(0x00000001), 0, UINT32_C(0x00000002), 0 },
+ };
+ static const CIDET2IN1OUTWITHFLAGSU64ENTRY s_a64Results[] =
+ {
+ { UINT64_C(0x0000000000000000), UINT64_C(0x0000000000000000), 0, UINT64_C(0x0000000000000000), ZF | PF },
+ { UINT64_C(0xfefdfcfbfaf9f8f7), UINT64_C(0x0102030405060709), 0, UINT64_C(0x0000000000000000), CF | ZF | AF | PF },
+ { UINT64_C(0x7efdfcfbfaf9f8f7), UINT64_C(0x8102030405060708), 0, UINT64_C(0xffffffffffffffff), SF | PF },
+ { UINT64_C(0x0000000000000001), UINT64_C(0x0000000000000001), 0, UINT64_C(0x0000000000000002), 0 },
+ };
+ static const CIDET2IN1OUTWITHFLAGS s_Results = CIDET2IN1OUTWITHFLAGS_INITIALIZER(CF | PF | AF | SF | OF);
+ return CidetGenericIn2Out1WithFlags(pThis, fInvalid, &s_Results);
+}
+
+
+/** First bunch of instructions. */
+const CIDETINSTR g_aCidetInstructions1[] =
+{
+#if 1
+ {
+ "add Eb,Gb", cidetInOutAdd, 1, {0x00, 0, 0}, 0, 2,
+ { CIDET_OF_K_GPR | CIDET_OF_Z_BYTE | CIDET_OF_M_RM | CIDET_OF_A_RW,
+ CIDET_OF_K_GPR | CIDET_OF_Z_BYTE | CIDET_OF_M_REG | CIDET_OF_A_R,
+ 0, 0 }, CIDET_IF_MODRM
+ },
+#endif
+#if 1
+ {
+ "add Ev,Gv", cidetInOutAdd, 1, {0x01, 0, 0}, 0, 2,
+ { CIDET_OF_K_GPR | CIDET_OF_Z_VAR_WDQ | CIDET_OF_M_RM | CIDET_OF_A_RW,
+ CIDET_OF_K_GPR | CIDET_OF_Z_VAR_WDQ | CIDET_OF_M_REG | CIDET_OF_A_R,
+ 0, 0 }, CIDET_IF_MODRM
+ },
+#endif
+};
+/** Number of instruction in the g_aInstructions1 array. */
+const uint32_t g_cCidetInstructions1 = RT_ELEMENTS(g_aCidetInstructions1);
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet.h b/src/VBox/ValidationKit/utils/cpu/cidet.h
new file mode 100644
index 00000000..2273b7b0
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet.h
@@ -0,0 +1,1092 @@
+/* $Id: cidet.h $ */
+/** @file
+ * CPU Instruction Decoding & Execution Tests - C/C++ Header.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_cpu_cidet_h
+#define VBOX_INCLUDED_SRC_cpu_cidet_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <iprt/x86.h>
+
+
+/** @name CIDET - Operand flags.
+ * @{ */
+#define CIDET_OF_FIXED_MASK UINT32_C(0x0000001f) /**< Fixed register/whatever mask. */
+
+#define CIDET_OF_Z_SHIFT 8 /**< Size shift. */
+#define CIDET_OF_Z_MASK UINT32_C(0x00000f00) /**< Size mask. */
+#define CIDET_OF_Z_NONE UINT32_C(0x00000000) /**< Unused zero value. */
+#define CIDET_OF_Z_BYTE UINT32_C(0x00000100) /**< Byte size. */
+#define CIDET_OF_Z_WORD UINT32_C(0x00000200) /**< Word (2 bytes) size. */
+#define CIDET_OF_Z_DWORD UINT32_C(0x00000300) /**< Double word (4 bytes) size. */
+#define CIDET_OF_Z_QWORD UINT32_C(0x00000400) /**< Quad word (8 bytes) size. */
+#define CIDET_OF_Z_TBYTE UINT32_C(0x00000500) /**< Ten byte (10 bytes) size - aka TWORD. */
+#define CIDET_OF_Z_OWORD UINT32_C(0x00000600) /**< Octa word (16 bytes) size - aka DQWORD. */
+#define CIDET_OF_Z_YWORD UINT32_C(0x00000700) /**< Yxx sized, i.e. 32 bytes. */
+#define CIDET_OF_Z_ZWORD UINT32_C(0x00000800) /**< Zxx sized, i.e. 64 bytes. */
+#define CIDET_OF_Z_VAR_WDQ UINT32_C(0x00000900) /**< Variable size depending on size prefix (2, 4, or 8 bytes). */
+#define CIDET_OF_Z_SPECIAL UINT32_C(0x00000f00) /**< Special size, see instruction flags or smth. */
+
+#define CIDET_OF_K_MASK UINT32_C(0x0000f000) /**< Kind of operand. */
+#define CIDET_OF_K_NONE UINT32_C(0x00000000) /**< Unused zero value. */
+#define CIDET_OF_K_GPR UINT32_C(0x00001000) /**< General purpose register. Includes memory when used with CIDET_OF_M_RM. */
+#define CIDET_OF_K_SREG UINT32_C(0x00002000) /**< Segment register. */
+#define CIDET_OF_K_CR UINT32_C(0x00003000) /**< Control register. */
+#define CIDET_OF_K_SSE UINT32_C(0x00004000) /**< SSE register. */
+#define CIDET_OF_K_AVX UINT32_C(0x00005000) /**< AVX register. */
+#define CIDET_OF_K_AVX512 UINT32_C(0x00006000) /**< AVX-512 register. */
+#define CIDET_OF_K_AVXFUTURE UINT32_C(0x00007000) /**< Reserved for future AVX register set. */
+#define CIDET_OF_K_VRX_TST_MASK UINT32_C(0x0000c000) /**< Used for testing for VRX register kind, see CIDET_OF_K_IS_VRX. */
+#define CIDET_OF_K_VRX_TST_RES UINT32_C(0x00004000) /**< Used for testing for VRX register kind, see CIDET_OF_K_IS_VRX. */
+#define CIDET_OF_K_FPU UINT32_C(0x00008000) /**< FPU register. */
+#define CIDET_OF_K_MMX UINT32_C(0x00009000) /**< MMX register. */
+#define CIDET_OF_K_TEST UINT32_C(0x0000a000) /**< Test register. */
+#define CIDET_OF_K_IMM UINT32_C(0x0000d000) /**< Immediate. */
+#define CIDET_OF_K_MEM UINT32_C(0x0000e000) /**< Memory. */
+#define CIDET_OF_K_SPECIAL UINT32_C(0x0000f000) /**< Special. */
+/** Check if @a a_fOp is a general purpose register. */
+#define CIDET_OF_K_IS_GPR(a_fOp) ( ((a_fOp) & CIDET_OF_K_MASK) == CIDET_OF_K_GPR )
+/** Check if @a a_fOp is a XMM (SSE), YMM (AVX), ZMM (AVX-512) or similar register. */
+#define CIDET_OF_K_IS_VRX(a_fOp) ( ((a_fOp) & CIDET_OF_K_VRX_TST_MASK) == CIDET_OF_K_VRX_TST_RES )
+/** Check if @a a_fOp1 and @a a_fOp2 specify the same kind of register,
+ * treating SSE, AVX, AVX-512 and AVX-future as the same kind and ignoring the
+ * special register kind. */
+#define CIDET_OF_K_IS_SAME(a_fOp1, a_fOp2) \
+ ( ((a_fOp1) & CIDET_OF_K_MASK) == ((a_fOp2) & CIDET_OF_K_MASK) \
+ ? ((a_fOp1) & CIDET_OF_K_MASK) != CIDET_OF_K_SPECIAL \
+ : (CIDET_OF_K_IS_VRX(a_fOp1) && CIDET_OF_K_IS_VRX(a_fOp2)) )
+
+#define CIDET_OF_M_RM_ONLY_R UINT32_C(0x00010000)
+#define CIDET_OF_M_RM_ONLY_M UINT32_C(0x00020000)
+#define CIDET_OF_M_RM (CIDET_OF_M_RM_ONLY_R | CIDET_OF_M_RM_ONLY_M)
+#define CIDET_OF_M_REG UINT32_C(0x00040000)
+
+#define CIDET_OF_A_R UINT32_C(0x00080000) /**< Read access. */
+#define CIDET_OF_A_W UINT32_C(0x00100000) /**< Write access. */
+#define CIDET_OF_A_RW UINT32_C(0x00180000) /**< Read & write access. */
+
+/** The operand defaults to 64-bit width in 64-bit mode, making 32-bit width
+ * inaccessible. */
+#define CIDET_OF_DEFAULT_64BIT UINT32_C(0x40000000)
+/** Operand always uses the ES segment for memory accesses. */
+#define CIDET_OF_ALWAYS_SEG_ES UINT32_C(0x80000000)
+/** @} */
+
+
+/** @name CIDET - Instruction flags.
+ * @{ */
+#define CIDET_IF_MODRM RT_BIT_64(0) /**< ModR/M encoded. */
+#define CIDET_IF_PRIVILEGED RT_BIT_64(1) /**< Privileged. */
+/** @} */
+
+
+/**
+ * Callback function for setting up the input and expected output CPU contexts.
+ *
+ * @returns VBox status code.
+ * @retval VINF_EOF when static test data wraps (first entry is returned).
+ * @retval VERR_NO_DATA if @a fInvalid is set and there are no invalid operand
+ * values for this instruction.
+ * @retval VERR_NOT_SUPPORTED if something in the setup prevents us from
+ * comming up with working set of inputs and outputs.
+ *
+ * @param pThis The core CIDET state structure. The InCtx
+ * and ExpectedCtx members will be modified.
+ * @param fInvalid When set, get the next invalid operands that will
+ * cause exceptions/faults.
+ */
+typedef DECLCALLBACKTYPE(int, FNCIDETSETUPINOUT,(struct CIDETCORE *pThis, bool fInvalid));
+/** Pointer to a FNCIDETSETUPINOUT function. */
+typedef FNCIDETSETUPINOUT *PFNCIDETSETUPINOUT;
+
+
+/**
+ * Instruction test descriptor.
+ */
+typedef struct CIDETINSTR
+{
+ /** The mnemonic (kind of). */
+ const char *pszMnemonic;
+ /** Setup input and outputs. */
+ PFNCIDETSETUPINOUT pfnSetupInOut;
+ /** Number of opcode bytes. */
+ uint8_t cbOpcode;
+ /** Opcode byte(s). */
+ uint8_t abOpcode[3];
+ /** Mandatory prefix (zero if not applicable). */
+ uint8_t bMandatoryPrefix;
+ /** Number of operands. */
+ uint8_t cOperands;
+ /** Operand flags. */
+ uint32_t afOperands[4];
+ /** Flags. */
+ uint64_t fFlags;
+} CIDETINSTR;
+/** Pointer to an instruction test descriptor. */
+typedef CIDETINSTR const *PCCIDETINSTR;
+
+
+/**
+ * CPU Context with a few extra bits for expectations and results.
+ */
+typedef struct CIDETCPUCTX
+{
+ uint64_t rip;
+ uint64_t rfl;
+ uint64_t aGRegs[16];
+ uint16_t aSRegs[6];
+
+#ifndef CIDET_REDUCED_CTX
+ uint16_t tr;
+ uint16_t ldtr;
+ uint64_t cr0;
+#else
+ uint16_t au16Padding[2];
+#endif
+ uint64_t cr2;
+#ifndef CIDET_REDUCED_CTX
+ uint64_t cr3;
+ uint64_t cr4;
+ uint64_t cr8;
+ uint64_t dr0;
+ uint64_t dr1;
+ uint64_t dr2;
+ uint64_t dr3;
+ uint64_t dr6;
+ uint64_t dr7;
+#endif
+
+ uint64_t uErr; /**< Exception error code. UINT64_MAX if not applicable. (Not for input context.) */
+ uint32_t uXcpt; /**< Exception number. UINT32_MAX if no exception. (Not for input context.) */
+
+ uint32_t fIgnoredRFlags; /**< Only for expected result. */
+ bool fTrickyStack; /**< Set if the stack might be bad. May come at the cost of accurate flags (32-bit). */
+} CIDETCPUCTX;
+typedef CIDETCPUCTX *PCIDETCPUCTX;
+typedef CIDETCPUCTX const *PCCIDETCPUCTX;
+
+/** Number of bytes of CIDETCPUCTX that can be compared quickly using memcmp.
+ * Anything following these bytes are not relevant to the compare. */
+#define CIDETCPUCTX_COMPARE_SIZE RT_UOFFSETOF(CIDETCPUCTX, fIgnoredRFlags)
+
+
+/** @name CPU mode + bits + environment.
+ * @{ */
+#define CIDETMODE_BIT_MASK UINT8_C(0x0e) /**< The instruction bit count. Results in byte size when masked. */
+#define CIDETMODE_BIT_16 UINT8_C(0x02) /**< 16-bit instructions. */
+#define CIDETMODE_BIT_32 UINT8_C(0x04) /**< 32-bit instructions. */
+#define CIDETMODE_BIT_64 UINT8_C(0x08) /**< 64-bit instructions. */
+#define CIDETMODE_MODE_MASK UINT8_C(0x70) /**< CPU mode mask. */
+#define CIDETMODE_MODE_RM UINT8_C(0x00) /**< Real mode. */
+#define CIDETMODE_MODE_PE UINT8_C(0x10) /**< Protected mode without paging. */
+#define CIDETMODE_MODE_PP UINT8_C(0x20) /**< Paged protected mode. */
+#define CIDETMODE_MODE_PAE UINT8_C(0x30) /**< PAE protected mode (paged). */
+#define CIDETMODE_MODE_LM UINT8_C(0x40) /**< Long mode (paged). */
+#define CIDETMODE_ENV_MASK UINT8_C(0x81) /**< Execution environment. */
+#define CIDETMODE_ENV_NORMAL UINT8_C(0x01) /**< Normal environment. */
+#define CIDETMODE_ENV_V86 UINT8_C(0x80) /**< V8086 environment. */
+#define CIDETMODE_RM (CIDETMODE_MODE_RM | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PE_16 (CIDETMODE_MODE_PE | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PE_32 (CIDETMODE_MODE_PE | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PE_V86 (CIDETMODE_MODE_PE | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86)
+#define CIDETMODE_PP_16 (CIDETMODE_MODE_PP | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PP_32 (CIDETMODE_MODE_PP | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PP_V86 (CIDETMODE_MODE_PP | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86)
+#define CIDETMODE_PAE_16 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PAE_32 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_PAE_V86 (CIDETMODE_MODE_PAE | CIDETMODE_BIT_16 | CIDETMODE_ENV_V86)
+#define CIDETMODE_LM_16 (CIDETMODE_MODE_LM | CIDETMODE_BIT_16 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_LM_32 (CIDETMODE_MODE_LM | CIDETMODE_BIT_32 | CIDETMODE_ENV_NORMAL)
+#define CIDETMODE_LM_64 (CIDETMODE_MODE_LM | CIDETMODE_BIT_64 | CIDETMODE_ENV_NORMAL)
+/** Test if @a a_bMode is a 16-bit mode. */
+#define CIDETMODE_IS_16BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_16 )
+/** Test if @a a_bMode is a 32-bit mode. */
+#define CIDETMODE_IS_32BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_32 )
+/** Test if @a a_bMode is a 64-bit mode. */
+#define CIDETMODE_IS_64BIT(a_bMode) ( ((a_bMode) & CIDETMODE_BIT_MASK) == CIDETMODE_BIT_64 )
+/** Get the instruction bit count. */
+#define CIDETMODE_GET_BIT_COUNT(a_bMode) ( CIDETMODE_GET_BYTE_COUNT(a_bMode) * 8 )
+/** Get the instruction byte count. */
+#define CIDETMODE_GET_BYTE_COUNT(a_bMode) ( (a_bMode) & CIDETMODE_BIT_MASK )
+/** Test if @a a_bMode long mode. */
+#define CIDETMODE_IS_LM(a_bMode) ( ((a_bMode) & CIDETMODE_MODE_MASK) == CIDETMODE_MODE_LM )
+/** Test if @a a_bMode some kind of protected mode. */
+#define CIDETMODE_IS_PROT(a_bMode) ( ((a_bMode) & CIDETMODE_MODE_MASK) >= CIDETMODE_MODE_PE )
+
+/** @} */
+
+
+/** @name Test Configuration Flags.
+ * @{ */
+#define CIDET_TESTCFG_SEG_PRF_CS UINT64_C(0x0000000000000001)
+#define CIDET_TESTCFG_SEG_PRF_SS UINT64_C(0x0000000000000002)
+#define CIDET_TESTCFG_SEG_PRF_DS UINT64_C(0x0000000000000004)
+#define CIDET_TESTCFG_SEG_PRF_ES UINT64_C(0x0000000000000008)
+#define CIDET_TESTCFG_SEG_PRF_FS UINT64_C(0x0000000000000010)
+#define CIDET_TESTCFG_SEG_PRF_GS UINT64_C(0x0000000000000020)
+#define CIDET_TESTCFG_SEG_PRF_MASK UINT64_C(0x000000000000003f)
+/** @} */
+
+/** */
+typedef enum CIDETREG
+{
+ kCidetReg_Gpr_Invalid = 0,
+
+ kCidetReg_Gpr_al,
+ kCidetReg_Gpr_cl,
+ kCidetReg_Gpr_dl,
+ kCidetReg_Gpr_bl,
+ kCidetReg_Gpr_spl,
+ kCidetReg_Gpr_bpl,
+ kCidetReg_Gpr_sil,
+ kCidetReg_Gpr_dil,
+ kCidetReg_Gpr_r8b,
+ kCidetReg_Gpr_r9b,
+ kCidetReg_Gpr_r10b,
+ kCidetReg_Gpr_r11b,
+ kCidetReg_Gpr_r12b,
+ kCidetReg_Gpr_r13b,
+ kCidetReg_Gpr_r14b,
+ kCidetReg_Gpr_r15b,
+ kCidetReg_Gpr_ah,
+ kCidetReg_Gpr_ch,
+ kCidetReg_Gpr_dh,
+ kCidetReg_Gpr_bh,
+#define kCidetReg_Gpr_Byte_First kCidetReg_Gpr_al
+#define kCidetReg_Gpr_Byte_First_Upper kCidetReg_Gpr_ah
+#define kCidetReg_Gpr_Byte_Last kCidetReg_Gpr_bh
+
+ kCidetReg_Gpr_ax,
+ kCidetReg_Gpr_cx,
+ kCidetReg_Gpr_dx,
+ kCidetReg_Gpr_bx,
+ kCidetReg_Gpr_sp,
+ kCidetReg_Gpr_bp,
+ kCidetReg_Gpr_si,
+ kCidetReg_Gpr_di,
+ kCidetReg_Gpr_r8w,
+ kCidetReg_Gpr_r9w,
+ kCidetReg_Gpr_r10w,
+ kCidetReg_Gpr_r11w,
+ kCidetReg_Gpr_r12w,
+ kCidetReg_Gpr_r13w,
+ kCidetReg_Gpr_r14w,
+ kCidetReg_Gpr_r15w,
+#define kCidetReg_Gpr_Word_First kCidetReg_Gpr_ax
+#define kCidetReg_Gpr_Word_Last kCidetReg_Gpr_r15w
+
+ kCidetReg_Gpr_eax,
+ kCidetReg_Gpr_ecx,
+ kCidetReg_Gpr_edx,
+ kCidetReg_Gpr_ebx,
+ kCidetReg_Gpr_esp,
+ kCidetReg_Gpr_ebp,
+ kCidetReg_Gpr_esi,
+ kCidetReg_Gpr_edi,
+ kCidetReg_Gpr_r8d,
+ kCidetReg_Gpr_r9d,
+ kCidetReg_Gpr_r10d,
+ kCidetReg_Gpr_r11d,
+ kCidetReg_Gpr_r12d,
+ kCidetReg_Gpr_r13d,
+ kCidetReg_Gpr_r14d,
+ kCidetReg_Gpr_r15d,
+#define kCidetReg_Gpr_DWord_First kCidetReg_Gpr_eax
+#define kCidetReg_Gpr_DWord_Last kCidetReg_Gpr_r15d
+
+ kCidetReg_Gpr_rax,
+ kCidetReg_Gpr_rcx,
+ kCidetReg_Gpr_rdx,
+ kCidetReg_Gpr_rbx,
+ kCidetReg_Gpr_rsp,
+ kCidetReg_Gpr_rbp,
+ kCidetReg_Gpr_rsi,
+ kCidetReg_Gpr_rdi,
+ kCidetReg_Gpr_r8,
+ kCidetReg_Gpr_r9,
+ kCidetReg_Gpr_r10,
+ kCidetReg_Gpr_r11,
+ kCidetReg_Gpr_r12,
+ kCidetReg_Gpr_r13,
+ kCidetReg_Gpr_r14,
+ kCidetReg_Gpr_r15,
+#define kCidetReg_Gpr_QWord_First kCidetReg_Gpr_rax
+#define kCidetReg_Gpr_QWord_Last kCidetReg_Gpr_r15
+
+ kCidetReg_Seg_es,
+ kCidetReg_Seg_cs,
+ kCidetReg_Seg_ss,
+ kCidetReg_Seg_ds,
+ kCidetReg_Seg_fs,
+ kCidetReg_Seg_gs,
+ kCidetReg_Seg_Inv6,
+ kCidetReg_Seg_Inv7,
+#define kCidetReg_Seg_First kCidetReg_Seg_es
+#define kCidetReg_Seg_Last kCidetReg_Seg_gs
+#define kCidetReg_Seg_Last_Inv kCidetReg_Seg_Inv7
+
+ kCidetReg_Misc_ip,
+ kCidetReg_Misc_eip,
+ kCidetReg_Misc_rip,
+ kCidetReg_Misc_flags,
+ kCidetReg_Misc_eflags,
+ kCidetReg_Misc_rflags,
+ kCidetReg_Misc_tr,
+ kCidetReg_Misc_ldtr,
+ kCidetReg_Misc_gdtr,
+ kCidetReg_Misc_idtr,
+
+ kCidetReg_Ctrl_cr0,
+ kCidetReg_Ctrl_cr1,
+ kCidetReg_Ctrl_cr2,
+ kCidetReg_Ctrl_cr3,
+ kCidetReg_Ctrl_cr4,
+ kCidetReg_Ctrl_cr5,
+ kCidetReg_Ctrl_cr6,
+ kCidetReg_Ctrl_cr7,
+ kCidetReg_Ctrl_cr8,
+ kCidetReg_Ctrl_cr9,
+ kCidetReg_Ctrl_cr10,
+ kCidetReg_Ctrl_cr11,
+ kCidetReg_Ctrl_cr12,
+ kCidetReg_Ctrl_cr13,
+ kCidetReg_Ctrl_cr14,
+ kCidetReg_Ctrl_cr15,
+#define kCidetReg_Ctrl_First kCidetReg_Ctrl_cr0
+#define kCidetReg_Ctrl_Last kCidetReg_Ctrl_cr15
+#define CIDETREG_CTRL_IS_VALID(a_iReg) ( (a_iReg) == kCidetReg_Ctrl_cr0 \
+ && (a_iReg) == kCidetReg_Ctrl_cr2 \
+ && (a_iReg) == kCidetReg_Ctrl_cr3 \
+ && (a_iReg) == kCidetReg_Ctrl_cr4 \
+ && (a_iReg) == kCidetReg_Ctrl_cr8 )
+
+ kCidetReg_Dbg_dr0,
+ kCidetReg_Dbg_dr1,
+ kCidetReg_Dbg_dr2,
+ kCidetReg_Dbg_dr3,
+ kCidetReg_Dbg_dr4,
+ kCidetReg_Dbg_dr5,
+ kCidetReg_Dbg_dr6,
+ kCidetReg_Dbg_dr7,
+ kCidetReg_Dbg_dr8,
+ kCidetReg_Dbg_dr9,
+ kCidetReg_Dbg_dr10,
+ kCidetReg_Dbg_dr11,
+ kCidetReg_Dbg_dr12,
+ kCidetReg_Dbg_dr13,
+ kCidetReg_Dbg_dr14,
+ kCidetReg_Dbg_dr15,
+#define kCidetReg_Dbg_First kCidetReg_Dbg_dr0
+#define kCidetReg_Dbg_Last kCidetReg_Dbg_dr15
+#define CIDETREG_DBG_IS_VALID(a_iReg) ((a_iReg) < kCidetReg_Dbg_dr8 && (a_iReg) >= kCidetReg_Dbg_First)
+
+ kCidetReg_Test_tr0,
+ kCidetReg_Test_tr1,
+ kCidetReg_Test_tr2,
+ kCidetReg_Test_tr3,
+ kCidetReg_Test_tr4,
+ kCidetReg_Test_tr5,
+ kCidetReg_Test_tr6,
+ kCidetReg_Test_tr7,
+ kCidetReg_Test_tr8,
+ kCidetReg_Test_tr9,
+ kCidetReg_Test_tr10,
+ kCidetReg_Test_tr11,
+ kCidetReg_Test_tr12,
+ kCidetReg_Test_tr13,
+ kCidetReg_Test_tr14,
+ kCidetReg_Test_tr15,
+#define kCidetReg_Test_First kCidetReg_Test_tr0
+#define kCidetReg_Test_Last kCidetReg_Test_tr15
+
+ kCidetReg_Fpu_st0,
+ kCidetReg_Fpu_st1,
+ kCidetReg_Fpu_st2,
+ kCidetReg_Fpu_st3,
+ kCidetReg_Fpu_st4,
+ kCidetReg_Fpu_st5,
+ kCidetReg_Fpu_st6,
+ kCidetReg_Fpu_st7,
+#define kCidetReg_Fpu_First kCidetReg_Mmx_st0
+#define kCidetReg_Fpu_Last kCidetReg_Mmx_st7
+
+ kCidetReg_FpuMisc_cs,
+ kCidetReg_FpuMisc_ip,
+ kCidetReg_FpuMisc_ds,
+ kCidetReg_FpuMisc_dp,
+ kCidetReg_FpuMisc_fop,
+ kCidetReg_FpuMisc_ftw,
+ kCidetReg_FpuMisc_fsw,
+ kCidetReg_FpuMisc_fcw,
+ kCidetReg_FpuMisc_mxcsr_mask,
+ kCidetReg_FpuMisc_mxcsr,
+
+ kCidetReg_Mmx_mm0,
+ kCidetReg_Mmx_mm1,
+ kCidetReg_Mmx_mm2,
+ kCidetReg_Mmx_mm3,
+ kCidetReg_Mmx_mm4,
+ kCidetReg_Mmx_mm5,
+ kCidetReg_Mmx_mm6,
+ kCidetReg_Mmx_mm7,
+#define kCidetReg_Mmx_First kCidetReg_Mmx_mm0
+#define kCidetReg_Mmx_Last kCidetReg_Mmx_mm7
+
+ kCidetReg_Sse_xmm0,
+ kCidetReg_Sse_xmm1,
+ kCidetReg_Sse_xmm2,
+ kCidetReg_Sse_xmm3,
+ kCidetReg_Sse_xmm4,
+ kCidetReg_Sse_xmm5,
+ kCidetReg_Sse_xmm6,
+ kCidetReg_Sse_xmm7,
+ kCidetReg_Sse_xmm8,
+ kCidetReg_Sse_xmm9,
+ kCidetReg_Sse_xmm10,
+ kCidetReg_Sse_xmm11,
+ kCidetReg_Sse_xmm12,
+ kCidetReg_Sse_xmm13,
+ kCidetReg_Sse_xmm14,
+ kCidetReg_Sse_xmm15,
+ kCidetReg_Sse_xmm16,
+ kCidetReg_Sse_xmm17,
+ kCidetReg_Sse_xmm18,
+ kCidetReg_Sse_xmm19,
+ kCidetReg_Sse_xmm20,
+ kCidetReg_Sse_xmm21,
+ kCidetReg_Sse_xmm22,
+ kCidetReg_Sse_xmm23,
+ kCidetReg_Sse_xmm24,
+ kCidetReg_Sse_xmm25,
+ kCidetReg_Sse_xmm26,
+ kCidetReg_Sse_xmm27,
+ kCidetReg_Sse_xmm28,
+ kCidetReg_Sse_xmm29,
+ kCidetReg_Sse_xmm30,
+ kCidetReg_Sse_xmm31,
+#define kCidetReg_Sse_First kCidetReg_Mmx_Xmm0
+#define kCidetReg_Sse_Last kCidetReg_Mmx_Xmm15
+#define kCidetReg_Sse_Last_Avx512 kCidetReg_Mmx_Xmm31
+
+ kCidetReg_Avx_Ymm0,
+ kCidetReg_Avx_Ymm1,
+ kCidetReg_Avx_Ymm2,
+ kCidetReg_Avx_Ymm3,
+ kCidetReg_Avx_Ymm4,
+ kCidetReg_Avx_Ymm5,
+ kCidetReg_Avx_Ymm6,
+ kCidetReg_Avx_Ymm7,
+ kCidetReg_Avx_Ymm8,
+ kCidetReg_Avx_Ymm9,
+ kCidetReg_Avx_Ymm10,
+ kCidetReg_Avx_Ymm11,
+ kCidetReg_Avx_Ymm12,
+ kCidetReg_Avx_Ymm13,
+ kCidetReg_Avx_Ymm14,
+ kCidetReg_Avx_Ymm15,
+ kCidetReg_Avx_Ymm16,
+ kCidetReg_Avx_Ymm17,
+ kCidetReg_Avx_Ymm18,
+ kCidetReg_Avx_Ymm19,
+ kCidetReg_Avx_Ymm20,
+ kCidetReg_Avx_Ymm21,
+ kCidetReg_Avx_Ymm22,
+ kCidetReg_Avx_Ymm23,
+ kCidetReg_Avx_Ymm24,
+ kCidetReg_Avx_Ymm25,
+ kCidetReg_Avx_Ymm26,
+ kCidetReg_Avx_Ymm27,
+ kCidetReg_Avx_Ymm28,
+ kCidetReg_Avx_Ymm29,
+ kCidetReg_Avx_Ymm30,
+ kCidetReg_Avx_Ymm31,
+#define kCidetReg_Avx_First kCidetReg_Avx_Ymm0
+#define kCidetReg_Avx_Last kCidetReg_Avx_Ymm15
+#define kCidetReg_Avx_Last_Avx512 kCidetReg_Avx_Ymm31
+
+ kCidetReg_Avx512_Zmm0,
+ kCidetReg_Avx512_Zmm1,
+ kCidetReg_Avx512_Zmm2,
+ kCidetReg_Avx512_Zmm3,
+ kCidetReg_Avx512_Zmm4,
+ kCidetReg_Avx512_Zmm5,
+ kCidetReg_Avx512_Zmm6,
+ kCidetReg_Avx512_Zmm7,
+ kCidetReg_Avx512_Zmm8,
+ kCidetReg_Avx512_Zmm9,
+ kCidetReg_Avx512_Zmm10,
+ kCidetReg_Avx512_Zmm11,
+ kCidetReg_Avx512_Zmm12,
+ kCidetReg_Avx512_Zmm13,
+ kCidetReg_Avx512_Zmm14,
+ kCidetReg_Avx512_Zmm15,
+ kCidetReg_Avx512_Zmm16,
+ kCidetReg_Avx512_Zmm17,
+ kCidetReg_Avx512_Zmm18,
+ kCidetReg_Avx512_Zmm19,
+ kCidetReg_Avx512_Zmm20,
+ kCidetReg_Avx512_Zmm21,
+ kCidetReg_Avx512_Zmm22,
+ kCidetReg_Avx512_Zmm23,
+ kCidetReg_Avx512_Zmm24,
+ kCidetReg_Avx512_Zmm25,
+ kCidetReg_Avx512_Zmm26,
+ kCidetReg_Avx512_Zmm27,
+ kCidetReg_Avx512_Zmm28,
+ kCidetReg_Avx512_Zmm29,
+ kCidetReg_Avx512_Zmm30,
+ kCidetReg_Avx512_Zmm31,
+#define kCidetReg_Avx512_First kCidetReg_Avx512_Zmm0
+#define kCidetReg_Avx512_Last kCidetReg_Avx512_Zmm31
+
+ kCidetReg_End
+} CIDETREG;
+
+
+/** @name CIDETBUF_XXX - buffer flags.
+ * @{ */
+#define CIDETBUF_PROT_MASK UINT32_C(0x0000000f) /**< Page protection mask. */
+#define CIDETBUF_PROT_RWX UINT32_C(0x00000001) /**< Read + write + execute. */
+#define CIDETBUF_PROT_RWNX UINT32_C(0x00000002) /**< Read + write + no execute. */
+#define CIDETBUF_PROT_RX UINT32_C(0x00000003) /**< Read + execute. */
+#define CIDETBUF_PROT_RNX UINT32_C(0x00000004) /**< Read + no execute. */
+#define CIDETBUF_PROT_RWX_1NP UINT32_C(0x00000005) /**< Read + write + execute; 1 page not present. */
+#define CIDETBUF_PROT_RWX_1RWNX UINT32_C(0x00000006) /**< Read + write + execute; 1 page read + write + no execute. */
+#define CIDETBUF_PROT_RWX_1RNX UINT32_C(0x00000007) /**< Read + write + execute; 1 page read + no execute. */
+#define CIDETBUF_PROT_RWX_1RWXS UINT32_C(0x00000008) /**< Read + write + execute; 1 page read + execute + supervisor. */
+
+#define CIDETBUF_LOC_MASK UINT32_C(0x000000f0) /**< Location mask. */
+/** Buffer located at top and start of the 32-bit address space. */
+#define CIDETBUF_LOC_32BIT_WRAP UINT32_C(0x00000010)
+/** Buffer located at the low canonical boundrary (AMD64). */
+#define CIDETBUF_LOC_CANON_LO UINT32_C(0x00000020)
+/** Buffer located at the high canonical boundrary (AMD64). */
+#define CIDETBUF_LOC_CANON_HI UINT32_C(0x00000030)
+
+/** Segment protection mask. */
+#define CIDETBUF_SEG_MASK UINT32_C(0x00000f00)
+#define CIDETBUF_SEG_EO UINT32_C(0x00000100) /**< Execute only */
+#define CIDETBUF_SEG_ER UINT32_C(0x00000200) /**< Execute + read */
+#define CIDETBUF_SEG_EO_CONF UINT32_C(0x00000300) /**< Execute only + conforming. */
+#define CIDETBUF_SEG_ER_CONF UINT32_C(0x00000400) /**< Execute + read + conforming. */
+#define CIDETBUF_SEG_RO UINT32_C(0x00000500) /**< Read only. */
+#define CIDETBUF_SEG_RW UINT32_C(0x00000600) /**< Read + write. */
+#define CIDETBUF_SEG_RO_DOWN UINT32_C(0x00000700) /**< Read only + expand down. */
+#define CIDETBUF_SEG_RW_DOWN UINT32_C(0x00000800) /**< Read + write + expand down. */
+
+#define CIDETBUF_DPL_MASK UINT32_C(0x00003000) /**< DPL mask. */
+#define CIDETBUF_DPL_0 UINT32_C(0x00000000) /**< DPL=0. */
+#define CIDETBUF_DPL_1 UINT32_C(0x00001000) /**< DPL=1. */
+#define CIDETBUF_DPL_2 UINT32_C(0x00002000) /**< DPL=2. */
+#define CIDETBUF_DPL_3 UINT32_C(0x00003000) /**< DPL=3. */
+#define CIDETBUF_DPL_SAME UINT32_C(0x00004000) /**< Same DPL as the execution environment. */
+
+#define CIDETBUF_SEG_LIMIT_BASE_CAP UINT32_C(0x00008000) /**< Capability to change segment limit and base. */
+
+#define CIDETBUF_KIND_DATA UINT32_C(0x00000000) /**< Data buffer. */
+#define CIDETBUF_KIND_CODE UINT32_C(0x80000000) /**< Code buffer. */
+/** Checks if @a a_fFlags describes a code buffer. */
+#define CIDETBUF_IS_CODE(a_fFlags) (((a_fFlags) & CIDETBUF_KIND_CODE) != 0)
+/** Checks if @a a_fFlags describes a data buffer. */
+#define CIDETBUF_IS_DATA(a_fFlags) (((a_fFlags) & CIDETBUF_KIND_CODE) == 0)
+/** @} */
+
+/** Code buffer size. (At least two pages.) */
+#define CIDET_CODE_BUF_SIZE (PAGE_SIZE * 2)
+/** Data buffer size. (At least two pages.) */
+#define CIDET_DATA_BUF_SIZE (PAGE_SIZE * 3)
+
+
+/**
+ * Detailed expected exception.
+ *
+ * This is used to internally in the core to calculate the expected exception
+ * considering all the things that may cause exceptions.
+ */
+typedef enum CIDETEXPECTXCPT
+{
+ kCidetExpectXcpt_Invalid = 0,
+ /** No exception expected. */
+ kCidetExpectXcpt_None,
+
+ /** Page not present. */
+ kCidetExpectXcpt_PageNotPresent,
+ /** Write access to a non-writable page. */
+ kCidetExpectXcpt_PageNotWritable,
+ /** Executable access to a non-executable page. */
+ kCidetExpectXcpt_PageNotExecutable,
+ /** Access to supervisor page from user mode code. */
+ kCidetExpectXcpt_PagePrivileged,
+#define kCidetExpectXcpt_First_PageFault kCidetExpectXcpt_PageNotPresent
+#define kCidetExpectXcpt_Last_PageFault kCidetExpectXcpt_PagePrivileged
+
+ /** Read or write access to an execute only segment. */
+ kCidetExpectXcpt_SegExecuteOnly,
+ /** Write to a read only or execute+read segment. */
+ kCidetExpectXcpt_SegNotWritable,
+ /** Exceeded the limit of a non-stack access. */
+ kCidetExpectXcpt_SegExceededLimit,
+ /** Non-canonical address via any segment other than the stack. */
+ kCidetExpectXcpt_AddrNotCanonical,
+ /** Misaligned 16 or 32 byte SSE or AVX operand. */
+ kCidetExpectXcpt_MisalignedSseAvx,
+ /** Privileged instruction. */
+ kCidetExpectXcpt_PrivilegedInstruction,
+#define kCidetExpectXcpt_First_GeneralProtectionFault kCidetExpectXcpt_SegExecuteOnly
+#define kCidetExpectXcpt_Last_GeneralProtectionFault kCidetExpectXcpt_PrivilegedInstruction
+
+ /** Exceeded the limit of a stack access. */
+ kCidetExpectXcpt_StackExceededLimit,
+ /** Non-canonical stack address. */
+ kCidetExpectXcpt_StackAddrNotCanonical,
+#define kCidetExpectXcpt_First_StackFault kCidetExpectXcpt_StackExceededLimit
+#define kCidetExpectXcpt_Last_StackFault kCidetExpectXcpt_StackAddrNotCanonical
+
+ /** Misaligned memory operand (and alignment checking is in effect) if AC is
+ * enabled (executing in ring-3). */
+ kCidetExpectXcpt_MisalignedIfAcEnabled,
+ /** Misaligned 16 byte memory operand resulting in \#AC if ring-3 and
+ * enable, otherwise \#GP(0). */
+ kCidetExpectXcpt_Misaligned16ByteAcEnabledOrGp,
+#define kCidetExpectXcpt_First_AlignmentCheckFault kCidetExpectXcpt_MisalignedIfAcEnabled
+#define kCidetExpectXcpt_Last_AlignmentCheckFault kCidetExpectXcpt_Misaligned16ByteAcEnabledOrGp
+
+ kCidetExpectXcpt_End
+} CIDETEXPECTXCPT;
+
+
+/**
+ * Buffer configuration.
+ */
+typedef struct CIDETBUFCFG
+{
+ /** The name of this buffer configuration. */
+ const char *pszName;
+ /** The buffer flags (CIDETBUF_XXX) */
+ uint32_t fFlags;
+} CIDETBUFCFG;
+/** Pointer to a constant buffer configuration. */
+typedef CIDETBUFCFG const *PCCIDETBUFCFG;
+
+
+/**
+ * CIDET buffer for code or data.
+ *
+ * ASSUMES page aligned buffers.
+ */
+typedef struct CIDETBUF
+{
+ /** @name Owned & modified by the front end.
+ * @{ */
+ /** Effective buffer address. */
+ uint64_t uEffBufAddr;
+ /** The segment base address. */
+ uint64_t uSegBase;
+ /** The active segment limit (see also cbSegLimit). UINT64_MAX if flat. */
+ uint64_t cbActiveSegLimit;
+ /** This specifies the selector to use if a non-flat segment limit or special
+ * segment flags was requested via pfnSetupBuf. UINT32_MAX if any segment is
+ * selector works. */
+ uint32_t uSeg;
+ /** The off value at the last pfnReinitBuf call. */
+ uint16_t offActive;
+ /** The cb value at the last pfnReinitBuf call. */
+ uint16_t cbActive;
+ /** Prologue (or front fence) size. */
+ uint16_t cbPrologue;
+ /** Epilogue (or tail fence) size. */
+ uint16_t cbEpilogue;
+ /** @} */
+
+ /** @name Set by the core before pfnReinitBuf call.
+ * @{ */
+ /** Pointer to the buffer config. */
+ PCCIDETBUFCFG pCfg;
+ /** The configuration index. */
+ uint32_t idxCfg;
+ /** The offset into the buffer of the data / code. */
+ uint16_t off;
+ /** The number of bytes of data / code. */
+ uint16_t cb;
+ /** The segment limit relative to the start of the buffer (last byte included
+ * in count). UINT16_MAX if maximum segment size should be used. */
+ uint16_t cbSegLimit;
+ /** Desired segment base offset.
+ * This is for checking where the alignment checks are performed. */
+ uint8_t offSegBase;
+
+ /** Set if this buffer is actively being used. */
+ bool fActive : 1;
+ /** The operand index (if data), 7 if not active. */
+ uint8_t idxOp : 3;
+ /** Code: Set if the expected exception is supposed to occur on the
+ * following insturction, not the instruction unter test. */
+ bool fXcptAfterInstruction : 1;
+ /** Set if the instruction will read from the buffer. */
+ bool fRead : 1;
+ /** Set if the instruction will write to the buffer. */
+ bool fWrite : 1;
+ /** The expected exception. */
+ CIDETEXPECTXCPT enmExpectXcpt;
+ /** @} */
+} CIDETBUF;
+/** Pointer to a CIDET buffer for code or data. */
+typedef CIDETBUF *PCIDETBUF;
+
+
+/**
+ * CPU Instruction Decoding & Execution Testing (CIDET) state.
+ */
+typedef struct CIDETCORE
+{
+ /** Magic number (CIDETCORE_MAGIC). */
+ uint32_t u32Magic;
+
+ /** The target CPU mode / environment. */
+ uint8_t bMode;
+ /** The target ring. */
+ uint8_t iRing;
+ /** Unused padding bytes. */
+ uint8_t abPadding1[2];
+
+ /** Test configuration. */
+ uint64_t fTestCfg;
+
+ /** Code buffer configurations to test.
+ * The first buffer must be a normal buffer that does not cause any problems. */
+ PCCIDETBUFCFG paCodeBufConfigs;
+ /** The number of code buffer configurations to test (pafCodeBufConfigs). */
+ uint32_t cCodeBufConfigs;
+ /** The number of data buffer configurations to test (pafDataBufConfigs). */
+ uint32_t cDataBufConfigs;
+ /** Data buffer configurations to test.
+ * The first buffer must be a normal buffer that does not cause any problems. */
+ PCCIDETBUFCFG paDataBufConfigs;
+
+ /** The instruction currently under testing. */
+ PCCIDETINSTR pCurInstr;
+
+ /** Primary data buffer. */
+ CIDETBUF DataBuf;
+ /** Secondary data buffer. */
+ CIDETBUF DataBuf2;
+
+ /** Handle to the random number source. */
+ RTRAND hRand;
+
+ /**
+ * Re-initializes one of the data buffers.
+ *
+ * @returns true on succes, false if the request cannot be satisfied.
+ * @param pThis The core state.
+ * @param pBuf Pointer to the buffer structure.
+ */
+ DECLCALLBACKMEMBER(bool, pfnReInitDataBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf));
+
+ /**
+ * Copies bytes into the data buffer and sets it up for execution.
+ *
+ * @returns true on succes, false if the request cannot be satisfied.
+ * @param pThis The core state.
+ * @param pBuf Pointer to the buffer structure.
+ * @param pvSrc The source bytes (size and destination offset
+ * given in pfnReinitBuf call).
+ */
+ DECLCALLBACKMEMBER(bool, pfnSetupDataBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf, void const *pvSrc));
+
+ /**
+ * Compares buffer content after test execution.
+ *
+ * This also checks any fill bytes in the buffer that the front end may
+ * have put up. The front end will double buffer the content of supposedly
+ * inaccessible pages as well as non-existing pages to simplify things for
+ * the core code.
+ *
+ * @returns true if equal, false if not.
+ * @param pThis The core state.
+ * @param pBuf Pointer to the buffer structure.
+ * @param pvExpected Pointer to the expected source bytes (size and
+ * buffer offset given in pfnReinitBuf call).
+ */
+ DECLCALLBACKMEMBER(bool, pfnIsBufEqual,(struct CIDETCORE *pThis, struct CIDETBUF *pBuf, void const *pvExpected));
+
+ /**
+ * Re-initializes the code buffer.
+ *
+ * @returns true on succes, false if the request cannot be satisfied.
+ * @param pThis The core state.
+ * @param pBuf Pointer to the CodeBuf member. The off and cb
+ * members represent what the core wants to
+ * execute.
+ */
+ DECLCALLBACKMEMBER(bool, pfnReInitCodeBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf));
+
+ /**
+ * Emit code into the code buffer, making everything ready for pfnExecute.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the core structure.
+ * @param pBuf Pointer to the CodeBuf member.
+ * @param pvInstr Pointer to the encoded instruction bytes.
+ */
+ DECLCALLBACKMEMBER(bool, pfnSetupCodeBuf,(struct CIDETCORE *pThis, PCIDETBUF pBuf, void const *pvInstr));
+
+ /**
+ * Executes the code indicated by InCtx, returning the result in ActualCtx.
+ *
+ * @returns true if execute, false if skipped.
+ * @param pThis Pointer to the core structure.
+ */
+ DECLCALLBACKMEMBER(bool, pfnExecute,(struct CIDETCORE *pThis));
+
+ /**
+ * Report a test failure.
+ *
+ * @param pThis Pointer to the core structure.
+ * @param pszFormat Format string containing failure details.
+ * @param va Arguments referenced in @a pszFormat.
+ */
+ DECLCALLBACKMEMBER(void, pfnFailure,(struct CIDETCORE *pThis, const char *pszFormat, va_list va));
+
+ /** Array of indexes for use by FNCIDETSETUPINOUT.
+ * Reset when changing instruction or switching between valid and invalid
+ * inputs. */
+ uint32_t aiInOut[4];
+
+ /** @name Copyied and extracted instruction information.
+ * @{ */
+ /** The flags (CIDET_OF_XXX) for the MODRM.REG operand, 0 if not applicable. */
+ uint32_t fMrmRegOp;
+ /** The flags (CIDET_OF_XXX) for the MODRM.RM operand, 0 if not applicable. */
+ uint32_t fMrmRmOp;
+ /** Instruction flags (CIDETINSTR::fFlags). */
+ uint64_t fInstrFlags;
+ /** Number of operands (CIDETINSTR::cOperands). */
+ uint8_t cOperands;
+ /** Number of memory operands (set by CidetCoreSetupFirstMemoryOperandConfig). */
+ uint8_t cMemoryOperands : 3;
+ /** Set if we're working on a MOD R/M byte. */
+ bool fUsesModRm : 1;
+ /** The index of the MODRM.REG operand, 7 if not applicable. */
+ uint8_t idxMrmRegOp : 3;
+ /** The index of the MODRM.RM operand, 7 if not applicable. */
+ uint8_t idxMrmRmOp : 3;
+ /** Set if the SIB byte uses VEX registers for indexing. */
+ bool fUsesVexIndexRegs : 1;
+ /** @} */
+
+ /** @name Basic encoding knobs, wheels and indicators.
+ * @{ */
+ /** Set if we're working on a SIB byte. */
+ bool fSib : 1;
+ /** Required segment prefix (X86_SREG_XXX), X86_SREG_COUNT if not. */
+ uint8_t uSegPrf : 3;
+ /** The address size prefix. */
+ bool fAddrSizePrf : 1;
+ /** The operand size prefix. */
+ bool fOpSizePrf : 1;
+ /** The REX.W prefix value. */
+ bool fRexW : 1;
+ /** The REX.R prefix value. */
+ bool fRexR : 1;
+ /** The REX.X prefix value. */
+ bool fRexX : 1;
+ /** The REX.B prefix value. */
+ bool fRexB : 1;
+ /** Set if a REX prefix is required with or without flags (for byte regs). */
+ bool fRex : 1;
+ /** Use VEX encoding. */
+ bool fVex : 1;
+ /** Use EVEX encoding. */
+ bool fEvex : 1;
+ /** Indicator: Effective addressing mode in bytes (2, 4, 8). */
+ uint8_t cbAddrMode : 4;
+ /** Indicator: Set if there is an operand accessing memory. */
+ bool fHasMemoryOperand : 1;
+ /** Indicator: Set if a register is used in two or more operands, and one of
+ * them being for addressing. */
+ bool fHasRegCollisionMem : 1;
+ /** Indicator: Helper indicator for tracking SIB.BASE collision. */
+ bool fHasRegCollisionMemBase : 1;
+ /** Indicator: Helper indicator for tracking SIB.INDEX collision. */
+ bool fHasRegCollisionMemIndex : 1;
+ /** Indicator: Set if a register is used directly in more than one operand. */
+ bool fHasRegCollisionDirect : 1;
+
+ /** Indicator: Set if MODRM.REG is the stack register. */
+ bool fHasStackRegInMrmReg : 1;
+ /** Indicator: Set if MODRM.RM or SIB.BASE is the stack register. */
+ bool fHasStackRegInMrmRmBase: 1;
+
+ /** Indicator: High byte-register specified by MODRM.REG. */
+ bool fHasHighByteRegInMrmReg : 1;
+ /** Indicator: High byte-register specified by MODRM.RM. */
+ bool fHasHighByteRegInMrmRm : 1;
+ /** Indicator: Set if REX prefixes are incompatible with the byte-register
+ * specified by MODRM.REG. */
+ bool fNoRexPrefixMrmReg : 1;
+ /** Indicator: Set if REX prefixes are incompatible with the byte-register
+ * specified by MODRM.RM. */
+ bool fNoRexPrefixMrmRm : 1;
+ /** Indicator: fNoRexPrefixMrmReg || fNoRexPrefixMrmMr. */
+ bool fNoRexPrefix : 1;
+ /** The MOD R/M byte we're working on (if fUsesModRm is set). */
+ uint8_t bModRm;
+ /** The SIB/VSIB byte we're working on (if fSib is set). */
+ uint8_t bSib;
+ /** @} */
+
+ /** The effective instruction address. (See InCtx.rip and InCtx.cs for the
+ * rest of the instruction addressing stuff.) */
+ uint64_t uInstrEffAddr;
+
+ /** Operand information, mainly for the FNCIDETSETUPINOUT and similar. */
+ struct
+ {
+ /** The operand flags copied from (CIDETINSTR::afOperands). */
+ uint32_t fFlags;
+ /** The encoded register number, if register, UINT8_MAX if not. */
+ uint8_t iReg;
+ /** The actual operand size (encoded). */
+ uint8_t cb;
+ /** Set if immediate value. */
+ bool fIsImmediate : 1;
+ /** Set if memory access. */
+ bool fIsMem : 1;
+ /** Set if addressing is relative to RIP. */
+ bool fIsRipRelative : 1;
+ /** Set if it's a high byte register. */
+ bool fIsHighByteRegister : 1;
+ /** Size of the disposition, 0 if none. */
+ uint8_t cbMemDisp;
+ /** Base register, UINT8_MAX if not applicable. */
+ uint8_t iMemBaseReg;
+ /** Index register, UINT8_MAX if not applicable. */
+ uint8_t iMemIndexReg;
+ /** Index register, 1 if not applicable. */
+ uint8_t uMemScale;
+ /** Effective segment register, UINT8_MAX if not memory access. */
+ uint8_t iEffSeg;
+ /** Segment offset if memory access. Undefined if not memory access. */
+ uint64_t offSeg;
+ /** The effective address if memory access. */
+ uint64_t uEffAddr;
+ /** Immediate or displacement value. */
+ uint64_t uImmDispValue;
+ /** Base register value, undefined if irrelevant. */
+ uint64_t uMemBaseRegValue;
+ /** Index register value, undefined if irrelevant. */
+ uint64_t uMemIndexRegValue;
+ /** Points to where the input data for this operand should be placed,
+ * when possible. In the fIsMem = true case, it either points directly
+ * to the input buffer or to a temporary one. While in the other case,
+ * it'll point into InCtx when possible. */
+ RTPTRUNION In;
+ /** Points to where the expected output data for this operand should be
+ * stored, when possible. In the fIsMem = false case, it'll point into
+ * ExpectedCtx when possible. */
+ RTPTRUNION Expected;
+ /** Pointer to the data buffer for this operand. */
+ PCIDETBUF pDataBuf;
+ } aOperands[4];
+
+ /** Buffer where we assemble the instruction. */
+ uint8_t abInstr[45];
+ /** The size of the instruction in abInstr. */
+ uint8_t cbInstr;
+ /** Offset of the instruction into the buffer. */
+ uint16_t offInstr;
+ /** Current code buffer. */
+ CIDETBUF CodeBuf;
+
+ /** The input context. Initalized by driver and FNCIDETSETUPINOUT. */
+ CIDETCPUCTX InCtx;
+ /** The expected output context. */
+ CIDETCPUCTX ExpectedCtx;
+ /** The actual output context. */
+ CIDETCPUCTX ActualCtx;
+ /** Template input context, initialized when setting the mode. */
+ CIDETCPUCTX InTemplateCtx;
+
+ /** Input and expected output temporary memory buffers. */
+ uint8_t abBuf[0x2000];
+
+
+ /** Number of skipped tests because of pfnSetupInOut failures. */
+ uint32_t cSkippedSetupInOut;
+ /** Number of skipped tests because of pfnReInitDataBuf failures. */
+ uint32_t cSkippedReInitDataBuf;
+ /** Number of skipped tests because of pfnSetupDataBuf failures. */
+ uint32_t cSkippedSetupDataBuf;
+ /** Number of skipped tests because RIP relative addressing constraints. */
+ uint32_t cSkippedDataBufWrtRip;
+ /** Number of skipped tests because of assemble failures. */
+ uint32_t cSkippedAssemble;
+ /** Number of skipped tests because of pfnReInitCodeBuf failures. */
+ uint32_t cSkippedReInitCodeBuf;
+ /** Number of skipped tests because of pfnSetupCodeBuf failures. */
+ uint32_t cSkippedSetupCodeBuf;
+ /** Number of skipped tests because the base and index registers are the same
+ * one and there was a remainder when trying to point to the data buffer. */
+ uint32_t cSkippedSameBaseIndexRemainder;
+ /** Number of skipped tests because index-only addressing left a remainder. */
+ uint32_t cSkippedOnlyIndexRemainder;
+ /** Number of skipped tests because of direct addressing overflowed. */
+ uint32_t cSkippedDirectAddressingOverflow;
+
+
+} CIDETCORE;
+/** Pointer to the CIDET core state. */
+typedef CIDETCORE *PCIDETCORE;
+
+/** Magic number for CIDETCORE (Lee Konitz). */
+#define CIDETCORE_MAGIC UINT32_C(0x19271013)
+
+
+int CidetCoreInit(PCIDETCORE pThis, RTRAND hRand);
+void CidetCoreDelete(PCIDETCORE pThis);
+int CidetCoreSetTargetMode(PCIDETCORE pThis, uint8_t bMode);
+uint32_t CidetCoreGetOperandSize(PCIDETCORE pThis, uint8_t iOp);
+bool CidetCoreTestInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr);
+
+
+extern const CIDETINSTR g_aCidetInstructions1[];
+extern const uint32_t g_cCidetInstructions1;
+
+#endif /* !VBOX_INCLUDED_SRC_cpu_cidet_h */
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet.mac b/src/VBox/ValidationKit/utils/cpu/cidet.mac
new file mode 100644
index 00000000..d55333df
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cidet.mac
@@ -0,0 +1,75 @@
+; $Id: cidet.mac $ ;
+;; @file
+; CPU Instruction Decoding & Execution Tests - Assembly Header.
+;
+
+;
+; Copyright (C) 2014-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%ifndef ___cidet_mac___
+%define ___cidet_mac___
+
+struc CIDETCPUCTX
+ .rip resq 1
+ .rfl resq 1
+ .aGRegs resq 16
+ .aSRegs resw 6
+
+%ifndef CIDET_REDUCED_CTX
+ .tr resw 1
+ .ldtr resw 1
+ .cr0 resq 1
+%else
+ .au16Padding resw 2
+%endif
+ .cr2 resq 1
+%ifndef CIDET_REDUCED_CTX
+ .cr3 resq 1
+ .cr4 resq 1
+ .cr8 resq 1
+ .dr0 resq 1
+ .dr1 resq 1
+ .dr2 resq 1
+ .dr3 resq 1
+ .dr6 resq 1
+ .dr7 resq 1
+%endif
+
+ .uErr resq 1
+ .uXcpt resd 1
+
+ .fIgnoredRFlags resd 1
+ .fTrickyStack resb 1
+endstruc
+
+%endif
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp b/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp
new file mode 100644
index 00000000..889cb600
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cpu-alloc-all-mem.cpp
@@ -0,0 +1,223 @@
+/* $Id: cpu-alloc-all-mem.cpp $ */
+/** @file
+ * Allocate all memory we can get and then quit.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
+
+#include <iprt/asm.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct TSTALLOC
+{
+ /** The page sequence number. */
+ size_t iPageSeq;
+ /** The allocation sequence number. */
+ size_t iAllocSeq;
+ /** The allocation size. */
+ size_t cb;
+ /** Pointer to the ourselves (paranoid). */
+ void *pv;
+ /** Linked list node. */
+ RTLISTNODE Node;
+
+} TSTALLOC;
+typedef TSTALLOC *PTSTALLOC;
+
+
+static bool checkList(PRTLISTNODE pHead)
+{
+ size_t iPageSeq = 0;
+ size_t iAllocSeq = 0;
+ PTSTALLOC pCur;
+ RTListForEach(pHead, pCur, TSTALLOC, Node)
+ {
+ RTTESTI_CHECK_RET(pCur->iAllocSeq == iAllocSeq, false);
+ RTTESTI_CHECK_RET(pCur->pv == pCur, false);
+
+ size_t const *pu = (size_t const *)pCur;
+ size_t const *puEnd = pu + pCur->cb / sizeof(size_t);
+ while (pu != puEnd)
+ {
+ RTTESTI_CHECK_RET(*pu == iPageSeq, false);
+ iPageSeq++;
+ pu += PAGE_SIZE / sizeof(size_t);
+ }
+ iAllocSeq++;
+ }
+ return true;
+}
+
+
+static void doTest(RTTEST hTest)
+{
+ RTTestSub(hTest, "Allocate all memory");
+
+ RTLISTANCHOR AllocHead;
+ PTSTALLOC pCur;
+ uint64_t cNsElapsed = 0;
+ size_t cbPrint = 0;
+ uint64_t uPrintTS = 0;
+ size_t cbTotal = 0;
+#if ARCH_BITS == 64
+ size_t const cbOneStart = 64 * _1M;
+ size_t const cbOneMin = 4 * _1M;
+#else
+ size_t const cbOneStart = 16 * _1M;
+ size_t const cbOneMin = 4 * _1M;
+#endif
+ size_t cbOne = cbOneStart;
+ size_t cAllocs = 0;
+ uint32_t iPageSeq = 0;
+ RTListInit(&AllocHead);
+
+ for (;;)
+ {
+ /*
+ * Allocate a chunk and make sure all the pages are there.
+ */
+ uint64_t const uStartTS = RTTimeNanoTS();
+ pCur = (PTSTALLOC)RTMemPageAlloc(cbOne);
+ if (pCur)
+ {
+ size_t *pu = (size_t *)pCur;
+ size_t *puEnd = pu + cbOne / sizeof(size_t);
+ while (pu != puEnd)
+ {
+ *pu = iPageSeq++;
+ pu += PAGE_SIZE / sizeof(size_t);
+ }
+ uint64_t const uEndTS = RTTimeNanoTS();
+ uint64_t const cNsThis = uEndTS - uStartTS;
+
+ /*
+ * Update the statistics.
+ */
+ cNsElapsed += cNsThis;
+ cbTotal += cbOne;
+ cAllocs++;
+
+ /*
+ * Link the allocation.
+ */
+ pCur->iAllocSeq = cAllocs - 1;
+ pCur->pv = pCur;
+ pCur->cb = cbOne;
+ RTListAppend(&AllocHead, &pCur->Node);
+
+ /*
+ * Print progress info?
+ */
+ if ( uEndTS - uPrintTS >= RT_NS_1SEC_64*10
+#if ARCH_BITS == 64
+ || cbTotal - cbPrint >= _4G
+#else
+ || cbTotal - cbPrint >= _2G
+#endif
+ )
+ {
+ cbPrint = cbTotal;
+ uPrintTS = uEndTS;
+
+ uint32_t cMBPerSec = (uint32_t)((long double)cbTotal / ((long double)cNsElapsed / RT_NS_1SEC) / _1M);
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%'zu bytes in %'llu ns - %'u MB/s\n",
+ cbTotal, cNsElapsed, cMBPerSec);
+ RTTESTI_CHECK_RETV(checkList(&AllocHead));
+ }
+ }
+ else
+ {
+ /*
+ * Try again with a smaller request.
+ */
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Failed to allocate %'zu bytes (after %'zu bytes)\n", cbOne, cbTotal);
+ if (cbOne <= cbOneMin)
+ break;
+ cbOne = cbOneMin;
+ }
+ }
+
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Verifying...\n");
+ RTTESTI_CHECK_RETV(checkList(&AllocHead));
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "... detected no corruption.\n");
+
+ /*
+ * Free up some memory before displaying the results.
+ */
+ size_t i = 0;
+ PTSTALLOC pPrev;
+ RTListForEachReverseSafe(&AllocHead, pCur, pPrev, TSTALLOC, Node)
+ {
+ RTMemPageFree(pCur->pv, pCur->cb);
+ if (++i > 32)
+ break;
+ }
+
+ RTTestValue(hTest, "amount", cbTotal, RTTESTUNIT_BYTES);
+ RTTestValue(hTest, "time", cNsElapsed, RTTESTUNIT_NS);
+ uint32_t cMBPerSec = (uint32_t)((long double)cbTotal / ((long double)cNsElapsed / RT_NS_1SEC) / _1M);
+ RTTestValue(hTest, "speed", cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC);
+ RTTestSubDone(hTest);
+}
+
+
+int main(int argc, char **argv)
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("memallocall", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(hTest);
+
+ NOREF(argv);
+ if (argc == 1)
+ doTest(hTest);
+ else
+ RTTestFailed(hTest, "This test takes no arguments!");
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp b/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp
new file mode 100644
index 00000000..1331c3ce
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/cpu-numa.cpp
@@ -0,0 +1,205 @@
+/* $Id: cpu-numa.cpp $ */
+/** @file
+ * numa - NUMA / memory benchmark.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
+
+#include <iprt/asm.h>
+//#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+//# include <iprt/asm-amd64-x86.h>
+//#endif
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The number of threads to skip when testing. */
+static uint32_t g_cThreadsToSkip = 1;
+
+/**
+ * Gets the next online CPU.
+ *
+ * @returns Next CPU index or RTCPUSET_MAX_CPUS.
+ * @param iCurCpu The current CPU (index).
+ */
+static int getNextCpu(unsigned iCurCpu)
+{
+ /* Skip to the next chip. */
+ iCurCpu = (iCurCpu / g_cThreadsToSkip) * g_cThreadsToSkip;
+ iCurCpu += g_cThreadsToSkip;
+
+ /* Skip offline cpus. */
+ while ( iCurCpu < RTCPUSET_MAX_CPUS
+ && !RTMpIsCpuOnline(iCurCpu) )
+ iCurCpu++;
+
+ /* Make sure we're within bounds (in case of bad input). */
+ if (iCurCpu > RTCPUSET_MAX_CPUS)
+ iCurCpu = RTCPUSET_MAX_CPUS;
+ return iCurCpu;
+}
+
+
+static void doTest(RTTEST hTest)
+{
+ NOREF(hTest);
+ uint32_t iAllocCpu = 0;
+ while (iAllocCpu < RTCPUSET_MAX_CPUS)
+ {
+ const uint32_t cbTestSet = _1M * 32;
+ const uint32_t cIterations = 384;
+
+ /*
+ * Change CPU and allocate a chunk of memory.
+ */
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadSetAffinityToCpu(RTMpCpuIdFromSetIndex(iAllocCpu)));
+
+ void *pvTest = RTMemPageAlloc(cbTestSet); /* may be leaked, who cares */
+ RTTESTI_CHECK_RETV(pvTest != NULL);
+ memset(pvTest, 0xef, cbTestSet);
+
+ /*
+ * Do the tests.
+ */
+ uint32_t iAccessCpu = 0;
+ while (iAccessCpu < RTCPUSET_MAX_CPUS)
+ {
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadSetAffinityToCpu(RTMpCpuIdFromSetIndex(iAccessCpu)));
+
+ /*
+ * The write test.
+ */
+ RTTimeNanoTS(); RTThreadYield();
+ uint64_t u64StartTS = RTTimeNanoTS();
+ for (uint32_t i = 0; i < cIterations; i++)
+ {
+ ASMCompilerBarrier(); /* paranoia */
+ memset(pvTest, i, cbTestSet);
+ }
+ uint64_t const cNsElapsedWrite = RTTimeNanoTS() - u64StartTS;
+ uint64_t cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */
+ / ((long double)cNsElapsedWrite / RT_NS_1SEC_64) /* seconds */
+ / _1M /* MB */ );
+ RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-write", iAllocCpu, iAccessCpu);
+
+ /*
+ * The read test.
+ */
+ memset(pvTest, 0, cbTestSet);
+ RTTimeNanoTS(); RTThreadYield();
+ u64StartTS = RTTimeNanoTS();
+ for (uint32_t i = 0; i < cIterations; i++)
+ {
+#if 1
+ size_t u = 0;
+ size_t volatile *puCur = (size_t volatile *)pvTest;
+ size_t volatile *puEnd = puCur + cbTestSet / sizeof(size_t);
+ while (puCur != puEnd)
+ u += *puCur++;
+#else
+ ASMCompilerBarrier(); /* paranoia */
+ void *pvFound = memchr(pvTest, (i & 127) + 1, cbTestSet);
+ RTTESTI_CHECK(pvFound == NULL);
+#endif
+ }
+ uint64_t const cNsElapsedRead = RTTimeNanoTS() - u64StartTS;
+ cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */
+ / ((long double)cNsElapsedRead / RT_NS_1SEC_64) /* seconds */
+ / _1M /* MB */ );
+ RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-read", iAllocCpu, iAccessCpu);
+
+ /*
+ * The read/write test.
+ */
+ RTTimeNanoTS(); RTThreadYield();
+ u64StartTS = RTTimeNanoTS();
+ for (uint32_t i = 0; i < cIterations; i++)
+ {
+ ASMCompilerBarrier(); /* paranoia */
+ memcpy(pvTest, (uint8_t *)pvTest + cbTestSet / 2, cbTestSet / 2);
+ }
+ uint64_t const cNsElapsedRW = RTTimeNanoTS() - u64StartTS;
+ cMBPerSec = (uint64_t)( ((uint64_t)cIterations * cbTestSet) /* bytes */
+ / ((long double)cNsElapsedRW / RT_NS_1SEC_64) /* seconds */
+ / _1M /* MB */ );
+ RTTestIValueF(cMBPerSec, RTTESTUNIT_MEGABYTES_PER_SEC, "cpu%02u-mem%02u-read-write", iAllocCpu, iAccessCpu);
+
+ /*
+ * Total time.
+ */
+ RTTestIValueF(cNsElapsedRead + cNsElapsedWrite + cNsElapsedRW, RTTESTUNIT_NS,
+ "cpu%02u-mem%02u-time", iAllocCpu, iAccessCpu);
+
+ /* advance */
+ iAccessCpu = getNextCpu(iAccessCpu);
+ }
+
+ /*
+ * Clean up and advance to the next CPU.
+ */
+ RTMemPageFree(pvTest, cbTestSet);
+ iAllocCpu = getNextCpu(iAllocCpu);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("numa-1", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(hTest);
+
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ /** @todo figure basic topology. */
+#endif
+ if (argc == 2)
+ g_cThreadsToSkip = RTStrToUInt8(argv[1]);
+
+ doTest(hTest);
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm b/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm
new file mode 100644
index 00000000..d96194ad
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/exceptionsR3-asm.asm
@@ -0,0 +1,160 @@
+; $Id: exceptionsR3-asm.asm $
+;; @file
+; exceptionsR3-asm.asm - assembly helpers.
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*******************************************************************************
+;* Header Files *
+;*******************************************************************************
+%include "iprt/asmdefs.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%ifdef RT_ARCH_AMD64
+ %define TST_XCPT_MAGIC 0123456789abcdef0h
+%else
+ %define TST_XCPT_MAGIC 012345678h
+%endif
+
+%macro tstXcptAsmProlog 0
+ push xBP
+ push xDI
+ push xSI
+ push xBX
+ %ifdef RT_ARCH_X86
+ push gs
+ push fs
+ push es
+ push ds
+ %endif
+ %ifdef RT_ARCH_AMD64
+ push r10
+ push r11
+ push r12
+ push r13
+ push r14
+ push r15
+ %endif
+
+ mov xAX, TST_XCPT_MAGIC
+ mov xBX, xAX
+ mov xCX, xAX
+ mov xDX, xAX
+ mov xDI, xAX
+ mov xSI, xAX
+ mov xBP, xAX
+ %ifdef RT_ARCH_AMD64
+ mov r8, xAX
+ mov r9, xAX
+ mov r10, xAX
+ mov r11, xAX
+ mov r12, xAX
+ mov r13, xAX
+ mov r14, xAX
+ mov r15, xAX
+ %endif
+%endmacro
+
+%macro tstXcptAsmEpilog 0
+ %ifdef RT_ARCH_AMD64
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop r11
+ pop r10
+ %endif
+ %ifdef RT_ARCH_X86
+ pop ds
+ pop es
+ pop fs
+ pop gs
+ %endif
+ pop xBX
+ pop xSI
+ pop xDI
+ pop xBP
+%endmacro
+
+
+BEGINCODE
+
+;;
+BEGINPROC tstXcptAsmNullPtrRead
+; tstXcptAsmProlog
+ xor eax, eax
+GLOBALNAME tstXcptAsmNullPtrRead_PC
+ mov al, [xAX]
+; tstXcptAsmEpilog
+ ret
+ENDPROC tstXcptAsmNullPtrRead
+
+
+;;
+BEGINPROC tstXcptAsmNullPtrWrite
+ tstXcptAsmProlog
+ xor eax, eax
+GLOBALNAME tstXcptAsmNullPtrWrite_PC
+ mov [xAX], al
+ tstXcptAsmEpilog
+ ret
+ENDPROC tstXcptAsmNullPtrWrite
+
+
+;;
+BEGINPROC tstXcptAsmSysCall
+ tstXcptAsmProlog
+GLOBALNAME tstXcptAsmSysCall_PC
+ syscall
+ tstXcptAsmEpilog
+ ret
+ENDPROC tstXcptAsmSysCall
+
+
+;;
+BEGINPROC tstXcptAsmSysEnter
+ tstXcptAsmProlog
+GLOBALNAME tstXcptAsmSysEnter_PC
+%ifdef RT_ARCH_AMD64
+ db 00fh, 034h ; test this on 64-bit, yasm complains...
+%else
+ sysenter
+%endif
+ tstXcptAsmEpilog
+ ret
+ENDPROC tstXcptAsmSysEnter
+
diff --git a/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp b/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp
new file mode 100644
index 00000000..474e0eeb
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/exceptionsR3.cpp
@@ -0,0 +1,272 @@
+/* $Id: exceptionsR3.cpp $ */
+/** @file
+ * exceptionsR3 - Tests various ring-3 CPU exceptions.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/ctype.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/x86.h>
+
+#include <setjmp.h>
+
+#ifndef RT_OS_WINDOWS
+# define USE_SIGNALS
+# include <signal.h>
+# include <stdlib.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Executes a simple test. */
+#define TST_XCPT(Trapper, iTrap, uErr) \
+ do \
+ { \
+ RTTestISub(#Trapper); \
+ tstXcptReset(); \
+ if (!setjmp(g_JmpBuf)) \
+ { \
+ tstXcptAsm##Trapper(); \
+ RTTestIFailed("%s didn't trap (line no %u)", #Trapper, __LINE__); \
+ } \
+ else if ( (iTrap) != tstXcptCurTrap() \
+ || (uErr) != tstXcptCurErr() ) \
+ RTTestIFailed("%s trapped with %#x/%#x, expected %#x/%#x (line no %u)", \
+ #Trapper, tstXcptCurTrap(), tstXcptCurErr(), (iTrap), (uErr), __LINE__); \
+ else \
+ RTTestISubDone(); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Where to longjmp to when getting a signal/exception. */
+jmp_buf g_JmpBuf;
+#ifdef USE_SIGNALS
+/** Pending signal.
+ * -1 if no signal is pending. */
+int32_t volatile g_iSignal;
+/** Pending signal info. */
+siginfo_t volatile g_SigInfo;
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLASM(void) tstXcptAsmNullPtrRead(void);
+DECLASM(void) tstXcptAsmNullPtrWrite(void);
+DECLASM(void) tstXcptAsmSysEnter(void);
+DECLASM(void) tstXcptAsmSysCall(void);
+
+
+
+#ifdef USE_SIGNALS
+/**
+ * Generic signal handler.
+ */
+static void tstXcptSigHandler(int iSignal, siginfo_t *pSigInfo, void *pvCtx)
+{
+#if 1
+ RTStrmPrintf(g_pStdErr, "signal %d pSigInfo=%p pvCtx=%p", iSignal, pSigInfo, pvCtx);
+ if (pSigInfo)
+ RTStrmPrintf(g_pStdErr, " si_addr=%p si_code=%#x sival_ptr=%p sival_int=%d",
+ pSigInfo->si_addr, pSigInfo->si_code, pSigInfo->si_value.sival_ptr, pSigInfo->si_value.sival_int);
+ RTStrmPrintf(g_pStdErr, "\n");
+#endif
+ if (g_iSignal == -1)
+ {
+ g_iSignal = iSignal;
+ if (pSigInfo)
+ memcpy((void *)&g_SigInfo, pSigInfo, sizeof(g_SigInfo));
+ longjmp(g_JmpBuf, 1);
+ }
+ else
+ {
+ /* we're up the infamous creek... */
+ _Exit(2);
+ }
+}
+
+#elif defined(RT_OS_WINDOWS)
+/** @todo */
+//# error "PORTME"
+
+#else
+# error "PORTME"
+#endif
+
+
+/** Reset the current exception state and get ready for a new trap. */
+static void tstXcptReset(void)
+{
+#ifdef USE_SIGNALS
+ g_iSignal = -1;
+ memset((void *)&g_SigInfo, 0, sizeof(g_SigInfo));
+#endif
+}
+
+
+
+/** Get the current intel trap number. Returns -1 if none. */
+static int tstXcptCurTrap(void)
+{
+#ifdef USE_SIGNALS
+ /** @todo this is just a quick sketch. */
+ switch (g_iSignal)
+ {
+ case SIGBUS:
+# ifdef RT_OS_DARWIN
+ if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/)
+ return X86_XCPT_PF;
+# endif
+ return X86_XCPT_GP;
+
+ case SIGSEGV:
+ return X86_XCPT_GP;
+ }
+#endif
+ return -1;
+}
+
+
+/** Get the exception error code if applicable. */
+static uint32_t tstXcptCurErr(void)
+{
+#ifdef USE_SIGNALS
+ /** @todo this is just a quick sketch. */
+ switch (g_iSignal)
+ {
+ case SIGBUS:
+# ifdef RT_OS_DARWIN
+ if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/)
+ return 0;
+# endif
+ break;
+
+ case SIGSEGV:
+ break;
+ }
+#endif
+ return UINT32_MAX;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Prolog.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("exceptionsR3", &hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Parse options.
+ */
+ bool volatile fRawMode = false;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--raw-mode", 'r', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGETOPTUNION ValUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((rc = RTGetOpt(&GetState, &ValUnion)))
+ {
+ switch (rc)
+ {
+ case 'r':
+ fRawMode = true;
+ break;
+
+ default:
+ return RTGetOptPrintError(rc, &ValUnion);
+ }
+ }
+
+ /*
+ * Test setup.
+ */
+#ifdef USE_SIGNALS
+ struct sigaction Act;
+ RT_ZERO(Act);
+ Act.sa_sigaction = tstXcptSigHandler;
+ Act.sa_flags = SA_SIGINFO;
+ sigfillset(&Act.sa_mask);
+
+ sigaction(SIGILL, &Act, NULL);
+ sigaction(SIGTRAP, &Act, NULL);
+# ifdef SIGEMT
+ sigaction(SIGEMT, &Act, NULL);
+# endif
+ sigaction(SIGFPE, &Act, NULL);
+ sigaction(SIGBUS, &Act, NULL);
+ sigaction(SIGSEGV, &Act, NULL);
+
+#else
+ /** @todo Implement this using structured exception handling on Windows and
+ * OS/2. */
+#endif
+
+ /*
+ * The tests.
+ */
+ RTTestBanner(hTest);
+ TST_XCPT(NullPtrRead, X86_XCPT_PF, 0);
+ TST_XCPT(NullPtrWrite, X86_XCPT_PF, 0);
+ if (fRawMode)
+ {
+ TST_XCPT(SysEnter, X86_XCPT_GP, 0);
+ TST_XCPT(SysCall, X86_XCPT_UD, 0);
+ }
+
+ /*
+ * Epilog.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm b/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm
new file mode 100644
index 00000000..751c8808
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/rdtsc-asm.asm
@@ -0,0 +1,162 @@
+; $Id: rdtsc-asm.asm $
+;; @file
+; RDTSC test, assembly code
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;*********************************************************************************************************************************
+;* Global Variables *
+;*********************************************************************************************************************************
+BEGINDATA
+;;
+; Where DoTscReads() returns the rdtsc values.
+;
+; @note The results are 32-bit value pairs in x86 mode and 64-bit pairs in
+; AMD64 mode.
+GLOBALNAME g_aRdTscResults
+%ifdef RT_ARCH_AMD64
+ dq 0, 0
+ dq 0, 0 ; first value stored
+ dq 0, 0
+ dq 0, 0
+ dq 0, 0
+ dq 0, 0
+ dq 0, 0
+%else
+ dq 0, 0
+ dd 0, 0 ; first value stored
+ dd 0, 0
+ dd 0, 0
+%endif
+
+
+BEGINCODE
+
+;; Takes no arguments, returns number of values read into g_aRdTscResults.
+BEGINPROC DoTscReads
+ push xBP
+ mov xBP, xSP
+%ifdef RT_ARCH_AMD64
+ mov rax, 0feedfacecafebabeh
+ mov rdx, 0cafebabefeedfaceh
+ mov r8, 0deadbeefdeadbeefh
+ mov r9, 0deadbeefdeadbeefh
+ mov r10, 0deadbeefdeadbeefh
+ mov r11, 0deadbeefdeadbeefh
+ push rbx
+ push r12
+ push r13
+ push r14
+ push r15
+
+ ; Read 6x TSC into registers.
+ rdtsc
+ mov r8, rax
+ mov r9, rdx
+ rdtsc
+ mov r10, rax
+ mov r11, rdx
+ rdtsc
+ mov r12, rax
+ mov r13, rdx
+ rdtsc
+ mov r14, rax
+ mov r15, rdx
+ rdtsc
+ mov rbx, rax
+ mov rcx, rdx
+ rdtsc
+
+ ; Store the values (64-bit).
+ mov [NAME(g_aRdTscResults) + 10h xWrtRIP], r8
+ mov [NAME(g_aRdTscResults) + 18h xWrtRIP], r9
+ mov [NAME(g_aRdTscResults) + 20h xWrtRIP], r10
+ mov [NAME(g_aRdTscResults) + 28h xWrtRIP], r11
+ mov [NAME(g_aRdTscResults) + 30h xWrtRIP], r12
+ mov [NAME(g_aRdTscResults) + 38h xWrtRIP], r13
+ mov [NAME(g_aRdTscResults) + 40h xWrtRIP], r14
+ mov [NAME(g_aRdTscResults) + 48h xWrtRIP], r15
+ mov [NAME(g_aRdTscResults) + 50h xWrtRIP], rbx
+ mov [NAME(g_aRdTscResults) + 58h xWrtRIP], rcx
+ mov [NAME(g_aRdTscResults) + 60h xWrtRIP], rax
+ mov [NAME(g_aRdTscResults) + 68h xWrtRIP], rdx
+
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop rbx
+
+ mov eax, 6
+%else
+ mov eax, 0feedfaceh
+ mov edx, 0cafebabeh
+ push esi
+ push edi
+ push ebx
+
+ ; Read 3x TSC into registers.
+ rdtsc
+ mov ebx, eax
+ mov ecx, edx
+ rdtsc
+ mov esi, eax
+ mov edi, edx
+ rdtsc
+
+ ; Store values.
+ mov [NAME(g_aRdTscResults) + 08h], ebx
+ mov [NAME(g_aRdTscResults) + 0ch], ecx
+ mov [NAME(g_aRdTscResults) + 10h], esi
+ mov [NAME(g_aRdTscResults) + 14h], edi
+ mov [NAME(g_aRdTscResults) + 18h], eax
+ mov [NAME(g_aRdTscResults) + 1ch], edx
+
+ pop ebx
+ pop edi
+ pop esi
+
+ mov eax, 3
+%endif
+ leave
+ ret
+ENDPROC DoTscReads
+
diff --git a/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp b/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp
new file mode 100644
index 00000000..81405f3d
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/rdtsc.cpp
@@ -0,0 +1,294 @@
+/* $Id: rdtsc.cpp $ */
+/** @file
+ * rdtsc - Test if three consecutive rdtsc instructions return different values.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RDTSCRESULT
+{
+ RTCCUINTREG uLow, uHigh;
+} RDTSCRESULT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern "C" RDTSCRESULT g_aRdTscResults[]; /* rdtsc-asm.asm */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+/**
+ * Does 3 (32-bit) or 6 (64-bit) fast TSC reads and stores the result
+ * in g_aRdTscResults, starting with the 2nd entry.
+ *
+ * Starting the result storing at g_aRdTscResults[1] make it easy to do the
+ * comparisons in a loop.
+ *
+ * @returns Number of results read into g_aRdTscResults[1] and onwards.
+ */
+DECLASM(uint32_t) DoTscReads(void);
+
+
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Tunables.
+ */
+ uint64_t offJumpThreshold = _4G * 2;
+ unsigned cMaxLoops = 10000000;
+ unsigned cStatusEvery = 2000000;
+ unsigned cMinSeconds = 0;
+
+ for (int i = 1; i < argc; i++)
+ {
+ const char *psz = argv[i];
+ if (*psz == '-')
+ {
+ psz++;
+ char chOpt;
+ while ((chOpt = *psz++) != '\0')
+ {
+ /* Option value. */
+ const char *pszValue = NULL;
+ uint64_t uValue = 0;
+ switch (chOpt)
+ {
+ case 'l':
+ case 's':
+ case 'm':
+ if (*psz == '\0')
+ {
+ if (i + 1 >= argc)
+ return RTMsgSyntax("The %c option requires a value", chOpt);
+ pszValue = argv[++i];
+ }
+ else
+ pszValue = psz + (*psz == ':' || *psz == '=');
+ switch (chOpt)
+ {
+ case 'l':
+ case 's':
+ case 'm':
+ {
+ char *pszNext = NULL;
+ rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &uValue);
+ if (RT_FAILURE(rc))
+ return RTMsgSyntax("Bad number: %s (%Rrc)", pszValue, rc);
+ if (pszNext && *pszNext != '\0')
+ {
+ if (*pszNext == 'M'&& pszNext[1] == '\0')
+ uValue *= _1M;
+ else if (*pszNext == 'K' && pszNext[1] == '\0')
+ uValue *= _1K;
+ else if (*pszNext == 'G' && pszNext[1] == '\0')
+ uValue *= _1G;
+ else
+ return RTMsgSyntax("Bad value format for option %c: %s", chOpt, pszValue);
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ /* handle the option. */
+ switch (chOpt)
+ {
+ case 'l':
+ cMaxLoops = uValue;
+ break;
+
+ case 'm':
+ cMinSeconds = uValue;
+ break;
+
+ case 's':
+ cStatusEvery = uValue;
+ break;
+
+ case 'h':
+ case '?':
+ RTPrintf("usage: rdtsc [-l <loops>] [-s <loops-between-status>]\n"
+ " [-m <minimum-seconds-to-run>]\n");
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTMsgSyntax("Unknown option %c (argument %d)\n", chOpt, i);
+ }
+ }
+ }
+ else
+ return RTMsgSyntax("argument %d (%s): not an option\n", i, psz);
+ }
+
+ /*
+ * Do the job.
+ */
+ uint64_t const nsTsStart = RTTimeNanoTS();
+ unsigned cOuterLoops = 0;
+ unsigned cLoopsToNextStatus = cStatusEvery;
+ unsigned cRdTscInstructions = 0;
+ unsigned cBackwards = 0;
+ unsigned cSame = 0;
+ unsigned cBadValues = 0;
+ unsigned cJumps = 0;
+ uint64_t offMaxJump = 0;
+ uint64_t offMinIncr = UINT64_MAX;
+ uint64_t offMaxIncr = 0;
+
+ g_aRdTscResults[0] = g_aRdTscResults[DoTscReads() - 1];
+
+ for (;;)
+ {
+ for (unsigned iLoop = 0; iLoop < cMaxLoops; iLoop++)
+ {
+ uint32_t const cResults = DoTscReads();
+ cRdTscInstructions += cResults;
+
+ for (uint32_t i = 0; i < cResults; i++)
+ {
+ uint64_t uPrev = RT_MAKE_U64((uint32_t)g_aRdTscResults[i ].uLow, (uint32_t)g_aRdTscResults[i ].uHigh);
+ uint64_t uCur = RT_MAKE_U64((uint32_t)g_aRdTscResults[i + 1].uLow, (uint32_t)g_aRdTscResults[i + 1].uHigh);
+ if (RT_LIKELY(uCur != uPrev))
+ {
+ int64_t offDelta = uCur - uPrev;
+ if (RT_LIKELY(offDelta >= 0))
+ {
+ if (RT_LIKELY((uint64_t)offDelta < offJumpThreshold))
+ {
+ if ((uint64_t)offDelta < offMinIncr)
+ offMinIncr = offDelta;
+ if ((uint64_t)offDelta > offMaxIncr && i != 0)
+ offMaxIncr = offDelta;
+ }
+ else
+ {
+ cJumps++;
+ if ((uint64_t)offDelta > offMaxJump)
+ offMaxJump = offDelta;
+ RTPrintf("%u/%u: Jump: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop,
+ (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow,
+ (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow);
+ }
+ }
+ else
+ {
+ cBackwards++;
+ RTPrintf("%u/%u: Back: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop,
+ (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow,
+ (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow);
+ }
+ }
+ else
+ {
+ cSame++;
+ RTPrintf("%u/%u: Same: %#010x`%08x -> %#010x`%08x\n", cOuterLoops, iLoop,
+ (unsigned)g_aRdTscResults[i].uHigh, (unsigned)g_aRdTscResults[i].uLow,
+ (unsigned)g_aRdTscResults[i + 1].uHigh, (unsigned)g_aRdTscResults[i + 1].uLow);
+ }
+#if ARCH_BITS == 64
+ if ((g_aRdTscResults[i + 1].uLow >> 32) || (g_aRdTscResults[i + 1].uHigh >> 32))
+ cBadValues++;
+#endif
+ }
+
+ /* Copy the last value for the next iteration. */
+ g_aRdTscResults[0] = g_aRdTscResults[cResults];
+
+ /* Display status. */
+ if (RT_LIKELY(--cLoopsToNextStatus > 0))
+ { /* likely */ }
+ else
+ {
+ cLoopsToNextStatus = cStatusEvery;
+ RTPrintf("%u/%u: %#010x`%08x\n", cOuterLoops, iLoop,
+ (unsigned)g_aRdTscResults[cResults].uHigh, (unsigned)g_aRdTscResults[cResults].uLow);
+ }
+ }
+
+ /*
+ * Check minimum number of seconds.
+ */
+ cOuterLoops++;
+ if (!cMinSeconds)
+ break;
+ uint64_t nsElapsed = RTTimeNanoTS() - nsTsStart;
+ if (nsElapsed >= cMinSeconds * RT_NS_1SEC_64)
+ break;
+ }
+
+ /*
+ * Summary.
+ */
+ if (cBackwards == 0 && cSame == 0 && cJumps == 0 && cBadValues == 0)
+ {
+ RTPrintf("rdtsc: Success (%u RDTSC over %u*%u loops, deltas: %#x`%08x..%#x`%08x)\n",
+ cRdTscInstructions, cOuterLoops, cMaxLoops,
+ (unsigned)(offMinIncr >> 32), (unsigned)offMinIncr, (unsigned)(offMaxIncr >> 32), (unsigned)offMaxIncr);
+ return RTEXITCODE_SUCCESS;
+ }
+ RTPrintf("RDTSC instructions: %u\n", cRdTscInstructions);
+ RTPrintf("Loops: %u * %u => %u\n", cMaxLoops, cOuterLoops, cOuterLoops * cMaxLoops);
+ RTPrintf("Backwards: %u\n", cBackwards);
+ RTPrintf("Jumps: %u\n", cJumps);
+ RTPrintf("Max jumps: %#010x`%08x\n", (unsigned)(offMaxJump >> 32), (unsigned)offMaxJump);
+ RTPrintf("Same value: %u\n", cSame);
+ RTPrintf("Bad values: %u\n", cBadValues);
+ RTPrintf("Min increment: %#010x`%08x\n", (unsigned)(offMinIncr >> 32), (unsigned)offMinIncr);
+ RTPrintf("Max increment: %#010x`%08x\n", (unsigned)(offMaxIncr >> 32), (unsigned)offMaxIncr);
+ return RTEXITCODE_FAILURE;
+}
+
diff --git a/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm b/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm
new file mode 100644
index 00000000..49a29e7a
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/xmmsaving-asm.asm
@@ -0,0 +1,162 @@
+; $Id: xmmsaving-asm.asm $
+;; @file
+; xmmsaving - assembly helpers.
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+%include "VBox/vmm/stam.mac"
+
+
+BEGINCODE
+
+
+;;
+; DECLASM(int) XmmSavingTestLoadSet(const MYXMMREGSET *pSet, const MYXMMREGSET *pPrevSet, PRTUINT128U pBadVal);
+;
+; @returns 0 on success, 1-based register number on failure.
+; @param pSet The new set.
+; @param pPrevSet The previous set. Can be NULL.
+; @param pBadVal Where to store the actual register value on failure.
+;
+BEGINPROC XmmSavingTestLoadSet
+ push xBP
+ mov xBP, xSP
+ sub xSP, 32 ; Space for storing an XMM register (in TEST_REG).
+ and xSP, ~31 ; Align it.
+
+ ; Unify register/arguments.
+%ifdef ASM_CALL64_GCC
+ mov r8, rdx ; pBadVal
+ mov xCX, rdi ; pSet
+ mov xDX, rsi ; pPrevSet
+%endif
+%ifdef RT_ARCH_X86
+ mov xCX, [ebp + 8] ; pSet
+ mov xDX, [ebp + 12] ; pPrevSet
+%endif
+
+ test xDX, xDX
+ jz near .just_load
+
+ ; Check that the old set is still correct.
+%macro TEST_REG 1,
+ movdqa [xSP], xmm %+ %1
+ mov xAX, [xDX + %1 * 8]
+ cmp [xSP], xAX
+ jne %%bad
+ mov xAX, [xDX + %1 * 8 + xCB]
+ cmp [xSP + xCB], xAX
+%ifdef RT_ARCH_X86
+ jne %%bad
+ mov xAX, [xDX + %1 * 8 + xCB*2]
+ cmp [xSP + xCB*2], xAX
+ jne %%bad
+ mov xAX, [xDX + %1 * 8 + xCB*3]
+ cmp [xSP + xCB*3], xAX
+%endif
+ je %%next
+%%bad:
+ mov eax, %1 + 1
+ jmp .return_copy_badval
+%%next:
+%endmacro
+
+ TEST_REG 0
+ TEST_REG 1
+ TEST_REG 2
+ TEST_REG 3
+ TEST_REG 4
+ TEST_REG 5
+ TEST_REG 6
+ TEST_REG 7
+%ifdef RT_ARCH_AMD64
+ TEST_REG 8
+ TEST_REG 9
+ TEST_REG 10
+ TEST_REG 11
+ TEST_REG 12
+ TEST_REG 13
+ TEST_REG 14
+ TEST_REG 15
+%endif
+
+ ; Load the new state.
+.just_load:
+ movdqu xmm0, [xCX + 0*8]
+ movdqu xmm1, [xCX + 1*8]
+ movdqu xmm2, [xCX + 2*8]
+ movdqu xmm3, [xCX + 3*8]
+ movdqu xmm4, [xCX + 4*8]
+ movdqu xmm5, [xCX + 5*8]
+ movdqu xmm6, [xCX + 6*8]
+ movdqu xmm7, [xCX + 7*8]
+%ifdef RT_ARCH_AMD64
+ movdqu xmm8, [xCX + 8*8]
+ movdqu xmm9, [xCX + 9*8]
+ movdqu xmm10, [xCX + 10*8]
+ movdqu xmm11, [xCX + 11*8]
+ movdqu xmm12, [xCX + 12*8]
+ movdqu xmm13, [xCX + 13*8]
+ movdqu xmm14, [xCX + 14*8]
+ movdqu xmm15, [xCX + 15*8]
+%endif
+ xor eax, eax
+ jmp .return
+
+.return_copy_badval:
+ ; don't touch eax here.
+%ifdef RT_ARCH_X86
+ mov edx, [ebp + 16]
+ mov ecx, [esp]
+ mov [edx ], ecx
+ mov ecx, [esp + 4]
+ mov [edx + 4], ecx
+ mov ecx, [esp + 8]
+ mov [edx + 8], ecx
+ mov ecx, [esp + 12]
+ mov [edx + 12], ecx
+%else
+ mov rdx, [rsp]
+ mov rcx, [rsp + 8]
+ mov [r8], rdx
+ mov [r8 + 8], rcx
+%endif
+ jmp .return
+
+.return:
+ leave
+ ret
+ENDPROC XmmSavingTestLoadSet
+
diff --git a/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp b/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp
new file mode 100644
index 00000000..a8d377f8
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/cpu/xmmsaving.cpp
@@ -0,0 +1,130 @@
+/* $Id: xmmsaving.cpp $ */
+/** @file
+ * xmmsaving - Test that all XMM register state is handled correctly and
+ * not corrupted the VMM.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
+#include <iprt/x86.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct MYXMMREGSET
+{
+ RTUINT128U aRegs[16];
+} MYXMMREGSET;
+
+
+DECLASM(int) XmmSavingTestLoadSet(const MYXMMREGSET *pSet, const MYXMMREGSET *pPrevSet, PRTUINT128U pBadVal);
+
+
+static void XmmSavingTest(void)
+{
+ RTTestISub("xmm saving and restoring");
+
+ /* Create the test sets. */
+ static MYXMMREGSET s_aSets[256];
+ for (unsigned s = 0; s < RT_ELEMENTS(s_aSets); s++)
+ {
+ for (unsigned r = 0; r < RT_ELEMENTS(s_aSets[s].aRegs); r++)
+ {
+ unsigned x = (s << 4) | r;
+ s_aSets[s].aRegs[r].au32[0] = x | UINT32_C(0x12345000);
+ s_aSets[s].aRegs[r].au32[1] = (x << 8) | UINT32_C(0x88700011);
+ s_aSets[s].aRegs[r].au32[2] = (x << 16) | UINT32_C(0xe000dcba);
+ s_aSets[s].aRegs[r].au32[3] = (x << 20) | UINT32_C(0x00087654);
+ }
+ }
+
+ /* Do the actual testing. */
+ const MYXMMREGSET *pPrev2 = NULL;
+ const MYXMMREGSET *pPrev = NULL;
+ for (int i = 0; i < 1000000; i++)
+ {
+ if ((i % 50000) == 0)
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, ".");
+ pPrev = pPrev2 = NULL; /* May be trashed by the above call. */
+ }
+ for (unsigned s = 0; s < RT_ELEMENTS(s_aSets); s++)
+ {
+ RTUINT128U BadVal;
+ const MYXMMREGSET *pSet = &s_aSets[s];
+ int r = XmmSavingTestLoadSet(pSet, pPrev, &BadVal);
+ if (r-- != 0)
+ {
+ RTTestIFailed("i=%d s=%d r=%d", i, s, r);
+ RTTestIFailureDetails("XMM%-2d = %08x,%08x,%08x,%08x\n",
+ r,
+ BadVal.au32[0],
+ BadVal.au32[1],
+ BadVal.au32[2],
+ BadVal.au32[3]);
+ RTTestIFailureDetails("Expected %08x,%08x,%08x,%08x\n",
+ pPrev->aRegs[r].au32[0],
+ pPrev->aRegs[r].au32[1],
+ pPrev->aRegs[r].au32[2],
+ pPrev->aRegs[r].au32[3]);
+ if (pPrev2)
+ RTTestIFailureDetails("PrevPrev %08x,%08x,%08x,%08x\n",
+ pPrev2->aRegs[r].au32[0],
+ pPrev2->aRegs[r].au32[1],
+ pPrev2->aRegs[r].au32[2],
+ pPrev2->aRegs[r].au32[3]);
+ return;
+ }
+ pPrev2 = pPrev;
+ pPrev = pSet;
+ }
+ }
+ RTTestISubDone();
+}
+
+
+int main()
+{
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("xmmsaving", &hTest);
+ if (rc)
+ return rc;
+ XmmSavingTest();
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/dos/DosSleep.c b/src/VBox/ValidationKit/utils/dos/DosSleep.c
new file mode 100644
index 00000000..f18c5fe9
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/dos/DosSleep.c
@@ -0,0 +1,67 @@
+/* $Id: DosSleep.c $ */
+/** @file
+ * 16-bit DOS sleep program.
+ *
+ * Build: wcl -I%WATCOM%\h\win -l=dos -k4096 -fm -W4 DosSleep.c
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+
+int main(int argc, char **argv)
+{
+ int iExit;
+
+ if (argc == 2)
+ {
+ int cSeconds = atoi(argv[1]);
+ sleep(cSeconds);
+ iExit = 0;
+ }
+ else
+ {
+ fprintf(stderr, "syntax error: only expected a number of seconds\n");
+ iExit = 4;
+ }
+
+ return iExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/dos/DosVmOff.asm b/src/VBox/ValidationKit/utils/dos/DosVmOff.asm
new file mode 100644
index 00000000..1958f9ce
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/dos/DosVmOff.asm
@@ -0,0 +1,79 @@
+; $Id: DosVmOff.asm $
+;; @file
+; 16-bit DOS COM program that powers off the VM.
+;
+; Build: yasm -f bin -i../../../../../include/ DosVmOff.asm -o DosVmOff.com
+;
+
+;
+; Copyright (C) 2018-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+
+%include "VBox/bios.mac"
+
+ org 100h
+
+segment text
+main:
+%if 0
+ ; Setup stack.
+ mov ax, stack
+ mov ss, ax
+ mov sp, top_of_stack
+%endif
+
+ ; Do the shutdown thing.
+ mov ax, cs
+ mov ds, ax
+
+ mov bl, 64
+ mov dx, VBOX_BIOS_SHUTDOWN_PORT
+ mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT
+.retry:
+ mov cx, 8
+ mov si, .s_szShutdown
+ rep outsb
+ xchg ax, dx ; alternate between the new (VBox) and old (Bochs) ports.
+ dec bl
+ jnz .retry
+
+
+ ; Probably not a VBox VM, exit the program with errorlevel 1.
+.whatever:
+ mov ax, 04c01h
+ int 021h
+ hlt
+ jmp .whatever
+
+.s_szShutdown:
+ db 'Shutdown', 0
+
diff --git a/src/VBox/ValidationKit/utils/dos/WinExit.asm b/src/VBox/ValidationKit/utils/dos/WinExit.asm
new file mode 100644
index 00000000..1705d261
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/dos/WinExit.asm
@@ -0,0 +1,95 @@
+; $Id: WinExit.asm $
+;; @file
+; 16-bit windows program that exits windows.
+;
+; Build: wcl -I%WATCOM%\h\win -l=windows -k4096 -fm WinExit.asm
+;
+
+;
+; Copyright (C) 2018-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+
+;.stack 4096
+STACK segment para stack 'STACK'
+STACK ends
+
+
+extrn INITTASK:FAR
+extrn INITAPP:FAR
+extrn EXITWINDOWS:FAR
+extrn WAITEVENT:FAR
+
+_TEXT segment word public 'CODE'
+start:
+ push bp
+ mov bp, sp
+
+ ;
+ ; Initialize the windows app.
+ ;
+ call INITTASK
+
+ xor ax, ax
+ push ax
+ call WAITEVENT
+
+ push di ; hInstance
+ push di
+ call INITAPP
+
+ ;
+ ; Do what we're here for, exitting windows.
+ ;
+ xor ax, ax
+ xor cx, cx
+ xor dx, dx
+ push ax
+ push ax
+ push ax
+ push ax
+ call EXITWINDOWS
+
+ ;
+ ; Exit via DOS interrupt.
+ ;
+ xor al, al
+ mov ah,04cH
+ int 021h
+
+ mov sp, bp
+ pop bp
+ ret
+
+_TEXT ends
+
+end start
+
diff --git a/src/VBox/ValidationKit/utils/fs/FsPerf.cpp b/src/VBox/ValidationKit/utils/fs/FsPerf.cpp
new file mode 100644
index 00000000..ae8b7d91
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/fs/FsPerf.cpp
@@ -0,0 +1,6834 @@
+/* $Id: FsPerf.cpp $ */
+/** @file
+ * FsPerf - File System (Shared Folders) Performance Benchmark.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_OS2
+# define INCL_BASE
+# include <os2.h>
+# undef RT_MAX
+#endif
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#ifdef RT_OS_LINUX
+# include <iprt/pipe.h>
+#endif
+#include <iprt/process.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+#include <iprt/tcp.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/zero.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/nt/nt-and-windows.h>
+#else
+# include <errno.h>
+# include <unistd.h>
+# include <limits.h>
+# include <sys/types.h>
+# include <sys/fcntl.h>
+# ifndef RT_OS_OS2
+# include <sys/mman.h>
+# include <sys/uio.h>
+# endif
+# include <sys/socket.h>
+# include <signal.h>
+# ifdef RT_OS_LINUX
+# include <sys/sendfile.h>
+# include <sys/syscall.h>
+# endif
+# ifdef RT_OS_DARWIN
+# include <sys/uio.h>
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Used for cutting the the -d parameter value short and avoid a number of buffer overflow checks. */
+#define FSPERF_MAX_NEEDED_PATH 224
+/** The max path used by this code.
+ * It greatly exceeds the RTPATH_MAX so we can push the limits on windows. */
+#define FSPERF_MAX_PATH (_32K)
+
+/** EOF marker character used by the master/slave comms. */
+#define FSPERF_EOF 0x1a
+/** EOF marker character used by the master/slave comms, string version. */
+#define FSPERF_EOF_STR "\x1a"
+
+/** @def FSPERF_TEST_SENDFILE
+ * Whether to enable the sendfile() tests. */
+#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
+# define FSPERF_TEST_SENDFILE
+#endif
+
+/**
+ * Macro for profiling @a a_fnCall (typically forced inline) for about @a a_cNsTarget ns.
+ *
+ * Always does an even number of iterations.
+ */
+#define PROFILE_FN(a_fnCall, a_cNsTarget, a_szDesc) \
+ do { \
+ /* Estimate how many iterations we need to fill up the given timeslot: */ \
+ fsPerfYield(); \
+ uint64_t nsStart = RTTimeNanoTS(); \
+ uint64_t nsPrf; \
+ do \
+ nsPrf = RTTimeNanoTS(); \
+ while (nsPrf == nsStart); \
+ nsStart = nsPrf; \
+ \
+ uint64_t iIteration = 0; \
+ do \
+ { \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ iIteration++; \
+ nsPrf = RTTimeNanoTS() - nsStart; \
+ } while (nsPrf < RT_NS_10MS || (iIteration & 1)); \
+ nsPrf /= iIteration; \
+ if (nsPrf > g_nsPerNanoTSCall + 32) \
+ nsPrf -= g_nsPerNanoTSCall; \
+ \
+ uint64_t cIterations = (a_cNsTarget) / nsPrf; \
+ if (cIterations <= 1) \
+ cIterations = 2; \
+ else if (cIterations & 1) \
+ cIterations++; \
+ \
+ /* Do the actual profiling: */ \
+ fsPerfYield(); \
+ iIteration = 0; \
+ nsStart = RTTimeNanoTS(); \
+ for (; iIteration < cIterations; iIteration++) \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ nsPrf = RTTimeNanoTS() - nsStart; \
+ RTTestIValue(a_szDesc, nsPrf / cIterations, RTTESTUNIT_NS_PER_OCCURRENCE); \
+ if (g_fShowDuration) \
+ RTTestIValueF(nsPrf, RTTESTUNIT_NS, "%s duration", a_szDesc); \
+ if (g_fShowIterations) \
+ RTTestIValueF(iIteration, RTTESTUNIT_OCCURRENCES, "%s iterations", a_szDesc); \
+ } while (0)
+
+
+/**
+ * Macro for profiling an operation on each file in the manytree directory tree.
+ *
+ * Always does an even number of tree iterations.
+ */
+#define PROFILE_MANYTREE_FN(a_szPath, a_fnCall, a_cEstimationIterations, a_cNsTarget, a_szDesc) \
+ do { \
+ if (!g_fManyFiles) \
+ break; \
+ \
+ /* Estimate how many iterations we need to fill up the given timeslot: */ \
+ fsPerfYield(); \
+ uint64_t nsStart = RTTimeNanoTS(); \
+ uint64_t ns; \
+ do \
+ ns = RTTimeNanoTS(); \
+ while (ns == nsStart); \
+ nsStart = ns; \
+ \
+ PFSPERFNAMEENTRY pCur; \
+ uint64_t iIteration = 0; \
+ do \
+ { \
+ RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
+ { \
+ memcpy(a_szPath, pCur->szName, pCur->cchName); \
+ for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
+ { \
+ RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ } \
+ } \
+ iIteration++; \
+ ns = RTTimeNanoTS() - nsStart; \
+ } while (ns < RT_NS_10MS || (iIteration & 1)); \
+ ns /= iIteration; \
+ if (ns > g_nsPerNanoTSCall + 32) \
+ ns -= g_nsPerNanoTSCall; \
+ \
+ uint32_t cIterations = (a_cNsTarget) / ns; \
+ if (cIterations <= 1) \
+ cIterations = 2; \
+ else if (cIterations & 1) \
+ cIterations++; \
+ \
+ /* Do the actual profiling: */ \
+ fsPerfYield(); \
+ uint32_t cCalls = 0; \
+ nsStart = RTTimeNanoTS(); \
+ for (iIteration = 0; iIteration < cIterations; iIteration++) \
+ { \
+ RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
+ { \
+ memcpy(a_szPath, pCur->szName, pCur->cchName); \
+ for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
+ { \
+ RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ cCalls++; \
+ } \
+ } \
+ } \
+ ns = RTTimeNanoTS() - nsStart; \
+ RTTestIValueF(ns / cCalls, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
+ if (g_fShowDuration) \
+ RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
+ if (g_fShowIterations) \
+ RTTestIValueF(iIteration, RTTESTUNIT_OCCURRENCES, "%s iterations", a_szDesc); \
+ } while (0)
+
+
+/**
+ * Execute a_fnCall for each file in the manytree.
+ */
+#define DO_MANYTREE_FN(a_szPath, a_fnCall) \
+ do { \
+ PFSPERFNAMEENTRY pCur; \
+ RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
+ { \
+ memcpy(a_szPath, pCur->szName, pCur->cchName); \
+ for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
+ { \
+ RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
+ a_fnCall; \
+ } \
+ } \
+ } while (0)
+
+
+/** @def FSPERF_VERR_PATH_NOT_FOUND
+ * Hides the fact that we only get VERR_PATH_NOT_FOUND on non-unix systems. */
+#if defined(RT_OS_WINDOWS) //|| defined(RT_OS_OS2) - using posix APIs IIRC, so lost in translation.
+# define FSPERF_VERR_PATH_NOT_FOUND VERR_PATH_NOT_FOUND
+#else
+# define FSPERF_VERR_PATH_NOT_FOUND VERR_FILE_NOT_FOUND
+#endif
+
+#ifdef RT_OS_WINDOWS
+/** @def CHECK_WINAPI
+ * Checks a windows API call, reporting the last error on failure. */
+# define CHECK_WINAPI_CALL(a_CallAndTestExpr) \
+ if (!(a_CallAndTestExpr)) { \
+ RTTestIFailed("line %u: %s failed - last error %u, last status %#x", \
+ __LINE__, #a_CallAndTestExpr, GetLastError(), RTNtLastStatusValue()); \
+ } else do {} while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct FSPERFNAMEENTRY
+{
+ RTLISTNODE Entry;
+ uint16_t cchName;
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char szName[RT_FLEXIBLE_ARRAY];
+} FSPERFNAMEENTRY;
+typedef FSPERFNAMEENTRY *PFSPERFNAMEENTRY;
+
+
+enum
+{
+ kCmdOpt_First = 128,
+
+ kCmdOpt_ManyFiles = kCmdOpt_First,
+ kCmdOpt_NoManyFiles,
+ kCmdOpt_Open,
+ kCmdOpt_NoOpen,
+ kCmdOpt_FStat,
+ kCmdOpt_NoFStat,
+#ifdef RT_OS_WINDOWS
+ kCmdOpt_NtQueryInfoFile,
+ kCmdOpt_NoNtQueryInfoFile,
+ kCmdOpt_NtQueryVolInfoFile,
+ kCmdOpt_NoNtQueryVolInfoFile,
+#endif
+ kCmdOpt_FChMod,
+ kCmdOpt_NoFChMod,
+ kCmdOpt_FUtimes,
+ kCmdOpt_NoFUtimes,
+ kCmdOpt_Stat,
+ kCmdOpt_NoStat,
+ kCmdOpt_ChMod,
+ kCmdOpt_NoChMod,
+ kCmdOpt_Utimes,
+ kCmdOpt_NoUtimes,
+ kCmdOpt_Rename,
+ kCmdOpt_NoRename,
+ kCmdOpt_DirOpen,
+ kCmdOpt_NoDirOpen,
+ kCmdOpt_DirEnum,
+ kCmdOpt_NoDirEnum,
+ kCmdOpt_MkRmDir,
+ kCmdOpt_NoMkRmDir,
+ kCmdOpt_StatVfs,
+ kCmdOpt_NoStatVfs,
+ kCmdOpt_Rm,
+ kCmdOpt_NoRm,
+ kCmdOpt_ChSize,
+ kCmdOpt_NoChSize,
+ kCmdOpt_ReadPerf,
+ kCmdOpt_NoReadPerf,
+ kCmdOpt_ReadTests,
+ kCmdOpt_NoReadTests,
+#ifdef FSPERF_TEST_SENDFILE
+ kCmdOpt_SendFile,
+ kCmdOpt_NoSendFile,
+#endif
+#ifdef RT_OS_LINUX
+ kCmdOpt_Splice,
+ kCmdOpt_NoSplice,
+#endif
+ kCmdOpt_WritePerf,
+ kCmdOpt_NoWritePerf,
+ kCmdOpt_WriteTests,
+ kCmdOpt_NoWriteTests,
+ kCmdOpt_Seek,
+ kCmdOpt_NoSeek,
+ kCmdOpt_FSync,
+ kCmdOpt_NoFSync,
+ kCmdOpt_MMap,
+ kCmdOpt_NoMMap,
+ kCmdOpt_MMapCoherency,
+ kCmdOpt_NoMMapCoherency,
+ kCmdOpt_MMapPlacement,
+ kCmdOpt_IgnoreNoCache,
+ kCmdOpt_NoIgnoreNoCache,
+ kCmdOpt_IoFileSize,
+ kCmdOpt_SetBlockSize,
+ kCmdOpt_AddBlockSize,
+ kCmdOpt_Copy,
+ kCmdOpt_NoCopy,
+ kCmdOpt_Remote,
+ kCmdOpt_NoRemote,
+
+ kCmdOpt_ShowDuration,
+ kCmdOpt_NoShowDuration,
+ kCmdOpt_ShowIterations,
+ kCmdOpt_NoShowIterations,
+
+ kCmdOpt_ManyTreeFilesPerDir,
+ kCmdOpt_ManyTreeSubdirsPerDir,
+ kCmdOpt_ManyTreeDepth,
+
+ kCmdOpt_MaxBufferSize,
+
+ kCmdOpt_End
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ { "--dir", 'd', RTGETOPT_REQ_STRING },
+ { "--relative-dir", 'r', RTGETOPT_REQ_NOTHING },
+ { "--comms-dir", 'c', RTGETOPT_REQ_STRING },
+ { "--comms-slave", 'C', RTGETOPT_REQ_NOTHING },
+ { "--seconds", 's', RTGETOPT_REQ_UINT32 },
+ { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 },
+
+ { "--enable-all", 'e', RTGETOPT_REQ_NOTHING },
+ { "--disable-all", 'z', RTGETOPT_REQ_NOTHING },
+
+ { "--many-files", kCmdOpt_ManyFiles, RTGETOPT_REQ_UINT32 },
+ { "--no-many-files", kCmdOpt_NoManyFiles, RTGETOPT_REQ_NOTHING },
+ { "--files-per-dir", kCmdOpt_ManyTreeFilesPerDir, RTGETOPT_REQ_UINT32 },
+ { "--subdirs-per-dir", kCmdOpt_ManyTreeSubdirsPerDir, RTGETOPT_REQ_UINT32 },
+ { "--tree-depth", kCmdOpt_ManyTreeDepth, RTGETOPT_REQ_UINT32 },
+ { "--max-buffer-size", kCmdOpt_MaxBufferSize, RTGETOPT_REQ_UINT32 },
+ { "--mmap-placement", kCmdOpt_MMapPlacement, RTGETOPT_REQ_STRING },
+ /// @todo { "--timestamp-style", kCmdOpt_TimestampStyle, RTGETOPT_REQ_STRING },
+
+ { "--open", kCmdOpt_Open, RTGETOPT_REQ_NOTHING },
+ { "--no-open", kCmdOpt_NoOpen, RTGETOPT_REQ_NOTHING },
+ { "--fstat", kCmdOpt_FStat, RTGETOPT_REQ_NOTHING },
+ { "--no-fstat", kCmdOpt_NoFStat, RTGETOPT_REQ_NOTHING },
+#ifdef RT_OS_WINDOWS
+ { "--nt-query-info-file", kCmdOpt_NtQueryInfoFile, RTGETOPT_REQ_NOTHING },
+ { "--no-nt-query-info-file", kCmdOpt_NoNtQueryInfoFile, RTGETOPT_REQ_NOTHING },
+ { "--nt-query-vol-info-file", kCmdOpt_NtQueryVolInfoFile, RTGETOPT_REQ_NOTHING },
+ { "--no-nt-query-vol-info-file",kCmdOpt_NoNtQueryVolInfoFile, RTGETOPT_REQ_NOTHING },
+#endif
+ { "--fchmod", kCmdOpt_FChMod, RTGETOPT_REQ_NOTHING },
+ { "--no-fchmod", kCmdOpt_NoFChMod, RTGETOPT_REQ_NOTHING },
+ { "--futimes", kCmdOpt_FUtimes, RTGETOPT_REQ_NOTHING },
+ { "--no-futimes", kCmdOpt_NoFUtimes, RTGETOPT_REQ_NOTHING },
+ { "--stat", kCmdOpt_Stat, RTGETOPT_REQ_NOTHING },
+ { "--no-stat", kCmdOpt_NoStat, RTGETOPT_REQ_NOTHING },
+ { "--chmod", kCmdOpt_ChMod, RTGETOPT_REQ_NOTHING },
+ { "--no-chmod", kCmdOpt_NoChMod, RTGETOPT_REQ_NOTHING },
+ { "--utimes", kCmdOpt_Utimes, RTGETOPT_REQ_NOTHING },
+ { "--no-utimes", kCmdOpt_NoUtimes, RTGETOPT_REQ_NOTHING },
+ { "--rename", kCmdOpt_Rename, RTGETOPT_REQ_NOTHING },
+ { "--no-rename", kCmdOpt_NoRename, RTGETOPT_REQ_NOTHING },
+ { "--dir-open", kCmdOpt_DirOpen, RTGETOPT_REQ_NOTHING },
+ { "--no-dir-open", kCmdOpt_NoDirOpen, RTGETOPT_REQ_NOTHING },
+ { "--dir-enum", kCmdOpt_DirEnum, RTGETOPT_REQ_NOTHING },
+ { "--no-dir-enum", kCmdOpt_NoDirEnum, RTGETOPT_REQ_NOTHING },
+ { "--mk-rm-dir", kCmdOpt_MkRmDir, RTGETOPT_REQ_NOTHING },
+ { "--no-mk-rm-dir", kCmdOpt_NoMkRmDir, RTGETOPT_REQ_NOTHING },
+ { "--stat-vfs", kCmdOpt_StatVfs, RTGETOPT_REQ_NOTHING },
+ { "--no-stat-vfs", kCmdOpt_NoStatVfs, RTGETOPT_REQ_NOTHING },
+ { "--rm", kCmdOpt_Rm, RTGETOPT_REQ_NOTHING },
+ { "--no-rm", kCmdOpt_NoRm, RTGETOPT_REQ_NOTHING },
+ { "--chsize", kCmdOpt_ChSize, RTGETOPT_REQ_NOTHING },
+ { "--no-chsize", kCmdOpt_NoChSize, RTGETOPT_REQ_NOTHING },
+ { "--read-tests", kCmdOpt_ReadTests, RTGETOPT_REQ_NOTHING },
+ { "--no-read-tests", kCmdOpt_NoReadTests, RTGETOPT_REQ_NOTHING },
+ { "--read-perf", kCmdOpt_ReadPerf, RTGETOPT_REQ_NOTHING },
+ { "--no-read-perf", kCmdOpt_NoReadPerf, RTGETOPT_REQ_NOTHING },
+#ifdef FSPERF_TEST_SENDFILE
+ { "--sendfile", kCmdOpt_SendFile, RTGETOPT_REQ_NOTHING },
+ { "--no-sendfile", kCmdOpt_NoSendFile, RTGETOPT_REQ_NOTHING },
+#endif
+#ifdef RT_OS_LINUX
+ { "--splice", kCmdOpt_Splice, RTGETOPT_REQ_NOTHING },
+ { "--no-splice", kCmdOpt_NoSplice, RTGETOPT_REQ_NOTHING },
+#endif
+ { "--write-tests", kCmdOpt_WriteTests, RTGETOPT_REQ_NOTHING },
+ { "--no-write-tests", kCmdOpt_NoWriteTests, RTGETOPT_REQ_NOTHING },
+ { "--write-perf", kCmdOpt_WritePerf, RTGETOPT_REQ_NOTHING },
+ { "--no-write-perf", kCmdOpt_NoWritePerf, RTGETOPT_REQ_NOTHING },
+ { "--seek", kCmdOpt_Seek, RTGETOPT_REQ_NOTHING },
+ { "--no-seek", kCmdOpt_NoSeek, RTGETOPT_REQ_NOTHING },
+ { "--fsync", kCmdOpt_FSync, RTGETOPT_REQ_NOTHING },
+ { "--no-fsync", kCmdOpt_NoFSync, RTGETOPT_REQ_NOTHING },
+ { "--mmap", kCmdOpt_MMap, RTGETOPT_REQ_NOTHING },
+ { "--no-mmap", kCmdOpt_NoMMap, RTGETOPT_REQ_NOTHING },
+ { "--mmap-coherency", kCmdOpt_MMapCoherency, RTGETOPT_REQ_NOTHING },
+ { "--no-mmap-coherency", kCmdOpt_NoMMapCoherency, RTGETOPT_REQ_NOTHING },
+ { "--ignore-no-cache", kCmdOpt_IgnoreNoCache, RTGETOPT_REQ_NOTHING },
+ { "--no-ignore-no-cache", kCmdOpt_NoIgnoreNoCache, RTGETOPT_REQ_NOTHING },
+ { "--io-file-size", kCmdOpt_IoFileSize, RTGETOPT_REQ_UINT64 },
+ { "--set-block-size", kCmdOpt_SetBlockSize, RTGETOPT_REQ_UINT32 },
+ { "--add-block-size", kCmdOpt_AddBlockSize, RTGETOPT_REQ_UINT32 },
+ { "--copy", kCmdOpt_Copy, RTGETOPT_REQ_NOTHING },
+ { "--no-copy", kCmdOpt_NoCopy, RTGETOPT_REQ_NOTHING },
+ { "--remote", kCmdOpt_Remote, RTGETOPT_REQ_NOTHING },
+ { "--no-remote", kCmdOpt_NoRemote, RTGETOPT_REQ_NOTHING },
+
+ { "--show-duration", kCmdOpt_ShowDuration, RTGETOPT_REQ_NOTHING },
+ { "--no-show-duration", kCmdOpt_NoShowDuration, RTGETOPT_REQ_NOTHING },
+ { "--show-iterations", kCmdOpt_ShowIterations, RTGETOPT_REQ_NOTHING },
+ { "--no-show-iterations", kCmdOpt_NoShowIterations, RTGETOPT_REQ_NOTHING },
+
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
+};
+
+/** The test handle. */
+static RTTEST g_hTest;
+/** The number of nanoseconds a RTTimeNanoTS call takes.
+ * This is used for adjusting loop count estimates. */
+static uint64_t g_nsPerNanoTSCall = 1;
+/** Whether or not to display the duration of each profile run.
+ * This is chiefly for verify the estimate phase. */
+static bool g_fShowDuration = false;
+/** Whether or not to display the iteration count for each profile run.
+ * This is chiefly for verify the estimate phase. */
+static bool g_fShowIterations = false;
+/** Verbosity level. */
+static uint32_t g_uVerbosity = 0;
+/** Max buffer size, UINT32_MAX for unlimited.
+ * This is for making sure we don't run into the MDL limit on windows, which
+ * a bit less than 64 MiB. */
+#if defined(RT_OS_WINDOWS)
+static uint32_t g_cbMaxBuffer = _32M;
+#else
+static uint32_t g_cbMaxBuffer = UINT32_MAX;
+#endif
+/** When to place the mmap test. */
+static int g_iMMapPlacement = 0;
+
+/** @name Selected subtest
+ * @{ */
+static bool g_fManyFiles = true;
+static bool g_fOpen = true;
+static bool g_fFStat = true;
+#ifdef RT_OS_WINDOWS
+static bool g_fNtQueryInfoFile = true;
+static bool g_fNtQueryVolInfoFile = true;
+#endif
+static bool g_fFChMod = true;
+static bool g_fFUtimes = true;
+static bool g_fStat = true;
+static bool g_fChMod = true;
+static bool g_fUtimes = true;
+static bool g_fRename = true;
+static bool g_fDirOpen = true;
+static bool g_fDirEnum = true;
+static bool g_fMkRmDir = true;
+static bool g_fStatVfs = true;
+static bool g_fRm = true;
+static bool g_fChSize = true;
+static bool g_fReadTests = true;
+static bool g_fReadPerf = true;
+#ifdef FSPERF_TEST_SENDFILE
+static bool g_fSendFile = true;
+#endif
+#ifdef RT_OS_LINUX
+static bool g_fSplice = true;
+#endif
+static bool g_fWriteTests = true;
+static bool g_fWritePerf = true;
+static bool g_fSeek = true;
+static bool g_fFSync = true;
+static bool g_fMMap = true;
+static bool g_fMMapCoherency = true;
+static bool g_fCopy = true;
+static bool g_fRemote = true;
+/** @} */
+
+/** The length of each test run. */
+static uint64_t g_nsTestRun = RT_NS_1SEC_64 * 10;
+
+/** For the 'manyfiles' subdir. */
+static uint32_t g_cManyFiles = 10000;
+
+/** Number of files in the 'manytree' directory tree. */
+static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/;
+/** Number of files per directory in the 'manytree' construct. */
+static uint32_t g_cManyTreeFilesPerDir = 640;
+/** Number of subdirs per directory in the 'manytree' construct. */
+static uint32_t g_cManyTreeSubdirsPerDir = 16;
+/** The depth of the 'manytree' directory tree. */
+static uint32_t g_cManyTreeDepth = 1;
+/** List of directories in the many tree, creation order. */
+static RTLISTANCHOR g_ManyTreeHead;
+
+/** Number of configured I/O block sizes. */
+static uint32_t g_cIoBlocks = 8;
+/** Configured I/O block sizes. */
+static uint32_t g_acbIoBlocks[16] = { 1, 512, 4096, 16384, 65536, _1M, _32M, _128M };
+/** The desired size of the test file we use for I/O. */
+static uint64_t g_cbIoFile = _512M;
+/** Whether to be less strict with non-cache file handle. */
+static bool g_fIgnoreNoCache = false;
+
+/** Set if g_szDir and friends are path relative to CWD rather than absolute. */
+static bool g_fRelativeDir = false;
+/** The length of g_szDir. */
+static size_t g_cchDir;
+/** The length of g_szEmptyDir. */
+static size_t g_cchEmptyDir;
+/** The length of g_szDeepDir. */
+static size_t g_cchDeepDir;
+
+/** The length of g_szCommsDir. */
+static size_t g_cchCommsDir;
+/** The length of g_szCommsSubDir. */
+static size_t g_cchCommsSubDir;
+
+/** The test directory (absolute). This will always have a trailing slash. */
+static char g_szDir[FSPERF_MAX_PATH];
+/** The test directory (absolute), 2nd copy for use with InDir2(). */
+static char g_szDir2[FSPERF_MAX_PATH];
+/** The empty test directory (absolute). This will always have a trailing slash. */
+static char g_szEmptyDir[FSPERF_MAX_PATH];
+/** The deep test directory (absolute). This will always have a trailing slash. */
+static char g_szDeepDir[FSPERF_MAX_PATH + _1K];
+
+/** The communcations directory. This will always have a trailing slash. */
+static char g_szCommsDir[FSPERF_MAX_PATH];
+/** The communcations subdirectory used for the actual communication. This will
+ * always have a trailing slash. */
+static char g_szCommsSubDir[FSPERF_MAX_PATH];
+
+/**
+ * Yield the CPU and stuff before starting a test run.
+ */
+DECLINLINE(void) fsPerfYield(void)
+{
+ RTThreadYield();
+ RTThreadYield();
+}
+
+
+/**
+ * Profiles the RTTimeNanoTS call, setting g_nsPerNanoTSCall.
+ */
+static void fsPerfNanoTS(void)
+{
+ fsPerfYield();
+
+ /* Make sure we start off on a changing timestamp on platforms will low time resoultion. */
+ uint64_t nsStart = RTTimeNanoTS();
+ uint64_t ns;
+ do
+ ns = RTTimeNanoTS();
+ while (ns == nsStart);
+ nsStart = ns;
+
+ /* Call it for 10 ms. */
+ uint32_t i = 0;
+ do
+ {
+ i++;
+ ns = RTTimeNanoTS();
+ }
+ while (ns - nsStart < RT_NS_10MS);
+
+ g_nsPerNanoTSCall = (ns - nsStart) / i;
+}
+
+
+/**
+ * Construct a path relative to the base test directory.
+ *
+ * @returns g_szDir.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InDir(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
+ memcpy(&g_szDir[g_cchDir], pszAppend, cchAppend);
+ g_szDir[g_cchDir + cchAppend] = '\0';
+ return &g_szDir[0];
+}
+
+
+/**
+ * Construct a path relative to the base test directory, 2nd copy.
+ *
+ * @returns g_szDir2.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InDir2(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
+ memcpy(g_szDir2, g_szDir, g_cchDir);
+ memcpy(&g_szDir2[g_cchDir], pszAppend, cchAppend);
+ g_szDir2[g_cchDir + cchAppend] = '\0';
+ return &g_szDir2[0];
+}
+
+
+/**
+ * Construct a path relative to the empty directory.
+ *
+ * @returns g_szEmptyDir.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InEmptyDir(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szEmptyDir[g_cchEmptyDir - 1] == RTPATH_SLASH);
+ memcpy(&g_szEmptyDir[g_cchEmptyDir], pszAppend, cchAppend);
+ g_szEmptyDir[g_cchEmptyDir + cchAppend] = '\0';
+ return &g_szEmptyDir[0];
+}
+
+
+/**
+ * Construct a path relative to the deep test directory.
+ *
+ * @returns g_szDeepDir.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InDeepDir(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szDeepDir[g_cchDeepDir - 1] == RTPATH_SLASH);
+ memcpy(&g_szDeepDir[g_cchDeepDir], pszAppend, cchAppend);
+ g_szDeepDir[g_cchDeepDir + cchAppend] = '\0';
+ return &g_szDeepDir[0];
+}
+
+
+
+/*********************************************************************************************************************************
+* Slave FsPerf Instance Interaction. *
+*********************************************************************************************************************************/
+
+/**
+ * Construct a path relative to the comms directory.
+ *
+ * @returns g_szCommsDir.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InCommsDir(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szCommsDir[g_cchCommsDir - 1] == RTPATH_SLASH);
+ memcpy(&g_szCommsDir[g_cchCommsDir], pszAppend, cchAppend);
+ g_szCommsDir[g_cchCommsDir + cchAppend] = '\0';
+ return &g_szCommsDir[0];
+}
+
+
+/**
+ * Construct a path relative to the comms sub-directory.
+ *
+ * @returns g_szCommsSubDir.
+ * @param pszAppend What to append.
+ * @param cchAppend How much to append.
+ */
+DECLINLINE(char *) InCommsSubDir(const char *pszAppend, size_t cchAppend)
+{
+ Assert(g_szCommsSubDir[g_cchCommsSubDir - 1] == RTPATH_SLASH);
+ memcpy(&g_szCommsSubDir[g_cchCommsSubDir], pszAppend, cchAppend);
+ g_szCommsSubDir[g_cchCommsSubDir + cchAppend] = '\0';
+ return &g_szCommsSubDir[0];
+}
+
+
+/**
+ * Creates a file under g_szCommsDir with the given content.
+ *
+ * Will modify g_szCommsDir to contain the given filename.
+ *
+ * @returns IPRT status code (fully bitched).
+ * @param pszFilename The filename.
+ * @param cchFilename The length of the filename.
+ * @param pszContent The file content.
+ * @param cchContent The length of the file content.
+ */
+static int FsPerfCommsWriteFile(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent)
+{
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, InCommsDir(pszFilename, cchFilename),
+ RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE_REPLACE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pszContent, cchContent, NULL);
+ if (RT_FAILURE(rc))
+ RTMsgError("Error writing %#zx bytes to '%s': %Rrc", cchContent, g_szCommsDir, rc);
+
+ int rc2 = RTFileClose(hFile);
+ if (RT_FAILURE(rc2))
+ {
+ RTMsgError("Error closing to '%s': %Rrc", g_szCommsDir, rc);
+ rc = rc2;
+ }
+ if (RT_SUCCESS(rc) && g_uVerbosity >= 3)
+ RTMsgInfo("comms: wrote '%s'\n", g_szCommsDir);
+ if (RT_FAILURE(rc))
+ RTFileDelete(g_szCommsDir);
+ }
+ else
+ RTMsgError("Failed to create '%s': %Rrc", g_szCommsDir, rc);
+ return rc;
+}
+
+
+/**
+ * Creates a file under g_szCommsDir with the given content, then renames it
+ * into g_szCommsSubDir.
+ *
+ * Will modify g_szCommsSubDir to contain the final filename and g_szCommsDir to
+ * hold the temporary one.
+ *
+ * @returns IPRT status code (fully bitched).
+ * @param pszFilename The filename.
+ * @param cchFilename The length of the filename.
+ * @param pszContent The file content.
+ * @param cchContent The length of the file content.
+ */
+static int FsPerfCommsWriteFileAndRename(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent)
+{
+ int rc = FsPerfCommsWriteFile(pszFilename, cchFilename, pszContent, cchContent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileRename(g_szCommsDir, InCommsSubDir(pszFilename, cchFilename), RTPATHRENAME_FLAGS_REPLACE);
+ if (RT_SUCCESS(rc) && g_uVerbosity >= 3)
+ RTMsgInfo("comms: placed '%s'\n", g_szCommsSubDir);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsDir, g_szCommsSubDir, rc);
+ RTFileDelete(g_szCommsDir);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Reads the given file from the comms subdir, ensuring that it is terminated by
+ * an EOF (0x1a) character.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_TRY_AGAIN if the file is incomplete.
+ * @retval VERR_FILE_TOO_BIG if the file is considered too big.
+ * @retval VERR_FILE_NOT_FOUND if not found.
+ *
+ * @param iSeqNo The sequence number.
+ * @param pszSuffix The filename suffix.
+ * @param ppszContent Where to return the content.
+ */
+static int FsPerfCommsReadFile(uint32_t iSeqNo, const char *pszSuffix, char **ppszContent)
+{
+ *ppszContent = NULL;
+
+ RTStrPrintf(&g_szCommsSubDir[g_cchCommsSubDir], sizeof(g_szCommsSubDir) - g_cchCommsSubDir, "%u%s", iSeqNo, pszSuffix);
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, g_szCommsSubDir, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbUsed = 0;
+ size_t cbAlloc = 1024;
+ char *pszBuf = (char *)RTMemAllocZ(cbAlloc);
+ for (;;)
+ {
+ /* Do buffer resizing. */
+ size_t cbMaxRead = cbAlloc - cbUsed - 1;
+ if (cbMaxRead < 8)
+ {
+ if (cbAlloc < _1M)
+ {
+ cbAlloc *= 2;
+ void *pvRealloced = RTMemRealloc(pszBuf, cbAlloc);
+ if (!pvRealloced)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ pszBuf = (char *)pvRealloced;
+ RT_BZERO(&pszBuf[cbAlloc / 2], cbAlloc);
+ cbMaxRead = cbAlloc - cbUsed - 1;
+ }
+ else
+ {
+ RTMsgError("File '%s' is too big - giving up at 1MB", g_szCommsSubDir);
+ rc = VERR_FILE_TOO_BIG;
+ break;
+ }
+ }
+
+ /* Do the reading. */
+ size_t cbActual = 0;
+ rc = RTFileRead(hFile, &pszBuf[cbUsed], cbMaxRead, &cbActual);
+ if (RT_SUCCESS(rc))
+ cbUsed += cbActual;
+ else
+ {
+ RTMsgError("Failed to read '%s': %Rrc", g_szCommsSubDir, rc);
+ break;
+ }
+
+ /* EOF? */
+ if (cbActual < cbMaxRead)
+ break;
+ }
+
+ RTFileClose(hFile);
+
+ /*
+ * Check if the file ends with the EOF marker.
+ */
+ if ( RT_SUCCESS(rc)
+ && ( cbUsed == 0
+ || pszBuf[cbUsed - 1] != FSPERF_EOF))
+ rc = VERR_TRY_AGAIN;
+
+ /*
+ * Return or free the content we've read.
+ */
+ if (RT_SUCCESS(rc))
+ *ppszContent = pszBuf;
+ else
+ RTMemFree(pszBuf);
+ }
+ else if (rc != VERR_FILE_NOT_FOUND && rc != VERR_SHARING_VIOLATION)
+ RTMsgError("Failed to open '%s': %Rrc", g_szCommsSubDir, rc);
+ return rc;
+}
+
+
+/**
+ * FsPerfCommsReadFile + renaming from the comms subdir to the comms dir.
+ *
+ * g_szCommsSubDir holds the original filename and g_szCommsDir the final
+ * filename on success.
+ */
+static int FsPerfCommsReadFileAndRename(uint32_t iSeqNo, const char *pszSuffix, const char *pszRenameSuffix, char **ppszContent)
+{
+ RTStrPrintf(&g_szCommsDir[g_cchCommsDir], sizeof(g_szCommsDir) - g_cchCommsDir, "%u%s", iSeqNo, pszRenameSuffix);
+ int rc = FsPerfCommsReadFile(iSeqNo, pszSuffix, ppszContent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileRename(g_szCommsSubDir, g_szCommsDir, RTPATHRENAME_FLAGS_REPLACE);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsSubDir, g_szCommsDir, rc);
+ RTMemFree(*ppszContent);
+ *ppszContent = NULL;
+ }
+ }
+ return rc;
+}
+
+
+/** The comms master sequence number. */
+static uint32_t g_iSeqNoMaster = 0;
+
+
+/**
+ * Sends a script to the remote comms slave.
+ *
+ * @returns IPRT status code giving the scripts execution status.
+ * @param pszScript The script.
+ */
+static int FsPerfCommsSend(const char *pszScript)
+{
+ /*
+ * Make sure the script is correctly terminated with an EOF control character.
+ */
+ size_t const cchScript = strlen(pszScript);
+ AssertReturn(cchScript > 0 && pszScript[cchScript - 1] == FSPERF_EOF, VERR_INVALID_PARAMETER);
+
+ /*
+ * Make sure the comms slave is running.
+ */
+ if (!RTFileExists(InCommsDir(RT_STR_TUPLE("slave.pid"))))
+ return VERR_PIPE_NOT_CONNECTED;
+
+ /*
+ * Format all the names we might want to check for.
+ */
+ char szSendNm[32];
+ size_t const cchSendNm = RTStrPrintf(szSendNm, sizeof(szSendNm), "%u-order.send", g_iSeqNoMaster);
+
+ char szAckNm[64];
+ size_t const cchAckNm = RTStrPrintf(szAckNm, sizeof(szAckNm), "%u-order.ack", g_iSeqNoMaster);
+
+ /*
+ * Produce the script file and submit it.
+ */
+ int rc = FsPerfCommsWriteFileAndRename(szSendNm, cchSendNm, pszScript, cchScript);
+ if (RT_SUCCESS(rc))
+ {
+ g_iSeqNoMaster++;
+
+ /*
+ * Wait for the result.
+ */
+ uint64_t const msTimeout = RT_MS_1MIN / 2;
+ uint64_t msStart = RTTimeMilliTS();
+ uint32_t msSleepX4 = 4;
+ for (;;)
+ {
+ /* Try read the result file: */
+ char *pszContent = NULL;
+ rc = FsPerfCommsReadFile(g_iSeqNoMaster - 1, "-order.done", &pszContent);
+ if (RT_SUCCESS(rc))
+ {
+ /* Split the result content into status code and error text: */
+ char *pszErrorText = strchr(pszContent, '\n');
+ if (pszErrorText)
+ {
+ *pszErrorText = '\0';
+ pszErrorText++;
+ }
+ else
+ {
+ char *pszEnd = strchr(pszContent, '\0');
+ Assert(pszEnd[-1] == FSPERF_EOF);
+ pszEnd[-1] = '\0';
+ }
+
+ /* Parse the status code: */
+ int32_t rcRemote = VERR_GENERAL_FAILURE;
+ rc = RTStrToInt32Full(pszContent, 0, &rcRemote);
+ if (rc != VINF_SUCCESS)
+ {
+ RTTestIFailed("FsPerfCommsSend: Failed to convert status code '%s'", pszContent);
+ rcRemote = VERR_GENERAL_FAILURE;
+ }
+
+ /* Display or return the text? */
+ if (RT_SUCCESS(rc) && g_uVerbosity >= 2)
+ RTMsgInfo("comms: order #%u: %Rrc%s%s\n",
+ g_iSeqNoMaster - 1, rcRemote, *pszErrorText ? " - " : "", pszErrorText);
+
+ RTMemFree(pszContent);
+ return rcRemote;
+ }
+
+ if (rc == VERR_TRY_AGAIN)
+ msSleepX4 = 4;
+
+ /* Check for timeout. */
+ if (RTTimeMilliTS() - msStart > msTimeout)
+ {
+ if (RT_SUCCESS(rc) && g_uVerbosity >= 2)
+ RTMsgInfo("comms: timed out waiting for order #%u'\n", g_iSeqNoMaster - 1);
+
+ rc = RTFileDelete(InCommsSubDir(szSendNm, cchSendNm));
+ if (RT_SUCCESS(rc))
+ {
+ g_iSeqNoMaster--;
+ rc = VERR_TIMEOUT;
+ }
+ else if (RTFileExists(InCommsDir(szAckNm, cchAckNm)))
+ rc = VERR_PIPE_BUSY;
+ else
+ rc = VERR_PIPE_IO_ERROR;
+ break;
+ }
+
+ /* Sleep a little while. */
+ msSleepX4++;
+ RTThreadSleep(msSleepX4 / 4);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Shuts down the comms slave if it exists.
+ */
+static void FsPerfCommsShutdownSlave(void)
+{
+ static bool s_fAlreadyShutdown = false;
+ if (g_szCommsDir[0] != '\0' && !s_fAlreadyShutdown)
+ {
+ s_fAlreadyShutdown = true;
+ FsPerfCommsSend("exit" FSPERF_EOF_STR);
+
+ g_szCommsDir[g_cchCommsDir] = '\0';
+ int rc = RTDirRemoveRecursive(g_szCommsDir, RTDIRRMREC_F_CONTENT_AND_DIR | (g_fRelativeDir ? RTDIRRMREC_F_NO_ABS_PATH : 0));
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szCommsDir, rc);
+ }
+}
+
+
+
+/*********************************************************************************************************************************
+* Comms Slave *
+*********************************************************************************************************************************/
+
+typedef struct FSPERFCOMMSSLAVESTATE
+{
+ uint32_t iSeqNo;
+ bool fTerminate;
+ RTEXITCODE rcExit;
+ RTFILE ahFiles[8];
+ char *apszFilenames[8];
+
+ /** The current command. */
+ const char *pszCommand;
+ /** The current line number. */
+ uint32_t iLineNo;
+ /** The current line content. */
+ const char *pszLine;
+ /** Where to return extra error info text. */
+ RTERRINFOSTATIC ErrInfo;
+} FSPERFCOMMSSLAVESTATE;
+
+
+static void FsPerfSlaveStateInit(FSPERFCOMMSSLAVESTATE *pState)
+{
+ pState->iSeqNo = 0;
+ pState->fTerminate = false;
+ pState->rcExit = RTEXITCODE_SUCCESS;
+ unsigned i = RT_ELEMENTS(pState->ahFiles);
+ while (i-- > 0)
+ {
+ pState->ahFiles[i] = NIL_RTFILE;
+ pState->apszFilenames[i] = NULL;
+ }
+ RTErrInfoInitStatic(&pState->ErrInfo);
+}
+
+
+static void FsPerfSlaveStateCleanup(FSPERFCOMMSSLAVESTATE *pState)
+{
+ unsigned i = RT_ELEMENTS(pState->ahFiles);
+ while (i-- > 0)
+ {
+ if (pState->ahFiles[i] != NIL_RTFILE)
+ {
+ RTFileClose(pState->ahFiles[i]);
+ pState->ahFiles[i] = NIL_RTFILE;
+ }
+ if (pState->apszFilenames[i] != NULL)
+ {
+ RTStrFree(pState->apszFilenames[i]);
+ pState->apszFilenames[i] = NULL;
+ }
+ }
+}
+
+
+/** Helper reporting a error. */
+static int FsPerfSlaveError(FSPERFCOMMSSLAVESTATE *pState, int rc, const char *pszError, ...)
+{
+ va_list va;
+ va_start(va, pszError);
+ RTErrInfoSetF(&pState->ErrInfo.Core, VERR_PARSE_ERROR, "line %u: %s: error: %N",
+ pState->iLineNo, pState->pszCommand, pszError, &va);
+ va_end(va);
+ return rc;
+}
+
+
+/** Helper reporting a syntax error. */
+static int FsPerfSlaveSyntax(FSPERFCOMMSSLAVESTATE *pState, const char *pszError, ...)
+{
+ va_list va;
+ va_start(va, pszError);
+ RTErrInfoSetF(&pState->ErrInfo.Core, VERR_PARSE_ERROR, "line %u: %s: syntax error: %N",
+ pState->iLineNo, pState->pszCommand, pszError, &va);
+ va_end(va);
+ return VERR_PARSE_ERROR;
+}
+
+
+/** Helper for parsing an unsigned 64-bit integer argument. */
+static int FsPerfSlaveParseU64(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, const char *pszName,
+ unsigned uBase, uint64_t uMin, uint64_t uLast, uint64_t *puValue)
+{
+ *puValue = uMin;
+ uint64_t uValue;
+ int rc = RTStrToUInt64Full(pszArg, uBase, &uValue);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveSyntax(pState, "invalid %s: %s (RTStrToUInt64Full -> %Rrc)", pszName, pszArg, rc);
+ if (uValue < uMin || uValue > uLast)
+ return FsPerfSlaveSyntax(pState, "%s is out of range: %u, valid range %u..%u", pszName, uValue, uMin, uLast);
+ *puValue = uValue;
+ return VINF_SUCCESS;
+}
+
+
+/** Helper for parsing an unsigned 32-bit integer argument. */
+static int FsPerfSlaveParseU32(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, const char *pszName,
+ unsigned uBase, uint32_t uMin, uint32_t uLast, uint32_t *puValue)
+{
+ *puValue = uMin;
+ uint32_t uValue;
+ int rc = RTStrToUInt32Full(pszArg, uBase, &uValue);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveSyntax(pState, "invalid %s: %s (RTStrToUInt32Full -> %Rrc)", pszName, pszArg, rc);
+ if (uValue < uMin || uValue > uLast)
+ return FsPerfSlaveSyntax(pState, "%s is out of range: %u, valid range %u..%u", pszName, uValue, uMin, uLast);
+ *puValue = uValue;
+ return VINF_SUCCESS;
+}
+
+
+/** Helper for parsing a file handle index argument. */
+static int FsPerfSlaveParseFileIdx(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, uint32_t *pidxFile)
+{
+ return FsPerfSlaveParseU32(pState, pszArg, "file index", 0, 0, RT_ELEMENTS(pState->ahFiles) - 1, pidxFile);
+}
+
+
+/**
+ * 'open {idxFile} {filename} {access} {disposition} [sharing] [mode]'
+ */
+static int FsPerfSlaveHandleOpen(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs > 1 + 6 || cArgs < 1 + 4)
+ return FsPerfSlaveSyntax(pState, "takes four to six arguments, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ const char *pszFilename = papszArgs[2];
+
+ uint64_t fOpen = 0;
+ rc = RTFileModeToFlagsEx(papszArgs[3], papszArgs[4], papszArgs[5], &fOpen);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveSyntax(pState, "failed to parse access (%s), disposition (%s) and sharing (%s): %Rrc",
+ papszArgs[3], papszArgs[4], papszArgs[5] ? papszArgs[5] : "", rc);
+
+ uint32_t uMode = 0660;
+ if (cArgs >= 1 + 6)
+ {
+ rc = FsPerfSlaveParseU32(pState, papszArgs[6], "mode", 8, 0, 0777, &uMode);
+ if (RT_FAILURE(rc))
+ return rc;
+ fOpen |= uMode << RTFILE_O_CREATE_MODE_SHIFT;
+ }
+
+ /*
+ * Is there already a file assigned to the file handle index?
+ */
+ if (pState->ahFiles[idxFile] != NIL_RTFILE)
+ return FsPerfSlaveError(pState, VERR_RESOURCE_BUSY, "handle #%u is already in use for '%s'",
+ idxFile, pState->apszFilenames[idxFile]);
+
+ /*
+ * Check the filename length.
+ */
+ size_t const cchFilename = strlen(pszFilename);
+ if (g_cchDir + cchFilename >= sizeof(g_szDir))
+ return FsPerfSlaveError(pState, VERR_FILENAME_TOO_LONG, "'%.*s%s'", g_cchDir, g_szDir, pszFilename);
+
+ /*
+ * Duplicate the name and execute the command.
+ */
+ char *pszDup = RTStrDup(pszFilename);
+ if (!pszDup)
+ return FsPerfSlaveError(pState, VERR_NO_STR_MEMORY, "out of memory");
+
+ RTFILE hFile = NIL_RTFILE;
+ rc = RTFileOpen(&hFile, InDir(pszFilename, cchFilename), fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ pState->ahFiles[idxFile] = hFile;
+ pState->apszFilenames[idxFile] = pszDup;
+ }
+ else
+ {
+ RTStrFree(pszDup);
+ rc = FsPerfSlaveError(pState, rc, "%s: %Rrc", pszFilename, rc);
+ }
+ return rc;
+}
+
+
+/**
+ * 'close {idxFile}'
+ */
+static int FsPerfSlaveHandleClose(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs > 1 + 1)
+ return FsPerfSlaveSyntax(pState, "takes exactly one argument, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do it.
+ */
+ rc = RTFileClose(pState->ahFiles[idxFile]);
+ if (RT_SUCCESS(rc))
+ {
+ pState->ahFiles[idxFile] = NIL_RTFILE;
+ RTStrFree(pState->apszFilenames[idxFile]);
+ pState->apszFilenames[idxFile] = NULL;
+ }
+ }
+ return rc;
+}
+
+/** @name Patterns for 'writepattern'
+ * @{ */
+static uint8_t const g_abPattern0[] = { 0xf0 };
+static uint8_t const g_abPattern1[] = { 0xf1 };
+static uint8_t const g_abPattern2[] = { 0xf2 };
+static uint8_t const g_abPattern3[] = { 0xf3 };
+static uint8_t const g_abPattern4[] = { 0xf4 };
+static uint8_t const g_abPattern5[] = { 0xf5 };
+static uint8_t const g_abPattern6[] = { 0xf6 };
+static uint8_t const g_abPattern7[] = { 0xf7 };
+static uint8_t const g_abPattern8[] = { 0xf8 };
+static uint8_t const g_abPattern9[] = { 0xf9 };
+static uint8_t const g_abPattern10[] = { 0x1f, 0x4e, 0x99, 0xec, 0x71, 0x71, 0x48, 0x0f, 0xa7, 0x5c, 0xb4, 0x5a, 0x1f, 0xc7, 0xd0, 0x93 };
+static struct
+{
+ uint8_t const *pb;
+ uint32_t cb;
+} const g_aPatterns[] =
+{
+ { g_abPattern0, sizeof(g_abPattern0) },
+ { g_abPattern1, sizeof(g_abPattern1) },
+ { g_abPattern2, sizeof(g_abPattern2) },
+ { g_abPattern3, sizeof(g_abPattern3) },
+ { g_abPattern4, sizeof(g_abPattern4) },
+ { g_abPattern5, sizeof(g_abPattern5) },
+ { g_abPattern6, sizeof(g_abPattern6) },
+ { g_abPattern7, sizeof(g_abPattern7) },
+ { g_abPattern8, sizeof(g_abPattern8) },
+ { g_abPattern9, sizeof(g_abPattern9) },
+ { g_abPattern10, sizeof(g_abPattern10) },
+};
+/** @} */
+
+/**
+ * 'writepattern {idxFile} {offFile} {idxPattern} {cbToWrite}'
+ */
+static int FsPerfSlaveHandleWritePattern(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs > 1 + 4)
+ return FsPerfSlaveSyntax(pState, "takes exactly four arguments, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t offFile;
+ rc = FsPerfSlaveParseU64(pState, papszArgs[2], "file offset", 0, 0, UINT64_MAX / 4, &offFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t idxPattern;
+ rc = FsPerfSlaveParseU32(pState, papszArgs[3], "pattern index", 0, 0, RT_ELEMENTS(g_aPatterns) - 1, &idxPattern);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t cbToWrite;
+ rc = FsPerfSlaveParseU64(pState, papszArgs[4], "number of bytes to write", 0, 0, _1G, &cbToWrite);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pState->ahFiles[idxFile] == NIL_RTFILE)
+ return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
+
+ /*
+ * Allocate a suitable buffer.
+ */
+ size_t cbMaxBuf = RT_MIN(_2M, g_cbMaxBuffer);
+ size_t cbBuf = cbToWrite >= cbMaxBuf ? cbMaxBuf : RT_ALIGN_Z((size_t)cbToWrite, 512);
+ uint8_t *pbBuf = (uint8_t *)RTMemTmpAlloc(cbBuf);
+ if (!pbBuf)
+ {
+ cbBuf = _4K;
+ pbBuf = (uint8_t *)RTMemTmpAlloc(cbBuf);
+ if (!pbBuf)
+ return FsPerfSlaveError(pState, VERR_NO_TMP_MEMORY, "failed to allocate 4KB for buffers");
+ }
+
+ /*
+ * Fill 1 byte patterns before we start looping.
+ */
+ if (g_aPatterns[idxPattern].cb == 1)
+ memset(pbBuf, g_aPatterns[idxPattern].pb[0], cbBuf);
+
+ /*
+ * The write loop.
+ */
+ uint32_t offPattern = 0;
+ while (cbToWrite > 0)
+ {
+ /*
+ * Fill the buffer if multi-byte pattern (single byte patterns are handled before the loop):
+ */
+ if (g_aPatterns[idxPattern].cb > 1)
+ {
+ uint32_t const cbSrc = g_aPatterns[idxPattern].cb;
+ uint8_t const * const pbSrc = g_aPatterns[idxPattern].pb;
+ size_t cbDst = cbBuf;
+ uint8_t *pbDst = pbBuf;
+
+ /* first iteration, potential partial pattern. */
+ if (offPattern >= cbSrc)
+ offPattern = 0;
+ size_t cbThis1 = RT_MIN(g_aPatterns[idxPattern].cb - offPattern, cbToWrite);
+ memcpy(pbDst, &pbSrc[offPattern], cbThis1);
+ cbDst -= cbThis1;
+ if (cbDst > 0)
+ {
+ pbDst += cbThis1;
+ offPattern = 0;
+
+ /* full patterns */
+ while (cbDst >= cbSrc)
+ {
+ memcpy(pbDst, pbSrc, cbSrc);
+ pbDst += cbSrc;
+ cbDst -= cbSrc;
+ }
+
+ /* partial final copy */
+ if (cbDst > 0)
+ {
+ memcpy(pbDst, pbSrc, cbDst);
+ offPattern = (uint32_t)cbDst;
+ }
+ }
+ }
+
+ /*
+ * Write.
+ */
+ size_t const cbThisWrite = (size_t)RT_MIN(cbToWrite, cbBuf);
+ rc = RTFileWriteAt(pState->ahFiles[idxFile], offFile, pbBuf, cbThisWrite, NULL);
+ if (RT_FAILURE(rc))
+ {
+ FsPerfSlaveError(pState, rc, "error writing %#zx bytes at %#RX64: %Rrc (file: %s)",
+ cbThisWrite, offFile, rc, pState->apszFilenames[idxFile]);
+ break;
+ }
+
+ offFile += cbThisWrite;
+ cbToWrite -= cbThisWrite;
+ }
+
+ RTMemTmpFree(pbBuf);
+ return rc;
+}
+
+
+/**
+ * 'truncate {idxFile} {cbFile}'
+ */
+static int FsPerfSlaveHandleTruncate(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs != 1 + 2)
+ return FsPerfSlaveSyntax(pState, "takes exactly two arguments, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t cbFile;
+ rc = FsPerfSlaveParseU64(pState, papszArgs[2], "new file size", 0, 0, UINT64_MAX / 4, &cbFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pState->ahFiles[idxFile] == NIL_RTFILE)
+ return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
+
+ /*
+ * Execute.
+ */
+ rc = RTFileSetSize(pState->ahFiles[idxFile], cbFile);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveError(pState, rc, "failed to set file size to %#RX64: %Rrc (file: %s)",
+ cbFile, rc, pState->apszFilenames[idxFile]);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 'futimes {idxFile} {modified|0} [access|0] [change|0] [birth|0]'
+ */
+static int FsPerfSlaveHandleFUTimes(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs < 1 + 2 || cArgs > 1 + 5)
+ return FsPerfSlaveSyntax(pState, "takes between two and five arguments, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t nsModifiedTime;
+ rc = FsPerfSlaveParseU64(pState, papszArgs[2], "modified time", 0, 0, UINT64_MAX, &nsModifiedTime);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t nsAccessTime = 0;
+ if (cArgs >= 1 + 3)
+ {
+ rc = FsPerfSlaveParseU64(pState, papszArgs[3], "access time", 0, 0, UINT64_MAX, &nsAccessTime);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ uint64_t nsChangeTime = 0;
+ if (cArgs >= 1 + 4)
+ {
+ rc = FsPerfSlaveParseU64(pState, papszArgs[4], "change time", 0, 0, UINT64_MAX, &nsChangeTime);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ uint64_t nsBirthTime = 0;
+ if (cArgs >= 1 + 5)
+ {
+ rc = FsPerfSlaveParseU64(pState, papszArgs[4], "birth time", 0, 0, UINT64_MAX, &nsBirthTime);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (pState->ahFiles[idxFile] == NIL_RTFILE)
+ return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
+
+ /*
+ * Execute.
+ */
+ RTTIMESPEC ModifiedTime;
+ RTTIMESPEC AccessTime;
+ RTTIMESPEC ChangeTime;
+ RTTIMESPEC BirthTime;
+ rc = RTFileSetTimes(pState->ahFiles[idxFile],
+ nsAccessTime ? RTTimeSpecSetNano(&AccessTime, nsAccessTime) : NULL,
+ nsModifiedTime ? RTTimeSpecSetNano(&ModifiedTime, nsModifiedTime) : NULL,
+ nsChangeTime ? RTTimeSpecSetNano(&ChangeTime, nsChangeTime) : NULL,
+ nsBirthTime ? RTTimeSpecSetNano(&BirthTime, nsBirthTime) : NULL);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveError(pState, rc, "failed to set file times to %RI64, %RI64, %RI64, %RI64: %Rrc (file: %s)",
+ nsModifiedTime, nsAccessTime, nsChangeTime, nsBirthTime, rc, pState->apszFilenames[idxFile]);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 'fchmod {idxFile} {cbFile}'
+ */
+static int FsPerfSlaveHandleFChMod(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs != 1 + 2)
+ return FsPerfSlaveSyntax(pState, "takes exactly two arguments, not %u", cArgs);
+
+ uint32_t idxFile;
+ int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t fAttribs;
+ rc = FsPerfSlaveParseU32(pState, papszArgs[2], "new file attributes", 0, 0, UINT32_MAX, &fAttribs);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pState->ahFiles[idxFile] == NIL_RTFILE)
+ return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
+
+ /*
+ * Execute.
+ */
+ rc = RTFileSetMode(pState->ahFiles[idxFile], fAttribs);
+ if (RT_FAILURE(rc))
+ return FsPerfSlaveError(pState, rc, "failed to set file mode to %#RX32: %Rrc (file: %s)",
+ fAttribs, rc, pState->apszFilenames[idxFile]);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 'reset'
+ */
+static int FsPerfSlaveHandleReset(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs > 1)
+ return FsPerfSlaveSyntax(pState, "takes zero arguments, not %u", cArgs);
+ RT_NOREF(papszArgs);
+
+ /*
+ * Execute the command.
+ */
+ FsPerfSlaveStateCleanup(pState);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 'exit [exitcode]'
+ */
+static int FsPerfSlaveHandleExit(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
+{
+ /*
+ * Parse parameters.
+ */
+ if (cArgs > 1 + 1)
+ return FsPerfSlaveSyntax(pState, "takes zero or one argument, not %u", cArgs);
+
+ if (cArgs >= 1 + 1)
+ {
+ uint32_t uExitCode;
+ int rc = FsPerfSlaveParseU32(pState, papszArgs[1], "exit code", 0, 0, 127, &uExitCode);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Execute the command.
+ */
+ pState->rcExit = (RTEXITCODE)uExitCode;
+ }
+ pState->fTerminate = true;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Executes a script line.
+ */
+static int FsPerfSlaveExecuteLine(FSPERFCOMMSSLAVESTATE *pState, char *pszLine)
+{
+ /*
+ * Parse the command line using bourne shell quoting style.
+ */
+ char **papszArgs;
+ int cArgs;
+ int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Failed to parse line %u: %s", pState->iLineNo, pszLine);
+ if (cArgs <= 0)
+ {
+ RTGetOptArgvFree(papszArgs);
+ return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "No command found on line %u: %s", pState->iLineNo, pszLine);
+ }
+
+ /*
+ * Execute the command.
+ */
+ static const struct
+ {
+ const char *pszCmd;
+ size_t cchCmd;
+ int (*pfnHandler)(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs);
+ } s_aHandlers[] =
+ {
+ { RT_STR_TUPLE("open"), FsPerfSlaveHandleOpen },
+ { RT_STR_TUPLE("close"), FsPerfSlaveHandleClose },
+ { RT_STR_TUPLE("writepattern"), FsPerfSlaveHandleWritePattern },
+ { RT_STR_TUPLE("truncate"), FsPerfSlaveHandleTruncate },
+ { RT_STR_TUPLE("futimes"), FsPerfSlaveHandleFUTimes},
+ { RT_STR_TUPLE("fchmod"), FsPerfSlaveHandleFChMod },
+ { RT_STR_TUPLE("reset"), FsPerfSlaveHandleReset },
+ { RT_STR_TUPLE("exit"), FsPerfSlaveHandleExit },
+ };
+ const char * const pszCmd = papszArgs[0];
+ size_t const cchCmd = strlen(pszCmd);
+ for (size_t i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
+ if ( s_aHandlers[i].cchCmd == cchCmd
+ && memcmp(pszCmd, s_aHandlers[i].pszCmd, cchCmd) == 0)
+ {
+ pState->pszCommand = s_aHandlers[i].pszCmd;
+ rc = s_aHandlers[i].pfnHandler(pState, papszArgs, cArgs);
+ RTGetOptArgvFree(papszArgs);
+ return rc;
+ }
+
+ rc = RTErrInfoSetF(&pState->ErrInfo.Core, VERR_NOT_FOUND, "Command on line %u not found: %s", pState->iLineNo, pszLine);
+ RTGetOptArgvFree(papszArgs);
+ return rc;
+}
+
+
+/**
+ * Executes a script.
+ */
+static int FsPerfSlaveExecuteScript(FSPERFCOMMSSLAVESTATE *pState, char *pszContent)
+{
+ /*
+ * Validate the encoding.
+ */
+ int rc = RTStrValidateEncoding(pszContent);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Invalid UTF-8 encoding");
+
+ /*
+ * Work the script content line by line.
+ */
+ pState->iLineNo = 0;
+ while (*pszContent != FSPERF_EOF && *pszContent != '\0')
+ {
+ pState->iLineNo++;
+
+ /* Figure the current line and move pszContent ahead: */
+ char *pszLine = RTStrStripL(pszContent);
+ char *pszEol = strchr(pszLine, '\n');
+ if (pszEol)
+ pszContent = pszEol + 1;
+ else
+ {
+ pszEol = strchr(pszLine, FSPERF_EOF);
+ AssertStmt(pszEol, pszEol = strchr(pszLine, '\0'));
+ pszContent = pszEol;
+ }
+
+ /* Terminate and strip it: */
+ *pszEol = '\0';
+ pszLine = RTStrStrip(pszLine);
+
+ /* Skip empty lines and comment lines: */
+ if (*pszLine == '\0' || *pszLine == '#')
+ continue;
+
+ /* Execute the line: */
+ pState->pszLine = pszLine;
+ rc = FsPerfSlaveExecuteLine(pState, pszLine);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ return rc;
+}
+
+
+/**
+ * Communication slave.
+ *
+ * @returns exit code.
+ */
+static int FsPerfCommsSlave(void)
+{
+ /*
+ * Make sure we've got a directory and create it and it's subdir.
+ */
+ if (g_cchCommsDir == 0)
+ return RTMsgError("no communcation directory was specified (-C)");
+
+ int rc = RTDirCreateFullPath(g_szCommsSubDir, 0775);
+ if (RT_FAILURE(rc))
+ return RTMsgError("Failed to create '%s': %Rrc", g_szCommsSubDir, rc);
+
+ /*
+ * Signal that we're here.
+ */
+ char szTmp[_4K];
+ rc = FsPerfCommsWriteFile(RT_STR_TUPLE("slave.pid"), szTmp, RTStrPrintf(szTmp, sizeof(szTmp),
+ "%u" FSPERF_EOF_STR, RTProcSelf()));
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+
+ /*
+ * Processing loop.
+ */
+ FSPERFCOMMSSLAVESTATE State;
+ FsPerfSlaveStateInit(&State);
+ uint32_t msSleep = 1;
+ while (!State.fTerminate)
+ {
+ /*
+ * Try read the next command script.
+ */
+ char *pszContent = NULL;
+ rc = FsPerfCommsReadFileAndRename(State.iSeqNo, "-order.send", "-order.ack", &pszContent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Execute it.
+ */
+ RTErrInfoInitStatic(&State.ErrInfo);
+ rc = FsPerfSlaveExecuteScript(&State, pszContent);
+
+ /*
+ * Write the result.
+ */
+ char szResult[64];
+ size_t cchResult = RTStrPrintf(szResult, sizeof(szResult), "%u-order.done", State.iSeqNo);
+ size_t cchTmp = RTStrPrintf(szTmp, sizeof(szTmp), "%d\n%s" FSPERF_EOF_STR,
+ rc, RTErrInfoIsSet(&State.ErrInfo.Core) ? State.ErrInfo.Core.pszMsg : "");
+ FsPerfCommsWriteFileAndRename(szResult, cchResult, szTmp, cchTmp);
+ State.iSeqNo++;
+
+ msSleep = 1;
+ }
+
+ /*
+ * Wait a little and check again.
+ */
+ RTThreadSleep(msSleep);
+ if (msSleep < 128)
+ msSleep++;
+ }
+
+ /*
+ * Remove the we're here indicator and quit.
+ */
+ RTFileDelete(InCommsDir(RT_STR_TUPLE("slave.pid")));
+ FsPerfSlaveStateCleanup(&State);
+ return State.rcExit;
+}
+
+
+
+/*********************************************************************************************************************************
+* Tests *
+*********************************************************************************************************************************/
+
+/**
+ * Prepares the test area.
+ * @returns VBox status code.
+ */
+static int fsPrepTestArea(void)
+{
+ /* The empty subdir and associated globals: */
+ static char s_szEmpty[] = "empty";
+ memcpy(g_szEmptyDir, g_szDir, g_cchDir);
+ memcpy(&g_szEmptyDir[g_cchDir], s_szEmpty, sizeof(s_szEmpty));
+ g_cchEmptyDir = g_cchDir + sizeof(s_szEmpty) - 1;
+ RTTESTI_CHECK_RC_RET(RTDirCreate(g_szEmptyDir, 0755, 0), VINF_SUCCESS, rcCheck);
+ g_szEmptyDir[g_cchEmptyDir++] = RTPATH_SLASH;
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Empty dir: %s\n", g_szEmptyDir);
+
+ /* Deep directory: */
+ memcpy(g_szDeepDir, g_szDir, g_cchDir);
+ g_cchDeepDir = g_cchDir;
+ do
+ {
+ static char const s_szSub[] = "d" RTPATH_SLASH_STR;
+ memcpy(&g_szDeepDir[g_cchDeepDir], s_szSub, sizeof(s_szSub));
+ g_cchDeepDir += sizeof(s_szSub) - 1;
+ int rc = RTDirCreate(g_szDeepDir, 0755, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTDirCreate(g_szDeepDir=%s) -> %Rrc\n", g_szDeepDir, rc);
+ return rc;
+ }
+ } while (g_cchDeepDir < 176);
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Deep dir: %s\n", g_szDeepDir);
+
+ /* Create known file in both deep and shallow dirs: */
+ RTFILE hKnownFile;
+ RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDir(RT_STR_TUPLE("known-file")),
+ RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
+ VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
+
+ RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDeepDir(RT_STR_TUPLE("known-file")),
+ RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
+ VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Create a name list entry.
+ * @returns Pointer to the entry, NULL if out of memory.
+ * @param pchName The name.
+ * @param cchName The name length.
+ */
+PFSPERFNAMEENTRY fsPerfCreateNameEntry(const char *pchName, size_t cchName)
+{
+ PFSPERFNAMEENTRY pEntry = (PFSPERFNAMEENTRY)RTMemAllocVar(RT_UOFFSETOF_DYN(FSPERFNAMEENTRY, szName[cchName + 1]));
+ if (pEntry)
+ {
+ RTListInit(&pEntry->Entry);
+ pEntry->cchName = (uint16_t)cchName;
+ memcpy(pEntry->szName, pchName, cchName);
+ pEntry->szName[cchName] = '\0';
+ }
+ return pEntry;
+}
+
+
+static int fsPerfManyTreeRecursiveDirCreator(size_t cchDir, uint32_t iDepth)
+{
+ PFSPERFNAMEENTRY pEntry = fsPerfCreateNameEntry(g_szDir, cchDir);
+ RTTESTI_CHECK_RET(pEntry, VERR_NO_MEMORY);
+ RTListAppend(&g_ManyTreeHead, &pEntry->Entry);
+
+ RTTESTI_CHECK_RC_RET(RTDirCreate(g_szDir, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
+ VINF_SUCCESS, rcCheck);
+
+ if (iDepth < g_cManyTreeDepth)
+ for (uint32_t i = 0; i < g_cManyTreeSubdirsPerDir; i++)
+ {
+ size_t cchSubDir = RTStrPrintf(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, "d%02u" RTPATH_SLASH_STR, i);
+ RTTESTI_CHECK_RC_RET(fsPerfManyTreeRecursiveDirCreator(cchDir + cchSubDir, iDepth + 1), VINF_SUCCESS, rcCheck);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+void fsPerfManyFiles(void)
+{
+ RTTestISub("manyfiles");
+
+ /*
+ * Create a sub-directory with like 10000 files in it.
+ *
+ * This does push the directory organization of the underlying file system,
+ * which is something we might not want to profile with shared folders. It
+ * is however useful for directory enumeration.
+ */
+ RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("manyfiles")), 0755,
+ RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
+ VINF_SUCCESS);
+
+ size_t offFilename = strlen(g_szDir);
+ g_szDir[offFilename++] = RTPATH_SLASH;
+
+ fsPerfYield();
+ RTFILE hFile;
+ uint64_t const nsStart = RTTimeNanoTS();
+ for (uint32_t i = 0; i < g_cManyFiles; i++)
+ {
+ RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
+ }
+ uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
+ RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Creating %u empty files in single directory", g_cManyFiles);
+ RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (single dir)");
+
+ /*
+ * Create a bunch of directories with exacly 32 files in each, hoping to
+ * avoid any directory organization artifacts.
+ */
+ /* Create the directories first, building a list of them for simplifying iteration: */
+ RTListInit(&g_ManyTreeHead);
+ InDir(RT_STR_TUPLE("manytree" RTPATH_SLASH_STR));
+ RTTESTI_CHECK_RC_RETV(fsPerfManyTreeRecursiveDirCreator(strlen(g_szDir), 0), VINF_SUCCESS);
+
+ /* Create the zero byte files: */
+ fsPerfYield();
+ uint64_t const nsStart2 = RTTimeNanoTS();
+ uint32_t cFiles = 0;
+ PFSPERFNAMEENTRY pCur;
+ RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry)
+ {
+ char szPath[FSPERF_MAX_PATH];
+ memcpy(szPath, pCur->szName, pCur->cchName);
+ for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++)
+ {
+ RTStrFormatU32(&szPath[pCur->cchName], sizeof(szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD);
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, szPath, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
+ cFiles++;
+ }
+ }
+ uint64_t const cNsElapsed2 = RTTimeNanoTS() - nsStart2;
+ RTTestIValueF(cNsElapsed2, RTTESTUNIT_NS, "Creating %u empty files in tree", cFiles);
+ RTTestIValueF(cNsElapsed2 / cFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (tree)");
+ RTTESTI_CHECK(g_cManyTreeFiles == cFiles);
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceReadonly(const char *pszFile)
+{
+ RTFILE hFile;
+ RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceWriteonly(const char *pszFile)
+{
+ RTFILE hFile;
+ RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+/** @note tstRTFileOpenEx-1.cpp has a copy of this code. */
+static void tstOpenExTest(unsigned uLine, int cbExist, int cbNext, const char *pszFilename, uint64_t fAction,
+ int rcExpect, RTFILEACTION enmActionExpected)
+{
+ uint64_t const fCreateMode = (0644 << RTFILE_O_CREATE_MODE_SHIFT);
+ RTFILE hFile;
+ int rc;
+
+ /*
+ * File existence and size.
+ */
+ bool fOkay = false;
+ RTFSOBJINFO ObjInfo;
+ rc = RTPathQueryInfoEx(pszFilename, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ fOkay = cbExist == (int64_t)ObjInfo.cbObject;
+ else
+ fOkay = rc == VERR_FILE_NOT_FOUND && cbExist < 0;
+ if (!fOkay)
+ {
+ if (cbExist >= 0)
+ {
+ rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | fCreateMode);
+ if (RT_SUCCESS(rc))
+ {
+ while (cbExist > 0)
+ {
+ int cbToWrite = (int)strlen(pszFilename);
+ if (cbToWrite > cbExist)
+ cbToWrite = cbExist;
+ rc = RTFileWrite(hFile, pszFilename, cbToWrite, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("%u: RTFileWrite(%s,%#x) -> %Rrc\n", uLine, pszFilename, cbToWrite, rc);
+ break;
+ }
+ cbExist -= cbToWrite;
+ }
+
+ RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
+ }
+ else
+ RTTestIFailed("%u: RTFileDelete(%s) -> %Rrc\n", uLine, pszFilename, rc);
+
+ }
+ else
+ {
+ rc = RTFileDelete(pszFilename);
+ if (rc != VINF_SUCCESS && rc != VERR_FILE_NOT_FOUND)
+ RTTestIFailed("%u: RTFileDelete(%s) -> %Rrc\n", uLine, pszFilename, rc);
+ }
+ }
+
+ /*
+ * The actual test.
+ */
+ RTFILEACTION enmActuallyTaken = RTFILEACTION_END;
+ hFile = NIL_RTFILE;
+ rc = RTFileOpenEx(pszFilename, fAction | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | fCreateMode, &hFile, &enmActuallyTaken);
+ if ( rc != rcExpect
+ || enmActuallyTaken != enmActionExpected
+ || (RT_SUCCESS(rc) ? hFile == NIL_RTFILE : hFile != NIL_RTFILE))
+ RTTestIFailed("%u: RTFileOpenEx(%s, %#llx) -> %Rrc + %d (hFile=%p), expected %Rrc + %d\n",
+ uLine, pszFilename, fAction, rc, enmActuallyTaken, hFile, rcExpect, enmActionExpected);
+ if (RT_SUCCESS(rc))
+ {
+ if ( enmActionExpected == RTFILEACTION_REPLACED
+ || enmActionExpected == RTFILEACTION_TRUNCATED)
+ {
+ uint8_t abBuf[16];
+ rc = RTFileRead(hFile, abBuf, 1, NULL);
+ if (rc != VERR_EOF)
+ RTTestIFailed("%u: RTFileRead(%s,,1,) -> %Rrc, expected VERR_EOF\n", uLine, pszFilename, rc);
+ }
+
+ while (cbNext > 0)
+ {
+ int cbToWrite = (int)strlen(pszFilename);
+ if (cbToWrite > cbNext)
+ cbToWrite = cbNext;
+ rc = RTFileWrite(hFile, pszFilename, cbToWrite, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("%u: RTFileWrite(%s,%#x) -> %Rrc\n", uLine, pszFilename, cbToWrite, rc);
+ break;
+ }
+ cbNext -= cbToWrite;
+ }
+
+ rc = RTFileClose(hFile);
+ if (RT_FAILURE(rc))
+ RTTestIFailed("%u: RTFileClose(%p) -> %Rrc\n", uLine, hFile, rc);
+ }
+}
+
+
+void fsPerfOpen(void)
+{
+ RTTestISub("open");
+
+ /* Opening non-existing files. */
+ RTFILE hFile;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-file")),
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_PATH_NOT_FOUND);
+
+ /*
+ * The following is copied from tstRTFileOpenEx-1.cpp:
+ */
+ InDir(RT_STR_TUPLE("file1"));
+ tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN, VERR_FILE_NOT_FOUND, RTFILEACTION_INVALID);
+ tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN_CREATE, VINF_SUCCESS, RTFILEACTION_CREATED);
+ tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN_CREATE, VINF_SUCCESS, RTFILEACTION_OPENED);
+ tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN, VINF_SUCCESS, RTFILEACTION_OPENED);
+
+ tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
+ tstOpenExTest(__LINE__, 0, 10, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
+ tstOpenExTest(__LINE__, 10, 10, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
+ tstOpenExTest(__LINE__, 10, -1, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
+ tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VERR_FILE_NOT_FOUND, RTFILEACTION_INVALID);
+ tstOpenExTest(__LINE__, -1, 0, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
+
+ tstOpenExTest(__LINE__, 0, -1, g_szDir, RTFILE_O_CREATE_REPLACE, VINF_SUCCESS, RTFILEACTION_REPLACED);
+ tstOpenExTest(__LINE__, -1, 0, g_szDir, RTFILE_O_CREATE_REPLACE, VINF_SUCCESS, RTFILEACTION_CREATED);
+ tstOpenExTest(__LINE__, 0, -1, g_szDir, RTFILE_O_CREATE, VERR_ALREADY_EXISTS, RTFILEACTION_ALREADY_EXISTS);
+ tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_CREATE, VINF_SUCCESS, RTFILEACTION_CREATED);
+
+ tstOpenExTest(__LINE__, -1, 10, g_szDir, RTFILE_O_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
+ tstOpenExTest(__LINE__, 10, 10, g_szDir, RTFILE_O_CREATE | RTFILE_O_TRUNCATE, VERR_ALREADY_EXISTS, RTFILEACTION_ALREADY_EXISTS);
+ tstOpenExTest(__LINE__, 10, -1, g_szDir, RTFILE_O_CREATE_REPLACE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_REPLACED);
+ tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_CREATE_REPLACE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
+
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
+
+ /*
+ * Create file1 and then try exclusivly creating it again.
+ * Then profile opening it for reading.
+ */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file1")),
+ RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VERR_ALREADY_EXISTS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Readonly");
+ PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Writeonly");
+
+ /*
+ * Profile opening in the deep directory too.
+ */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file1")),
+ RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/readonly");
+ PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/writeonly");
+
+ /* Manytree: */
+ char szPath[FSPERF_MAX_PATH];
+ PROFILE_MANYTREE_FN(szPath, fsPerfOpenExistingOnceReadonly(szPath), 1, g_nsTestRun, "RTFileOpen/Close/manytree/readonly");
+}
+
+
+void fsPerfFStat(void)
+{
+ RTTestISub("fstat");
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo = {0};
+ PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), g_nsTestRun, "RTFileQueryInfo/NOTHING");
+ PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_UNIX), g_nsTestRun, "RTFileQueryInfo/UNIX");
+
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+}
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Nt(Query|Set|QueryDir)Information(File|) information class info.
+ */
+static const struct
+{
+ const char *pszName;
+ int enmValue;
+ bool fQuery;
+ bool fSet;
+ bool fQueryDir;
+ uint8_t cbMin;
+} g_aNtQueryInfoFileClasses[] =
+{
+#define E(a_enmValue, a_fQuery, a_fSet, a_fQueryDir, a_cbMin) \
+ { #a_enmValue, a_enmValue, a_fQuery, a_fSet, a_fQueryDir, a_cbMin }
+ { "invalid0", 0, false, false, false, 0 },
+ E(FileDirectoryInformation, false, false, true, sizeof(FILE_DIRECTORY_INFORMATION)), // 0x00, 0x00, 0x48
+ E(FileFullDirectoryInformation, false, false, true, sizeof(FILE_FULL_DIR_INFORMATION)), // 0x00, 0x00, 0x48
+ E(FileBothDirectoryInformation, false, false, true, sizeof(FILE_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x60
+ E(FileBasicInformation, true, true, false, sizeof(FILE_BASIC_INFORMATION)),
+ E(FileStandardInformation, true, false, false, sizeof(FILE_STANDARD_INFORMATION)),
+ E(FileInternalInformation, true, false, false, sizeof(FILE_INTERNAL_INFORMATION)),
+ E(FileEaInformation, true, false, false, sizeof(FILE_EA_INFORMATION)),
+ E(FileAccessInformation, true, false, false, sizeof(FILE_ACCESS_INFORMATION)),
+ E(FileNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)),
+ E(FileRenameInformation, false, true, false, sizeof(FILE_RENAME_INFORMATION)),
+ E(FileLinkInformation, false, true, false, sizeof(FILE_LINK_INFORMATION)),
+ E(FileNamesInformation, false, false, true, sizeof(FILE_NAMES_INFORMATION)), // 0x00, 0x00, 0x10
+ E(FileDispositionInformation, false, true, false, sizeof(FILE_DISPOSITION_INFORMATION)), // 0x00, 0x01,
+ E(FilePositionInformation, true, true, false, sizeof(FILE_POSITION_INFORMATION)), // 0x08, 0x08,
+ E(FileFullEaInformation, false, false, false, sizeof(FILE_FULL_EA_INFORMATION)), // 0x00, 0x00,
+ E(FileModeInformation, true, true, false, sizeof(FILE_MODE_INFORMATION)), // 0x04, 0x04,
+ E(FileAlignmentInformation, true, false, false, sizeof(FILE_ALIGNMENT_INFORMATION)), // 0x04, 0x00,
+ E(FileAllInformation, true, false, false, sizeof(FILE_ALL_INFORMATION)), // 0x68, 0x00,
+ E(FileAllocationInformation, false, true, false, sizeof(FILE_ALLOCATION_INFORMATION)), // 0x00, 0x08,
+ E(FileEndOfFileInformation, false, true, false, sizeof(FILE_END_OF_FILE_INFORMATION)), // 0x00, 0x08,
+ E(FileAlternateNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)), // 0x08, 0x00,
+ E(FileStreamInformation, true, false, false, sizeof(FILE_STREAM_INFORMATION)), // 0x20, 0x00,
+ E(FilePipeInformation, true, true, false, sizeof(FILE_PIPE_INFORMATION)), // 0x08, 0x08,
+ E(FilePipeLocalInformation, true, false, false, sizeof(FILE_PIPE_LOCAL_INFORMATION)), // 0x28, 0x00,
+ E(FilePipeRemoteInformation, true, true, false, sizeof(FILE_PIPE_REMOTE_INFORMATION)), // 0x10, 0x10,
+ E(FileMailslotQueryInformation, true, false, false, sizeof(FILE_MAILSLOT_QUERY_INFORMATION)), // 0x18, 0x00,
+ E(FileMailslotSetInformation, false, true, false, sizeof(FILE_MAILSLOT_SET_INFORMATION)), // 0x00, 0x08,
+ E(FileCompressionInformation, true, false, false, sizeof(FILE_COMPRESSION_INFORMATION)), // 0x10, 0x00,
+ E(FileObjectIdInformation, true, true, true, sizeof(FILE_OBJECTID_INFORMATION)), // 0x48, 0x48,
+ E(FileCompletionInformation, false, true, false, sizeof(FILE_COMPLETION_INFORMATION)), // 0x00, 0x10,
+ E(FileMoveClusterInformation, false, true, false, sizeof(FILE_MOVE_CLUSTER_INFORMATION)), // 0x00, 0x18,
+ E(FileQuotaInformation, true, true, true, sizeof(FILE_QUOTA_INFORMATION)), // 0x38, 0x38, 0x38
+ E(FileReparsePointInformation, true, false, true, sizeof(FILE_REPARSE_POINT_INFORMATION)), // 0x10, 0x00, 0x10
+ E(FileNetworkOpenInformation, true, false, false, sizeof(FILE_NETWORK_OPEN_INFORMATION)), // 0x38, 0x00,
+ E(FileAttributeTagInformation, true, false, false, sizeof(FILE_ATTRIBUTE_TAG_INFORMATION)), // 0x08, 0x00,
+ E(FileTrackingInformation, false, true, false, sizeof(FILE_TRACKING_INFORMATION)), // 0x00, 0x10,
+ E(FileIdBothDirectoryInformation, false, false, true, sizeof(FILE_ID_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x70
+ E(FileIdFullDirectoryInformation, false, false, true, sizeof(FILE_ID_FULL_DIR_INFORMATION)), // 0x00, 0x00, 0x58
+ E(FileValidDataLengthInformation, false, true, false, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)), // 0x00, 0x08,
+ E(FileShortNameInformation, false, true, false, sizeof(FILE_NAME_INFORMATION)), // 0x00, 0x08,
+ E(FileIoCompletionNotificationInformation, true, true, false, sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION)), // 0x04, 0x04,
+ E(FileIoStatusBlockRangeInformation, false, true, false, sizeof(IO_STATUS_BLOCK) /*?*/), // 0x00, 0x10,
+ E(FileIoPriorityHintInformation, true, true, false, sizeof(FILE_IO_PRIORITY_HINT_INFORMATION)), // 0x04, 0x04,
+ E(FileSfioReserveInformation, true, true, false, sizeof(FILE_SFIO_RESERVE_INFORMATION)), // 0x14, 0x14,
+ E(FileSfioVolumeInformation, true, false, false, sizeof(FILE_SFIO_VOLUME_INFORMATION)), // 0x0C, 0x00,
+ E(FileHardLinkInformation, true, false, false, sizeof(FILE_LINKS_INFORMATION)), // 0x20, 0x00,
+ E(FileProcessIdsUsingFileInformation, true, false, false, sizeof(FILE_PROCESS_IDS_USING_FILE_INFORMATION)), // 0x10, 0x00,
+ E(FileNormalizedNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)), // 0x08, 0x00,
+ E(FileNetworkPhysicalNameInformation, true, false, false, sizeof(FILE_NETWORK_PHYSICAL_NAME_INFORMATION)), // 0x08, 0x00,
+ E(FileIdGlobalTxDirectoryInformation, false, false, true, sizeof(FILE_ID_GLOBAL_TX_DIR_INFORMATION)), // 0x00, 0x00, 0x60
+ E(FileIsRemoteDeviceInformation, true, false, false, sizeof(FILE_IS_REMOTE_DEVICE_INFORMATION)), // 0x01, 0x00,
+ E(FileUnusedInformation, false, false, false, 0), // 0x00, 0x00,
+ E(FileNumaNodeInformation, true, false, false, sizeof(FILE_NUMA_NODE_INFORMATION)), // 0x02, 0x00,
+ E(FileStandardLinkInformation, true, false, false, sizeof(FILE_STANDARD_LINK_INFORMATION)), // 0x0C, 0x00,
+ E(FileRemoteProtocolInformation, true, false, false, sizeof(FILE_REMOTE_PROTOCOL_INFORMATION)), // 0x74, 0x00,
+ E(FileRenameInformationBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
+ E(FileLinkInformationBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
+ E(FileVolumeNameInformation, true, false, false, sizeof(FILE_VOLUME_NAME_INFORMATION)), // 0x08, 0x00,
+ E(FileIdInformation, true, false, false, sizeof(FILE_ID_INFORMATION)), // 0x18, 0x00,
+ E(FileIdExtdDirectoryInformation, false, false, true, sizeof(FILE_ID_EXTD_DIR_INFORMATION)), // 0x00, 0x00, 0x60
+ E(FileReplaceCompletionInformation, false, true, false, sizeof(FILE_COMPLETION_INFORMATION)), // 0x00, 0x10,
+ E(FileHardLinkFullIdInformation, true, false, false, sizeof(FILE_LINK_ENTRY_FULL_ID_INFORMATION)), // 0x24, 0x00,
+ E(FileIdExtdBothDirectoryInformation, false, false, true, sizeof(FILE_ID_EXTD_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x78
+ E(FileDispositionInformationEx, false, true, false, sizeof(FILE_DISPOSITION_INFORMATION_EX)), // 0x00, 0x04,
+ E(FileRenameInformationEx, false, true, false, sizeof(FILE_RENAME_INFORMATION)), // 0x00, 0x18,
+ E(FileRenameInformationExBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
+ E(FileDesiredStorageClassInformation, true, true, false, sizeof(FILE_DESIRED_STORAGE_CLASS_INFORMATION)), // 0x08, 0x08,
+ E(FileStatInformation, true, false, false, sizeof(FILE_STAT_INFORMATION)), // 0x48, 0x00,
+ E(FileMemoryPartitionInformation, false, true, false, 0x10), // 0x00, 0x10,
+ E(FileStatLxInformation, true, false, false, sizeof(FILE_STAT_LX_INFORMATION)), // 0x60, 0x00,
+ E(FileCaseSensitiveInformation, true, true, false, sizeof(FILE_CASE_SENSITIVE_INFORMATION)), // 0x04, 0x04,
+ E(FileLinkInformationEx, false, true, false, sizeof(FILE_LINK_INFORMATION)), // 0x00, 0x18,
+ E(FileLinkInformationExBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
+ E(FileStorageReserveIdInformation, true, true, false, 0x04), // 0x04, 0x04,
+ E(FileCaseSensitiveInformationForceAccessCheck, true, true, false, sizeof(FILE_CASE_SENSITIVE_INFORMATION)), // 0x04, 0x04,
+#undef E
+};
+
+void fsPerfNtQueryInfoFileWorker(HANDLE hNtFile1, uint32_t fType)
+{
+ char const chType = fType == RTFS_TYPE_DIRECTORY ? 'd' : 'r';
+
+ /** @todo may run out of buffer for really long paths? */
+ union
+ {
+ uint8_t ab[4096];
+ FILE_ACCESS_INFORMATION Access;
+ FILE_ALIGNMENT_INFORMATION Align;
+ FILE_ALL_INFORMATION All;
+ FILE_ALLOCATION_INFORMATION Alloc;
+ FILE_ATTRIBUTE_TAG_INFORMATION AttribTag;
+ FILE_BASIC_INFORMATION Basic;
+ FILE_BOTH_DIR_INFORMATION BothDir;
+ FILE_CASE_SENSITIVE_INFORMATION CaseSensitivity;
+ FILE_COMPLETION_INFORMATION Completion;
+ FILE_COMPRESSION_INFORMATION Compression;
+ FILE_DESIRED_STORAGE_CLASS_INFORMATION StorageClass;
+ FILE_DIRECTORY_INFORMATION Dir;
+ FILE_DISPOSITION_INFORMATION Disp;
+ FILE_DISPOSITION_INFORMATION_EX DispEx;
+ FILE_EA_INFORMATION Ea;
+ FILE_END_OF_FILE_INFORMATION EndOfFile;
+ FILE_FULL_DIR_INFORMATION FullDir;
+ FILE_FULL_EA_INFORMATION FullEa;
+ FILE_ID_BOTH_DIR_INFORMATION IdBothDir;
+ FILE_ID_EXTD_BOTH_DIR_INFORMATION ExtIdBothDir;
+ FILE_ID_EXTD_DIR_INFORMATION ExtIdDir;
+ FILE_ID_FULL_DIR_INFORMATION IdFullDir;
+ FILE_ID_GLOBAL_TX_DIR_INFORMATION IdGlobalTx;
+ FILE_ID_INFORMATION IdInfo;
+ FILE_INTERNAL_INFORMATION Internal;
+ FILE_IO_COMPLETION_NOTIFICATION_INFORMATION IoCompletion;
+ FILE_IO_PRIORITY_HINT_INFORMATION IoPrioHint;
+ FILE_IS_REMOTE_DEVICE_INFORMATION IsRemoteDev;
+ FILE_LINK_ENTRY_FULL_ID_INFORMATION LinkFullId;
+ FILE_LINK_INFORMATION Link;
+ FILE_MAILSLOT_QUERY_INFORMATION MailslotQuery;
+ FILE_MAILSLOT_SET_INFORMATION MailslotSet;
+ FILE_MODE_INFORMATION Mode;
+ FILE_MOVE_CLUSTER_INFORMATION MoveCluster;
+ FILE_NAME_INFORMATION Name;
+ FILE_NAMES_INFORMATION Names;
+ FILE_NETWORK_OPEN_INFORMATION NetOpen;
+ FILE_NUMA_NODE_INFORMATION Numa;
+ FILE_OBJECTID_INFORMATION ObjId;
+ FILE_PIPE_INFORMATION Pipe;
+ FILE_PIPE_LOCAL_INFORMATION PipeLocal;
+ FILE_PIPE_REMOTE_INFORMATION PipeRemote;
+ FILE_POSITION_INFORMATION Pos;
+ FILE_PROCESS_IDS_USING_FILE_INFORMATION Pids;
+ FILE_QUOTA_INFORMATION Quota;
+ FILE_REMOTE_PROTOCOL_INFORMATION RemoteProt;
+ FILE_RENAME_INFORMATION Rename;
+ FILE_REPARSE_POINT_INFORMATION Reparse;
+ FILE_SFIO_RESERVE_INFORMATION SfiRes;
+ FILE_SFIO_VOLUME_INFORMATION SfioVol;
+ FILE_STANDARD_INFORMATION Std;
+ FILE_STANDARD_LINK_INFORMATION StdLink;
+ FILE_STAT_INFORMATION Stat;
+ FILE_STAT_LX_INFORMATION StatLx;
+ FILE_STREAM_INFORMATION Stream;
+ FILE_TRACKING_INFORMATION Tracking;
+ FILE_VALID_DATA_LENGTH_INFORMATION ValidDataLen;
+ FILE_VOLUME_NAME_INFORMATION VolName;
+ } uBuf;
+
+ IO_STATUS_BLOCK const VirginIos = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aNtQueryInfoFileClasses); i++)
+ {
+ FILE_INFORMATION_CLASS const enmClass = (FILE_INFORMATION_CLASS)g_aNtQueryInfoFileClasses[i].enmValue;
+ const char * const pszClass = g_aNtQueryInfoFileClasses[i].pszName;
+
+ memset(&uBuf, 0xff, sizeof(uBuf));
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ ULONG cbBuf = sizeof(uBuf);
+ NTSTATUS rcNt = NtQueryInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (Ios.Status == VirginIos.Status || Ios.Information == VirginIos.Information)
+ RTTestIFailed("%s/%#x: I/O status block was not modified: %#x %#zx", pszClass, cbBuf, Ios.Status, Ios.Information);
+ else if (!g_aNtQueryInfoFileClasses[i].fQuery)
+ RTTestIFailed("%s/%#x: This isn't supposed to be queriable! (rcNt=%#x)", pszClass, cbBuf, rcNt);
+ else
+ {
+ ULONG const cbActualMin = enmClass != FileStorageReserveIdInformation ? Ios.Information : 4; /* weird */
+
+ switch (enmClass)
+ {
+ case FileNameInformation:
+ case FileAlternateNameInformation:
+ case FileShortNameInformation:
+ case FileNormalizedNameInformation:
+ case FileNetworkPhysicalNameInformation:
+ if ( RT_UOFFSETOF_DYN(FILE_NAME_INFORMATION, FileName[uBuf.Name.FileNameLength / sizeof(WCHAR)])
+ != cbActualMin)
+ RTTestIFailed("%s/%#x: Wrong FileNameLength=%#x vs cbActual=%#x",
+ pszClass, cbActualMin, uBuf.Name.FileNameLength, cbActualMin);
+ if (uBuf.Name.FileName[uBuf.Name.FileNameLength / sizeof(WCHAR) - 1] == '\0')
+ RTTestIFailed("%s/%#x: Zero terminated name!", pszClass, cbActualMin);
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#x: FileNameLength=%#x FileName='%.*ls'\n",
+ pszClass, cbActualMin, uBuf.Name.FileNameLength,
+ uBuf.Name.FileNameLength / sizeof(WCHAR), uBuf.Name.FileName);
+ break;
+
+ case FileVolumeNameInformation:
+ if (RT_UOFFSETOF_DYN(FILE_VOLUME_NAME_INFORMATION,
+ DeviceName[uBuf.VolName.DeviceNameLength / sizeof(WCHAR)]) != cbActualMin)
+ RTTestIFailed("%s/%#x: Wrong DeviceNameLength=%#x vs cbActual=%#x",
+ pszClass, cbActualMin, uBuf.VolName.DeviceNameLength, cbActualMin);
+ if (uBuf.VolName.DeviceName[uBuf.VolName.DeviceNameLength / sizeof(WCHAR) - 1] == '\0')
+ RTTestIFailed("%s/%#x: Zero terminated name!", pszClass, cbActualMin);
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#x: DeviceNameLength=%#x DeviceName='%.*ls'\n",
+ pszClass, cbActualMin, uBuf.VolName.DeviceNameLength,
+ uBuf.VolName.DeviceNameLength / sizeof(WCHAR), uBuf.VolName.DeviceName);
+ break;
+ default:
+ break;
+ }
+
+ ULONG const cbMin = g_aNtQueryInfoFileClasses[i].cbMin;
+ ULONG const cbMax = RT_MIN(cbActualMin + 64, sizeof(uBuf));
+ for (cbBuf = 0; cbBuf < cbMax; cbBuf++)
+ {
+ memset(&uBuf, 0xfe, sizeof(uBuf));
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
+ if (!ASMMemIsAllU8(&uBuf.ab[cbBuf], sizeof(uBuf) - cbBuf, 0xfe))
+ RTTestIFailed("%s/%#x: Touched memory beyond end of buffer (rcNt=%#x)", pszClass, cbBuf, rcNt);
+ if (cbBuf < cbMin)
+ {
+ if (rcNt != STATUS_INFO_LENGTH_MISMATCH)
+ RTTestIFailed("%s/%#x: %#x, expected STATUS_INFO_LENGTH_MISMATCH", pszClass, cbBuf, rcNt);
+ if (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
+ RTTestIFailed("%s/%#x: I/O status block was modified (STATUS_INFO_LENGTH_MISMATCH): %#x %#zx",
+ pszClass, cbBuf, Ios.Status, Ios.Information);
+ }
+ else if (cbBuf < cbActualMin)
+ {
+ if ( rcNt != STATUS_BUFFER_OVERFLOW
+ /* RDR2/w10 returns success if the buffer can hold exactly the share name: */
+ && !( rcNt == STATUS_SUCCESS
+ && enmClass == FileNetworkPhysicalNameInformation)
+ )
+ RTTestIFailed("%s/%#x: %#x, expected STATUS_BUFFER_OVERFLOW", pszClass, cbBuf, rcNt);
+ /** @todo check name and length fields */
+ }
+ else
+ {
+ if ( !ASMMemIsAllU8(&uBuf.ab[cbActualMin], sizeof(uBuf) - cbActualMin, 0xfe)
+ && enmClass != FileStorageReserveIdInformation /* NTFS bug? */ )
+ RTTestIFailed("%s/%#x: Touched memory beyond returned length (cbActualMin=%#x, rcNt=%#x)",
+ pszClass, cbBuf, cbActualMin, rcNt);
+
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!g_aNtQueryInfoFileClasses[i].fQuery)
+ {
+ if ( rcNt != STATUS_INVALID_INFO_CLASS
+ && ( rcNt != STATUS_INVALID_PARAMETER /* w7rtm-32 result */
+ || enmClass != FileUnusedInformation))
+ RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_INVALID_INFO_CLASS", pszClass, cbBuf, chType, rcNt);
+ }
+ else if ( rcNt != STATUS_INVALID_INFO_CLASS
+ && rcNt != STATUS_INVALID_PARAMETER
+ && !(rcNt == STATUS_OBJECT_NAME_NOT_FOUND && enmClass == FileAlternateNameInformation)
+ && !( rcNt == STATUS_ACCESS_DENIED
+ && ( enmClass == FileIoPriorityHintInformation
+ || enmClass == FileSfioReserveInformation
+ || enmClass == FileStatLxInformation))
+ && !(rcNt == STATUS_NO_SUCH_DEVICE && enmClass == FileNumaNodeInformation)
+ && !( rcNt == STATUS_NOT_SUPPORTED /* RDR2/W10-17763 */
+ && ( enmClass == FileMailslotQueryInformation
+ || enmClass == FileObjectIdInformation
+ || enmClass == FileReparsePointInformation
+ || enmClass == FileSfioVolumeInformation
+ || enmClass == FileHardLinkInformation
+ || enmClass == FileStandardLinkInformation
+ || enmClass == FileHardLinkFullIdInformation
+ || enmClass == FileDesiredStorageClassInformation
+ || enmClass == FileStatInformation
+ || enmClass == FileCaseSensitiveInformation
+ || enmClass == FileStorageReserveIdInformation
+ || enmClass == FileCaseSensitiveInformationForceAccessCheck)
+ || ( fType == RTFS_TYPE_DIRECTORY
+ && (enmClass == FileSfioReserveInformation || enmClass == FileStatLxInformation)))
+ && !(rcNt == STATUS_INVALID_DEVICE_REQUEST && fType == RTFS_TYPE_FILE)
+ )
+ RTTestIFailed("%s/%#x/%c: %#x", pszClass, cbBuf, chType, rcNt);
+ if ( (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
+ && !(fType == RTFS_TYPE_DIRECTORY && Ios.Status == rcNt && Ios.Information == 0) /* NTFS/W10-17763 */
+ && !( enmClass == FileUnusedInformation
+ && Ios.Status == rcNt && Ios.Information == sizeof(uBuf)) /* NTFS/VBoxSF/w7rtm */ )
+ RTTestIFailed("%s/%#x/%c: I/O status block was modified: %#x %#zx",
+ pszClass, cbBuf, chType, Ios.Status, Ios.Information);
+ if (!ASMMemIsAllU8(&uBuf, sizeof(uBuf), 0xff))
+ RTTestIFailed("%s/%#x/%c: Buffer was touched in failure case!", pszClass, cbBuf, chType);
+ }
+ }
+}
+
+void fsPerfNtQueryInfoFile(void)
+{
+ RTTestISub("NtQueryInformationFile");
+
+ /* On a regular file: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qif")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
+ fsPerfNtQueryInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ /* On a directory: */
+ HANDLE hDir1 = INVALID_HANDLE_VALUE;
+ RTTESTI_CHECK_RC_RETV(RTNtPathOpenDir(InDir(RT_STR_TUPLE("")), GENERIC_READ | SYNCHRONIZE | FILE_SYNCHRONOUS_IO_NONALERT,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN, 0, &hDir1, NULL), VINF_SUCCESS);
+ fsPerfNtQueryInfoFileWorker(hDir1, RTFS_TYPE_DIRECTORY);
+ RTTESTI_CHECK(CloseHandle(hDir1) == TRUE);
+}
+
+
+/**
+ * Nt(Query|Set)VolumeInformationFile) information class info.
+ */
+static const struct
+{
+ const char *pszName;
+ int enmValue;
+ bool fQuery;
+ bool fSet;
+ uint8_t cbMin;
+} g_aNtQueryVolInfoFileClasses[] =
+{
+#define E(a_enmValue, a_fQuery, a_fSet, a_cbMin) \
+ { #a_enmValue, a_enmValue, a_fQuery, a_fSet, a_cbMin }
+ { "invalid0", 0, false, false, 0 },
+ E(FileFsVolumeInformation, 1, 0, sizeof(FILE_FS_VOLUME_INFORMATION)),
+ E(FileFsLabelInformation, 0, 1, sizeof(FILE_FS_LABEL_INFORMATION)),
+ E(FileFsSizeInformation, 1, 0, sizeof(FILE_FS_SIZE_INFORMATION)),
+ E(FileFsDeviceInformation, 1, 0, sizeof(FILE_FS_DEVICE_INFORMATION)),
+ E(FileFsAttributeInformation, 1, 0, sizeof(FILE_FS_ATTRIBUTE_INFORMATION)),
+ E(FileFsControlInformation, 1, 1, sizeof(FILE_FS_CONTROL_INFORMATION)),
+ E(FileFsFullSizeInformation, 1, 0, sizeof(FILE_FS_FULL_SIZE_INFORMATION)),
+ E(FileFsObjectIdInformation, 1, 1, sizeof(FILE_FS_OBJECTID_INFORMATION)),
+ E(FileFsDriverPathInformation, 1, 0, sizeof(FILE_FS_DRIVER_PATH_INFORMATION)),
+ E(FileFsVolumeFlagsInformation, 1, 1, sizeof(FILE_FS_VOLUME_FLAGS_INFORMATION)),
+ E(FileFsSectorSizeInformation, 1, 0, sizeof(FILE_FS_SECTOR_SIZE_INFORMATION)),
+ E(FileFsDataCopyInformation, 1, 0, sizeof(FILE_FS_DATA_COPY_INFORMATION)),
+ E(FileFsMetadataSizeInformation, 1, 0, sizeof(FILE_FS_METADATA_SIZE_INFORMATION)),
+ E(FileFsFullSizeInformationEx, 1, 0, sizeof(FILE_FS_FULL_SIZE_INFORMATION_EX)),
+#undef E
+};
+
+void fsPerfNtQueryVolInfoFileWorker(HANDLE hNtFile1, uint32_t fType)
+{
+ char const chType = fType == RTFS_TYPE_DIRECTORY ? 'd' : 'r';
+ union
+ {
+ uint8_t ab[4096];
+ FILE_FS_VOLUME_INFORMATION Vol;
+ FILE_FS_LABEL_INFORMATION Label;
+ FILE_FS_SIZE_INFORMATION Size;
+ FILE_FS_DEVICE_INFORMATION Dev;
+ FILE_FS_ATTRIBUTE_INFORMATION Attrib;
+ FILE_FS_CONTROL_INFORMATION Ctrl;
+ FILE_FS_FULL_SIZE_INFORMATION FullSize;
+ FILE_FS_OBJECTID_INFORMATION ObjId;
+ FILE_FS_DRIVER_PATH_INFORMATION DrvPath;
+ FILE_FS_VOLUME_FLAGS_INFORMATION VolFlags;
+ FILE_FS_SECTOR_SIZE_INFORMATION SectorSize;
+ FILE_FS_DATA_COPY_INFORMATION DataCopy;
+ FILE_FS_METADATA_SIZE_INFORMATION Metadata;
+ FILE_FS_FULL_SIZE_INFORMATION_EX FullSizeEx;
+ } uBuf;
+
+ IO_STATUS_BLOCK const VirginIos = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aNtQueryVolInfoFileClasses); i++)
+ {
+ FS_INFORMATION_CLASS const enmClass = (FS_INFORMATION_CLASS)g_aNtQueryVolInfoFileClasses[i].enmValue;
+ const char * const pszClass = g_aNtQueryVolInfoFileClasses[i].pszName;
+
+ memset(&uBuf, 0xff, sizeof(uBuf));
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ ULONG cbBuf = sizeof(uBuf);
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
+ if (g_uVerbosity > 3)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: rcNt=%#x Ios.Status=%#x Info=%#zx\n",
+ pszClass, cbBuf, chType, rcNt, Ios.Status, Ios.Information);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (Ios.Status == VirginIos.Status || Ios.Information == VirginIos.Information)
+ RTTestIFailed("%s/%#x/%c: I/O status block was not modified: %#x %#zx",
+ pszClass, cbBuf, chType, Ios.Status, Ios.Information);
+ else if (!g_aNtQueryVolInfoFileClasses[i].fQuery)
+ RTTestIFailed("%s/%#x/%c: This isn't supposed to be queriable! (rcNt=%#x)", pszClass, cbBuf, chType, rcNt);
+ else
+ {
+ ULONG const cbActualMin = Ios.Information;
+ ULONG *pcbName = NULL;
+ ULONG offName = 0;
+
+ switch (enmClass)
+ {
+ case FileFsVolumeInformation:
+ pcbName = &uBuf.Vol.VolumeLabelLength;
+ offName = RT_UOFFSETOF(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
+ if (RT_UOFFSETOF_DYN(FILE_FS_VOLUME_INFORMATION,
+ VolumeLabel[uBuf.Vol.VolumeLabelLength / sizeof(WCHAR)]) != cbActualMin)
+ RTTestIFailed("%s/%#x/%c: Wrong VolumeLabelLength=%#x vs cbActual=%#x",
+ pszClass, cbActualMin, chType, uBuf.Vol.VolumeLabelLength, cbActualMin);
+ if (uBuf.Vol.VolumeLabel[uBuf.Vol.VolumeLabelLength / sizeof(WCHAR) - 1] == '\0')
+ RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: VolumeLabelLength=%#x VolumeLabel='%.*ls'\n",
+ pszClass, cbActualMin, chType, uBuf.Vol.VolumeLabelLength,
+ uBuf.Vol.VolumeLabelLength / sizeof(WCHAR), uBuf.Vol.VolumeLabel);
+ break;
+
+ case FileFsAttributeInformation:
+ pcbName = &uBuf.Attrib.FileSystemNameLength;
+ offName = RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);
+ if (RT_UOFFSETOF_DYN(FILE_FS_ATTRIBUTE_INFORMATION,
+ FileSystemName[uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR)]) != cbActualMin)
+ RTTestIFailed("%s/%#x/%c: Wrong FileSystemNameLength=%#x vs cbActual=%#x",
+ pszClass, cbActualMin, chType, uBuf.Attrib.FileSystemNameLength, cbActualMin);
+ if (uBuf.Attrib.FileSystemName[uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR) - 1] == '\0')
+ RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: FileSystemNameLength=%#x FileSystemName='%.*ls' Attribs=%#x MaxCompName=%#x\n",
+ pszClass, cbActualMin, chType, uBuf.Attrib.FileSystemNameLength,
+ uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR), uBuf.Attrib.FileSystemName,
+ uBuf.Attrib.FileSystemAttributes, uBuf.Attrib.MaximumComponentNameLength);
+ break;
+
+ case FileFsDriverPathInformation:
+ pcbName = &uBuf.DrvPath.DriverNameLength;
+ offName = RT_UOFFSETOF(FILE_FS_DRIVER_PATH_INFORMATION, DriverName);
+ if (RT_UOFFSETOF_DYN(FILE_FS_DRIVER_PATH_INFORMATION,
+ DriverName[uBuf.DrvPath.DriverNameLength / sizeof(WCHAR)]) != cbActualMin)
+ RTTestIFailed("%s/%#x/%c: Wrong DriverNameLength=%#x vs cbActual=%#x",
+ pszClass, cbActualMin, chType, uBuf.DrvPath.DriverNameLength, cbActualMin);
+ if (uBuf.DrvPath.DriverName[uBuf.DrvPath.DriverNameLength / sizeof(WCHAR) - 1] == '\0')
+ RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: DriverNameLength=%#x DriverName='%.*ls'\n",
+ pszClass, cbActualMin, chType, uBuf.DrvPath.DriverNameLength,
+ uBuf.DrvPath.DriverNameLength / sizeof(WCHAR), uBuf.DrvPath.DriverName);
+ break;
+
+ case FileFsSectorSizeInformation:
+ if (g_uVerbosity > 1)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: Flags=%#x log=%#x atomic=%#x perf=%#x eff=%#x offSec=%#x offPart=%#x\n",
+ pszClass, cbActualMin, chType, uBuf.SectorSize.Flags,
+ uBuf.SectorSize.LogicalBytesPerSector,
+ uBuf.SectorSize.PhysicalBytesPerSectorForAtomicity,
+ uBuf.SectorSize.PhysicalBytesPerSectorForPerformance,
+ uBuf.SectorSize.FileSystemEffectivePhysicalBytesPerSectorForAtomicity,
+ uBuf.SectorSize.ByteOffsetForSectorAlignment,
+ uBuf.SectorSize.ByteOffsetForPartitionAlignment);
+ break;
+
+ default:
+ if (g_uVerbosity > 2)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c:\n", pszClass, cbActualMin, chType);
+ break;
+ }
+ ULONG const cbName = pcbName ? *pcbName : 0;
+ uint8_t abNameCopy[4096];
+ RT_ZERO(abNameCopy);
+ if (pcbName)
+ memcpy(abNameCopy, &uBuf.ab[offName], cbName);
+
+ ULONG const cbMin = g_aNtQueryVolInfoFileClasses[i].cbMin;
+ ULONG const cbMax = RT_MIN(cbActualMin + 64, sizeof(uBuf));
+ for (cbBuf = 0; cbBuf < cbMax; cbBuf++)
+ {
+ memset(&uBuf, 0xfe, sizeof(uBuf));
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryVolumeInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
+ if (!ASMMemIsAllU8(&uBuf.ab[cbBuf], sizeof(uBuf) - cbBuf, 0xfe))
+ RTTestIFailed("%s/%#x/%c: Touched memory beyond end of buffer (rcNt=%#x)", pszClass, cbBuf, chType, rcNt);
+ if (cbBuf < cbMin)
+ {
+ if (rcNt != STATUS_INFO_LENGTH_MISMATCH)
+ RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_INFO_LENGTH_MISMATCH", pszClass, cbBuf, chType, rcNt);
+ if (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
+ RTTestIFailed("%s/%#x/%c: I/O status block was modified (STATUS_INFO_LENGTH_MISMATCH): %#x %#zx",
+ pszClass, cbBuf, chType, Ios.Status, Ios.Information);
+ }
+ else if (cbBuf < cbActualMin)
+ {
+ if (rcNt != STATUS_BUFFER_OVERFLOW)
+ RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_BUFFER_OVERFLOW", pszClass, cbBuf, chType, rcNt);
+ if (pcbName)
+ {
+ size_t const cbNameAlt = offName < cbBuf ? cbBuf - offName : 0;
+ if ( *pcbName != cbName
+ && !( *pcbName == cbNameAlt
+ && (enmClass == FileFsAttributeInformation /*NTFS,FAT*/)))
+ RTTestIFailed("%s/%#x/%c: Wrong name length: %#x, expected %#x (or %#x)",
+ pszClass, cbBuf, chType, *pcbName, cbName, cbNameAlt);
+ if (memcmp(abNameCopy, &uBuf.ab[offName], cbNameAlt) != 0)
+ RTTestIFailed("%s/%#x/%c: Wrong partial name: %.*Rhxs",
+ pszClass, cbBuf, chType, cbNameAlt, &uBuf.ab[offName]);
+ }
+ if (Ios.Information != cbBuf)
+ RTTestIFailed("%s/%#x/%c: Ios.Information = %#x, expected %#x",
+ pszClass, cbBuf, chType, Ios.Information, cbBuf);
+ }
+ else
+ {
+ if ( !ASMMemIsAllU8(&uBuf.ab[cbActualMin], sizeof(uBuf) - cbActualMin, 0xfe)
+ && enmClass != FileStorageReserveIdInformation /* NTFS bug? */ )
+ RTTestIFailed("%s/%#x/%c: Touched memory beyond returned length (cbActualMin=%#x, rcNt=%#x)",
+ pszClass, cbBuf, chType, cbActualMin, rcNt);
+ if (pcbName && *pcbName != cbName)
+ RTTestIFailed("%s/%#x/%c: Wrong name length: %#x, expected %#x",
+ pszClass, cbBuf, chType, *pcbName, cbName);
+ if (pcbName && memcmp(abNameCopy, &uBuf.ab[offName], cbName) != 0)
+ RTTestIFailed("%s/%#x/%c: Wrong name: %.*Rhxs",
+ pszClass, cbBuf, chType, cbName, &uBuf.ab[offName]);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!g_aNtQueryVolInfoFileClasses[i].fQuery)
+ {
+ if (rcNt != STATUS_INVALID_INFO_CLASS)
+ RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_INVALID_INFO_CLASS", pszClass, cbBuf, chType, rcNt);
+ }
+ else if ( rcNt != STATUS_INVALID_INFO_CLASS
+ && rcNt != STATUS_INVALID_PARAMETER
+ && !(rcNt == STATUS_ACCESS_DENIED && enmClass == FileFsControlInformation /* RDR2/W10 */)
+ && !(rcNt == STATUS_OBJECT_NAME_NOT_FOUND && enmClass == FileFsObjectIdInformation /* RDR2/W10 */)
+ )
+ RTTestIFailed("%s/%#x/%c: %#x", pszClass, cbBuf, chType, rcNt);
+ if ( (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
+ && !( Ios.Status == 0 && Ios.Information == 0
+ && fType == RTFS_TYPE_DIRECTORY
+ && ( enmClass == FileFsObjectIdInformation /* RDR2+NTFS on W10 */
+ || enmClass == FileFsControlInformation /* RDR2 on W10 */
+ || enmClass == FileFsVolumeFlagsInformation /* RDR2+NTFS on W10 */
+ || enmClass == FileFsDataCopyInformation /* RDR2 on W10 */
+ || enmClass == FileFsMetadataSizeInformation /* RDR2+NTFS on W10 */
+ || enmClass == FileFsFullSizeInformationEx /* RDR2 on W10 */
+ ) )
+ )
+ RTTestIFailed("%s/%#x/%c: I/O status block was modified: %#x %#zx (rcNt=%#x)",
+ pszClass, cbBuf, chType, Ios.Status, Ios.Information, rcNt);
+ if (!ASMMemIsAllU8(&uBuf, sizeof(uBuf), 0xff))
+ RTTestIFailed("%s/%#x/%c: Buffer was touched in failure case!", pszClass, cbBuf, chType);
+ }
+ }
+ RT_NOREF(fType);
+}
+
+void fsPerfNtQueryVolInfoFile(void)
+{
+ RTTestISub("NtQueryVolumeInformationFile");
+
+ /* On a regular file: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qvif")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
+ fsPerfNtQueryVolInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ /* On a directory: */
+ HANDLE hDir1 = INVALID_HANDLE_VALUE;
+ RTTESTI_CHECK_RC_RETV(RTNtPathOpenDir(InDir(RT_STR_TUPLE("")), GENERIC_READ | SYNCHRONIZE | FILE_SYNCHRONOUS_IO_NONALERT,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN, 0, &hDir1, NULL), VINF_SUCCESS);
+ fsPerfNtQueryVolInfoFileWorker(hDir1, RTFS_TYPE_DIRECTORY);
+ RTTESTI_CHECK(CloseHandle(hDir1) == TRUE);
+
+ /* On a regular file opened for reading: */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qvif")),
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ fsPerfNtQueryVolInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+}
+
+#endif /* RT_OS_WINDOWS */
+
+void fsPerfFChMod(void)
+{
+ RTTestISub("fchmod");
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file4")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo = {0};
+ RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
+ RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
+ RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
+ PROFILE_FN(RTFileSetMode(hFile1, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTFileSetMode");
+
+ RTFileSetMode(hFile1, ObjInfo.Attr.fMode);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+}
+
+
+void fsPerfFUtimes(void)
+{
+ RTTestISub("futimes");
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file5")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTIMESPEC Time1;
+ RTTimeNow(&Time1);
+ RTTIMESPEC Time2 = Time1;
+ RTTimeSpecSubSeconds(&Time2, 3636);
+
+ RTFSOBJINFO ObjInfo0 = {0};
+ RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo0, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
+
+ /* Modify modification time: */
+ RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, NULL, &Time2, NULL, NULL), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo1 = {0};
+ RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo1, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
+ RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
+ char sz1[RTTIME_STR_LEN], sz2[RTTIME_STR_LEN]; /* Div by 1000 here for posix impl. using timeval. */
+ RTTESTI_CHECK_MSG(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000,
+ ("%s, expected %s", RTTimeSpecToString(&ObjInfo1.AccessTime, sz1, sizeof(sz1)),
+ RTTimeSpecToString(&ObjInfo0.AccessTime, sz2, sizeof(sz2))));
+
+ /* Modify access time: */
+ RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, &Time1, NULL, NULL, NULL), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo2 = {0};
+ RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo2, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
+ RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
+ RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000);
+
+ /* Benchmark it: */
+ PROFILE_FN(RTFileSetTimes(hFile1, NULL, iIteration & 1 ? &Time1 : &Time2, NULL, NULL), g_nsTestRun, "RTFileSetTimes");
+
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+}
+
+
+void fsPerfStat(void)
+{
+ RTTestISub("stat");
+ RTFSOBJINFO ObjInfo;
+
+ /* Non-existing files. */
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-file")),
+ &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
+ &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
+ &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_PATH_NOT_FOUND);
+
+ /* Shallow: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file3")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
+ "RTPathQueryInfoEx/NOTHING");
+ PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
+ "RTPathQueryInfoEx/UNIX");
+
+
+ /* Deep: */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file3")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
+ "RTPathQueryInfoEx/deep/NOTHING");
+ PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
+ "RTPathQueryInfoEx/deep/UNIX");
+
+ /* Manytree: */
+ char szPath[FSPERF_MAX_PATH];
+ PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK),
+ 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/NOTHING");
+ PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK),
+ 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/UNIX");
+}
+
+
+void fsPerfChmod(void)
+{
+ RTTestISub("chmod");
+
+ /* Non-existing files. */
+ RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-file")), 0665),
+ VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0665),
+ FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathSetMode(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0665), VERR_PATH_NOT_FOUND);
+
+ /* Shallow: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file14")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ RTFSOBJINFO ObjInfo;
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
+ RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
+ RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
+ PROFILE_FN(RTPathSetMode(g_szDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode");
+ RTPathSetMode(g_szDir, ObjInfo.Attr.fMode);
+
+ /* Deep: */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file14")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(RTPathSetMode(g_szDeepDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode/deep");
+ RTPathSetMode(g_szDeepDir, ObjInfo.Attr.fMode);
+
+ /* Manytree: */
+ char szPath[FSPERF_MAX_PATH];
+ PROFILE_MANYTREE_FN(szPath, RTPathSetMode(szPath, iIteration & 1 ? fOddMode : fEvenMode), 1, g_nsTestRun,
+ "RTPathSetMode/manytree");
+ DO_MANYTREE_FN(szPath, RTPathSetMode(szPath, ObjInfo.Attr.fMode));
+}
+
+
+void fsPerfUtimes(void)
+{
+ RTTestISub("utimes");
+
+ RTTIMESPEC Time1;
+ RTTimeNow(&Time1);
+ RTTIMESPEC Time2 = Time1;
+ RTTimeSpecSubSeconds(&Time2, 3636);
+
+ /* Non-existing files. */
+ RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-file")), NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
+ VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
+ NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
+ FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathSetTimesEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
+ NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
+ VERR_PATH_NOT_FOUND);
+
+ /* Shallow: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file15")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ RTFSOBJINFO ObjInfo0 = {0};
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo0, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
+
+ /* Modify modification time: */
+ RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, NULL, &Time2, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo1;
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo1, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
+ RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
+ RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000 /* posix timeval */);
+
+ /* Modify access time: */
+ RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, &Time1, NULL, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
+ RTFSOBJINFO ObjInfo2 = {0};
+ RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo2, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
+ RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
+ RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000 /* posix timeval */);
+
+ /* Profile shallow: */
+ PROFILE_FN(RTPathSetTimesEx(g_szDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
+ NULL, NULL, RTPATH_F_ON_LINK),
+ g_nsTestRun, "RTPathSetTimesEx");
+
+ /* Deep: */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(RTPathSetTimesEx(g_szDeepDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
+ NULL, NULL, RTPATH_F_ON_LINK),
+ g_nsTestRun, "RTPathSetTimesEx/deep");
+
+ /* Manytree: */
+ char szPath[FSPERF_MAX_PATH];
+ PROFILE_MANYTREE_FN(szPath, RTPathSetTimesEx(szPath, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
+ NULL, NULL, RTPATH_F_ON_LINK),
+ 1, g_nsTestRun, "RTPathSetTimesEx/manytree");
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfRenameMany(const char *pszFile, uint32_t iIteration)
+{
+ char szRenamed[FSPERF_MAX_PATH];
+ strcat(strcpy(szRenamed, pszFile), "-renamed");
+ if (!(iIteration & 1))
+ return RTPathRename(pszFile, szRenamed, 0);
+ return RTPathRename(szRenamed, pszFile, 0);
+}
+
+
+void fsPerfRename(void)
+{
+ RTTestISub("rename");
+ char szPath[FSPERF_MAX_PATH];
+
+/** @todo rename directories too! */
+/** @todo check overwriting files and directoris (empty ones should work on
+ * unix). */
+
+ /* Non-existing files. */
+ strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
+ RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-file")), szPath, 0), VERR_FILE_NOT_FOUND);
+ strcpy(szPath, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "other-no-such-file")));
+ RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), szPath, 0),
+ FSPERF_VERR_PATH_NOT_FOUND);
+ strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
+ RTTESTI_CHECK_RC(RTPathRename(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), szPath, 0), VERR_PATH_NOT_FOUND);
+
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file16")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ strcat(strcpy(szPath, g_szDir), "-no-such-dir" RTPATH_SLASH_STR "file16");
+ RTTESTI_CHECK_RC(RTPathRename(szPath, g_szDir, 0), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTPathRename(g_szDir, szPath, 0), FSPERF_VERR_PATH_NOT_FOUND);
+
+ /* Shallow: */
+ strcat(strcpy(szPath, g_szDir), "-other");
+ PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDir, iIteration & 1 ? g_szDir : szPath, 0), g_nsTestRun, "RTPathRename");
+
+ /* Deep: */
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ strcat(strcpy(szPath, g_szDeepDir), "-other");
+ PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDeepDir, iIteration & 1 ? g_szDeepDir : szPath, 0),
+ g_nsTestRun, "RTPathRename/deep");
+
+ /* Manytree: */
+ PROFILE_MANYTREE_FN(szPath, fsPerfRenameMany(szPath, iIteration), 2, g_nsTestRun, "RTPathRename/manytree");
+}
+
+
+/**
+ * Wrapper around RTDirOpen/RTDirOpenFiltered which takes g_fRelativeDir into
+ * account.
+ */
+DECL_FORCE_INLINE(int) fsPerfOpenDirWrap(PRTDIR phDir, const char *pszPath)
+{
+ if (!g_fRelativeDir)
+ return RTDirOpen(phDir, pszPath);
+ return RTDirOpenFiltered(phDir, pszPath, RTDIRFILTER_NONE, RTDIR_F_NO_ABS_PATH);
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfOpenClose(const char *pszDir)
+{
+ RTDIR hDir;
+ RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, pszDir), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+void vsPerfDirOpen(void)
+{
+ RTTestISub("dir open");
+ RTDIR hDir;
+
+ /*
+ * Non-existing files.
+ */
+ RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
+
+ /*
+ * Check that open + close works.
+ */
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+
+
+ /*
+ * Profile empty dir and dir with many files.
+ */
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ PROFILE_FN(fsPerfOpenClose(g_szEmptyDir), g_nsTestRun, "RTDirOpen/Close empty");
+ if (g_fManyFiles)
+ {
+ InDir(RT_STR_TUPLE("manyfiles"));
+ PROFILE_FN(fsPerfOpenClose(g_szDir), g_nsTestRun, "RTDirOpen/Close manyfiles");
+ }
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfEnumEmpty(void)
+{
+ RTDIR hDir;
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS, rcCheck);
+
+ RTDIRENTRY Entry;
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
+
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfEnumManyFiles(void)
+{
+ RTDIR hDir;
+ RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS, rcCheck);
+ uint32_t cLeft = g_cManyFiles + 2;
+ for (;;)
+ {
+ RTDIRENTRY Entry;
+ if (cLeft > 0)
+ RTTESTI_CHECK_RC_BREAK(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
+ else
+ {
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
+ break;
+ }
+ cLeft--;
+ }
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+void vsPerfDirEnum(void)
+{
+ RTTestISub("dir enum");
+ RTDIR hDir;
+
+ /*
+ * The empty directory.
+ */
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS);
+
+ uint32_t fDots = 0;
+ RTDIRENTRY Entry;
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
+ fDots |= RT_BIT_32(Entry.cbName - 1);
+
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
+ fDots |= RT_BIT_32(Entry.cbName - 1);
+ RTTESTI_CHECK(fDots == 3);
+
+ RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
+
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+
+ /*
+ * The directory with many files in it.
+ */
+ if (g_fManyFiles)
+ {
+ fDots = 0;
+ uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64);
+ void *pvBitmap = alloca(cBitmap / 8);
+ RT_BZERO(pvBitmap, cBitmap / 8);
+ for (uint32_t i = g_cManyFiles; i < cBitmap; i++)
+ ASMBitSet(pvBitmap, i);
+
+ uint32_t cFiles = 0;
+ RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS);
+ for (;;)
+ {
+ int rc = RTDirRead(hDir, &Entry, NULL);
+ if (rc == VINF_SUCCESS)
+ {
+ if (Entry.szName[0] == '.')
+ {
+ if (Entry.szName[1] == '.')
+ {
+ RTTESTI_CHECK(!(fDots & 2));
+ fDots |= 2;
+ }
+ else
+ {
+ RTTESTI_CHECK(Entry.szName[1] == '\0');
+ RTTESTI_CHECK(!(fDots & 1));
+ fDots |= 1;
+ }
+ }
+ else
+ {
+ uint32_t iFile = UINT32_MAX;
+ RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS);
+ if ( iFile < g_cManyFiles
+ && !ASMBitTest(pvBitmap, iFile))
+ {
+ ASMBitSet(pvBitmap, iFile);
+ cFiles++;
+ }
+ else
+ RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles);
+ }
+ }
+ else if (rc == VERR_NO_MORE_FILES)
+ break;
+ else
+ {
+ RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc);
+ RTDirClose(hDir);
+ return;
+ }
+ }
+ RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
+ RTTESTI_CHECK(fDots == 3);
+ RTTESTI_CHECK(cFiles == g_cManyFiles);
+ RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff));
+ }
+
+ /*
+ * Profile.
+ */
+ PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty");
+ if (g_fManyFiles)
+ PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles");
+}
+
+
+void fsPerfMkRmDir(void)
+{
+ RTTestISub("mkdir/rmdir");
+
+ /* Non-existing directories: */
+ RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir"))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), VERR_PATH_NOT_FOUND);
+
+ RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0755, 0), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0755, 0), VERR_PATH_NOT_FOUND);
+
+ /* Already existing directories and files: */
+ RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE(".")), 0755, 0), VERR_ALREADY_EXISTS);
+ RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("..")), 0755, 0), VERR_ALREADY_EXISTS);
+
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file"))), VERR_NOT_A_DIRECTORY);
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR))), VERR_NOT_A_DIRECTORY);
+
+ /* Remove directory with subdirectories: */
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
+#else
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER); /* EINVAL for '.' */
+#endif
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ int rc = RTDirRemove(InDir(RT_STR_TUPLE("..")));
+# ifdef RT_OS_WINDOWS
+ if (rc != VERR_DIR_NOT_EMPTY /*ntfs root*/ && rc != VERR_SHARING_VIOLATION /*ntfs weird*/ && rc != VERR_ACCESS_DENIED /*fat32 root*/)
+ RTTestIFailed("RTDirRemove(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY, VERR_SHARING_VIOLATION or VERR_ACCESS_DENIED", g_szDir, rc);
+# else
+ if (rc != VERR_DIR_NOT_EMPTY && rc != VERR_RESOURCE_BUSY /*IPRT/kLIBC fun*/)
+ RTTestIFailed("RTDirRemove(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY or VERR_RESOURCE_BUSY", g_szDir, rc);
+
+ APIRET orc;
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE(".")))) == ERROR_ACCESS_DENIED,
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("..")))) == ERROR_ACCESS_DENIED,
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("")))) == ERROR_PATH_NOT_FOUND, /* a little weird (fsrouter) */
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_PATH_NOT_FOUND));
+
+# endif
+#else
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_DIR_NOT_EMPTY);
+#endif
+ RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
+
+ /* Create a directory and remove it: */
+ RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("subdir-1")), 0755, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VINF_SUCCESS);
+
+ /* Create a file and try remove it or create a directory with the same name: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file18")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VERR_NOT_A_DIRECTORY);
+ RTTESTI_CHECK_RC(RTDirCreate(g_szDir, 0755, 0), VERR_ALREADY_EXISTS);
+ RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("file18" RTPATH_SLASH_STR "subdir")), 0755, 0), VERR_PATH_NOT_FOUND);
+
+ /*
+ * Profile alternately creating and removing a bunch of directories.
+ */
+ RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS);
+ size_t cchDir = strlen(g_szDir);
+ g_szDir[cchDir++] = RTPATH_SLASH;
+ g_szDir[cchDir++] = 's';
+
+ uint32_t cCreated = 0;
+ uint64_t nsCreate = 0;
+ uint64_t nsRemove = 0;
+ for (;;)
+ {
+ /* Create a bunch: */
+ uint64_t nsStart = RTTimeNanoTS();
+ for (uint32_t i = 0; i < 998; i++)
+ {
+ RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
+ RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS);
+ }
+ nsCreate += RTTimeNanoTS() - nsStart;
+ cCreated += 998;
+
+ /* Remove the bunch: */
+ nsStart = RTTimeNanoTS();
+ for (uint32_t i = 0; i < 998; i++)
+ {
+ RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
+ RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS);
+ }
+ nsRemove = RTTimeNanoTS() - nsStart;
+
+ /* Check if we got time for another round: */
+ if ( ( nsRemove >= g_nsTestRun
+ && nsCreate >= g_nsTestRun)
+ || nsCreate + nsRemove >= g_nsTestRun * 3)
+ break;
+ }
+ RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
+ RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
+}
+
+
+void fsPerfStatVfs(void)
+{
+ RTTestISub("statvfs");
+
+ g_szEmptyDir[g_cchEmptyDir] = '\0';
+ RTFOFF cbTotal;
+ RTFOFF cbFree;
+ uint32_t cbBlock;
+ uint32_t cbSector;
+ RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS);
+
+ uint32_t uSerial;
+ RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS);
+
+ RTFSPROPERTIES Props;
+ RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS);
+
+ RTFSTYPE enmType;
+ RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS);
+
+ g_szDeepDir[g_cchDeepDir] = '\0';
+ PROFILE_FN(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), g_nsTestRun, "RTFsQuerySize/empty");
+ PROFILE_FN(RTFsQuerySizes(g_szDeepDir, &cbTotal, &cbFree, &cbBlock, &cbSector), g_nsTestRun, "RTFsQuerySize/deep");
+}
+
+
+void fsPerfRm(void)
+{
+ RTTestISub("rm");
+
+ /* Non-existing files. */
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file" RTPATH_SLASH_STR))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), VERR_PATH_NOT_FOUND);
+
+ /* Existing file but specified as if it was a directory: */
+#if defined(RT_OS_WINDOWS)
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR ))), VERR_INVALID_NAME);
+#else
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR))), VERR_PATH_NOT_FOUND);
+#endif
+
+ /* Directories: */
+#if defined(RT_OS_WINDOWS)
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_ACCESS_DENIED);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_ACCESS_DENIED);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
+#elif defined(RT_OS_DARWIN) /* unlink() on xnu 16.7.0 is behaviour totally werid: */
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VINF_SUCCESS /*WTF?!?*/);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
+#elif defined(RT_OS_OS2) /* OS/2 has a busted unlink, it think it should remove directories too. */
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
+ int rc = RTFileDelete(InDir(RT_STR_TUPLE("..")));
+ if (rc != VERR_DIR_NOT_EMPTY && rc != VERR_FILE_NOT_FOUND && rc != VERR_RESOURCE_BUSY)
+ RTTestIFailed("RTFileDelete(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY or VERR_FILE_NOT_FOUND or VERR_RESOURCE_BUSY", g_szDir, rc);
+ RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
+ APIRET orc;
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE(".")))) == ERROR_ACCESS_DENIED,
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("..")))) == ERROR_ACCESS_DENIED,
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
+ RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("")))) == ERROR_PATH_NOT_FOUND,
+ ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_PATH_NOT_FOUND)); /* hpfs+jfs; weird. */
+
+#else
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_IS_A_DIRECTORY);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_IS_A_DIRECTORY);
+ RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_IS_A_DIRECTORY);
+#endif
+
+ /* Shallow: */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file19")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VERR_FILE_NOT_FOUND);
+
+ if (g_fManyFiles)
+ {
+ /*
+ * Profile the deletion of the manyfiles content.
+ */
+ {
+ InDir(RT_STR_TUPLE("manyfiles" RTPATH_SLASH_STR));
+ size_t const offFilename = strlen(g_szDir);
+ fsPerfYield();
+ uint64_t const nsStart = RTTimeNanoTS();
+ for (uint32_t i = 0; i < g_cManyFiles; i++)
+ {
+ RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
+ RTTESTI_CHECK_RC_RETV(RTFileDelete(g_szDir), VINF_SUCCESS);
+ }
+ uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
+ RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files from a single directory", g_cManyFiles);
+ RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (single dir)");
+ }
+
+ /*
+ * Ditto for the manytree.
+ */
+ {
+ char szPath[FSPERF_MAX_PATH];
+ uint64_t const nsStart = RTTimeNanoTS();
+ DO_MANYTREE_FN(szPath, RTTESTI_CHECK_RC_RETV(RTFileDelete(szPath), VINF_SUCCESS));
+ uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
+ RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files in tree", g_cManyTreeFiles);
+ RTTestIValueF(cNsElapsed / g_cManyTreeFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (tree)");
+ }
+ }
+}
+
+
+void fsPerfChSize(void)
+{
+ RTTestISub("chsize");
+
+ /*
+ * We need some free space to perform this test.
+ */
+ g_szDir[g_cchDir] = '\0';
+ RTFOFF cbFree = 0;
+ RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
+ if (cbFree < _1M)
+ {
+ RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree);
+ return;
+ }
+
+ /*
+ * Create a file and play around with it's size.
+ * We let the current file position follow the end position as we make changes.
+ */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
+ uint64_t cbFile = UINT64_MAX;
+ RTTESTI_CHECK_RC(RTFileQuerySize(hFile1, &cbFile), VINF_SUCCESS);
+ RTTESTI_CHECK(cbFile == 0);
+
+ uint8_t abBuf[4096];
+ static uint64_t const s_acbChanges[] =
+ {
+ 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M,
+ _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0
+ };
+ uint64_t cbOld = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++)
+ {
+ uint64_t cbNew = s_acbChanges[i];
+ if (cbNew + _64K >= (uint64_t)cbFree)
+ continue;
+
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileQuerySize(hFile1, &cbFile), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew));
+
+ if (cbNew > cbOld)
+ {
+ /* Check that the extension is all zeroed: */
+ uint64_t cbLeft = cbNew - cbOld;
+ while (cbLeft > 0)
+ {
+ memset(abBuf, 0xff, sizeof(abBuf));
+ size_t cbToRead = sizeof(abBuf);
+ if (cbToRead > cbLeft)
+ cbToRead = (size_t)cbLeft;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead));
+ cbLeft -= cbToRead;
+ }
+ }
+ else
+ {
+ /* Check that reading fails with EOF because current position is now beyond the end: */
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
+
+ /* Keep current position at the end of the file: */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ }
+ cbOld = cbNew;
+ }
+
+ /*
+ * Profile just the file setting operation itself, keeping the changes within
+ * an allocation unit to avoid needing to adjust the actual (host) FS allocation.
+ * ASSUMES allocation unit >= 512 and power of two.
+ */
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS);
+ PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc");
+
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
+}
+
+
+int fsPerfIoPrepFileWorker(RTFILE hFile1, uint64_t cbFile, uint8_t *pbBuf, size_t cbBuf)
+{
+ /*
+ * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
+ */
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+ memset(pbBuf, 0xf6, cbBuf);
+ uint64_t cbLeft = cbFile;
+ uint64_t off = 0;
+ while (cbLeft > 0)
+ {
+ Assert(!(off & (_1K - 1)));
+ Assert(!(cbBuf & (_1K - 1)));
+ for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K)
+ *(uint64_t *)&pbBuf[offBuf] = off;
+
+ size_t cbToWrite = cbBuf;
+ if (cbToWrite > cbLeft)
+ cbToWrite = (size_t)cbLeft;
+
+ RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck);
+ cbLeft -= cbToWrite;
+ }
+ return VINF_SUCCESS;
+}
+
+int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree)
+{
+ /*
+ * Seek to the end - 4K and write the last 4K.
+ * This should have the effect of filling the whole file with zeros.
+ */
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck);
+
+ /*
+ * Check that the space we searched across actually is zero filled.
+ */
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+ size_t cbBuf = RT_MIN(_1M, g_cbMaxBuffer);
+ uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf);
+ RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
+ uint64_t cbLeft = cbFile;
+ while (cbLeft > 0)
+ {
+ size_t cbToRead = cbBuf;
+ if (cbToRead > cbLeft)
+ cbToRead = (size_t)cbLeft;
+ pbBuf[cbToRead - 1] = 0xff;
+
+ RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH);
+
+ cbLeft -= cbToRead;
+ }
+
+ /*
+ * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
+ */
+ return fsPerfIoPrepFileWorker(hFile1, cbFile, pbBuf, cbBuf);
+}
+
+/**
+ * Used in relation to the mmap test when in non-default position.
+ */
+int fsPerfReinitFile(RTFILE hFile1, uint64_t cbFile)
+{
+ size_t cbBuf = RT_MIN(_1M, g_cbMaxBuffer);
+ uint8_t *pbBuf = (uint8_t *)RTMemAlloc(cbBuf);
+ RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
+
+ int rc = fsPerfIoPrepFileWorker(hFile1, cbFile, pbBuf, cbBuf);
+
+ RTMemFree(pbBuf);
+ return rc;
+}
+
+/**
+ * Checks the content read from the file fsPerfIoPrepFile() prepared.
+ */
+bool fsPerfCheckReadBuf(unsigned uLineNo, uint64_t off, uint8_t const *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
+{
+ uint32_t cMismatches = 0;
+ size_t offBuf = 0;
+ uint32_t offBlock = (uint32_t)(off & (_1K - 1));
+ while (offBuf < cbBuf)
+ {
+ /*
+ * Check the offset marker:
+ */
+ if (offBlock < sizeof(uint64_t))
+ {
+ RTUINT64U uMarker;
+ uMarker.u = off + offBuf - offBlock;
+ unsigned offMarker = offBlock & (sizeof(uint64_t) - 1);
+ while (offMarker < sizeof(uint64_t) && offBuf < cbBuf)
+ {
+ if (uMarker.au8[offMarker] != pbBuf[offBuf])
+ {
+ RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#x",
+ uLineNo, offBuf, off + offBuf, pbBuf[offBuf], uMarker.au8[offMarker]);
+ if (cMismatches++ > 32)
+ return false;
+ }
+ offMarker++;
+ offBuf++;
+ }
+ offBlock = sizeof(uint64_t);
+ }
+
+ /*
+ * Check the filling:
+ */
+ size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf - offBuf);
+ if ( cbFilling == 0
+ || ASMMemIsAllU8(&pbBuf[offBuf], cbFilling, bFiller))
+ offBuf += cbFilling;
+ else
+ {
+ /* Some mismatch, locate it/them: */
+ while (cbFilling > 0 && offBuf < cbBuf)
+ {
+ if (pbBuf[offBuf] != bFiller)
+ {
+ RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#04x",
+ uLineNo, offBuf, off + offBuf, pbBuf[offBuf], bFiller);
+ if (cMismatches++ > 32)
+ return false;
+ }
+ offBuf++;
+ cbFilling--;
+ }
+ }
+ offBlock = 0;
+ }
+ return cMismatches == 0;
+}
+
+
+/**
+ * Sets up write buffer with offset markers and fillers.
+ */
+void fsPerfFillWriteBuf(uint64_t off, uint8_t *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
+{
+ uint32_t offBlock = (uint32_t)(off & (_1K - 1));
+ while (cbBuf > 0)
+ {
+ /* The marker. */
+ if (offBlock < sizeof(uint64_t))
+ {
+ RTUINT64U uMarker;
+ uMarker.u = off + offBlock;
+ if (cbBuf > sizeof(uMarker) - offBlock)
+ {
+ memcpy(pbBuf, &uMarker.au8[offBlock], sizeof(uMarker) - offBlock);
+ pbBuf += sizeof(uMarker) - offBlock;
+ cbBuf -= sizeof(uMarker) - offBlock;
+ off += sizeof(uMarker) - offBlock;
+ }
+ else
+ {
+ memcpy(pbBuf, &uMarker.au8[offBlock], cbBuf);
+ return;
+ }
+ offBlock = sizeof(uint64_t);
+ }
+
+ /* Do the filling. */
+ size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf);
+ memset(pbBuf, bFiller, cbFilling);
+ pbBuf += cbFilling;
+ cbBuf -= cbFilling;
+ off += cbFilling;
+
+ offBlock = 0;
+ }
+}
+
+
+
+void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile)
+{
+ /*
+ * Do a bunch of search tests, most which are random.
+ */
+ struct
+ {
+ int rc;
+ uint32_t uMethod;
+ int64_t offSeek;
+ uint64_t offActual;
+
+ } aSeeks[9 + 64] =
+ {
+ { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 },
+ { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
+ { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile },
+ { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 },
+ { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 },
+ { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) },
+ { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 },
+#if defined(RT_OS_WINDOWS)
+ { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 },
+#else
+ { VERR_INVALID_PARAMETER, RTFILE_SEEK_CURRENT, -1, 0 },
+#endif
+ { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
+ };
+
+ uint64_t offActual = 0;
+ for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++)
+ {
+ switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END))
+ {
+ default: AssertFailedBreak();
+ case RTFILE_SEEK_BEGIN:
+ aSeeks[i].uMethod = RTFILE_SEEK_BEGIN;
+ aSeeks[i].rc = VINF_SUCCESS;
+ aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8);
+ aSeeks[i].offActual = offActual = aSeeks[i].offSeek;
+ break;
+
+ case RTFILE_SEEK_CURRENT:
+ aSeeks[i].uMethod = RTFILE_SEEK_CURRENT;
+ aSeeks[i].rc = VINF_SUCCESS;
+ aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual;
+ aSeeks[i].offActual = offActual += aSeeks[i].offSeek;
+ break;
+
+ case RTFILE_SEEK_END:
+ aSeeks[i].uMethod = RTFILE_SEEK_END;
+ aSeeks[i].rc = VINF_SUCCESS;
+ aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile);
+ aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek;
+ break;
+ }
+ }
+
+ for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++)
+ {
+ offActual = UINT64_MAX;
+ int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual);
+ if (rc != aSeeks[i].rc)
+ RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc);
+ if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual)
+ RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t offTell = RTFileTell(hFile1);
+ if (offTell != offActual)
+ RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell);
+ }
+
+ if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck)
+ {
+ uint8_t abBuf[_2K];
+ RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual);
+ uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */
+ if (uMarker != offActual + offMarker)
+ RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64",
+ i, offActual, uMarker, offActual + offMarker);
+
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS);
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Profile seeking relative to the beginning of the file and relative
+ * to the end. The latter might be more expensive in a SF context.
+ */
+ PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL),
+ g_nsTestRun, "RTFileSeek/BEGIN");
+ PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL),
+ g_nsTestRun, "RTFileSeek/END");
+
+}
+
+#ifdef FSPERF_TEST_SENDFILE
+
+/**
+ * Send file thread arguments.
+ */
+typedef struct FSPERFSENDFILEARGS
+{
+ uint64_t offFile;
+ size_t cbSend;
+ uint64_t cbSent;
+ size_t cbBuf;
+ uint8_t *pbBuf;
+ uint8_t bFiller;
+ bool fCheckBuf;
+ RTSOCKET hSocket;
+ uint64_t volatile tsThreadDone;
+} FSPERFSENDFILEARGS;
+
+/** Thread receiving the bytes from a sendfile() call. */
+static DECLCALLBACK(int) fsPerfSendFileThread(RTTHREAD hSelf, void *pvUser)
+{
+ FSPERFSENDFILEARGS *pArgs = (FSPERFSENDFILEARGS *)pvUser;
+ int rc = VINF_SUCCESS;
+
+ if (pArgs->fCheckBuf)
+ RTTestSetDefault(g_hTest, NULL);
+
+ uint64_t cbReceived = 0;
+ while (cbReceived < pArgs->cbSent)
+ {
+ size_t const cbToRead = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbReceived);
+ size_t cbActual = 0;
+ RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTTcpRead(pArgs->hSocket, pArgs->pbBuf, cbToRead, &cbActual), VINF_SUCCESS);
+ RTTEST_CHECK_BREAK(g_hTest, cbActual != 0);
+ RTTEST_CHECK(g_hTest, cbActual <= cbToRead);
+ if (pArgs->fCheckBuf)
+ fsPerfCheckReadBuf(__LINE__, pArgs->offFile + cbReceived, pArgs->pbBuf, cbActual, pArgs->bFiller);
+ cbReceived += cbActual;
+ }
+
+ pArgs->tsThreadDone = RTTimeNanoTS();
+
+ if (cbReceived == pArgs->cbSent && RT_SUCCESS(rc))
+ {
+ size_t cbActual = 0;
+ rc = RTSocketReadNB(pArgs->hSocket, pArgs->pbBuf, 1, &cbActual);
+ if (rc != VINF_SUCCESS && rc != VINF_TRY_AGAIN)
+ RTTestFailed(g_hTest, "RTSocketReadNB(sendfile client socket) -> %Rrc; expected VINF_SUCCESS or VINF_TRY_AGAIN\n", rc);
+ else if (cbActual != 0)
+ RTTestFailed(g_hTest, "sendfile client socket still contains data when done!\n");
+ }
+
+ RTTEST_CHECK_RC(g_hTest, RTSocketClose(pArgs->hSocket), VINF_SUCCESS);
+ pArgs->hSocket = NIL_RTSOCKET;
+
+ RT_NOREF(hSelf);
+ return rc;
+}
+
+
+static uint64_t fsPerfSendFileOne(FSPERFSENDFILEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
+ size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckBuf, unsigned iLine)
+{
+ /* Copy parameters to the argument structure: */
+ pArgs->offFile = offFile;
+ pArgs->cbSend = cbSend;
+ pArgs->cbSent = cbSent;
+ pArgs->bFiller = bFiller;
+ pArgs->fCheckBuf = fCheckBuf;
+
+ /* Create a socket pair. */
+ pArgs->hSocket = NIL_RTSOCKET;
+ RTSOCKET hServer = NIL_RTSOCKET;
+ RTTESTI_CHECK_RC_RET(RTTcpCreatePair(&hServer, &pArgs->hSocket, 0), VINF_SUCCESS, 0);
+
+ /* Create the receiving thread: */
+ int rc;
+ RTTHREAD hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSendFileThread, pArgs, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "sendfile"), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t const tsStart = RTTimeNanoTS();
+
+# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+ /* SystemV sendfile: */
+ loff_t offFileSf = pArgs->offFile;
+ ssize_t cbActual = sendfile((int)RTSocketToNative(hServer), (int)RTFileToNative(hFile1), &offFileSf, pArgs->cbSend);
+ int const iErr = errno;
+ if (cbActual < 0)
+ RTTestIFailed("%u: sendfile(socket, file, &%#X64, %#zx) failed (%zd): %d (%Rrc), offFileSf=%#RX64\n",
+ iLine, pArgs->offFile, pArgs->cbSend, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileSf);
+ else if ((uint64_t)cbActual != pArgs->cbSent)
+ RTTestIFailed("%u: sendfile(socket, file, &%#RX64, %#zx): %#zx, expected %#RX64 (offFileSf=%#RX64)\n",
+ iLine, pArgs->offFile, pArgs->cbSend, cbActual, pArgs->cbSent, (uint64_t)offFileSf);
+ else if ((uint64_t)offFileSf != pArgs->offFile + pArgs->cbSent)
+ RTTestIFailed("%u: sendfile(socket, file, &%#RX64, %#zx): %#zx; offFileSf=%#RX64, expected %#RX64\n",
+ iLine, pArgs->offFile, pArgs->cbSend, cbActual, (uint64_t)offFileSf, pArgs->offFile + pArgs->cbSent);
+#else
+ /* BSD sendfile: */
+# ifdef SF_SYNC
+ int fSfFlags = SF_SYNC;
+# else
+ int fSfFlags = 0;
+# endif
+ off_t cbActual = pArgs->cbSend;
+ rc = sendfile((int)RTFileToNative(hFile1), (int)RTSocketToNative(hServer),
+# ifdef RT_OS_DARWIN
+ pArgs->offFile, &cbActual, NULL, fSfFlags);
+# else
+ pArgs->offFile, cbActual, NULL, &cbActual, fSfFlags);
+# endif
+ int const iErr = errno;
+ if (rc != 0)
+ RTTestIFailed("%u: sendfile(file, socket, %#RX64, %#zx, NULL,, %#x) failed (%d): %d (%Rrc), cbActual=%#RX64\n",
+ iLine, pArgs->offFile, (size_t)pArgs->cbSend, rc, iErr, RTErrConvertFromErrno(iErr), (uint64_t)cbActual);
+ if ((uint64_t)cbActual != pArgs->cbSent)
+ RTTestIFailed("%u: sendfile(file, socket, %#RX64, %#zx, NULL,, %#x): cbActual=%#RX64, expected %#RX64 (rc=%d, errno=%d)\n",
+ iLine, pArgs->offFile, (size_t)pArgs->cbSend, (uint64_t)cbActual, pArgs->cbSent, rc, iErr);
+# endif
+ RTTESTI_CHECK_RC(RTSocketClose(hServer), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
+
+ if (pArgs->tsThreadDone >= tsStart)
+ return RT_MAX(pArgs->tsThreadDone - tsStart, 1);
+ }
+ return 0;
+}
+
+
+static void fsPerfSendFile(RTFILE hFile1, uint64_t cbFile)
+{
+ RTTestISub("sendfile");
+# ifdef RT_OS_LINUX
+ uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
+# else
+ uint64_t const cbFileMax = RT_MIN(cbFile, SSIZE_MAX - PAGE_OFFSET_MASK);
+# endif
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Allocate a buffer.
+ */
+ FSPERFSENDFILEARGS Args;
+ Args.cbBuf = RT_MIN(RT_MIN(cbFileMax, _16M), g_cbMaxBuffer);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ while (!Args.pbBuf)
+ {
+ Args.cbBuf /= 8;
+ RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ }
+
+ /*
+ * First iteration with default buffer content.
+ */
+ fsPerfSendFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, true /*fCheckBuf*/, __LINE__);
+ if (cbFileMax == cbFile)
+ fsPerfSendFileOne(&Args, hFile1, 63, cbFileMax, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
+ else
+ fsPerfSendFileOne(&Args, hFile1, 63, cbFileMax - 63, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
+
+ /*
+ * Write a block using the regular API and then send it, checking that
+ * the any caching that sendfile does is correctly updated.
+ */
+ uint8_t bFiller = 0xf6;
+ size_t cbToSend = RT_MIN(cbFileMax, Args.cbBuf);
+ do
+ {
+ fsPerfSendFileOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__); /* prime cache */
+
+ bFiller += 1;
+ fsPerfFillWriteBuf(0, Args.pbBuf, cbToSend, bFiller);
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, cbToSend, NULL), VINF_SUCCESS);
+
+ fsPerfSendFileOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__);
+
+ cbToSend /= 2;
+ } while (cbToSend >= PAGE_SIZE && ((unsigned)bFiller - 0xf7U) < 64);
+
+ /*
+ * Restore buffer content
+ */
+ bFiller = 0xf6;
+ fsPerfFillWriteBuf(0, Args.pbBuf, Args.cbBuf, bFiller);
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, Args.cbBuf, NULL), VINF_SUCCESS);
+
+ /*
+ * Do 128 random sends.
+ */
+ uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
+ for (uint32_t iTest = 0; iTest < 128; iTest++)
+ {
+ cbToSend = (size_t)RTRandU64Ex(1, iTest < 64 ? cbSmall : cbFileMax);
+ uint64_t const offToSendFrom = RTRandU64Ex(0, cbFile - 1);
+ uint64_t const cbSent = offToSendFrom + cbToSend <= cbFile ? cbToSend : cbFile - offToSendFrom;
+
+ fsPerfSendFileOne(&Args, hFile1, offToSendFrom, cbToSend, cbSent, bFiller, true /*fCheckBuf*/, __LINE__);
+ }
+
+ /*
+ * Benchmark it.
+ */
+ uint32_t cIterations = 0;
+ uint64_t nsElapsed = 0;
+ for (;;)
+ {
+ uint64_t cNsThis = fsPerfSendFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
+ nsElapsed += cNsThis;
+ cIterations++;
+ if (!cNsThis || nsElapsed >= g_nsTestRun)
+ break;
+ }
+ uint64_t cbTotal = cbFileMax * cIterations;
+ RTTestIValue("latency", nsElapsed / cIterations, RTTESTUNIT_NS_PER_CALL);
+ RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("calls", cIterations, RTTESTUNIT_CALLS);
+ RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
+ if (g_fShowDuration)
+ RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
+
+ /*
+ * Cleanup.
+ */
+ RTMemFree(Args.pbBuf);
+}
+
+#endif /* FSPERF_TEST_SENDFILE */
+#ifdef RT_OS_LINUX
+
+#ifndef __NR_splice
+# if defined(RT_ARCH_AMD64)
+# define __NR_splice 275
+# elif defined(RT_ARCH_X86)
+# define __NR_splice 313
+# else
+# error "fix me"
+# endif
+#endif
+
+/** FsPerf is built against ancient glibc, so make the splice syscall ourselves. */
+DECLINLINE(ssize_t) syscall_splice(int fdIn, loff_t *poffIn, int fdOut, loff_t *poffOut, size_t cbChunk, unsigned fFlags)
+{
+ return syscall(__NR_splice, fdIn, poffIn, fdOut, poffOut, cbChunk, fFlags);
+}
+
+
+/**
+ * Send file thread arguments.
+ */
+typedef struct FSPERFSPLICEARGS
+{
+ uint64_t offFile;
+ size_t cbSend;
+ uint64_t cbSent;
+ size_t cbBuf;
+ uint8_t *pbBuf;
+ uint8_t bFiller;
+ bool fCheckBuf;
+ uint32_t cCalls;
+ RTPIPE hPipe;
+ uint64_t volatile tsThreadDone;
+} FSPERFSPLICEARGS;
+
+
+/** Thread receiving the bytes from a splice() call. */
+static DECLCALLBACK(int) fsPerfSpliceToPipeThread(RTTHREAD hSelf, void *pvUser)
+{
+ FSPERFSPLICEARGS *pArgs = (FSPERFSPLICEARGS *)pvUser;
+ int rc = VINF_SUCCESS;
+
+ if (pArgs->fCheckBuf)
+ RTTestSetDefault(g_hTest, NULL);
+
+ uint64_t cbReceived = 0;
+ while (cbReceived < pArgs->cbSent)
+ {
+ size_t const cbToRead = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbReceived);
+ size_t cbActual = 0;
+ RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTPipeReadBlocking(pArgs->hPipe, pArgs->pbBuf, cbToRead, &cbActual), VINF_SUCCESS);
+ RTTEST_CHECK_BREAK(g_hTest, cbActual != 0);
+ RTTEST_CHECK(g_hTest, cbActual <= cbToRead);
+ if (pArgs->fCheckBuf)
+ fsPerfCheckReadBuf(__LINE__, pArgs->offFile + cbReceived, pArgs->pbBuf, cbActual, pArgs->bFiller);
+ cbReceived += cbActual;
+ }
+
+ pArgs->tsThreadDone = RTTimeNanoTS();
+
+ if (cbReceived == pArgs->cbSent && RT_SUCCESS(rc))
+ {
+ size_t cbActual = 0;
+ rc = RTPipeRead(pArgs->hPipe, pArgs->pbBuf, 1, &cbActual);
+ if (rc != VINF_SUCCESS && rc != VINF_TRY_AGAIN && rc != VERR_BROKEN_PIPE)
+ RTTestFailed(g_hTest, "RTPipeReadBlocking() -> %Rrc; expected VINF_SUCCESS or VINF_TRY_AGAIN\n", rc);
+ else if (cbActual != 0)
+ RTTestFailed(g_hTest, "splice read pipe still contains data when done!\n");
+ }
+
+ RTTEST_CHECK_RC(g_hTest, RTPipeClose(pArgs->hPipe), VINF_SUCCESS);
+ pArgs->hPipe = NIL_RTPIPE;
+
+ RT_NOREF(hSelf);
+ return rc;
+}
+
+
+/** Sends hFile1 to a pipe via the Linux-specific splice() syscall. */
+static uint64_t fsPerfSpliceToPipeOne(FSPERFSPLICEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
+ size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckBuf, unsigned iLine)
+{
+ /* Copy parameters to the argument structure: */
+ pArgs->offFile = offFile;
+ pArgs->cbSend = cbSend;
+ pArgs->cbSent = cbSent;
+ pArgs->bFiller = bFiller;
+ pArgs->fCheckBuf = fCheckBuf;
+
+ /* Create a socket pair. */
+ pArgs->hPipe = NIL_RTPIPE;
+ RTPIPE hPipeW = NIL_RTPIPE;
+ RTTESTI_CHECK_RC_RET(RTPipeCreate(&pArgs->hPipe, &hPipeW, 0 /*fFlags*/), VINF_SUCCESS, 0);
+
+ /* Create the receiving thread: */
+ int rc;
+ RTTHREAD hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSpliceToPipeThread, pArgs, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "splicerecv"), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t const tsStart = RTTimeNanoTS();
+ size_t cbLeft = cbSend;
+ size_t cbTotal = 0;
+ do
+ {
+ loff_t offFileIn = offFile;
+ ssize_t cbActual = syscall_splice((int)RTFileToNative(hFile1), &offFileIn, (int)RTPipeToNative(hPipeW), NULL,
+ cbLeft, 0 /*fFlags*/);
+ int const iErr = errno;
+ if (RT_UNLIKELY(cbActual < 0))
+ {
+ if (iErr == EPIPE && cbTotal == pArgs->cbSent)
+ break;
+ RTTestIFailed("%u: splice(file, &%#RX64, pipe, NULL, %#zx, 0) failed (%zd): %d (%Rrc), offFileIn=%#RX64\n",
+ iLine, offFile, cbLeft, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileIn);
+ break;
+ }
+ RTTESTI_CHECK_BREAK((uint64_t)cbActual <= cbLeft);
+ if ((uint64_t)offFileIn != offFile + (uint64_t)cbActual)
+ {
+ RTTestIFailed("%u: splice(file, &%#RX64, pipe, NULL, %#zx, 0): %#zx; offFileIn=%#RX64, expected %#RX64\n",
+ iLine, offFile, cbLeft, cbActual, (uint64_t)offFileIn, offFile + (uint64_t)cbActual);
+ break;
+ }
+ if (cbActual > 0)
+ {
+ pArgs->cCalls++;
+ offFile += (size_t)cbActual;
+ cbTotal += (size_t)cbActual;
+ cbLeft -= (size_t)cbActual;
+ }
+ else
+ break;
+ } while (cbLeft > 0);
+
+ if (cbTotal != pArgs->cbSent)
+ RTTestIFailed("%u: spliced a total of %#zx bytes, expected %#zx!\n", iLine, cbTotal, pArgs->cbSent);
+
+ RTTESTI_CHECK_RC(RTPipeClose(hPipeW), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
+
+ if (pArgs->tsThreadDone >= tsStart)
+ return RT_MAX(pArgs->tsThreadDone - tsStart, 1);
+ }
+ return 0;
+}
+
+
+static void fsPerfSpliceToPipe(RTFILE hFile1, uint64_t cbFile)
+{
+ RTTestISub("splice/to-pipe");
+
+ /*
+ * splice was introduced in 2.6.17 according to the man-page.
+ */
+ char szRelease[64];
+ RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
+ if (RTStrVersionCompare(szRelease, "2.6.17") < 0)
+ {
+ RTTestPassed(g_hTest, "too old kernel (%s)", szRelease);
+ return;
+ }
+
+ uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Allocate a buffer.
+ */
+ FSPERFSPLICEARGS Args;
+ Args.cbBuf = RT_MIN(RT_MIN(cbFileMax, _16M), g_cbMaxBuffer);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ while (!Args.pbBuf)
+ {
+ Args.cbBuf /= 8;
+ RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ }
+
+ /*
+ * First iteration with default buffer content.
+ */
+ fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, true /*fCheckBuf*/, __LINE__);
+ if (cbFileMax == cbFile)
+ fsPerfSpliceToPipeOne(&Args, hFile1, 63, cbFileMax, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
+ else
+ fsPerfSpliceToPipeOne(&Args, hFile1, 63, cbFileMax - 63, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
+
+ /*
+ * Write a block using the regular API and then send it, checking that
+ * the any caching that sendfile does is correctly updated.
+ */
+ uint8_t bFiller = 0xf6;
+ size_t cbToSend = RT_MIN(cbFileMax, Args.cbBuf);
+ do
+ {
+ fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__); /* prime cache */
+
+ bFiller += 1;
+ fsPerfFillWriteBuf(0, Args.pbBuf, cbToSend, bFiller);
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, cbToSend, NULL), VINF_SUCCESS);
+
+ fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__);
+
+ cbToSend /= 2;
+ } while (cbToSend >= PAGE_SIZE && ((unsigned)bFiller - 0xf7U) < 64);
+
+ /*
+ * Restore buffer content
+ */
+ bFiller = 0xf6;
+ fsPerfFillWriteBuf(0, Args.pbBuf, Args.cbBuf, bFiller);
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, Args.cbBuf, NULL), VINF_SUCCESS);
+
+ /*
+ * Do 128 random sends.
+ */
+ uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
+ for (uint32_t iTest = 0; iTest < 128; iTest++)
+ {
+ cbToSend = (size_t)RTRandU64Ex(1, iTest < 64 ? cbSmall : cbFileMax);
+ uint64_t const offToSendFrom = RTRandU64Ex(0, cbFile - 1);
+ uint64_t const cbSent = offToSendFrom + cbToSend <= cbFile ? cbToSend : cbFile - offToSendFrom;
+
+ fsPerfSpliceToPipeOne(&Args, hFile1, offToSendFrom, cbToSend, cbSent, bFiller, true /*fCheckBuf*/, __LINE__);
+ }
+
+ /*
+ * Benchmark it.
+ */
+ Args.cCalls = 0;
+ uint32_t cIterations = 0;
+ uint64_t nsElapsed = 0;
+ for (;;)
+ {
+ uint64_t cNsThis = fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
+ nsElapsed += cNsThis;
+ cIterations++;
+ if (!cNsThis || nsElapsed >= g_nsTestRun)
+ break;
+ }
+ uint64_t cbTotal = cbFileMax * cIterations;
+ RTTestIValue("latency", nsElapsed / Args.cCalls, RTTESTUNIT_NS_PER_CALL);
+ RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("calls", Args.cCalls, RTTESTUNIT_CALLS);
+ RTTestIValue("bytes/call", cbTotal / Args.cCalls, RTTESTUNIT_BYTES);
+ RTTestIValue("iterations", cIterations, RTTESTUNIT_NONE);
+ RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
+ if (g_fShowDuration)
+ RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
+
+ /*
+ * Cleanup.
+ */
+ RTMemFree(Args.pbBuf);
+}
+
+
+/** Thread sending the bytes to a splice() call. */
+static DECLCALLBACK(int) fsPerfSpliceToFileThread(RTTHREAD hSelf, void *pvUser)
+{
+ FSPERFSPLICEARGS *pArgs = (FSPERFSPLICEARGS *)pvUser;
+ int rc = VINF_SUCCESS;
+
+ uint64_t offFile = pArgs->offFile;
+ uint64_t cbTotalSent = 0;
+ while (cbTotalSent < pArgs->cbSent)
+ {
+ size_t const cbToSend = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbTotalSent);
+ fsPerfFillWriteBuf(offFile, pArgs->pbBuf, cbToSend, pArgs->bFiller);
+ RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTPipeWriteBlocking(pArgs->hPipe, pArgs->pbBuf, cbToSend, NULL), VINF_SUCCESS);
+ offFile += cbToSend;
+ cbTotalSent += cbToSend;
+ }
+
+ pArgs->tsThreadDone = RTTimeNanoTS();
+
+ RTTEST_CHECK_RC(g_hTest, RTPipeClose(pArgs->hPipe), VINF_SUCCESS);
+ pArgs->hPipe = NIL_RTPIPE;
+
+ RT_NOREF(hSelf);
+ return rc;
+}
+
+
+/** Fill hFile1 via a pipe and the Linux-specific splice() syscall. */
+static uint64_t fsPerfSpliceToFileOne(FSPERFSPLICEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
+ size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckFile, unsigned iLine)
+{
+ /* Copy parameters to the argument structure: */
+ pArgs->offFile = offFile;
+ pArgs->cbSend = cbSend;
+ pArgs->cbSent = cbSent;
+ pArgs->bFiller = bFiller;
+ pArgs->fCheckBuf = false;
+
+ /* Create a socket pair. */
+ pArgs->hPipe = NIL_RTPIPE;
+ RTPIPE hPipeR = NIL_RTPIPE;
+ RTTESTI_CHECK_RC_RET(RTPipeCreate(&hPipeR, &pArgs->hPipe, 0 /*fFlags*/), VINF_SUCCESS, 0);
+
+ /* Create the receiving thread: */
+ int rc;
+ RTTHREAD hThread = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSpliceToFileThread, pArgs, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "splicerecv"), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the splicing.
+ */
+ uint64_t const tsStart = RTTimeNanoTS();
+ size_t cbLeft = cbSend;
+ size_t cbTotal = 0;
+ do
+ {
+ loff_t offFileOut = offFile;
+ ssize_t cbActual = syscall_splice((int)RTPipeToNative(hPipeR), NULL, (int)RTFileToNative(hFile1), &offFileOut,
+ cbLeft, 0 /*fFlags*/);
+ int const iErr = errno;
+ if (RT_UNLIKELY(cbActual < 0))
+ {
+ RTTestIFailed("%u: splice(pipe, NULL, file, &%#RX64, %#zx, 0) failed (%zd): %d (%Rrc), offFileOut=%#RX64\n",
+ iLine, offFile, cbLeft, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileOut);
+ break;
+ }
+ RTTESTI_CHECK_BREAK((uint64_t)cbActual <= cbLeft);
+ if ((uint64_t)offFileOut != offFile + (uint64_t)cbActual)
+ {
+ RTTestIFailed("%u: splice(pipe, NULL, file, &%#RX64, %#zx, 0): %#zx; offFileOut=%#RX64, expected %#RX64\n",
+ iLine, offFile, cbLeft, cbActual, (uint64_t)offFileOut, offFile + (uint64_t)cbActual);
+ break;
+ }
+ if (cbActual > 0)
+ {
+ pArgs->cCalls++;
+ offFile += (size_t)cbActual;
+ cbTotal += (size_t)cbActual;
+ cbLeft -= (size_t)cbActual;
+ }
+ else
+ break;
+ } while (cbLeft > 0);
+ uint64_t const nsElapsed = RTTimeNanoTS() - tsStart;
+
+ if (cbTotal != pArgs->cbSent)
+ RTTestIFailed("%u: spliced a total of %#zx bytes, expected %#zx!\n", iLine, cbTotal, pArgs->cbSent);
+
+ RTTESTI_CHECK_RC(RTPipeClose(hPipeR), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
+
+ /* Check the file content. */
+ if (fCheckFile && cbTotal == pArgs->cbSent)
+ {
+ offFile = pArgs->offFile;
+ cbLeft = cbSent;
+ while (cbLeft > 0)
+ {
+ size_t cbToRead = RT_MIN(cbLeft, pArgs->cbBuf);
+ RTTESTI_CHECK_RC_BREAK(RTFileReadAt(hFile1, offFile, pArgs->pbBuf, cbToRead, NULL), VINF_SUCCESS);
+ if (!fsPerfCheckReadBuf(iLine, offFile, pArgs->pbBuf, cbToRead, pArgs->bFiller))
+ break;
+ offFile += cbToRead;
+ cbLeft -= cbToRead;
+ }
+ }
+ return nsElapsed;
+ }
+ return 0;
+}
+
+
+static void fsPerfSpliceToFile(RTFILE hFile1, uint64_t cbFile)
+{
+ RTTestISub("splice/to-file");
+
+ /*
+ * splice was introduced in 2.6.17 according to the man-page.
+ */
+ char szRelease[64];
+ RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
+ if (RTStrVersionCompare(szRelease, "2.6.17") < 0)
+ {
+ RTTestPassed(g_hTest, "too old kernel (%s)", szRelease);
+ return;
+ }
+
+ uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Allocate a buffer.
+ */
+ FSPERFSPLICEARGS Args;
+ Args.cbBuf = RT_MIN(RT_MIN(cbFileMax, _16M), g_cbMaxBuffer);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ while (!Args.pbBuf)
+ {
+ Args.cbBuf /= 8;
+ RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
+ Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
+ }
+
+ /*
+ * Do the whole file.
+ */
+ uint8_t bFiller = 0x76;
+ fsPerfSpliceToFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, bFiller, true /*fCheckFile*/, __LINE__);
+
+ /*
+ * Do 64 random chunks (this is slower).
+ */
+ uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
+ for (uint32_t iTest = 0; iTest < 64; iTest++)
+ {
+ size_t const cbToWrite = (size_t)RTRandU64Ex(1, iTest < 24 ? cbSmall : cbFileMax);
+ uint64_t const offToWriteAt = RTRandU64Ex(0, cbFile - cbToWrite);
+ uint64_t const cbTryRead = cbToWrite + (iTest & 1 ? RTRandU32Ex(0, _64K) : 0);
+
+ bFiller++;
+ fsPerfSpliceToFileOne(&Args, hFile1, offToWriteAt, cbTryRead, cbToWrite, bFiller, true /*fCheckFile*/, __LINE__);
+ }
+
+ /*
+ * Benchmark it.
+ */
+ Args.cCalls = 0;
+ uint32_t cIterations = 0;
+ uint64_t nsElapsed = 0;
+ for (;;)
+ {
+ uint64_t cNsThis = fsPerfSpliceToFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
+ nsElapsed += cNsThis;
+ cIterations++;
+ if (!cNsThis || nsElapsed >= g_nsTestRun)
+ break;
+ }
+ uint64_t cbTotal = cbFileMax * cIterations;
+ RTTestIValue("latency", nsElapsed / Args.cCalls, RTTESTUNIT_NS_PER_CALL);
+ RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("calls", Args.cCalls, RTTESTUNIT_CALLS);
+ RTTestIValue("bytes/call", cbTotal / Args.cCalls, RTTESTUNIT_BYTES);
+ RTTestIValue("iterations", cIterations, RTTESTUNIT_NONE);
+ RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
+ if (g_fShowDuration)
+ RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
+
+ /*
+ * Cleanup.
+ */
+ RTMemFree(Args.pbBuf);
+}
+
+#endif /* RT_OS_LINUX */
+
+/** For fsPerfIoRead and fsPerfIoWrite. */
+#define PROFILE_IO_FN(a_szOperation, a_fnCall) \
+ do \
+ { \
+ RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); \
+ uint64_t offActual = 0; \
+ uint32_t cSeeks = 0; \
+ \
+ /* Estimate how many iterations we need to fill up the given timeslot: */ \
+ fsPerfYield(); \
+ uint64_t nsStart = RTTimeNanoTS(); \
+ uint64_t ns; \
+ do \
+ ns = RTTimeNanoTS(); \
+ while (ns == nsStart); \
+ nsStart = ns; \
+ \
+ uint64_t iIteration = 0; \
+ do \
+ { \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ iIteration++; \
+ ns = RTTimeNanoTS() - nsStart; \
+ } while (ns < RT_NS_10MS); \
+ ns /= iIteration; \
+ if (ns > g_nsPerNanoTSCall + 32) \
+ ns -= g_nsPerNanoTSCall; \
+ uint64_t cIterations = g_nsTestRun / ns; \
+ if (cIterations < 2) \
+ cIterations = 2; \
+ else if (cIterations & 1) \
+ cIterations++; \
+ \
+ /* Do the actual profiling: */ \
+ cSeeks = 0; \
+ iIteration = 0; \
+ fsPerfYield(); \
+ nsStart = RTTimeNanoTS(); \
+ for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
+ { \
+ for (; iIteration < cIterations; iIteration++)\
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ ns = RTTimeNanoTS() - nsStart;\
+ if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
+ break; \
+ cIterations += cIterations / 4; \
+ if (cIterations & 1) \
+ cIterations++; \
+ nsStart += g_nsPerNanoTSCall; \
+ } \
+ RTTestIValueF(ns / iIteration, \
+ RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation "/seq/%RU32 latency", cbBlock); \
+ RTTestIValueF((uint64_t)((double)(iIteration * cbBlock) / ((double)ns / RT_NS_1SEC)), \
+ RTTESTUNIT_BYTES_PER_SEC, a_szOperation "/seq/%RU32 throughput", cbBlock); \
+ RTTestIValueF(iIteration, \
+ RTTESTUNIT_CALLS, a_szOperation "/seq/%RU32 calls", cbBlock); \
+ RTTestIValueF((uint64_t)iIteration * cbBlock, \
+ RTTESTUNIT_BYTES, a_szOperation "/seq/%RU32 bytes", cbBlock); \
+ RTTestIValueF(cSeeks, \
+ RTTESTUNIT_OCCURRENCES, a_szOperation "/seq/%RU32 seeks", cbBlock); \
+ if (g_fShowDuration) \
+ RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation "/seq/%RU32 duration", cbBlock); \
+ } while (0)
+
+
+/**
+ * One RTFileRead profiling iteration.
+ */
+DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
+ uint64_t *poffActual, uint32_t *pcSeeks)
+{
+ /* Do we need to seek back to the start? */
+ if (*poffActual + cbBlock <= cbFile)
+ { /* likely */ }
+ else
+ {
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+ *pcSeeks += 1;
+ *poffActual = 0;
+ }
+
+ size_t cbActuallyRead = 0;
+ RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck);
+ if (cbActuallyRead == cbBlock)
+ {
+ *poffActual += cbActuallyRead;
+ return VINF_SUCCESS;
+ }
+ RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock);
+ *poffActual += cbActuallyRead;
+ return VERR_READ_ERROR;
+}
+
+
+void fsPerfIoReadBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
+{
+ RTTestISubF("IO - Sequential read %RU32", cbBlock);
+ if (cbBlock <= cbFile)
+ {
+
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
+ if (pbBuf)
+ {
+ memset(pbBuf, 0xf7, cbBlock);
+ PROFILE_IO_FN("RTFileRead", fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
+ RTMemPageFree(pbBuf, cbBlock);
+ }
+ else
+ RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
+ }
+ else
+ RTTestSkipped(g_hTest, "test file too small");
+}
+
+
+/** preadv is too new to be useful, so we use the readv api via this wrapper. */
+DECLINLINE(int) myFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
+{
+ int rc = RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileSgRead(hFile, pSgBuf, cbToRead, pcbRead);
+ return rc;
+}
+
+
+void fsPerfRead(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
+{
+ RTTestISubF("IO - RTFileRead");
+
+ /*
+ * Allocate a big buffer we can play around with. Min size is 1MB.
+ */
+ size_t cbMaxBuf = RT_MIN(_64M, g_cbMaxBuffer);
+ size_t cbBuf = cbFile < cbMaxBuf ? (size_t)cbFile : cbMaxBuf;
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ while (!pbBuf)
+ {
+ cbBuf /= 2;
+ RTTESTI_CHECK_RETV(cbBuf >= _1M);
+ pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
+ }
+
+#if 1
+ /*
+ * Start at the beginning and read the full buffer in random small chunks, thereby
+ * checking that unaligned buffer addresses, size and file offsets work fine.
+ */
+ struct
+ {
+ uint64_t offFile;
+ uint32_t cbMax;
+ } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
+ for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++)
+ {
+ memset(pbBuf, 0x55, cbBuf);
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ for (size_t offBuf = 0; offBuf < cbBuf; )
+ {
+ uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
+ uint32_t const cbToRead = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, RT_MIN(aRuns[i].cbMax, cbLeft))
+ : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
+ : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
+ size_t cbActual = 0;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
+ if (cbActual == cbToRead)
+ {
+ offBuf += cbActual;
+ RTTESTI_CHECK_MSG(RTFileTell(hFile1) == aRuns[i].offFile + offBuf,
+ ("%#RX64, expected %#RX64\n", RTFileTell(hFile1), aRuns[i].offFile + offBuf));
+ }
+ else
+ {
+ RTTestIFailed("Attempting to read %#x bytes at %#zx, only got %#x bytes back! (cbLeft=%#x cbBuf=%#zx)\n",
+ cbToRead, offBuf, cbActual, cbLeft, cbBuf);
+ if (cbActual)
+ offBuf += cbActual;
+ else
+ pbBuf[offBuf++] = 0x11;
+ }
+ }
+ fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf);
+ }
+
+ /*
+ * Test reading beyond the end of the file.
+ */
+ size_t const acbMax[] = { cbBuf, _64K, _16K, _4K, 256 };
+ uint32_t const aoffFromEos[] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 63, 64, 127, 128, 255, 254, 256, 1023, 1024, 2048,
+ 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 8192, 16384, 32767, 32768, 32769, 65535, 65536, _1M - 1
+ };
+ for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
+ {
+ size_t const cbMaxRead = acbMax[iMax];
+ for (uint32_t iOffFromEos = 0; iOffFromEos < RT_ELEMENTS(aoffFromEos); iOffFromEos++)
+ {
+ uint32_t off = aoffFromEos[iOffFromEos];
+ if (off >= cbMaxRead)
+ continue;
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ size_t cbActual = ~(size_t)0;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
+ RTTESTI_CHECK(cbActual == off);
+
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ cbActual = ~(size_t)0;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, off, &cbActual), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbActual == off, ("%#zx vs %#zx\n", cbActual, off));
+
+ cbActual = ~(size_t)0;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, 1, &cbActual), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbActual == 0, ("cbActual=%zu\n", cbActual));
+
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
+
+ /* Repeat using native APIs in case IPRT or other layers hide status codes: */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+# ifdef RT_OS_OS2
+ ULONG cbActual2 = ~(ULONG)0;
+ APIRET orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, cbMaxRead, &cbActual2);
+ RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
+ RTTESTI_CHECK_MSG(cbActual2 == off, ("%#x vs %#x\n", cbActual2, off));
+# else
+ IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, pbBuf, (ULONG)cbMaxRead, NULL /*poffFile*/, NULL /*Key*/);
+ if (off == 0)
+ {
+ RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
+ RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
+ ("%#x vs %x/%#x; off=%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE, off));
+ RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
+ ("%#zx vs %zx/0; off=%#x\n", Ios.Information, IosVirgin.Information, off));
+ }
+ else
+ {
+ RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x, expected 0 (off=%#x cbMaxRead=%#zx)\n", rcNt, off, cbMaxRead));
+ RTTESTI_CHECK_MSG(Ios.Status == STATUS_SUCCESS, ("%#x; off=%#x\n", Ios.Status, off));
+ RTTESTI_CHECK_MSG(Ios.Information == off, ("%#zx vs %#x\n", Ios.Information, off));
+ }
+# endif
+
+# ifdef RT_OS_OS2
+ cbActual2 = ~(ULONG)0;
+ orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, 1, &cbActual2);
+ RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
+ RTTESTI_CHECK_MSG(cbActual2 == 0, ("cbActual2=%u\n", cbActual2));
+# else
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, pbBuf, 1, NULL /*poffFile*/, NULL /*Key*/);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
+# endif
+
+#endif
+ }
+ }
+
+ /*
+ * Test reading beyond end of the file.
+ */
+ for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
+ {
+ size_t const cbMaxRead = acbMax[iMax];
+ for (uint32_t off = 0; off < 256; off++)
+ {
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ size_t cbActual = ~(size_t)0;
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
+ RTTESTI_CHECK(cbActual == 0);
+
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
+
+ /* Repeat using native APIs in case IPRT or other layers hid status codes: */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+# ifdef RT_OS_OS2
+ ULONG cbActual2 = ~(ULONG)0;
+ APIRET orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, cbMaxRead, &cbActual2);
+ RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
+ RTTESTI_CHECK_MSG(cbActual2 == 0, ("%#x vs %#x\n", cbActual2, off));
+# else
+ IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, pbBuf, (ULONG)cbMaxRead, NULL /*poffFile*/, NULL /*Key*/);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
+ RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
+ ("%#x vs %x/%#x; off=%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE, off));
+ RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
+ ("%#zx vs %zx/0; off=%#x\n", Ios.Information, IosVirgin.Information, off));
+
+ /* Need to work with sector size on uncached, but might be worth it for non-fastio path. */
+ uint32_t cbSector = 0x1000;
+ uint32_t off2 = off * cbSector + (cbFile & (cbSector - 1) ? cbSector - (cbFile & (cbSector - 1)) : 0);
+ RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, cbFile + off2, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ size_t const cbMaxRead2 = RT_ALIGN_Z(cbMaxRead, cbSector);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtReadFile((HANDLE)RTFileToNative(hFileNoCache), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
+ &Ios, pbBuf, (ULONG)cbMaxRead2, NULL /*poffFile*/, NULL /*Key*/);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE,
+ ("rcNt=%#x, expected %#x; off2=%x cbMaxRead2=%#x\n", rcNt, STATUS_END_OF_FILE, off2, cbMaxRead2));
+ RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/,
+ ("%#x vs %x; off2=%#x cbMaxRead2=%#x\n", Ios.Status, IosVirgin.Status, off2, cbMaxRead2));
+ RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/,
+ ("%#zx vs %zx; off2=%#x cbMaxRead2=%#x\n", Ios.Information, IosVirgin.Information, off2, cbMaxRead2));
+# endif
+#endif
+ }
+ }
+
+ /*
+ * Do uncached access, must be page aligned.
+ */
+ uint32_t cbPage = PAGE_SIZE;
+ memset(pbBuf, 0x66, cbBuf);
+ if (!g_fIgnoreNoCache || hFileNoCache != NIL_RTFILE)
+ {
+ RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ for (size_t offBuf = 0; offBuf < cbBuf; )
+ {
+ uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
+ uint32_t const cPagesToRead = RTRandU32Ex(1, cPagesLeft);
+ size_t const cbToRead = cPagesToRead * (size_t)cbPage;
+ size_t cbActual = 0;
+ RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
+ if (cbActual == cbToRead)
+ offBuf += cbActual;
+ else
+ {
+ RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x bytes back!\n", cbToRead, offBuf, cbActual);
+ if (cbActual)
+ offBuf += cbActual;
+ else
+ {
+ memset(&pbBuf[offBuf], 0x11, cbPage);
+ offBuf += cbPage;
+ }
+ }
+ }
+ fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf);
+ }
+
+ /*
+ * Check reading zero bytes at the end of the file.
+ * Requires native call because RTFileWrite doesn't call kernel on zero byte reads.
+ */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
+# ifdef RT_OS_WINDOWS
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
+ RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
+ RTTESTI_CHECK(Ios.Information == 0);
+
+ IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 1, NULL, NULL);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x", rcNt));
+ RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
+ ("%#x vs %x/%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE));
+ RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
+ ("%#zx vs %zx/0\n", Ios.Information, IosVirgin.Information));
+# else
+ ssize_t cbRead = read((int)RTFileToNative(hFile1), pbBuf, 0);
+ RTTESTI_CHECK(cbRead == 0);
+# endif
+
+#else
+ RT_NOREF(hFileNoCache);
+#endif
+
+ /*
+ * Scatter read function operation.
+ */
+#ifdef RT_OS_WINDOWS
+ /** @todo RTFileSgReadAt is just a RTFileReadAt loop for windows NT. Need
+ * to use ReadFileScatter (nocache + page aligned). */
+#elif !defined(RT_OS_OS2) /** @todo implement RTFileSg using list i/o */
+
+# ifdef UIO_MAXIOV
+ RTSGSEG aSegs[UIO_MAXIOV];
+# else
+ RTSGSEG aSegs[512];
+# endif
+ RTSGBUF SgBuf;
+ uint32_t cIncr = 1;
+ for (uint32_t cSegs = 1; cSegs <= RT_ELEMENTS(aSegs); cSegs += cIncr)
+ {
+ size_t const cbSeg = cbBuf / cSegs;
+ size_t const cbToRead = cbSeg * cSegs;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ aSegs[iSeg].cbSeg = cbSeg;
+ aSegs[iSeg].pvSeg = &pbBuf[cbToRead - (iSeg + 1) * cbSeg];
+ }
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rc = myFileSgReadAt(hFile1, 0, &SgBuf, cbToRead, NULL);
+ if (RT_SUCCESS(rc))
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ if (!fsPerfCheckReadBuf(__LINE__, iSeg * cbSeg, &pbBuf[cbToRead - (iSeg + 1) * cbSeg], cbSeg))
+ {
+ cSegs = RT_ELEMENTS(aSegs);
+ break;
+ }
+ }
+ else
+ {
+ RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%u cbSegs=%#zx cbToRead=%#zx", rc, cSegs, cbSeg, cbToRead);
+ break;
+ }
+ if (cSegs == 16)
+ cIncr = 7;
+ else if (cSegs == 16 * 7 + 16 /*= 128*/)
+ cIncr = 64;
+ }
+
+ for (uint32_t iTest = 0; iTest < 128; iTest++)
+ {
+ uint32_t cSegs = RTRandU32Ex(1, RT_ELEMENTS(aSegs));
+ uint32_t iZeroSeg = cSegs > 10 ? RTRandU32Ex(0, cSegs - 1) : UINT32_MAX / 2;
+ uint32_t cZeroSegs = cSegs > 10 ? RTRandU32Ex(1, RT_MIN(cSegs - iZeroSeg, 25)) : 0;
+ size_t cbToRead = 0;
+ size_t cbLeft = cbBuf;
+ uint8_t *pbCur = &pbBuf[cbBuf];
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t iAlign = RTRandU32Ex(0, 3);
+ if (iAlign & 2) /* end is page aligned */
+ {
+ cbLeft -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
+ pbCur -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
+ }
+
+ size_t cbSegOthers = (cSegs - iSeg) * _8K;
+ size_t cbSegMax = cbLeft > cbSegOthers ? cbLeft - cbSegOthers
+ : cbLeft > cSegs ? cbLeft - cSegs
+ : cbLeft;
+ size_t cbSeg = cbLeft != 0 ? RTRandU32Ex(0, cbSegMax) : 0;
+ if (iAlign & 1) /* start is page aligned */
+ cbSeg += ((uintptr_t)pbCur - cbSeg) & PAGE_OFFSET_MASK;
+
+ if (iSeg - iZeroSeg < cZeroSegs)
+ cbSeg = 0;
+
+ cbToRead += cbSeg;
+ cbLeft -= cbSeg;
+ pbCur -= cbSeg;
+ aSegs[iSeg].cbSeg = cbSeg;
+ aSegs[iSeg].pvSeg = pbCur;
+ }
+
+ uint64_t offFile = cbToRead < cbFile ? RTRandU64Ex(0, cbFile - cbToRead) : 0;
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, NULL);
+ if (RT_SUCCESS(rc))
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ if (!fsPerfCheckReadBuf(__LINE__, offFile, (uint8_t *)aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg))
+ {
+ RTTestIFailureDetails("iSeg=%#x cSegs=%#x cbSeg=%#zx cbToRead=%#zx\n", iSeg, cSegs, aSegs[iSeg].cbSeg, cbToRead);
+ iTest = _16K;
+ break;
+ }
+ offFile += aSegs[iSeg].cbSeg;
+ }
+ else
+ {
+ RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%#x cbToRead=%#zx", rc, cSegs, cbToRead);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ RTTestIFailureDetails("aSeg[%u] = %p LB %#zx (last %p)\n", iSeg, aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg,
+ (uint8_t *)aSegs[iSeg].pvSeg + aSegs[iSeg].cbSeg - 1);
+ break;
+ }
+ }
+
+ /* reading beyond the end of the file */
+ for (uint32_t cSegs = 1; cSegs < 6; cSegs++)
+ for (uint32_t iTest = 0; iTest < 128; iTest++)
+ {
+ uint32_t const cbToRead = RTRandU32Ex(0, cbBuf);
+ uint32_t const cbBeyond = cbToRead ? RTRandU32Ex(0, cbToRead) : 0;
+ uint32_t const cbSeg = cbToRead / cSegs;
+ uint32_t cbLeft = cbToRead;
+ uint8_t *pbCur = &pbBuf[cbToRead];
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ aSegs[iSeg].cbSeg = iSeg + 1 < cSegs ? cbSeg : cbLeft;
+ aSegs[iSeg].pvSeg = pbCur -= aSegs[iSeg].cbSeg;
+ cbLeft -= aSegs[iSeg].cbSeg;
+ }
+ Assert(pbCur == pbBuf);
+
+ uint64_t offFile = cbFile + cbBeyond - cbToRead;
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rcExpect = cbBeyond == 0 || cbToRead == 0 ? VINF_SUCCESS : VERR_EOF;
+ int rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, NULL);
+ if (rc != rcExpect)
+ {
+ RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%#x cbToRead=%#zx cbBeyond=%#zx\n", rc, cSegs, cbToRead, cbBeyond);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ RTTestIFailureDetails("aSeg[%u] = %p LB %#zx (last %p)\n", iSeg, aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg,
+ (uint8_t *)aSegs[iSeg].pvSeg + aSegs[iSeg].cbSeg - 1);
+ }
+
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ size_t cbActual = 0;
+ rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, &cbActual);
+ if (rc != VINF_SUCCESS || cbActual != cbToRead - cbBeyond)
+ RTTestIFailed("myFileSgReadAt failed: %Rrc cbActual=%#zu - cSegs=%#x cbToRead=%#zx cbBeyond=%#zx expected %#zx\n",
+ rc, cbActual, cSegs, cbToRead, cbBeyond, cbToRead - cbBeyond);
+ if (RT_SUCCESS(rc) && cbActual > 0)
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ if (!fsPerfCheckReadBuf(__LINE__, offFile, (uint8_t *)aSegs[iSeg].pvSeg, RT_MIN(cbActual, aSegs[iSeg].cbSeg)))
+ {
+ RTTestIFailureDetails("iSeg=%#x cSegs=%#x cbSeg=%#zx cbActual%#zx cbToRead=%#zx cbBeyond=%#zx\n",
+ iSeg, cSegs, aSegs[iSeg].cbSeg, cbActual, cbToRead, cbBeyond);
+ iTest = _16K;
+ break;
+ }
+ if (cbActual <= aSegs[iSeg].cbSeg)
+ break;
+ cbActual -= aSegs[iSeg].cbSeg;
+ offFile += aSegs[iSeg].cbSeg;
+ }
+ }
+
+#endif
+
+ /*
+ * Other OS specific stuff.
+ */
+#ifdef RT_OS_WINDOWS
+ /* Check that reading at an offset modifies the position: */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
+
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ LARGE_INTEGER offNt;
+ offNt.QuadPart = cbFile / 2;
+ rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
+ RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
+ RTTESTI_CHECK(Ios.Information == _4K);
+ RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
+ fsPerfCheckReadBuf(__LINE__, cbFile / 2, pbBuf, _4K);
+#endif
+
+
+ RTMemPageFree(pbBuf, cbBuf);
+}
+
+
+/**
+ * One RTFileWrite profiling iteration.
+ */
+DECL_FORCE_INLINE(int) fsPerfIoWriteWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
+ uint64_t *poffActual, uint32_t *pcSeeks)
+{
+ /* Do we need to seek back to the start? */
+ if (*poffActual + cbBlock <= cbFile)
+ { /* likely */ }
+ else
+ {
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+ *pcSeeks += 1;
+ *poffActual = 0;
+ }
+
+ size_t cbActuallyWritten = 0;
+ RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBlock, cbBlock, &cbActuallyWritten), VINF_SUCCESS, rcCheck);
+ if (cbActuallyWritten == cbBlock)
+ {
+ *poffActual += cbActuallyWritten;
+ return VINF_SUCCESS;
+ }
+ RTTestIFailed("RTFileWrite at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyWritten, cbBlock);
+ *poffActual += cbActuallyWritten;
+ return VERR_WRITE_ERROR;
+}
+
+
+void fsPerfIoWriteBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
+{
+ RTTestISubF("IO - Sequential write %RU32", cbBlock);
+
+ if (cbBlock <= cbFile)
+ {
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
+ if (pbBuf)
+ {
+ memset(pbBuf, 0xf7, cbBlock);
+ PROFILE_IO_FN("RTFileWrite", fsPerfIoWriteWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
+ RTMemPageFree(pbBuf, cbBlock);
+ }
+ else
+ RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
+ }
+ else
+ RTTestSkipped(g_hTest, "test file too small");
+}
+
+
+/** pwritev is too new to be useful, so we use the writev api via this wrapper. */
+DECLINLINE(int) myFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ int rc = RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileSgWrite(hFile, pSgBuf, cbToWrite, pcbWritten);
+ return rc;
+}
+
+
+void fsPerfWrite(RTFILE hFile1, RTFILE hFileNoCache, RTFILE hFileWriteThru, uint64_t cbFile)
+{
+ RTTestISubF("IO - RTFileWrite");
+
+ /*
+ * Allocate a big buffer we can play around with. Min size is 1MB.
+ */
+ size_t cbMaxBuf = RT_MIN(_64M, g_cbMaxBuffer);
+ size_t cbBuf = cbFile < cbMaxBuf ? (size_t)cbFile : cbMaxBuf;
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ while (!pbBuf)
+ {
+ cbBuf /= 2;
+ RTTESTI_CHECK_RETV(cbBuf >= _1M);
+ pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
+ }
+
+ uint8_t bFiller = 0x88;
+
+#if 1
+ /*
+ * Start at the beginning and write out the full buffer in random small chunks, thereby
+ * checking that unaligned buffer addresses, size and file offsets work fine.
+ */
+ struct
+ {
+ uint64_t offFile;
+ uint32_t cbMax;
+ } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
+ for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++, bFiller)
+ {
+ fsPerfFillWriteBuf(aRuns[i].offFile, pbBuf, cbBuf, bFiller);
+ fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
+
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ for (size_t offBuf = 0; offBuf < cbBuf; )
+ {
+ uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
+ uint32_t const cbToWrite = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, RT_MIN(aRuns[i].cbMax, cbLeft))
+ : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
+ : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
+ size_t cbActual = 0;
+ RTTESTI_CHECK_RC(RTFileWrite(hFile1, &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
+ if (cbActual == cbToWrite)
+ {
+ offBuf += cbActual;
+ RTTESTI_CHECK_MSG(RTFileTell(hFile1) == aRuns[i].offFile + offBuf,
+ ("%#RX64, expected %#RX64\n", RTFileTell(hFile1), aRuns[i].offFile + offBuf));
+ }
+ else
+ {
+ RTTestIFailed("Attempting to write %#x bytes at %#zx (%#x left), only got %#x written!\n",
+ cbToWrite, offBuf, cbLeft, cbActual);
+ if (cbActual)
+ offBuf += cbActual;
+ else
+ pbBuf[offBuf++] = 0x11;
+ }
+ }
+
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, aRuns[i].offFile, pbBuf, cbBuf, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
+ }
+
+
+ /*
+ * Do uncached and write-thru accesses, must be page aligned.
+ */
+ RTFILE ahFiles[2] = { hFileWriteThru, hFileNoCache };
+ for (unsigned iFile = 0; iFile < RT_ELEMENTS(ahFiles); iFile++, bFiller++)
+ {
+ if (g_fIgnoreNoCache && ahFiles[iFile] == NIL_RTFILE)
+ continue;
+
+ fsPerfFillWriteBuf(0, pbBuf, cbBuf, bFiller);
+ fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
+ RTTESTI_CHECK_RC(RTFileSeek(ahFiles[iFile], 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+
+ uint32_t cbPage = PAGE_SIZE;
+ for (size_t offBuf = 0; offBuf < cbBuf; )
+ {
+ uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
+ uint32_t const cPagesToWrite = RTRandU32Ex(1, cPagesLeft);
+ size_t const cbToWrite = cPagesToWrite * (size_t)cbPage;
+ size_t cbActual = 0;
+ RTTESTI_CHECK_RC(RTFileWrite(ahFiles[iFile], &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
+ if (cbActual == cbToWrite)
+ {
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offBuf, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, offBuf, pbBuf, cbToWrite, bFiller);
+ offBuf += cbActual;
+ }
+ else
+ {
+ RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
+ if (cbActual)
+ offBuf += cbActual;
+ else
+ {
+ memset(&pbBuf[offBuf], 0x11, cbPage);
+ offBuf += cbPage;
+ }
+ }
+ }
+
+ RTTESTI_CHECK_RC(RTFileReadAt(ahFiles[iFile], 0, pbBuf, cbBuf, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
+ }
+
+ /*
+ * Check the behavior of writing zero bytes to the file _4K from the end
+ * using native API. In the olden days zero sized write have been known
+ * to be used to truncate a file.
+ */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, -_4K, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
+# ifdef RT_OS_WINDOWS
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
+ RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
+ RTTESTI_CHECK(Ios.Information == 0);
+# else
+ ssize_t cbWritten = write((int)RTFileToNative(hFile1), pbBuf, 0);
+ RTTESTI_CHECK(cbWritten == 0);
+# endif
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, _4K, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, cbFile - _4K, pbBuf, _4K, pbBuf[0x8]);
+
+#else
+ RT_NOREF(hFileNoCache, hFileWriteThru);
+#endif
+
+ /*
+ * Gather write function operation.
+ */
+#ifdef RT_OS_WINDOWS
+ /** @todo RTFileSgWriteAt is just a RTFileWriteAt loop for windows NT. Need
+ * to use WriteFileGather (nocache + page aligned). */
+#elif !defined(RT_OS_OS2) /** @todo implement RTFileSg using list i/o */
+
+# ifdef UIO_MAXIOV
+ RTSGSEG aSegs[UIO_MAXIOV];
+# else
+ RTSGSEG aSegs[512];
+# endif
+ RTSGBUF SgBuf;
+ uint32_t cIncr = 1;
+ for (uint32_t cSegs = 1; cSegs <= RT_ELEMENTS(aSegs); cSegs += cIncr, bFiller++)
+ {
+ size_t const cbSeg = cbBuf / cSegs;
+ size_t const cbToWrite = cbSeg * cSegs;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ aSegs[iSeg].cbSeg = cbSeg;
+ aSegs[iSeg].pvSeg = &pbBuf[cbToWrite - (iSeg + 1) * cbSeg];
+ fsPerfFillWriteBuf(iSeg * cbSeg, (uint8_t *)aSegs[iSeg].pvSeg, cbSeg, bFiller);
+ }
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rc = myFileSgWriteAt(hFile1, 0, &SgBuf, cbToWrite, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, 0, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbToWrite, bFiller);
+ }
+ else
+ {
+ RTTestIFailed("myFileSgWriteAt failed: %Rrc - cSegs=%u cbSegs=%#zx cbToWrite=%#zx", rc, cSegs, cbSeg, cbToWrite);
+ break;
+ }
+ if (cSegs == 16)
+ cIncr = 7;
+ else if (cSegs == 16 * 7 + 16 /*= 128*/)
+ cIncr = 64;
+ }
+
+ /* random stuff, including zero segments. */
+ for (uint32_t iTest = 0; iTest < 128; iTest++, bFiller++)
+ {
+ uint32_t cSegs = RTRandU32Ex(1, RT_ELEMENTS(aSegs));
+ uint32_t iZeroSeg = cSegs > 10 ? RTRandU32Ex(0, cSegs - 1) : UINT32_MAX / 2;
+ uint32_t cZeroSegs = cSegs > 10 ? RTRandU32Ex(1, RT_MIN(cSegs - iZeroSeg, 25)) : 0;
+ size_t cbToWrite = 0;
+ size_t cbLeft = cbBuf;
+ uint8_t *pbCur = &pbBuf[cbBuf];
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t iAlign = RTRandU32Ex(0, 3);
+ if (iAlign & 2) /* end is page aligned */
+ {
+ cbLeft -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
+ pbCur -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
+ }
+
+ size_t cbSegOthers = (cSegs - iSeg) * _8K;
+ size_t cbSegMax = cbLeft > cbSegOthers ? cbLeft - cbSegOthers
+ : cbLeft > cSegs ? cbLeft - cSegs
+ : cbLeft;
+ size_t cbSeg = cbLeft != 0 ? RTRandU32Ex(0, cbSegMax) : 0;
+ if (iAlign & 1) /* start is page aligned */
+ cbSeg += ((uintptr_t)pbCur - cbSeg) & PAGE_OFFSET_MASK;
+
+ if (iSeg - iZeroSeg < cZeroSegs)
+ cbSeg = 0;
+
+ cbToWrite += cbSeg;
+ cbLeft -= cbSeg;
+ pbCur -= cbSeg;
+ aSegs[iSeg].cbSeg = cbSeg;
+ aSegs[iSeg].pvSeg = pbCur;
+ }
+
+ uint64_t const offFile = cbToWrite < cbFile ? RTRandU64Ex(0, cbFile - cbToWrite) : 0;
+ uint64_t offFill = offFile;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ if (aSegs[iSeg].cbSeg)
+ {
+ fsPerfFillWriteBuf(offFill, (uint8_t *)aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg, bFiller);
+ offFill += aSegs[iSeg].cbSeg;
+ }
+
+ RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
+ int rc = myFileSgWriteAt(hFile1, offFile, &SgBuf, cbToWrite, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offFile, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, offFile, pbBuf, cbToWrite, bFiller);
+ }
+ else
+ {
+ RTTestIFailed("myFileSgWriteAt failed: %Rrc - cSegs=%#x cbToWrite=%#zx", rc, cSegs, cbToWrite);
+ break;
+ }
+ }
+
+#endif
+
+ /*
+ * Other OS specific stuff.
+ */
+#ifdef RT_OS_WINDOWS
+ /* Check that reading at an offset modifies the position: */
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, cbFile / 2, pbBuf, _4K, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
+
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ LARGE_INTEGER offNt;
+ offNt.QuadPart = cbFile / 2;
+ rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
+ RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
+ RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
+ RTTESTI_CHECK(Ios.Information == _4K);
+ RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
+#endif
+
+ RTMemPageFree(pbBuf, cbBuf);
+}
+
+
+/**
+ * Worker for testing RTFileFlush.
+ */
+DECL_FORCE_INLINE(int) fsPerfFSyncWorker(RTFILE hFile1, uint64_t cbFile, uint8_t *pbBuf, size_t cbBuf, uint64_t *poffFile)
+{
+ if (*poffFile + cbBuf <= cbFile)
+ { /* likely */ }
+ else
+ {
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ *poffFile = 0;
+ }
+
+ RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbBuf, NULL), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RC_RET(RTFileFlush(hFile1), VINF_SUCCESS, rcCheck);
+
+ *poffFile += cbBuf;
+ return VINF_SUCCESS;
+}
+
+
+void fsPerfFSync(RTFILE hFile1, uint64_t cbFile)
+{
+ RTTestISub("fsync");
+
+ RTTESTI_CHECK_RC(RTFileFlush(hFile1), VINF_SUCCESS);
+
+ PROFILE_FN(RTFileFlush(hFile1), g_nsTestRun, "RTFileFlush");
+
+ size_t cbBuf = PAGE_SIZE;
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ RTTESTI_CHECK_RETV(pbBuf != NULL);
+ memset(pbBuf, 0xf4, cbBuf);
+
+ RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ uint64_t offFile = 0;
+ PROFILE_FN(fsPerfFSyncWorker(hFile1, cbFile, pbBuf, cbBuf, &offFile), g_nsTestRun, "RTFileWrite[Page]/RTFileFlush");
+
+ RTMemPageFree(pbBuf, cbBuf);
+}
+
+
+#ifndef RT_OS_OS2
+/**
+ * Worker for profiling msync.
+ */
+DECL_FORCE_INLINE(int) fsPerfMSyncWorker(uint8_t *pbMapping, size_t offMapping, size_t cbFlush, size_t *pcbFlushed)
+{
+ uint8_t *pbCur = &pbMapping[offMapping];
+ for (size_t offFlush = 0; offFlush < cbFlush; offFlush += PAGE_SIZE)
+ *(size_t volatile *)&pbCur[offFlush + 8] = cbFlush;
+# ifdef RT_OS_WINDOWS
+ CHECK_WINAPI_CALL(FlushViewOfFile(pbCur, cbFlush) == TRUE);
+# else
+ RTTESTI_CHECK(msync(pbCur, cbFlush, MS_SYNC) == 0);
+# endif
+ if (*pcbFlushed < offMapping + cbFlush)
+ *pcbFlushed = offMapping + cbFlush;
+ return VINF_SUCCESS;
+}
+#endif /* !RT_OS_OS2 */
+
+
+void fsPerfMMap(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
+{
+ RTTestISub("mmap");
+#if !defined(RT_OS_OS2)
+ static const char * const s_apszStates[] = { "readonly", "writecopy", "readwrite" };
+ enum { kMMap_ReadOnly = 0, kMMap_WriteCopy, kMMap_ReadWrite, kMMap_End };
+ for (int enmState = kMMap_ReadOnly; enmState < kMMap_End; enmState++)
+ {
+ /*
+ * Do the mapping.
+ */
+ size_t cbMapping = (size_t)cbFile;
+ if (cbMapping != cbFile)
+ cbMapping = _256M;
+ uint8_t *pbMapping;
+
+# ifdef RT_OS_WINDOWS
+ HANDLE hSection;
+ pbMapping = NULL;
+ for (;; cbMapping /= 2)
+ {
+ hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile1), NULL,
+ enmState == kMMap_ReadOnly ? PAGE_READONLY
+ : enmState == kMMap_WriteCopy ? PAGE_WRITECOPY : PAGE_READWRITE,
+ (uint32_t)((uint64_t)cbMapping >> 32), (uint32_t)cbMapping, NULL);
+ DWORD dwErr1 = GetLastError();
+ DWORD dwErr2 = 0;
+ if (hSection != NULL)
+ {
+ pbMapping = (uint8_t *)MapViewOfFile(hSection,
+ enmState == kMMap_ReadOnly ? FILE_MAP_READ
+ : enmState == kMMap_WriteCopy ? FILE_MAP_COPY
+ : FILE_MAP_WRITE,
+ 0, 0, cbMapping);
+ if (pbMapping)
+ break;
+ dwErr2 = GetLastError();
+ CHECK_WINAPI_CALL(CloseHandle(hSection) == TRUE);
+ }
+ if (cbMapping <= _2M)
+ {
+ RTTestIFailed("%u/%s: CreateFileMapping or MapViewOfFile failed: %u, %u",
+ enmState, s_apszStates[enmState], dwErr1, dwErr2);
+ break;
+ }
+ }
+# else
+ for (;; cbMapping /= 2)
+ {
+ pbMapping = (uint8_t *)mmap(NULL, cbMapping,
+ enmState == kMMap_ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE,
+ enmState == kMMap_WriteCopy ? MAP_PRIVATE : MAP_SHARED,
+ (int)RTFileToNative(hFile1), 0);
+ if ((void *)pbMapping != MAP_FAILED)
+ break;
+ if (cbMapping <= _2M)
+ {
+ RTTestIFailed("%u/%s: mmap failed: %s (%u)", enmState, s_apszStates[enmState], strerror(errno), errno);
+ break;
+ }
+ }
+# endif
+ if (cbMapping <= _2M)
+ continue;
+
+ /*
+ * Time page-ins just for fun.
+ */
+ size_t const cPages = cbMapping >> PAGE_SHIFT;
+ size_t uDummy = 0;
+ uint64_t ns = RTTimeNanoTS();
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ uDummy += ASMAtomicReadU8(&pbMapping[iPage << PAGE_SHIFT]);
+ ns = RTTimeNanoTS() - ns;
+ RTTestIValueF(ns / cPages, RTTESTUNIT_NS_PER_OCCURRENCE, "page-in %s", s_apszStates[enmState]);
+
+ /* Check the content. */
+ fsPerfCheckReadBuf(__LINE__, 0, pbMapping, cbMapping);
+
+ if (enmState != kMMap_ReadOnly)
+ {
+ /* Write stuff to the first two megabytes. In the COW case, we'll detect
+ corruption of shared data during content checking of the RW iterations. */
+ fsPerfFillWriteBuf(0, pbMapping, _2M, 0xf7);
+ if (enmState == kMMap_ReadWrite && g_fMMapCoherency)
+ {
+ /* For RW we can try read back from the file handle and check if we get
+ a match there first. */
+ uint8_t abBuf[_4K];
+ for (uint32_t off = 0; off < _2M; off += sizeof(abBuf))
+ {
+ RTTESTI_CHECK_RC(RTFileReadAt(hFile1, off, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
+ fsPerfCheckReadBuf(__LINE__, off, abBuf, sizeof(abBuf), 0xf7);
+ }
+# ifdef RT_OS_WINDOWS
+ CHECK_WINAPI_CALL(FlushViewOfFile(pbMapping, _2M) == TRUE);
+# else
+ RTTESTI_CHECK(msync(pbMapping, _2M, MS_SYNC) == 0);
+# endif
+ }
+
+ /*
+ * Time modifying and flushing a few different number of pages.
+ */
+ if (enmState == kMMap_ReadWrite)
+ {
+ static size_t const s_acbFlush[] = { PAGE_SIZE, PAGE_SIZE * 2, PAGE_SIZE * 3, PAGE_SIZE * 8, PAGE_SIZE * 16, _2M };
+ for (unsigned iFlushSize = 0 ; iFlushSize < RT_ELEMENTS(s_acbFlush); iFlushSize++)
+ {
+ size_t const cbFlush = s_acbFlush[iFlushSize];
+ if (cbFlush > cbMapping)
+ continue;
+
+ char szDesc[80];
+ RTStrPrintf(szDesc, sizeof(szDesc), "touch/flush/%zu", cbFlush);
+ size_t const cFlushes = cbMapping / cbFlush;
+ size_t const cbMappingUsed = cFlushes * cbFlush;
+ size_t cbFlushed = 0;
+ PROFILE_FN(fsPerfMSyncWorker(pbMapping, (iIteration * cbFlush) % cbMappingUsed, cbFlush, &cbFlushed),
+ g_nsTestRun, szDesc);
+
+ /*
+ * Check that all the changes made it thru to the file:
+ */
+ if (!g_fIgnoreNoCache || hFileNoCache != NIL_RTFILE)
+ {
+ size_t cbBuf = RT_MIN(_2M, g_cbMaxBuffer);
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ if (!pbBuf)
+ {
+ cbBuf = _4K;
+ pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ }
+ RTTESTI_CHECK(pbBuf != NULL);
+ if (pbBuf)
+ {
+ RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ size_t const cbToCheck = RT_MIN(cFlushes * cbFlush, cbFlushed);
+ unsigned cErrors = 0;
+ for (size_t offBuf = 0; cErrors < 32 && offBuf < cbToCheck; offBuf += cbBuf)
+ {
+ size_t cbToRead = RT_MIN(cbBuf, cbToCheck - offBuf);
+ RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, pbBuf, cbToRead, NULL), VINF_SUCCESS);
+
+ for (size_t offFlush = 0; offFlush < cbToRead; offFlush += PAGE_SIZE)
+ if (*(size_t volatile *)&pbBuf[offFlush + 8] != cbFlush)
+ {
+ RTTestIFailed("Flush issue at offset #%zx: %#zx, expected %#zx (cbFlush=%#zx, %#RX64)",
+ offBuf + offFlush + 8, *(size_t volatile *)&pbBuf[offFlush + 8],
+ cbFlush, cbFlush, *(uint64_t volatile *)&pbBuf[offFlush]);
+ if (++cErrors > 32)
+ break;
+ }
+ }
+ RTMemPageFree(pbBuf, cbBuf);
+ }
+ }
+ }
+
+# if 0 /* not needed, very very slow */
+ /*
+ * Restore the file to 0xf6 state for the next test.
+ */
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Restoring content...\n");
+ fsPerfFillWriteBuf(0, pbMapping, cbMapping, 0xf6);
+# ifdef RT_OS_WINDOWS
+ CHECK_WINAPI_CALL(FlushViewOfFile(pbMapping, cbMapping) == TRUE);
+# else
+ RTTESTI_CHECK(msync(pbMapping, cbMapping, MS_SYNC) == 0);
+# endif
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "... done\n");
+# endif
+ }
+ }
+
+ /*
+ * Observe how regular writes affects a read-only or readwrite mapping.
+ * These should ideally be immediately visible in the mapping, at least
+ * when not performed thru an no-cache handle.
+ */
+ if ( (enmState == kMMap_ReadOnly || enmState == kMMap_ReadWrite)
+ && g_fMMapCoherency)
+ {
+ size_t cbBuf = RT_MIN(RT_MIN(_2M, cbMapping / 2), g_cbMaxBuffer);
+ uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ if (!pbBuf)
+ {
+ cbBuf = _4K;
+ pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
+ }
+ RTTESTI_CHECK(pbBuf != NULL);
+ if (pbBuf)
+ {
+ /* Do a number of random writes to the file (using hFile1).
+ Immediately undoing them. */
+ for (uint32_t i = 0; i < 128; i++)
+ {
+ /* Generate a randomly sized write at a random location, making
+ sure it differs from whatever is there already before writing. */
+ uint32_t const cbToWrite = RTRandU32Ex(1, (uint32_t)cbBuf);
+ uint64_t const offToWrite = RTRandU64Ex(0, cbMapping - cbToWrite);
+
+ fsPerfFillWriteBuf(offToWrite, pbBuf, cbToWrite, 0xf8);
+ pbBuf[0] = ~pbBuf[0];
+ if (cbToWrite > 1)
+ pbBuf[cbToWrite - 1] = ~pbBuf[cbToWrite - 1];
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, offToWrite, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
+
+ /* Check the mapping. */
+ if (memcmp(&pbMapping[(size_t)offToWrite], pbBuf, cbToWrite) != 0)
+ {
+ RTTestIFailed("Write #%u @ %#RX64 LB %#x was not reflected in the mapping!\n", i, offToWrite, cbToWrite);
+ }
+
+ /* Restore */
+ fsPerfFillWriteBuf(offToWrite, pbBuf, cbToWrite, 0xf6);
+ RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, offToWrite, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
+ }
+
+ RTMemPageFree(pbBuf, cbBuf);
+ }
+ }
+
+ /*
+ * Unmap it.
+ */
+# ifdef RT_OS_WINDOWS
+ CHECK_WINAPI_CALL(UnmapViewOfFile(pbMapping) == TRUE);
+ CHECK_WINAPI_CALL(CloseHandle(hSection) == TRUE);
+# else
+ RTTESTI_CHECK(munmap(pbMapping, cbMapping) == 0);
+# endif
+ }
+
+ /*
+ * Memory mappings without open handles (pretty common).
+ */
+ for (uint32_t i = 0; i < 32; i++)
+ {
+ /* Create a new file, 256 KB in size, and fill it with random bytes.
+ Try uncached access if we can to force the page-in to do actual reads. */
+ char szFile2[FSPERF_MAX_PATH + 32];
+ memcpy(szFile2, g_szDir, g_cchDir);
+ RTStrPrintf(&szFile2[g_cchDir], sizeof(szFile2) - g_cchDir, "mmap-%u.noh", i);
+ RTFILE hFile2 = NIL_RTFILE;
+ int rc = (i & 3) == 3 ? VERR_TRY_AGAIN
+ : RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_NO_CACHE);
+ if (RT_FAILURE(rc))
+ {
+ RTTESTI_CHECK_RC_BREAK(RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE),
+ VINF_SUCCESS);
+ }
+
+ static char s_abContentUnaligned[256*1024 + PAGE_SIZE - 1];
+ char * const pbContent = &s_abContentUnaligned[PAGE_SIZE - ((uintptr_t)&s_abContentUnaligned[0] & PAGE_OFFSET_MASK)];
+ size_t const cbContent = 256*1024;
+ RTRandBytes(pbContent, cbContent);
+ RTTESTI_CHECK_RC(rc = RTFileWrite(hFile2, pbContent, cbContent, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ /* Reopen the file with normal caching. Every second time, we also
+ does a read-only open of it to confuse matters. */
+ RTFILE hFile3 = NIL_RTFILE;
+ if ((i & 3) == 3)
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile3, szFile2, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE), VINF_SUCCESS);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC_BREAK(RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE),
+ VINF_SUCCESS);
+ if ((i & 3) == 1)
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile3, szFile2, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE), VINF_SUCCESS);
+
+ /* Memory map it read-write (no COW). */
+#ifdef RT_OS_WINDOWS
+ HANDLE hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile2), NULL, PAGE_READWRITE, 0, cbContent, NULL);
+ CHECK_WINAPI_CALL(hSection != NULL);
+ uint8_t *pbMapping = (uint8_t *)MapViewOfFile(hSection, FILE_MAP_WRITE, 0, 0, cbContent);
+ CHECK_WINAPI_CALL(pbMapping != NULL);
+ CHECK_WINAPI_CALL(CloseHandle(hSection) == TRUE);
+# else
+ uint8_t *pbMapping = (uint8_t *)mmap(NULL, cbContent, PROT_READ | PROT_WRITE, MAP_SHARED,
+ (int)RTFileToNative(hFile2), 0);
+ if ((void *)pbMapping == MAP_FAILED)
+ pbMapping = NULL;
+ RTTESTI_CHECK_MSG(pbMapping != NULL, ("errno=%s (%d)\n", strerror(errno), errno));
+# endif
+
+ /* Close the file handles. */
+ if ((i & 7) == 7)
+ {
+ RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
+ hFile3 = NIL_RTFILE;
+ }
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ if ((i & 7) == 5)
+ {
+ RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
+ hFile3 = NIL_RTFILE;
+ }
+ if (pbMapping)
+ {
+ RTThreadSleep(2); /* fudge for cleanup/whatever */
+
+ /* Page in the mapping by comparing with the content we wrote above. */
+ RTTESTI_CHECK(memcmp(pbMapping, pbContent, cbContent) == 0);
+
+ /* Now dirty everything by inverting everything. */
+ size_t *puCur = (size_t *)pbMapping;
+ size_t cLeft = cbContent / sizeof(*puCur);
+ while (cLeft-- > 0)
+ {
+ *puCur = ~*puCur;
+ puCur++;
+ }
+
+ /* Sync it all. */
+# ifdef RT_OS_WINDOWS
+ //CHECK_WINAPI_CALL(FlushViewOfFile(pbMapping, cbContent) == TRUE);
+ SetLastError(0);
+ if (FlushViewOfFile(pbMapping, cbContent) != TRUE)
+ RTTestIFailed("line %u, i=%u: FlushViewOfFile(%p, %#zx) failed: %u / %#x", __LINE__, i,
+ pbMapping, cbContent, GetLastError(), RTNtLastStatusValue());
+# else
+ RTTESTI_CHECK(msync(pbMapping, cbContent, MS_SYNC) == 0);
+# endif
+
+ /* Unmap it. */
+# ifdef RT_OS_WINDOWS
+ CHECK_WINAPI_CALL(UnmapViewOfFile(pbMapping) == TRUE);
+# else
+ RTTESTI_CHECK(munmap(pbMapping, cbContent) == 0);
+# endif
+ }
+
+ if (hFile3 != NIL_RTFILE)
+ RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
+ }
+ RTTESTI_CHECK_RC(RTFileDelete(szFile2), VINF_SUCCESS);
+ }
+
+
+#else
+ RTTestSkipped(g_hTest, "not supported/implemented");
+ RT_NOREF(hFile1, hFileNoCache, cbFile);
+#endif
+}
+
+
+/**
+ * This does the read, write and seek tests.
+ */
+void fsPerfIo(void)
+{
+ RTTestISub("I/O");
+
+ /*
+ * Determin the size of the test file.
+ */
+ g_szDir[g_cchDir] = '\0';
+ RTFOFF cbFree = 0;
+ RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
+ uint64_t cbFile = g_cbIoFile;
+ if (cbFile + _16M < (uint64_t)cbFree)
+ cbFile = RT_ALIGN_64(cbFile, _64K);
+ else if (cbFree < _32M)
+ {
+ RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
+ return;
+ }
+ else
+ {
+ cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
+ cbFile = RT_ALIGN_64(cbFile, _64K);
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
+ }
+ if (cbFile < _64K)
+ {
+ RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile);
+ return;
+ }
+
+ /*
+ * Create a cbFile sized test file.
+ */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
+ RTFILE hFileNoCache;
+ if (!g_fIgnoreNoCache)
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileNoCache, g_szDir,
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE),
+ VINF_SUCCESS);
+ else
+ {
+ int rc = RTFileOpen(&hFileNoCache, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Unable to open I/O file with non-cache flag (%Rrc), skipping related tests.\n", rc);
+ hFileNoCache = NIL_RTFILE;
+ }
+ }
+ RTFILE hFileWriteThru;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileWriteThru, g_szDir,
+ RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_WRITE_THROUGH),
+ VINF_SUCCESS);
+
+ uint8_t *pbFree = NULL;
+ int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
+ RTMemFree(pbFree);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the testing & profiling.
+ */
+ if (g_fSeek)
+ fsPerfIoSeek(hFile1, cbFile);
+
+ if (g_fMMap && g_iMMapPlacement < 0)
+ {
+ fsPerfMMap(hFile1, hFileNoCache, cbFile);
+ fsPerfReinitFile(hFile1, cbFile);
+ }
+
+ if (g_fReadTests)
+ fsPerfRead(hFile1, hFileNoCache, cbFile);
+ if (g_fReadPerf)
+ for (unsigned i = 0; i < g_cIoBlocks; i++)
+ fsPerfIoReadBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
+#ifdef FSPERF_TEST_SENDFILE
+ if (g_fSendFile)
+ fsPerfSendFile(hFile1, cbFile);
+#endif
+#ifdef RT_OS_LINUX
+ if (g_fSplice)
+ fsPerfSpliceToPipe(hFile1, cbFile);
+#endif
+ if (g_fMMap && g_iMMapPlacement == 0)
+ fsPerfMMap(hFile1, hFileNoCache, cbFile);
+
+ /* This is destructive to the file content. */
+ if (g_fWriteTests)
+ fsPerfWrite(hFile1, hFileNoCache, hFileWriteThru, cbFile);
+ if (g_fWritePerf)
+ for (unsigned i = 0; i < g_cIoBlocks; i++)
+ fsPerfIoWriteBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
+#ifdef RT_OS_LINUX
+ if (g_fSplice)
+ fsPerfSpliceToFile(hFile1, cbFile);
+#endif
+ if (g_fFSync)
+ fsPerfFSync(hFile1, cbFile);
+
+ if (g_fMMap && g_iMMapPlacement > 0)
+ {
+ fsPerfReinitFile(hFile1, cbFile);
+ fsPerfMMap(hFile1, hFileNoCache, cbFile);
+ }
+ }
+
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ if (hFileNoCache != NIL_RTFILE || !g_fIgnoreNoCache)
+ RTTESTI_CHECK_RC(RTFileClose(hFileNoCache), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFileWriteThru), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
+}
+
+
+DECL_FORCE_INLINE(int) fsPerfCopyWorker1(const char *pszSrc, const char *pszDst)
+{
+ RTFileDelete(pszDst);
+ return RTFileCopy(pszSrc, pszDst);
+}
+
+
+#ifdef RT_OS_LINUX
+DECL_FORCE_INLINE(int) fsPerfCopyWorkerSendFile(RTFILE hFile1, RTFILE hFile2, size_t cbFile)
+{
+ RTTESTI_CHECK_RC_RET(RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
+
+ loff_t off = 0;
+ ssize_t cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), &off, cbFile);
+ if (cbSent > 0 && (size_t)cbSent == cbFile)
+ return 0;
+
+ int rc = VERR_GENERAL_FAILURE;
+ if (cbSent < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)", cbFile, cbSent, errno, rc);
+ }
+ else
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
+ cbFile, cbSent, cbFile, cbSent - cbFile);
+ return rc;
+}
+#endif /* RT_OS_LINUX */
+
+
+static void fsPerfCopy(void)
+{
+ RTTestISub("copy");
+
+ /*
+ * Non-existing files.
+ */
+ RTTESTI_CHECK_RC(RTFileCopy(InEmptyDir(RT_STR_TUPLE("no-such-file")),
+ InDir2(RT_STR_TUPLE("whatever"))), VERR_FILE_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileCopy(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
+ InDir2(RT_STR_TUPLE("no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
+ InDir2(RT_STR_TUPLE("whatever"))), VERR_PATH_NOT_FOUND);
+
+ RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file")),
+ InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
+ RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file")),
+ InDir2(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
+
+ /*
+ * Determin the size of the test file.
+ * We want to be able to make 1 copy of it.
+ */
+ g_szDir[g_cchDir] = '\0';
+ RTFOFF cbFree = 0;
+ RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
+ uint64_t cbFile = g_cbIoFile;
+ if (cbFile + _16M < (uint64_t)cbFree)
+ cbFile = RT_ALIGN_64(cbFile, _64K);
+ else if (cbFree < _32M)
+ {
+ RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
+ return;
+ }
+ else
+ {
+ cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
+ cbFile = RT_ALIGN_64(cbFile, _64K);
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
+ }
+ if (cbFile < _512K * 2)
+ {
+ RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 1MB", cbFile);
+ return;
+ }
+ cbFile /= 2;
+
+ /*
+ * Create a cbFile sized test file.
+ */
+ RTFILE hFile1;
+ RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file22")),
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
+ uint8_t *pbFree = NULL;
+ int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
+ RTMemFree(pbFree);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Make copies.
+ */
+ /* plain */
+ RTFileDelete(InDir2(RT_STR_TUPLE("file23")));
+ RTTESTI_CHECK_RC(RTFileCopy(g_szDir, g_szDir2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCopy(g_szDir, g_szDir2), VERR_ALREADY_EXISTS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+
+ /* by handle */
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ RTFILE hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCopyByHandles(hFile1, hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+
+ /* copy part */
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCopyPart(hFile1, 0, hFile2, 0, cbFile / 2, 0, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCopyPart(hFile1, cbFile / 2, hFile2, cbFile / 2, cbFile - cbFile / 2, 0, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+
+#ifdef RT_OS_LINUX
+ /*
+ * On linux we can also use sendfile between two files, except for 2.5.x to 2.6.33.
+ */
+ uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_C(0x7ffff000));
+ char szRelease[64];
+ RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
+ bool const fSendFileBetweenFiles = RTStrVersionCompare(szRelease, "2.5.0") < 0
+ || RTStrVersionCompare(szRelease, "2.6.33") >= 0;
+ if (fSendFileBetweenFiles)
+ {
+ /* Copy the whole file: */
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ RTFileDelete(g_szDir2);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ ssize_t cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), NULL, cbFile);
+ if (cbSent < 0)
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)",
+ cbFile, cbSent, errno, RTErrConvertFromErrno(errno));
+ else if ((size_t)cbSent != cbFileMax)
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
+ cbFile, cbSent, cbFileMax, cbSent - cbFileMax);
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+
+ /* Try copy a little bit too much: */
+ if (cbFile == cbFileMax)
+ {
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ RTFileDelete(g_szDir2);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ size_t cbToCopy = cbFile + RTRandU32Ex(1, _64M);
+ cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), NULL, cbToCopy);
+ if (cbSent < 0)
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)",
+ cbToCopy, cbSent, errno, RTErrConvertFromErrno(errno));
+ else if ((size_t)cbSent != cbFile)
+ RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
+ cbToCopy, cbSent, cbFile, cbSent - cbFile);
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+ }
+
+ /* Do partial copy: */
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ for (uint32_t i = 0; i < 64; i++)
+ {
+ size_t cbToCopy = RTRandU32Ex(0, cbFileMax - 1);
+ uint32_t const offFile = RTRandU32Ex(1, (uint64_t)RT_MIN(cbFileMax - cbToCopy, UINT32_MAX));
+ RTTESTI_CHECK_RC_BREAK(RTFileSeek(hFile2, offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ loff_t offFile2 = offFile;
+ cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), &offFile2, cbToCopy);
+ if (cbSent < 0)
+ RTTestIFailed("sendfile(file,file,%#x,%#zx) failed (%zd): %d (%Rrc)",
+ offFile, cbToCopy, cbSent, errno, RTErrConvertFromErrno(errno));
+ else if ((size_t)cbSent != cbToCopy)
+ RTTestIFailed("sendfile(file,file,%#x,%#zx) returned %#zx, expected %#zx (diff %zd)",
+ offFile, cbToCopy, cbSent, cbToCopy, cbSent - cbToCopy);
+ else if (offFile2 != (loff_t)(offFile + cbToCopy))
+ RTTestIFailed("sendfile(file,file,%#x,%#zx) returned %#zx + off=%#RX64, expected off %#x",
+ offFile, cbToCopy, cbSent, offFile2, offFile + cbToCopy);
+ }
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
+ }
+#endif
+
+ /*
+ * Do some benchmarking.
+ */
+#define PROFILE_COPY_FN(a_szOperation, a_fnCall) \
+ do \
+ { \
+ /* Estimate how many iterations we need to fill up the given timeslot: */ \
+ fsPerfYield(); \
+ uint64_t nsStart = RTTimeNanoTS(); \
+ uint64_t ns; \
+ do \
+ ns = RTTimeNanoTS(); \
+ while (ns == nsStart); \
+ nsStart = ns; \
+ \
+ uint64_t iIteration = 0; \
+ do \
+ { \
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ iIteration++; \
+ ns = RTTimeNanoTS() - nsStart; \
+ } while (ns < RT_NS_10MS); \
+ ns /= iIteration; \
+ if (ns > g_nsPerNanoTSCall + 32) \
+ ns -= g_nsPerNanoTSCall; \
+ uint64_t cIterations = g_nsTestRun / ns; \
+ if (cIterations < 2) \
+ cIterations = 2; \
+ else if (cIterations & 1) \
+ cIterations++; \
+ \
+ /* Do the actual profiling: */ \
+ iIteration = 0; \
+ fsPerfYield(); \
+ nsStart = RTTimeNanoTS(); \
+ for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
+ { \
+ for (; iIteration < cIterations; iIteration++)\
+ RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
+ ns = RTTimeNanoTS() - nsStart;\
+ if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
+ break; \
+ cIterations += cIterations / 4; \
+ if (cIterations & 1) \
+ cIterations++; \
+ nsStart += g_nsPerNanoTSCall; \
+ } \
+ RTTestIValueF(ns / iIteration, \
+ RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation " latency"); \
+ RTTestIValueF((uint64_t)((double)(iIteration * cbFile) / ((double)ns / RT_NS_1SEC)), \
+ RTTESTUNIT_BYTES_PER_SEC, a_szOperation " throughput"); \
+ RTTestIValueF((uint64_t)iIteration * cbFile, \
+ RTTESTUNIT_BYTES, a_szOperation " bytes"); \
+ RTTestIValueF(iIteration, \
+ RTTESTUNIT_OCCURRENCES, a_szOperation " iterations"); \
+ if (g_fShowDuration) \
+ RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation " duration"); \
+ } while (0)
+
+ PROFILE_COPY_FN("RTFileCopy/Replace", fsPerfCopyWorker1(g_szDir, g_szDir2));
+
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ RTFileDelete(g_szDir2);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ PROFILE_COPY_FN("RTFileCopyByHandles/Overwrite", RTFileCopyByHandles(hFile1, hFile2));
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ /* We could benchmark RTFileCopyPart with various block sizes and whatnot...
+ But it's currently well covered by the two previous operations. */
+
+#ifdef RT_OS_LINUX
+ if (fSendFileBetweenFiles)
+ {
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
+ RTFileDelete(g_szDir2);
+ hFile2 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
+ PROFILE_COPY_FN("sendfile/overwrite", fsPerfCopyWorkerSendFile(hFile1, hFile2, cbFileMax));
+ RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+ }
+#endif
+ }
+
+ /*
+ * Clean up.
+ */
+ RTFileDelete(InDir2(RT_STR_TUPLE("file22c1")));
+ RTFileDelete(InDir2(RT_STR_TUPLE("file22c2")));
+ RTFileDelete(InDir2(RT_STR_TUPLE("file22c3")));
+ RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
+}
+
+
+static void fsPerfRemote(void)
+{
+ RTTestISub("remote");
+ uint8_t abBuf[16384];
+
+
+ /*
+ * Create a file on the remote end and check that we can immediately see it.
+ */
+ RTTESTI_CHECK_RC_RETV(FsPerfCommsSend("reset\n"
+ "open 0 'file30' 'w' 'ca'\n"
+ "writepattern 0 0 0 4096" FSPERF_EOF_STR), VINF_SUCCESS);
+
+ RTFILEACTION enmActuallyTaken = RTFILEACTION_END;
+ RTFILE hFile0 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
+ &hFile0, &enmActuallyTaken), VINF_SUCCESS);
+ RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 4096, NULL), VINF_SUCCESS);
+ AssertCompile(RT_ELEMENTS(g_abPattern0) == 1);
+ RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 4096, g_abPattern0[0]));
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+
+ /*
+ * Append a little to it on the host and see that we can read it.
+ */
+ RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 4096 1 1024" FSPERF_EOF_STR), VINF_SUCCESS);
+ AssertCompile(RT_ELEMENTS(g_abPattern1) == 1);
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1024, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 1024, g_abPattern1[0]));
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+
+ /*
+ * Have the host truncate the file.
+ */
+ RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 1024" FSPERF_EOF_STR), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1024, NULL), VINF_SUCCESS);
+ AssertCompile(RT_ELEMENTS(g_abPattern0) == 1);
+ RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 4096, g_abPattern0[0]));
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+
+ /*
+ * Write a bunch of stuff to the file here, then truncate it to a given size,
+ * then have the host add more, finally test that we can successfully chop off
+ * what the host added by reissuing the same truncate call as before (issue of
+ * RDBSS using cached size to noop out set-eof-to-same-size).
+ */
+ memset(abBuf, 0xe9, sizeof(abBuf));
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 16384, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 8000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 8000 0 1000" FSPERF_EOF_STR), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 8000), VINF_SUCCESS);
+ uint64_t cbFile = 0;
+ RTTESTI_CHECK_RC(RTFileQuerySize(hFile0, &cbFile), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbFile == 8000, ("cbFile=%u\n", cbFile));
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+
+ /* Same, but using RTFileRead to find out and RTFileWrite to define the size. */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 5000, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 5000 0 1000" FSPERF_EOF_STR), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 5000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+ RTTESTI_CHECK_RC(RTFileQuerySize(hFile0, &cbFile), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbFile == 5000, ("cbFile=%u\n", cbFile));
+
+ /* Same, but host truncates rather than adding stuff. */
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 16384, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 10000), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 4000" FSPERF_EOF_STR), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileQuerySize(hFile0, &cbFile), VINF_SUCCESS);
+ RTTESTI_CHECK_MSG(cbFile == 4000, ("cbFile=%u\n", cbFile));
+ RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
+
+ /*
+ * Test noticing remote size changes when opening a file. Need to keep hFile0
+ * open here so we're sure to have an inode/FCB for the file in question.
+ */
+ memset(abBuf, 0xe7, sizeof(abBuf));
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 12288, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 12288), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 12288 2 4096" FSPERF_EOF_STR), VINF_SUCCESS);
+
+ enmActuallyTaken = RTFILEACTION_END;
+ RTFILE hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
+ &hFile1, &enmActuallyTaken), VINF_SUCCESS);
+ RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
+ AssertCompile(sizeof(abBuf) >= 16384);
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 16384, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 12288, 0xe7));
+ AssertCompile(RT_ELEMENTS(g_abPattern2) == 1);
+ RTTESTI_CHECK(ASMMemIsAllU8(&abBuf[12288], 4096, g_abPattern2[0]));
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ /* Same, but remote end truncates the file: */
+ memset(abBuf, 0xe6, sizeof(abBuf));
+ RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 12288, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 12288), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 7500" FSPERF_EOF_STR), VINF_SUCCESS);
+
+ enmActuallyTaken = RTFILEACTION_END;
+ hFile1 = NIL_RTFILE;
+ RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
+ &hFile1, &enmActuallyTaken), VINF_SUCCESS);
+ RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 7500, NULL), VINF_SUCCESS);
+ RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 7500, 0xe6));
+ RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
+ RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
+
+ RTTESTI_CHECK_RC(RTFileClose(hFile0), VINF_SUCCESS);
+}
+
+
+
+/**
+ * Display the usage to @a pStrm.
+ */
+static void Usage(PRTSTREAM pStrm)
+{
+ char szExec[FSPERF_MAX_PATH];
+ RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
+ RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "options: \n");
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ char szHelp[80];
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'd': pszHelp = "The directory to use for testing. default: CWD/fstestdir"; break;
+ case 'r': pszHelp = "Don't abspath test dir (good for deep dirs). default: disabled"; break;
+ case 'e': pszHelp = "Enables all tests. default: -e"; break;
+ case 'z': pszHelp = "Disables all tests. default: -e"; break;
+ case 's': pszHelp = "Set benchmark duration in seconds. default: 10 sec"; break;
+ case 'm': pszHelp = "Set benchmark duration in milliseconds. default: 10000 ms"; break;
+ case 'v': pszHelp = "More verbose execution."; break;
+ case 'q': pszHelp = "Quiet execution."; break;
+ case 'h': pszHelp = "Displays this help and exit"; break;
+ case 'V': pszHelp = "Displays the program revision"; break;
+ case kCmdOpt_ShowDuration: pszHelp = "Show duration of profile runs. default: --no-show-duration"; break;
+ case kCmdOpt_NoShowDuration: pszHelp = "Hide duration of profile runs. default: --no-show-duration"; break;
+ case kCmdOpt_ShowIterations: pszHelp = "Show iteration count for profile runs. default: --no-show-iterations"; break;
+ case kCmdOpt_NoShowIterations: pszHelp = "Hide iteration count for profile runs. default: --no-show-iterations"; break;
+ case kCmdOpt_ManyFiles: pszHelp = "Count of files in big test dir. default: --many-files 10000"; break;
+ case kCmdOpt_NoManyFiles: pszHelp = "Skip big test dir with many files. default: --many-files 10000"; break;
+ case kCmdOpt_ManyTreeFilesPerDir: pszHelp = "Count of files per directory in test tree. default: 640"; break;
+ case kCmdOpt_ManyTreeSubdirsPerDir: pszHelp = "Count of subdirs per directory in test tree. default: 16"; break;
+ case kCmdOpt_ManyTreeDepth: pszHelp = "Depth of test tree (not counting root). default: 1"; break;
+#if defined(RT_OS_WINDOWS)
+ case kCmdOpt_MaxBufferSize: pszHelp = "For avoiding the MDL limit on windows. default: 32MiB"; break;
+#else
+ case kCmdOpt_MaxBufferSize: pszHelp = "For avoiding the MDL limit on windows. default: 0"; break;
+#endif
+ case kCmdOpt_MMapPlacement: pszHelp = "When to do mmap testing (caching effects): first, between (default), last "; break;
+ case kCmdOpt_IgnoreNoCache: pszHelp = "Ignore error wrt no-cache handle. default: --no-ignore-no-cache"; break;
+ case kCmdOpt_NoIgnoreNoCache: pszHelp = "Do not ignore error wrt no-cache handle. default: --no-ignore-no-cache"; break;
+ case kCmdOpt_IoFileSize: pszHelp = "Size of file used for I/O tests. default: 512 MB"; break;
+ case kCmdOpt_SetBlockSize: pszHelp = "Sets single I/O block size (in bytes)."; break;
+ case kCmdOpt_AddBlockSize: pszHelp = "Adds an I/O block size (in bytes)."; break;
+ default:
+ if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
+ {
+ if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
+ RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
+ else
+ RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
+ pszHelp = szHelp;
+ }
+ else
+ pszHelp = "Option undocumented";
+ break;
+ }
+ if ((unsigned)g_aCmdOptions[i].iShort < 127U)
+ {
+ char szOpt[64];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
+ }
+ else
+ RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
+ }
+}
+
+
+static uint32_t fsPerfCalcManyTreeFiles(void)
+{
+ uint32_t cDirs = 1;
+ for (uint32_t i = 0, cDirsAtLevel = 1; i < g_cManyTreeDepth; i++)
+ {
+ cDirs += cDirsAtLevel * g_cManyTreeSubdirsPerDir;
+ cDirsAtLevel *= g_cManyTreeSubdirsPerDir;
+ }
+ return g_cManyTreeFilesPerDir * cDirs;
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT and globals.
+ */
+ int rc = RTTestInitAndCreate("FsPerf", &g_hTest);
+ if (rc)
+ return rc;
+ RTListInit(&g_ManyTreeHead);
+
+ /*
+ * Default values.
+ */
+ char szDefaultDir[RTPATH_MAX];
+ const char *pszDir = szDefaultDir;
+
+ /* As default retrieve the system's temporary directory and create a test directory beneath it,
+ * as this binary might get executed from a read-only medium such as ${CDROM}. */
+ rc = RTPathTemp(szDefaultDir, sizeof(szDefaultDir));
+ if (RT_SUCCESS(rc))
+ {
+ char szDirName[32];
+ RTStrPrintf2(szDirName, sizeof(szDirName), "fstestdir-%u" RTPATH_SLASH_STR, RTProcSelf());
+ rc = RTPathAppend(szDefaultDir, sizeof(szDefaultDir), szDirName);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "Unable to append dir name in temp dir, rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "Unable to retrieve temp dir, rc=%Rrc\n", rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+
+ RTTestIPrintf(RTTESTLVL_INFO, "Default directory is: %s\n", szDefaultDir);
+
+ bool fCommsSlave = false;
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'c':
+ if (!g_fRelativeDir)
+ rc = RTPathAbs(ValueUnion.psz, g_szCommsDir, sizeof(g_szCommsDir) - 128);
+ else
+ rc = RTStrCopy(g_szCommsDir, sizeof(g_szCommsDir) - 128, ValueUnion.psz);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ RTPathEnsureTrailingSeparator(g_szCommsDir, sizeof(g_szCommsDir));
+ g_cchCommsDir = strlen(g_szCommsDir);
+
+ rc = RTPathJoin(g_szCommsSubDir, sizeof(g_szCommsSubDir) - 128, g_szCommsDir, "comms" RTPATH_SLASH_STR);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "RTPathJoin(%s,,'comms/') failed: %Rrc\n", g_szCommsDir, rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ g_cchCommsSubDir = strlen(g_szCommsSubDir);
+ break;
+
+ case 'C':
+ fCommsSlave = true;
+ break;
+
+ case 'd':
+ pszDir = ValueUnion.psz;
+ break;
+
+ case 'r':
+ g_fRelativeDir = true;
+ break;
+
+ case 's':
+ if (ValueUnion.u32 == 0)
+ g_nsTestRun = RT_NS_1SEC_64 * 10;
+ else
+ g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64;
+ break;
+
+ case 'm':
+ if (ValueUnion.u64 == 0)
+ g_nsTestRun = RT_NS_1SEC_64 * 10;
+ else
+ g_nsTestRun = ValueUnion.u64 * RT_NS_1MS;
+ break;
+
+ case 'e':
+ g_fManyFiles = true;
+ g_fOpen = true;
+ g_fFStat = true;
+#ifdef RT_OS_WINDOWS
+ g_fNtQueryInfoFile = true;
+ g_fNtQueryVolInfoFile = true;
+#endif
+ g_fFChMod = true;
+ g_fFUtimes = true;
+ g_fStat = true;
+ g_fChMod = true;
+ g_fUtimes = true;
+ g_fRename = true;
+ g_fDirOpen = true;
+ g_fDirEnum = true;
+ g_fMkRmDir = true;
+ g_fStatVfs = true;
+ g_fRm = true;
+ g_fChSize = true;
+ g_fReadTests = true;
+ g_fReadPerf = true;
+#ifdef FSPERF_TEST_SENDFILE
+ g_fSendFile = true;
+#endif
+#ifdef RT_OS_LINUX
+ g_fSplice = true;
+#endif
+ g_fWriteTests = true;
+ g_fWritePerf = true;
+ g_fSeek = true;
+ g_fFSync = true;
+ g_fMMap = true;
+ g_fMMapCoherency = true;
+ g_fCopy = true;
+ g_fRemote = true;
+ break;
+
+ case 'z':
+ g_fManyFiles = false;
+ g_fOpen = false;
+ g_fFStat = false;
+#ifdef RT_OS_WINDOWS
+ g_fNtQueryInfoFile = false;
+ g_fNtQueryVolInfoFile = false;
+#endif
+ g_fFChMod = false;
+ g_fFUtimes = false;
+ g_fStat = false;
+ g_fChMod = false;
+ g_fUtimes = false;
+ g_fRename = false;
+ g_fDirOpen = false;
+ g_fDirEnum = false;
+ g_fMkRmDir = false;
+ g_fStatVfs = false;
+ g_fRm = false;
+ g_fChSize = false;
+ g_fReadTests = false;
+ g_fReadPerf = false;
+#ifdef FSPERF_TEST_SENDFILE
+ g_fSendFile = false;
+#endif
+#ifdef RT_OS_LINUX
+ g_fSplice = false;
+#endif
+ g_fWriteTests = false;
+ g_fWritePerf = false;
+ g_fSeek = false;
+ g_fFSync = false;
+ g_fMMap = false;
+ g_fMMapCoherency = false;
+ g_fCopy = false;
+ g_fRemote = false;
+ break;
+
+#define CASE_OPT(a_Stem) \
+ case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \
+ case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break
+ CASE_OPT(Open);
+ CASE_OPT(FStat);
+#ifdef RT_OS_WINDOWS
+ CASE_OPT(NtQueryInfoFile);
+ CASE_OPT(NtQueryVolInfoFile);
+#endif
+ CASE_OPT(FChMod);
+ CASE_OPT(FUtimes);
+ CASE_OPT(Stat);
+ CASE_OPT(ChMod);
+ CASE_OPT(Utimes);
+ CASE_OPT(Rename);
+ CASE_OPT(DirOpen);
+ CASE_OPT(DirEnum);
+ CASE_OPT(MkRmDir);
+ CASE_OPT(StatVfs);
+ CASE_OPT(Rm);
+ CASE_OPT(ChSize);
+ CASE_OPT(ReadTests);
+ CASE_OPT(ReadPerf);
+#ifdef FSPERF_TEST_SENDFILE
+ CASE_OPT(SendFile);
+#endif
+#ifdef RT_OS_LINUX
+ CASE_OPT(Splice);
+#endif
+ CASE_OPT(WriteTests);
+ CASE_OPT(WritePerf);
+ CASE_OPT(Seek);
+ CASE_OPT(FSync);
+ CASE_OPT(MMap);
+ CASE_OPT(MMapCoherency);
+ CASE_OPT(IgnoreNoCache);
+ CASE_OPT(Copy);
+ CASE_OPT(Remote);
+
+ CASE_OPT(ShowDuration);
+ CASE_OPT(ShowIterations);
+#undef CASE_OPT
+
+ case kCmdOpt_ManyFiles:
+ g_fManyFiles = ValueUnion.u32 > 0;
+ g_cManyFiles = ValueUnion.u32;
+ break;
+
+ case kCmdOpt_NoManyFiles:
+ g_fManyFiles = false;
+ break;
+
+ case kCmdOpt_ManyTreeFilesPerDir:
+ if (ValueUnion.u32 > 0 && ValueUnion.u32 <= _64M)
+ {
+ g_cManyTreeFilesPerDir = ValueUnion.u32;
+ g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
+ break;
+ }
+ RTTestFailed(g_hTest, "Out of range --files-per-dir value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
+ return RTTestSummaryAndDestroy(g_hTest);
+
+ case kCmdOpt_ManyTreeSubdirsPerDir:
+ if (ValueUnion.u32 > 0 && ValueUnion.u32 <= 1024)
+ {
+ g_cManyTreeSubdirsPerDir = ValueUnion.u32;
+ g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
+ break;
+ }
+ RTTestFailed(g_hTest, "Out of range --subdirs-per-dir value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
+ return RTTestSummaryAndDestroy(g_hTest);
+
+ case kCmdOpt_ManyTreeDepth:
+ if (ValueUnion.u32 <= 8)
+ {
+ g_cManyTreeDepth = ValueUnion.u32;
+ g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
+ break;
+ }
+ RTTestFailed(g_hTest, "Out of range --tree-depth value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
+ return RTTestSummaryAndDestroy(g_hTest);
+
+ case kCmdOpt_MaxBufferSize:
+ if (ValueUnion.u32 >= 4096)
+ g_cbMaxBuffer = ValueUnion.u32;
+ else if (ValueUnion.u32 == 0)
+ g_cbMaxBuffer = UINT32_MAX;
+ else
+ {
+ RTTestFailed(g_hTest, "max buffer size is less than 4KB: %#x\n", ValueUnion.u32);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case kCmdOpt_IoFileSize:
+ if (ValueUnion.u64 == 0)
+ g_cbIoFile = _512M;
+ else
+ g_cbIoFile = ValueUnion.u64;
+ break;
+
+ case kCmdOpt_SetBlockSize:
+ if (ValueUnion.u32 > 0)
+ {
+ g_cIoBlocks = 1;
+ g_acbIoBlocks[0] = ValueUnion.u32;
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "Invalid I/O block size: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case kCmdOpt_AddBlockSize:
+ if (g_cIoBlocks >= RT_ELEMENTS(g_acbIoBlocks))
+ RTTestFailed(g_hTest, "Too many I/O block sizes: max %u\n", RT_ELEMENTS(g_acbIoBlocks));
+ else if (ValueUnion.u32 == 0)
+ RTTestFailed(g_hTest, "Invalid I/O block size: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
+ else
+ {
+ g_acbIoBlocks[g_cIoBlocks++] = ValueUnion.u32;
+ break;
+ }
+ return RTTestSummaryAndDestroy(g_hTest);
+
+ case kCmdOpt_MMapPlacement:
+ if (strcmp(ValueUnion.psz, "first") == 0)
+ g_iMMapPlacement = -1;
+ else if ( strcmp(ValueUnion.psz, "between") == 0
+ || strcmp(ValueUnion.psz, "default") == 0)
+ g_iMMapPlacement = 0;
+ else if (strcmp(ValueUnion.psz, "last") == 0)
+ g_iMMapPlacement = 1;
+ else
+ {
+ RTTestFailed(g_hTest,
+ "Invalid --mmap-placment directive '%s'! Expected 'first', 'last', 'between' or 'default'.\n",
+ ValueUnion.psz);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case 'q':
+ g_uVerbosity = 0;
+ break;
+
+ case 'v':
+ g_uVerbosity++;
+ break;
+
+ case 'h':
+ Usage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ {
+ char szRev[] = "$Revision: 155244 $";
+ szRev[RT_ELEMENTS(szRev) - 2] = '\0';
+ RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
+ return RTEXITCODE_SUCCESS;
+ }
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Populate g_szDir.
+ */
+ if (!g_fRelativeDir)
+ rc = RTPathAbs(pszDir, g_szDir, sizeof(g_szDir) - FSPERF_MAX_NEEDED_PATH);
+ else
+ rc = RTStrCopy(g_szDir, sizeof(g_szDir) - FSPERF_MAX_NEEDED_PATH, pszDir);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ RTPathEnsureTrailingSeparator(g_szDir, sizeof(g_szDir));
+ g_cchDir = strlen(g_szDir);
+
+ /*
+ * If communication slave, go do that and be done.
+ */
+ if (fCommsSlave)
+ {
+ if (pszDir == szDefaultDir)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The slave must have a working directory specified (-d)!");
+ return FsPerfCommsSlave();
+ }
+
+ /*
+ * Create the test directory with an 'empty' subdirectory under it,
+ * execute the tests, and remove directory when done.
+ */
+ RTTestBanner(g_hTest);
+ if (!RTPathExists(g_szDir))
+ {
+ /* The base dir: */
+ rc = RTDirCreate(g_szDir, 0755,
+ RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
+ rc = fsPrepTestArea();
+ if (RT_SUCCESS(rc))
+ {
+ /* Profile RTTimeNanoTS(). */
+ fsPerfNanoTS();
+
+ /* Do tests: */
+ if (g_fManyFiles)
+ fsPerfManyFiles();
+ if (g_fOpen)
+ fsPerfOpen();
+ if (g_fFStat)
+ fsPerfFStat();
+#ifdef RT_OS_WINDOWS
+ if (g_fNtQueryInfoFile)
+ fsPerfNtQueryInfoFile();
+ if (g_fNtQueryVolInfoFile)
+ fsPerfNtQueryVolInfoFile();
+#endif
+ if (g_fFChMod)
+ fsPerfFChMod();
+ if (g_fFUtimes)
+ fsPerfFUtimes();
+ if (g_fStat)
+ fsPerfStat();
+ if (g_fChMod)
+ fsPerfChmod();
+ if (g_fUtimes)
+ fsPerfUtimes();
+ if (g_fRename)
+ fsPerfRename();
+ if (g_fDirOpen)
+ vsPerfDirOpen();
+ if (g_fDirEnum)
+ vsPerfDirEnum();
+ if (g_fMkRmDir)
+ fsPerfMkRmDir();
+ if (g_fStatVfs)
+ fsPerfStatVfs();
+ if (g_fRm || g_fManyFiles)
+ fsPerfRm(); /* deletes manyfiles and manytree */
+ if (g_fChSize)
+ fsPerfChSize();
+ if ( g_fReadPerf || g_fReadTests || g_fWritePerf || g_fWriteTests
+#ifdef FSPERF_TEST_SENDFILE
+ || g_fSendFile
+#endif
+#ifdef RT_OS_LINUX
+ || g_fSplice
+#endif
+ || g_fSeek || g_fFSync || g_fMMap)
+ fsPerfIo();
+ if (g_fCopy)
+ fsPerfCopy();
+ if (g_fRemote && g_szCommsDir[0] != '\0')
+ fsPerfRemote();
+ }
+
+ /*
+ * Cleanup:
+ */
+ FsPerfCommsShutdownSlave();
+
+ g_szDir[g_cchDir] = '\0';
+ rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR | (g_fRelativeDir ? RTDIRRMREC_F_NO_ABS_PATH : 0));
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
+
+ FsPerfCommsShutdownSlave();
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/fs/Makefile.kmk b/src/VBox/ValidationKit/utils/fs/Makefile.kmk
new file mode 100644
index 00000000..270c9caa
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/fs/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - File system tests.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# File System Performance (think shared folders).
+#
+PROGRAMS += FsPerf
+FsPerf_TEMPLATE = VBoxValidationKitR3
+FsPerf_SOURCES = FsPerf.cpp
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/misc/Makefile.kmk b/src/VBox/ValidationKit/utils/misc/Makefile.kmk
new file mode 100644
index 00000000..693625ac
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/misc/Makefile.kmk
@@ -0,0 +1,84 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Miscellaneous Utilites.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Load generator, optionally with an ring-0 IPI generator.
+#
+PROGRAMS += LoadGenerator
+if1of ($(KBUILD_TARGET_ARCH), amd64)
+ ifdef VBOX_WITH_R0_MODULES
+ ifdef VBOX_WITH_VBOXR0_AS_DLL
+ DLLS += loadgeneratorR0
+ else
+ SYSMODS += loadgeneratorR0
+ endif
+ loadgeneratorR0_TEMPLATE := VBoxValidationKitR0
+ loadgeneratorR0_SOURCES := loadgeneratorR0.cpp
+ endif
+ LoadGenerator_TEMPLATE := VBoxValidationKitR3SupDrv
+ LoadGenerator_DEFS := WITH_IPI_LOAD_GEN
+else
+ LoadGenerator_TEMPLATE := VBoxValidationKitR3
+endif
+LoadGenerator_SOURCES := loadgenerator.cpp
+
+#
+# vts_rm - Test cleanup utility similar to unix rm.
+#
+PROGRAMS += vts_rm
+vts_rm_TEMPLATE = VBoxValidationKitR3
+vts_rm_SOURCES = vts_rm.cpp
+
+#
+# vts_tar - Tar for untarring and tarring test related stuff.
+#
+PROGRAMS += vts_tar
+vts_tar_TEMPLATE = VBoxValidationKitR3
+vts_tar_SDKS = VBoxZlibStatic
+vts_tar_SOURCES = vts_tar.cpp
+
+#
+# vts_shutdown - initiates a guest or host shut down / reboot.
+#
+PROGRAMS += vts_shutdown
+vts_shutdown_TEMPLATE = VBoxValidationKitR3
+vts_shutdown_SOURCES = ../../../Runtime/tools/RTShutdown.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/misc/loadgenerator.cpp b/src/VBox/ValidationKit/utils/misc/loadgenerator.cpp
new file mode 100644
index 00000000..28b7d1af
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/misc/loadgenerator.cpp
@@ -0,0 +1,366 @@
+/* $Id: loadgenerator.cpp $ */
+/** @file
+ * Load Generator.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/errcore.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/mp.h>
+#include <iprt/asm.h>
+#include <iprt/getopt.h>
+#ifdef WITH_IPI_LOAD_GEN
+# include <VBox/sup.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether the threads should quit or not. */
+static bool volatile g_fQuit = false;
+
+
+static void LoadGenSpin(uint64_t cNanoSeconds)
+{
+ const uint64_t u64StartTS = RTTimeNanoTS();
+ do
+ {
+ for (uint32_t volatile i = 0; i < 10240 && !g_fQuit; i++)
+ i++;
+ } while (RTTimeNanoTS() - u64StartTS < cNanoSeconds && !g_fQuit);
+}
+
+
+static DECLCALLBACK(int) LoadGenSpinThreadFunction(RTTHREAD hThreadSelf, void *pvUser)
+{
+ NOREF(hThreadSelf);
+ LoadGenSpin(*(uint64_t *)pvUser);
+ return VINF_SUCCESS;
+}
+
+#ifdef WITH_IPI_LOAD_GEN
+
+static int LoadGenIpiInit(void)
+{
+ /*
+ * Try make sure the support library is initialized...
+ */
+ SUPR3Init(NULL);
+
+ /*
+ * Load the module.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTPathAppPrivateArchTop(szPath, sizeof(szPath) - sizeof("/loadgenerator.r0"));
+ if (RT_SUCCESS(rc))
+ {
+ strcat(szPath, "/loadgeneratorR0.r0");
+ void *pvImageBase;
+ rc = SUPR3LoadServiceModule(szPath, "loadgeneratorR0", "LoadGenR0ServiceReqHandler", &pvImageBase);
+ if (RT_SUCCESS(rc))
+ {
+ /* done */
+ }
+ else
+ RTMsgError("SUPR3LoadServiceModule(%s): %Rrc", szPath, rc);
+ }
+ else
+ RTMsgError("RTPathAppPrivateArch: %Rrc", rc);
+ return rc;
+}
+
+
+static void LoadGenIpi(uint64_t cNanoSeconds)
+{
+ const uint64_t u64StartTS = RTTimeNanoTS();
+ do
+ {
+ int rc = SUPR3CallR0Service("loadgeneratorR0", sizeof("loadgeneratorR0") - 1,
+ 0 /* uOperation */, 1 /* cIpis */, NULL /* pReqHdr */);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("SUPR3CallR0Service: %Rrc", rc);
+ break;
+ }
+ } while (RTTimeNanoTS() - u64StartTS < cNanoSeconds && !g_fQuit);
+}
+
+
+static DECLCALLBACK(int) LoadGenIpiThreadFunction(RTTHREAD hThreadSelf, void *pvUser)
+{
+ LoadGenIpi(*(uint64_t *)pvUser);
+ NOREF(hThreadSelf);
+ return VINF_SUCCESS;
+}
+
+#endif
+
+
+int main(int argc, char **argv)
+{
+ static const struct LOADGENTYPE
+ {
+ const char *pszName;
+ int (*pfnInit)(void);
+ PFNRTTHREAD pfnThread;
+ } s_aLoadTypes[] =
+ {
+ { "spin", NULL, LoadGenSpinThreadFunction },
+#ifdef WITH_IPI_LOAD_GEN
+ { "ipi", LoadGenIpiInit, LoadGenIpiThreadFunction },
+#endif
+ };
+ unsigned iLoadType = 0;
+ static RTTHREAD s_aThreads[256];
+ int rc;
+ uint32_t cThreads = 1;
+ bool fScaleByCpus = false;
+ RTTHREADTYPE enmThreadType = RTTHREADTYPE_DEFAULT;
+ RTPROCPRIORITY enmProcPriority = RTPROCPRIORITY_DEFAULT;
+ uint64_t cNanoSeconds = UINT64_MAX;
+
+ RTR3InitExe(argc, &argv, 0);
+
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--number-of-threads", 'n', RTGETOPT_REQ_UINT32 },
+ { "--timeout", 't', RTGETOPT_REQ_STRING },
+ { "--thread-type", 'p', RTGETOPT_REQ_STRING },
+ { "--scale-by-cpus", 'c', RTGETOPT_REQ_NOTHING },
+ { "--load", 'l', RTGETOPT_REQ_STRING },
+ };
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'n':
+ cThreads = ValueUnion.u64;
+ if (cThreads == 0 || cThreads > RT_ELEMENTS(s_aThreads))
+ return RTMsgSyntax("Requested number of threads, %RU32, is out of range (1..%d).",
+ cThreads, RT_ELEMENTS(s_aThreads) - 1);
+ break;
+
+ case 't':
+ {
+ char *psz;
+ rc = RTStrToUInt64Ex(ValueUnion.psz, &psz, 0, &cNanoSeconds);
+ if (RT_FAILURE(rc))
+ return RTMsgSyntax("Failed reading the alleged timeout number '%s' (rc=%Rrc).",
+ ValueUnion.psz, rc);
+ while (*psz == ' ' || *psz == '\t')
+ psz++;
+ if (*psz)
+ {
+ uint64_t u64Factor = 1;
+ if (!strcmp(psz, "ns"))
+ u64Factor = 1;
+ else if (!strcmp(psz, "ms"))
+ u64Factor = 1000;
+ else if (!strcmp(psz, "s"))
+ u64Factor = 1000000000;
+ else if (!strcmp(psz, "m"))
+ u64Factor = UINT64_C(60000000000);
+ else if (!strcmp(psz, "h"))
+ u64Factor = UINT64_C(3600000000000);
+ else
+ return RTMsgSyntax("Unknown time suffix '%s'", psz);
+ uint64_t u64 = cNanoSeconds * u64Factor;
+ if (u64 < cNanoSeconds || (u64 < u64Factor && u64))
+ return RTMsgSyntax("Time representation overflowed! (%RU64 * %RU64)",
+ cNanoSeconds, u64Factor);
+ cNanoSeconds = u64;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ enmProcPriority = RTPROCPRIORITY_NORMAL;
+
+ uint32_t u32 = RTTHREADTYPE_INVALID;
+ char *psz;
+ rc = RTStrToUInt32Ex(ValueUnion.psz, &psz, 0, &u32);
+ if (RT_FAILURE(rc) || *psz)
+ {
+ if (!strcmp(ValueUnion.psz, "default"))
+ {
+ enmProcPriority = RTPROCPRIORITY_DEFAULT;
+ enmThreadType = RTTHREADTYPE_DEFAULT;
+ }
+ else if (!strcmp(ValueUnion.psz, "idle"))
+ {
+ enmProcPriority = RTPROCPRIORITY_LOW;
+ enmThreadType = RTTHREADTYPE_INFREQUENT_POLLER;
+ }
+ else if (!strcmp(ValueUnion.psz, "high"))
+ {
+ enmProcPriority = RTPROCPRIORITY_HIGH;
+ enmThreadType = RTTHREADTYPE_IO;
+ }
+ else
+ return RTMsgSyntax("can't grok thread type '%s'",
+ ValueUnion.psz);
+ }
+ else
+ {
+ enmThreadType = (RTTHREADTYPE)u32;
+ if (enmThreadType <= RTTHREADTYPE_INVALID || enmThreadType >= RTTHREADTYPE_END)
+ return RTMsgSyntax("thread type '%d' is out of range (%d..%d)",
+ ValueUnion.psz, RTTHREADTYPE_INVALID + 1, RTTHREADTYPE_END - 1);
+ }
+ break;
+ }
+
+ case 'c':
+ fScaleByCpus = true;
+ break;
+
+ case 'l':
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aLoadTypes); i++)
+ if (!strcmp(s_aLoadTypes[i].pszName, ValueUnion.psz))
+ {
+ ValueUnion.psz = NULL;
+ iLoadType = i;
+ break;
+ }
+ if (ValueUnion.psz)
+ return RTMsgSyntax("Unknown load type '%s'.", ValueUnion.psz);
+ break;
+ }
+
+ case 'h':
+ RTPrintf("Usage: %s [-p|--thread-type <type>] [-t|--timeout <sec|xxx[h|m|s|ms|ns]>] \\\n"
+ " %*s [-n|--number-of-threads <threads>] [-l|--load <loadtype>]\n"
+ "\n"
+ "Load types: "
+ , RTProcShortName(), strlen(RTProcShortName()), "");
+ for (size_t i = 0; i < RT_ELEMENTS(s_aLoadTypes); i++)
+ RTPrintf(i == 0 ? "%s (default)" : ", %s", s_aLoadTypes[i].pszName);
+ RTPrintf("\n");
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ case VINF_GETOPT_NOT_OPTION:
+ return RTMsgSyntax("Unknown argument #%d: '%s'", GetState.iNext-1, ValueUnion.psz);
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Scale thread count by host cpu count.
+ */
+ if (fScaleByCpus)
+ {
+ const unsigned cCpus = RTMpGetOnlineCount();
+ if (cCpus * cThreads > RT_ELEMENTS(s_aThreads))
+ return RTMsgSyntax("Requested number of threads, %RU32, is out of range (1..%d) when scaled by %d.",
+ cThreads, RT_ELEMENTS(s_aThreads) - 1, cCpus);
+ cThreads *= cCpus;
+ }
+
+ /*
+ * Modify process and thread priority? (ignore failure)
+ */
+ if (enmProcPriority != RTPROCPRIORITY_DEFAULT)
+ RTProcSetPriority(enmProcPriority);
+ if (enmThreadType != RTTHREADTYPE_DEFAULT)
+ RTThreadSetType(RTThreadSelf(), enmThreadType);
+
+ /*
+ * Load type specific init.
+ */
+ if (s_aLoadTypes[iLoadType].pfnInit)
+ {
+ rc = s_aLoadTypes[iLoadType].pfnInit();
+ if (RT_FAILURE(rc))
+ return 1;
+ }
+
+
+ /*
+ * Start threads.
+ */
+ for (unsigned i = 1; i < cThreads; i++)
+ {
+ s_aThreads[i] = NIL_RTTHREAD;
+ rc = RTThreadCreate(&s_aThreads[i], s_aLoadTypes[iLoadType].pfnThread,
+ &cNanoSeconds, 128*1024, enmThreadType, RTTHREADFLAGS_WAITABLE, "spinner");
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicXchgBool(&g_fQuit, true);
+ RTMsgError("failed to create thread #%d: %Rrc", i, rc);
+ while (i-- > 1)
+ RTThreadWait(s_aThreads[i], 1500, NULL);
+ return 1;
+ }
+ }
+
+ /* our selves */
+ s_aLoadTypes[iLoadType].pfnThread(RTThreadSelf(), &cNanoSeconds);
+
+ /*
+ * Wait for threads.
+ */
+ ASMAtomicXchgBool(&g_fQuit, true);
+ for (unsigned i = 1; i < cThreads; i++)
+ RTThreadWait(s_aThreads[i], 1500, NULL);
+
+ return 0;
+}
+
diff --git a/src/VBox/ValidationKit/utils/misc/loadgeneratorR0.cpp b/src/VBox/ValidationKit/utils/misc/loadgeneratorR0.cpp
new file mode 100644
index 00000000..006e3db8
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/misc/loadgeneratorR0.cpp
@@ -0,0 +1,101 @@
+/* $Id: loadgeneratorR0.cpp $ */
+/** @file
+ * Load Generator, Ring-0 Service.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/mp.h>
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+
+
+
+
+/**
+ * Worker for loadgenR0Ipi.
+ */
+static DECLCALLBACK(void) loadgenR0IpiWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ NOREF(idCpu);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+}
+
+
+/**
+ * Generate broadcast inter processor interrupts (IPI), aka cross calls.
+ *
+ * @returns VBox status code.
+ * @param cIpis The number of IPIs to do.
+ */
+static int loadgenR0Ipi(uint64_t cIpis)
+{
+ if (cIpis > _1G || !cIpis)
+ return VERR_INVALID_PARAMETER;
+
+ while (cIpis-- > 0)
+ {
+ int rc = RTMpOnAll(loadgenR0IpiWorker, NULL, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Service request handler entry point.
+ *
+ * @copydoc FNSUPR0SERVICEREQHANDLER
+ */
+extern "C" DECLEXPORT(int) LoadGenR0ServiceReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation,
+ uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
+
+{
+ switch (uOperation)
+ {
+ case 0:
+ if (pReqHdr)
+ return VERR_INVALID_PARAMETER;
+ return loadgenR0Ipi(u64Arg);
+
+ default:
+ NOREF(pSession);
+ return VERR_NOT_SUPPORTED;
+ }
+}
+
diff --git a/src/VBox/ValidationKit/utils/misc/vts_rm.cpp b/src/VBox/ValidationKit/utils/misc/vts_rm.cpp
new file mode 100644
index 00000000..9b59ab24
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/misc/vts_rm.cpp
@@ -0,0 +1,54 @@
+/* $Id: vts_rm.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - rm like utility.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/path.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ return RTPathRmCmd(argc, argv);
+}
+
diff --git a/src/VBox/ValidationKit/utils/misc/vts_tar.cpp b/src/VBox/ValidationKit/utils/misc/vts_tar.cpp
new file mode 100644
index 00000000..1e9c5c3b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/misc/vts_tar.cpp
@@ -0,0 +1,54 @@
+/* $Id: vts_tar.cpp $ */
+/** @file
+ * VirtualBox Validation Kit - tar like utility.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/zip.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+ return RTZipTarCmd(argc, argv);
+}
+
diff --git a/src/VBox/ValidationKit/utils/network/Makefile.kmk b/src/VBox/ValidationKit/utils/network/Makefile.kmk
new file mode 100644
index 00000000..7e5d6db3
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/network/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Network tests.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Network Performance Benchmark.
+#
+PROGRAMS += NetPerf
+NetPerf_TEMPLATE = VBoxValidationKitR3
+NetPerf_SOURCES = NetPerf.cpp
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/network/NetPerf.cpp b/src/VBox/ValidationKit/utils/network/NetPerf.cpp
new file mode 100644
index 00000000..ceb6a201
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/network/NetPerf.cpp
@@ -0,0 +1,2008 @@
+/* $Id: NetPerf.cpp $ */
+/** @file
+ * NetPerf - Network Performance Benchmark.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/tcp.h>
+#include <iprt/thread.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/timer.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Default TCP port (update help text if you change this) */
+#define NETPERF_DEFAULT_PORT 5002
+
+/** Default TCP packet size (bytes) */
+#define NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT 8192
+/** Default TCP packet size (bytes) */
+#define NETPERF_DEFAULT_PKT_SIZE_LATENCY 1024
+/** Maximum packet size possible (bytes). */
+#define NETPERF_MAX_PKT_SIZE _1M
+/** Minimum packet size possible (bytes). */
+#define NETPERF_MIN_PKT_SIZE sizeof(NETPERFHDR)
+
+/** Default timeout in (seconds) */
+#define NETPERF_DEFAULT_TIMEOUT 10
+/** Maximum timeout possible (seconds). */
+#define NETPERF_MAX_TIMEOUT 3600 /* 1h */
+/** Minimum timeout possible (seconds). */
+#define NETPERF_MIN_TIMEOUT 1
+
+/** The default warmup time (ms). */
+#define NETPERF_DEFAULT_WARMUP 1000 /* 1s */
+/** The maxium warmup time (ms). */
+#define NETPERF_MAX_WARMUP 60000 /* 60s */
+/** The minimum warmup time (ms). */
+#define NETPERF_MIN_WARMUP 1000 /* 1s */
+
+/** The default cool down time (ms). */
+#define NETPERF_DEFAULT_COOL_DOWN 1000 /* 1s */
+/** The maxium cool down time (ms). */
+#define NETPERF_MAX_COOL_DOWN 60000 /* 60s */
+/** The minimum cool down time (ms). */
+#define NETPERF_MIN_COOL_DOWN 1000 /* 1s */
+
+/** Maximum socket buffer size possible (bytes). */
+#define NETPERF_MAX_BUF_SIZE _128M
+/** Minimum socket buffer size possible (bytes). */
+#define NETPERF_MIN_BUF_SIZE 256
+
+/** The length of the length prefix used when submitting parameters and
+ * results. */
+#define NETPERF_LEN_PREFIX 4
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef enum NETPERFPROTO
+{
+ NETPERFPROTO_INVALID = 0,
+ NETPERFPROTO_TCP
+ //NETPERFPROTO_UDP
+} NETPERFPROTO;
+
+/**
+ * What kind of test we're performing.
+ */
+typedef enum NETPERFMODE
+{
+ NETPERFMODE_INVALID = 0,
+ /** Latency of a symmetric packet exchange. */
+ NETPERFMODE_LATENCY,
+ /** Separate throughput measurements for each direction. */
+ NETPERFMODE_THROUGHPUT,
+ /** Transmit throughput. */
+ NETPERFMODE_THROUGHPUT_XMIT,
+ /** Transmit throughput. */
+ NETPERFMODE_THROUGHPUT_RECV
+} NETPERFMODE;
+
+/**
+ * Statistics.
+ */
+typedef struct NETPERFSTATS
+{
+ uint64_t cTx;
+ uint64_t cRx;
+ uint64_t cEchos;
+ uint64_t cErrors;
+ uint64_t cNsElapsed;
+} NETPERFSTATS;
+
+/**
+ * Settings & a little bit of state.
+ */
+typedef struct NETPERFPARAMS
+{
+ /** @name Static settings
+ * @{ */
+ /** The TCP port number. */
+ uint32_t uPort;
+ /** Client: Use server statistcs. */
+ bool fServerStats;
+ /** Server: Quit after the first client. */
+ bool fSingleClient;
+ /** Send and receive buffer sizes for TCP sockets, zero if to use defaults. */
+ uint32_t cbBufferSize;
+ /** @} */
+
+ /** @name Dynamic settings
+ * @{ */
+ /** Disable send packet coalescing. */
+ bool fNoDelay;
+ /** Detect broken payloads. */
+ bool fCheckData;
+ /** The test mode. */
+ NETPERFMODE enmMode;
+ /** The number of seconds to run each of the test steps. */
+ uint32_t cSecTimeout;
+ /** Number of millisecond to spend warning up before testing. */
+ uint32_t cMsWarmup;
+ /** Number of millisecond to spend cooling down after the testing. */
+ uint32_t cMsCoolDown;
+ /** The packet size. */
+ uint32_t cbPacket;
+ /** @} */
+
+ /** @name State
+ * @{ */
+ RTSOCKET hSocket;
+ /** @} */
+} NETPERFPARAMS;
+
+/**
+ * Packet header used in tests.
+ *
+ * Need to indicate when we've timed out and it's time to reverse the roles or
+ * stop testing.
+ */
+typedef struct NETPERFHDR
+{
+ /** Magic value (little endian). */
+ uint32_t u32Magic;
+ /** State value. */
+ uint32_t u32State;
+ /** Sequence number (little endian). */
+ uint32_t u32Seq;
+ /** Reserved, must be zero. */
+ uint32_t u32Reserved;
+} NETPERFHDR;
+
+/** Magic value for NETPERFHDR::u32Magic. */
+#define NETPERFHDR_MAGIC UINT32_C(0xfeedf00d)
+
+/** @name Packet State (NETPERF::u32Magic)
+ * @{ */
+/** Warm up. */
+#define NETPERFHDR_WARMUP UINT32_C(0x0c0ffe01)
+/** The clock is running. */
+#define NETPERFHDR_TESTING UINT32_C(0x0c0ffe02)
+/** Stop the clock but continue the package flow. */
+#define NETPERFHDR_COOL_DOWN UINT32_C(0x0c0ffe03)
+/** Done, stop the clock if not done already and reply with results. */
+#define NETPERFHDR_DONE UINT32_C(0x0c0ffe04)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Connection start/identifier to make sure other end is NetPerf. */
+static const char g_ConnectStart[] = "yo! waaazzzzzaaaaup dude?";
+/** Start of parameters proposal made by the client. */
+static const char g_szStartParams[] = "deal?";
+/** All okay to start test */
+static const char g_szAck[] = "okay!";
+/** Negative. */
+static const char g_szNegative[] = "nope!";
+AssertCompile(sizeof(g_szAck) == sizeof(g_szNegative));
+/** Start of statistics. */
+static const char g_szStartStats[] = "dude, stats";
+
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ { "--server", 's', RTGETOPT_REQ_NOTHING },
+ { "--client", 'c', RTGETOPT_REQ_STRING },
+ { "--interval", 'i', RTGETOPT_REQ_UINT32 },
+ { "--port", 'p', RTGETOPT_REQ_UINT32 },
+ { "--len", 'l', RTGETOPT_REQ_UINT32 },
+ { "--nodelay", 'N', RTGETOPT_REQ_NOTHING },
+ { "--mode", 'm', RTGETOPT_REQ_STRING },
+ { "--warmup", 'w', RTGETOPT_REQ_UINT32 },
+ { "--cool-down", 'W', RTGETOPT_REQ_UINT32 },
+ { "--server-stats", 'S', RTGETOPT_REQ_NOTHING },
+ { "--single-client", '1', RTGETOPT_REQ_NOTHING },
+ { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
+ { "--daemonized", 'D', RTGETOPT_REQ_NOTHING },
+ { "--check-data", 'C', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--buffer-size", 'b', RTGETOPT_REQ_UINT32 },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
+};
+
+/** The test handle. */
+static RTTEST g_hTest;
+/** Verbosity level. */
+static uint32_t g_uVerbosity = 0;
+
+
+
+static void Usage(PRTSTREAM pStrm)
+{
+ char szExec[RTPATH_MAX];
+ RTStrmPrintf(pStrm, "usage: %s <-s|-c <host>> [options]\n",
+ RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "options: \n");
+
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'h':
+ pszHelp = "Displays this help and exit";
+ break;
+ case 's':
+ pszHelp = "Run in server mode, waiting for clients (default)";
+ break;
+ case 'c':
+ pszHelp = "Run in client mode, connecting to <host>";
+ break;
+ case 'i':
+ pszHelp = "Interval in seconds to run the test (default " RT_XSTR(NETPERF_DEFAULT_TIMEOUT) " s)";
+ break;
+ case 'p':
+ pszHelp = "Server port to listen/connect to (default " RT_XSTR(NETPERF_DEFAULT_PORT) ")";
+ break;
+ case 'l':
+ pszHelp = "Packet size in bytes (defaults to " RT_XSTR(NETPERF_DEFAULT_PKT_SIZE_LATENCY)
+ " for latency and " RT_XSTR(NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT) " for throughput)";
+ break;
+ case 'm':
+ pszHelp = "Test mode: latency (default), throughput, throughput-xmit or throughput-recv";
+ break;
+ case 'N':
+ pszHelp = "Set TCP no delay, disabling Nagle's algorithm";
+ break;
+ case 'S':
+ pszHelp = "Report server stats, ignored if server";
+ break;
+ case '1':
+ pszHelp = "Stop the server after the first client";
+ break;
+ case 'd':
+ pszHelp = "Daemonize if server, ignored if client";
+ break;
+ case 'D':
+ continue; /* internal */
+ case 'w':
+ pszHelp = "Warmup time, in milliseconds (default " RT_XSTR(NETPERF_DEFAULT_WARMUP) " ms)";
+ break;
+ case 'W':
+ pszHelp = "Cool down time, in milliseconds (default " RT_XSTR(NETPERF_DEFAULT_COOL_DOWN) " ms)";
+ break;
+ case 'C':
+ pszHelp = "Check payload data at the receiving end";
+ break;
+ case 'b':
+ pszHelp = "Send and receive buffer sizes for TCP";
+ break;
+ case 'v':
+ pszHelp = "Verbose execution.";
+ break;
+ default:
+ pszHelp = "Option undocumented";
+ break;
+ }
+ char szOpt[256];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
+ }
+}
+
+/**
+ * Timer callback employed to set the stop indicator.
+ *
+ * This is used both by the client and server side.
+ *
+ * @param hTimer The timer, ignored.
+ * @param pvUser Pointer to the stop variable.
+ * @param iTick The tick, ignored.
+ */
+static DECLCALLBACK(void) netperfStopTimerCallback(RTTIMERLR hTimer, void *pvUser, uint64_t iTick)
+{
+ bool volatile *pfStop = (bool volatile *)pvUser;
+ if (g_uVerbosity > 0)
+ RTPrintf("Time's Up!\n");
+ ASMAtomicWriteBool(pfStop, true);
+ NOREF(hTimer); NOREF(iTick);
+}
+
+/**
+ * Sends a statistics packet to our peer.
+ *
+ * @returns IPRT status code.
+ * @param pStats The stats to send.
+ * @param hSocket The TCP socket to send them to.
+ */
+static int netperfSendStats(NETPERFSTATS const *pStats, RTSOCKET hSocket)
+{
+ char szBuf[256 + NETPERF_LEN_PREFIX];
+ size_t cch = RTStrPrintf(&szBuf[NETPERF_LEN_PREFIX], sizeof(szBuf) - NETPERF_LEN_PREFIX,
+ "%s:%llu:%llu:%llu:%llu:%llu",
+ g_szStartStats,
+ pStats->cTx,
+ pStats->cRx,
+ pStats->cEchos,
+ pStats->cErrors,
+ pStats->cNsElapsed);
+
+ RTStrPrintf(szBuf, NETPERF_LEN_PREFIX + 1, "%0*u", NETPERF_LEN_PREFIX, cch);
+ szBuf[NETPERF_LEN_PREFIX] = g_szStartStats[0];
+ Assert(strlen(szBuf) == cch + NETPERF_LEN_PREFIX);
+
+ int rc = RTTcpWrite(hSocket, szBuf, cch + NETPERF_LEN_PREFIX);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "stats: Failed to send stats: %Rrc\n", rc);
+
+ /*
+ * Wait for ACK.
+ */
+ rc = RTTcpRead(hSocket, szBuf, sizeof(g_szAck) - 1, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "stats: failed to write stats: %Rrc\n", rc);
+ szBuf[sizeof(g_szAck) - 1] = '\0';
+ if (!strcmp(szBuf, g_szNegative))
+ return RTTestIFailedRc(rc, "stats: client failed to parse them\n");
+ if (strcmp(szBuf, g_szAck))
+ return RTTestIFailedRc(rc, "stats: got '%s' in instead of ack/nack\n", szBuf);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Receives a statistics packet from our peer.
+ *
+ * @returns IPRT status code. Error signalled.
+ * @param pStats Where to receive the stats.
+ * @param hSocket The TCP socket to recevie them from.
+ */
+static int netperfRecvStats(NETPERFSTATS *pStats, RTSOCKET hSocket)
+{
+ /*
+ * Read the stats message.
+ */
+ /* the length prefix */
+ char szBuf[256 + NETPERF_LEN_PREFIX];
+ int rc = RTTcpRead(hSocket, szBuf, NETPERF_LEN_PREFIX, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "stats: failed to read stats prefix: %Rrc\n", rc);
+
+ szBuf[NETPERF_LEN_PREFIX] = '\0';
+ uint32_t cch;
+ rc = RTStrToUInt32Full(szBuf, 10, &cch);
+ if (rc != VINF_SUCCESS)
+ return RTTestIFailedRc(RT_SUCCESS(rc) ? -rc : rc, "stats: bad stat length prefix: '%s' - %Rrc\n", szBuf, rc);
+ if (cch >= sizeof(szBuf))
+ return RTTestIFailedRc(VERR_BUFFER_OVERFLOW, "stats: too large: %u bytes\n", cch);
+
+ /* the actual message */
+ rc = RTTcpRead(hSocket, szBuf, cch, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "failed to read stats: %Rrc\n", rc);
+ szBuf[cch] = '\0';
+
+ /*
+ * Validate the message header.
+ */
+ if ( strncmp(szBuf, g_szStartStats, sizeof(g_szStartStats) - 1)
+ || szBuf[sizeof(g_szStartStats) - 1] != ':')
+ return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "stats: invalid packet start: '%s'\n", szBuf);
+ char *pszCur = &szBuf[sizeof(g_szStartStats)];
+
+ /*
+ * Parse it.
+ */
+ static const char * const s_apszNames[] =
+ {
+ "cTx", "cRx", "cEchos", "cErrors", "cNsElapsed"
+ };
+ uint64_t *apu64[RT_ELEMENTS(s_apszNames)] =
+ {
+ &pStats->cTx,
+ &pStats->cRx,
+ &pStats->cEchos,
+ &pStats->cErrors,
+ &pStats->cNsElapsed
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(apu64); i++)
+ {
+ if (!pszCur)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "stats: missing %s\n", s_apszNames[i]);
+
+ char *pszNext = strchr(pszCur, ':');
+ if (pszNext)
+ *pszNext++ = '\0';
+ rc = RTStrToUInt64Full(pszCur, 10, apu64[i]);
+ if (rc != VINF_SUCCESS)
+ return RTTestIFailedRc(RT_SUCCESS(rc) ? -rc : rc, "stats: bad value for %s: '%s' - %Rrc\n",
+ s_apszNames[i], pszCur, rc);
+
+ pszCur = pszNext;
+ }
+
+ if (pszCur)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "stats: Unparsed data: '%s'\n", pszCur);
+
+ /*
+ * Send ACK.
+ */
+ rc = RTTcpWrite(hSocket, g_szAck, sizeof(g_szAck) - 1);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "stats: failed to write ack: %Rrc\n", rc);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * TCP Throughput: Print the statistics.
+ *
+ * @param pSendStats Send stats.
+ * @param pRecvStats Receive stats.
+ * @param cbPacket Packet size.
+ */
+static void netperfPrintThroughputStats(NETPERFSTATS const *pSendStats, NETPERFSTATS const *pRecvStats, uint32_t cbPacket)
+{
+ RTTestIValue("Packet size", cbPacket, RTTESTUNIT_BYTES);
+
+ if (pSendStats)
+ {
+ double rdSecElapsed = (double)pSendStats->cNsElapsed / 1000000000.0;
+ RTTestIValue("Sends", pSendStats->cTx, RTTESTUNIT_PACKETS);
+ RTTestIValue("Send Interval", pSendStats->cNsElapsed, RTTESTUNIT_NS);
+ RTTestIValue("Send Throughput", (uint64_t)((double)(cbPacket * pSendStats->cTx) / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("Send Rate", (uint64_t)((double)pSendStats->cTx / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
+ RTTestIValue("Send Latency", (uint64_t)(rdSecElapsed / (double)pSendStats->cTx * 1000000000.0), RTTESTUNIT_NS_PER_PACKET);
+ }
+
+ if (pRecvStats)
+ {
+ double rdSecElapsed = (double)pRecvStats->cNsElapsed / 1000000000.0;
+ RTTestIValue("Receives", pRecvStats->cRx, RTTESTUNIT_PACKETS);
+ RTTestIValue("Receive Interval", pRecvStats->cNsElapsed, RTTESTUNIT_NS);
+ RTTestIValue("Receive Throughput", (uint64_t)((double)(cbPacket * pRecvStats->cRx) / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("Receive Rate", (uint64_t)((double)pRecvStats->cRx / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
+ RTTestIValue("Receive Latency", (uint64_t)(rdSecElapsed / (double)pRecvStats->cRx * 1000000000.0), RTTESTUNIT_NS_PER_PACKET);
+ }
+}
+
+/**
+ * TCP Throughput: Send data to the other party.
+ *
+ * @returns IPRT status code.
+ * @param pParams The TCP parameters block.
+ * @param pBuf The buffer we're using when sending.
+ * @param pSendStats Where to return the statistics.
+ */
+static int netperfTCPThroughputSend(NETPERFPARAMS const *pParams, NETPERFHDR *pBuf, NETPERFSTATS *pSendStats)
+{
+ RT_ZERO(*pSendStats);
+
+ /*
+ * Create the timer
+ */
+ RTTIMERLR hTimer;
+ bool volatile fStop = false;
+ int rc = RTTimerLRCreateEx(&hTimer, 0 /* nsec */, RTTIMER_FLAGS_CPU_ANY, netperfStopTimerCallback, (void *)&fStop);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Seq = 0;
+
+ RT_BZERO(pBuf, pParams->cbPacket);
+ pBuf->u32Magic = RT_H2LE_U32_C(NETPERFHDR_MAGIC);
+ pBuf->u32State = 0;
+ pBuf->u32Seq = 0;
+ pBuf->u32Reserved = 0;
+
+ /*
+ * Warm up.
+ */
+ if (g_uVerbosity > 0)
+ RTPrintf("Warmup...\n");
+ pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
+ rc = RTTimerLRStart(hTimer, pParams->cMsWarmup * UINT64_C(1000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ while (!fStop)
+ {
+ u32Seq++;
+ pBuf->u32Seq = RT_H2LE_U32(u32Seq);
+ rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/warmup: %Rrc\n", rc);
+
+ /*
+ * The real thing.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTPrintf("The real thing...\n");
+ pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_TESTING);
+ fStop = false;
+ rc = RTTimerLRStart(hTimer, pParams->cSecTimeout * UINT64_C(1000000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t u64StartTS = RTTimeNanoTS();
+ while (!fStop)
+ {
+ u32Seq++;
+ pBuf->u32Seq = RT_H2LE_U32(u32Seq);
+ rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/testing: %Rrc\n", rc);
+ break;
+ }
+ pSendStats->cTx++;
+ }
+ pSendStats->cNsElapsed = RTTimeNanoTS() - u64StartTS;
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
+ }
+
+ /*
+ * Cool down.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTPrintf("Cool down...\n");
+ pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN);
+ fStop = false;
+ rc = RTTimerLRStart(hTimer, pParams->cMsCoolDown * UINT64_C(1000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ while (!fStop)
+ {
+ u32Seq++;
+ pBuf->u32Seq = RT_H2LE_U32(u32Seq);
+ rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/cool down: %Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
+ }
+
+ /*
+ * Send DONE packet.
+ */
+ if (g_uVerbosity > 0)
+ RTPrintf("Done\n");
+ if (RT_SUCCESS(rc))
+ {
+ u32Seq++;
+ pBuf->u32Seq = RT_H2LE_U32(u32Seq);
+ pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_DONE);
+ rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ RTTestIFailed("RTTcpWrite/done: %Rrc\n", rc);
+ }
+
+ RTTimerLRDestroy(hTimer);
+ }
+ else
+ RTTestIFailed("Failed to create timer object: %Rrc\n", rc);
+ return rc;
+}
+
+
+/**
+ * TCP Throughput: Receive data from the other party.
+ *
+ * @returns IPRT status code.
+ * @param pParams The TCP parameters block.
+ * @param pBuf The buffer we're using when sending.
+ * @param pStats Where to return the statistics.
+ */
+static int netperfTCPThroughputRecv(NETPERFPARAMS const *pParams, NETPERFHDR *pBuf, NETPERFSTATS *pStats)
+{
+ RT_ZERO(*pStats);
+
+ int rc;
+ uint32_t u32Seq = 0;
+ uint64_t cRx = 0;
+ uint64_t u64StartTS = 0;
+ uint32_t uState = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
+
+ for (;;)
+ {
+ rc = RTTcpRead(pParams->hSocket, pBuf, pParams->cbPacket, NULL);
+ if (RT_FAILURE(rc))
+ {
+ pStats->cErrors++;
+ RTTestIFailed("RTTcpRead failed: %Rrc\n", rc);
+ break;
+ }
+ if (RT_UNLIKELY( pBuf->u32Magic != RT_H2LE_U32_C(NETPERFHDR_MAGIC)
+ || pBuf->u32Reserved != 0))
+ {
+ pStats->cErrors++;
+ RTTestIFailed("Invalid magic or reserved field value: %#x %#x\n", RT_H2LE_U32(pBuf->u32Magic), RT_H2LE_U32(pBuf->u32Reserved));
+ rc = VERR_INVALID_MAGIC;
+ break;
+ }
+
+ u32Seq += 1;
+ if (RT_UNLIKELY(pBuf->u32Seq != RT_H2LE_U32(u32Seq)))
+ {
+ pStats->cErrors++;
+ RTTestIFailed("Out of sequence: got %#x, expected %#x\n", RT_H2LE_U32(pBuf->u32Seq), u32Seq);
+ rc = VERR_WRONG_ORDER;
+ break;
+ }
+
+ if (pParams->fCheckData && uState == RT_H2LE_U32_C(NETPERFHDR_TESTING))
+ {
+ unsigned i = sizeof(NETPERFHDR);
+ for (;i < pParams->cbPacket; ++i)
+ if (((unsigned char *)pBuf)[i])
+ break;
+ if (i != pParams->cbPacket)
+ {
+ pStats->cErrors++;
+ RTTestIFailed("Broken payload: at %#x got %#x, expected %#x\n", i, ((unsigned char *)pBuf)[i], 0);
+ rc = VERR_NOT_EQUAL;
+ break;
+ }
+ }
+ if (RT_LIKELY(pBuf->u32State == uState))
+ cRx++;
+ /*
+ * Validate and act on switch state.
+ */
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_WARMUP)
+ && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_TESTING))
+ {
+ cRx = 0;
+ u64StartTS = RTTimeNanoTS();
+ uState = pBuf->u32State;
+ }
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_TESTING)
+ && ( pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
+ || pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE)) )
+ {
+ pStats->cNsElapsed = RTTimeNanoTS() - u64StartTS;
+ pStats->cRx = cRx + 1;
+ uState = pBuf->u32State;
+ if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
+ break;
+ }
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
+ && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE))
+ {
+ uState = pBuf->u32State;
+ break;
+ }
+ else
+ {
+ pStats->cErrors++;
+ RTTestIFailed("Protocol error: invalid state transition %#x -> %#x\n",
+ RT_LE2H_U32(uState), RT_LE2H_U32(pBuf->u32State));
+ rc = VERR_INVALID_MAGIC;
+ break;
+ }
+ }
+
+ AssertReturn(uState == RT_H2LE_U32_C(NETPERFHDR_DONE) || RT_FAILURE(rc), VERR_INVALID_STATE);
+ return rc;
+}
+
+
+/**
+ * Prints the statistics for the latency test.
+ *
+ * @param pStats The statistics.
+ * @param cbPacket The packet size in bytes.
+ */
+static void netperfPrintLatencyStats(NETPERFSTATS const *pStats, uint32_t cbPacket)
+{
+ double rdSecElapsed = (double)pStats->cNsElapsed / 1000000000.0;
+ RTTestIValue("Transmitted", pStats->cTx, RTTESTUNIT_PACKETS);
+ RTTestIValue("Successful echos", pStats->cEchos, RTTESTUNIT_PACKETS);
+ RTTestIValue("Errors", pStats->cErrors, RTTESTUNIT_PACKETS);
+ RTTestIValue("Interval", pStats->cNsElapsed, RTTESTUNIT_NS);
+ RTTestIValue("Packet size", cbPacket, RTTESTUNIT_BYTES);
+ RTTestIValue("Average rate", (uint64_t)((double)pStats->cEchos / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
+ RTTestIValue("Average throughput", (uint64_t)((double)(cbPacket * pStats->cEchos) / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
+ RTTestIValue("Average latency", (uint64_t)(rdSecElapsed / (double)pStats->cEchos * 1000000000.0), RTTESTUNIT_NS_PER_ROUND_TRIP);
+ RTTestISubDone();
+}
+
+
+/**
+ * NETPERFMODE -> string.
+ *
+ * @returns readonly string.
+ * @param enmMode The mode.
+ */
+static const char *netperfModeToString(NETPERFMODE enmMode)
+{
+ switch (enmMode)
+ {
+ case NETPERFMODE_LATENCY: return "latency";
+ case NETPERFMODE_THROUGHPUT: return "throughput";
+ case NETPERFMODE_THROUGHPUT_XMIT: return "throughput-xmit";
+ case NETPERFMODE_THROUGHPUT_RECV: return "throughput-recv";
+ default: AssertFailed(); return "internal-error";
+ }
+}
+
+/**
+ * String -> NETPERFMODE.
+ *
+ * @returns The corresponding NETPERFMODE, NETPERFMODE_INVALID on failure.
+ * @param pszMode The mode string.
+ */
+static NETPERFMODE netperfModeFromString(const char *pszMode)
+{
+ if (!strcmp(pszMode, "latency"))
+ return NETPERFMODE_LATENCY;
+ if ( !strcmp(pszMode, "throughput")
+ || !strcmp(pszMode, "thruput") )
+ return NETPERFMODE_THROUGHPUT;
+ if ( !strcmp(pszMode, "throughput-xmit")
+ || !strcmp(pszMode, "thruput-xmit")
+ || !strcmp(pszMode, "xmit") )
+ return NETPERFMODE_THROUGHPUT_XMIT;
+ if ( !strcmp(pszMode, "throughput-recv")
+ || !strcmp(pszMode, "thruput-recv")
+ || !strcmp(pszMode, "recv") )
+ return NETPERFMODE_THROUGHPUT_RECV;
+ return NETPERFMODE_INVALID;
+}
+
+
+
+
+
+/**
+ * TCP Server: Throughput test.
+ *
+ * @returns IPRT status code.
+ * @param pParams The parameters to use for this test.
+ */
+static int netperfTCPServerDoThroughput(NETPERFPARAMS const *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Receive first, then Send. The reverse of the client.
+ */
+ NETPERFSTATS RecvStats;
+ int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
+ if (RT_SUCCESS(rc))
+ {
+ rc = netperfSendStats(&RecvStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS SendStats;
+ rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
+ if (RT_SUCCESS(rc))
+ {
+ rc = netperfSendStats(&SendStats, pParams->hSocket);
+ netperfPrintThroughputStats(&SendStats, &RecvStats, pParams->cbPacket);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * TCP Server: Throughput xmit test (receive from client).
+ *
+ * @returns IPRT status code.
+ * @param pParams The parameters to use for this test.
+ */
+static int netperfTCPServerDoThroughputXmit(NETPERFPARAMS const *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Receive the transmitted data (reverse of client).
+ */
+ NETPERFSTATS RecvStats;
+ int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
+ if (RT_SUCCESS(rc))
+ {
+ rc = netperfSendStats(&RecvStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ netperfPrintThroughputStats(NULL, &RecvStats, pParams->cbPacket);
+ }
+
+ return rc;
+}
+
+/**
+ * TCP Server: Throughput recv test (transmit to client).
+ *
+ * @returns IPRT status code.
+ * @param pParams The parameters to use for this test.
+ */
+static int netperfTCPServerDoThroughputRecv(NETPERFPARAMS const *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Send data to the client (reverse of client).
+ */
+ NETPERFSTATS SendStats;
+ int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
+ if (RT_SUCCESS(rc))
+ {
+ rc = netperfSendStats(&SendStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ netperfPrintThroughputStats(&SendStats, NULL, pParams->cbPacket);
+ }
+
+ return rc;
+}
+
+/**
+ * TCP Server: Latency test.
+ *
+ * @returns IPRT status code.
+ * @param pParams The parameters to use for this test.
+ */
+static int netperfTCPServerDoLatency(NETPERFPARAMS const *pParams)
+{
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Failed to allocated packet buffer of %u bytes.\n", pParams->cbPacket);
+
+ /*
+ * Ping pong with client.
+ */
+ int rc;
+ uint32_t uState = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
+ uint32_t u32Seq = 0;
+ uint64_t cTx = 0;
+ uint64_t cRx = 0;
+ uint64_t u64StartTS = 0;
+ NETPERFSTATS Stats;
+ RT_ZERO(Stats);
+ for (;;)
+ {
+ rc = RTTcpRead(pParams->hSocket, pBuf, pParams->cbPacket, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("Failed to read data from client: %Rrc\n", rc);
+ break;
+ }
+
+ /*
+ * Validate the packet
+ */
+ if (RT_UNLIKELY( pBuf->u32Magic != RT_H2LE_U32_C(NETPERFHDR_MAGIC)
+ || pBuf->u32Reserved != 0))
+ {
+ RTTestIFailed("Invalid magic or reserved field value: %#x %#x\n", RT_H2LE_U32(pBuf->u32Magic), RT_H2LE_U32(pBuf->u32Reserved));
+ rc = VERR_INVALID_MAGIC;
+ break;
+ }
+
+ u32Seq += 1;
+ if (RT_UNLIKELY(pBuf->u32Seq != RT_H2LE_U32(u32Seq)))
+ {
+ RTTestIFailed("Out of sequence: got %#x, expected %#x\n", RT_H2LE_U32(pBuf->u32Seq), u32Seq);
+ rc = VERR_WRONG_ORDER;
+ break;
+ }
+
+ /*
+ * Count the packet if the state remains unchanged.
+ */
+ if (RT_LIKELY(pBuf->u32State == uState))
+ cRx++;
+ /*
+ * Validate and act on the state transition.
+ */
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_WARMUP)
+ && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_TESTING))
+ {
+ cRx = cTx = 0;
+ u64StartTS = RTTimeNanoTS();
+ uState = pBuf->u32State;
+ }
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_TESTING)
+ && ( pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
+ || pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE)) )
+ {
+ Stats.cNsElapsed = RTTimeNanoTS() - u64StartTS;
+ Stats.cEchos = cTx;
+ Stats.cTx = cTx;
+ Stats.cRx = cRx;
+ uState = pBuf->u32State;
+ if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
+ break;
+ }
+ else if ( uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
+ && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE))
+ {
+ uState = pBuf->u32State;
+ break;
+ }
+ else
+ {
+ RTTestIFailed("Protocol error: invalid state transition %#x -> %#x\n",
+ RT_LE2H_U32(uState), RT_LE2H_U32(pBuf->u32State));
+ break;
+ }
+
+ /*
+ * Write same data back to client.
+ */
+ rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("Failed to write data to client: %Rrc\n", rc);
+ break;
+ }
+
+ cTx++;
+ }
+
+ /*
+ * Send stats to client and print them.
+ */
+ if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
+ netperfSendStats(&Stats, pParams->hSocket);
+
+ if ( uState == RT_H2LE_U32_C(NETPERFHDR_DONE)
+ || uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN))
+ netperfPrintLatencyStats(&Stats, pParams->cbPacket);
+
+ RTMemFree(pBuf);
+ return rc;
+}
+
+/**
+ * Parses the parameters the client has sent us.
+ *
+ * @returns IPRT status code. Message has been shown on failure.
+ * @param pParams The parameter structure to store the parameters
+ * in.
+ * @param pszParams The parameter string sent by the client.
+ */
+static int netperfTCPServerParseParams(NETPERFPARAMS *pParams, char *pszParams)
+{
+ /*
+ * Set defaults for the dynamic settings.
+ */
+ pParams->fNoDelay = false;
+ pParams->enmMode = NETPERFMODE_LATENCY;
+ pParams->cSecTimeout = NETPERF_DEFAULT_WARMUP;
+ pParams->cMsCoolDown = NETPERF_DEFAULT_COOL_DOWN;
+ pParams->cMsWarmup = NETPERF_DEFAULT_WARMUP;
+ pParams->cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
+
+ /*
+ * Parse the client parameters.
+ */
+ /* first arg: transport type. [mandatory] */
+ char *pszCur = strchr(pszParams, ':');
+ if (!pszCur)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: No colon\n");
+ char *pszNext = strchr(++pszCur, ':');
+ if (pszNext)
+ *pszNext++ = '\0';
+ if (strcmp(pszCur, "TCP"))
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Invalid transport type: \"%s\"\n", pszCur);
+ pszCur = pszNext;
+
+ /* second arg: mode. [mandatory] */
+ if (!pszCur)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Missing test mode\n");
+ pszNext = strchr(pszCur, ':');
+ if (pszNext)
+ *pszNext++ = '\0';
+ pParams->enmMode = netperfModeFromString(pszCur);
+ if (pParams->enmMode == NETPERFMODE_INVALID)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Invalid test mode: \"%s\"\n", pszCur);
+ pszCur = pszNext;
+
+ /*
+ * The remainder are uint32_t or bool.
+ */
+ struct
+ {
+ bool fBool;
+ bool fMandatory;
+ void *pvValue;
+ uint32_t uMin;
+ uint32_t uMax;
+ const char *pszName;
+ } aElements[] =
+ {
+ { false, true, &pParams->cSecTimeout, NETPERF_MIN_TIMEOUT, NETPERF_MAX_TIMEOUT, "timeout" },
+ { false, true, &pParams->cbPacket, NETPERF_MIN_PKT_SIZE, NETPERF_MAX_PKT_SIZE, "packet size" },
+ { false, true, &pParams->cMsWarmup, NETPERF_MIN_WARMUP, NETPERF_MAX_WARMUP, "warmup period" },
+ { false, true, &pParams->cMsCoolDown, NETPERF_MIN_COOL_DOWN, NETPERF_MAX_COOL_DOWN, "cool down period" },
+ { true, true, &pParams->fNoDelay, false, true, "no delay" },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(aElements); i++)
+ {
+ if (!pszCur)
+ return aElements[i].fMandatory
+ ? RTTestIFailedRc(VERR_PARSE_ERROR, "client params: missing %s\n", aElements[i].pszName)
+ : VINF_SUCCESS;
+
+ pszNext = strchr(pszCur, ':');
+ if (pszNext)
+ *pszNext++ = '\0';
+ uint32_t u32;
+ int rc = RTStrToUInt32Full(pszCur, 10, &u32);
+ if (rc != VINF_SUCCESS)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: bad %s value \"%s\": %Rrc\n",
+ aElements[i].pszName, pszCur, rc);
+
+ if ( u32 < aElements[i].uMin
+ || u32 > aElements[i].uMax)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: %s %u s is out of range (%u..%u)\n",
+ aElements[i].pszName, u32, aElements[i].uMin, aElements[i].uMax);
+ if (aElements[i].fBool)
+ *(bool *)aElements[i].pvValue = u32 ? true : false;
+ else
+ *(uint32_t *)aElements[i].pvValue = u32;
+
+ pszCur = pszNext;
+ }
+
+ /* Fail if too many elements. */
+ if (pszCur)
+ return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: too many elements: \"%s\"\n",
+ pszCur);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * TCP server callback that handles one client connection.
+ *
+ * @returns IPRT status code. VERR_TCP_SERVER_STOP is special.
+ * @param hSocket The client socket.
+ * @param pvUser Our parameters.
+ */
+static DECLCALLBACK(int) netperfTCPServerWorker(RTSOCKET hSocket, void *pvUser)
+{
+ NETPERFPARAMS *pParams = (NETPERFPARAMS *)pvUser;
+ AssertReturn(pParams, VERR_INVALID_POINTER);
+
+ pParams->hSocket = hSocket;
+
+ RTNETADDR Addr;
+ int rc = RTTcpGetPeerAddress(hSocket, &Addr);
+ if (RT_SUCCESS(rc))
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Client connected from %RTnaddr\n", &Addr);
+ else
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Failed to get client details: %Rrc\n", rc);
+ Addr.enmType = RTNETADDRTYPE_INVALID;
+ }
+
+ /*
+ * Adjust send and receive buffer sizes if necessary.
+ */
+ if (pParams->cbBufferSize)
+ {
+ rc = RTTcpSetBufferSize(hSocket, pParams->cbBufferSize);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to set socket buffer sizes to %#x: %Rrc\n", pParams->cbBufferSize, rc);
+ }
+
+ /*
+ * Greet the other dude.
+ */
+ rc = RTTcpWrite(hSocket, g_ConnectStart, sizeof(g_ConnectStart) - 1);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to send connection start Id: %Rrc\n", rc);
+
+ /*
+ * Read connection parameters.
+ */
+ char szBuf[256];
+ rc = RTTcpRead(hSocket, szBuf, NETPERF_LEN_PREFIX, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to read connection parameters: %Rrc\n", rc);
+ szBuf[NETPERF_LEN_PREFIX] = '\0';
+ uint32_t cchParams;
+ rc = RTStrToUInt32Full(szBuf, 10, &cchParams);
+ if (rc != VINF_SUCCESS)
+ return RTTestIFailedRc(RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR : rc,
+ "Failed to read connection parameters: %Rrc\n", rc);
+ if (cchParams >= sizeof(szBuf))
+ return RTTestIFailedRc(VERR_TOO_MUCH_DATA, "parameter packet is too big (%u bytes)\n", cchParams);
+ rc = RTTcpRead(hSocket, szBuf, cchParams, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to read connection parameters: %Rrc\n", rc);
+ szBuf[cchParams] = '\0';
+
+ if (strncmp(szBuf, g_szStartParams, sizeof(g_szStartParams) - 1))
+ return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Invalid connection parameters '%s'\n", szBuf);
+
+ /*
+ * Parse the parameters and signal whether we've got a deal or not.
+ */
+ rc = netperfTCPServerParseParams(pParams, szBuf);
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = RTTcpWrite(hSocket, g_szNegative, sizeof(g_szNegative) - 1);
+ if (RT_FAILURE(rc2))
+ RTTestIFailed("Failed to send negative ack: %Rrc\n", rc2);
+ return rc;
+ }
+
+ if (Addr.enmType != RTNETADDRTYPE_INVALID)
+ RTTestISubF("%RTnaddr - %s, %u s, %u bytes", &Addr,
+ netperfModeToString(pParams->enmMode), pParams->cSecTimeout, pParams->cbPacket);
+ else
+ RTTestISubF("Unknown - %s, %u s, %u bytes",
+ netperfModeToString(pParams->enmMode), pParams->cSecTimeout, pParams->cbPacket);
+
+ rc = RTTcpSetSendCoalescing(hSocket, !pParams->fNoDelay);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to apply no-delay option (%RTbool): %Rrc\n", pParams->fNoDelay, rc);
+
+ rc = RTTcpWrite(hSocket, g_szAck, sizeof(g_szAck) - 1);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to send start test commend to client: %Rrc\n", rc);
+
+ /*
+ * Take action according to our mode.
+ */
+ switch (pParams->enmMode)
+ {
+ case NETPERFMODE_LATENCY:
+ rc = netperfTCPServerDoLatency(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT:
+ rc = netperfTCPServerDoThroughput(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT_XMIT:
+ rc = netperfTCPServerDoThroughputXmit(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT_RECV:
+ rc = netperfTCPServerDoThroughputRecv(pParams);
+ break;
+
+ case NETPERFMODE_INVALID:
+ rc = VERR_INTERNAL_ERROR;
+ break;
+
+ /* no default! */
+ }
+ if (rc == VERR_NO_MEMORY)
+ return VERR_TCP_SERVER_STOP;
+
+ /*
+ * Wait for other clients or quit.
+ */
+ if (pParams->fSingleClient)
+ return VERR_TCP_SERVER_STOP;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * TCP server.
+ *
+ * @returns IPRT status code.
+ * @param pParams The TCP parameter block.
+ */
+static int netperfTCPServer(NETPERFPARAMS *pParams)
+{
+ /*
+ * Spawn the TCP server thread & listen.
+ */
+ PRTTCPSERVER pServer;
+ int rc = RTTcpServerCreateEx(NULL, pParams->uPort, &pServer);
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("Server listening on TCP port %d\n", pParams->uPort);
+ rc = RTTcpServerListen(pServer, netperfTCPServerWorker, pParams);
+ RTTcpServerDestroy(pServer);
+ }
+ else
+ RTPrintf("Failed to create TCP server thread: %Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * The server part.
+ *
+ * @returns Exit code.
+ * @param enmProto The protocol.
+ * @param pParams The parameter block.
+ */
+static RTEXITCODE netperfServer(NETPERFPROTO enmProto, NETPERFPARAMS *pParams)
+{
+
+ switch (enmProto)
+ {
+ case NETPERFPROTO_TCP:
+ {
+ int rc = netperfTCPServer(pParams);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+
+ default:
+ RTTestIFailed("Protocol not supported.\n");
+ return RTEXITCODE_FAILURE;
+ }
+}
+
+
+
+
+
+/**
+ * TCP client: Do the throughput test.
+ *
+ * @returns IPRT status code
+ * @param pParams The parameters.
+ */
+static int netperfTCPClientDoThroughput(NETPERFPARAMS *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Send first, then Receive.
+ */
+ NETPERFSTATS SendStats;
+ int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS SrvSendStats;
+ rc = netperfRecvStats(&SrvSendStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS RecvStats;
+ rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS SrvRecvStats;
+ rc = netperfRecvStats(&SrvRecvStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ {
+ if (pParams->fServerStats)
+ netperfPrintThroughputStats(&SrvSendStats, &SrvRecvStats, pParams->cbPacket);
+ else
+ netperfPrintThroughputStats(&SendStats, &RecvStats, pParams->cbPacket);
+ }
+ }
+ }
+ }
+
+ RTTestISubDone();
+ return rc;
+}
+
+/**
+ * TCP client: Do the throughput xmit test.
+ *
+ * @returns IPRT status code
+ * @param pParams The parameters.
+ */
+static int netperfTCPClientDoThroughputXmit(NETPERFPARAMS *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Do the job.
+ */
+ NETPERFSTATS SendStats;
+ int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS SrvSendStats;
+ rc = netperfRecvStats(&SrvSendStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ {
+ if (pParams->fServerStats)
+ netperfPrintThroughputStats(&SrvSendStats, NULL, pParams->cbPacket);
+ else
+ netperfPrintThroughputStats(&SendStats, NULL, pParams->cbPacket);
+ }
+ }
+
+ RTTestISubDone();
+ return rc;
+}
+
+/**
+ * TCP client: Do the throughput recv test.
+ *
+ * @returns IPRT status code
+ * @param pParams The parameters.
+ */
+static int netperfTCPClientDoThroughputRecv(NETPERFPARAMS *pParams)
+{
+ /*
+ * Allocate the buffer.
+ */
+ NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!pBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ /*
+ * Do the job.
+ */
+ NETPERFSTATS RecvStats;
+ int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
+ if (RT_SUCCESS(rc))
+ {
+ NETPERFSTATS SrvRecvStats;
+ rc = netperfRecvStats(&SrvRecvStats, pParams->hSocket);
+ if (RT_SUCCESS(rc))
+ {
+ if (pParams->fServerStats)
+ netperfPrintThroughputStats(NULL, &SrvRecvStats, pParams->cbPacket);
+ else
+ netperfPrintThroughputStats(NULL, &RecvStats, pParams->cbPacket);
+ }
+ }
+
+ RTTestISubDone();
+ return rc;
+}
+
+/**
+ * TCP client: Do the latency test.
+ *
+ * @returns IPRT status code
+ * @param pParams The parameters.
+ */
+static int netperfTCPClientDoLatency(NETPERFPARAMS *pParams)
+{
+ /*
+ * Generate a selection of packages before we start, after all we're not
+ * benchmarking the random number generator, are we. :-)
+ */
+ void *pvReadBuf = RTMemAllocZ(pParams->cbPacket);
+ if (!pvReadBuf)
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+
+ size_t i;
+ NETPERFHDR *apPackets[256];
+ for (i = 0; i < RT_ELEMENTS(apPackets); i++)
+ {
+ apPackets[i] = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
+ if (!apPackets[i])
+ {
+ while (i-- > 0)
+ RTMemFree(apPackets[i]);
+ RTMemFree(pvReadBuf);
+ return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
+ }
+ RTRandBytes(apPackets[i], pParams->cbPacket);
+ apPackets[i]->u32Magic = RT_H2LE_U32_C(NETPERFHDR_MAGIC);
+ apPackets[i]->u32State = 0;
+ apPackets[i]->u32Seq = 0;
+ apPackets[i]->u32Reserved = 0;
+ }
+
+ /*
+ * Create & start a timer to eventually disconnect.
+ */
+ bool volatile fStop = false;
+ RTTIMERLR hTimer;
+ int rc = RTTimerLRCreateEx(&hTimer, 0 /* nsec */, RTTIMER_FLAGS_CPU_ANY, netperfStopTimerCallback, (void *)&fStop);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Seq = 0;
+ NETPERFSTATS Stats;
+ RT_ZERO(Stats);
+
+ /*
+ * Warm up.
+ */
+ if (g_uVerbosity > 0)
+ RTPrintf("Warmup...\n");
+ rc = RTTimerLRStart(hTimer, pParams->cMsWarmup * UINT64_C(1000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ while (!fStop)
+ {
+ NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
+ u32Seq++;
+ pPacket->u32Seq = RT_H2LE_U32(u32Seq);
+ pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
+ rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
+ break;
+ }
+ rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpRead/warmup: %Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/warmup: %Rrc\n", rc);
+
+ /*
+ * The real thing.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTPrintf("The real thing...\n");
+ fStop = false;
+ rc = RTTimerLRStart(hTimer, pParams->cSecTimeout * UINT64_C(1000000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t u64StartTS = RTTimeNanoTS();
+ while (!fStop)
+ {
+ NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
+ u32Seq++;
+ pPacket->u32Seq = RT_H2LE_U32(u32Seq);
+ pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_TESTING);
+ rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/testing: %Rrc\n", rc);
+ break;
+ }
+ Stats.cTx++;
+
+ rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpRead/testing: %Rrc\n", rc);
+ break;
+ }
+ Stats.cRx++;
+
+ if (!memcmp(pvReadBuf, pPacket, pParams->cbPacket))
+ Stats.cEchos++;
+ else
+ Stats.cErrors++;
+ }
+ Stats.cNsElapsed = RTTimeNanoTS() - u64StartTS;
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
+ }
+
+ /*
+ * Cool down.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (g_uVerbosity > 0)
+ RTPrintf("Cool down...\n");
+ fStop = false;
+ rc = RTTimerLRStart(hTimer, pParams->cMsCoolDown * UINT64_C(1000000) /* nsec */);
+ if (RT_SUCCESS(rc))
+ {
+ while (!fStop)
+ {
+ NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
+ u32Seq++;
+ pPacket->u32Seq = RT_H2LE_U32(u32Seq);
+ pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN);
+ rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
+ break;
+ }
+ rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("RTTcpRead/warmup: %Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ else
+ RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
+ }
+
+ /*
+ * Send DONE packet.
+ */
+ if (g_uVerbosity > 0)
+ RTPrintf("Done\n");
+ if (RT_SUCCESS(rc))
+ {
+ u32Seq++;
+ NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
+ pPacket->u32Seq = RT_H2LE_U32(u32Seq);
+ pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_DONE);
+ rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
+ if (RT_FAILURE(rc))
+ RTTestIFailed("RTTcpWrite/done: %Rrc\n", rc);
+ }
+
+
+ /*
+ * Get and print stats.
+ */
+ NETPERFSTATS SrvStats;
+ if (RT_SUCCESS(rc))
+ {
+ rc = netperfRecvStats(&SrvStats, pParams->hSocket);
+ if (RT_SUCCESS(rc) && pParams->fServerStats)
+ netperfPrintLatencyStats(&SrvStats, pParams->cbPacket);
+ else if (!pParams->fServerStats)
+ netperfPrintLatencyStats(&Stats, pParams->cbPacket);
+ }
+
+ /* clean up*/
+ RTTimerLRDestroy(hTimer);
+ }
+ else
+ RTTestIFailed("Failed to create timer object: %Rrc\n", rc);
+ for (i = 0; i < RT_ELEMENTS(apPackets); i++)
+ RTMemFree(apPackets[i]);
+
+ RTMemFree(pvReadBuf);
+
+ return rc;
+}
+
+/**
+ * TCP client test driver.
+ *
+ * @returns IPRT status code
+ * @param pszServer The server name.
+ * @param pParams The parameter structure.
+ */
+static int netperfTCPClient(const char *pszServer, NETPERFPARAMS *pParams)
+{
+ AssertReturn(pParams, VERR_INVALID_POINTER);
+ RTTestISubF("TCP - %u s, %u bytes%s", pParams->cSecTimeout,
+ pParams->cbPacket, pParams->fNoDelay ? ", no delay" : "");
+
+ RTSOCKET hSocket = NIL_RTSOCKET;
+ int rc = RTTcpClientConnect(pszServer, pParams->uPort, &hSocket);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to connect to %s on port %u: %Rrc\n", pszServer, pParams->uPort, rc);
+ pParams->hSocket = hSocket;
+
+ /*
+ * Disable send coalescing (no-delay).
+ */
+ if (pParams->fNoDelay)
+ {
+ rc = RTTcpSetSendCoalescing(hSocket, false /*fEnable*/);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to set no-delay option: %Rrc\n", rc);
+ }
+
+ /*
+ * Adjust send and receive buffer sizes if necessary.
+ */
+ if (pParams->cbBufferSize)
+ {
+ rc = RTTcpSetBufferSize(hSocket, pParams->cbBufferSize);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to set socket buffer sizes to %#x: %Rrc\n", pParams->cbBufferSize, rc);
+ }
+
+ /*
+ * Verify the super secret Start Connect Id to start the connection.
+ */
+ char szBuf[256 + NETPERF_LEN_PREFIX];
+ RT_ZERO(szBuf);
+ rc = RTTcpRead(hSocket, szBuf, sizeof(g_ConnectStart) - 1, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to read connection initializer: %Rrc\n", rc);
+
+ if (strcmp(szBuf, g_ConnectStart))
+ return RTTestIFailedRc(VERR_INVALID_MAGIC, "Invalid connection initializer '%s'\n", szBuf);
+
+ /*
+ * Send all the dynamic parameters to the server.
+ * (If the server is newer than the client, it will select default for any
+ * missing parameters.)
+ */
+ size_t cchParams = RTStrPrintf(&szBuf[NETPERF_LEN_PREFIX], sizeof(szBuf) - NETPERF_LEN_PREFIX,
+ "%s:%s:%s:%u:%u:%u:%u:%u",
+ g_szStartParams,
+ "TCP",
+ netperfModeToString(pParams->enmMode),
+ pParams->cSecTimeout,
+ pParams->cbPacket,
+ pParams->cMsWarmup,
+ pParams->cMsCoolDown,
+ pParams->fNoDelay);
+ RTStrPrintf(szBuf, NETPERF_LEN_PREFIX + 1, "%0*u", NETPERF_LEN_PREFIX, cchParams);
+ szBuf[NETPERF_LEN_PREFIX] = g_szStartParams[0];
+ Assert(strlen(szBuf) == NETPERF_LEN_PREFIX + cchParams);
+ rc = RTTcpWrite(hSocket, szBuf, NETPERF_LEN_PREFIX + cchParams);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to send connection parameters: %Rrc\n", rc);
+
+ /*
+ * Wait for acknowledgment.
+ */
+ rc = RTTcpRead(hSocket, szBuf, sizeof(g_szAck) - 1, NULL);
+ if (RT_FAILURE(rc))
+ return RTTestIFailedRc(rc, "Failed to send parameters: %Rrc\n", rc);
+ szBuf[sizeof(g_szAck) - 1] = '\0';
+
+ if (!strcmp(szBuf, g_szNegative))
+ return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Server failed to accept packet size of %u bytes.\n", pParams->cbPacket);
+ if (strcmp(szBuf, g_szAck))
+ return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Invalid response from server '%s'\n", szBuf);
+
+ /*
+ * Take action according to our mode.
+ */
+ switch (pParams->enmMode)
+ {
+ case NETPERFMODE_LATENCY:
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the latency test for %u seconds.\n",
+ pszServer, pParams->uPort, pParams->cSecTimeout);
+ rc = netperfTCPClientDoLatency(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT:
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput test for %u seconds in each direction.\n",
+ pszServer, pParams->uPort, pParams->cSecTimeout);
+ rc = netperfTCPClientDoThroughput(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT_XMIT:
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput-xmit test for %u seconds.\n",
+ pszServer, pParams->uPort, pParams->cSecTimeout);
+ rc = netperfTCPClientDoThroughputXmit(pParams);
+ break;
+
+ case NETPERFMODE_THROUGHPUT_RECV:
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput-recv test for %u seconds.\n",
+ pszServer, pParams->uPort, pParams->cSecTimeout);
+ rc = netperfTCPClientDoThroughputRecv(pParams);
+ break;
+
+ case NETPERFMODE_INVALID:
+ rc = VERR_INTERNAL_ERROR;
+ break;
+
+ /* no default! */
+ }
+ return rc;
+}
+
+/**
+ * The client part.
+ *
+ * @returns Exit code.
+ * @param enmProto The protocol.
+ * @param pszServer The server name.
+ * @param pvUser The parameter block as opaque user data.
+ */
+static RTEXITCODE netperfClient(NETPERFPROTO enmProto, const char *pszServer, void *pvUser)
+{
+ switch (enmProto)
+ {
+ case NETPERFPROTO_TCP:
+ {
+ NETPERFPARAMS *pParams = (NETPERFPARAMS *)pvUser;
+ int rc = netperfTCPClient(pszServer, pParams);
+ if (pParams->hSocket != NIL_RTSOCKET)
+ {
+ RTTcpClientClose(pParams->hSocket);
+ pParams->hSocket = NIL_RTSOCKET;
+ }
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ }
+
+ default:
+ RTTestIFailed("Protocol not supported.\n");
+ return RTEXITCODE_FAILURE;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT and globals.
+ */
+ int rc = RTTestInitAndCreate("NetPerf", &g_hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Special case.
+ */
+ if (argc < 2)
+ {
+ RTTestFailed(g_hTest, "No arguments given.");
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+
+ /*
+ * Default values.
+ */
+ NETPERFPROTO enmProtocol = NETPERFPROTO_TCP;
+ bool fServer = true;
+ bool fDaemonize = false;
+ bool fDaemonized = false;
+ bool fPacketSizeSet = false;
+ const char *pszServerAddress= NULL;
+
+ NETPERFPARAMS Params;
+ Params.uPort = NETPERF_DEFAULT_PORT;
+ Params.fServerStats = false;
+ Params.fSingleClient = false;
+
+ Params.fNoDelay = false;
+ Params.fCheckData = false;
+ Params.enmMode = NETPERFMODE_LATENCY;
+ Params.cSecTimeout = NETPERF_DEFAULT_TIMEOUT;
+ Params.cMsWarmup = NETPERF_DEFAULT_WARMUP;
+ Params.cMsCoolDown = NETPERF_DEFAULT_COOL_DOWN;
+ Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
+ Params.cbBufferSize = 0;
+
+ Params.hSocket = NIL_RTSOCKET;
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (rc)
+ {
+ case 's':
+ fServer = true;
+ break;
+
+ case 'c':
+ fServer = false;
+ pszServerAddress = ValueUnion.psz;
+ break;
+
+ case 'd':
+ fDaemonize = true;
+ break;
+
+ case 'D':
+ fDaemonized = true;
+ break;
+
+ case 'i':
+ Params.cSecTimeout = ValueUnion.u32;
+ if ( Params.cSecTimeout < NETPERF_MIN_TIMEOUT
+ || Params.cSecTimeout > NETPERF_MAX_TIMEOUT)
+ {
+ RTTestFailed(g_hTest, "Invalid interval %u s, valid range: %u-%u\n",
+ Params.cbPacket, NETPERF_MIN_TIMEOUT, NETPERF_MAX_TIMEOUT);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case 'l':
+ Params.cbPacket = ValueUnion.u32;
+ if ( Params.cbPacket < NETPERF_MIN_PKT_SIZE
+ || Params.cbPacket > NETPERF_MAX_PKT_SIZE)
+ {
+ RTTestFailed(g_hTest, "Invalid packet size %u bytes, valid range: %u-%u\n",
+ Params.cbPacket, NETPERF_MIN_PKT_SIZE, NETPERF_MAX_PKT_SIZE);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ fPacketSizeSet = true;
+ break;
+
+ case 'm':
+ Params.enmMode = netperfModeFromString(ValueUnion.psz);
+ if (Params.enmMode == NETPERFMODE_INVALID)
+ {
+ RTTestFailed(g_hTest, "Invalid test mode: \"%s\"\n", ValueUnion.psz);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ if (!fPacketSizeSet)
+ switch (Params.enmMode)
+ {
+ case NETPERFMODE_LATENCY:
+ Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
+ break;
+ case NETPERFMODE_THROUGHPUT:
+ case NETPERFMODE_THROUGHPUT_XMIT:
+ case NETPERFMODE_THROUGHPUT_RECV:
+ Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT;
+ break;
+ case NETPERFMODE_INVALID:
+ break;
+ /* no default! */
+ }
+ break;
+
+ case 'p':
+ Params.uPort = ValueUnion.u32;
+ break;
+
+ case 'N':
+ Params.fNoDelay = true;
+ break;
+
+ case 'S':
+ Params.fServerStats = true;
+ break;
+
+ case '1':
+ Params.fSingleClient = true;
+ break;
+
+ case 'v':
+ g_uVerbosity++;
+ break;
+
+ case 'h':
+ Usage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return RTEXITCODE_SUCCESS;
+
+ case 'w':
+ Params.cMsWarmup = ValueUnion.u32;
+ if ( Params.cMsWarmup < NETPERF_MIN_WARMUP
+ || Params.cMsWarmup > NETPERF_MAX_WARMUP)
+ {
+ RTTestFailed(g_hTest, "invalid warmup time %u ms, valid range: %u-%u\n",
+ Params.cMsWarmup, NETPERF_MIN_WARMUP, NETPERF_MAX_WARMUP);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case 'W':
+ Params.cMsCoolDown = ValueUnion.u32;
+ if ( Params.cMsCoolDown < NETPERF_MIN_COOL_DOWN
+ || Params.cMsCoolDown > NETPERF_MAX_COOL_DOWN)
+ {
+ RTTestFailed(g_hTest, "invalid cool down time %u ms, valid range: %u-%u\n",
+ Params.cMsCoolDown, NETPERF_MIN_COOL_DOWN, NETPERF_MAX_COOL_DOWN);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ case 'C':
+ Params.fCheckData = true;
+ break;
+
+ case 'b':
+ Params.cbBufferSize = ValueUnion.u32;
+ if ( ( Params.cbBufferSize < NETPERF_MIN_BUF_SIZE
+ || Params.cbBufferSize > NETPERF_MAX_BUF_SIZE)
+ && Params.cbBufferSize != 0)
+ {
+ RTTestFailed(g_hTest, "Invalid packet size %u bytes, valid range: %u-%u or 0\n",
+ Params.cbBufferSize, NETPERF_MIN_BUF_SIZE, NETPERF_MAX_BUF_SIZE);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ break;
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Handle the server process daemoniziation.
+ */
+ if (fDaemonize && !fDaemonized && fServer)
+ {
+ rc = RTProcDaemonize(argv, "--daemonized");
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize failed: %Rrc\n", rc);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ /*
+ * Get down to business.
+ */
+ RTTestBanner(g_hTest);
+ if (fServer)
+ rc = netperfServer(enmProtocol, &Params);
+ else if (pszServerAddress)
+ rc = netperfClient(enmProtocol, pszServerAddress, &Params);
+ else
+ RTTestFailed(g_hTest, "missing server address to connect to\n");
+
+ RTEXITCODE rc2 = RTTestSummaryAndDestroy(g_hTest);
+ return rc2 != RTEXITCODE_FAILURE ? (RTEXITCODE)rc2 : rc;
+}
+
diff --git a/src/VBox/ValidationKit/utils/nt/Makefile.kmk b/src/VBox/ValidationKit/utils/nt/Makefile.kmk
new file mode 100644
index 00000000..67ab901a
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/nt/Makefile.kmk
@@ -0,0 +1,63 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Windows NT Specific Utilities.
+#
+
+#
+# Copyright (C) 2010-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Set Clock Frequency Utility.
+#
+PROGRAMS.win += ntSetFreq
+ntSetFreq_TEMPLATE = VBoxValidationKitR3
+ntSetFreq_SOURCES = ntsetfreq.cpp
+ntSetFreq_VBOX_IMPORT_CHECKER.win.x86 = nt350
+
+#
+# Test coherency among NT time sources.
+#
+PROGRAMS.win += ntTimeSources
+ntTimeSources_TEMPLATE = VBoxValidationKitR3
+ntTimeSources_SOURCES = nttimesources.cpp
+
+#
+# Test NtFlushVirtualMemory.
+#
+PROGRAMS.win += ntFlushVirtualMemory
+ntFlushVirtualMemory_TEMPLATE = VBoxValidationKitR3
+ntFlushVirtualMemory_SOURCES = ntFlushVirtualMemory.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/nt/ntFlushVirtualMemory.cpp b/src/VBox/ValidationKit/utils/nt/ntFlushVirtualMemory.cpp
new file mode 100644
index 00000000..cb739498
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/nt/ntFlushVirtualMemory.cpp
@@ -0,0 +1,498 @@
+/* $Id: ntFlushVirtualMemory.cpp $ */
+/** @file
+ * Memory mapped files testcase - NT.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt.h>
+
+#include <iprt/alloca.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/err.h>
+#include <iprt/x86.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Create page signature. */
+#define MAKE_PAGE_SIGNATURE(a_iPage) ((a_iPage) | UINT32_C(0x42000000) )
+
+/** Number history entries on the page. */
+#define NUM_ROUND_HISTORY 16
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** How chatty we should be. */
+static uint32_t g_cVerbosity = 0;
+
+
+/**
+ * Checks if the on-disk file matches our expectations.
+ *
+ * @returns IPRT status code, fully bitched.
+ * @param pszFilename The name of the file.
+ * @param pu32BufChk Buffer to read the file into
+ * @param pu32BufOrg Expected file content.
+ * @param cbBuf The buffer size.
+ * @param iRound The update round.
+ */
+static int CheckFile(const char *pszFilename, uint32_t *pu32BufChk, uint32_t const *pu32BufOrg, size_t cbBuf, uint32_t iRound)
+{
+ /*
+ * Open and read the file into memory.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFilename,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/,
+ &Ios, pu32BufChk, (ULONG)cbBuf, NULL /*poffFile*/, NULL /*pvKey*/);
+ if (NT_SUCCESS(rcNt) || Ios.Information != cbBuf)
+ {
+ /*
+ * See if the content of the file matches our expectations.
+ */
+ if (memcmp(pu32BufChk, pu32BufOrg, cbBuf) == 0)
+ { /* matches - likely */ }
+ else
+ {
+ RTMsgError("Round %u: Buffer mismatch!\n", iRound);
+
+ /* Try figure where the differences are. */
+ size_t const cPages = cbBuf / X86_PAGE_SIZE;
+ size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32BufOrg[0]);
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ for (uint32_t iItem = 0; iItem < cItemsPerPage; iItem++)
+ {
+ uint32_t uValue = pu32BufChk[iPage * cItemsPerPage + iItem];
+ uint32_t uExpected = pu32BufOrg[iPage * cItemsPerPage + iItem];
+ if (uValue != uExpected)
+ RTMsgError("Round %u: page #%u, index #%u: %#x, expected %#x\n",
+ iRound, iPage, iItem, uValue, uExpected);
+ }
+
+ rc = VERR_MISMATCH;
+ }
+ }
+ else if (NT_SUCCESS(rcNt))
+ {
+ RTMsgError("Round %u: NtReadFile return %zu bytes intead of %zu!\n", iRound, Ios.Information, cbBuf);
+ rc = VERR_READ_ERROR;
+ }
+ else
+ {
+ RTMsgError("Round %u: NtReadFile(%#x) failed: %#x (%#x)\n", iRound, cbBuf, rcNt, Ios.Status);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+
+ /*
+ * Close the file and return.
+ */
+ rcNt = NtClose(hFile);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMsgError("Round %u: NtCloseFile() failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ }
+ else
+ RTMsgError("Round %u: RTNtPathOpen() failed: %Rrc\n", iRound, rc);
+ return rc;
+}
+
+
+/**
+ * Manually checks whether the buffer matches up to our expectations.
+ *
+ * @returns IPRT status code, fully bitched.
+ * @param pu32Buf The buffer/mapping to check.
+ * @param cbBuf The buffer size.
+ * @param iRound The update round.
+ * @param cFlushesLeft Number of flushes left in the round.
+ */
+static int CheckBuffer(uint32_t const *pu32Buf, size_t cbBuf, uint32_t iRound, uint32_t cFlushesLeft)
+{
+ size_t const cPages = cbBuf / X86_PAGE_SIZE;
+ size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32Buf[0]);
+ size_t const offPage = iRound & (NUM_ROUND_HISTORY - 1);
+ uint32_t const uValue = iRound | (cFlushesLeft << 20);
+//RTPrintf("debug: CheckBuffer: %p %u/%u\n", pu32Buf, iRound, cFlushesLeft);
+
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ {
+ uint32_t uActual = pu32Buf[iPage * cItemsPerPage + offPage];
+ if (uActual != uValue)
+ {
+ RTMsgError("Round %u/%u: page #%u: last entry is corrupted: %#x, expected %#x\n",
+ iRound, cFlushesLeft, iPage, uActual, uValue);
+ return VERR_MISMATCH;
+ }
+
+ uActual = pu32Buf[iPage * cItemsPerPage + cItemsPerPage - 1];
+ if (uActual != MAKE_PAGE_SIGNATURE(iPage))
+ {
+ RTMsgError("Round %u/%u: page #%u magic corrupted: %#x, expected %#x\n",
+ iRound, cFlushesLeft, iPage, uActual, MAKE_PAGE_SIGNATURE(iPage));
+ return VERR_INVALID_MAGIC;
+ }
+ }
+
+ /*
+ * Check previous rounds.
+ */
+ for (uint32_t cRoundsAgo = 1; cRoundsAgo < NUM_ROUND_HISTORY - 1 && cRoundsAgo <= iRound; cRoundsAgo++)
+ {
+ uint32_t iOldRound = iRound - cRoundsAgo;
+ size_t const offOldPage = iOldRound & (NUM_ROUND_HISTORY - 1);
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ {
+ uint32_t uActual = pu32Buf[iPage * cItemsPerPage + offOldPage];
+ if (uActual != iOldRound)
+ {
+ RTMsgError("Round %u/%u: page #%u: entry from %u rounds ago is corrupted: %#x, expected %#x\n",
+ iRound, cFlushesLeft, iPage, cRoundsAgo, uActual, uValue);
+ return VERR_MISMATCH;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Updates the buffer.
+ *
+ * @param pu32Buf The buffer/mapping to update.
+ * @param cbBuf The buffer size.
+ * @param iRound The update round.
+ * @param cFlushesLeft Number of flushes left in this round.
+ */
+static void UpdateBuffer(uint32_t *pu32Buf, size_t cbBuf, uint32_t iRound, uint32_t cFlushesLeft)
+{
+ size_t const cPages = cbBuf / X86_PAGE_SIZE;
+ size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(pu32Buf[0]);
+ size_t const offPage = iRound & (NUM_ROUND_HISTORY - 1);
+ uint32_t const uValue = iRound | (cFlushesLeft << 20);
+//RTPrintf("debug: UpdateBuffer: %p %u/%u\n", pu32Buf, iRound, cFlushesLeft);
+
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ pu32Buf[iPage * cItemsPerPage + offPage] = uValue;
+}
+
+
+
+/**
+ * Modifies the file via memory mapping.
+ *
+ * @returns IPRT status code, fully bitched.
+ * @param pszFilename The file we're using as a test bed.
+ * @param pu32BufOrg The sane copy of the file that gets updated in
+ * parallel.
+ * @param cbBuf The size of the file and bufer.
+ * @param iRound The current round number.
+ * @param fCheckFirst Whether to read from the mapping the mapping
+ * before dirtying it the first time around.
+ * @param fCheckAfterFlush Whether to read from the mapping the mapping
+ * before dirtying it after a flush.
+ * @param cFlushes How many times we modify the mapping and flush
+ * it before one final modification and unmapping.
+ * @param fLargePages Whether to use large pages.
+ */
+static int MakeModifications(const char *pszFilename, uint32_t *pu32BufOrg, size_t cbBuf, uint32_t iRound,
+ bool fCheckFirst, bool fCheckAfterFlush, uint32_t cFlushes, bool fLargePages)
+{
+
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ int rc = RTNtPathOpen(pszFilename,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSection;
+ NTSTATUS rcNt = NtCreateSection(&hSection,
+ SECTION_ALL_ACCESS,
+ NULL, /*pObjAttrs*/
+ NULL, /*pcbMax*/
+ PAGE_READWRITE,
+ SEC_COMMIT,
+ hFile);
+ NtClose(hFile);
+ if (NT_SUCCESS(rcNt))
+ {
+ PVOID pvMapping = NULL;
+ SIZE_T cbMapping = 0;
+ rcNt = NtMapViewOfSection(hSection, NtCurrentProcess(),
+ &pvMapping,
+ 0, /* ZeroBits */
+ 0, /* CommitSize */
+ NULL, /* SectionOffset */
+ &cbMapping,
+ ViewUnmap,
+ fLargePages ? MEM_LARGE_PAGES : 0,
+ PAGE_READWRITE);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Make the modifications.
+ */
+ if (g_cVerbosity >= 2)
+ RTPrintf("debug: pvMapping=%p LB %#x\n", pvMapping, cbBuf);
+
+ for (uint32_t iInner = 0;; iInner++)
+ {
+ if (iInner ? fCheckAfterFlush : fCheckFirst)
+ {
+ if (iInner == 0)
+ rc = CheckBuffer((uint32_t *)pvMapping, cbBuf, iRound - 1, 0);
+ else
+ rc = CheckBuffer((uint32_t *)pvMapping, cbBuf, iRound, cFlushes - iInner + 1);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Round %u/%u: NtUnmapViewOfSection failed: %#x\n", iRound, rcNt);
+ break;
+ }
+ }
+
+ UpdateBuffer((uint32_t *)pvMapping, cbBuf, iRound, cFlushes - iInner);
+ UpdateBuffer(pu32BufOrg, cbBuf, iRound, cFlushes - iInner);
+
+ if (iInner >= cFlushes)
+ break;
+
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ SIZE_T cbBuf2 = cbBuf;
+ PVOID pvMapping2 = pvMapping;
+ rcNt = NtFlushVirtualMemory(NtCurrentProcess(), &pvMapping2, &cbBuf2, &Ios);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMsgError("Round %u: NtFlushVirtualMemory failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ break;
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ rcNt = NtUnmapViewOfSection(NtCurrentProcess(), pvMapping);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMsgError("Round %u: NtUnmapViewOfSection failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ }
+ else
+ {
+ RTMsgError("Round %u: NtMapViewOfSection failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+
+ rcNt = NtClose(hSection);
+ if (!NT_SUCCESS(rcNt))
+ {
+ RTMsgError("Round %u: NtClose(hSection) failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ }
+ else
+ {
+ RTMsgError("Round %u: NtCreateSection failed: %#x\n", iRound, rcNt);
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ }
+ else
+ RTMsgError("Round %u: Error opening file '%s' for memory mapping: %Rrc\n", iRound, pszFilename, rc);
+ return rc;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Init IPRT.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Parse arguments.
+ */
+ const char *pszFilename = NULL;
+ uint32_t cRounds = 4096;
+ uint32_t cPages = 128;
+ bool fLargePages = false;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--rounds", 'r', RTGETOPT_REQ_UINT32 },
+ { "--pages", 'p', RTGETOPT_REQ_UINT32 },
+ { "--filename", 'f', RTGETOPT_REQ_STRING },
+ { "--large-pages", 'l', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGETOPTSTATE State;
+ RTGetOptInit(&State, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ RTGETOPTUNION ValueUnion;
+ int chOpt;
+ while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
+ {
+ switch (chOpt)
+ {
+ case 'r': cRounds = ValueUnion.u32; break;
+ case 'p': cPages = ValueUnion.u32; break;
+ case 'f': pszFilename = ValueUnion.psz; break;
+ case 'l': fLargePages = true; break;
+ case 'q': g_cVerbosity = 0; break;
+ case 'v': g_cVerbosity += 1; break;
+ case 'h':
+ RTPrintf("usage: ntFlushVirtualMemory [-c <times>] [-p <pages>] [-l|--large-pages] [-f <filename>]\n"
+ "\n"
+ "Aims at testing memory mapped files on NT w/ NtFlushVirtualMemory / FlushViewOfFile.\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(chOpt, &ValueUnion);
+ }
+ }
+
+ /*
+ * Allocate buffers and initialize the original with page numbers.
+ *
+ * We keep a original copy that gets updated in parallel to the memory
+ * mapping, allowing for simple file initialization and memcpy checking.
+ *
+ * The second buffer is for reading the file from disk and check.
+ */
+ size_t const cbBuf = cPages * X86_PAGE_SIZE;
+ size_t const cItemsPerPage = X86_PAGE_SIZE / sizeof(uint32_t);
+ uint32_t *pu32BufOrg = (uint32_t *)RTMemPageAllocZ(cbBuf);
+ uint32_t *pu32BufChk = (uint32_t *)RTMemPageAllocZ(cbBuf);
+ if (pu32BufOrg == NULL || pu32BufChk == NULL)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to allocate two %zu sized buffers!\n", cbBuf);
+
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ pu32BufOrg[iPage * cItemsPerPage + cItemsPerPage - 1] = MAKE_PAGE_SIGNATURE(iPage);
+
+ rc = CheckBuffer(pu32BufOrg, cbBuf, 0, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Internal error: CheckBuffer failed on virgin buffer: %Rrc\n", rc);
+
+ /*
+ * Open the file and write out the orignal one.
+ */
+ RTFILE hFile;
+ if (!pszFilename)
+ {
+ char *pszBuf = (char *)alloca(RTPATH_MAX);
+ rc = RTFileOpenTemp(&hFile, pszBuf, RTPATH_MAX, RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary file: %Rrc\n", rc);
+ pszFilename = pszBuf;
+ }
+ else
+ {
+ rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s': %Rrc\n", rc);
+ }
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+
+ rc = RTFileWrite(hFile, pu32BufOrg, cbBuf, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTFileClose(hFile);
+
+ /*
+ * Do the rounds. We count from 1 here to make verifying the previous round simpler.
+ */
+ for (uint32_t iRound = 1; iRound <= cRounds; iRound++)
+ {
+ rc = MakeModifications(pszFilename, pu32BufOrg, cbBuf, iRound,
+ ((iRound >> 5) & 1) == 1, ((iRound >> 5) & 3) == 3, (iRound >> 3) & 31, fLargePages);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CheckBuffer(pu32BufOrg, cbBuf, iRound, 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CheckFile(pszFilename, pu32BufChk, pu32BufOrg, cbBuf, iRound);
+ if (RT_SUCCESS(rc))
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Error writing initial %zu bytes to '%s': %Rrc\n", cbBuf, rc);
+ RTFileClose(hFile);
+ }
+ RTFileDelete(pszFilename);
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/nt/ntsetfreq.cpp b/src/VBox/ValidationKit/utils/nt/ntsetfreq.cpp
new file mode 100644
index 00000000..c2a936dd
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/nt/ntsetfreq.cpp
@@ -0,0 +1,161 @@
+/* $Id: ntsetfreq.cpp $ */
+/** @file
+ * Set the NT timer frequency.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt.h>
+
+#include <iprt/initterm.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Parse arguments.
+ */
+ bool fVerbose = true;
+ uint32_t u32NewRes = 0;
+ uint32_t cSecsSleep = UINT32_MAX;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--resolution", 'r', RTGETOPT_REQ_UINT32 },
+ { "--sleep", 's', RTGETOPT_REQ_UINT32 },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (rc)
+ {
+ case 'r':
+ u32NewRes = ValueUnion.u32;
+ if (u32NewRes > 16*10000 /* 16 ms */ || u32NewRes < 1000 /* 100 microsec */)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "syntax error: the new timer resolution (%RU32) is out of range\n",
+ u32NewRes);
+ break;
+
+ case 's':
+ cSecsSleep = ValueUnion.u32;
+ break;
+
+ case 'q':
+ fVerbose = false;
+ break;
+
+ case 'v':
+ fVerbose = true;
+ break;
+
+ case 'h':
+ RTPrintf("Usage: ntsetfreq [-q|--quiet] [-v|--verbose] [-r|--resolution <100ns>] [-s|--sleep <1s>]\n");
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+
+ /*
+ * Query the current resolution.
+ */
+ ULONG Cur = UINT32_MAX;
+ ULONG Min = UINT32_MAX;
+ ULONG Max = UINT32_MAX;
+ NTSTATUS rcNt = STATUS_SUCCESS;
+ if (fVerbose || !u32NewRes)
+ {
+ rcNt = NtQueryTimerResolution(&Min, &Max, &Cur);
+ if (NT_SUCCESS(rcNt))
+ RTMsgInfo("cur: %u (%u.%02u Hz) min: %u (%u.%02u Hz) max: %u (%u.%02u Hz)\n",
+ Cur, 10000000 / Cur, (10000000 / (Cur * 100)) % 100,
+ Min, 10000000 / Min, (10000000 / (Min * 100)) % 100,
+ Max, 10000000 / Max, (10000000 / (Max * 100)) % 100);
+ else
+ RTMsgError("NTQueryTimerResolution failed with status %#x\n", rcNt);
+ }
+
+ if (u32NewRes)
+ {
+ rcNt = NtSetTimerResolution(u32NewRes, TRUE, &Cur);
+ if (!NT_SUCCESS(rcNt))
+ RTMsgError("NTSetTimerResolution(%RU32,,) failed with status %#x\n", u32NewRes, rcNt);
+ else if (fVerbose)
+ {
+ Cur = Min = Max = UINT32_MAX;
+ rcNt = NtQueryTimerResolution(&Min, &Max, &Cur);
+ if (NT_SUCCESS(rcNt))
+ RTMsgInfo("new: %u (%u.%02u Hz) requested %RU32 (%u.%02u Hz)\n",
+ Cur, 10000000 / Cur, (10000000 / (Cur * 100)) % 100,
+ u32NewRes, 10000000 / u32NewRes, (10000000 / (u32NewRes * 100)) % 100);
+ else
+ RTMsgError("NTSetTimerResolution succeeded but the NTQueryTimerResolution call failed with status %#x (ignored)\n",
+ rcNt);
+ rcNt = STATUS_SUCCESS;
+ }
+ }
+
+ if (u32NewRes && NT_SUCCESS(rcNt))
+ {
+ if (cSecsSleep == UINT32_MAX)
+ for (;;)
+ RTThreadSleep(RT_INDEFINITE_WAIT);
+ else
+ while (cSecsSleep-- > 0)
+ RTThreadSleep(1000);
+ }
+
+ return NT_SUCCESS(rcNt) ? 0 : 1;
+}
+
diff --git a/src/VBox/ValidationKit/utils/nt/nttimesources.cpp b/src/VBox/ValidationKit/utils/nt/nttimesources.cpp
new file mode 100644
index 00000000..dfa86b79
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/nt/nttimesources.cpp
@@ -0,0 +1,246 @@
+/* $Id: nttimesources.cpp $ */
+/** @file
+ * Check the various time sources on Windows NT.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct _MY_KSYSTEM_TIME
+{
+ ULONG LowPart;
+ LONG High1Time;
+ LONG High2Time;
+} MY_KSYSTEM_TIME;
+
+typedef struct _MY_KUSER_SHARED_DATA
+{
+ ULONG TickCountLowDeprecated;
+ ULONG TickCountMultiplier;
+ volatile MY_KSYSTEM_TIME InterruptTime;
+ volatile MY_KSYSTEM_TIME SystemTime;
+ volatile MY_KSYSTEM_TIME TimeZoneBias;
+ /* The rest is not relevant. */
+} MY_KUSER_SHARED_DATA;
+
+/** The fixed pointer to the user shared data. */
+#define MY_USER_SHARED_DATA ((MY_KUSER_SHARED_DATA *)0x7ffe0000)
+
+/** Spins until GetTickCount() changes. */
+static void SpinUntilTick(void)
+{
+ /* spin till GetTickCount changes. */
+ DWORD dwMsTick = GetTickCount();
+ while (GetTickCount() == dwMsTick)
+ /* nothing */;
+}
+
+/** Delay function that tries to return right after GetTickCount changed. */
+static void DelayMillies(DWORD dwMsStart, DWORD cMillies)
+{
+ /* Delay cMillies - 1. */
+ Sleep(cMillies - 1);
+ while (GetTickCount() - dwMsStart < cMillies - 1U)
+ Sleep(1);
+
+ SpinUntilTick();
+}
+
+
+int main(int argc, char **argv)
+{
+ RT_NOREF1(argv);
+
+ /*
+ * Init, create a test instance and "parse" arguments.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("nttimesources", &hTest);
+ if (rc)
+ return rc;
+ if (argc > 1)
+ {
+ RTTestFailed(hTest, "Syntax error! no arguments expected");
+ return RTTestSummaryAndDestroy(hTest);
+ }
+
+ /*
+ * Guess MHz using GetTickCount.
+ */
+ RTTestSub(hTest, "Guess MHz");
+ DWORD dwTickStart, dwTickEnd, cMsTicks;
+ uint64_t u64TscStart, u64TscEnd, cTscTicks;
+
+ /* get a good start time. */
+ SpinUntilTick();
+ do
+ {
+ dwTickStart = GetTickCount();
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+ u64TscStart = ASMReadTSC();
+ ASMCompilerBarrier();
+ } while (GetTickCount() != dwTickStart);
+
+ /* delay a good while. */
+ DelayMillies(dwTickStart, 256);
+
+ /* get a good end time. */
+ do
+ {
+ dwTickEnd = GetTickCount();
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+ u64TscEnd = ASMReadTSC();
+ ASMCompilerBarrier();
+ } while (GetTickCount() != dwTickEnd);
+ cMsTicks = dwTickEnd - dwTickStart;
+ cTscTicks = u64TscEnd - u64TscStart;
+
+ /* Calc an approximate TSC frequency:
+ cTscTicks / uTscHz = cMsTicks / 1000
+ 1 / uTscHz = (cMsTicks / 1000) / cTscTicks
+ uTscHz = cTscTicks / (cMsTicks / 1000) */
+ uint64_t u64TscHz = (long double)cTscTicks / ((long double)cMsTicks / 1000.0);
+ if ( u64TscHz > _1M*3
+ && u64TscHz < _1T)
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "u64TscHz=%'llu", u64TscHz);
+ else
+ {
+ RTTestFailed(hTest, "u64TscHz=%'llu - out of range", u64TscHz);
+ u64TscHz = 0;
+ }
+
+
+ /*
+ * Pit GetTickCount, InterruptTime, Performance Counters and TSC against each other.
+ */
+ LARGE_INTEGER PrfHz;
+ LARGE_INTEGER PrfStart, PrfEnd, cPrfTicks;
+ LARGE_INTEGER IntStart, IntEnd, cIntTicks;
+ for (uint32_t i = 0; i < 7; i++)
+ {
+ RTTestSubF(hTest, "The whole bunch - pass #%u", i + 1);
+
+ if (!QueryPerformanceFrequency(&PrfHz))
+ {
+ RTTestFailed(hTest, "QueryPerformanceFrequency failed (%u)", GetLastError());
+ return RTTestSummaryAndDestroy(hTest);
+ }
+
+ /* get a good start time. */
+ SpinUntilTick();
+ do
+ {
+ IntStart.HighPart = MY_USER_SHARED_DATA->InterruptTime.High1Time;
+ IntStart.LowPart = MY_USER_SHARED_DATA->InterruptTime.LowPart;
+ dwTickStart = GetTickCount();
+ if (!QueryPerformanceCounter(&PrfStart))
+ {
+ RTTestFailed(hTest, "QueryPerformanceCounter failed (%u)", GetLastError());
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+ u64TscStart = ASMReadTSC();
+ ASMCompilerBarrier();
+ } while ( MY_USER_SHARED_DATA->InterruptTime.High2Time != IntStart.HighPart
+ || MY_USER_SHARED_DATA->InterruptTime.LowPart != IntStart.LowPart
+ || GetTickCount() != dwTickStart);
+
+ /* delay a good while. */
+ DelayMillies(dwTickStart, 256);
+
+ /* get a good end time. */
+ do
+ {
+ IntEnd.HighPart = MY_USER_SHARED_DATA->InterruptTime.High1Time;
+ IntEnd.LowPart = MY_USER_SHARED_DATA->InterruptTime.LowPart;
+ dwTickEnd = GetTickCount();
+ if (!QueryPerformanceCounter(&PrfEnd))
+ {
+ RTTestFailed(hTest, "QueryPerformanceCounter failed (%u)", GetLastError());
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ ASMCompilerBarrier();
+ ASMSerializeInstruction();
+ u64TscEnd = ASMReadTSC();
+ ASMCompilerBarrier();
+ } while ( MY_USER_SHARED_DATA->InterruptTime.High2Time != IntEnd.HighPart
+ || MY_USER_SHARED_DATA->InterruptTime.LowPart != IntEnd.LowPart
+ || GetTickCount() != dwTickEnd);
+
+ cMsTicks = dwTickEnd - dwTickStart;
+ cTscTicks = u64TscEnd - u64TscStart;
+ cIntTicks.QuadPart = IntEnd.QuadPart - IntStart.QuadPart;
+ cPrfTicks.QuadPart = PrfEnd.QuadPart - PrfStart.QuadPart;
+
+ /* Recalc to micro seconds. */
+ uint64_t u64MicroSecMs = (uint64_t)cMsTicks * 1000;
+ uint64_t u64MicroSecTsc = u64TscHz ? (long double)cTscTicks / (long double)u64TscHz * 1000000 : u64MicroSecMs;
+ uint64_t u64MicroSecPrf = (long double)cPrfTicks.QuadPart / (long double)PrfHz.QuadPart * 1000000;
+ uint64_t u64MicroSecInt = cIntTicks.QuadPart / 10; /* 100ns units*/
+
+ /* check how much they differ using the millisecond tick count as the standard candle. */
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - GetTickCount\n", u64MicroSecMs, 0);
+
+ int64_t off = u64MicroSecTsc - u64MicroSecMs;
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - TSC\n", u64MicroSecTsc, off);
+ RTTEST_CHECK(hTest, RT_ABS(off) < 50000 /*us*/); /* some extra uncertainty with TSC. */
+
+ off = u64MicroSecInt - u64MicroSecMs;
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - InterruptTime\n", u64MicroSecInt, off);
+ RTTEST_CHECK(hTest, RT_ABS(off) < 25000 /*us*/);
+
+ off = u64MicroSecPrf - u64MicroSecMs;
+ RTTestPrintf(hTest, RTTESTLVL_ALWAYS, " %9llu / %7lld us - QueryPerformanceCounter\n", u64MicroSecPrf, off);
+ RTTEST_CHECK(hTest, RT_ABS(off) < 25000 /*us*/);
+ }
+
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/serial/Makefile.kmk b/src/VBox/ValidationKit/utils/serial/Makefile.kmk
new file mode 100644
index 00000000..57c331fc
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/serial/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Serial port tests.
+#
+
+#
+# Copyright (C) 2017-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Serial port testing utility.
+#
+PROGRAMS += SerialTest
+SerialTest_TEMPLATE = VBoxValidationKitR3
+SerialTest_SOURCES = SerialTest.cpp
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/serial/SerialTest.cpp b/src/VBox/ValidationKit/utils/serial/SerialTest.cpp
new file mode 100644
index 00000000..4e7354c8
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/serial/SerialTest.cpp
@@ -0,0 +1,1115 @@
+/* $Id: SerialTest.cpp $ */
+/** @file
+ * SerialTest - Serial port testing utility.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/rand.h>
+#include <iprt/serialport.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Number of times to toggle the status lines during the test. */
+#define SERIALTEST_STS_LINE_TOGGLE_COUNT 100
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+
+/**
+ * Serial test mode.
+ */
+typedef enum SERIALTESTMODE
+{
+ /** Invalid mode. */
+ SERIALTESTMODE_INVALID = 0,
+ /** Serial port is looped back to itself */
+ SERIALTESTMODE_LOOPBACK,
+ /** A secondary serial port is used with a null modem cable in between. */
+ SERIALTESTMODE_SECONDARY,
+ /** The serial port is connected externally over which we have no control. */
+ SERIALTESTMODE_EXTERNAL,
+ /** Usual 32bit hack. */
+ SERIALTESTMODE_32BIT_HACK = 0x7fffffff
+} SERIALTESTMODE;
+/** Pointer to a serial test mode. */
+typedef SERIALTESTMODE *PSERIALTESTMDOE;
+
+/** Pointer to the serial test data instance. */
+typedef struct SERIALTEST *PSERIALTEST;
+
+/**
+ * Test callback function.
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test instance data.
+ */
+typedef DECLCALLBACKTYPE(int, FNSERIALTESTRUN,(PSERIALTEST pSerialTest));
+/** Pointer to the serial test callback. */
+typedef FNSERIALTESTRUN *PFNSERIALTESTRUN;
+
+
+/**
+ * The serial test instance data.
+ */
+typedef struct SERIALTEST
+{
+ /** The assigned test handle. */
+ RTTEST hTest;
+ /** The assigned serial port. */
+ RTSERIALPORT hSerialPort;
+ /** The currently active config. */
+ PCRTSERIALPORTCFG pSerialCfg;
+} SERIALTEST;
+
+
+/**
+ * Test descriptor.
+ */
+typedef struct SERIALTESTDESC
+{
+ /** Test ID. */
+ const char *pszId;
+ /** Test description. */
+ const char *pszDesc;
+ /** Test run callback. */
+ PFNSERIALTESTRUN pfnRun;
+} SERIALTESTDESC;
+/** Pointer to a test descriptor. */
+typedef SERIALTESTDESC *PSERIALTESTDESC;
+/** Pointer to a constant test descriptor. */
+typedef const SERIALTESTDESC *PCSERIALTESTDESC;
+
+
+/**
+ * TX/RX buffer containing a simple counter.
+ */
+typedef struct SERIALTESTTXRXBUFCNT
+{
+ /** The current counter value. */
+ uint32_t iCnt;
+ /** Number of bytes left to receive/transmit. */
+ size_t cbTxRxLeft;
+ /** The offset into the buffer to receive to/send from. */
+ size_t offBuf;
+ /** Maximum size to send/receive before processing is needed again. */
+ size_t cbTxRxMax;
+ /** The data buffer. */
+ uint8_t abBuf[_1K];
+} SERIALTESTTXRXBUFCNT;
+/** Pointer to a TX/RX buffer. */
+typedef SERIALTESTTXRXBUFCNT *PSERIALTESTTXRXBUFCNT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ {"--device", 'd', RTGETOPT_REQ_STRING },
+ {"--baudrate", 'b', RTGETOPT_REQ_UINT32 },
+ {"--parity", 'p', RTGETOPT_REQ_STRING },
+ {"--databits", 'c', RTGETOPT_REQ_UINT32 },
+ {"--stopbits", 's', RTGETOPT_REQ_STRING },
+ {"--mode", 'm', RTGETOPT_REQ_STRING },
+ {"--secondarydevice", 'l', RTGETOPT_REQ_STRING },
+ {"--tests", 't', RTGETOPT_REQ_STRING },
+ {"--txbytes", 'x', RTGETOPT_REQ_UINT32 },
+ {"--abort-on-error", 'a', RTGETOPT_REQ_NOTHING},
+ {"--verbose", 'v', RTGETOPT_REQ_NOTHING},
+ {"--help", 'h', RTGETOPT_REQ_NOTHING}
+};
+
+
+static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest);
+static DECLCALLBACK(int) serialTestRunWrite(PSERIALTEST pSerialTest);
+static DECLCALLBACK(int) serialTestRunReadVerify(PSERIALTEST pSerialTest);
+static DECLCALLBACK(int) serialTestRunStsLines(PSERIALTEST pSerialTest);
+static DECLCALLBACK(int) serialTestRunEcho(PSERIALTEST pSerialTest);
+
+/** Implemented tests. */
+static const SERIALTESTDESC g_aSerialTests[] =
+{
+ {"readwrite", "Simple Read/Write test on the same serial port", serialTestRunReadWrite },
+ {"write", "Simple write test (verification done somewhere else)", serialTestRunWrite },
+ {"readverify", "Counterpart to write test (reads and verifies data)", serialTestRunReadVerify },
+ {"stslines", "Testing the status line setting and receiving", serialTestRunStsLines },
+ {"echo", "Echoes received data back to the sender (not real test)", serialTestRunEcho },
+};
+
+/** Verbosity value. */
+static unsigned g_cVerbosity = 0;
+/** The test handle. */
+static RTTEST g_hTest = NIL_RTTEST;
+/** The serial test mode. */
+static SERIALTESTMODE g_enmMode = SERIALTESTMODE_LOOPBACK;
+/** Random number generator. */
+static RTRAND g_hRand = NIL_RTRAND;
+/** The serial port handle. */
+static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT;
+/** The loopback serial port handle if configured. */
+static RTSERIALPORT g_hSerialPortSecondary = NIL_RTSERIALPORT;
+/** Number of bytes to transmit for read/write tests. */
+static size_t g_cbTx = _1M;
+/** Flag whether to abort the tool when encountering the first error. */
+static bool g_fAbortOnError = false;
+/** The config used. */
+static RTSERIALPORTCFG g_SerialPortCfg =
+{
+ /* uBaudRate */
+ 115200,
+ /* enmParity */
+ RTSERIALPORTPARITY_NONE,
+ /* enmDataBitCount */
+ RTSERIALPORTDATABITS_8BITS,
+ /* enmStopBitCount */
+ RTSERIALPORTSTOPBITS_ONE
+};
+
+
+/**
+ * RTTestFailed() wrapper which aborts the program if the option is set.
+ */
+static void serialTestFailed(RTTEST hTest, const char *pszFmt, ...)
+{
+ va_list va;
+ va_start(va, pszFmt);
+ RTTestFailedV(hTest, pszFmt, va);
+ va_end(va);
+ if (g_fAbortOnError)
+ RT_BREAKPOINT();
+}
+
+
+/**
+ * Initializes a TX buffer.
+ *
+ * @param pSerBuf The serial buffer to initialize.
+ * @param cbTx Maximum number of bytes to transmit.
+ */
+static void serialTestTxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbTx)
+{
+ pSerBuf->iCnt = 0;
+ pSerBuf->offBuf = 0;
+ pSerBuf->cbTxRxMax = 0;
+ pSerBuf->cbTxRxLeft = cbTx;
+ RT_ZERO(pSerBuf->abBuf);
+}
+
+
+/**
+ * Initializes a RX buffer.
+ *
+ * @param pSerBuf The serial buffer to initialize.
+ * @param cbRx Maximum number of bytes to receive.
+ */
+static void serialTestRxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbRx)
+{
+ pSerBuf->iCnt = 0;
+ pSerBuf->offBuf = 0;
+ pSerBuf->cbTxRxMax = sizeof(pSerBuf->abBuf);
+ pSerBuf->cbTxRxLeft = cbRx;
+ RT_ZERO(pSerBuf->abBuf);
+}
+
+
+/**
+ * Prepares the given TX buffer with data for sending it out.
+ *
+ * @param pSerBuf The TX buffer pointer.
+ */
+static void serialTestTxBufPrepare(PSERIALTESTTXRXBUFCNT pSerBuf)
+{
+ /* Move the data to the front to make room at the end to fill. */
+ if (pSerBuf->offBuf)
+ {
+ memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[pSerBuf->offBuf], sizeof(pSerBuf->abBuf) - pSerBuf->offBuf);
+ pSerBuf->offBuf = 0;
+ }
+
+ /* Fill up with data. */
+ uint32_t offData = 0;
+ while (pSerBuf->cbTxRxMax + sizeof(uint32_t) <= sizeof(pSerBuf->abBuf))
+ {
+ pSerBuf->iCnt++;
+ *(uint32_t *)&pSerBuf->abBuf[pSerBuf->offBuf + offData] = pSerBuf->iCnt;
+ pSerBuf->cbTxRxMax += sizeof(uint32_t);
+ offData += sizeof(uint32_t);
+ }
+}
+
+
+/**
+ * Sends a new batch of data from the TX buffer preapring new data if required.
+ *
+ * @returns IPRT status code.
+ * @param hSerialPort The serial port handle to send the data to.
+ * @param pSerBuf The TX buffer pointer.
+ */
+static int serialTestTxBufSend(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pSerBuf->cbTxRxLeft)
+ {
+ if (!pSerBuf->cbTxRxMax)
+ serialTestTxBufPrepare(pSerBuf);
+
+ size_t cbToWrite = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
+ size_t cbWritten = 0;
+ rc = RTSerialPortWriteNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ pSerBuf->cbTxRxMax -= cbWritten;
+ pSerBuf->offBuf += cbWritten;
+ pSerBuf->cbTxRxLeft -= cbWritten;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Receives dat from the given serial port into the supplied RX buffer and does some validity checking.
+ *
+ * @returns IPRT status code.
+ * @param hSerialPort The serial port handle to receive data from.
+ * @param pSerBuf The RX buffer pointer.
+ */
+static int serialTestRxBufRecv(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pSerBuf->cbTxRxLeft)
+ {
+ size_t cbToRead = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
+ size_t cbRead = 0;
+ rc = RTSerialPortReadNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToRead, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ pSerBuf->offBuf += cbRead;
+ pSerBuf->cbTxRxMax -= cbRead;
+ pSerBuf->cbTxRxLeft -= cbRead;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Verifies the data in the given RX buffer for correct transmission.
+ *
+ * @returns Flag whether verification failed.
+ * @param hTest The test handle to report errors to.
+ * @param pSerBuf The RX buffer pointer.
+ * @param iCntTx The current TX counter value the RX buffer should never get ahead of,
+ * UINT32_MAX disables this check.
+ */
+static bool serialTestRxBufVerify(RTTEST hTest, PSERIALTESTTXRXBUFCNT pSerBuf, uint32_t iCntTx)
+{
+ uint32_t offRx = 0;
+ bool fFailed = false;
+
+ while (offRx + sizeof(uint32_t) < pSerBuf->offBuf)
+ {
+ uint32_t u32Val = *(uint32_t *)&pSerBuf->abBuf[offRx];
+ offRx += sizeof(uint32_t);
+
+ if (RT_UNLIKELY(u32Val != ++pSerBuf->iCnt))
+ {
+ fFailed = true;
+ if (g_cVerbosity > 0)
+ serialTestFailed(hTest, "Data corruption/loss detected, expected counter value %u got %u\n",
+ pSerBuf->iCnt, u32Val);
+ }
+ }
+
+ if (RT_UNLIKELY(pSerBuf->iCnt > iCntTx))
+ {
+ fFailed = true;
+ serialTestFailed(hTest, "Overtook the send buffer, expected maximum counter value %u got %u\n",
+ iCntTx, pSerBuf->iCnt);
+ }
+
+ /* Remove processed data from the buffer and move the rest to the front. */
+ if (offRx)
+ {
+ memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[offRx], sizeof(pSerBuf->abBuf) - offRx);
+ pSerBuf->offBuf -= offRx;
+ pSerBuf->cbTxRxMax += offRx;
+ }
+
+ return fFailed;
+}
+
+
+DECLINLINE(bool) serialTestRndTrue(void)
+{
+ return RTRandAdvU32Ex(g_hRand, 0, 1) == 1;
+}
+
+
+/**
+ * Runs a simple read/write test.
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test configuration.
+ */
+static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest)
+{
+ uint64_t tsStart = RTTimeNanoTS();
+ bool fFailed = false;
+ SERIALTESTTXRXBUFCNT SerBufTx;
+ SERIALTESTTXRXBUFCNT SerBufRx;
+
+ serialTestTxBufInit(&SerBufTx, g_cbTx);
+ serialTestRxBufInit(&SerBufRx, g_cbTx);
+
+ int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
+ while ( RT_SUCCESS(rc)
+ && ( SerBufTx.cbTxRxLeft
+ || SerBufRx.cbTxRxLeft))
+ {
+ uint32_t fEvts = 0;
+ uint32_t fEvtsQuery = 0;
+ if (SerBufTx.cbTxRxLeft)
+ fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
+ if (SerBufRx.cbTxRxLeft)
+ fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
+ {
+ rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
+ if (RT_FAILURE(rc))
+ break;
+
+ bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, SerBufTx.iCnt);
+ if (fRes && !fFailed)
+ {
+ fFailed = true;
+ serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
+ }
+ }
+ if ( RT_SUCCESS(rc)
+ && (fEvts & RTSERIALPORT_EVT_F_DATA_TX))
+ rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
+ }
+
+ uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
+ size_t cNsPerByte = tsRuntime / g_cbTx;
+ uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
+ RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
+
+ return rc;
+}
+
+
+/**
+ * Runs a simple write test without doing any verification.
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test configuration.
+ */
+static DECLCALLBACK(int) serialTestRunWrite(PSERIALTEST pSerialTest)
+{
+ uint64_t tsStart = RTTimeNanoTS();
+ SERIALTESTTXRXBUFCNT SerBufTx;
+
+ serialTestTxBufInit(&SerBufTx, g_cbTx);
+
+ int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
+ while ( RT_SUCCESS(rc)
+ && SerBufTx.cbTxRxLeft)
+ {
+ uint32_t fEvts = 0;
+
+ rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_DATA_TX, &fEvts, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
+ rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
+ }
+
+ uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
+ size_t cNsPerByte = tsRuntime / g_cbTx;
+ uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
+ RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
+
+ return rc;
+}
+
+
+/**
+ * Runs the counterpart to the write test, reading and verifying data.
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test configuration.
+ */
+static DECLCALLBACK(int) serialTestRunReadVerify(PSERIALTEST pSerialTest)
+{
+ int rc = VINF_SUCCESS;
+ uint64_t tsStart = RTTimeNanoTS();
+ bool fFailed = false;
+ SERIALTESTTXRXBUFCNT SerBufRx;
+
+ serialTestRxBufInit(&SerBufRx, g_cbTx);
+
+ while ( RT_SUCCESS(rc)
+ && SerBufRx.cbTxRxLeft)
+ {
+ uint32_t fEvts = 0;
+ uint32_t fEvtsQuery = RTSERIALPORT_EVT_F_DATA_RX;
+
+ rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
+ {
+ rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
+ if (RT_FAILURE(rc))
+ break;
+
+ bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, UINT32_MAX);
+ if (fRes && !fFailed)
+ {
+ fFailed = true;
+ serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
+ }
+ }
+ }
+
+ uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
+ size_t cNsPerByte = tsRuntime / g_cbTx;
+ uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
+ RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
+
+ return rc;
+}
+
+
+/**
+ * Tests setting status lines and getting notified about status line changes.
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test configuration.
+ */
+static DECLCALLBACK(int) serialTestRunStsLines(PSERIALTEST pSerialTest)
+{
+ int rc = VINF_SUCCESS;
+
+ if (g_enmMode == SERIALTESTMODE_LOOPBACK)
+ {
+ uint32_t fStsLinesQueriedOld = 0;
+
+ rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort,
+ RTSERIALPORT_CHG_STS_LINES_F_RTS | RTSERIALPORT_CHG_STS_LINES_F_DTR,
+ 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueriedOld);
+ if (RT_SUCCESS(rc))
+ {
+ /* Everything should be clear at this stage. */
+ if (!fStsLinesQueriedOld)
+ {
+ uint32_t fStsLinesSetOld = 0;
+
+ for (uint32_t i = 0; i < SERIALTEST_STS_LINE_TOGGLE_COUNT; i++)
+ {
+ uint32_t fStsLinesSet = 0;
+ uint32_t fStsLinesClear = 0;
+
+ /* Change RTS? */
+ if (serialTestRndTrue())
+ {
+ /* Clear, if set previously otherwise set it. */
+ if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_RTS)
+ fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
+ else
+ fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
+ }
+
+ /* Change DTR? */
+ if (serialTestRndTrue())
+ {
+ /* Clear, if set previously otherwise set it. */
+ if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
+ else
+ fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
+ }
+
+ rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort, fStsLinesClear, fStsLinesSet);
+ if (RT_FAILURE(rc))
+ {
+ serialTestFailed(g_hTest, "Changing status lines failed with %Rrc on iteration %u (fSet=%#x fClear=%#x)\n",
+ rc, i, fStsLinesSet, fStsLinesClear);
+ break;
+ }
+
+ /* Wait for status line monitor event. */
+ uint32_t fEvtsRecv = 0;
+ rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED,
+ &fEvtsRecv, RT_MS_1SEC);
+ if ( RT_FAILURE(rc)
+ && (rc != VERR_TIMEOUT && !fStsLinesSet && !fStsLinesClear))
+ {
+ serialTestFailed(g_hTest, "Waiting for status line change failed with %Rrc on iteration %u\n",
+ rc, i);
+ break;
+ }
+
+ uint32_t fStsLinesQueried = 0;
+ rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueried);
+ if (RT_FAILURE(rc))
+ {
+ serialTestFailed(g_hTest, "Querying status lines failed with %Rrc on iteration %u\n",
+ rc, i);
+ break;
+ }
+
+ /* Compare expected and real result. */
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
+ != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DSR))
+ {
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
+ && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DSR line got set when it shouldn't be on iteration %u\n", i);
+ else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
+ && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DSR line got cleared when it shouldn't be on iteration %u\n", i);
+ }
+ else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DSR line didn't change when it should have on iteration %u\n", i);
+
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
+ != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DCD))
+ {
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
+ && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DCD line got set when it shouldn't be on iteration %u\n", i);
+ else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
+ && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DCD line got cleared when it shouldn't be on iteration %u\n", i);
+ }
+ else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ serialTestFailed(g_hTest, "DCD line didn't change when it should have on iteration %u\n", i);
+
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
+ != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_CTS))
+ {
+ if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
+ && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ serialTestFailed(g_hTest, "CTS line got set when it shouldn't be on iteration %u\n", i);
+ else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
+ && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ serialTestFailed(g_hTest, "CTS line got cleared when it shouldn't be on iteration %u\n", i);
+ }
+ else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
+ || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ serialTestFailed(g_hTest, "CTS line didn't change when it should have on iteration %u\n", i);
+
+ if (RTTestErrorCount(g_hTest) > 0)
+ break;
+
+ fStsLinesSetOld |= fStsLinesSet;
+ fStsLinesSetOld &= ~fStsLinesClear;
+ fStsLinesQueriedOld = fStsLinesQueried;
+ }
+ }
+ else
+ serialTestFailed(g_hTest, "Status lines active which should be clear (%#x, but expected %#x)\n",
+ fStsLinesQueriedOld, 0);
+ }
+ else
+ serialTestFailed(g_hTest, "Querying status lines failed with %Rrc\n", rc);
+ }
+ else
+ serialTestFailed(g_hTest, "Clearing status lines failed with %Rrc\n", rc);
+ }
+ else
+ rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+
+/**
+ * Runs a simple echo service (not a real test on its own).
+ *
+ * @returns IPRT status code.
+ * @param pSerialTest The serial test configuration.
+ */
+static DECLCALLBACK(int) serialTestRunEcho(PSERIALTEST pSerialTest)
+{
+ int rc = VINF_SUCCESS;
+ uint64_t tsStart = RTTimeNanoTS();
+ uint8_t abBuf[_1K];
+ size_t cbLeft = g_cbTx;
+ size_t cbInBuf = 0;
+
+ while ( RT_SUCCESS(rc)
+ && ( cbLeft
+ || cbInBuf))
+ {
+ uint32_t fEvts = 0;
+ uint32_t fEvtsQuery = 0;
+ if (cbInBuf)
+ fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
+ if (cbLeft && cbInBuf < sizeof(abBuf))
+ fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
+ {
+ size_t cbThisRead = RT_MIN(cbLeft, sizeof(abBuf) - cbInBuf);
+ size_t cbRead = 0;
+ rc = RTSerialPortReadNB(pSerialTest->hSerialPort, &abBuf[cbInBuf], cbThisRead, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ cbInBuf += cbRead;
+ cbLeft -= cbRead;
+ }
+ else if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
+ {
+ size_t cbWritten = 0;
+ rc = RTSerialPortWriteNB(pSerialTest->hSerialPort, &abBuf[0], cbInBuf, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ memmove(&abBuf[0], &abBuf[cbWritten], cbInBuf - cbWritten);
+ cbInBuf -= cbWritten;
+ }
+ }
+ }
+
+ uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
+ size_t cNsPerByte = tsRuntime / g_cbTx;
+ uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
+ RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
+
+ return rc;
+}
+
+
+/**
+ * Returns an array of test descriptors get from the given string.
+ *
+ * @returns Pointer to the array of test descriptors.
+ * @param pszTests The string containing the tests separated with ':'.
+ */
+static PSERIALTESTDESC serialTestSelectFromCmdLine(const char *pszTests)
+{
+ size_t cTests = 1;
+
+ const char *pszNext = strchr(pszTests, ':');
+ while (pszNext)
+ {
+ pszNext++;
+ cTests++;
+ pszNext = strchr(pszNext, ':');
+ }
+
+ PSERIALTESTDESC paTests = (PSERIALTESTDESC)RTMemAllocZ((cTests + 1) * sizeof(SERIALTESTDESC));
+ if (RT_LIKELY(paTests))
+ {
+ uint32_t iTest = 0;
+
+ pszNext = strchr(pszTests, ':');
+ while (pszNext)
+ {
+ bool fFound = false;
+
+ pszNext++; /* Skip : character. */
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
+ {
+ if (!RTStrNICmp(pszTests, g_aSerialTests[i].pszId, pszNext - pszTests - 1))
+ {
+ memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
+ fFound = true;
+ break;
+ }
+ }
+
+ if (RT_UNLIKELY(!fFound))
+ {
+ RTPrintf("Testcase \"%.*s\" not known\n", pszNext - pszTests - 1, pszTests);
+ RTMemFree(paTests);
+ return NULL;
+ }
+
+ pszTests = pszNext;
+ pszNext = strchr(pszTests, ':');
+ }
+
+ /* Fill last descriptor. */
+ bool fFound = false;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
+ {
+ if (!RTStrICmp(pszTests, g_aSerialTests[i].pszId))
+ {
+ memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
+ fFound = true;
+ break;
+ }
+ }
+
+ if (RT_UNLIKELY(!fFound))
+ {
+ RTPrintf("Testcase \"%s\" not known\n", pszTests);
+ RTMemFree(paTests);
+ paTests = NULL;
+ }
+ }
+ else
+ RTPrintf("Failed to allocate test descriptors for %u selected tests\n", cTests);
+
+ return paTests;
+}
+
+
+/**
+ * Shows tool usage text.
+ */
+static void serialTestUsage(PRTSTREAM pStrm)
+{
+ char szExec[RTPATH_MAX];
+ RTStrmPrintf(pStrm, "usage: %s [options]\n",
+ RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "options: \n");
+
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'h':
+ pszHelp = "Displays this help and exit";
+ break;
+ case 'd':
+ pszHelp = "Use the specified serial port device";
+ break;
+ case 'b':
+ pszHelp = "Use the given baudrate";
+ break;
+ case 'p':
+ pszHelp = "Use the given parity, valid modes are: none, even, odd, mark, space";
+ break;
+ case 'c':
+ pszHelp = "Use the given data bitcount, valid are: 5, 6, 7, 8";
+ break;
+ case 's':
+ pszHelp = "Use the given stop bitcount, valid are: 1, 1.5, 2";
+ break;
+ case 'm':
+ pszHelp = "Mode of the serial port, valid are: loopback, secondary, external";
+ break;
+ case 'l':
+ pszHelp = "Use the given serial port device as the secondary device";
+ break;
+ case 't':
+ pszHelp = "The tests to run separated by ':'";
+ break;
+ case 'x':
+ pszHelp = "Number of bytes to transmit during read/write tests";
+ break;
+ default:
+ pszHelp = "Option undocumented";
+ break;
+ }
+ char szOpt[256];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-30s%s\n", szOpt, pszHelp);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT and globals.
+ */
+ int rc = RTTestInitAndCreate("SerialTest", &g_hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Default values.
+ */
+ const char *pszDevice = NULL;
+ const char *pszDeviceSecondary = NULL;
+ PSERIALTESTDESC paTests = NULL;
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (rc)
+ {
+ case 'h':
+ serialTestUsage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+ case 'v':
+ g_cVerbosity++;
+ break;
+ case 'd':
+ pszDevice = ValueUnion.psz;
+ break;
+ case 'l':
+ pszDeviceSecondary = ValueUnion.psz;
+ break;
+ case 'b':
+ g_SerialPortCfg.uBaudRate = ValueUnion.u32;
+ break;
+ case 'p':
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_NONE;
+ else if (!RTStrICmp(ValueUnion.psz, "even"))
+ g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_EVEN;
+ else if (!RTStrICmp(ValueUnion.psz, "odd"))
+ g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_ODD;
+ else if (!RTStrICmp(ValueUnion.psz, "mark"))
+ g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_MARK;
+ else if (!RTStrICmp(ValueUnion.psz, "space"))
+ g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_SPACE;
+ else
+ {
+ RTPrintf("Unknown parity \"%s\" given\n", ValueUnion.psz);
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+ case 'c':
+ if (ValueUnion.u32 == 5)
+ g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
+ else if (ValueUnion.u32 == 6)
+ g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
+ else if (ValueUnion.u32 == 7)
+ g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
+ else if (ValueUnion.u32 == 8)
+ g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
+ else
+ {
+ RTPrintf("Unknown data bitcount \"%u\" given\n", ValueUnion.u32);
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+ case 's':
+ if (!RTStrICmp(ValueUnion.psz, "1"))
+ g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
+ else if (!RTStrICmp(ValueUnion.psz, "1.5"))
+ g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
+ else if (!RTStrICmp(ValueUnion.psz, "2"))
+ g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
+ else
+ {
+ RTPrintf("Unknown stop bitcount \"%s\" given\n", ValueUnion.psz);
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+ case 'm':
+ if (!RTStrICmp(ValueUnion.psz, "loopback"))
+ g_enmMode = SERIALTESTMODE_LOOPBACK;
+ else if (!RTStrICmp(ValueUnion.psz, "secondary"))
+ g_enmMode = SERIALTESTMODE_SECONDARY;
+ else if (!RTStrICmp(ValueUnion.psz, "external"))
+ g_enmMode = SERIALTESTMODE_EXTERNAL;
+ else
+ {
+ RTPrintf("Unknown serial test mode \"%s\" given\n", ValueUnion.psz);
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+ case 't':
+ paTests = serialTestSelectFromCmdLine(ValueUnion.psz);
+ if (!paTests)
+ return RTEXITCODE_FAILURE;
+ break;
+ case 'x':
+ g_cbTx = ValueUnion.u32;
+ break;
+ case 'a':
+ g_fAbortOnError = true;
+ break;
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ if (g_enmMode == SERIALTESTMODE_SECONDARY && !pszDeviceSecondary)
+ {
+ RTPrintf("Mode set to secondary device but no secondary device given\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ if (!paTests)
+ {
+ /* Select all. */
+ paTests = (PSERIALTESTDESC)RTMemAllocZ((RT_ELEMENTS(g_aSerialTests) + 1) * sizeof(SERIALTESTDESC));
+ if (RT_UNLIKELY(!paTests))
+ {
+ RTPrintf("Failed to allocate memory for test descriptors\n");
+ return RTEXITCODE_FAILURE;
+ }
+ memcpy(paTests, &g_aSerialTests[0], RT_ELEMENTS(g_aSerialTests) * sizeof(SERIALTESTDESC));
+ }
+
+ rc = RTRandAdvCreateParkMiller(&g_hRand);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("Failed to create random number generator: %Rrc\n", rc);
+ return RTEXITCODE_FAILURE;
+ }
+
+ rc = RTRandAdvSeed(g_hRand, UINT64_C(0x123456789abcdef));
+ AssertRC(rc);
+
+ /*
+ * Start testing.
+ */
+ RTTestBanner(g_hTest);
+
+ if (pszDevice)
+ {
+ uint32_t fFlags = RTSERIALPORT_OPEN_F_READ
+ | RTSERIALPORT_OPEN_F_WRITE
+ | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
+
+ RTTestSub(g_hTest, "Opening device");
+ rc = RTSerialPortOpen(&g_hSerialPort, pszDevice, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_enmMode == SERIALTESTMODE_SECONDARY)
+ {
+ RTTestSub(g_hTest, "Opening secondary device");
+ rc = RTSerialPortOpen(&g_hSerialPortSecondary, pszDeviceSecondary, fFlags);
+ if (RT_FAILURE(rc))
+ serialTestFailed(g_hTest, "Opening secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTTestSub(g_hTest, "Setting serial port configuration");
+
+ rc = RTSerialPortCfgSet(g_hSerialPort, &g_SerialPortCfg ,NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_enmMode == SERIALTESTMODE_SECONDARY)
+ {
+ RTTestSub(g_hTest, "Setting serial port configuration for secondary device");
+ rc = RTSerialPortCfgSet(g_hSerialPortSecondary, &g_SerialPortCfg, NULL);
+ if (RT_FAILURE(rc))
+ serialTestFailed(g_hTest, "Setting configuration of secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ SERIALTEST Test;
+ PSERIALTESTDESC pTest = &paTests[0];
+
+ Test.hTest = g_hTest;
+ Test.hSerialPort = g_hSerialPort;
+ Test.pSerialCfg = &g_SerialPortCfg;
+
+ while (pTest->pszId)
+ {
+ RTTestSub(g_hTest, pTest->pszDesc);
+ rc = pTest->pfnRun(&Test);
+ if ( RT_FAILURE(rc)
+ || RTTestErrorCount(g_hTest) > 0)
+ serialTestFailed(g_hTest, "Running test \"%s\" failed (%Rrc, cErrors=%u)\n",
+ pTest->pszId, rc, RTTestErrorCount(g_hTest));
+
+ RTTestSubDone(g_hTest);
+ pTest++;
+ }
+ }
+ }
+ else
+ serialTestFailed(g_hTest, "Setting configuration of device \"%s\" failed with %Rrc\n", pszDevice, rc);
+
+ RTSerialPortClose(g_hSerialPort);
+ }
+ }
+ else
+ serialTestFailed(g_hTest, "Opening device \"%s\" failed with %Rrc\n", pszDevice, rc);
+ }
+ else
+ serialTestFailed(g_hTest, "No device given on command line\n");
+
+ RTRandAdvDestroy(g_hRand);
+ RTMemFree(paTests);
+ RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/storage/IoPerf.cpp b/src/VBox/ValidationKit/utils/storage/IoPerf.cpp
new file mode 100644
index 00000000..db3b7211
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/storage/IoPerf.cpp
@@ -0,0 +1,1405 @@
+/* $Id: IoPerf.cpp $ */
+/** @file
+ * IoPerf - Storage I/O Performance Benchmark.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/ioqueue.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/zero.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Size multiplier for the random data buffer to seek around. */
+#define IOPERF_RAND_DATA_BUF_FACTOR 3
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Forward declaration of the master. */
+typedef struct IOPERFMASTER *PIOPERFMASTER;
+
+/**
+ * I/O perf supported tests.
+ */
+typedef enum IOPERFTEST
+{
+ /** Invalid test handle. */
+ IOPERFTEST_INVALID = 0,
+ /** The test was disabled. */
+ IOPERFTEST_DISABLED,
+ IOPERFTEST_FIRST_WRITE,
+ IOPERFTEST_SEQ_READ,
+ IOPERFTEST_SEQ_WRITE,
+ IOPERFTEST_REV_READ,
+ IOPERFTEST_REV_WRITE,
+ IOPERFTEST_RND_READ,
+ IOPERFTEST_RND_WRITE,
+ IOPERFTEST_SEQ_READWRITE,
+ IOPERFTEST_RND_READWRITE,
+ /** Special shutdown test which lets the workers exit, must be LAST. */
+ IOPERFTEST_SHUTDOWN,
+ IOPERFTEST_32BIT_HACK = 0x7fffffff
+} IOPERFTEST;
+
+
+/**
+ * I/O perf test set preparation method.
+ */
+typedef enum IOPERFTESTSETPREP
+{
+ IOPERFTESTSETPREP_INVALID = 0,
+ /** Just create the file and don't set any sizes. */
+ IOPERFTESTSETPREP_JUST_CREATE,
+ /** Standard RTFileSetSize() call which might create a sparse file. */
+ IOPERFTESTSETPREP_SET_SZ,
+ /** Uses RTFileSetAllocationSize() to ensure storage is allocated for the file. */
+ IOPERFTESTSETPREP_SET_ALLOC_SZ,
+ /** 32bit hack. */
+ IOPERFTESTSETPREP_32BIT_HACK = 0x7fffffff
+} IOPERFTESTSETPREP;
+
+
+/**
+ * Statistics values for a single request kept around until the
+ * test completed for statistics collection.
+ */
+typedef struct IOPERFREQSTAT
+{
+ /** Start timestamp for the request. */
+ uint64_t tsStart;
+ /** Completion timestamp for the request. */
+ uint64_t tsComplete;
+} IOPERFREQSTAT;
+/** Pointer to a request statistics record. */
+typedef IOPERFREQSTAT *PIOPERFREQSTAT;
+
+
+/**
+ * I/O perf request.
+ */
+typedef struct IOPERFREQ
+{
+ /** Request operation code. */
+ RTIOQUEUEOP enmOp;
+ /** Start offset. */
+ uint64_t offXfer;
+ /** Transfer size for the request. */
+ size_t cbXfer;
+ /** The buffer used for the transfer. */
+ void *pvXfer;
+ /** This is the statically assigned destination buffer for read requests for this request. */
+ void *pvXferRead;
+ /** Size of the read buffer. */
+ size_t cbXferRead;
+ /** Pointer to statistics record. */
+ PIOPERFREQSTAT pStats;
+} IOPERFREQ;
+/** Pointer to an I/O perf request. */
+typedef IOPERFREQ *PIOPERFREQ;
+/** Pointer to a constant I/O perf request. */
+typedef const IOPERFREQ *PCIOPERFREQ;
+
+
+/**
+ * I/O perf job data.
+ */
+typedef struct IOPERFJOB
+{
+ /** Pointer to the master if multiple jobs are running. */
+ PIOPERFMASTER pMaster;
+ /** Job ID. */
+ uint32_t idJob;
+ /** The test this job is executing. */
+ volatile IOPERFTEST enmTest;
+ /** The thread executing the job. */
+ RTTHREAD hThread;
+ /** The I/O queue for the job. */
+ RTIOQUEUE hIoQueue;
+ /** The file path used. */
+ char *pszFilename;
+ /** The handle to use for the I/O queue. */
+ RTHANDLE Hnd;
+ /** Multi event semaphore to synchronise with other jobs. */
+ RTSEMEVENTMULTI hSemEvtMultiRendezvous;
+ /** The test set size. */
+ uint64_t cbTestSet;
+ /** Size of one I/O block. */
+ size_t cbIoBlock;
+ /** Maximum number of requests to queue. */
+ uint32_t cReqsMax;
+ /** Pointer to the array of request specific data. */
+ PIOPERFREQ paIoReqs;
+ /** Page aligned chunk of memory assigned as read buffers for the individual requests. */
+ void *pvIoReqReadBuf;
+ /** Size of the read memory buffer. */
+ size_t cbIoReqReadBuf;
+ /** Random number generator used. */
+ RTRAND hRand;
+ /** The random data buffer used for writes. */
+ uint8_t *pbRandWrite;
+ /** Size of the random write buffer in 512 byte blocks. */
+ uint32_t cRandWriteBlocks512B;
+ /** Chance in percent to get a write. */
+ unsigned uWriteChance;
+ /** Flag whether to verify read data. */
+ bool fVerifyReads;
+ /** Start timestamp. */
+ uint64_t tsStart;
+ /** End timestamp. for the job. */
+ uint64_t tsFinish;
+ /** Number of request statistic records. */
+ uint32_t cReqStats;
+ /** Index of the next free statistics record to use. */
+ uint32_t idxReqStatNext;
+ /** Array of request statistic records for the whole test. */
+ PIOPERFREQSTAT paReqStats;
+ /** Test dependent data. */
+ union
+ {
+ /** Sequential read write. */
+ uint64_t offNextSeq;
+ /** Data for random acess. */
+ struct
+ {
+ /** Number of valid entries in the bitmap. */
+ uint32_t cBlocks;
+ /** Pointer to the bitmap marking accessed blocks. */
+ uint8_t *pbMapAccessed;
+ /** Number of unaccessed blocks. */
+ uint32_t cBlocksLeft;
+ } Rnd;
+ } Tst;
+} IOPERFJOB;
+/** Pointer to an I/O Perf job. */
+typedef IOPERFJOB *PIOPERFJOB;
+
+
+/**
+ * I/O perf master instance coordinating the job execution.
+ */
+typedef struct IOPERFMASTER
+{
+ /** Event semaphore. */
+ /** Number of jobs. */
+ uint32_t cJobs;
+ /** Job instances, variable in size. */
+ IOPERFJOB aJobs[1];
+} IOPERFMASTER;
+
+
+enum
+{
+ kCmdOpt_First = 128,
+
+ kCmdOpt_FirstWrite = kCmdOpt_First,
+ kCmdOpt_NoFirstWrite,
+ kCmdOpt_SeqRead,
+ kCmdOpt_NoSeqRead,
+ kCmdOpt_SeqWrite,
+ kCmdOpt_NoSeqWrite,
+ kCmdOpt_RndRead,
+ kCmdOpt_NoRndRead,
+ kCmdOpt_RndWrite,
+ kCmdOpt_NoRndWrite,
+ kCmdOpt_RevRead,
+ kCmdOpt_NoRevRead,
+ kCmdOpt_RevWrite,
+ kCmdOpt_NoRevWrite,
+ kCmdOpt_SeqReadWrite,
+ kCmdOpt_NoSeqReadWrite,
+ kCmdOpt_RndReadWrite,
+ kCmdOpt_NoRndReadWrite,
+
+ kCmdOpt_End
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ { "--dir", 'd', RTGETOPT_REQ_STRING },
+ { "--relative-dir", 'r', RTGETOPT_REQ_NOTHING },
+
+ { "--jobs", 'j', RTGETOPT_REQ_UINT32 },
+ { "--io-engine", 'i', RTGETOPT_REQ_STRING },
+ { "--test-set-size", 's', RTGETOPT_REQ_UINT64 },
+ { "--block-size", 'b', RTGETOPT_REQ_UINT32 },
+ { "--maximum-requests", 'm', RTGETOPT_REQ_UINT32 },
+ { "--verify-reads", 'y', RTGETOPT_REQ_BOOL },
+ { "--use-cache", 'c', RTGETOPT_REQ_BOOL },
+
+ { "--first-write", kCmdOpt_FirstWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-first-write", kCmdOpt_NoFirstWrite, RTGETOPT_REQ_NOTHING },
+ { "--seq-read", kCmdOpt_SeqRead, RTGETOPT_REQ_NOTHING },
+ { "--no-seq-read", kCmdOpt_NoSeqRead, RTGETOPT_REQ_NOTHING },
+ { "--seq-write", kCmdOpt_SeqWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-seq-write", kCmdOpt_NoSeqWrite, RTGETOPT_REQ_NOTHING },
+ { "--rnd-read", kCmdOpt_RndRead, RTGETOPT_REQ_NOTHING },
+ { "--no-rnd-read", kCmdOpt_NoRndRead, RTGETOPT_REQ_NOTHING },
+ { "--rnd-write", kCmdOpt_RndWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-rnd-write", kCmdOpt_NoRndWrite, RTGETOPT_REQ_NOTHING },
+ { "--rev-read", kCmdOpt_RevRead, RTGETOPT_REQ_NOTHING },
+ { "--no-rev-read", kCmdOpt_NoRevRead, RTGETOPT_REQ_NOTHING },
+ { "--rev-write", kCmdOpt_RevWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-rev-write", kCmdOpt_NoRevWrite, RTGETOPT_REQ_NOTHING },
+ { "--seq-read-write", kCmdOpt_SeqReadWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-seq-read-write", kCmdOpt_NoSeqReadWrite, RTGETOPT_REQ_NOTHING },
+ { "--rnd-read-write", kCmdOpt_RndReadWrite, RTGETOPT_REQ_NOTHING },
+ { "--no-rnd-read-write", kCmdOpt_NoRndReadWrite, RTGETOPT_REQ_NOTHING },
+
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
+};
+
+/** The test handle. */
+static RTTEST g_hTest;
+/** Verbosity level. */
+static uint32_t g_uVerbosity = 0;
+/** Selected I/O engine for the tests, NULL means pick best default one. */
+static const char *g_pszIoEngine = NULL;
+/** Number of jobs to run concurrently. */
+static uint32_t g_cJobs = 1;
+/** Size of each test set (file) in bytes. */
+static uint64_t g_cbTestSet = _2G;
+/** Block size for each request. */
+static size_t g_cbIoBlock = _4K;
+/** Maximum number of concurrent requests for each job. */
+static uint32_t g_cReqsMax = 16;
+/** Flag whether to open the file without caching enabled. */
+static bool g_fNoCache = true;
+/** Write chance for mixed read/write tests. */
+static unsigned g_uWriteChance = 50;
+/** Flag whether to verify read data. */
+static bool g_fVerifyReads = true;
+
+/** @name Configured tests, this must match the IOPERFTEST order.
+ * @{ */
+static IOPERFTEST g_aenmTests[] =
+{
+ IOPERFTEST_DISABLED, /** @< The invalid test value is disabled of course. */
+ IOPERFTEST_DISABLED,
+ IOPERFTEST_FIRST_WRITE,
+ IOPERFTEST_SEQ_READ,
+ IOPERFTEST_SEQ_WRITE,
+ IOPERFTEST_REV_READ,
+ IOPERFTEST_REV_WRITE,
+ IOPERFTEST_RND_READ,
+ IOPERFTEST_RND_WRITE,
+ IOPERFTEST_SEQ_READWRITE,
+ IOPERFTEST_RND_READWRITE,
+ IOPERFTEST_SHUTDOWN
+};
+/** The test index being selected next. */
+static uint32_t g_idxTest = 2;
+/** @} */
+
+/** Set if g_szDir and friends are path relative to CWD rather than absolute. */
+static bool g_fRelativeDir = false;
+/** The length of g_szDir. */
+static size_t g_cchDir;
+
+/** The test directory (absolute). This will always have a trailing slash. */
+static char g_szDir[RTPATH_BIG_MAX];
+
+
+/*********************************************************************************************************************************
+* Tests *
+*********************************************************************************************************************************/
+
+
+/**
+ * Selects the next test to run.
+ *
+ * @return Next test to run.
+ */
+static IOPERFTEST ioPerfJobTestSelectNext()
+{
+ AssertReturn(g_idxTest < RT_ELEMENTS(g_aenmTests), IOPERFTEST_SHUTDOWN);
+
+ while ( g_idxTest < RT_ELEMENTS(g_aenmTests)
+ && g_aenmTests[g_idxTest] == IOPERFTEST_DISABLED)
+ g_idxTest++;
+
+ AssertReturn(g_idxTest < RT_ELEMENTS(g_aenmTests), IOPERFTEST_SHUTDOWN);
+
+ return g_aenmTests[g_idxTest++];
+}
+
+
+/**
+ * Returns the I/O queue operation for the next request.
+ *
+ * @returns I/O queue operation enum.
+ * @param pJob The job data for the current worker.
+ */
+static RTIOQUEUEOP ioPerfJobTestGetIoQOp(PIOPERFJOB pJob)
+{
+ switch (pJob->enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ case IOPERFTEST_SEQ_WRITE:
+ case IOPERFTEST_REV_WRITE:
+ case IOPERFTEST_RND_WRITE:
+ return RTIOQUEUEOP_WRITE;
+
+ case IOPERFTEST_SEQ_READ:
+ case IOPERFTEST_RND_READ:
+ case IOPERFTEST_REV_READ:
+ return RTIOQUEUEOP_READ;
+
+ case IOPERFTEST_SEQ_READWRITE:
+ case IOPERFTEST_RND_READWRITE:
+ {
+ uint32_t uRnd = RTRandAdvU32Ex(pJob->hRand, 0, 100);
+ return (uRnd < pJob->uWriteChance) ? RTIOQUEUEOP_WRITE : RTIOQUEUEOP_READ;
+ }
+
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", pJob->enmTest));
+ break;
+ }
+
+ return RTIOQUEUEOP_INVALID;
+}
+
+
+/**
+ * Returns the offset to use for the next request.
+ *
+ * @returns Offset to use.
+ * @param pJob The job data for the current worker.
+ */
+static uint64_t ioPerfJobTestGetOffsetNext(PIOPERFJOB pJob)
+{
+ uint64_t offNext = 0;
+
+ switch (pJob->enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ case IOPERFTEST_SEQ_WRITE:
+ case IOPERFTEST_SEQ_READ:
+ case IOPERFTEST_SEQ_READWRITE:
+ offNext = pJob->Tst.offNextSeq;
+ pJob->Tst.offNextSeq += pJob->cbIoBlock;
+ break;
+ case IOPERFTEST_REV_WRITE:
+ case IOPERFTEST_REV_READ:
+ offNext = pJob->Tst.offNextSeq;
+ if (pJob->Tst.offNextSeq == 0)
+ pJob->Tst.offNextSeq = pJob->cbTestSet;
+ else
+ pJob->Tst.offNextSeq -= pJob->cbIoBlock;
+ break;
+ case IOPERFTEST_RND_WRITE:
+ case IOPERFTEST_RND_READ:
+ case IOPERFTEST_RND_READWRITE:
+ {
+ int idx = -1;
+
+ idx = ASMBitFirstClear(pJob->Tst.Rnd.pbMapAccessed, pJob->Tst.Rnd.cBlocks);
+
+ /* In case this is the last request we don't need to search further. */
+ if (pJob->Tst.Rnd.cBlocksLeft > 1)
+ {
+ int idxIo;
+ idxIo = RTRandAdvU32Ex(pJob->hRand, idx, pJob->Tst.Rnd.cBlocks - 1);
+
+ /*
+ * If the bit is marked free use it, otherwise search for the next free bit
+ * and if that doesn't work use the first free bit.
+ */
+ if (ASMBitTest(pJob->Tst.Rnd.pbMapAccessed, idxIo))
+ {
+ idxIo = ASMBitNextClear(pJob->Tst.Rnd.pbMapAccessed, pJob->Tst.Rnd.cBlocks, idxIo);
+ if (idxIo != -1)
+ idx = idxIo;
+ }
+ else
+ idx = idxIo;
+ }
+
+ Assert(idx != -1);
+ offNext = (uint64_t)idx * pJob->cbIoBlock;
+ pJob->Tst.Rnd.cBlocksLeft--;
+ ASMBitSet(pJob->Tst.Rnd.pbMapAccessed, idx);
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", pJob->enmTest));
+ break;
+ }
+
+ return offNext;
+}
+
+
+/**
+ * Returns a pointer to the write buffer with random data for the given offset which
+ * is predictable for data verification.
+ *
+ * @returns Pointer to I/O block sized data buffer with random data.
+ * @param pJob The job data for the current worker.
+ * @param off The offset to get the buffer for.
+ */
+static void *ioPerfJobTestGetWriteBufForOffset(PIOPERFJOB pJob, uint64_t off)
+{
+ /*
+ * Dividing the file into 512 byte blocks so buffer pointers are at least
+ * 512 byte aligned to work with async I/O on some platforms (Linux and O_DIRECT for example).
+ */
+ uint64_t uBlock = off / 512;
+ uint32_t idxBuf = uBlock % pJob->cRandWriteBlocks512B;
+ return pJob->pbRandWrite + idxBuf * 512;
+}
+
+
+/**
+ * Initialize the given request for submission.
+ *
+ * @param pJob The job data for the current worker.
+ * @param pIoReq The request to initialize.
+ */
+static void ioPerfJobTestReqInit(PIOPERFJOB pJob, PIOPERFREQ pIoReq)
+{
+ pIoReq->enmOp = ioPerfJobTestGetIoQOp(pJob);
+ pIoReq->offXfer = ioPerfJobTestGetOffsetNext(pJob);
+ pIoReq->cbXfer = pJob->cbIoBlock;
+ if (pIoReq->enmOp == RTIOQUEUEOP_READ)
+ pIoReq->pvXfer = pIoReq->pvXferRead;
+ else if (pIoReq->enmOp == RTIOQUEUEOP_WRITE)
+ pIoReq->pvXfer = ioPerfJobTestGetWriteBufForOffset(pJob, pIoReq->offXfer);
+ else /* Flush */
+ pIoReq->pvXfer = NULL;
+
+ Assert(pJob->idxReqStatNext < pJob->cReqStats);
+ if (RT_LIKELY(pJob->idxReqStatNext < pJob->cReqStats))
+ {
+ pIoReq->pStats = &pJob->paReqStats[pJob->idxReqStatNext++];
+ pIoReq->pStats->tsStart = RTTimeNanoTS();
+ }
+ else
+ pIoReq->pStats = NULL;
+}
+
+
+/**
+ * Returns a stringified version of the test given.
+ *
+ * @returns Pointer to string representation of the test.
+ * @param enmTest The test to stringify.
+ */
+static const char *ioPerfJobTestStringify(IOPERFTEST enmTest)
+{
+ switch (enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ return "FirstWrite";
+ case IOPERFTEST_SEQ_WRITE:
+ return "SequentialWrite";
+ case IOPERFTEST_SEQ_READ:
+ return "SequentialRead";
+ case IOPERFTEST_REV_WRITE:
+ return "ReverseWrite";
+ case IOPERFTEST_REV_READ:
+ return "ReverseRead";
+ case IOPERFTEST_RND_WRITE:
+ return "RandomWrite";
+ case IOPERFTEST_RND_READ:
+ return "RandomRead";
+ case IOPERFTEST_SEQ_READWRITE:
+ return "SequentialReadWrite";
+ case IOPERFTEST_RND_READWRITE:
+ return "RandomReadWrite";
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", enmTest));
+ break;
+ }
+
+ return "INVALID_TEST";
+}
+
+
+/**
+ * Initializes the test state for the current test.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job data for the current worker.
+ */
+static int ioPerfJobTestInit(PIOPERFJOB pJob)
+{
+ int rc = VINF_SUCCESS;
+
+ pJob->idxReqStatNext = 0;
+
+ switch (pJob->enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ case IOPERFTEST_SEQ_WRITE:
+ case IOPERFTEST_SEQ_READ:
+ case IOPERFTEST_SEQ_READWRITE:
+ pJob->Tst.offNextSeq = 0;
+ break;
+ case IOPERFTEST_REV_WRITE:
+ case IOPERFTEST_REV_READ:
+ pJob->Tst.offNextSeq = pJob->cbTestSet - pJob->cbIoBlock;
+ break;
+ case IOPERFTEST_RND_WRITE:
+ case IOPERFTEST_RND_READ:
+ case IOPERFTEST_RND_READWRITE:
+ {
+ pJob->Tst.Rnd.cBlocks = (uint32_t)( pJob->cbTestSet / pJob->cbIoBlock
+ + (pJob->cbTestSet % pJob->cbIoBlock ? 1 : 0));
+ pJob->Tst.Rnd.cBlocksLeft = pJob->Tst.Rnd.cBlocks;
+ pJob->Tst.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ( pJob->Tst.Rnd.cBlocks / 8
+ + ((pJob->Tst.Rnd.cBlocks % 8)
+ ? 1
+ : 0));
+ if (!pJob->Tst.Rnd.pbMapAccessed)
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", pJob->enmTest));
+ break;
+ }
+
+ pJob->tsStart = RTTimeNanoTS();
+ return rc;
+}
+
+
+/**
+ * Frees allocated resources specific for the current test.
+ *
+ * @param pJob The job data for the current worker.
+ */
+static void ioPerfJobTestFinish(PIOPERFJOB pJob)
+{
+ pJob->tsFinish = RTTimeNanoTS();
+
+ switch (pJob->enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ case IOPERFTEST_SEQ_WRITE:
+ case IOPERFTEST_SEQ_READ:
+ case IOPERFTEST_REV_WRITE:
+ case IOPERFTEST_REV_READ:
+ case IOPERFTEST_SEQ_READWRITE:
+ break; /* Nothing to do. */
+
+ case IOPERFTEST_RND_WRITE:
+ case IOPERFTEST_RND_READ:
+ case IOPERFTEST_RND_READWRITE:
+ RTMemFree(pJob->Tst.Rnd.pbMapAccessed);
+ break;
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", pJob->enmTest));
+ break;
+ }
+}
+
+
+/**
+ * Returns whether the current test is done with submitting new requests (reached test set size).
+ *
+ * @returns True when the test has submitted all required requests, false if there are still requests required
+ */
+static bool ioPerfJobTestIsDone(PIOPERFJOB pJob)
+{
+ switch (pJob->enmTest)
+ {
+ case IOPERFTEST_FIRST_WRITE:
+ case IOPERFTEST_SEQ_WRITE:
+ case IOPERFTEST_SEQ_READ:
+ case IOPERFTEST_REV_WRITE:
+ case IOPERFTEST_REV_READ:
+ case IOPERFTEST_SEQ_READWRITE:
+ return pJob->Tst.offNextSeq == pJob->cbTestSet;
+ case IOPERFTEST_RND_WRITE:
+ case IOPERFTEST_RND_READ:
+ case IOPERFTEST_RND_READWRITE:
+ return pJob->Tst.Rnd.cBlocksLeft == 0;
+ break;
+ default:
+ AssertMsgFailed(("Invalid/unknown test selected: %d\n", pJob->enmTest));
+ break;
+ }
+
+ return true;
+}
+
+
+/**
+ * The test I/O loop pumping I/O.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job data for the current worker.
+ */
+static int ioPerfJobTestIoLoop(PIOPERFJOB pJob)
+{
+ int rc = ioPerfJobTestInit(pJob);
+ if (RT_SUCCESS(rc))
+ {
+ /* Allocate the completion event array. */
+ uint32_t cReqsQueued = 0;
+ PRTIOQUEUECEVT paIoQCEvt = (PRTIOQUEUECEVT)RTMemAllocZ(pJob->cReqsMax * sizeof(RTIOQUEUECEVT));
+ if (RT_LIKELY(paIoQCEvt))
+ {
+ /* Queue requests up to the maximum. */
+ while ( (cReqsQueued < pJob->cReqsMax)
+ && !ioPerfJobTestIsDone(pJob)
+ && RT_SUCCESS(rc))
+ {
+ PIOPERFREQ pReq = &pJob->paIoReqs[cReqsQueued];
+ ioPerfJobTestReqInit(pJob, pReq);
+ RTTESTI_CHECK_RC(RTIoQueueRequestPrepare(pJob->hIoQueue, &pJob->Hnd, pReq->enmOp,
+ pReq->offXfer, pReq->pvXfer, pReq->cbXfer, 0 /*fReqFlags*/,
+ pReq), VINF_SUCCESS);
+ cReqsQueued++;
+ }
+
+ /* Commit the prepared requests. */
+ if ( RT_SUCCESS(rc)
+ && cReqsQueued)
+ {
+ RTTESTI_CHECK_RC(RTIoQueueCommit(pJob->hIoQueue), VINF_SUCCESS);
+ }
+
+ /* Enter wait loop and process completed requests. */
+ while ( RT_SUCCESS(rc)
+ && cReqsQueued)
+ {
+ uint32_t cCEvtCompleted = 0;
+
+ RTTESTI_CHECK_RC(RTIoQueueEvtWait(pJob->hIoQueue, paIoQCEvt, pJob->cReqsMax, 1 /*cMinWait*/,
+ &cCEvtCompleted, 0 /*fFlags*/), VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cReqsThisQueued = 0;
+
+ /* Process any completed event and continue to fill the queue as long as there is stuff to do. */
+ for (uint32_t i = 0; i < cCEvtCompleted; i++)
+ {
+ PIOPERFREQ pReq = (PIOPERFREQ)paIoQCEvt[i].pvUser;
+
+ if (RT_SUCCESS(paIoQCEvt[i].rcReq))
+ {
+ Assert(paIoQCEvt[i].cbXfered == pReq->cbXfer);
+
+ if (pReq->pStats)
+ pReq->pStats->tsComplete = RTTimeNanoTS();
+
+ if ( pJob->fVerifyReads
+ && pReq->enmOp == RTIOQUEUEOP_READ)
+ {
+ const void *pvBuf = ioPerfJobTestGetWriteBufForOffset(pJob, pReq->offXfer);
+ if (memcmp(pReq->pvXferRead, pvBuf, pReq->cbXfer))
+ {
+ if (g_uVerbosity > 1)
+ RTTestIFailed("IoPerf: Corrupted data detected by read at offset %#llu (sz: %zu)", pReq->offXfer, pReq->cbXfer);
+ else
+ RTTestIErrorInc();
+ }
+ }
+
+ if (!ioPerfJobTestIsDone(pJob))
+ {
+ ioPerfJobTestReqInit(pJob, pReq);
+ RTTESTI_CHECK_RC(RTIoQueueRequestPrepare(pJob->hIoQueue, &pJob->Hnd, pReq->enmOp,
+ pReq->offXfer, pReq->pvXfer, pReq->cbXfer, 0 /*fReqFlags*/,
+ pReq), VINF_SUCCESS);
+ cReqsThisQueued++;
+ }
+ else
+ cReqsQueued--;
+ }
+ else
+ RTTestIErrorInc();
+ }
+
+ if ( cReqsThisQueued
+ && RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC(RTIoQueueCommit(pJob->hIoQueue), VINF_SUCCESS);
+ }
+ }
+ }
+
+ RTMemFree(paIoQCEvt);
+ }
+
+ ioPerfJobTestFinish(pJob);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Calculates the statistic values for the given job after a
+ * test finished.
+ *
+ * @param pJob The job data.
+ */
+static void ioPerfJobStats(PIOPERFJOB pJob)
+{
+ const char *pszTest = ioPerfJobTestStringify(pJob->enmTest);
+ uint64_t nsJobRuntime = pJob->tsFinish - pJob->tsStart;
+ RTTestIValueF(nsJobRuntime, RTTESTUNIT_NS, "%s/Job/%RU32/Runtime", pszTest, pJob->idJob);
+
+ uint64_t *paReqRuntimeNs = (uint64_t *)RTMemAllocZ(pJob->cReqStats * sizeof(uint64_t));
+ if (RT_LIKELY(paReqRuntimeNs))
+ {
+ /* Calculate runtimes for each request first. */
+ for (uint32_t i = 0; i < pJob->cReqStats; i++)
+ {
+ PIOPERFREQSTAT pStat = &pJob->paReqStats[i];
+ paReqRuntimeNs[i] = pStat->tsComplete - pStat->tsStart;
+ }
+
+ /* Get average bandwidth for the job. */
+ RTTestIValueF((uint64_t)((double)pJob->cbTestSet / ((double)nsJobRuntime / RT_NS_1SEC)),
+ RTTESTUNIT_BYTES_PER_SEC, "%s/Job/%RU32/AvgBandwidth", pszTest, pJob->idJob);
+
+ RTTestIValueF((uint64_t)(pJob->cReqStats / ((double)nsJobRuntime / RT_NS_1SEC)),
+ RTTESTUNIT_OCCURRENCES_PER_SEC, "%s/Job/%RU32/AvgIops", pszTest, pJob->idJob);
+
+ /* Calculate the average latency for the requests. */
+ uint64_t uLatency = 0;
+ for (uint32_t i = 0; i < pJob->cReqStats; i++)
+ uLatency += paReqRuntimeNs[i];
+ RTTestIValueF(uLatency / pJob->cReqStats, RTTESTUNIT_NS, "%s/Job/%RU32/AvgLatency", pszTest, pJob->idJob);
+
+ RTMemFree(paReqRuntimeNs);
+ }
+ else
+ RTTestIErrorInc();
+}
+
+
+/**
+ * Synchronizes with the other jobs and waits for the current test to execute.
+ *
+ * @returns IPRT status.
+ * @param pJob The job data for the current worker.
+ */
+static int ioPerfJobSync(PIOPERFJOB pJob)
+{
+ if (pJob->pMaster)
+ {
+ /* Enter the rendezvous semaphore. */
+ int rc = VINF_SUCCESS;
+
+ return rc;
+ }
+
+ /* Single threaded run, collect the results from our current test and select the next test. */
+ /** @todo Results and statistics collection. */
+ pJob->enmTest = ioPerfJobTestSelectNext();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * I/O perf job main work loop.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job data for the current worker.
+ */
+static int ioPerfJobWorkLoop(PIOPERFJOB pJob)
+{
+ int rc = VINF_SUCCESS;
+
+ for (;;)
+ {
+ /* Synchronize with the other jobs and the master. */
+ rc = ioPerfJobSync(pJob);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (pJob->enmTest == IOPERFTEST_SHUTDOWN)
+ break;
+
+ rc = ioPerfJobTestIoLoop(pJob);
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Do the statistics here for a single job run,
+ * the master will do this for each job and combined statistics
+ * otherwise.
+ */
+ if (!pJob->pMaster)
+ ioPerfJobStats(pJob);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Job thread entry point.
+ */
+static DECLCALLBACK(int) ioPerfJobThread(RTTHREAD hThrdSelf, void *pvUser)
+{
+ RT_NOREF(hThrdSelf);
+
+ PIOPERFJOB pJob = (PIOPERFJOB)pvUser;
+ return ioPerfJobWorkLoop(pJob);
+}
+
+
+/**
+ * Prepares the test set by laying out the files and filling them with data.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job to initialize.
+ */
+static int ioPerfJobTestSetPrep(PIOPERFJOB pJob)
+{
+ int rc = RTRandAdvCreateParkMiller(&pJob->hRand);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTRandAdvSeed(pJob->hRand, RTTimeNanoTS());
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a random data buffer for writes, we'll use multiple of the I/O block size to
+ * be able to seek in the buffer quite a bit to make the file content as random as possible
+ * to avoid mechanisms like compression or deduplication for now which can influence storage
+ * benchmarking unpredictably.
+ */
+ pJob->cRandWriteBlocks512B = (uint32_t)(((IOPERF_RAND_DATA_BUF_FACTOR - 1) * pJob->cbIoBlock) / 512);
+ pJob->pbRandWrite = (uint8_t *)RTMemPageAllocZ(IOPERF_RAND_DATA_BUF_FACTOR * pJob->cbIoBlock);
+ if (RT_LIKELY(pJob->pbRandWrite))
+ {
+ RTRandAdvBytes(pJob->hRand, pJob->pbRandWrite, IOPERF_RAND_DATA_BUF_FACTOR * pJob->cbIoBlock);
+
+ /* Write the content here if the first write test is disabled. */
+ if (g_aenmTests[IOPERFTEST_FIRST_WRITE] == IOPERFTEST_DISABLED)
+ {
+ for (uint64_t off = 0; off < pJob->cbTestSet && RT_SUCCESS(rc); off += pJob->cbIoBlock)
+ {
+ void *pvWrite = ioPerfJobTestGetWriteBufForOffset(pJob, off);
+ rc = RTFileWriteAt(pJob->Hnd.u.hFile, off, pvWrite, pJob->cbIoBlock, NULL);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ RTMemPageFree(pJob->pbRandWrite, IOPERF_RAND_DATA_BUF_FACTOR * pJob->cbIoBlock);
+ }
+ }
+ RTRandAdvDestroy(pJob->hRand);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initializes the given job instance.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job to initialize.
+ * @param pMaster The coordination master if any.
+ * @param idJob ID of the job.
+ * @param pszIoEngine I/O queue engine for this job, NULL for best default.
+ * @param pszTestDir The test directory to create the file in - requires a slash a the end.
+ * @param enmPrepMethod Test set preparation method to use.
+ * @param cbTestSet Size of the test set ofr this job.
+ * @param cbIoBlock I/O block size for the given job.
+ * @param cReqsMax Maximum number of concurrent requests for this job.
+ * @param uWriteChance The write chance for mixed read/write tests.
+ * @param fVerifyReads Flag whether to verify read data.
+ */
+static int ioPerfJobInit(PIOPERFJOB pJob, PIOPERFMASTER pMaster, uint32_t idJob,
+ const char *pszIoEngine, const char *pszTestDir,
+ IOPERFTESTSETPREP enmPrepMethod,
+ uint64_t cbTestSet, size_t cbIoBlock, uint32_t cReqsMax,
+ unsigned uWriteChance, bool fVerifyReads)
+{
+ pJob->pMaster = pMaster;
+ pJob->idJob = idJob;
+ pJob->enmTest = IOPERFTEST_INVALID;
+ pJob->hThread = NIL_RTTHREAD;
+ pJob->Hnd.enmType = RTHANDLETYPE_FILE;
+ pJob->cbTestSet = cbTestSet;
+ pJob->cbIoBlock = cbIoBlock;
+ pJob->cReqsMax = cReqsMax;
+ pJob->cbIoReqReadBuf = cReqsMax * cbIoBlock;
+ pJob->uWriteChance = uWriteChance;
+ pJob->fVerifyReads = fVerifyReads;
+ pJob->cReqStats = (uint32_t)(pJob->cbTestSet / pJob->cbIoBlock + ((pJob->cbTestSet % pJob->cbIoBlock) ? 1 : 0));
+ pJob->idxReqStatNext = 0;
+
+ int rc = VINF_SUCCESS;
+ pJob->paIoReqs = (PIOPERFREQ)RTMemAllocZ(cReqsMax * sizeof(IOPERFREQ));
+ if (RT_LIKELY(pJob->paIoReqs))
+ {
+ pJob->paReqStats = (PIOPERFREQSTAT)RTMemAllocZ(pJob->cReqStats * sizeof(IOPERFREQSTAT));
+ if (RT_LIKELY(pJob->paReqStats))
+ {
+ pJob->pvIoReqReadBuf = RTMemPageAlloc(pJob->cbIoReqReadBuf);
+ if (RT_LIKELY(pJob->pvIoReqReadBuf))
+ {
+ uint8_t *pbReadBuf = (uint8_t *)pJob->pvIoReqReadBuf;
+
+ for (uint32_t i = 0; i < cReqsMax; i++)
+ {
+ pJob->paIoReqs[i].pvXferRead = pbReadBuf;
+ pJob->paIoReqs[i].cbXferRead = cbIoBlock;
+ pbReadBuf += cbIoBlock;
+ }
+
+ /* Create the file. */
+ pJob->pszFilename = RTStrAPrintf2("%sioperf-%u.file", pszTestDir, idJob);
+ if (RT_LIKELY(pJob->pszFilename))
+ {
+ uint32_t fOpen = RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_ASYNC_IO;
+ if (g_fNoCache)
+ fOpen |= RTFILE_O_NO_CACHE;
+ rc = RTFileOpen(&pJob->Hnd.u.hFile, pJob->pszFilename, fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmPrepMethod)
+ {
+ case IOPERFTESTSETPREP_JUST_CREATE:
+ break;
+ case IOPERFTESTSETPREP_SET_SZ:
+ rc = RTFileSetSize(pJob->Hnd.u.hFile, pJob->cbTestSet);
+ break;
+ case IOPERFTESTSETPREP_SET_ALLOC_SZ:
+ rc = RTFileSetAllocationSize(pJob->Hnd.u.hFile, pJob->cbTestSet, RTFILE_ALLOC_SIZE_F_DEFAULT);
+ break;
+ default:
+ AssertMsgFailed(("Invalid file preparation method: %d\n", enmPrepMethod));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = ioPerfJobTestSetPrep(pJob);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create I/O queue. */
+ PCRTIOQUEUEPROVVTABLE pIoQProv = NULL;
+ if (!pszIoEngine)
+ pIoQProv = RTIoQueueProviderGetBestForHndType(RTHANDLETYPE_FILE);
+ else
+ pIoQProv = RTIoQueueProviderGetById(pszIoEngine);
+
+ if (RT_LIKELY(pIoQProv))
+ {
+ rc = RTIoQueueCreate(&pJob->hIoQueue, pIoQProv, 0 /*fFlags*/, cReqsMax, cReqsMax);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTIoQueueHandleRegister(pJob->hIoQueue, &pJob->Hnd);
+ if (RT_SUCCESS(rc))
+ {
+ /* Spin up the worker thread. */
+ if (pMaster)
+ rc = RTThreadCreateF(&pJob->hThread, ioPerfJobThread, pJob, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "ioperf-%u", idJob);
+
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ RTRandAdvDestroy(pJob->hRand);
+ }
+
+ RTFileClose(pJob->Hnd.u.hFile);
+ RTFileDelete(pJob->pszFilename);
+ }
+
+ RTStrFree(pJob->pszFilename);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+
+ RTMemPageFree(pJob->pvIoReqReadBuf, pJob->cbIoReqReadBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ RTMemFree(pJob->paReqStats);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Teardown a job instance and free all associated resources.
+ *
+ * @returns IPRT status code.
+ * @param pJob The job to teardown.
+ */
+static int ioPerfJobTeardown(PIOPERFJOB pJob)
+{
+ if (pJob->pMaster)
+ {
+ int rc = RTThreadWait(pJob->hThread, RT_INDEFINITE_WAIT, NULL);
+ AssertRC(rc); RT_NOREF(rc);
+ }
+
+ RTIoQueueHandleDeregister(pJob->hIoQueue, &pJob->Hnd);
+ RTIoQueueDestroy(pJob->hIoQueue);
+ RTRandAdvDestroy(pJob->hRand);
+ RTMemPageFree(pJob->pbRandWrite, IOPERF_RAND_DATA_BUF_FACTOR * pJob->cbIoBlock);
+ RTFileClose(pJob->Hnd.u.hFile);
+ RTFileDelete(pJob->pszFilename);
+ RTStrFree(pJob->pszFilename);
+ RTMemPageFree(pJob->pvIoReqReadBuf, pJob->cbIoReqReadBuf);
+ RTMemFree(pJob->paIoReqs);
+ RTMemFree(pJob->paReqStats);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Single job testing entry point.
+ *
+ * @returns IPRT status code.
+ */
+static int ioPerfDoTestSingle(void)
+{
+ IOPERFJOB Job;
+
+ int rc = ioPerfJobInit(&Job, NULL, 0, g_pszIoEngine,
+ g_szDir, IOPERFTESTSETPREP_SET_SZ,
+ g_cbTestSet, g_cbIoBlock, g_cReqsMax,
+ g_uWriteChance, g_fVerifyReads);
+ if (RT_SUCCESS(rc))
+ {
+ rc = ioPerfJobWorkLoop(&Job);
+ if (RT_SUCCESS(rc))
+ {
+ rc = ioPerfJobTeardown(&Job);
+ AssertRC(rc); RT_NOREF(rc);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Multi job testing entry point.
+ *
+ * @returns IPRT status code.
+ */
+static int ioPerfDoTestMulti(void)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Display the usage to @a pStrm.
+ */
+static void Usage(PRTSTREAM pStrm)
+{
+ char szExec[RTPATH_MAX];
+ RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
+ RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "options: \n");
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ char szHelp[80];
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'd': pszHelp = "The directory to use for testing. default: CWD/fstestdir"; break;
+ case 'r': pszHelp = "Don't abspath test dir (good for deep dirs). default: disabled"; break;
+ case 'y': pszHelp = "Flag whether to verify read data. default: enabled"; break;
+ case 'c': pszHelp = "Flag whether to use the filesystem cache. default: disabled"; break;
+ case 'v': pszHelp = "More verbose execution."; break;
+ case 'q': pszHelp = "Quiet execution."; break;
+ case 'h': pszHelp = "Displays this help and exit"; break;
+ case 'V': pszHelp = "Displays the program revision"; break;
+ default:
+ if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
+ {
+ if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
+ RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
+ else
+ RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
+ pszHelp = szHelp;
+ }
+ else
+ pszHelp = "Option undocumented";
+ break;
+ }
+ if ((unsigned)g_aCmdOptions[i].iShort < 127U)
+ {
+ char szOpt[64];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
+ }
+ else
+ RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT and globals.
+ */
+ int rc = RTTestInitAndCreate("IoPerf", &g_hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Default values.
+ */
+ char szDefaultDir[32];
+ const char *pszDir = szDefaultDir;
+ RTStrPrintf(szDefaultDir, sizeof(szDefaultDir), "ioperfdir-%u" RTPATH_SLASH_STR, RTProcSelf());
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'd':
+ pszDir = ValueUnion.psz;
+ break;
+
+ case 'r':
+ g_fRelativeDir = true;
+ break;
+
+ case 'i':
+ g_pszIoEngine = ValueUnion.psz;
+ break;
+
+ case 's':
+ g_cbTestSet = ValueUnion.u64;
+ break;
+
+ case 'b':
+ g_cbIoBlock = ValueUnion.u32;
+ break;
+
+ case 'm':
+ g_cReqsMax = ValueUnion.u32;
+ break;
+
+ case 'y':
+ g_fVerifyReads = ValueUnion.f;
+ break;
+
+ case 'c':
+ g_fNoCache = !ValueUnion.f;
+ break;
+
+ case kCmdOpt_FirstWrite:
+ g_aenmTests[IOPERFTEST_FIRST_WRITE] = IOPERFTEST_FIRST_WRITE;
+ break;
+ case kCmdOpt_NoFirstWrite:
+ g_aenmTests[IOPERFTEST_FIRST_WRITE] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_SeqRead:
+ g_aenmTests[IOPERFTEST_SEQ_READ] = IOPERFTEST_SEQ_READ;
+ break;
+ case kCmdOpt_NoSeqRead:
+ g_aenmTests[IOPERFTEST_SEQ_READ] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_SeqWrite:
+ g_aenmTests[IOPERFTEST_SEQ_WRITE] = IOPERFTEST_SEQ_WRITE;
+ break;
+ case kCmdOpt_NoSeqWrite:
+ g_aenmTests[IOPERFTEST_SEQ_WRITE] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_RndRead:
+ g_aenmTests[IOPERFTEST_RND_READ] = IOPERFTEST_RND_READ;
+ break;
+ case kCmdOpt_NoRndRead:
+ g_aenmTests[IOPERFTEST_RND_READ] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_RndWrite:
+ g_aenmTests[IOPERFTEST_RND_WRITE] = IOPERFTEST_RND_WRITE;
+ break;
+ case kCmdOpt_NoRndWrite:
+ g_aenmTests[IOPERFTEST_RND_WRITE] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_RevRead:
+ g_aenmTests[IOPERFTEST_REV_READ] = IOPERFTEST_REV_READ;
+ break;
+ case kCmdOpt_NoRevRead:
+ g_aenmTests[IOPERFTEST_REV_READ] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_RevWrite:
+ g_aenmTests[IOPERFTEST_REV_WRITE] = IOPERFTEST_REV_WRITE;
+ break;
+ case kCmdOpt_NoRevWrite:
+ g_aenmTests[IOPERFTEST_REV_WRITE] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_SeqReadWrite:
+ g_aenmTests[IOPERFTEST_SEQ_READWRITE] = IOPERFTEST_SEQ_READWRITE;
+ break;
+ case kCmdOpt_NoSeqReadWrite:
+ g_aenmTests[IOPERFTEST_SEQ_READWRITE] = IOPERFTEST_DISABLED;
+ break;
+ case kCmdOpt_RndReadWrite:
+ g_aenmTests[IOPERFTEST_RND_READWRITE] = IOPERFTEST_RND_READWRITE;
+ break;
+ case kCmdOpt_NoRndReadWrite:
+ g_aenmTests[IOPERFTEST_RND_READWRITE] = IOPERFTEST_DISABLED;
+ break;
+
+ case 'q':
+ g_uVerbosity = 0;
+ break;
+
+ case 'v':
+ g_uVerbosity++;
+ break;
+
+ case 'h':
+ Usage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ {
+ char szRev[] = "$Revision: 157380 $";
+ szRev[RT_ELEMENTS(szRev) - 2] = '\0';
+ RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
+ return RTEXITCODE_SUCCESS;
+ }
+
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Populate g_szDir.
+ */
+ if (!g_fRelativeDir)
+ rc = RTPathAbs(pszDir, g_szDir, sizeof(g_szDir));
+ else
+ rc = RTStrCopy(g_szDir, sizeof(g_szDir), pszDir);
+ if (RT_FAILURE(rc))
+ {
+ RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc);
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ RTPathEnsureTrailingSeparator(g_szDir, sizeof(g_szDir));
+ g_cchDir = strlen(g_szDir);
+
+ /*
+ * Create the test directory with an 'empty' subdirectory under it,
+ * execute the tests, and remove directory when done.
+ */
+ RTTestBanner(g_hTest);
+ if (!RTPathExists(g_szDir))
+ {
+ /* The base dir: */
+ rc = RTDirCreate(g_szDir, 0755,
+ RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
+
+ if (g_cJobs == 1)
+ rc = ioPerfDoTestSingle();
+ else
+ rc = ioPerfDoTestMulti();
+
+ g_szDir[g_cchDir] = '\0';
+ rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR | (g_fRelativeDir ? RTDIRRMREC_F_NO_ABS_PATH : 0));
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
+ }
+ else
+ RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/ValidationKit/utils/storage/Makefile.kmk b/src/VBox/ValidationKit/utils/storage/Makefile.kmk
new file mode 100644
index 00000000..7042541f
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/storage/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Storage I/O performance tests.
+#
+
+#
+# Copyright (C) 2019-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Storage I/O Performance.
+#
+PROGRAMS += IoPerf
+IoPerf_TEMPLATE = VBoxValidationKitR3
+IoPerf_SOURCES = IoPerf.cpp
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/usb/Makefile.kmk b/src/VBox/ValidationKit/utils/usb/Makefile.kmk
new file mode 100644
index 00000000..e8dc1923
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/Makefile.kmk
@@ -0,0 +1,74 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - USB test helpers.
+#
+
+#
+# Copyright (C) 2014-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# USB Linux test frontend.
+#
+ifeq ($(KBUILD_TARGET),linux)
+ PROGRAMS += UsbTest
+ UsbTest_TEMPLATE = VBoxValidationKitR3
+ UsbTest_SOURCES = UsbTest.cpp
+endif
+
+PROGRAMS += UsbTestService
+UsbTestService_TEMPLATE = VBoxValidationKitR3
+ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING
+ UsbTestService_DEFS = \
+ KBUILD_TARGET="$(KBUILD_TARGET)" \
+ KBUILD_TARGET_ARCH="$(KBUILD_TARGET_ARCH)"
+else
+ UsbTestService_DEFS = \
+ KBUILD_TARGET=\"$(KBUILD_TARGET)\" \
+ KBUILD_TARGET_ARCH=\"$(KBUILD_TARGET_ARCH)\"
+endif
+UsbTestService_SOURCES = \
+ UsbTestService.cpp \
+ UsbTestServiceGadgetCfg.cpp \
+ UsbTestServiceGadgetClassTest.cpp \
+ UsbTestServiceGadgetHost.cpp \
+ UsbTestServiceGadgetHostUsbIp.cpp \
+ UsbTestServiceGadget.cpp \
+ UsbTestServiceProtocol.cpp \
+ UsbTestServiceTcp.cpp
+UsbTestService_SOURCES.linux = \
+ UsbTestServicePlatform-linux.cpp
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTest.cpp b/src/VBox/ValidationKit/utils/usb/UsbTest.cpp
new file mode 100644
index 00000000..809e3fe1
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTest.cpp
@@ -0,0 +1,667 @@
+/* $Id: UsbTest.cpp $ */
+/** @file
+ * UsbTest - User frontend for the Linux usbtest USB test and benchmarking module.
+ * Integrates with our test framework for nice outputs.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+#include <iprt/linux/sysfs.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * USB test request data.
+ * There is no public header with this information so we define it ourself here.
+ */
+typedef struct USBTESTPARMS
+{
+ /** Specifies the test to run. */
+ uint32_t idxTest;
+ /** How many iterations the test should be executed. */
+ uint32_t cIterations;
+ /** Size of the data packets. */
+ uint32_t cbData;
+ /** Size of */
+ uint32_t cbVariation;
+ /** Length of the S/G list for the test. */
+ uint32_t cSgLength;
+ /** Returned time data after completing the test. */
+ struct timeval TimeTest;
+} USBTESTPARAMS;
+/** Pointer to a test parameter structure. */
+typedef USBTESTPARAMS *PUSBTESTPARAMS;
+
+/**
+ * USB device descriptor. Used to search for the test device based
+ * on the vendor and product id.
+ */
+#pragma pack(1)
+typedef struct USBDEVDESC
+{
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+} USBDEVDESC;
+#pragma pack()
+
+#define USBTEST_REQUEST _IOWR('U', 100, USBTESTPARMS)
+
+/**
+ * Callback to set up the test parameters for a specific test.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code
+ * otherwise indicating the kind of error.
+ * @param idxTest The test index.
+ * @param pszTest Test name.
+ * @param pParams The USB test parameters to set up.
+ */
+typedef DECLCALLBACKTYPE(int, FNUSBTESTPARAMSSETUP,(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams));
+/** Pointer to a USB test parameters setup callback. */
+typedef FNUSBTESTPARAMSSETUP *PFNUSBTESTPARAMSSETUP;
+
+/**
+ * USB test descriptor.
+ */
+typedef struct USBTESTDESC
+{
+ /** (Sort of) Descriptive test name. */
+ const char *pszName;
+ /** Flag whether the test is excluded. */
+ bool fExcluded;
+ /** The parameter setup callback. */
+ PFNUSBTESTPARAMSSETUP pfnParamsSetup;
+} USBTESTDESC;
+/** Pointer a USB test descriptor. */
+typedef USBTESTDESC *PUSBTESTDESC;
+
+/**
+ * USB speed values.
+ */
+typedef enum USBTESTSPEED
+{
+ USBTESTSPEED_ANY = 0,
+ USBTESTSPEED_UNKNOWN,
+ USBTESTSPEED_LOW,
+ USBTESTSPEED_FULL,
+ USBTESTSPEED_HIGH,
+ USBTESTSPEED_SUPER
+} USBTESTSPEED;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Some forward method declarations. */
+static DECLCALLBACK(int) usbTestParamsSetupReadWrite(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams);
+static DECLCALLBACK(int) usbTestParamsSetupControlWrites(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams);
+
+/** Command line parameters */
+static const RTGETOPTDEF g_aCmdOptions[] =
+{
+ {"--device", 'd', RTGETOPT_REQ_STRING },
+ {"--help", 'h', RTGETOPT_REQ_NOTHING},
+ {"--exclude", 'e', RTGETOPT_REQ_UINT32},
+ {"--exclude-all", 'a', RTGETOPT_REQ_NOTHING},
+ {"--include", 'i', RTGETOPT_REQ_UINT32},
+ {"--expected-speed", 's', RTGETOPT_REQ_STRING }
+};
+
+static USBTESTDESC g_aTests[] =
+{
+ /* pszTest fExcluded pfnParamsSetup */
+ {"NOP", false, usbTestParamsSetupReadWrite},
+ {"Non-queued Bulk write", false, usbTestParamsSetupReadWrite},
+ {"Non-queued Bulk read", false, usbTestParamsSetupReadWrite},
+ {"Non-queued Bulk write variabe size", false, usbTestParamsSetupReadWrite},
+ {"Non-queued Bulk read variabe size", false, usbTestParamsSetupReadWrite},
+ {"Queued Bulk write", false, usbTestParamsSetupReadWrite},
+ {"Queued Bulk read", false, usbTestParamsSetupReadWrite},
+ {"Queued Bulk write variabe size", false, usbTestParamsSetupReadWrite},
+ {"Queued Bulk read variabe size", false, usbTestParamsSetupReadWrite},
+ {"Chapter 9 Control Test", false, usbTestParamsSetupReadWrite},
+ {"Queued control messaging", false, usbTestParamsSetupReadWrite},
+ {"Unlink reads", false, usbTestParamsSetupReadWrite},
+ {"Unlink writes", false, usbTestParamsSetupReadWrite},
+ {"Set/Clear halts", false, usbTestParamsSetupReadWrite},
+ {"Control writes", false, usbTestParamsSetupControlWrites},
+ {"Isochronous write", false, usbTestParamsSetupReadWrite},
+ {"Isochronous read", false, usbTestParamsSetupReadWrite},
+ {"Bulk write unaligned (DMA)", false, usbTestParamsSetupReadWrite},
+ {"Bulk read unaligned (DMA)", false, usbTestParamsSetupReadWrite},
+ {"Bulk write unaligned (no DMA)", false, usbTestParamsSetupReadWrite},
+ {"Bulk read unaligned (no DMA)", false, usbTestParamsSetupReadWrite},
+ {"Control writes unaligned", false, usbTestParamsSetupControlWrites},
+ {"Isochronous write unaligned", false, usbTestParamsSetupReadWrite},
+ {"Isochronous read unaligned", false, usbTestParamsSetupReadWrite},
+ {"Unlink queued Bulk", false, usbTestParamsSetupReadWrite}
+};
+
+/** The test handle. */
+static RTTEST g_hTest;
+/** The expected device speed. */
+static USBTESTSPEED g_enmSpeed = USBTESTSPEED_ANY;
+
+/**
+ * Setup callback for basic read/write (bulk, isochronous) tests.
+ *
+ * @copydoc FNUSBTESTPARAMSSETUP
+ */
+static DECLCALLBACK(int) usbTestParamsSetupReadWrite(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams)
+{
+ NOREF(idxTest);
+ NOREF(pszTest);
+
+ pParams->cIterations = 1000;
+ pParams->cbData = 512;
+ pParams->cbVariation = 512;
+ pParams->cSgLength = 32;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Setup callback for the control writes test.
+ *
+ * @copydoc FNUSBTESTPARAMSSETUP
+ */
+static DECLCALLBACK(int) usbTestParamsSetupControlWrites(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams)
+{
+ NOREF(idxTest);
+ NOREF(pszTest);
+
+ pParams->cIterations = 1000;
+ pParams->cbData = 512;
+ /*
+ * Must be smaller than cbData or the parameter check in the usbtest module fails,
+ * no idea yet why it must be this.
+ */
+ pParams->cbVariation = 256;
+ pParams->cSgLength = 32;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Shows tool usage text.
+ */
+static void usbTestUsage(PRTSTREAM pStrm)
+{
+ char szExec[RTPATH_MAX];
+ RTStrmPrintf(pStrm, "usage: %s [options]\n",
+ RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
+ RTStrmPrintf(pStrm, "\n");
+ RTStrmPrintf(pStrm, "options: \n");
+
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
+ {
+ const char *pszHelp;
+ switch (g_aCmdOptions[i].iShort)
+ {
+ case 'h':
+ pszHelp = "Displays this help and exit";
+ break;
+ case 'd':
+ pszHelp = "Use the specified test device";
+ break;
+ case 'e':
+ pszHelp = "Exclude the given test id from the list";
+ break;
+ case 'a':
+ pszHelp = "Exclude all tests from the list (useful to enable single tests later with --include)";
+ break;
+ case 'i':
+ pszHelp = "Include the given test id in the list";
+ break;
+ case 's':
+ pszHelp = "The device speed to expect";
+ break;
+ default:
+ pszHelp = "Option undocumented";
+ break;
+ }
+ char szOpt[256];
+ RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
+ RTStrmPrintf(pStrm, " %-30s%s\n", szOpt, pszHelp);
+ }
+}
+
+/**
+ * Searches for a USB test device and returns the bus and device ID and the device speed.
+ */
+static int usbTestDeviceQueryBusAndDevId(uint16_t *pu16BusId, uint16_t *pu16DevId, USBTESTSPEED *penmSpeed)
+{
+ bool fFound = false;
+
+#define USBTEST_USB_DEV_SYSFS "/sys/bus/usb/devices/"
+
+ RTDIR hDirUsb = NULL;
+ int rc = RTDirOpen(&hDirUsb, USBTEST_USB_DEV_SYSFS);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTDIRENTRY DirUsbBus;
+ rc = RTDirRead(hDirUsb, &DirUsbBus, NULL);
+ if ( RT_SUCCESS(rc)
+ && RTStrNCmp(DirUsbBus.szName, "usb", 3)
+ && RTLinuxSysFsExists(USBTEST_USB_DEV_SYSFS "%s/idVendor", DirUsbBus.szName))
+ {
+ int64_t idVendor = 0;
+ int64_t idProduct = 0;
+ int64_t iBusId = 0;
+ int64_t iDevId = 0;
+ char aszSpeed[20];
+
+ rc = RTLinuxSysFsReadIntFile(16, &idVendor, USBTEST_USB_DEV_SYSFS "%s/idVendor", DirUsbBus.szName);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadIntFile(16, &idProduct, USBTEST_USB_DEV_SYSFS "%s/idProduct", DirUsbBus.szName);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadIntFile(16, &iBusId, USBTEST_USB_DEV_SYSFS "%s/busnum", DirUsbBus.szName);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadIntFile(16, &iDevId, USBTEST_USB_DEV_SYSFS "%s/devnum", DirUsbBus.szName);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadStrFile(&aszSpeed[0], sizeof(aszSpeed), NULL, USBTEST_USB_DEV_SYSFS "%s/speed", DirUsbBus.szName);
+
+ if ( RT_SUCCESS(rc)
+ && idVendor == 0x0525
+ && idProduct == 0xa4a0)
+ {
+ if (penmSpeed)
+ {
+ /* Parse the speed. */
+ if (!RTStrCmp(&aszSpeed[0], "1.5"))
+ *penmSpeed = USBTESTSPEED_LOW;
+ else if (!RTStrCmp(&aszSpeed[0], "12"))
+ *penmSpeed = USBTESTSPEED_FULL;
+ else if (!RTStrCmp(&aszSpeed[0], "480"))
+ *penmSpeed = USBTESTSPEED_HIGH;
+ else if ( !RTStrCmp(&aszSpeed[0], "5000")
+ || !RTStrCmp(&aszSpeed[0], "10000"))
+ *penmSpeed = USBTESTSPEED_SUPER;
+ else
+ *penmSpeed = USBTESTSPEED_UNKNOWN;
+ }
+
+ if (pu16BusId)
+ *pu16BusId = (uint16_t)iBusId;
+ if (pu16DevId)
+ *pu16DevId = (uint16_t)iDevId;
+ fFound = true;
+ break;
+ }
+ }
+ else if (rc != VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ } while ( RT_SUCCESS(rc)
+ && !fFound);
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ RTDirClose(hDirUsb);
+ }
+
+ if (RT_SUCCESS(rc) && !fFound)
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+/**
+ * Search for a USB test device and return the device path.
+ *
+ * @returns Path to the USB test device or NULL if none was found.
+ */
+static char *usbTestFindDevice(void)
+{
+ /*
+ * Very crude and quick way to search for the correct test device.
+ * Assumption is that the path looks like /dev/bus/usb/%3d/%3d.
+ */
+ char *pszDevPath = NULL;
+
+ RTDIR hDirUsb = NULL;
+ int rc = RTDirOpen(&hDirUsb, "/dev/bus/usb");
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTDIRENTRY DirUsbBus;
+ rc = RTDirRead(hDirUsb, &DirUsbBus, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char aszPath[RTPATH_MAX + 1];
+ RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath), "/dev/bus/usb/%s", DirUsbBus.szName);
+
+ RTDIR hDirUsbBus = NULL;
+ rc = RTDirOpen(&hDirUsbBus, &aszPath[0]);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTDIRENTRY DirUsbDev;
+ rc = RTDirRead(hDirUsbBus, &DirUsbDev, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char aszPathDev[RTPATH_MAX + 1];
+ RTStrPrintf(&aszPathDev[0], RT_ELEMENTS(aszPathDev), "/dev/bus/usb/%s/%s",
+ DirUsbBus.szName, DirUsbDev.szName);
+
+ RTFILE hFileDev;
+ rc = RTFileOpen(&hFileDev, aszPathDev, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ USBDEVDESC DevDesc;
+
+ rc = RTFileRead(hFileDev, &DevDesc, sizeof(DevDesc), NULL);
+ RTFileClose(hFileDev);
+
+ if ( RT_SUCCESS(rc)
+ && DevDesc.idVendor == 0x0525
+ && DevDesc.idProduct == 0xa4a0)
+ pszDevPath = RTStrDup(aszPathDev);
+ }
+
+ rc = VINF_SUCCESS;
+ }
+ else if (rc != VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ } while ( RT_SUCCESS(rc)
+ && !pszDevPath);
+
+ rc = VINF_SUCCESS;
+ RTDirClose(hDirUsbBus);
+ }
+ }
+ else if (rc != VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+ } while ( RT_SUCCESS(rc)
+ && !pszDevPath);
+
+ RTDirClose(hDirUsb);
+ }
+
+ return pszDevPath;
+}
+
+static int usbTestIoctl(int iDevFd, int iInterface, PUSBTESTPARAMS pParams)
+{
+ struct usbdevfs_ioctl IoCtlData;
+
+ IoCtlData.ifno = iInterface;
+ IoCtlData.ioctl_code = (int)USBTEST_REQUEST;
+ IoCtlData.data = pParams;
+ return ioctl(iDevFd, USBDEVFS_IOCTL, &IoCtlData);
+}
+
+/**
+ * Test execution worker.
+ *
+ * @param pszDevice The device to use for testing.
+ */
+static void usbTestExec(const char *pszDevice)
+{
+ int iDevFd;
+
+ RTTestSub(g_hTest, "Opening device");
+ iDevFd = open(pszDevice, O_RDWR);
+ if (iDevFd != -1)
+ {
+ USBTESTPARAMS Params;
+
+ RTTestPassed(g_hTest, "Opening device successful\n");
+
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+ {
+ RTTestSub(g_hTest, g_aTests[i].pszName);
+
+ if (g_aTests[i].fExcluded)
+ {
+ RTTestSkipped(g_hTest, "Excluded from list");
+ continue;
+ }
+
+ int rc = g_aTests[i].pfnParamsSetup(i, g_aTests[i].pszName, &Params);
+ if (RT_SUCCESS(rc))
+ {
+ Params.idxTest = i;
+
+ /* Assume the test interface has the number 0 for now. */
+ int rcPosix = usbTestIoctl(iDevFd, 0, &Params);
+ if (rcPosix < 0 && errno == EOPNOTSUPP)
+ {
+ RTTestSkipped(g_hTest, "Not supported");
+ continue;
+ }
+
+ if (rcPosix < 0)
+ {
+ /*
+ * The error status code of the unlink testcase is
+ * offset by 2000 for the sync and 1000 for the sync code path
+ * (see drivers/usb/misc/usbtest.c in the Linux kernel sources).
+ *
+ * Adjust to the actual status code so converting doesn't assert.
+ */
+ int iTmpErrno = errno;
+ if (iTmpErrno >= 2000)
+ iTmpErrno -= 2000;
+ else if (iTmpErrno >= 1000)
+ iTmpErrno -= 1000;
+ RTTestFailed(g_hTest, "Test failed with %Rrc\n", RTErrConvertFromErrno(iTmpErrno));
+ }
+ else
+ {
+ uint64_t u64Ns = Params.TimeTest.tv_sec * RT_NS_1SEC + Params.TimeTest.tv_usec * RT_NS_1US;
+ RTTestValue(g_hTest, "Runtime", u64Ns, RTTESTUNIT_NS);
+ }
+ }
+ else
+ RTTestFailed(g_hTest, "Setting up test parameters failed with %Rrc\n", rc);
+ RTTestSubDone(g_hTest);
+ }
+
+ close(iDevFd);
+ }
+ else
+ RTTestFailed(g_hTest, "Opening device failed with %Rrc\n", RTErrConvertFromErrno(errno));
+
+}
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Init IPRT and globals.
+ */
+ int rc = RTTestInitAndCreate("UsbTest", &g_hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Default values.
+ */
+ const char *pszDevice = NULL;
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (rc)
+ {
+ case 'h':
+ usbTestUsage(g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+ case 'd':
+ pszDevice = ValueUnion.psz;
+ break;
+ case 's':
+ if (!RTStrICmp(ValueUnion.psz, "Low"))
+ g_enmSpeed = USBTESTSPEED_LOW;
+ else if (!RTStrICmp(ValueUnion.psz, "Full"))
+ g_enmSpeed = USBTESTSPEED_FULL;
+ else if (!RTStrICmp(ValueUnion.psz, "High"))
+ g_enmSpeed = USBTESTSPEED_HIGH;
+ else if (!RTStrICmp(ValueUnion.psz, "Super"))
+ g_enmSpeed = USBTESTSPEED_SUPER;
+ else
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid speed passed to --expected-speed\n");
+ RTTestErrorInc(g_hTest);
+ return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
+ }
+ break;
+ case 'e':
+ if (ValueUnion.u32 < RT_ELEMENTS(g_aTests))
+ g_aTests[ValueUnion.u32].fExcluded = true;
+ else
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid test number passed to --exclude\n");
+ RTTestErrorInc(g_hTest);
+ return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
+ }
+ break;
+ case 'a':
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+ g_aTests[i].fExcluded = true;
+ break;
+ case 'i':
+ if (ValueUnion.u32 < RT_ELEMENTS(g_aTests))
+ g_aTests[ValueUnion.u32].fExcluded = false;
+ else
+ {
+ RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid test number passed to --include\n");
+ RTTestErrorInc(g_hTest);
+ return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
+ }
+ break;
+ default:
+ return RTGetOptPrintError(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Start testing.
+ */
+ RTTestBanner(g_hTest);
+
+ /* Find the first test device if none was given. */
+ if (!pszDevice)
+ {
+ RTTestSub(g_hTest, "Detecting device");
+ pszDevice = usbTestFindDevice();
+ if (!pszDevice)
+ RTTestFailed(g_hTest, "Failed to find suitable device\n");
+
+ RTTestSubDone(g_hTest);
+ }
+
+ if (pszDevice)
+ {
+ /* First check that the requested speed matches. */
+ if (g_enmSpeed != USBTESTSPEED_ANY)
+ {
+ RTTestSub(g_hTest, "Checking correct device speed");
+
+ USBTESTSPEED enmSpeed = USBTESTSPEED_UNKNOWN;
+ rc = usbTestDeviceQueryBusAndDevId(NULL, NULL, &enmSpeed);
+ if (RT_SUCCESS(rc))
+ {
+ if (enmSpeed == g_enmSpeed)
+ RTTestPassed(g_hTest, "Reported device speed matches requested speed\n");
+ else
+ RTTestFailed(g_hTest, "Reported device speed doesn'match requested speed (%u vs %u)\n",
+ enmSpeed, g_enmSpeed);
+ }
+ else
+ RTTestFailed(g_hTest, "Failed to query device speed with rc=%Rrc\n", rc);
+
+ RTTestSubDone(g_hTest);
+ }
+ usbTestExec(pszDevice);
+ }
+
+ RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestService.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestService.cpp
new file mode 100644
index 00000000..0912ff51
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestService.cpp
@@ -0,0 +1,1658 @@
+/* $Id: UsbTestService.cpp $ */
+/** @file
+ * UsbTestService - Remote USB test configuration and execution server.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/crc.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/handle.h>
+#include <iprt/initterm.h>
+#include <iprt/json.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+#include "UsbTestServiceInternal.h"
+#include "UsbTestServiceGadget.h"
+#include "UsbTestServicePlatform.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+#define UTS_USBIP_PORT_FIRST 3240
+#define UTS_USBIP_PORT_LAST 3340
+
+/**
+ * UTS client state.
+ */
+typedef enum UTSCLIENTSTATE
+{
+ /** Invalid client state. */
+ UTSCLIENTSTATE_INVALID = 0,
+ /** Client is initialising, only the HOWDY and BYE packets are allowed. */
+ UTSCLIENTSTATE_INITIALISING,
+ /** Client is in fully cuntional state and ready to process all requests. */
+ UTSCLIENTSTATE_READY,
+ /** Client is destroying. */
+ UTSCLIENTSTATE_DESTROYING,
+ /** 32bit hack. */
+ UTSCLIENTSTATE_32BIT_HACK = 0x7fffffff
+} UTSCLIENTSTATE;
+
+/**
+ * UTS client instance.
+ */
+typedef struct UTSCLIENT
+{
+ /** List node for new clients. */
+ RTLISTNODE NdLst;
+ /** The current client state. */
+ UTSCLIENTSTATE enmState;
+ /** Transport backend specific data. */
+ PUTSTRANSPORTCLIENT pTransportClient;
+ /** Client hostname. */
+ char *pszHostname;
+ /** Gadget host handle. */
+ UTSGADGETHOST hGadgetHost;
+ /** Handle fo the current configured gadget. */
+ UTSGADGET hGadget;
+} UTSCLIENT;
+/** Pointer to a UTS client instance. */
+typedef UTSCLIENT *PUTSCLIENT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Transport layers.
+ */
+static const PCUTSTRANSPORT g_apTransports[] =
+{
+ &g_TcpTransport,
+ //&g_SerialTransport,
+ //&g_FileSysTransport,
+ //&g_GuestPropTransport,
+ //&g_TestDevTransport,
+};
+
+/** The select transport layer. */
+static PCUTSTRANSPORT g_pTransport;
+/** The config path. */
+static char g_szCfgPath[RTPATH_MAX];
+/** The scratch path. */
+static char g_szScratchPath[RTPATH_MAX];
+/** The default scratch path. */
+static char g_szDefScratchPath[RTPATH_MAX];
+/** The CD/DVD-ROM path. */
+static char g_szCdRomPath[RTPATH_MAX];
+/** The default CD/DVD-ROM path. */
+static char g_szDefCdRomPath[RTPATH_MAX];
+/** The operating system short name. */
+static char g_szOsShortName[16];
+/** The CPU architecture short name. */
+static char g_szArchShortName[16];
+/** The combined "OS.arch" name. */
+static char g_szOsDotArchShortName[32];
+/** The combined "OS/arch" name. */
+static char g_szOsSlashArchShortName[32];
+/** The executable suffix. */
+static char g_szExeSuff[8];
+/** The shell script suffix. */
+static char g_szScriptSuff[8];
+/** Whether to display the output of the child process or not. */
+static bool g_fDisplayOutput = true;
+/** Whether to terminate or not.
+ * @todo implement signals and stuff. */
+static bool volatile g_fTerminate = false;
+/** Configuration AST. */
+static RTJSONVAL g_hCfgJson = NIL_RTJSONVAL;
+/** Pipe for communicating with the serving thread about new clients. - read end */
+static RTPIPE g_hPipeR;
+/** Pipe for communicating with the serving thread about new clients. - write end */
+static RTPIPE g_hPipeW;
+/** Thread serving connected clients. */
+static RTTHREAD g_hThreadServing;
+/** Critical section protecting the list of new clients. */
+static RTCRITSECT g_CritSectClients;
+/** List of new clients waiting to be picked up by the client worker thread. */
+static RTLISTANCHOR g_LstClientsNew;
+/** First USB/IP port we can use. */
+static uint16_t g_uUsbIpPortFirst = UTS_USBIP_PORT_FIRST;
+/** Last USB/IP port we can use. */
+static uint16_t g_uUsbIpPortLast = UTS_USBIP_PORT_LAST;
+/** Next free port. */
+static uint16_t g_uUsbIpPortNext = UTS_USBIP_PORT_FIRST;
+
+
+
+/**
+ * Returns the string represenation of the given state.
+ */
+static const char *utsClientStateStringify(UTSCLIENTSTATE enmState)
+{
+ switch (enmState)
+ {
+ case UTSCLIENTSTATE_INVALID:
+ return "INVALID";
+ case UTSCLIENTSTATE_INITIALISING:
+ return "INITIALISING";
+ case UTSCLIENTSTATE_READY:
+ return "READY";
+ case UTSCLIENTSTATE_DESTROYING:
+ return "DESTROYING";
+ case UTSCLIENTSTATE_32BIT_HACK:
+ default:
+ break;
+ }
+
+ AssertMsgFailed(("Unknown state %#x\n", enmState));
+ return "UNKNOWN";
+}
+
+/**
+ * Calculates the checksum value, zero any padding space and send the packet.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPkt The packet to send. Must point to a correctly
+ * aligned buffer.
+ */
+static int utsSendPkt(PUTSCLIENT pClient, PUTSPKTHDR pPkt)
+{
+ Assert(pPkt->cb >= sizeof(*pPkt));
+ pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(UTSPKTHDR, achOpcode));
+ if (pPkt->cb != RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT))
+ memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, UTSPKT_ALIGNMENT) - pPkt->cb);
+
+ Log(("utsSendPkt: cb=%#x opcode=%.8s\n", pPkt->cb, pPkt->achOpcode));
+ Log2(("%.*Rhxd\n", RT_MIN(pPkt->cb, 256), pPkt));
+ int rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
+ while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !g_fTerminate)
+ rc = g_pTransport->pfnSendPkt(pClient->pTransportClient, pPkt);
+ if (RT_FAILURE(rc))
+ Log(("utsSendPkt: rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+/**
+ * Sends a babble reply and disconnects the client (if applicable).
+ *
+ * @param pClient The UTS client structure.
+ * @param pszOpcode The BABBLE opcode.
+ */
+static void utsReplyBabble(PUTSCLIENT pClient, const char *pszOpcode)
+{
+ UTSPKTHDR Reply;
+ Reply.cb = sizeof(Reply);
+ Reply.uCrc32 = 0;
+ memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
+
+ g_pTransport->pfnBabble(pClient->pTransportClient, &Reply, 20*1000);
+}
+
+/**
+ * Receive and validate a packet.
+ *
+ * Will send bable responses to malformed packets that results in a error status
+ * code.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param ppPktHdr Where to return the packet on success. Free
+ * with RTMemFree.
+ * @param fAutoRetryOnFailure Whether to retry on error.
+ */
+static int utsRecvPkt(PUTSCLIENT pClient, PPUTSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
+{
+ for (;;)
+ {
+ PUTSPKTHDR pPktHdr;
+ int rc = g_pTransport->pfnRecvPkt(pClient->pTransportClient, &pPktHdr);
+ if (RT_SUCCESS(rc))
+ {
+ /* validate the packet. */
+ if ( pPktHdr->cb >= sizeof(UTSPKTHDR)
+ && pPktHdr->cb < UTSPKT_MAX_SIZE)
+ {
+ Log2(("utsRecvPkt: pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n"
+ "%.*Rhxd\n",
+ pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode, RT_MIN(pPktHdr->cb, 256), pPktHdr));
+ uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
+ ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(UTSPKTHDR, achOpcode))
+ : 0;
+ if (pPktHdr->uCrc32 == uCrc32Calc)
+ {
+ AssertCompileMemberSize(UTSPKTHDR, achOpcode, 8);
+ if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
+ && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
+ && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
+ && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
+ )
+ {
+ Log(("utsRecvPkt: cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
+ *ppPktHdr = pPktHdr;
+ return rc;
+ }
+
+ rc = VERR_IO_BAD_COMMAND;
+ }
+ else
+ {
+ Log(("utsRecvPkt: cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
+ pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
+ rc = VERR_IO_CRC;
+ }
+ }
+ else
+ rc = VERR_IO_BAD_LENGTH;
+
+ /* Send babble reply and disconnect the client if the transport is
+ connection oriented. */
+ if (rc == VERR_IO_BAD_LENGTH)
+ utsReplyBabble(pClient, "BABBLE L");
+ else if (rc == VERR_IO_CRC)
+ utsReplyBabble(pClient, "BABBLE C");
+ else if (rc == VERR_IO_BAD_COMMAND)
+ utsReplyBabble(pClient, "BABBLE O");
+ else
+ utsReplyBabble(pClient, "BABBLE ");
+ RTMemFree(pPktHdr);
+ }
+
+ /* Try again or return failure? */
+ if ( g_fTerminate
+ || rc != VERR_INTERRUPTED
+ || !fAutoRetryOnFailure
+ )
+ {
+ Log(("utsRecvPkt: rc=%Rrc\n", rc));
+ return rc;
+ }
+ }
+}
+
+/**
+ * Make a simple reply, only status opcode.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pReply The reply packet.
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param cbExtra Bytes in addition to the header.
+ */
+static int utsReplyInternal(PUTSCLIENT pClient, PUTSPKTSTS pReply, const char *pszOpcode, size_t cbExtra)
+{
+ /* copy the opcode, don't be too strict in case of a padding screw up. */
+ size_t cchOpcode = strlen(pszOpcode);
+ if (RT_LIKELY(cchOpcode == sizeof(pReply->Hdr.achOpcode)))
+ memcpy(pReply->Hdr.achOpcode, pszOpcode, sizeof(pReply->Hdr.achOpcode));
+ else
+ {
+ Assert(cchOpcode == sizeof(pReply->Hdr.achOpcode));
+ while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
+ cchOpcode--;
+ AssertMsgReturn(cchOpcode < sizeof(pReply->Hdr.achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
+ memcpy(pReply->Hdr.achOpcode, pszOpcode, cchOpcode);
+ memset(&pReply->Hdr.achOpcode[cchOpcode], ' ', sizeof(pReply->Hdr.achOpcode) - cchOpcode);
+ }
+
+ pReply->Hdr.cb = (uint32_t)sizeof(UTSPKTSTS) + (uint32_t)cbExtra;
+ pReply->Hdr.uCrc32 = 0;
+
+ return utsSendPkt(pClient, &pReply->Hdr);
+}
+
+/**
+ * Make a simple reply, only status opcode.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The original packet (for future use).
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ */
+static int utsReplySimple(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode)
+{
+ UTSPKTSTS Pkt;
+
+ RT_ZERO(Pkt);
+ Pkt.rcReq = VINF_SUCCESS;
+ Pkt.cchStsMsg = 0;
+ NOREF(pPktHdr);
+ return utsReplyInternal(pClient, &Pkt, pszOpcode, 0);
+}
+
+/**
+ * Acknowledges a packet with success.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The original packet (for future use).
+ */
+static int utsReplyAck(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ return utsReplySimple(pClient, pPktHdr, "ACK ");
+}
+
+/**
+ * Replies with a failure.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The original packet (for future use).
+ * @param rcReq Status code.
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param rcReq The status code of the request.
+ * @param pszDetailFmt Longer description of the problem (format string).
+ * @param va Format arguments.
+ */
+static int utsReplyFailureV(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
+{
+ NOREF(pPktHdr);
+ union
+ {
+ UTSPKTSTS Hdr;
+ char ach[256];
+ } uPkt;
+
+ RT_ZERO(uPkt);
+ size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(UTSPKTSTS)],
+ sizeof(uPkt) - sizeof(UTSPKTSTS),
+ pszDetailFmt, va);
+ uPkt.Hdr.rcReq = rcReq;
+ uPkt.Hdr.cchStsMsg = cchDetail;
+ return utsReplyInternal(pClient, &uPkt.Hdr, pszOpcode, cchDetail + 1);
+}
+
+/**
+ * Replies with a failure.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The original packet (for future use).
+ * @param pszOpcode The status opcode. Exactly 8 chars long, padd
+ * with space.
+ * @param rcReq Status code.
+ * @param pszDetailFmt Longer description of the problem (format string).
+ * @param ... Format arguments.
+ */
+static int utsReplyFailure(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
+{
+ va_list va;
+ va_start(va, pszDetailFmt);
+ int rc = utsReplyFailureV(pClient, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
+ va_end(va);
+ return rc;
+}
+
+/**
+ * Replies according to the return code.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet to reply to.
+ * @param rcOperation The status code to report.
+ * @param pszOperationFmt The operation that failed. Typically giving the
+ * function call with important arguments.
+ * @param ... Arguments to the format string.
+ */
+static int utsReplyRC(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
+{
+ if (RT_SUCCESS(rcOperation))
+ return utsReplyAck(pClient, pPktHdr);
+
+ char szOperation[128];
+ va_list va;
+ va_start(va, pszOperationFmt);
+ RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
+ va_end(va);
+
+ return utsReplyFailure(pClient, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
+ szOperation, rcOperation, pPktHdr->achOpcode);
+}
+
+#if 0 /* unused */
+/**
+ * Signal a bad packet minum size.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet to reply to.
+ * @param cbMin The minimum size.
+ */
+static int utsReplyBadMinSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cbMin)
+{
+ return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at least %zu bytes, got %u (opcode '%.8s')",
+ cbMin, pPktHdr->cb, pPktHdr->achOpcode);
+}
+#endif
+
+/**
+ * Signal a bad packet exact size.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet to reply to.
+ * @param cb The wanted size.
+ */
+static int utsReplyBadSize(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr, size_t cb)
+{
+ return utsReplyFailure(pClient, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
+ cb, pPktHdr->cb, pPktHdr->achOpcode);
+}
+
+#if 0 /* unused */
+/**
+ * Deals with a command that isn't implemented yet.
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet which opcode isn't implemented.
+ */
+static int utsReplyNotImplemented(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ return utsReplyFailure(pClient, pPktHdr, "NOT IMPL", VERR_NOT_IMPLEMENTED, "Opcode '%.8s' is not implemented", pPktHdr->achOpcode);
+}
+#endif
+
+/**
+ * Deals with a unknown command.
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet to reply to.
+ */
+static int utsReplyUnknown(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ return utsReplyFailure(pClient, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
+}
+
+/**
+ * Deals with a command which contains an unterminated string.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet containing the unterminated string.
+ */
+static int utsReplyBadStrTermination(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ return utsReplyFailure(pClient, pPktHdr, "BAD TERM", VERR_INVALID_PARAMETER, "Opcode '%.8s' contains an unterminated string", pPktHdr->achOpcode);
+}
+
+/**
+ * Deals with a command sent in an invalid client state.
+ *
+ * @returns IPRT status code of the send.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The packet containing the unterminated string.
+ */
+static int utsReplyInvalidState(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ return utsReplyFailure(pClient, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
+ pPktHdr->achOpcode, utsClientStateStringify(pClient->enmState));
+}
+
+/**
+ * Parses an unsigned integer from the given value string.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_OUT_OF_RANGE if the parsed value exceeds the given maximum.
+ * @param pszVal The value string.
+ * @param uMax The maximum value.
+ * @param pu64 Where to store the parsed number on success.
+ */
+static int utsDoGadgetCreateCfgParseUInt(const char *pszVal, uint64_t uMax, uint64_t *pu64)
+{
+ int rc = RTStrToUInt64Ex(pszVal, NULL, 0, pu64);
+ if (RT_SUCCESS(rc))
+ {
+ if (*pu64 > uMax)
+ rc = VERR_OUT_OF_RANGE;
+ }
+
+ return rc;
+}
+
+/**
+ * Parses a signed integer from the given value string.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_OUT_OF_RANGE if the parsed value exceeds the given range.
+ * @param pszVal The value string.
+ * @param iMin The minimum value.
+ * @param iMax The maximum value.
+ * @param pi64 Where to store the parsed number on success.
+ */
+static int utsDoGadgetCreateCfgParseInt(const char *pszVal, int64_t iMin, int64_t iMax, int64_t *pi64)
+{
+ int rc = RTStrToInt64Ex(pszVal, NULL, 0, pi64);
+ if (RT_SUCCESS(rc))
+ {
+ if ( *pi64 < iMin
+ || *pi64 > iMax)
+ rc = VERR_OUT_OF_RANGE;
+ }
+
+ return rc;
+}
+
+/**
+ * Parses the given config item and fills in the value according to the given type.
+ *
+ * @returns IPRT status code.
+ * @param pCfgItem The config item to parse.
+ * @param u32Type The config type.
+ * @param pszVal The value encoded as a string.
+ */
+static int utsDoGadgetCreateCfgParseItem(PUTSGADGETCFGITEM pCfgItem, uint32_t u32Type,
+ const char *pszVal)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (u32Type)
+ {
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_BOOLEAN:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_BOOLEAN;
+ if ( RTStrICmp(pszVal, "enabled")
+ || RTStrICmp(pszVal, "1")
+ || RTStrICmp(pszVal, "true"))
+ pCfgItem->Val.u.f = true;
+ else if ( RTStrICmp(pszVal, "disabled")
+ || RTStrICmp(pszVal, "0")
+ || RTStrICmp(pszVal, "false"))
+ pCfgItem->Val.u.f = false;
+ else
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_STRING:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_STRING;
+ pCfgItem->Val.u.psz = RTStrDup(pszVal);
+ if (!pCfgItem->Val.u.psz)
+ rc = VERR_NO_STR_MEMORY;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT8:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT8;
+
+ uint64_t u64;
+ rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT8_MAX, &u64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.u8 = (uint8_t)u64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT16:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT16;
+
+ uint64_t u64;
+ rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT16_MAX, &u64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.u16 = (uint16_t)u64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT32:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT32;
+
+ uint64_t u64;
+ rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT32_MAX, &u64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.u32 = (uint32_t)u64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_UINT64:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_UINT64;
+ rc = utsDoGadgetCreateCfgParseUInt(pszVal, UINT64_MAX, &pCfgItem->Val.u.u64);
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_INT8:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT8;
+
+ int64_t i64;
+ rc = utsDoGadgetCreateCfgParseInt(pszVal, INT8_MIN, INT8_MAX, &i64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.i8 = (int8_t)i64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_INT16:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT16;
+
+ int64_t i64;
+ rc = utsDoGadgetCreateCfgParseInt(pszVal, INT16_MIN, INT16_MAX, &i64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.i16 = (int16_t)i64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_INT32:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT32;
+
+ int64_t i64;
+ rc = utsDoGadgetCreateCfgParseInt(pszVal, INT32_MIN, INT32_MAX, &i64);
+ if (RT_SUCCESS(rc))
+ pCfgItem->Val.u.i32 = (int32_t)i64;
+ break;
+ }
+ case UTSPKT_GDGT_CFG_ITEM_TYPE_INT64:
+ {
+ pCfgItem->Val.enmType = UTSGADGETCFGTYPE_INT64;
+ rc = utsDoGadgetCreateCfgParseInt(pszVal, INT64_MIN, INT64_MAX, &pCfgItem->Val.u.i64);
+ break;
+ }
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+/**
+ * Creates the configuration from the given GADGET CREATE packet.
+ *
+ * @returns IPRT status code.
+ * @param pCfgItem The first config item header in the request packet.
+ * @param cCfgItems Number of config items in the packet to parse.
+ * @param cbPkt Number of bytes left in the packet for the config data.
+ * @param paCfg The array of configuration items to fill.
+ */
+static int utsDoGadgetCreateFillCfg(PUTSPKTREQGDGTCTORCFGITEM pCfgItem, unsigned cCfgItems,
+ size_t cbPkt, PUTSGADGETCFGITEM paCfg)
+{
+ int rc = VINF_SUCCESS;
+ unsigned idxCfg = 0;
+
+ while ( RT_SUCCESS(rc)
+ && cCfgItems
+ && cbPkt)
+ {
+ if (cbPkt >= sizeof(UTSPKTREQGDGTCTORCFGITEM))
+ {
+ cbPkt -= sizeof(UTSPKTREQGDGTCTORCFGITEM);
+ if (pCfgItem->u32KeySize + pCfgItem->u32ValSize >= cbPkt)
+ {
+ const char *pszKey = (const char *)(pCfgItem + 1);
+ const char *pszVal = pszKey + pCfgItem->u32KeySize;
+
+ /* Validate termination. */
+ if ( *(pszKey + pCfgItem->u32KeySize - 1) != '\0'
+ || *(pszVal + pCfgItem->u32ValSize - 1) != '\0')
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ paCfg[idxCfg].pszKey = RTStrDup(pszKey);
+
+ rc = utsDoGadgetCreateCfgParseItem(&paCfg[idxCfg], pCfgItem->u32Type, pszVal);
+ if (RT_SUCCESS(rc))
+ {
+ cbPkt -= pCfgItem->u32KeySize + pCfgItem->u32ValSize;
+ cCfgItems--;
+ idxCfg++;
+ pCfgItem = (PUTSPKTREQGDGTCTORCFGITEM)(pszVal + pCfgItem->u32ValSize);
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+/**
+ * Verifies and acknowledges a "BYE" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The howdy packet.
+ */
+static int utsDoBye(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ int rc;
+ if (pPktHdr->cb == sizeof(UTSPKTHDR))
+ rc = utsReplyAck(pClient, pPktHdr);
+ else
+ rc = utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTHDR));
+ return rc;
+}
+
+/**
+ * Verifies and acknowledges a "HOWDY" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The howdy packet.
+ */
+static int utsDoHowdy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pPktHdr->cb != sizeof(UTSPKTREQHOWDY))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQHOWDY));
+
+ if (pClient->enmState != UTSCLIENTSTATE_INITIALISING)
+ return utsReplyInvalidState(pClient, pPktHdr);
+
+ PUTSPKTREQHOWDY pReq = (PUTSPKTREQHOWDY)pPktHdr;
+
+ if (pReq->uVersion != UTS_PROTOCOL_VS)
+ return utsReplyRC(pClient, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
+
+ /* Verify hostname string. */
+ if (pReq->cchHostname >= sizeof(pReq->achHostname))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(pReq->achHostname) - 1);
+
+ if (pReq->achHostname[pReq->cchHostname] != '\0')
+ return utsReplyBadStrTermination(pClient, pPktHdr);
+
+ /* Extract string. */
+ pClient->pszHostname = RTStrDup(&pReq->achHostname[0]);
+ if (!pClient->pszHostname)
+ return utsReplyRC(pClient, pPktHdr, VERR_NO_MEMORY, "Failed to allocate memory for the hostname string");
+
+ if (pReq->fUsbConn & UTSPKT_HOWDY_CONN_F_PHYSICAL)
+ return utsReplyRC(pClient, pPktHdr, VERR_NOT_SUPPORTED, "Physical connections are not yet supported");
+
+ if (pReq->fUsbConn & UTSPKT_HOWDY_CONN_F_USBIP)
+ {
+ /* Set up the USB/IP server, find an unused port we can start the server on. */
+ UTSGADGETCFGITEM aCfg[2];
+
+ uint16_t uPort = g_uUsbIpPortNext;
+
+ if (g_uUsbIpPortNext == g_uUsbIpPortLast)
+ g_uUsbIpPortNext = g_uUsbIpPortFirst;
+ else
+ g_uUsbIpPortNext++;
+
+ aCfg[0].pszKey = "UsbIp/Port";
+ aCfg[0].Val.enmType = UTSGADGETCFGTYPE_UINT16;
+ aCfg[0].Val.u.u16 = uPort;
+ aCfg[1].pszKey = NULL;
+
+ rc = utsGadgetHostCreate(UTSGADGETHOSTTYPE_USBIP, &aCfg[0], &pClient->hGadgetHost);
+ if (RT_SUCCESS(rc))
+ {
+ /* Send the reply with the configured USB/IP port. */
+ UTSPKTREPHOWDY Rep;
+
+ RT_ZERO(Rep);
+
+ Rep.uVersion = UTS_PROTOCOL_VS;
+ Rep.fUsbConn = UTSPKT_HOWDY_CONN_F_USBIP;
+ Rep.uUsbIpPort = uPort;
+ Rep.cUsbIpDevices = 1;
+ Rep.cPhysicalDevices = 0;
+
+ rc = utsReplyInternal(pClient, &Rep.Sts, "ACK ", sizeof(Rep) - sizeof(UTSPKTSTS));
+ if (RT_SUCCESS(rc))
+ {
+ g_pTransport->pfnNotifyHowdy(pClient->pTransportClient);
+ pClient->enmState = UTSCLIENTSTATE_READY;
+ RTDirRemoveRecursive(g_szScratchPath, RTDIRRMREC_F_CONTENT_ONLY);
+ }
+ }
+ else
+ return utsReplyRC(pClient, pPktHdr, rc, "Creating the USB/IP gadget host failed");
+ }
+ else
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "No access method requested");
+
+ return rc;
+}
+
+/**
+ * Verifies and processes a "GADGET CREATE" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The gadget create packet.
+ */
+static int utsDoGadgetCreate(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pPktHdr->cb < sizeof(UTSPKTREQGDGTCTOR))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTCTOR));
+
+ if ( pClient->enmState != UTSCLIENTSTATE_READY
+ || pClient->hGadgetHost == NIL_UTSGADGETHOST)
+ return utsReplyInvalidState(pClient, pPktHdr);
+
+ PUTSPKTREQGDGTCTOR pReq = (PUTSPKTREQGDGTCTOR)pPktHdr;
+
+ if (pReq->u32GdgtType != UTSPKT_GDGT_CREATE_TYPE_TEST)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "The given gadget type is not supported");
+
+ if (pReq->u32GdgtAccess != UTSPKT_GDGT_CREATE_ACCESS_USBIP)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_PARAMETER, "The given gadget access method is not supported");
+
+ PUTSGADGETCFGITEM paCfg = NULL;
+ if (pReq->u32CfgItems > 0)
+ {
+ paCfg = (PUTSGADGETCFGITEM)RTMemAllocZ((pReq->u32CfgItems + 1) * sizeof(UTSGADGETCFGITEM));
+ if (RT_UNLIKELY(!paCfg))
+ return utsReplyRC(pClient, pPktHdr, VERR_NO_MEMORY, "Failed to allocate memory for configration items");
+
+ rc = utsDoGadgetCreateFillCfg((PUTSPKTREQGDGTCTORCFGITEM)(pReq + 1), pReq->u32CfgItems,
+ pPktHdr->cb - sizeof(UTSPKTREQGDGTCTOR), paCfg);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(paCfg);
+ return utsReplyRC(pClient, pPktHdr, rc, "Failed to parse configuration");
+ }
+ }
+
+ rc = utsGadgetCreate(pClient->hGadgetHost, UTSGADGETCLASS_TEST, paCfg, &pClient->hGadget);
+ if (RT_SUCCESS(rc))
+ {
+ UTSPKTREPGDGTCTOR Rep;
+ RT_ZERO(Rep);
+
+ Rep.idGadget = 0;
+ Rep.u32BusId = utsGadgetGetBusId(pClient->hGadget);
+ Rep.u32DevId = utsGadgetGetDevId(pClient->hGadget);
+ rc = utsReplyInternal(pClient, &Rep.Sts, "ACK ", sizeof(Rep) - sizeof(UTSPKTSTS));
+ }
+ else
+ rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to create gadget with %Rrc\n", rc);
+
+ return rc;
+}
+
+/**
+ * Verifies and processes a "GADGET DESTROY" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The gadget destroy packet.
+ */
+static int utsDoGadgetDestroy(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(UTSPKTREQGDGTDTOR))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTDTOR));
+
+ if ( pClient->enmState != UTSCLIENTSTATE_READY
+ || pClient->hGadgetHost == NIL_UTSGADGETHOST)
+ return utsReplyInvalidState(pClient, pPktHdr);
+
+ PUTSPKTREQGDGTDTOR pReq = (PUTSPKTREQGDGTDTOR)pPktHdr;
+
+ if (pReq->idGadget != 0)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
+ if (pClient->hGadget == NIL_UTSGADGET)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
+
+ utsGadgetRelease(pClient->hGadget);
+ pClient->hGadget = NIL_UTSGADGET;
+
+ return utsReplyAck(pClient, pPktHdr);
+}
+
+/**
+ * Verifies and processes a "GADGET CONNECT" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The gadget connect packet.
+ */
+static int utsDoGadgetConnect(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(UTSPKTREQGDGTCNCT))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTCNCT));
+
+ if ( pClient->enmState != UTSCLIENTSTATE_READY
+ || pClient->hGadgetHost == NIL_UTSGADGETHOST)
+ return utsReplyInvalidState(pClient, pPktHdr);
+
+ PUTSPKTREQGDGTCNCT pReq = (PUTSPKTREQGDGTCNCT)pPktHdr;
+
+ if (pReq->idGadget != 0)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
+ if (pClient->hGadget == NIL_UTSGADGET)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
+
+ int rc = utsGadgetConnect(pClient->hGadget);
+ if (RT_SUCCESS(rc))
+ rc = utsReplyAck(pClient, pPktHdr);
+ else
+ rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to connect the gadget");
+
+ return rc;
+}
+
+/**
+ * Verifies and processes a "GADGET DISCONNECT" request.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure.
+ * @param pPktHdr The gadget disconnect packet.
+ */
+static int utsDoGadgetDisconnect(PUTSCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ if (pPktHdr->cb != sizeof(UTSPKTREQGDGTDCNT))
+ return utsReplyBadSize(pClient, pPktHdr, sizeof(UTSPKTREQGDGTDCNT));
+
+ if ( pClient->enmState != UTSCLIENTSTATE_READY
+ || pClient->hGadgetHost == NIL_UTSGADGETHOST)
+ return utsReplyInvalidState(pClient, pPktHdr);
+
+ PUTSPKTREQGDGTDCNT pReq = (PUTSPKTREQGDGTDCNT)pPktHdr;
+
+ if (pReq->idGadget != 0)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_HANDLE, "The given gadget handle is invalid");
+ if (pClient->hGadget == NIL_UTSGADGET)
+ return utsReplyRC(pClient, pPktHdr, VERR_INVALID_STATE, "The gadget is not set up");
+
+ int rc = utsGadgetDisconnect(pClient->hGadget);
+ if (RT_SUCCESS(rc))
+ rc = utsReplyAck(pClient, pPktHdr);
+ else
+ rc = utsReplyRC(pClient, pPktHdr, rc, "Failed to disconnect the gadget");
+
+ return rc;
+}
+
+/**
+ * Main request processing routine for each client.
+ *
+ * @returns IPRT status code.
+ * @param pClient The UTS client structure sending the request.
+ */
+static int utsClientReqProcess(PUTSCLIENT pClient)
+{
+ /*
+ * Read client command packet and process it.
+ */
+ PUTSPKTHDR pPktHdr = NULL;
+ int rc = utsRecvPkt(pClient, &pPktHdr, true /*fAutoRetryOnFailure*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do a string switch on the opcode bit.
+ */
+ /* Connection: */
+ if ( utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_HOWDY))
+ rc = utsDoHowdy(pClient, pPktHdr);
+ else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_BYE))
+ rc = utsDoBye(pClient, pPktHdr);
+ /* Gadget API. */
+ else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_CREATE))
+ rc = utsDoGadgetCreate(pClient, pPktHdr);
+ else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_DESTROY))
+ rc = utsDoGadgetDestroy(pClient, pPktHdr);
+ else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_CONNECT))
+ rc = utsDoGadgetConnect(pClient, pPktHdr);
+ else if (utsIsSameOpcode(pPktHdr, UTSPKT_OPCODE_GADGET_DISCONNECT))
+ rc = utsDoGadgetDisconnect(pClient, pPktHdr);
+ /* Misc: */
+ else
+ rc = utsReplyUnknown(pClient, pPktHdr);
+
+ RTMemFree(pPktHdr);
+
+ return rc;
+}
+
+/**
+ * Destroys a client instance.
+ *
+ * @param pClient The UTS client structure.
+ */
+static void utsClientDestroy(PUTSCLIENT pClient)
+{
+ if (pClient->pszHostname)
+ RTStrFree(pClient->pszHostname);
+ if (pClient->hGadget != NIL_UTSGADGET)
+ utsGadgetRelease(pClient->hGadget);
+ if (pClient->hGadgetHost != NIL_UTSGADGETHOST)
+ utsGadgetHostRelease(pClient->hGadgetHost);
+ RTMemFree(pClient);
+}
+
+/**
+ * The main thread worker serving the clients.
+ */
+static DECLCALLBACK(int) utsClientWorker(RTTHREAD hThread, void *pvUser)
+{
+ RT_NOREF2(hThread, pvUser);
+ unsigned cClientsMax = 0;
+ unsigned cClientsCur = 0;
+ PUTSCLIENT *papClients = NULL;
+ RTPOLLSET hPollSet;
+
+ int rc = RTPollSetCreate(&hPollSet);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Add the pipe to the poll set. */
+ rc = RTPollSetAddPipe(hPollSet, g_hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
+ if (RT_SUCCESS(rc))
+ {
+ while (!g_fTerminate)
+ {
+ uint32_t fEvts;
+ uint32_t uId;
+ rc = RTPoll(hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
+ if (RT_SUCCESS(rc))
+ {
+ if (uId == 0)
+ {
+ if (fEvts & RTPOLL_EVT_ERROR)
+ break;
+
+ /* We got woken up because of a new client. */
+ Assert(fEvts & RTPOLL_EVT_READ);
+
+ uint8_t bRead;
+ size_t cbRead = 0;
+ rc = RTPipeRead(g_hPipeR, &bRead, 1, &cbRead);
+ AssertRC(rc);
+
+ RTCritSectEnter(&g_CritSectClients);
+ /* Walk the list and add all new clients. */
+ PUTSCLIENT pIt, pItNext;
+ RTListForEachSafe(&g_LstClientsNew, pIt, pItNext, UTSCLIENT, NdLst)
+ {
+ RTListNodeRemove(&pIt->NdLst);
+ Assert(cClientsCur <= cClientsMax);
+ if (cClientsCur == cClientsMax)
+ {
+ /* Realloc to accommodate for the new clients. */
+ PUTSCLIENT *papClientsNew = (PUTSCLIENT *)RTMemReallocZ(papClients, cClientsMax * sizeof(PUTSCLIENT), (cClientsMax + 10) * sizeof(PUTSCLIENT));
+ if (RT_LIKELY(papClientsNew))
+ {
+ cClientsMax += 10;
+ papClients = papClientsNew;
+ }
+ }
+
+ if (cClientsCur < cClientsMax)
+ {
+ /* Find a free slot in the client array. */
+ unsigned idxSlt = 0;
+ while ( idxSlt < cClientsMax
+ && papClients[idxSlt] != NULL)
+ idxSlt++;
+
+ rc = g_pTransport->pfnPollSetAdd(hPollSet, pIt->pTransportClient, idxSlt + 1);
+ if (RT_SUCCESS(rc))
+ {
+ cClientsCur++;
+ papClients[idxSlt] = pIt;
+ }
+ else
+ {
+ g_pTransport->pfnNotifyBye(pIt->pTransportClient);
+ utsClientDestroy(pIt);
+ }
+ }
+ else
+ {
+ g_pTransport->pfnNotifyBye(pIt->pTransportClient);
+ utsClientDestroy(pIt);
+ }
+ }
+ RTCritSectLeave(&g_CritSectClients);
+ }
+ else
+ {
+ /* Client sends a request, pick the right client and process it. */
+ PUTSCLIENT pClient = papClients[uId - 1];
+ AssertPtr(pClient);
+ if (fEvts & RTPOLL_EVT_READ)
+ rc = utsClientReqProcess(pClient);
+
+ if ( (fEvts & RTPOLL_EVT_ERROR)
+ || RT_FAILURE(rc))
+ {
+ /* Close connection and remove client from array. */
+ rc = g_pTransport->pfnPollSetRemove(hPollSet, pClient->pTransportClient, uId);
+ AssertRC(rc);
+
+ g_pTransport->pfnNotifyBye(pClient->pTransportClient);
+ papClients[uId - 1] = NULL;
+ cClientsCur--;
+ utsClientDestroy(pClient);
+ }
+ }
+ }
+ }
+ }
+
+ RTPollSetDestroy(hPollSet);
+
+ return rc;
+}
+
+/**
+ * The main loop.
+ *
+ * @returns exit code.
+ */
+static RTEXITCODE utsMainLoop(void)
+{
+ RTEXITCODE enmExitCode = RTEXITCODE_SUCCESS;
+ while (!g_fTerminate)
+ {
+ /*
+ * Wait for new connection and spin off a new thread
+ * for every new client.
+ */
+ PUTSTRANSPORTCLIENT pTransportClient;
+ int rc = g_pTransport->pfnWaitForConnect(&pTransportClient);
+ if (RT_FAILURE(rc))
+ continue;
+
+ /*
+ * New connection, create new client structure and spin of
+ * the request handling thread.
+ */
+ PUTSCLIENT pClient = (PUTSCLIENT)RTMemAllocZ(sizeof(UTSCLIENT));
+ if (RT_LIKELY(pClient))
+ {
+ pClient->enmState = UTSCLIENTSTATE_INITIALISING;
+ pClient->pTransportClient = pTransportClient;
+ pClient->pszHostname = NULL;
+ pClient->hGadgetHost = NIL_UTSGADGETHOST;
+ pClient->hGadget = NIL_UTSGADGET;
+
+ /* Add client to the new list and inform the worker thread. */
+ RTCritSectEnter(&g_CritSectClients);
+ RTListAppend(&g_LstClientsNew, &pClient->NdLst);
+ RTCritSectLeave(&g_CritSectClients);
+
+ size_t cbWritten = 0;
+ rc = RTPipeWrite(g_hPipeW, "", 1, &cbWritten);
+ if (RT_FAILURE(rc))
+ RTMsgError("Failed to inform worker thread of a new client");
+ }
+ else
+ {
+ RTMsgError("Creating new client structure failed with out of memory error\n");
+ g_pTransport->pfnNotifyBye(pTransportClient);
+ }
+
+
+ }
+
+ return enmExitCode;
+}
+
+/**
+ * Initializes the global UTS state.
+ *
+ * @returns IPRT status code.
+ */
+static int utsInit(void)
+{
+ int rc = VINF_SUCCESS;
+ PRTERRINFO pErrInfo = NULL;
+
+ RTListInit(&g_LstClientsNew);
+
+ rc = RTJsonParseFromFile(&g_hCfgJson, g_szCfgPath, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = utsPlatformInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&g_CritSectClients);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPipeCreate(&g_hPipeR, &g_hPipeW, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* Spin off the thread serving connections. */
+ rc = RTThreadCreate(&g_hThreadServing, utsClientWorker, NULL, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
+ "USBTSTSRV");
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ else
+ RTMsgError("Creating the client worker thread failed with %Rrc\n", rc);
+
+ RTPipeClose(g_hPipeR);
+ RTPipeClose(g_hPipeW);
+ }
+ else
+ RTMsgError("Creating communications pipe failed with %Rrc\n", rc);
+
+ RTCritSectDelete(&g_CritSectClients);
+ }
+ else
+ RTMsgError("Creating global critical section failed with %Rrc\n", rc);
+
+ RTJsonValueRelease(g_hCfgJson);
+ }
+ else
+ RTMsgError("Initializing the platform failed with %Rrc\n", rc);
+ }
+ else
+ {
+ if (RTErrInfoIsSet(pErrInfo))
+ {
+ RTMsgError("Failed to parse config with detailed error: %s (%Rrc)\n",
+ pErrInfo->pszMsg, pErrInfo->rc);
+ RTErrInfoFree(pErrInfo);
+ }
+ else
+ RTMsgError("Failed to parse config with unknown error (%Rrc)\n", rc);
+ }
+
+ return rc;
+}
+
+/**
+ * Determines the default configuration.
+ */
+static void utsSetDefaults(void)
+{
+ /*
+ * OS and ARCH.
+ */
+ AssertCompile(sizeof(KBUILD_TARGET) <= sizeof(g_szOsShortName));
+ strcpy(g_szOsShortName, KBUILD_TARGET);
+
+ AssertCompile(sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szArchShortName));
+ strcpy(g_szArchShortName, KBUILD_TARGET_ARCH);
+
+ AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsDotArchShortName));
+ strcpy(g_szOsDotArchShortName, KBUILD_TARGET);
+ g_szOsDotArchShortName[sizeof(KBUILD_TARGET) - 1] = '.';
+ strcpy(&g_szOsDotArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
+
+ AssertCompile(sizeof(KBUILD_TARGET) + sizeof(KBUILD_TARGET_ARCH) <= sizeof(g_szOsSlashArchShortName));
+ strcpy(g_szOsSlashArchShortName, KBUILD_TARGET);
+ g_szOsSlashArchShortName[sizeof(KBUILD_TARGET) - 1] = '/';
+ strcpy(&g_szOsSlashArchShortName[sizeof(KBUILD_TARGET)], KBUILD_TARGET_ARCH);
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ strcpy(g_szExeSuff, ".exe");
+ strcpy(g_szScriptSuff, ".cmd");
+#else
+ strcpy(g_szExeSuff, "");
+ strcpy(g_szScriptSuff, ".sh");
+#endif
+
+ /*
+ * The CD/DVD-ROM location.
+ */
+ /** @todo do a better job here :-) */
+#ifdef RT_OS_WINDOWS
+ strcpy(g_szDefCdRomPath, "D:/");
+#elif defined(RT_OS_OS2)
+ strcpy(g_szDefCdRomPath, "D:/");
+#else
+ if (RTDirExists("/media"))
+ strcpy(g_szDefCdRomPath, "/media/cdrom");
+ else
+ strcpy(g_szDefCdRomPath, "/mnt/cdrom");
+#endif
+ strcpy(g_szCdRomPath, g_szDefCdRomPath);
+
+ /*
+ * Temporary directory.
+ */
+ int rc = RTPathTemp(g_szDefScratchPath, sizeof(g_szDefScratchPath));
+ if (RT_SUCCESS(rc))
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DOS)
+ rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXX.tmp");
+#else
+ rc = RTPathAppend(g_szDefScratchPath, sizeof(g_szDefScratchPath), "uts-XXXXXXXXX.tmp");
+#endif
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("RTPathTemp/Append failed when constructing scratch path: %Rrc\n", rc);
+ strcpy(g_szDefScratchPath, "/tmp/uts-XXXX.tmp");
+ }
+ strcpy(g_szScratchPath, g_szDefScratchPath);
+
+ /*
+ * Config file location.
+ */
+ /** @todo Improve */
+#if !defined(RT_OS_WINDOWS)
+ strcpy(g_szCfgPath, "/etc/uts.conf");
+#else
+ strcpy(g_szCfgPath, "");
+#endif
+
+ /*
+ * The default transporter is the first one.
+ */
+ g_pTransport = g_apTransports[0];
+}
+
+/**
+ * Prints the usage.
+ *
+ * @param pStrm Where to print it.
+ * @param pszArgv0 The program name (argv[0]).
+ */
+static void utsUsage(PRTSTREAM pStrm, const char *pszArgv0)
+{
+ RTStrmPrintf(pStrm,
+ "Usage: %Rbn [options]\n"
+ "\n"
+ "Options:\n"
+ " --config <path>\n"
+ " Where to load the config from\n"
+ " --cdrom <path>\n"
+ " Where the CD/DVD-ROM will be mounted.\n"
+ " Default: %s\n"
+ " --scratch <path>\n"
+ " Where to put scratch files.\n"
+ " Default: %s \n"
+ ,
+ pszArgv0,
+ g_szDefCdRomPath,
+ g_szDefScratchPath);
+ RTStrmPrintf(pStrm,
+ " --transport <name>\n"
+ " Use the specified transport layer, one of the following:\n");
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ RTStrmPrintf(pStrm, " %s - %s\n", g_apTransports[i]->szName, g_apTransports[i]->pszDesc);
+ RTStrmPrintf(pStrm, " Default: %s\n", g_pTransport->szName);
+ RTStrmPrintf(pStrm,
+ " --display-output, --no-display-output\n"
+ " Display the output and the result of all child processes.\n");
+ RTStrmPrintf(pStrm,
+ " --foreground\n"
+ " Don't daemonize, run in the foreground.\n");
+ RTStrmPrintf(pStrm,
+ " --help, -h, -?\n"
+ " Display this message and exit.\n"
+ " --version, -V\n"
+ " Display the version and exit.\n");
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (g_apTransports[i]->cOpts)
+ {
+ RTStrmPrintf(pStrm,
+ "\n"
+ "Options for %s:\n", g_apTransports[i]->szName);
+ g_apTransports[i]->pfnUsage(g_pStdOut);
+ }
+}
+
+/**
+ * Parses the arguments.
+ *
+ * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
+ * @param argc The number of arguments.
+ * @param argv The argument vector.
+ * @param pfExit For indicating exit when the exit code is zero.
+ */
+static RTEXITCODE utsParseArgv(int argc, char **argv, bool *pfExit)
+{
+ *pfExit = false;
+
+ /*
+ * Storage for locally handled options.
+ */
+ bool fDaemonize = true;
+ bool fDaemonized = false;
+
+ /*
+ * Combine the base and transport layer option arrays.
+ */
+ static const RTGETOPTDEF s_aBaseOptions[] =
+ {
+ { "--config", 'C', RTGETOPT_REQ_STRING },
+ { "--transport", 't', RTGETOPT_REQ_STRING },
+ { "--cdrom", 'c', RTGETOPT_REQ_STRING },
+ { "--scratch", 's', RTGETOPT_REQ_STRING },
+ { "--display-output", 'd', RTGETOPT_REQ_NOTHING },
+ { "--no-display-output",'D', RTGETOPT_REQ_NOTHING },
+ { "--foreground", 'f', RTGETOPT_REQ_NOTHING },
+ { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
+ };
+
+ size_t cOptions = RT_ELEMENTS(s_aBaseOptions);
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ cOptions += g_apTransports[i]->cOpts;
+
+ PRTGETOPTDEF paOptions = (PRTGETOPTDEF)alloca(cOptions * sizeof(RTGETOPTDEF));
+ if (!paOptions)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "alloca failed\n");
+
+ memcpy(paOptions, s_aBaseOptions, sizeof(s_aBaseOptions));
+ cOptions = RT_ELEMENTS(s_aBaseOptions);
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ {
+ memcpy(&paOptions[cOptions], g_apTransports[i]->paOpts, g_apTransports[i]->cOpts * sizeof(RTGETOPTDEF));
+ cOptions += g_apTransports[i]->cOpts;
+ }
+
+ /*
+ * Parse the arguments.
+ */
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv, paOptions, cOptions, 1, 0 /* fFlags */);
+ AssertRC(rc);
+
+ int ch;
+ RTGETOPTUNION Val;
+ while ((ch = RTGetOpt(&GetState, &Val)))
+ {
+ switch (ch)
+ {
+ case 'C':
+ rc = RTStrCopy(g_szCfgPath, sizeof(g_szCfgPath), Val.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Config file path is path too long (%Rrc)\n", rc);
+ break;
+
+ case 'c':
+ rc = RTStrCopy(g_szCdRomPath, sizeof(g_szCdRomPath), Val.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "CD/DVD-ROM is path too long (%Rrc)\n", rc);
+ break;
+
+ case 'd':
+ g_fDisplayOutput = true;
+ break;
+
+ case 'D':
+ g_fDisplayOutput = false;
+ break;
+
+ case 'f':
+ fDaemonize = false;
+ break;
+
+ case 'h':
+ utsUsage(g_pStdOut, argv[0]);
+ *pfExit = true;
+ return RTEXITCODE_SUCCESS;
+
+ case 's':
+ rc = RTStrCopy(g_szScratchPath, sizeof(g_szScratchPath), Val.psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "scratch path is too long (%Rrc)\n", rc);
+ break;
+
+ case 't':
+ {
+ PCUTSTRANSPORT pTransport = NULL;
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (!strcmp(g_apTransports[i]->szName, Val.psz))
+ {
+ pTransport = g_apTransports[i];
+ break;
+ }
+ if (!pTransport)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown transport layer name '%s'\n", Val.psz);
+ g_pTransport = pTransport;
+ break;
+ }
+
+ case 'V':
+ RTPrintf("$Revision: 157380 $\n");
+ *pfExit = true;
+ return RTEXITCODE_SUCCESS;
+
+ case 'Z':
+ fDaemonized = true;
+ fDaemonize = false;
+ break;
+
+ default:
+ {
+ rc = VERR_TRY_AGAIN;
+ for (size_t i = 0; i < RT_ELEMENTS(g_apTransports); i++)
+ if (g_apTransports[i]->cOpts)
+ {
+ rc = g_apTransports[i]->pfnOption(ch, &Val);
+ if (RT_SUCCESS(rc))
+ break;
+ if (rc != VERR_TRY_AGAIN)
+ {
+ *pfExit = true;
+ return RTEXITCODE_SYNTAX;
+ }
+ }
+ if (rc == VERR_TRY_AGAIN)
+ {
+ *pfExit = true;
+ return RTGetOptPrintError(ch, &Val);
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Daemonize ourselves if asked to.
+ */
+ if (fDaemonize && !*pfExit)
+ {
+ rc = RTProcDaemonize(argv, "--daemonized");
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
+ *pfExit = true;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Initialize the runtime.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Determine defaults and parse the arguments.
+ */
+ utsSetDefaults();
+ bool fExit;
+ RTEXITCODE rcExit = utsParseArgv(argc, argv, &fExit);
+ if (rcExit != RTEXITCODE_SUCCESS || fExit)
+ return rcExit;
+
+ /*
+ * Initialize global state.
+ */
+ rc = utsInit();
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+
+ /*
+ * Initialize the transport layer.
+ */
+ rc = g_pTransport->pfnInit();
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+
+ /*
+ * Ok, start working
+ */
+ rcExit = utsMainLoop();
+
+ /*
+ * Cleanup.
+ */
+ g_pTransport->pfnTerm();
+
+ utsPlatformTerm();
+
+ return rcExit;
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.cpp
new file mode 100644
index 00000000..05b4c569
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.cpp
@@ -0,0 +1,211 @@
+/* $Id: UsbTestServiceGadget.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, USB gadget host API.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "UsbTestServiceGadgetInternal.h"
+
+
+/*********************************************************************************************************************************
+* Constants And Macros, Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal UTS gadget host instance data.
+ */
+typedef struct UTSGADGETINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Pointer to the gadget class callback table. */
+ PCUTSGADGETCLASSIF pClassIf;
+ /** The gadget host handle. */
+ UTSGADGETHOST hGadgetHost;
+ /** Class specific instance data - variable in size. */
+ uint8_t abClassInst[1];
+} UTSGADGETINT;
+/** Pointer to the internal gadget host instance data. */
+typedef UTSGADGETINT *PUTSGADGETINT;
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+/** Known gadget host interfaces. */
+static const PCUTSGADGETCLASSIF g_apUtsGadgetClass[] =
+{
+ &g_UtsGadgetClassTest
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Destroys a gadget instance.
+ *
+ * @param pThis The gadget instance.
+ */
+static void utsGadgetDestroy(PUTSGADGETINT pThis)
+{
+ pThis->pClassIf->pfnTerm((PUTSGADGETCLASSINT)&pThis->abClassInst[0]);
+ RTMemFree(pThis);
+}
+
+
+DECLHIDDEN(int) utsGadgetCreate(UTSGADGETHOST hGadgetHost, UTSGADGETCLASS enmClass,
+ PCUTSGADGETCFGITEM paCfg, PUTSGADET phGadget)
+{
+ int rc = VINF_SUCCESS;
+ PCUTSGADGETCLASSIF pClassIf = NULL;
+
+ /* Get the interface. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apUtsGadgetClass); i++)
+ {
+ if (g_apUtsGadgetClass[i]->enmClass == enmClass)
+ {
+ pClassIf = g_apUtsGadgetClass[i];
+ break;
+ }
+ }
+
+ if (RT_LIKELY(pClassIf))
+ {
+ PUTSGADGETINT pThis = (PUTSGADGETINT)RTMemAllocZ(RT_UOFFSETOF_DYN(UTSGADGETINT, abClassInst[pClassIf->cbClass]));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->cRefs = 1;
+ pThis->hGadgetHost = hGadgetHost;
+ pThis->pClassIf = pClassIf;
+ rc = pClassIf->pfnInit((PUTSGADGETCLASSINT)&pThis->abClassInst[0], paCfg);
+ if (RT_SUCCESS(rc))
+ {
+ /* Connect the gadget to the host. */
+ rc = utsGadgetHostGadgetConnect(pThis->hGadgetHost, pThis);
+ if (RT_SUCCESS(rc))
+ *phGadget = pThis;
+ }
+ else
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetRetain(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, 0);
+
+ return ASMAtomicIncU32(&pThis->cRefs);
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetRelease(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, 0);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ utsGadgetDestroy(pThis);
+
+ return cRefs;
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetGetBusId(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ return pThis->pClassIf->pfnGetBusId((PUTSGADGETCLASSINT)&pThis->abClassInst[0]);
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetGetDevId(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ return 1; /** @todo Current assumption which is true on Linux with dummy_hcd. */
+}
+
+
+DECLHIDDEN(int) utsGadgetConnect(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ int rc = pThis->pClassIf->pfnConnect((PUTSGADGETCLASSINT)&pThis->abClassInst[0]);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetHostGadgetConnect(pThis->hGadgetHost, hGadget);
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetDisconnect(UTSGADGET hGadget)
+{
+ PUTSGADGETINT pThis = hGadget;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ int rc = utsGadgetHostGadgetDisconnect(pThis->hGadgetHost, hGadget);
+ if (RT_SUCCESS(rc))
+ rc = pThis->pClassIf->pfnDisconnect((PUTSGADGETCLASSINT)&pThis->abClassInst[0]);
+
+ return rc;
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.h b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.h
new file mode 100644
index 00000000..19d2603a
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadget.h
@@ -0,0 +1,546 @@
+/* $Id: UsbTestServiceGadget.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Gadget API.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServiceGadget_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServiceGadget_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+RT_C_DECLS_BEGIN
+
+/** Opaque gadget host handle. */
+typedef struct UTSGADGETHOSTINT *UTSGADGETHOST;
+/** Pointer to a gadget host handle. */
+typedef UTSGADGETHOST *PUTSGADGETHOST;
+
+/** NIL gadget host handle. */
+#define NIL_UTSGADGETHOST ((UTSGADGETHOST)0)
+
+/** Opaque USB gadget handle. */
+typedef struct UTSGADGETINT *UTSGADGET;
+/** Pointer to a USB gadget handle. */
+typedef UTSGADGET *PUTSGADET;
+
+/** NIL gadget handle. */
+#define NIL_UTSGADGET ((UTSGADGET)0)
+
+/**
+ * Gadget/Gadget host configuration item type.
+ */
+typedef enum UTSGADGETCFGTYPE
+{
+ /** Don't use! */
+ UTSGADGETCFGTYPE_INVALID = 0,
+ /** Boolean type. */
+ UTSGADGETCFGTYPE_BOOLEAN,
+ /** UTF-8 string. */
+ UTSGADGETCFGTYPE_STRING,
+ /** Unsigned 8bit integer. */
+ UTSGADGETCFGTYPE_UINT8,
+ /** Unsigned 16bit integer. */
+ UTSGADGETCFGTYPE_UINT16,
+ /** Unsigned 32bit integer. */
+ UTSGADGETCFGTYPE_UINT32,
+ /** Unsigned 64bit integer. */
+ UTSGADGETCFGTYPE_UINT64,
+ /** Signed 8bit integer. */
+ UTSGADGETCFGTYPE_INT8,
+ /** Signed 16bit integer. */
+ UTSGADGETCFGTYPE_INT16,
+ /** Signed 32bit integer. */
+ UTSGADGETCFGTYPE_INT32,
+ /** Signed 64bit integer. */
+ UTSGADGETCFGTYPE_INT64,
+ /** 32bit hack. */
+ UTSGADGETCFGTYPE_32BIT_HACK = 0x7fffffff
+} UTSGADGETCFGTYPE;
+
+/**
+ * Gadget configuration value.
+ */
+typedef struct UTSGADGETCFGVAL
+{
+ /** Value type */
+ UTSGADGETCFGTYPE enmType;
+ /** Value based on the type. */
+ union
+ {
+ bool f;
+ const char *psz;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ } u;
+} UTSGADGETCFGVAL;
+/** Pointer to a gadget configuration value. */
+typedef UTSGADGETCFGVAL *PUTSGADGETCFGVAL;
+/** Pointer to a const gadget configuration value. */
+typedef const UTSGADGETCFGVAL *PCUTSGADGETCFGVAL;
+
+/**
+ * Gadget configuration item.
+ */
+typedef struct UTSGADGETCFGITEM
+{
+ /** Item key. */
+ const char *pszKey;
+ /** Item value. */
+ UTSGADGETCFGVAL Val;
+} UTSGADGETCFGITEM;
+/** Pointer to a gadget configuration item. */
+typedef UTSGADGETCFGITEM *PUTSGADGETCFGITEM;
+/** Pointer to a const gadget configuration item. */
+typedef const UTSGADGETCFGITEM *PCUTSGADGETCFGITEM;
+
+/**
+ * Type for the gadget host.
+ */
+typedef enum UTSGADGETHOSTTYPE
+{
+ /** Invalid type, don't use. */
+ UTSGADGETHOSTTYPE_INVALID = 0,
+ /** USB/IP host, gadgets are exported using a USB/IP server. */
+ UTSGADGETHOSTTYPE_USBIP,
+ /** Physical connection using a device or OTG port. */
+ UTSGADGETHOSTTYPE_PHYSICAL,
+ /** 32bit hack. */
+ UTSGADGETHOSTTYPE_32BIT_HACK = 0x7fffffff
+} UTSGADGETHOSTTYPE;
+
+/**
+ * USB gadget class.
+ */
+typedef enum UTSGADGETCLASS
+{
+ /** Invalid class, don't use. */
+ UTSGADGETCLASS_INVALID = 0,
+ /** Special test device class. */
+ UTSGADGETCLASS_TEST,
+ /** MSD device. */
+ UTSGADGETCLASS_MSD,
+ /** 32bit hack. */
+ UTSGADGETCLASS_32BIT_HACK = 0x7fffffff
+} UTSGADGETCLASS;
+
+/**
+ * Queries the value of a given boolean key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pf Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryBool(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ bool *pf);
+
+/**
+ * Queries the value of a given boolean key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pf Where to store the value on success.
+ * @param fDef The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryBoolDef(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ bool *pf, bool fDef);
+
+/**
+ * Queries the string value of a given key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param ppszVal Where to store the pointer to the string on success,
+ * must be freed with RTStrFree().
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryString(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ char **ppszVal);
+
+/**
+ * Queries the string value of a given key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param ppszVal Where to store the pointer to the string on success,
+ * must be freed with RTStrFree().
+ * @param pszDef The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryStringDef(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ char **ppszVal, const char *pszDef);
+
+/**
+ * Queries the value of a given uint8_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu8 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU8(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint8_t *pu8);
+
+/**
+ * Queries the value of a given uint8_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu8 Where to store the value on success.
+ * @param u8Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU8Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint8_t *pu8, uint8_t u8Def);
+
+/**
+ * Queries the value of a given uint16_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu16 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU16(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pu16);
+
+/**
+ * Queries the value of a given uint16_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu16 Where to store the value on success.
+ * @param u16Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU16Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pu16, uint16_t u16Def);
+
+/**
+ * Queries the value of a given uint32_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu32 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU32(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pu32);
+
+/**
+ * Queries the value of a given uint32_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu32 Where to store the value on success.
+ * @param u32Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU32Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pu32, uint32_t u32Def);
+
+/**
+ * Queries the value of a given uint64_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu64 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU64(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pu64);
+
+/**
+ * Queries the value of a given uint64_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pu64 Where to store the value on success.
+ * @param u64Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryU64Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pu64, uint64_t u64Def);
+
+/**
+ * Queries the value of a given int8_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi8 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS8(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ int8_t *pi8);
+
+/**
+ * Queries the value of a given int8_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi8 Where to store the value on success.
+ * @param i8Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS8Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ int8_t *pi8, uint8_t i8Def);
+
+/**
+ * Queries the value of a given int16_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi16 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS16(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pi16);
+
+/**
+ * Queries the value of a given int16_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi16 Where to store the value on success.
+ * @param i16Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS16Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pi16, uint16_t i16Def);
+
+/**
+ * Queries the value of a given int32_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi32 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS32(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pi32);
+
+/**
+ * Queries the value of a given int32_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi32 Where to store the value on success.
+ * @param i32Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS32Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pi32, uint32_t i32Def);
+
+/**
+ * Queries the value of a given int64_t key from the given configuration array.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi64 Where to store the value on success.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS64(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pi64);
+
+/**
+ * Queries the value of a given int64_t key from the given configuration array,
+ * setting a default if not found.
+ *
+ * @returns IPRT status code.
+ * @param paCfg The configuration items.
+ * @param pszKey The key query the value for.
+ * @param pi64 Where to store the value on success.
+ * @param i64Def The default value to assign if the key is not found.
+ */
+DECLHIDDEN(int) utsGadgetCfgQueryS64Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pi64, uint64_t i64Def);
+
+/**
+ * Creates a new USB gadget host.
+ *
+ * @returns IPRT status code.
+ * @param enmType The host type.
+ * @param paCfg Additional configuration parameters - optional.
+ * The array must be terminated with a NULL entry.
+ * @param phGadgetHost Where to store the handle to the gadget host on success.
+ */
+DECLHIDDEN(int) utsGadgetHostCreate(UTSGADGETHOSTTYPE enmType, PCUTSGADGETCFGITEM paCfg,
+ PUTSGADGETHOST phGadgetHost);
+
+/**
+ * Retains the given gadget host handle.
+ *
+ * @returns New reference count.
+ * @param hGadgetHost The gadget host handle to retain.
+ */
+DECLHIDDEN(uint32_t) utsGadgetHostRetain(UTSGADGETHOST hGadgetHost);
+
+/**
+ * Releases the given gadget host handle, destroying it if the reference
+ * count reaches 0.
+ *
+ * @returns New reference count.
+ * @param hGadgetHost The gadget host handle to release.
+ */
+DECLHIDDEN(uint32_t) utsGadgetHostRelease(UTSGADGETHOST hGadgetHost);
+
+/**
+ * Returns the current config of the given gadget host.
+ *
+ * @returns Pointer to a constant array of configuration items for the given gadget host.
+ * @param hGadgetHost The gadget host handle.
+ */
+DECLHIDDEN(PCUTSGADGETCFGITEM) utsGadgetHostGetCfg(UTSGADGETHOST hGadgetHost);
+
+/**
+ * Connects the given gadget to the host.
+ *
+ * @returns IPRT status code.
+ * @param hGadgetHost The gadget host handle.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(int) utsGadgetHostGadgetConnect(UTSGADGETHOST hGadgetHost, UTSGADGET hGadget);
+
+/**
+ * Disconnects the given gadget from the host.
+ *
+ * @returns IPRT status code.
+ * @param hGadgetHost The gadget host handle.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(int) utsGadgetHostGadgetDisconnect(UTSGADGETHOST hGadgetHost, UTSGADGET hGadget);
+
+/**
+ * Creates a new USB gadget based the class.
+ *
+ * @returns IPRT status code.
+ * @param hGadgetHost The gadget host the gadget is part of.
+ * @param enmClass The gadget class.
+ * @param paCfg Array of optional configuration items for the gadget.
+ * @param phGadget Where to store the gadget handle on success.
+ */
+DECLHIDDEN(int) utsGadgetCreate(UTSGADGETHOST hGadgetHost, UTSGADGETCLASS enmClass,
+ PCUTSGADGETCFGITEM paCfg, PUTSGADET phGadget);
+
+/**
+ * Retains the given gadget handle.
+ *
+ * @returns New reference count.
+ * @param hGadget The gadget handle to retain.
+ */
+DECLHIDDEN(uint32_t) utsGadgetRetain(UTSGADGET hGadget);
+
+/**
+ * Releases the given gadget handle, destroying it if the reference
+ * count reaches 0.
+ *
+ * @returns New reference count.
+ * @param hGadget The gadget handle to destroy.
+ */
+DECLHIDDEN(uint32_t) utsGadgetRelease(UTSGADGET hGadget);
+
+/**
+ * Returns the current config of the given gadget.
+ *
+ * @returns Pointer to a constant array of configuration items for the given gadget.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(PCUTSGADGETCFGITEM) utsGadgetGetCfg(UTSGADGET hGadget);
+
+/**
+ * Returns the path of the given gadget from which it can be accessed.
+ *
+ * @returns Access path.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(const char *) utsGadgetGetAccessPath(UTSGADGET hGadget);
+
+/**
+ * Returns the bus ID the gadget is on.
+ *
+ * @returns Bus ID of the gadget.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(uint32_t) utsGadgetGetBusId(UTSGADGET hGadget);
+
+/**
+ * Returns the device ID of the gagdet.
+ *
+ * @returns Device ID of the gadget.
+ * @param hGadget The gadget handle.
+ */
+DECLHIDDEN(uint32_t) utsGadgetGetDevId(UTSGADGET hGadget);
+
+/**
+ * Mark the gadget as connected to the host. Depending
+ * on the host type it will be appear as physically attached
+ * or will appear in the exported USB device list.
+ *
+ * @returns IPRT status code.
+ * @param hGadget The gadget handle to connect.
+ */
+DECLHIDDEN(int) utsGadgetConnect(UTSGADGET hGadget);
+
+/**
+ * Mark the gadget as disconnected from the host.
+ *
+ * @returns IPRT status code.
+ * @param hGadget The gadget handle to disconnect.
+ */
+DECLHIDDEN(int) utsGadgetDisconnect(UTSGADGET hGadget);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServiceGadget_h */
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetCfg.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetCfg.cpp
new file mode 100644
index 00000000..7ae631a5
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetCfg.cpp
@@ -0,0 +1,462 @@
+/* $Id: UsbTestServiceGadgetCfg.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, USB gadget Cfg API.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "UsbTestServiceGadget.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Returns the gadget configuration item matching the given key.
+ *
+ * @returns Pointer to the configuration item on success or NULL if not found.
+ * @param paCfg The configuration item array.
+ * @param pszKey The key to look for.
+ */
+static PCUTSGADGETCFGITEM utsGadgetCfgGetItemFromKey(PCUTSGADGETCFGITEM paCfg, const char *pszKey)
+{
+ while ( paCfg
+ && paCfg->pszKey)
+ {
+ if (!RTStrCmp(paCfg->pszKey, pszKey))
+ return paCfg;
+
+ paCfg++;
+ }
+ return NULL;
+}
+
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryBool(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ bool *pf)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_BOOLEAN)
+ {
+ *pf = pCfgItem->Val.u.f;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryBoolDef(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ bool *pf, bool fDef)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_BOOLEAN)
+ {
+ *pf = pCfgItem ? pCfgItem->Val.u.f : fDef;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryString(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ char **ppszVal)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_STRING)
+ {
+ *ppszVal = RTStrDup(pCfgItem->Val.u.psz);
+ if (*ppszVal)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryStringDef(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ char **ppszVal, const char *pszDef)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_STRING)
+ {
+ *ppszVal = RTStrDup(pCfgItem ? pCfgItem->Val.u.psz : pszDef);
+ if (*ppszVal)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU8(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint8_t *pu8)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT8)
+ {
+ *pu8 = pCfgItem->Val.u.u8;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU8Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint8_t *pu8, uint8_t u8Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT8)
+ {
+ *pu8 = pCfgItem ? pCfgItem->Val.u.u8 : u8Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU16(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pu16)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT16)
+ {
+ *pu16 = pCfgItem->Val.u.u16;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU16Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pu16, uint16_t u16Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT16)
+ {
+ *pu16 = pCfgItem ? pCfgItem->Val.u.u16 : u16Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU32(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pu32)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT32)
+ {
+ *pu32 = pCfgItem->Val.u.u32;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU32Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pu32, uint32_t u32Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT32)
+ {
+ *pu32 = pCfgItem ? pCfgItem->Val.u.u32 : u32Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU64(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pu64)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT64)
+ {
+ *pu64 = pCfgItem->Val.u.u64;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryU64Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pu64, uint64_t u64Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_UINT64)
+ {
+ *pu64 = pCfgItem ? pCfgItem->Val.u.u64 : u64Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS8(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ int8_t *pi8)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT8)
+ {
+ *pi8 = pCfgItem->Val.u.i8;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS8Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ int8_t *pi8, uint8_t i8Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT8)
+ {
+ *pi8 = pCfgItem ? pCfgItem->Val.u.i8 : i8Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS16(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pi16)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT16)
+ {
+ *pi16 = pCfgItem->Val.u.i16;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS16Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint16_t *pi16, uint16_t i16Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT16)
+ {
+ *pi16 = pCfgItem ? pCfgItem->Val.u.i16 : i16Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS32(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pi32)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT32)
+ {
+ *pi32 = pCfgItem->Val.u.i32;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS32Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint32_t *pi32, uint32_t i32Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT32)
+ {
+ *pi32 = pCfgItem ? pCfgItem->Val.u.i32 : i32Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS64(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pi64)
+{
+ int rc = VERR_NOT_FOUND;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if (pCfgItem)
+ {
+ if (pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT64)
+ {
+ *pi64 = pCfgItem->Val.u.i64;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsGadgetCfgQueryS64Def(PCUTSGADGETCFGITEM paCfg, const char *pszKey,
+ uint64_t *pi64, uint64_t i64Def)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ PCUTSGADGETCFGITEM pCfgItem = utsGadgetCfgGetItemFromKey(paCfg, pszKey);
+
+ if ( !pCfgItem
+ || pCfgItem->Val.enmType == UTSGADGETCFGTYPE_INT64)
+ {
+ *pi64 = pCfgItem ? pCfgItem->Val.u.i64 : i64Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetClassTest.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetClassTest.cpp
new file mode 100644
index 00000000..6a2f3626
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetClassTest.cpp
@@ -0,0 +1,470 @@
+/* $Id: UsbTestServiceGadgetClassTest.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, USB gadget class
+ * for the test device.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/thread.h>
+
+#include <iprt/linux/sysfs.h>
+
+#include "UsbTestServiceGadgetInternal.h"
+#include "UsbTestServicePlatform.h"
+
+
+/*********************************************************************************************************************************
+* Constants And Macros, Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Default configfs mount point. */
+#define UTS_GADGET_CLASS_CONFIGFS_MNT_DEF "/sys/kernel/config/usb_gadget"
+/** Gadget template name */
+#define UTS_GADGET_TEMPLATE_NAME "gadget_test"
+
+/** Default vendor ID which is recognized by the usbtest driver. */
+#define UTS_GADGET_TEST_VENDOR_ID_DEF UINT16_C(0x0525)
+/** Default product ID which is recognized by the usbtest driver. */
+#define UTS_GADGET_TEST_PRODUCT_ID_DEF UINT16_C(0xa4a0)
+/** Default device class. */
+#define UTS_GADGET_TEST_DEVICE_CLASS_DEF UINT8_C(0xff)
+/** Default serial number string. */
+#define UTS_GADGET_TEST_SERIALNUMBER_DEF "0123456789"
+/** Default manufacturer string. */
+#define UTS_GADGET_TEST_MANUFACTURER_DEF "Oracle Inc."
+/** Default product string. */
+#define UTS_GADGET_TEST_PRODUCT_DEF "USB test device"
+
+/**
+ * Internal UTS gadget host instance data.
+ */
+typedef struct UTSGADGETCLASSINT
+{
+ /** Gadget template path. */
+ char *pszGadgetPath;
+ /** The UDC this gadget is connected to. */
+ char *pszUdc;
+ /** Bus identifier for the used UDC. */
+ uint32_t uBusId;
+ /** Device identifier. */
+ uint32_t uDevId;
+} UTSGADGETCLASSINT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Number of already created gadgets, used for the template name. */
+static volatile uint32_t g_cGadgets = 0;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Creates a new directory pointed to by the given format string.
+ *
+ * @returns IPRT status code.
+ * @param pszFormat The format string.
+ * @param va The arguments.
+ */
+static int utsGadgetClassTestDirCreateV(const char *pszFormat, va_list va)
+{
+ int rc = VINF_SUCCESS;
+ char aszPath[RTPATH_MAX + 1];
+
+ size_t cbStr = RTStrPrintfV(&aszPath[0], sizeof(aszPath), pszFormat, va);
+ if (cbStr <= sizeof(aszPath) - 1)
+ rc = RTDirCreateFullPath(aszPath, 0700);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ return rc;
+}
+
+
+/**
+ * Creates a new directory pointed to by the given format string.
+ *
+ * @returns IPRT status code.
+ * @param pszFormat The format string.
+ * @param ... The arguments.
+ */
+static int utsGadgetClassTestDirCreate(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = utsGadgetClassTestDirCreateV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Removes a directory pointed to by the given format string.
+ *
+ * @returns IPRT status code.
+ * @param pszFormat The format string.
+ * @param va The arguments.
+ */
+static int utsGadgetClassTestDirRemoveV(const char *pszFormat, va_list va)
+{
+ int rc = VINF_SUCCESS;
+ char aszPath[RTPATH_MAX + 1];
+
+ size_t cbStr = RTStrPrintfV(&aszPath[0], sizeof(aszPath), pszFormat, va);
+ if (cbStr <= sizeof(aszPath) - 1)
+ rc = RTDirRemove(aszPath);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ return rc;
+}
+
+
+/**
+ * Removes a directory pointed to by the given format string.
+ *
+ * @returns IPRT status code.
+ * @param pszFormat The format string.
+ * @param ... The arguments.
+ */
+static int utsGadgetClassTestDirRemove(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = utsGadgetClassTestDirRemoveV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Links the given function to the given config.
+ *
+ * @returns IPRT status code.
+ * @param pClass The gadget class instance data.
+ * @param pszFunc The function to link.
+ * @param pszCfg The configuration which the function will be part of.
+ */
+static int utsGadgetClassTestLinkFuncToCfg(PUTSGADGETCLASSINT pClass, const char *pszFunc, const char *pszCfg)
+{
+ int rc = VINF_SUCCESS;
+ char aszPathFunc[RTPATH_MAX + 1];
+ char aszPathCfg[RTPATH_MAX + 1];
+
+ size_t cbStr = RTStrPrintf(&aszPathFunc[0], sizeof(aszPathFunc), "%s/functions/%s",
+ pClass->pszGadgetPath, pszFunc);
+ if (cbStr <= sizeof(aszPathFunc) - 1)
+ {
+ cbStr = RTStrPrintf(&aszPathCfg[0], sizeof(aszPathCfg), "%s/configs/%s/%s",
+ pClass->pszGadgetPath, pszCfg, pszFunc);
+ if (cbStr <= sizeof(aszPathCfg) - 1)
+ rc = RTSymlinkCreate(&aszPathCfg[0], &aszPathFunc[0], RTSYMLINKTYPE_DIR, 0);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ return rc;
+}
+
+
+/**
+ * Unlinks the given function from the given configuration.
+ *
+ * @returns IPRT status code.
+ * @param pClass The gadget class instance data.
+ * @param pszFunc The function to unlink.
+ * @param pszCfg The configuration which the function is currently part of.
+ */
+static int utsGadgetClassTestUnlinkFuncFromCfg(PUTSGADGETCLASSINT pClass, const char *pszFunc, const char *pszCfg)
+{
+ int rc = VINF_SUCCESS;
+ char aszPath[RTPATH_MAX + 1];
+ size_t cbStr = RTStrPrintf(&aszPath[0], sizeof(aszPath), "%s/configs/%s/%s",
+ pClass->pszGadgetPath, pszCfg, pszFunc);
+ if (cbStr <= sizeof(aszPath) - 1)
+ rc = RTSymlinkDelete(&aszPath[0], 0);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ return rc;
+}
+
+
+/**
+ * Cleans up any leftover configurations from the gadget class.
+ *
+ * @param pClass The gadget class instance data.
+ */
+static void utsGadgetClassTestCleanup(PUTSGADGETCLASSINT pClass)
+{
+ /* Unbind the gadget from the currently assigned UDC first. */
+ int rc = RTLinuxSysFsWriteStrFile("", 0, NULL, "%s/UDC", pClass->pszGadgetPath);
+ AssertRC(rc);
+
+ /* Delete the symlinks, ignore any errors. */
+ utsGadgetClassTestUnlinkFuncFromCfg(pClass, "Loopback.0", "c.2");
+ utsGadgetClassTestUnlinkFuncFromCfg(pClass, "SourceSink.0", "c.1");
+
+ /* Delete configuration strings and then the configuration directories. */
+ utsGadgetClassTestDirRemove("%s/configs/c.2/strings/0x409", pClass->pszGadgetPath);
+ utsGadgetClassTestDirRemove("%s/configs/c.1/strings/0x409", pClass->pszGadgetPath);
+
+ utsGadgetClassTestDirRemove("%s/configs/c.2", pClass->pszGadgetPath);
+ utsGadgetClassTestDirRemove("%s/configs/c.1", pClass->pszGadgetPath);
+
+ /* Delete the functions. */
+ utsGadgetClassTestDirRemove("%s/functions/Loopback.0", pClass->pszGadgetPath);
+ utsGadgetClassTestDirRemove("%s/functions/SourceSink.0", pClass->pszGadgetPath);
+
+ /* Delete the english strings. */
+ utsGadgetClassTestDirRemove("%s/strings/0x409", pClass->pszGadgetPath);
+
+ /* Finally delete the gadget template. */
+ utsGadgetClassTestDirRemove(pClass->pszGadgetPath);
+
+ /* Release the UDC. */
+ if (pClass->pszUdc)
+ {
+ rc = utsPlatformLnxReleaseUDC(pClass->pszUdc);
+ AssertRC(rc);
+ RTStrFree(pClass->pszUdc);
+ }
+}
+
+/**
+ * @interface_method_impl{UTSGADGETCLASSIF,pfnInit}
+ */
+static DECLCALLBACK(int) utsGadgetClassTestInit(PUTSGADGETCLASSINT pClass, PCUTSGADGETCFGITEM paCfg)
+{
+ int rc = VINF_SUCCESS;
+
+ if (RTLinuxSysFsExists(UTS_GADGET_CLASS_CONFIGFS_MNT_DEF))
+ {
+ /* Create the gadget template */
+ unsigned idx = ASMAtomicIncU32(&g_cGadgets);
+
+ int rcStr = RTStrAPrintf(&pClass->pszGadgetPath, "%s/%s%u", UTS_GADGET_CLASS_CONFIGFS_MNT_DEF,
+ UTS_GADGET_TEMPLATE_NAME, idx);
+ if (rcStr == -1)
+ return VERR_NO_STR_MEMORY;
+
+ rc = utsGadgetClassTestDirCreate(pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ {
+ uint16_t idVendor = 0;
+ uint16_t idProduct = 0;
+ uint8_t bDeviceClass = 0;
+ char *pszSerial = NULL;
+ char *pszManufacturer = NULL;
+ char *pszProduct = NULL;
+ bool fSuperSpeed = false;
+
+ /* Get basic device config. */
+ rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idVendor", &idVendor, UTS_GADGET_TEST_VENDOR_ID_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idProduct", &idProduct, UTS_GADGET_TEST_PRODUCT_ID_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryU8Def(paCfg, "Gadget/bDeviceClass", &bDeviceClass, UTS_GADGET_TEST_DEVICE_CLASS_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/SerialNumber", &pszSerial, UTS_GADGET_TEST_SERIALNUMBER_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Manufacturer", &pszManufacturer, UTS_GADGET_TEST_MANUFACTURER_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Product", &pszProduct, UTS_GADGET_TEST_PRODUCT_DEF);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetCfgQueryBoolDef(paCfg, "Gadget/SuperSpeed", &fSuperSpeed, false);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Write basic attributes. */
+ rc = RTLinuxSysFsWriteU16File(16, idVendor, "%s/idVendor", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteU16File(16, idProduct, "%s/idProduct", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteU16File(16, bDeviceClass, "%s/bDeviceClass", pClass->pszGadgetPath);
+
+ /* Create english language strings. */
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/strings/0x409", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile(pszSerial, 0, NULL, "%s/strings/0x409/serialnumber", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile(pszManufacturer, 0, NULL, "%s/strings/0x409/manufacturer", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile(pszProduct, 0, NULL, "%s/strings/0x409/product", pClass->pszGadgetPath);
+
+ /* Create the gadget functions. */
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/functions/SourceSink.0", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/functions/Loopback.0", pClass->pszGadgetPath);
+
+ /* Create the device configs. */
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/configs/c.1", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/configs/c.2", pClass->pszGadgetPath);
+
+ /* Write configuration strings. */
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/configs/c.1/strings/0x409", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestDirCreate("%s/configs/c.2/strings/0x409", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile("source and sink data", 0, NULL, "%s/configs/c.1/strings/0x409/configuration", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile("loop input to output", 0, NULL, "%s/configs/c.2/strings/0x409/configuration", pClass->pszGadgetPath);
+
+ /* Link the functions into the configurations. */
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestLinkFuncToCfg(pClass, "SourceSink.0", "c.1");
+ if (RT_SUCCESS(rc))
+ rc = utsGadgetClassTestLinkFuncToCfg(pClass, "Loopback.0", "c.2");
+
+ /* Finally enable the gadget by attaching it to a UDC. */
+ if (RT_SUCCESS(rc))
+ {
+ pClass->pszUdc = NULL;
+
+ rc = utsPlatformLnxAcquireUDC(fSuperSpeed, &pClass->pszUdc, &pClass->uBusId);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsWriteStrFile(pClass->pszUdc, 0, NULL, "%s/UDC", pClass->pszGadgetPath);
+ if (RT_SUCCESS(rc))
+ RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
+ }
+ }
+
+ if (pszSerial)
+ RTStrFree(pszSerial);
+ if (pszManufacturer)
+ RTStrFree(pszManufacturer);
+ if (pszProduct)
+ RTStrFree(pszProduct);
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ if (RT_FAILURE(rc))
+ utsGadgetClassTestCleanup(pClass);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETCLASSIF,pfnTerm}
+ */
+static DECLCALLBACK(void) utsGadgetClassTestTerm(PUTSGADGETCLASSINT pClass)
+{
+ utsGadgetClassTestCleanup(pClass);
+
+ if (pClass->pszGadgetPath)
+ RTStrFree(pClass->pszGadgetPath);
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETCLASSIF,pfnGetBusId}
+ */
+static DECLCALLBACK(uint32_t) utsGadgetClassTestGetBusId(PUTSGADGETCLASSINT pClass)
+{
+ return pClass->uBusId;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETCLASSIF,pfnConnect}
+ */
+static DECLCALLBACK(int) utsGadgetClassTestConnect(PUTSGADGETCLASSINT pClass)
+{
+ int rc = RTLinuxSysFsWriteStrFile("connect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);
+ if (RT_SUCCESS(rc))
+ RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETCLASSIF,pfnDisconnect}
+ */
+static DECLCALLBACK(int) utsGadgetClassTestDisconnect(PUTSGADGETCLASSINT pClass)
+{
+ return RTLinuxSysFsWriteStrFile("disconnect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);}
+
+
+
+/**
+ * The gadget host interface callback table.
+ */
+const UTSGADGETCLASSIF g_UtsGadgetClassTest =
+{
+ /** enmType */
+ UTSGADGETCLASS_TEST,
+ /** pszDesc */
+ "UTS test device gadget class",
+ /** cbIf */
+ sizeof(UTSGADGETCLASSINT),
+ /** pfnInit */
+ utsGadgetClassTestInit,
+ /** pfnTerm */
+ utsGadgetClassTestTerm,
+ /** pfnGetBusId */
+ utsGadgetClassTestGetBusId,
+ /** pfnConnect */
+ utsGadgetClassTestConnect,
+ /** pfnDisconnect. */
+ utsGadgetClassTestDisconnect
+};
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHost.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHost.cpp
new file mode 100644
index 00000000..1f8cc53b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHost.cpp
@@ -0,0 +1,179 @@
+/* $Id: UsbTestServiceGadgetHost.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, USB gadget host API.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "UsbTestServiceGadget.h"
+#include "UsbTestServiceGadgetHostInternal.h"
+
+
+/*********************************************************************************************************************************
+* Constants And Macros, Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal UTS gadget host instance data.
+ */
+typedef struct UTSGADGETHOSTINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Pointer to the gadget host callback table. */
+ PCUTSGADGETHOSTIF pHstIf;
+ /** Interface specific instance data - variable in size. */
+ uint8_t abIfInst[1];
+} UTSGADGETHOSTINT;
+/** Pointer to the internal gadget host instance data. */
+typedef UTSGADGETHOSTINT *PUTSGADGETHOSTINT;
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+/** Known gadget host interfaces. */
+static const PCUTSGADGETHOSTIF g_apUtsGadgetHostIf[] =
+{
+ &g_UtsGadgetHostIfUsbIp,
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Destroys a gadget host instance.
+ *
+ * @param pThis The gadget host instance.
+ */
+static void utsGadgetHostDestroy(PUTSGADGETHOSTINT pThis)
+{
+ /** @todo Remove all gadgets. */
+ pThis->pHstIf->pfnTerm((PUTSGADGETHOSTTYPEINT)&pThis->abIfInst[0]);
+ RTMemFree(pThis);
+}
+
+
+DECLHIDDEN(int) utsGadgetHostCreate(UTSGADGETHOSTTYPE enmType, PCUTSGADGETCFGITEM paCfg,
+ PUTSGADGETHOST phGadgetHost)
+{
+ int rc = VINF_SUCCESS;
+ PCUTSGADGETHOSTIF pIf = NULL;
+
+ /* Get the interface. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apUtsGadgetHostIf); i++)
+ {
+ if (g_apUtsGadgetHostIf[i]->enmType == enmType)
+ {
+ pIf = g_apUtsGadgetHostIf[i];
+ break;
+ }
+ }
+
+ if (RT_LIKELY(pIf))
+ {
+ PUTSGADGETHOSTINT pThis = (PUTSGADGETHOSTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(UTSGADGETHOSTINT, abIfInst[pIf->cbIf]));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->cRefs = 1;
+ pThis->pHstIf = pIf;
+ rc = pIf->pfnInit((PUTSGADGETHOSTTYPEINT)&pThis->abIfInst[0], paCfg);
+ if (RT_SUCCESS(rc))
+ *phGadgetHost = pThis;
+ else
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetHostRetain(UTSGADGETHOST hGadgetHost)
+{
+ PUTSGADGETHOSTINT pThis = hGadgetHost;
+
+ AssertPtrReturn(pThis, 0);
+
+ return ASMAtomicIncU32(&pThis->cRefs);
+}
+
+
+DECLHIDDEN(uint32_t) utsGadgetHostRelease(UTSGADGETHOST hGadgetHost)
+{
+ PUTSGADGETHOSTINT pThis = hGadgetHost;
+
+ AssertPtrReturn(pThis, 0);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ utsGadgetHostDestroy(pThis);
+
+ return cRefs;
+}
+
+
+DECLHIDDEN(int) utsGadgetHostGadgetConnect(UTSGADGETHOST hGadgetHost, UTSGADGET hGadget)
+{
+ PUTSGADGETHOSTINT pThis = hGadgetHost;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ return pThis->pHstIf->pfnGadgetConnect((PUTSGADGETHOSTTYPEINT)&pThis->abIfInst[0], hGadget);
+}
+
+
+DECLHIDDEN(int) utsGadgetHostGadgetDisconnect(UTSGADGETHOST hGadgetHost, UTSGADGET hGadget)
+{
+ PUTSGADGETHOSTINT pThis = hGadgetHost;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ return pThis->pHstIf->pfnGadgetDisconnect((PUTSGADGETHOSTTYPEINT)&pThis->abIfInst[0], hGadget);
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostInternal.h b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostInternal.h
new file mode 100644
index 00000000..6ae11fd0
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostInternal.h
@@ -0,0 +1,132 @@
+/* $Id: UsbTestServiceGadgetHostInternal.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Gadget API.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetHostInternal_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetHostInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#include "UsbTestServiceGadget.h"
+
+RT_C_DECLS_BEGIN
+
+/** Pointer to an opaque type dependent gadget host instance data. */
+typedef struct UTSGADGETHOSTTYPEINT *PUTSGADGETHOSTTYPEINT;
+/** Pointer to a gadget host instance pointer. */
+typedef PUTSGADGETHOSTTYPEINT *PPUTSGADETHOSTTYPEINT;
+
+/**
+ * Gadget host interface.
+ */
+typedef struct UTSGADGETHOSTIF
+{
+ /** The gadget host type implemented. */
+ UTSGADGETHOSTTYPE enmType;
+ /** Description. */
+ const char *pszDesc;
+ /** Size of the interface specific instance data. */
+ size_t cbIf;
+
+ /**
+ * Initializes the gadget host interface.
+ *
+ * @returns IPRT status code.
+ * @param pIf The interface specific instance data.
+ * @param paCfg The configuration of the interface.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnInit, (PUTSGADGETHOSTTYPEINT pIf, PCUTSGADGETCFGITEM paCfg));
+
+ /**
+ * Terminates the gadget host interface.
+ *
+ * @param pIf The interface specific instance data.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnTerm, (PUTSGADGETHOSTTYPEINT pIf));
+
+ /**
+ * Adds the given gadget to the host interface.
+ *
+ * @returns IPRT status code.
+ * @param pIf The interface specific instance data.
+ * @param hGadget The gadget handle to add.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnGadgetAdd, (PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget));
+
+ /**
+ * Removes the given gadget from the host interface.
+ *
+ * @returns IPRT status code.
+ * @param pIf The interface specific instance data.
+ * @param hGadget The gadget handle to remove.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnGadgetRemove, (PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget));
+
+ /**
+ * Connects the given gadget to the host interface so it appears as connected to the client
+ * of the gadget host.
+ *
+ * @returns IPRT status code.
+ * @param pIf The interface specific instance data.
+ * @param hGadget The gadget handle to add.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnGadgetConnect, (PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget));
+
+ /**
+ * Disconnects the given gadget from the host interface so it appears as disconnected to the client
+ * of the gadget host.
+ *
+ * @returns IPRT status code.
+ * @param pIf The interface specific instance data.
+ * @param hGadget The gadget handle to add.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnGadgetDisconnect, (PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget));
+
+} UTSGADGETHOSTIF;
+/** Pointer to a gadget host callback table. */
+typedef UTSGADGETHOSTIF *PUTSGADGETHOSTIF;
+/** Pointer to a const gadget host callback table. */
+typedef const struct UTSGADGETHOSTIF *PCUTSGADGETHOSTIF;
+
+extern UTSGADGETHOSTIF const g_UtsGadgetHostIfUsbIp;
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetHostInternal_h */
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostUsbIp.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostUsbIp.cpp
new file mode 100644
index 00000000..a0f183ca
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetHostUsbIp.cpp
@@ -0,0 +1,263 @@
+/* $Id: UsbTestServiceGadgetHostUsbIp.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, USB gadget host interface
+ * for USB/IP.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "UsbTestServiceGadgetHostInternal.h"
+#include "UsbTestServicePlatform.h"
+
+
+/*********************************************************************************************************************************
+* Constants And Macros, Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal UTS gadget host instance data.
+ */
+typedef struct UTSGADGETHOSTTYPEINT
+{
+ /** Handle to the USB/IP daemon process. */
+ RTPROCESS hProcUsbIp;
+} UTSGADGETHOSTTYPEINT;
+
+/** Default port of the USB/IP server. */
+#define UTS_GADGET_HOST_USBIP_PORT_DEF 3240
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Worker for binding/unbinding the given gadget from the USB/IP server.
+ *
+ * @returns IPRT status code.
+ * @param pThis The gadget host instance.
+ * @param hGadget The gadget handle.
+ * @param fBind Flag whether to do a bind or unbind.
+ */
+static int usbGadgetHostUsbIpBindUnbind(PUTSGADGETHOSTTYPEINT pThis, UTSGADGET hGadget, bool fBind)
+{
+ RT_NOREF1(pThis);
+ uint32_t uBusId, uDevId;
+ char aszBus[32];
+
+ uBusId = utsGadgetGetBusId(hGadget);
+ uDevId = utsGadgetGetDevId(hGadget);
+
+ /* Create the busid argument string. */
+ size_t cbRet = RTStrPrintf(&aszBus[0], RT_ELEMENTS(aszBus), "%u-%u", uBusId, uDevId);
+ if (cbRet == RT_ELEMENTS(aszBus))
+ return VERR_BUFFER_OVERFLOW;
+
+ /* Bind to the USB/IP server. */
+ RTPROCESS hProcUsbIp = NIL_RTPROCESS;
+ const char *apszArgv[5];
+
+ apszArgv[0] = "usbip";
+ apszArgv[1] = fBind ? "bind" : "unbind";
+ apszArgv[2] = "-b";
+ apszArgv[3] = &aszBus[0];
+ apszArgv[4] = NULL;
+
+ int rc = RTProcCreate("usbip", apszArgv, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProcUsbIp);
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS ProcSts;
+ rc = RTProcWait(hProcUsbIp, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
+ if (RT_SUCCESS(rc))
+ {
+ /* Evaluate the process status. */
+ if ( ProcSts.enmReason != RTPROCEXITREASON_NORMAL
+ || ProcSts.iStatus != 0)
+ rc = VERR_UNRESOLVED_ERROR; /** @todo Log and give finer grained status code. */
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnInit}
+ */
+static DECLCALLBACK(int) utsGadgetHostUsbIpInit(PUTSGADGETHOSTTYPEINT pIf, PCUTSGADGETCFGITEM paCfg)
+{
+ int rc = VINF_SUCCESS;
+ uint16_t uPort = 0;
+
+ pIf->hProcUsbIp = NIL_RTPROCESS;
+
+ rc = utsGadgetCfgQueryU16Def(paCfg, "UsbIp/Port", &uPort, UTS_GADGET_HOST_USBIP_PORT_DEF);
+ if (RT_SUCCESS(rc))
+ {
+ /* Make sure the kernel drivers are loaded. */
+ rc = utsPlatformModuleLoad("usbip-core", NULL, 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = utsPlatformModuleLoad("usbip-host", NULL, 0);
+ if (RT_SUCCESS(rc))
+ {
+ char aszPort[10];
+ char aszPidFile[64];
+ const char *apszArgv[6];
+
+ RTStrPrintf(aszPort, RT_ELEMENTS(aszPort), "%u", uPort);
+ RTStrPrintf(aszPidFile, RT_ELEMENTS(aszPidFile), "/var/run/usbipd-%u.pid", uPort);
+ /* Start the USB/IP server process. */
+ apszArgv[0] = "usbipd";
+ apszArgv[1] = "--tcp-port";
+ apszArgv[2] = aszPort;
+ apszArgv[3] = "--pid";
+ apszArgv[4] = aszPidFile;
+ apszArgv[5] = NULL;
+ rc = RTProcCreate("usbipd", apszArgv, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &pIf->hProcUsbIp);
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait for a bit to make sure the server started up successfully. */
+ uint64_t tsStart = RTTimeMilliTS();
+ do
+ {
+ RTPROCSTATUS ProcSts;
+ rc = RTProcWait(pIf->hProcUsbIp, RTPROCWAIT_FLAGS_NOBLOCK, &ProcSts);
+ if (rc != VERR_PROCESS_RUNNING)
+ {
+ rc = VERR_INVALID_HANDLE;
+ break;
+ }
+ RTThreadSleep(1);
+ rc = VINF_SUCCESS;
+ } while (RTTimeMilliTS() - tsStart < 2 * 1000); /* 2 seconds. */
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnTerm}
+ */
+static DECLCALLBACK(void) utsGadgetHostUsbIpTerm(PUTSGADGETHOSTTYPEINT pIf)
+{
+ /* Kill the process and wait for it to terminate. */
+ RTProcTerminate(pIf->hProcUsbIp);
+
+ RTPROCSTATUS ProcSts;
+ RTProcWait(pIf->hProcUsbIp, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnGadgetAdd}
+ */
+static DECLCALLBACK(int) utsGadgetHostUsbIpGadgetAdd(PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget)
+{
+ /* Nothing to do so far. */
+ RT_NOREF2(pIf, hGadget);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnGadgetRemove}
+ */
+static DECLCALLBACK(int) utsGadgetHostUsbIpGadgetRemove(PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget)
+{
+ /* Nothing to do so far. */
+ RT_NOREF2(pIf, hGadget);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnGadgetConnect}
+ */
+static DECLCALLBACK(int) utsGadgetHostUsbIpGadgetConnect(PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget)
+{
+ return usbGadgetHostUsbIpBindUnbind(pIf, hGadget, true /* fBind */);
+}
+
+
+/**
+ * @interface_method_impl{UTSGADGETHOSTIF,pfnGadgetDisconnect}
+ */
+static DECLCALLBACK(int) utsGadgetHostUsbIpGadgetDisconnect(PUTSGADGETHOSTTYPEINT pIf, UTSGADGET hGadget)
+{
+ return usbGadgetHostUsbIpBindUnbind(pIf, hGadget, false /* fBind */);
+}
+
+
+
+/**
+ * The gadget host interface callback table.
+ */
+const UTSGADGETHOSTIF g_UtsGadgetHostIfUsbIp =
+{
+ /** enmType */
+ UTSGADGETHOSTTYPE_USBIP,
+ /** pszDesc */
+ "UTS USB/IP gadget host",
+ /** cbIf */
+ sizeof(UTSGADGETHOSTTYPEINT),
+ /** pfnInit */
+ utsGadgetHostUsbIpInit,
+ /** pfnTerm */
+ utsGadgetHostUsbIpTerm,
+ /** pfnGadgetAdd */
+ utsGadgetHostUsbIpGadgetAdd,
+ /** pfnGadgetRemove */
+ utsGadgetHostUsbIpGadgetRemove,
+ /** pfnGadgetConnect */
+ utsGadgetHostUsbIpGadgetConnect,
+ /** pfnGadgetDisconnect */
+ utsGadgetHostUsbIpGadgetDisconnect
+};
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetInternal.h b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetInternal.h
new file mode 100644
index 00000000..dce2719f
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetInternal.h
@@ -0,0 +1,118 @@
+/* $Id: UsbTestServiceGadgetInternal.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Interal gadget interfaces.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetInternal_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#include "UsbTestServiceGadget.h"
+
+RT_C_DECLS_BEGIN
+
+/** Pointer to an opaque type dependent gadget host instance data. */
+typedef struct UTSGADGETCLASSINT *PUTSGADGETCLASSINT;
+/** Pointer to a gadget host instance pointer. */
+typedef PUTSGADGETCLASSINT *PPUTSGADGETCLASSINT;
+
+/**
+ * Gadget class interface.
+ */
+typedef struct UTSGADGETCLASSIF
+{
+ /** The gadget class type implemented. */
+ UTSGADGETCLASS enmClass;
+ /** Description. */
+ const char *pszDesc;
+ /** Size of the class specific instance data. */
+ size_t cbClass;
+
+ /**
+ * Initializes the gadget class instance.
+ *
+ * @returns IPRT status code.
+ * @param pClass The interface specific instance data.
+ * @param paCfg The configuration of the interface.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnInit, (PUTSGADGETCLASSINT pClass, PCUTSGADGETCFGITEM paCfg));
+
+ /**
+ * Terminates the gadget class instance.
+ *
+ * @param pClass The interface specific instance data.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnTerm, (PUTSGADGETCLASSINT pClass));
+
+ /**
+ * Returns the bus ID of the class instance.
+ *
+ * @returns Bus ID.
+ * @param pClass The interface specific instance data.
+ */
+ DECLR3CALLBACKMEMBER(uint32_t, pfnGetBusId, (PUTSGADGETCLASSINT pClass));
+
+ /**
+ * Connects the gadget.
+ *
+ * @returns IPRT status code.
+ * @param pClass The interface specific instance data.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnConnect, (PUTSGADGETCLASSINT pClass));
+
+ /**
+ * Disconnect the gadget.
+ *
+ * @returns IPRT status code.
+ * @param pClass The interface specific instance data.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnDisconnect, (PUTSGADGETCLASSINT pClass));
+
+} UTSGADGETCLASSIF;
+/** Pointer to a gadget class callback table. */
+typedef UTSGADGETCLASSIF *PUTSGADGETCLASSIF;
+/** Pointer to a const gadget host callback table. */
+typedef const struct UTSGADGETCLASSIF *PCUTSGADGETCLASSIF;
+
+extern UTSGADGETCLASSIF const g_UtsGadgetClassTest;
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServiceGadgetInternal_h */
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceInternal.h b/src/VBox/ValidationKit/utils/usb/UsbTestServiceInternal.h
new file mode 100644
index 00000000..71e2d961
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceInternal.h
@@ -0,0 +1,226 @@
+/* $Id: UsbTestServiceInternal.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServiceInternal_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServiceInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+
+#include "UsbTestServiceProtocol.h"
+
+RT_C_DECLS_BEGIN
+
+/** Opaque UTS transport layer specific client data. */
+typedef struct UTSTRANSPORTCLIENT *PUTSTRANSPORTCLIENT;
+typedef PUTSTRANSPORTCLIENT *PPUTSTRANSPORTCLIENT;
+
+/**
+ * Transport layer descriptor.
+ */
+typedef struct UTSTRANSPORT
+{
+ /** The name. */
+ char szName[16];
+ /** The description. */
+ const char *pszDesc;
+ /** Pointer to an array of options. */
+ PCRTGETOPTDEF paOpts;
+ /** The number of options in the array. */
+ size_t cOpts;
+
+ /**
+ * Print the usage information for this transport layer.
+ *
+ * @param pStream The stream to print the usage info to.
+ *
+ * @remarks This is only required if TXSTRANSPORT::cOpts is greater than 0.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnUsage, (PRTSTREAM pStream));
+
+ /**
+ * Handle an option.
+ *
+ * When encountering an options that is not part of the base options, we'll call
+ * this method for each transport layer until one handles it.
+ *
+ * @retval VINF_SUCCESS if handled.
+ * @retval VERR_TRY_AGAIN if not handled.
+ * @retval VERR_INVALID_PARAMETER if we should exit with a non-zero status.
+ *
+ * @param ch The short option value.
+ * @param pVal Pointer to the value union.
+ *
+ * @remarks This is only required if TXSTRANSPORT::cOpts is greater than 0.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnOption, (int ch, PCRTGETOPTUNION pVal));
+
+ /**
+ * Initializes the transport layer.
+ *
+ * @returns IPRT status code. On errors, the transport layer shall call
+ * RTMsgError to display the error details to the user.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnInit, (void));
+
+ /**
+ * Terminate the transport layer, closing and freeing resources.
+ *
+ * On errors, the transport layer shall call RTMsgError to display the error
+ * details to the user.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnTerm, (void));
+
+ /**
+ * Waits for a new client to connect and returns the client specific data on
+ * success.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnWaitForConnect, (PPUTSTRANSPORTCLIENT ppClientNew));
+
+ /**
+ * Polls for incoming packets.
+ *
+ * @returns true if there are pending packets, false if there isn't.
+ * @param pClient The client to poll for data.
+ */
+ DECLR3CALLBACKMEMBER(bool, pfnPollIn, (PUTSTRANSPORTCLIENT pClient));
+
+ /**
+ * Adds any pollable handles to the poll set.
+ *
+ * @returns IPRT status code.
+ * @param hPollSet The poll set to add them to.
+ * @param pClient The transport client structure.
+ * @param idStart The handle ID to start at.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnPollSetAdd, (RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart));
+
+ /**
+ * Removes the given client frmo the given pollset.
+ *
+ * @returns IPRT status code.
+ * @param hPollSet The poll set to remove from.
+ * @param pClient The transport client structure.
+ * @param idStart The handle ID to remove.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnPollSetRemove, (RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart));
+
+ /**
+ * Receives an incoming packet.
+ *
+ * This will block until the data becomes available or we're interrupted by a
+ * signal or something.
+ *
+ * @returns IPRT status code. On error conditions other than VERR_INTERRUPTED,
+ * the current operation will be aborted when applicable. When
+ * interrupted, the transport layer will store the data until the next
+ * receive call.
+ *
+ * @param pClient The transport client structure.
+ * @param ppPktHdr Where to return the pointer to the packet we've
+ * read. This is allocated from the heap using
+ * RTMemAlloc (w/ UTSPKT_ALIGNMENT) and must be
+ * free by calling RTMemFree.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnRecvPkt, (PUTSTRANSPORTCLIENT pClient, PPUTSPKTHDR ppPktHdr));
+
+ /**
+ * Sends an outgoing packet.
+ *
+ * This will block until the data has been written.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_INTERRUPTED if interrupted before anything was sent.
+ *
+ * @param pClient The transport client structure.
+ * @param pPktHdr The packet to send. The size is given by
+ * aligning the size in the header by
+ * UTSPKT_ALIGNMENT.
+ */
+ DECLR3CALLBACKMEMBER(int, pfnSendPkt, (PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr));
+
+ /**
+ * Sends a babble packet and disconnects the client (if applicable).
+ *
+ * @param pClient The transport client structure.
+ * @param pPktHdr The packet to send. The size is given by
+ * aligning the size in the header by
+ * UTSPKT_ALIGNMENT.
+ * @param cMsSendTimeout The send timeout measured in milliseconds.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnBabble, (PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout));
+
+ /**
+ * Notification about a client HOWDY.
+ *
+ * @param pClient The transport client structure.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyHowdy, (PUTSTRANSPORTCLIENT pClient));
+
+ /**
+ * Notification about a client BYE.
+ *
+ * For connection oriented transport layers, it would be good to disconnect the
+ * client at this point.
+ *
+ * @param pClient The transport client structure.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyBye, (PUTSTRANSPORTCLIENT pClient));
+
+ /**
+ * Notification about a REBOOT or SHUTDOWN.
+ *
+ * For connection oriented transport layers, stop listening for and
+ * accepting at this point.
+ */
+ DECLR3CALLBACKMEMBER(void, pfnNotifyReboot, (void));
+
+ /** Non-zero end marker. */
+ uint32_t u32EndMarker;
+} UTSTRANSPORT;
+/** Pointer to a const transport layer descriptor. */
+typedef const struct UTSTRANSPORT *PCUTSTRANSPORT;
+
+
+extern UTSTRANSPORT const g_TcpTransport;
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServiceInternal_h */
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform-linux.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform-linux.cpp
new file mode 100644
index 00000000..0f422479
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform-linux.cpp
@@ -0,0 +1,448 @@
+/* $Id: UsbTestServicePlatform-linux.cpp $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Platform
+ * specific helpers - Linux version.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+#include <iprt/linux/sysfs.h>
+
+#include "UsbTestServicePlatform.h"
+
+
+/*********************************************************************************************************************************
+* Constants And Macros, Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Where the dummy_hcd.* and dummy_udc.* entries are stored. */
+#define UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/sys/devices/platform"
+
+/**
+ * A USB bus provided by the dummy HCD.
+ */
+typedef struct UTSPLATFORMLNXDUMMYHCDBUS
+{
+ /** The bus ID on the host the dummy HCD is serving. */
+ uint32_t uBusId;
+ /** Flag whether this is a super speed bus. */
+ bool fSuperSpeed;
+} UTSPLATFORMLNXDUMMYHCDBUS;
+/** Pointer to a Dummy HCD bus. */
+typedef UTSPLATFORMLNXDUMMYHCDBUS *PUTSPLATFORMLNXDUMMYHCDBUS;
+
+/**
+ * A dummy UDC descriptor.
+ */
+typedef struct UTSPLATFORMLNXDUMMYHCD
+{
+ /** Index of the dummy hcd entry. */
+ uint32_t idxDummyHcd;
+ /** Name for the dummy HCD. */
+ const char *pszHcdName;
+ /** Name for the accompanying dummy HCD. */
+ const char *pszUdcName;
+ /** Flag whether this HCD is free for use. */
+ bool fAvailable;
+ /** Flag whether this HCD contains a super speed capable bus. */
+ bool fSuperSpeed;
+ /** Number of busses this HCD instance serves. */
+ unsigned cBusses;
+ /** Bus structures the HCD serves.*/
+ PUTSPLATFORMLNXDUMMYHCDBUS paBusses;
+} UTSPLATFORMLNXDUMMYHCD;
+/** Pointer to a dummy HCD entry. */
+typedef UTSPLATFORMLNXDUMMYHCD *PUTSPLATFORMLNXDUMMYHCD;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Array of dummy HCD entries. */
+static PUTSPLATFORMLNXDUMMYHCD g_paDummyHcd = NULL;
+/** Number of Dummy hCD entries in the array. */
+static unsigned g_cDummyHcd = 0;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Queries the assigned busses for the given dummy HCD instance.
+ *
+ * @returns IPRT status code.
+ * @param pHcd The dummy HCD bus instance.
+ * @param pszHcdName The base HCD name.
+ */
+static int utsPlatformLnxDummyHcdQueryBusses(PUTSPLATFORMLNXDUMMYHCD pHcd, const char *pszHcdName)
+{
+ int rc = VINF_SUCCESS;
+ char aszPath[RTPATH_MAX + 1];
+ unsigned idxBusCur = 0;
+ unsigned idxBusMax = 0;
+
+ size_t cchPath = RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath), UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.%u/usb*",
+ pszHcdName, pHcd->idxDummyHcd);
+ if (cchPath == RT_ELEMENTS(aszPath))
+ return VERR_BUFFER_OVERFLOW;
+
+ RTDIR hDir = NULL;
+ rc = RTDirOpenFiltered(&hDir, aszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTDIRENTRY DirFolderContent;
+ rc = RTDirRead(hDir, &DirFolderContent, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uBusId = 0;
+
+ /* Extract the bus number - it is after "usb", i.e. "usb9" indicates a bus ID of 9. */
+ rc = RTStrToUInt32Ex(&DirFolderContent.szName[3], NULL, 10, &uBusId);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check whether this is a super speed bus. */
+ int64_t iSpeed = 0;
+ bool fSuperSpeed = false;
+ rc = RTLinuxSysFsReadIntFile(10, &iSpeed, UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.%u/%s/speed",
+ pszHcdName, pHcd->idxDummyHcd, DirFolderContent.szName);
+ if ( RT_SUCCESS(rc)
+ && (iSpeed == 5000 || iSpeed == 10000))
+ {
+ fSuperSpeed = true;
+ pHcd->fSuperSpeed = true;
+ }
+
+ /* Add to array of available busses for this HCD. */
+ if (idxBusCur == idxBusMax)
+ {
+ size_t cbNew = (idxBusMax + 10) * sizeof(UTSPLATFORMLNXDUMMYHCDBUS);
+ PUTSPLATFORMLNXDUMMYHCDBUS pNew = (PUTSPLATFORMLNXDUMMYHCDBUS)RTMemRealloc(pHcd->paBusses, cbNew);
+ if (pNew)
+ {
+ idxBusMax += 10;
+ pHcd->paBusses = pNew;
+ }
+ }
+
+ if (idxBusCur < idxBusMax)
+ {
+ pHcd->paBusses[idxBusCur].uBusId = uBusId;
+ pHcd->paBusses[idxBusCur].fSuperSpeed = fSuperSpeed;
+ idxBusCur++;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ } while (RT_SUCCESS(rc));
+
+ pHcd->cBusses = idxBusCur;
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ RTDirClose(hDir);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Scans all available HCDs with the given name.
+ *
+ * @returns IPRT status code.
+ * @param pszHcdName The base HCD name.
+ * @param pszUdcName The base UDC name.
+ */
+static int utsPlatformLnxHcdScanByName(const char *pszHcdName, const char *pszUdcName)
+{
+ char aszPath[RTPATH_MAX + 1];
+ size_t cchPath = RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath),
+ UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.*", pszHcdName);
+ if (cchPath == RT_ELEMENTS(aszPath))
+ return VERR_BUFFER_OVERFLOW;
+
+ /* Enumerate the available HCD and their bus numbers. */
+ RTDIR hDir = NULL;
+ int rc = RTDirOpenFiltered(&hDir, aszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned idxHcdCur = g_cDummyHcd;
+ unsigned idxHcdMax = g_cDummyHcd;
+
+ do
+ {
+ RTDIRENTRY DirFolderContent;
+ rc = RTDirRead(hDir, &DirFolderContent, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the HCD index and assigned bus number form the sysfs entries,
+ * Any error here is silently ignored and results in the HCD not being
+ * added to the list of available controllers.
+ */
+ const char *pszIdx = RTStrStr(DirFolderContent.szName, ".");
+ if (pszIdx)
+ {
+ /* Skip the separator and convert number to index. */
+ pszIdx++;
+
+ uint32_t idxHcd = 0;
+ rc = RTStrToUInt32Ex(pszIdx, NULL, 10, &idxHcd);
+ if (RT_SUCCESS(rc))
+ {
+ /* Add to array of available HCDs. */
+ if (idxHcdCur == idxHcdMax)
+ {
+ size_t cbNew = (idxHcdMax + 10) * sizeof(UTSPLATFORMLNXDUMMYHCD);
+ PUTSPLATFORMLNXDUMMYHCD pNew = (PUTSPLATFORMLNXDUMMYHCD)RTMemRealloc(g_paDummyHcd, cbNew);
+ if (pNew)
+ {
+ idxHcdMax += 10;
+ g_paDummyHcd = pNew;
+ }
+ }
+
+ if (idxHcdCur < idxHcdMax)
+ {
+ g_paDummyHcd[idxHcdCur].idxDummyHcd = idxHcd;
+ g_paDummyHcd[idxHcdCur].pszHcdName = pszHcdName;
+ g_paDummyHcd[idxHcdCur].pszUdcName = pszUdcName;
+ g_paDummyHcd[idxHcdCur].fAvailable = true;
+ g_paDummyHcd[idxHcdCur].fSuperSpeed = false;
+ g_paDummyHcd[idxHcdCur].cBusses = 0;
+ g_paDummyHcd[idxHcdCur].paBusses = NULL;
+ rc = utsPlatformLnxDummyHcdQueryBusses(&g_paDummyHcd[idxHcdCur], pszHcdName);
+ if (RT_SUCCESS(rc))
+ idxHcdCur++;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+ } while (RT_SUCCESS(rc));
+
+ g_cDummyHcd = idxHcdCur;
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ RTDirClose(hDir);
+ }
+
+ return rc;
+}
+
+DECLHIDDEN(int) utsPlatformInit(void)
+{
+ /* Load the modules required for setting up USB/IP testing. */
+ int rc = utsPlatformModuleLoad("libcomposite", NULL, 0);
+ if (RT_SUCCESS(rc))
+ {
+ const char *apszArg[] = { "num=20" }; /** @todo Make configurable from config. */
+ rc = utsPlatformModuleLoad("dummy_hcd", &apszArg[0], RT_ELEMENTS(apszArg));
+ if (RT_SUCCESS(rc))
+ rc = utsPlatformModuleLoad("dummy_hcd_ss", &apszArg[0], RT_ELEMENTS(apszArg));
+ if (RT_SUCCESS(rc))
+ rc = utsPlatformLnxHcdScanByName("dummy_hcd", "dummy_udc");
+ if (RT_SUCCESS(rc))
+ rc = utsPlatformLnxHcdScanByName("dummy_hcd_ss", "dummy_udc_ss");
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(void) utsPlatformTerm(void)
+{
+ /* Unload dummy HCD. */
+ utsPlatformModuleUnload("dummy_hcd");
+ utsPlatformModuleUnload("dummy_hcd_ss");
+
+ RTMemFree(g_paDummyHcd);
+}
+
+
+DECLHIDDEN(int) utsPlatformModuleLoad(const char *pszModule, const char **papszArgv,
+ unsigned cArgv)
+{
+ RTPROCESS hProcModprobe = NIL_RTPROCESS;
+ const char **papszArgs = (const char **)RTMemAllocZ((3 + cArgv) * sizeof(const char *));
+ if (RT_UNLIKELY(!papszArgs))
+ return VERR_NO_MEMORY;
+
+ papszArgs[0] = "modprobe";
+ papszArgs[1] = pszModule;
+
+ unsigned idx;
+ for (idx = 0; idx < cArgv; idx++)
+ papszArgs[2+idx] = papszArgv[idx];
+ papszArgs[2+idx] = NULL;
+
+ int rc = RTProcCreate("modprobe", papszArgs, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProcModprobe);
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS ProcSts;
+ rc = RTProcWait(hProcModprobe, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
+ if (RT_SUCCESS(rc))
+ {
+ /* Evaluate the process status. */
+ if ( ProcSts.enmReason != RTPROCEXITREASON_NORMAL
+ || ProcSts.iStatus != 0)
+ rc = VERR_UNRESOLVED_ERROR; /** @todo Log and give finer grained status code. */
+ }
+ }
+
+ RTMemFree(papszArgs);
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsPlatformModuleUnload(const char *pszModule)
+{
+ RTPROCESS hProcModprobe = NIL_RTPROCESS;
+ const char *apszArgv[3];
+
+ apszArgv[0] = "rmmod";
+ apszArgv[1] = pszModule;
+ apszArgv[2] = NULL;
+
+ int rc = RTProcCreate("rmmod", apszArgv, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProcModprobe);
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS ProcSts;
+ rc = RTProcWait(hProcModprobe, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
+ if (RT_SUCCESS(rc))
+ {
+ /* Evaluate the process status. */
+ if ( ProcSts.enmReason != RTPROCEXITREASON_NORMAL
+ || ProcSts.iStatus != 0)
+ rc = VERR_UNRESOLVED_ERROR; /** @todo Log and give finer grained status code. */
+ }
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsPlatformLnxAcquireUDC(bool fSuperSpeed, char **ppszUdc, uint32_t *puBusId)
+{
+ int rc = VERR_NOT_FOUND;
+
+ for (unsigned i = 0; i < g_cDummyHcd; i++)
+ {
+ PUTSPLATFORMLNXDUMMYHCD pHcd = &g_paDummyHcd[i];
+
+ /*
+ * We can't use a super speed capable UDC for gadgets with lower speeds
+ * because they hardcode the maximum speed to SuperSpeed most of the time
+ * which will make it unusable for lower speeds.
+ */
+ if ( pHcd->fAvailable
+ && pHcd->fSuperSpeed == fSuperSpeed)
+ {
+ /* Check all assigned busses for a speed match. */
+ for (unsigned idxBus = 0; idxBus < pHcd->cBusses; idxBus++)
+ {
+ if (pHcd->paBusses[idxBus].fSuperSpeed == fSuperSpeed)
+ {
+ rc = VINF_SUCCESS;
+ int cbRet = RTStrAPrintf(ppszUdc, "%s.%u", pHcd->pszUdcName, pHcd->idxDummyHcd);
+ if (cbRet == -1)
+ rc = VERR_NO_STR_MEMORY;
+ *puBusId = pHcd->paBusses[idxBus].uBusId;
+ pHcd->fAvailable = false;
+ break;
+ }
+ }
+
+ if (rc != VERR_NOT_FOUND)
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) utsPlatformLnxReleaseUDC(const char *pszUdc)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ const char *pszIdx = RTStrStr(pszUdc, ".");
+ if (pszIdx)
+ {
+ size_t cchUdcName = pszIdx - pszUdc;
+ pszIdx++;
+ uint32_t idxHcd = 0;
+ rc = RTStrToUInt32Ex(pszIdx, NULL, 10, &idxHcd);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_NOT_FOUND;
+
+ for (unsigned i = 0; i < g_cDummyHcd; i++)
+ {
+ if ( g_paDummyHcd[i].idxDummyHcd == idxHcd
+ && !RTStrNCmp(g_paDummyHcd[i].pszUdcName, pszUdc, cchUdcName))
+ {
+ AssertReturn(!g_paDummyHcd[i].fAvailable, VERR_INVALID_PARAMETER);
+ g_paDummyHcd[i].fAvailable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform.h b/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform.h
new file mode 100644
index 00000000..cf5eed26
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform.h
@@ -0,0 +1,105 @@
+/* $Id: UsbTestServicePlatform.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Platform specific helpers.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServicePlatform_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServicePlatform_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Initializes the platform specific structures for UTS.
+ *
+ * @returns IPRT status code.
+ */
+DECLHIDDEN(int) utsPlatformInit(void);
+
+/**
+ * Frees all platform specific structures for UTS.
+ */
+DECLHIDDEN(void) utsPlatformTerm(void);
+
+/**
+ * Loads the specified kernel module on the platform.
+ *
+ * @returns IPRT status code.
+ * @param pszModule The module to load.
+ * @param papszArgv Array of arguments to pass to the module.
+ * @param cArgv Number of argument array entries.
+ */
+DECLHIDDEN(int) utsPlatformModuleLoad(const char *pszModule, const char **papszArgv,
+ unsigned cArgv);
+
+/**
+ * Unloads the specified kernel module on the platform.
+ *
+ * @returns IPRT status code.
+ * @param pszModule The module to unload.
+ */
+DECLHIDDEN(int) utsPlatformModuleUnload(const char *pszModule);
+
+#ifdef RT_OS_LINUX
+
+/**
+ * Acquires a free UDC to attach a gadget to.
+ *
+ * @returns IPRT status code.
+ * @param fSuperSpeed Flag whether a super speed bus is required.
+ * @param ppszUdc Where to store the pointer to the name of the UDC on success.
+ * Free with RTStrFree().
+ * @param puBusId Where to store the bus ID the UDC is attached to on the host side.
+ */
+DECLHIDDEN(int) utsPlatformLnxAcquireUDC(bool fSuperSpeed, char **ppszUdc, uint32_t *puBusId);
+
+/**
+ * Releases the given UDC for other use.
+ *
+ * @returns IPRT status code.
+ * @param pszUdc The UDC to release.
+ */
+DECLHIDDEN(int) utsPlatformLnxReleaseUDC(const char *pszUdc);
+
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServicePlatform_h */
+
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.cpp
new file mode 100644
index 00000000..a32e8a3e
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.cpp
@@ -0,0 +1,120 @@
+/* $Id: UsbTestServiceProtocol.cpp $ */
+/** @file
+ * UsbTestService - Remote USB test configuration and execution server, Protocol helpers.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/asm.h>
+#include <iprt/cdefs.h>
+
+#include "UsbTestServiceProtocol.h"
+
+
+
+/**
+ * Converts a UTS packet header from host to network byte order.
+ *
+ * @param pPktHdr The packet header to convert.
+ */
+DECLINLINE(void) utsProtocolPktHdrH2N(PUTSPKTHDR pPktHdr)
+{
+ pPktHdr->cb = RT_H2N_U32(pPktHdr->cb);
+ pPktHdr->uCrc32 = RT_H2N_U32(pPktHdr->uCrc32);
+}
+
+
+/**
+ * Converts a UTS packet header from network to host byte order.
+ *
+ * @param pPktHdr The packet header to convert.
+ */
+DECLINLINE(void) utsProtocolPktHdrN2H(PUTSPKTHDR pPktHdr)
+{
+ pPktHdr->cb = RT_N2H_U32(pPktHdr->cb);
+ pPktHdr->uCrc32 = RT_N2H_U32(pPktHdr->uCrc32);
+}
+
+
+/**
+ * Converts a UTS status header from host to network byte order.
+ *
+ * @param pPktHdr The packet header to convert.
+ */
+DECLINLINE(void) utsProtocolStsHdrH2N(PUTSPKTSTS pPktHdr)
+{
+ utsProtocolPktHdrH2N(&pPktHdr->Hdr);
+ pPktHdr->rcReq = RT_H2N_U32(pPktHdr->rcReq);
+ pPktHdr->cchStsMsg = RT_H2N_U32(pPktHdr->cchStsMsg);
+}
+
+
+/**
+ * Converts a UTS status header from network to host byte order.
+ *
+ * @param pPktHdr The packet header to convert.
+ */
+DECLINLINE(void) utsProtocolStsHdrN2H(PUTSPKTSTS pPktHdr)
+{
+ utsProtocolPktHdrN2H(&pPktHdr->Hdr);
+ pPktHdr->rcReq = RT_N2H_U32(pPktHdr->rcReq);
+ pPktHdr->cchStsMsg = RT_N2H_U32(pPktHdr->cchStsMsg);
+}
+
+
+DECLHIDDEN(void) utsProtocolReqH2N(PUTSPKTHDR pPktHdr)
+{
+ utsProtocolPktHdrH2N(pPktHdr);
+}
+
+
+DECLHIDDEN(void) utsProtocolReqN2H(PUTSPKTHDR pPktHdr)
+{
+ RT_NOREF1(pPktHdr);
+}
+
+
+DECLHIDDEN(void) utsProtocolRepH2N(PUTSPKTSTS pPktHdr)
+{
+ RT_NOREF1(pPktHdr);
+}
+
+
+DECLHIDDEN(void) utsProtocolRepN2H(PUTSPKTSTS pPktHdr)
+{
+ RT_NOREF1(pPktHdr);
+}
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.h b/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.h
new file mode 100644
index 00000000..5378493b
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceProtocol.h
@@ -0,0 +1,373 @@
+/* $Id: UsbTestServiceProtocol.h $ */
+/** @file
+ * UsbTestServ - Remote USB test configuration and execution server, Protocol Header.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_usb_UsbTestServiceProtocol_h
+#define VBOX_INCLUDED_SRC_usb_UsbTestServiceProtocol_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Common Packet header (for requests and replies).
+ */
+typedef struct UTSPKTHDR
+{
+ /** The unpadded packet length. This include this header. */
+ uint32_t cb;
+ /** The CRC-32 for the packet starting from the opcode field. 0 if the packet
+ * hasn't been CRCed. */
+ uint32_t uCrc32;
+ /** Packet opcode, an unterminated ASCII string. */
+ uint8_t achOpcode[8];
+} UTSPKTHDR;
+AssertCompileSize(UTSPKTHDR, 16);
+/** Pointer to a packet header. */
+typedef UTSPKTHDR *PUTSPKTHDR;
+/** Pointer to a packet header. */
+typedef UTSPKTHDR const *PCUTSPKTHDR;
+/** Pointer to a packet header pointer. */
+typedef PUTSPKTHDR *PPUTSPKTHDR;
+
+/** Packet alignment. */
+#define UTSPKT_ALIGNMENT 16
+/** Max packet size. */
+#define UTSPKT_MAX_SIZE _256K
+
+/**
+ * Status packet.
+ */
+typedef struct UTSPKTSTS
+{
+ /** Embedded common packet header. */
+ UTSPKTHDR Hdr;
+ /** The IPRT status code of the request. */
+ int32_t rcReq;
+ /** Size of the optional status message following this structure -
+ * only for errors. */
+ uint32_t cchStsMsg;
+ /** Padding - reserved. */
+ uint8_t au8Padding[8];
+} UTSPKTSTS;
+AssertCompileSizeAlignment(UTSPKTSTS, UTSPKT_ALIGNMENT);
+/** Pointer to a status packet header. */
+typedef UTSPKTSTS *PUTSPKTSTS;
+
+#define UTSPKT_OPCODE_HOWDY "HOWDY "
+
+/** 32bit protocol version consisting of a 16bit major and 16bit minor part. */
+#define UTS_PROTOCOL_VS (UTS_PROTOCOL_VS_MAJOR | UTS_PROTOCOL_VS_MINOR)
+/** The major version part of the protocol version. */
+#define UTS_PROTOCOL_VS_MAJOR (1 << 16)
+/** The minor version part of the protocol version. */
+#define UTS_PROTOCOL_VS_MINOR (0)
+
+/**
+ * The HOWDY request structure.
+ */
+typedef struct UTSPKTREQHOWDY
+{
+ /** Embedded packet header. */
+ UTSPKTHDR Hdr;
+ /** Version of the protocol the client wants to use. */
+ uint32_t uVersion;
+ /** Mask of USB device connections the client wants to use. */
+ uint32_t fUsbConn;
+ /** The number of characters for the hostname. */
+ uint32_t cchHostname;
+ /** The client host name as terminated ASCII string. */
+ char achHostname[68];
+} UTSPKTREQHOWDY;
+AssertCompileSizeAlignment(UTSPKTREQHOWDY, UTSPKT_ALIGNMENT);
+/** Pointer to a HOWDY request structure. */
+typedef UTSPKTREQHOWDY *PUTSPKTREQHOWDY;
+
+/**
+ * The HOWDY reply structure.
+ */
+typedef struct UTSPKTREPHOWDY
+{
+ /** Status packet. */
+ UTSPKTSTS Sts;
+ /** Version to use for the established connection. */
+ uint32_t uVersion;
+ /** Mask of supported USB device connections for this connection. */
+ uint32_t fUsbConn;
+ /** Port number the USB/IP server is listening on if
+ * the client requested USB/IP support and the server can
+ * deliver it. */
+ uint32_t uUsbIpPort;
+ /** Maximum number of devices supported over USB/IP
+ * at the same time. */
+ uint32_t cUsbIpDevices;
+ /** Maximum number of physical devices supported for this client
+ * if a physical connection is present. */
+ uint32_t cPhysicalDevices;
+ /** Padding - reserved. */
+ uint8_t au8Padding[12];
+} UTSPKTREPHOWDY;
+AssertCompileSizeAlignment(UTSPKTREPHOWDY, UTSPKT_ALIGNMENT);
+/** Pointer to a HOWDY reply structure. */
+typedef UTSPKTREPHOWDY *PUTSPKTREPHOWDY;
+
+/** Connections over USB/IP are supported. */
+#define UTSPKT_HOWDY_CONN_F_USBIP RT_BIT_32(0)
+/** The server has a physical connection available to the client
+ * which can be used for testing. */
+#define UTSPKT_HOWDY_CONN_F_PHYSICAL RT_BIT_32(1)
+
+
+#define UTSPKT_OPCODE_BYE "BYE "
+
+/* No additional structures for BYE. */
+
+#define UTSPKT_OPCODE_GADGET_CREATE "GDGTCRT "
+
+/**
+ * The GADGET CREATE request structure.
+ */
+typedef struct UTSPKTREQGDGTCTOR
+{
+ /** Embedded packet header. */
+ UTSPKTHDR Hdr;
+ /** Gadget type. */
+ uint32_t u32GdgtType;
+ /** Access methods. */
+ uint32_t u32GdgtAccess;
+ /** Number of config items - following this structure. */
+ uint32_t u32CfgItems;
+ /** Reserved. */
+ uint32_t u32Rsvd0;
+} UTSPKTREQGDGTCTOR;
+AssertCompileSizeAlignment(UTSPKTREQGDGTCTOR, UTSPKT_ALIGNMENT);
+/** Pointer to a GADGET CREATE structure. */
+typedef UTSPKTREQGDGTCTOR *PUTSPKTREQGDGTCTOR;
+
+/** Gadget type - Test device. */
+#define UTSPKT_GDGT_CREATE_TYPE_TEST UINT32_C(0x1)
+
+/** Gadget acess method - USB/IP. */
+#define UTSPKT_GDGT_CREATE_ACCESS_USBIP UINT32_C(0x1)
+
+/**
+ * Configuration item.
+ */
+typedef struct UTSPKTREQGDGTCTORCFGITEM
+{
+ /** Size of the key incuding termination in bytes. */
+ uint32_t u32KeySize;
+ /** Item type. */
+ uint32_t u32Type;
+ /** Size of the value string including termination in bytes. */
+ uint32_t u32ValSize;
+ /** Reserved. */
+ uint32_t u32Rsvd0;
+} UTSPKTREQGDGTCTORCFGITEM;
+AssertCompileSizeAlignment(UTSPKTREQGDGTCTORCFGITEM, UTSPKT_ALIGNMENT);
+/** Pointer to a configuration item. */
+typedef UTSPKTREQGDGTCTORCFGITEM *PUTSPKTREQGDGTCTORCFGITEM;
+
+/** Boolean configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_BOOLEAN UINT32_C(1)
+/** String configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_STRING UINT32_C(2)
+/** Unsigned 8-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_UINT8 UINT32_C(3)
+/** Unsigned 16-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_UINT16 UINT32_C(4)
+/** Unsigned 32-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_UINT32 UINT32_C(5)
+/** Unsigned 64-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_UINT64 UINT32_C(6)
+/** Signed 8-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_INT8 UINT32_C(7)
+/** Signed 16-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_INT16 UINT32_C(8)
+/** Signed 32-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_INT32 UINT32_C(9)
+/** Signed 64-bit integer configuration item type. */
+#define UTSPKT_GDGT_CFG_ITEM_TYPE_INT64 UINT32_C(10)
+
+/**
+ * The GADGET CREATE reply structure.
+ */
+typedef struct UTSPKTREPGDGTCTOR
+{
+ /** Status packet. */
+ UTSPKTSTS Sts;
+ /** The gadget ID on success. */
+ uint32_t idGadget;
+ /** Bus ID the gadget is attached to */
+ uint32_t u32BusId;
+ /** Device ID of the gadget on the bus. */
+ uint32_t u32DevId;
+ /** Padding - reserved. */
+ uint8_t au8Padding[4];
+} UTSPKTREPGDGTCTOR;
+AssertCompileSizeAlignment(UTSPKTREPGDGTCTOR, UTSPKT_ALIGNMENT);
+/** Pointer to a GADGET CREATE structure. */
+typedef UTSPKTREPGDGTCTOR *PUTSPKTREPGDGTCTOR;
+
+
+#define UTSPKT_OPCODE_GADGET_DESTROY "GDGTDTOR"
+
+/**
+ * The GADGET DESTROY request structure.
+ */
+typedef struct UTSPKTREQGDGTDTOR
+{
+ /** Embedded packet header. */
+ UTSPKTHDR Hdr;
+ /** Gadget ID as returned from the GADGET CREATE request on success. */
+ uint32_t idGadget;
+ /** Padding - reserved. */
+ uint8_t au8Padding[12];
+} UTSPKTREQGDGTDTOR;
+AssertCompileSizeAlignment(UTSPKTREQGDGTDTOR, UTSPKT_ALIGNMENT);
+/** Pointer to a GADGET DESTROY structure. */
+typedef UTSPKTREQGDGTDTOR *PUTSPKTREQGDGTDTOR;
+
+/* No additional structure for the reply (just standard STATUS packet). */
+
+#define UTSPKT_OPCODE_GADGET_CONNECT "GDGTCNCT"
+
+/**
+ * The GADGET CONNECT request structure.
+ */
+typedef struct UTSPKTREQGDGTCNCT
+{
+ /** Embedded packet header. */
+ UTSPKTHDR Hdr;
+ /** Gadget ID as returned from the GADGET CREATE request on success. */
+ uint32_t idGadget;
+ /** Padding - reserved. */
+ uint8_t au8Padding[12];
+} UTSPKTREQGDGTCNCT;
+AssertCompileSizeAlignment(UTSPKTREQGDGTCNCT, UTSPKT_ALIGNMENT);
+/** Pointer to a GADGET CONNECT request structure. */
+typedef UTSPKTREQGDGTCNCT *PUTSPKTREQGDGTCNCT;
+
+/* No additional structure for the reply (just standard STATUS packet). */
+
+#define UTSPKT_OPCODE_GADGET_DISCONNECT "GDGTDCNT"
+
+/**
+ * The GADGET DISCONNECT request structure.
+ */
+typedef struct UTSPKTREQGDGTDCNT
+{
+ /** Embedded packet header. */
+ UTSPKTHDR Hdr;
+ /** Gadget ID as returned from the GADGET CREATE request on success. */
+ uint32_t idGadget;
+ /** Padding - reserved. */
+ uint8_t au8Padding[12];
+} UTSPKTREQGDGTDCNT;
+AssertCompileSizeAlignment(UTSPKTREQGDGTDCNT, UTSPKT_ALIGNMENT);
+/** Pointer to a GADGET CONNECT request structure. */
+typedef UTSPKTREQGDGTDCNT *PUTSPKTREQGDGTDCNT;
+
+/* No additional structure for the reply (just standard STATUS packet). */
+
+/**
+ * Checks if the two opcodes match.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pPktHdr The packet header.
+ * @param pszOpcode2 The opcode we're comparing with. Does not have
+ * to be the whole 8 chars long.
+ */
+DECLINLINE(bool) utsIsSameOpcode(PCUTSPKTHDR pPktHdr, const char *pszOpcode2)
+{
+ if (pPktHdr->achOpcode[0] != pszOpcode2[0])
+ return false;
+ if (pPktHdr->achOpcode[1] != pszOpcode2[1])
+ return false;
+
+ unsigned i = 2;
+ while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
+ && pszOpcode2[i] != '\0')
+ {
+ if (pPktHdr->achOpcode[i] != pszOpcode2[i])
+ break;
+ i++;
+ }
+
+ if ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
+ && pszOpcode2[i] == '\0')
+ {
+ while ( i < RT_SIZEOFMEMB(UTSPKTHDR, achOpcode)
+ && pPktHdr->achOpcode[i] == ' ')
+ i++;
+ }
+
+ return i == RT_SIZEOFMEMB(UTSPKTHDR, achOpcode);
+}
+
+/**
+ * Converts a UTS request packet from host to network byte ordering.
+ *
+ * @param pPktHdr The packet to convert.
+ */
+DECLHIDDEN(void) utsProtocolReqH2N(PUTSPKTHDR pPktHdr);
+
+/**
+ * Converts a UTS request packet from network to host byte ordering.
+ *
+ * @param pPktHdr The packet to convert.
+ */
+DECLHIDDEN(void) utsProtocolReqN2H(PUTSPKTHDR pPktHdr);
+
+/**
+ * Converts a UTS reply packet from host to network byte ordering.
+ *
+ * @param pPktHdr The packet to convert.
+ */
+DECLHIDDEN(void) utsProtocolRepH2N(PUTSPKTHDR pPktHdr);
+
+/**
+ * Converts a UTS reply packet from network to host byte ordering.
+ *
+ * @param pPktHdr The packet to convert.
+ */
+DECLHIDDEN(void) utsProtocolRepN2H(PUTSPKTHDR pPktHdr);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_usb_UsbTestServiceProtocol_h */
diff --git a/src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp b/src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp
new file mode 100644
index 00000000..4d9013f7
--- /dev/null
+++ b/src/VBox/ValidationKit/utils/usb/UsbTestServiceTcp.cpp
@@ -0,0 +1,512 @@
+/* $Id: UsbTestServiceTcp.cpp $ */
+/** @file
+ * UsbTestService - Remote USB test configuration and execution server, TCP/IP Transport Layer.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/tcp.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "UsbTestServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The default server port. */
+#define UTS_TCP_DEF_BIND_PORT 6042
+/** The default server bind address. */
+#define UTS_TCP_DEF_BIND_ADDRESS ""
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * TCP specific client data.
+ */
+typedef struct UTSTRANSPORTCLIENT
+{
+ /** Socket of the current client. */
+ RTSOCKET hTcpClient;
+ /** The size of the stashed data. */
+ size_t cbTcpStashed;
+ /** The size of the stashed data allocation. */
+ size_t cbTcpStashedAlloced;
+ /** The stashed data. */
+ uint8_t *pbTcpStashed;
+} UTSTRANSPORTCLIENT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name TCP Parameters
+ * @{ */
+/** The addresses to bind to. Empty string means any. */
+static char g_szTcpBindAddr[256] = UTS_TCP_DEF_BIND_ADDRESS;
+/** The TCP port to listen to. */
+static uint32_t g_uTcpBindPort = UTS_TCP_DEF_BIND_PORT;
+/** @} */
+
+/** Pointer to the TCP server instance. */
+static PRTTCPSERVER g_pTcpServer = NULL;
+#if 0 /* unused */
+/** Stop connecting attempts when set. */
+static bool g_fTcpStopConnecting = false;
+#endif
+
+
+
+/**
+ * Disconnects the current client and frees all stashed data.
+ */
+static void utsTcpDisconnectClient(PUTSTRANSPORTCLIENT pClient)
+{
+ if (pClient->hTcpClient != NIL_RTSOCKET)
+ {
+ int rc = RTTcpServerDisconnectClient2(pClient->hTcpClient);
+ pClient->hTcpClient = NIL_RTSOCKET;
+ AssertRCSuccess(rc);
+ }
+
+ if (pClient->pbTcpStashed)
+ {
+ RTMemFree(pClient->pbTcpStashed);
+ pClient->pbTcpStashed = NULL;
+ }
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnWaitForConnect}
+ */
+static DECLCALLBACK(int) utsTcpWaitForConnect(PPUTSTRANSPORTCLIENT ppClientNew)
+{
+ int rc;
+ RTSOCKET hClientNew;
+
+ rc = RTTcpServerListen2(g_pTcpServer, &hClientNew);
+ Log(("utsTcpWaitForConnect: RTTcpServerListen2 -> %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ PUTSTRANSPORTCLIENT pClient = (PUTSTRANSPORTCLIENT)RTMemAllocZ(sizeof(UTSTRANSPORTCLIENT));
+ if (RT_LIKELY(pClient))
+ {
+ pClient->hTcpClient = hClientNew;
+ pClient->cbTcpStashed = 0;
+ pClient->cbTcpStashedAlloced = 0;
+ pClient->pbTcpStashed = NULL;
+ *ppClientNew = pClient;
+ }
+ else
+ {
+ RTTcpServerDisconnectClient2(hClientNew);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnNotifyReboot}
+ */
+static DECLCALLBACK(void) utsTcpNotifyReboot(void)
+{
+ Log(("utsTcpNotifyReboot: RTTcpServerDestroy(%p)\n", g_pTcpServer));
+ if (g_pTcpServer)
+ {
+ int rc = RTTcpServerDestroy(g_pTcpServer);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpServerDestroy failed in utsTcpNotifyReboot: %Rrc", rc);
+ g_pTcpServer = NULL;
+ }
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnNotifyBye}
+ */
+static DECLCALLBACK(void) utsTcpNotifyBye(PUTSTRANSPORTCLIENT pClient)
+{
+ Log(("utsTcpNotifyBye: utsTcpDisconnectClient %RTsock\n", pClient->hTcpClient));
+ utsTcpDisconnectClient(pClient);
+ RTMemFree(pClient);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnNotifyHowdy}
+ */
+static DECLCALLBACK(void) utsTcpNotifyHowdy(PUTSTRANSPORTCLIENT pClient)
+{
+ /* nothing to do here */
+ RT_NOREF1(pClient);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnBabble}
+ */
+static DECLCALLBACK(void) utsTcpBabble(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
+{
+ /*
+ * Try send the babble reply.
+ */
+ NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
+ int rc;
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT);
+ do rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
+ while (rc == VERR_INTERRUPTED);
+
+ /*
+ * Disconnect the client.
+ */
+ Log(("utsTcpBabble: utsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", pClient->hTcpClient, rc));
+ utsTcpDisconnectClient(pClient);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnSendPkt}
+ */
+static DECLCALLBACK(int) utsTcpSendPkt(PUTSTRANSPORTCLIENT pClient, PCUTSPKTHDR pPktHdr)
+{
+ Assert(pPktHdr->cb >= sizeof(UTSPKTHDR));
+
+ /*
+ * Write it.
+ */
+ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, UTSPKT_ALIGNMENT);
+ int rc = RTTcpWrite(pClient->hTcpClient, pPktHdr, cbToSend);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_INTERRUPTED)
+ {
+ /* assume fatal connection error. */
+ Log(("RTTcpWrite -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
+ utsTcpDisconnectClient(pClient);
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnRecvPkt}
+ */
+static DECLCALLBACK(int) utsTcpRecvPkt(PUTSTRANSPORTCLIENT pClient, PPUTSPKTHDR ppPktHdr)
+{
+ int rc = VINF_SUCCESS;
+ *ppPktHdr = NULL;
+
+ /*
+ * Read state.
+ */
+ size_t offData = 0;
+ size_t cbData = 0;
+ size_t cbDataAlloced;
+ uint8_t *pbData = NULL;
+
+ /*
+ * Any stashed data?
+ */
+ if (pClient->cbTcpStashedAlloced)
+ {
+ offData = pClient->cbTcpStashed;
+ cbDataAlloced = pClient->cbTcpStashedAlloced;
+ pbData = pClient->pbTcpStashed;
+
+ pClient->cbTcpStashed = 0;
+ pClient->cbTcpStashedAlloced = 0;
+ pClient->pbTcpStashed = NULL;
+ }
+ else
+ {
+ cbDataAlloced = RT_ALIGN_Z(64, UTSPKT_ALIGNMENT);
+ pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
+ if (!pbData)
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Read and valid the length.
+ */
+ while (offData < sizeof(uint32_t))
+ {
+ size_t cbRead;
+ rc = RTTcpRead(pClient->hTcpClient, pbData + offData, sizeof(uint32_t) - offData, &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbRead == 0)
+ {
+ Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#1)\n", rc));
+ rc = VERR_NET_NOT_CONNECTED;
+ break;
+ }
+ offData += cbRead;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ ASMCompilerBarrier(); /* paranoia^3 */
+ cbData = *(uint32_t volatile *)pbData;
+ if (cbData >= sizeof(UTSPKTHDR) && cbData <= UTSPKT_MAX_SIZE)
+ {
+ /*
+ * Align the length and reallocate the return packet it necessary.
+ */
+ cbData = RT_ALIGN_Z(cbData, UTSPKT_ALIGNMENT);
+ if (cbData > cbDataAlloced)
+ {
+ void *pvNew = RTMemRealloc(pbData, cbData);
+ if (pvNew)
+ {
+ pbData = (uint8_t *)pvNew;
+ cbDataAlloced = cbData;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the remainder of the data.
+ */
+ while (offData < cbData)
+ {
+ size_t cbRead;
+ rc = RTTcpRead(pClient->hTcpClient, pbData + offData, cbData - offData, &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbRead == 0)
+ {
+ Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc / cbRead=0 -> VERR_NET_NOT_CONNECTED (#2)\n", rc));
+ rc = VERR_NET_NOT_CONNECTED;
+ break;
+ }
+ offData += cbRead;
+ }
+ }
+ }
+ else
+ rc = VERR_NET_PROTOCOL_ERROR;
+ }
+ if (RT_SUCCESS(rc))
+ *ppPktHdr = (PUTSPKTHDR)pbData;
+ else
+ {
+ /*
+ * Deal with errors.
+ */
+ if (rc == VERR_INTERRUPTED)
+ {
+ /* stash it away for the next call. */
+ pClient->cbTcpStashed = cbData;
+ pClient->cbTcpStashedAlloced = cbDataAlloced;
+ pClient->pbTcpStashed = pbData;
+ }
+ else
+ {
+ RTMemFree(pbData);
+
+ /* assume fatal connection error. */
+ Log(("utsTcpRecvPkt: RTTcpRead -> %Rrc -> utsTcpDisconnectClient(%RTsock)\n", rc, pClient->hTcpClient));
+ utsTcpDisconnectClient(pClient);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnPollSetAdd}
+ */
+static DECLCALLBACK(int) utsTcpPollSetAdd(RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart)
+{
+ return RTPollSetAddSocket(hPollSet, pClient->hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnPollSetRemove}
+ */
+static DECLCALLBACK(int) utsTcpPollSetRemove(RTPOLLSET hPollSet, PUTSTRANSPORTCLIENT pClient, uint32_t idStart)
+{
+ RT_NOREF1(pClient);
+ return RTPollSetRemove(hPollSet, idStart);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnPollIn}
+ */
+static DECLCALLBACK(bool) utsTcpPollIn(PUTSTRANSPORTCLIENT pClient)
+{
+ int rc = RTTcpSelectOne(pClient->hTcpClient, 0/*cMillies*/);
+ return RT_SUCCESS(rc);
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnTerm}
+ */
+static DECLCALLBACK(void) utsTcpTerm(void)
+{
+ /* Shut down the server (will wake up thread). */
+ if (g_pTcpServer)
+ {
+ Log(("utsTcpTerm: Destroying server...\n"));
+ int rc = RTTcpServerDestroy(g_pTcpServer);
+ if (RT_FAILURE(rc))
+ RTMsgInfo("RTTcpServerDestroy failed in utsTcpTerm: %Rrc", rc);
+ g_pTcpServer = NULL;
+ }
+
+ Log(("utsTcpTerm: done\n"));
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnInit}
+ */
+static DECLCALLBACK(int) utsTcpInit(void)
+{
+ int rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NET_DOWN)
+ {
+ RTMsgInfo("RTTcpServerCreateEx(%s, %u,) failed: %Rrc, retrying for 20 seconds...\n",
+ g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
+ uint64_t StartMs = RTTimeMilliTS();
+ do
+ {
+ RTThreadSleep(1000);
+ rc = RTTcpServerCreateEx(g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, &g_pTcpServer);
+ } while ( rc == VERR_NET_DOWN
+ && RTTimeMilliTS() - StartMs < 20000);
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("RTTcpServerCreateEx succceeded.\n");
+ }
+ if (RT_FAILURE(rc))
+ {
+ g_pTcpServer = NULL;
+ RTMsgError("RTTcpServerCreateEx(%s, %u,) failed: %Rrc\n",
+ g_szTcpBindAddr[0] ? g_szTcpBindAddr : NULL, g_uTcpBindPort, rc);
+ }
+ }
+
+ return rc;
+}
+
+/** Options */
+enum UTSTCPOPT
+{
+ UTSTCPOPT_BIND_ADDRESS = 1000,
+ UTSTCPOPT_BIND_PORT
+};
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnOption}
+ */
+static DECLCALLBACK(int) utsTcpOption(int ch, PCRTGETOPTUNION pVal)
+{
+ int rc;
+
+ switch (ch)
+ {
+ case UTSTCPOPT_BIND_ADDRESS:
+ rc = RTStrCopy(g_szTcpBindAddr, sizeof(g_szTcpBindAddr), pVal->psz);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorRc(VERR_INVALID_PARAMETER, "TCP bind address is too long (%Rrc)", rc);
+ return VINF_SUCCESS;
+
+ case UTSTCPOPT_BIND_PORT:
+ g_uTcpBindPort = pVal->u16 == 0 ? UTS_TCP_DEF_BIND_PORT : pVal->u16;
+ return VINF_SUCCESS;
+ }
+ return VERR_TRY_AGAIN;
+}
+
+/**
+ * @interface_method_impl{UTSTRANSPORT,pfnUsage}
+ */
+DECLCALLBACK(void) utsTcpUsage(PRTSTREAM pStream)
+{
+ RTStrmPrintf(pStream,
+ " --tcp-bind-address <address>\n"
+ " The address(es) to listen to TCP connection on. Empty string\n"
+ " means any address, this is the default.\n"
+ " --tcp-bind-port <port>\n"
+ " The port to listen to TCP connections on.\n"
+ " Default: %u\n"
+ , UTS_TCP_DEF_BIND_PORT);
+}
+
+/** Command line options for the TCP/IP transport layer. */
+static const RTGETOPTDEF g_TcpOpts[] =
+{
+ { "--tcp-bind-address", UTSTCPOPT_BIND_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--tcp-bind-port", UTSTCPOPT_BIND_PORT, RTGETOPT_REQ_UINT16 }
+};
+
+/** TCP/IP transport layer. */
+const UTSTRANSPORT g_TcpTransport =
+{
+ /* .szName = */ "tcp",
+ /* .pszDesc = */ "TCP/IP",
+ /* .cOpts = */ &g_TcpOpts[0],
+ /* .paOpts = */ RT_ELEMENTS(g_TcpOpts),
+ /* .pfnUsage = */ utsTcpUsage,
+ /* .pfnOption = */ utsTcpOption,
+ /* .pfnInit = */ utsTcpInit,
+ /* .pfnTerm = */ utsTcpTerm,
+ /* .pfnWaitForConnect = */ utsTcpWaitForConnect,
+ /* .pfnPollIn = */ utsTcpPollIn,
+ /* .pfnPollSetAdd = */ utsTcpPollSetAdd,
+ /* .pfnPollSetRemove = */ utsTcpPollSetRemove,
+ /* .pfnRecvPkt = */ utsTcpRecvPkt,
+ /* .pfnSendPkt = */ utsTcpSendPkt,
+ /* .pfnBabble = */ utsTcpBabble,
+ /* .pfnNotifyHowdy = */ utsTcpNotifyHowdy,
+ /* .pfnNotifyBye = */ utsTcpNotifyBye,
+ /* .pfnNotifyReboot = */ utsTcpNotifyReboot,
+ /* .u32EndMarker = */ UINT32_C(0x12345678)
+};
diff --git a/src/VBox/ValidationKit/vms/t-dos20.txt b/src/VBox/ValidationKit/vms/t-dos20.txt
new file mode 100644
index 00000000..f251f1f8
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos20.txt
@@ -0,0 +1,27 @@
+
+Test VM - t-dos20 - PC-DOS 2.0 on a harddisk
+============================================
+
+Setup:
+ - Create a default DOS VM 't-dos20', but restrict the disk size to 31MB.
+ - Partition the disk with a single partition.
+ - format C: /s
+ - Install (copy?) the DOS files to C:\DOS
+ - Copy DosSleep.exe and DosVmOff.com onto the disk.
+ - Create config.sys if needed.
+ - autoexec.bat (cannot test from test.bat, so all in one file):
+ PATH C:\DOS;C:\
+ ECHO ON
+ ECHO TESTING chkdsk C: >COM1
+ dossleep 1
+ C:\DOS\CHKDSK C:
+ ECHO PASSED>COM1
+ ECHO Powering off VM in 5 seconds...
+ DosSleep 1
+ DosSleep 1
+ DosSleep 1
+ DosSleep 1
+ DosSleep 1
+ - More tests can be added, if desired. If test failure can be detected, end
+ with echoing 'FAILED' to COM1 instead of 'PASSED'.
+
diff --git a/src/VBox/ValidationKit/vms/t-dos401-emm386-win30me.txt b/src/VBox/ValidationKit/vms/t-dos401-emm386-win30me.txt
new file mode 100644
index 00000000..38dc7a75
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos401-emm386-win30me.txt
@@ -0,0 +1,9 @@
+
+Test VM - t-dos401-emm386-win30me - DOS 4.01 /w patch and Windows 3.0 Multimedia Edition
+========================================================================================
+
+1. Set up t-dos401-win30me first.
+2. Clone it into 't-dos401-emm386-win30me', make sure the .vdi is also renamed.
+3. Edit C:\config.sys, adding "DEVICE=c:\DOS\EMM386.SYS 8192" after the HIMEM line.
+4. Edit C:\test.bat to jump over the 386 mode and standard mode windows tests.
+
diff --git a/src/VBox/ValidationKit/vms/t-dos401-win30me.txt b/src/VBox/ValidationKit/vms/t-dos401-win30me.txt
new file mode 100644
index 00000000..a68d197b
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos401-win30me.txt
@@ -0,0 +1,88 @@
+
+Test VM - t-dos401-win30me - DOS 4.01 /w patch and Windows 3.0 Multimedia Edition
+=================================================================================
+
+Setup:
+ - Create a default DOS VM 't-dos401-win30me', but restrict the memory to 4MB.
+ - Install DOS (single partition, etc).
+ - Install CDROM driver (e.g. OAK).
+ - Install Window 3.0 multimedia edition into C:\MWINDOWS.
+ - Select tandy soundblaster driver, make sure to configure
+ them correctly (port 220, irq 5, dma 5/7) or they will hang
+ the system. Check that it works.
+ - Disable the tandy welcome screen.
+ - Copy DosSleep.exe, DosVmOff.com, and WinExit.exe onto the disk.
+ - Start the macro recorder (recorder.exe) in windows and record two different
+ macro files, both which are associated with Shift-F7 and play in normal time.
+ First C:\WinExit.Rec:
+ - [Anything that might be useful+easy to test in read mode]
+ - File->Run in the program manager using keyboard (Alt-F, R)
+ - Type in "C:\WinExit.exe" and enter (no mouse).
+ - Save macro.
+ Second (hit File->New first) C:\TestMM.Rec:
+ - File->Run in the program manager using keyboard (Alt-F, R)
+ - Type in "welcome.exe" and enter (no mouse).
+ - Wait for the tandy welcome animation to finish.
+ - [Anything else that might be useful+easy to test]
+ - File->Run in program manager again.
+ - Type in "C:\WinExit.exe" and enter (no mouse).
+ - Save macro.
+ - Edit win.ini changing the REC file association to:
+ rec=RECORDER.EXE -H +F7 ^.REC
+ This allows us to load .REC with Shift-F7 macros inside them at startup and
+ execute them. (Windows 3.0 didn't have a startup folder.)
+ - Make a copy of win.ini called win-mm.ini changing the 'load=scrnsvr.exe'
+ statement to 'load=c:\testmm.rec'.
+ - Make a copy of win.ini called win-exit.ini changing the 'load=scrnsvr.exe'
+ statement to 'load=c:\winexit.rec'.
+ - Create c:\test.bat with the following content:
+ :dos-stuff
+ echo TESTING chkdsk C: >COM1
+ dossleep 1
+ chkdsk c:
+ if not errorlevel 0 goto fail
+
+ echo TESTING c:\mwindows\msd.exe /f nul >COM1
+ dossleep 1
+ c:\mwindows\msd.exe /f nul
+ if not errorlevel 0 goto fail
+
+ :386mode
+ echo TESTING win /3 >COM1
+ dossleep 1
+ copy c:\mwindows\win-mm.ini c:\mwindows\win.ini
+ win /3
+ if not errorlevel 0 goto fail
+
+ :standardmode-max-4mb
+ echo TESTING win /s >COM1
+ dossleep 1
+ copy c:\mwindows\win-mm.ini c:\mwindows\win.ini
+ win /s
+ if not errorlevel 0 goto fail
+
+ :realmode
+ echo TESTING win /r >COM1
+ dossleep 1
+ copy c:\mwindows\win-exit.ini c:\mwindows\win.ini
+ win /r
+ if not errorlevel 0 goto fail
+
+ :success
+ echo PASSED>COM1
+ goto done
+
+ :fail
+ echo ERRORLEVEL=%ERRORLEVEL%
+ echo FAILED>COM1
+
+ :done
+ echo powering off the vm in 5 seconds...
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dosvmoff
+ - Edit c:\autoexec.bat appending "echo on" and "call c:\test.bat".
+
diff --git a/src/VBox/ValidationKit/vms/t-dos50-emm386-win31.txt b/src/VBox/ValidationKit/vms/t-dos50-emm386-win31.txt
new file mode 100644
index 00000000..d0d2a872
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos50-emm386-win31.txt
@@ -0,0 +1,9 @@
+
+Test VM - t-dos50-emm386-win31 - DOS 5.0 and Windows 3.1, using EMM386
+======================================================================
+
+1. Set up t-dos50-win31 first.
+2. Clone it into 't-dos50-emm386-win31', make sure the .vdi is also renamed.
+3. Edit C:\config.sys, adding "DEVICE=c:\WINDOWS\EMM386.SYS" after the HIMEM line.
+
+
diff --git a/src/VBox/ValidationKit/vms/t-dos50-win31.txt b/src/VBox/ValidationKit/vms/t-dos50-win31.txt
new file mode 100644
index 00000000..a64432bf
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos50-win31.txt
@@ -0,0 +1,78 @@
+
+Test VM - t-dos50-win31 - DOS 5.0 and Windows 3.1
+=================================================
+
+Setup:
+ - Create a default DOS VM 't-dos50-win31'.
+ - Install DOS (single partition, etc).
+ - Install CDROM driver (e.g. OAK).
+ - Install Window 3.1 into C:\WINDOWS.
+ - Open the control panel and install sound blaster 1.5 drivers (port 220, irq 5,
+ dma 5/7).
+ - Open the control panel and make sure all system sounds have .WAV files associated
+ with them. Test that you can hear them.
+ - Copy DosSleep.exe, DosVmOff.com, and WinExit.exe onto the disk (C:\).
+ - Start the macro recorder (recorder.exe) in windows and record a macro file
+ that can be run automatically, make sure to associate it with Shift-F7 and
+ that it plays in normal time.
+ First C:\AbtExit.rec:
+ - Help->About in the program manager using keyboard (Alt-H, A).
+ - Dismiss using the enter key after a beat (no mouse).
+ - [Anything that might be useful+easy to test]
+ - File->Run in the program manager using keyboard (Alt-F, R)
+ - Type in "C:\WinExit.exe" and enter (no mouse).
+ - Save macro.
+ - Open the startup folder and create a program item with the following command line:
+ C:\WINDOWS\RECORDER.EXE -H +F7 C:\ABTREXIT.REC
+ - Create c:\test.bat with the following content:
+ :dos-stuff
+ echo TESTING CHKDSK C: >COM1
+ dossleep 1
+ C:\DOS\CHKDSK.EXE C:
+ if not errorlevel 0 goto fail
+
+ echo TESTING msd.exe /P NUL >COM1
+ dossleep 1
+ C:\WINDOWS\MSD.EXE /P NUL
+ if not errorlevel 0 goto fail
+
+ echo TESTING qbasic.exe /RUN C:\HELLO.BAS >COM1
+ dossleep 1
+ C:\DOS\QBASIC.EXE /RUN C:\HELLO.BAS
+ if not errorlevel 0 goto fail
+
+ :386mode
+ echo TESTING win /3 >COM1
+ dossleep 1
+ win /3
+ if not errorlevel 0 goto fail
+
+ :standardmode
+ echo TESTING win /s >COM1
+ dossleep 1
+ win /s
+ if not errorlevel 0 goto fail
+
+ :success
+ echo PASSED>COM1
+ goto done
+
+ :failed
+ echo FAILED>COM1
+ goto done
+
+ :done
+ echo powering off the VM in 5 seconds...
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dosvmoff
+ - Create C:\HELLO.BAS with the following content:
+ PRINT "Hello World!"
+ SYSTEM
+ END
+ - Edit c:\autoexec.bat appending "echo on" and "call c:\test.bat".
+ - Check that C:\config.sys contains himem.sys from windows and no emm386.
+
diff --git a/src/VBox/ValidationKit/vms/t-dos622-emm386.txt b/src/VBox/ValidationKit/vms/t-dos622-emm386.txt
new file mode 100644
index 00000000..30291cc6
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos622-emm386.txt
@@ -0,0 +1,8 @@
+
+Test VM - t-dos622-emm386 - DOS 6.22 using EMM386
+=================================================
+
+1. Set up t-dos622 first.
+2. Clone it into 't-dos622-emm386', make sure the .vdi is also renamed.
+3. Edit C:\config.sys, adding "DEVICE=c:\DOS\EMM386.SYS /V" after the HIMEM line.
+
diff --git a/src/VBox/ValidationKit/vms/t-dos622.txt b/src/VBox/ValidationKit/vms/t-dos622.txt
new file mode 100644
index 00000000..6fcd888f
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos622.txt
@@ -0,0 +1,50 @@
+
+Test VM - t-dos622 - DOS 6.22
+=============================
+
+Setup:
+ - Create a default DOS VM 't-dos622'.
+ - Install DOS (single partition, etc).
+ - Install CDROM driver (e.g. OAK).
+ - Copy DosSleep.exe and DosVmOff.com onto the disk (C:\).
+ - Create c:\test.bat with the following content:
+ echo on
+
+ echo TESTING chkdsk.exe C: >COM1
+ dossleep 1
+ C:\DOS\CHKDSK.EXE C:
+ IF NOT ERRORLEVEL 0 goto fail
+
+ echo TESTING msd.exe /P NUL >COM1
+ dossleep 1
+ C:\DOS\MSD.EXE /P NUL
+
+ echo TESTING qbasic.exe /RUN C:\HELLO.BAS >COM1
+ dossleep 1
+ C:\DOS\QBASIC.EXE /RUN C:\HELLO.BAS
+ IF NOT ERRORLEVEL 0 goto fail
+
+ REM Done
+ echo PASSED>COM1
+ goto done
+
+ :fail
+ echo FAILED>COM1
+ goto done
+
+ :done
+ echo Powering off the VM in 5 seconds...
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dossleep 1
+ dosvmoff
+ :exit
+ - Create C:\HELLO.BAS with the following content:
+ PRINT "Hello World!"
+ SYSTEM
+ END
+ - Edit c:\autoexec.bat appending "echo on" and "call c:\test.bat".
+ - Check that C:\config.sys contains himem.sys from windows and no emm386.
+
diff --git a/src/VBox/ValidationKit/vms/t-dos71.txt b/src/VBox/ValidationKit/vms/t-dos71.txt
new file mode 100644
index 00000000..37cdb1be
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-dos71.txt
@@ -0,0 +1,34 @@
+
+Test VM - t-dos71 - PC-DOS 7.1 (or 7.0)
+=======================================
+
+Setup:
+ - Create a default DOS VM 't-dos71'.
+ - Install DOS (single partition, etc). Make sure to install the IBM anti virus.
+ - Make sure POWER.EXE is loaded by Config.sys and that emm386 isn't used.
+ - Add /V to HIMEM.
+ - Install CDROM driver (e.g. OAK).
+ - Copy DosSleep.exe and DosVmOff.com onto the disk (C:\).
+ - Create c:\test.bat with the following content:
+ echo TESTING: chkdsk C: >COM1
+ C:\DOS\CHKDSK.COM C:
+ @IF NOT ERRORLEVEL 0 GOTO fail
+ @
+ echo TESTING: IBM anti virus scan >COM1
+ c:\dos\ibmavsp.exe C: -ALL -NLOG
+ @IF NOT ERRORLEVEL 0 GOTO fail
+ @
+ echo PASSED>COM1
+ goto done
+
+ :fail
+ echo FAILED>COM1
+ goto done
+
+ :done
+ @echo Powering off in 5 seconds...
+ dossleep 5
+ dosvmoff
+ - Edit c:\autoexec.bat appending "echo on" and "call c:\test.bat".
+ - Check that C:\config.sys contains himem.sys from windows and no emm386.
+
diff --git a/src/VBox/ValidationKit/vms/t-nsthwvirt-ubuntu-64.txt b/src/VBox/ValidationKit/vms/t-nsthwvirt-ubuntu-64.txt
new file mode 100644
index 00000000..8a090a2e
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-nsthwvirt-ubuntu-64.txt
@@ -0,0 +1,26 @@
+
+Test VM - nsthwvirt-ubuntu - Nested hardware-virtualization Ubuntu
+==================================================================
+
+Setup:
+ - Configure a VM tst-nsthwvirt-ubuntu-64 with default Ubuntu 64-bit setting,
+ with a 6 GB or larger disk.
+ - Configure the VM with 4 GB of RAM.
+ - Make sure networking is NAT.
+ - Disable audio for the VM.
+ - Install Ubuntu 17.04 amd64.
+ - Disable screen-blanking, and suspend:
+ - Click 'System Settings' from the Unity bar on the left side or open
+ a terminal and run 'unity-control-center'.
+ - From 'Power':
+ - Switch 'Suspend when inactive for' to 'Dont suspend'.
+ - From 'Brightness & Lock':
+ - Switch 'Turn screen off when inactive' to 'Never'
+ - Switch 'Lock' to 'off'.
+ - Close the system settings window.
+ - Disable auto-mounting of the CDROM:
+ gsettings set org.gnome.desktop.media-handling automount false
+ - Disable VirtualBox Guest Additions that ship with Ubuntu 17.04 by default:
+ sudo sh -c "echo 'blacklist vboxguest' > /etc/modprobe.d/blacklist.conf"
+ sudo sh -c "echo 'blacklist vboxvideo' > /etc/modprobe.d/blacklist.conf"
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-nt310.txt b/src/VBox/ValidationKit/vms/t-nt310.txt
new file mode 100644
index 00000000..32db4621
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-nt310.txt
@@ -0,0 +1,27 @@
+
+Test VM - t-nt310 - Windows NT 3.1 no service pack
+==================================================
+
+Setup:
+ - Configure a VM t-nt310 with using the Windows NT 3.x settings, but with only
+ 64 MiB RAM (can't detect more) and 512 MiB disk (2GiB fails NTFS conversion).
+ - Enable COM1.
+ - Make sure networking is NAT and PCNET.
+ - Make sure you're using BusLogic for both HD and DVD-ROM.
+ - Enable DNS proxying:
+ VBoxManage modifyvm t-nt310 --natdnsproxy1 on.
+ - Optionally set 486 as the CPU profile (may avoid install problems):
+ VBoxManage modifyvm t-nt310 --cpu-profile "Intel 80486"
+ - Install the OS.
+ - PCNet are not included on install media, so get them of the
+ net and install via floppy when it fails to detect NICs.
+ - Use 'password' as Administrator password.
+ - Open the system settings and add an environment variable "SystemDrive" with the
+ value "C:" (NT 3.50 and later sets this automagically).
+ - While in the system settings, reduce the boot wait to 1 second.
+ - Open the network settings and add the TCPIP protocol. Configure it with IP
+ 10.0.2.15/24, gateway 10.0.2.2, and DNS 10.0.2.3 (via "Connections..." button).
+ - Reboot and check that you can ping stuff.
+ - Insert the ValidationKit ISO and install the test execution service, see
+ vboxtxs-readme.txt for details. Needs the NT 3.x bits.
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-nt350.txt b/src/VBox/ValidationKit/vms/t-nt350.txt
new file mode 100644
index 00000000..3553edf7
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-nt350.txt
@@ -0,0 +1,23 @@
+
+Test VM - t-nt350 - Windows NT 3.5(0) no service pack
+=====================================================
+
+Setup:
+ - Configure a VM t-nt350 with using the Windows NT 3.x settings, up
+ the memory to 128MiB.
+ - Enable COM1.
+ - Make sure networking is NAT and PCNET.
+ - Make sure you're using BusLogic for both HD and DVD-ROM.
+ - Optionally set 486 as the CPU profile (may avoid install problems):
+ VBoxManage modifyvm t-nt350 --cpu-profile "Intel 80486"
+ - Install the OS.
+ - You probably have to select the AMD PCNET Family Ethernet Adapter
+ manually if detection fails.
+ - Make sure to configure DHCP and DNS.
+ - In the system settings:
+ - Reduce the boot wait to 1 second.
+ - Disable automatic reboot in recovery settings.
+ - Check that you can ping stuff.
+ - Insert the ValidationKit ISO and install the test execution service, see
+ vboxtxs-readme.txt for details. Needs the NT 3.x bits.
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-nt4sp1.txt b/src/VBox/ValidationKit/vms/t-nt4sp1.txt
new file mode 100644
index 00000000..9f1eac9c
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-nt4sp1.txt
@@ -0,0 +1,21 @@
+
+Test VM - nt4sp1 - Windows NT 4 Service Pack 1
+==============================================
+
+Setup:
+ - Configure a VM t-nt4sp1 with default Windows NT 4 settings. Make sure to
+ configure the disk to 8 GB or higher capacity (2 GB suffices).
+ - Make sure networking is NAT.
+ - Install Windows NT 4 SP 1.
+ - Disable CD autorun:
+ - Start regedit.
+ - Set the value HKLM/System/CurrentControlSet/Services/Cdrom to 0.
+ - Shorten boot menu wait, disable automatic reboot on STOP, make it write a
+ dump:
+ - Right click on "My Computer", select "Properties" and go to the
+ "Startup/Shutdown" tab.
+ - Change the "Show list for" entry field to "1" second.
+ - Uncheck "Automatically reboot".
+ - Check "Write debugging information to".
+ - Check "Overwrite any existing file".
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-sol11u1.txt b/src/VBox/ValidationKit/vms/t-sol11u1.txt
new file mode 100644
index 00000000..b57fc640
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-sol11u1.txt
@@ -0,0 +1,16 @@
+Test VM - sol11u1 - Solaris 11 update 1
+=======================================
+
+Setup:
+ - Configure a VM t-sol11u1 with the default Solaris 11 settings.
+ - Make sure networking is NAT.
+ - Install Solaris 11 update 1, default user 'test'.
+ - Change the default password policy in /etc/default/passwd:
+ - MINUPPER=0
+ - MINLOWER=0
+ - MAXREPEATS=0
+ - MINSPECIAL=0
+ - MINDIGIT=0
+ - Adjust the grub timeout to 1 second.
+ - ?More?
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-ubuntu-15_10-64-efi.txt b/src/VBox/ValidationKit/vms/t-ubuntu-15_10-64-efi.txt
new file mode 100644
index 00000000..7e2315bb
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-ubuntu-15_10-64-efi.txt
@@ -0,0 +1,12 @@
+
+Test VM - tst-ubuntu-15_10-64-efi - Ubuntu smoke test
+=====================================================
+
+Setup:
+ - Configure a VM tst-ubuntu-15_10-64-efi with default Ubuntu 64-bit setting,
+ with a 6 GB or larger disk.
+ - Make sure that EFI under "System / Extended Features" is checked.
+ - Configure the VM with 4 GB of RAM.
+ - Make sure networking is NAT.
+ - Install Ubuntu 15.10 amd64.
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-ubuntu-20_04-64.txt b/src/VBox/ValidationKit/vms/t-ubuntu-20_04-64.txt
new file mode 100644
index 00000000..fb06acf7
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-ubuntu-20_04-64.txt
@@ -0,0 +1,36 @@
+Test VM - ubuntu-20-04_64 - Ubuntu 20.04 64-bit (for IOMMU testing)
+===================================================================
+
+Setup:
+ - Configure a VM tst-ubuntu-20_04-64 with default Ubuntu 64-bit setting,
+ with a default 10 GB or larger disk.
+ - Configure the VM with 2 GB of RAM, ICH9 chipset, I/O APIC and IOMMU (AMD or Intel).
+ - Make sure networking is NAT.
+ - Disable audio for the VM.
+ - Install Ubuntu 20.04 amd64.
+ Disallow "Install Updates During Installation"
+ - After installation, open "Settings" and configure as follows:
+ - Power:
+ - Set "Blank Screen" to "Never"
+ - Set "Automatic Suspend" to "Off"
+ - Displays:
+ - Set screen resolution to 1280x960 (4:3)
+ - Privacy -> Screen lock:
+ - Set "Automatic Screen Lock" to "Off"
+ - Set "Lock Screen On Suspend" to "Off"
+ - Set "Show notifications on Lock screen" to "Off"
+ - Disable VirtualBox Guest Additions that ship with Ubuntu by default:
+ - sudo sh -c "echo 'blacklist vboxguest' >> /etc/modprobe.d/blacklist.conf"
+ - sudo sh -c "echo 'blacklist vboxvideo' >> /etc/modprobe.d/blacklist.conf"
+ - Disable auto-mounting of the CDROM:
+ gsettings set org.gnome.desktop.media-handling automount false
+ sudo mkdir -p /media/cdrom
+ Add /dev/sr0 to /etc/fstab for mounting CD (see readme_first.txt for details).
+ - Enable address translation for Intel IOMMU:
+ - sudo nano /etc/default/grub
+ - Change GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on"
+ i.e. remove "quiet splash" and replaces with "intel_iommu=on" instead.
+ This setting has no effect when AMD IOMMU is configured so always configure this so we can switch testing
+ AMD or Intel IOMMUs.
+ - Proceed as shown in readme_first.txt
+
diff --git a/src/VBox/ValidationKit/vms/t-win7-32-1.txt b/src/VBox/ValidationKit/vms/t-win7-32-1.txt
new file mode 100644
index 00000000..2a35719a
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-win7-32-1.txt
@@ -0,0 +1,8 @@
+
+Test VM - tst-win7-32-1
+=======================
+
+Based on tst-win7-32, but solves the following problems:
+
+- Activation nag screen should be gone, which should make TxS start reliably.
+- Power management settings altered to not turn off the display after 5 / 10 minutes (blank screen).
diff --git a/src/VBox/ValidationKit/vms/t-win7-32.txt b/src/VBox/ValidationKit/vms/t-win7-32.txt
new file mode 100644
index 00000000..d1ce7ebd
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-win7-32.txt
@@ -0,0 +1,14 @@
+
+Test VM - tst-win7-32
+=====================
+
+Setup:
+ - Configure a VM t-win7-32 with default windows 7 32-bit settings and disk size.
+ - VBoxManage unattended install t-win7-32 --iso i:\Windows\7\rtm\en_windows_7_enterprise_x86_dvd_x15-70745.iso \
+ --hostname=t-win7-32.smoketest --user=Administrator --password=password
+ - Start VM and perform unattended installation.
+ - Eject DVD and floppy.
+ - Create a user 'test' without a password.
+ - Create a user 'test2' with 'test2' as password.
+ - Enable the guest user.
+ - Proceed as shown in readme_first.txt
diff --git a/src/VBox/ValidationKit/vms/t-xppro.txt b/src/VBox/ValidationKit/vms/t-xppro.txt
new file mode 100644
index 00000000..50dbb471
--- /dev/null
+++ b/src/VBox/ValidationKit/vms/t-xppro.txt
@@ -0,0 +1,44 @@
+
+Test VM - xppro - Windows XP Professional
+=========================================
+
+Setup:
+ - Configure a VM t-xppro1 with default Windows XP settings. 2 GB or more disk.
+ - Make sure PAE is disabled.
+ - Make sure networking is NAT.
+ - Install Windows XP Pro.
+ - Create a user 'test' without a password.
+ - Disable non-windows-logo signed driver popups:
+ - Start menu -> Right-click on "My Computer" -> Select Properties.
+ - Go to the Hardware tab and press the Driver Signing button.
+ - Select the "Ignore - Install the software anyway and don't ask for my
+ approval" radio button.
+ - Make sure "Make this action the system default" is checked.
+ - Ok both dialogs.
+ - Redo from start to verify that it actually changed.
+ - Complete disable windows update:
+ - Start menu -> Right-click on "My Computer" -> Select Properties.
+ - Go to the Automatic Updates tab.
+ - Select the "Turn off automatic updating" radio button.
+ - Ok the dialog.
+ - Disable automatic reboot on STOP, make it write a
+ dump:
+ - Right click on "My Computer", select "Properties" and go to the
+ "Advanced" tab and press the "Startupp and Recovery" button.
+ - Make sure the "Time to display list of operating systems" is 1.
+ - Uncheck "Automatically restart".
+ - Select "Kernel memory dump".
+ - Check "Overwrite any existing file".
+ - Ok both dialogs.
+ - Redo from start to verify correctness.
+ - Disable CD autorun. Follow these instructions:
+ http://support.microsoft.com/kb/967715
+ - Disable driver search on Windows Update (will fail bitching about
+ unsigned/unknown publisher of iuident.cab, because we're getting
+ redirected when doing v4 update accesses):
+ - Run/start gpedit.msc.
+ - Navigate to User Configuration -> Administrative Templates -> System.
+ - Change "Configure driver search locations" to "Enabled" and check
+ the "Don't search Windows Update".
+ - Change "Windows Automatic Updates" to "Disabled".
+ - Proceed as shown in readme_first.txt